feat!: la famille ORM est publiee enveloppee, et le ng publie cesse de rendre des promesses

This commit is contained in:
Sylvain Duchesne
2026-09-21 12:38:17 +02:00
parent 2aac703784
commit d0ea043a5b
21 changed files with 1102 additions and 35 deletions
@@ -36,6 +36,9 @@ import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/boo
import { connectedUser } from "../src/emulated-verifier/connect";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { share } from "../src/surface/inbox";
import { insertObject } from "../src/surface/orm";
import type { InsertObjectLike } from "../src/model/types";
import type { BaseType, ShapeType } from "@ng-org/shex-orm";
import { post, postToDocument, read as readInbox } from "../src/surface/inbox";
import { readUnion } from "../src/surface/read-model";
import { sparqlUpdate } from "../src/surface/docs";
@@ -252,9 +255,20 @@ function makeFakeNg() {
return { doc_create, sparql_update, sparql_query, _quads: quads };
}
/** The SDK's `insertObject`, stood in for - and a record of whether it was reached. */
const ormInserts: unknown[][] = [];
const fakeInsertObject: InsertObjectLike = <T extends BaseType>(
shapeType: ShapeType<T>,
object: T,
): Promise<T> => {
ormInserts.push([shapeType, object]);
return Promise.resolve(object);
};
function inject() {
const ng = makeFakeNg();
configure({ ng: ng as any, useShape: (() => {}) as any });
ormInserts.length = 0;
configure({ ng: ng as any, useShape: (() => {}) as any, insertObject: fakeInsertObject });
configureStoreRegistry({ getSession: async () => SESSION, normalizeId: (id) => id.trim().toLowerCase() });
resetRegistryCache();
resetCaps();
@@ -785,3 +799,35 @@ test("a third party resolves another user's inbox, from the shim and not from a
const bobView = await userInbox("alice", "protected");
expect(bobView).toBe(aliceView);
});
// The same asymmetry on the ORM's write, and it is the one an application is most
// likely to meet: `insertObject` needs the document's `@graph`, which a recipient HAS -
// the reference travelled with the key. Holding the key is what lets Bob read; it is
// not what would let him write, and upstream it never was: `verify_permission` fires on
// the commit and asks membership of the repo, never how the reader came by the key.
//
// Bob OBTAINS the capability the only way an application can - Alice deposits it, he
// connects - so what he holds here is what he would really hold.
test("a cap received in an inbox does not let the ORM insert into that document", async () => {
inject();
const { protDoc } = await aliceSetsUpHerDocuments();
await userInbox("bob", "protected"); // Bob exists - `share` refuses a recipient nobody is
setCurrentUser("alice");
await share(protDoc, "bob");
setCurrentUser("bob");
await connectedUser();
expect(hasCap(protDoc)).toBe(true); // he reads it...
const shape = { shape: "urn:e2e:S", schema: {} } as ShapeType<BaseType>;
// ...and the insert is refused before it is attempted.
await expect(
insertObject(shape, { "@id": "", "@graph": protDoc } as BaseType),
).rejects.toThrow(/refused/i);
expect(ormInserts).toHaveLength(0);
// Alice, who owns it, writes through the same call.
setCurrentUser("alice");
await insertObject(shape, { "@id": "", "@graph": protDoc } as BaseType);
expect(ormInserts).toHaveLength(1);
});