@tinycloud/sdk-services 3.1.0-beta.2 → 3.1.0-beta.7

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.js CHANGED
@@ -896,6 +896,106 @@ var ServiceContext = class {
896
896
  }
897
897
  };
898
898
 
899
+ // src/errors.ts
900
+ import { CAPABILITY_REGISTRY } from "@tinycloud/bootstrap";
901
+
902
+ // src/encryption/networkId.ts
903
+ var URN_PREFIX = "urn:tinycloud:encryption:";
904
+ var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
905
+ var PKH_EIP155_DID_RE = /^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/;
906
+ var NetworkIdError = class extends Error {
907
+ constructor(message) {
908
+ super(message);
909
+ this.name = "NetworkIdError";
910
+ }
911
+ };
912
+ function parseNetworkId(networkId) {
913
+ if (typeof networkId !== "string" || networkId.length === 0) {
914
+ throw new NetworkIdError("networkId must be a non-empty string");
915
+ }
916
+ if (!networkId.startsWith(URN_PREFIX)) {
917
+ throw new NetworkIdError(
918
+ `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
919
+ );
920
+ }
921
+ const body = networkId.slice(URN_PREFIX.length);
922
+ const lastColon = body.lastIndexOf(":");
923
+ if (lastColon <= 0 || lastColon === body.length - 1) {
924
+ throw new NetworkIdError(
925
+ `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`
926
+ );
927
+ }
928
+ const ownerDid = body.slice(0, lastColon);
929
+ const name = body.slice(lastColon + 1);
930
+ if (!ownerDid.startsWith("did:")) {
931
+ throw new NetworkIdError(
932
+ `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`
933
+ );
934
+ }
935
+ const didParts = ownerDid.split(":");
936
+ if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
937
+ throw new NetworkIdError(
938
+ `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`
939
+ );
940
+ }
941
+ if (!NETWORK_NAME_RE.test(name)) {
942
+ throw new NetworkIdError(
943
+ `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
944
+ );
945
+ }
946
+ return { networkId, ownerDid, name };
947
+ }
948
+ function buildNetworkId(ownerDid, name) {
949
+ if (typeof ownerDid !== "string" || !ownerDid.startsWith("did:")) {
950
+ throw new NetworkIdError("ownerDid must be a DID");
951
+ }
952
+ if (typeof name !== "string" || !NETWORK_NAME_RE.test(name)) {
953
+ throw new NetworkIdError(
954
+ `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
955
+ );
956
+ }
957
+ const networkId = `${URN_PREFIX}${ownerDid}:${name}`;
958
+ parseNetworkId(networkId);
959
+ return networkId;
960
+ }
961
+ function isNetworkId(networkId) {
962
+ if (typeof networkId !== "string") {
963
+ return false;
964
+ }
965
+ try {
966
+ parseNetworkId(networkId);
967
+ return true;
968
+ } catch {
969
+ return false;
970
+ }
971
+ }
972
+ function parsePkhOwnerDid(ownerDid) {
973
+ const match = ownerDid.match(PKH_EIP155_DID_RE);
974
+ if (!match) return null;
975
+ return {
976
+ chainId: match[1],
977
+ address: match[2].toLowerCase()
978
+ };
979
+ }
980
+ function ownerDidMatches(a, b) {
981
+ const aPkh = parsePkhOwnerDid(a);
982
+ const bPkh = parsePkhOwnerDid(b);
983
+ if (aPkh && bPkh) {
984
+ return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;
985
+ }
986
+ return a === b;
987
+ }
988
+ function networkDiscoveryKey(name) {
989
+ if (!NETWORK_NAME_RE.test(name)) {
990
+ throw new NetworkIdError(
991
+ `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
992
+ );
993
+ }
994
+ return `.well-known/encryption/network/${name}`;
995
+ }
996
+ var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
997
+ var NETWORK_NAME_PATTERN = NETWORK_NAME_RE;
998
+
899
999
  // src/errors.ts
900
1000
  function parsePermissionHint(value) {
901
1001
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
@@ -955,11 +1055,31 @@ function networkError(service, message, cause) {
955
1055
  cause
956
1056
  };
957
1057
  }
