@mdbase-dev/sdk 0.1.0-beta.178 → 0.1.0-beta.179
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 +48 -0
- package/dist/app-host/cloud-copy-host.d.ts +9 -0
- package/dist/app-host/cloud-copy-host.js +26 -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 +197 -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 +340 -80
- package/dist/browser/mdbase-sdk.js +14383 -14057
- 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;
|
|
@@ -36932,6 +36949,15 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
36932
36949
|
return null;
|
|
36933
36950
|
}
|
|
36934
36951
|
}
|
|
36952
|
+
/** For a collection opened before its log: has the reopened replica its
|
|
36953
|
+
* keys back from the log's control prefix? `failed` is what a waited open
|
|
36954
|
+
* refuses as `recovery_required`. */
|
|
36955
|
+
keyringSettled() {
|
|
36956
|
+
if (!this.native) throw fail15("binding");
|
|
36957
|
+
const observation = this.native.observations();
|
|
36958
|
+
if (observation.requiresReopen || observation.keyringRebuildFailed) return "failed";
|
|
36959
|
+
return observation.keyringRebuilding ? "pending" : "settled";
|
|
36960
|
+
}
|
|
36935
36961
|
/** Local writes not yet confirmed by the log (native sync status). */
|
|
36936
36962
|
pendingWrites() {
|
|
36937
36963
|
try {
|
|
@@ -37016,16 +37042,16 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37016
37042
|
* behind can finish its catch-up turn; without a pump it is fixed.
|
|
37017
37043
|
* No status-based admission, write retry, key override or private grant. */
|
|
37018
37044
|
waitForVerifiedRead(options) {
|
|
37019
|
-
const { timeoutMs = 15e3, cold = false } = options;
|
|
37045
|
+
const { timeoutMs = 15e3, cold = false, local = false } = options;
|
|
37020
37046
|
const signal = this.operationSignal(options.signal);
|
|
37021
37047
|
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)
|
|
37048
|
+
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
37049
|
throw fail15("binding");
|
|
37024
37050
|
if (this.readWork)
|
|
37025
37051
|
return this.readWork.then(() => {
|
|
37026
37052
|
this.checkCollection(signal);
|
|
37027
37053
|
});
|
|
37028
|
-
const work2 = this.verifyRead(signal, timeoutMs, cold, options.onProgress);
|
|
37054
|
+
const work2 = this.verifyRead(signal, timeoutMs, cold, local, options.onProgress);
|
|
37029
37055
|
this.readWork = work2;
|
|
37030
37056
|
void work2.finally(() => {
|
|
37031
37057
|
if (this.readWork === work2) this.readWork = null;
|
|
@@ -37033,8 +37059,9 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37033
37059
|
});
|
|
37034
37060
|
return work2;
|
|
37035
37061
|
}
|
|
37036
|
-
async verifyRead(signal, timeoutMs, cold, onProgress) {
|
|
37062
|
+
async verifyRead(signal, timeoutMs, cold, local, onProgress) {
|
|
37037
37063
|
const ctrl = new AbortController(), abort = () => ctrl.abort();
|
|
37064
|
+
const pump = () => local ? null : this.pump;
|
|
37038
37065
|
this.readAbort = ctrl;
|
|
37039
37066
|
signal.addEventListener("abort", abort, { once: true });
|
|
37040
37067
|
let timer = null;
|
|
@@ -37057,7 +37084,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37057
37084
|
};
|
|
37058
37085
|
const observe = () => {
|
|
37059
37086
|
this.checkCollection(signal);
|
|
37060
|
-
const refusal2 = cold ?
|
|
37087
|
+
const refusal2 = cold ? pump().terminalProblem : null;
|
|
37061
37088
|
if (isAppServerProblem(refusal2))
|
|
37062
37089
|
throw new AppHttpLogError(appServerOutcome(refusal2) ?? "unavailable", refusal2);
|
|
37063
37090
|
const observation = this.native.observations();
|
|
@@ -37068,14 +37095,14 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37068
37095
|
};
|
|
37069
37096
|
this.readVerified = false;
|
|
37070
37097
|
try {
|
|
37071
|
-
if (
|
|
37098
|
+
if (pump()) {
|
|
37072
37099
|
const progress2 = () => {
|
|
37073
37100
|
const observation = observe();
|
|
37074
37101
|
return [
|
|
37075
37102
|
observation.status.confirmedThrough,
|
|
37076
37103
|
observation.status.installing?.done ?? 0,
|
|
37077
37104
|
observation.status.installing?.total ?? 0,
|
|
37078
|
-
|
|
37105
|
+
pump().readProgress
|
|
37079
37106
|
];
|
|
37080
37107
|
};
|
|
37081
37108
|
let high = progress2(), lastProgress = Date.now();
|
|
@@ -37089,7 +37116,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37089
37116
|
deferred2 = null;
|
|
37090
37117
|
}
|
|
37091
37118
|
high = next.map((value2, i) => Math.max(value2, high[i]));
|
|
37092
|
-
const active =
|
|
37119
|
+
const active = pump().responseProgress();
|
|
37093
37120
|
const ids = new Set(active.map((body) => body.id));
|
|
37094
37121
|
for (const id4 of bodies.keys()) if (!ids.has(id4)) bodies.delete(id4);
|
|
37095
37122
|
for (const body of active) {
|
|
@@ -37134,7 +37161,8 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37134
37161
|
let delay = 250;
|
|
37135
37162
|
while (!ctrl.signal.aborted) {
|
|
37136
37163
|
this.native.tick();
|
|
37137
|
-
|
|
37164
|
+
const turn = pump();
|
|
37165
|
+
if (turn) await Promise.race([turn.pump(), aborted]);
|
|
37138
37166
|
this.checkCollection(signal);
|
|
37139
37167
|
const observation = observe();
|
|
37140
37168
|
if (cold && initialSyncWait(observation.status) !== null) {
|
|
@@ -37153,7 +37181,7 @@ var AppWebCloudCopyHost = class _AppWebCloudCopyHost {
|
|
|
37153
37181
|
return;
|
|
37154
37182
|
} catch (error4) {
|
|
37155
37183
|
this.checkCollection(signal);
|
|
37156
|
-
if (!(error4 instanceof MdbaseError) || error4.code !== "unavailable" || !
|
|
37184
|
+
if (!(error4 instanceof MdbaseError) || error4.code !== "unavailable" || !pump() || ctrl.signal.aborted)
|
|
37157
37185
|
throw error4;
|
|
37158
37186
|
}
|
|
37159
37187
|
await waitForOpenRetry(delay, ctrl.signal);
|
|
@@ -37787,6 +37815,7 @@ var AppWebSignInSession = class {
|
|
|
37787
37815
|
attested = false;
|
|
37788
37816
|
acknowledged = false;
|
|
37789
37817
|
consentPending = false;
|
|
37818
|
+
listCached = false;
|
|
37790
37819
|
fileAccess = null;
|
|
37791
37820
|
freshApproval = false;
|
|
37792
37821
|
/** The window this session's approval (or file access request) is shown in. */
|
|
@@ -37980,7 +38009,15 @@ var AppWebSignInSession = class {
|
|
|
37980
38009
|
if (this.flow.collectionConsentView()?.state === "pending") {
|
|
37981
38010
|
this.consentPending = true;
|
|
37982
38011
|
this.publish({ status: "authorizing" });
|
|
37983
|
-
} else
|
|
38012
|
+
} else {
|
|
38013
|
+
let kept = null;
|
|
38014
|
+
try {
|
|
38015
|
+
kept = this.flow.cachedApprovedCollections(this.installation);
|
|
38016
|
+
} catch {
|
|
38017
|
+
kept = null;
|
|
38018
|
+
}
|
|
38019
|
+
await this.refreshCollections(kept);
|
|
38020
|
+
}
|
|
37984
38021
|
} else this.publish({ status: view.state === "pending" ? "signed_out" : "authorizing" });
|
|
37985
38022
|
return this.getSnapshot();
|
|
37986
38023
|
});
|
|
@@ -38338,10 +38375,15 @@ var AppWebSignInSession = class {
|
|
|
38338
38375
|
this.acknowledged = true;
|
|
38339
38376
|
}
|
|
38340
38377
|
}
|
|
38341
|
-
|
|
38342
|
-
|
|
38378
|
+
/** `kept`: this installation's last listing instead of a read. It is not an
|
|
38379
|
+
* accepted inventory, so an approval missing from it is not a removal. */
|
|
38380
|
+
async refreshCollections(kept = null) {
|
|
38381
|
+
const collections = kept ?? await this.flow.listApprovedCollections(this.installation);
|
|
38343
38382
|
this.check();
|
|
38344
|
-
|
|
38383
|
+
this.listCached = kept !== null;
|
|
38384
|
+
if (!kept) {
|
|
38385
|
+
for (const id4 of this.flow.view().collectionIds) if (!collections.some((c) => c.collectionId === id4)) this.removedScopes.add(id4);
|
|
38386
|
+
}
|
|
38345
38387
|
const visible = Object.freeze(collections.filter((c) => !this.forgotten.has(c.collectionId)));
|
|
38346
38388
|
for (const slot of this.slots.values()) {
|
|
38347
38389
|
if (slot.usable && slot.selected && !visible.some((c) => c.collectionId === slot.id)) {
|
|
@@ -38549,14 +38591,24 @@ var AppWebSignInSession = class {
|
|
|
38549
38591
|
if (this.openingSlot === slot) this.openingSlot = null;
|
|
38550
38592
|
slot.stopAssessment = selected.subscribe?.(() => {
|
|
38551
38593
|
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
|
|
38594
|
+
let ended2 = null;
|
|
38595
|
+
try {
|
|
38596
|
+
ended2 = selected.ended ?? null;
|
|
38597
|
+
} catch {
|
|
38598
|
+
ended2 = null;
|
|
38599
|
+
}
|
|
38600
|
+
if (ended2) {
|
|
38601
|
+
this.accessEnded(slot, selected, ended2);
|
|
38602
|
+
return;
|
|
38603
|
+
}
|
|
38604
|
+
let failed2 = null;
|
|
38553
38605
|
try {
|
|
38554
|
-
|
|
38606
|
+
failed2 = selected.failure ?? null;
|
|
38555
38607
|
} catch {
|
|
38556
|
-
|
|
38608
|
+
failed2 = null;
|
|
38557
38609
|
}
|
|
38558
|
-
if (
|
|
38559
|
-
this.
|
|
38610
|
+
if (failed2 !== null) {
|
|
38611
|
+
this.openFailedLate(slot, selected, failed2);
|
|
38560
38612
|
return;
|
|
38561
38613
|
}
|
|
38562
38614
|
let next;
|
|
@@ -38567,6 +38619,26 @@ var AppWebSignInSession = class {
|
|
|
38567
38619
|
}
|
|
38568
38620
|
this.publishSlot(slot, next.status === "needs_unlock" ? { ...next, problem: null, transport: null, live: null } : { ...next, ...this.liveProblem(slot, selected) });
|
|
38569
38621
|
}) ?? null;
|
|
38622
|
+
let ended = null;
|
|
38623
|
+
try {
|
|
38624
|
+
ended = selected.ended ?? null;
|
|
38625
|
+
} catch {
|
|
38626
|
+
ended = null;
|
|
38627
|
+
}
|
|
38628
|
+
let failed = null;
|
|
38629
|
+
try {
|
|
38630
|
+
failed = selected.failure ?? null;
|
|
38631
|
+
} catch {
|
|
38632
|
+
failed = null;
|
|
38633
|
+
}
|
|
38634
|
+
if (ended) {
|
|
38635
|
+
this.accessEnded(slot, selected, ended);
|
|
38636
|
+
return;
|
|
38637
|
+
}
|
|
38638
|
+
if (failed !== null) {
|
|
38639
|
+
this.openFailedLate(slot, selected, failed);
|
|
38640
|
+
return;
|
|
38641
|
+
}
|
|
38570
38642
|
this.publishSlot(slot, { ...first, transport: null, live: null, openingProgress: null });
|
|
38571
38643
|
}
|
|
38572
38644
|
/** The adapter's own state, validated: `needs_unlock` only with its lock
|
|
@@ -38654,6 +38726,35 @@ var AppWebSignInSession = class {
|
|
|
38654
38726
|
};
|
|
38655
38727
|
stop();
|
|
38656
38728
|
}
|
|
38729
|
+
/** A collection opened from its replica, whose log was then refused. The
|
|
38730
|
+
* same block and detail as a failed open; stopping it fences every page's
|
|
38731
|
+
* data ports. Local data, unsent edits included, stays on this device. */
|
|
38732
|
+
openFailedLate(slot, selected, error4) {
|
|
38733
|
+
if (slot.selected !== selected || !slot.usable) return;
|
|
38734
|
+
const { problem: problem4, detail } = webSignInProblem(error4);
|
|
38735
|
+
if (problem4 === "open_failed") {
|
|
38736
|
+
try {
|
|
38737
|
+
console.error("mdbase web sign-in open failed", detail.name, detail.code);
|
|
38738
|
+
} catch {
|
|
38739
|
+
}
|
|
38740
|
+
}
|
|
38741
|
+
slot.usable = false;
|
|
38742
|
+
this.publishSlot(slot, { status: "blocked", problem: problem4, problemDetail: detail, transport: null, live: null });
|
|
38743
|
+
const stop = () => {
|
|
38744
|
+
if (this.closing || this.options.signal.aborted || slot.selected !== selected || this.slots.get(slot.id) !== slot) return;
|
|
38745
|
+
if (this.pending) {
|
|
38746
|
+
void this.pending.catch(() => {
|
|
38747
|
+
}).then(stop);
|
|
38748
|
+
return;
|
|
38749
|
+
}
|
|
38750
|
+
void this.operation(async () => {
|
|
38751
|
+
await this.closeSlot(slot);
|
|
38752
|
+
this.publishSlot(slot, { status: "blocked", problem: problem4, problemDetail: detail });
|
|
38753
|
+
}, true).catch(() => {
|
|
38754
|
+
});
|
|
38755
|
+
};
|
|
38756
|
+
stop();
|
|
38757
|
+
}
|
|
38657
38758
|
/** Live problem of an open collection: shown while it lasts, cleared after. */
|
|
38658
38759
|
liveProblem(slot, selected) {
|
|
38659
38760
|
let server = null;
|
|
@@ -38729,6 +38830,12 @@ var AppWebSignInSession = class {
|
|
|
38729
38830
|
return this.getSnapshot();
|
|
38730
38831
|
}, true);
|
|
38731
38832
|
}
|
|
38833
|
+
/** The published list is the one this installation kept, shown by a
|
|
38834
|
+
* signed-in start; Connect's has not been read since. The owner calls
|
|
38835
|
+
* `refreshCollectionList` once, when that read can no longer delay a page. */
|
|
38836
|
+
collectionListCached() {
|
|
38837
|
+
return this.listCached;
|
|
38838
|
+
}
|
|
38732
38839
|
/** Accepted COMPLETE inventory omission or terminal native/log refusal only.
|
|
38733
38840
|
* Local Forget, failed GETs and transient transport failures do not qualify. */
|
|
38734
38841
|
scopeWasRemoved(collectionId) {
|
|
@@ -39184,6 +39291,10 @@ var AppScopedSignInSession = class {
|
|
|
39184
39291
|
await session.pollFileAccess(requestId);
|
|
39185
39292
|
return this.getSnapshot();
|
|
39186
39293
|
}
|
|
39294
|
+
/** Any of the sessions still shows the list it kept (a signed-in start). */
|
|
39295
|
+
collectionListCached() {
|
|
39296
|
+
return this.original.collectionListCached() || [...this.scoped.values()].some((actor) => actor.session.collectionListCached());
|
|
39297
|
+
}
|
|
39187
39298
|
async refreshCollectionList() {
|
|
39188
39299
|
await this.original.refreshCollectionList();
|
|
39189
39300
|
for (const actor of this.scoped.values()) {
|
|
@@ -39919,6 +40030,7 @@ function createAppCollectionSql(source) {
|
|
|
39919
40030
|
var PAUSED_AFTER_MS = 1e4;
|
|
39920
40031
|
var BACKGROUND_FLUSH_MS = 12e4;
|
|
39921
40032
|
var WARM_SYNC_MS = 2e3;
|
|
40033
|
+
var LOCAL_READ_MS = 5e3;
|
|
39922
40034
|
var INITIAL_LOG_RETRY_MS = 15e3;
|
|
39923
40035
|
var INITIAL_LOG_ATTEMPTS = 3;
|
|
39924
40036
|
var AppWebCollectionError = class extends Error {
|
|
@@ -39959,6 +40071,10 @@ function createAppWebCollectionAdapter(source) {
|
|
|
39959
40071
|
let assessment = "setup_required";
|
|
39960
40072
|
let locked = null;
|
|
39961
40073
|
let started = false;
|
|
40074
|
+
let logBound = false;
|
|
40075
|
+
let early = null;
|
|
40076
|
+
let failure2 = null;
|
|
40077
|
+
let keys2 = "settled";
|
|
39962
40078
|
let push = null, paused = false, pausing = null, kicked = false;
|
|
39963
40079
|
let ended = null;
|
|
39964
40080
|
const attached = /* @__PURE__ */ new Map();
|
|
@@ -40119,6 +40235,79 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40119
40235
|
return false;
|
|
40120
40236
|
}
|
|
40121
40237
|
};
|
|
40238
|
+
const startLog = () => host.startCollectionLog({ signal: life.signal, fetch: options.fetch, allowLoopbackHttp: options.allowLoopbackHttp });
|
|
40239
|
+
const openPush = () => {
|
|
40240
|
+
try {
|
|
40241
|
+
push = typeof host.openLogPush === "function" ? host.openLogPush({
|
|
40242
|
+
push: (frame) => {
|
|
40243
|
+
if (!current2()) return;
|
|
40244
|
+
host.pushLog(frame);
|
|
40245
|
+
kick();
|
|
40246
|
+
},
|
|
40247
|
+
live: onLive,
|
|
40248
|
+
// Access ended: the replica has the `closed` push; drive so it
|
|
40249
|
+
// settles, then the session stops this collection in every tab.
|
|
40250
|
+
ended: (reason) => {
|
|
40251
|
+
ended = reason;
|
|
40252
|
+
kick();
|
|
40253
|
+
onLive(false);
|
|
40254
|
+
settleInitial?.();
|
|
40255
|
+
changed();
|
|
40256
|
+
}
|
|
40257
|
+
}) : null;
|
|
40258
|
+
} catch {
|
|
40259
|
+
push = null;
|
|
40260
|
+
}
|
|
40261
|
+
if (push) {
|
|
40262
|
+
push.start();
|
|
40263
|
+
onLive(false);
|
|
40264
|
+
} else setPaused(true);
|
|
40265
|
+
};
|
|
40266
|
+
const bindLog = async () => {
|
|
40267
|
+
const first = early;
|
|
40268
|
+
early = null;
|
|
40269
|
+
await (first ?? startLog());
|
|
40270
|
+
check3();
|
|
40271
|
+
logBound = true;
|
|
40272
|
+
openPush();
|
|
40273
|
+
};
|
|
40274
|
+
const settleKeys = () => {
|
|
40275
|
+
if (keys2 !== "pending" || !logBound) return;
|
|
40276
|
+
try {
|
|
40277
|
+
keys2 = host.keyringSettled?.() ?? "settled";
|
|
40278
|
+
} catch {
|
|
40279
|
+
return;
|
|
40280
|
+
}
|
|
40281
|
+
if (keys2 === "failed") refuse(new AppCloudCopyHostError("recovery_required"));
|
|
40282
|
+
};
|
|
40283
|
+
const refuse = (error4) => {
|
|
40284
|
+
failure2 = error4;
|
|
40285
|
+
started = false;
|
|
40286
|
+
if (timer !== null) clearTimeout(timer);
|
|
40287
|
+
timer = null;
|
|
40288
|
+
changed();
|
|
40289
|
+
};
|
|
40290
|
+
const bindFailed = (error4) => {
|
|
40291
|
+
let server = null;
|
|
40292
|
+
try {
|
|
40293
|
+
server = error4.server;
|
|
40294
|
+
} catch {
|
|
40295
|
+
server = null;
|
|
40296
|
+
}
|
|
40297
|
+
const outcome = isAppServerProblem(server) && appServerOutcome(server) !== null ? server : null;
|
|
40298
|
+
const foreign = !isAppServerProblem(server) && error4 instanceof AppCpAuthorityError && error4.reason === "response";
|
|
40299
|
+
const next = outcome ? null : foreign ? { kind: "unreachable" } : appLogHealth(error4);
|
|
40300
|
+
if (!outcome && (next === null || next.kind === "auth")) {
|
|
40301
|
+
refuse(error4);
|
|
40302
|
+
return;
|
|
40303
|
+
}
|
|
40304
|
+
unhealthy = next ? unhealthy + 1 : 0;
|
|
40305
|
+
if (outcome !== problem4 || JSON.stringify(next) !== JSON.stringify(health)) {
|
|
40306
|
+
problem4 = outcome;
|
|
40307
|
+
health = next;
|
|
40308
|
+
changed();
|
|
40309
|
+
}
|
|
40310
|
+
};
|
|
40122
40311
|
const schedule = () => {
|
|
40123
40312
|
if (!started || !current2()) return;
|
|
40124
40313
|
if (!foreground && !flushing() && !kicked) {
|
|
@@ -40134,13 +40323,16 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40134
40323
|
if (!current2()) return;
|
|
40135
40324
|
const wake = foreground && Date.now() >= wakeAt;
|
|
40136
40325
|
if (wake) wakeAt = Number.POSITIVE_INFINITY;
|
|
40137
|
-
const work2 = driving = wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
|
|
40326
|
+
const work2 = driving = !logBound ? bindLog().then(() => host.driveCollection({ signal: life.signal })) : wake ? host.wakeCollectionLog({ signal: life.signal }) : host.driveCollection({ signal: life.signal });
|
|
40138
40327
|
void work2.then(
|
|
40139
40328
|
() => {
|
|
40140
40329
|
if (driving === work2) driving = null;
|
|
40141
40330
|
failures = 0;
|
|
40142
40331
|
turned = true;
|
|
40143
|
-
if (current2())
|
|
40332
|
+
if (current2()) {
|
|
40333
|
+
observe();
|
|
40334
|
+
settleKeys();
|
|
40335
|
+
}
|
|
40144
40336
|
settleInitial?.();
|
|
40145
40337
|
schedule();
|
|
40146
40338
|
},
|
|
@@ -40149,7 +40341,12 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40149
40341
|
(error4) => {
|
|
40150
40342
|
if (driving === work2) driving = null;
|
|
40151
40343
|
failures++;
|
|
40152
|
-
if (current2())
|
|
40344
|
+
if (current2()) {
|
|
40345
|
+
if (logBound) {
|
|
40346
|
+
observe();
|
|
40347
|
+
settleKeys();
|
|
40348
|
+
} else bindFailed(error4);
|
|
40349
|
+
}
|
|
40153
40350
|
turnFailed(error4);
|
|
40154
40351
|
settleInitial?.();
|
|
40155
40352
|
schedule();
|
|
@@ -40301,24 +40498,34 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40301
40498
|
report("opening_store");
|
|
40302
40499
|
await host.openCollectionSql({ signal: life.signal, replicaId: receipt3.deviceId, endpoint: 0, openSql: openLedgerSql });
|
|
40303
40500
|
check3();
|
|
40304
|
-
|
|
40305
|
-
|
|
40306
|
-
|
|
40307
|
-
|
|
40308
|
-
|
|
40309
|
-
|
|
40310
|
-
|
|
40311
|
-
|
|
40312
|
-
|
|
40313
|
-
|
|
40314
|
-
|
|
40315
|
-
|
|
40316
|
-
|
|
40317
|
-
|
|
40318
|
-
|
|
40319
|
-
|
|
40501
|
+
const localFirst = !isPrivate && await ledger.syncedBefore({ signal: life.signal });
|
|
40502
|
+
check3();
|
|
40503
|
+
const connectLog = async () => {
|
|
40504
|
+
report("connecting_log");
|
|
40505
|
+
const retryUntil = Date.now() + INITIAL_LOG_RETRY_MS;
|
|
40506
|
+
for (let attempt = 0; ; attempt++) {
|
|
40507
|
+
try {
|
|
40508
|
+
await startLog();
|
|
40509
|
+
check3();
|
|
40510
|
+
break;
|
|
40511
|
+
} catch (error4) {
|
|
40512
|
+
check3();
|
|
40513
|
+
const server = error4 instanceof AppCpAuthorityError && error4.reason === "unavailable" ? error4.server : null;
|
|
40514
|
+
if (!isAppServerProblem(server) || server.status !== 503 || server.code !== "busy" || appServerOutcome(server) !== null || attempt + 1 >= INITIAL_LOG_ATTEMPTS) throw error4;
|
|
40515
|
+
const wait2 = server.retryAfterMs ?? 1e3 * 2 ** attempt;
|
|
40516
|
+
if (Date.now() + wait2 >= retryUntil) throw error4;
|
|
40517
|
+
await waitForOpenRetry(wait2, life.signal);
|
|
40518
|
+
check3();
|
|
40519
|
+
if (Date.now() >= retryUntil) throw error4;
|
|
40520
|
+
}
|
|
40320
40521
|
}
|
|
40321
|
-
|
|
40522
|
+
logBound = true;
|
|
40523
|
+
};
|
|
40524
|
+
if (localFirst) {
|
|
40525
|
+
early = startLog();
|
|
40526
|
+
void early.catch(() => {
|
|
40527
|
+
});
|
|
40528
|
+
} else await connectLog();
|
|
40322
40529
|
const generation = crypto.randomUUID();
|
|
40323
40530
|
let attachOpened = null;
|
|
40324
40531
|
let detachOpened = null;
|
|
@@ -40333,44 +40540,48 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40333
40540
|
}
|
|
40334
40541
|
}
|
|
40335
40542
|
const finishOpen = async () => {
|
|
40336
|
-
const warm = await ledger.syncedBefore({ signal: life.signal });
|
|
40543
|
+
const warm = isPrivate ? await ledger.syncedBefore({ signal: life.signal }) : localFirst;
|
|
40337
40544
|
check3();
|
|
40338
|
-
|
|
40339
|
-
|
|
40340
|
-
|
|
40341
|
-
|
|
40342
|
-
|
|
40343
|
-
|
|
40344
|
-
|
|
40345
|
-
|
|
40346
|
-
|
|
40347
|
-
|
|
40348
|
-
|
|
40349
|
-
|
|
40350
|
-
|
|
40351
|
-
|
|
40352
|
-
|
|
40353
|
-
// settles, then the session stops this collection in every tab.
|
|
40354
|
-
ended: (reason) => {
|
|
40355
|
-
ended = reason;
|
|
40356
|
-
kick();
|
|
40357
|
-
onLive(false);
|
|
40358
|
-
settleInitial?.();
|
|
40359
|
-
changed();
|
|
40360
|
-
}
|
|
40361
|
-
}) : null;
|
|
40362
|
-
} catch {
|
|
40363
|
-
push = null;
|
|
40545
|
+
let local = false;
|
|
40546
|
+
if (localFirst && !logBound) {
|
|
40547
|
+
try {
|
|
40548
|
+
await host.waitForVerifiedRead({ signal: life.signal, local: true, timeoutMs: LOCAL_READ_MS, onProgress: report });
|
|
40549
|
+
check3();
|
|
40550
|
+
local = true;
|
|
40551
|
+
} catch {
|
|
40552
|
+
check3();
|
|
40553
|
+
const first = early;
|
|
40554
|
+
early = null;
|
|
40555
|
+
const bound = first ? await first.then(() => true, () => false) : false;
|
|
40556
|
+
check3();
|
|
40557
|
+
if (bound) logBound = true;
|
|
40558
|
+
else await connectLog();
|
|
40559
|
+
}
|
|
40364
40560
|
}
|
|
40365
|
-
if (
|
|
40366
|
-
push.start();
|
|
40367
|
-
onLive(false);
|
|
40368
|
-
} else paused = true;
|
|
40369
|
-
if (warm) {
|
|
40561
|
+
if (!local) {
|
|
40370
40562
|
report("syncing");
|
|
40371
|
-
await
|
|
40563
|
+
await host.waitForVerifiedRead({ signal: life.signal, cold: !warm, onProgress: report });
|
|
40564
|
+
check3();
|
|
40565
|
+
}
|
|
40566
|
+
started = true;
|
|
40567
|
+
host.onLogWork?.(kick);
|
|
40568
|
+
if (local) {
|
|
40569
|
+
keys2 = "pending";
|
|
40570
|
+
kicked = true;
|
|
40571
|
+
schedule();
|
|
40572
|
+
pausing = setTimeout(() => {
|
|
40573
|
+
pausing = null;
|
|
40574
|
+
if (current2() && !push?.live) setPaused(true);
|
|
40575
|
+
}, PAUSED_AFTER_MS);
|
|
40576
|
+
} else {
|
|
40577
|
+
schedule();
|
|
40578
|
+
openPush();
|
|
40579
|
+
if (warm) {
|
|
40580
|
+
report("syncing");
|
|
40581
|
+
await waitForWarmSync();
|
|
40582
|
+
}
|
|
40583
|
+
check3();
|
|
40372
40584
|
}
|
|
40373
|
-
check3();
|
|
40374
40585
|
report("checking_setup");
|
|
40375
40586
|
void ledger.markSynced({ signal: life.signal }).catch(() => {
|
|
40376
40587
|
});
|
|
@@ -40557,6 +40768,9 @@ function createAppWebCollectionAdapter(source) {
|
|
|
40557
40768
|
get ended() {
|
|
40558
40769
|
return current2() ? ended : null;
|
|
40559
40770
|
},
|
|
40771
|
+
get failure() {
|
|
40772
|
+
return current2() ? failure2 : null;
|
|
40773
|
+
},
|
|
40560
40774
|
connection,
|
|
40561
40775
|
close,
|
|
40562
40776
|
subscribe(listener) {
|
|
@@ -41280,6 +41494,8 @@ function wipeUnlockSecret(request3) {
|
|
|
41280
41494
|
}
|
|
41281
41495
|
}
|
|
41282
41496
|
var APPLICATION_WORKER_MAX_CLIENTS = 32;
|
|
41497
|
+
var LIST_CONFIRM_MS = 2e3;
|
|
41498
|
+
var LIST_CONFIRM_TRIES = 3;
|
|
41283
41499
|
var ApplicationWorkerHost = class {
|
|
41284
41500
|
session;
|
|
41285
41501
|
lifetime = new AbortController();
|
|
@@ -41293,6 +41509,10 @@ var ApplicationWorkerHost = class {
|
|
|
41293
41509
|
sessionForeground = null;
|
|
41294
41510
|
collecting = false;
|
|
41295
41511
|
revalidationQueued = false;
|
|
41512
|
+
/** A signed-in start showed the kept collection list; Connect's is read once. */
|
|
41513
|
+
listConfirm = "idle";
|
|
41514
|
+
listTimer = null;
|
|
41515
|
+
listAttempts = 0;
|
|
41296
41516
|
nativeRpc = 0;
|
|
41297
41517
|
nativeWait = null;
|
|
41298
41518
|
/** The latest proxy portal's person-closed report (page `approvalClosed`). */
|
|
@@ -41697,18 +41917,55 @@ var ApplicationWorkerHost = class {
|
|
|
41697
41917
|
/** One inventory read, in the SAME queue as lifecycle actions. Visibility and
|
|
41698
41918
|
* native transport wake immediately; metadata cannot make a tab's start/select
|
|
41699
41919
|
* fail busy or overlap an original consent/receipt operation. */
|
|
41700
|
-
revalidateForeground() {
|
|
41920
|
+
revalidateForeground(shown = true) {
|
|
41701
41921
|
if (this.stopping || this.revalidationQueued) return;
|
|
41702
41922
|
this.revalidationQueued = true;
|
|
41703
41923
|
this.queue.push({ client: null, request: null, job: async () => {
|
|
41704
41924
|
try {
|
|
41705
|
-
if (this.foreground && this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41925
|
+
if ((this.foreground || !shown) && this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41706
41926
|
} finally {
|
|
41707
41927
|
this.revalidationQueued = false;
|
|
41708
41928
|
}
|
|
41709
41929
|
} });
|
|
41710
41930
|
this.pump();
|
|
41711
41931
|
}
|
|
41932
|
+
/** A signed-in start published the collection list this installation kept
|
|
41933
|
+
* and did not wait for Connect. Read Connect's list once, in the same queue,
|
|
41934
|
+
* where it cannot delay the page: after the page's next request (opening its
|
|
41935
|
+
* remembered collection), at once when a collection is already open, or
|
|
41936
|
+
* shortly after when no request comes (the chooser is showing). */
|
|
41937
|
+
confirmListing(after) {
|
|
41938
|
+
if (this.stopping || this.listConfirm === "done") return;
|
|
41939
|
+
if (after === "start") {
|
|
41940
|
+
if (this.listConfirm !== "idle" || !this.session.collectionListCached()) return;
|
|
41941
|
+
this.listConfirm = "armed";
|
|
41942
|
+
if (this.session.heldCollections().length === 0) {
|
|
41943
|
+
this.listTimer = setTimeout(() => {
|
|
41944
|
+
this.listTimer = null;
|
|
41945
|
+
this.confirmListing("timer");
|
|
41946
|
+
}, LIST_CONFIRM_MS);
|
|
41947
|
+
return;
|
|
41948
|
+
}
|
|
41949
|
+
} else if (this.listConfirm !== "armed") return;
|
|
41950
|
+
if (this.listTimer !== null) clearTimeout(this.listTimer);
|
|
41951
|
+
this.listTimer = null;
|
|
41952
|
+
this.listConfirm = "done";
|
|
41953
|
+
if (!this.session.collectionListCached()) return;
|
|
41954
|
+
const attempt = ++this.listAttempts;
|
|
41955
|
+
this.queue.push({ client: null, request: null, job: async () => {
|
|
41956
|
+
try {
|
|
41957
|
+
if (this.session.getSnapshot().signIn?.state === "paired") await this.session.refreshCollectionList();
|
|
41958
|
+
} catch {
|
|
41959
|
+
}
|
|
41960
|
+
if (this.stopping || !this.session.collectionListCached() || attempt >= LIST_CONFIRM_TRIES) return;
|
|
41961
|
+
this.listConfirm = "armed";
|
|
41962
|
+
this.listTimer = setTimeout(() => {
|
|
41963
|
+
this.listTimer = null;
|
|
41964
|
+
this.confirmListing("timer");
|
|
41965
|
+
}, LIST_CONFIRM_MS);
|
|
41966
|
+
} });
|
|
41967
|
+
this.pump();
|
|
41968
|
+
}
|
|
41712
41969
|
/** `wake`: a page reported visible/focus/online, so a visible connection
|
|
41713
41970
|
* syncs now even if it was already in the foreground. `report`: a tab told
|
|
41714
41971
|
* us, so the session hears it even when the aggregate did not change. */
|
|
@@ -41848,6 +42105,7 @@ var ApplicationWorkerHost = class {
|
|
|
41848
42105
|
this.active = null;
|
|
41849
42106
|
this.reconcile();
|
|
41850
42107
|
this.applyForeground();
|
|
42108
|
+
if (next.client !== null) this.confirmListing(next.request[3] === "start" ? "start" : "request");
|
|
41851
42109
|
this.pump();
|
|
41852
42110
|
}
|
|
41853
42111
|
};
|
|
@@ -41992,6 +42250,8 @@ var ApplicationWorkerHost = class {
|
|
|
41992
42250
|
* original SDK leases; a subsequent call returns the SAME rejection. */
|
|
41993
42251
|
stop(detach = true) {
|
|
41994
42252
|
if (this.stopping) return this.stopping;
|
|
42253
|
+
if (this.listTimer !== null) clearTimeout(this.listTimer);
|
|
42254
|
+
this.listTimer = null;
|
|
41995
42255
|
const pending = this.pending;
|
|
41996
42256
|
for (const item of this.queue) wipeUnlockSecret(item.request);
|
|
41997
42257
|
this.queue.length = 0;
|