@mdbase-dev/sdk 0.1.0-beta.178 → 0.1.0-beta.180
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 +52 -0
- package/dist/app-host/cloud-copy-host.d.ts +13 -0
- package/dist/app-host/cloud-copy-host.js +43 -11
- package/dist/app-host/cloud-copy-host.js.map +1 -1
- package/dist/app-host/installation-sign-in.d.ts +7 -0
- package/dist/app-host/installation-sign-in.js +20 -1
- package/dist/app-host/installation-sign-in.js.map +1 -1
- package/dist/app-host/scoped-sign-in-session.d.ts +2 -0
- package/dist/app-host/scoped-sign-in-session.js +4 -0
- package/dist/app-host/scoped-sign-in-session.js.map +1 -1
- package/dist/app-host/web-collection.js +206 -60
- package/dist/app-host/web-collection.js.map +1 -1
- package/dist/app-host/web-sign-in-session.d.ts +15 -0
- package/dist/app-host/web-sign-in-session.js +90 -7
- package/dist/app-host/web-sign-in-session.js.map +1 -1
- package/dist/application/asset-pins.json +4 -4
- package/dist/application/worker-host.d.ts +10 -0
- package/dist/application/worker-host.js +65 -9
- package/dist/application/worker-host.js.map +1 -1
- package/dist/application/worker.js +359 -80
- package/dist/browser/mdbase-sdk.js +14398 -14053
- package/dist/browser/mdbase-sdk.min.js +63 -63
- package/dist/native/provenance.json +5 -5
- package/dist/native/runtime.wasm +0 -0
- package/package.json +1 -1
|
@@ -15691,12 +15691,12 @@ function appReleaseTrust() {
|
|
|
15691
15691
|
// src/application/asset-pins.json
|
|
15692
15692
|
var asset_pins_default = {
|
|
15693
15693
|
runtime: {
|
|
15694
|
-
sha256: "
|
|
15695
|
-
bytes:
|
|
15696
|
-
maxBytes:
|
|
15694
|
+
sha256: "be17475e2ca164642fb5b29b4bf003c03ad9a8dbacd8aebc135dad7e91b73585",
|
|
15695
|
+
bytes: 3849256,
|
|
15696
|
+
maxBytes: 3849256,
|
|
15697
15697
|
source: {
|
|
15698
15698
|
repository: "mdbase-dev/mdbase-connect",
|
|
15699
|
-
commit: "
|
|
15699
|
+
commit: "323d30aff40fc6f3f172d44f60cd7e67bb17d977"
|
|
15700
15700
|
}
|
|
15701
15701
|
},
|
|
15702
15702
|
sqlite: {
|
|
@@ -17790,6 +17790,23 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
|
|
|
17790
17790
|
}
|
|
17791
17791
|
/** Current approved/member cloud-copy metadata only; never readiness, keying,
|
|
17792
17792
|
* or permission inferred from this response or retained consent snapshots. */
|
|
17793
|
+
/** The kept listing narrowed to this installation's consent. File actions
|
|
17794
|
+
* are never kept, so every row has none until the next read. */
|
|
17795
|
+
keptCollections() {
|
|
17796
|
+
if (this.state.collectionListing === void 0) return null;
|
|
17797
|
+
return Object.freeze(collectionListing(this.state.collectionListing).filter((c) => this.state.collectionIds.includes(c.collectionId) && this.modeConsented(c)).map((c) => Object.freeze({ ...c, fileActions: Object.freeze([]) })));
|
|
17798
|
+
}
|
|
17799
|
+
/** The approved collections as this installation last read them, or null
|
|
17800
|
+
* when no listing is kept. No request. It can be out of date in every way a
|
|
17801
|
+
* list can; `listApprovedCollections` is still the authority. */
|
|
17802
|
+
cachedApprovedCollections(installation) {
|
|
17803
|
+
this.consentGuard(installation)();
|
|
17804
|
+
try {
|
|
17805
|
+
return this.keptCollections();
|
|
17806
|
+
} catch {
|
|
17807
|
+
return null;
|
|
17808
|
+
}
|
|
17809
|
+
}
|
|
17793
17810
|
listApprovedCollections(installation) {
|
|
17794
17811
|
const check3 = this.consentGuard(installation);
|
|
17795
17812
|
return this.exclusive(async () => {
|
|
@@ -17806,7 +17823,7 @@ var AppProtectedInstallationSignIn = class _AppProtectedInstallationSignIn {
|
|
|
17806
17823
|
} catch (error4) {
|
|
17807
17824
|
check3();
|
|
17808
17825
|
if (error4 instanceof AppInstallationSignInError && error4.reason === "unavailable" && !error4.server && !nonOkResponses.has(error4) && this.state.collectionListing !== void 0)
|
|
17809
|
-
return
|
|
17826
|
+
return this.keptCollections();
|
|
17810
17827
|
if (error4 instanceof AppInstallationSignInError && (error4.server || nonOkResponses.has(error4)) && this.state.collectionListing !== void 0) {
|
|
17811
17828
|
const state2 = { ...this.state };
|
|
17812
17829
|
delete state2.collectionListing;
|
|
@@ -36924,6 +36941,18 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
36924
36941
|
return "unavailable";
|
|
36925
36942
|
}
|
|
36926
36943
|
}
|
|
36944
|
+
/** Whether the replica on this device holds anything from the log. Every
|
|
36945
|
+
* log starts with the entries that admitted this device, so a replica
|
|
36946
|
+
* that knows no head is empty (its storage was lost), not up to date. */
|
|
36947
|
+
holdsLog() {
|
|
36948
|
+
try {
|
|
36949
|
+
if (!this.native) return false;
|
|
36950
|
+
const status = this.native.observations().status;
|
|
36951
|
+
return status.mode === "local_only" || status.headKnown > 0;
|
|
36952
|
+
} catch {
|
|
36953
|
+
return false;
|
|
36954
|
+
}
|
|
36955
|
+
}
|
|
36927
36956
|
/** Diagnostics while waiting for a key (native sync status), else null. */
|
|
36928
36957
|
keyWaitDetail() {
|
|
36929
36958
|
try {
|
|
@@ -36932,6 +36961,15 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
36932
36961
|
return null;
|
|
36933
36962
|
}
|
|
36934
36963
|
}
|
|
36964
|
+
/** For a collection opened before its log: has the reopened replica its
|
|
36965
|
+
* keys back from the log's control prefix? `failed` is what a waited open
|
|
36966
|
+
* refuses as `recovery_required`. */
|
|
36967
|
+
keyringSettled() {
|
|
36968
|
+
if (!this.native) throw fail15("binding");
|
|
36969
|
+
const observation = this.native.observations();
|
|
36970
|
+
if (observation.requiresReopen || observation.keyringRebuildFailed) return "failed";
|
|
36971
|
+
return observation.keyringRebuilding ? "pending" : "settled";
|
|
36972
|
+
}
|
|
36935
36973
|
/** Local writes not yet confirmed by the log (native sync status). */
|
|
36936
36974
|
pendingWrites() {
|
|
36937
36975
|
try {
|
|
@@ -37016,16 +37054,17 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37016
37054
|
* behind can finish its catch-up turn; without a pump it is fixed.
|
|
37017
37055
|
* No status-based admission, write retry, key override or private grant. */
|
|
37018
37056
|
waitForVerifiedRead(options) {
|
|
37019
|
-
const { timeoutMs = 15e3, cold = false } = options;
|
|
37057
|
+
const { timeoutMs = 15e3, cold = false, local = false } = options;
|
|
37020
37058
|
const signal = this.operationSignal(options.signal);
|
|
37021
37059
|
this.checkCollection(signal);
|
|
37022
|
-
if (!this.native || !this.collectionScope || this.adoptedEndpoint === null || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 15e3 || typeof cold !== "boolean" || cold && !this.pump)
|
|
37060
|
+
if (!this.native || !this.collectionScope || this.adoptedEndpoint === null || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 15e3 || typeof cold !== "boolean" || typeof local !== "boolean" || cold && (local || !this.pump))
|
|
37023
37061
|
throw fail15("binding");
|
|
37062
|
+
if (local && !this.holdsLog()) return Promise.reject(fail15("unavailable"));
|
|
37024
37063
|
if (this.readWork)
|
|
37025
37064
|
return this.readWork.then(() => {
|
|
37026
37065
|
this.checkCollection(signal);
|
|
37027
37066
|
});
|
|
37028
|
-
const work2 = this.verifyRead(signal, timeoutMs, cold, options.onProgress);
|
|
37067
|
+
const work2 = this.verifyRead(signal, timeoutMs, cold, local, options.onProgress);
|
|
37029
37068
|
this.readWork = work2;
|
|
37030
37069
|
void work2.finally(() => {
|
|
37031
37070
|
if (this.readWork === work2) this.readWork = null;
|
|
@@ -37033,8 +37072,9 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37033
37072
|
});
|
|
37034
37073
|
return work2;
|
|
37035
37074
|
}
|
|
37036
|
-
async verifyRead(signal, timeoutMs, cold, onProgress) {
|
|
37075
|
+
async verifyRead(signal, timeoutMs, cold, local, onProgress) {
|
|
37037
37076
|
const ctrl = new AbortController(), abort = () => ctrl.abort();
|
|
37077
|
+
const pump = () => local ? null : this.pump;
|
|
37038
37078
|
this.readAbort = ctrl;
|
|
37039
37079
|
signal.addEventListener("abort", abort, { once: true });
|
|
37040
37080
|
let timer = null;
|
|
@@ -37057,7 +37097,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37057
37097
|
};
|
|
37058
37098
|
const observe = () => {
|
|
37059
37099
|
this.checkCollection(signal);
|
|
37060
|
-
const refusal2 = cold ?
|
|
37100
|
+
const refusal2 = cold ? pump().terminalProblem : null;
|
|
37061
37101
|
if (isAppServerProblem(refusal2))
|
|
37062
37102
|
throw new AppHttpLogError(appServerOutcome(refusal2) ?? "unavailable", refusal2);
|
|
37063
37103
|
const observation = this.native.observations();
|
|
@@ -37068,14 +37108,14 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37068
37108
|
};
|
|
37069
37109
|
this.readVerified = false;
|
|
37070
37110
|
try {
|
|
37071
|
-
if (
|
|
37111
|
+
if (pump()) {
|
|
37072
37112
|
const progress2 = () => {
|
|
37073
37113
|
const observation = observe();
|
|
37074
37114
|
return [
|
|
37075
37115
|
observation.status.confirmedThrough,
|
|
37076
37116
|
observation.status.installing?.done ?? 0,
|
|
37077
37117
|
observation.status.installing?.total ?? 0,
|
|
37078
|
-
|
|
37118
|
+
pump().readProgress
|
|
37079
37119
|
];
|
|
37080
37120
|
};
|
|
37081
37121
|
let high = progress2(), lastProgress = Date.now();
|
|
@@ -37089,7 +37129,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37089
37129
|
deferred2 = null;
|
|
37090
37130
|
}
|
|
37091
37131
|
high = next.map((value2, i) => Math.max(value2, high[i]));
|
|
37092
|
-
const active =
|
|
37132
|
+
const active = pump().responseProgress();
|
|
37093
37133
|
const ids = new Set(active.map((body) => body.id));
|
|
37094
37134
|
for (const id4 of bodies.keys()) if (!ids.has(id4)) bodies.delete(id4);
|
|
37095
37135
|
for (const body of active) {
|
|
@@ -37134,7 +37174,8 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37134
37174
|
let delay = 250;
|
|
37135
37175
|
while (!ctrl.signal.aborted) {
|
|
37136
37176
|
this.native.tick();
|
|
37137
|
-
|
|
37177
|
+
const turn = pump();
|
|
37178
|
+
if (turn) await Promise.race([turn.pump(), aborted]);
|
|
37138
37179
|
this.checkCollection(signal);
|
|
37139
37180
|
const observation = observe();
|
|
37140
37181
|
if (cold && initialSyncWait(observation.status) !== null) {
|
|
@@ -37153,7 +37194,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37153
37194
|
return;
|
|
37154
37195
|
} catch (error4) {
|
|
37155
37196
|
this.checkCollection(signal);
|
|
37156
|
-
if (!(error4 instanceof MdbaseError) || error4.code !== "unavailable" || !
|
|
37197
|
+
if (!(error4 instanceof MdbaseError) || error4.code !== "unavailable" || !pump() || ctrl.signal.aborted)
|
|
37157
37198
|
throw error4;
|
|
37158
37199
|
}
|
|
37159
37200
|
await waitForOpenRetry(delay, ctrl.signal);
|
|
@@ -37787,6 +37828,7 @@ var AppWebSignInSession = class {
|
|
|
37787
37828
|
attested = false;
|
|
37788
37829
|
acknowledged = false;
|
|
37789
37830
|
consentPending = false;
|
|
37831
|
+
listCached = false;
|
|
37790
37832
|
fileAccess = null;
|
|
37791
37833
|
freshApproval = false;
|
|
37792
37834
|
/** The window this session's approval (or file access request) is shown in. */
|
|
@@ -37980,7 +38022,15 @@ var AppWebSignInSession = class {
|
|
|
37980
38022
|
if (this.flow.collectionConsentView()?.state === "pending") {
|
|
37981
38023
|
this.consentPending = true;
|
|
37982
38024
|
this.publish({ status: "authorizing" });
|
|
37983
|
-
} else
|
|
38025
|
+
} else {
|
|
38026
|
+
let kept = null;
|
|
38027
|
+
try {
|
|
38028
|
+
kept = this.flow.cachedApprovedCollections(this.installation);
|
|
38029
|
+
} catch {
|
|
38030
|
+
kept = null;
|
|
38031
|
+
}
|
|
38032
|
+
await this.refreshCollections(kept);
|
|
38033
|
+
}
|
|
37984
38034
|
} else this.publish({ status: view.state === "pending" ? "signed_out" : "authorizing" });
|
|
37985
38035
|
return this.getSnapshot();
|
|
37986
38036
|
});
|
|
@@ -38338,10 +38388,15 @@ var AppWebSignInSession = class {
|
|
|
38338
38388
|
this.acknowledged = true;
|
|
38339
38389
|
}
|
|
38340
38390
|
}
|
|
38341
|
-
|
|
38342
|
-
|
|
38391
|
+
/** `kept`: this installation's last listing instead of a read. It is not an
|
|
38392
|
+
* accepted inventory, so an approval missing from it is not a removal. */
|
|
38393
|
+
async refreshCollections(kept = null) {
|
|
38394
|
+
const collections = kept ?? await this.flow.listApprovedCollections(this.installation);
|
|
38343
38395
|
this.check();
|
|
38344
|
-
|
|
38396
|
+
this.listCached = kept !== null;
|
|
38397
|
+
if (!kept) {
|
|
38398
|
+
for (const id4 of this.flow.view().collectionIds) if (!collections.some((c) => c.collectionId === id4)) this.removedScopes.add(id4);
|
|
38399
|
+
}
|
|
38345
38400
|
const visible = Object.freeze(collections.filter((c) => !this.forgotten.has(c.collectionId)));
|
|
38346
38401
|
for (const slot of this.slots.values()) {
|
|
38347
38402
|
if (slot.usable && slot.selected && !visible.some((c) => c.collectionId === slot.id)) {
|
|
@@ -38549,14 +38604,24 @@ var AppWebSignInSession = class {
|
|
|
38549
38604
|
if (this.openingSlot === slot) this.openingSlot = null;
|
|
38550
38605
|
slot.stopAssessment = selected.subscribe?.(() => {
|
|
38551
38606
|
if (this.closing || this.slots.get(slot.id) !== slot || !slot.usable || slot.selected !== selected || slot.lifetime?.signal.aborted || !this.sameScope(slot.scope, slot.leaseScope) || slot.scope?.collection !== slot.id) return;
|
|
38552
|
-
let
|
|
38607
|
+
let ended2 = null;
|
|
38553
38608
|
try {
|
|
38554
|
-
|
|
38609
|
+
ended2 = selected.ended ?? null;
|
|
38555
38610
|
} catch {
|
|
38556
|
-
|
|
38611
|
+
ended2 = null;
|
|
38557
38612
|
}
|
|
38558
|
-
if (
|
|
38559
|
-
this.accessEnded(slot, selected,
|
|
38613
|
+
if (ended2) {
|
|
38614
|
+
this.accessEnded(slot, selected, ended2);
|
|
38615
|
+
return;
|
|
38616
|
+
}
|
|
38617
|
+
let failed2 = null;
|
|
38618
|
+
try {
|
|
38619
|
+
failed2 = selected.failure ?? null;
|
|
38620
|
+
} catch {
|
|
38621
|
+
failed2 = null;
|
|
38622
|
+
}
|
|
38623
|
+
if (failed2 !== null) {
|
|
38624
|
+
this.openFailedLate(slot, selected, failed2);
|
|
38560
38625
|
return;
|
|
38561
38626
|
}
|
|
38562
38627
|
let next;
|
|
@@ -38567,6 +38632,26 @@ var AppWebSignInSession = class {
|
|
|
38567
38632
|
}
|
|
38568
38633
|
this.publishSlot(slot, next.status === "needs_unlock" ? { ...next, problem: null, transport: null, live: null } : { ...next, ...this.liveProblem(slot, selected) });
|
|
38569
38634
|
}) ?? null;
|
|
38635
|
+
let ended = null;
|
|
38636
|
+
try {
|
|
38637
|
+
ended = selected.ended ?? null;
|
|
38638
|
+
} catch {
|
|
38639
|
+
ended = null;
|
|
38640
|
+
}
|
|
38641
|
+
let failed = null;
|
|
38642
|
+
try {
|
|
38643
|
+
failed = selected.failure ?? null;
|
|
38644
|
+
} catch {
|
|
38645
|
+
failed = null;
|
|
38646
|
+
}
|
|
38647
|
+
if (ended) {
|
|
38648
|
+
this.accessEnded(slot, selected, ended);
|
|
38649
|
+
return;
|
|
38650
|
+
}
|
|
38651
|
+
if (failed !== null) {
|
|
38652
|
+
this.openFailedLate(slot, selected, failed);
|
|
38653
|
+
return;
|
|
38654
|
+
}
|
|
38570
38655
|
this.publishSlot(slot, { ...first, transport: null, live: null, openingProgress: null });
|
|
38571
38656
|
}
|
|
38572
38657
|
/** The adapter's own state, validated: `needs_unlock` only with its lock
|
|
@@ -38654,6 +38739,35 @@ var AppWebSignInSession = class {
|
|
|
38654
38739
|
};
|
|
38655
38740
|
stop();
|
|
38656
38741
|
}
|
|
38742
|
+
/** A collection opened from its replica, whose log was then refused. The
|
|
38743
|
+
* same block and detail as a failed open; stopping it fences every page's
|
|
38744
|
+
* data ports. Local data, unsent edits included, stays on this device. */
|
|
38745
|
+
openFailedLate(slot, selected, error4) {
|
|
38746
|
+
if (slot.selected !== selected || !slot.usable) return;
|
|
38747
|
+
const { problem: problem4, detail } = webSignInProblem(error4);
|
|
38748
|
+
if (problem4 === "open_failed") {
|
|
38749
|
+
try {
|
|
38750
|
+
console.error("mdbase web sign-in open failed", detail.name, detail.code);
|
|
38751
|
+
} catch {
|
|
38752
|
+
}
|
|
38753
|
+
}
|
|
38754
|
+
slot.usable = false;
|
|
38755
|
+
this.publishSlot(slot, { status: "blocked", problem: problem4, problemDetail: detail, transport: null, live: null });
|
|
38756
|
+
const stop = () => {
|
|
38757
|
+
if (this.closing || this.options.signal.aborted || slot.selected !== selected || this.slots.get(slot.id) !== slot) return;
|
|
38758
|
+
if (this.pending) {
|
|
38759
|
+
void this.pending.catch(() => {
|
|
38760
|
+
}).then(stop);
|
|
38761
|
+
return;
|
|
38762
|
+
}
|
|
38763
|
+
void this.operation(async () => {
|
|
38764
|
+
await this.closeSlot(slot);
|
|
38765
|
+
this.publishSlot(slot, { status: "blocked", problem: problem4, problemDetail: detail });
|
|
38766
|
+
}, true).catch(() => {
|
|
38767
|
+
});
|
|
38768
|
+
};
|
|
38769
|
+
stop();
|
|
38770
|
+
}
|
|
38657
38771
|
/** Live problem of an open collection: shown while it lasts, cleared after. */
|
|
38658
38772
|
liveProblem(slot, selected) {
|
|
38659
38773
|
let server = null;
|
|
@@ -38729,6 +38843,12 @@ var AppWebSignInSession = class {
|
|
|
38729
38843
|
return this.getSnapshot();
|
|
38730
38844
|
}, true);
|
|
38731
38845
|
}
|
|
38846
|
+
/** The published list is the one this installation kept, shown by a
|
|
38847
|
+
* signed-in start; Connect's has not been read since. The owner calls
|
|
38848
|
+
* `refreshCollectionList` once, when that read can no longer delay a page. */
|
|
38849
|
+
collectionListCached() {
|
|
38850
|
+
return this.listCached;
|
|
38851
|
+
}
|
|
38732
38852
|
/** Accepted COMPLETE inventory omission or terminal native/log refusal only.
|
|
38733
38853
|
* Local Forget, failed GETs and transient transport failures do not qualify. */
|
|
38734
38854
|
scopeWasRemoved(collectionId) {
|
|
@@ -39184,6 +39304,10 @@ var AppScopedSignInSession = class {
|
|
|
39184
39304
|
await session.pollFileAccess(requestId);
|
|
39185
39305
|
return this.getSnapshot();
|
|
39186
39306
|
}
|
|
39307
|
+
/** Any of the sessions still shows the list it kept (a signed-in start). */
|
|
39308
|
+
collectionListCached() {
|
|
39309
|
+
return this.original.collectionListCached() || [...this.scoped.values()].some((actor) => actor.session.collectionListCached());
|
|
39310
|
+
}
|
|
39187
39311
|
async refreshCollectionList() {
|
|
39188
39312
|
await this.original.refreshCollectionList();
|
|
39189
39313
|
for (const actor of this.scoped.values()) {
|
|
@@ -39919,6 +40043,7 @@ function createAppCollectionSql(source) {
|
|
|
39919
40043
|
var PAUSED_AFTER_MS = 1e4;
|
|
39920
40044
|
var BACKGROUND_FLUSH_MS = 12e4;
|
|
39921
40045
|
var WARM_SYNC_MS = 2e3;
|
|
40046
|
+
var LOCAL_READ_MS = 5e3;
|
|
39922
40047
|
var INITIAL_LOG_RETRY_MS = 15e3;
|
|
39923
40048
|
var INITIAL_LOG_ATTEMPTS = 3;
|
|
39924
40049
|
var AppWebCollectionError = class extends Error {
|
|
@@ -39959,6 +40084,10 @@ function createAppWebCollectionAdapter(source) {
|
|
|
39959
40084
|
let assessment = "setup_required";
|
|
39960
40085
|
let locked = null;
|
|
39961
40086
|
let started = false;
|
|
40087
|
+
let logBound = false;
|
|
40088
|
+
let early = null;
|
|
40089
|
+
let failure2 = null;
|
|
40090
|
+
let keys2 = "settled";
|
|
39962
40091
|
let push = null, paused = false, pausing = null, kicked = false;
|
|
39963
40092
|
let ended = null;
|
|
39964
40093
|
const attached = /* @__PURE__ */ new Map();
|
|
@@ -40119,6 +40248,79 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40119
40248
|
return false;
|
|
40120
40249
|
}
|
|
40121
40250
|
};
|
|
40251
|
+
const startLog = () => host.startCollectionLog({ signal: life.signal, fetch: options.fetch, allowLoopbackHttp: options.allowLoopbackHttp });
|
|
40252
|
+
const openPush = () => {
|
|
40253
|
+
try {
|
|
40254
|
+
push = typeof host.openLogPush === "function" ? host.openLogPush({
|
|
40255
|
+
push: (frame) => {
|
|
40256
|
+
if (!current2()) return;
|
|
40257
|
+
host.pushLog(frame);
|
|
40258
|
+
kick();
|
|
40259
|
+
},
|
|
40260
|
+
live: onLive,
|
|
40261
|
+
// Access ended: the replica has the `closed` push; drive so it
|
|
40262
|
+
// settles, then the session stops this collection in every tab.
|
|
40263
|
+
ended: (reason) => {
|
|
40264
|
+
ended = reason;
|
|
40265
|
+
kick();
|
|
40266
|
+
onLive(false);
|
|
40267
|
+
settleInitial?.();
|
|
40268
|
+
changed();
|
|
40269
|
+
}
|
|
40270
|
+
}) : null;
|
|
40271
|
+
} catch {
|
|
40272
|
+
push = null;
|
|
40273
|
+
}
|
|
40274
|
+
if (push) {
|
|
40275
|
+
push.start();
|
|
40276
|
+
onLive(false);
|
|
40277
|
+
} else setPaused(true);
|
|
40278
|
+
};
|
|
40279
|
+
const bindLog = async () => {
|
|
40280
|
+
const first = early;
|
|
40281
|
+
early = null;
|
|
40282
|
+
await (first ?? startLog());
|
|
40283
|
+
check3();
|
|
40284
|
+
logBound = true;
|
|
40285
|
+
openPush();
|
|
40286
|
+
};
|
|
40287
|
+
const settleKeys = () => {
|
|
40288
|
+
if (keys2 !== "pending" || !logBound) return;
|
|
40289
|
+
try {
|
|
40290
|
+
keys2 = host.keyringSettled?.() ?? "settled";
|
|
40291
|
+
} catch {
|
|
40292
|
+
return;
|
|
40293
|
+
}
|
|
40294
|
+
if (keys2 === "failed") refuse(new AppCloudCopyHostError("recovery_required"));
|
|
40295
|
+
};
|
|
40296
|
+
const refuse = (error4) => {
|
|
40297
|
+
failure2 = error4;
|
|
40298
|
+
started = false;
|
|
40299
|
+
if (timer !== null) clearTimeout(timer);
|
|
40300
|
+
timer = null;
|
|
40301
|
+
changed();
|
|
40302
|
+
};
|
|
40303
|
+
const bindFailed = (error4) => {
|
|
40304
|
+
let server = null;
|
|
40305
|
+
try {
|
|
40306
|
+
server = error4.server;
|
|
40307
|
+
} catch {
|
|
40308
|
+
server = null;
|
|
40309
|
+
}
|
|
40310
|
+
const outcome = isAppServerProblem(server) && appServerOutcome(server) !== null ? server : null;
|
|
40311
|
+
const foreign = !isAppServerProblem(server) && error4 instanceof AppCpAuthorityError && error4.reason === "response";
|
|
40312
|
+
const next = outcome ? null : foreign ? { kind: "unreachable" } : appLogHealth(error4);
|
|
40313
|
+
if (!outcome && (next === null || next.kind === "auth")) {
|
|
40314
|
+
refuse(error4);
|
|
40315
|
+
return;
|
|
40316
|
+
}
|
|
40317
|
+
unhealthy = next ? unhealthy + 1 : 0;
|
|
40318
|
+
if (outcome !== problem4 || JSON.stringify(next) !== JSON.stringify(health)) {
|
|
40319
|
+
problem4 = outcome;
|
|
40320
|
+
health = next;
|
|
40321
|
+
changed();
|
|
40322
|
+
}
|
|
40323
|
+
};
|
|
40122
40324
|
const schedule = () => {
|
|
40123
40325
|
if (!started || !current2()) return;
|
|
40124
40326
|
if (!foreground && !flushing() && !kicked) {
|
|
@@ -40134,13 +40336,16 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40134
40336
|
if (!current2()) return;
|
|
40135
40337
|
const wake = foreground && Date.now() >= wakeAt;
|
|
40136
40338
|
if (wake) wakeAt = Number.POSITIVE_INFINITY;
|
|
40137
|
-
const work2 = driving = wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
|
|
40339
|
+
const work2 = driving = !logBound ? bindLog().then(() => host.driveCollection({ signal: life.signal })) : wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
|
|
40138
40340
|
void work2.then(
|
|
40139
40341
|
() => {
|
|
40140
40342
|
if (driving === work2) driving = null;
|
|
40141
40343
|
failures = 0;
|
|
40142
40344
|
turned = true;
|
|
40143
|
-
if (current2())
|
|
40345
|
+
if (current2()) {
|
|
40346
|
+
observe();
|
|
40347
|
+
settleKeys();
|
|
40348
|
+
}
|
|
40144
40349
|
settleInitial?.();
|
|
40145
40350
|
schedule();
|
|
40146
40351
|
},
|
|
@@ -40149,7 +40354,12 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40149
40354
|
(error4) => {
|
|
40150
40355
|
if (driving === work2) driving = null;
|
|
40151
40356
|
failures++;
|
|
40152
|
-
if (current2())
|
|
40357
|
+
if (current2()) {
|
|
40358
|
+
if (logBound) {
|
|
40359
|
+
observe();
|
|
40360
|
+
settleKeys();
|
|
40361
|
+
} else bindFailed(error4);
|
|
40362
|
+
}
|
|
40153
40363
|
turnFailed(error4);
|
|
40154
40364
|
settleInitial?.();
|
|
40155
40365
|
schedule();
|
|
@@ -40301,24 +40511,40 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40301
40511
|
report("opening_store");
|
|
40302
40512
|
await host.openCollectionSql({ signal: life.signal, replicaId: receipt3.deviceId, endpoint: 0, openSql: openLedgerSql });
|
|
40303
40513
|
check3();
|
|
40304
|
-
|
|
40305
|
-
|
|
40306
|
-
|
|
40307
|
-
|
|
40308
|
-
|
|
40309
|
-
check3();
|
|
40310
|
-
break;
|
|
40311
|
-
} catch (error4) {
|
|
40312
|
-
check3();
|
|
40313
|
-
const server = error4 instanceof AppCpAuthorityError && error4.reason === "unavailable" ? error4.server : null;
|
|
40314
|
-
if (!isAppServerProblem(server) || server.status !== 503 || server.code !== "busy" || appServerOutcome(server) !== null || attempt + 1 >= INITIAL_LOG_ATTEMPTS) throw error4;
|
|
40315
|
-
const wait2 = server.retryAfterMs ?? 1e3 * 2 ** attempt;
|
|
40316
|
-
if (Date.now() + wait2 >= retryUntil) throw error4;
|
|
40317
|
-
await waitForOpenRetry(wait2, life.signal);
|
|
40318
|
-
check3();
|
|
40319
|
-
if (Date.now() >= retryUntil) throw error4;
|
|
40320
|
-
}
|
|
40514
|
+
let holdsLog = true;
|
|
40515
|
+
try {
|
|
40516
|
+
holdsLog = host.holdsLog?.() ?? true;
|
|
40517
|
+
} catch {
|
|
40518
|
+
holdsLog = false;
|
|
40321
40519
|
}
|
|
40520
|
+
const localFirst = !isPrivate && holdsLog && await ledger.syncedBefore({ signal: life.signal });
|
|
40521
|
+
check3();
|
|
40522
|
+
const connectLog = async () => {
|
|
40523
|
+
report("connecting_log");
|
|
40524
|
+
const retryUntil = Date.now() + INITIAL_LOG_RETRY_MS;
|
|
40525
|
+
for (let attempt = 0; ; attempt++) {
|
|
40526
|
+
try {
|
|
40527
|
+
await startLog();
|
|
40528
|
+
check3();
|
|
40529
|
+
break;
|
|
40530
|
+
} catch (error4) {
|
|
40531
|
+
check3();
|
|
40532
|
+
const server = error4 instanceof AppCpAuthorityError && error4.reason === "unavailable" ? error4.server : null;
|
|
40533
|
+
if (!isAppServerProblem(server) || server.status !== 503 || server.code !== "busy" || appServerOutcome(server) !== null || attempt + 1 >= INITIAL_LOG_ATTEMPTS) throw error4;
|
|
40534
|
+
const wait2 = server.retryAfterMs ?? 1e3 * 2 ** attempt;
|
|
40535
|
+
if (Date.now() + wait2 >= retryUntil) throw error4;
|
|
40536
|
+
await waitForOpenRetry(wait2, life.signal);
|
|
40537
|
+
check3();
|
|
40538
|
+
if (Date.now() >= retryUntil) throw error4;
|
|
40539
|
+
}
|
|
40540
|
+
}
|
|
40541
|
+
logBound = true;
|
|
40542
|
+
};
|
|
40543
|
+
if (localFirst) {
|
|
40544
|
+
early = startLog();
|
|
40545
|
+
void early.catch(() => {
|
|
40546
|
+
});
|
|
40547
|
+
} else await connectLog();
|
|
40322
40548
|
const generation = crypto.randomUUID();
|
|
40323
40549
|
let attachOpened = null;
|
|
40324
40550
|
let detachOpened = null;
|
|
@@ -40333,44 +40559,48 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40333
40559
|
}
|
|
40334
40560
|
}
|
|
40335
40561
|
const finishOpen = async () => {
|
|
40336
|
-
const warm = await ledger.syncedBefore({ signal: life.signal });
|
|
40337
|
-
check3();
|
|
40338
|
-
report("syncing");
|
|
40339
|
-
await host.waitForVerifiedRead({ signal: life.signal, cold: !warm, onProgress: report });
|
|
40562
|
+
const warm = isPrivate ? await ledger.syncedBefore({ signal: life.signal }) : localFirst;
|
|
40340
40563
|
check3();
|
|
40341
|
-
|
|
40342
|
-
|
|
40343
|
-
|
|
40344
|
-
|
|
40345
|
-
|
|
40346
|
-
|
|
40347
|
-
|
|
40348
|
-
|
|
40349
|
-
|
|
40350
|
-
|
|
40351
|
-
|
|
40352
|
-
|
|
40353
|
-
|
|
40354
|
-
|
|
40355
|
-
|
|
40356
|
-
kick();
|
|
40357
|
-
onLive(false);
|
|
40358
|
-
settleInitial?.();
|
|
40359
|
-
changed();
|
|
40360
|
-
}
|
|
40361
|
-
}) : null;
|
|
40362
|
-
} catch {
|
|
40363
|
-
push = null;
|
|
40564
|
+
let local = false;
|
|
40565
|
+
if (localFirst && !logBound) {
|
|
40566
|
+
try {
|
|
40567
|
+
await host.waitForVerifiedRead({ signal: life.signal, local: true, timeoutMs: LOCAL_READ_MS, onProgress: report });
|
|
40568
|
+
check3();
|
|
40569
|
+
local = true;
|
|
40570
|
+
} catch {
|
|
40571
|
+
check3();
|
|
40572
|
+
const first = early;
|
|
40573
|
+
early = null;
|
|
40574
|
+
const bound = first ? await first.then(() => true, () => false) : false;
|
|
40575
|
+
check3();
|
|
40576
|
+
if (bound) logBound = true;
|
|
40577
|
+
else await connectLog();
|
|
40578
|
+
}
|
|
40364
40579
|
}
|
|
40365
|
-
if (
|
|
40366
|
-
push.start();
|
|
40367
|
-
onLive(false);
|
|
40368
|
-
} else paused = true;
|
|
40369
|
-
if (warm) {
|
|
40580
|
+
if (!local) {
|
|
40370
40581
|
report("syncing");
|
|
40371
|
-
await
|
|
40582
|
+
await host.waitForVerifiedRead({ signal: life.signal, cold: !warm, onProgress: report });
|
|
40583
|
+
check3();
|
|
40584
|
+
}
|
|
40585
|
+
started = true;
|
|
40586
|
+
host.onLogWork?.(kick);
|
|
40587
|
+
if (local) {
|
|
40588
|
+
keys2 = "pending";
|
|
40589
|
+
kicked = true;
|
|
40590
|
+
schedule();
|
|
40591
|
+
pausing = setTimeout(() => {
|
|
40592
|
+
pausing = null;
|
|
40593
|
+
if (current2() && !push?.live) setPaused(true);
|
|
40594
|
+
}, PAUSED_AFTER_MS);
|
|
40595
|
+
} else {
|
|
40596
|
+
schedule();
|
|
40597
|
+
openPush();
|
|
40598
|
+
if (warm) {
|
|
40599
|
+
report("syncing");
|
|
40600
|
+
await waitForWarmSync();
|
|
40601
|
+
}
|
|
40602
|
+
check3();
|
|
40372
40603
|
}
|
|
40373
|
-
check3();
|
|
40374
40604
|
report("checking_setup");
|
|
40375
40605
|
void ledger.markSynced({ signal: life.signal }).catch(() => {
|
|
40376
40606
|
});
|
|
@@ -40557,6 +40787,9 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40557
40787
|
get ended() {
|
|
40558
40788
|
return current2() ? ended : null;
|
|
40559
40789
|
},
|
|
40790
|
+
get failure() {
|
|
40791
|
+
return current2() ? failure2 : null;
|
|
40792
|
+
},
|
|
40560
40793
|
connection,
|
|
40561
40794
|
close,
|
|
40562
40795
|
subscribe(listener) {
|
|
@@ -41280,6 +41513,8 @@ function wipeUnlockSecret(request3) {
|
|
|
41280
41513
|
}
|
|
41281
41514
|
}
|
|
41282
41515
|
var APPLICATION_WORKER_MAX_CLIENTS = 32;
|
|
41516
|
+
var LIST_CONFIRM_MS = 2e3;
|
|
41517
|
+
var LIST_CONFIRM_TRIES = 3;
|
|
41283
41518
|
var ApplicationWorkerHost = class {
|
|
41284
41519
|
session;
|
|
41285
41520
|
lifetime = new AbortController();
|
|
@@ -41293,6 +41528,10 @@ var ApplicationWorkerHost = class {
|
|
|
41293
41528
|
sessionForeground = null;
|
|
41294
41529
|
collecting = false;
|
|
41295
41530
|
revalidationQueued = false;
|
|
41531
|
+
/** A signed-in start showed the kept collection list; Connect's is read once. */
|
|
41532
|
+
listConfirm = "idle";
|
|
41533
|
+
listTimer = null;
|
|
41534
|
+
listAttempts = 0;
|
|
41296
41535
|
nativeRpc = 0;
|
|
41297
41536
|
nativeWait = null;
|
|
41298
41537
|
/** The latest proxy portal's person-closed report (page `approvalClosed`). */
|
|
@@ -41697,18 +41936,55 @@ var ApplicationWorkerHost = class {
|
|
|
41697
41936
|
/** One inventory read, in the SAME queue as lifecycle actions. Visibility and
|
|
41698
41937
|
* native transport wake immediately; metadata cannot make a tab's start/select
|
|
41699
41938
|
* fail busy or overlap an original consent/receipt operation. */
|
|
41700
|
-
revalidateForeground() {
|
|
41939
|
+
revalidateForeground(shown = true) {
|
|
41701
41940
|
if (this.stopping || this.revalidationQueued) return;
|
|
41702
41941
|
this.revalidationQueued = true;
|
|
41703
41942
|
this.queue.push({ client: null, request: null, job: async () => {
|
|
41704
41943
|
try {
|
|
41705
|
-
if (this.foreground && this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41944
|
+
if ((this.foreground || !shown) && this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41706
41945
|
} finally {
|
|
41707
41946
|
this.revalidationQueued = false;
|
|
41708
41947
|
}
|
|
41709
41948
|
} });
|
|
41710
41949
|
this.pump();
|
|
41711
41950
|
}
|
|
41951
|
+
/** A signed-in start published the collection list this installation kept
|
|
41952
|
+
* and did not wait for Connect. Read Connect's list once, in the same queue,
|
|
41953
|
+
* where it cannot delay the page: after the page's next request (opening its
|
|
41954
|
+
* remembered collection), at once when a collection is already open, or
|
|
41955
|
+
* shortly after when no request comes (the chooser is showing). */
|
|
41956
|
+
confirmListing(after) {
|
|
41957
|
+
if (this.stopping || this.listConfirm === "done") return;
|
|
41958
|
+
if (after === "start") {
|
|
41959
|
+
if (this.listConfirm !== "idle" || !this.session.collectionListCached()) return;
|
|
41960
|
+
this.listConfirm = "armed";
|
|
41961
|
+
if (this.session.heldCollections().length === 0) {
|
|
41962
|
+
this.listTimer = setTimeout(() => {
|
|
41963
|
+
this.listTimer = null;
|
|
41964
|
+
this.confirmListing("timer");
|
|
41965
|
+
}, LIST_CONFIRM_MS);
|
|
41966
|
+
return;
|
|
41967
|
+
}
|
|
41968
|
+
} else if (this.listConfirm !== "armed") return;
|
|
41969
|
+
if (this.listTimer !== null) clearTimeout(this.listTimer);
|
|
41970
|
+
this.listTimer = null;
|
|
41971
|
+
this.listConfirm = "done";
|
|
41972
|
+
if (!this.session.collectionListCached()) return;
|
|
41973
|
+
const attempt = ++this.listAttempts;
|
|
41974
|
+
this.queue.push({ client: null, request: null, job: async () => {
|
|
41975
|
+
try {
|
|
41976
|
+
if (this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41977
|
+
} catch {
|
|
41978
|
+
}
|
|
41979
|
+
if (this.stopping || !this.session.collectionListCached() || attempt >= LIST_CONFIRM_TRIES) return;
|
|
41980
|
+
this.listConfirm = "armed";
|
|
41981
|
+
this.listTimer = setTimeout(() => {
|
|
41982
|
+
this.listTimer = null;
|
|
41983
|
+
this.confirmListing("timer");
|
|
41984
|
+
}, LIST_CONFIRM_MS);
|
|
41985
|
+
} });
|
|
41986
|
+
this.pump();
|
|
41987
|
+
}
|
|
41712
41988
|
/** `wake`: a page reported visible/focus/online, so a visible connection
|
|
41713
41989
|
* syncs now even if it was already in the foreground. `report`: a tab told
|
|
41714
41990
|
* us, so the session hears it even when the aggregate did not change. */
|
|
@@ -41848,6 +42124,7 @@ var ApplicationWorkerHost = class {
|
|
|
41848
42124
|
this.active = null;
|
|
41849
42125
|
this.reconcile();
|
|
41850
42126
|
this.applyForeground();
|
|
42127
|
+
if (next.client !== null) this.confirmListing(next.request[3] === "start" ? "start" : "request");
|
|
41851
42128
|
this.pump();
|
|
41852
42129
|
}
|
|
41853
42130
|
};
|
|
@@ -41992,6 +42269,8 @@ var ApplicationWorkerHost = class {
|
|
|
41992
42269
|
* original SDK leases; a subsequent call returns the SAME rejection. */
|
|
41993
42270
|
stop(detach = true) {
|
|
41994
42271
|
if (this.stopping) return this.stopping;
|
|
42272
|
+
if (this.listTimer !== null) clearTimeout(this.listTimer);
|
|
42273
|
+
this.listTimer = null;
|
|
41995
42274
|
const pending = this.pending;
|
|
41996
42275
|
for (const item of this.queue) wipeUnlockSecret(item.request);
|
|
41997
42276
|
this.queue.length = 0;
|