958
- function timeoutError(service) {
1058
+ var RequestTimeoutError = class extends Error {
1059
+ constructor(timeoutMs) {
1060
+ super(`Request timed out after ${timeoutMs}ms.`);
1061
+ this.name = "TimeoutError";
1062
+ this.timeoutMs = timeoutMs;
1063
+ }
1064
+ };
1065
+ function isRequestTimeoutError(error) {
1066
+ return error instanceof RequestTimeoutError || error instanceof Error && error.name === "TimeoutError" && typeof error.timeoutMs === "number";
1067
+ }
1068
+ function timeoutError(service, timeoutMs, cause) {
1069
+ if (timeoutMs === void 0) {
1070
+ return {
1071
+ code: ErrorCodes.TIMEOUT,
1072
+ message: "Request timed out.",
1073
+ service,
1074
+ ...cause === void 0 ? {} : { cause }
1075
+ };
1076
+ }
959
1077
  return {
960
1078
  code: ErrorCodes.TIMEOUT,
961
- message: "Request timed out.",
962
- service
1079
+ message: `Request timed out after ${timeoutMs}ms.`,
1080
+ service,
1081
+ ...cause === void 0 ? {} : { cause },
1082
+ meta: { timeoutMs }
963
1083
  };
964
1084
  }
965
1085
  function abortedError(service) {
@@ -993,6 +1113,70 @@ function parseAuthError(responseText) {
993
1113
  function authUnauthorizedError(service, message, meta) {
994
1114
  return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });
995
1115
  }
1116
+ var grantableActions = new Set(
1117
+ CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith("/*"))
1118
+ );
1119
+ var capabilityServices = new Set(
1120
+ CAPABILITY_REGISTRY.map(({ service }) => service.slice("tinycloud.".length))
1121
+ );
1122
+ function parseCapabilityResource(resource, service) {
1123
+ if (resource.length === 0 || resource.length > 1024 || !capabilityServices.has(service) || /[\s\p{C}*#]/u.test(resource) || resource.includes("://")) return void 0;
1124
+ const segments = resource.split("/");
1125
+ if (!/^tinycloud:[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(segments[0])) return void 0;
1126
+ const serviceIndex = segments.findIndex(
1127
+ (segment, index) => index > 0 && capabilityServices.has(segment)
1128
+ );
1129
+ if (serviceIndex < 1 || segments[serviceIndex] !== service) return void 0;
1130
+ if (segments.slice(1, serviceIndex).some(
1131
+ (segment) => segment.length === 0 || segment === "." || segment === ".."
1132
+ )) return void 0;
1133
+ const pathSegments = segments.slice(serviceIndex + 1);
1134
+ if (pathSegments.some((segment) => segment === "." || segment === "..")) return void 0;
1135
+ if (service !== "kv" && (pathSegments.length === 0 || pathSegments[pathSegments.length - 1] === "")) return void 0;
1136
+ return {
1137
+ space: segments.slice(0, serviceIndex).join("/"),
1138
+ path: pathSegments.join("/")
1139
+ };
1140
+ }
1141
+ function safeCapabilityResource(value, service, action) {
1142
+ if (typeof value !== "string" || value.length === 0 || value.length > 1024) return false;
1143
+ if (service === "encryption" && value.startsWith("urn:tinycloud:encryption:")) {
1144
+ if (!/^[A-Za-z0-9_][A-Za-z0-9_.:/-]*$/.test(value) || value.includes("//") || value.split("/").some((segment) => segment === "." || segment === "..")) return false;
1145
+ try {
1146
+ parseNetworkId(value);
1147
+ return true;
1148
+ } catch {
1149
+ return false;
1150
+ }
1151
+ }
1152
+ const parsed = parseCapabilityResource(value, service);
1153
+ return parsed !== void 0 && (!parsed.path.endsWith("/") || service === "kv" && action === "tinycloud.kv/list") && (parsed.path !== "" || service === "kv" && action === "tinycloud.kv/list");
1154
+ }
1155
+ function validatedCapabilityOf(error) {
1156
+ const verdict = authorizationVerdictOf(error);
1157
+ if (verdict !== "unauthenticated" && verdict !== "forbidden") return void 0;
1158
+ const seen = /* @__PURE__ */ new Set();
1159
+ let node = error;
1160
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
1161
+ seen.add(node);
1162
+ const record = node;
1163
+ const status = errorStatusOf(record);
1164
+ const meta = record.meta;
1165
+ if ((status === 401 || status === 403 || status === void 0 && record.code === ErrorCodes.AUTH_UNAUTHORIZED) && typeof meta === "object" && meta !== null && !Array.isArray(meta)) {
1166
+ const metadata = meta;
1167
+ const { resource, requiredAction } = metadata;
1168
+ if (typeof requiredAction === "string" && grantableActions.has(requiredAction)) {
1169
+ const actionService = requiredAction.slice("tinycloud.".length, requiredAction.indexOf("/"));
1170
+ const explicitService = record.service === void 0 ? metadata.service : record.service;
1171
+ if (safeCapabilityResource(resource, actionService, requiredAction) && (explicitService === void 0 || explicitService === actionService || explicitService === `tinycloud.${actionService}`)) {
1172
+ return { resource, requiredAction };
1173
+ }
1174
+ }
1175
+ }
1176
+ node = record.cause;
1177
+ }
1178
+ return void 0;
1179
+ }
996
1180
  var MAX_CAUSE_DEPTH = 8;
997
1181
  function errorStatusOf(node) {
998
1182
  const meta = node.meta;
@@ -1059,6 +1243,9 @@ function isStorageFullError(error) {
1059
1243
  }
1060
1244
  function wrapError(service, error, defaultCode = ErrorCodes.NETWORK_ERROR) {
1061
1245
  if (error instanceof Error) {
1246
+ if (isRequestTimeoutError(error)) {
1247
+ return timeoutError(service, error.timeoutMs, error);
1248
+ }
1062
1249
  if (error.name === "AbortError") {
1063
1250
  return abortedError(service);
1064
1251
  }
@@ -1083,6 +1270,7 @@ function errorResult(error) {
1083
1270
  }
1084
1271
 
1085
1272
  // src/base/BaseService.ts
1273
+ var MAX_TIMER_DELAY_MS = 2147483647;
1086
1274
  var BaseService = class {
1087
1275
  constructor() {
1088
1276
  /**
@@ -1263,6 +1451,64 @@ var BaseService = class {
1263
1451
  }
1264
1452
  return controller.signal;
1265
1453
  }
1454
+ /**
1455
+ * Create the abort signal for one request, bounded by the request timeout.
1456
+ *
1457
+ * The signal aborts when the service signs out, the context aborts, the
1458
+ * caller's `signal` aborts, or the timeout elapses, whichever comes first.
1459
+ * A timeout aborts with a {@link RequestTimeoutError} (`TimeoutError`), which
1460
+ * `wrapError` maps to `ErrorCodes.TIMEOUT`; the other sources keep their own
1461
+ * reason (normally `AbortError`, mapped to `ErrorCodes.ABORTED`).
1462
+ *
1463
+ * The timeout is `timeoutMs` when given, otherwise `config.timeout`. Only a
1464
+ * positive, finite value (at most 2^31-1 ms) applies; `undefined`, `0`, and
1465
+ * `Infinity` mean no timeout, so a per-call `0` opts out of a configured one.
1466
+ *
1467
+ * Callers must call `dispose()` once the response body has been consumed,
1468
+ * which clears the timer and removes the listeners this adds to the
1469
+ * long-lived service and context signals.
1470
+ *
1471
+ * @param signal - The caller's abort signal for this request
1472
+ * @param timeoutMs - Per-request timeout override in milliseconds
1473
+ */
1474
+ createRequestSignal(signal, timeoutMs) {
1475
+ const controller = new AbortController();
1476
+ const detachers = [];
1477
+ let timer;
1478
+ const dispose = () => {
1479
+ if (timer !== void 0) {
1480
+ clearTimeout(timer);
1481
+ timer = void 0;
1482
+ }
1483
+ for (const detach of detachers.splice(0)) {
1484
+ detach();
1485
+ }
1486
+ };
1487
+ const abort = (reason) => {
1488
+ dispose();
1489
+ controller.abort(reason);
1490
+ };
1491
+ const requestSignal = { signal: controller.signal, dispose };
1492
+ const parents = [
1493
+ this.abortController.signal,
1494
+ this.context?.abortSignal,
1495
+ signal
1496
+ ].filter((parent) => parent !== void 0);
1497
+ for (const parent of parents) {
1498
+ if (parent.aborted) {
1499
+ abort(parent.reason);
1500
+ return requestSignal;
1501
+ }
1502
+ const onAbort = () => abort(parent.reason);
1503
+ parent.addEventListener("abort", onAbort, { once: true });
1504
+ detachers.push(() => parent.removeEventListener("abort", onAbort));
1505
+ }
1506
+ const timeout = timeoutMs ?? this._config.timeout;
1507
+ if (typeof timeout === "number" && timeout > 0 && timeout <= MAX_TIMER_DELAY_MS) {
1508
+ timer = setTimeout(() => abort(new RequestTimeoutError(timeout)), timeout);
1509
+ }
1510
+ return requestSignal;
1511
+ }
1266
1512
  /**
1267
1513
  * Wrap an operation with error handling and telemetry.
1268
1514
  *
@@ -1523,7 +1769,7 @@ var KVService = class extends BaseService {
1523
1769
  * @param path - Resource path
1524
1770
  * @param action - KV action
1525
1771
  * @param body - Optional request body
1526
- * @param signal - Optional abort signal
1772
+ * @param signal - Request signal from createRequestSignal()
1527
1773
  * @returns Fetch response
1528
1774
  */
1529
1775
  async invokeOperation(path, action, body, signal, extraHeaders) {
@@ -1539,7 +1785,7 @@ var KVService = class extends BaseService {
1539
1785
  method: "POST",
1540
1786
  headers: requestHeaders,
1541
1787
  body,
1542
- signal: this.combineSignals(signal)
1788
+ signal
1543
1789
  });
1544
1790
  }
1545
1791
  /**
@@ -1716,6 +1962,7 @@ var KVService = class extends BaseService {
1716
1962
  "kv"
1717
1963
  ));
1718
1964
  }
1965
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1719
1966
  try {
1720
1967
  const session = this.context.session;
1721
1968
  const invocationHeaders = this.context.invokeAny(
@@ -1737,15 +1984,17 @@ var KVService = class extends BaseService {
1737
1984
  const response = await this.context.fetch(`${this.host}/invoke`, {
1738
1985
  method: "POST",
1739
1986
  headers,
1740
- signal: this.combineSignals(options?.signal)
1987
+ signal: request.signal
1741
1988
  });
1742
1989
  if (!response.ok) {
1743
- const errorText = await response.text();
1990
+ const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
1744
1991
  if (response.status === 401 || response.status === 403) {
1745
1992
  return this.authorizationFailure(
1746
1993
  `Failed to batch read ${keys.length} key(s)`,
1747
1994
  response,
1748
- errorText
1995
+ errorText,
1996
+ paths,
1997
+ action
1749
1998
  );
1750
1999
  }
1751
2000
  if (response.status === 413) {
@@ -1800,6 +2049,8 @@ var KVService = class extends BaseService {
1800
2049
  return ok({ results, count: results.length });
1801
2050
  } catch (error) {
1802
2051
  return err(wrapError("kv", error));
2052
+ } finally {
2053
+ request.dispose();
1803
2054
  }
1804
2055
  }
1805
2056
  /**
@@ -1835,7 +2086,7 @@ var KVService = class extends BaseService {
1835
2086
  return text;
1836
2087
  }
1837
2088
  }
1838
- async createSignedReadUrlError(response, key) {
2089
+ async createSignedReadUrlError(response, key, path) {
1839
2090
  let errorText = response.statusText;
1840
2091
  try {
1841
2092
  const text = await response.text();
@@ -1848,7 +2099,9 @@ var KVService = class extends BaseService {
1848
2099
  return this.authorizationFailure(
1849
2100
  `Failed to create signed read URL for key ${JSON.stringify(key)}`,
1850
2101
  response,
1851
- errorText
2102
+ errorText,
2103
+ [path],
2104
+ KVAction.GET
1852
2105
  );
1853
2106
  }
1854
2107
  const code = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
@@ -1861,21 +2114,42 @@ var KVService = class extends BaseService {
1861
2114
  )
1862
2115
  );
1863
2116
  }
2117
+ /** A known 401/403 keeps its typed status even if reading its body fails. */
2118
+ async readAuthorizationText(response) {
2119
+ try {
2120
+ return await response.text();
2121
+ } catch {
2122
+ return "";
2123
+ }
2124
+ }
1864
2125
  /**
1865
2126
  * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
1866
2127
  * status and the server text (`<context>: <status> - <text>`, the same
1867
2128
  * shape as every other KV failure) so callers that rethrow only the message
1868
2129
  * still see the status; `meta.status` carries it for typed callers.
1869
- * Resource/ability hints are parsed from the raw body, never the message.
2130
+ * Server hints must match the requested path/action (whether the node sends
2131
+ * a relative path or full resource); metadata uses only the request-derived
2132
+ * canonical resource under the authenticated session's space.
1870
2133
  */
1871
- authorizationFailure(context, response, errorText, extraMeta = {}) {
2134
+ authorizationFailure(context, response, errorText, paths, expectedAction, extraMeta = {}) {
1872
2135
  const { resource, action } = parseAuthError(errorText);
2136
+ const requestedPath = paths.find(
2137
+ (path) => resource === path || resource === `${this.context.session.spaceId}/kv/${path}`
2138
+ );
2139
+ const capability = requestedPath !== void 0 && action === expectedAction ? validatedCapabilityOf({
2140
+ service: "kv",
2141
+ code: ErrorCodes.AUTH_UNAUTHORIZED,
2142
+ meta: {
2143
+ status: response.status,
2144
+ resource: `${this.context.session.spaceId}/kv/${requestedPath}`,
2145
+ requiredAction: action
2146
+ }
2147
+ }) : void 0;
1873
2148
  const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
1874
2149
  return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
1875
2150
  status: response.status,
1876
- ...action && { requiredAction: action },
1877
- ...resource && { resource },
1878
- ...extraMeta
2151
+ ...extraMeta,
2152
+ ...capability
1879
2153
  }));
1880
2154
  }
1881
2155
  normalizeSignedReadUrlResponse(data) {
@@ -1909,23 +2183,35 @@ var KVService = class extends BaseService {
1909
2183
  ));
1910
2184
  }
1911
2185
  const path = this.getFullPath(key, options?.prefix);
2186
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1912
2187
  try {
1913
2188
  const response = await this.invokeOperation(
1914
2189
  path,
1915
2190
  KVAction.GET,
1916
2191
  void 0,
1917
- options?.signal,
2192
+ request.signal,
1918
2193
  options?.maxResponseBytes === void 0 ? void 0 : { "x-tinycloud-max-response-bytes": String(options.maxResponseBytes) }
1919
2194
  );
1920
2195
  if (!response.ok) {
1921
2196
  if (response.status === 401 || response.status === 403) {
1922
- const errorText2 = await response.text();
2197
+ const errorText2 = await this.readAuthorizationText(response);
1923
2198
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
2199
+ const structuredCapability = permissionHint?.service === "tinycloud.kv" && permissionHint.space === this.context.session.spaceId && permissionHint.path === path ? validatedCapabilityOf({
2200
+ service: "kv",
2201
+ code: ErrorCodes.AUTH_UNAUTHORIZED,
2202
+ meta: {
2203
+ status: response.status,
2204
+ resource: `${this.context.session.spaceId}/kv/${path}`,
2205
+ requiredAction: KVAction.GET
2206
+ }
2207
+ }) : void 0;
1924
2208
  return this.authorizationFailure(
1925
2209
  `Failed to get key ${JSON.stringify(key)}`,
1926
2210
  response,
1927
2211
  errorText2,
1928
- permissionHint === void 0 ? {} : { permissionHint }
2212
+ [path],
2213
+ KVAction.GET,
2214
+ structuredCapability ? { permissionHint, ...structuredCapability } : {}
1929
2215
  );
1930
2216
  }
1931
2217
  if (response.status === 404) {
@@ -1960,6 +2246,8 @@ var KVService = class extends BaseService {
1960
2246
  });
1961
2247
  } catch (error) {
1962
2248
  return err(wrapError("kv", error));
2249
+ } finally {
2250
+ request.dispose();
1963
2251
  }
1964
2252
  });
1965
2253
  }
@@ -1987,12 +2275,13 @@ var KVService = class extends BaseService {
1987
2275
  }
1988
2276
  const path = this.getFullPath(key, options?.prefix);
1989
2277
  const body = this.serializePutValue(value, options?.contentType);
2278
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
1990
2279
  try {
1991
2280
  const response = await this.invokeOperation(
1992
2281
  path,
1993
2282
  KVAction.PUT,
1994
2283
  body,
1995
- options?.signal,
2284
+ request.signal,
1996
2285
  {
1997
2286
  ...options?.ifMatch === void 0 ? {} : { "if-match": options.ifMatch },
1998
2287
  ...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
@@ -2002,7 +2291,9 @@ var KVService = class extends BaseService {
2002
2291
  return this.authorizationFailure(
2003
2292
  `Failed to put key ${JSON.stringify(key)}`,
2004
2293
  response,
2005
- await response.text()
2294
+ await this.readAuthorizationText(response),
2295
+ [path],
2296
+ KVAction.PUT
2006
2297
  );
2007
2298
  }
2008
2299
  if (!response.ok) {
@@ -2046,6 +2337,8 @@ var KVService = class extends BaseService {
2046
2337
  });
2047
2338
  } catch (error) {
2048
2339
  return err(wrapError("kv", error));
2340
+ } finally {
2341
+ request.dispose();
2049
2342
  }
2050
2343
  });
2051
2344
  }
