@tinycloud/node-sdk 3.2.0 → 3.2.1-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.
package/dist/core.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, AccountApplication, AccountApplicationListOptions, AccountApplicationRegisterOptions, AccountDelegation, AccountDelegationListOptions, AccountDelegationRevokeOptions, AccountIndexEnsureResult, AccountIndexRebuildResult, AccountIndexStatus, AccountIndexedReadOptions, AccountService, AccountServiceConfig, AccountSpace, AccountSpaceListOptions, AccountStatus, AuthorityRefusal, AutoApproveSpaceCreationHandler, AutoRejectStrategy, AutoSignStrategy, BatchOptions, BatchResponse, CallbackStrategy, CanonicalAddress, CanonicalParsedNetworkId, CapabilityEntry, CapabilityKeyRegistry, CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, ClientSession, ColumnInfo, ComposeManifestOptions, ComposedManifestRequest, CreateDelegationParams, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DataVaultConfig, DataVaultService, DatabaseHandle, DelegateReceivedShareParams, DelegatedShareAccess, Delegation, DelegationChain, DelegationChainV2, DelegationDirection, DelegationError, DelegationErrorCode, DelegationErrorCodes, DelegationFilters, DelegationManager, DelegationManagerConfig, DelegationRecord, DelegationResult, DidCacheKeyOptions, DidEqualsOptions, DuckDbAction, DuckDbActionType, DuckDbBatchOptions, DuckDbBatchResponse, DuckDbDatabaseHandle, DuckDbExecuteOptions, DuckDbExecuteResponse, DuckDbOptions, DuckDbQueryOptions, DuckDbQueryResponse, DuckDbService, DuckDbServiceConfig, DuckDbStatement, DuckDbValue, EncodedShareData, ExecuteOptions, ExecuteResponse, Extension, FetchFunction, GenerateShareParams, ICapabilityKeyRegistry, IDataVaultService, IDatabaseHandle, IDuckDbDatabaseHandle, IDuckDbService, IENSResolver, IKVService, INotificationHandler, IPrefixedKVService, ISQLService, ISecretsService, ISessionManager, ISessionStorage, ISharingService, ISigner, ISpace, ISpaceCreationHandler, ISpaceScopedDelegations, ISpaceScopedSharing, ISpaceService, IUserAuthorization, IWasmBindings, IdentityParseError, IngestOptions, InvokeFunction, JWK, KVBatchReadResponse, KVCreateSignedReadUrlOptions, KVListPage, KVReadThrough, KVReplicaHandle, KVReplicaSpec, KVReplicaStorage, KVReplicationController, KVReplicationDeps, KVResponse, KVService, KVServiceConfig, KVSignedReadUrlResponse, KeyInfo, KeyProvider, KeyType, LocalGetResult, LocalListResult, LocalReadMeta, LocalReplicaStatus, LocalSyncResult, Manifest, ManifestDefaults, ManifestRegistryRecord, ManifestSecretActions, ManifestValidationError, OpenKeyCallbackStrategy, OpenKeySigningRequestBody, OpenKeySigningResponseBody, OpenKeySigningStrategyOptions, ParsedSecretCatalogKey, PendingWriteRecord, PendingWriteState, PendingWriteStore, PermissionEntry, PermissionNotInManifestError, PersistedSessionData, PinnedKey, PkhDidParts, PrefixedKVService, ProtocolMismatchError, PurgeTarget, QueryOptions, QueryResponse, ReceiveOptions, RegisterBatchSuccess, RegisterPolicyV3Input, RegisterPolicyV3Receipt, ReplicaAuthorityState, ReplicaCoverage, ReplicaDevice, ReplicaGrantInfo, ReplicaState, ReplicaStatusEntry, ReplicationAuthority, ReplicationControl, ReplicationEvent, ReplicationIdentity, ReplicationOptions, ReplicationPurgeReport, ReplicationReason, ReplicationScheduler, ResolvedCapabilities, ResolvedDelegate, ResolvedReplicationOptions, ResolvedSecretPath, ResourceCapability, SECRET_NAME_RE, SQLAction, SQLActionType, SQLService, SQLServiceConfig, SchemaInfo, SecretCatalogEntry, SecretPayload, SecretScopeOptions, SecretsError, SecretsService, ServiceContext, ServiceContextConfig, ServiceSession, SessionExpiredError, ShareAccess, ShareLink, ShareLinkData, ShareSchema, SharingService, SharingServiceConfig, SignCallback, SignInOptions, SignRequest, SignResponse, SilentNotificationHandler, Space, SpaceAbilitiesMap, SpaceConfig, SpaceCreationContext, SpaceErrorCode, SpaceErrorCodes, SpaceInfo, SpaceOwnership, SpaceService, SpaceServiceConfig, SqlStatement, SqlValue, StoredDelegationChain, TableInfo, TelemetryConfig, TelemetryEventHandler, TinyCloud, TinyCloudConfig, TinyCloudDebugEnableOptions, TinyCloudDebugEvent, TinyCloudDebugLevel, TinyCloudDebugLogger, TinyCloudDebugTimer, TinyCloudSession, UnsupportedFeatureError, VAULT_PERMISSION_SERVICE, VaultCrypto, VaultEntry, VaultError, VaultGetOptions, VaultGrantOptions, VaultHeaders, VaultListOptions, VaultNetworkReadResult, VaultPublicSpaceKVActions, VaultPutOptions, VersionCheckError, ViewInfo, WasmVaultFunctions, addressStorageKey, buildSpaceUri, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeSecretScope, checkNodeInfo, clearTinyCloudDebugLogs, composeManifestRequest, createCapabilityKeyRegistry, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, createVaultCrypto, defaultSpaceCreationHandler, didCacheKey, didEquals, disableTinyCloudDebug, enableTinyCloudDebug, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, getTinyCloudDebugLogs, installTinyCloudDebugGlobals, isCapabilitySubset, isEvmAddress, loadManifest, makePkhSpaceId, makePublicSpaceId, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseSecretCatalogKey, parseSpaceUri, pkhDid, principalDid, principalDidEquals, resolveManifest, resolveSecretListPrefix, resolveSecretPath, resourceCapabilitiesToSpaceAbilitiesMap, tinyCloudDebugLogger, validateManifest } from '@tinycloud/sdk-core';
2
2
  import { _ as __authority } from './authority-DDGtccBK.cjs';
