@tinycloud/sdk-services 3.12.0-beta.7 → 3.12.0-beta.8

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.
@@ -1,4 +1,4 @@
1
- import { E as EncryptionCrypto, I as InlineEncryptedEnvelope, J as Json } from '../types-cWtQmCCw.cjs';
1
+ import { E as EncryptionCrypto, I as InlineEncryptedEnvelope, J as Json } from '../types-CoZ0vXo8.cjs';
2
2
  import { ParsedKeyResource } from './resource.cjs';
3
3
  import '../types-DjAP8dve.cjs';
4
4
 
@@ -1,4 +1,4 @@
1
- import { E as EncryptionCrypto, I as InlineEncryptedEnvelope, J as Json } from '../types-XE4tyBLH.js';
1
+ import { E as EncryptionCrypto, I as InlineEncryptedEnvelope, J as Json } from '../types-Ceti5xVp.js';
2
2
  import { ParsedKeyResource } from './resource.js';
3
3
  import '../types-DjAP8dve.js';
4
4
 
package/dist/index.cjs CHANGED
@@ -104,6 +104,7 @@ __export(src_exports, {
104
104
  clearTinyCloudDebugLogs: () => clearTinyCloudDebugLogs,
105
105
  createKVResponseSchema: () => createKVResponseSchema,
106
106
  createResourceRoutingFetch: () => createResourceRoutingFetch,
107
+ createResourceTransportInvocationFetch: () => createResourceTransportInvocationFetch,
107
108
  createResultSchema: () => createResultSchema,
108
109
  createVaultCrypto: () => createVaultCrypto,
109
110
  decryptEnvelopeWithKey: () => decryptEnvelopeWithKey,
@@ -146,7 +147,9 @@ __export(src_exports, {
146
147
  requiresSecretsOptIn: () => requiresSecretsOptIn,
147
148
  resolveSecretListPrefix: () => resolveSecretListPrefix,
148
149
  resolveSecretPath: () => resolveSecretPath,
150
+ routeResourceRequest: () => routeResourceRequest,
149
151
  routingFailure: () => routingFailure,
152
+ routingResourceOf: () => routingResourceOf,
150
153
  serviceError: () => serviceError,
151
154
  storageLimitReachedError: () => storageLimitReachedError,
152
155
  storageQuotaExceededError: () => storageQuotaExceededError,
@@ -989,6 +992,7 @@ async function verifyResourceDestination(target, fetchFn) {
989
992
  if (infoDid !== target.nodeDid || keysDid !== target.nodeDid) {
990
993
  throw new NodeIdentityMismatchError(target.url, target.nodeDid, infoDid !== target.nodeDid ? infoDid : keysDid);
991
994
  }
995
+ return info;
992
996
  }
993
997
  var NodeNotRespondingError = class extends Error {
994
998
  constructor(hosts, cause) {
@@ -1056,6 +1060,17 @@ function authorizationOf(headers) {
1056
1060
  if (headers === void 0) return void 0;
1057
1061
  return (Array.isArray(headers) ? headers : Object.entries(headers)).find(([name]) => name.toLowerCase() === "authorization")?.[1];
1058
1062
  }
1063
+ function routingResourceOf(uri) {
1064
+ if (uri.startsWith("tinycloud://")) {
1065
+ const resource = uri.slice("tinycloud://".length).split("/", 1)[0];
1066
+ if (resource) return resource;
1067
+ } else if (uri.startsWith("urn:tinycloud:encryption:")) {
1068
+ return uri;
1069
+ } else if (uri.startsWith("tinycloud:")) {
1070
+ return uri.split("/", 1)[0];
1071
+ }
1072
+ throw new ResourceNotRoutedError(uri);
1073
+ }
1059
1074
  function invocationResources(authorization) {
1060
1075
  const encoded = authorization.split(".")[1];
1061
1076
  if (!encoded) throw new ResourceNotRoutedError("invalid invocation");
@@ -1067,8 +1082,7 @@ function invocationResources(authorization) {
1067
1082
  for (const [uri, abilities] of Object.entries(payload.att)) {
1068
1083
  if (typeof abilities !== "object" || abilities === null || Array.isArray(abilities)) throw new ResourceNotRoutedError(uri);
1069
1084
  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);
1085
+ const resource = routingResourceOf(uri);
1072
1086
  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
1087
  const access = resource.startsWith("urn:tinycloud:encryption:") ? "encryption" : action === "tinycloud.space/host" ? "host" : read2 ? "read" : "write";
1074
1088
  if (!resources.some((entry) => entry.resource === resource && entry.access === access)) resources.push({ resource, access });
@@ -1077,6 +1091,83 @@ function invocationResources(authorization) {
1077
1091
  if (resources.length === 0) throw new ResourceNotRoutedError("empty invocation");
1078
1092
  return resources;
1079
1093
  }
1094
+ async function routeResourceRequest(resources, routing, send, options) {
1095
+ if (resources.length === 0) throw new ResourceNotRoutedError("empty request");
1096
+ const routes = await Promise.all(resources.map(({ resource, access }) => routing.resolveResource(resource, access)));
1097
+ if (routes.some((route) => route.length === 0)) throw new ResourceNotRoutedError(resources[routes.findIndex((route) => route.length === 0)].resource);
1098
+ const write = options?.independentResources === true || resources.some(({ access }) => access === "write" || access === "host");
1099
+ let targets;
1100
+ if (options?.independentResources) {
1101
+ targets = routes.flat();
1102
+ const identities = /* @__PURE__ */ new Map();
1103
+ for (const target of targets) {
1104
+ if (identities.has(target.url) && identities.get(target.url) !== target.nodeDid) throw new ResourceNotRoutedError(target.url);
1105
+ identities.set(target.url, target.nodeDid);
1106
+ }
1107
+ } else {
1108
+ targets = routes[0].filter((target) => routes.every((route) => route.some((other) => other.url === target.url && other.nodeDid === target.nodeDid)));
1109
+ if (targets.length === 0 || write && routes.some((route) => route.length !== targets.length)) throw new ResourceNotRoutedError(resources.map(({ resource }) => resource).join(", "));
1110
+ }
1111
+ targets = targets.filter((target, index) => targets.findIndex((other) => other.url === target.url) === index);
1112
+ if (options?.nodeDid) targets = targets.filter((target) => target.nodeDid === options.nodeDid || target.nodeDid === void 0);
1113
+ if (targets.length === 0 || options?.nodeLocal && targets.length !== 1) throw new ResourceNotRoutedError(resources.map(({ resource }) => resource).join(", "));
1114
+ if (!write) {
1115
+ let response2;
1116
+ let failure;
1117
+ for (const target of targets) {
1118
+ try {
1119
+ response2 = await send(target);
1120
+ if (response2.ok || response2.status !== 404) return response2;
1121
+ } catch (error) {
1122
+ if (!(error instanceof NodeNotRespondingError)) throw error;
1123
+ failure = error;
1124
+ }
1125
+ }
1126
+ if (failure) throw new NodeNotRespondingError(targets.map(({ url }) => url), failure);
1127
+ if (response2) return response2;
1128
+ throw new NodeNotRespondingError(targets.map(({ url }) => url), failure);
1129
+ }
1130
+ const succeeded = [];
1131
+ const failed = [];
1132
+ let response;
1133
+ let failureResponse;
1134
+ for (const target of targets) {
1135
+ try {
1136
+ const result = await send(target);
1137
+ if (result.ok) {
1138
+ succeeded.push(target.url);
1139
+ response ?? (response = result);
1140
+ } else {
1141
+ failed.push({ url: target.url, status: result.status });
1142
+ failureResponse ?? (failureResponse = result);
1143
+ }
1144
+ } catch (error) {
1145
+ if (!routingFailure(error) && succeeded.length === 0) throw error;
1146
+ failed.push({ url: target.url, cause: error });
1147
+ }
1148
+ }
1149
+ if (failed.length > 0) {
1150
+ if (succeeded.length > 0 || targets.length > 1) throw new PartialNodeWriteError(succeeded, failed);
1151
+ if (failureResponse) return failureResponse;
1152
+ if (failed[0]?.cause && !(failed[0].cause instanceof NodeNotRespondingError)) throw failed[0].cause;
1153
+ throw new NodeNotRespondingError(targets.map(({ url }) => url), failed[0]?.cause);
1154
+ }
1155
+ return response;
1156
+ }
1157
+ function createResourceTransportInvocationFetch(transport, session) {
1158
+ return async (url, init) => {
1159
+ const parsed = new URL(url);
1160
+ if (parsed.pathname !== "/invoke") throw new ResourceNotRoutedError(parsed.pathname);
1161
+ const authorization = authorizationOf(init?.headers);
1162
+ if (!authorization) throw new ResourceNotRoutedError("unsigned invocation");
1163
+ return transport(invocationResources(authorization), async (target) => {
1164
+ const signed = await target.bindAudience(authorization);
1165
+ const headers = (Array.isArray(init?.headers) ? init.headers : Object.entries(init?.headers ?? {})).filter(([name]) => name.toLowerCase() !== "authorization");
1166
+ headers.push(["Authorization", signed]);
1167
+ return target.fetch(`${target.url}${parsed.pathname}${parsed.search}`, { ...init, headers });
1168
+ }, { invocationSession: session });
1169
+ };
1170
+ }
1080
1171
  function createResourceRoutingFetch(fetchFn, routing) {
1081
1172
  return async (url, init) => {
1082
1173
  const parsed = new URL(url);
@@ -1084,12 +1175,6 @@ function createResourceRoutingFetch(fetchFn, routing) {
1084
1175
  const authorization = authorizationOf(init?.headers);
1085
1176
  if (!authorization) throw new ResourceNotRoutedError("unsigned invocation");
1086
1177
  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
1178
  const send = async (target) => {
1094
1179
  init?.signal?.throwIfAborted();
1095
1180
  if (routing.verifyDestination) await routing.verifyDestination(target);
@@ -1107,48 +1192,7 @@ function createResourceRoutingFetch(fetchFn, routing) {
1107
1192
  throw new NodeNotRespondingError([target.url], error);
1108
1193
  }
1109
1194
  };
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;
1195
+ return routeResourceRequest(resources, routing, send);
1152
1196
  };
1153
1197
  }
1154
1198
 
@@ -9080,6 +9124,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
9080
9124
  clearTinyCloudDebugLogs,
9081
9125
  createKVResponseSchema,
9082
9126
  createResourceRoutingFetch,
9127
+ createResourceTransportInvocationFetch,
9083
9128
  createResultSchema,
9084
9129
  createVaultCrypto,
9085
9130
  decryptEnvelopeWithKey,
@@ -9122,7 +9167,9 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
9122
9167
  requiresSecretsOptIn,
9123
9168
  resolveSecretListPrefix,
9124
9169
  resolveSecretPath,
9170
+ routeResourceRequest,
9125
9171
  routingFailure,
9172
+ routingResourceOf,
9126
9173
  serviceError,
9127
9174
  storageLimitReachedError,
9128
9175
  storageQuotaExceededError,