@@ -2085,6 +2378,7 @@ var KVService = class extends BaseService {
2085
2378
  seen.add(path);
2086
2379
  }
2087
2380
  let requestMayHaveDispatched = false;
2381
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
2088
2382
  try {
2089
2383
  const body = new FormData();
2090
2384
  for (let index = 0; index < items.length; index++) {
@@ -2107,7 +2401,7 @@ var KVService = class extends BaseService {
2107
2401
  method: "POST",
2108
2402
  headers,
2109
2403
  body,
2110
- signal: this.combineSignals(options?.signal)
2404
+ signal: request.signal
2111
2405
  };
2112
2406
  const fetchFn = this.context.fetch;
2113
2407
  requestMayHaveDispatched = true;
@@ -2131,7 +2425,9 @@ var KVService = class extends BaseService {
2131
2425
  return this.authorizationFailure(
2132
2426
  `Failed to batch put ${items.length} key(s)`,
2133
2427
  response,
2134
- errorText
2428
+ errorText,
2429
+ paths,
2430
+ KVAction.PUT
2135
2431
  );
2136
2432
  }
2137
2433
  const quotaError = this.handleQuotaErrorResponse(
@@ -2156,14 +2452,16 @@ var KVService = class extends BaseService {
2156
2452
  rawBody = await response.json();
2157
2453
  } catch (jsonError) {
2158
2454
  const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));
2455
+ const timedOut = isRequestTimeoutError(cause);
2159
2456
  return err(
2160
2457
  serviceError(
2161
- ErrorCodes.NETWORK_ERROR,
2162
- `KV batchPut response was not valid JSON: ${cause.message}`,
2458
+ timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
2459
+ timedOut ? `KV batchPut response body did not arrive in time: ${cause.message}` : `KV batchPut response was not valid JSON: ${cause.message}`,
2163
2460
  "kv",
2164
2461
  {
2165
2462
  cause,
2166
2463
  meta: {
2464
+ ...timedOut ? { timeoutMs: cause.timeoutMs } : {},
2167
2465
  requestMayHaveDispatched: true,
2168
2466
  responseReceived: true,
2169
2467
  status: response.status,
@@ -2218,6 +2516,8 @@ var KVService = class extends BaseService {
2218
2516
  ...wrapped,
2219
2517
  meta: { ...wrapped.meta, requestMayHaveDispatched }
2220
2518
  });
2519
+ } finally {
2520
+ request.dispose();
2221
2521
  }
2222
2522
  });
2223
2523
  }
@@ -2240,23 +2540,26 @@ var KVService = class extends BaseService {
2240
2540
  if (options?.path) {
2241
2541
  listPath = listPath ? `${listPath}/${options.path}` : options.path;
2242
2542
  }
2543
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
2243
2544
  try {
2244
2545
  const response = await this.invokeOperation(
2245
2546
  listPath,
2246
2547
  KVAction.LIST,
2247
2548
  void 0,
2248
- options?.signal,
2549
+ request.signal,
2249
2550
  options?.limit === void 0 && options?.cursor === void 0 ? void 0 : {
2250
2551
  ...options?.limit === void 0 ? {} : { "x-tinycloud-limit": String(options.limit) },
2251
2552
  ...options?.cursor === void 0 ? {} : { "x-tinycloud-cursor": options.cursor }
2252
2553
  }
2253
2554
  );
2254
2555
  if (!response.ok) {
2255
- if (response.status === 401) {
2556
+ if (response.status === 401 || response.status === 403) {
2256
2557
  return this.authorizationFailure(
2257
2558
  "Failed to list keys",
2258
2559
  response,
2259
- await response.text()
2560
+ await this.readAuthorizationText(response),
2561
+ [listPath],
2562
+ KVAction.LIST
2260
2563
  );
2261
2564
  }
2262
2565
  const errorText = await response.text();
@@ -2284,6 +2587,8 @@ var KVService = class extends BaseService {
2284
2587
  });
2285
2588
  } catch (error) {
2286
2589
  return err(wrapError("kv", error));
2590
+ } finally {
2591
+ request.dispose();
2287
2592
  }
2288
2593
  });
2289
2594
  }
