@tinycloud/node-sdk 3.0.0-beta.12 → 3.0.0-beta.14

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/index.cjs CHANGED
@@ -17060,6 +17060,7 @@ __export(index_exports, {
17060
17060
  FileSessionStorage: () => FileSessionStorage,
17061
17061
  HooksService: () => import_sdk_core18.HooksService,
17062
17062
  IdentityParseError: () => import_sdk_core11.IdentityParseError,
17063
+ InvalidRestoredSessionError: () => InvalidRestoredSessionError,
17063
17064
  KVService: () => import_sdk_core13.KVService,
17064
17065
  ManifestValidationError: () => import_sdk_core12.ManifestValidationError,
17065
17066
  MemorySessionStorage: () => MemorySessionStorage,
@@ -17139,6 +17140,7 @@ __export(index_exports, {
17139
17140
  expandActionShortNames: () => import_sdk_core12.expandActionShortNames,
17140
17141
  expandPermissionEntries: () => import_sdk_core12.expandPermissionEntries,
17141
17142
  expandPermissionEntry: () => import_sdk_core12.expandPermissionEntry,
17143
+ extractSiweExpiration: () => extractSiweExpiration,
17142
17144
  generateRandomReceiverKey: () => import_sdk_core17.generateRandomReceiverKey,
17143
17145
  getTinyCloudDebugLogs: () => import_sdk_core11.getTinyCloudDebugLogs,
17144
17146
  grantAuthRequest: () => grantAuthRequest,
@@ -19384,18 +19386,23 @@ var UnsupportedSessionRestoreError = class extends Error {
19384
19386
  this.name = "UnsupportedSessionRestoreError";
19385
19387
  }
19386
19388
  };
19389
+ var InvalidRestoredSessionError = class extends Error {
19390
+ constructor(message = "Persisted session authority is invalid or expired.") {
19391
+ super(message);
19392
+ this.code = "AUTH_EXPIRED";
19393
+ this.name = "InvalidRestoredSessionError";
19394
+ }
19395
+ };
19387
19396
  function canonicalRestoredVerificationMethod(canonicalVerificationMethod, persistedVerificationMethod) {
19388
19397
  if (typeof persistedVerificationMethod !== "string") return void 0;
19389
19398
  const principal = canonicalVerificationMethod.split("#", 1)[0];
19390
19399
  return persistedVerificationMethod === principal || persistedVerificationMethod === canonicalVerificationMethod ? canonicalVerificationMethod : void 0;
19391
19400
  }
19392
19401
  function persistedExpiry(value) {
19393
- if (typeof value !== "string") {
19394
- throw new Error("Persisted session without a SIWE expiration must include expiresAt.");
19395
- }
19402
+ if (typeof value !== "string") throw new InvalidRestoredSessionError("Persisted session must include expiresAt.");
19396
19403
  const expiry = new Date(value);
19397
19404
  if (!Number.isFinite(expiry.getTime()) || expiry.getTime() <= Date.now()) {
19398
- throw new Error("Persisted session expiry is invalid or expired.");
19405
+ throw new InvalidRestoredSessionError();
19399
19406
  }
19400
19407
  return expiry;
19401
19408
  }
@@ -20587,7 +20594,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20587
20594
  return;
20588
20595
  } catch (error) {
20589
20596
  const message = error instanceof Error ? error.message : String(error);
20590
- if (/Unauthorized Action|\b401\b/.test(message)) {
20597
+ if (/Unauthorized Action|\b(?:401|403)\b/.test(message)) {
20591
20598
  console.warn(
20592
20599
  "TinyCloud account registry sync stopped: authorization verdict is not retryable",
20593
20600
  error
@@ -20837,9 +20844,14 @@ var _TinyCloudNode = class _TinyCloudNode {
20837
20844
  */
20838
20845
  async restoreSession(sessionData) {
20839
20846
  await this.wasmBindings.ensureInitialized?.();
20840
- const restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20847
+ let restoredJwk;
20848
+ try {
20849
+ restoredJwk = clonePersistedSessionJwk(sessionData.jwk);
20850
+ } catch {
20851
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20852
+ }
20841
20853
  if (sessionData.chainId !== void 0 && (!Number.isSafeInteger(sessionData.chainId) || sessionData.chainId <= 0)) {
20842
- throw new Error("Persisted session chain ID must be a positive safe integer.");
20854
+ throw new InvalidRestoredSessionError("Persisted session chain ID must be a positive safe integer.");
20843
20855
  }
20844
20856
  const stagedManager = this.wasmBindings.createSessionManager();
20845
20857
  const stagedReplace = stagedManager.replaceSessionKey;
@@ -20851,32 +20863,31 @@ var _TinyCloudNode = class _TinyCloudNode {
20851
20863
  let stagedKeyId;
20852
20864
  let stagedJwk;
20853
20865
  let canonicalVerificationMethod;
20866
+ const keyIds = liveKeys.call(this.sessionManager);
20867
+ if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20868
+ throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20869
+ }
20870
+ for (const keyId of keyIds) {
20871
+ if (keyId === this.sessionKeyId) continue;
20872
+ const jwk = this.sessionManager.jwk(keyId);
20873
+ if (!jwk) throw new Error("missing live session key");
20874
+ stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20875
+ }
20854
20876
  try {
20855
- const keyIds = liveKeys.call(this.sessionManager);
20856
- if (!Array.isArray(keyIds) || !keyIds.every((keyId) => typeof keyId === "string")) {
20857
- throw new UnsupportedSessionRestoreError("it cannot reliably enumerate every live session signer");
20858
- }
20859
- for (const keyId of keyIds) {
20860
- if (keyId === this.sessionKeyId) continue;
20861
- const jwk = this.sessionManager.jwk(keyId);
20862
- if (!jwk) throw new Error("missing live session key");
20863
- stagedReplace.call(stagedManager, clonePersistedSessionJwk(JSON.parse(jwk)), keyId);
20864
- }
20865
20877
  stagedKeyId = stagedReplace.call(stagedManager, restoredJwk, this.sessionKeyId);
20866
- const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20867
- if (!stagedJwkJson) throw new Error("missing restored session key");
20868
- stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20869
- canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20870
- } catch (error) {
20871
- if (error instanceof UnsupportedSessionRestoreError) throw error;
20872
- throw new Error("Persisted session has an invalid private Ed25519 session key.");
20878
+ } catch {
20879
+ throw new InvalidRestoredSessionError("Persisted session has an invalid private Ed25519 session key.");
20873
20880
  }
20881
+ const stagedJwkJson = stagedManager.jwk(stagedKeyId);
20882
+ if (!stagedJwkJson) throw new Error("missing restored session key");
20883
+ stagedJwk = clonePersistedSessionJwk(JSON.parse(stagedJwkJson));
20884
+ canonicalVerificationMethod = stagedManager.getDID(stagedKeyId);
20874
20885
  const restoredVerificationMethod = canonicalRestoredVerificationMethod(
20875
20886
  canonicalVerificationMethod,
20876
20887
  sessionData.verificationMethod
20877
20888
  );
20878
20889
  if (!restoredVerificationMethod) {
20879
- throw new Error(
20890
+ throw new InvalidRestoredSessionError(
20880
20891
  "Persisted session verification method does not match its private Ed25519 session key."
20881
20892
  );
20882
20893
  }
@@ -20888,25 +20899,36 @@ var _TinyCloudNode = class _TinyCloudNode {
20888
20899
  ];
20889
20900
  const hasPersistedProof = proofValues.every((value) => value !== void 0);
20890
20901
  if (!hasPersistedProof && (proofValues.some((value) => value !== void 0) || sessionData.expiresAt !== void 0)) {
20891
- throw new Error("Persisted session authority metadata is incomplete.");
20902
+ throw new InvalidRestoredSessionError("Persisted session authority metadata is incomplete.");
20903
+ }
20904
+ let restoredAddress;
20905
+ try {
20906
+ restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
20907
+ } catch {
20908
+ throw new InvalidRestoredSessionError("Persisted session has an invalid wallet address.");
20892
20909
  }
20893
- const restoredAddress = hasPersistedProof ? (0, import_sdk_core7.canonicalizeAddress)(sessionData.address) : void 0;
20894
20910
  let stagedSessionExpiry = /* @__PURE__ */ new Date(0);
20895
20911
  let stagedRecap = [];
20896
20912
  if (hasPersistedProof) {
20897
20913
  if (typeof this.wasmBindings.validatePersistedSession !== "function") {
20898
20914
  throw new UnsupportedSessionRestoreError("it cannot verify persisted SIWE authority");
20899
20915
  }
20900
- const verified = this.wasmBindings.validatePersistedSession({
20901
- delegationHeader: sessionData.delegationHeader,
20902
- delegationCid: sessionData.delegationCid,
20903
- spaceId: sessionData.spaceId,
20904
- jwk: stagedJwk,
20905
- address: restoredAddress,
20906
- chainId: sessionData.chainId,
20907
- siwe: sessionData.siwe,
20908
- signature: sessionData.signature
20909
- });
20916
+ const verified = (() => {
20917
+ try {
20918
+ return this.wasmBindings.validatePersistedSession({
20919
+ delegationHeader: sessionData.delegationHeader,
20920
+ delegationCid: sessionData.delegationCid,
20921
+ spaceId: sessionData.spaceId,
20922
+ jwk: stagedJwk,
20923
+ address: restoredAddress,
20924
+ chainId: sessionData.chainId,
20925
+ siwe: sessionData.siwe,
20926
+ signature: sessionData.signature
20927
+ });
20928
+ } catch (error) {
20929
+ throw new InvalidRestoredSessionError(error instanceof Error ? error.message : String(error));
20930
+ }
20931
+ })();
20910
20932
  const exactRecap = verified.verifiedRecap;
20911
20933
  if (!Array.isArray(exactRecap) || !exactRecap.every(
20912
20934
  (entry) => entry !== null && typeof entry === "object" && typeof entry.service === "string" && typeof entry.space === "string" && typeof entry.path === "string" && Array.isArray(entry.actions) && entry.actions.every((action) => typeof action === "string") && Array.isArray(entry.caveats) && entry.caveats.every(
@@ -20927,7 +20949,7 @@ var _TinyCloudNode = class _TinyCloudNode {
20927
20949
  const signedExpiry = verified.expiresAt === void 0 ? void 0 : persistedExpiry(verified.expiresAt);
20928
20950
  const persistedPolicyExpiry = sessionData.expiresAt === void 0 ? void 0 : persistedExpiry(sessionData.expiresAt);
20929
20951
  if (signedExpiry && persistedPolicyExpiry && !sameInstant(signedExpiry, persistedPolicyExpiry)) {
20930
- throw new Error("Persisted session expiry does not match its signed SIWE authority.");
20952
+ throw new InvalidRestoredSessionError("Persisted session expiry does not match its signed SIWE authority.");
20931
20953
  }
20932
20954
  stagedSessionExpiry = signedExpiry ?? persistedPolicyExpiry ?? persistedExpiry(void 0);
20933
20955
  }
@@ -21120,7 +21142,7 @@ var _TinyCloudNode = class _TinyCloudNode {
21120
21142
  const delegations = input.recap.map((entry) => ({
21121
21143
  cid: session.delegationCid,
21122
21144
  delegateDID: session.verificationMethod,
21123
- spaceId: entry.space,
21145
+ spaceId: this.spaceIdsEqual(entry.space, session.spaceId) ? session.spaceId : entry.space,
21124
21146
  path: entry.path,
21125
21147
  actions: [...entry.actions],
21126
21148
  caveats: cloneRecapCaveats(entry.caveats),
@@ -23890,21 +23912,15 @@ var _TinyCloudNode = class _TinyCloudNode {
23890
23912
  }
23891
23913
  return granted.spaceId !== void 0 && requested.spaceId !== void 0 && this.spaceIdsEqual(granted.spaceId, requested.spaceId) && this.pathContains(granted.path, requested.path);
23892
23914
  }
23893
- // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. The embedded
23894
- // EIP-155 address is case-insensitive, but the CLI canonicalizes it to
23895
- // lowercase when building a space URI while stored runtime delegations keep
23896
- // the EIP-55 checksummed form — so a byte-for-byte compare spuriously rejects
23897
- // an otherwise-valid grant. Lowercase ONLY the `eip155:<chain>:0x<addr>`
23898
- // segment and leave everything else (crucially the case-sensitive space NAME)
23899
- // byte-exact. Mirrors the CLI's `normalizeSpaceForCompare` (OPENKEY_SCOPE_MISMATCH fix).
23915
+ // Space IDs are `tinycloud:pkh:eip155:<chain>:<0xADDR>:<name>`. Compare
23916
+ // only the embedded EIP-155 address without case sensitivity; chain, name,
23917
+ // and all non-PKH space identifiers remain byte-exact.
23900
23918
  spaceIdsEqual(a, b) {
23901
23919
  return this.normalizeSpaceAddress(a) === this.normalizeSpaceAddress(b);
23902
23920
  }
23903
23921
  normalizeSpaceAddress(space) {
23904
- return space.replace(
23905
- /(eip155:\d+:)(0x[0-9a-fA-F]{40})/,
23906
- (_match, prefix, addr) => prefix + addr.toLowerCase()
23907
- );
23922
+ const match = /^(tinycloud:pkh:eip155:\d+:)(0x[0-9a-fA-F]{40})(:.+)$/.exec(space);
23923
+ return match === null ? space : `${match[1]}${match[2].toLowerCase()}${match[3]}`;
23908
23924
  }
23909
23925
  actionContains(grantedAction, requestedAction) {
23910
23926
  if (grantedAction === requestedAction) {
@@ -25001,6 +25017,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
25001
25017
  FileSessionStorage,
25002
25018
  HooksService,
25003
25019
  IdentityParseError,
25020
+ InvalidRestoredSessionError,
25004
25021
  KVService,
25005
25022
  ManifestValidationError,
25006
25023
  MemorySessionStorage,
@@ -25080,6 +25097,7 @@ var import_sdk_core23 = require("@tinycloud/sdk-core");
25080
25097
  expandActionShortNames,
25081
25098
  expandPermissionEntries,
25082
25099
  expandPermissionEntry,
25100
+ extractSiweExpiration,
25083
25101
  generateRandomReceiverKey,
25084
25102
  getTinyCloudDebugLogs,
25085
25103
  grantAuthRequest,