@tinycloud/sdk-services 3.11.0-beta.2 → 3.12.0-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.
Files changed (71) hide show
  1. package/dist/{BaseService-Ma2MwJAd.d.ts → BaseService-BRDG69KR.d.ts} +1 -1
  2. package/dist/{BaseService-O0iyuFLk.d.cts → BaseService-CdBkfPGE.d.cts} +1 -1
  3. package/dist/{IHooksService-4QNODVy6.d.cts → IHooksService-C9p3stsB.d.cts} +1 -1
  4. package/dist/{IHooksService-CvTpJbQD.d.ts → IHooksService-D6eyZat1.d.ts} +1 -1
  5. package/dist/{ISQLService-Aly1CBsW.d.ts → ISQLService-B_4KcGA-.d.ts} +1 -1
  6. package/dist/{ISQLService-BOgWzHLT.d.cts → ISQLService-C6nNpcls.d.cts} +1 -1
  7. package/dist/context.cjs +4 -0
  8. package/dist/context.cjs.map +1 -1
  9. package/dist/context.d.cts +1 -1
  10. package/dist/context.d.ts +1 -1
  11. package/dist/context.js +4 -0
  12. package/dist/context.js.map +1 -1
  13. package/dist/encryption/index.cjs +60 -3
  14. package/dist/encryption/index.cjs.map +1 -1
  15. package/dist/encryption/index.d.cts +12 -22
  16. package/dist/encryption/index.d.ts +12 -22
  17. package/dist/encryption/index.js +60 -3
  18. package/dist/encryption/index.js.map +1 -1
  19. package/dist/encryption/named-keys.cjs +2 -0
  20. package/dist/encryption/named-keys.cjs.map +1 -1
  21. package/dist/encryption/named-keys.d.cts +2 -2
  22. package/dist/encryption/named-keys.d.ts +2 -2
  23. package/dist/encryption/named-keys.js +2 -0
  24. package/dist/encryption/named-keys.js.map +1 -1
  25. package/dist/encryption/resource.cjs.map +1 -1
  26. package/dist/encryption/resource.js.map +1 -1
  27. package/dist/feed/index.cjs +4 -0
  28. package/dist/feed/index.cjs.map +1 -1
  29. package/dist/feed/index.d.cts +3 -3
  30. package/dist/feed/index.d.ts +3 -3
  31. package/dist/feed/index.js +4 -0
  32. package/dist/feed/index.js.map +1 -1
  33. package/dist/index.cjs +260 -7
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +14 -12
  36. package/dist/index.d.ts +14 -12
  37. package/dist/index.js +253 -7
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal/decrypt-transport-response-error.d.cts +1 -1
  40. package/dist/internal/decrypt-transport-response-error.d.ts +1 -1
  41. package/dist/kv/index.cjs +34 -0
  42. package/dist/kv/index.cjs.map +1 -1
  43. package/dist/kv/index.d.cts +4 -4
  44. package/dist/kv/index.d.ts +4 -4
  45. package/dist/kv/index.js +34 -0
  46. package/dist/kv/index.js.map +1 -1
  47. package/dist/kv/replication/index.cjs +34 -0
  48. package/dist/kv/replication/index.cjs.map +1 -1
  49. package/dist/kv/replication/index.d.cts +3 -3
  50. package/dist/kv/replication/index.d.ts +3 -3
  51. package/dist/kv/replication/index.js +34 -0
  52. package/dist/kv/replication/index.js.map +1 -1
  53. package/dist/{scope-DEgc9f-W.d.cts → scope-DtcbyRKY.d.cts} +1 -1
  54. package/dist/{scope-BZr0QwxX.d.ts → scope-J20PQP3u.d.ts} +1 -1
  55. package/dist/sql/index.cjs +36 -0
  56. package/dist/sql/index.cjs.map +1 -1
  57. package/dist/sql/index.d.cts +4 -4
  58. package/dist/sql/index.d.ts +4 -4
  59. package/dist/sql/index.js +36 -0
  60. package/dist/sql/index.js.map +1 -1
  61. package/dist/telemetry/requestLog.cjs +4 -0
  62. package/dist/telemetry/requestLog.cjs.map +1 -1
  63. package/dist/telemetry/requestLog.d.cts +1 -1
  64. package/dist/telemetry/requestLog.d.ts +1 -1
  65. package/dist/telemetry/requestLog.js +4 -0
  66. package/dist/telemetry/requestLog.js.map +1 -1
  67. package/dist/{types-CSd0RYir.d.ts → types-DjAP8dve.d.cts} +4 -0
  68. package/dist/{types-CSd0RYir.d.cts → types-DjAP8dve.d.ts} +4 -0
  69. package/dist/{types-D4XbIfFJ.d.cts → types-XE4tyBLH.d.ts} +60 -3
  70. package/dist/{types-Bf91gup6.d.ts → types-cWtQmCCw.d.cts} +60 -3
  71. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -51,7 +51,11 @@ __export(src_exports, {
51
51
  MAX_SECRET_SCOPE_LENGTH: () => MAX_SECRET_SCOPE_LENGTH,
52
52
  NETWORK_NAME_PATTERN: () => NETWORK_NAME_PATTERN,
53
53
  NetworkIdError: () => NetworkIdError,
54
+ NodeIdentityMismatchError: () => NodeIdentityMismatchError,
55
+ NodeNotRespondingError: () => NodeNotRespondingError,
56
+ PartialNodeWriteError: () => PartialNodeWriteError,
54
57
  PrefixedKVService: () => PrefixedKVService,
58
+ ResourceNotRoutedError: () => ResourceNotRoutedError,
55
59
  RetryPolicySchema: () => RetryPolicySchema,
56
60
  SECRET_NAME_RE: () => SECRET_NAME_RE,
57
61
  SECRET_SCOPE_RE: () => SECRET_SCOPE_RE,
@@ -99,6 +103,7 @@ __export(src_exports, {
99
103
  checkDecryptInvocationInput: () => checkDecryptInvocationInput,
100
104
  clearTinyCloudDebugLogs: () => clearTinyCloudDebugLogs,
101
105
  createKVResponseSchema: () => createKVResponseSchema,
106
+ createResourceRoutingFetch: () => createResourceRoutingFetch,
102
107
  createResultSchema: () => createResultSchema,
103
108
  createVaultCrypto: () => createVaultCrypto,
104
109
  decryptEnvelopeWithKey: () => decryptEnvelopeWithKey,
@@ -141,6 +146,7 @@ __export(src_exports, {
141
146
  requiresSecretsOptIn: () => requiresSecretsOptIn,
142
147
  resolveSecretListPrefix: () => resolveSecretListPrefix,
143
148
  resolveSecretPath: () => resolveSecretPath,
149
+ routingFailure: () => routingFailure,
144
150
  serviceError: () => serviceError,
145
151
  storageLimitReachedError: () => storageLimitReachedError,
146
152
  storageQuotaExceededError: () => storageQuotaExceededError,
@@ -161,6 +167,7 @@ __export(src_exports, {
161
167
  validateTelemetrySpanEvent: () => validateTelemetrySpanEvent,
162
168
  validatedCapabilityOf: () => validatedCapabilityOf,
163
169
  verifyDecryptResponse: () => verifyDecryptResponse,
170
+ verifyResourceDestination: () => verifyResourceDestination,
164
171
  wrapError: () => wrapError
165
172
  });
166
173
  module.exports = __toCommonJS(src_exports);
@@ -177,6 +184,10 @@ var ErrorCodes = {
177
184
  /** HTTP 401: a delegation the cited one rests on was revoked. */
178
185
  AUTH_DELEGATION_ANCESTOR_REVOKED: "AUTH_DELEGATION_ANCESTOR_REVOKED",
179
186
  NETWORK_ERROR: "NETWORK_ERROR",
187
+ NODE_NOT_RESPONDING: "NODE_NOT_RESPONDING",
188
+ NODE_IDENTITY_MISMATCH: "NODE_IDENTITY_MISMATCH",
189
+ PARTIAL_NODE_WRITE: "PARTIAL_NODE_WRITE",
190
+ RESOURCE_NOT_ROUTED: "RESOURCE_NOT_ROUTED",
180
191
  TIMEOUT: "TIMEOUT",
181
192
  ABORTED: "ABORTED",
182
193
  INVALID_INPUT: "INVALID_INPUT",
@@ -951,6 +962,196 @@ function networkDiscoveryKey(name) {
951
962
  var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
952
963
  var NETWORK_NAME_PATTERN = NETWORK_NAME_RE;
953
964
 
965
+ // src/resourceRouting.ts
966
+ async function verifyResourceDestination(target, fetchFn) {
967
+ if (!target.nodeDid) return;
968
+ let info;
969
+ let keys;
970
+ try {
971
+ const signal = AbortSignal.timeout(5e3);
972
+ const responses = await Promise.all([
973
+ fetchFn(`${target.url}/info`, { signal, redirect: "error" }),
974
+ fetchFn(`${target.url}/.well-known/tinycloud/node-keys`, { signal, redirect: "error" })
975
+ ]);
976
+ if (responses.some((response) => response.status >= 500)) throw new NodeNotRespondingError([target.url]);
977
+ if (responses.some((response) => !response.ok)) throw new NodeIdentityMismatchError(target.url, target.nodeDid);
978
+ try {
979
+ [info, keys] = await Promise.all(responses.map((response) => response.json()));
980
+ } catch {
981
+ throw new NodeIdentityMismatchError(target.url, target.nodeDid);
982
+ }
983
+ } catch (error) {
984
+ if (error instanceof NodeIdentityMismatchError || error instanceof NodeNotRespondingError) throw error;
985
+ throw new NodeNotRespondingError([target.url], error);
986
+ }
987
+ const infoDid = typeof info === "object" && info !== null && "nodeId" in info && typeof info.nodeId === "string" ? info.nodeId : void 0;
988
+ const keysDid = typeof keys === "object" && keys !== null && "nodeDid" in keys && typeof keys.nodeDid === "string" ? keys.nodeDid : void 0;
989
+ if (infoDid !== target.nodeDid || keysDid !== target.nodeDid) {
990
+ throw new NodeIdentityMismatchError(target.url, target.nodeDid, infoDid !== target.nodeDid ? infoDid : keysDid);
991
+ }
992
+ }
993
+ var NodeNotRespondingError = class extends Error {
994
+ constructor(hosts, cause) {
995
+ super("The place your data is stored isn't responding right now. Please try again.");
996
+ this.hosts = hosts;
997
+ this.cause = cause;
998
+ this.code = "NODE_NOT_RESPONDING";
999
+ this.name = "NodeNotRespondingError";
1000
+ }
1001
+ };
1002
+ var NodeIdentityMismatchError = class extends Error {
1003
+ constructor(url, expectedDid, actualDid) {
1004
+ super(`TinyCloud node identity does not match the registry at ${url}`);
1005
+ this.url = url;
1006
+ this.expectedDid = expectedDid;
1007
+ this.actualDid = actualDid;
1008
+ this.code = "NODE_IDENTITY_MISMATCH";
1009
+ this.name = "NodeIdentityMismatchError";
1010
+ }
1011
+ };
1012
+ var ResourceNotRoutedError = class extends Error {
1013
+ constructor(resource) {
1014
+ super(`No registered destination can serve ${resource}`);
1015
+ this.resource = resource;
1016
+ this.code = "RESOURCE_NOT_ROUTED";
1017
+ this.name = "ResourceNotRoutedError";
1018
+ }
1019
+ };
1020
+ var PartialNodeWriteError = class extends Error {
1021
+ constructor(succeeded, failed) {
1022
+ super("The write did not complete on every registered writable node.");
1023
+ this.succeeded = succeeded;
1024
+ this.failed = failed;
1025
+ this.code = "PARTIAL_NODE_WRITE";
1026
+ this.name = "PartialNodeWriteError";
1027
+ }
1028
+ };
1029
+ var ROUTING_FAILURE_CODES = {
1030
+ NODE_NOT_RESPONDING: true,
1031
+ PARTIAL_NODE_WRITE: true,
1032
+ NODE_IDENTITY_MISMATCH: true,
1033
+ RESOURCE_NOT_ROUTED: true,
1034
+ DECRYPTION_DEPENDENCY_UNAVAILABLE: true,
1035
+ registry_unavailable: true,
1036
+ node_not_found: true,
1037
+ node_registry_entry_exists: true
1038
+ };
1039
+ function isRoutingFailureCode(code) {
1040
+ return Object.prototype.hasOwnProperty.call(ROUTING_FAILURE_CODES, code);
1041
+ }
1042
+ function routingFailure(error) {
1043
+ if (typeof error === "object" && error !== null && "code" in error && "message" in error && typeof error.code === "string" && typeof error.message === "string" && isRoutingFailureCode(error.code)) {
1044
+ const candidate = "meta" in error ? error.meta : void 0;
1045
+ let meta = typeof candidate === "object" && candidate !== null && !Array.isArray(candidate) ? candidate : void 0;
1046
+ if (error.code === "NODE_NOT_RESPONDING" && "hosts" in error && Array.isArray(error.hosts)) {
1047
+ meta = { ...meta, hosts: error.hosts };
1048
+ } else if (error.code === "PARTIAL_NODE_WRITE" && "succeeded" in error && Array.isArray(error.succeeded) && "failed" in error && Array.isArray(error.failed)) {
1049
+ meta = { ...meta, succeeded: error.succeeded, failed: error.failed };
1050
+ }
1051
+ return { code: error.code, message: error.message, ...error instanceof Error ? { cause: error } : {}, ...meta ? { meta } : {} };
1052
+ }
1053
+ return void 0;
1054
+ }
1055
+ function authorizationOf(headers) {
1056
+ if (headers === void 0) return void 0;
1057
+ return (Array.isArray(headers) ? headers : Object.entries(headers)).find(([name]) => name.toLowerCase() === "authorization")?.[1];
1058
+ }
1059
+ function invocationResources(authorization) {
1060
+ const encoded = authorization.split(".")[1];
1061
+ if (!encoded) throw new ResourceNotRoutedError("invalid invocation");
1062
+ const payload = JSON.parse(atob(encoded.replace(/-/g, "+").replace(/_/g, "/")));
1063
+ if (typeof payload !== "object" || payload === null || !("att" in payload) || typeof payload.att !== "object" || payload.att === null || Array.isArray(payload.att)) {
1064
+ throw new ResourceNotRoutedError("invalid invocation attenuation");
1065
+ }
1066
+ const resources = [];
1067
+ for (const [uri, abilities] of Object.entries(payload.att)) {
1068
+ if (typeof abilities !== "object" || abilities === null || Array.isArray(abilities)) throw new ResourceNotRoutedError(uri);
1069
+ for (const action of Object.keys(abilities)) {
1070
+ const resource = uri.startsWith("tinycloud:") ? uri.split("/")[0] : uri;
1071
+ if (!resource.startsWith("tinycloud:") && !resource.startsWith("urn:tinycloud:encryption:")) throw new ResourceNotRoutedError(uri);
1072
+ const read2 = /^(tinycloud\.(kv\/(get|list|metadata|head|sync)|sql\/(read|export)|duckdb\/(read|describe|export)|capabilities\/read|space\/(list|info)|hooks\/list))$/.test(action);
1073
+ const access = resource.startsWith("urn:tinycloud:encryption:") ? "encryption" : action === "tinycloud.space/host" ? "host" : read2 ? "read" : "write";
1074
+ if (!resources.some((entry) => entry.resource === resource && entry.access === access)) resources.push({ resource, access });
1075
+ }
1076
+ }
1077
+ if (resources.length === 0) throw new ResourceNotRoutedError("empty invocation");
1078
+ return resources;
1079
+ }
1080
+ function createResourceRoutingFetch(fetchFn, routing) {
1081
+ return async (url, init) => {
1082
+ const parsed = new URL(url);
1083
+ if (parsed.pathname !== "/invoke") return fetchFn(url, init);
1084
+ const authorization = authorizationOf(init?.headers);
1085
+ if (!authorization) throw new ResourceNotRoutedError("unsigned invocation");
1086
+ const resources = invocationResources(authorization);
1087
+ const routes = await Promise.all(resources.map(({ resource, access }) => routing.resolveResource(resource, access)));
1088
+ if (routes.some((route) => route.length === 0)) throw new ResourceNotRoutedError(resources[routes.findIndex((route) => route.length === 0)].resource);
1089
+ const write = resources.some(({ access }) => access === "write" || access === "host");
1090
+ let targets = routes[0].filter((target) => routes.every((route) => route.some((other) => other.url === target.url && other.nodeDid === target.nodeDid)));
1091
+ if (targets.length === 0 || write && routes.some((route) => route.length !== targets.length)) throw new ResourceNotRoutedError(resources.map(({ resource }) => resource).join(", "));
1092
+ targets = targets.filter((target, index) => targets.findIndex((other) => other.url === target.url) === index);
1093
+ const send = async (target) => {
1094
+ init?.signal?.throwIfAborted();
1095
+ if (routing.verifyDestination) await routing.verifyDestination(target);
1096
+ else await verifyResourceDestination(target, fetchFn);
1097
+ const signed = target.nodeDid ? await routing.bindAudience(authorization, target.nodeDid) : authorization;
1098
+ const headers = (Array.isArray(init?.headers) ? init.headers : Object.entries(init?.headers ?? {})).filter(([name]) => name.toLowerCase() !== "authorization");
1099
+ headers.push(["Authorization", signed]);
1100
+ const timeout = AbortSignal.timeout(5e3);
1101
+ const signal = init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout;
1102
+ try {
1103
+ return await fetchFn(`${target.url}${parsed.pathname}${parsed.search}`, { ...init, headers, signal, redirect: "error" });
1104
+ } catch (error) {
1105
+ if (init?.signal?.aborted) throw error;
1106
+ if (routingFailure(error)) throw error;
1107
+ throw new NodeNotRespondingError([target.url], error);
1108
+ }
1109
+ };
1110
+ if (!write) {
1111
+ let response2;
1112
+ let failure;
1113
+ for (const target of targets) {
1114
+ try {
1115
+ response2 = await send(target);
1116
+ if (response2.ok || response2.status !== 404) return response2;
1117
+ } catch (error) {
1118
+ if (!(error instanceof NodeNotRespondingError)) throw error;
1119
+ failure = error;
1120
+ }
1121
+ }
1122
+ if (failure) throw new NodeNotRespondingError(targets.map(({ url: url2 }) => url2), failure);
1123
+ if (response2) return response2;
1124
+ throw new NodeNotRespondingError(targets.map(({ url: url2 }) => url2), failure);
1125
+ }
1126
+ const succeeded = [];
1127
+ const failed = [];
1128
+ let response;
1129
+ let failureResponse;
1130
+ for (const target of targets) {
1131
+ try {
1132
+ const result = await send(target);
1133
+ if (result.ok) {
1134
+ succeeded.push(target.url);
1135
+ response ?? (response = result);
1136
+ } else {
1137
+ failed.push({ url: target.url, status: result.status });
1138
+ failureResponse ?? (failureResponse = result);
1139
+ }
1140
+ } catch (error) {
1141
+ if (!routingFailure(error) && succeeded.length === 0) throw error;
1142
+ failed.push({ url: target.url, cause: error });
1143
+ }
1144
+ }
1145
+ if (failed.length > 0) {
1146
+ if (succeeded.length > 0 || targets.length > 1) throw new PartialNodeWriteError(succeeded, failed);
1147
+ if (failureResponse) return failureResponse;
1148
+ if (failed[0]?.cause && !(failed[0].cause instanceof NodeNotRespondingError)) throw failed[0].cause;
1149
+ throw new NodeNotRespondingError(targets.map(({ url: url2 }) => url2), failed[0]?.cause);
1150
+ }
1151
+ return response;
1152
+ };
1153
+ }
1154
+
954
1155
  // src/errors.ts
955
1156
  function parsePermissionHint(value) {
956
1157
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -1256,6 +1457,8 @@ function isStorageFullError(error) {
1256
1457
  return error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error?.code === ErrorCodes.STORAGE_LIMIT_REACHED;
1257
1458
  }
1258
1459
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
1460
+ const routing = routingFailure(error);
1461
+ if (routing) return { ...routing, service };
1259
1462
  if (error instanceof Error) {
1260
1463
  if (isRequestTimeoutError(error)) {
1261
1464
  return timeoutError(service, error.timeoutMs, error);
@@ -6197,6 +6400,8 @@ function defaultVaultMessage(input) {
6197
6400
  case "STORAGE_QUOTA_EXCEEDED":
6198
6401
  case "STORAGE_LIMIT_REACHED":
6199
6402
  return input.message;
6403
+ default:
6404
+ return input.message ?? input.code;
6200
6405
  }
6201
6406
  }
6202
6407
  function vaultError(input) {
@@ -6248,6 +6453,8 @@ function ifMatchWriteError(key, error) {
6248
6453
  });
6249
6454
  }
6250
6455
  function kvWriteError(context, error) {
6456
+ const routing = routingFailure(error);
6457
+ if (routing) return vaultError(routing);
6251
6458
  if (error.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED || error.code === ErrorCodes.STORAGE_LIMIT_REACHED) {
6252
6459
  return vaultError({ code: error.code, message: error.message, meta: error.meta });
6253
6460
  }
@@ -6554,6 +6761,8 @@ var DataVaultService = class extends BaseService {
6554
6761
  { aad, metadata }
6555
6762
  );
6556
6763
  if (!envelopeResult.ok) {
6764
+ const routing = routingFailure(envelopeResult.error);
6765
+ if (routing) return vaultError(routing);
6557
6766
  return vaultError({
6558
6767
  code: "STORAGE_ERROR",
6559
6768
  cause: new Error(envelopeResult.error.message)
@@ -6580,6 +6789,8 @@ var DataVaultService = class extends BaseService {
6580
6789
  }
6581
6790
  return { ok: true, data: void 0 };
6582
6791
  } catch (error) {
6792
+ const routing = routingFailure(error);
6793
+ if (routing) return vaultError(routing);
6583
6794
  return vaultError({
6584
6795
  code: "STORAGE_ERROR",
6585
6796
  cause: toError(error)
@@ -6606,6 +6817,8 @@ var DataVaultService = class extends BaseService {
6606
6817
  ...options?.source === void 0 ? {} : { source: options.source }
6607
6818
  });
6608
6819
  if (!valueResult.ok) {
6820
+ const routing = routingFailure(valueResult.error);
6821
+ if (routing) return vaultError(routing);
6609
6822
  return vaultError({ code: "KEY_NOT_FOUND", key });
6610
6823
  }
6611
6824
  const rawEnvelope = unwrapKVData(valueResult.data);
@@ -6626,6 +6839,8 @@ var DataVaultService = class extends BaseService {
6626
6839
  const proof = await this.decryptCapabilityProof();
6627
6840
  const plaintextResult = await config.service.decryptEnvelope(envelope, proof);
6628
6841
  if (!plaintextResult.ok) {
6842
+ const routing = routingFailure(plaintextResult.error);
6843
+ if (routing) return vaultError(routing);
6629
6844
  return vaultError({
6630
6845
  code: "DECRYPTION_FAILED",
6631
6846
  message: plaintextResult.error.message
@@ -6645,6 +6860,8 @@ var DataVaultService = class extends BaseService {
6645
6860
  data: { value, metadata, keyId, ...etag === void 0 ? {} : { etag } }
6646
6861
  };
6647
6862
  } catch (error) {
6863
+ const routing = routingFailure(error);
6864
+ if (routing) return vaultError(routing);
6648
6865
  return vaultError({
6649
6866
  code: "STORAGE_ERROR",
6650
6867
  cause: toError(error)
@@ -6657,6 +6874,8 @@ var DataVaultService = class extends BaseService {
6657
6874
  * Unlike {@link getNetworkEncrypted}, this is intentionally message-free so
6658
6875
  * an operation layer can distinguish an authorized absence from a failed
6659
6876
  * read without receiving node, envelope, plaintext, or key material.
6877
+ * Typed routing failures reject unchanged rather than becoming an ordinary
6878
+ * read/decrypt failure; the operation boundary projects their safe code.
6660
6879
  */
6661
6880
  async readNetworkEncrypted(key, options) {
6662
6881
  const config = this.networkEncryption;
@@ -6666,10 +6885,12 @@ var DataVaultService = class extends BaseService {
6666
6885
  let valueResult;
6667
6886
  try {
6668
6887
  valueResult = await this.tc.kv.get(`vault/${key}`, { raw: true, source: "network" });
6669
- } catch {
6888
+ } catch (error) {
6889
+ if (routingFailure(error)) throw error;
6670
6890
  return { status: "node_unreachable" };
6671
6891
  }
6672
6892
  if (!valueResult.ok) {
6893
+ if (routingFailure(valueResult.error)) throw valueResult.error;
6673
6894
  const permissionHint = parsePermissionHint(valueResult.error.meta?.permissionHint);
6674
6895
  if (valueResult.error.code === "AUTH_UNAUTHORIZED" && permissionHint !== void 0) {
6675
6896
  return { status: "permission_required", hint: permissionHint };
@@ -6677,7 +6898,7 @@ var DataVaultService = class extends BaseService {
6677
6898
  if ((valueResult.error.code === "NOT_FOUND" || valueResult.error.code === "KV_NOT_FOUND") && !hasHttpResponse(valueResult.error)) {
6678
6899
  return { status: "not_found" };
6679
6900
  }
6680
- if (valueResult.error.code === "NETWORK_ERROR" || valueResult.error.code === "TIMEOUT" || valueResult.error.code === "ABORTED") {
6901
+ if (valueResult.error.code === "NODE_NOT_RESPONDING" || valueResult.error.code === "NETWORK_ERROR" || valueResult.error.code === "TIMEOUT" || valueResult.error.code === "ABORTED") {
6681
6902
  return hasHttpResponse(valueResult.error) ? { status: "read_failed" } : { status: "node_unreachable" };
6682
6903
  }
6683
6904
  return { status: "read_failed" };
@@ -6699,10 +6920,12 @@ var DataVaultService = class extends BaseService {
6699
6920
  try {
6700
6921
  const proof = await this.decryptCapabilityProof();
6701
6922
  plaintextResult = await config.service.decryptEnvelope(envelope, proof);
6702
- } catch {
6923
+ } catch (error) {
6924
+ if (routingFailure(error)) throw error;
6703
6925
  return { status: "decrypt_failed" };
6704
6926
  }
6705
6927
  if (!plaintextResult.ok) {
6928
+ if (routingFailure(plaintextResult.error)) throw plaintextResult.error;
6706
6929
  const permissionHint = parsePermissionHint(
6707
6930
  plaintextResult.error.permissionHint
6708
6931
  );
@@ -6712,7 +6935,7 @@ var DataVaultService = class extends BaseService {
6712
6935
  if (plaintextResult.error.code === "INVALID_ENVELOPE") {
6713
6936
  return { status: "corrupt_envelope" };
6714
6937
  }
6715
- if (plaintextResult.error.code === "TRANSPORT_ERROR") {
6938
+ if (plaintextResult.error.code === "NODE_NOT_RESPONDING" || plaintextResult.error.code === "TRANSPORT_ERROR") {
6716
6939
  return { status: "node_unreachable" };
6717
6940
  }
6718
6941
  return { status: "decrypt_failed" };
@@ -7869,6 +8092,8 @@ function defaultEncryptionMessage(input) {
7869
8092
  return input.message ?? input.cause.message;
7870
8093
  case "INVALID_INPUT":
7871
8094
  return input.message;
8095
+ default:
8096
+ return input.message ?? input.code;
7872
8097
  }
7873
8098
  }
7874
8099
  function encryptionError(input) {
@@ -7933,6 +8158,8 @@ async function discoverNetwork(input) {
7933
8158
  return { ok: true, data: { descriptor: validated.data, source: "node" } };
7934
8159
  }
7935
8160
  } catch (err3) {
8161
+ const routing = routingFailure(err3);
8162
+ if (routing) return { ok: false, error: encryptionError(routing) };
7936
8163
  }
7937
8164
  }
7938
8165
  if (input.wellKnown !== void 0) {
@@ -7950,6 +8177,8 @@ async function discoverNetwork(input) {
7950
8177
  };
7951
8178
  }
7952
8179
  } catch (err3) {
8180
+ const routing = routingFailure(err3);
8181
+ if (routing) return { ok: false, error: encryptionError(routing) };
7953
8182
  }
7954
8183
  }
7955
8184
  return {
@@ -8534,6 +8763,14 @@ function encOk(data) {
8534
8763
  function encErr(error) {
8535
8764
  return { ok: false, error };
8536
8765
  }
8766
+ function decryptionError(input) {
8767
+ const unavailable = input.code === "NODE_NOT_RESPONDING" || input.code === "TRANSPORT_ERROR" && input.cause.name !== "AbortError";
8768
+ return encryptionError(unavailable ? {
8769
+ ...input,
8770
+ code: "DECRYPTION_DEPENDENCY_UNAVAILABLE",
8771
+ message: "The decryption service isn't responding right now. Please try again."
8772
+ } : input);
8773
+ }
8537
8774
  var EncryptionService = class extends BaseService {
8538
8775
  constructor(config) {
8539
8776
  super();
@@ -8581,6 +8818,8 @@ var EncryptionService = class extends BaseService {
8581
8818
  });
8582
8819
  return encOk(result.envelope);
8583
8820
  } catch (error) {
8821
+ const routing = routingFailure(error);
8822
+ if (routing) return encErr(encryptionError(routing));
8584
8823
  return encErr(
8585
8824
  encryptionError({
8586
8825
  code: "TRANSPORT_ERROR",
@@ -8607,12 +8846,12 @@ var EncryptionService = class extends BaseService {
8607
8846
  descriptor = options.descriptor;
8608
8847
  } else {
8609
8848
  const discovered = await this.discoverNetwork(envelope.networkId);
8610
- if (!discovered.ok) return discovered;
8849
+ if (!discovered.ok) return encErr(decryptionError(discovered.error));
8611
8850
  descriptor = discovered.data;
8612
8851
  }
8613
8852
  const usable = ensureNetworkUsableForDecrypt(descriptor);
8614
8853
  if (!usable.ok) return usable;
8615
- const targetNode = options?.targetNode ?? descriptor.members[0]?.nodeId;
8854
+ const targetNode = this._config.resolveTargetNode ? await this._config.resolveTargetNode(envelope.networkId) : options?.targetNode ?? descriptor.members[0]?.nodeId;
8616
8855
  if (targetNode === void 0) {
8617
8856
  return encErr(
8618
8857
  encryptionError({
@@ -8621,6 +8860,9 @@ var EncryptionService = class extends BaseService {
8621
8860
  })
8622
8861
  );
8623
8862
  }
8863
+ if (this._config.resolveTargetNode && (options?.targetNode !== void 0 && options.targetNode !== targetNode || !descriptor.members.some((member) => member.nodeId === targetNode))) {
8864
+ return encErr(encryptionError({ code: "INVALID_INPUT", message: "Encryption destination does not match the registry" }));
8865
+ }
8624
8866
  const receiverKey = generateRandomReceiverKey({ crypto: this.crypto });
8625
8867
  const receiverPublicKey = base64Encode(receiverKey.publicKey);
8626
8868
  const receiverPublicKeyHash = canonicalHashHex(
@@ -8679,6 +8921,8 @@ var EncryptionService = class extends BaseService {
8679
8921
  });
8680
8922
  this.assertActive();
8681
8923
  } catch (error) {
8924
+ const routing = routingFailure(error);
8925
+ if (routing) return encErr(decryptionError(routing));
8682
8926
  if (error instanceof import_decrypt_transport_response_error.DecryptTransportResponseError) {
8683
8927
  return encErr(
8684
8928
  encryptionError(
@@ -8695,7 +8939,7 @@ var EncryptionService = class extends BaseService {
8695
8939
  );
8696
8940
  }
8697
8941
  return encErr(
8698
- encryptionError({
8942
+ decryptionError({
8699
8943
  code: "TRANSPORT_ERROR",
8700
8944
  cause: toError2(error)
8701
8945
  })
@@ -8722,6 +8966,8 @@ var EncryptionService = class extends BaseService {
8722
8966
  );
8723
8967
  return encOk(plaintext);
8724
8968
  } catch (error) {
8969
+ const routing = routingFailure(error);
8970
+ if (routing) return encErr(decryptionError(routing));
8725
8971
  return encErr(
8726
8972
  encryptionError({
8727
8973
  code: "INVALID_RESPONSE",
@@ -8771,7 +9017,11 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8771
9017
  MAX_SECRET_SCOPE_LENGTH,
8772
9018
  NETWORK_NAME_PATTERN,
8773
9019
  NetworkIdError,
9020
+ NodeIdentityMismatchError,
9021
+ NodeNotRespondingError,
9022
+ PartialNodeWriteError,
8774
9023
  PrefixedKVService,
9024
+ ResourceNotRoutedError,
8775
9025
  RetryPolicySchema,
8776
9026
  SECRET_NAME_RE,
8777
9027
  SECRET_SCOPE_RE,
@@ -8819,6 +9069,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8819
9069
  checkDecryptInvocationInput,
8820
9070
  clearTinyCloudDebugLogs,
8821
9071
  createKVResponseSchema,
9072
+ createResourceRoutingFetch,
8822
9073
  createResultSchema,
8823
9074
  createVaultCrypto,
8824
9075
  decryptEnvelopeWithKey,
@@ -8861,6 +9112,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8861
9112
  requiresSecretsOptIn,
8862
9113
  resolveSecretListPrefix,
8863
9114
  resolveSecretPath,
9115
+ routingFailure,
8864
9116
  serviceError,
8865
9117
  storageLimitReachedError,
8866
9118
  storageQuotaExceededError,
@@ -8881,6 +9133,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8881
9133
  validateTelemetrySpanEvent,
8882
9134
  validatedCapabilityOf,
8883
9135
  verifyDecryptResponse,
9136
+ verifyResourceDestination,
8884
9137
  wrapError
8885
9138
  });
8886
9139
  //# sourceMappingURL=index.cjs.map