@@ -2296,20 +2601,23 @@ var KVService = class extends BaseService {
2296
2601
  return err(authRequiredError("kv"));
2297
2602
  }
2298
2603
  const path = this.getFullPath(key, options?.prefix);
2604
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
2299
2605
  try {
2300
2606
  const response = await this.invokeOperation(
2301
2607
  path,
2302
2608
  KVAction.DELETE,
2303
2609
  void 0,
2304
- options?.signal,
2610
+ request.signal,
2305
2611
  options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
2306
2612
  );
2307
2613
  if (!response.ok) {
2308
- if (response.status === 401) {
2614
+ if (response.status === 401 || response.status === 403) {
2309
2615
  return this.authorizationFailure(
2310
2616
  `Failed to delete key ${JSON.stringify(key)}`,
2311
2617
  response,
2312
- await response.text()
2618
+ await this.readAuthorizationText(response),
2619
+ [path],
2620
+ KVAction.DELETE
2313
2621
  );
2314
2622
  }
2315
2623
  if (response.status === 404) {
@@ -2347,6 +2655,8 @@ var KVService = class extends BaseService {
2347
2655
  });
2348
2656
  } catch (error) {
2349
2657
  return err(wrapError("kv", error));
2658
+ } finally {
2659
+ request.dispose();
2350
2660
  }
2351
2661
  });
