@mdbase-dev/sdk 0.1.0-beta.162 → 0.1.0-beta.164
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.
- package/README.md +1 -0
- package/dist/app-grant/setup.js +25 -17
- package/dist/app-grant/setup.js.map +1 -1
- package/dist/app-host/local-facade.d.ts +1 -1
- package/dist/app-host/local-facade.js +1 -1
- package/dist/app-host/local-facade.js.map +1 -1
- package/dist/app-host/unsynced-collection.d.ts +99 -0
- package/dist/app-host/unsynced-collection.js +530 -0
- package/dist/app-host/unsynced-collection.js.map +1 -0
- package/dist/app-host/wasm-runtime.d.ts +12 -0
- package/dist/app-host/wasm-runtime.js +32 -0
- package/dist/app-host/wasm-runtime.js.map +1 -1
- package/dist/app-host/web-sign-in-session.d.ts +13 -0
- package/dist/app-host/web-sign-in-session.js.map +1 -1
- package/dist/application/asset-pins.json +4 -4
- package/dist/application/declaration.d.ts +2 -0
- package/dist/application/declaration.js +3 -3
- package/dist/application/declaration.js.map +1 -1
- package/dist/application/entry.d.ts +40 -0
- package/dist/application/entry.js +72 -2
- package/dist/application/entry.js.map +1 -1
- package/dist/application/front-door.d.ts +20 -1
- package/dist/application/front-door.js +22 -0
- package/dist/application/front-door.js.map +1 -1
- package/dist/application/native-assets.d.ts +13 -0
- package/dist/application/native-assets.js +6 -1
- package/dist/application/native-assets.js.map +1 -1
- package/dist/application/phase.d.ts +11 -1
- package/dist/application/phase.js +12 -0
- package/dist/application/phase.js.map +1 -1
- package/dist/application/runtime.d.ts +17 -0
- package/dist/application/runtime.js +102 -2
- package/dist/application/runtime.js.map +1 -1
- package/dist/application/setup.js +5 -0
- package/dist/application/setup.js.map +1 -1
- package/dist/application/unsynced-transfer.d.ts +111 -0
- package/dist/application/unsynced-transfer.js +194 -0
- package/dist/application/unsynced-transfer.js.map +1 -0
- package/dist/application/worker-client.js +3 -1
- package/dist/application/worker-client.js.map +1 -1
- package/dist/application/worker-host.d.ts +32 -0
- package/dist/application/worker-host.js +257 -16
- package/dist/application/worker-host.js.map +1 -1
- package/dist/application/worker-protocol.d.ts +1 -1
- package/dist/application/worker-protocol.js +7 -1
- package/dist/application/worker-protocol.js.map +1 -1
- package/dist/application/worker-session.d.ts +15 -0
- package/dist/application/worker-session.js +2 -1
- package/dist/application/worker-session.js.map +1 -1
- package/dist/application/worker.js +866 -144
- package/dist/browser/mdbase-sdk.js +7541 -6983
- package/dist/browser/mdbase-sdk.min.js +70 -70
- package/dist/client.d.ts +8 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/describe-error.d.ts +1 -1
- package/dist/describe-error.js +1 -0
- package/dist/describe-error.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js.map +1 -1
- package/dist/manifest.d.ts +11 -0
- package/dist/manifest.js +12 -0
- package/dist/manifest.js.map +1 -0
- package/dist/native/provenance.json +5 -5
- package/dist/native/runtime.wasm +0 -0
- package/dist/testing/application.d.ts +5 -1
- package/dist/testing/application.js +45 -1
- package/dist/testing/application.js.map +1 -1
- package/dist/thin-client.d.ts +1 -1
- package/dist/thin-client.js.map +1 -1
- package/dist/wire.d.ts +4 -1
- package/dist/wire.js +3 -1
- package/dist/wire.js.map +1 -1
- 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
|
|
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: "
|
|
15312
|
-
bytes:
|
|
15313
|
-
maxBytes:
|
|
15311
|
+
sha256: "4d377128dd7e76241cbbf63d00dd3f232286d8a506908fa240d20c6ac85c69a8",
|
|
15312
|
+
bytes: 3811508,
|
|
15313
|
+
maxBytes: 3811508,
|
|
15314
15314
|
source: {
|
|
15315
15315
|
repository: "mdbase-dev/mdbase-connect",
|
|
15316
|
-
commit: "
|
|
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
|
|
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(
|
|
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
|
|
16737
|
-
if (!["web_push", "fcm"].includes(
|
|
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(
|
|
16740
|
-
kind:
|
|
16741
|
-
targetDigest: text(
|
|
16742
|
-
channelId:
|
|
16743
|
-
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
|
|
16895
|
-
return { grant_id:
|
|
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,
|
|
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:
|
|
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(
|
|
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
|
|
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,
|
|
17241
|
+
async consentWindow(requestId, work2) {
|
|
17242
17242
|
try {
|
|
17243
|
-
return await
|
|
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
|
|
17678
|
-
return
|
|
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
|
|
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
|
|
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))
|
|
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)
|
|
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))
|
|
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))
|
|
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)
|
|
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"))
|
|
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")
|
|
19098
|
+
if (typeof payload !== "boolean") fail19();
|
|
19096
19099
|
} else if (action === "approvalClosed") {
|
|
19097
|
-
if (payload !== true)
|
|
19098
|
-
} else if (payload !== null)
|
|
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,
|
|
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 =
|
|
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
|
|
19238
|
-
if (!
|
|
19239
|
-
if (typeof
|
|
19240
|
-
const parts =
|
|
19241
|
-
if (parts.length === 5 && parts[0] === cpOrigin && parts[2] === connectorId && parts[3] === deviceId)
|
|
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
|
-
|
|
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",
|
|
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
|
|
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(
|
|
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((
|
|
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(
|
|
23417
|
-
return this.io = this.io.then(
|
|
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
|
|
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())
|
|
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
|
-
|
|
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
|
-
|
|
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())
|
|
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)
|
|
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) =>
|
|
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
|
|
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
|
|
25861
|
-
if (!["web_push", "fcm"].includes(
|
|
25862
|
-
|
|
25863
|
-
if (new Set(
|
|
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 (
|
|
25866
|
-
if (typeof
|
|
25867
|
-
target2 = { transport: "fcm", 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(
|
|
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: [...
|
|
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:
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
|
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
|
|
28946
|
-
const result2 = { issuer: url(
|
|
28947
|
-
if (
|
|
28948
|
-
if (typeof
|
|
28949
|
-
result2.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
|
|
28975
|
+
const row3 = object6(value2);
|
|
28970
28976
|
if (resource2 === "identity") {
|
|
28971
|
-
const result2 = profile(
|
|
28972
|
-
if (
|
|
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(
|
|
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((
|
|
29029
|
-
if (!plainObject(
|
|
29030
|
-
return Object.freeze({ ...
|
|
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((
|
|
29035
|
-
if (!plainObject(
|
|
29036
|
-
return Object.freeze({ ...
|
|
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((
|
|
29039
|
-
if (!plainObject(
|
|
29040
|
-
return Object.freeze({ path:
|
|
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,
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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
|
|
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:
|
|
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 { ...
|
|
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:
|
|
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) =>
|
|
29858
|
-
return Object.freeze({ ...
|
|
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
|
|
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 =
|
|
30664
|
+
this.logWork = work2;
|
|
30628
30665
|
try {
|
|
30629
|
-
await
|
|
30666
|
+
await work2;
|
|
30630
30667
|
} finally {
|
|
30631
|
-
if (this.logWork ===
|
|
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
|
|
30836
|
-
this.readWork =
|
|
30837
|
-
void
|
|
30838
|
-
if (this.readWork ===
|
|
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
|
|
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(
|
|
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 (
|
|
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 (
|
|
31204
|
-
result2 = { outcomeMode: "existing", 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(
|
|
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,
|
|
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(
|
|
31283
|
+
tx.oncomplete = () => resolve(change3 === "mark" || found);
|
|
31247
31284
|
const records2 = tx.objectStore(STORE7);
|
|
31248
|
-
if (
|
|
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(
|
|
31372
|
+
function bounded2(work2, fallback, ms = SIGN_OUT_ERASE_TIMEOUT_MS) {
|
|
31336
31373
|
let timer;
|
|
31337
|
-
return Promise.race([
|
|
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(
|
|
31623
|
+
poll(work2) {
|
|
31587
31624
|
if (this.pending) return;
|
|
31588
|
-
void this.operation(
|
|
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(
|
|
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
|
|
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
|
|
33749
|
-
void
|
|
33785
|
+
const work2 = driving = wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
|
|
33786
|
+
void work2.then(
|
|
33750
33787
|
() => {
|
|
33751
|
-
if (driving ===
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
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")
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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 =
|
|
34470
|
-
|
|
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 =
|
|
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
|
-
|
|
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;
|