@tinycloud/node-sdk 3.2.6-beta.0 → 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.js
CHANGED
|
@@ -389,6 +389,13 @@ function cloneAbilitiesMap(actions) {
|
|
|
389
389
|
var NodeUserAuthorization = class {
|
|
390
390
|
constructor(config) {
|
|
391
391
|
this.activeSessionKeyId = "default";
|
|
392
|
+
/** Incremented by resetSession; a sign-in started earlier publishes nothing. */
|
|
393
|
+
this.sessionGeneration = 0;
|
|
394
|
+
/**
|
|
395
|
+
* Sign-in saves run one at a time, in order, each together with its own
|
|
396
|
+
* cancellation cleanup, so no later write can land between the two.
|
|
397
|
+
*/
|
|
398
|
+
this.persistenceQueue = Promise.resolve();
|
|
392
399
|
this.extensions = [];
|
|
393
400
|
this._nodeFeatures = [];
|
|
394
401
|
this._nodeFeaturesKnown = false;
|
|
@@ -519,6 +526,27 @@ var NodeUserAuthorization = class {
|
|
|
519
526
|
this.tinycloudHosts = [...hosts];
|
|
520
527
|
}
|
|
521
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* @internal End the session: adopt a fresh session manager whose active key
|
|
531
|
+
* is `sessionKeyId`, and return to the constructed state.
|
|
532
|
+
*/
|
|
533
|
+
resetSession(sessionManager, sessionKeyId, hosts) {
|
|
534
|
+
this.sessionGeneration++;
|
|
535
|
+
this.installRestoredSession(sessionManager, void 0, hosts);
|
|
536
|
+
this.activeSessionKeyId = sessionKeyId;
|
|
537
|
+
}
|
|
538
|
+
/** Throws when the session was reset (signed out) after `generation` was read. */
|
|
539
|
+
assertSignInCurrent(generation) {
|
|
540
|
+
if (generation !== this.sessionGeneration) {
|
|
541
|
+
throw new Error("Sign-in was cancelled by sign-out.");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
/** Runs `task` after every earlier queued persistence task has settled. */
|
|
545
|
+
enqueuePersistence(task) {
|
|
546
|
+
const run = this.persistenceQueue.then(task);
|
|
547
|
+
this.persistenceQueue = run.catch(() => void 0);
|
|
548
|
+
return run;
|
|
549
|
+
}
|
|
522
550
|
/** @internal Atomically adopt the state staged by TinyCloudNode.restoreSession. */
|
|
523
551
|
installRestoredSession(sessionManager, session, hosts, nodeFeatures) {
|
|
524
552
|
this.sessionManager = sessionManager;
|
|
@@ -1207,11 +1235,14 @@ var NodeUserAuthorization = class {
|
|
|
1207
1235
|
* @param options - Optional per-call SIWE overrides for this sign-in only
|
|
1208
1236
|
*/
|
|
1209
1237
|
async signIn(options) {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1238
|
+
const generation = this.sessionGeneration;
|
|
1239
|
+
const address = canonicalizeAddress(await this.signer.getAddress());
|
|
1240
|
+
const chainId = await this.signer.getChainId();
|
|
1241
|
+
this.assertSignInCurrent(generation);
|
|
1242
|
+
this._address = address;
|
|
1243
|
+
this._chainId = chainId;
|
|
1214
1244
|
await this.resolveTinyCloudHostsForSignIn(address, chainId);
|
|
1245
|
+
this.assertSignInCurrent(generation);
|
|
1215
1246
|
const keyId = `session-${Date.now()}`;
|
|
1216
1247
|
this.sessionManager.renameSessionKeyId(this.activeSessionKeyId, keyId);
|
|
1217
1248
|
this.activeSessionKeyId = keyId;
|
|
@@ -1244,6 +1275,7 @@ var NodeUserAuthorization = class {
|
|
|
1244
1275
|
type: "siwe",
|
|
1245
1276
|
purpose: "sign-in"
|
|
1246
1277
|
});
|
|
1278
|
+
this.assertSignInCurrent(generation);
|
|
1247
1279
|
const session = this.wasm.completeSessionSetup({
|
|
1248
1280
|
...prepared,
|
|
1249
1281
|
signature
|
|
@@ -1288,18 +1320,27 @@ var NodeUserAuthorization = class {
|
|
|
1288
1320
|
version: "1.0",
|
|
1289
1321
|
tinycloudHosts: this.tinycloudHosts
|
|
1290
1322
|
};
|
|
1291
|
-
await this.
|
|
1323
|
+
await this.enqueuePersistence(async () => {
|
|
1324
|
+
await this.sessionStorage.save(address, persistedData);
|
|
1325
|
+
if (generation !== this.sessionGeneration) {
|
|
1326
|
+
await this.sessionStorage.clear(address);
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
this.assertSignInCurrent(generation);
|
|
1292
1330
|
this._session = clientSession;
|
|
1293
1331
|
this._tinyCloudSession = tinyCloudSession;
|
|
1294
1332
|
this._address = address;
|
|
1295
1333
|
this._chainId = chainId;
|
|
1296
1334
|
await this.refreshNodeInfo();
|
|
1335
|
+
this.assertSignInCurrent(generation);
|
|
1297
1336
|
for (const ext of this.extensions) {
|
|
1298
1337
|
if (ext.afterSignIn) {
|
|
1299
1338
|
await ext.afterSignIn(clientSession);
|
|
1339
|
+
this.assertSignInCurrent(generation);
|
|
1300
1340
|
}
|
|
1301
1341
|
}
|
|
1302
1342
|
await this.ensureSpaceExists();
|
|
1343
|
+
this.assertSignInCurrent(generation);
|
|
1303
1344
|
return clientSession;
|
|
1304
1345
|
}
|
|
1305
1346
|
/**
|
|
@@ -2546,17 +2587,24 @@ import {
|
|
|
2546
2587
|
ServiceContext
|
|
2547
2588
|
} from "@tinycloud/sdk-core";
|
|
2548
2589
|
var DelegatedAccess = class {
|
|
2549
|
-
|
|
2590
|
+
/**
|
|
2591
|
+
* @param lifetime - Optional owner of this access: its `fetch` is used for
|
|
2592
|
+
* requests and the service context is registered with `track`, so the
|
|
2593
|
+
* owner can cancel and retire the access (TinyCloudNode does this when its
|
|
2594
|
+
* session is replaced or signed out).
|
|
2595
|
+
*/
|
|
2596
|
+
constructor(session, delegation, host, invoke, invokeAny, telemetry, lifetime) {
|
|
2550
2597
|
this.session = session;
|
|
2551
2598
|
this._delegation = delegation;
|
|
2552
2599
|
this.host = host;
|
|
2553
|
-
|
|
2600
|
+
const context = new ServiceContext({
|
|
2554
2601
|
invoke,
|
|
2555
2602
|
invokeAny,
|
|
2556
|
-
fetch: globalThis.fetch.bind(globalThis),
|
|
2603
|
+
fetch: lifetime?.fetch ?? globalThis.fetch.bind(globalThis),
|
|
2557
2604
|
hosts: [host],
|
|
2558
2605
|
telemetry
|
|
2559
2606
|
});
|
|
2607
|
+
this._serviceContext = lifetime ? lifetime.track(context) : context;
|
|
2560
2608
|
const prefix = this._delegation.path.replace(/\/$/, "");
|
|
2561
2609
|
this._kv = new KVService({ prefix });
|
|
2562
2610
|
this._kv.initialize(this._serviceContext);
|
|
@@ -3140,6 +3188,19 @@ function exactVerifiedRecap(verified) {
|
|
|
3140
3188
|
caveats: cloneRecapCaveats(entry.caveats)
|
|
3141
3189
|
}));
|
|
3142
3190
|
}
|
|
3191
|
+
function createDefaultSessionKey(wasm) {
|
|
3192
|
+
const manager = wasm.createSessionManager();
|
|
3193
|
+
let keyId = "default";
|
|
3194
|
+
let jwkStr = manager.jwk(keyId);
|
|
3195
|
+
if (!jwkStr) {
|
|
3196
|
+
keyId = manager.createSessionKey(keyId);
|
|
3197
|
+
jwkStr = manager.jwk(keyId);
|
|
3198
|
+
}
|
|
3199
|
+
if (!jwkStr) {
|
|
3200
|
+
throw new Error("Failed to get session key JWK");
|
|
3201
|
+
}
|
|
3202
|
+
return { manager, keyId, jwk: JSON.parse(jwkStr) };
|
|
3203
|
+
}
|
|
3143
3204
|
var ServiceGraphLifetime = class {
|
|
3144
3205
|
constructor(invokeFn, invokeAnyFn, fetchFn, observeContext) {
|
|
3145
3206
|
this.invokeFn = invokeFn;
|
|
@@ -3189,9 +3250,13 @@ var ServiceGraphLifetime = class {
|
|
|
3189
3250
|
this.contexts.clear();
|
|
3190
3251
|
}
|
|
3191
3252
|
}
|
|
3253
|
+
/** Aborted when the graph retires: cancels requests made for its session. */
|
|
3254
|
+
get signal() {
|
|
3255
|
+
return this.abortController.signal;
|
|
3256
|
+
}
|
|
3192
3257
|
assertActive() {
|
|
3193
3258
|
if (this.retired) {
|
|
3194
|
-
throw new Error("Service graph has been retired
|
|
3259
|
+
throw new Error("Service graph has been retired: its session was replaced or signed out.");
|
|
3195
3260
|
}
|
|
3196
3261
|
}
|
|
3197
3262
|
combineSignals(signal) {
|
|
@@ -3493,6 +3558,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3493
3558
|
* so it must outlast a slow but healthy sync (9–18 s on prod 1.20.2, TC-998).
|
|
3494
3559
|
*/
|
|
3495
3560
|
this.accountRegistryDeadlineMs = 3e4;
|
|
3561
|
+
/**
|
|
3562
|
+
* Incremented by every sign-out. A sign-in or restore records it when it
|
|
3563
|
+
* starts and does not establish its session once it has changed.
|
|
3564
|
+
*/
|
|
3565
|
+
this.sessionEpoch = 0;
|
|
3566
|
+
/**
|
|
3567
|
+
* Numbers sign-in and restore attempts. A cancelled attempt cleans up only
|
|
3568
|
+
* while it is still the latest, so it never ends a newer session.
|
|
3569
|
+
*/
|
|
3570
|
+
this.latestSessionAttempt = 0;
|
|
3496
3571
|
/**
|
|
3497
3572
|
* True when the last signIn() detected an interactive signer and skipped
|
|
3498
3573
|
* client-side bootstrap. Apps can read this to know whether bootstrap was
|
|
@@ -3626,54 +3701,17 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3626
3701
|
"wasmBindings must be provided in config. Import from '@tinycloud/node-sdk' (not '/core') for automatic Node.js defaults."
|
|
3627
3702
|
);
|
|
3628
3703
|
}
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
this.sessionKeyId = defaultKeyId;
|
|
3634
|
-
} else {
|
|
3635
|
-
this.sessionKeyId = this.sessionManager.createSessionKey(defaultKeyId);
|
|
3636
|
-
jwkStr = this.sessionManager.jwk(this.sessionKeyId);
|
|
3637
|
-
}
|
|
3638
|
-
if (!jwkStr) {
|
|
3639
|
-
throw new Error("Failed to get session key JWK");
|
|
3640
|
-
}
|
|
3641
|
-
this.sessionKeyJwk = JSON.parse(jwkStr);
|
|
3704
|
+
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
3705
|
+
this.sessionManager = sessionKey.manager;
|
|
3706
|
+
this.sessionKeyId = sessionKey.keyId;
|
|
3707
|
+
this.sessionKeyJwk = sessionKey.jwk;
|
|
3642
3708
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
3643
3709
|
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
3644
3710
|
this._keyProvider = new WasmKeyProvider({
|
|
3645
3711
|
sessionManager: this.sessionManager
|
|
3646
3712
|
});
|
|
3647
3713
|
this.notificationHandler = config.notificationHandler ?? new SilentNotificationHandler();
|
|
3648
|
-
|
|
3649
|
-
this._sharingService = new SharingService({
|
|
3650
|
-
hosts: [this.config.host],
|
|
3651
|
-
// session: undefined - not needed for receive()
|
|
3652
|
-
invoke: receiveOnlyGraph.invoke,
|
|
3653
|
-
fetch: receiveOnlyGraph.fetch,
|
|
3654
|
-
assertActive: () => receiveOnlyGraph.assertActive(),
|
|
3655
|
-
keyProvider: this._keyProvider,
|
|
3656
|
-
registry: this._capabilityRegistry,
|
|
3657
|
-
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
3658
|
-
computeCid: (data, codec) => {
|
|
3659
|
-
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
3660
|
-
return this.wasmBindings.computeCid(data, codec);
|
|
3661
|
-
},
|
|
3662
|
-
// delegationManager: undefined - not needed for receive()
|
|
3663
|
-
createKVService: (config2) => {
|
|
3664
|
-
const prefix = config2.pathPrefix?.replace(/\/$/, "");
|
|
3665
|
-
const kvService = new KVService2({ prefix });
|
|
3666
|
-
const kvContext = receiveOnlyGraph.track(new ServiceContext2({
|
|
3667
|
-
invoke: config2.invoke,
|
|
3668
|
-
fetch: config2.fetch ?? globalThis.fetch.bind(globalThis),
|
|
3669
|
-
hosts: config2.hosts,
|
|
3670
|
-
telemetry: this.config.telemetry
|
|
3671
|
-
}));
|
|
3672
|
-
kvContext.setSession(config2.session);
|
|
3673
|
-
kvService.initialize(kvContext);
|
|
3674
|
-
return kvService;
|
|
3675
|
-
}
|
|
3676
|
-
});
|
|
3714
|
+
this._sharingService = this.createReceiveOnlySharing();
|
|
3677
3715
|
if (config.signer) {
|
|
3678
3716
|
this.signer = config.signer;
|
|
3679
3717
|
this.setupAuth(config);
|
|
@@ -3815,6 +3853,42 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
3815
3853
|
get siweDomain() {
|
|
3816
3854
|
return this.config.domain ?? "app.tinycloud.xyz";
|
|
3817
3855
|
}
|
|
3856
|
+
/**
|
|
3857
|
+
* SharingService for receive-only access (no session required), bound to the
|
|
3858
|
+
* current service graph. This lets session-only users receive sharing links
|
|
3859
|
+
* without signIn(); full capabilities (generate) are added after signIn().
|
|
3860
|
+
*/
|
|
3861
|
+
createReceiveOnlySharing() {
|
|
3862
|
+
const receiveOnlyGraph = this._serviceGraph;
|
|
3863
|
+
return new SharingService({
|
|
3864
|
+
hosts: [this.config.host],
|
|
3865
|
+
// session: undefined - not needed for receive()
|
|
3866
|
+
invoke: receiveOnlyGraph.invoke,
|
|
3867
|
+
fetch: receiveOnlyGraph.fetch,
|
|
3868
|
+
assertActive: () => receiveOnlyGraph.assertActive(),
|
|
3869
|
+
keyProvider: this._keyProvider,
|
|
3870
|
+
registry: this._capabilityRegistry,
|
|
3871
|
+
createDelegationWasm: (params) => this.createDelegationWrapper(params),
|
|
3872
|
+
computeCid: (data, codec) => {
|
|
3873
|
+
if (!this.wasmBindings.computeCid) throw new Error("computeCid is unavailable");
|
|
3874
|
+
return this.wasmBindings.computeCid(data, codec);
|
|
3875
|
+
},
|
|
3876
|
+
// delegationManager: undefined - not needed for receive()
|
|
3877
|
+
createKVService: (config) => {
|
|
3878
|
+
const prefix = config.pathPrefix?.replace(/\/$/, "");
|
|
3879
|
+
const kvService = new KVService2({ prefix });
|
|
3880
|
+
const kvContext = receiveOnlyGraph.track(new ServiceContext2({
|
|
3881
|
+
invoke: config.invoke,
|
|
3882
|
+
fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
|
|
3883
|
+
hosts: config.hosts,
|
|
3884
|
+
telemetry: this.config.telemetry
|
|
3885
|
+
}));
|
|
3886
|
+
kvContext.setSession(config.session);
|
|
3887
|
+
kvService.initialize(kvContext);
|
|
3888
|
+
return kvService;
|
|
3889
|
+
}
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3818
3892
|
/**
|
|
3819
3893
|
* Set up authorization handler and TinyCloud instance.
|
|
3820
3894
|
* @internal
|
|
@@ -4074,6 +4148,102 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4074
4148
|
get restorableSession() {
|
|
4075
4149
|
return this.currentTinyCloudSession();
|
|
4076
4150
|
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Sign out and end the session on this instance.
|
|
4153
|
+
*
|
|
4154
|
+
* Every service built for the session (and every `useDelegation()` result)
|
|
4155
|
+
* is retired and the session key is replaced before anything is awaited, so
|
|
4156
|
+
* nothing captured from the session can sign once sign-out has started.
|
|
4157
|
+
* Pending work is aborted and the persisted session is cleared from the
|
|
4158
|
+
* configured session storage. Afterwards session accessors return
|
|
4159
|
+
* `undefined` and service calls fail with "Not signed in" until
|
|
4160
|
+
* {@link signIn} or {@link restoreSession}, which wait for a sign-out in
|
|
4161
|
+
* progress.
|
|
4162
|
+
*/
|
|
4163
|
+
signOut() {
|
|
4164
|
+
this.signOutInFlight ?? (this.signOutInFlight = this.endSession().finally(() => {
|
|
4165
|
+
this.signOutInFlight = void 0;
|
|
4166
|
+
}));
|
|
4167
|
+
return this.signOutInFlight;
|
|
4168
|
+
}
|
|
4169
|
+
async endSession() {
|
|
4170
|
+
const leavingAddress = this.address ?? this._restoredTcSession?.address;
|
|
4171
|
+
this.teardownSession();
|
|
4172
|
+
try {
|
|
4173
|
+
await this.accountRegistryTail;
|
|
4174
|
+
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4175
|
+
} finally {
|
|
4176
|
+
if (leavingAddress) await this.clearPersistedSessionFor(leavingAddress);
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Synchronously drop every piece of in-memory session authority and
|
|
4181
|
+
* invalidate sign-ins and restores still in flight (see `sessionEpoch`).
|
|
4182
|
+
*/
|
|
4183
|
+
teardownSession() {
|
|
4184
|
+
this.sessionEpoch++;
|
|
4185
|
+
this.pendingAccountRegistrySync?.controller.abort();
|
|
4186
|
+
const oldGraph = this._serviceGraph;
|
|
4187
|
+
const oldCore = this.tc;
|
|
4188
|
+
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
4189
|
+
this.sessionManager = sessionKey.manager;
|
|
4190
|
+
this.sessionKeyId = sessionKey.keyId;
|
|
4191
|
+
this.sessionKeyJwk = sessionKey.jwk;
|
|
4192
|
+
this.auth?.resetSession(
|
|
4193
|
+
sessionKey.manager,
|
|
4194
|
+
sessionKey.keyId,
|
|
4195
|
+
this.explicitHost ? [this.explicitHost] : void 0
|
|
4196
|
+
);
|
|
4197
|
+
this.config.host = this.explicitHost ?? DEFAULT_HOST;
|
|
4198
|
+
this._address = void 0;
|
|
4199
|
+
this._chainId = 1;
|
|
4200
|
+
this._restoredTcSession = void 0;
|
|
4201
|
+
this._restoredNodeFeatures = void 0;
|
|
4202
|
+
this._activeServiceSession = void 0;
|
|
4203
|
+
this._serviceContext = void 0;
|
|
4204
|
+
this._kv = void 0;
|
|
4205
|
+
this._sql = void 0;
|
|
4206
|
+
this._duckdb = void 0;
|
|
4207
|
+
this._hooks = void 0;
|
|
4208
|
+
this._vault = void 0;
|
|
4209
|
+
this._encryption = void 0;
|
|
4210
|
+
this._baseVaults.clear();
|
|
4211
|
+
this._baseSecrets.clear();
|
|
4212
|
+
this._secrets.clear();
|
|
4213
|
+
this._account = void 0;
|
|
4214
|
+
this._publicKV = void 0;
|
|
4215
|
+
this._delegationManager = void 0;
|
|
4216
|
+
this._spaceService = void 0;
|
|
4217
|
+
this.runtimePermissionGrants = [];
|
|
4218
|
+
this._recapOperationsCache = void 0;
|
|
4219
|
+
this.confirmedHostedSpaceIds.clear();
|
|
4220
|
+
this.hostedOwnedSpaceIds.clear();
|
|
4221
|
+
this.cancelOwnedSpaceHosts();
|
|
4222
|
+
this.storageFull.clear();
|
|
4223
|
+
this._bootstrapSkipped = false;
|
|
4224
|
+
this._bootstrapStatus = { skipped: false };
|
|
4225
|
+
this.replicationDelegateSession = void 0;
|
|
4226
|
+
this.replicationScopeError = void 0;
|
|
4227
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4228
|
+
oldGraph.retire();
|
|
4229
|
+
this.tc = this.auth ? new TinyCloud(this.auth, {
|
|
4230
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
4231
|
+
telemetry: this.config.telemetry
|
|
4232
|
+
}) : null;
|
|
4233
|
+
oldCore?.retireServices();
|
|
4234
|
+
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
4235
|
+
this._keyProvider = new WasmKeyProvider({ sessionManager: this.sessionManager });
|
|
4236
|
+
this._sharingService = this.createReceiveOnlySharing();
|
|
4237
|
+
}
|
|
4238
|
+
async clearPersistedSessionFor(address) {
|
|
4239
|
+
await (this.auth ? this.auth.clearPersistedSession(address) : this.config.sessionStorage?.clear(address));
|
|
4240
|
+
}
|
|
4241
|
+
/** Throws when a sign-out started after `epoch` was read. */
|
|
4242
|
+
assertSessionEpoch(epoch, operation) {
|
|
4243
|
+
if (epoch !== this.sessionEpoch) {
|
|
4244
|
+
throw new Error(`${operation} was cancelled by sign-out.`);
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4077
4247
|
/**
|
|
4078
4248
|
* Sign in and create a new session.
|
|
4079
4249
|
* This creates the user's space if it doesn't exist.
|
|
@@ -4087,11 +4257,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4087
4257
|
"Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
|
|
4088
4258
|
);
|
|
4089
4259
|
}
|
|
4260
|
+
const epoch = this.sessionEpoch;
|
|
4261
|
+
const attempt = ++this.latestSessionAttempt;
|
|
4262
|
+
await this.signOutInFlight;
|
|
4263
|
+
try {
|
|
4264
|
+
await this.establishSignIn(epoch, options);
|
|
4265
|
+
} catch (error) {
|
|
4266
|
+
if (epoch !== this.sessionEpoch && attempt === this.latestSessionAttempt) {
|
|
4267
|
+
await this.endSession();
|
|
4268
|
+
}
|
|
4269
|
+
throw error;
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
async establishSignIn(epoch, options) {
|
|
4273
|
+
if (!this.signer || !this.tc) {
|
|
4274
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
4275
|
+
}
|
|
4090
4276
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
4091
4277
|
await this.accountRegistryTail;
|
|
4092
4278
|
await this.wasmBindings.ensureInitialized?.();
|
|
4093
4279
|
const address = canonicalizeAddress2(await this.signer.getAddress());
|
|
4094
4280
|
const chainId = await this.signer.getChainId();
|
|
4281
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4095
4282
|
if (address !== this._address || chainId !== this._chainId) this.storageFull.clear();
|
|
4096
4283
|
this._address = address;
|
|
4097
4284
|
this._chainId = chainId;
|
|
@@ -4110,6 +4297,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4110
4297
|
this.confirmedHostedSpaceIds.clear();
|
|
4111
4298
|
this.hostedOwnedSpaceIds.clear();
|
|
4112
4299
|
await this.tc.signIn(options);
|
|
4300
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4113
4301
|
this.cancelOwnedSpaceHosts();
|
|
4114
4302
|
this.syncResolvedHostFromAuth();
|
|
4115
4303
|
for (const spaceId of this.auth.lastActivationActivatedSpaceIds ?? []) {
|
|
@@ -4118,6 +4306,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4118
4306
|
this.replicationDelegateSession = void 0;
|
|
4119
4307
|
this.replicationScopeError = void 0;
|
|
4120
4308
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4309
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4121
4310
|
const oldGraph = this._serviceGraph;
|
|
4122
4311
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4123
4312
|
oldGraph.retire();
|
|
@@ -4128,15 +4317,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4128
4317
|
this.sessionKeyJwk = signedInSession.jwk;
|
|
4129
4318
|
}
|
|
4130
4319
|
await this.initializeServices();
|
|
4320
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4131
4321
|
const primarySession = this.currentTinyCloudSession();
|
|
4132
4322
|
if (primarySession) {
|
|
4133
4323
|
this.registerPrimarySessionGrant(primarySession);
|
|
4134
4324
|
}
|
|
4135
4325
|
const bootstrapped = await this.bootstrapAccountIfNeeded();
|
|
4326
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4136
4327
|
await this.ensureRequestedEncryptionNetworks();
|
|
4328
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4137
4329
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
4138
4330
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(SECRETS_SPACE2));
|
|
4139
4331
|
}
|
|
4332
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4140
4333
|
if (!bootstrapped) {
|
|
4141
4334
|
this.scheduleAccountRegistrySync();
|
|
4142
4335
|
}
|
|
@@ -4318,6 +4511,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4318
4511
|
throw new Error("Account bootstrap requires a TinyCloud host");
|
|
4319
4512
|
}
|
|
4320
4513
|
const auth = this.auth;
|
|
4514
|
+
const graph = this._serviceGraph;
|
|
4321
4515
|
const sessions = /* @__PURE__ */ new Map();
|
|
4322
4516
|
const rawAbilitiesBySpace = /* @__PURE__ */ new Map();
|
|
4323
4517
|
const warnings = [];
|
|
@@ -4326,6 +4520,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4326
4520
|
const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
|
|
4327
4521
|
for (const step of steps) {
|
|
4328
4522
|
if (step.kind !== "session") continue;
|
|
4523
|
+
graph.assertActive();
|
|
4329
4524
|
if (step.space === "default" && canReusePrimaryBootstrapSession && primarySession) {
|
|
4330
4525
|
sessions.set(step.space, primarySession);
|
|
4331
4526
|
continue;
|
|
@@ -4353,6 +4548,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4353
4548
|
}
|
|
4354
4549
|
for (const step of steps) {
|
|
4355
4550
|
if (step.kind !== "host") continue;
|
|
4551
|
+
graph.assertActive();
|
|
4356
4552
|
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
4357
4553
|
if (!hosted.success) {
|
|
4358
4554
|
throw Object.assign(
|
|
@@ -4364,11 +4560,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4364
4560
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4365
4561
|
for (const step of steps) {
|
|
4366
4562
|
if (step.kind !== "activate") continue;
|
|
4563
|
+
graph.assertActive();
|
|
4367
4564
|
const session = sessions.get(step.space);
|
|
4368
4565
|
if (!session) {
|
|
4369
4566
|
throw new Error(`Missing bootstrap session for ${step.space}`);
|
|
4370
4567
|
}
|
|
4371
|
-
const activated = await activateSessionWithHost2(host, session.delegationHeader)
|
|
4568
|
+
const activated = await activateSessionWithHost2(host, session.delegationHeader, graph.signal).catch((error) => {
|
|
4569
|
+
graph.assertActive();
|
|
4570
|
+
throw error;
|
|
4571
|
+
});
|
|
4572
|
+
graph.assertActive();
|
|
4372
4573
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
4373
4574
|
throw Object.assign(
|
|
4374
4575
|
new Error(
|
|
@@ -4384,6 +4585,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4384
4585
|
);
|
|
4385
4586
|
}
|
|
4386
4587
|
for (const step of steps) {
|
|
4588
|
+
graph.assertActive();
|
|
4387
4589
|
if (step.kind === "account-index-schema") {
|
|
4388
4590
|
const ensured = await this.account.index.ensure();
|
|
4389
4591
|
if (!ensured.ok) {
|
|
@@ -5159,6 +5361,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5159
5361
|
* @param sessionData - The stored delegation data from the browser flow
|
|
5160
5362
|
*/
|
|
5161
5363
|
async restoreSession(sessionData) {
|
|
5364
|
+
const epoch = this.sessionEpoch;
|
|
5365
|
+
const attempt = ++this.latestSessionAttempt;
|
|
5366
|
+
await this.signOutInFlight;
|
|
5162
5367
|
await this.wasmBindings.ensureInitialized?.();
|
|
5163
5368
|
let restoredJwk;
|
|
5164
5369
|
try {
|
|
@@ -5348,6 +5553,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5348
5553
|
});
|
|
5349
5554
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
5350
5555
|
await this.accountRegistryTail;
|
|
5556
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5351
5557
|
const oldGraph = this._serviceGraph;
|
|
5352
5558
|
const oldCore = this.tc;
|
|
5353
5559
|
this.sessionManager = stagedManager;
|
|
@@ -5393,6 +5599,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5393
5599
|
this.replicationDelegateSession = compactReplicationSession;
|
|
5394
5600
|
this.replicationScopeError = replicationScopeError;
|
|
5395
5601
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
5602
|
+
if (epoch !== this.sessionEpoch) {
|
|
5603
|
+
oldGraph.retire();
|
|
5604
|
+
oldCore?.retireServices?.();
|
|
5605
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5606
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5607
|
+
}
|
|
5396
5608
|
oldGraph.retire();
|
|
5397
5609
|
if (replicationScopeError) {
|
|
5398
5610
|
stagedGraph.serviceContext.emit("replication.state", {
|
|
@@ -5403,6 +5615,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5403
5615
|
}
|
|
5404
5616
|
await this.bindReplicationRuntime(stagedGraph.serviceContext, serviceSession, stagedGraph.kv, replicationPrincipal);
|
|
5405
5617
|
oldCore?.retireServices?.();
|
|
5618
|
+
if (epoch !== this.sessionEpoch) {
|
|
5619
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5620
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5621
|
+
}
|
|
5406
5622
|
}
|
|
5407
5623
|
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
5408
5624
|
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
@@ -7261,6 +7477,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7261
7477
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
7262
7478
|
*/
|
|
7263
7479
|
async useRuntimeDelegation(delegation) {
|
|
7480
|
+
const graph = this._serviceGraph;
|
|
7264
7481
|
const session = this.currentTinyCloudSession();
|
|
7265
7482
|
if (!session) {
|
|
7266
7483
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7279,9 +7496,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7279
7496
|
try {
|
|
7280
7497
|
activateResult = await activateSessionWithHost2(
|
|
7281
7498
|
targetHost,
|
|
7282
|
-
delegation.delegationHeader
|
|
7499
|
+
delegation.delegationHeader,
|
|
7500
|
+
graph.signal
|
|
7283
7501
|
);
|
|
7284
7502
|
} catch (cause) {
|
|
7503
|
+
graph.assertActive();
|
|
7285
7504
|
throw Object.assign(
|
|
7286
7505
|
new Error("The TinyCloud host could not be reached to activate the delegation."),
|
|
7287
7506
|
{ code: "ACTIVATION_TRANSPORT_FAILED", cause }
|
|
@@ -7301,6 +7520,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7301
7520
|
{ cause: activateResult }
|
|
7302
7521
|
);
|
|
7303
7522
|
}
|
|
7523
|
+
graph.assertActive();
|
|
7304
7524
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
7305
7525
|
(grant) => grant.delegation.cid !== delegation.cid
|
|
7306
7526
|
);
|
|
@@ -7317,6 +7537,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7317
7537
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
7318
7538
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
7319
7539
|
}
|
|
7540
|
+
const graph = this._serviceGraph;
|
|
7320
7541
|
const session = this.currentTinyCloudSession();
|
|
7321
7542
|
if (!session) {
|
|
7322
7543
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7397,14 +7618,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7397
7618
|
}
|
|
7398
7619
|
})();
|
|
7399
7620
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
7621
|
+
graph.assertActive();
|
|
7400
7622
|
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
7401
7623
|
...prepared,
|
|
7402
7624
|
signature
|
|
7403
7625
|
});
|
|
7404
7626
|
const activateResult = await activateSessionWithHost2(
|
|
7405
7627
|
this.config.host,
|
|
7406
|
-
delegatedSession.delegationHeader
|
|
7407
|
-
|
|
7628
|
+
delegatedSession.delegationHeader,
|
|
7629
|
+
graph.signal
|
|
7630
|
+
).catch((error) => {
|
|
7631
|
+
graph.assertActive();
|
|
7632
|
+
throw error;
|
|
7633
|
+
});
|
|
7408
7634
|
if (!activateResult.success) {
|
|
7409
7635
|
if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
|
|
7410
7636
|
throw Object.assign(
|
|
@@ -7425,6 +7651,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7425
7651
|
expiresAt,
|
|
7426
7652
|
{ siwe: prepared.siwe, signature }
|
|
7427
7653
|
);
|
|
7654
|
+
graph.assertActive();
|
|
7428
7655
|
this.runtimePermissionGrants.push({
|
|
7429
7656
|
session: {
|
|
7430
7657
|
delegationHeader: delegatedSession.delegationHeader,
|
|
@@ -8888,6 +9115,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8888
9115
|
* @returns A DelegatedAccess instance for performing operations
|
|
8889
9116
|
*/
|
|
8890
9117
|
async useDelegation(delegation) {
|
|
9118
|
+
const graph = this._serviceGraph;
|
|
8891
9119
|
const delegationHeader = delegation.delegationHeader;
|
|
8892
9120
|
const targetHost = delegation.host ?? this.config.host;
|
|
8893
9121
|
if (this.isSessionOnly) {
|
|
@@ -8929,7 +9157,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8929
9157
|
targetHost,
|
|
8930
9158
|
this.wasmBindings.invoke,
|
|
8931
9159
|
this.wasmBindings.invokeAny,
|
|
8932
|
-
this.config.telemetry
|
|
9160
|
+
this.config.telemetry,
|
|
9161
|
+
graph
|
|
8933
9162
|
);
|
|
8934
9163
|
}
|
|
8935
9164
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -8953,14 +9182,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8953
9182
|
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
8954
9183
|
});
|
|
8955
9184
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
9185
|
+
graph.assertActive();
|
|
8956
9186
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
8957
9187
|
...prepared,
|
|
8958
9188
|
signature
|
|
8959
9189
|
});
|
|
8960
9190
|
const activateResult = await activateSessionWithHost2(
|
|
8961
9191
|
targetHost,
|
|
8962
|
-
invokerSession.delegationHeader
|
|
8963
|
-
|
|
9192
|
+
invokerSession.delegationHeader,
|
|
9193
|
+
graph.signal
|
|
9194
|
+
).catch((error) => {
|
|
9195
|
+
graph.assertActive();
|
|
9196
|
+
throw error;
|
|
9197
|
+
});
|
|
8964
9198
|
if (!activateResult.success) {
|
|
8965
9199
|
throw Object.assign(
|
|
8966
9200
|
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
@@ -8979,6 +9213,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8979
9213
|
siwe: prepared.siwe,
|
|
8980
9214
|
signature
|
|
8981
9215
|
};
|
|
9216
|
+
graph.assertActive();
|
|
8982
9217
|
this.trackReceivedDelegation(delegation, jwk);
|
|
8983
9218
|
this.installRuntimeGrantFromServiceSession(
|
|
8984
9219
|
delegation,
|
|
@@ -8997,7 +9232,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8997
9232
|
targetHost,
|
|
8998
9233
|
this.wasmBindings.invoke,
|
|
8999
9234
|
this.wasmBindings.invokeAny,
|
|
9000
|
-
this.config.telemetry
|
|
9235
|
+
this.config.telemetry,
|
|
9236
|
+
graph
|
|
9001
9237
|
);
|
|
9002
9238
|
}
|
|
9003
9239
|
/**
|