2352
2662
  }
@@ -2359,19 +2669,22 @@ var KVService = class extends BaseService {
2359
2669
  return err(authRequiredError("kv"));
2360
2670
  }
2361
2671
  const path = this.getFullPath(key, options?.prefix);
2672
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
2362
2673
  try {
2363
2674
  const response = await this.invokeOperation(
2364
2675
  path,
2365
2676
  KVAction.HEAD,
2366
2677
  void 0,
2367
- options?.signal
2678
+ request.signal
2368
2679
  );
2369
2680
  if (!response.ok) {
2370
- if (response.status === 401) {
2681
+ if (response.status === 401 || response.status === 403) {
2371
2682
  return this.authorizationFailure(
2372
2683
  `Failed to get metadata for key ${JSON.stringify(key)}`,
2373
2684
  response,
2374
- await response.text()
2685
+ await this.readAuthorizationText(response),
2686
+ [path],
2687
+ KVAction.HEAD
2375
2688
  );
2376
2689
  }
2377
2690
  if (response.status === 404) {
@@ -2393,6 +2706,8 @@ var KVService = class extends BaseService {
2393
2706
  });
2394
2707
  } catch (error) {
2395
2708
  return err(wrapError("kv", error));
2709
+ } finally {
2710
+ request.dispose();
2396
2711
  }
2397
2712
  });
2398
2713
  }
@@ -2430,15 +2745,16 @@ var KVService = class extends BaseService {
2430
2745
  if (options?.etag !== void 0) {
2431
2746
  body.etag = options.etag;
2432
2747
  }
2748
+ const request = this.createRequestSignal(options?.signal, options?.timeout);
2433
2749
  try {
2434
2750
  const response = await this.context.fetch(`${this.host}/signed/kv`, {
2435
2751
  method: "POST",
2436
2752
  headers: this.withJsonContentType(headers),
2437
2753
  body: JSON.stringify(body),
2438
- signal: this.combineSignals(options?.signal)
2754
+ signal: request.signal
2439
2755
  });
2440
2756
  if (!response.ok) {
2441
- return this.createSignedReadUrlError(response, key);
2757
+ return this.createSignedReadUrlError(response, key, path);
2442
2758
  }
2443
2759
  const signedUrl = this.normalizeSignedReadUrlResponse(
2444
2760
  await response.json()
@@ -2455,6 +2771,8 @@ var KVService = class extends BaseService {
2455
2771
  return ok(signedUrl);
2456
2772
  } catch (error) {
2457
2773
  return err(wrapError("kv", error));
2774
+ } finally {
2775
+ request.dispose();
2458
2776
  }
2459
2777
  });
2460
2778
  }
@@ -2668,6 +2986,7 @@ var SQLService = class extends BaseService {
2668
2986
  if (!this.requireAuth()) {
2669
2987
  return err(authRequiredError("sql"));
2670
2988
  }
2989
+ const request = this.createRequestSignal(options?.signal);
2671
2990
  try {
2672
2991
  const body = {
2673
2992
  action: "query",
@@ -2676,19 +2995,22 @@ var SQLService = class extends BaseService {
2676
2995
  };
2677
2996
  if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
2678
2997
  if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
2998
+ const action = this.actionForSql(sql, SQLAction.READ);
2679
2999
  const response = await this.invokeSQL(
2680
3000
  dbName,
2681
- this.actionForSql(sql, SQLAction.READ),
3001
+ action,
2682
3002
  body,
2683
- options?.signal
3003
+ request.signal
2684
3004
  );
2685
3005
  if (!response.ok) {
2686
- return this.handleErrorResponse(response, "query");
3006
+ return this.handleErrorResponse(response, "query", dbName, [action]);
2687
3007
  }
2688
3008
  const data = await response.json();
2689
3009
  return ok(data);
2690
3010
  } catch (error) {
2691
3011
  return err(wrapError("sql", error));
3012
+ } finally {
3013
+ request.dispose();
2692
3014
  }
2693
3015
  });
2694
3016
  }
