@tinycloud/node-sdk 2.4.1-beta.0 → 2.5.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.
@@ -1,4 +1,4 @@
1
- import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
1
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
2
2
  import { EventEmitter } from 'events';
3
3
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
@@ -430,7 +430,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
430
  * Create a specific owned space on the server via host delegation.
431
431
  * Used by manifest registry setup for the account space.
432
432
  */
433
- hostOwnedSpace(spaceId: string): Promise<boolean>;
433
+ hostOwnedSpace(spaceId: string, purpose?: SignRequest["purpose"]): Promise<boolean>;
434
434
  /**
435
435
  * Ensure the user's space exists on the TinyCloud server.
436
436
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -469,7 +469,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
469
469
  /**
470
470
  * Sign a message with the connected signer.
471
471
  */
472
- signMessage(message: string): Promise<string>;
472
+ signMessage(message: string, purpose?: SignRequest["purpose"]): Promise<string>;
473
473
  /**
474
474
  * Prepare a session for external signing.
475
475
  *
@@ -917,6 +917,27 @@ declare class TinyCloudNode {
917
917
  * {@link currentTinyCloudSession} as a fallback for `auth.tinyCloudSession`.
918
918
  */
919
919
  private _restoredTcSession?;
920
+ /**
921
+ * True when the last signIn() detected an interactive signer and skipped
922
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
923
+ * deferred to the server (OpenKey) or requires a separate user action.
924
+ */
925
+ private _bootstrapSkipped;
926
+ /**
927
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
928
+ * true when bootstrap did not complete (interactive signer, auto-sign
929
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
930
+ * can surface a "finish account setup" call-to-action.
931
+ */
932
+ private _bootstrapStatus;
933
+ /** Whether the last signIn() skipped client-side bootstrap because the
934
+ * signer is interactive (browser wallet / EIP-1193 provider). */
935
+ get bootstrapSkipped(): boolean;
936
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
937
+ get bootstrapStatus(): {
938
+ skipped: boolean;
939
+ reason?: string;
940
+ };
920
941
  private get nodeFeatures();
921
942
  /** SIWE domain — uses config override or defaults to app.tinycloud.xyz */
922
943
  private get siweDomain();
@@ -1,4 +1,4 @@
1
- import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
1
+ import { ISessionStorage, PersistedSessionData, AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, IUserAuthorization, ISigner, ISpaceCreationHandler, IWasmBindings, SiweConfig, Manifest, ComposedManifestRequest, ClientSession, TinyCloudSession, Extension, SignRequest, SignInOptions, Delegation, DelegatedResource, PermissionEntry, TelemetryConfig, IKVService, ISQLService, IDuckDbService, IHooksService, INotificationHandler, IENSResolver, AccountService, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, CreateDelegationParams, DelegationResult, ResolvedDelegate, KeyProvider, ISessionManager, JWK } from '@tinycloud/sdk-core';
2
2
  import { EventEmitter } from 'events';
3
3
  import { InvokeFunction, InvokeAnyFunction } from '@tinycloud/sdk-services';
4
4
 
@@ -430,7 +430,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
430
430
  * Create a specific owned space on the server via host delegation.
431
431
  * Used by manifest registry setup for the account space.
432
432
  */
433
- hostOwnedSpace(spaceId: string): Promise<boolean>;
433
+ hostOwnedSpace(spaceId: string, purpose?: SignRequest["purpose"]): Promise<boolean>;
434
434
  /**
435
435
  * Ensure the user's space exists on the TinyCloud server.
436
436
  * Creates the space if it doesn't exist and autoCreateSpace is enabled.
@@ -469,7 +469,7 @@ declare class NodeUserAuthorization implements IUserAuthorization {
469
469
  /**
470
470
  * Sign a message with the connected signer.
471
471
  */
472
- signMessage(message: string): Promise<string>;
472
+ signMessage(message: string, purpose?: SignRequest["purpose"]): Promise<string>;
473
473
  /**
474
474
  * Prepare a session for external signing.
475
475
  *
@@ -917,6 +917,27 @@ declare class TinyCloudNode {
917
917
  * {@link currentTinyCloudSession} as a fallback for `auth.tinyCloudSession`.
918
918
  */
919
919
  private _restoredTcSession?;
920
+ /**
921
+ * True when the last signIn() detected an interactive signer and skipped
922
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
923
+ * deferred to the server (OpenKey) or requires a separate user action.
924
+ */
925
+ private _bootstrapSkipped;
926
+ /**
927
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
928
+ * true when bootstrap did not complete (interactive signer, auto-sign
929
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
930
+ * can surface a "finish account setup" call-to-action.
931
+ */
932
+ private _bootstrapStatus;
933
+ /** Whether the last signIn() skipped client-side bootstrap because the
934
+ * signer is interactive (browser wallet / EIP-1193 provider). */
935
+ get bootstrapSkipped(): boolean;
936
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
937
+ get bootstrapStatus(): {
938
+ skipped: boolean;
939
+ reason?: string;
940
+ };
920
941
  private get nodeFeatures();