3
3
  import { _ as __runtime } from './runtime-B5P5SRpC.cjs';
4
- export { e as BootstrapWarning, C as CompactRuntimeDelegationInput, f as CreateOwnerDelegationParams, D as DelegateToOptions, g as DelegateToResult, h as DelegatedAccess, E as EnsureEncryptionNetworkOptions, F as FileSessionStorage, I as InvalidRestoredSessionError, M as MemorySessionStorage, N as NodeEventEmitterStrategy, j as NodeUserAuthorization, k as NodeUserAuthorizationConfig, O as OwnerDelegationReceipt, P as PortableDelegation, p as RuntimePermissionGrantOptions, S as SecretPermissionHint, q as SecretReadInput, r as SecretReadResult, t as SignStrategy, T as TinyCloudNode, u as TinyCloudNodeConfig, U as UnifiedOwnerRootInput, v as UnifiedOwnerRootReceipt, w as UnsupportedSessionRestoreError, V as ValidatedRuntimeDelegation, W as WasmKeyProvider, y as WasmKeyProviderConfig, z as activateCompactRuntimeDelegation, H as createWasmKeyProvider, J as defaultSignStrategy, K as deserializeDelegation, Q as serializeDelegation } from './WasmKeyProvider-6_YwgSkd.cjs';
4
+ export { e as BootstrapWarning, C as CompactRuntimeDelegationInput, f as CreateOwnerDelegationParams, D as DelegateToOptions, g as DelegateToResult, h as DelegatedAccess, E as EnsureEncryptionNetworkOptions, F as FileSessionStorage, I as InvalidRestoredSessionError, M as MemorySessionStorage, N as NodeEventEmitterStrategy, j as NodeUserAuthorization, k as NodeUserAuthorizationConfig, O as OwnerDelegationReceipt, P as PortableDelegation, p as RuntimePermissionGrantOptions, S as SecretPermissionHint, q as SecretReadInput, r as SecretReadResult, t as SignStrategy, T as TinyCloudNode, u as TinyCloudNodeConfig, U as UnifiedOwnerRootInput, v as UnifiedOwnerRootReceipt, w as UnsupportedSessionRestoreError, V as ValidatedRuntimeDelegation, W as WasmKeyProvider, y as WasmKeyProviderConfig, z as activateCompactRuntimeDelegation, H as createWasmKeyProvider, J as defaultSignStrategy, K as deserializeDelegation, Q as serializeDelegation } from './WasmKeyProvider-DRlOpvpG.cjs';
5
5
  import '@tinycloud/sdk-services';
6
6
  import 'events';
7
7
 
