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