@mdbase-dev/sdk 0.1.0-beta.161 → 0.1.0-beta.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +1 -0
  2. package/dist/app-grant/setup.js +25 -17
  3. package/dist/app-grant/setup.js.map +1 -1
  4. package/dist/app-host/local-facade.d.ts +1 -1
  5. package/dist/app-host/local-facade.js +1 -1
  6. package/dist/app-host/local-facade.js.map +1 -1
  7. package/dist/app-host/unsynced-collection.d.ts +99 -0
  8. package/dist/app-host/unsynced-collection.js +530 -0
  9. package/dist/app-host/unsynced-collection.js.map +1 -0
  10. package/dist/app-host/wasm-runtime.d.ts +12 -0
  11. package/dist/app-host/wasm-runtime.js +32 -0
  12. package/dist/app-host/wasm-runtime.js.map +1 -1
  13. package/dist/app-host/web-sign-in-session.d.ts +13 -0
  14. package/dist/app-host/web-sign-in-session.js.map +1 -1
  15. package/dist/application/asset-pins.json +4 -4
  16. package/dist/application/declaration.d.ts +2 -0
  17. package/dist/application/declaration.js +3 -3
  18. package/dist/application/declaration.js.map +1 -1
  19. package/dist/application/entry.d.ts +40 -0
  20. package/dist/application/entry.js +72 -2
  21. package/dist/application/entry.js.map +1 -1
  22. package/dist/application/front-door.d.ts +20 -1
  23. package/dist/application/front-door.js +22 -0
  24. package/dist/application/front-door.js.map +1 -1
  25. package/dist/application/native-assets.d.ts +13 -0
  26. package/dist/application/native-assets.js +6 -1
  27. package/dist/application/native-assets.js.map +1 -1
  28. package/dist/application/phase.d.ts +11 -1
  29. package/dist/application/phase.js +12 -0
  30. package/dist/application/phase.js.map +1 -1
  31. package/dist/application/runtime.d.ts +17 -0
  32. package/dist/application/runtime.js +102 -2
  33. package/dist/application/runtime.js.map +1 -1
  34. package/dist/application/setup.js +5 -0
  35. package/dist/application/setup.js.map +1 -1
  36. package/dist/application/unsynced-transfer.d.ts +111 -0
  37. package/dist/application/unsynced-transfer.js +194 -0
  38. package/dist/application/unsynced-transfer.js.map +1 -0
  39. package/dist/application/worker-client.js +3 -1
  40. package/dist/application/worker-client.js.map +1 -1
  41. package/dist/application/worker-host.d.ts +32 -0
  42. package/dist/application/worker-host.js +257 -16
  43. package/dist/application/worker-host.js.map +1 -1
  44. package/dist/application/worker-protocol.d.ts +1 -1
  45. package/dist/application/worker-protocol.js +7 -1
  46. package/dist/application/worker-protocol.js.map +1 -1
  47. package/dist/application/worker-session.d.ts +15 -0
  48. package/dist/application/worker-session.js +2 -1
  49. package/dist/application/worker-session.js.map +1 -1
  50. package/dist/application/worker.js +866 -144
  51. package/dist/browser/mdbase-sdk.js +7541 -6983
  52. package/dist/browser/mdbase-sdk.min.js +70 -70
  53. package/dist/client.d.ts +8 -1
  54. package/dist/client.js +2 -0
  55. package/dist/client.js.map +1 -1
  56. package/dist/describe-error.d.ts +1 -1
  57. package/dist/describe-error.js +1 -0
  58. package/dist/describe-error.js.map +1 -1
  59. package/dist/index.d.ts +2 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/manifest.d.ts +11 -0
  62. package/dist/manifest.js +12 -0
  63. package/dist/manifest.js.map +1 -0
  64. package/dist/native/provenance.json +5 -5
  65. package/dist/native/runtime.wasm +0 -0
  66. package/dist/testing/application.d.ts +5 -1
  67. package/dist/testing/application.js +45 -1
  68. package/dist/testing/application.js.map +1 -1
  69. package/dist/thin-client.d.ts +1 -1
  70. package/dist/thin-client.js.map +1 -1
  71. package/dist/wire.d.ts +4 -1
  72. package/dist/wire.js +3 -1
  73. package/dist/wire.js.map +1 -1
  74. package/package.json +5 -1