@@ -2697,6 +3019,7 @@ var SQLService = class extends BaseService {
2697
3019
  if (!this.requireAuth()) {
2698
3020
  return err(authRequiredError("sql"));
2699
3021
  }
3022
+ const request = this.createRequestSignal(options?.signal);
2700
3023
  try {
2701
3024
  const body = {
2702
3025
  action: "execute",
@@ -2712,19 +3035,22 @@ var SQLService = class extends BaseService {
2712
3035
  (statement) => this.actionForSql(statement, SQLAction.SCHEMA)
2713
3036
  )
2714
3037
  ];
3038
+ const requestedActions = this.dedupeActions(actions);
2715
3039
  const response = await this.invokeSQL(
2716
3040
  dbName,
2717
- this.dedupeActions(actions),
3041
+ requestedActions,
2718
3042
  body,
2719
- options?.signal
3043
+ request.signal
2720
3044
  );
2721
3045
  if (!response.ok) {
2722
- return this.handleErrorResponse(response, "execute");
3046
+ return this.handleErrorResponse(response, "execute", dbName, requestedActions);
2723
3047
  }
2724
3048
  const data = await response.json();
2725
3049
  return ok(data);
2726
3050
  } catch (error) {
2727
3051
  return err(wrapError("sql", error));
3052
+ } finally {
3053
+ request.dispose();
2728
3054
  }
2729
3055
  });
2730
3056
  }
@@ -2733,20 +3059,24 @@ var SQLService = class extends BaseService {
2733
3059
  if (!this.requireAuth()) {
2734
3060
  return err(authRequiredError("sql"));
2735
3061
  }
3062
+ const request = this.createRequestSignal(options?.signal);
2736
3063
  try {
3064
+ const requestedActions = this.actionsForSqlBatch(statements);
2737
3065
  const response = await this.invokeSQL(
2738
3066
  dbName,
2739
- this.actionsForSqlBatch(statements),
3067
+ requestedActions,
2740
3068
  { action: "batch", statements },
2741
- options?.signal
3069
+ request.signal
2742
3070
  );
2743
3071
  if (!response.ok) {
2744
- return this.handleErrorResponse(response, "batch");
3072
+ return this.handleErrorResponse(response, "batch", dbName, requestedActions);
2745
3073
  }
2746
3074
  const data = await response.json();
2747
3075
  return ok(data);
2748
3076
  } catch (error) {
2749
3077
  return err(wrapError("sql", error));
3078
+ } finally {
3079
+ request.dispose();
2750
3080
  }
2751
3081
  });
2752
3082
  }
@@ -2755,20 +3085,23 @@ var SQLService = class extends BaseService {
2755
3085
  if (!this.requireAuth()) {
2756
3086
  return err(authRequiredError("sql"));
2757
3087
  }
3088
+ const request = this.createRequestSignal(options?.signal);
2758
3089
  try {
2759
3090
  const response = await this.invokeSQL(
2760
3091
  dbName,
2761
3092
  SQLAction.WRITE,
2762
3093
  { action: "execute_statement", name, params: params ?? [] },
2763
- options?.signal
3094
+ request.signal
2764
3095
  );
2765
3096
  if (!response.ok) {
2766
- return this.handleErrorResponse(response, "executeStatement");
3097
+ return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
2767
3098
  }
2768
3099
  const data = await response.json();
2769
3100
  return ok(data);
2770
3101
  } catch (error) {
2771
3102
  return err(wrapError("sql", error));
3103
+ } finally {
3104
+ request.dispose();
2772
3105
  }
2773
3106
  });
2774
3107
  }
@@ -2777,15 +3110,16 @@ var SQLService = class extends BaseService {
2777
3110
  if (!this.requireAuth()) {
2778
3111
  return err(authRequiredError("sql"));
2779
3112
  }
3113
+ const request = this.createRequestSignal(options?.signal);
2780
3114
  try {
2781
3115
  const response = await this.invokeSQL(
2782
3116
  dbName,
2783
3117
  SQLAction.READ,
2784
3118
  { action: "export" },
2785
- options?.signal
3119
+ request.signal
2786
3120
  );
2787
3121
  if (!response.ok) {
2788
- return this.handleErrorResponse(response, "export");
3122
+ return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
2789
3123
  }
2790
3124
  const resp = response;
2791
3125
  if (typeof resp.blob === "function") {
@@ -2796,6 +3130,8 @@ var SQLService = class extends BaseService {
2796
3130
  return ok(text);
2797
3131
  } catch (error) {
2798
3132
  return err(wrapError("sql", error));
3133
+ } finally {
3134
+ request.dispose();
2799
3135
  }
2800
3136
  });
2801
3137
  }
@@ -2886,7 +3222,7 @@ var SQLService = class extends BaseService {
2886
3222
  "Content-Type": "application/json"
2887
3223
  },
2888
3224
  body: JSON.stringify(body),
2889
- signal: this.combineSignals(signal)
3225
+ signal
2890
3226
  });
2891
3227
  }
2892
3228
  actionForSql(sql, fallback) {
@@ -2919,7 +3255,7 @@ var SQLService = class extends BaseService {
2919
3255
  }))
2920
3256
  );
2921
3257
  }
