diff --git a/apps/editor/package.json b/apps/editor/package.json
index 0fe777e6f..e9edd1bc9 100644
--- a/apps/editor/package.json
+++ b/apps/editor/package.json
@@ -48,7 +48,7 @@
"@mdbase-dev/connect": "workspace:*",
"@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.145-e6e66bcd172f.tgz",
"@mdbase-dev/i18n": "workspace:*",
- "@mdbase-dev/sdk": "file:vendor/sdk-19197d2c-production-beta173.tgz",
+ "@mdbase-dev/sdk": "file:vendor/sdk-e8ffb806-production-beta188.tgz",
"@mdbase-dev/ui": "0.1.0-beta.167",
"@mdbase/connect-ui": "workspace:*",
"@phosphor-icons/react": "^2.1.10",
diff --git a/apps/editor/src/SdkEditorRoot.private.test.tsx b/apps/editor/src/SdkEditorRoot.private.test.tsx
index 9e53330cc..d6c113284 100644
--- a/apps/editor/src/SdkEditorRoot.private.test.tsx
+++ b/apps/editor/src/SdkEditorRoot.private.test.tsx
@@ -1,6 +1,6 @@
import { act, fireEvent, render, screen, waitFor, within } from "./test/locale";
import { StrictMode, useEffect } from "react";
-import { connect, type MdbaseClient } from "@mdbase-dev/sdk";
+import type { MdbaseClient } from "@mdbase-dev/sdk";
import { describeError } from "@mdbase-dev/sdk/describe";
import { COLLECTION_UNAVAILABLE_REASONS, mergeCollectionEntries, type CollectionAvailability, type MdbasePrivateCollection } from "@mdbase-dev/sdk/app-grant";
import { MemoryReplica } from "@mdbase-dev/sdk/testing";
@@ -30,7 +30,7 @@ function fakePrivate(entries: MdbasePrivateCollection[]) {
return available;
}),
open: vi.fn(async (_collection: string, _options?: { signal?: AbortSignal }) => {
- const client = await connect({ connector: replica.connector(), app: { name: "editor-private-test", version: "test" }, reconnect: false });
+ const client = await replica.client();
clients.push(client);
return client;
}),
diff --git a/apps/editor/src/SdkEditorRoot.test.tsx b/apps/editor/src/SdkEditorRoot.test.tsx
index 7ac699e63..48e210f0d 100644
--- a/apps/editor/src/SdkEditorRoot.test.tsx
+++ b/apps/editor/src/SdkEditorRoot.test.tsx
@@ -91,7 +91,7 @@ describe("SdkEditorRoot behind the shared collection gate", () => {
it("a refused registration logs only the developer detail", async () => {
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
const h = await testApp({ phase: { problem: "application_registration_refused" } });
- h.controls.publishSnapshot({ ...h.application.getSnapshot(),
+ h.controls.publishSnapshot({ ...h.mdbase.getSnapshot(),
problemDetail: { name: "MdbaseError", code: "forbidden", registration: { code: "invalid_application_manifest", paths: ["/homepage"] } } });
render();
await waitFor(() => expect(warn).toHaveBeenCalledWith(expect.stringContaining("invalid manifest: /homepage")));
diff --git a/apps/editor/src/SdkEditorRoot.tsx b/apps/editor/src/SdkEditorRoot.tsx
index 583e7093f..d006e4272 100644
--- a/apps/editor/src/SdkEditorRoot.tsx
+++ b/apps/editor/src/SdkEditorRoot.tsx
@@ -101,7 +101,7 @@ export function SdkEditorRoot({ sdk, renderNative, onFeedbackContext, privateCol
priv.scope && }>
- {(connection) => }
;
@@ -191,7 +191,6 @@ function usePrivateSection(sdk: Mdbase, privates: PrivateCollections | null) {
}
let live = true;
const life = new AbortController();
- const application = sdk.application;
const connection: EditorConnection = { id: client.collection, displayName: entry.displayName, generation: `private:${++privateGeneration}`,
records: client, files: client.files, isCurrent: () => live && current(signal) };
const requireCurrent = () => {
@@ -205,28 +204,28 @@ function usePrivateSection(sdk: Mdbase, privates: PrivateCollections | null) {
};
signal.addEventListener("abort", leave, { once: true });
const actions: EditorScopeActions = {
- chooseAnother: async () => { leave(); return application.getSnapshot(); },
+ chooseAnother: async () => { leave(); return sdk.getSnapshot(); },
reauthorize: async (options) => {
requireCurrent();
const requested = requestSignal(options?.signal); requested.throwIfAborted();
await privates.authorize(entry.collectionId, { ...options, signal: requested });
requested.throwIfAborted(); requireCurrent();
- leave(); return application.getSnapshot();
+ leave(); return sdk.getSnapshot();
},
select: async (collectionId, options) => {
requireCurrent();
const requested = AbortSignal.any([signal, ...(options?.signal ? [options.signal] : [])]);
requested.throwIfAborted(); leave();
- return application.select(collectionId, { ...options, signal: requested });
+ return sdk.select(collectionId, { ...options, signal: requested });
},
forget: async (collectionId, options) => {
requireCurrent();
const requested = AbortSignal.any([signal, ...(options?.signal ? [options.signal] : [])]);
requested.throwIfAborted();
- if (collectionId !== entry.collectionId) return application.forget(collectionId, { ...options, signal: requested });
+ if (collectionId !== entry.collectionId) return sdk.forget(collectionId, { ...options, signal: requested });
leave(); await privates.forget(collectionId);
requested.throwIfAborted();
- return application.getSnapshot();
+ return sdk.getSnapshot();
}
};
setScope({ connection, actions });
diff --git a/apps/editor/src/SdkEditorRoot.unsynced.test.tsx b/apps/editor/src/SdkEditorRoot.unsynced.test.tsx
index fb1e3b3e1..a65013a53 100644
--- a/apps/editor/src/SdkEditorRoot.unsynced.test.tsx
+++ b/apps/editor/src/SdkEditorRoot.unsynced.test.tsx
@@ -39,7 +39,7 @@ describe("work before connecting", () => {
const body = await screen.findByRole("textbox", { name: "Note body" });
expect(body).toHaveValue("First thoughts\n");
fireEvent.change(body, { target: { value: "First thoughts, revised\n" } });
- await waitFor(async () => expect((await h.unsynced.client.find({ path: "draft.md" }, { body: true }))?.body).toBe("# Draft\n\nFirst thoughts, revised\n"), { timeout: 10_000 });
+ await waitFor(async () => expect((await h.unsynced.client.find({ path: "draft.md" }, { include: { body: true } }))?.body).toBe("# Draft\n\nFirst thoughts, revised\n"), { timeout: 10_000 });
// The SDK marks the work once something is saved; the gate then shows one quiet line.
act(() => h.markWork());
diff --git a/apps/editor/src/native-file-preview-gateway.test.ts b/apps/editor/src/native-file-preview-gateway.test.ts
index 28e2ca73e..6059f420b 100644
--- a/apps/editor/src/native-file-preview-gateway.test.ts
+++ b/apps/editor/src/native-file-preview-gateway.test.ts
@@ -52,7 +52,7 @@ async function fixture(overrides: Partial = {}) {
const download = vi.spyOn(f.client.files, "download").mockResolvedValue(new Uint8Array([104, 105]));
const downloadStream = vi.spyOn(f.client.files, "downloadStream").mockReturnValue(stream);
const close = vi.spyOn(f.client, "close");
- const gateway = new NativeEditorGateway(f.application, f.connection);
+ const gateway = new NativeEditorGateway(f.mdbase, f.connection);
gateways.push(gateway);
const selected = (await gateway.listFiles())[0]!;
return { ...f, file, metadata, controller, stream, cancel, release, read, list, get, download, downloadStream, close, gateway, selected };
diff --git a/apps/editor/src/native-gateway.test.ts b/apps/editor/src/native-gateway.test.ts
index 8eb35c8f4..94888af54 100644
--- a/apps/editor/src/native-gateway.test.ts
+++ b/apps/editor/src/native-gateway.test.ts
@@ -18,7 +18,7 @@ afterEach(() => {
async function fixture(...args: Parameters) {
const f = await readyFixture(...args);
- const gateway = new NativeEditorGateway(f.application, f.connection, declaration);
+ const gateway = new NativeEditorGateway(f.mdbase, f.connection, declaration);
gateways.push(gateway);
return { ...f, gateway };
}
@@ -36,7 +36,7 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
it("can retire a borrowed scope before mounting without reading its closed hello", async () => {
const f = await readyFixture();
f.client.close();
- const gateway = new NativeEditorGateway(f.application, { ...f.connection, isCurrent: () => true }, declaration);
+ const gateway = new NativeEditorGateway(f.mdbase, { ...f.connection, isCurrent: () => true }, declaration);
gateways.push(gateway);
expect(gateway.sessionSnapshot()).toEqual({ status: "unselected", connections: [] });
gateway.dispose();
@@ -59,7 +59,7 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
const f = await fixture();
const before = f.gateway.sessionSnapshot();
expect(before.status === "ready" && before.connection.private).toBeUndefined();
- const snapshot = f.application.getSnapshot();
+ const snapshot = f.mdbase.getSnapshot();
f.controls.publishSnapshot({ ...snapshot, collections: snapshot.collections.map((entry) => ({ ...entry, kind: "private" as const })) });
const after = f.gateway.sessionSnapshot();
expect(after.status === "ready" && after.connection.private).toBe(true);
@@ -67,7 +67,7 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
it("a Viewer grant exposes no write operations or misleading access-update prompts", async () => {
const f = await fixture({ capabilities: ["collection.read"], role: "viewer" });
- const membership = f.application.getSnapshot();
+ const membership = f.mdbase.getSnapshot();
f.controls.publishSnapshot({ ...membership, collections: membership.collections.map((entry) => ({ ...entry, role: "viewer" })) });
const snapshot = f.gateway.sessionSnapshot();
const connection = snapshot.status === "ready" ? snapshot.connection : null;
@@ -82,7 +82,7 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
it("uses live SDK membership and refuses mutations before local capture", async () => {
const f = await fixture({}, seedNotes);
- const snapshot = f.application.getSnapshot();
+ const snapshot = f.mdbase.getSnapshot();
const setRole = (role: "viewer" | "editor") => f.controls.publishSnapshot({
...snapshot, collections: snapshot.collections.map((entry) => ({ ...entry, role }))
});
@@ -168,7 +168,7 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
const hello = vi.spyOn(f.client, "hello", "get").mockReturnValue({ ...original, version: { major: 1, minor: 3 },
grant: { role: "editor", capabilities: ["collection.read", "records.edit"] }
});
- const snapshot = f.application.getSnapshot();
+ const snapshot = f.mdbase.getSnapshot();
f.controls.publishSnapshot({ ...snapshot, collections: snapshot.collections.map((entry) => ({ ...entry, role: "viewer" })) });
expect(f.gateway.sessionSnapshot()).toMatchObject({ connection: { role: "viewer" } });
await expect(f.gateway.create({ path: "early-downgrade.md", title: "Refused", body: "viewer", properties: {} }))
@@ -202,18 +202,16 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
it("describes type schemas, versions and contract schemas from the native catalog", async () => {
const f = await fixture({}, seedNotes);
const describe = f.client.describe.bind(f.client);
- const map = (value: object): Map => new Map(Object.entries(value).map(([key, item]) =>
- [key, item && typeof item === "object" && !Array.isArray(item) ? map(item) : item]));
const schema = { type: "object", properties: { name: { type: "string" }, email: { type: "string" } } };
vi.spyOn(f.client, "describe").mockImplementation(async (options) => {
const base = await describe(options);
- if (!(typeof options === "object" && !(options instanceof AbortSignal) && options.definitions)) return base;
+ if (!options?.definitions) return base;
return { ...base,
types: [{ name: "person", path: "_types/person.md", version: 2, revision: `sha256:${"a".repeat(64)}`,
- implements: [{ contract: "mdbase.person", version: "2.0.0", fields: new Map([["name", "name"]]), digest: `sha256:${"b".repeat(64)}` }],
- definition: map({ kind: "mdbase.type", name: "person", version: 2, description: "People", extensions: { "x-app": { on: true } }, schema }), schema: map(schema) }],
+ implements: [{ contract: "mdbase.person", version: "2.0.0", fields: { name: "name" }, digest: `sha256:${"b".repeat(64)}` }],
+ definition: { kind: "mdbase.type", name: "person", version: 2, description: "People", extensions: { "x-app": { on: true } }, schema }, schema }],
contracts: [{ id: "mdbase.person", version: "2.0.0", path: "_contracts/person.md", digest: `sha256:${"c".repeat(64)}`, contractType: "record",
- implementedBy: ["person"], schemas: new Map([["record_schema", map(schema)]]) }] } as Awaited>;
+ implementedBy: ["person"], schemas: { record_schema: schema } }] } as Awaited>;
});
const description = await f.gateway.describe();
expect(description.typing).toBeUndefined();
@@ -227,8 +225,8 @@ describe("NativeEditorGateway over the public SDK (MemoryReplica, not LAB)", ()
const f = await fixture();
const original = f.client.call.bind(f.client);
const pages = [
- { version: 1, contractDigest: "sha256:test", page: { records: [{ id: "person-a", path: "people/a.md" }], cursor: "next-person-page", complete: true }, records: [{ id: "person-a", implementations: [{ typeName: "person", fields: new Map([["name", "Ada"]]), issues: [] }] }] },
- { version: 1, contractDigest: "sha256:test", page: { records: [{ id: "person-b", path: "people/b.md" }], complete: true }, records: [{ id: "person-b", implementations: [{ typeName: "person", fields: new Map([["name", "Bea"]]), issues: [] }] }] },
+ { version: 1, contractDigest: "sha256:test", page: { records: [{ id: "person-a", path: "people/a.md" }], cursor: "next-person-page", complete: true }, records: [{ id: "person-a", implementations: [{ typeName: "person", fields: { name: "Ada" }, issues: [] }] }] },
+ { version: 1, contractDigest: "sha256:test", page: { records: [{ id: "person-b", path: "people/b.md" }], complete: true }, records: [{ id: "person-b", implementations: [{ typeName: "person", fields: { name: "Bea" }, issues: [] }] }] },
];
const selections: Array