@@ -15244,13 +15244,13 @@ function applicationDeclaration(input) {
15244
15244
  const kind = data.installationKind ?? (platform === "mobile" ? "mobile" : "app-runtime");
15245
15245
  if (platform === null || kind !== "app-runtime" && kind !== "mobile" || platform === "mobile" && kind !== "mobile" || (platform === "extension" || platform === "native") && kind !== "app-runtime") fail();
15246
15246
  const override = data.storage;
15247
- if (override !== void 0 && (!override || Array.isArray(override) || typeof override !== "object" || Object.keys(override).some((key3) => !["joinDatabaseName", "setupDatabaseName", "applicationNamespace"].includes(key3)))) fail();
15247
+ if (override !== void 0 && (!override || Array.isArray(override) || typeof override !== "object" || Object.keys(override).some((key3) => !["joinDatabaseName", "setupDatabaseName", "unsyncedDatabaseName", "applicationNamespace"].includes(key3)))) fail();
15248
15248
  const fields3 = override;
15249
15249
  const namespace = fields3?.applicationNamespace ?? id4;
15250
15250
  const databaseOwner = typeof namespace === "string" && namespace !== id4 ? `namespace.${storageNamespaceDigest(JSON.stringify([id4, namespace]))}` : id4;
15251
- const storage = { joinDatabaseName: fields3?.joinDatabaseName ?? `mdbase.${databaseOwner}.native-collection-join.v1`, setupDatabaseName: fields3?.setupDatabaseName ?? `mdbase.${databaseOwner}.native-model-setup.v1`, applicationNamespace: namespace };
15251
+ const storage = { joinDatabaseName: fields3?.joinDatabaseName ?? `mdbase.${databaseOwner}.native-collection-join.v1`, setupDatabaseName: fields3?.setupDatabaseName ?? `mdbase.${databaseOwner}.native-model-setup.v1`, unsyncedDatabaseName: fields3?.unsyncedDatabaseName ?? `mdbase.${databaseOwner}.unsynced.v1`, applicationNamespace: namespace };
15252
15252
  for (const value2 of Object.values(storage)) if (typeof value2 !== "string" || !value2 || value2.length > 256 || /[\u0000-\u001f\u007f]/.test(value2)) fail();
15253
- if (storage.joinDatabaseName === storage.setupDatabaseName) fail();
15253
+ if ((/* @__PURE__ */ new Set([storage.joinDatabaseName, storage.setupDatabaseName, storage.unsyncedDatabaseName])).size !== 3) fail();
15254
15254
  const resolved = Object.freeze({ id: id4, name: name2, origin: origin4, environment, installationKind: kind, manifest, storage: Object.freeze(storage) });
15255
15255
  storageOverrides.set(resolved, Object.freeze({ ...fields3 }));
15256
15256
  return resolved;
@@ -15308,12 +15308,12 @@ function appReleaseTrust() {
15308
15308
  // src/application/asset-pins.json
15309
15309
  var asset_pins_default = {
15310
15310
  runtime: {
15311
- sha256: "c30e2e23c1fbc6642936996b8495133ce66e87caaf49a99050fc38f8a7422667",
15312
- bytes: 3803805,
15313
- maxBytes: 3803805,
15311
+ sha256: "4d377128dd7e76241cbbf63d00dd3f232286d8a506908fa240d20c6ac85c69a8",
15312
+ bytes: 3811508,
15313
+ maxBytes: 3811508,
15314
15314
  source: {
15315
15315
  repository: "mdbase-dev/mdbase-connect",
15316
- commit: "81462447473808e040d3b31d15ede54e987ffa1e"
15316
+ commit: "419d7ddfaade89b3795836674685f99738db9427"
15317
15317
  }
15318
15318
  },
15319
15319
  sqlite: {
@@ -16725,22 +16725,22 @@ function timerConsent(value2, requestId, approved, confirmed2, selected) {
16725
16725
  if (!timerKeys(value2)) return void 0;
16726
16726
  if (![true, false, null].includes(value2.timer_consent) || !Array.isArray(value2.timer_grants) || value2.timer_grants.length > 1e3) throw fail3("response");
16727
16727
  const grants = value2.timer_grants.map((item) => {
16728
- const row2 = object2(item, "grant_id,collection_id,declaration_digest"), collectionId = id(row2.collection_id);
16728
+ const row3 = object2(item, "grant_id,collection_id,declaration_digest"), collectionId = id(row3.collection_id);
16729
16729
  if (!approved.includes(collectionId)) throw fail3("response");
16730
- return Object.freeze({ grantId: id(row2.grant_id), collectionId, declarationDigest: text(row2.declaration_digest, /^[0-9a-f]{64}$/) });
16730
+ return Object.freeze({ grantId: id(row3.grant_id), collectionId, declarationDigest: text(row3.declaration_digest, /^[0-9a-f]{64}$/) });
16731
16731
  });
16732
16732
  if (new Set(grants.map((grant) => grant.grantId)).size !== grants.length || new Set(grants.map((grant) => grant.collectionId)).size !== grants.length || (value2.timer_consent === true ? !confirmed2 || !selected || !grants.some((grant) => grant.collectionId === selected) : grants.length !== 0) || new Set(grants.map((grant) => grant.declarationDigest)).size > 1) throw fail3("response");
16733
16733
  return value2.timer_consent === null ? void 0 : Object.freeze({ requestId, approved: value2.timer_consent === true, grants: Object.freeze(grants.filter((grant) => grant.collectionId === selected)) });
16734
16734
  }
16735
16735
  function timerChannel(value2) {
16736
- const row2 = object2(value2, "grantId,kind,targetDigest,channelId,pending");
16737
- if (!["web_push", "fcm"].includes(row2.kind) || ![null, "register", "delete"].includes(row2.pending) || row2.channelId === null && row2.pending !== "register") throw fail3("response");
16736
+ const row3 = object2(value2, "grantId,kind,targetDigest,channelId,pending");
16737
+ if (!["web_push", "fcm"].includes(row3.kind) || ![null, "register", "delete"].includes(row3.pending) || row3.channelId === null && row3.pending !== "register") throw fail3("response");
16738
16738
  return Object.freeze({
16739
- grantId: id(row2.grantId),
16740
- kind: row2.kind,
16741
- targetDigest: text(row2.targetDigest, /^[0-9a-f]{64}$/),
16742
- channelId: row2.channelId === null ? null : id(row2.channelId),
16743
- pending: row2.pending
16739
+ grantId: id(row3.grantId),
16740
+ kind: row3.kind,
16741
+ targetDigest: text(row3.targetDigest, /^[0-9a-f]{64}$/),
16742
+ channelId: row3.channelId === null ? null : id(row3.channelId),
16743
+ pending: row3.pending
16744
16744
  });
16745
16745
  }
16746
16746
  function collectionListing(value2) {
@@ -16891,8 +16891,8 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
16891
16891
  id(receipt3.requestId);
16892
16892
  if (!s.token || !s.selection || typeof receipt3.approved !== "boolean" || !Array.isArray(receipt3.grants) || receipt3.grants.length > 1) throw fail3("recovery_required");
16893
16893
  const grants = receipt3.grants.map((grant) => {
16894
- const row2 = object2(grant, "grantId,collectionId,declarationDigest");
16895
- return { grant_id: row2.grantId, collection_id: row2.collectionId, declaration_digest: row2.declarationDigest };
16894
+ const row3 = object2(grant, "grantId,collectionId,declarationDigest");
16895
+ return { grant_id: row3.grantId, collection_id: row3.collectionId, declaration_digest: row3.declarationDigest };
16896
16896
  });
16897
16897
  const confirmed2 = s.selection.portalConfirmed === true || s.consentRequest?.portalConfirmed === true;
16898
16898
  s.timerConsent = timerConsent({ timer_consent: receipt3.approved, timer_grants: grants }, id(receipt3.requestId), s.collectionIds, confirmed2, grants[0]?.collection_id);
@@ -16955,8 +16955,8 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
16955
16955
  }
16956
16956
  view() {
16957
16957
  this.check();
16958
- const s = this.state, identity2 = s.consentRequest?.portalConfirmed ? s.consentRequest : s.selection;
16959
- return Object.freeze({ requestId: s.requestId, installationId: s.installationId, deviceId: s.deviceId, appId: s.appId, kind: s.kind, verificationUri: `${s.cpOrigin}/pair/${s.requestId}`, state: s.token ? "paired" : s.proof ? "awaiting_approval" : s.confirmed ? "account_confirmed" : s.selection ? "account_selected" : "pending", accountId: s.selection?.accountId ?? null, accountEmail: identity2?.accountEmail ?? null, ...identity2?.accountProvider ? { accountProvider: identity2.accountProvider, accountLogin: identity2.accountLogin } : {}, collectionSelection: s.collectionSelection ? Object.freeze({ ...s.collectionSelection, createdCollectionIds: Object.freeze([...s.collectionSelection.createdCollectionIds]) }) : null, nativeSelection: s.nativeSelection && s.nativeGrants?.some((c) => c.authorization_request_id === s.nativeSelection.authorizationRequestId && c.collection_id === s.nativeSelection.collectionId) ? Object.freeze({ ...s.nativeSelection }) : null, nativeGrants: Object.freeze([...s.nativeGrants ?? []]), ...s.timerConsent ? { timerConsent: s.timerConsent } : {}, collectionIds: Object.freeze([...s.collectionIds]), createCollections: s.createCollections, requestedCreateCollections: s.requestedCreateCollections });
16958
+ const s = this.state, identity3 = s.consentRequest?.portalConfirmed ? s.consentRequest : s.selection;
16959
+ return Object.freeze({ requestId: s.requestId, installationId: s.installationId, deviceId: s.deviceId, appId: s.appId, kind: s.kind, verificationUri: `${s.cpOrigin}/pair/${s.requestId}`, state: s.token ? "paired" : s.proof ? "awaiting_approval" : s.confirmed ? "account_confirmed" : s.selection ? "account_selected" : "pending", accountId: s.selection?.accountId ?? null, accountEmail: identity3?.accountEmail ?? null, ...identity3?.accountProvider ? { accountProvider: identity3.accountProvider, accountLogin: identity3.accountLogin } : {}, collectionSelection: s.collectionSelection ? Object.freeze({ ...s.collectionSelection, createdCollectionIds: Object.freeze([...s.collectionSelection.createdCollectionIds]) }) : null, nativeSelection: s.nativeSelection && s.nativeGrants?.some((c) => c.authorization_request_id === s.nativeSelection.authorizationRequestId && c.collection_id === s.nativeSelection.collectionId) ? Object.freeze({ ...s.nativeSelection }) : null, nativeGrants: Object.freeze([...s.nativeGrants ?? []]), ...s.timerConsent ? { timerConsent: s.timerConsent } : {}, collectionIds: Object.freeze([...s.collectionIds]), createCollections: s.createCollections, requestedCreateCollections: s.requestedCreateCollections });
16960
16960
  }
16961
16961
  /** Persist before exposing changed selection/proof/credential or native ACK. */
16962
16962
  async save(next) {
@@ -16973,12 +16973,12 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
16973
16973
  bytes8.fill(0);
16974
16974
  }
16975
16975
  }
16976
- async exclusive(work) {
16976
+ async exclusive(work2) {
16977
16977
  this.check();
16978
16978
  if (this.busy) throw fail3("busy");
16979
16979
  this.busy = true;
16980
16980
  try {
16981
- return await work();
16981
+ return await work2();
16982
16982
  } finally {
16983
16983
  this.busy = false;
16984
16984
  }
@@ -17238,9 +17238,9 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
17238
17238
  /** A consent window the CP reports closed (denied in the portal, or expired) can
17239
17239
  * never be reopened, and re-consent windows are not renewable. Forget it, so the
17240
17240
  * next explicit consent starts a new window instead of re-sending the dead one. */
17241
- async consentWindow(requestId, work) {
17241
+ async consentWindow(requestId, work2) {
17242
17242
  try {
17243
- return await work();
17243
+ return await work2();
17244
17244
  } catch (error4) {
17245
17245
  const code = error4 instanceof AppInstallationSignInError ? error4.server?.code : null;
17246
17246
  if ((code === "installation_pairing_denied" || code === "installation_pairing_expired") && error4 instanceof AppInstallationSignInError && error4.server?.status === 404) {
@@ -17674,8 +17674,8 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
17674
17674
  this.check();
17675
17675
  this.receipt();
17676
17676
  if (!this.state.timerConsent?.approved || !this.state.timerConsent.grants.some((grant) => grant.grantId === grantId)) throw fail3("fenced");
17677
- const row2 = this.state.timerChannel;
17678
- return row2?.grantId === grantId ? row2 : null;
17677
+ const row3 = this.state.timerChannel;
17678
+ return row3?.grantId === grantId ? row3 : null;
17679
17679
  }
17680
17680
  retainTimerChannel(grantId, value2) {
17681
17681
  return this.exclusive(async () => {
@@ -19071,31 +19071,34 @@ function applicationWorkerBinding(value2) {
19071
19071
  return typeof value2 === "string" && /^[a-zA-Z0-9_-]{16,128}$/.test(value2);
19072
19072
  }
19073
19073
  function applicationWorkerRequest(value2, binding) {
19074
- const fail18 = () => {
19074
+ const fail19 = () => {
19075
19075
  throw new ApplicationWorkerError("application_worker_binding");
19076
19076
  };
19077
- if (!Array.isArray(value2) || value2.length !== 5 || value2[0] !== APPLICATION_WORKER_PROTOCOL || value2[1] !== binding || !Number.isInteger(value2[2]) || value2[2] < 0 || value2[2] > APPLICATION_WORKER_MAX_ID || value2[2] === 0 && value2[3] !== "foreground" && value2[3] !== "approvalClosed" || value2[2] !== 0 && value2[3] === "approvalClosed") return fail18();
19077
+ if (!Array.isArray(value2) || value2.length !== 5 || value2[0] !== APPLICATION_WORKER_PROTOCOL || value2[1] !== binding || !Number.isInteger(value2[2]) || value2[2] < 0 || value2[2] > APPLICATION_WORKER_MAX_ID || value2[2] === 0 && value2[3] !== "foreground" && value2[3] !== "approvalClosed" || value2[2] !== 0 && value2[3] === "approvalClosed") return fail19();
19078
19078
  const action = value2[3], payload = value2[4];
19079
- if (!["start", "authorize", "renewExpiredPairing", "select", "clearSelection", "forget", "refreshCollections", "signOut", "deleteCollection", "requestFileAccess", "pollFileAccess", "foreground", "approvalClosed", "close"].includes(action)) fail18();
19079
+ if (!["start", "authorize", "renewExpiredPairing", "select", "clearSelection", "forget", "refreshCollections", "signOut", "deleteCollection", "requestFileAccess", "pollFileAccess", "openUnsynced", "attachUnsynced", "detachUnsynced", "retireUnsynced", "discardUnsynced", "foreground", "approvalClosed", "close"].includes(action)) fail19();
19080
19080
  const uuid7 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
19081
19081
  if (action === "requestFileAccess") {
19082
- if (!Array.isArray(payload) || payload.length !== 3 || typeof payload[0] !== "string" || !uuid7.test(payload[0]) || !Array.isArray(payload[1]) || !payload[1].length || payload[1].length > 6 || payload[1].some((a) => typeof a !== "string" || !["list", "read", "add", "replace", "move", "delete"].includes(a)) || typeof payload[2] !== "string" || !payload[2] || payload[2].length > 140) fail18();
19082
+ if (!Array.isArray(payload) || payload.length !== 3 || typeof payload[0] !== "string" || !uuid7.test(payload[0]) || !Array.isArray(payload[1]) || !payload[1].length || payload[1].length > 6 || payload[1].some((a) => typeof a !== "string" || !["list", "read", "add", "replace", "move", "delete"].includes(a)) || typeof payload[2] !== "string" || !payload[2] || payload[2].length > 140) fail19();
19083
19083
  return Object.freeze([value2[0], value2[1], value2[2], action, Object.freeze([payload[0], Object.freeze([...payload[1]]), payload[2]])]);
19084
19084
  }
19085
19085
  if (action === "pollFileAccess") {
19086
- if (typeof payload !== "string" || !uuid7.test(payload)) fail18();
19086
+ if (typeof payload !== "string" || !uuid7.test(payload)) fail19();
19087
19087
  } else if (action === "select" || action === "forget") {
19088
- if (typeof payload !== "string" || !payload || payload.length > 256 || /[\u0000-\u001f\u007f]/.test(payload)) fail18();
19088
+ if (typeof payload !== "string" || !payload || payload.length > 256 || /[\u0000-\u001f\u007f]/.test(payload)) fail19();
19089
19089
  } else if (action === "deleteCollection") {
19090
- if (!Array.isArray(payload) || payload.length !== 2 || typeof payload[0] !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(payload[0]) || typeof payload[1] !== "string" || !payload[1] || payload[1].length > 1024) fail18();
19090
+ if (!Array.isArray(payload) || payload.length !== 2 || typeof payload[0] !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(payload[0]) || typeof payload[1] !== "string" || !payload[1] || payload[1].length > 1024) fail19();
19091
+ return Object.freeze([value2[0], value2[1], value2[2], action, Object.freeze([payload[0], payload[1]])]);
19092
+ } else if (action === "retireUnsynced") {
19093
+ if (!Array.isArray(payload) || payload.length !== 2 || payload.some((id4) => typeof id4 !== "string" || !uuid7.test(id4)) || payload[0] === payload[1]) fail19();
19091
19094
  return Object.freeze([value2[0], value2[1], value2[2], action, Object.freeze([payload[0], payload[1]])]);
19092
19095
  } else if (action === "authorize") {
19093
- if (payload !== null && (typeof payload !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(payload) || payload === "00000000-0000-0000-0000-000000000000")) fail18();
19096
+ if (payload !== null && (typeof payload !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(payload) || payload === "00000000-0000-0000-0000-000000000000")) fail19();
19094
19097
  } else if (action === "foreground") {
19095
- if (typeof payload !== "boolean") fail18();
19098
+ if (typeof payload !== "boolean") fail19();
19096
19099
  } else if (action === "approvalClosed") {
19097
- if (payload !== true) fail18();
19098
- } else if (payload !== null) fail18();
19100
+ if (payload !== true) fail19();
19101
+ } else if (payload !== null) fail19();
19099
19102
  return Object.freeze([...value2]);
19100
19103
  }
19101
19104
  function applicationWorkerProblem(error4) {
@@ -19183,7 +19186,7 @@ function open() {
19183
19186
  request3.onerror = request3.onblocked = () => resolve(null);
19184
19187
  });
19185
19188
  }
19186
- function run(mode, work) {
19189
+ function run(mode, work2) {
19187
19190
  return open().then((db) => new Promise((resolve) => {
19188
19191
  if (!db) {
19189
19192
  resolve(null);
@@ -19191,7 +19194,7 @@ function run(mode, work) {
19191
19194
  }
19192
19195
  let result2 = null;
19193
19196
  try {
19194
- const tx = db.transaction(STORE2, mode), request3 = work(tx.objectStore(STORE2));
19197
+ const tx = db.transaction(STORE2, mode), request3 = work2(tx.objectStore(STORE2));
19195
19198
  if (request3) request3.onsuccess = () => {
19196
19199
  result2 = request3.result ?? null;
19197
19200
  };
@@ -19234,13 +19237,13 @@ async function eraseAppLogTokens(scope) {
19234
19237
  try {
19235
19238
  const tx = db.transaction(STORE2, "readwrite"), cursor = tx.objectStore(STORE2).openCursor();
19236
19239
  cursor.onsuccess = () => {
19237
- const row2 = cursor.result;
19238
- if (!row2) return;
19239
- if (typeof row2.key === "string") {
19240
- const parts = row2.key.split(" ");
19241
- if (parts.length === 5 && parts[0] === cpOrigin && parts[2] === connectorId && parts[3] === deviceId) row2.delete();
19240
+ const row3 = cursor.result;
19241
+ if (!row3) return;
19242
+ if (typeof row3.key === "string") {
19243
+ const parts = row3.key.split(" ");
19244
+ if (parts.length === 5 && parts[0] === cpOrigin && parts[2] === connectorId && parts[3] === deviceId) row3.delete();
19242
19245
  }
19243
- row2.continue();
19246
+ row3.continue();
19244
19247
  };
19245
19248
  tx.oncomplete = () => {
19246
19249
  db.close();
@@ -19904,6 +19907,7 @@ var op = union("op", [
19904
19907
  ],
19905
19908
  [12, "sync_settings", [[1, "inclusion", fileInclusion]]]
19906
19909
  ]);
19910
+ var validationLevel = enumOf("level", ["off", "warn", "error"]);
19907
19911
  var submitParams = struct("submit-params", [
19908
19912
  [0, "ops", list(op, { nonEmpty: true }), "req1"],
19909
19913
  [1, "mutationId", uuid2, "opt"],
@@ -19913,7 +19917,8 @@ var submitParams = struct("submit-params", [
19913
19917
  [5, "mutationIds", list(uuid2), "opt1"],
19914
19918
  [6, "dryRun", bool, "opt"],
19915
19919
  [7, "include", include, "opt"],
19916
- [8, "wait", enumOf("wait-for", ["pending", "confirmed", "published"]), "opt"]
19920
+ [8, "wait", enumOf("wait-for", ["pending", "confirmed", "published"]), "opt"],
19921
+ [9, "validation", validationLevel, "opt"]
19917
19922
  ]);
19918
19923
  var CONFLICT_FORMS = ["missing", "value", "text", "blob", "deleted"];
19919
19924
  var conflictValue = {
@@ -20185,7 +20190,7 @@ var mutation = struct("mutation", [
20185
20190
  [6, "ops", list(op, { nonEmpty: true }), "req1"],
20186
20191
  [7, "onBehalf", uuid2, "opt"],
20187
20192
  [8, "conflictMode", conflictMode, "opt"],
20188
- [9, "validatedAt", enumOf("level", ["off", "warn", "error"]), "opt"],
20193
+ [9, "validatedAt", validationLevel, "opt"],
20189
20194
  [
20190
20195
  10,
20191
20196
  "room",
@@ -20587,9 +20592,9 @@ function decodeAppBasesResult(bytes8, expectedWindow) {
20587
20592
  return { index, code: text2(i.get(1)), detail: text2(i.get(2)) };
20588
20593
  });
20589
20594
  const rows2 = list2(m2.get(3), ROWS_MAX).map((v) => {
20590
- const row2 = tuple3(v, 4), cells = list2(row2[3], 64);
20595
+ const row3 = tuple3(v, 4), cells = list2(row3[3], 64);
20591
20596
  if (cells.length !== columns.length) throw bad2("row cell count differs from columns");
20592
- return { record: uuid2.dec(row2[0]), path: text2(row2[1]), sourceRevision: hash.dec(row2[2]), cells: cells.map((v2) => decodeAppBasesCell(v2)) };
20597
+ return { record: uuid2.dec(row3[0]), path: text2(row3[1]), sourceRevision: hash.dec(row3[2]), cells: cells.map((v2) => decodeAppBasesCell(v2)) };
20593
20598
  });
20594
20599
  const groups = list2(m2.get(4), ROWS_MAX).map((v) => {
20595
20600
  const group = tuple3(v, 2), key3 = decodeAppBasesCell(group[0]);
@@ -22546,7 +22551,7 @@ var RecordsApi = class {
22546
22551
  if (this.watch) return;
22547
22552
  const watch = this.client.watchChanges(void 0, (batch) => {
22548
22553
  for (const [id4, slot] of this.slots)
22549
- if (slot.leases > 0 && (batch.reset || batch.changes.some((change2) => change2.id === id4)))
22554
+ if (slot.leases > 0 && (batch.reset || batch.changes.some((change3) => change3.id === id4)))
22550
22555
  void slot.session.refresh();
22551
22556
  });
22552
22557
  this.watch = watch;
@@ -23413,8 +23418,8 @@ var WriteLedger = class {
23413
23418
  this.changed();
23414
23419
  return this.persist(() => this.entries.has(id4) ? void 0 : this.store.remove(id4));
23415
23420
  }
23416
- persist(work) {
23417
- return this.io = this.io.then(work).catch(() => {
23421
+ persist(work2) {
23422
+ return this.io = this.io.then(work2).catch(() => {
23418
23423
  });
23419
23424
  }
23420
23425
  changed() {
@@ -24655,7 +24660,7 @@ var MdbaseClient = class _MdbaseClient {
24655
24660
  this.restorers = this.restorers.filter((f) => f !== start);
24656
24661
  offLink();
24657
24662
  };
24658
- const fail18 = (why) => {
24663
+ const fail19 = (why) => {
24659
24664
  if (stopped) return;
24660
24665
  stopped = true;
24661
24666
  epoch++;
@@ -24687,7 +24692,7 @@ var MdbaseClient = class _MdbaseClient {
24687
24692
  try {
24688
24693
  await this.readFence(signal);
24689
24694
  } catch (e) {
24690
- if (current2()) fail18(schemaToError(e, "changes watch"));
24695
+ if (current2()) fail19(schemaToError(e, "changes watch"));
24691
24696
  return;
24692
24697
  }
24693
24698
  if (!current2()) return;
@@ -24722,13 +24727,13 @@ var MdbaseClient = class _MdbaseClient {
24722
24727
  }
24723
24728
  const charge = 64 + batch.cursor.length * 2 + batch.changes.reduce((n, c) => n + 384 + c.path.length * 2, 0);
24724
24729
  if (early.length >= 16 || weight + charge > 1024 * 1024) {
24725
- fail18(mdbaseError("unavailable", "change watch startup buffer exceeded"));
24730
+ fail19(mdbaseError("unavailable", "change watch startup buffer exceeded"));
24726
24731
  return;
24727
24732
  }
24728
24733
  weight += charge;
24729
24734
  early.push(batch);
24730
24735
  } catch (e) {
24731
- fail18(schemaToError(e, "changes push"));
24736
+ fail19(schemaToError(e, "changes push"));
24732
24737
  }
24733
24738
  });
24734
24739
  publish("starting");
@@ -24745,17 +24750,17 @@ var MdbaseClient = class _MdbaseClient {
24745
24750
  publish("active");
24746
24751
  if (current2()) resolveReady();
24747
24752
  } catch (e) {
24748
- if (current2()) fail18(schemaToError(e, "changes watch"));
24753
+ if (current2()) fail19(schemaToError(e, "changes watch"));
24749
24754
  }
24750
24755
  };
24751
24756
  const offLink = this.onLink((link, why) => {
24752
24757
  if (link === "closed") {
24753
- if (why) fail18(why);
24758
+ if (why) fail19(why);
24754
24759
  else stop();
24755
24760
  }
24756
24761
  });
24757
24762
  this.onSession(start);
24758
- void this.ready().then(start).catch((e) => fail18(schemaToError(e, "changes watch")));
24763
+ void this.ready().then(start).catch((e) => fail19(schemaToError(e, "changes watch")));
24759
24764
  const watch = Object.assign(stop, { ready, subscribe: (listener) => {
24760
24765
  if (state !== "closed" && state !== "failed") listeners.add(listener);
24761
24766
  notify(listener);
@@ -24842,6 +24847,7 @@ var MdbaseClient = class _MdbaseClient {
24842
24847
  if (o.dryRun) p.dryRun = true;
24843
24848
  if (o.include) p.include = o.include;
24844
24849
  if (o.wait) p.wait = o.wait;
24850
+ if (o.validation) p.validation = o.validation;
24845
24851
  if (!o.dryRun) {
24846
24852
  this.startLedger();
24847
24853
  await this.writeLedger.begin(p.mutationIds ?? [p.mutationId], ops);
@@ -25835,7 +25841,7 @@ function attachAppTimerReceiver(port, options) {
25835
25841
  const id4 = frame[3], verb = frame[4], args = frame[5], ctrl = new AbortController();
25836
25842
  last = id4;
25837
25843
  let dispatched = false, write = verb !== "list" && verb !== "lookupOperation";
25838
- const work = Promise.resolve().then(async () => {
25844
+ const work2 = Promise.resolve().then(async () => {
25839
25845
  const timer = setTimeout(() => ctrl.abort(), 1e4);
25840
25846
  let response2, reader;
25841
25847
  const bytes8 = new Uint8Array(MAX2);
@@ -25857,16 +25863,16 @@ function attachAppTimerReceiver(port, options) {
25857
25863
  } else if (verb === "registerChannel" && args.length === 1) {
25858
25864
  if (!options.journal) throw error2("upgrade_required", "native_timer_channel_journal_unavailable");
25859
25865
  const input = args[0];
25860
- const row2 = record(input, `kind,criteria,${input?.kind === "web_push" ? "subscription" : "token"}`);
25861
- if (!["web_push", "fcm"].includes(row2.kind) || !Array.isArray(row2.criteria) || !row2.criteria.length || row2.criteria.length > 100) throw error2("invalid_request", "timer_input");
25862
- row2.criteria.forEach(criterion);
25863
- if (new Set(row2.criteria).size !== row2.criteria.length) throw error2("invalid_request", "timer_input");
25866
+ const row3 = record(input, `kind,criteria,${input?.kind === "web_push" ? "subscription" : "token"}`);
25867
+ if (!["web_push", "fcm"].includes(row3.kind) || !Array.isArray(row3.criteria) || !row3.criteria.length || row3.criteria.length > 100) throw error2("invalid_request", "timer_input");
25868
+ row3.criteria.forEach(criterion);
25869
+ if (new Set(row3.criteria).size !== row3.criteria.length) throw error2("invalid_request", "timer_input");
25864
25870
  let target2;
25865
- if (row2.kind === "fcm") {
25866
- if (typeof row2.token !== "string" || row2.token.length < 16 || row2.token.length > 4096 || /[\s\u0000-\u001f\u007f]/.test(row2.token)) throw error2("invalid_request", "timer_input");
25867
- target2 = { transport: "fcm", token: row2.token };
25871
+ if (row3.kind === "fcm") {
25872
+ if (typeof row3.token !== "string" || row3.token.length < 16 || row3.token.length > 4096 || /[\s\u0000-\u001f\u007f]/.test(row3.token)) throw error2("invalid_request", "timer_input");
25873
+ target2 = { transport: "fcm", token: row3.token };
25868
25874
  } else {
25869
- const subscription = record(row2.subscription, "endpoint,expirationTime,keys"), keys2 = record(subscription.keys, "p256dh,auth");
25875
+ const subscription = record(row3.subscription, "endpoint,expirationTime,keys"), keys2 = record(subscription.keys, "p256dh,auth");
25870
25876
  if (typeof subscription.endpoint !== "string" || subscription.endpoint.length > 2048 || typeof keys2.p256dh !== "string" || typeof keys2.auth !== "string" || !/^[A-Za-z0-9_-]{87}$/.test(keys2.p256dh) || !/^[A-Za-z0-9_-]{22}$/.test(keys2.auth)) throw error2("invalid_request", "timer_input");
25871
25877
  let endpoint2;
25872
25878
  try {
@@ -25878,7 +25884,7 @@ function attachAppTimerReceiver(port, options) {
25878
25884
  if (subscription.expirationTime !== null && (!Number.isFinite(subscription.expirationTime) || subscription.expirationTime < 0)) throw error2("invalid_request", "timer_input");
25879
25885
  target2 = { subscription };
25880
25886
  }
25881
- request3 = { path: channelBase, method: "POST", write: true, body: { installation_id: options.scope.installation, criteria: [...row2.criteria].sort(), ...target2 } };
25887
+ request3 = { path: channelBase, method: "POST", write: true, body: { installation_id: options.scope.installation, criteria: [...row3.criteria].sort(), ...target2 } };
25882
25888
  const bytes9 = new TextEncoder().encode(JSON.stringify(request3.body));
25883
25889
  let digest2;
25884
25890
  try {
@@ -25891,7 +25897,7 @@ function attachAppTimerReceiver(port, options) {
25891
25897
  const previous = options.journal.read();
25892
25898
  previousChannel = previous;
25893
25899
  if (previous?.pending) throw error2("outcome_unknown", "timer_channel_reapproval_required");
25894
- pending = { grantId: options.grantId, kind: row2.kind, targetDigest: digest2, channelId: previous?.channelId ?? null, pending: "register" };
25900
+ pending = { grantId: options.grantId, kind: row3.kind, targetDigest: digest2, channelId: previous?.channelId ?? null, pending: "register" };
25895
25901
  } else if (verb === "unregisterChannel" && args.length === 1 && ["web_push", "fcm"].includes(args[0])) {
25896
25902
  if (!options.journal) throw error2("upgrade_required", "native_timer_channel_journal_unavailable");
25897
25903
  const previous = options.journal.read();
@@ -25997,7 +26003,7 @@ function attachAppTimerReceiver(port, options) {
25997
26003
  if (slot?.id === id4) slot = null;
25998
26004
  }
25999
26005
  });
26000
- slot = { id: id4, ctrl, work };
26006
+ slot = { id: id4, ctrl, work: work2 };
26001
26007
  };
26002
26008
  port.addEventListener("message", receive);
26003
26009
  port.start();
@@ -26155,7 +26161,7 @@ function attachAppLocalFacade(runtime2, channel, source, logWork) {
26155
26161
  native.send(frame);
26156
26162
  if (logWork && queuesLogWork(frame)) {
26157
26163
  try {
26158
- logWork();
26164
+ logWork(frame.get(2));
26159
26165
  } catch {
26160
26166
  }
26161
26167
  }
@@ -28513,11 +28519,11 @@ function packIntent(value2) {
28513
28519
  if (value2.version !== 2) fail12("recovery_required");
28514
28520
  return value2;
28515
28521
  }
28516
- function openDatabase(database, signal, current2) {
28522
+ function openDatabase(database2, signal, current2) {
28517
28523
  current2();
28518
28524
  if (!globalThis.indexedDB) fail12("recovery_required");
28519
28525
  return new Promise((resolve, reject) => {
28520
- const request3 = indexedDB.open(database, 1);
28526
+ const request3 = indexedDB.open(database2, 1);
28521
28527
  let abandoned = false;
28522
28528
  const stop = () => {
28523
28529
  abandoned = true;
@@ -28598,7 +28604,7 @@ var AppCollectionSetupJournal = class {
28598
28604
  fail12("fenced");
28599
28605
  }
28600
28606
  }
28601
- async exclusive(signal, work) {
28607
+ async exclusive(signal, work2) {
28602
28608
  this.check(signal);
28603
28609
  const previous = this.turn;
28604
28610
  let done;
@@ -28619,7 +28625,7 @@ var AppCollectionSetupJournal = class {
28619
28625
  this.check(signal);
28620
28626
  db = await openDatabase(this.owner.databaseName, signal, () => this.check(signal));
28621
28627
  this.check(signal);
28622
- const result2 = await work(db);
28628
+ const result2 = await work2(db);
28623
28629
  this.check(signal);
28624
28630
  return result2;
28625
28631
  } finally {
@@ -28942,11 +28948,11 @@ function url(value2) {
28942
28948
  return original;
28943
28949
  }
28944
28950
  function profile(value2) {
28945
- const row2 = object6(value2);
28946
- const result2 = { issuer: url(row2.issuer), subject: text4(row2.subject), name: text4(row2.name) };
28947
- if (row2.account_id !== void 0) {
28948
- if (typeof row2.account_id !== "string" || !uuid5.test(row2.account_id) || row2.account_id === "00000000-0000-0000-0000-000000000000") throw response();
28949
- result2.account_id = row2.account_id;
28951
+ const row3 = object6(value2);
28952
+ const result2 = { issuer: url(row3.issuer), subject: text4(row3.subject), name: text4(row3.name) };
28953
+ if (row3.account_id !== void 0) {
28954
+ if (typeof row3.account_id !== "string" || !uuid5.test(row3.account_id) || row3.account_id === "00000000-0000-0000-0000-000000000000") throw response();
28955
+ result2.account_id = row3.account_id;
28950
28956
  }
28951
28957
  return result2;
28952
28958
  }
@@ -28966,14 +28972,14 @@ function memberRows(value2) {
28966
28972
  }
28967
28973
  function collectionPeopleDto(resource2, value2) {
28968
28974
  try {
28969
- const row2 = object6(value2);
28975
+ const row3 = object6(value2);
28970
28976
  if (resource2 === "identity") {
28971
- const result2 = profile(row2);
28972
- if (row2.person_settings_url !== void 0) result2.person_settings_url = url(row2.person_settings_url);
28977
+ const result2 = profile(row3);
28978
+ if (row3.person_settings_url !== void 0) result2.person_settings_url = url(row3.person_settings_url);
28973
28979
  return result2;
28974
28980
  }
28975
28981
  if (resource2 !== "members") throw response();
28976
- const members = memberRows(row2.members).map((value3) => {
28982
+ const members = memberRows(row3.members).map((value3) => {
28977
28983
  const member = object6(value3), result2 = profile(member), role2 = member.role;
28978
28984
  if (role2 !== "owner" && role2 !== "editor" && role2 !== "viewer") throw response();
28979
28985
  return { ...result2, role: role2 };
@@ -29025,19 +29031,19 @@ function resourceReview(value2) {
29025
29031
  if (!plainObject(value2) || !keys(value2, "configuration,packs,staged") || !Array.isArray(value2.packs) || value2.packs.length > 16 || !Array.isArray(value2.configuration) || value2.configuration.length > 64 || !Array.isArray(value2.staged) || value2.staged.length > 1024) throw fail13("binding");
29026
29032
  const packs = value2.packs.map((pack) => {
29027
29033
  if (!plainObject(pack) || !keys(pack, "id,installedVersion,resources,status,version") || !text5(pack.id, 150) || !text5(pack.version, 128) || !["current", "install", "upgrade", "downgrade", "reconfigure", "conflict"].includes(pack.status) || !(pack.installedVersion === null || text5(pack.installedVersion, 128)) || !Array.isArray(pack.resources) || pack.resources.length > 1024) throw fail13("binding");
29028
- const resources = pack.resources.map((row2) => {
29029
- if (!plainObject(row2) || !keys(row2, "action,installed,kind,mode,target", ["currentDigest", "reason"]) || !text5(row2.target) || !text5(row2.kind, 64) || row2.mode !== "managed" && row2.mode !== "seed" || typeof row2.installed !== "boolean" || !["create", "update", "delete", "adopt", "preserve", "conflict"].includes(row2.action) || row2.currentDigest !== void 0 && (typeof row2.currentDigest !== "string" || !digestText.test(row2.currentDigest)) || row2.reason !== void 0 && !text5(row2.reason, 8192)) throw fail13("binding");
29030
- return Object.freeze({ ...row2 });
29034
+ const resources = pack.resources.map((row3) => {
29035
+ if (!plainObject(row3) || !keys(row3, "action,installed,kind,mode,target", ["currentDigest", "reason"]) || !text5(row3.target) || !text5(row3.kind, 64) || row3.mode !== "managed" && row3.mode !== "seed" || typeof row3.installed !== "boolean" || !["create", "update", "delete", "adopt", "preserve", "conflict"].includes(row3.action) || row3.currentDigest !== void 0 && (typeof row3.currentDigest !== "string" || !digestText.test(row3.currentDigest)) || row3.reason !== void 0 && !text5(row3.reason, 8192)) throw fail13("binding");
29036
+ return Object.freeze({ ...row3 });
29031
29037
  });
29032
29038
  return Object.freeze({ id: pack.id, version: pack.version, status: pack.status, installedVersion: pack.installedVersion, resources: Object.freeze(resources) });
29033
29039
  });
29034
- const configuration = value2.configuration.map((row2) => {
29035
- if (!plainObject(row2) || !keys(row2, "action,path,requirement", ["value"]) || !text5(row2.requirement, 256) || !text5(row2.path, 1024) || row2.action !== "add" && row2.action !== "conflict" || !(row2.value === void 0 || row2.value === null || ["string", "number", "boolean"].includes(typeof row2.value))) throw fail13("binding");
29036
- return Object.freeze({ ...row2 });
29040
+ const configuration = value2.configuration.map((row3) => {
29041
+ if (!plainObject(row3) || !keys(row3, "action,path,requirement", ["value"]) || !text5(row3.requirement, 256) || !text5(row3.path, 1024) || row3.action !== "add" && row3.action !== "conflict" || !(row3.value === void 0 || row3.value === null || ["string", "number", "boolean"].includes(typeof row3.value))) throw fail13("binding");
29042
+ return Object.freeze({ ...row3 });
29037
29043
  });
29038
- const staged = value2.staged.map((row2) => {
29039
- if (!plainObject(row2) || !keys(row2, "document,path") || !text5(row2.path) || typeof row2.document !== "string" || row2.document.length > 512 * 1024) throw fail13("binding");
29040
- return Object.freeze({ path: row2.path, document: row2.document });
29044
+ const staged = value2.staged.map((row3) => {
29045
+ if (!plainObject(row3) || !keys(row3, "document,path") || !text5(row3.path) || typeof row3.document !== "string" || row3.document.length > 512 * 1024) throw fail13("binding");
29046
+ return Object.freeze({ path: row3.path, document: row3.document });
29041
29047
  });
29042
29048
  return Object.freeze({ packs: Object.freeze(packs), configuration: Object.freeze(configuration), staged: Object.freeze(staged) });
29043
29049
  }
@@ -29078,7 +29084,7 @@ var journalAction = (action) => action === "load" || action === "prepare" || act
29078
29084
  function attachAppCollectionControl(port, source, options) {
29079
29085
  options = Object.freeze({ ...options });
29080
29086
  const original = pin2(source);
29081
- let stopped = false, work = null, closing = null, lastId = 0;
29087
+ let stopped = false, work2 = null, closing = null, lastId = 0;
29082
29088
  const me = {}, turn = options.setupTurn;
29083
29089
  const release = () => {
29084
29090
  if (turn?.owner === me) turn.owner = null;
@@ -29145,7 +29151,7 @@ function attachAppCollectionControl(port, source, options) {
29145
29151
  reply(id4, "result", result2);
29146
29152
  return;
29147
29153
  }
29148
- if (work) {
29154
+ if (work2) {
29149
29155
  reply(id4, "error", "busy");
29150
29156
  return;
29151
29157
  }
@@ -29237,7 +29243,7 @@ function attachAppCollectionControl(port, source, options) {
29237
29243
  current2();
29238
29244
  reply(id4, "result", result2);
29239
29245
  };
29240
- const pending = work = Promise.resolve().then(operation).catch((error4) => {
29246
+ const pending = work2 = Promise.resolve().then(operation).catch((error4) => {
29241
29247
  if (stopped || options.signal.aborted) return;
29242
29248
  try {
29243
29249
  const resource2 = peopleResource(action);
@@ -29247,7 +29253,7 @@ function attachAppCollectionControl(port, source, options) {
29247
29253
  }
29248
29254
  });
29249
29255
  void pending.finally(() => {
29250
- if (work === pending) work = null;
29256
+ if (work2 === pending) work2 = null;
29251
29257
  }).catch(() => {
29252
29258
  });
29253
29259
  } catch {
@@ -29259,7 +29265,7 @@ function attachAppCollectionControl(port, source, options) {
29259
29265
  if (closing) return closing;
29260
29266
  closing = Promise.resolve().then(async () => {
29261
29267
  stop();
29262
- await work;
29268
+ await work2;
29263
29269
  });
29264
29270
  return closing;
29265
29271
  } };
@@ -29431,6 +29437,37 @@ var AppWasmRuntime = class _AppWasmRuntime extends WasmRuntime {
29431
29437
  c.kemSecretKey?.fill(0);
29432
29438
  }
29433
29439
  }
29440
+ /** One attempt: open this app's unsynced collection, a local-only replica
29441
+ * with no log, no trust pins and no stored keys (`rt_app_open_local`). */
29442
+ openUnsyncedConsuming(c) {
29443
+ let encoded = null;
29444
+ try {
29445
+ if (this.attempted || this.deviceAttempted || !this.sql || this.sql.needsRecovery || typeof this.app.rt_app_open_local !== "function") throw bad6();
29446
+ this.attempted = true;
29447
+ if (!Number.isSafeInteger(c.sqliteVersion) || c.sqliteVersion < 0 || c.sqliteVersion > 4294967295) throw bad6();
29448
+ const opened = c.opened === "fresh" ? 0 : c.opened === "existing" ? 1 : c.opened === "unclean" ? 2 : -1;
29449
+ if (opened < 0) throw bad6();
29450
+ encoded = encodeSecret(/* @__PURE__ */ new Map([
29451
+ [0, 1],
29452
+ [1, uuidToBytes(c.collection)],
29453
+ [2, uuidToBytes(c.replicaId)],
29454
+ [3, uuidToBytes(c.deviceId)],
29455
+ [12, opened],
29456
+ [13, c.sqliteVersion]
29457
+ ]));
29458
+ const error4 = this.guard(() => this.takeOut(this.app.rt_app_open_local(...this.put(encoded))));
29459
+ if (error4.length) throw bad6();
29460
+ this.collection = c.collection;
29461
+ this.deviceId = c.deviceId;
29462
+ this.active = true;
29463
+ this.markOpened();
29464
+ } catch {
29465
+ this.fail();
29466
+ throw bad6();
29467
+ } finally {
29468
+ encoded?.fill(0);
29469
+ }
29470
+ }
29434
29471
  tick() {
29435
29472
  if (this.active && !this.retired) this.guard(() => super.tick());
29436
29473
  }
@@ -29605,10 +29642,10 @@ var AppWasmRuntime = class _AppWasmRuntime extends WasmRuntime {
29605
29642
  const out = this.guard(() => this.takeOut(this.app.rt_app_device_open(...this.put(encoded))));
29606
29643
  if (!out.length || !this.deviceCurrent()) throw bad6();
29607
29644
  const result2 = this.guard(() => {
29608
- const { m: m2, identity: identity2 } = this.publicResult(out);
29645
+ const { m: m2, identity: identity3 } = this.publicResult(out);
29609
29646
  const wrapped = m2.get(3);
29610
29647
  if (!(wrapped instanceof Uint8Array) || !wrapped.length || wrapped.length > 1024) throw bad6();
29611
- return { ...identity2, envelope: new Uint8Array(wrapped) };
29648
+ return { ...identity3, envelope: new Uint8Array(wrapped) };
29612
29649
  });
29613
29650
  this.publicIdentity = { signPublicKey: new Uint8Array(result2.signPublicKey), kemPublicKey: new Uint8Array(result2.kemPublicKey), noisePublicKey: new Uint8Array(result2.noisePublicKey) };
29614
29651
  this.noiseEnvelope = new Uint8Array(result2.envelope);
@@ -29852,10 +29889,10 @@ var AppWasmRuntime = class _AppWasmRuntime extends WasmRuntime {
29852
29889
  throw bad6();
29853
29890
  }
29854
29891
  if (!out.length) throw bad6();
29855
- const { m: m2, identity: identity2 } = this.guard(() => this.publicResult(out));
29892
+ const { m: m2, identity: identity3 } = this.guard(() => this.publicResult(out));
29856
29893
  const signature = m2.get(3);
29857
- if (!(signature instanceof Uint8Array) || signature.length !== 64 || ["signPublicKey", "kemPublicKey", "noisePublicKey"].some((k) => identity2[k].some((b, i) => b !== this.publicIdentity[k][i]))) throw bad6();
29858
- return Object.freeze({ ...identity2, signature: new Uint8Array(signature) });
29894
+ if (!(signature instanceof Uint8Array) || signature.length !== 64 || ["signPublicKey", "kemPublicKey", "noisePublicKey"].some((k) => identity3[k].some((b, i) => b !== this.publicIdentity[k][i]))) throw bad6();
29895
+ return Object.freeze({ ...identity3, signature: new Uint8Array(signature) });
29859
29896
  } catch {
29860
29897
  this.retireLog();
29861
29898
  throw bad6();
@@ -30619,16 +30656,16 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
30619
30656
  );
30620
30657
  }
30621
30658
  const authority = this.authority;
30622
- const work = (async () => {
30659
+ const work2 = (async () => {
30623
30660
  await authority.accessToken(Object.freeze({ signal }));
30624
30661
  this.checkCollection(signal);
30625
30662
  this.pump = this.native.bindLogTransport(authority.logTransport());
30626
30663
  })();
30627
- this.logWork = work;
30664
+ this.logWork = work2;
30628
30665
  try {
30629
- await work;
30666
+ await work2;
30630
30667
  } finally {
30631
- if (this.logWork === work) this.logWork = null;
30668
+ if (this.logWork === work2) this.logWork = null;
30632
30669
  }
30633
30670
  }
30634
30671
  /** Read-only metadata for this adopted collection. Uses the already pinned
@@ -30832,13 +30869,13 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
30832
30869
  return this.readWork.then(() => {
30833
30870
  this.checkCollection(signal);
30834
30871
  });
30835
- const work = this.verifyRead(signal, timeoutMs, cold, options.onProgress);
30836
- this.readWork = work;
30837
- void work.finally(() => {
30838
- if (this.readWork === work) this.readWork = null;
30872
+ const work2 = this.verifyRead(signal, timeoutMs, cold, options.onProgress);
30873
+ this.readWork = work2;
30874
+ void work2.finally(() => {
30875
+ if (this.readWork === work2) this.readWork = null;
30839
30876
  }).catch(() => {
30840
30877
  });
30841
- return work;
30878
+ return work2;
30842
30879
  }
30843
30880
  async verifyRead(signal, timeoutMs, cold, onProgress) {
30844
30881
  const ctrl = new AbortController(), abort = () => ctrl.abort();
@@ -31155,7 +31192,7 @@ var AppCollectionJoinLedger = class {
31155
31192
  };
31156
31193
  });
31157
31194
  }
31158
- async change(change2, signal) {
31195
+ async change(change3, signal) {
31159
31196
  this.check(signal);
31160
31197
  if (this.busy) throw fail15("busy");
31161
31198
  this.busy = true;
@@ -31195,13 +31232,13 @@ var AppCollectionJoinLedger = class {
31195
31232
  this.check(signal);
31196
31233
  const previous = request3.result;
31197
31234
  if (previous === void 0) {
31198
- if (change2 !== "prepare") throw fail15();
31235
+ if (change3 !== "prepare") throw fail15();
31199
31236
  records2.add({ version: 1, ...this.options.scope, phase: "attempted" }, this.key);
31200
31237
  result2 = { outcomeMode: "fresh", phase: "attempted" };
31201
31238
  } else {
31202
31239
  const marker = this.marker(previous);
31203
- if (change2 === "complete" && marker.phase !== "completed") records2.put({ ...marker, phase: "completed" }, this.key);
31204
- result2 = { outcomeMode: "existing", phase: change2 === "complete" ? "completed" : marker.phase };
31240
+ if (change3 === "complete" && marker.phase !== "completed") records2.put({ ...marker, phase: "completed" }, this.key);
31241
+ result2 = { outcomeMode: "existing", phase: change3 === "complete" ? "completed" : marker.phase };
31205
31242
  }
31206
31243
  } catch (error4) {
31207
31244
  reason = error4;
@@ -31233,19 +31270,19 @@ var AppCollectionJoinLedger = class {
31233
31270
  async markSynced(options) {
31234
31271
  await this.synced("mark", options.signal);
31235
31272
  }
31236
- async synced(change2, signal) {
31273
+ async synced(change3, signal) {
31237
31274
  let db = null;
31238
31275
  try {
31239
31276
  db = await this.open(signal);
31240
31277
  this.check(signal);
31241
31278
  const key3 = JSON.stringify([this.options.scope.account, this.options.scope.installation, this.options.scope.collection, "synced"]);
31242
31279
  return await new Promise((resolve) => {
31243
- const tx = db.transaction(STORE7, change2 === "mark" ? "readwrite" : "readonly");
31280
+ const tx = db.transaction(STORE7, change3 === "mark" ? "readwrite" : "readonly");
31244
31281
  let found = false;
31245
31282
  tx.onabort = tx.onerror = () => resolve(false);
31246
- tx.oncomplete = () => resolve(change2 === "mark" || found);
31283
+ tx.oncomplete = () => resolve(change3 === "mark" || found);
31247
31284
  const records2 = tx.objectStore(STORE7);
31248
- if (change2 === "mark") records2.put({ version: 1, synced: true }, key3);
31285
+ if (change3 === "mark") records2.put({ version: 1, synced: true }, key3);
31249
31286
  else {
31250
31287
  const request3 = records2.get(key3);
31251
31288
  request3.onsuccess = () => {
@@ -31332,9 +31369,9 @@ var lower = (id4) => {
31332
31369
  uuidToBytes(id4);
31333
31370
  return id4.toLowerCase();
31334
31371
  };
31335
- function bounded2(work, fallback, ms = SIGN_OUT_ERASE_TIMEOUT_MS) {
31372
+ function bounded2(work2, fallback, ms = SIGN_OUT_ERASE_TIMEOUT_MS) {
31336
31373
  let timer;
31337
- return Promise.race([work.catch(() => fallback), new Promise((resolve) => {
31374
+ return Promise.race([work2.catch(() => fallback), new Promise((resolve) => {
31338
31375
  timer = setTimeout(() => resolve(fallback), ms);
31339
31376
  })]).finally(() => clearTimeout(timer));
31340
31377
  }
@@ -31583,9 +31620,9 @@ var AppWebSignInSession = class {
31583
31620
  }
31584
31621
  }
31585
31622
  /** Background approval read; a sign-in gesture may wait for it (authorize). */
31586
- poll(work) {
31623
+ poll(work2) {
31587
31624
  if (this.pending) return;
31588
- void this.operation(work).catch(() => {
31625
+ void this.operation(work2).catch(() => {
31589
31626
  });
31590
31627
  this.polling = this.pending;
31591
31628
  }
@@ -31594,14 +31631,14 @@ var AppWebSignInSession = class {
31594
31631
  this.timer = null;
31595
31632
  }
31596
31633
  /** `contained`: a failure rejects the caller only; the session stays as it was. */
31597
- operation(work, contained = false) {
31634
+ operation(work2, contained = false) {
31598
31635
  this.check();
31599
31636
  if (this.pending) return Promise.reject(failure("busy"));
31600
31637
  this.foregroundRead = null;
31601
31638
  this.stopPolling();
31602
31639
  const workResult = Promise.resolve().then(() => {
31603
31640
  this.check();
31604
- return work();
31641
+ return work2();
31605
31642
  });
31606
31643
  const pending = workResult.catch(async (error4) => {
31607
31644
  if (!contained && !this.closing && !this.options.signal.aborted) {
@@ -33745,10 +33782,10 @@ function createAppWebCollectionAdapter(source) {
33745
33782
  if (!current2()) return;
33746
33783
  const wake = foreground && Date.now() >= wakeAt;
33747
33784
  if (wake) wakeAt = Number.POSITIVE_INFINITY;
33748
- const work = driving = wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
33749
- void work.then(
33785
+ const work2 = driving = wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
33786
+ void work2.then(
33750
33787
  () => {
33751
- if (driving === work) driving = null;
33788
+ if (driving === work2) driving = null;
33752
33789
  failures = 0;
33753
33790
  turned = true;
33754
33791
  if (current2()) observe();
@@ -33758,7 +33795,7 @@ function createAppWebCollectionAdapter(source) {
33758
33795
  // A failed turn (offline token mint, transient fence) backs off and
33759
33796
  // retries; it never silently ends remote-change delivery for the session.
33760
33797
  (error4) => {
33761
- if (driving === work) driving = null;
33798
+ if (driving === work2) driving = null;
33762
33799
  failures++;
33763
33800
  if (current2()) observe();
33764
33801
  turnFailed(error4);
@@ -34284,7 +34321,13 @@ function applicationNativeAssets(declaration, packaged, handoff, fetcher) {
34284
34321
  allowLoopbackHttp: environment.allowLoopbackHttp,
34285
34322
  allowNativeLoopbackHttp: environment.allowNativeLoopbackHttp
34286
34323
  });
34287
- return Object.freeze({ release, environment, loadRuntime, collection });
34324
+ const unsynced = Object.freeze({
34325
+ databaseName: declaration.storage.unsyncedDatabaseName,
34326
+ setupJournal: Object.freeze({ ...collection.setupJournal, databaseName: `${declaration.storage.unsyncedDatabaseName}.setup` }),
34327
+ sql,
34328
+ loadRuntime
34329
+ });
34330
+ return Object.freeze({ release, environment, loadRuntime, collection, unsynced });
34288
34331
  }
34289
34332
 
34290
34333
  // src/application/worker-session.ts
@@ -34324,7 +34367,464 @@ function applicationWorkerSession(declaration, packaged, host) {
34324
34367
  // account has none to pick); the user still decides in Connect.
34325
34368
  freshRequestedCreateCollections: true
34326
34369
  });
34327
- return Object.freeze({ session, compiled, collection });
34370
+ const unsynced = Object.freeze({ ...assets.unsynced, locks });
34371
+ return Object.freeze({ session, compiled, collection, unsynced, displayName: binding.name });
34372
+ }
34373
+
34374
+ // src/app-host/unsynced-collection.ts
34375
+ var STORE8 = "unsynced";
34376
+ var KEY6 = "current";
34377
+ var WORK = "work";
34378
+ var RETIRED = "retired.";
34379
+ var MOVED = "moved.";
34380
+ var UUID7 = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/;
34381
+ var MAX_TABS = 32;
34382
+ var AppUnsyncedCollectionError = class extends Error {
34383
+ constructor(reason) {
34384
+ super(`app unsynced collection: ${reason}`);
34385
+ this.reason = reason;
34386
+ this.name = "AppUnsyncedCollectionError";
34387
+ }
34388
+ reason;
34389
+ };
34390
+ var fail18 = (reason = "unavailable") => new AppUnsyncedCollectionError(reason);
34391
+ function identity2(value2) {
34392
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2) || Object.keys(value2).sort().join() !== "account,collection,device,installation,replica,version") throw fail18();
34393
+ const row3 = value2;
34394
+ const ids = [row3.account, row3.installation, row3.collection, row3.replica, row3.device];
34395
+ if (row3.version !== 1 || ids.some((id4) => typeof id4 !== "string" || !UUID7.test(id4)) || new Set(ids).size !== ids.length) throw fail18();
34396
+ return Object.freeze({ ...row3 });
34397
+ }
34398
+ function work(value2) {
34399
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2) || Object.keys(value2).sort().join() !== "since,version") throw fail18();
34400
+ const row3 = value2;
34401
+ if (row3.version !== 1 || !Number.isSafeInteger(row3.since) || row3.since < 0) throw fail18();
34402
+ return Object.freeze({ version: 1, since: row3.since });
34403
+ }
34404
+ function retired(value2) {
34405
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2) || Object.keys(value2).sort().join() !== "account,destination,identity,retiredAt,version") throw fail18();
34406
+ const row3 = value2;
34407
+ if (row3.version !== 1 || [row3.destination, row3.account].some((id4) => typeof id4 !== "string" || !UUID7.test(id4)) || !Number.isSafeInteger(row3.retiredAt) || row3.retiredAt < 0) throw fail18();
34408
+ return Object.freeze({ version: 1, identity: identity2(row3.identity), destination: row3.destination, account: row3.account, retiredAt: row3.retiredAt });
34409
+ }
34410
+ function database(name2, signal) {
34411
+ signal.throwIfAborted();
34412
+ if (!globalThis.indexedDB) throw fail18();
34413
+ return new Promise((resolve, reject) => {
34414
+ const request3 = indexedDB.open(name2, 1);
34415
+ let abandoned = false;
34416
+ const finish = () => signal.removeEventListener("abort", stop);
34417
+ const stop = () => {
34418
+ abandoned = true;
34419
+ finish();
34420
+ try {
34421
+ request3.transaction?.abort();
34422
+ } catch {
34423
+ }
34424
+ reject(fail18("fenced"));
34425
+ };
34426
+ signal.addEventListener("abort", stop, { once: true });
34427
+ request3.onblocked = request3.onerror = stop;
34428
+ request3.onupgradeneeded = () => {
34429
+ try {
34430
+ if (abandoned || request3.result.objectStoreNames.length) throw fail18();
34431
+ request3.result.createObjectStore(STORE8);
34432
+ } catch {
34433
+ try {
34434
+ request3.transaction?.abort();
34435
+ } catch {
34436
+ }
34437
+ }
34438
+ };
34439
+ request3.onsuccess = () => {
34440
+ finish();
34441
+ if (abandoned || request3.result.objectStoreNames.length !== 1 || !request3.result.objectStoreNames.contains(STORE8)) {
34442
+ request3.result.close();
34443
+ reject(fail18());
34444
+ return;
34445
+ }
34446
+ resolve(request3.result);
34447
+ };
34448
+ });
34449
+ }
34450
+ async function row2(databaseName, key3, parse2, make, signal) {
34451
+ const db = await database(databaseName, signal);
34452
+ try {
34453
+ return await new Promise((resolve, reject) => {
34454
+ const tx = db.transaction(STORE8, make ? "readwrite" : "readonly", { durability: "strict" });
34455
+ let result2 = null, problem3 = null;
34456
+ tx.onabort = tx.onerror = () => reject(problem3 ?? fail18());
34457
+ tx.oncomplete = () => resolve(result2);
34458
+ const rows2 = tx.objectStore(STORE8), read = rows2.get(key3);
34459
+ read.onsuccess = () => {
34460
+ try {
34461
+ if (read.result !== void 0) {
34462
+ result2 = parse2(read.result);
34463
+ return;
34464
+ }
34465
+ if (!make) return;
34466
+ const made = parse2(make());
34467
+ rows2.add(made, key3);
34468
+ result2 = made;
34469
+ } catch (error4) {
34470
+ problem3 = error4;
34471
+ tx.abort();
34472
+ }
34473
+ };
34474
+ });
34475
+ } finally {
34476
+ db.close();
34477
+ }
34478
+ }
34479
+ function appUnsyncedIdentity(databaseName, create, signal) {
34480
+ return row2(databaseName, KEY6, identity2, create ? () => ({
34481
+ version: 1,
34482
+ account: crypto.randomUUID(),
34483
+ installation: crypto.randomUUID(),
34484
+ collection: crypto.randomUUID(),
34485
+ replica: crypto.randomUUID(),
34486
+ device: crypto.randomUUID()
34487
+ }) : null, signal);
34488
+ }
34489
+ async function appUnsyncedWork(databaseName, signal) {
34490
+ if (await exists(databaseName, signal) !== true) return null;
34491
+ return row2(databaseName, WORK, work, null, signal);
34492
+ }
34493
+ async function exists(databaseName, signal) {
34494
+ const list3 = globalThis.indexedDB?.databases;
34495
+ if (typeof list3 !== "function") return null;
34496
+ const names = await list3.call(globalThis.indexedDB);
34497
+ signal.throwIfAborted();
34498
+ return names.some((entry2) => entry2.name === databaseName);
34499
+ }
34500
+ async function change2(databaseName, signal, work2) {
34501
+ const db = await database(databaseName, signal);
34502
+ try {
34503
+ return await new Promise((resolve, reject) => {
34504
+ const tx = db.transaction(STORE8, "readwrite", { durability: "strict" });
34505
+ let result2, problem3 = null;
34506
+ tx.onabort = tx.onerror = () => reject(problem3 ?? fail18());
34507
+ tx.oncomplete = () => resolve(result2);
34508
+ work2(tx.objectStore(STORE8), (value2) => {
34509
+ result2 = value2;
34510
+ }, (error4) => {
34511
+ problem3 = error4;
34512
+ tx.abort();
34513
+ });
34514
+ });
34515
+ } finally {
34516
+ db.close();
34517
+ }
34518
+ }
34519
+ function retireAppUnsynced(databaseName, source, destination, account, signal) {
34520
+ return change2(databaseName, signal, (rows2, done, refuse) => {
34521
+ const read = rows2.get(KEY6);
34522
+ read.onsuccess = () => {
34523
+ try {
34524
+ if (read.result === void 0 || identity2(read.result).collection !== source) {
34525
+ done(false);
34526
+ return;
34527
+ }
34528
+ const value2 = retired({ version: 1, identity: read.result, destination, account, retiredAt: Date.now() });
34529
+ rows2.put(value2, RETIRED + source);
34530
+ rows2.delete(KEY6);
34531
+ rows2.delete(WORK);
34532
+ rows2.delete(MOVED + source);
34533
+ done(true);
34534
+ } catch (error4) {
34535
+ refuse(error4);
34536
+ }
34537
+ };
34538
+ });
34539
+ }
34540
+ function discardAppUnsynced(databaseName, source, signal) {
34541
+ return change2(databaseName, signal, (rows2, done, refuse) => {
34542
+ const read = rows2.get(KEY6);
34543
+ read.onsuccess = () => {
34544
+ try {
34545
+ if (read.result === void 0) {
34546
+ done(null);
34547
+ return;
34548
+ }
34549
+ const found = identity2(read.result);
34550
+ if (found.collection !== source) {
34551
+ done(null);
34552
+ return;
34553
+ }
34554
+ rows2.delete(KEY6);
34555
+ rows2.delete(WORK);
34556
+ rows2.delete(MOVED + source);
34557
+ done(found);
34558
+ } catch (error4) {
34559
+ refuse(error4);
34560
+ }
34561
+ };
34562
+ });
34563
+ }
34564
+ async function appUnsyncedRetired(databaseName, signal) {
34565
+ if (await exists(databaseName, signal) === false) return [];
34566
+ return change2(databaseName, signal, (rows2, done, refuse) => {
34567
+ const read = rows2.getAll(IDBKeyRange.bound(RETIRED, RETIRED + "\uFFFF"));
34568
+ read.onsuccess = () => {
34569
+ try {
34570
+ done(Object.freeze(read.result.map(retired)));
34571
+ } catch (error4) {
34572
+ refuse(error4);
34573
+ }
34574
+ };
34575
+ });
34576
+ }
34577
+ function forgetAppUnsyncedRetired(databaseName, collection, signal) {
34578
+ return change2(databaseName, signal, (rows2, done) => {
34579
+ rows2.delete(RETIRED + collection);
34580
+ done();
34581
+ });
34582
+ }
34583
+ var APP_UNSYNCED_RECOVERY_MS = 30 * 24 * 60 * 60 * 1e3;
34584
+ async function pruneAppUnsyncedRetired(databaseName, drop, signal) {
34585
+ let removed = 0;
34586
+ try {
34587
+ for (const copy2 of await appUnsyncedRetired(databaseName, signal)) {
34588
+ if (!drop(copy2) || !await eraseAppUnsyncedStorage(copy2.identity)) continue;
34589
+ await forgetAppUnsyncedRetired(databaseName, copy2.identity.collection, signal);
34590
+ removed++;
34591
+ }
34592
+ } catch {
34593
+ }
34594
+ return removed;
34595
+ }
34596
+ async function eraseAppUnsyncedStorage(id4) {
34597
+ const storage = globalThis.navigator?.storage;
34598
+ if (typeof storage?.getDirectory !== "function") return true;
34599
+ try {
34600
+ const root = await storage.getDirectory();
34601
+ await root.removeEntry(appPoolNames({ account: id4.account, installation: id4.installation, collection: id4.collection }).directory, { recursive: true });
34602
+ return true;
34603
+ } catch (error4) {
34604
+ return error4 instanceof DOMException && error4.name === "NotFoundError";
34605
+ }
34606
+ }
34607
+ async function openAppUnsyncedCollection(options, displayName, parent) {
34608
+ if (typeof options.loadRuntime !== "function" || typeof displayName !== "string" || !displayName.trim()) throw fail18("binding");
34609
+ const openSql = createAppCollectionSql(options.sql);
34610
+ const id4 = await appUnsyncedIdentity(options.databaseName, true, parent);
34611
+ if (!id4) throw fail18();
34612
+ const scope = Object.freeze({ account: id4.account, installation: id4.installation, collection: id4.collection });
34613
+ const life = new AbortController(), signal = AbortSignal.any([parent, life.signal]);
34614
+ let closed = false, closing = null, lease = null;
34615
+ let runtime2 = null, sqlClose = null, fence = null;
34616
+ let journal = null, ledger = null;
34617
+ let assessment = "setup_required";
34618
+ let saved = null, marking = false, writes = 0;
34619
+ let mode = "approved-new";
34620
+ const generation = crypto.randomUUID(), setupTurn = { owner: null };
34621
+ const listeners = /* @__PURE__ */ new Set();
34622
+ const tabs = /* @__PURE__ */ new Map();
34623
+ const current2 = () => !closed && !signal.aborted;
34624
+ const changed = () => {
34625
+ for (const listener of [...listeners]) try {
34626
+ listener();
34627
+ } catch {
34628
+ }
34629
+ };
34630
+ let ticking = false;
34631
+ const tick = () => {
34632
+ if (ticking || !current2()) return;
34633
+ ticking = true;
34634
+ queueMicrotask(() => {
34635
+ ticking = false;
34636
+ try {
34637
+ if (current2()) runtime2?.tick();
34638
+ } catch {
34639
+ void close().catch(() => {
34640
+ });
34641
+ }
34642
+ });
34643
+ };
34644
+ const submitted = (method) => {
34645
+ tick();
34646
+ if (method === "submit") writes++;
34647
+ if (method !== "submit" || saved || marking || !current2()) return;
34648
+ marking = true;
34649
+ void row2(options.databaseName, WORK, work, () => ({ version: 1, since: Date.now() }), signal).then((value2) => {
34650
+ if (value2 && current2()) {
34651
+ saved = value2;
34652
+ changed();
34653
+ }
34654
+ }).catch(() => {
34655
+ }).finally(() => {
34656
+ marking = false;
34657
+ });
34658
+ };
34659
+ const drop = (handoff) => {
34660
+ const tab = tabs.get(handoff.dataPort);
34661
+ if (!tab) return;
34662
+ tabs.delete(handoff.dataPort);
34663
+ try {
34664
+ tab.facade.close();
34665
+ } catch {
34666
+ }
34667
+ void tab.control.close().catch(() => {
34668
+ });
34669
+ for (const port of [tab.data.port1, tab.command.port1]) try {
34670
+ port.close();
34671
+ } catch {
34672
+ }
34673
+ };
34674
+ const close = () => closing ??= (async () => {
34675
+ closed = true;
34676
+ life.abort();
34677
+ changed();
34678
+ let failed = false;
34679
+ for (const tab of [...tabs.values()]) {
34680
+ try {
34681
+ await tab.control.close();
34682
+ } catch {
34683
+ failed = true;
34684
+ }
34685
+ try {
34686
+ tab.facade.close();
34687
+ } catch {
34688
+ failed = true;
34689
+ }
34690
+ try {
34691
+ tab.data.port1.postMessage([1, JSON.stringify([scope.account, scope.installation, scope.collection]), "close"]);
34692
+ } catch {
34693
+ failed = true;
34694
+ }
34695
+ for (const port of [tab.data.port1, tab.data.port2, tab.command.port1, tab.command.port2]) try {
34696
+ port.close();
34697
+ } catch {
34698
+ failed = true;
34699
+ }
34700
+ }
34701
+ tabs.clear();
34702
+ try {
34703
+ journal?.close();
34704
+ } catch {
34705
+ failed = true;
34706
+ }
34707
+ try {
34708
+ const clean2 = await runtime2?.close();
34709
+ if (runtime2 && clean2 !== true) fence?.();
34710
+ } catch {
34711
+ failed = true;
34712
+ fence?.();
34713
+ }
34714
+ try {
34715
+ await sqlClose?.();
34716
+ } catch {
34717
+ failed = true;
34718
+ }
34719
+ try {
34720
+ await lease?.release();
34721
+ } catch {
34722
+ failed = true;
34723
+ }
34724
+ listeners.clear();
34725
+ if (failed) throw fail18();
34726
+ })();
34727
+ try {
34728
+ lease = await acquireAppReplicaLease(options.locks, scope, signal);
34729
+ journal = new AppCollectionSetupJournal({ ...options.setupJournal, scope, isCurrent: current2 });
34730
+ const artifact = await options.loadRuntime({ signal });
34731
+ if (!current2()) throw fail18("fenced");
34732
+ const sql = await openSql({ scope, signal });
34733
+ sqlClose = () => sql.close();
34734
+ fence = () => sql.sql.fence();
34735
+ ledger = sql.ledger ?? null;
34736
+ if (!current2()) throw fail18("fenced");
34737
+ runtime2 = await AppWasmRuntime.create(new Uint8Array(artifact), sql.sql);
34738
+ if (!current2()) throw fail18("fenced");
34739
+ runtime2.openUnsyncedConsuming({ collection: id4.collection, replicaId: id4.replica, deviceId: id4.device, opened: sql.opened, sqliteVersion: sql.sqliteVersion });
34740
+ const intent = await journal.load(signal);
34741
+ if (!current2()) throw fail18("fenced");
34742
+ saved = await row2(options.databaseName, WORK, work, null, signal);
34743
+ if (!current2()) throw fail18("fenced");
34744
+ if (intent && intent.version !== 1 && (intent.phase === "attempted" || intent.phase === "confirmed")) mode = "reconcile-original";
34745
+ const native = runtime2, local = Object.freeze({ ...scope, isCurrent: current2 });
34746
+ const attach = () => {
34747
+ if (!current2() || tabs.size >= MAX_TABS) return null;
34748
+ const data = new MessageChannel(), command = new MessageChannel();
34749
+ let control = null, facade = null;
34750
+ try {
34751
+ facade = attachAppLocalFacade(native, data.port1, local, submitted);
34752
+ let value2 = null;
34753
+ control = attachAppCollectionControl(command.port1, { scope, generation }, {
34754
+ journal,
34755
+ resourceSetup: facade.resourceSetup,
34756
+ signal,
34757
+ isCurrent: current2,
34758
+ ledger: () => current2() ? ledger : null,
34759
+ setupTurn,
34760
+ assess(state) {
34761
+ if (!current2()) return;
34762
+ if (assessment !== state) {
34763
+ assessment = state;
34764
+ changed();
34765
+ }
34766
+ },
34767
+ // Nothing syncs, so visibility changes nothing here.
34768
+ foreground() {
34769
+ },
34770
+ onProtocolFailure() {
34771
+ if (value2) drop(value2);
34772
+ }
34773
+ });
34774
+ value2 = Object.freeze({
34775
+ schema: "mdbn-app-collection/1",
34776
+ scope,
34777
+ generation,
34778
+ displayName,
34779
+ setupMode: mode === "reconcile-original" ? mode : assessment === "ready" ? "inspect" : "approved-new",
34780
+ dataPort: data.port2,
34781
+ controlPort: command.port2,
34782
+ people: true
34783
+ });
34784
+ tabs.set(data.port2, { data, command, facade, control });
34785
+ return value2;
34786
+ } catch {
34787
+ try {
34788
+ facade?.close();
34789
+ } catch {
34790
+ }
34791
+ void control?.close().catch(() => {
34792
+ });
34793
+ for (const port of [data.port1, data.port2, command.port1, command.port2]) port.close();
34794
+ return null;
34795
+ }
34796
+ };
34797
+ return Object.freeze({
34798
+ scope,
34799
+ generation,
34800
+ isCurrent: current2,
34801
+ setForeground() {
34802
+ },
34803
+ attach,
34804
+ detach: drop,
34805
+ close,
34806
+ get state() {
34807
+ return current2() ? assessment : "setup_required";
34808
+ },
34809
+ get work() {
34810
+ return saved;
34811
+ },
34812
+ get writes() {
34813
+ return writes;
34814
+ },
34815
+ subscribe(listener) {
34816
+ if (!current2()) throw fail18("fenced");
34817
+ listeners.add(listener);
34818
+ return () => {
34819
+ listeners.delete(listener);
34820
+ };
34821
+ }
34822
+ });
34823
+ } catch (error4) {
34824
+ await close().catch(() => {
34825
+ });
34826
+ throw error4;
34827
+ }
34328
34828
  }
34329
34829
 
34330
34830
  // src/application/worker-host.ts
@@ -34353,6 +34853,14 @@ var ApplicationWorkerHost = class {
34353
34853
  /** The latest proxy portal's person-closed report (page `approvalClosed`). */
34354
34854
  approval = null;
34355
34855
  unsubscribe = null;
34856
+ /** The app's unsynced collection (work before connecting): open while a tab
34857
+ * shows it, beside the session's collections. Never listed or remembered. */
34858
+ unsyncedOptions;
34859
+ unsyncedName;
34860
+ unsynced = null;
34861
+ unsyncedProblem = null;
34862
+ /** Whether the unsynced collection holds work, shown to every tab. */
34863
+ unsyncedWork = null;
34356
34864
  constructor(port, binding, declaration, packaged, locks, storageNamespace, fetcher) {
34357
34865
  if (!applicationWorkerBinding(binding)) throw new ApplicationWorkerError("application_worker_binding");
34358
34866
  const composed = applicationWorkerSession(declaration, packaged, {
@@ -34364,20 +34872,179 @@ var ApplicationWorkerHost = class {
34364
34872
  ...fetcher ? { fetch: fetcher } : {}
34365
34873
  });
34366
34874
  this.session = composed.session;
34875
+ this.unsyncedOptions = composed.unsynced;
34876
+ this.unsyncedName = composed.displayName;
34367
34877
  this.primary = this.client(port, binding, true);
34368
34878
  this.unsubscribe = this.session.subscribe(() => {
34369
34879
  this.applyForeground();
34370
34880
  if (this.stopping) return;
34371
- if (this.session.getSnapshot().status === "signed_out") for (const client of this.clients.values()) this.leave(client);
34881
+ if (this.session.getSnapshot().status === "signed_out") {
34882
+ for (const client of this.clients.values()) if (!this.showsUnsynced(client)) this.leave(client);
34883
+ }
34372
34884
  for (const client of [...this.clients.values()]) this.post(client, [APPLICATION_WORKER_PROTOCOL, client.binding, "nativeSnapshot", 0, this.view(client)]);
34373
34885
  this.reconcile();
34374
34886
  });
34375
34887
  this.post(this.primary, [APPLICATION_WORKER_PROTOCOL, binding, "nativeSnapshot", 0, this.view(this.primary)]);
34888
+ const name2 = this.unsyncedOptions?.databaseName;
34889
+ if (name2) {
34890
+ void appUnsyncedWork(name2, this.lifetime.signal).then((found) => {
34891
+ if (found) this.noteUnsyncedWork(found);
34892
+ }).catch(() => {
34893
+ });
34894
+ const now = Date.now();
34895
+ void pruneAppUnsyncedRetired(name2, (copy2) => copy2.retiredAt + APP_UNSYNCED_RECOVERY_MS < now, this.lifetime.signal);
34896
+ }
34897
+ }
34898
+ noteUnsyncedWork(found) {
34899
+ if (this.stopping || this.unsyncedWork?.since === found?.since) return;
34900
+ this.unsyncedWork = found ? Object.freeze({ since: found.since }) : null;
34901
+ for (const client of [...this.clients.values()]) this.post(client, [APPLICATION_WORKER_PROTOCOL, client.binding, "nativeSnapshot", 0, this.view(client)]);
34376
34902
  }
34377
34903
  /** What one tab sees: the shared sign-in with its own collection. A tab that
34378
34904
  * did not start an approval in progress keeps showing its open collection. */
34379
34905
  view(client) {
34380
- return this.session.viewFor(client.selected, { bystander: this.approval?.client !== client });
34906
+ const bystander = this.approval?.client !== client;
34907
+ const work2 = this.unsyncedWork;
34908
+ if (!this.showsUnsynced(client)) {
34909
+ const view = this.session.viewFor(client.selected, { bystander });
34910
+ return work2 ? Object.freeze({ ...view, unsyncedWork: work2 }) : view;
34911
+ }
34912
+ const base = this.session.viewFor(null, { bystander }), open2 = this.unsynced?.isCurrent() === true;
34913
+ const status = base.status === "authorizing" ? base.status : open2 ? this.unsynced.state : this.unsyncedProblem ? "blocked" : "opening";
34914
+ return Object.freeze({
34915
+ ...base,
34916
+ status,
34917
+ selectedCollectionId: client.selected,
34918
+ problem: status === "blocked" ? this.unsyncedProblem : base.status === "authorizing" ? base.problem : null,
34919
+ openingProgress: null,
34920
+ transport: null,
34921
+ live: null,
34922
+ unsynced: Object.freeze({ collectionId: client.selected, sessionStatus: base.status }),
34923
+ unsyncedWork: work2
34924
+ });
34925
+ }
34926
+ showsUnsynced(client) {
34927
+ return client.selected !== null && client.selected === this.unsynced?.scope.collection;
34928
+ }
34929
+ /** The open collection with this id: the unsynced one or the session's. */
34930
+ connectionOf(id4) {
34931
+ return this.unsynced?.scope.collection === id4 ? this.unsynced : this.session.connection(id4);
34932
+ }
34933
+ /** The unsynced collection, opening it (creating it on first use) if needed. */
34934
+ async ensureUnsynced() {
34935
+ let connection = this.unsynced?.isCurrent() ? this.unsynced : null;
34936
+ if (!connection) {
34937
+ const previous = this.unsynced;
34938
+ this.unsynced = null;
34939
+ await previous?.close().catch(() => {
34940
+ });
34941
+ this.unsyncedProblem = null;
34942
+ try {
34943
+ connection = await openAppUnsyncedCollection(this.unsyncedOptions, this.unsyncedName, this.lifetime.signal);
34944
+ } catch (error4) {
34945
+ this.unsyncedProblem = applicationWorkerReplyProblem(error4).code;
34946
+ throw error4;
34947
+ }
34948
+ if (this.stopping || this.lifetime.signal.aborted) {
34949
+ await connection.close().catch(() => {
34950
+ });
34951
+ throw new ApplicationWorkerError("application_worker_fenced");
34952
+ }
34953
+ this.unsynced = connection;
34954
+ const opened = connection;
34955
+ opened.subscribe(() => {
34956
+ if (this.stopping) return;
34957
+ const found = opened.work;
34958
+ if (found && this.unsyncedWork?.since !== found.since) {
34959
+ this.noteUnsyncedWork(found);
34960
+ return;
34961
+ }
34962
+ for (const other of [...this.clients.values()]) if (this.showsUnsynced(other)) this.post(other, [APPLICATION_WORKER_PROTOCOL, other.binding, "nativeSnapshot", 0, this.view(other)]);
34963
+ });
34964
+ }
34965
+ return connection;
34966
+ }
34967
+ async openUnsynced(client, id4) {
34968
+ const connection = await this.ensureUnsynced();
34969
+ const collection = connection.scope.collection;
34970
+ if (client.selected !== collection) {
34971
+ this.leave(client);
34972
+ client.selected = collection;
34973
+ }
34974
+ if (client.generation === connection.generation) return;
34975
+ const value2 = connection.attach();
34976
+ if (!value2) throw new ApplicationWorkerError("application_worker_unavailable");
34977
+ client.generation = value2.generation;
34978
+ client.handoff = value2;
34979
+ client.collection = collection;
34980
+ this.post(client, [APPLICATION_WORKER_PROTOCOL, client.binding, "handoff", id4, value2], [value2.dataPort, value2.controlPort]);
34981
+ }
34982
+ /** Read access to the unsynced collection for this tab, beside the
34983
+ * collection it shows: the source of an unsynced transfer. One per tab. */
34984
+ async attachUnsynced(client, id4) {
34985
+ if (client.source && client.source.generation !== this.unsynced?.generation) this.releaseSource(client);
34986
+ if (client.source) throw new ApplicationWorkerError("application_worker_busy");
34987
+ const connection = await this.ensureUnsynced();
34988
+ if (client.gone || this.stopping) throw new ApplicationWorkerError("application_worker_fenced");
34989
+ const value2 = connection.attach();
34990
+ if (!value2) throw new ApplicationWorkerError("application_worker_unavailable");
34991
+ client.source = value2;
34992
+ client.sourceMark = Object.freeze({ generation: connection.generation, writes: connection.writes });
34993
+ this.post(client, [APPLICATION_WORKER_PROTOCOL, client.binding, "handoff", id4, value2], [value2.dataPort, value2.controlPort]);
34994
+ }
34995
+ /** Whether a tab other than `client` shows or reads the unsynced collection. */
34996
+ unsyncedHeldElsewhere(client, collection) {
34997
+ for (const other of this.clients.values()) if (other !== client && !other.gone && (other.selected === collection || other.source?.scope.collection === collection)) return true;
34998
+ return false;
34999
+ }
35000
+ /** Close the unsynced collection for good: this tab lets go of it first. */
35001
+ async closeUnsynced(client) {
35002
+ if (this.showsUnsynced(client)) this.leave(client);
35003
+ this.releaseSource(client);
35004
+ const open2 = this.unsynced;
35005
+ this.unsynced = null;
35006
+ await open2?.close();
35007
+ }
35008
+ /** After a transfer into the collection this tab shows: the unsynced
35009
+ * collection becomes a retired copy of the signed-in account, and the next
35010
+ * open starts a new one. Busy while another tab still works in it. */
35011
+ async retireUnsynced(client, source, destination) {
35012
+ const name2 = this.unsyncedOptions?.databaseName, account = this.session.getSnapshot().signIn?.accountId;
35013
+ if (!name2 || !account || client.selected !== destination || this.session.connection(destination)?.isCurrent() !== true) throw new ApplicationWorkerError("application_worker_unavailable");
35014
+ if (this.unsyncedHeldElsewhere(client, source)) throw new ApplicationWorkerError("application_worker_busy");
35015
+ const open2 = this.unsynced?.scope.collection === source ? this.unsynced : null, mark = client.sourceMark;
35016
+ client.sourceMark = null;
35017
+ if (!open2 || !mark || open2.generation !== mark.generation || open2.writes !== mark.writes) throw new ApplicationWorkerError("application_worker_busy");
35018
+ await this.closeUnsynced(client);
35019
+ if (await retireAppUnsynced(name2, source, destination, account, this.lifetime.signal)) this.noteUnsyncedWork(null);
35020
+ }
35021
+ /** Discard the unsynced collection and erase it from this browser. A tab
35022
+ * that showed it continues in a new, empty one. */
35023
+ async discardUnsynced(client, id4) {
35024
+ const name2 = this.unsyncedOptions?.databaseName;
35025
+ if (!name2) throw new ApplicationWorkerError("application_worker_unavailable");
35026
+ const current2 = await appUnsyncedIdentity(name2, false, this.lifetime.signal);
35027
+ const showing = this.showsUnsynced(client);
35028
+ if (current2) {
35029
+ if (this.unsyncedHeldElsewhere(client, current2.collection)) {
35030
+ if (showing) this.leave(client);
35031
+ throw new ApplicationWorkerError("application_worker_busy");
35032
+ }
35033
+ if (this.unsynced?.scope.collection === current2.collection) await this.closeUnsynced(client);
35034
+ else this.releaseSource(client);
35035
+ const gone = await discardAppUnsynced(name2, current2.collection, this.lifetime.signal);
35036
+ if (gone) await eraseAppUnsyncedStorage(gone);
35037
+ }
35038
+ this.noteUnsyncedWork(null);
35039
+ if (showing) await this.openUnsynced(client, id4);
35040
+ }
35041
+ releaseSource(client) {
35042
+ const source = client.source;
35043
+ client.source = null;
35044
+ if (source && this.unsynced?.generation === source.generation) try {
35045
+ this.unsynced.detach(source);
35046
+ } catch {
35047
+ }
34381
35048
  }
34382
35049
  client(port, binding, primary) {
34383
35050
  const client = {
@@ -34391,6 +35058,8 @@ var ApplicationWorkerHost = class {
34391
35058
  generation: null,
34392
35059
  handoff: null,
34393
35060
  collection: null,
35061
+ source: null,
35062
+ sourceMark: null,
34394
35063
  onMessage: (event) => this.onMessage(client, event),
34395
35064
  onMessageError: () => primary ? this.failStop("application_worker_unavailable") : this.detach(client)
34396
35065
  };
@@ -34426,6 +35095,7 @@ var ApplicationWorkerHost = class {
34426
35095
  }
34427
35096
  for (let i = this.queue.length - 1; i >= 0; i--) if (this.queue[i].client === client) this.queue.splice(i, 1);
34428
35097
  this.leave(client);
35098
+ this.releaseSource(client);
34429
35099
  client.port.removeEventListener("message", client.onMessage);
34430
35100
  client.port.removeEventListener("messageerror", client.onMessageError);
34431
35101
  client.port.close();
@@ -34441,7 +35111,7 @@ var ApplicationWorkerHost = class {
34441
35111
  client.generation = null;
34442
35112
  client.collection = null;
34443
35113
  if (!handoff || collection === null) return;
34444
- const connection = this.session.connection(collection);
35114
+ const connection = this.connectionOf(collection);
34445
35115
  if (connection?.generation === handoff.generation) try {
34446
35116
  connection.detach?.(handoff);
34447
35117
  } catch {
@@ -34452,7 +35122,7 @@ var ApplicationWorkerHost = class {
34452
35122
  if (this.stopping) return;
34453
35123
  for (const client of [...this.clients.values()]) {
34454
35124
  if (client.gone || client.selected === null) continue;
34455
- const connection = this.session.connection(client.selected);
35125
+ const connection = this.connectionOf(client.selected);
34456
35126
  if (!connection?.isCurrent() || typeof connection.attach !== "function" || client.generation === connection.generation) continue;
34457
35127
  const value2 = connection.attach();
34458
35128
  if (!value2) continue;
@@ -34466,18 +35136,31 @@ var ApplicationWorkerHost = class {
34466
35136
  /** Close, in turn with requests, each held collection no tab shows. */
34467
35137
  collect() {
34468
35138
  if (this.stopping || this.collecting) return;
34469
- const shown = /* @__PURE__ */ new Set();
34470
- for (const client of this.clients.values()) if (!client.gone && client.selected !== null) shown.add(client.selected);
34471
- if (!this.session.heldCollections().some((id4) => !shown.has(id4))) return;
35139
+ const shown = this.shown();
35140
+ const unsynced = this.unsynced !== null && !shown.has(this.unsynced.scope.collection);
35141
+ if (!unsynced && !this.session.heldCollections().some((id4) => !shown.has(id4))) return;
34472
35142
  this.collecting = true;
34473
35143
  this.queue.push({ client: null, request: null, job: async () => {
34474
35144
  this.collecting = false;
34475
- const shown2 = /* @__PURE__ */ new Set();
34476
- for (const client of this.clients.values()) if (!client.gone && client.selected !== null) shown2.add(client.selected);
35145
+ const shown2 = this.shown();
34477
35146
  for (const id4 of this.session.heldCollections()) if (!shown2.has(id4)) await this.session.releaseCollection(id4);
35147
+ const open2 = this.unsynced;
35148
+ if (open2 && !shown2.has(open2.scope.collection)) {
35149
+ this.unsynced = null;
35150
+ await open2.close();
35151
+ }
34478
35152
  } });
34479
35153
  this.pump();
34480
35154
  }
35155
+ /** Collections some tab shows, or reads as a transfer source. */
35156
+ shown() {
35157
+ const shown = /* @__PURE__ */ new Set();
35158
+ for (const client of this.clients.values()) if (!client.gone) {
35159
+ if (client.selected !== null) shown.add(client.selected);
35160
+ if (client.source) shown.add(client.source.scope.collection);
35161
+ }
35162
+ return shown;
35163
+ }
34481
35164
  post(client, value2, transfer = []) {
34482
35165
  if (client.gone) return;
34483
35166
  try {
@@ -34765,15 +35448,52 @@ var ApplicationWorkerHost = class {
34765
35448
  this.leave(client);
34766
35449
  this.collect();
34767
35450
  break;
35451
+ case "openUnsynced":
35452
+ try {
35453
+ await this.openUnsynced(client, request3[2]);
35454
+ } finally {
35455
+ this.collect();
35456
+ }
35457
+ break;
35458
+ case "attachUnsynced":
35459
+ try {
35460
+ await this.attachUnsynced(client, request3[2]);
35461
+ } finally {
35462
+ this.collect();
35463
+ }
35464
+ break;
35465
+ case "detachUnsynced":
35466
+ this.releaseSource(client);
35467
+ this.collect();
35468
+ break;
35469
+ case "retireUnsynced": {
35470
+ const [source, destination] = payload;
35471
+ try {
35472
+ await this.retireUnsynced(client, source, destination);
35473
+ } finally {
35474
+ this.collect();
35475
+ }
35476
+ break;
35477
+ }
35478
+ case "discardUnsynced":
35479
+ try {
35480
+ await this.discardUnsynced(client, request3[2]);
35481
+ } finally {
35482
+ this.collect();
35483
+ }
35484
+ break;
34768
35485
  case "forget":
34769
35486
  await this.session.forget(payload);
34770
35487
  break;
34771
35488
  case "refreshCollections":
34772
35489
  await this.session.refreshCollectionList();
34773
35490
  break;
34774
- case "signOut":
35491
+ case "signOut": {
35492
+ const account = this.session.getSnapshot().signIn?.accountId, name2 = this.unsyncedOptions?.databaseName;
34775
35493
  await this.session.signOut();
35494
+ if (account && name2) await pruneAppUnsyncedRetired(name2, (copy2) => copy2.account === account, this.lifetime.signal);
34776
35495
  break;
35496
+ }
34777
35497
  case "deleteCollection": {
34778
35498
  const [id4, name2] = payload;
34779
35499
  await this.session.deleteCollection(id4, name2);
@@ -34803,7 +35523,9 @@ var ApplicationWorkerHost = class {
34803
35523
  const pending = this.pending;
34804
35524
  this.queue.length = 0;
34805
35525
  this.stopping = Promise.resolve().then(async () => {
34806
- await this.session.close();
35526
+ const unsynced = this.unsynced;
35527
+ this.unsynced = null;
35528
+ await Promise.all([this.session.close(), unsynced?.close()]);
34807
35529
  await pending;
34808
35530
  this.unsubscribe?.();
34809
35531
  this.unsubscribe = null;