@tinycloud/sdk-services 3.11.0 → 3.12.0-beta.4

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 +270 -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 +263 -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" };
@@ -6964,6 +7187,8 @@ var DataVaultService = class extends BaseService {
6964
7187
  if (this.usesNetworkEncryption) {
6965
7188
  const valueDelResult2 = await this.tc.kv.delete(`vault/${key}`);
6966
7189
  if (!valueDelResult2.ok) {
7190
+ const routing = routingFailure(valueDelResult2.error);
7191
+ if (routing) return vaultError(routing);
6967
7192
  return vaultError({ code: "KEY_NOT_FOUND", key });
6968
7193
  }
6969
7194
  return ok(void 0);
@@ -6973,10 +7198,14 @@ var DataVaultService = class extends BaseService {
6973
7198
  this.tc.kv.delete(`vault/${key}`)
6974
7199
  ]);
6975
7200
  if (!keyDelResult.ok && !valueDelResult.ok) {
7201
+ const routing = routingFailure(keyDelResult.error) ?? routingFailure(valueDelResult.error);
7202
+ if (routing) return vaultError(routing);
6976
7203
  return vaultError({ code: "KEY_NOT_FOUND", key });
6977
7204
  }
6978
7205
  return ok(void 0);
6979
7206
  } catch (error) {
7207
+ const routing = routingFailure(error);
7208
+ if (routing) return vaultError(routing);
6980
7209
  return vaultError({
6981
7210
  code: "STORAGE_ERROR",
6982
7211
  cause: toError(error)
@@ -7018,6 +7247,8 @@ var DataVaultService = class extends BaseService {
7018
7247
  ...options?.cursor === void 0 ? {} : { cursor: options.cursor }
7019
7248
  });
7020
7249
  if (!listResult.ok) {
7250
+ const routing = routingFailure(listResult.error);
7251
+ if (routing) return vaultError(routing);
7021
7252
  return vaultError({
7022
7253
  code: "STORAGE_ERROR",
7023
7254
  cause: new Error(
@@ -7038,6 +7269,8 @@ var DataVaultService = class extends BaseService {
7038
7269
  ...listResult.data.nextCursor === void 0 ? {} : { nextCursor: listResult.data.nextCursor }
7039
7270
  });
7040
7271
  } catch (error) {
7272
+ const routing = routingFailure(error);
7273
+ if (routing) return vaultError(routing);
7041
7274
  return vaultError({
7042
7275
  code: "STORAGE_ERROR",
7043
7276
  cause: toError(error)
@@ -7869,6 +8102,8 @@ function defaultEncryptionMessage(input) {
7869
8102
  return input.message ?? input.cause.message;
7870
8103
  case "INVALID_INPUT":
7871
8104
  return input.message;
8105
+ default:
8106
+ return input.message ?? input.code;
7872
8107
  }
7873
8108
  }
7874
8109
  function encryptionError(input) {
@@ -7933,6 +8168,8 @@ async function discoverNetwork(input) {
7933
8168
  return { ok: true, data: { descriptor: validated.data, source: "node" } };
7934
8169
  }
7935
8170
  } catch (err3) {
8171
+ const routing = routingFailure(err3);
8172
+ if (routing) return { ok: false, error: encryptionError(routing) };
7936
8173
  }
7937
8174
  }
7938
8175
  if (input.wellKnown !== void 0) {
@@ -7950,6 +8187,8 @@ async function discoverNetwork(input) {
7950
8187
  };
7951
8188
  }
7952
8189
  } catch (err3) {
8190
+ const routing = routingFailure(err3);
8191
+ if (routing) return { ok: false, error: encryptionError(routing) };
7953
8192
  }
7954
8193
  }
7955
8194
  return {
@@ -8534,6 +8773,14 @@ function encOk(data) {
8534
8773
  function encErr(error) {
8535
8774
  return { ok: false, error };
8536
8775
  }
8776
+ function decryptionError(input) {
8777
+ const unavailable = input.code === "NODE_NOT_RESPONDING" || input.code === "TRANSPORT_ERROR" && input.cause.name !== "AbortError";
8778
+ return encryptionError(unavailable ? {
8779
+ ...input,
8780
+ code: "DECRYPTION_DEPENDENCY_UNAVAILABLE",
8781
+ message: "The decryption service isn't responding right now. Please try again."
8782
+ } : input);
8783
+ }
8537
8784
  var EncryptionService = class extends BaseService {
8538
8785
  constructor(config) {
8539
8786
  super();
@@ -8581,6 +8828,8 @@ var EncryptionService = class extends BaseService {
8581
8828
  });
8582
8829
  return encOk(result.envelope);
8583
8830
  } catch (error) {
8831
+ const routing = routingFailure(error);
8832
+ if (routing) return encErr(encryptionError(routing));
8584
8833
  return encErr(
8585
8834
  encryptionError({
8586
8835
  code: "TRANSPORT_ERROR",
@@ -8607,12 +8856,12 @@ var EncryptionService = class extends BaseService {
8607
8856
  descriptor = options.descriptor;
8608
8857
  } else {
8609
8858
  const discovered = await this.discoverNetwork(envelope.networkId);
8610
- if (!discovered.ok) return discovered;
8859
+ if (!discovered.ok) return encErr(decryptionError(discovered.error));
8611
8860
  descriptor = discovered.data;
8612
8861
  }
8613
8862
  const usable = ensureNetworkUsableForDecrypt(descriptor);
8614
8863
  if (!usable.ok) return usable;
8615
- const targetNode = options?.targetNode ?? descriptor.members[0]?.nodeId;
8864
+ const targetNode = this._config.resolveTargetNode ? await this._config.resolveTargetNode(envelope.networkId) : options?.targetNode ?? descriptor.members[0]?.nodeId;
8616
8865
  if (targetNode === void 0) {
8617
8866
  return encErr(
8618
8867
  encryptionError({
@@ -8621,6 +8870,9 @@ var EncryptionService = class extends BaseService {
8621
8870
  })
8622
8871
  );
8623
8872
  }
8873
+ if (this._config.resolveTargetNode && (options?.targetNode !== void 0 && options.targetNode !== targetNode || !descriptor.members.some((member) => member.nodeId === targetNode))) {
8874
+ return encErr(encryptionError({ code: "INVALID_INPUT", message: "Encryption destination does not match the registry" }));
8875
+ }
8624
8876
  const receiverKey = generateRandomReceiverKey({ crypto: this.crypto });
8625
8877
  const receiverPublicKey = base64Encode(receiverKey.publicKey);
8626
8878
  const receiverPublicKeyHash = canonicalHashHex(
@@ -8679,6 +8931,8 @@ var EncryptionService = class extends BaseService {
8679
8931
  });
8680
8932
  this.assertActive();
8681
8933
  } catch (error) {
8934
+ const routing = routingFailure(error);
8935
+ if (routing) return encErr(decryptionError(routing));
8682
8936
  if (error instanceof import_decrypt_transport_response_error.DecryptTransportResponseError) {
8683
8937
  return encErr(
8684
8938
  encryptionError(
@@ -8695,7 +8949,7 @@ var EncryptionService = class extends BaseService {
8695
8949
  );
8696
8950
  }
8697
8951
  return encErr(
8698
- encryptionError({
8952
+ decryptionError({
8699
8953
  code: "TRANSPORT_ERROR",
8700
8954
  cause: toError2(error)
8701
8955
  })
@@ -8722,6 +8976,8 @@ var EncryptionService = class extends BaseService {
8722
8976
  );
8723
8977
  return encOk(plaintext);
8724
8978
  } catch (error) {
8979
+ const routing = routingFailure(error);
8980
+ if (routing) return encErr(decryptionError(routing));
8725
8981
  return encErr(
8726
8982
  encryptionError({
8727
8983
  code: "INVALID_RESPONSE",
@@ -8771,7 +9027,11 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8771
9027
  MAX_SECRET_SCOPE_LENGTH,
8772
9028
  NETWORK_NAME_PATTERN,
8773
9029
  NetworkIdError,
9030
+ NodeIdentityMismatchError,
9031
+ NodeNotRespondingError,
9032
+ PartialNodeWriteError,
8774
9033
  PrefixedKVService,
9034
+ ResourceNotRoutedError,
8775
9035
  RetryPolicySchema,
8776
9036
  SECRET_NAME_RE,
8777
9037
  SECRET_SCOPE_RE,
@@ -8819,6 +9079,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8819
9079
  checkDecryptInvocationInput,
8820
9080
  clearTinyCloudDebugLogs,
8821
9081
  createKVResponseSchema,
9082
+ createResourceRoutingFetch,
8822
9083
  createResultSchema,
8823
9084
  createVaultCrypto,
8824
9085
  decryptEnvelopeWithKey,
@@ -8861,6 +9122,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8861
9122
  requiresSecretsOptIn,
8862
9123
  resolveSecretListPrefix,
8863
9124
  resolveSecretPath,
9125
+ routingFailure,
8864
9126
  serviceError,
8865
9127
  storageLimitReachedError,
8866
9128
  storageQuotaExceededError,
@@ -8881,6 +9143,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
8881
9143
  validateTelemetrySpanEvent,
8882
9144
  validatedCapabilityOf,
8883
9145
  verifyDecryptResponse,
9146
+ verifyResourceDestination,
8884
9147
  wrapError
8885
9148
  });
8886
9149
  //# sourceMappingURL=index.cjs.map