package/dist/core.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { ACCOUNT_REGISTRY_PATH, ACCOUNT_REGISTRY_SPACE, AccountApplication, AccountApplicationListOptions, AccountApplicationRegisterOptions, AccountDelegation, AccountDelegationListOptions, AccountDelegationRevokeOptions, AccountIndexEnsureResult, AccountIndexRebuildResult, AccountIndexStatus, AccountIndexedReadOptions, AccountService, AccountServiceConfig, AccountSpace, AccountSpaceListOptions, AccountStatus, AuthorityRefusal, AutoApproveSpaceCreationHandler, AutoRejectStrategy, AutoSignStrategy, BatchOptions, BatchResponse, CallbackStrategy, CanonicalAddress, CanonicalParsedNetworkId, CapabilityEntry, CapabilityKeyRegistry, CapabilityKeyRegistryErrorCode, CapabilityKeyRegistryErrorCodes, CaveatedDelegationUnsupportedError, ClientSession, ColumnInfo, ComposeManifestOptions, ComposedManifestRequest, CreateDelegationParams, DEFAULT_MANIFEST_SPACE, DEFAULT_MANIFEST_VERSION, DEFAULT_SIGNED_READ_URL_EXPIRY_MS, DataVaultConfig, DataVaultService, DatabaseHandle, DelegateReceivedShareParams, DelegatedShareAccess, Delegation, DelegationChain, DelegationChainV2, DelegationDirection, DelegationError, DelegationErrorCode, DelegationErrorCodes, DelegationFilters, DelegationManager, DelegationManagerConfig, DelegationRecord, DelegationResult, DidCacheKeyOptions, DidEqualsOptions, DuckDbAction, DuckDbActionType, DuckDbBatchOptions, DuckDbBatchResponse, DuckDbDatabaseHandle, DuckDbExecuteOptions, DuckDbExecuteResponse, DuckDbOptions, DuckDbQueryOptions, DuckDbQueryResponse, DuckDbService, DuckDbServiceConfig, DuckDbStatement, DuckDbValue, EncodedShareData, ExecuteOptions, ExecuteResponse, Extension, FetchFunction, GenerateShareParams, ICapabilityKeyRegistry, IDataVaultService, IDatabaseHandle, IDuckDbDatabaseHandle, IDuckDbService, IENSResolver, IKVService, INotificationHandler, IPrefixedKVService, ISQLService, ISecretsService, ISessionManager, ISessionStorage, ISharingService, ISigner, ISpace, ISpaceCreationHandler, ISpaceScopedDelegations, ISpaceScopedSharing, ISpaceService, IUserAuthorization, IWasmBindings, IdentityParseError, IngestOptions, InvokeFunction, JWK, KVBatchReadResponse, KVCreateSignedReadUrlOptions, KVListPage, KVReadThrough, KVReplicaHandle, KVReplicaSpec, KVReplicaStorage, KVReplicationController, KVReplicationDeps, KVResponse, KVService, KVServiceConfig, KVSignedReadUrlResponse, KeyInfo, KeyProvider, KeyType, LocalGetResult, LocalListResult, LocalReadMeta, LocalReplicaStatus, LocalSyncResult, Manifest, ManifestDefaults, ManifestRegistryRecord, ManifestSecretActions, ManifestValidationError, OpenKeyCallbackStrategy, OpenKeySigningRequestBody, OpenKeySigningResponseBody, OpenKeySigningStrategyOptions, ParsedSecretCatalogKey, PendingWriteRecord, PendingWriteState, PendingWriteStore, PermissionEntry, PermissionNotInManifestError, PersistedSessionData, PinnedKey, PkhDidParts, PrefixedKVService, ProtocolMismatchError, PurgeTarget, QueryOptions, QueryResponse, ReceiveOptions, RegisterBatchSuccess, RegisterPolicyV3Input, RegisterPolicyV3Receipt, ReplicaAuthorityState, ReplicaCoverage, ReplicaDevice, ReplicaGrantInfo, ReplicaState, ReplicaStatusEntry, ReplicationAuthority, ReplicationControl, ReplicationEvent, ReplicationIdentity, ReplicationOptions, ReplicationPurgeReport, ReplicationReason, ReplicationScheduler, ResolvedCapabilities, ResolvedDelegate, ResolvedReplicationOptions, ResolvedSecretPath, ResourceCapability, SECRET_NAME_RE, SQLAction, SQLActionType, SQLService, SQLServiceConfig, SchemaInfo, SecretCatalogEntry, SecretPayload, SecretScopeOptions, SecretsError, SecretsService, ServiceContext, ServiceContextConfig, ServiceSession, SessionExpiredError, ShareAccess, ShareLink, ShareLinkData, ShareSchema, SharingService, SharingServiceConfig, SignCallback, SignInOptions, SignRequest, SignResponse, SilentNotificationHandler, Space, SpaceAbilitiesMap, SpaceConfig, SpaceCreationContext, SpaceErrorCode, SpaceErrorCodes, SpaceInfo, SpaceOwnership, SpaceService, SpaceServiceConfig, SqlStatement, SqlValue, StoredDelegationChain, TableInfo, TelemetryConfig, TelemetryEventHandler, TinyCloud, TinyCloudConfig, TinyCloudDebugEnableOptions, TinyCloudDebugEvent, TinyCloudDebugLevel, TinyCloudDebugLogger, TinyCloudDebugTimer, TinyCloudSession, UnsupportedFeatureError, VAULT_PERMISSION_SERVICE, VaultCrypto, VaultEntry, VaultError, VaultGetOptions, VaultGrantOptions, VaultHeaders, VaultListOptions, VaultNetworkReadResult, VaultPublicSpaceKVActions, VaultPutOptions, VersionCheckError, ViewInfo, WasmVaultFunctions, addressStorageKey, buildSpaceUri, canonicalizeAddress, canonicalizeDid, canonicalizeDidUrl, canonicalizeNetworkId, canonicalizeSecretScope, checkNodeInfo, clearTinyCloudDebugLogs, composeManifestRequest, createCapabilityKeyRegistry, createOpenKeyCallbackSigningStrategy, createSharingService, createSpaceService, createVaultCrypto, defaultSpaceCreationHandler, didCacheKey, didEquals, disableTinyCloudDebug, enableTinyCloudDebug, expandActionShortNames, expandPermissionEntries, expandPermissionEntry, getTinyCloudDebugLogs, installTinyCloudDebugGlobals, isCapabilitySubset, isEvmAddress, loadManifest, makePkhSpaceId, makePublicSpaceId, parseCanonicalNetworkId, parseExpiry, parsePkhDid, parseSecretCatalogKey, parseSpaceUri, pkhDid, principalDid, principalDidEquals, resolveManifest, resolveSecretListPrefix, resolveSecretPath, resourceCapabilitiesToSpaceAbilitiesMap, tinyCloudDebugLogger, validateManifest } from '@tinycloud/sdk-core';
2
2
  import { _ as __authority } from './authority-DDGtccBK.js';