2922
- async handleErrorResponse(response, operation) {
3258
+ async handleErrorResponse(response, operation, dbName, requestedActions) {
2923
3259
  const errorText = await response.text();
2924
3260
  const meta = responseErrorMeta(response.status, response.statusText, errorText);
2925
3261
  if (response.status === 402) {
@@ -2938,9 +3274,11 @@ var SQLService = class extends BaseService {
2938
3274
  errorBody
2939
3275
  );
2940
3276
  if (response.status === 401) {
2941
- const { resource, action } = parseAuthError(errorText);
2942
- if (action) meta.requiredAction = action;
2943
- if (resource) meta.resource = resource;
3277
+ const { action } = parseAuthError(errorText);
3278
+ if (action && requestedActions.includes(action)) {
3279
+ meta.requiredAction = action;
3280
+ meta.resource = `${this.context.session.spaceId}/sql/${dbName}`;
3281
+ }
2944
3282
  }
2945
3283
  return err(
2946
3284
  serviceError(errorCode, message, "sql", { meta })
@@ -3148,12 +3486,13 @@ var DuckDbService = class extends BaseService {
3148
3486
  if (!this.requireAuth()) {
3149
3487
  return err(authRequiredError("duckdb"));
3150
3488
  }
3489
+ const request = this.createRequestSignal(options?.signal);
3151
3490
  try {
3152
3491
  const response = await this.invokeDuckDb(
3153
3492
  dbName,
3154
3493
  DuckDbAction.READ,
3155
3494
  { action: "query", sql, params: params ?? [] },
3156
- options?.signal
3495
+ request.signal
3157
3496
  );
3158
3497
  if (!response.ok) {
3159
3498
  return this.handleErrorResponse(response, "query");
@@ -3162,6 +3501,8 @@ var DuckDbService = class extends BaseService {
3162
3501
  return ok(data);
3163
3502
  } catch (error) {
3164
3503
  return err(wrapError("duckdb", error));
3504
+ } finally {
3505
+ request.dispose();
3165
3506
  }
3166
3507
  });
3167
3508
  }
@@ -3170,12 +3511,13 @@ var DuckDbService = class extends BaseService {
3170
3511
  if (!this.requireAuth()) {
3171
3512
  return err(authRequiredError("duckdb"));
3172
3513
  }
3514
+ const request = this.createRequestSignal(options?.signal);
3173
3515
  try {
3174
3516
  const response = await this.invokeDuckDb(
3175
3517
  dbName,
3176
3518
  DuckDbAction.READ,
3177
3519
  { action: "query", sql, params: params ?? [] },
3178
- options?.signal,
3520
+ request.signal,
3179
3521
  { Accept: "application/vnd.apache.arrow.stream" }
3180
3522
  );
3181
3523
  if (!response.ok) {
@@ -3185,6 +3527,8 @@ var DuckDbService = class extends BaseService {
3185
3527
  return ok(buffer);
3186
3528
  } catch (error) {
3187
3529
  return err(wrapError("duckdb", error));
3530
+ } finally {
3531
+ request.dispose();
3188
3532
  }
3189
3533
  });
3190
3534
  }
@@ -3193,6 +3537,7 @@ var DuckDbService = class extends BaseService {
3193
3537
  if (!this.requireAuth()) {
3194
3538
  return err(authRequiredError("duckdb"));
3195
3539
  }
3540
+ const request = this.createRequestSignal(options?.signal);
3196
3541
  try {
3197
3542
  const body = {
3198
3543
  action: "execute",
@@ -3206,7 +3551,7 @@ var DuckDbService = class extends BaseService {
3206
3551
  dbName,
3207
3552
  DuckDbAction.WRITE,
3208
3553
  body,
3209
- options?.signal
3554
+ request.signal
3210
3555
  );
3211
3556
  if (!response.ok) {
3212
3557
  return this.handleErrorResponse(response, "execute");
@@ -3215,6 +3560,8 @@ var DuckDbService = class extends BaseService {
3215
3560
  return ok(data);
3216
3561
  } catch (error) {
3217
3562
  return err(wrapError("duckdb", error));
3563
+ } finally {
3564
+ request.dispose();
3218
3565
  }
3219
3566
  });
3220
3567
  }
@@ -3223,6 +3570,7 @@ var DuckDbService = class extends BaseService {
3223
3570
  if (!this.requireAuth()) {
3224
3571
  return err(authRequiredError("duckdb"));
3225
3572
  }
3573
+ const request = this.createRequestSignal(options?.signal);
3226
3574
  try {
3227
3575
  const body = {
3228
3576
  action: "batch",
@@ -3235,7 +3583,7 @@ var DuckDbService = class extends BaseService {
3235
3583
  dbName,
3236
3584
  DuckDbAction.WRITE,
3237
3585
  body,
3238
- options?.signal
3586
+ request.signal
3239
3587
  );
3240
3588
  if (!response.ok) {
3241
3589
  return this.handleErrorResponse(response, "batch");
@@ -3244,6 +3592,8 @@ var DuckDbService = class extends BaseService {
3244
3592
  return ok(data);
3245
3593
  } catch (error) {
3246
3594
  return err(wrapError("duckdb", error));
3595
+ } finally {
3596
+ request.dispose();
3247
3597
  }
3248
3598
  });
3249
3599
  }
@@ -3252,12 +3602,13 @@ var DuckDbService = class extends BaseService {
3252
3602
  if (!this.requireAuth()) {
3253
3603
  return err(authRequiredError("duckdb"));
3254
3604
  }
3605
+ const request = this.createRequestSignal(options?.signal);
3255
3606
  try {
3256
3607
  const response = await this.invokeDuckDb(
3257
3608
  dbName,
3258
3609
  DuckDbAction.WRITE,
3259
3610
  { action: "executeStatement", name, params: params ?? [] },
3260
- options?.signal
3611
+ request.signal
3261
3612
  );
3262
3613
  if (!response.ok) {
3263
3614
  return this.handleErrorResponse(response, "executeStatement");
@@ -3266,6 +3617,8 @@ var DuckDbService = class extends BaseService {
3266
3617
  return ok(data);
3267
3618
  } catch (error) {
3268
3619
  return err(wrapError("duckdb", error));
3620
+ } finally {
3621
+ request.dispose();
3269
3622
  }
3270
3623
  });
3271
3624
  }
@@ -3274,12 +3627,13 @@ var DuckDbService = class extends BaseService {
3274
3627
  if (!this.requireAuth()) {
3275
3628
  return err(authRequiredError("duckdb"));
3276
3629
  }
3630
+ const request = this.createRequestSignal(options?.signal);
3277
3631
  try {
3278
3632
  const response = await this.invokeDuckDb(
3279
3633
  dbName,
3280
3634
  DuckDbAction.READ,
3281
3635
  { action: "describe" },
3282
- options?.signal
3636
+ request.signal
3283
3637
  );
3284
3638
  if (!response.ok) {
3285
3639
  return this.handleErrorResponse(response, "describe");
@@ -3288,6 +3642,8 @@ var DuckDbService = class extends BaseService {
3288
3642
  return ok(data);
3289
3643
  } catch (error) {
3290
3644
  return err(wrapError("duckdb", error));
3645
+ } finally {
3646
+ request.dispose();
3291
3647
  }
3292
3648
  });
3293
3649
  }
@@ -3296,12 +3652,13 @@ var DuckDbService = class extends BaseService {
3296
3652
  if (!this.requireAuth()) {
3297
3653
  return err(authRequiredError("duckdb"));
3298
3654
  }
3655
+ const request = this.createRequestSignal(options?.signal);
3299
3656
  try {
3300
3657
  const response = await this.invokeDuckDb(
3301
3658
  dbName,
3302
3659
  DuckDbAction.EXPORT,
3303
3660
  { action: "export" },
3304
- options?.signal
3661
+ request.signal
3305
3662
  );
3306
3663
  if (!response.ok) {
3307
3664
  return this.handleErrorResponse(response, "export");
@@ -3310,6 +3667,8 @@ var DuckDbService = class extends BaseService {
3310
3667
  return ok(blob);
3311
3668
  } catch (error) {
3312
3669
  return err(wrapError("duckdb", error));
3670
+ } finally {
3671
+ request.dispose();
3313
3672
  }
3314
3673
  });
3315
3674
  }
@@ -3318,6 +3677,7 @@ var DuckDbService = class extends BaseService {
3318
3677
  if (!this.requireAuth()) {
3319
3678
  return err(authRequiredError("duckdb"));
3320
3679
  }
3680
+ const request = this.createRequestSignal(options?.signal);
3321
3681
  try {
3322
3682
  const session = this.context.session;
3323
3683
  const headers = this.context.invoke(
@@ -3333,7 +3693,7 @@ var DuckDbService = class extends BaseService {
3333
3693
  "Content-Type": "application/x-duckdb"
3334
3694
  },
3335
3695
  body: new Blob([data]),
3336
- signal: this.combineSignals(options?.signal)
3696
+ signal: request.signal
3337
3697
  });
3338
3698
  if (!response.ok) {
3339
3699
  return this.handleErrorResponse(response, "import");
@@ -3341,6 +3701,8 @@ var DuckDbService = class extends BaseService {
3341
3701
  return ok(void 0);
3342
3702
  } catch (error) {
3343
3703
  return err(wrapError("duckdb", error));
3704
+ } finally {
3705
+ request.dispose();
3344
3706
  }
3345
3707
  });
3346
3708
  }
@@ -3356,7 +3718,7 @@ var DuckDbService = class extends BaseService {
3356
3718
  ...extraHeaders
3357
3719
  },
3358
3720
  body: JSON.stringify(body),
3359
- signal: this.combineSignals(signal)
3721
+ signal
3360
3722
  });
3361
3723
  }
3362
3724
  async handleErrorResponse(response, operation) {
@@ -5919,103 +6281,6 @@ function canonicalHashHex(sha256, value) {
5919
6281
  return hexEncode2(sha256(utf8Encode(canonical)));
5920
6282
  }
5921
6283
 
5922
- // src/encryption/networkId.ts
5923
- var URN_PREFIX = "urn:tinycloud:encryption:";
5924
- var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
5925
- var PKH_EIP155_DID_RE = /^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/;
5926
- var NetworkIdError = class extends Error {
5927
- constructor(message) {
5928
- super(message);
5929
- this.name = "NetworkIdError";
5930
- }
5931
- };
5932
- function parseNetworkId(networkId) {
5933
- if (typeof networkId !== "string" || networkId.length === 0) {
5934
- throw new NetworkIdError("networkId must be a non-empty string");
5935
- }
5936
- if (!networkId.startsWith(URN_PREFIX)) {
5937
- throw new NetworkIdError(
5938
- `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
5939
- );
5940
- }
5941
- const body = networkId.slice(URN_PREFIX.length);
5942
- const lastColon = body.lastIndexOf(":");
5943
- if (lastColon <= 0 || lastColon === body.length - 1) {
5944
- throw new NetworkIdError(
5945
- `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`
5946
- );
5947
- }
5948
- const ownerDid = body.slice(0, lastColon);
5949
- const name = body.slice(lastColon + 1);
5950
- if (!ownerDid.startsWith("did:")) {
5951
- throw new NetworkIdError(
5952
- `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`
5953
- );
5954
- }
5955
- const didParts = ownerDid.split(":");
5956
- if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
5957
- throw new NetworkIdError(
5958
- `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`
5959
- );
5960
- }
5961
- if (!NETWORK_NAME_RE.test(name)) {
5962
- throw new NetworkIdError(
5963
- `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
5964
- );
5965
- }
5966
- return { networkId, ownerDid, name };
5967
- }
5968
- function buildNetworkId(ownerDid, name) {
5969
- if (typeof ownerDid !== "string" || !ownerDid.startsWith("did:")) {
5970
- throw new NetworkIdError("ownerDid must be a DID");
5971
- }
5972
- if (typeof name !== "string" || !NETWORK_NAME_RE.test(name)) {
5973
- throw new NetworkIdError(
5974
- `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
5975
- );
5976
- }
5977
- const networkId = `${URN_PREFIX}${ownerDid}:${name}`;
5978
- parseNetworkId(networkId);
5979
- return networkId;
5980
- }
5981
- function isNetworkId(networkId) {
5982
- if (typeof networkId !== "string") {
5983
- return false;
5984
- }
5985
- try {
5986
- parseNetworkId(networkId);
5987
- return true;
5988
- } catch {
5989
- return false;
5990
- }
5991
- }
5992
- function parsePkhOwnerDid(ownerDid) {
5993
- const match = ownerDid.match(PKH_EIP155_DID_RE);
5994
- if (!match) return null;
5995
- return {
5996
- chainId: match[1],
5997
- address: match[2].toLowerCase()
5998
- };
5999
- }
6000
- function ownerDidMatches(a, b) {
6001
- const aPkh = parsePkhOwnerDid(a);
6002
- const bPkh = parsePkhOwnerDid(b);
6003
- if (aPkh && bPkh) {
6004
- return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;
6005
- }
6006
- return a === b;
6007
- }
6008
- function networkDiscoveryKey(name) {
6009
- if (!NETWORK_NAME_RE.test(name)) {
6010
- throw new NetworkIdError(
6011
- `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
6012
- );
6013
- }
6014
- return `.well-known/encryption/network/${name}`;
6015
- }
6016
- var ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;
6017
- var NETWORK_NAME_PATTERN = NETWORK_NAME_RE;
6018
-
6019
6284
  // src/encryption/types.ts
6020
6285
  var DEFAULT_ENCRYPTION_ALG = "x25519-aes256gcm/v1";
6021
6286
  var ENVELOPE_VERSION = 1;
@@ -6958,6 +7223,7 @@ export {
6958
7223
  ok,
6959
7224
  openWrappedKey,
6960
7225
  parseAuthError,
7226
+ parseCapabilityResource,
6961
7227
  parseNetworkId,
6962
7228
  parsePermissionHint,
6963
7229
  parsePermissionHintFromErrorText,
@@ -6983,6 +7249,7 @@ export {
6983
7249
  validateServiceResponseEvent,
6984
7250
  validateServiceSession,
6985
7251
  validateTelemetrySpanEvent,
7252
+ validatedCapabilityOf,
6986
7253
  verifyDecryptResponse,
6987
7254
  wrapError
6988
7255
  };