@tinycloud/sdk-services 2.6.3 → 2.7.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 (35) hide show
  1. package/dist/BaseService-BwBU5CQ4.d.cts +158 -0
  2. package/dist/BaseService-CnPTKR5G.d.ts +158 -0
  3. package/dist/encryption/index.cjs +46 -6
  4. package/dist/encryption/index.cjs.map +1 -1
  5. package/dist/encryption/index.d.cts +9 -1
  6. package/dist/encryption/index.d.ts +9 -1
  7. package/dist/encryption/index.js +45 -6
  8. package/dist/encryption/index.js.map +1 -1
  9. package/dist/index.cjs +299 -21
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +58 -9
  12. package/dist/index.d.ts +58 -9
  13. package/dist/index.js +293 -18
  14. package/dist/index.js.map +1 -1
  15. package/dist/internal/decrypt-transport-response-error.cjs +38 -0
  16. package/dist/internal/decrypt-transport-response-error.cjs.map +1 -0
  17. package/dist/internal/decrypt-transport-response-error.d.cts +10 -0
  18. package/dist/internal/decrypt-transport-response-error.d.ts +10 -0
  19. package/dist/internal/decrypt-transport-response-error.js +13 -0
  20. package/dist/internal/decrypt-transport-response-error.js.map +1 -0
  21. package/dist/kv/index.cjs +46 -4
  22. package/dist/kv/index.cjs.map +1 -1
  23. package/dist/kv/index.d.cts +2 -1
  24. package/dist/kv/index.d.ts +2 -1
  25. package/dist/kv/index.js +46 -4
  26. package/dist/kv/index.js.map +1 -1
  27. package/dist/sql/index.cjs +6 -2
  28. package/dist/sql/index.cjs.map +1 -1
  29. package/dist/sql/index.d.cts +2 -1
  30. package/dist/sql/index.d.ts +2 -1
  31. package/dist/sql/index.js +6 -2
  32. package/dist/sql/index.js.map +1 -1
  33. package/dist/{BaseService-BdRvNm2s.d.cts → types-DAY1O1kW.d.cts} +14 -156
  34. package/dist/{BaseService-BdRvNm2s.d.ts → types-DAY1O1kW.d.ts} +14 -156
  35. package/package.json +11 -3
package/dist/index.cjs CHANGED
@@ -18,8 +18,8 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
23
  BaseService: () => BaseService,
24
24
  DECRYPT_ACTION: () => DECRYPT_ACTION,
25
25
  DECRYPT_FACT_TYPE: () => DECRYPT_FACT_TYPE,