3
3
  import { _ as __runtime } from './runtime-B5P5SRpC.js';
4
- export { e as BootstrapWarning, C as CompactRuntimeDelegationInput, f as CreateOwnerDelegationParams, D as DelegateToOptions, g as DelegateToResult, h as DelegatedAccess, E as EnsureEncryptionNetworkOptions, F as FileSessionStorage, I as InvalidRestoredSessionError, M as MemorySessionStorage, N as NodeEventEmitterStrategy, j as NodeUserAuthorization, k as NodeUserAuthorizationConfig, O as OwnerDelegationReceipt, P as PortableDelegation, p as RuntimePermissionGrantOptions, S as SecretPermissionHint, q as SecretReadInput, r as SecretReadResult, t as SignStrategy, T as TinyCloudNode, u as TinyCloudNodeConfig, U as UnifiedOwnerRootInput, v as UnifiedOwnerRootReceipt, w as UnsupportedSessionRestoreError, V as ValidatedRuntimeDelegation, W as WasmKeyProvider, y as WasmKeyProviderConfig, z as activateCompactRuntimeDelegation, H as createWasmKeyProvider, J as defaultSignStrategy, K as deserializeDelegation, Q as serializeDelegation } from './WasmKeyProvider-6_YwgSkd.js';
4
+ export { e as BootstrapWarning, C as CompactRuntimeDelegationInput, f as CreateOwnerDelegationParams, D as DelegateToOptions, g as DelegateToResult, h as DelegatedAccess, E as EnsureEncryptionNetworkOptions, F as FileSessionStorage, I as InvalidRestoredSessionError, M as MemorySessionStorage, N as NodeEventEmitterStrategy, j as NodeUserAuthorization, k as NodeUserAuthorizationConfig, O as OwnerDelegationReceipt, P as PortableDelegation, p as RuntimePermissionGrantOptions, S as SecretPermissionHint, q as SecretReadInput, r as SecretReadResult, t as SignStrategy, T as TinyCloudNode, u as TinyCloudNodeConfig, U as UnifiedOwnerRootInput, v as UnifiedOwnerRootReceipt, w as UnsupportedSessionRestoreError, V as ValidatedRuntimeDelegation, W as WasmKeyProvider, y as WasmKeyProviderConfig, z as activateCompactRuntimeDelegation, H as createWasmKeyProvider, J as defaultSignStrategy, K as deserializeDelegation, Q as serializeDelegation } from './WasmKeyProvider-DRlOpvpG.js';
5
5
  import '@tinycloud/sdk-services';
6
6
  import 'events';
7
7
 
