d804a436d7
Polyfill capabilities landed for Festipod's T02 features (all generic,
zero-domain — the consumer injects the domain).
- inbox: implement the previously-stubbed namespace. post(target,{from?,
payload,ts?}) deposits {from,payload,ts} as RDF via docs.sparqlUpdate (the
real injected ng, never makeNg); read/materialize + watch emulate the curator
in-lib (deposits read via docs.sparqlQuery). `from` optional = anonymity.
- write-guard: caps.hasWritePolicy() + ng-proxy.sparql_update rejects when the
target doc is under a write policy and the current user lacks its write cap;
passthrough otherwise (no regression). Read-cap registry unchanged.
- sparql.ts (new): escapeLiteral / escapeIri / assertNuri, exported from index.
store-registry now escapes every literal and validates/encodes every IRI-
position value — closes a SPARQL-injection hole where an untrusted username
could inject triples into the shim (the account→doc-NURI trust root).
Tests: inbox, sparql (incl. injection), ng-proxy write-guard, isolation-active.
68 tests pass; tsc --noEmit rc=0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
2.3 KiB
TypeScript
60 lines
2.3 KiB
TypeScript
/**
|
|
* The wrapped `ng`: a Proxy that forwards every method to the real SDK and
|
|
* overrides only what the broker/verifier will do natively at migration. The
|
|
* surface stays identical to `@ng-org/web`'s `ng`.
|
|
*/
|
|
|
|
import { getConfig, getCaps, getCurrentUser } from "./polyfill";
|
|
import type { Nuri } from "./types";
|
|
|
|
export function makeNg(): Record<string, any> {
|
|
return new Proxy({} as Record<string, any>, {
|
|
get(_target, prop: string) {
|
|
const { ng } = getConfig();
|
|
|
|
// login / session_start → open the SHARED wallet invisibly.
|
|
if (prop === "login" || prop === "session_start") {
|
|
return (...args: any[]) => {
|
|
// TODO(polyfill): supply shared-wallet credentials so no wallet UI
|
|
// is shown. For now, passthrough.
|
|
return ng[prop]!(...args);
|
|
};
|
|
}
|
|
|
|
// sparql_update → write guard (emulated write-cap check).
|
|
// Mirrors the target broker/verifier: a write is refused unless the wallet
|
|
// holds the document's WRITE cap. Emulated per-document via CapRegistry.
|
|
// args = (session_id, query, anchor?) — `anchor` is the target doc NURI.
|
|
if (prop === "sparql_update") {
|
|
return (...args: any[]) => {
|
|
const anchor = args[2] as Nuri | undefined;
|
|
const caps = getCaps();
|
|
// Passthrough (no regression) unless a WRITE policy exists AND this
|
|
// specific document is governed by it. Ungoverned docs (mono-store
|
|
// default, no cap declared) flow through exactly as before.
|
|
if (
|
|
typeof anchor === "string" &&
|
|
caps.hasWritePolicy() &&
|
|
caps.governsWrite(anchor) &&
|
|
!caps.canWrite(anchor, getCurrentUser())
|
|
) {
|
|
return Promise.reject(
|
|
new Error(
|
|
`[ng-eventually] write denied: current user lacks the write cap for ${anchor}`,
|
|
),
|
|
);
|
|
}
|
|
return ng.sparql_update!(...args);
|
|
};
|
|
}
|
|
|
|
// TODO(anticipated API): inbox_post_link + capability operations — expose
|
|
// here with their anticipated signatures, emulated for now.
|
|
|
|
// Everything else: passthrough to the real SDK, unchanged.
|
|
const real = ng[prop];
|
|
return typeof real === "function" ? real.bind(ng) : real;
|
|
},
|
|
});
|
|
}
|