diff --git a/package-lock.json b/package-lock.json index 8fbfb4b..1a9d4ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "name": "mdbase-journal", "dependencies": { "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.145-e6e66bcd172f.tgz", - "@mdbase-dev/sdk": "file:vendor/sdk-1907e67f-production-beta187.tgz", + "@mdbase-dev/sdk": "file:vendor/sdk-e8ffb806-production-beta188.tgz", "@mdbase-dev/ui": "file:vendor/ui-dev-fe158b59.tgz", "react": "19.2.8", "react-dom": "19.2.8" @@ -682,9 +682,9 @@ } }, "node_modules/@mdbase-dev/sdk": { - "version": "0.1.0-beta.187", - "resolved": "file:vendor/sdk-1907e67f-production-beta187.tgz", - "integrity": "sha512-bNsqlaeBKwA5ig3e4WZaCBRKmnlJs/b1FB11pogNtb7ur/bQf8txlcJWawKU2Fjto5/7+8MYz9bZFzM7JOfK/Q==", + "version": "0.1.0-beta.188", + "resolved": "file:vendor/sdk-e8ffb806-production-beta188.tgz", + "integrity": "sha512-EDaInyP8q2Lz0Akz8P+CNj5O6OvinMJWR1bE04fKtWRFDt9mVRBRBYRja5okOtb3gtnKYyMdQFGLlFln7F1RrA==", "bundleDependencies": [ "@mdbase-dev/connect-protocol" ], diff --git a/package.json b/package.json index bfef8e6..be04c24 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@mdbase-dev/connect-protocol": "file:vendor/mdbase-dev-connect-protocol-0.1.0-beta.145-e6e66bcd172f.tgz", - "@mdbase-dev/sdk": "file:vendor/sdk-1907e67f-production-beta187.tgz", + "@mdbase-dev/sdk": "file:vendor/sdk-e8ffb806-production-beta188.tgz", "@mdbase-dev/ui": "file:vendor/ui-dev-fe158b59.tgz", "react": "19.2.8", "react-dom": "19.2.8" diff --git a/src/connection-notice.ts b/src/connection-notice.ts index f1cdafa..5656471 100644 --- a/src/connection-notice.ts +++ b/src/connection-notice.ts @@ -12,13 +12,13 @@ export const connectionNoticeLabels: Record = { reconnecting: "Reconnecting", saved_on_device: "Saved on this device; waiting for sync", }; -type Observed = Partial>; +type Observed = Partial>; export function connectionNotice(snapshot: Observed | null | undefined, online = true): ConnectionNotice | null { if (!snapshot || (snapshot.phase && snapshot.phase.kind !== "ready")) return null; - if (snapshot.writes?.saveState === "saved_on_device") return "saved_on_device"; + if (snapshot.sync?.writes?.saveState === "saved_on_device") return "saved_on_device"; const health = snapshot.sync?.health ?? snapshot.transport?.kind ?? null; if (!online || health === "offline") return "offline"; if (health === "unreachable" || health === "server_busy" || health === "rate_limited" || - snapshot.sync?.kind === "offline" || (snapshot.sync?.live ?? snapshot.live) === false) return "reconnecting"; + snapshot.sync?.connection === "offline" || (snapshot.sync?.live ?? snapshot.live) === false) return "reconnecting"; return null; } diff --git a/src/journal-data.ts b/src/journal-data.ts index cddf1ce..43aabe6 100644 --- a/src/journal-data.ts +++ b/src/journal-data.ts @@ -1,17 +1,17 @@ -import {revisionOf, uuidv7, type MdbaseApplicationConnection, type RecordLease, type RecordSessionSnapshot, type wire, type FileRef} from "@mdbase-dev/sdk"; +import {revisionOf, uuidv7, type DescribeResult, type MdbaseApplicationConnection, type RecordLease, type RecordSessionSnapshot, type RecordView, type FileRef} from "@mdbase-dev/sdk"; import {JournalDrafts, type JournalDraft} from "./drafts.js"; export const JOURNAL_CONTRACT = Object.freeze({id: "journal.entry", version: "0.1.0", digest: "sha256:7e95bdf03621919fb0e722e5a29b48a68d7a4eaa3a94aea033d96ea2f7ec34cc"}); export interface JournalEntryType {readonly name: string; readonly createdField: string} export interface JournalEntry {readonly id: string; readonly created: string; readonly createdMs: number; readonly path: string} /** Only current canonical catalog bindings, never the starter type name. */ -export function journalEntryTypes(catalog: wire.DescribeResult): JournalEntryType[] { +export function journalEntryTypes(catalog: DescribeResult): JournalEntryType[] { const contract = catalog.contracts.filter(c => c.id === JOURNAL_CONTRACT.id && c.version === JOURNAL_CONTRACT.version && c.digest === JOURNAL_CONTRACT.digest); if (contract.length !== 1) throw new Error("This collection does not provide the required Journal contract."); const types: JournalEntryType[] = []; for (const type of catalog.types) { const implementations = type.implements.filter(i => i.contract === JOURNAL_CONTRACT.id && i.version === JOURNAL_CONTRACT.version); if (implementations.length > 1) throw new Error("This collection has an ambiguous Journal type binding."); - const field = implementations[0]?.fields.get("created"); + const field = implementations[0]?.fields["created"]; if (field === undefined) continue; // The native public create API accepts top-level fields. Do not implement a // second field-reference parser or silently write a dotted literal key. @@ -24,7 +24,7 @@ export function journalEntryTypes(catalog: wire.DescribeResult): JournalEntryTyp } /** A readable original record: confirmed, or kept on this device while the SDK * write ledger settles it. Never a protected or inconsistent readback. */ -function currentRecord(record: wire.RecordView, id: string): void { +function currentRecord(record: RecordView, id: string): void { if (record.id !== id || (record.state.state !== "confirmed" && record.state.state !== "pending") || record.state.hold !== undefined || (record.state.unresolved ?? 0) !== 0 || typeof record.body !== "string" || typeof record.document !== "string" || revisionOf(record.document) !== record.revision) { throw new Error("Keep this note on this device until its original record can be read safely."); @@ -60,18 +60,18 @@ export class JournalData { } async types(): Promise { this.ensure(); - const catalog = await this.connection.records.describe(this.caller.signal); + const catalog = await this.connection.records.describe({signal: this.caller.signal}); this.ensure(); return journalEntryTypes(catalog); } async entries(types: JournalEntryType[]): Promise { this.ensure(); const entries: JournalEntry[] = []; - for await (const page of this.connection.records.pages({types: types.map(type => type.name)}, {}, this.caller.signal)) { + for await (const page of this.connection.records.pages({types: types.map(type => type.name)}, {signal: this.caller.signal})) { this.ensure(); for (const record of page.records) { const type = types.find(type => record.types.includes(type.name)); - const created = type ? record.frontmatter.get(type.createdField) : null; + const created = type ? record.frontmatter[type.createdField] : null; if (typeof created !== "string" || !Number.isFinite(Date.parse(created))) continue; entries.push({id: record.id, created, createdMs: Date.parse(created), path: record.path}); } @@ -83,7 +83,7 @@ export class JournalData { this.ensure(); const local = this.drafts.list().find(draft => draft.targetCollectionId === this.connection.id && draft.recordId === entry.id); if (local) return local; - const record = await this.connection.records.get(entry.id, {body: true, document: true}, this.caller.signal); + const record = await this.connection.records.get(entry.id, {include: {body: true, document: true}, signal: this.caller.signal}); this.ensure(); currentRecord(record, entry.id); const draft = await this.drafts.mutate(() => this.drafts.put({...this.drafts.blank(this.connection.id, new Date(entry.createdMs)), id: entry.id, created: entry.created, body: record.body!, savedBody: record.body!, path: record.path, recordId: record.id})); @@ -123,19 +123,19 @@ export class JournalData { typeName: type.name, createdField: type.createdField, path, created: original.created, account: this.connection.scope.account, installation: this.connection.scope.installation}})); const attempt = draft.createAttempt!; - const result = await this.connection.records.save(this.connection.records.create({id: attempt.recordId, path: attempt.path, type: attempt.typeName, + const result = await this.connection.records.settle(this.connection.records.create({id: attempt.recordId, path: attempt.path, type: attempt.typeName, frontmatter: {[attempt.createdField]: attempt.created}, body: attempt.body}, {mutationId: attempt.mutationId, signal: this.caller.signal}), {signal: this.caller.signal}); this.ensure(draft); // A refusal is definite: nothing was created, so the next save starts a new intent. if (result.outcome === "rejected") { await this.drafts.mutate(() => this.drafts.patch(id, {createAttempt: null})); - throw new Error(result.problem?.message ?? "mdbase refused this note. Your text is still on this device."); + throw new Error(result.problem.message); } } // Confirmed or kept on this device: the SDK write ledger settles it. Read the // ORIGINAL record by its pinned ID; creation is never resubmitted. const attempt = draft.createAttempt!; - const record = await this.connection.records.get(attempt.recordId, {body: true, document: true, effective: true}, this.caller.signal) + const record = await this.connection.records.get(attempt.recordId, {include: {body: true, document: true, effective: true}, signal: this.caller.signal}) .catch(() => {throw new Error("Keep this page open while its original save is unresolved.");}); this.ensure(draft); currentRecord(record, attempt.recordId); if (record.body !== attempt.body) throw new Error("The original saved note has changed. Your local text has been preserved."); @@ -215,7 +215,7 @@ export class JournalData { const original = this.drafts.get(id); if (!original?.recordId) throw new Error("The original saved note is not available yet. Keep your text here."); this.ensure(original, true); - const record = await this.connection.records.get(original.recordId, {body: true, document: true}, this.caller.signal); + const record = await this.connection.records.get(original.recordId, {include: {body: true, document: true}, signal: this.caller.signal}); this.ensure(original, true); currentRecord(record, original.recordId); const owned = this.leases.get(id); // A released conflict can still be retained by this SDK client. Reopen it diff --git a/src/view.ts b/src/view.ts index a351d0c..3a664b6 100644 --- a/src/view.ts +++ b/src/view.ts @@ -135,7 +135,7 @@ export function mountJournal(document: Document, drafts: JournalDrafts, mdbase: if (!message && opening?.state === "unresolved") message = "The original save is unresolved. Your text stays on this device."; if (!message && record && ["conflict", "recovery", "deleted", "error"].includes(record.state)) message = "Your text is on this device. Resolve its original save before continuing."; if (!message && actionProblem) message = actionProblem.title; - if (!message && phase?.kind === "ready" && (phase.problem || state?.problem)) message = state?.sync?.text ?? phase.problem?.title ?? describeError(state)?.title; + if (!message && phase?.kind === "ready" && (phase.problem || state?.problem)) message = phase.problem?.title ?? describeError(state)?.title; // Never "Saving…" or "Saved" while offline, reconnecting or holding changes on this device. const notice = connectionNotice(state, document.defaultView?.navigator.onLine ?? true); if (!message && notice) message = connectionNoticeLabels[notice]; diff --git a/tests/autosave-ownership.test.ts b/tests/autosave-ownership.test.ts index 8f46388..e5cf890 100644 --- a/tests/autosave-ownership.test.ts +++ b/tests/autosave-ownership.test.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import {mock, test} from "node:test"; import {readFileSync} from "node:fs"; import {JSDOM} from "jsdom"; -import {connect, type Mdbase, type MdbaseClient} from "@mdbase-dev/sdk"; +import type {Mdbase, MdbaseClient} from "@mdbase-dev/sdk"; import {JournalDrafts, type DraftLock} from "../src/drafts.js"; import {mountJournal} from "../src/view.js"; import {JournalData} from "../src/journal-data.js"; @@ -14,7 +14,7 @@ async function until(check: () => boolean): Promise { } async function tabs(delayCatalog = false) { const f = await applicationFixture(); - const peer = await connect({connector: f.replica.connector(), app: {name: "peer-journal-tab", version: "test"}, reconnect: false}); + const peer = await f.replica.client(); const locks = memoryLocks(), storage = memoryStorage(); let tail = Promise.resolve(), pause: Promise | null = null; const lock: DraftLock = operation => {const barrier = pause, next = tail.then(async () => {if (barrier) await barrier; return operation();}); tail = next.then(() => {}, () => {}); return next;}; @@ -79,7 +79,7 @@ test("idle status sweep never autosaves a peer prefix; owner saves the complete try {assert.equal((await passive.session.flush()).ok, true);} finally {passive.release();} assert.equal((await lease.session.flush()).ok, true); await until(() => a.window.document.getElementById("status")!.textContent === "Saved"); - assert.equal((await f.records.get(recordId, {body: true})).body, full); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, full); assert.equal(a.page.value, full); assert.equal(a.drafts.get(id)?.body, full); assert.equal(a.window.document.getElementById("draft-conflict")!.hidden, true); assert.equal(b.view.currentDraft().id, blank); assert.equal(b.page.value, ""); @@ -106,12 +106,11 @@ test("closing the owner hands its latest unfinished draft to an idle tab", async assert.equal((await lease.session.flush()).ok, true); await until(() => b.drafts.get(id)?.savedBody === full); assert.equal(updateB.mock.callCount(), 1); - assert.equal((await f.records.get(recordId, {body: true})).body, full); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, full); assert.equal(b.drafts.get(id)?.body, full); assert.equal(b.drafts.get(id)?.savedBody, full); assert.equal(createB.mock.callCount(), 0, "takeover retains the original create identity"); assert.equal(b.page.value, "", "background recovery leaves the idle page alone"); - fresh = await connect({connector: f.replica.connector(), app: {name: "reloaded-journal-tab", version: "test"}, reconnect: false}); - mock.method(fresh, "describe", async () => catalog()); + fresh = await f.replica.client(); reloaded = await t.tab(t.application(fresh)); await until(() => reloaded!.window.document.getElementById("status")!.textContent === "Saved"); const resumed = await fresh.records.open(recordId, {autosave: {idleMs: 1200}}); @@ -162,7 +161,7 @@ test("the owner follows peer edits to its previous page without a second session a.storage(id, previous); await until(() => lease!.session.getSnapshot().body === full); assert.equal((await lease.session.flush()).ok, true); - assert.equal((await f.records.get(recordId, {body: true})).body, full); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, full); assert.equal(updateB.mock.callCount(), 0); assert.equal(a.page.value, ""); } finally {lease?.release(); follower?.close(); await t.close();} @@ -203,7 +202,7 @@ test("an independent editor still conflicts and local text is discarded only by a.window.document.getElementById("use-saved-version")!.click(); await until(() => a.page.value === "Independent edit"); assert.equal(a.drafts.get(id)?.body, "Independent edit"); - assert.equal((await f.records.get(recordId, {body: true})).body, "Independent edit"); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, "Independent edit"); } finally {active?.release(); editor?.release(); await t.close();} }); test("a delayed peer edit of a hidden page cannot borrow a later outside revision", async () => { @@ -228,7 +227,7 @@ test("a delayed peer edit of a hidden page cannot borrow a later outside revisio a.storage(id, previous); await until(() => a.drafts.get(id)?.reviewRequired === true); assert.equal((await active.session.flush()).ok, true); - assert.equal((await f.records.get(recordId, {body: true})).body, "Independent edit"); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, "Independent edit"); assert.equal(a.drafts.get(id)?.body, full); assert.equal(follower.page.value, full); } finally {active?.release(); editor?.release(); follower?.close(); await t.close();} @@ -250,7 +249,7 @@ test("queued input on the current page cannot skip the previous page's last peer t.pause(null); gate.resolve(); await until(() => active!.session.getSnapshot().body === full); assert.equal((await active.session.flush()).ok, true); - assert.equal((await f.records.get(recordId, {body: true})).body, full); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, full); } finally {t.pause(null); gate.resolve(); active?.release(); follower?.close(); await t.close();} }); for (const hideOwner of [true, false]) test(`a conflict is recoverable from another tab (owner ${hideOwner ? "hidden" : "displayed"})`, async () => { @@ -282,7 +281,7 @@ for (const hideOwner of [true, false]) test(`a conflict is recoverable from anot follower.write("Independent edit continued"); await until(() => resumed.session.getSnapshot().body === follower!.page.value); assert.equal((await resumed.session.flush()).ok, true); - assert.equal((await f.records.get(recordId, {body: true})).body, follower.page.value); + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, follower.page.value); assert.equal(a.drafts.get(id)?.reviewRequired, false); } finally {resumed.release();} } finally {active?.release(); editor?.release(); follower?.close(); await t.close();} diff --git a/tests/connection-notice.test.ts b/tests/connection-notice.test.ts index 29d2f47..0b43d15 100644 --- a/tests/connection-notice.test.ts +++ b/tests/connection-notice.test.ts @@ -2,12 +2,12 @@ import assert from "node:assert/strict"; import {test} from "node:test"; import {connectionNotice} from "../src/connection-notice.js"; const ready = {phase: {kind: "ready"}} as const; -const sync = (health: string | null, live: boolean | null = true) => ({kind: "synced", health, live}) as never; +const sync = (health: string | null, live: boolean | null = true) => ({connection: "online", health, live}) as never; void test("status never reads as saving or saved while offline, reconnecting or holding changes", () => { assert.equal(connectionNotice({...ready, sync: sync(null)} as never), null); assert.equal(connectionNotice({...ready, sync: sync(null)} as never, false), "offline"); assert.equal(connectionNotice({...ready, sync: sync("unreachable")} as never), "reconnecting"); assert.equal(connectionNotice({...ready, sync: sync(null, false)} as never), "reconnecting"); - assert.equal(connectionNotice({...ready, sync: sync("offline"), writes: {saveState: "saved_on_device"}} as never), "saved_on_device"); + assert.equal(connectionNotice({...ready, sync: {connection: "online", health: "offline", live: true, writes: {saveState: "saved_on_device"}}} as never), "saved_on_device"); assert.equal(connectionNotice({phase: {kind: "opening"}, live: false} as never), null); }); diff --git a/tests/draft-concurrency.test.ts b/tests/draft-concurrency.test.ts index 0c94dd3..7967683 100644 --- a/tests/draft-concurrency.test.ts +++ b/tests/draft-concurrency.test.ts @@ -1,10 +1,10 @@ import assert from "node:assert/strict"; -import {mock, test} from "node:test"; +import {test} from "node:test"; import {readFileSync} from "node:fs"; import {JSDOM} from "jsdom"; import {DraftConflictError, DraftPersistenceError, JournalDrafts, type DraftLock} from "../src/drafts.js"; import {mountJournal} from "../src/view.js"; -import {applicationFixture, catalog, deferred, memoryStorage} from "./support.js"; +import {applicationFixture, deferred, memoryStorage} from "./support.js"; const html = readFileSync(new URL("../public/index.html", import.meta.url), "utf8"); const tick = () => new Promise(resolve => setTimeout(resolve, 10)); function serialLock(): DraftLock { @@ -78,7 +78,6 @@ test("a single-tab save echo arriving ahead of queued input is not an outside ed let tail = Promise.resolve(), pause: Promise | null = null; const lock: DraftLock = operation => {const barrier = pause, next = tail.then(async () => {if (barrier) await barrier; return operation();}); tail = next.then(() => {}, () => {}); return next;}; const drafts = new JournalDrafts(memoryStorage(), undefined, lock); - const describe = mock.method(f.records, "describe", async () => catalog()); const view = await drafts.mutate(() => mountJournal(h.window.document, drafts, f.mdbase)); let lease: Awaited> | null = null; const until = async (ready: () => boolean) => {for (let i = 0; i < 100 && !ready(); i++) await new Promise(resolve => setTimeout(resolve, 5)); assert(ready());}; @@ -101,8 +100,8 @@ test("a single-tab save echo arriving ahead of queued input is not an outside ed await until(() => view.currentDraft().id === id); assert.equal(h.page.value, "Seed and my edit plus more"); assert.equal(h.window.document.getElementById("draft-conflict")!.hidden, true); - assert.equal((await f.records.get(recordId, {body: true})).body, h.page.value); - } finally {pause = null; gate.resolve(); await tail; lease?.release(); view.dispose(); describe.mock.restore(); h.window.close(); f.close();} + assert.equal((await f.records.get(recordId, {include: {body: true}})).body, h.page.value); + } finally {pause = null; gate.resolve(); await tail; lease?.release(); view.dispose(); h.window.close(); f.close();} }); test("rapid queued input retains every character without a false self-conflict", async () => { const drafts = new JournalDrafts(memoryStorage(), undefined, serialLock()), h = host(); diff --git a/tests/journal-data.test.ts b/tests/journal-data.test.ts index cbb3743..2b9ba8d 100644 --- a/tests/journal-data.test.ts +++ b/tests/journal-data.test.ts @@ -26,11 +26,11 @@ test("real SDK create pins original IDs, then edits through the same record leas const saved = drafts.get(draft.id)!; assert.equal(saved.recordId, saved.createAttempt?.recordId); assert.equal(saved.savedBody, "First page"); assert.equal(saved.createAttempt?.path, "journal/entry.md"); - const record = await h.records.get(saved.recordId!, {body: true}); - assert.equal(record.frontmatter.get("written"), draft.created); assert.equal(record.body, "First page"); + const record = await h.records.get(saved.recordId!, {include: {body: true}}); + assert.equal(record.frontmatter["written"], draft.created); assert.equal(record.body, "First page"); assert.equal(await data.open(draft.id, journalEntryTypes(catalog())[0]!), lease); data.setBody(draft.id, "Revised page"); await data.flush(draft.id); - assert.equal((await h.records.get(saved.recordId!, {body: true})).body, "Revised page"); + assert.equal((await h.records.get(saved.recordId!, {include: {body: true}})).body, "Revised page"); assert.equal(drafts.get(draft.id)?.savedBody, "Revised page"); } finally {data.release(); assert.equal(h.records.link, "open"); h.close();} }); @@ -87,7 +87,7 @@ test("quota failure inside a real session notification is contained and blocks f assert.match(data.problem(draft.id)!, /original save outcome/); assert.equal(drafts.get(draft.id)?.savedBody, "Original"); assert.equal(drafts.get(draft.id)?.body, "Revised"); await assert.rejects(data.flush(draft.id), /original save outcome/); - assert.equal((await h.records.get(drafts.get(draft.id)!.recordId!, {body: true})).body, "Revised"); + assert.equal((await h.records.get(drafts.get(draft.id)!.recordId!, {include: {body: true}})).body, "Revised"); } finally {data.release(); h.close();} }); test("protected original readback refuses Saved and recovery retains the original mutation and path", async () => { @@ -136,7 +136,7 @@ test("stale displayed native text cannot use a fresh SDK revision, including aft const reloaded = new JournalData(h.connection, drafts); try {await assert.rejects(reloaded.open(draft.id), /changed elsewhere/);} finally {reloaded.release();} assert.equal(update.mock.callCount(), 0); - assert.equal((await h.records.get(drafts.get(draft.id)!.recordId!, {body: true})).body, "Seed\nOther tab"); + assert.equal((await h.records.get(drafts.get(draft.id)!.recordId!, {include: {body: true}})).body, "Seed\nOther tab"); const selected = await data.useSavedVersion(draft.id); assert.equal(selected.body, "Seed\nOther tab"); assert.equal(selected.reviewRequired, false); assert.equal(update.mock.callCount(), 0, "using the saved version is read-only"); @@ -180,14 +180,14 @@ test("an own save witness cannot excuse a later same-body outside metadata edit" const lease = await data.open(draft.id, journalEntryTypes(catalog())[0]!); await data.setBody(draft.id, "My edit"); const shown = lease.session.getSnapshot(); await data.flush(draft.id); const own = lease.session.getSnapshot(); - const write = await h.records.save(h.records.update(own.record, {patch: {outside: "Other editor"}})); + const write = await h.records.settle(h.records.update(own.record, {patch: {outside: "Other editor"}})); assert.equal(write.outcome, "confirmed"); for (let i = 0; i < 100 && lease.session.getSnapshot().record.revision === own.record.revision; i++) await new Promise(resolve => setTimeout(resolve, 5)); assert.notEqual(lease.session.getSnapshot().record.revision, own.record.revision); assert.equal(lease.session.getSnapshot().body, shown.body); await assert.rejects(data.setBody(draft.id, "My edit with stale typing", {body: shown.body, revision: shown.record.revision}), /changed elsewhere/); assert.equal(drafts.get(draft.id)?.reviewRequired, true); - assert.equal((await h.records.get(own.record.id, {body: true})).body, "My edit"); + assert.equal((await h.records.get(own.record.id, {include: {body: true}})).body, "My edit"); } finally {data.release(); h.close();} }); test("files use the same public FilesApi and real SDK protocol", async () => { diff --git a/tests/navigation.test.ts b/tests/navigation.test.ts index f7f37e6..de11f48 100644 --- a/tests/navigation.test.ts +++ b/tests/navigation.test.ts @@ -75,8 +75,7 @@ test("day arrows load saved pages using their original record identity without a const h = await applicationFixture(), drafts = new JournalDrafts(memoryStorage()); const previous = new Date(); previous.setDate(previous.getDate() - 1); previous.setHours(12, 0, 0, 0); const id = "018e0000-0000-7000-8000-000000000001"; - await h.records.save(h.records.create({id, type: "collection-owned-entry", path: "journal/yesterday.md", frontmatter: {created: previous.toISOString()}, body: "Earlier saved page"})); - const describe = mock.method(h.records, "describe", async () => catalog()); + await h.records.settle(h.records.create({id, type: "collection-owned-entry", path: "journal/yesterday.md", frontmatter: {created: previous.toISOString()}, body: "Earlier saved page"})); const window = new JSDOM(html, {url: "https://journal.fixture/"}).window; const view = mountJournal(window.document, drafts, h.mdbase), create = mock.method(h.records, "create"); try { @@ -85,13 +84,12 @@ test("day arrows load saved pages using their original record identity without a assert.equal(view.currentDraft().recordId, id); assert.equal(view.currentDraft().body, "Earlier saved page"); assert.equal(view.currentDraft().createAttempt, null); assert.equal(create.mock.callCount(), 0); assert.equal((window.document.getElementById("entries") as HTMLDialogElement).open, false); - } finally {create.mock.restore(); describe.mock.restore(); view.dispose(); window.close(); h.close();} + } finally {create.mock.restore(); view.dispose(); window.close(); h.close();} }); test("a late day lookup cannot replace a new page made while it was loading", async () => { const h = await applicationFixture(), window = new JSDOM(html, {url: "https://journal.fixture/"}).window; const drafts = new JournalDrafts(memoryStorage()), job = deferred(); - const describe = mock.method(h.records, "describe", async () => catalog()); const lookup = mock.method(JournalData.prototype, "entries", () => job.promise); const view = mountJournal(window.document, drafts, h.mdbase); try { @@ -106,12 +104,11 @@ test("a late day lookup cannot replace a new page made while it was loading", as assert.equal(view.currentDraft().id, replacement); assert.equal(page.value, ""); assert.equal(drafts.get(original)?.body, "Keep original text during lookup"); assert.equal((window.document.getElementById("previous-day") as HTMLButtonElement).disabled, false); - } finally {job.resolve([]); lookup.mock.restore(); describe.mock.restore(); view.dispose(); window.close(); h.close();} + } finally {job.resolve([]); lookup.mock.restore(); view.dispose(); window.close(); h.close();} }); test("a refused day lookup keeps the original page and re-enables navigation", async () => { const h = await applicationFixture(), window = new JSDOM(html, {url: "https://journal.fixture/"}).window; - const describe = mock.method(h.records, "describe", async () => catalog()); const lookup = mock.method(JournalData.prototype, "entries", async () => {throw new Error("offline");}); const view = mountJournal(window.document, new JournalDrafts(memoryStorage()), h.mdbase); try { @@ -122,12 +119,12 @@ test("a refused day lookup keeps the original page and re-enables navigation", a assert.match(window.document.getElementById("status")!.textContent!, /current page is unchanged/); assert.equal((window.document.getElementById("entries") as HTMLDialogElement).open, false); assert.equal((window.document.getElementById("next-day") as HTMLButtonElement).disabled, false); - } finally {lookup.mock.restore(); describe.mock.restore(); view.dispose(); window.close(); h.close();} + } finally {lookup.mock.restore(); view.dispose(); window.close(); h.close();} }); test("browsing before the catalog is ready loads saved entries as soon as it arrives", async () => { const h = await applicationFixture(), job = deferred>(); - await h.records.save(h.records.create({type: "collection-owned-entry", path: "journal/earlier.md", frontmatter: {created: "2025-01-02T09:00:00+00:00"}, body: "Earlier saved entry"})); + await h.records.settle(h.records.create({type: "collection-owned-entry", path: "journal/earlier.md", frontmatter: {created: "2025-01-02T09:00:00+00:00"}, body: "Earlier saved entry"})); const describe = mock.method(h.records, "describe", () => job.promise); const window = new JSDOM(html, {url: "https://journal.fixture/"}).window, document = window.document; const dialog = document.querySelector("#entries")!; @@ -147,7 +144,7 @@ test("saved entries use the bound type and original record, without another crea const h = await applicationFixture(), drafts = new JournalDrafts(memoryStorage()), data = new JournalData(h.connection, drafts); const type = journalEntryTypes(catalog("written", "my-entry"))[0]!; try { - const saved = await h.records.save(h.records.create({type: type.name, path: "journal/old.md", frontmatter: {written: "2025-01-02T09:00:00+00:00"}, body: "Earlier saved entry"})); + const saved = await h.records.settle(h.records.create({type: type.name, path: "journal/old.md", frontmatter: {written: "2025-01-02T09:00:00+00:00"}, body: "Earlier saved entry"})); assert.equal(saved.outcome, "confirmed"); const entries = await data.entries([type]); assert.equal(entries.length, 1); const spy = mock.method(h.records, "create"); @@ -157,7 +154,7 @@ test("saved entries use the bound type and original record, without another crea assert.equal(draft.body, "Earlier saved entry"); await data.open(draft.id); data.setBody(draft.id, "Earlier saved entry edited"); await data.flush(draft.id); - assert.equal((await h.records.get(draft.recordId!, {body: true})).body, "Earlier saved entry edited"); + assert.equal((await h.records.get(draft.recordId!, {include: {body: true}})).body, "Earlier saved entry edited"); assert.equal(spy.mock.callCount(), 0); assert.equal((await data.resume(entries[0]!)).id, draft.id); } finally {spy.mock.restore();} @@ -169,7 +166,7 @@ test("existing entries save without choosing a type in a collection with several const describe = mock.method(f.records, "describe", async () => ({...first, types: [...first.types, ...other.types], contracts: [{...first.contracts[0]!, implementedBy: [first.types[0]!.name, other.types[0]!.name]}]})); const date = new Date(); date.setDate(date.getDate() - 1); - const created = await f.records.save(f.records.create({type: first.types[0]!.name, path: "journal/existing.md", frontmatter: {created: date.toISOString()}, body: "Existing page"})); + const created = await f.records.settle(f.records.create({type: first.types[0]!.name, path: "journal/existing.md", frontmatter: {created: date.toISOString()}, body: "Existing page"})); assert.equal(created.outcome, "confirmed"); const window = new JSDOM(html, {url: "https://journal.fixture/"}).window; const {memoryLocks} = await import("./support.js"); @@ -189,6 +186,6 @@ test("existing entries save without choosing a type in a collection with several assert.equal((await lease.session.flush()).ok, true); await until(() => window.document.getElementById("status")!.textContent === "Saved"); assert.equal(create.mock.callCount(), 0); - assert.equal((await f.records.get(view.currentDraft().recordId!, {body: true})).body, page.value); + assert.equal((await f.records.get(view.currentDraft().recordId!, {include: {body: true}})).body, page.value); } finally {lease?.release(); create.mock.restore(); view.dispose(); describe.mock.restore(); window.close(); await f.fixture.close();} }); diff --git a/tests/support.ts b/tests/support.ts index 50f73ac..8112f92 100644 --- a/tests/support.ts +++ b/tests/support.ts @@ -1,5 +1,5 @@ -import type {MdbaseApplicationConnection, MdbaseApplicationSnapshot, wire} from "@mdbase-dev/sdk"; -import {createTestMdbase} from "@mdbase-dev/sdk/testing"; +import type {DescribeResult, MdbaseApplicationConnection} from "@mdbase-dev/sdk"; +import {createTestMdbase, type ApplicationFixtureSnapshot} from "@mdbase-dev/sdk/testing"; import {shareTestDocument} from "@mdbase-dev/ui/collections/testing"; import {JSDOM} from "jsdom"; import {JOURNAL_CONTRACT} from "../src/journal-data.js"; @@ -13,30 +13,31 @@ export function memoryStorage(): Storage { } /** Controlled application/canonical-catalog metadata, NOT native runtime or * registration evidence. Record/file calls use the actual SDK protocol client. */ -export function snapshot(patch: Partial = {}): MdbaseApplicationSnapshot { +export function snapshot(patch: Partial = {}): ApplicationFixtureSnapshot { const facet = {checking: false, assessment: {status: "ready" as const, problem: null}, problem: null}; const id = patch.selectedCollectionId ?? null, collection = patch.collections?.find(item => item.collectionId === id) ?? null; const usable = patch.status === "ready" || patch.status === "setup_required" ? collection : null; // Explicit controlled fixture selection, not an app URL/approval engine. - const selection: MdbaseApplicationSnapshot["selection"] = {state: usable ? "usable" : id ? "pending" : "none", problem: null, usable, + const selection: ApplicationFixtureSnapshot["selection"] = {state: usable ? "usable" : id ? "pending" : "none", problem: null, usable, requested: {collectionId: id, source: id ? "user" : "none", displayName: collection?.displayName ?? null, approved: collection !== null}}; return {status: "signed_out", signIn: null, collections: [], selectedCollectionId: null, timing: null, problem: null, selection, readiness: {...facet, facets: {capabilities: facet, contracts: facet, configuration: facet, files: facet, people: facet, notifications: facet}}, ...patch}; } -export function catalog(field = "created", name = "collection-owned-entry"): wire.DescribeResult { - return {specVersion: "test", settings: new Map(), inclusion: {include: []}, issues: [], +export function catalog(field = "created", name = "collection-owned-entry"): DescribeResult { + return {specVersion: "test", settings: {}, inclusion: {include: []}, issues: [], contracts: [{...JOURNAL_CONTRACT, path: "_contracts/journal.entry.md", contractType: "record", implementedBy: [name]}], - types: [{name, path: "_types/custom.md", implements: [{contract: JOURNAL_CONTRACT.id, version: JOURNAL_CONTRACT.version, fields: new Map([["created", field]])}]}]}; + types: [{name, path: "_types/custom.md", implements: [{contract: JOURNAL_CONTRACT.id, version: JOURNAL_CONTRACT.version, fields: {created: field}}]}]}; } /** The SDK's test front door over one collection. `confirmed: false` keeps * writes pending (the log is unreachable) until `replica.confirmAll()`. */ export async function applicationFixture({confirmed = true} = {}) { - const mdbase = await createTestMdbase({collections: [{name: "Writing", role: "owner"}]}); + const mdbase = await createTestMdbase({collections: [{name: "Writing", role: "owner", contracts: [{id: JOURNAL_CONTRACT.id, version: JOURNAL_CONTRACT.version, + type: "collection-owned-entry", fields: {created: "created"}, contractDigest: JOURNAL_CONTRACT.digest}]}]}); const {fixture} = mdbase, replica = fixture.replica(), records = fixture.client(); if (!confirmed) replica.setOnline(false); - const ready = fixture.application.getSnapshot() as MdbaseApplicationSnapshot; + const ready = fixture.mdbase.getSnapshot(); let active = true; - const connection: MdbaseApplicationConnection = {...fixture.entry.connection(records.collection)!, isCurrent: () => active}; + const connection: MdbaseApplicationConnection = {...fixture.mdbase.connection(records.collection)!, isCurrent: () => active}; return {replica, records, connection, fixture, mdbase, ready, retire: () => {active = false;}, close: () => {void fixture.close();}}; } export function deferred() { diff --git a/tests/text-loss.test.ts b/tests/text-loss.test.ts index 78a0728..a6d583d 100644 --- a/tests/text-loss.test.ts +++ b/tests/text-loss.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import {mock, test} from "node:test"; +import {test} from "node:test"; import {readFileSync} from "node:fs"; import {JSDOM} from "jsdom"; import {JournalDrafts, type DraftLock} from "../src/drafts.js"; @@ -29,7 +29,7 @@ for (const offline of [false, true]) test(`a delayed storage echo cannot replace f.replica.setOnline(true); f.replica.confirmAll(); } await data.flush(draft.id); - assert.equal((await f.records.get(drafts.get(draft.id)!.recordId!, {body: true})).body, "Seed\n[test] has all its text"); + assert.equal((await f.records.get(drafts.get(draft.id)!.recordId!, {include: {body: true}})).body, "Seed\n[test] has all its text"); } finally {data.release(); f.close();} }); const html = readFileSync(new URL("../public/index.html", import.meta.url), "utf8"); @@ -44,7 +44,6 @@ async function until(check: () => boolean): Promise { test("fast offline input through the origin lock keeps its complete text after reconnect", {timeout: 20_000}, async () => { const f = await applicationFixture(), window = new JSDOM(html, {url: "https://journal.fixture/", runScripts: "outside-only"}).window; const drafts = new JournalDrafts(memoryStorage(), undefined, serialLock()); - const describe = mock.method(f.records, "describe", async () => catalog()); const view = await drafts.mutate(() => mountJournal(window.document, drafts, f.mdbase)); const page = window.document.querySelector("#page")!; const status = window.document.getElementById("status")!; @@ -62,6 +61,6 @@ test("fast offline input through the origin lock keeps its complete text after r assert.equal(window.document.getElementById("draft-conflict")!.hidden, true); f.replica.setOnline(true); f.replica.confirmAll(); await until(() => status.textContent === "Saved"); - assert.equal((await f.records.get(drafts.get(view.currentDraft().id)!.recordId!, {body: true})).body, expected); - } finally {view.dispose(); describe.mock.restore(); window.close(); f.close();} + assert.equal((await f.records.get(drafts.get(view.currentDraft().id)!.recordId!, {include: {body: true}})).body, expected); + } finally {view.dispose(); window.close(); f.close();} }); diff --git a/tests/view-audits.test.ts b/tests/view-audits.test.ts index 634ac80..3b55b6c 100644 --- a/tests/view-audits.test.ts +++ b/tests/view-audits.test.ts @@ -36,14 +36,14 @@ test("legacy text is accessible and editable locally once, with no original stor test("same-scope readiness notifications cannot clear restored writing pinned to another collection", async () => { const f = await applicationFixture(), h = dom(), storage = memoryStorage(); storage.setItem("journal.note.earlier", JSON.stringify({body: "Original earlier text", targetCollectionId: "another-original-collection", path: "original.md"})); - const describe = mock.method(f.records, "describe", async () => catalog()), create = mock.method(f.records, "create"); + const create = mock.method(f.records, "create"); const view = mountJournal(h.window.document, new JournalDrafts(storage), f.mdbase); h.window.document.querySelector("#pending-details button")!.click(); const id = view.currentDraft().id; f.fixture.controls.publishSnapshot({...f.ready, readiness: {...f.ready.readiness, checking: true}}); assert.equal(view.currentDraft().id, id); assert.equal(h.page.value, "Original earlier text"); assert.equal(view.currentDraft().targetCollectionId, "another-original-collection"); assert.equal(create.mock.callCount(), 0); - view.dispose(); describe.mock.restore(); create.mock.restore(); h.window.close(); f.close(); + view.dispose(); create.mock.restore(); h.window.close(); f.close(); }); test("entry type selection is restored only from the current canonical catalog and survives reload", async () => { const f = await applicationFixture(), h = dom(), storage = memoryStorage(), drafts = new JournalDrafts(storage); @@ -69,7 +69,7 @@ test("a removed saved type remains unselected rather than inventing a mapping", }); test("an older uncertain entry does not replace the new page's truthful Saved state", {timeout: 10_000}, async () => { const f = await applicationFixture(), h = dom(), drafts = new JournalDrafts(memoryStorage()); - const describe = mock.method(f.records, "describe", async () => catalog()), original = f.records.create.bind(f.records); + const original = f.records.create.bind(f.records); const create = mock.method(f.records, "create", async (...args: Parameters) => {if (args[0].body === "First") throw new Error("unknown original outcome"); return original(...args);}); const saved = deferred(), status = h.window.document.getElementById("status")!; const observer = new h.window.MutationObserver(() => {if (status.textContent === "Saved") saved.resolve();}); @@ -83,5 +83,5 @@ test("an older uncertain entry does not replace the new page's truthful Saved st assert.equal(h.window.document.getElementById("status")!.textContent, "Saved"); assert.match(h.window.document.getElementById("pending-details")!.textContent!, /Original save unresolved/); assert.equal(create.mock.callCount(), 2); assert.notEqual(first.path, view.currentDraft().path); - } finally {observer.disconnect(); view.dispose(); describe.mock.restore(); create.mock.restore(); h.window.close(); f.close();} + } finally {observer.disconnect(); view.dispose(); create.mock.restore(); h.window.close(); f.close();} }); diff --git a/tests/view-errors.test.ts b/tests/view-errors.test.ts index e4a0b2e..64cf824 100644 --- a/tests/view-errors.test.ts +++ b/tests/view-errors.test.ts @@ -2,7 +2,7 @@ import assert from "node:assert/strict"; import {readFileSync} from "node:fs"; import {mock, test} from "node:test"; import {JSDOM} from "jsdom"; -import {describeError, summarizeStatus} from "@mdbase-dev/sdk"; +import {describeError} from "@mdbase-dev/sdk"; import {JournalDrafts} from "../src/drafts.js"; import {journalConnection} from "../src/application.js"; import {mountJournal} from "../src/view.js"; @@ -59,7 +59,7 @@ for (const problem of ["collection_migrating", "update_required", "account_suspe f.fixture.controls.publishSnapshot({...f.ready, problem}); assert.equal(journalConnection(f.mdbase), originalLease, "server refusal preserves the exact original public lease"); const status = window.document.getElementById("status")!.textContent!; - assert.equal(status, summarizeStatus(f.records.status, problem).text); assert(!status.includes(problem)); + assert.equal(status, describeError(problem)!.title); assert(!status.includes(problem)); const page = window.document.getElementById("page") as HTMLTextAreaElement; page.value = "uipolish: retained during upgrade"; page.dispatchEvent(new window.Event("input")); const original = view.currentDraft(); view.newPage(); view.undoNewPage(); diff --git a/tests/view-retry.test.ts b/tests/view-retry.test.ts index 43e64f2..4613768 100644 --- a/tests/view-retry.test.ts +++ b/tests/view-retry.test.ts @@ -4,7 +4,7 @@ import {readFileSync} from "node:fs"; import {JSDOM} from "jsdom"; import {JournalDrafts} from "../src/drafts.js"; import {mountJournal} from "../src/view.js"; -import {applicationFixture, catalog, memoryStorage} from "./support.js"; +import {applicationFixture, memoryStorage} from "./support.js"; const html = readFileSync(new URL("../public/index.html", import.meta.url), "utf8"); async function until(check: () => boolean): Promise { for (let i = 0; i < 400; i++) {if (check()) return; await new Promise(resolve => setTimeout(resolve, 10));} @@ -17,7 +17,6 @@ function dom() { } void test("Retry appears only for an unknown save outcome and settles the original write", {timeout: 15_000}, async () => { const f = await applicationFixture(), h = dom(), drafts = new JournalDrafts(memoryStorage()); - const describe = mock.method(f.records, "describe", async () => catalog()); const original = f.records.awaitReceipt.bind(f.records), update = mock.method(f.records, "update"); const view = mountJournal(h.window.document, drafts, f.mdbase); const status = h.window.document.getElementById("status")!, retry = h.window.document.getElementById("retry-button") as HTMLButtonElement; @@ -31,13 +30,12 @@ void test("Retry appears only for an unknown save outcome and settles the origin assert.equal(retry.disabled, true); await until(() => status.textContent === "Saved" && retry.hidden); assert.equal(update.mock.callCount(), 1, "Retry settles the original write without resubmitting it"); - assert.equal((await f.records.get(drafts.get(view.currentDraft().id)!.recordId!, {body: true})).body, "First, revised"); + assert.equal((await f.records.get(drafts.get(view.currentDraft().id)!.recordId!, {include: {body: true}})).body, "First, revised"); receipt.mock.restore(); - } finally {view.dispose(); describe.mock.restore(); update.mock.restore(); h.window.close(); f.close();} + } finally {view.dispose(); update.mock.restore(); h.window.close(); f.close();} }); void test("a write kept on this device while offline does not offer Retry", {timeout: 15_000}, async () => { const f = await applicationFixture({confirmed: false}), h = dom(), drafts = new JournalDrafts(memoryStorage()); - const describe = mock.method(f.records, "describe", async () => catalog()); const view = mountJournal(h.window.document, drafts, f.mdbase); const status = h.window.document.getElementById("status")!, retry = h.window.document.getElementById("retry-button") as HTMLButtonElement; try { @@ -48,5 +46,5 @@ void test("a write kept on this device while offline does not offer Retry", {tim assert.equal(status.textContent, "Saved on this device; waiting for sync"); f.replica.setOnline(true); f.replica.confirmAll(); await until(() => status.textContent === "Saved"); assert.equal(retry.hidden, true); - } finally {view.dispose(); describe.mock.restore(); h.window.close(); f.close();} + } finally {view.dispose(); h.window.close(); f.close();} }); diff --git a/tests/view.test.ts b/tests/view.test.ts index eba48dd..3ad2505 100644 --- a/tests/view.test.ts +++ b/tests/view.test.ts @@ -239,7 +239,6 @@ test("Sign out in settings goes through the SDK's signOut, never close, and keep test("a durable peer-tab edit feeds the same ready native lease without losing either addition", async () => { const f = await applicationFixture(), ha = dom(), hb = dom(), storage = memoryStorage(); const a = new JournalDrafts(storage), b = new JournalDrafts(storage); - const describe = mock.method(f.records, "describe", async () => catalog()); const va = mountJournal(ha.window.document, a, f.mdbase); let vb: ReturnType | null = null; try { @@ -250,30 +249,29 @@ test("a durable peer-tab edit feeds the same ready native lease without losing e ha.window.dispatchEvent(new ha.window.StorageEvent("storage", {key: `journal.native.note.${va.currentDraft().id}`})); await until(() => a.get(va.currentDraft().id)?.savedBody === "Seed\\nA\\nB" || undefined, 500); const draft = a.get(va.currentDraft().id)!; - assert.equal((await f.records.get(draft.recordId!, {body: true})).body, "Seed\\nA\\nB"); + assert.equal((await f.records.get(draft.recordId!, {include: {body: true}})).body, "Seed\\nA\\nB"); assert.equal(ha.page.value, "Seed\\nA\\nB"); assert.equal(draft.reviewRequired, undefined); - } finally {va.dispose(); vb?.dispose(); describe.mock.restore(); ha.window.close(); hb.window.close(); f.close();} + } finally {va.dispose(); vb?.dispose(); ha.window.close(); hb.window.close(); f.close();} }); test("reload reacquires the original confirmed entry and shows Saved without another write", async () => { const f = await applicationFixture(), h = dom(), storage = memoryStorage(), drafts = new JournalDrafts(storage); - const describe = mock.method(f.records, "describe", async () => catalog()); const first = mountJournal(h.window.document, drafts, f.mdbase); let reloaded: ReturnType | null = null; try { h.write("Confirmed original note"); await until(() => h.window.document.getElementById("status")!.textContent === "Saved" || undefined); const original = first.currentDraft(); first.dispose(); - const create = mock.method(f.records, "create"), save = mock.method(f.records, "save"); + const create = mock.method(f.records, "create"), save = mock.method(f.records, "settle"); try { reloaded = mountJournal(h.window.document, new JournalDrafts(storage), f.mdbase); await until(() => h.window.document.getElementById("status")!.textContent === "Saved" || undefined); assert.equal(reloaded.currentDraft().id, original.id); assert.equal(h.page.value, original.body); assert.equal(reloaded.currentDraft().recordId, original.recordId); assert.equal(create.mock.callCount(), 0); assert.equal(save.mock.callCount(), 0); - assert.equal((await f.records.get(original.recordId!, {body: true})).body, original.body); + assert.equal((await f.records.get(original.recordId!, {include: {body: true}})).body, original.body); } finally {create.mock.restore(); save.mock.restore();} - } finally {first.dispose(); reloaded?.dispose(); describe.mock.restore(); h.window.close(); f.close();} + } finally {first.dispose(); reloaded?.dispose(); h.window.close(); f.close();} }); test("late catalog completion after disposal cannot start data writes or close a borrowed client", async () => { const f = await applicationFixture(), h = dom(), job = deferred>(); diff --git a/vendor/sdk-e8ffb806-production-beta188.tgz b/vendor/sdk-e8ffb806-production-beta188.tgz new file mode 100644 index 0000000..b4d999c Binary files /dev/null and b/vendor/sdk-e8ffb806-production-beta188.tgz differ diff --git a/vendor/sdk-production-beta188.json b/vendor/sdk-production-beta188.json new file mode 100644 index 0000000..7b3bc27 --- /dev/null +++ b/vendor/sdk-production-beta188.json @@ -0,0 +1,46 @@ +{ + "schema": "tnapp/production-sdk-original/1", + "artifact": "sdk-e8ffb806-production-beta188.tgz", + "version": "0.1.0-beta.188", + "sourceCommit": "e8ffb806d16c58fe03cf50c96fb66e29ebb612ba", + "environment": "production", + "bytes": 4819840, + "sha256": "9ac39582b5440272f7e95177ac4f4840aebd4c8ddf832b24545232abe99c6b27", + "sri": "sha512-EDaInyP8q2Lz0Akz8P+CNj5O6OvinMJWR1bE04fKtWRFDt9mVRBRBYRja5okOtb3gtnKYyMdQFGLlFln7F1RrA==", + "publication": { + "repository": "mdbase-dev/mdbase-connect", + "workflow": ".github/workflows/publish-sdk-npm.yml", + "ref": "refs/tags/v0.1.0-beta.188", + "npm": "@mdbase-dev/sdk@0.1.0-beta.188", + "integrity": "sha512-EDaInyP8q2Lz0Akz8P+CNj5O6OvinMJWR1bE04fKtWRFDt9mVRBRBYRja5okOtb3gtnKYyMdQFGLlFln7F1RrA==", + "tarball": "https://registry.npmjs.org/@mdbase-dev/sdk/-/sdk-0.1.0-beta.188.tgz", + "attestations": "https://registry.npmjs.org/-/npm/v1/attestations/@mdbase-dev%2fsdk@0.1.0-beta.188" + }, + "trust": { + "package": "@mdbase-dev/sdk", + "sha256": "7912e40838213e60b65848350d319a48019021317b112e3326429b250f0133d7", + "bytes": 840, + "cpOrigin": "https://connect.mdbase.dev", + "logOrigin": "https://log.mdbase.dev", + "sourceCommit": "36abd4b065be9495a7f5ae0808865a1f2279af04", + "sourceVersion": "0.1.0-beta.141" + }, + "runtime": { + "sha256": "7004351583c3a79411e644fafe9f5786816d32ea058605d1905fd50666012f40", + "bytes": 3875815, + "maxBytes": 3875815, + "source": { + "repository": "mdbase-dev/mdbase-connect", + "commit": "aae332572834fbfa065c0f696017c0de3540649d" + } + }, + "packagedTrustModuleSha256": "cf9690968d68b3c1eed2356ccfa2c72198b2f8cf07b9b24b1bb7be499b05bffb", + "bundledDependencies": [ + { + "name": "@mdbase-dev/connect-protocol", + "member": "package/vendor/mdbase-dev-connect-protocol-0.1.0-beta.145-e6e66bcd172f.tgz", + "bytes": 106404, + "sha256": "e901144b8d93dc96fcf71d9d8f85dddbf647e9908913be21191c484e9224bb98" + } + ] +}