@tinycloud/node-sdk 3.2.6-beta.1 → 3.2.6-beta.3
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-DEdSyQ0I.d.cts} +68 -3
- package/dist/{WasmKeyProvider-LXRTjZTA.d.ts → WasmKeyProvider-DEdSyQ0I.d.ts} +68 -3
- package/dist/core.cjs +308 -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 +308 -59
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +308 -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 +308 -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,115 @@ 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
|
+
const leavingAuth = this.auth;
|
|
4172
|
+
try {
|
|
4173
|
+
this.teardownSession();
|
|
4174
|
+
} finally {
|
|
4175
|
+
try {
|
|
4176
|
+
await this.accountRegistryTail;
|
|
4177
|
+
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4178
|
+
} finally {
|
|
4179
|
+
if (leavingAddress) {
|
|
4180
|
+
await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
/**
|
|
4186
|
+
* Synchronously drop every piece of in-memory session authority and
|
|
4187
|
+
* invalidate sign-ins and restores still in flight (see `sessionEpoch`).
|
|
4188
|
+
*/
|
|
4189
|
+
teardownSession() {
|
|
4190
|
+
this.sessionEpoch++;
|
|
4191
|
+
this.pendingAccountRegistrySync?.controller.abort();
|
|
4192
|
+
const oldGraph = this._serviceGraph;
|
|
4193
|
+
const oldCore = this.tc;
|
|
4194
|
+
const oldAuth = this.auth;
|
|
4195
|
+
this.auth = null;
|
|
4196
|
+
this.tc = null;
|
|
4197
|
+
this._address = void 0;
|
|
4198
|
+
this._restoredTcSession = void 0;
|
|
4199
|
+
this._activeServiceSession = void 0;
|
|
4200
|
+
oldGraph.retire();
|
|
4201
|
+
oldCore?.retireServices();
|
|
4202
|
+
oldAuth?.resetSession(
|
|
4203
|
+
this.sessionManager,
|
|
4204
|
+
this.sessionKeyId,
|
|
4205
|
+
this.explicitHost ? [this.explicitHost] : void 0
|
|
4206
|
+
);
|
|
4207
|
+
this.config.host = this.explicitHost ?? DEFAULT_HOST;
|
|
4208
|
+
this._chainId = 1;
|
|
4209
|
+
this._restoredNodeFeatures = void 0;
|
|
4210
|
+
this._activeServiceSession = void 0;
|
|
4211
|
+
this._serviceContext = void 0;
|
|
4212
|
+
this._kv = void 0;
|
|
4213
|
+
this._sql = void 0;
|
|
4214
|
+
this._duckdb = void 0;
|
|
4215
|
+
this._hooks = void 0;
|
|
4216
|
+
this._vault = void 0;
|
|
4217
|
+
this._encryption = void 0;
|
|
4218
|
+
this._baseVaults.clear();
|
|
4219
|
+
this._baseSecrets.clear();
|
|
4220
|
+
this._secrets.clear();
|
|
4221
|
+
this._account = void 0;
|
|
4222
|
+
this._publicKV = void 0;
|
|
4223
|
+
this._delegationManager = void 0;
|
|
4224
|
+
this._spaceService = void 0;
|
|
4225
|
+
this.runtimePermissionGrants = [];
|
|
4226
|
+
this._recapOperationsCache = void 0;
|
|
4227
|
+
this.confirmedHostedSpaceIds.clear();
|
|
4228
|
+
this.hostedOwnedSpaceIds.clear();
|
|
4229
|
+
this.cancelOwnedSpaceHosts();
|
|
4230
|
+
this.storageFull.clear();
|
|
4231
|
+
this._bootstrapSkipped = false;
|
|
4232
|
+
this._bootstrapStatus = { skipped: false };
|
|
4233
|
+
this.replicationDelegateSession = void 0;
|
|
4234
|
+
this.replicationScopeError = void 0;
|
|
4235
|
+
const sessionKey = createDefaultSessionKey(this.wasmBindings);
|
|
4236
|
+
this.sessionManager = sessionKey.manager;
|
|
4237
|
+
this.sessionKeyId = sessionKey.keyId;
|
|
4238
|
+
this.sessionKeyJwk = sessionKey.jwk;
|
|
4239
|
+
oldAuth?.resetSession(
|
|
4240
|
+
sessionKey.manager,
|
|
4241
|
+
sessionKey.keyId,
|
|
4242
|
+
this.explicitHost ? [this.explicitHost] : void 0
|
|
4243
|
+
);
|
|
4244
|
+
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4245
|
+
this.auth = oldAuth;
|
|
4246
|
+
this.tc = this.auth ? new TinyCloud(this.auth, {
|
|
4247
|
+
invokeAny: this.invokeAnyWithRuntimePermissions,
|
|
4248
|
+
telemetry: this.config.telemetry
|
|
4249
|
+
}) : null;
|
|
4250
|
+
this._capabilityRegistry = new CapabilityKeyRegistry();
|
|
4251
|
+
this._keyProvider = new WasmKeyProvider({ sessionManager: this.sessionManager });
|
|
4252
|
+
this._sharingService = this.createReceiveOnlySharing();
|
|
4253
|
+
}
|
|
4254
|
+
/** Throws when a sign-out started after `epoch` was read. */
|
|
4255
|
+
assertSessionEpoch(epoch, operation) {
|
|
4256
|
+
if (epoch !== this.sessionEpoch) {
|
|
4257
|
+
throw new Error(`${operation} was cancelled by sign-out.`);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4077
4260
|
/**
|
|
4078
4261
|
* Sign in and create a new session.
|
|
4079
4262
|
* This creates the user's space if it doesn't exist.
|
|
@@ -4087,11 +4270,28 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4087
4270
|
"Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
|
|
4088
4271
|
);
|
|
4089
4272
|
}
|
|
4273
|
+
const epoch = this.sessionEpoch;
|
|
4274
|
+
const attempt = ++this.latestSessionAttempt;
|
|
4275
|
+
await this.signOutInFlight;
|
|
4276
|
+
try {
|
|
4277
|
+
await this.establishSignIn(epoch, options);
|
|
4278
|
+
} catch (error) {
|
|
4279
|
+
if (epoch !== this.sessionEpoch && attempt === this.latestSessionAttempt) {
|
|
4280
|
+
await this.endSession();
|
|
4281
|
+
}
|
|
4282
|
+
throw error;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
async establishSignIn(epoch, options) {
|
|
4286
|
+
if (!this.signer || !this.tc) {
|
|
4287
|
+
throw new Error("Not signed in. Call signIn() first.");
|
|
4288
|
+
}
|
|
4090
4289
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
4091
4290
|
await this.accountRegistryTail;
|
|
4092
4291
|
await this.wasmBindings.ensureInitialized?.();
|
|
4093
4292
|
const address = canonicalizeAddress2(await this.signer.getAddress());
|
|
4094
4293
|
const chainId = await this.signer.getChainId();
|
|
4294
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4095
4295
|
if (address !== this._address || chainId !== this._chainId) this.storageFull.clear();
|
|
4096
4296
|
this._address = address;
|
|
4097
4297
|
this._chainId = chainId;
|
|
@@ -4110,6 +4310,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4110
4310
|
this.confirmedHostedSpaceIds.clear();
|
|
4111
4311
|
this.hostedOwnedSpaceIds.clear();
|
|
4112
4312
|
await this.tc.signIn(options);
|
|
4313
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4113
4314
|
this.cancelOwnedSpaceHosts();
|
|
4114
4315
|
this.syncResolvedHostFromAuth();
|
|
4115
4316
|
for (const spaceId of this.auth.lastActivationActivatedSpaceIds ?? []) {
|
|
@@ -4118,6 +4319,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4118
4319
|
this.replicationDelegateSession = void 0;
|
|
4119
4320
|
this.replicationScopeError = void 0;
|
|
4120
4321
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
4322
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4121
4323
|
const oldGraph = this._serviceGraph;
|
|
4122
4324
|
this._serviceGraph = this.createServiceGraphLifetime();
|
|
4123
4325
|
oldGraph.retire();
|
|
@@ -4128,15 +4330,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4128
4330
|
this.sessionKeyJwk = signedInSession.jwk;
|
|
4129
4331
|
}
|
|
4130
4332
|
await this.initializeServices();
|
|
4333
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4131
4334
|
const primarySession = this.currentTinyCloudSession();
|
|
4132
4335
|
if (primarySession) {
|
|
4133
4336
|
this.registerPrimarySessionGrant(primarySession);
|
|
4134
4337
|
}
|
|
4135
4338
|
const bootstrapped = await this.bootstrapAccountIfNeeded();
|
|
4339
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4136
4340
|
await this.ensureRequestedEncryptionNetworks();
|
|
4341
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4137
4342
|
if (!bootstrapped && this.config.manifest === void 0 && this.config.capabilityRequest === void 0) {
|
|
4138
4343
|
await this.ensureOwnedSpaceHostedById(this.ownedSpaceId(SECRETS_SPACE2));
|
|
4139
4344
|
}
|
|
4345
|
+
this.assertSessionEpoch(epoch, "Sign-in");
|
|
4140
4346
|
if (!bootstrapped) {
|
|
4141
4347
|
this.scheduleAccountRegistrySync();
|
|
4142
4348
|
}
|
|
@@ -4318,6 +4524,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4318
4524
|
throw new Error("Account bootstrap requires a TinyCloud host");
|
|
4319
4525
|
}
|
|
4320
4526
|
const auth = this.auth;
|
|
4527
|
+
const graph = this._serviceGraph;
|
|
4321
4528
|
const sessions = /* @__PURE__ */ new Map();
|
|
4322
4529
|
const rawAbilitiesBySpace = /* @__PURE__ */ new Map();
|
|
4323
4530
|
const warnings = [];
|
|
@@ -4326,6 +4533,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4326
4533
|
const canReusePrimaryBootstrapSession = primarySession?.spaceId === defaultSpaceId && auth.capabilityRequest === BOOTSTRAP_SESSION_REQUESTS.default;
|
|
4327
4534
|
for (const step of steps) {
|
|
4328
4535
|
if (step.kind !== "session") continue;
|
|
4536
|
+
graph.assertActive();
|
|
4329
4537
|
if (step.space === "default" && canReusePrimaryBootstrapSession && primarySession) {
|
|
4330
4538
|
sessions.set(step.space, primarySession);
|
|
4331
4539
|
continue;
|
|
@@ -4353,6 +4561,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4353
4561
|
}
|
|
4354
4562
|
for (const step of steps) {
|
|
4355
4563
|
if (step.kind !== "host") continue;
|
|
4564
|
+
graph.assertActive();
|
|
4356
4565
|
const hosted = await auth.hostOwnedSpaceResult(step.spaceId, "bootstrap-host");
|
|
4357
4566
|
if (!hosted.success) {
|
|
4358
4567
|
throw Object.assign(
|
|
@@ -4364,11 +4573,16 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4364
4573
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4365
4574
|
for (const step of steps) {
|
|
4366
4575
|
if (step.kind !== "activate") continue;
|
|
4576
|
+
graph.assertActive();
|
|
4367
4577
|
const session = sessions.get(step.space);
|
|
4368
4578
|
if (!session) {
|
|
4369
4579
|
throw new Error(`Missing bootstrap session for ${step.space}`);
|
|
4370
4580
|
}
|
|
4371
|
-
const activated = await activateSessionWithHost2(host, session.delegationHeader)
|
|
4581
|
+
const activated = await activateSessionWithHost2(host, session.delegationHeader, graph.signal).catch((error) => {
|
|
4582
|
+
graph.assertActive();
|
|
4583
|
+
throw error;
|
|
4584
|
+
});
|
|
4585
|
+
graph.assertActive();
|
|
4372
4586
|
if (!activated.success || activated.skipped?.includes(step.spaceId)) {
|
|
4373
4587
|
throw Object.assign(
|
|
4374
4588
|
new Error(
|
|
@@ -4384,6 +4598,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
4384
4598
|
);
|
|
4385
4599
|
}
|
|
4386
4600
|
for (const step of steps) {
|
|
4601
|
+
graph.assertActive();
|
|
4387
4602
|
if (step.kind === "account-index-schema") {
|
|
4388
4603
|
const ensured = await this.account.index.ensure();
|
|
4389
4604
|
if (!ensured.ok) {
|
|
@@ -5159,6 +5374,9 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5159
5374
|
* @param sessionData - The stored delegation data from the browser flow
|
|
5160
5375
|
*/
|
|
5161
5376
|
async restoreSession(sessionData) {
|
|
5377
|
+
const epoch = this.sessionEpoch;
|
|
5378
|
+
const attempt = ++this.latestSessionAttempt;
|
|
5379
|
+
await this.signOutInFlight;
|
|
5162
5380
|
await this.wasmBindings.ensureInitialized?.();
|
|
5163
5381
|
let restoredJwk;
|
|
5164
5382
|
try {
|
|
@@ -5348,6 +5566,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5348
5566
|
});
|
|
5349
5567
|
this.pendingAccountRegistrySync?.controller.abort();
|
|
5350
5568
|
await this.accountRegistryTail;
|
|
5569
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5351
5570
|
const oldGraph = this._serviceGraph;
|
|
5352
5571
|
const oldCore = this.tc;
|
|
5353
5572
|
this.sessionManager = stagedManager;
|
|
@@ -5393,6 +5612,12 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5393
5612
|
this.replicationDelegateSession = compactReplicationSession;
|
|
5394
5613
|
this.replicationScopeError = replicationScopeError;
|
|
5395
5614
|
if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
|
|
5615
|
+
if (epoch !== this.sessionEpoch) {
|
|
5616
|
+
oldGraph.retire();
|
|
5617
|
+
oldCore?.retireServices?.();
|
|
5618
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5619
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5620
|
+
}
|
|
5396
5621
|
oldGraph.retire();
|
|
5397
5622
|
if (replicationScopeError) {
|
|
5398
5623
|
stagedGraph.serviceContext.emit("replication.state", {
|
|
@@ -5403,6 +5628,10 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
5403
5628
|
}
|
|
5404
5629
|
await this.bindReplicationRuntime(stagedGraph.serviceContext, serviceSession, stagedGraph.kv, replicationPrincipal);
|
|
5405
5630
|
oldCore?.retireServices?.();
|
|
5631
|
+
if (epoch !== this.sessionEpoch) {
|
|
5632
|
+
if (attempt === this.latestSessionAttempt) await this.endSession();
|
|
5633
|
+
this.assertSessionEpoch(epoch, "Session restore");
|
|
5634
|
+
}
|
|
5406
5635
|
}
|
|
5407
5636
|
stagePrimarySessionState(session, verificationMethod, host, sessionExpiry, recap) {
|
|
5408
5637
|
if (!session?.siwe) return { cache: void 0, grants: [] };
|
|
@@ -7261,6 +7490,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7261
7490
|
* matching service calls and downstream `delegateTo()` calls can use it.
|
|
7262
7491
|
*/
|
|
7263
7492
|
async useRuntimeDelegation(delegation) {
|
|
7493
|
+
const graph = this._serviceGraph;
|
|
7264
7494
|
const session = this.currentTinyCloudSession();
|
|
7265
7495
|
if (!session) {
|
|
7266
7496
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7279,9 +7509,11 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7279
7509
|
try {
|
|
7280
7510
|
activateResult = await activateSessionWithHost2(
|
|
7281
7511
|
targetHost,
|
|
7282
|
-
delegation.delegationHeader
|
|
7512
|
+
delegation.delegationHeader,
|
|
7513
|
+
graph.signal
|
|
7283
7514
|
);
|
|
7284
7515
|
} catch (cause) {
|
|
7516
|
+
graph.assertActive();
|
|
7285
7517
|
throw Object.assign(
|
|
7286
7518
|
new Error("The TinyCloud host could not be reached to activate the delegation."),
|
|
7287
7519
|
{ code: "ACTIVATION_TRANSPORT_FAILED", cause }
|
|
@@ -7301,6 +7533,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7301
7533
|
{ cause: activateResult }
|
|
7302
7534
|
);
|
|
7303
7535
|
}
|
|
7536
|
+
graph.assertActive();
|
|
7304
7537
|
this.runtimePermissionGrants = this.runtimePermissionGrants.filter(
|
|
7305
7538
|
(grant) => grant.delegation.cid !== delegation.cid
|
|
7306
7539
|
);
|
|
@@ -7317,6 +7550,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7317
7550
|
if (!Array.isArray(permissions) || permissions.length === 0) {
|
|
7318
7551
|
throw new Error("grantRuntimePermissions requires a non-empty permissions array");
|
|
7319
7552
|
}
|
|
7553
|
+
const graph = this._serviceGraph;
|
|
7320
7554
|
const session = this.currentTinyCloudSession();
|
|
7321
7555
|
if (!session) {
|
|
7322
7556
|
throw new SessionExpiredError(/* @__PURE__ */ new Date(0));
|
|
@@ -7397,14 +7631,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7397
7631
|
}
|
|
7398
7632
|
})();
|
|
7399
7633
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
7634
|
+
graph.assertActive();
|
|
7400
7635
|
const delegatedSession = this.wasmBindings.completeSessionSetup({
|
|
7401
7636
|
...prepared,
|
|
7402
7637
|
signature
|
|
7403
7638
|
});
|
|
7404
7639
|
const activateResult = await activateSessionWithHost2(
|
|
7405
7640
|
this.config.host,
|
|
7406
|
-
delegatedSession.delegationHeader
|
|
7407
|
-
|
|
7641
|
+
delegatedSession.delegationHeader,
|
|
7642
|
+
graph.signal
|
|
7643
|
+
).catch((error) => {
|
|
7644
|
+
graph.assertActive();
|
|
7645
|
+
throw error;
|
|
7646
|
+
});
|
|
7408
7647
|
if (!activateResult.success) {
|
|
7409
7648
|
if (rawForDelegation.some((entry) => this.isRawRevocationPermissionEntry(entry)) && activateResult.status === 401 && /Cannot find parent delegation/i.test(activateResult.error ?? "")) {
|
|
7410
7649
|
throw Object.assign(
|
|
@@ -7425,6 +7664,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
7425
7664
|
expiresAt,
|
|
7426
7665
|
{ siwe: prepared.siwe, signature }
|
|
7427
7666
|
);
|
|
7667
|
+
graph.assertActive();
|
|
7428
7668
|
this.runtimePermissionGrants.push({
|
|
7429
7669
|
session: {
|
|
7430
7670
|
delegationHeader: delegatedSession.delegationHeader,
|
|
@@ -8888,6 +9128,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8888
9128
|
* @returns A DelegatedAccess instance for performing operations
|
|
8889
9129
|
*/
|
|
8890
9130
|
async useDelegation(delegation) {
|
|
9131
|
+
const graph = this._serviceGraph;
|
|
8891
9132
|
const delegationHeader = delegation.delegationHeader;
|
|
8892
9133
|
const targetHost = delegation.host ?? this.config.host;
|
|
8893
9134
|
if (this.isSessionOnly) {
|
|
@@ -8929,7 +9170,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8929
9170
|
targetHost,
|
|
8930
9171
|
this.wasmBindings.invoke,
|
|
8931
9172
|
this.wasmBindings.invokeAny,
|
|
8932
|
-
this.config.telemetry
|
|
9173
|
+
this.config.telemetry,
|
|
9174
|
+
graph
|
|
8933
9175
|
);
|
|
8934
9176
|
}
|
|
8935
9177
|
const mySession = this.auth?.tinyCloudSession;
|
|
@@ -8953,14 +9195,19 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8953
9195
|
...Object.keys(rawAbilities).length > 0 ? { rawAbilities } : {}
|
|
8954
9196
|
});
|
|
8955
9197
|
const signature = await this.signer.signMessage(prepared.siwe);
|
|
9198
|
+
graph.assertActive();
|
|
8956
9199
|
const invokerSession = this.wasmBindings.completeSessionSetup({
|
|
8957
9200
|
...prepared,
|
|
8958
9201
|
signature
|
|
8959
9202
|
});
|
|
8960
9203
|
const activateResult = await activateSessionWithHost2(
|
|
8961
9204
|
targetHost,
|
|
8962
|
-
invokerSession.delegationHeader
|
|
8963
|
-
|
|
9205
|
+
invokerSession.delegationHeader,
|
|
9206
|
+
graph.signal
|
|
9207
|
+
).catch((error) => {
|
|
9208
|
+
graph.assertActive();
|
|
9209
|
+
throw error;
|
|
9210
|
+
});
|
|
8964
9211
|
if (!activateResult.success) {
|
|
8965
9212
|
throw Object.assign(
|
|
8966
9213
|
new Error(`Failed to activate delegated session: ${describeHostFailure2(activateResult)}`),
|
|
@@ -8979,6 +9226,7 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8979
9226
|
siwe: prepared.siwe,
|
|
8980
9227
|
signature
|
|
8981
9228
|
};
|
|
9229
|
+
graph.assertActive();
|
|
8982
9230
|
this.trackReceivedDelegation(delegation, jwk);
|
|
8983
9231
|
this.installRuntimeGrantFromServiceSession(
|
|
8984
9232
|
delegation,
|
|
@@ -8997,7 +9245,8 @@ var _TinyCloudNode = class _TinyCloudNode {
|
|
|
8997
9245
|
targetHost,
|
|
8998
9246
|
this.wasmBindings.invoke,
|
|
8999
9247
|
this.wasmBindings.invokeAny,
|
|
9000
|
-
this.config.telemetry
|
|
9248
|
+
this.config.telemetry,
|
|
9249
|
+
graph
|
|
9001
9250
|
);
|
|
9002
9251
|
}
|
|
9003
9252
|
/**
|