@tinycloud/node-sdk 3.11.1-beta.0 → 3.12.0-beta.1

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.
Files changed (35) hide show
  1. package/dist/NodeUserAuthorization-BtoDI9in.d.cts +900 -0
  2. package/dist/NodeUserAuthorization-BtoDI9in.d.ts +900 -0
  3. package/dist/{TinyCloudNode-AUotFGzy.d.ts → TinyCloudNode-C24728N_.d.cts} +30 -875
  4. package/dist/{TinyCloudNode-BmpjkpWR.d.cts → TinyCloudNode-DLrINr98.d.ts} +30 -875
  5. package/dist/{authority-dRnJD4iL.d.ts → authority-BQN2Z3st.d.ts} +1 -1
  6. package/dist/{authority-BYSEyjDI.d.cts → authority-DDdheOL6.d.cts} +1 -1
  7. package/dist/core.cjs +145 -90
  8. package/dist/core.cjs.map +1 -1
  9. package/dist/core.d.cts +5 -4
  10. package/dist/core.d.ts +5 -4
  11. package/dist/core.js +145 -90
  12. package/dist/core.js.map +1 -1
  13. package/dist/credentials-core.d.cts +1 -1
  14. package/dist/credentials-core.d.ts +1 -1
  15. package/dist/credentials.d.cts +1 -1
  16. package/dist/credentials.d.ts +1 -1
  17. package/dist/{delegation-Ckc3UETl.d.cts → delegation-BQbAuKad.d.cts} +1 -1
  18. package/dist/{delegation-Ckc3UETl.d.ts → delegation-BQbAuKad.d.ts} +1 -1
  19. package/dist/encryption-keys.d.cts +3 -2
  20. package/dist/encryption-keys.d.ts +3 -2
  21. package/dist/index.cjs +145 -90
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +4 -3
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.js +145 -90
  26. package/dist/index.js.map +1 -1
  27. package/dist/replication/authority.d.cts +4 -3
  28. package/dist/replication/authority.d.ts +4 -3
  29. package/dist/space-hosting.cjs +141 -0
  30. package/dist/space-hosting.cjs.map +1 -0
  31. package/dist/space-hosting.d.cts +69 -0
  32. package/dist/space-hosting.d.ts +69 -0
  33. package/dist/space-hosting.js +115 -0
  34. package/dist/space-hosting.js.map +1 -0
  35. package/package.json +11 -1
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-AUotFGzy.js';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-DLrINr98.js';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-BmpjkpWR.cjs';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-C24728N_.cjs';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
package/dist/core.cjs CHANGED
@@ -568,6 +568,8 @@ var NodeUserAuthorization = class {
568
568
  * delegation lists them: the node answers a repeat with empty lists.
569
569
  */
570
570
  this._lastActivationActivatedSpaceIds = [];
571
+ /** See {@link spaceHosting}. Written only from node answers. */
572
+ this._spaceHosting = "unknown";
571
573
  this.wasm = config.wasmBindings;
572
574
  this.signer = config.signer;
573
575
  this.signStrategy = config.signStrategy ?? defaultSignStrategy;
@@ -727,6 +729,7 @@ var NodeUserAuthorization = class {
727
729
  this.registryVerdict = void 0;
728
730
  this._lastActivationSkippedSpaceIds = [];
729
731
  this._lastActivationActivatedSpaceIds = [];
732
+ this._spaceHosting = "unknown";
730
733
  if (!session) {
731
734
  this._session = void 0;
732
735
  this._tinyCloudSession = void 0;
@@ -867,6 +870,18 @@ var NodeUserAuthorization = class {
867
870
  get lastActivationActivatedSpaceIds() {
868
871
  return [...this._lastActivationActivatedSpaceIds];
869
872
  }
873
+ /**
874
+ * The node's last answer for whether this session's primary space is hosted:
875
+ * recorded from the sign-in activation, or by {@link noteSpaceHosting}.
876
+ * `unknown` until one of them answers, and after a restore.
877
+ */
878
+ get spaceHosting() {
879
+ return this._spaceHosting;
880
+ }
881
+ /** @internal Record the hosting probe's answer for the primary space. */
882
+ noteSpaceHosting(hosting) {
883
+ this._spaceHosting = hosting;
884
+ }
870
885
  /**
871
886
  * Compute the `abilities` map the WASM `prepareSession` call should
872
887
  * see at sign-in time.
@@ -1327,103 +1342,55 @@ var NodeUserAuthorization = class {
1327
1342
  this._tinyCloudSession.delegationHeader
1328
1343
  );
1329
1344
  this.recordActivationSkippedSpaces(result, primarySpaceId);
1345
+ if (!result.success && result.status !== 404) {
1346
+ throw Object.assign(
1347
+ new Error(`Failed to activate session: ${describeHostFailure(result)}`),
1348
+ { cause: result }
1349
+ );
1350
+ }
1351
+ await this.ensurePrimarySpaceHosted(this._spaceHosting);
1352
+ }
1353
+ /**
1354
+ * The create step of {@link ensureSpaceExists}: given the node's answer for
1355
+ * the primary space, host it through the space creation handler when it is
1356
+ * `not-hosted`. Any other answer, and a missing handler, change nothing. The
1357
+ * caller supplies the answer (sign-in's activation, or the hosting probe);
1358
+ * an empty activation list is never one.
1359
+ *
1360
+ * @throws Error if the creation handler fails or times out, or hosting fails
1361
+ */
1362
+ async ensurePrimarySpaceHosted(hosting) {
1363
+ if (hosting !== "not-hosted") {
1364
+ return;
1365
+ }
1366
+ const session = this._tinyCloudSession;
1367
+ if (!session) {
1368
+ throw new Error("Must be signed in to host the primary space");
1369
+ }
1330
1370
  const handler = this.spaceCreationHandler ?? (this.autoCreateSpace ? new import_sdk_core5.AutoApproveSpaceCreationHandler() : void 0);
1371
+ if (!handler) {
1372
+ return;
1373
+ }
1374
+ await this.ensureTinyCloudHosts();
1375
+ const host = this.primaryTinyCloudHost;
1376
+ const primarySpaceId = session.spaceId;
1331
1377
  const creationContext = {
1332
1378
  spaceId: primarySpaceId,
1333
1379
  address: this._address,
1334
1380
  chainId: this._chainId,
1335
1381
  host
1336
1382
  };
1337
- if (result.success) {
1338
- const primarySkipped = result.skipped?.includes(primarySpaceId);
1339
- if (!primarySkipped) {
1340
- return;
1341
- }
1342
- if (!handler) {
1343
- return;
1344
- }
1345
- await this.guardSpaceCreation(handler, creationContext);
1346
- const confirmed = await handler.confirmSpaceCreation(creationContext);
1347
- if (!confirmed) {
1348
- return;
1349
- }
1350
- try {
1351
- const created = await this.hostSpace();
1352
- if (!created.success) {
1353
- const err2 = Object.assign(
1354
- new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
1355
- { cause: created }
1356
- );
1357
- throw err2;
1358
- }
1359
- } catch (error) {
1360
- handler.onSpaceCreationFailed?.(
1361
- creationContext,
1362
- error instanceof Error ? error : new Error(String(error))
1363
- );
1364
- throw error;
1365
- }
1366
- await new Promise((resolve) => setTimeout(resolve, 100));
1367
- const retryResult = await (0, import_sdk_core5.activateSessionWithHost)(
1368
- host,
1369
- this._tinyCloudSession.delegationHeader
1370
- );
1371
- if (!retryResult.success) {
1372
- const err2 = Object.assign(
1373
- new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
1374
- { cause: retryResult }
1375
- );
1376
- handler.onSpaceCreationFailed?.(creationContext, err2);
1377
- throw err2;
1378
- }
1379
- handler.onSpaceCreated?.(creationContext);
1380
- return;
1381
- }
1382
- if (result.status === 404) {
1383
- if (!handler) {
1384
- return;
1385
- }
1386
- await this.guardSpaceCreation(handler, creationContext);
1387
- const confirmed = await handler.confirmSpaceCreation(creationContext);
1388
- if (!confirmed) {
1389
- return;
1390
- }
1391
- try {
1392
- const created = await this.hostSpace();
1393
- if (!created.success) {
1394
- const err2 = Object.assign(
1395
- new Error(`Failed to create space ${primarySpaceId}: ${describeHostFailure(created)}`),
1396
- { cause: created }
1397
- );
1398
- throw err2;
1399
- }
1400
- } catch (error) {
1401
- handler.onSpaceCreationFailed?.(
1402
- creationContext,
1403
- error instanceof Error ? error : new Error(String(error))
1404
- );
1405
- throw error;
1406
- }
1407
- await new Promise((resolve) => setTimeout(resolve, 100));
1408
- const retryResult = await (0, import_sdk_core5.activateSessionWithHost)(
1409
- host,
1410
- this._tinyCloudSession.delegationHeader
1411
- );
1412
- if (!retryResult.success) {
1413
- const err2 = Object.assign(
1414
- new Error(`Failed to activate session after creating space: ${describeHostFailure(retryResult)}`),
1415
- { cause: retryResult }
1416
- );
1417
- handler.onSpaceCreationFailed?.(creationContext, err2);
1418
- throw err2;
1419
- }
1420
- handler.onSpaceCreated?.(creationContext);
1421
- return;
1422
- }
1423
- throw Object.assign(
1424
- new Error(`Failed to activate session: ${describeHostFailure(result)}`),
1425
- { cause: result }
1426
- );
1383
+ const { createPrimarySpace } = await import("./space-hosting.cjs");
1384
+ await createPrimarySpace({
1385
+ handler,
1386
+ context: creationContext,
1387
+ guard: () => this.guardSpaceCreation(handler, creationContext),
1388
+ host: () => this.hostSpace(primarySpaceId),
1389
+ onHosted: () => {
1390
+ this._spaceHosting = "hosted";
1391
+ },
1392
+ activate: () => (0, import_sdk_core5.activateSessionWithHost)(host, session.delegationHeader)
1393
+ });
1427
1394
  }
1428
1395
  /** {@link assertMayCreateSpace}, reporting a refusal to the creation handler before rethrowing it. */
1429
1396
  async guardSpaceCreation(handler, context) {
@@ -1439,10 +1406,12 @@ var NodeUserAuthorization = class {
1439
1406
  if (result.success) {
1440
1407
  this._lastActivationSkippedSpaceIds = result.skipped ?? [];
1441
1408
  this._lastActivationActivatedSpaceIds = result.activated ?? [];
1409
+ this._spaceHosting = this._lastActivationSkippedSpaceIds.includes(primarySpaceId) ? "not-hosted" : this._lastActivationActivatedSpaceIds.includes(primarySpaceId) ? "hosted" : "unknown";
1442
1410
  return;
1443
1411
  }
1444
1412
  this._lastActivationActivatedSpaceIds = [];
1445
1413
  this._lastActivationSkippedSpaceIds = result.status === 404 ? [primarySpaceId] : [];
1414
+ this._spaceHosting = result.status === 404 ? "not-hosted" : "unknown";
1446
1415
  }
1447
1416
  async createBootstrapSession(options) {
1448
1417
  refuseCaveated(options.capabilityRequest.manifests);
@@ -4074,6 +4043,8 @@ var _TinyCloudNode = class _TinyCloudNode {
4074
4043
  * hosting is confirmed against the session that was active at the time.
4075
4044
  */
4076
4045
  this.confirmedHostedSpaceIds = /* @__PURE__ */ new Set();
4046
+ /** The primary space's hosting answer when no authorization layer holds it (a session-only restore). */
4047
+ this._restoredSpaceHosting = "unknown";
4077
4048
  /**
4078
4049
  * Owned space ids known to be hosted for the current session: the sign-in
4079
4050
  * activation listed them as `activated`, or this session hosted them. Unlike
@@ -4412,6 +4383,84 @@ var _TinyCloudNode = class _TinyCloudNode {
4412
4383
  ));
4413
4384
  }
4414
4385
  }
4386
+ /**
4387
+ * Whether the TinyCloud node hosts the primary space (the default) or a named
4388
+ * or full-id space: `"hosted"`, `"not-hosted"`, or `"unknown"` when the node
4389
+ * cannot say.
4390
+ *
4391
+ * Sends one authorized read the session already holds on that space (a kv
4392
+ * list under a granted prefix, else kv metadata, `tinycloud.space/info`, or
4393
+ * a kv get of a key that does not exist), signed for that space. A session
4394
+ * with no such read sends nothing and answers `unknown`. A 401/403 registers
4395
+ * the session delegation with the node once and asks again once. Network
4396
+ * failures, 5xx and a 5 s timeout answer `unknown`; this never throws.
4397
+ *
4398
+ * `hosted` is remembered for the session and `not-hosted` never is. The
4399
+ * answer for the primary space is also what {@link spaceHosting} reports; a
4400
+ * `unknown` answer leaves the previous one in place.
4401
+ */
4402
+ async checkSpaceHosting(space) {
4403
+ try {
4404
+ return await this.probeSpaceHosting(space);
4405
+ } catch {
4406
+ return "unknown";
4407
+ }
4408
+ }
4409
+ /**
4410
+ * The node's last answer for the primary space: recorded at sign-in from the
4411
+ * activation, or by {@link checkSpaceHosting}. `unknown` after a restore until
4412
+ * something asks.
4413
+ */
4414
+ get spaceHosting() {
4415
+ return this.auth ? this.auth.spaceHosting : this._restoredSpaceHosting;
4416
+ }
4417
+ async probeSpaceHosting(space) {
4418
+ const context = this._serviceContext;
4419
+ const session = context?.session;
4420
+ const host = context?.hosts[0];
4421
+ if (!context || !session || !host) {
4422
+ return "unknown";
4423
+ }
4424
+ const spaceId = space === void 0 ? session.spaceId : space.startsWith("tinycloud:") ? space : this.ownedSpaceId(space);
4425
+ const graph = this._serviceGraph;
4426
+ const epoch = this.sessionEpoch;
4427
+ const assertCurrent = () => {
4428
+ graph.assertActive();
4429
+ if (epoch !== this.sessionEpoch || graph !== this._serviceGraph || this._serviceContext !== context) {
4430
+ throw new Error("The session changed during the hosting check.");
4431
+ }
4432
+ };
4433
+ const { askSpaceHosting } = await import("./space-hosting.cjs");
4434
+ assertCurrent();
4435
+ this.pruneExpiredRuntimePermissionGrants();
4436
+ const primary = this.currentTinyCloudSession();
4437
+ const answer = await askSpaceHosting({
4438
+ spaceId,
4439
+ operations: [
4440
+ ...this.runtimePermissionGrants.flatMap((grant) => grant.operations),
4441
+ ...primary ? this.recapOperationsFromSession(primary) : []
4442
+ ],
4443
+ covers: (granted, requested) => this.operationCovers(granted, requested),
4444
+ scope: context,
4445
+ host,
4446
+ fetch: unrecordedFetchOf(context),
4447
+ sign: (entries) => {
4448
+ assertCurrent();
4449
+ return graph.invokeAny({ ...session, spaceId }, entries);
4450
+ },
4451
+ // A session saved but never registered on this node is refused: it registers once.
4452
+ activate: (signal) => {
4453
+ assertCurrent();
4454
+ return (0, import_sdk_core11.activateSessionWithHost)(host, session.delegationHeader, AbortSignal.any([signal, graph.signal]));
4455
+ }
4456
+ });
4457
+ assertCurrent();
4458
+ if (answer !== "unknown" && this.spaceIdsEqual(spaceId, session.spaceId)) {
4459
+ if (this.auth) this.auth.noteSpaceHosting(answer);
4460
+ else this._restoredSpaceHosting = answer;
4461
+ }
4462
+ return answer;
4463
+ }
4415
4464
  get nodeFeatures() {
4416
4465
  return this.auth?.nodeFeatures ?? this._restoredNodeFeatures ?? [];
4417
4466
  }
@@ -4903,6 +4952,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4903
4952
  this._spaceService = void 0;
4904
4953
  this.runtimePermissionGrants = [];
4905
4954
  this._recapOperationsCache = void 0;
4955
+ this._restoredSpaceHosting = "unknown";
4906
4956
  this.confirmedHostedSpaceIds.clear();
4907
4957
  this.hostedOwnedSpaceIds.clear();
4908
4958
  this.cancelOwnedSpaceHosts();
@@ -5096,6 +5146,10 @@ var _TinyCloudNode = class _TinyCloudNode {
5096
5146
  const primarySession = this.currentTinyCloudSession();
5097
5147
  if (primarySession) {
5098
5148
  this.registerPrimarySessionGrant(primarySession);
5149
+ if (this.auth?.spaceHosting === "unknown") {
5150
+ await this.checkSpaceHosting();
5151
+ this.assertSessionEpoch(epoch, "Sign-in");
5152
+ }
5099
5153
  }
5100
5154
  const bootstrapped = await this.bootstrapAccountIfNeeded();
5101
5155
  this.assertSessionEpoch(epoch, "Sign-in");
@@ -6475,6 +6529,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6475
6529
  this.tc = stagedGraph.core;
6476
6530
  this._recapOperationsCache = stagedPrimary.cache;
6477
6531
  this.runtimePermissionGrants = stagedPrimary.grants;
6532
+ this._restoredSpaceHosting = "unknown";
6478
6533
  this.confirmedHostedSpaceIds.clear();
6479
6534
  this.hostedOwnedSpaceIds.clear();
6480
6535
  this.cancelOwnedSpaceHosts();