@tinycloud/node-sdk 3.2.6-beta.1 → 3.2.6-beta.2
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/dist/{WasmKeyProvider-LXRTjZTA.d.cts → WasmKeyProvider-CYU_dqOx.d.cts} +69 -3
- package/dist/{WasmKeyProvider-LXRTjZTA.d.ts → WasmKeyProvider-CYU_dqOx.d.ts} +69 -3
- package/dist/core.cjs +295 -59
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +295 -59
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +295 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +295 -59
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/core.cjs
CHANGED
|
@@ -461,6 +461,13 @@ function cloneAbilitiesMap(actions) {
|
|
|
461
461
|
var NodeUserAuthorization = class {
|
|
462
462
|
constructor(config) {
|
|
463
463
|
this.activeSessionKeyId = "default";
|
|
464
|
+
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
465
|
+
this.sessionGeneration = 0;
|
|
466
|
+
/**
|
|
467
|
+
* Sign-in saves run one at a time, in order, each together with its own
|
|
468
|
+
* cancellation cleanup, so no later write can land between the two.
|
|
469
|
+
*/
|
|
470
|
+
this.persistenceQueue = Promise.resolve();
|
|
464
471
|
this.extensions = [];
|
|
465
472
|
this._nodeFeatures = [];
|
|
466
473
|
this._nodeFeaturesKnown = false;
|
|
@@ -591,6 +598,27 @@ var NodeUserAuthorization = class {
|
|
|
591
598
|
this.tinycloudHosts = [...hosts];
|
|
592
599
|
}
|
|
593
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* @internal End the session: adopt a fresh session manager whose active key
|
|
603
|
+
* is `sessionKeyId`, and return to the constructed state.
|
|
604
|
+
*/
|
|
605
|
+
resetSession(sessionManager, sessionKeyId, hosts) {
|
|
606
|
+
this.sessionGeneration++;
|
|
607
|
+
this.installRestoredSession(sessionManager, void 0, hosts);
|
|
608
|
+
this.activeSessionKeyId = sessionKeyId;
|
|
609
|
+
}
|
|
610
|
+
/** Throws when the session was reset (signed out) after `generation` was read. */
|
|
611
|
+
assertSignInCurrent(generation) {
|
|
612
|
+
if (generation !== this.sessionGeneration) {
|
|
613
|
+
throw new Error("Sign-in was cancelled by sign-out.");
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
/** Runs `task` after every earlier queued persistence task has settled. */
|
|
617
|
+
enqueuePersistence(task) {
|
|
618
|
+
const run = this.persistenceQueue.then(task);
|
|
619
|
+
this.persistenceQueue = run.catch(() => void 0);
|
|
620
|
+
return run;
|
|
621
|
+
}
|
|
594
622
|
/** @internal Atomically adopt the state staged by TinyCloudNode.restoreSession. */
|
|
595
623
|
installRestoredSession(sessionManager, session, hosts, nodeFeatures) {
|
|
596
624
|
this.sessionManager = sessionManager;
|
|
@@ -1279,11 +1307,14 @@ var NodeUserAuthorization = class {
|
|
|
1279
1307
|
* @param options - Optional per-call SIWE overrides for this sign-in only
|
|
1280
1308
|
*/
|
|
1281
1309
|
async signIn(options) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1310
|
+
const generation = this.sessionGeneration;
|
|
1311
|
+
const address = (0, import_sdk_core4.canonicalizeAddress)(await this.signer.getAddress());
|
|
1312
|
+
const chainId = await this.signer.getChainId();
|
|
1313
|
+
this.assertSignInCurrent(generation);
|
|
1314
|
+
this._address = address;
|
|
1315
|
+
this._chainId = chainId;
|
|
1286
1316
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
1317
|
+
this.assertSignInCurrent(generation);
|
|
1287
1318
|
const keyId = `session-${Date.now()}`;
|
|
1288
1319
|
this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
|
|
1289
1320
|
this.activeSessionKeyId = keyId;
|
|
@@ -1316,6 +1347,7 @@ var NodeUserAuthorization = class {
|
|
|
1316
1347
|
type: "siwe",
|
|
1317
1348
|
purpose: "sign-in"
|
|
1318
1349
|
});
|
|
1350
|
+
this.assertSignInCurrent(generation);
|
|
1319
1351
|
const session = this.wasm.completeSessionSetup({
|
|
1320
1352
|
...prepared,
|
|
1321
1353
|
signature
|
|
@@ -1360,18 +1392,27 @@ var NodeUserAuthorization = class {
|
|
|
1360
1392
|
version: "1.0",
|
|
1361
1393
|
tinycloudHosts: this.tinycloudHosts
|
|
1362
1394
|
};
|
|
1363
|
-
await this.
|
|
1395
|
+
await this.enqueuePersistence(async () => {
|
|
1396
|
+
await this.sessionStorage.save(address, persistedData);
|
|
1397
|
+
if (generation !== this.sessionGeneration) {
|
|
1398
|
+
await this.sessionStorage.clear(address);
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
this.assertSignInCurrent(generation);
|
|
1364
1402
|
this._session = clientSession;
|
|
1365
1403
|
this._tinyCloudSession = tinyCloudSession;
|
|
1366
1404
|
this._address = address;
|
|
1367
1405
|
this._chainId = chainId;
|
|
1368
1406
|
await this.refreshNodeInfo();
|
|
1407
|
+
this.assertSignInCurrent(generation);
|
|
1369
1408
|
for (const ext of this.extensions) {
|
|
1370
1409
|
if (ext.afterSignIn) {
|
|
1371
1410
|
await ext.afterSignIn(clientSession);
|
|
1411
|
+
this.assertSignInCurrent(generation);
|
|
1372
1412
|
}
|
|
1373
1413
|
}
|
|
1374
1414
|
await this.ensureSpaceExists();
|
|
1415
|
+
this.assertSignInCurrent(generation);
|
|
1375
1416
|
return clientSession;
|
|
1376
1417
|
}
|
|
1377
1418
|
/**
|
|
@@ -2533,17 +2574,24 @@ var import_sdk_core6 = require("@tinycloud/sdk-core");
|
|
|
2533
2574
|
// src/DelegatedAccess.ts
|
|
2534
2575
|
var import_sdk_core7 = require("@tinycloud/sdk-core");
|
|
2535
2576
|
var DelegatedAccess = class {
|
|
2536
|
-
|
|
2577
|
+
/**
|
|
2578
|
+
* @param lifetime - Optional owner of this access: its `fetch` is used for
|
|
2579
|
+
* requests and the service context is registered with `track`, so the
|
|
2580
|
+
* owner can cancel and retire the access (TinyCloudNode does this when its
|
|
2581
|
+
* session is replaced or signed out).
|
|
2582
|
+
*/
|
|
2583
|
+
constructor(session, delegation, host, invoke, invokeAny, telemetry, lifetime) {
|
|
2537
2584
|
this.session = session;
|
|
2538
2585
|
this._delegation = delegation;
|
|
2539
2586
|
this.host = host;
|
|
2540
|
-
|
|
2587
|
+
const context = new import_sdk_core7.ServiceContext({
|
|
2541
2588
|
invoke,
|
|
2542
2589
|
invokeAny,
|
|
2543
|
-
fetch: globalThis.fetch.bind(globalThis),
|
|
2590
|
+
fetch: lifetime?.fetch ?? globalThis.fetch.bind(globalThis),
|
|
2544
2591
|
hosts: [host],
|
|
2545
2592
|
telemetry
|
|
2546
2593
|
});
|
|
2594
|
+
this._serviceContext = lifetime ? lifetime.track(context) : context;
|
|
2547
2595
|
const prefix = this._delegation.path.replace(/\/$/, "");
|
|
2548
2596
|
this._kv = new import_sdk_core7.KVService({ prefix });
|
|
2549
2597
|
this._kv.initialize(this._serviceContext);
|
|
@@ -3117,6 +3165,19 @@ function exactVerifiedRecap(verified) {
|
|
|
3117
3165
|
caveats: cloneRecapCaveats(entry.caveats)
|
|
3118
3166
|
}));
|
|
3119
3167
|
}
|
|
3168
|
+
function createDefaultSessionKey(wasm) {
|
|
3169
|
+
const manager = wasm.createSessionManager();
|
|
3170
|
+
let keyId = "default";
|
|
3171
|
+
let jwkStr = manager.jwk(keyId);
|
|
3172
|
+
if (!jwkStr) {
|
|
3173
|
+
keyId = manager.createSessionKey(keyId);
|
|
3174
|
+
jwkStr = manager.jwk(keyId);
|
|
3175
|
+
}
|
|
3176
|
+
if (!jwkStr) {
|
|
3177
|
+
throw new Error("Failed to get session key JWK");
|
|
3178
|
+
}
|
|
3179
|
+
return { manager, keyId, jwk: JSON.parse(jwkStr) };
|
|
3180
|
+
}
|
|
3120
3181
|
var ServiceGraphLifetime = class {
|
|
3121
3182
|
constructor(invokeFn, invokeAnyFn, fetchFn, observeContext) {
|
|
3122
3183
|
this.invokeFn = invokeFn;
|
|
@@ -3166,9 +3227,13 @@ var ServiceGraphLifetime = class {
|
|
|
3166
3227
|
this.contexts.clear();
|
|
3167
3228
|
}
|
|
3168
3229
|
}
|
|
3230
|
+
/** Aborted when the graph retires: cancels requests made for its session. */
|
|
3231
|
+
get signal() {
|
|
3232
|
+
return this.abortController.signal;
|
|
3233
|
+
}
|
|
3169
3234
|
assertActive() {
|
|
3170
3235
|
if (this.retired) {
|
|
3171
|
-
throw new Error("Service graph has been retired
|
|
3236
|
+
throw new Error("Service graph has been retired: its session was replaced or signed out.");
|
|
3172
3237
|
}
|
|
3173
3238
|
}
|
|
3174
3239
|
combineSignals(signal) {
|
|
@@ -3470,6 +3535,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3470
3535
|
* so it must outlast a slow but healthy sync (9–18 s on prod 1.20.2, TC-998).
|
|
3471
3536
|
*/
|
|
3472
3537
|
this.accountRegistryDeadlineMs = 3e4;
|
|
3538
|
+
/**
|
|
3539
|
+
* Incremented by every sign-out. A sign-in or restore records it when it
|
|
3540
|
+
* starts and does not establish its session once it has changed.
|
|
3541
|
+
*/
|
|
3542
|
+
this.sessionEpoch = 0;
|
|
3543
|
+
/**
|
|
3544
|
+
* Numbers sign-in and restore attempts. A cancelled attempt cleans up only
|
|
3545
|
+
* while it is still the latest, so it never ends a newer session.
|
|
3546
|
+
*/
|
|
3547
|
+
this.latestSessionAttempt = 0;
|
|
3473
3548
|
/**
|
|
3474
3549
|
* True when the last signIn() detected an interactive signer and skipped
|
|
3475
3550
|
* client-side bootstrap. Apps can read this to know whether bootstrap was
|
|
@@ -3603,54 +3678,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3603
3678
|
"wasmBindings must be provided in config. Import from '@tinycloud/node-sdk' (not '/core') for automatic Node.js defaults."
|
|
3604
3679
|
);
|
|
3605
3680
|
}
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
this.sessionKeyId = defaultKeyId;
|
|
3611
|
-
} else {
|
|
3612
|
-
this.sessionKeyId = this.sessionManager.createSessionKey(defaultKeyId);
|
|
3613
|
-
jwkStr = this.sessionManager.jwk(this.sessionKeyId);
|
|
3614
|
-
}
|
|
3615
|
-
if (!jwkStr) {
|
|
3616
|
-
throw new Error("Failed to get session key JWK");
|
|
3617
|
-
}
|
|
3618
|
-
this.sessionKeyJwk = JSON.parse(jwkStr);
|
|
3681
|
+
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
3682
|
+
this.sessionManager = sessionKey.manager;
|
|
3683
|
+
this.sessionKeyId = sessionKey.keyId;
|
|
3684
|
+
this.sessionKeyJwk = sessionKey.jwk;
|
|
3619
3685
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
3620
3686
|
this._capabilityRegistry = new import_sdk_core10.CapabilityKeyRegistry();
|
|
3621
3687
|
this._keyProvider = new WasmKeyProvider({
|
|
3622
3688
|
sessionManager: this.sessionManager
|
|
3623
3689
|
});
|
|
3624
3690
|
this.notificationHandler = config.notificationHandler ?? new import_sdk_core10.SilentNotificationHandler();
|
|
3625
|
-
|
|
3626
|
-
this._sharingService = new import_sdk_core10.SharingService({
|
|
3627
|
-
hosts: [this.config.host],
|
|
3628
|
-
// session: undefined - not needed for receive()
|
|
3629
|
-
invoke: receiveOnlyGraph.invoke,
|
|
3630
|
-
fetch: receiveOnlyGraph.fetch,
|
|
3631
|
-
assertActive: () => receiveOnlyGraph.assertActive(),
|
|
3632
|
-
keyProvider: this._keyProvider,
|
|
3633
|
-
registry: this._capabilityRegistry,
|
|
3634
|
-
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
3635
|
-
computeCid: (data, codec) => {
|
|
3636
|
-
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
3637
|
-
return this.wasmBindings.computeCid(data, codec);
|
|
3638
|
-
},
|
|
3639
|
-
// delegationManager: undefined - not needed for receive()
|
|
3640
|
-
createKVService: (config2) => {
|
|
3641
|
-
const prefix = config2.pathPrefix?.replace(/\/$/, "");
|
|
3642
|
-
const kvService = new import_sdk_core10.KVService({ prefix });
|
|
3643
|
-
const kvContext = receiveOnlyGraph.track(new import_sdk_core10.ServiceContext({
|
|
3644
|
-
invoke: config2.invoke,
|
|
3645
|
-
fetch: config2.fetch ?? globalThis.fetch.bind(globalThis),
|
|
3646
|
-
hosts: config2.hosts,
|
|
3647
|
-
telemetry: this.config.telemetry
|
|
3648
|
-
}));
|
|
3649
|
-
kvContext.setSession(config2.session);
|
|
3650
|
-
kvService.initialize(kvContext);
|
|
3651
|
-
return kvService;
|
|
3652
|
-
}
|
|
3653
|
-
});
|
|
3691
|
+
this._sharingService = this.createReceiveOnlySharing();
|
|
3654
3692
|
if (config.signer) {
|
|
3655
3693
|
this.signer = config.signer;
|
|
3656
3694
|
this.setupAuth(config);
|
|
@@ -3792,6 +3830,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3792
3830
|
get siweDomain() {
|
|
3793
3831
|
return this.config.domain ?? "app.tinycloud.xyz";
|
|
3794
3832
|
}
|
|
3833
|
+
/**
|
|
3834
|
+
* SharingService for receive-only access (no session required), bound to the
|
|
3835
|
+
* current service graph. This lets session-only users receive sharing links
|
|
3836
|
+
* without signIn(); full capabilities (generate) are added after signIn().
|
|
3837
|
+
*/
|
|
3838
|
+
createReceiveOnlySharing() {
|
|
3839
|
+
const receiveOnlyGraph = this._serviceGraph;
|
|
3840
|
+
return new import_sdk_core10.SharingService({
|
|
3841
|
+
hosts: [this.config.host],
|
|
3842
|
+
// session: undefined - not needed for receive()
|
|
3843
|
+
invoke: receiveOnlyGraph.invoke,
|
|
3844
|
+
fetch: receiveOnlyGraph.fetch,
|
|
3845
|
+
assertActive: () => receiveOnlyGraph.assertActive(),
|
|
3846
|
+
keyProvider: this._keyProvider,
|
|
3847
|
+
registry: this._capabilityRegistry,
|
|
3848
|
+
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
3849
|
+
computeCid: (data, codec) => {
|
|
3850
|
+
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
3851
|
+
return this.wasmBindings.computeCid(data, codec);
|
|
3852
|
+
},
|
|
3853
|
+
// delegationManager: undefined - not needed for receive()
|
|
3854
|
+
createKVService: (config) => {
|
|
3855
|
+
const prefix = config.pathPrefix?.replace(/\/$/, "");
|
|
3856
|
+
const kvService = new import_sdk_core10.KVService({ prefix });
|
|
3857
|
+
const kvContext = receiveOnlyGraph.track(new import_sdk_core10.ServiceContext({
|
|
3858
|
+
invoke: config.invoke,
|
|
3859
|
+
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
3860
|
+
hosts: config.hosts,
|
|
3861
|
+
telemetry: this.config.telemetry
|
|
3862
|
+
}));
|
|
3863
|
+
kvContext.setSession(config.session);
|
|
3864
|
+
kvService.initialize(kvContext);
|
|
3865
|
+
return kvService;
|
|
3866
|
+
}
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3795
3869
|
/**
|
|
3796
3870
|
* Set up authorization handler and TinyCloud instance.
|
|
3797
3871
|
* @internal
|
|
@@ -4051,6 +4125,102 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4051
4125
|
get restorableSession() {
|
|
4052
4126
|
return this.currentTinyCloudSession();
|
|
4053
4127
|
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Sign out and end the session on this instance.
|
|
4130
|
+
*
|
|
4131
|
+
* Every service built for the session (and every `useDelegation()` result)
|
|
4132
|
+
* is retired and the session key is replaced before anything is awaited, so
|
|
4133
|
+
* nothing captured from the session can sign once sign-out has started.
|
|
4134
|
+
* Pending work is aborted and the persisted session is cleared from the
|
|
4135
|
+
* configured session storage. Afterwards session accessors return
|
|
4136
|
+
* `undefined` and service calls fail with "Not signed in" until
|
|
4137
|
+
* {@link signIn} or {@link restoreSession}, which wait for a sign-out in
|
|
4138
|
+
* progress.
|
|
4139
|
+
*/
|
|
4140
|
+
signOut() {
|
|
4141
|
+
this.signOutInFlight ?? (this.signOutInFlight = this.endSession().finally(() => {
|
|
4142
|
+
this.signOutInFlight = void 0;
|
|
4143
|
+
}));
|
|
4144
|
+
return this.signOutInFlight;
|
|
4145
|
+
}
|
|
4146
|
+
async endSession() {
|
|
4147
|
+
const leavingAddress = this.address ?? this._restoredTcSession?.address;
|
|
4148
|
+
this.teardownSession();
|
|
4149
|
+
try {
|
|
4150
|
+
await this.accountRegistryTail;
|
|
4151
|
+
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4152
|
+
} finally {
|
|
4153
|
+
if (leavingAddress) await this.clearPersistedSessionFor(leavingAddress);
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
/**
|
|
4157
|
+
* Synchronously drop every piece of in-memory session authority and
|
|
4158
|
+
* invalidate sign-ins and restores still in flight (see `sessionEpoch`).
|
|
4159
|
+
*/
|
|
4160
|
+
teardownSession() {
|
|
4161
|
+
this.sessionEpoch++;
|
|
4162
|
+
this.pendingAccountRegistrySync?.controller.abort();
|
|
4163
|
+
const oldGraph = this._serviceGraph;
|
|
4164
|
+
const oldCore = this.tc;
|
|
4165
|
+
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
4166
|
+
this.sessionManager = sessionKey.manager;
|
|
4167
|
+
this.sessionKeyId = sessionKey.keyId;
|
|
4168
|
+
this.sessionKeyJwk = sessionKey.jwk;
|
|
4169
|
+
this.auth?.resetSession(
|
|
4170
|
+
sessionKey.manager,
|
|
4171
|
+
sessionKey.keyId,
|
|
4172
|
+
this.explicitHost ? [this.explicitHost] : void 0
|
|
4173
|
+
);
|
|
4174
|
+
this.config.host = this.explicitHost ?? DEFAULT_HOST;
|
|
4175
|
+
this._address = void 0;
|
|
4176
|
+
this._chainId = 1;
|
|
4177
|
+
this._restoredTcSession = void 0;
|
|
4178
|
+
this._restoredNodeFeatures = void 0;
|
|
4179
|
+
this._activeServiceSession = void 0;
|
|
4180
|
+
this._serviceContext = void 0;
|
|
4181
|
+
this._kv = void 0;
|
|
4182
|
+
this._sql = void 0;
|
|
4183
|
+
this._duckdb = void 0;
|
|
4184
|
+
this._hooks = void 0;
|
|
4185
|
+
this._vault = void 0;
|
|
4186
|
+
this._encryption = void 0;
|
|
4187
|
+
this._baseVaults.clear();
|
|
4188
|
+
this._baseSecrets.clear();
|
|
4189
|
+
this._secrets.clear();
|
|
4190
|
+
this._account = void 0;
|
|
4191
|
+
this._publicKV = void 0;
|
|
4192
|
+
this._delegationManager = void 0;
|
|
4193
|
+
this._spaceService = void 0;
|
|
4194
|
+
this.runtimePermissionGrants = [];
|
|
4195
|
+
this._recapOperationsCache = void 0;
|
|
4196
|
+
this.confirmedHostedSpaceIds.clear();
|
|
4197
|
+
this.hostedOwnedSpaceIds.clear();
|
|
4198
|
+
this.cancelOwnedSpaceHosts();
|
|
4199
|
+
this.storageFull.clear();
|
|
4200
|
+
this._bootstrapSkipped = false;
|
|
4201
|
+
this._bootstrapStatus = { skipped: false };
|
|
4202
|
+
this.replicationDelegateSession = void 0;
|
|
4203
|
+
this.replicationScopeError = void 0;
|
|
4204
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4205
|
+
oldGraph.retire();
|
|
4206
|
+
this.tc = this.auth ? new import_sdk_core10.TinyCloud(this.auth, {
|
|
4207
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
4208
|
+
telemetry: this.config.telemetry
|
|
4209
|
+
}) : null;
|
|
4210
|
+
oldCore?.retireServices();
|
|
4211
|
+
this._capabilityRegistry = new import_sdk_core10.CapabilityKeyRegistry();
|
|
4212
|
+
this._keyProvider = new WasmKeyProvider({ sessionManager: this.sessionManager });
|
|
4213
|
+
this._sharingService = this.createReceiveOnlySharing();
|
|
4214
|
+
}
|
|
4215
|
+
async clearPersistedSessionFor(address) {
|
|
4216
|
+
await (this.auth ? this.auth.clearPersistedSession(address) : this.config.sessionStorage?.clear(address));
|
|
4217
|
+
}
|
|
4218
|
+
/** Throws when a sign-out started after `epoch` was read. */
|
|
4219
|
+
assertSessionEpoch(epoch, operation) {
|
|
4220
|
+
if (epoch !== this.sessionEpoch) {
|
|
4221
|
+
throw new Error(`${operation} was cancelled by sign-out.`);
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4054
4224
|
/**
|
|
4055
4225
|
* Sign in and create a new session.
|
|
4056
4226
|
* This creates the user's space if it doesn't exist.
|
|
@@ -4064,11 +4234,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4064
4234
|
"Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
|
|
4065
4235
|
);
|
|
4066
4236
|
}
|
|
4237
|
+
const epoch = this.sessionEpoch;
|
|
4238
|
+
const attempt = ++this.latestSessionAttempt;
|
|
4239
|
+
await this.signOutInFlight;
|
|
4240
|
+
try {
|
|
4241
|
+
await this.establishSignIn(epoch, options);
|
|
4242
|
+
} catch (error) {
|
|
4243
|
+
if (epoch !== this.sessionEpoch && attempt === this.latestSessionAttempt) {
|
|
4244
|
+
await this.endSession();
|
|
4245
|
+
}
|
|
4246
|
+
throw error;
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
async establishSignIn(epoch, options) {
|
|
4250
|
+
if (!this.signer || !this.tc) {
|
|
4251
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
4252
|
+
}
|
|
4067
4253
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
4068
4254
|
await this.accountRegistryTail;
|
|
4069
4255
|
await this.wasmBindings.ensureInitialized?.();
|
|
4070
4256
|
const address = (0, import_sdk_core10.canonicalizeAddress)(await this.signer.getAddress());
|
|
4071
4257
|
const chainId = await this.signer.getChainId();
|
|
4258
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4072
4259
|
if (address !== this._address || chainId !== this._chainId) this.storageFull.clear();
|
|
4073
4260
|
this._address = address;
|
|
4074
4261
|
this._chainId = chainId;
|
|
@@ -4087,6 +4274,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4087
4274
|
this.confirmedHostedSpaceIds.clear();
|
|
4088
4275
|
this.hostedOwnedSpaceIds.clear();
|
|
4089
4276
|
await this.tc.signIn(options);
|
|
4277
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4090
4278
|
this.cancelOwnedSpaceHosts();
|
|
4091
4279
|
this.syncResolvedHostFromAuth();
|
|
4092
4280
|
for (const spaceId of this.auth.lastActivationActivatedSpaceIds ?? []) {
|
|
@@ -4095,6 +4283,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4095
4283
|
this.replicationDelegateSession = void 0;
|
|
4096
4284
|
this.replicationScopeError = void 0;
|
|
4097
4285
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4286
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4098
4287
|
const oldGraph = this._serviceGraph;
|
|
4099
4288
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4100
4289
|
oldGraph.retire();
|
|
@@ -4105,15 +4294,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4105
4294
|
this.sessionKeyJwk = signedInSession.jwk;
|
|
4106
4295
|
}
|
|
4107
4296
|
await this.initializeServices();
|
|
4297
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4108
4298
|
const primarySession = this.currentTinyCloudSession();
|
|
4109
4299
|
if (primarySession) {
|
|
4110
4300
|
this.registerPrimarySessionGrant(primarySession);
|
|
4111
4301
|
}
|
|
4112
4302
|
const bootstrapped = await this.bootstrapAccountIfNeeded();
|
|
4303
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4113
4304
|
await this.ensureRequestedEncryptionNetworks();
|
|
4305
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4114
4306
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
4115
4307
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(import_sdk_core10.SECRETS_SPACE));
|
|
4116
4308
|
}
|
|
4309
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4117
4310
|
if (!bootstrapped) {
|
|
4118
4311
|
this.scheduleAccountRegistrySync();
|
|
4119
4312
|
}
|
|
@@ -4295,6 +4488,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4295
4488
|
throw new Error("Account bootstrap requires a TinyCloud host");
|
|
4296
4489
|
}
|
|
4297
4490
|
const auth = this.auth;
|
|
4491
|
+
const graph = this._serviceGraph;
|
|
4298
4492
|
const sessions = /* @__PURE__ */ new Map();
|
|
4299
4493
|
const rawAbilitiesBySpace = /* @__PURE__ */ new Map();
|
|
4300
4494
|
const warnings = [];
|
|
@@ -4303,6 +4497,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4303
4497
|
const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === import_sdk_core10.BOOTSTRAP_SESSION_REQUESTS.default;
|
|
4304
4498
|
for (const step of steps) {
|
|
4305
4499
|
if (step.kind !== "session") continue;
|
|
4500
|
+
graph.assertActive();
|
|
4306
4501
|
if (step.space === "default" && canReusePrimaryBootstrapSession && primarySession) {
|
|
4307
4502
|
sessions.set(step.space, primarySession);
|
|
4308
4503
|
continue;
|
|
@@ -4330,6 +4525,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4330
4525
|
}
|
|
4331
4526
|
for (const step of steps) {
|
|
4332
4527
|
if (step.kind !== "host") continue;
|
|
4528
|
+
graph.assertActive();
|
|
4333
4529
|
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
4334
4530
|
if (!hosted.success) {
|
|
4335
4531
|
throw Object.assign(
|
|
@@ -4341,11 +4537,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4341
4537
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4342
4538
|
for (const step of steps) {
|
|
4343
4539
|
if (step.kind !== "activate") continue;
|
|
4540
|
+
graph.assertActive();
|
|
4344
4541
|
const session = sessions.get(step.space);
|
|
4345
4542
|
if (!session) {
|
|
4346
4543
|
throw new Error(`Missing bootstrap session for ${step.space}`);
|
|
4347
4544
|
}
|
|
4348
|
-
const activated = await (0, import_sdk_core10.activateSessionWithHost)(host, session.delegationHeader)
|
|
4545
|
+
const activated = await (0, import_sdk_core10.activateSessionWithHost)(host, session.delegationHeader, graph.signal).catch((error) => {
|
|
4546
|
+
graph.assertActive();
|
|
4547
|
+
throw error;
|
|
4548
|
+
});
|
|
4549
|
+
graph.assertActive();
|
|
4349
4550
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
4350
4551
|
throw Object.assign(
|
|
4351
4552
|
new Error(
|
|
@@ -4361,6 +4562,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4361
4562
|
);
|
|
4362
4563
|
}
|
|
4363
4564
|
for (const step of steps) {
|
|
4565
|
+
graph.assertActive();
|
|
4364
4566
|
if (step.kind === "account-index-schema") {
|
|
4365
4567
|
const ensured = await this.account.index.ensure();
|
|
4366
4568
|
if (!ensured.ok) {
|
|
@@ -5136,6 +5338,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5136
5338
|
* @param sessionData - The stored delegation data from the browser flow
|
|
5137
5339
|
*/
|
|
5138
5340
|
async restoreSession(sessionData) {
|
|
5341
|
+
const epoch = this.sessionEpoch;
|
|
5342
|
+
const attempt = ++this.latestSessionAttempt;
|
|
5343
|
+
await this.signOutInFlight;
|
|
5139
5344
|
await this.wasmBindings.ensureInitialized?.();
|
|
5140
5345
|
let restoredJwk;
|
|
5141
5346
|
try {
|
|
@@ -5325,6 +5530,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5325
5530
|
});
|
|
5326
5531
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
5327
5532
|
await this.accountRegistryTail;
|
|
5533
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5328
5534
|
const oldGraph = this._serviceGraph;
|
|
5329
5535
|
const oldCore = this.tc;
|
|
5330
5536
|
this.sessionManager = stagedManager;
|
|
@@ -5370,6 +5576,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5370
5576
|
this.replicationDelegateSession = compactReplicationSession;
|
|
5371
5577
|
this.replicationScopeError = replicationScopeError;
|
|
5372
5578
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
5579
|
+
if (epoch !== this.sessionEpoch) {
|
|
5580
|
+
oldGraph.retire();
|
|
5581
|
+
oldCore?.retireServices?.();
|
|
5582
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5583
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5584
|
+
}
|
|
5373
5585
|
oldGraph.retire();
|
|
5374
5586
|
if (replicationScopeError) {
|
|
5375
5587
|
stagedGraph.serviceContext.emit("replication.state", {
|
|
@@ -5380,6 +5592,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5380
5592
|
}
|
|
5381
5593
|
await this.bindReplicationRuntime(stagedGraph.serviceContext, serviceSession, stagedGraph.kv, replicationPrincipal);
|
|
5382
5594
|
oldCore?.retireServices?.();
|
|
5595
|
+
if (epoch !== this.sessionEpoch) {
|
|
5596
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5597
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5598
|
+
}
|
|
5383
5599
|
}
|
|
5384
5600
|
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
5385
5601
|
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
@@ -7238,6 +7454,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7238
7454
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
7239
7455
|
*/
|
|
7240
7456
|
async useRuntimeDelegation(delegation) {
|
|
7457
|
+
const graph = this._serviceGraph;
|
|
7241
7458
|
const session = this.currentTinyCloudSession();
|
|
7242
7459
|
if (!session) {
|
|
7243
7460
|
throw new import_sdk_core10.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7256,9 +7473,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7256
7473
|
try {
|
|
7257
7474
|
activateResult = await (0, import_sdk_core10.activateSessionWithHost)(
|
|
7258
7475
|
targetHost,
|
|
7259
|
-
delegation.delegationHeader
|
|
7476
|
+
delegation.delegationHeader,
|
|
7477
|
+
graph.signal
|
|
7260
7478
|
);
|
|
7261
7479
|
} catch (cause) {
|
|
7480
|
+
graph.assertActive();
|
|
7262
7481
|
throw Object.assign(
|
|
7263
7482
|
new Error("The TinyCloud host could not be reached to activate the delegation."),
|
|
7264
7483
|
{ code: "ACTIVATION_TRANSPORT_FAILED", cause }
|
|
@@ -7278,6 +7497,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7278
7497
|
{ cause: activateResult }
|
|
7279
7498
|
);
|
|
7280
7499
|
}
|
|
7500
|
+
graph.assertActive();
|
|
7281
7501
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
7282
7502
|
(grant) => grant.delegation.cid !== delegation.cid
|
|
7283
7503
|
);
|
|
@@ -7294,6 +7514,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7294
7514
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
7295
7515
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
7296
7516
|
}
|
|
7517
|
+
const graph = this._serviceGraph;
|
|
7297
7518
|
const session = this.currentTinyCloudSession();
|
|
7298
7519
|
if (!session) {
|
|
7299
7520
|
throw new import_sdk_core10.SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7374,14 +7595,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7374
7595
|
}
|
|
7375
7596
|
})();
|
|
7376
7597
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
7598
|
+
graph.assertActive();
|
|
7377
7599
|
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
7378
7600
|
...prepared,
|
|
7379
7601
|
signature
|
|
7380
7602
|
});
|
|
7381
7603
|
const activateResult = await (0, import_sdk_core10.activateSessionWithHost)(
|
|
7382
7604
|
this.config.host,
|
|
7383
|
-
delegatedSession.delegationHeader
|
|
7384
|
-
|
|
7605
|
+
delegatedSession.delegationHeader,
|
|
7606
|
+
graph.signal
|
|
7607
|
+
).catch((error) => {
|
|
7608
|
+
graph.assertActive();
|
|
7609
|
+
throw error;
|
|
7610
|
+
});
|
|
7385
7611
|
if (!activateResult.success) {
|
|
7386
7612
|
if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
|
|
7387
7613
|
throw Object.assign(
|
|
@@ -7402,6 +7628,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7402
7628
|
expiresAt,
|
|
7403
7629
|
{ siwe: prepared.siwe, signature }
|
|
7404
7630
|
);
|
|
7631
|
+
graph.assertActive();
|
|
7405
7632
|
this.runtimePermissionGrants.push({
|
|
7406
7633
|
session: {
|
|
7407
7634
|
delegationHeader: delegatedSession.delegationHeader,
|
|
@@ -8865,6 +9092,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8865
9092
|
* @returns A DelegatedAccess instance for performing operations
|
|
8866
9093
|
*/
|
|
8867
9094
|
async useDelegation(delegation) {
|
|
9095
|
+
const graph = this._serviceGraph;
|
|
8868
9096
|
const delegationHeader = delegation.delegationHeader;
|
|
8869
9097
|
const targetHost = delegation.host ?? this.config.host;
|
|
8870
9098
|
if (this.isSessionOnly) {
|
|
@@ -8906,7 +9134,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8906
9134
|
targetHost,
|
|
8907
9135
|
this.wasmBindings.invoke,
|
|
8908
9136
|
this.wasmBindings.invokeAny,
|
|
8909
|
-
this.config.telemetry
|
|
9137
|
+
this.config.telemetry,
|
|
9138
|
+
graph
|
|
8910
9139
|
);
|
|
8911
9140
|
}
|
|
8912
9141
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -8930,14 +9159,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8930
9159
|
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
8931
9160
|
});
|
|
8932
9161
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
9162
|
+
graph.assertActive();
|
|
8933
9163
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
8934
9164
|
...prepared,
|
|
8935
9165
|
signature
|
|
8936
9166
|
});
|
|
8937
9167
|
const activateResult = await (0, import_sdk_core10.activateSessionWithHost)(
|
|
8938
9168
|
targetHost,
|
|
8939
|
-
invokerSession.delegationHeader
|
|
8940
|
-
|
|
9169
|
+
invokerSession.delegationHeader,
|
|
9170
|
+
graph.signal
|
|
9171
|
+
).catch((error) => {
|
|
9172
|
+
graph.assertActive();
|
|
9173
|
+
throw error;
|
|
9174
|
+
});
|
|
8941
9175
|
if (!activateResult.success) {
|
|
8942
9176
|
throw Object.assign(
|
|
8943
9177
|
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
@@ -8956,6 +9190,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8956
9190
|
siwe: prepared.siwe,
|
|
8957
9191
|
signature
|
|
8958
9192
|
};
|
|
9193
|
+
graph.assertActive();
|
|
8959
9194
|
this.trackReceivedDelegation(delegation, jwk);
|
|
8960
9195
|
this.installRuntimeGrantFromServiceSession(
|
|
8961
9196
|
delegation,
|
|
@@ -8974,7 +9209,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8974
9209
|
targetHost,
|
|
8975
9210
|
this.wasmBindings.invoke,
|
|
8976
9211
|
this.wasmBindings.invokeAny,
|
|
8977
|
-
this.config.telemetry
|
|
9212
|
+
this.config.telemetry,
|
|
9213
|
+
graph
|
|
8978
9214
|
);
|
|
8979
9215
|
}
|
|
8980
9216
|
/**
|