921
942
  /** SIWE domain — uses config override or defaults to app.tinycloud.xyz */
922
943
  private get siweDomain();
package/dist/core.cjs CHANGED
@@ -702,7 +702,7 @@ var NodeUserAuthorization = class {
702
702
  * Create the space on the TinyCloud server (host delegation).
703
703
  * This registers the user as the owner of the space.
704
704
  */
705
- async hostSpace(targetSpaceId) {
705
+ async hostSpace(targetSpaceId, purpose) {
706
706
  if (!this._tinyCloudSession || !this._address || !this._chainId) {
707
707
  throw new Error("Must be signed in to host space");
708
708
  }
@@ -718,7 +718,7 @@ var NodeUserAuthorization = class {
718
718
  spaceId,
719
719
  peerId
720
720
  });
721
- const signature = await this.signMessage(siwe);
721
+ const signature = await this.signMessage(siwe, purpose);
722
722
  const headers = this.wasm.siweToDelegationHeaders({ siwe, signature });
723
723
  const result = await (0, import_sdk_core3.submitHostDelegation)(host, headers);
724
724
  return result.success;
@@ -734,8 +734,8 @@ var NodeUserAuthorization = class {
734
734
  * Create a specific owned space on the server via host delegation.
735
735
  * Used by manifest registry setup for the account space.
736
736
  */
737
- async hostOwnedSpace(spaceId) {
738
- return this.hostSpace(spaceId);
737
+ async hostOwnedSpace(spaceId, purpose) {
738
+ return this.hostSpace(spaceId, purpose);
739
739
  }
740
740
  /**
741
741
  * Ensure the user's space exists on the TinyCloud server.
@@ -885,7 +885,8 @@ var NodeUserAuthorization = class {
885
885
  address,
886
886
  chainId,
887
887
  message: prepared.siwe,
888
- type: "siwe"
888
+ type: "siwe",
889
+ purpose: "bootstrap-session"
889
890
  });
890
891
  const session = this.wasm.completeSessionSetup({
891
892
  ...prepared,
@@ -949,7 +950,8 @@ var NodeUserAuthorization = class {
949
950
  address,
950
951
  chainId,
951
952
  message: prepared.siwe,
952
- type: "siwe"
953
+ type: "siwe",
954
+ purpose: "sign-in"
953
955
  });
954
956
  const session = this.wasm.completeSessionSetup({
955
957
  ...prepared,
@@ -1037,7 +1039,7 @@ var NodeUserAuthorization = class {
1037
1039
  /**
1038
1040
  * Sign a message with the connected signer.
1039
1041
  */
1040
- async signMessage(message) {
1042
+ async signMessage(message, purpose) {
1041
1043
  if (!this._address) {
1042
1044
  this._address = (0, import_sdk_core3.canonicalizeAddress)(await this.signer.getAddress());
1043
1045
  }
@@ -1048,7 +1050,8 @@ var NodeUserAuthorization = class {
1048
1050
  address: this._address,
1049
1051
  chainId: this._chainId,
1050
1052
  message,
1051
- type: "message"
1053
+ type: "message",
1054
+ ...purpose ? { purpose } : {}
1052
1055
  });
1053
1056
  }
1054
1057
  /**
@@ -1679,6 +1682,15 @@ var DEFAULT_SESSION_EXPIRATION_MS = import_sdk_core8.EXPIRY.SESSION_MS;
1679
1682
  function isOpenKeyAutoSignStrategy(strategy) {
1680
1683
  return strategy?.openKeyAutoSign === true;
1681
1684
  }
1685
+ function isInteractiveSigner(config) {
1686
+ if (config.privateKey) {
1687
+ return false;
1688
+ }
1689
+ if (isOpenKeyAutoSignStrategy(config.signStrategy)) {
1690
+ return false;
1691
+ }
1692
+ return config.signer !== void 0;
1693
+ }
1682
1694
  function didPrincipalMatches2(actual, expected) {
1683
1695
  try {
1684
1696
  return (0, import_sdk_core8.principalDidEquals)(actual, expected);
@@ -1815,6 +1827,21 @@ var _TinyCloudNode = class _TinyCloudNode {
1815
1827
  this._baseSecrets = /* @__PURE__ */ new Map();
1816
1828
  this._secrets = /* @__PURE__ */ new Map();
1817
1829
  this.runtimePermissionGrants = [];
1830
+ /**
1831
+ * True when the last signIn() detected an interactive signer and skipped
1832
+ * client-side bootstrap. Apps can read this to know whether bootstrap was
1833
+ * deferred to the server (OpenKey) or requires a separate user action.
1834
+ */
1835
+ this._bootstrapSkipped = false;
1836
+ /**
1837
+ * Outcome of the last signIn()'s account-bootstrap attempt. `skipped` is
1838
+ * true when bootstrap did not complete (interactive signer, auto-sign
1839
+ * denied, or a bootstrap step failed); `reason` carries the cause so apps
1840
+ * can surface a "finish account setup" call-to-action.
1841
+ */
1842
+ this._bootstrapStatus = {
1843
+ skipped: false
1844
+ };
1818
1845
  this.invokeWithRuntimePermissions = (session, service, path, action, facts) => {
1819
1846
  return this.wasmBindings.invoke(
1820
1847
  this.selectInvocationSession(session, service, path, action),
@@ -1907,6 +1934,15 @@ var _TinyCloudNode = class _TinyCloudNode {
1907
1934
  static registerNodeDefaults(defaults) {
1908
1935
  _TinyCloudNode.nodeDefaults = defaults;
1909
1936
  }
1937
+ /** Whether the last signIn() skipped client-side bootstrap because the
1938
+ * signer is interactive (browser wallet / EIP-1193 provider). */
1939
+ get bootstrapSkipped() {
1940
+ return this._bootstrapSkipped;
1941
+ }
1942
+ /** Outcome of the last signIn()'s account-bootstrap attempt. */
1943
+ get bootstrapStatus() {
1944
+ return this._bootstrapStatus;
1945
+ }
1910
1946
  get nodeFeatures() {
1911
1947
  return this.auth?.nodeFeatures ?? [];
1912
1948
  }
@@ -2130,17 +2166,38 @@ var _TinyCloudNode = class _TinyCloudNode {
2130
2166
  return this.wasmBindings.makeSpaceId(this._address, this._chainId, name);
2131
2167
  }
2132
2168
  async bootstrapAccountIfNeeded() {
2169
+ this._bootstrapSkipped = false;
2170
+ this._bootstrapStatus = { skipped: false };
2133
2171
  if (this.config.autoBootstrapAccount === false) {
2134
2172
  return false;
2135
2173
  }
2136
2174
  if (!this.auth || !this._address) {
2137
2175
  return false;
2138
2176
  }
2177
+ if (isInteractiveSigner(this.config)) {
2178
+ console.debug(
2179
+ "[TinyCloudNode] bootstrap skipped: interactive signer detected. Server-side bootstrap (OpenKey) is expected to have provisioned the account."
2180
+ );
2181
+ this._bootstrapSkipped = true;
2182
+ this._bootstrapStatus = { skipped: true, reason: "interactive-signer" };
2183
+ return false;
2184
+ }
2139
2185
  const steps = (0, import_sdk_core8.bootstrapSteps)(this._address, this._chainId);
2140
2186
  if (!await this.isFreshBootstrapAccount(steps)) {
2141
2187
  return false;
2142
2188
  }
2143
- await this.runAccountBootstrap(steps);
2189
+ try {
2190
+ await this.runAccountBootstrap(steps);
2191
+ } catch (err) {
2192
+ const reason = err instanceof Error ? err.message : String(err);
2193
+ this._bootstrapSkipped = true;
2194
+ this._bootstrapStatus = { skipped: true, reason };
2195
+ this.notificationHandler.warning(
2196
+ `Account bootstrap did not complete: ${reason}`
2197
+ );
2198
+ console.warn(`[TinyCloudNode] account bootstrap failed: ${reason}`);
2199
+ return false;
2200
+ }
2144
2201
  return true;
2145
2202
  }
2146
2203
  async isFreshBootstrapAccount(steps) {
@@ -2192,16 +2249,23 @@ var _TinyCloudNode = class _TinyCloudNode {
2192
2249
  if (rawAbilities) {
2193
2250
  rawAbilitiesBySpace.set(step.space, rawAbilities);
2194
2251
  }
2195
- const session = await auth.createBootstrapSession({
2196
- spaceId: step.spaceId,
2197
- capabilityRequest: step.request ?? import_sdk_core8.BOOTSTRAP_SESSION_REQUESTS[step.space],
2198
- rawAbilities
2199
- });
2252
+ let session;
2253
+ try {
2254
+ session = await auth.createBootstrapSession({
2255
+ spaceId: step.spaceId,
2256
+ capabilityRequest: step.request ?? import_sdk_core8.BOOTSTRAP_SESSION_REQUESTS[step.space],
2257
+ rawAbilities
2258
+ });
2259
+ } catch (err) {
2260
+ throw new Error(
2261
+ `Account bootstrap aborted: signature rejected for space "${step.space}". Cause: ${err instanceof Error ? err.message : String(err)}`
2262
+ );
2263
+ }
2200
2264
  sessions.set(step.space, session);
2201
2265
  }
2202
2266
  for (const step of steps) {
2203
2267
  if (step.kind !== "host") continue;
2204
- const hosted = await auth.hostOwnedSpace(step.spaceId);
2268
+ const hosted = await auth.hostOwnedSpace(step.spaceId, "bootstrap-host");
2205
2269
  if (!hosted) {
2206
2270
  throw new Error(`Failed to host bootstrap space: ${step.spaceId}`);
2207
2271
  }