@@ -29,6 +29,7 @@ __export(index_exports, {
29
29
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS: () => DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
30
30
  DataVaultService: () => DataVaultService,
31
31
  DatabaseHandle: () => DatabaseHandle,
32
+ DecryptTransportResponseError: () => import_decrypt_transport_response_error3.DecryptTransportResponseError,
32
33
  DuckDbAction: () => DuckDbAction,
33
34
  DuckDbDatabaseHandle: () => DuckDbDatabaseHandle,
34
35
  DuckDbService: () => DuckDbService,
@@ -112,6 +113,8 @@ __export(index_exports, {
112
113
  openWrappedKey: () => openWrappedKey,
113
114
  parseAuthError: () => parseAuthError,
114
115
  parseNetworkId: () => parseNetworkId,
116
+ parsePermissionHint: () => parsePermissionHint,
117
+ parsePermissionHintFromErrorText: () => parsePermissionHintFromErrorText,
115
118
  permissionDeniedError: () => permissionDeniedError,
116
119
  resolveSecretListPrefix: () => resolveSecretListPrefix,
117
120
  resolveSecretPath: () => resolveSecretPath,
@@ -134,7 +137,7 @@ __export(index_exports, {
134
137
  verifyDecryptResponse: () => verifyDecryptResponse,
135
138
  wrapError: () => wrapError
136
139
  });
137
- module.exports = __toCommonJS(index_exports);
140
+ module.exports = __toCommonJS(src_exports);
138
141
 
139
142
  // src/types.ts
140
143
  var ErrorCodes = {
@@ -205,6 +208,63 @@ function serviceError(code, message, service, options) {
205
208
  };
206
209
  }
207
210
 
211
+ // src/diagnostics.ts
212
+ var REDACTED = "[REDACTED]";
213
+ var SAFE_NUMBER_FIELDS = /* @__PURE__ */ new Set([
214
+ "duration",
215
+ "durationMs",
216
+ "endedAt",
217
+ "startedAt",
218
+ "status",
219
+ "timestamp"
220
+ ]);
221
+ var SAFE_BOOLEAN_FIELDS = /* @__PURE__ */ new Set(["authenticated", "ok", "persisted"]);
222
+ function finiteNumber(value) {
223
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
224
+ }
225
+ function isBinaryData(value) {
226
+ try {
227
+ return Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
228
+ } catch {
229
+ return true;
230
+ }
231
+ }
232
+ function read(value, key) {
233
+ try {
234
+ return value[key];
235
+ } catch {
236
+ return REDACTED;
237
+ }
238
+ }
239
+ function projectDiagnosticError(error) {
240
+ if (typeof error !== "object" || error === null) return {};
241
+ const status = finiteNumber(read(error, "status"));
242
+ return status !== void 0 && status >= 100 && status <= 599 ? { status } : {};
243
+ }
244
+ function projectDiagnosticData(value) {
245
+ if (value === null || value === void 0) return value;
246
+ if (typeof value !== "object") return REDACTED;
247
+ try {
248
+ if (isBinaryData(value)) return REDACTED;
249
+ const projected = {};
250
+ for (const key of SAFE_NUMBER_FIELDS) {
251
+ const number = finiteNumber(read(value, key));
252
+ if (number !== void 0) projected[key] = number;
253
+ }
254
+ for (const key of SAFE_BOOLEAN_FIELDS) {
255
+ const boolean = read(value, key);
256
+ if (typeof boolean === "boolean") projected[key] = boolean;
257
+ }
258
+ const url = read(value, "url");
259
+ if (url !== void 0) projected.url = REDACTED;
260
+ const error = read(value, "error");
261
+ if (error !== void 0) projected.error = projectDiagnosticError(error);
262
+ return projected;
263
+ } catch {
264
+ return REDACTED;
265
+ }
266
+ }
267
+
208
268
  // src/debug.ts
209
269
  var DEBUG_FLAG = "TinyCloud_debug";
210
270
  var MAX_EVENTS = 1e3;
@@ -363,6 +423,8 @@ var TinyCloudDebugLogger = class {
363
423
  record(event) {
364
424
  const debugEvent = {
365
425
  ...event,
426
+ ...event.data === void 0 ? {} : { data: projectDiagnosticData(event.data) },
427
+ ...event.message === void 0 ? {} : { message: "[REDACTED]" },
366
428
  sequence: ++this.sequence
367
429
  };
368
430
  this.events.push(debugEvent);
@@ -642,6 +704,7 @@ var ServiceContext = class {
642
704
  this._services = /* @__PURE__ */ new Map();
643
705
  this._eventHandlers = /* @__PURE__ */ new Map();
644
706
  this._abortController = new AbortController();
707
+ this._retired = false;
645
708
  this._invoke = this.wrapInvoke(config.invoke);
646
709
  this._invokeAny = config.invokeAny ? this.wrapInvokeAny(config.invokeAny) : void 0;
647
710
  this._fetch = this.wrapFetch(config.fetch ?? globalThis.fetch.bind(globalThis));
@@ -661,13 +724,13 @@ var ServiceContext = class {
661
724
  * Get the current session.
662
725
  */
663
726
  get session() {
664
- return this._session;
727
+ return this._retired ? null : this._session;
665
728
  }
666
729
  /**
667
730
  * Check if the context has an authenticated session.
668
731
  */
669
732
  get isAuthenticated() {
670
- return this._session !== null;
733
+ return !this._retired && this._session !== null;
671
734
  }
672
735
  /**
673
736
  * Update the session and notify all registered services.
@@ -675,6 +738,7 @@ var ServiceContext = class {
675
738
  * @param session - New session or null to clear
676
739
  */
677
740
  setSession(session) {
741
+ this.assertActive();
678
742
  this._session = session;
679
743
  this.emit("session.changed", { authenticated: session !== null });
680
744
  for (const service of this._services.values()) {
@@ -688,24 +752,28 @@ var ServiceContext = class {
688
752
  * Get the invoke function for WASM operations.
689
753
  */
690
754
  get invoke() {
755
+ this.assertActive();
691
756
  return this._invoke;
692
757
  }
693
758
  /**
694
759
  * Get the multi-resource invoke function when available.
695
760
  */
696
761
  get invokeAny() {
762
+ this.assertActive();
697
763
  return this._invokeAny;
698
764
  }
699
765
  /**
700
766
  * Get the fetch function for HTTP requests.
701
767
  */
702
768
  get fetch() {
769
+ this.assertActive();
703
770
  return this._fetch;
704
771
  }
705
772
  /**
706
773
  * Get the list of TinyCloud host URLs.
707
774
  */
708
775
  get hosts() {
776
+ this.assertActive();
709
777
  return this._hosts;
710
778
  }
711
779
  // ============================================================
@@ -750,7 +818,7 @@ var ServiceContext = class {
750
818
  tinyCloudDebugLogger.log(event, data);
751
819
  if (this._telemetryEnabled && this._telemetryHandler) {
752
820
  try {
753
- this._telemetryHandler(event, data);
821
+ this._telemetryHandler(event, projectDiagnosticData(data));
754
822
  } catch (error) {
755
823
  console.error(`Error in telemetry handler for "${event}":`, error);
756
824
  }
@@ -809,21 +877,45 @@ var ServiceContext = class {
809
877
  get abortSignal() {
810
878
  return this._abortController.signal;
811
879
  }
880
+ /**
881
+ * Permanently retire this graph after its owner installs a replacement.
882
+ * Unlike `abort()`, retirement never creates a fresh controller, so captured
883
+ * services cannot resume calls under a superseded session.
884
+ */
885
+ retire() {
886
+ if (this._retired) return;
887
+ this._retired = true;
888
+ this._session = null;
889
+ this._abortController.abort();
890
+ for (const service of this._services.values()) {
891
+ try {
892
+ service.onSignOut();
893
+ } catch (error) {
894
+ console.error("Error retiring service:", error);
895
+ }
896
+ }
897
+ }
812
898
  /**
813
899
  * Abort all pending operations and notify services.
814
900
  * Creates a new AbortController for future operations.
815
901
  */
816
902
  abort() {
903
+ if (this._retired) return;
817
904
  this._abortController.abort();
818
905
  this._abortController = new AbortController();
819
906
  for (const service of this._services.values()) {
820
- service.onSignOut();
907
+ try {
908
+ service.onSignOut();
909
+ } catch (error) {
910
+ console.error("Error signing out service:", error);
911
+ }
821
912
  }
822
913
  }
823
914
  /**
824
915
  * Sign out - abort operations and clear session.
825
916
  */
826
917
  signOut() {
918
+ if (this._retired) return;
827
919
  this.abort();
828
920
  this.setSession(null);
829
921
  this.emit("session.expired", {});
@@ -839,6 +931,7 @@ var ServiceContext = class {
839
931
  }
840
932
  wrapInvoke(invoke) {
841
933
  return (session, service, path, action, facts) => {
934
+ this.assertActive();
842
935
  if (!tinyCloudDebugLogger.isEnabled()) {
843
936
  return invoke(session, service, path, action, facts);
844
937
  }
@@ -858,8 +951,14 @@ var ServiceContext = class {
858
951
  }
859
952
  };
860
953
  }
954
+ assertActive() {
955
+ if (this._retired) {
956
+ throw new Error("Service graph has been retired by session replacement.");
957
+ }
958
+ }
861
959
  wrapInvokeAny(invokeAny) {
862
960
  return (session, entries, facts) => {
961
+ this.assertActive();
863
962
  if (!tinyCloudDebugLogger.isEnabled()) {
864
963
  return invokeAny(session, entries, facts);
865
964
  }
@@ -879,20 +978,25 @@ var ServiceContext = class {
879
978
  }
880
979
  wrapFetch(fetchFn) {
881
980
  return async (url, init) => {
981
+ this.assertActive();
982
+ const request = {
983
+ ...init,
984
+ signal: this.combineSignals(init?.signal)
985
+ };
882
986
  if (!tinyCloudDebugLogger.isEnabled()) {
883
- return fetchFn(url, init);
987
+ return fetchFn(url, request);
884
988
  }
885
989
  const timer = tinyCloudDebugLogger.startTimer("sdk.fetch", {
886
990
  url,
887
- method: init?.method ?? "GET",
888
- init
991
+ method: request.method ?? "GET",
992
+ init: request
889
993
  });
890
994
  try {
891
- const response = await fetchFn(url, init);
995
+ const response = await fetchFn(url, request);
892
996
  timer.stop({
893
997
  ok: response.ok,
894
998
  url,
895
- method: init?.method ?? "GET",
999
+ method: request.method ?? "GET",
896
1000
  status: response.status,
897
1001
  statusText: response.statusText
898
1002
  });
@@ -901,16 +1005,70 @@ var ServiceContext = class {
901
1005
  timer.stop({
902
1006
  ok: false,
903
1007
  url,
904
- method: init?.method ?? "GET",
1008
+ method: request.method ?? "GET",
905
1009
  error
906
1010
  });
907
1011
  throw error;
908
1012
  }
909
1013
  };
910
1014
  }
1015
+ combineSignals(signal) {
1016
+ if (!signal) return this._abortController.signal;
1017
+ const combined = new AbortController();
1018
+ const abort = (source) => combined.abort(source.reason);
1019
+ if (signal.aborted) {
1020
+ abort(signal);
1021
+ } else if (this._abortController.signal.aborted) {
1022
+ abort(this._abortController.signal);
1023
+ } else {
1024
+ signal.addEventListener("abort", () => abort(signal), { once: true });
1025
+ this._abortController.signal.addEventListener(
1026
+ "abort",
1027
+ () => abort(this._abortController.signal),
1028
+ { once: true }
1029
+ );
1030
+ }
1031
+ return combined.signal;
1032
+ }
911
1033
  };
912
1034
 
913
1035
  // src/errors.ts
1036
+ function parsePermissionHint(value) {
1037
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1038
+ const candidate = value;
1039
+ const keys = Object.keys(candidate).sort();
1040
+ if (keys.some((key) => !["actions", "path", "service", "space"].includes(key))) return void 0;
1041
+ if (candidate.service !== "tinycloud.kv" && candidate.service !== "tinycloud.encryption") return void 0;
1042
+ if (typeof candidate.path !== "string" || candidate.path.length === 0 || candidate.path.includes("*")) return void 0;
1043
+ if (candidate.service === "tinycloud.kv" && (typeof candidate.space !== "string" || candidate.space.length === 0 || !candidate.path.startsWith("vault/") || candidate.path.endsWith("/"))) {
1044
+ return void 0;
1045
+ }
1046
+ if (candidate.service === "tinycloud.encryption" && (candidate.space !== void 0 || !candidate.path.startsWith("urn:tinycloud:encryption:") || candidate.path.endsWith(":"))) {
1047
+ return void 0;
1048
+ }
1049
+ if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 || typeof candidate.actions[0] !== "string" || candidate.actions[0].includes("*")) return void 0;
1050
+ const expectedAction = candidate.service === "tinycloud.kv" ? "tinycloud.kv/get" : "tinycloud.encryption/decrypt";
1051
+ if (candidate.actions[0] !== expectedAction) return void 0;
1052
+ const space = typeof candidate.space === "string" ? candidate.space : void 0;
1053
+ return {
1054
+ service: candidate.service,
1055
+ ...space === void 0 ? {} : { space },
1056
+ path: candidate.path,
1057
+ actions: [candidate.actions[0]]
1058
+ };
1059
+ }
1060
+ function parsePermissionHintFromErrorText(text) {
1061
+ try {
1062
+ const parsed = JSON.parse(text);
1063
+ if (typeof parsed !== "object" || parsed === null) return void 0;
1064
+ const root = parsed;
1065
+ const nested = root.error;
1066
+ const nestedRecord = typeof nested === "object" && nested !== null ? nested : void 0;
1067
+ return parsePermissionHint(root.permissionHint) ?? parsePermissionHint(nestedRecord?.permissionHint);
1068
+ } catch {
1069
+ return void 0;
1070
+ }
1071
+ }
914
1072
  function authRequiredError(service) {
915
1073
  return {
916
1074
  code: ErrorCodes.AUTH_REQUIRED,
@@ -1061,7 +1219,7 @@ var BaseService = class {
1061
1219
  * Combines the service-level abort with context-level abort.
1062
1220
  */
1063
1221
  get abortSignal() {
1064
- return this.abortController.signal;
1222
+ return this.combineSignals();
1065
1223
  }
1066
1224
  /**
1067
1225
  * Check if the service is authenticated.
@@ -1177,7 +1335,11 @@ var BaseService = class {
1177
1335
  */
1178
1336
  combineSignals(...signals) {
1179
1337
  const controller = new AbortController();
1180
- const allSignals = [this.abortSignal, ...signals.filter(Boolean)];
1338
+ const allSignals = [
1339
+ this.abortController.signal,
1340
+ this.context?.abortSignal,
1341
+ ...signals.filter(Boolean)
1342
+ ].filter(Boolean);
1181
1343
  for (const signal of allSignals) {
1182
1344
  if (signal.aborted) {
1183
1345
  controller.abort(signal.reason);
@@ -1650,13 +1812,15 @@ var KVService = class extends BaseService {
1650
1812
  options?.signal
1651
1813
  );
1652
1814
  if (!response.ok) {
1653
- if (response.status === 401) {
1815
+ if (response.status === 401 || response.status === 403) {
1654
1816
  const errorText2 = await response.text();
1655
1817
  const { resource, action } = parseAuthError(errorText2);
1818
+ const permissionHint = parsePermissionHintFromErrorText(errorText2);
1656
1819
  return err(authUnauthorizedError("kv", errorText2, {
1657
1820
  status: response.status,
1658
1821
  ...action && { requiredAction: action },
1659
- ...resource && { resource }
1822
+ ...resource && { resource },
1823
+ ...permissionHint === void 0 ? {} : { permissionHint }
1660
1824
  }));
1661
1825
  }
1662
1826
  if (response.status === 404) {
@@ -3882,6 +4046,9 @@ function unwrapKVData(value) {
3882
4046
  }
3883
4047
  return value;
3884
4048
  }
4049
+ function hasHttpResponse(error) {
4050
+ return typeof error.meta?.status === "number";
4051
+ }
3885
4052
  function isUnlockSigner(signer) {
3886
4053
  return typeof signer === "object" && signer !== null && typeof signer.signMessage === "function";
3887
4054
  }
@@ -4255,6 +4422,80 @@ var DataVaultService = class extends BaseService {
4255
4422
  });
4256
4423
  }
4257
4424
  }
4425
+ /**
4426
+ * Read a network-encrypted entry while preserving safe failure phases.
4427
+ *
4428
+ * Unlike {@link getNetworkEncrypted}, this is intentionally message-free so
4429
+ * an operation layer can distinguish an authorized absence from a failed
4430
+ * read without receiving node, envelope, plaintext, or key material.
4431
+ */
4432
+ async readNetworkEncrypted(key, options) {
4433
+ const config = this.networkEncryption;
4434
+ if (!config || !this.requireAuth()) {
4435
+ return { status: "read_failed" };
4436
+ }
4437
+ let valueResult;
4438
+ try {
4439
+ valueResult = await this.tc.kv.get(`vault/${key}`, { raw: true });
4440
+ } catch {
4441
+ return { status: "node_unreachable" };
4442
+ }
4443
+ if (!valueResult.ok) {
4444
+ const permissionHint = parsePermissionHint(valueResult.error.meta?.permissionHint);
4445
+ if (valueResult.error.code === "AUTH_UNAUTHORIZED" && permissionHint !== void 0) {
4446
+ return { status: "permission_required", hint: permissionHint };
4447
+ }
4448
+ if ((valueResult.error.code === "NOT_FOUND" || valueResult.error.code === "KV_NOT_FOUND") && !hasHttpResponse(valueResult.error)) {
4449
+ return { status: "not_found" };
4450
+ }
4451
+ if (valueResult.error.code === "NETWORK_ERROR" || valueResult.error.code === "TIMEOUT" || valueResult.error.code === "ABORTED") {
4452
+ return hasHttpResponse(valueResult.error) ? { status: "read_failed" } : { status: "node_unreachable" };
4453
+ }
4454
+ return { status: "read_failed" };
4455
+ }
4456
+ let envelope;
4457
+ try {
4458
+ const rawEnvelope = unwrapKVData(valueResult.data);
4459
+ envelope = typeof rawEnvelope === "string" ? JSON.parse(rawEnvelope) : rawEnvelope;
4460
+ } catch {
4461
+ return { status: "corrupt_envelope" };
4462
+ }
4463
+ let plaintextResult;
4464
+ try {
4465
+ const proof = await this.decryptCapabilityProof();
4466
+ plaintextResult = await config.service.decryptEnvelope(envelope, proof);
4467
+ } catch {
4468
+ return { status: "decrypt_failed" };
4469
+ }
4470
+ if (!plaintextResult.ok) {
4471
+ const permissionHint = parsePermissionHint(
4472
+ plaintextResult.error.permissionHint
4473
+ );
4474
+ if (plaintextResult.error.code === "DECRYPT_DENIED" && permissionHint !== void 0) {
4475
+ return { status: "permission_required", hint: permissionHint };
4476
+ }
4477
+ if (plaintextResult.error.code === "INVALID_ENVELOPE") {
4478
+ return { status: "corrupt_envelope" };
4479
+ }
4480
+ if (plaintextResult.error.code === "TRANSPORT_ERROR") {
4481
+ return { status: "node_unreachable" };
4482
+ }
4483
+ return { status: "decrypt_failed" };
4484
+ }
4485
+ try {
4486
+ const metadata = envelope.metadata ?? {};
4487
+ const contentType = metadata[VaultHeaders.CONTENT_TYPE] ?? "application/json";
4488
+ const keyId = metadata[VaultHeaders.KEY_ID] ?? envelope.encryptedSymmetricKeyHash.slice(0, 16);
4489
+ const value = this.deserializeValue(
4490
+ plaintextResult.data,
4491
+ contentType,
4492
+ options
4493
+ );
4494
+ return { status: "ok", entry: { value, metadata, keyId } };
4495
+ } catch {
4496
+ return { status: "invalid_payload" };
4497
+ }
4498
+ }
4258
4499
  async headNetworkEncrypted(key) {
4259
4500
  if (!this.requireAuth()) {
4260
4501
  return vaultError({
@@ -6082,6 +6323,8 @@ function openWrappedKey(crypto2, receiverPrivateKey, response) {
6082
6323
  }
6083
6324
 
6084
6325
  // src/encryption/EncryptionService.ts
6326
+ var import_decrypt_transport_response_error = require("@tinycloud/sdk-services/internal/decrypt-transport-response-error");
6327
+ var import_decrypt_transport_response_error2 = require("@tinycloud/sdk-services/internal/decrypt-transport-response-error");
6085
6328
  function encOk(data) {
6086
6329
  return { ok: true, data };
6087
6330
  }
@@ -6100,17 +6343,20 @@ var EncryptionService = class extends BaseService {
6100
6343
  return this._config.crypto;
6101
6344
  }
6102
6345
  async discoverNetwork(identifier, ownerDid) {
6346
+ this.assertActive();
6103
6347
  const result = await discoverNetwork({
6104
6348
  identifier,
6105
6349
  ...ownerDid !== void 0 ? { ownerDid } : {},
6106
6350
  ...this._config.node !== void 0 ? { node: this._config.node } : {},
6107
6351
  ...this._config.wellKnown !== void 0 ? { wellKnown: this._config.wellKnown } : {}
6108
6352
  });
6353
+ this.assertActive();
6109
6354
  if (!result.ok) return result;
6110
6355
  return encOk(result.data.descriptor);
6111
6356
  }
6112
6357
  async encryptToNetwork(networkId, plaintext, options) {
6113
6358
  try {
6359
+ this.assertActive();
6114
6360
  const discovered = await this.discoverNetwork(networkId);
6115
6361
  if (!discovered.ok) return discovered;
6116
6362
  const usable = ensureNetworkUsableForDecrypt(discovered.data);
@@ -6138,6 +6384,7 @@ var EncryptionService = class extends BaseService {
6138
6384
  }
6139
6385
  async decryptEnvelope(envelope, capabilityProof, options) {
6140
6386
  try {
6387
+ this.assertActive();
6141
6388
  const validated = validateEnvelope(this.crypto, envelope);
6142
6389
  if (!validated.ok) return validated;
6143
6390
  if (options?.aad !== void 0 && validated.data.aad !== base64Encode2(options.aad)) {
@@ -6203,16 +6450,38 @@ var EncryptionService = class extends BaseService {
6203
6450
  facts,
6204
6451
  proof: capabilityProof
6205
6452
  });
6206
- if (!built.ok) return built;
6453
+ this.assertActive();
6454
+ if (!built.ok) {
6455
+ if (built.error.code !== "TRANSPORT_ERROR") return built;
6456
+ return encErr(
6457
+ encryptionError({
6458
+ code: "INVALID_INPUT",
6459
+ message: "Unable to build decrypt request"
6460
+ })
6461
+ );
6462
+ }
6207
6463
  let response;
6208
6464
  try {
6209
6465
  response = await this._config.transport.postDecrypt({
6210
6466
  targetNode,
6211
6467
  networkId: envelope.networkId,
6212
6468
  authorization: built.data.authorization,
6213
- canonicalBody: built.data.canonicalBody
6469
+ canonicalBody: built.data.canonicalBody,
6470
+ signal: this.abortSignal
6214
6471
  });
6472
+ this.assertActive();
6215
6473
  } catch (error) {
6474
+ if (error instanceof import_decrypt_transport_response_error.DecryptTransportResponseError) {
6475
+ return encErr(
6476
+ encryptionError(
6477
+ error.status === 401 || error.status === 403 ? {
6478
+ code: "DECRYPT_DENIED",
6479
+ message: "Node denied decrypt request",
6480
+ ...error.permissionHint === void 0 ? {} : { permissionHint: error.permissionHint }
6481
+ } : { code: "INVALID_RESPONSE", message: "Node decrypt request failed" }
6482
+ )
6483
+ );
6484
+ }
6216
6485
  return encErr(
6217
6486
  encryptionError({
6218
6487
  code: "TRANSPORT_ERROR",
@@ -6243,14 +6512,20 @@ var EncryptionService = class extends BaseService {
6243
6512
  } catch (error) {
6244
6513
  return encErr(
6245
6514
  encryptionError({
6246
- code: "TRANSPORT_ERROR",
6247
- cause: toError2(error)
6515
+ code: "INVALID_RESPONSE",
6516
+ message: "Local decryption failed"
6248
6517
  })
6249
6518
  );
6250
6519
  }
6251
6520
  }
6521
+ assertActive() {
6522
+ this._config.assertActive?.();
6523
+ }
6252
6524
  };
6253
6525
  EncryptionService.serviceName = "encryption";
6526
+
6527
+ // src/encryption/index.ts
6528
+ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/internal/decrypt-transport-response-error");
6254
6529
  // Annotate the CommonJS export names for ESM import in node:
6255
6530
  0 && (module.exports = {
6256
6531
  BaseService,
@@ -6262,6 +6537,7 @@ EncryptionService.serviceName = "encryption";
6262
6537
  DEFAULT_SIGNED_READ_URL_EXPIRY_MS,
6263
6538
  DataVaultService,
6264
6539
  DatabaseHandle,
6540
+ DecryptTransportResponseError,
6265
6541
  DuckDbAction,
6266
6542
  DuckDbDatabaseHandle,
6267
6543
  DuckDbService,
@@ -6345,6 +6621,8 @@ EncryptionService.serviceName = "encryption";
6345
6621
  openWrappedKey,
6346
6622
  parseAuthError,
6347
6623
  parseNetworkId,
6624
+ parsePermissionHint,
6625
+ parsePermissionHintFromErrorText,
6348
6626
  permissionDeniedError,
6349
6627
  resolveSecretListPrefix,
6350
6628
  resolveSecretPath,