Commit Graph

5 Commits

Author SHA1 Message Date
Sylvain Duchesne c85c635f63 perf(client): targeted shim account resolution (no full-scan on hot paths)
loadShim() read EVERY account record in the shim (SELECT over the whole anchor
graph). On a shim that accumulates accounts (the shared wallet grows), that is
O(accounts) and hangs the hot path (~90s at ensureAccount → loadShim). Same
principle as per-doc reads: never scan a shared structure.

Add resolveAccount(username): a BOUNDED SELECT anchored on the single subject
accountSubject(username) → O(1), independent of account count. Cache in a
per-account Map (cleared by resetRegistryCache). Hot paths now use it:
ensureAccount (existence check), indexInboxNuri/@index (discovery), resolveInbox
Anchor, resolveWriteGraph, createEntityDoc, listMyEntityDocs. loadShim kept only
for genuine all-accounts needs (allAccounts, the listEntityDocs fan-out fallback).

The 90s ensureAccount/loadShim hang is gone. 93 tests pass; tsc rc=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 11:30:50 +02:00
Sylvain Duchesne b1d06b68b9 fix(client): per-entity write round-trip + dedicated inbox anchor
Real-broker validation of the per-document model surfaced round-trip breaks the
fake-ng unit tests missed:
- The inbox anchor was the shim graph itself, making loadShim ~60s; give the
  inbox its own document so anchor resolution is fast and isolated.
- store-registry adjustments so per-entity documents created via the SDK are
  indexed and readable back through the scope fan-out.
docs/simulation.md updated. 89 tests pass; tsc rc=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 17:26:31 +02:00
Sylvain Duchesne e0d88b5076 feat(client): SDK-shaped scope resolvers (resolveScopeGraph/resolveInboxAnchor)
Expose a clean scope-based surface so consumers work by scope (public/protected/
private) and never see a physical store id — the library resolves placement and
performs the shared-wallet simulation internally. RegistrySession gains optional
protected/public store ids, supplied at the single injection point
(configureStoreRegistry). Zero domain knowledge. docs/simulation.md updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:41:34 +02:00
Sylvain Duchesne d804a436d7 feat(client): inbox mechanism, write-guard, SPARQL injection hardening
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>
2026-07-03 15:51:00 +02:00
Sylvain Duchesne 654cb90d99 feat(client): generic shared-wallet shim surface (docs/storeRegistry/isolation/accounts)
Port the shared-wallet shim mechanics from the Festipod app into the
generic @ng-eventually/client library — zero app-specific knowledge, the
consumer injects the domain (entity->scope mapping, connections, storage).

New namespaces exposed from src/index.ts:
- docs      docCreate/sparqlUpdate/sparqlQuery via the REAL injected `ng`
            (getConfig().ng), never the public makeNg proxy — the JS-over-
            iframe double proxy breaks doc_create postMessage marshaling
            (DataCloneError). Validated hard constraint.
- storeRegistry  generic (account,scope)->NURI resolver, createEntityDoc/
            listEntityDocs + per-scope index, sharedWalletShim in the
            private_store, cache. Consumer wiring injected via
            configureStoreRegistry({ getSession, normalizeUser }).
- isolation  pure applyIsolation (public=all / protected=owner+connections
            / private=owner); accessors + connection graph injected.
- accounts  AccountStore (localStorage-backed faux login, storage injected)
            + normalizeUsername. React wrapper intentionally NOT ported.

polyfill.ts gains configureStoreRegistry/getStoreRegistryDeps + resetConfig.
36/36 bun test, tsc --noEmit rc=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 10:12:55 +02:00