package/dist/core.js CHANGED
@@ -390,6 +390,11 @@ var NodeUserAuthorization = class {
390
390
  this._nodeFeatures = [];
391
391
  this._nodeFeaturesKnown = false;
392
392
  this._lastActivationSkippedSpaceIds = [];
393
+ /**
394
+ * Spaces the sign-in activation reported hosted. Only the first POST of a
395
+ * delegation lists them: the node answers a repeat with empty lists.
396
+ */
397
+ this._lastActivationActivatedSpaceIds = [];
393
398
  this.wasm = config.wasmBindings;
394
399
  this.signer = config.signer;
395
400
  this.signStrategy = config.signStrategy ?? defaultSignStrategy;
@@ -519,6 +524,7 @@ var NodeUserAuthorization = class {
519
524
  this._nodeInfoIdentity = void 0;
520
525
  this.tinycloudHosts = hosts && hosts.length > 0 ? [...hosts] : void 0;
521
526
  this._lastActivationSkippedSpaceIds = [];
527
+ this._lastActivationActivatedSpaceIds = [];
522
528
  if (!session) {
523
529
  this._session = void 0;
524
530
  this._tinyCloudSession = void 0;
@@ -613,6 +619,9 @@ var NodeUserAuthorization = class {
613
619
  get lastActivationSkippedSpaceIds() {
614
620
  return [...this._lastActivationSkippedSpaceIds];
615
621
  }
622
+ get lastActivationActivatedSpaceIds() {
623
+ return [...this._lastActivationActivatedSpaceIds];
624
+ }
616
625
  /**
617
626
  * Compute the `abilities` map the WASM `prepareSession` call should
618
627
  * see at sign-in time.
@@ -1108,8 +1117,10 @@ var NodeUserAuthorization = class {
1108
1117
  recordActivationSkippedSpaces(result, primarySpaceId) {
1109
1118
  if (result.success) {
1110
1119
  this._lastActivationSkippedSpaceIds = result.skipped ?? [];
1120
+ this._lastActivationActivatedSpaceIds = result.activated ?? [];
1111
1121
  return;
1112
1122
  }
1123
+ this._lastActivationActivatedSpaceIds = [];
1113
1124
  this._lastActivationSkippedSpaceIds = result.status === 404 ? [primarySpaceId] : [];
1114
1125
  }
1115
1126
  async createBootstrapSession(options) {
@@ -3456,10 +3467,26 @@ var _TinyCloudNode = class _TinyCloudNode {
3456
3467
  * hosting is confirmed against the session that was active at the time.
3457
3468
  */
3458
3469
  this.confirmedHostedSpaceIds = /* @__PURE__ */ new Set();
3470
+ /**
3471
+ * Owned space ids known to be hosted for the current session: the sign-in
3472
+ * activation listed them as `activated`, or this session hosted them. Unlike
3473
+ * {@link confirmedHostedSpaceIds}, nothing here is inferred from a space being
3474
+ * merely absent from `skipped`. Read by {@link ensureOwnedSpaceHosted}.
3475
+ */
3476
+ this.hostedOwnedSpaceIds = /* @__PURE__ */ new Set();
3477
+ /**
3478
+ * In-flight host delegations, so concurrent callers share one approval per
3479
+ * space. `waiters` counts callers still waiting; the request is cancelled
3480
+ * once every caller with a signal has aborted and none waits without one.
3481
+ */
3482
+ this.ownedSpaceHosts = /* @__PURE__ */ new Map();
3459
3483
  /** Serializes account-registry writes for the active node instance. */
3460
3484
  this.accountRegistryTail = Promise.resolve();
3461
- /** Bounds best-effort registry work on the sign-in critical path. */
3462
- this.accountRegistryDeadlineMs = 4e3;
3485
+ /**
3486
+ * Hang guard for the background registry sync. It is off the sign-in path,
3487
+ * so it must outlast a slow but healthy sync (9–18 s on prod 1.20.2, TC-998).
3488
+ */
3489
+ this.accountRegistryDeadlineMs = 3e4;
3463
3490
  /**
3464
3491
  * True when the last signIn() detected an interactive signer and skipped
3465
3492
  * client-side bootstrap. Apps can read this to know whether bootstrap was
@@ -4054,6 +4081,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4054
4081
  "Cannot signIn() in session-only mode. Provide a privateKey in config to create your own space."
4055
4082
  );
4056
4083
  }
4084
+ this.pendingAccountRegistrySync?.controller.abort();
4057
4085
  await this.accountRegistryTail;
4058
4086
  await this.wasmBindings.ensureInitialized?.();
4059
4087
  const address = canonicalizeAddress2(await this.signer.getAddress());
@@ -4074,8 +4102,13 @@ var _TinyCloudNode = class _TinyCloudNode {
4074
4102
  this._serviceContext = void 0;
4075
4103
  this.runtimePermissionGrants = [];
4076
4104
  this.confirmedHostedSpaceIds.clear();
4105
+ this.hostedOwnedSpaceIds.clear();
4077
4106
  await this.tc.signIn(options);
4107
+ this.cancelOwnedSpaceHosts();
4078
4108
  this.syncResolvedHostFromAuth();
4109
+ for (const spaceId of this.auth.lastActivationActivatedSpaceIds ?? []) {
4110
+ this.hostedOwnedSpaceIds.add(spaceId);
4111
+ }
4079
4112
  this.replicationDelegateSession = void 0;
4080
4113
  this.replicationScopeError = void 0;
4081
4114
  if (this.replicationRuntime) await (await this.replicationRuntime).unbind();
@@ -4100,10 +4133,20 @@ var _TinyCloudNode = class _TinyCloudNode {
4100
4133
  }
4101
4134
  if (!bootstrapped) {
4102
4135
  this.scheduleAccountRegistrySync();
4103
- await this.accountRegistryTail;
4104
4136
  }
4105
4137
  this.notificationHandler.success("Successfully signed in");
4106
4138
  }
4139
+ /**
4140
+ * Wait for the account-registry sync that {@link signIn} starts in the
4141
+ * background (account index, this app's manifest record, space list).
4142
+ * Resolves once it has finished, failed or hit its hang guard; it never
4143
+ * rejects, and failures are logged as warnings. `signIn()` does not wait for
4144
+ * it, so call this only when you need the registry written, or before a first
4145
+ * write against a SQLite-backed node older than 1.20.2 (TC-899).
4146
+ */
4147
+ async waitForAccountRegistrySync() {
4148
+ await this.accountRegistryTail;
4149
+ }
4107
4150
  ownedSpaceId(name) {
4108
4151
  if (!this._address) {
4109
4152
  throw new Error("Cannot resolve owned space before sign-in");
@@ -4243,6 +4286,13 @@ var _TinyCloudNode = class _TinyCloudNode {
4243
4286
  * A marker written before a canonical space was added triggers one bounded repair.
4244
4287
  */
4245
4288
  isAcceptedBootstrapCompletionMarker(value, canonicalStepIds) {
4289
+ if (typeof value === "string") {
4290
+ try {
4291
+ value = JSON.parse(value);
4292
+ } catch {
4293
+ return false;
4294
+ }
4295
+ }
4246
4296
  if (typeof value !== "object" || value === null) return false;
4247
4297
  const entries = Object.entries(value);
4248
4298
  const markerValue = (name) => entries.find(([key]) => key === name)?.[1];
@@ -4702,17 +4752,17 @@ var _TinyCloudNode = class _TinyCloudNode {
4702
4752
  clearTimeout(deadline);
4703
4753
  }
4704
4754
  });
4705
- this.pendingAccountRegistrySync = { session, promise };
4706
- const clearPendingSync = () => {
4755
+ this.pendingAccountRegistrySync = { session, promise, controller };
4756
+ const settle = () => {
4757
+ clearTimeout(deadline);
4707
4758
  if (this.pendingAccountRegistrySync?.promise === promise) {
4708
4759
  this.pendingAccountRegistrySync = void 0;
4709
4760
  }
4710
4761
  };
4711
- void promise.then(clearPendingSync, clearPendingSync);
4712
- void promise.then(
4713
- () => clearTimeout(deadline),
4714
- () => clearTimeout(deadline)
4715
- );
4762
+ void promise.then(settle, (error) => {
4763
+ settle();
4764
+ console.warn("TinyCloud account registry sync failed", error);
4765
+ });
4716
4766
  }
4717
4767
  enqueueAccountRegistryOperation(session, task) {
4718
4768
  const operation = this.accountRegistryTail.then(async () => {
@@ -4750,10 +4800,15 @@ var _TinyCloudNode = class _TinyCloudNode {
4750
4800
  }
4751
4801
  async withAccountRegistryRetry(task, signal) {
4752
4802
  const delays = [250, 1e3, 3e3];
4803
+ const stopped = (detail) => {
4804
+ if (signal.reason instanceof DOMException && signal.reason.name === "TimeoutError") {
4805
+ console.warn("TinyCloud account registry sync stopped: deadline exceeded", detail);
4806
+ }
4807
+ };
4753
4808
  let lastError;
4754
4809
  for (let attempt = 0; attempt < delays.length; attempt += 1) {
4755
4810
  if (signal.aborted) {
4756
- console.warn("TinyCloud account registry sync stopped: deadline exceeded", signal.reason);
4811
+ stopped(signal.reason);
4757
4812
  return;
4758
4813
  }
4759
4814
  try {
@@ -4761,7 +4816,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4761
4816
  return;
4762
4817
  } catch (error) {
4763
4818
  if (signal.aborted) {
4764
- console.warn("TinyCloud account registry sync stopped: deadline exceeded", error);
4819
+ stopped(error);
4765
4820
  return;
4766
4821
  }
4767
4822
  const verdict = authorizationVerdictOf(error);
@@ -4854,7 +4909,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4854
4909
  if (!session) {
4855
4910
  throw new Error("Owned space hosting requires an active session");
4856
4911
  }
4857
- if (this.confirmedHostedSpaceIds.has(spaceId)) {
4912
+ if (this.confirmedHostedSpaceIds.has(spaceId) || this.hostedOwnedSpaceIds.has(spaceId)) {
4858
4913
  return;
4859
4914
  }
4860
4915
  const host = this.hosts[0] ?? this.config.host;
@@ -4872,8 +4927,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4872
4927
  { cause: activation }
4873
4928
  );
4874
4929
  }
4875
- const authorization = this.auth;
4876
- const created = signal ? await authorization.hostOwnedSpaceResult(spaceId, void 0, signal) : await authorization.hostOwnedSpaceResult(spaceId);
4930
+ const created = await this.hostOwnedSpaceOnce(spaceId, signal);
4877
4931
  if (!created.success) {
4878
4932
  throw Object.assign(
4879
4933
  new Error(`Failed to create owned space ${spaceId}: ${describeHostFailure2(created)}`),
@@ -4890,7 +4944,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4890
4944
  { cause: retry }
4891
4945
  );
4892
4946
  }
4893
- this.confirmedHostedSpaceIds.add(spaceId);
4947
+ if (this.auth.tinyCloudSession === session) {
4948
+ this.confirmedHostedSpaceIds.add(spaceId);
4949
+ this.hostedOwnedSpaceIds.add(spaceId);
4950
+ }
4894
4951
  }
4895
4952
  /**
4896
4953
  * Host one of this user's owned spaces by name (e.g. `"applications"`).
@@ -4922,7 +4979,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4922
4979
  if (!host) {
4923
4980
  throw new Error("Owned space hosting requires a TinyCloud host");
4924
4981
  }
4925
- const hosted = await this.auth.hostOwnedSpaceResult(spaceId);
4982
+ const hosted = await this.hostOwnedSpaceOnce(spaceId);
4926
4983
  if (!hosted.success) {
4927
4984
  throw Object.assign(
4928
4985
  new Error(`Failed to host owned space ${spaceId}: ${describeHostFailure2(hosted)}`),
@@ -4941,6 +4998,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4941
4998
  { cause: activation }
4942
4999
  );
4943
5000
  }
5001
+ if (this.auth?.tinyCloudSession === session) this.hostedOwnedSpaceIds.add(spaceId);
4944
5002
  try {
4945
5003
  await this.enqueueAccountRegistryOperation(session, async () => {
4946
5004
  await this.account.spaces.register({
@@ -4968,18 +5026,19 @@ var _TinyCloudNode = class _TinyCloudNode {
4968
5026
  * registered on the node.
4969
5027
  *
4970
5028
  * Calling this resolves `name` to the owner's owned-space URI
4971
- * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It first consults the
4972
- * account-space spaces registry (`account/spaces/{space_id}`, the canonical
4973
- * KV source of truth, fronted by a best-effort SQLite index): if the space is
4974
- * already registered/hosted it returns the URI WITHOUT submitting a host
4975
- * delegation, avoiding a redundant host-SIWE signature prompt for owners who
4976
- * already use the space. Only when the space is absent — or the registry
4977
- * check fails for any reason (e.g. a cold SQLite index reporting
4978
- * `no such table: spaces`) — does it fall through to {@link hostOwnedSpace}.
4979
- *
4980
- * The registry check is purely an optimization: any failure falls back to
4981
- * hosting, and the host SIWE is idempotent server-side, so re-hosting an
4982
- * existing space remains a safe no-op. Must be called after {@link signIn}.
5029
+ * (`tinycloud:pkh:eip155:<chain>:<addr>:<name>`). It returns WITHOUT
5030
+ * submitting a host delegation when the space is known to be hosted: the
5031
+ * sign-in activation listed it as `activated` (any space the session's recap
5032
+ * covers), this session already hosted it, or the account-space spaces
5033
+ * registry (`account/spaces/{space_id}`, the canonical KV source of truth,
5034
+ * fronted by a best-effort SQLite index) has its record. Otherwise it falls
5035
+ * through to {@link hostOwnedSpace}, which asks for a signature; a host
5036
+ * already in flight for the same space (e.g. from the background registry
5037
+ * sync) is shared rather than asked for twice.
5038
+ *
5039
+ * Hosting is still needed when the space really isn't hosted, or when the
5040
+ * session's recap doesn't cover it and the registry has no record; the host
5041
+ * SIWE is idempotent server-side. Must be called after {@link signIn}.
4983
5042
  *
4984
5043
  * @param name - The owned space name (e.g. `"secrets"`).
4985
5044
  * @returns The hosted owned-space URI.
@@ -4988,13 +5047,57 @@ var _TinyCloudNode = class _TinyCloudNode {
4988
5047
  if (!this.auth || !this.auth.tinyCloudSession) {
4989
5048
  throw new Error("Not signed in. Call signIn() first.");
4990
5049
  }
4991
- await this.accountRegistryTail;
4992
5050
  const spaceId = this.ownedSpaceId(name);
4993
- if (await this.isOwnedSpaceRegistered(spaceId)) {
5051
+ if (this.hostedOwnedSpaceIds.has(spaceId) || await this.isOwnedSpaceRegistered(spaceId)) {
4994
5052
  return spaceId;
4995
5053
  }
4996
5054
  return this.hostOwnedSpace(name);
4997
5055
  }
5056
+ /**
5057
+ * Submit the host delegation for an owned space, sharing one in-flight
5058
+ * request (and so one approval) between concurrent callers in this session,
5059
+ * such as the background registry sync and {@link ensureOwnedSpaceHosted}.
5060
+ * Each caller's `signal` releases only its own wait; the request itself is
5061
+ * cancelled once no caller is left waiting, or when the session is replaced
5062
+ * ({@link cancelOwnedSpaceHosts}), so a late approval is discarded.
5063
+ */
5064
+ hostOwnedSpaceOnce(spaceId, signal) {
5065
+ if (signal?.aborted) return Promise.reject(signal.reason);
5066
+ let entry = this.ownedSpaceHosts.get(spaceId);
5067
+ if (!entry) {
5068
+ const controller = new AbortController();
5069
+ const promise = this.auth.hostOwnedSpaceResult(spaceId, void 0, controller.signal);
5070
+ const created = { promise, controller, waiters: 0 };
5071
+ this.ownedSpaceHosts.set(spaceId, created);
5072
+ const forget = () => {
5073
+ if (this.ownedSpaceHosts.get(spaceId) === created) this.ownedSpaceHosts.delete(spaceId);
5074
+ };
5075
+ promise.then(forget, forget);
5076
+ entry = created;
5077
+ }
5078
+ if (!signal) {
5079
+ entry.waiters = Number.POSITIVE_INFINITY;
5080
+ return entry.promise;
5081
+ }
5082
+ entry.waiters += 1;
5083
+ const shared = entry;
5084
+ return new Promise((resolve, reject2) => {
5085
+ const onAbort = () => {
5086
+ reject2(signal.reason);
5087
+ shared.waiters -= 1;
5088
+ if (shared.waiters !== 0) return;
5089
+ shared.controller.abort(signal.reason);
5090
+ if (this.ownedSpaceHosts.get(spaceId) === shared) this.ownedSpaceHosts.delete(spaceId);
5091
+ };
5092
+ signal.addEventListener("abort", onAbort, { once: true });
5093
+ shared.promise.then(resolve, reject2).finally(() => signal.removeEventListener("abort", onAbort));
5094
+ });
5095
+ }
5096
+ /** Session replacement: cancel the replaced session's in-flight hosts and forget them. */
5097
+ cancelOwnedSpaceHosts() {
5098
+ for (const { controller } of this.ownedSpaceHosts.values()) controller.abort();
5099
+ this.ownedSpaceHosts.clear();
5100
+ }
4998
5101
  /** Resolve and authenticate the owner encoded by an owned-space URI. */
4999
5102
  credentialSpaceOwnerDid(spaceId) {
5000
5103
  const ownerDid = this.ownerDidFromSpaceId(spaceId);
@@ -5237,6 +5340,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5237
5340
  sessionExpiry: stagedSessionExpiry,
5238
5341
  recap: stagedRecap
5239
5342
  });
5343
+ this.pendingAccountRegistrySync?.controller.abort();
5344
+ await this.accountRegistryTail;
5240
5345
  const oldGraph = this._serviceGraph;
5241
5346
  const oldCore = this.tc;
5242
5347
  this.sessionManager = stagedManager;
@@ -5270,6 +5375,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5270
5375
  this._recapOperationsCache = stagedPrimary.cache;
5271
5376
  this.runtimePermissionGrants = stagedPrimary.grants;
5272
5377
  this.confirmedHostedSpaceIds.clear();
5378
+ this.hostedOwnedSpaceIds.clear();
5379
+ this.cancelOwnedSpaceHosts();
5273
5380
  this._restoredNodeFeatures = restoredNodeFeatures;
5274
5381
  if (this.auth) {
5275
5382
  this.auth.installRestoredSession(stagedManager, stagedTcSession, [stagedHost], restoredNodeFeatures);