@tinycloud/sdk-services 3.2.0-beta.2 → 3.2.0-beta.5
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-DWbPbov7.d.cts → BaseService-BDQXjWMx.d.cts} +1 -1
- package/dist/{BaseService-B_UKhVht.d.ts → BaseService-Di_hVfUV.d.ts} +1 -1
- package/dist/encryption/index.cjs +7 -1
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +2 -2
- package/dist/encryption/index.d.ts +2 -2
- package/dist/encryption/index.js +7 -1
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +605 -357
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -26
- package/dist/index.d.ts +37 -26
- package/dist/index.js +600 -357
- 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 +540 -338
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +18 -468
- package/dist/kv/index.d.ts +18 -468
- package/dist/kv/index.js +534 -337
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/replication/index.cjs +1398 -0
- package/dist/kv/replication/index.cjs.map +1 -0
- package/dist/kv/replication/index.d.cts +10 -0
- package/dist/kv/replication/index.d.ts +10 -0
- package/dist/kv/replication/index.js +1365 -0
- package/dist/kv/replication/index.js.map +1 -0
- package/dist/kv/replication/prefix.cjs +43 -0
- package/dist/kv/replication/prefix.cjs.map +1 -0
- package/dist/kv/replication/prefix.d.cts +6 -0
- package/dist/kv/replication/prefix.d.ts +6 -0
- package/dist/kv/replication/prefix.js +17 -0
- package/dist/kv/replication/prefix.js.map +1 -0
- package/dist/scope-BKge7zDT.d.ts +957 -0
- package/dist/scope-C05VVAXH.d.cts +957 -0
- package/dist/sql/index.cjs +12 -6
- package/dist/sql/index.cjs.map +1 -1
- package/dist/sql/index.d.cts +2 -2
- package/dist/sql/index.d.ts +2 -2
- package/dist/sql/index.js +12 -6
- package/dist/sql/index.js.map +1 -1
- package/dist/{types-CVwwxqXF.d.cts → types-7A5rHWRY.d.cts} +10 -1
- package/dist/{types-CVwwxqXF.d.ts → types-7A5rHWRY.d.ts} +10 -1
- package/package.json +31 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BaseService: () => BaseService,
|
|
24
|
+
CLEAR_PENDING_WARNING: () => CLEAR_PENDING_WARNING,
|
|
24
25
|
DECRYPT_ACTION: () => DECRYPT_ACTION,
|
|
25
26
|
DECRYPT_FACT_TYPE: () => DECRYPT_FACT_TYPE,
|
|
26
27
|
DECRYPT_RESULT_TYPE: () => DECRYPT_RESULT_TYPE,
|
|
@@ -88,6 +89,7 @@ __export(src_exports, {
|
|
|
88
89
|
buildDecryptInvocation: () => buildDecryptInvocation,
|
|
89
90
|
buildNetworkId: () => buildNetworkId,
|
|
90
91
|
canonicalHashHex: () => canonicalHashHex,
|
|
92
|
+
canonicalReplicationIdentity: () => canonicalReplicationIdentity,
|
|
91
93
|
canonicalSignedResponse: () => canonicalSignedResponse,
|
|
92
94
|
canonicalizeEncryptionJson: () => canonicalize,
|
|
93
95
|
canonicalizeSecretScope: () => canonicalizeSecretScope,
|
|
@@ -114,6 +116,7 @@ __export(src_exports, {
|
|
|
114
116
|
installTinyCloudDebugGlobals: () => installTinyCloudDebugGlobals,
|
|
115
117
|
isNetworkId: () => isNetworkId,
|
|
116
118
|
isStorageFullError: () => isStorageFullError,
|
|
119
|
+
kvPrefixCovers: () => kvPrefixCovers,
|
|
117
120
|
networkDiscoveryKey: () => networkDiscoveryKey,
|
|
118
121
|
networkError: () => networkError,
|
|
119
122
|
notFoundError: () => notFoundError,
|
|
@@ -127,6 +130,8 @@ __export(src_exports, {
|
|
|
127
130
|
parseSecretCatalogKey: () => parseSecretCatalogKey,
|
|
128
131
|
parseStorageQuotaBytes: () => parseStorageQuotaBytes,
|
|
129
132
|
permissionDeniedError: () => permissionDeniedError,
|
|
133
|
+
replicationIdentityKey: () => replicationIdentityKey,
|
|
134
|
+
requiresSecretsOptIn: () => requiresSecretsOptIn,
|
|
130
135
|
resolveSecretListPrefix: () => resolveSecretListPrefix,
|
|
131
136
|
resolveSecretPath: () => resolveSecretPath,
|
|
132
137
|
serviceError: () => serviceError,
|
|
@@ -214,7 +219,12 @@ var TelemetryEvents = {
|
|
|
214
219
|
SERVICE_ERROR: "service.error",
|
|
215
220
|
SERVICE_RETRY: "service.retry",
|
|
216
221
|
SESSION_CHANGED: "session.changed",
|
|
217
|
-
SESSION_EXPIRED: "session.expired"
|
|
222
|
+
SESSION_EXPIRED: "session.expired",
|
|
223
|
+
REPLICATION_READ: "replication.read",
|
|
224
|
+
REPLICATION_WRITE: "replication.write",
|
|
225
|
+
REPLICATION_SYNC: "replication.sync",
|
|
226
|
+
REPLICATION_STATE: "replication.state",
|
|
227
|
+
REPLICATION_DIVERGENCE: "replication.divergence"
|
|
218
228
|
};
|
|
219
229
|
function ok(data) {
|
|
220
230
|
return { ok: true, data };
|
|
@@ -240,9 +250,34 @@ var SAFE_NUMBER_FIELDS = /* @__PURE__ */ new Set([
|
|
|
240
250
|
"endedAt",
|
|
241
251
|
"startedAt",
|
|
242
252
|
"status",
|
|
243
|
-
"timestamp"
|
|
253
|
+
"timestamp",
|
|
254
|
+
"latencyMs",
|
|
255
|
+
"stalenessMs",
|
|
256
|
+
"count",
|
|
257
|
+
"changes",
|
|
258
|
+
"deleted",
|
|
259
|
+
"fetched",
|
|
260
|
+
"pages",
|
|
261
|
+
"lagMs",
|
|
262
|
+
"pendingCleared"
|
|
244
263
|
]);
|
|
245
264
|
var SAFE_BOOLEAN_FIELDS = /* @__PURE__ */ new Set(["authenticated", "ok", "persisted"]);
|
|
265
|
+
var SAFE_REPLICATION_FIELDS = {
|
|
266
|
+
source: ["replica", "network", "none"],
|
|
267
|
+
reason: ["hit", "absent", "deleted", "content_missing", "coverage_incomplete", "not_covered", "pending_write", "grant_missing", "grant_expired", "grant_revoked", "grant_not_yet_valid", "stale", "replica_error", "runtime_unsupported", "replica_unavailable", "unsupported_option", "network_cursor", "cursor_restart", "aborted", "REPLICA_BEHIND_OWN_WRITES", "REPLICA_UNPROVEN_SINCE_START", "NETWORK_REQUESTED"],
|
|
268
|
+
op: ["get", "list", "put", "delete", "batchPut"],
|
|
269
|
+
trigger: ["start", "interval", "stale_read", "manual"],
|
|
270
|
+
outcome: ["found", "not_found", "error", "committed", "failed", "ambiguous", "ok", "busy", "aborted"],
|
|
271
|
+
class: ["offline", "authority", "storage", "node"],
|
|
272
|
+
state: ["opened", "grant_installed", "grant_missing", "runtime_unsupported", "unavailable", "revoked", "recreated", "purged", "purge_failed", "pending_store_error", "pinned", "pending_cleared", "closed", "idle", "ready"],
|
|
273
|
+
kind: ["value", "missing_local", "extra_local", "keys", "order"],
|
|
274
|
+
coverage: ["empty", "bootstrapping", "complete"],
|
|
275
|
+
authority: ["valid", "expired", "revoked", "not-yet-valid"],
|
|
276
|
+
code: ["NETWORK_ERROR", "TIMEOUT", "ABORTED", "REPLICA_BUSY", "REPLICA_BEHIND_OWN_WRITES", "REPLICA_UNPROVEN_SINCE_START", "NETWORK_REQUESTED", "INTEGRITY_ERROR", "STORAGE_ERROR", "REPLICA_CLOSED", "RUNTIME_UNSUPPORTED", "GRANT_EXPIRED", "GRANT_REVOKED", "GRANT_NOT_YET_VALID", "GRANT_MISSING", "GRANT_INVALID", "SOURCE_CHANGED", "PROTOCOL_ERROR", "SCOPE_VIOLATION", "NODE_ERROR", "CONTENT_MISMATCH", "STORAGE_FULL"],
|
|
277
|
+
pendingState: ["in_flight", "committed", "ambiguous"],
|
|
278
|
+
strategy: ["session", "minted", "installed"],
|
|
279
|
+
verify: ["match", "diverged", "error", "aborted"]
|
|
280
|
+
};
|
|
246
281
|
function finiteNumber(value) {
|
|
247
282
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
248
283
|
}
|
|
@@ -279,6 +314,12 @@ function projectDiagnosticData(value) {
|
|
|
279
314
|
const boolean = read(value, key);
|
|
280
315
|
if (typeof boolean === "boolean") projected[key] = boolean;
|
|
281
316
|
}
|
|
317
|
+
for (const [key, allowed] of Object.entries(SAFE_REPLICATION_FIELDS)) {
|
|
318
|
+
const candidate = read(value, key);
|
|
319
|
+
if (typeof candidate === "string" && allowed.includes(candidate)) {
|
|
320
|
+
projected[key] = candidate;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
282
323
|
const url = read(value, "url");
|
|
283
324
|
if (url !== void 0) projected.url = REDACTED;
|
|
284
325
|
const error = read(value, "error");
|
|
@@ -903,6 +944,12 @@ var ServiceContext = class {
|
|
|
903
944
|
get abortSignal() {
|
|
904
945
|
return this._abortController.signal;
|
|
905
946
|
}
|
|
947
|
+
get operationAbortSignal() {
|
|
948
|
+
return this._operationAbortSignal;
|
|
949
|
+
}
|
|
950
|
+
setOperationAbortSignal(signal) {
|
|
951
|
+
this._operationAbortSignal = signal;
|
|
952
|
+
}
|
|
906
953
|
/**
|
|
907
954
|
* Permanently retire this graph after its owner installs a replacement.
|
|
908
955
|
* Unlike `abort()`, retirement never creates a fresh controller, so captured
|
|
@@ -1654,6 +1701,7 @@ var BaseService = class {
|
|
|
1654
1701
|
const parents = [
|
|
1655
1702
|
this.abortController.signal,
|
|
1656
1703
|
this.context?.abortSignal,
|
|
1704
|
+
this.context?.operationAbortSignal,
|
|
1657
1705
|
signal
|
|
1658
1706
|
].filter((parent) => parent !== void 0);
|
|
1659
1707
|
for (const parent of parents) {
|
|
@@ -1899,14 +1947,28 @@ var KVService = class extends BaseService {
|
|
|
1899
1947
|
*/
|
|
1900
1948
|
constructor(config = {}) {
|
|
1901
1949
|
super();
|
|
1950
|
+
this.readThrough = null;
|
|
1951
|
+
this.readThroughReady = null;
|
|
1902
1952
|
this._config = config;
|
|
1903
1953
|
}
|
|
1954
|
+
/** Delay KV routing until an asynchronously loaded read-through is attached. */
|
|
1955
|
+
setReadThroughReady(ready) {
|
|
1956
|
+
const settled = ready.then(() => void 0, () => void 0);
|
|
1957
|
+
this.readThroughReady = settled;
|
|
1958
|
+
void settled.then(() => {
|
|
1959
|
+
if (this.readThroughReady === settled) this.readThroughReady = null;
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1904
1962
|
/**
|
|
1905
1963
|
* Get the service configuration.
|
|
1906
1964
|
*/
|
|
1907
1965
|
get config() {
|
|
1908
1966
|
return this._config;
|
|
1909
1967
|
}
|
|
1968
|
+
/** Install or remove the optional replication read-through policy. */
|
|
1969
|
+
setReadThrough(readThrough) {
|
|
1970
|
+
this.readThrough = readThrough;
|
|
1971
|
+
}
|
|
1910
1972
|
handleQuotaErrorResponse(response, errorText, key) {
|
|
1911
1973
|
if (response.status !== 402 && response.status !== 413) {
|
|
1912
1974
|
return void 0;
|
|
@@ -2160,6 +2222,7 @@ var KVService = class extends BaseService {
|
|
|
2160
2222
|
return aligned;
|
|
2161
2223
|
}
|
|
2162
2224
|
async batchRead(keys, action, options) {
|
|
2225
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
2163
2226
|
if (!this.requireAuth()) return err(authRequiredError("kv"));
|
|
2164
2227
|
if (keys.length === 0) return ok({ results: [], count: 0 });
|
|
2165
2228
|
if (keys.length > MAX_KV_BATCH_READ_ITEMS) {
|
|
@@ -2410,6 +2473,7 @@ var KVService = class extends BaseService {
|
|
|
2410
2473
|
*/
|
|
2411
2474
|
async get(key, options) {
|
|
2412
2475
|
return this.withTelemetry("get", key, async () => {
|
|
2476
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
2413
2477
|
if (!this.requireAuth()) {
|
|
2414
2478
|
return err(authRequiredError("kv"));
|
|
2415
2479
|
}
|
|
@@ -2423,64 +2487,38 @@ var KVService = class extends BaseService {
|
|
|
2423
2487
|
const path = this.getFullPath(key, options?.prefix);
|
|
2424
2488
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
2425
2489
|
try {
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
response,
|
|
2449
|
-
errorText2,
|
|
2450
|
-
[path],
|
|
2451
|
-
KVAction.GET,
|
|
2452
|
-
structuredCapability ? { permissionHint, ...structuredCapability } : {}
|
|
2453
|
-
);
|
|
2454
|
-
}
|
|
2455
|
-
if (response.status === 404) {
|
|
2456
|
-
return await this.classifyNotFound(response, key);
|
|
2457
|
-
}
|
|
2458
|
-
const errorText = await response.text();
|
|
2459
|
-
if (response.status === 413) {
|
|
2460
|
-
return err(serviceError(
|
|
2461
|
-
ErrorCodes.KV_RESPONSE_TOO_LARGE,
|
|
2462
|
-
`KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
|
|
2463
|
-
"kv",
|
|
2464
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2465
|
-
));
|
|
2490
|
+
const network = () => this.getFromNetwork(key, path, options, request.signal);
|
|
2491
|
+
if (options?.source === "network") {
|
|
2492
|
+
const observer = this.readThrough;
|
|
2493
|
+
if (observer === null) return await network();
|
|
2494
|
+
let outcome = "error";
|
|
2495
|
+
const startedAt = Date.now();
|
|
2496
|
+
try {
|
|
2497
|
+
const result = await network();
|
|
2498
|
+
outcome = result.ok ? "found" : result.error.code === ErrorCodes.KV_NOT_FOUND ? "not_found" : "error";
|
|
2499
|
+
return result;
|
|
2500
|
+
} finally {
|
|
2501
|
+
try {
|
|
2502
|
+
observer.observeNetworkRequested({
|
|
2503
|
+
op: "get",
|
|
2504
|
+
space: this.context.session.spaceId,
|
|
2505
|
+
path,
|
|
2506
|
+
reason: "NETWORK_REQUESTED",
|
|
2507
|
+
outcome,
|
|
2508
|
+
latencyMs: Date.now() - startedAt
|
|
2509
|
+
});
|
|
2510
|
+
} catch {
|
|
2511
|
+
}
|
|
2466
2512
|
}
|
|
2467
|
-
return err(
|
|
2468
|
-
serviceError(
|
|
2469
|
-
ErrorCodes.NETWORK_ERROR,
|
|
2470
|
-
`Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
2471
|
-
"kv",
|
|
2472
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2473
|
-
)
|
|
2474
|
-
);
|
|
2475
2513
|
}
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2514
|
+
if (this.readThrough === null) return await network();
|
|
2515
|
+
return await this.readThrough.get({
|
|
2516
|
+
space: this.context.session.spaceId,
|
|
2517
|
+
key,
|
|
2518
|
+
path,
|
|
2519
|
+
options,
|
|
2520
|
+
signal: request.signal,
|
|
2521
|
+
network
|
|
2484
2522
|
});
|
|
2485
2523
|
} catch (error) {
|
|
2486
2524
|
return err(wrapError("kv", error));
|
|
@@ -2489,6 +2527,56 @@ var KVService = class extends BaseService {
|
|
|
2489
2527
|
}
|
|
2490
2528
|
});
|
|
2491
2529
|
}
|
|
2530
|
+
async getFromNetwork(key, path, options, signal) {
|
|
2531
|
+
const response = await this.invokeOperation(
|
|
2532
|
+
path,
|
|
2533
|
+
KVAction.GET,
|
|
2534
|
+
void 0,
|
|
2535
|
+
signal,
|
|
2536
|
+
options?.maxResponseBytes === void 0 ? void 0 : { "x-tinycloud-max-response-bytes": String(options.maxResponseBytes) }
|
|
2537
|
+
);
|
|
2538
|
+
if (!response.ok) {
|
|
2539
|
+
if (response.status === 401 || response.status === 403) {
|
|
2540
|
+
const errorText2 = await this.readAuthorizationText(response);
|
|
2541
|
+
const permissionHint = parsePermissionHintFromErrorText(errorText2);
|
|
2542
|
+
const structuredCapability = permissionHint?.service === "tinycloud.kv" && permissionHint.space === this.context.session.spaceId && permissionHint.path === path ? validatedCapabilityOf({
|
|
2543
|
+
service: "kv",
|
|
2544
|
+
code: ErrorCodes.AUTH_UNAUTHORIZED,
|
|
2545
|
+
meta: {
|
|
2546
|
+
status: response.status,
|
|
2547
|
+
resource: `${this.context.session.spaceId}/kv/${path}`,
|
|
2548
|
+
requiredAction: KVAction.GET
|
|
2549
|
+
}
|
|
2550
|
+
}) : void 0;
|
|
2551
|
+
return this.authorizationFailure(
|
|
2552
|
+
`Failed to get key ${JSON.stringify(key)}`,
|
|
2553
|
+
response,
|
|
2554
|
+
errorText2,
|
|
2555
|
+
[path],
|
|
2556
|
+
KVAction.GET,
|
|
2557
|
+
structuredCapability ? { permissionHint, ...structuredCapability } : {}
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
if (response.status === 404) return this.classifyNotFound(response, key);
|
|
2561
|
+
const errorText = await response.text();
|
|
2562
|
+
if (response.status === 413) {
|
|
2563
|
+
return err(serviceError(
|
|
2564
|
+
ErrorCodes.KV_RESPONSE_TOO_LARGE,
|
|
2565
|
+
`KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,
|
|
2566
|
+
"kv",
|
|
2567
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2568
|
+
));
|
|
2569
|
+
}
|
|
2570
|
+
return err(serviceError(
|
|
2571
|
+
ErrorCodes.NETWORK_ERROR,
|
|
2572
|
+
`Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
2573
|
+
"kv",
|
|
2574
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2575
|
+
));
|
|
2576
|
+
}
|
|
2577
|
+
const data = await this.parseResponse(response, options?.raw, options?.binary);
|
|
2578
|
+
return ok({ data, headers: this.createResponseHeaders(response.headers) });
|
|
2579
|
+
}
|
|
2492
2580
|
async batchGet(keys, options) {
|
|
2493
2581
|
return this.withTelemetry(
|
|
2494
2582
|
"batchGet",
|
|
@@ -2501,6 +2589,7 @@ var KVService = class extends BaseService {
|
|
|
2501
2589
|
*/
|
|
2502
2590
|
async put(key, value, options) {
|
|
2503
2591
|
return this.withTelemetry("put", key, async () => {
|
|
2592
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
2504
2593
|
if (!this.requireAuth()) {
|
|
2505
2594
|
return err(authRequiredError("kv"));
|
|
2506
2595
|
}
|
|
@@ -2515,63 +2604,15 @@ var KVService = class extends BaseService {
|
|
|
2515
2604
|
const body = this.serializePutValue(value, options?.contentType);
|
|
2516
2605
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
2517
2606
|
try {
|
|
2518
|
-
const
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
{
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
);
|
|
2528
|
-
if (response.status === 401 || response.status === 403) {
|
|
2529
|
-
return this.authorizationFailure(
|
|
2530
|
-
`Failed to put key ${JSON.stringify(key)}`,
|
|
2531
|
-
response,
|
|
2532
|
-
await this.readAuthorizationText(response),
|
|
2533
|
-
[path],
|
|
2534
|
-
KVAction.PUT
|
|
2535
|
-
);
|
|
2536
|
-
}
|
|
2537
|
-
if (!response.ok) {
|
|
2538
|
-
const errorText = await response.text();
|
|
2539
|
-
if (response.status === 412) {
|
|
2540
|
-
return err(serviceError(
|
|
2541
|
-
ErrorCodes.KV_PRECONDITION_FAILED,
|
|
2542
|
-
`KV precondition failed for key ${JSON.stringify(key)}`,
|
|
2543
|
-
"kv",
|
|
2544
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2545
|
-
));
|
|
2546
|
-
}
|
|
2547
|
-
if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
|
|
2548
|
-
return err(serviceError(
|
|
2549
|
-
ErrorCodes.KV_CONFLICT,
|
|
2550
|
-
`Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
|
|
2551
|
-
"kv",
|
|
2552
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2553
|
-
));
|
|
2554
|
-
}
|
|
2555
|
-
const quotaError = this.handleQuotaErrorResponse(
|
|
2556
|
-
response,
|
|
2557
|
-
errorText,
|
|
2558
|
-
key
|
|
2559
|
-
);
|
|
2560
|
-
if (quotaError) {
|
|
2561
|
-
return quotaError;
|
|
2562
|
-
}
|
|
2563
|
-
return err(
|
|
2564
|
-
serviceError(
|
|
2565
|
-
ErrorCodes.KV_WRITE_FAILED,
|
|
2566
|
-
`Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
2567
|
-
"kv",
|
|
2568
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2569
|
-
)
|
|
2570
|
-
);
|
|
2571
|
-
}
|
|
2572
|
-
return ok({
|
|
2573
|
-
data: void 0,
|
|
2574
|
-
headers: this.createResponseHeaders(response.headers)
|
|
2607
|
+
const network = () => this.putToNetwork(key, path, body, options, request.signal);
|
|
2608
|
+
if (this.readThrough === null) return await network();
|
|
2609
|
+
return await this.readThrough.write({
|
|
2610
|
+
op: "put",
|
|
2611
|
+
space: this.context.session.spaceId,
|
|
2612
|
+
entries: [{ path, body }],
|
|
2613
|
+
signal: request.signal,
|
|
2614
|
+
conditional: options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0,
|
|
2615
|
+
network
|
|
2575
2616
|
});
|
|
2576
2617
|
} catch (error) {
|
|
2577
2618
|
return err(wrapError("kv", error));
|
|
@@ -2580,11 +2621,64 @@ var KVService = class extends BaseService {
|
|
|
2580
2621
|
}
|
|
2581
2622
|
});
|
|
2582
2623
|
}
|
|
2624
|
+
async putToNetwork(key, path, body, options, signal) {
|
|
2625
|
+
const response = await this.invokeOperation(
|
|
2626
|
+
path,
|
|
2627
|
+
KVAction.PUT,
|
|
2628
|
+
body,
|
|
2629
|
+
signal,
|
|
2630
|
+
{
|
|
2631
|
+
...options?.ifMatch === void 0 ? {} : { "if-match": options.ifMatch },
|
|
2632
|
+
...options?.ifNoneMatch === void 0 ? {} : { "if-none-match": options.ifNoneMatch }
|
|
2633
|
+
}
|
|
2634
|
+
);
|
|
2635
|
+
if (response.status === 401 || response.status === 403) {
|
|
2636
|
+
return this.authorizationFailure(
|
|
2637
|
+
`Failed to put key ${JSON.stringify(key)}`,
|
|
2638
|
+
response,
|
|
2639
|
+
await this.readAuthorizationText(response),
|
|
2640
|
+
[path],
|
|
2641
|
+
KVAction.PUT
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
if (!response.ok) {
|
|
2645
|
+
const errorText = await response.text();
|
|
2646
|
+
if (response.status === 412) {
|
|
2647
|
+
return err(serviceError(
|
|
2648
|
+
ErrorCodes.KV_PRECONDITION_FAILED,
|
|
2649
|
+
`KV precondition failed for key ${JSON.stringify(key)}`,
|
|
2650
|
+
"kv",
|
|
2651
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2652
|
+
));
|
|
2653
|
+
}
|
|
2654
|
+
if (response.status === 503 && (options?.ifMatch !== void 0 || options?.ifNoneMatch !== void 0)) {
|
|
2655
|
+
return err(serviceError(
|
|
2656
|
+
ErrorCodes.KV_CONFLICT,
|
|
2657
|
+
`Concurrent KV update conflicted for key ${JSON.stringify(key)}`,
|
|
2658
|
+
"kv",
|
|
2659
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2660
|
+
));
|
|
2661
|
+
}
|
|
2662
|
+
const quotaError = this.handleQuotaErrorResponse(response, errorText, key);
|
|
2663
|
+
if (quotaError) return quotaError;
|
|
2664
|
+
return err(serviceError(
|
|
2665
|
+
ErrorCodes.KV_WRITE_FAILED,
|
|
2666
|
+
`Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
2667
|
+
"kv",
|
|
2668
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2669
|
+
));
|
|
2670
|
+
}
|
|
2671
|
+
return ok({
|
|
2672
|
+
data: void 0,
|
|
2673
|
+
headers: this.createResponseHeaders(response.headers)
|
|
2674
|
+
});
|
|
2675
|
+
}
|
|
2583
2676
|
/**
|
|
2584
2677
|
* Store multiple values in one TinyCloud KV invocation.
|
|
2585
2678
|
*/
|
|
2586
2679
|
async batchPut(items, options) {
|
|
2587
2680
|
return this.withTelemetry("batchPut", String(items.length), async () => {
|
|
2681
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
2588
2682
|
if (!this.requireAuth()) {
|
|
2589
2683
|
return err(authRequiredError("kv"));
|
|
2590
2684
|
}
|
|
@@ -2615,155 +2709,177 @@ var KVService = class extends BaseService {
|
|
|
2615
2709
|
}
|
|
2616
2710
|
seen.add(path);
|
|
2617
2711
|
}
|
|
2618
|
-
let requestMayHaveDispatched = false;
|
|
2619
2712
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
2620
2713
|
try {
|
|
2621
|
-
const
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
session,
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2714
|
+
const network = () => this.batchPutToNetwork(items, paths, request.signal);
|
|
2715
|
+
if (this.readThrough === null) return await network();
|
|
2716
|
+
const entries = items.map((item, index) => ({
|
|
2717
|
+
path: paths[index],
|
|
2718
|
+
body: this.serializeBatchPutValue(item)
|
|
2719
|
+
}));
|
|
2720
|
+
return await this.readThrough.write({
|
|
2721
|
+
op: "batchPut",
|
|
2722
|
+
space: session.spaceId,
|
|
2723
|
+
entries,
|
|
2724
|
+
signal: request.signal,
|
|
2725
|
+
network: () => this.batchPutToNetwork(items, paths, request.signal, entries)
|
|
2726
|
+
});
|
|
2727
|
+
} catch (error) {
|
|
2728
|
+
const wrapped = wrapError("kv", error);
|
|
2729
|
+
return err({ ...wrapped, meta: { ...wrapped.meta, requestMayHaveDispatched: false } });
|
|
2730
|
+
} finally {
|
|
2731
|
+
request.dispose();
|
|
2732
|
+
}
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
async batchPutToNetwork(items, paths, signal, entries) {
|
|
2736
|
+
const session = this.context.session;
|
|
2737
|
+
let requestMayHaveDispatched = false;
|
|
2738
|
+
try {
|
|
2739
|
+
const body = new FormData();
|
|
2740
|
+
for (let index = 0; index < items.length; index++) {
|
|
2741
|
+
body.append(
|
|
2742
|
+
encodeKvBatchPartName(paths[index]),
|
|
2743
|
+
entries?.[index]?.body ?? this.serializeBatchPutValue(items[index])
|
|
2636
2744
|
);
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
return this.authorizationFailure(
|
|
2664
|
-
`Failed to batch put ${items.length} key(s)`,
|
|
2665
|
-
response,
|
|
2666
|
-
errorText,
|
|
2667
|
-
paths,
|
|
2668
|
-
KVAction.PUT
|
|
2669
|
-
);
|
|
2670
|
-
}
|
|
2671
|
-
const quotaError = this.handleQuotaErrorResponse(
|
|
2672
|
-
response,
|
|
2673
|
-
errorText,
|
|
2674
|
-
"batch"
|
|
2675
|
-
);
|
|
2676
|
-
if (quotaError) {
|
|
2677
|
-
return quotaError;
|
|
2678
|
-
}
|
|
2745
|
+
}
|
|
2746
|
+
const headers = this.context.invokeAny(
|
|
2747
|
+
session,
|
|
2748
|
+
paths.map((path) => ({
|
|
2749
|
+
spaceId: session.spaceId,
|
|
2750
|
+
service: "kv",
|
|
2751
|
+
path,
|
|
2752
|
+
action: KVAction.PUT
|
|
2753
|
+
}))
|
|
2754
|
+
);
|
|
2755
|
+
const url = `${this.host}/invoke`;
|
|
2756
|
+
const init = {
|
|
2757
|
+
method: "POST",
|
|
2758
|
+
headers,
|
|
2759
|
+
body,
|
|
2760
|
+
signal
|
|
2761
|
+
};
|
|
2762
|
+
const fetchFn = this.context.fetch;
|
|
2763
|
+
requestMayHaveDispatched = true;
|
|
2764
|
+
const response = await fetchFn(url, init);
|
|
2765
|
+
if (!response.ok) {
|
|
2766
|
+
let errorText;
|
|
2767
|
+
try {
|
|
2768
|
+
errorText = await response.text();
|
|
2769
|
+
} catch (textError) {
|
|
2770
|
+
const cause = textError instanceof Error ? textError : new Error(String(textError));
|
|
2679
2771
|
return err(
|
|
2680
2772
|
serviceError(
|
|
2681
2773
|
ErrorCodes.KV_WRITE_FAILED,
|
|
2682
|
-
`Failed to batch put ${items.length} key(s): ${response.status} - ${
|
|
2774
|
+
`Failed to batch put ${items.length} key(s): ${response.status} - <response body could not be read: ${cause.message}>`,
|
|
2683
2775
|
"kv",
|
|
2684
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2776
|
+
{ cause, meta: { status: response.status, statusText: response.statusText } }
|
|
2685
2777
|
)
|
|
2686
2778
|
);
|
|
2687
2779
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
serviceError(
|
|
2696
|
-
timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
|
|
2697
|
-
timedOut ? `KV batchPut response body did not arrive in time: ${cause.message}` : `KV batchPut response was not valid JSON: ${cause.message}`,
|
|
2698
|
-
"kv",
|
|
2699
|
-
{
|
|
2700
|
-
cause,
|
|
2701
|
-
meta: {
|
|
2702
|
-
...timedOut ? { timeoutMs: cause.timeoutMs } : {},
|
|
2703
|
-
requestMayHaveDispatched: true,
|
|
2704
|
-
responseReceived: true,
|
|
2705
|
-
status: response.status,
|
|
2706
|
-
outcome: "batch-unconfirmed"
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
)
|
|
2780
|
+
if (response.status === 401 || response.status === 403) {
|
|
2781
|
+
return this.authorizationFailure(
|
|
2782
|
+
`Failed to batch put ${items.length} key(s)`,
|
|
2783
|
+
response,
|
|
2784
|
+
errorText,
|
|
2785
|
+
paths,
|
|
2786
|
+
KVAction.PUT
|
|
2710
2787
|
);
|
|
2711
2788
|
}
|
|
2712
|
-
const
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
{
|
|
2720
|
-
meta: {
|
|
2721
|
-
requestMayHaveDispatched: true,
|
|
2722
|
-
responseReceived: true,
|
|
2723
|
-
status: response.status,
|
|
2724
|
-
outcome: "batch-unconfirmed"
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
)
|
|
2728
|
-
);
|
|
2789
|
+
const quotaError = this.handleQuotaErrorResponse(
|
|
2790
|
+
response,
|
|
2791
|
+
errorText,
|
|
2792
|
+
"batch"
|
|
2793
|
+
);
|
|
2794
|
+
if (quotaError) {
|
|
2795
|
+
return quotaError;
|
|
2729
2796
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2797
|
+
return err(
|
|
2798
|
+
serviceError(
|
|
2799
|
+
ErrorCodes.KV_WRITE_FAILED,
|
|
2800
|
+
`Failed to batch put ${items.length} key(s): ${response.status} - ${errorText}`,
|
|
2801
|
+
"kv",
|
|
2802
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2803
|
+
)
|
|
2804
|
+
);
|
|
2805
|
+
}
|
|
2806
|
+
let rawBody;
|
|
2807
|
+
try {
|
|
2808
|
+
rawBody = await response.json();
|
|
2809
|
+
} catch (jsonError) {
|
|
2810
|
+
const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));
|
|
2811
|
+
const timedOut = isRequestTimeoutError(cause);
|
|
2812
|
+
return err(
|
|
2813
|
+
serviceError(
|
|
2814
|
+
timedOut ? ErrorCodes.TIMEOUT : ErrorCodes.NETWORK_ERROR,
|
|
2815
|
+
timedOut ? `KV batchPut response body did not arrive in time: ${cause.message}` : `KV batchPut response was not valid JSON: ${cause.message}`,
|
|
2816
|
+
"kv",
|
|
2817
|
+
{
|
|
2818
|
+
cause,
|
|
2819
|
+
meta: {
|
|
2820
|
+
...timedOut ? { timeoutMs: cause.timeoutMs } : {},
|
|
2821
|
+
requestMayHaveDispatched: true,
|
|
2822
|
+
responseReceived: true,
|
|
2823
|
+
status: response.status,
|
|
2824
|
+
outcome: "batch-unconfirmed"
|
|
2746
2825
|
}
|
|
2747
|
-
|
|
2748
|
-
)
|
|
2749
|
-
|
|
2750
|
-
return ok(batchResponse);
|
|
2751
|
-
} catch (error) {
|
|
2752
|
-
const wrapped = wrapError("kv", error);
|
|
2753
|
-
return err({
|
|
2754
|
-
...wrapped,
|
|
2755
|
-
meta: { ...wrapped.meta, requestMayHaveDispatched }
|
|
2756
|
-
});
|
|
2757
|
-
} finally {
|
|
2758
|
-
request.dispose();
|
|
2826
|
+
}
|
|
2827
|
+
)
|
|
2828
|
+
);
|
|
2759
2829
|
}
|
|
2760
|
-
|
|
2830
|
+
const batchResponse = this.normalizeBatchPutResponse(rawBody);
|
|
2831
|
+
if (!batchResponse) {
|
|
2832
|
+
return err(
|
|
2833
|
+
serviceError(
|
|
2834
|
+
ErrorCodes.NETWORK_ERROR,
|
|
2835
|
+
"KV batchPut response did not include matching written keys and count",
|
|
2836
|
+
"kv",
|
|
2837
|
+
{
|
|
2838
|
+
meta: {
|
|
2839
|
+
requestMayHaveDispatched: true,
|
|
2840
|
+
responseReceived: true,
|
|
2841
|
+
status: response.status,
|
|
2842
|
+
outcome: "batch-unconfirmed"
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
)
|
|
2846
|
+
);
|
|
2847
|
+
}
|
|
2848
|
+
const requestedPaths = new Set(paths);
|
|
2849
|
+
const writtenPaths = new Set(batchResponse.written);
|
|
2850
|
+
const matchesRequest = batchResponse.count === paths.length && batchResponse.written.length === paths.length && writtenPaths.size === paths.length && batchResponse.written.every((key) => requestedPaths.has(key));
|
|
2851
|
+
if (!matchesRequest) {
|
|
2852
|
+
return err(
|
|
2853
|
+
serviceError(
|
|
2854
|
+
ErrorCodes.NETWORK_ERROR,
|
|
2855
|
+
`KV batchPut response did not confirm all ${paths.length} requested key(s) were written`,
|
|
2856
|
+
"kv",
|
|
2857
|
+
{
|
|
2858
|
+
meta: {
|
|
2859
|
+
requestMayHaveDispatched: true,
|
|
2860
|
+
responseReceived: true,
|
|
2861
|
+
status: response.status,
|
|
2862
|
+
outcome: "batch-unconfirmed"
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
)
|
|
2866
|
+
);
|
|
2867
|
+
}
|
|
2868
|
+
return ok(batchResponse);
|
|
2869
|
+
} catch (error) {
|
|
2870
|
+
const wrapped = wrapError("kv", error);
|
|
2871
|
+
return err({
|
|
2872
|
+
...wrapped,
|
|
2873
|
+
meta: { ...wrapped.meta, requestMayHaveDispatched }
|
|
2874
|
+
});
|
|
2875
|
+
}
|
|
2761
2876
|
}
|
|
2762
2877
|
/**
|
|
2763
2878
|
* List keys with optional prefix filtering.
|
|
2764
2879
|
*/
|
|
2765
2880
|
async list(options) {
|
|
2766
2881
|
return this.withTelemetry("list", options?.prefix, async () => {
|
|
2882
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
2767
2883
|
if (!this.requireAuth()) {
|
|
2768
2884
|
return err(authRequiredError("kv"));
|
|
2769
2885
|
}
|
|
@@ -2780,49 +2896,44 @@ var KVService = class extends BaseService {
|
|
|
2780
2896
|
}
|
|
2781
2897
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
2782
2898
|
try {
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2899
|
+
const network = () => this.listFromNetwork(listPath, options, request.signal);
|
|
2900
|
+
let page;
|
|
2901
|
+
if (options?.source === "network") {
|
|
2902
|
+
const observer = this.readThrough;
|
|
2903
|
+
if (observer === null) {
|
|
2904
|
+
page = await network();
|
|
2905
|
+
} else {
|
|
2906
|
+
let outcome = "error";
|
|
2907
|
+
const startedAt = Date.now();
|
|
2908
|
+
try {
|
|
2909
|
+
page = await network();
|
|
2910
|
+
outcome = page.ok ? "found" : "error";
|
|
2911
|
+
} finally {
|
|
2912
|
+
try {
|
|
2913
|
+
observer.observeNetworkRequested({
|
|
2914
|
+
op: "list",
|
|
2915
|
+
space: this.context.session.spaceId,
|
|
2916
|
+
path: listPath,
|
|
2917
|
+
reason: "NETWORK_REQUESTED",
|
|
2918
|
+
outcome,
|
|
2919
|
+
latencyMs: Date.now() - startedAt
|
|
2920
|
+
});
|
|
2921
|
+
} catch {
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2802
2924
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
let keys = await this.parseResponse(response, options?.raw);
|
|
2814
|
-
keys = keys ?? [];
|
|
2815
|
-
if (options?.removePrefix && listPath) {
|
|
2816
|
-
const prefixWithSlash = listPath.endsWith("/") ? listPath : `${listPath}/`;
|
|
2817
|
-
keys = keys.map(
|
|
2818
|
-
(key) => key.startsWith(prefixWithSlash) ? key.slice(prefixWithSlash.length) : key
|
|
2819
|
-
);
|
|
2925
|
+
} else if (this.readThrough === null) {
|
|
2926
|
+
page = await network();
|
|
2927
|
+
} else {
|
|
2928
|
+
page = await this.readThrough.list({
|
|
2929
|
+
space: this.context.session.spaceId,
|
|
2930
|
+
listPath,
|
|
2931
|
+
options,
|
|
2932
|
+
signal: request.signal,
|
|
2933
|
+
network
|
|
2934
|
+
});
|
|
2820
2935
|
}
|
|
2821
|
-
return ok(
|
|
2822
|
-
keys,
|
|
2823
|
-
...response.headers.get("x-tinycloud-truncated") === null ? {} : { truncated: response.headers.get("x-tinycloud-truncated") === "true" },
|
|
2824
|
-
...response.headers.get("x-tinycloud-next-cursor") === null ? {} : { nextCursor: response.headers.get("x-tinycloud-next-cursor") ?? void 0 }
|
|
2825
|
-
});
|
|
2936
|
+
return page.ok ? this.finishList(page.data, listPath, options?.removePrefix) : page;
|
|
2826
2937
|
} catch (error) {
|
|
2827
2938
|
return err(wrapError("kv", error));
|
|
2828
2939
|
} finally {
|
|
@@ -2830,6 +2941,56 @@ var KVService = class extends BaseService {
|
|
|
2830
2941
|
}
|
|
2831
2942
|
});
|
|
2832
2943
|
}
|
|
2944
|
+
async listFromNetwork(listPath, options, signal) {
|
|
2945
|
+
const response = await this.invokeOperation(
|
|
2946
|
+
listPath,
|
|
2947
|
+
KVAction.LIST,
|
|
2948
|
+
void 0,
|
|
2949
|
+
signal,
|
|
2950
|
+
options?.limit === void 0 && options?.cursor === void 0 ? void 0 : {
|
|
2951
|
+
...options?.limit === void 0 ? {} : { "x-tinycloud-limit": String(options.limit) },
|
|
2952
|
+
...options?.cursor === void 0 ? {} : { "x-tinycloud-cursor": options.cursor }
|
|
2953
|
+
}
|
|
2954
|
+
);
|
|
2955
|
+
if (!response.ok) {
|
|
2956
|
+
if (response.status === 401 || response.status === 403) {
|
|
2957
|
+
return this.authorizationFailure(
|
|
2958
|
+
"Failed to list keys",
|
|
2959
|
+
response,
|
|
2960
|
+
await this.readAuthorizationText(response),
|
|
2961
|
+
[listPath],
|
|
2962
|
+
KVAction.LIST
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
const errorText = await response.text();
|
|
2966
|
+
return err(serviceError(
|
|
2967
|
+
ErrorCodes.NETWORK_ERROR,
|
|
2968
|
+
`Failed to list keys: ${response.status} - ${errorText}`,
|
|
2969
|
+
"kv",
|
|
2970
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
2971
|
+
));
|
|
2972
|
+
}
|
|
2973
|
+
const keys = await this.parseResponse(response, options?.raw) ?? [];
|
|
2974
|
+
return ok({
|
|
2975
|
+
keys,
|
|
2976
|
+
...response.headers.get("x-tinycloud-truncated") === null ? {} : { truncated: response.headers.get("x-tinycloud-truncated") === "true" },
|
|
2977
|
+
...response.headers.get("x-tinycloud-next-cursor") === null ? {} : { nextCursor: response.headers.get("x-tinycloud-next-cursor") ?? void 0 }
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2980
|
+
finishList(page, listPath, removePrefix) {
|
|
2981
|
+
let keys = page.keys;
|
|
2982
|
+
if (removePrefix && listPath) {
|
|
2983
|
+
const prefixWithSlash = listPath.endsWith("/") ? listPath : `${listPath}/`;
|
|
2984
|
+
keys = keys.map(
|
|
2985
|
+
(key) => key.startsWith(prefixWithSlash) ? key.slice(prefixWithSlash.length) : key
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
return ok({
|
|
2989
|
+
keys,
|
|
2990
|
+
...page.truncated === void 0 ? {} : { truncated: page.truncated },
|
|
2991
|
+
...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor }
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2833
2994
|
/**
|
|
2834
2995
|
* Read one page of the `tinycloud.kv/sync` change feed for a prefix.
|
|
2835
2996
|
*/
|
|
@@ -2852,6 +3013,7 @@ var KVService = class extends BaseService {
|
|
|
2852
3013
|
"kv"
|
|
2853
3014
|
));
|
|
2854
3015
|
}
|
|
3016
|
+
const requestPrefix = options.prefix === "/" ? "" : options.prefix;
|
|
2855
3017
|
const headers = {};
|
|
2856
3018
|
if (options.limit !== void 0) headers["x-tinycloud-limit"] = String(options.limit);
|
|
2857
3019
|
if (options.cursor !== void 0) headers["x-tinycloud-cursor"] = options.cursor;
|
|
@@ -2861,7 +3023,7 @@ var KVService = class extends BaseService {
|
|
|
2861
3023
|
const request = this.createRequestSignal(options.signal, options.timeout);
|
|
2862
3024
|
try {
|
|
2863
3025
|
const response = await this.invokeOperation(
|
|
2864
|
-
|
|
3026
|
+
requestPrefix,
|
|
2865
3027
|
KVAction.SYNC,
|
|
2866
3028
|
void 0,
|
|
2867
3029
|
request.signal,
|
|
@@ -2877,7 +3039,7 @@ var KVService = class extends BaseService {
|
|
|
2877
3039
|
} catch {
|
|
2878
3040
|
body = void 0;
|
|
2879
3041
|
}
|
|
2880
|
-
const page = this.normalizeChangesResponse(body);
|
|
3042
|
+
const page = this.normalizeChangesResponse(body, options.prefix);
|
|
2881
3043
|
if (!page) {
|
|
2882
3044
|
return err(serviceError(
|
|
2883
3045
|
ErrorCodes.NETWORK_ERROR,
|
|
@@ -2957,7 +3119,7 @@ var KVService = class extends BaseService {
|
|
|
2957
3119
|
{ meta }
|
|
2958
3120
|
));
|
|
2959
3121
|
}
|
|
2960
|
-
normalizeChangesResponse(data) {
|
|
3122
|
+
normalizeChangesResponse(data, requestedPrefix) {
|
|
2961
3123
|
if (!isJsonObject(data)) return void 0;
|
|
2962
3124
|
const { changes, more, cursor, source, authority } = data;
|
|
2963
3125
|
if (!Array.isArray(changes) || typeof more !== "boolean" || typeof cursor !== "string" || !isJsonObject(source) || typeof source.nodeDid !== "string" || typeof source.space !== "string" || typeof source.prefix !== "string" || !isJsonObject(authority)) {
|
|
@@ -2989,7 +3151,11 @@ var KVService = class extends BaseService {
|
|
|
2989
3151
|
changes: parsed,
|
|
2990
3152
|
more,
|
|
2991
3153
|
cursor,
|
|
2992
|
-
source: {
|
|
3154
|
+
source: {
|
|
3155
|
+
nodeDid: source.nodeDid,
|
|
3156
|
+
space: source.space,
|
|
3157
|
+
prefix: requestedPrefix === "/" && source.prefix === "" ? "/" : source.prefix
|
|
3158
|
+
},
|
|
2993
3159
|
authority: { notBefore, expiresAt, retainUntil }
|
|
2994
3160
|
};
|
|
2995
3161
|
}
|
|
@@ -2998,61 +3164,22 @@ var KVService = class extends BaseService {
|
|
|
2998
3164
|
*/
|
|
2999
3165
|
async delete(key, options) {
|
|
3000
3166
|
return this.withTelemetry("delete", key, async () => {
|
|
3167
|
+
if (this.readThroughReady !== null) await this.readThroughReady;
|
|
3001
3168
|
if (!this.requireAuth()) {
|
|
3002
3169
|
return err(authRequiredError("kv"));
|
|
3003
3170
|
}
|
|
3004
3171
|
const path = this.getFullPath(key, options?.prefix);
|
|
3005
3172
|
const request = this.createRequestSignal(options?.signal, options?.timeout);
|
|
3006
3173
|
try {
|
|
3007
|
-
const
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
return this.authorizationFailure(
|
|
3017
|
-
`Failed to delete key ${JSON.stringify(key)}`,
|
|
3018
|
-
response,
|
|
3019
|
-
await this.readAuthorizationText(response),
|
|
3020
|
-
[path],
|
|
3021
|
-
KVAction.DELETE
|
|
3022
|
-
);
|
|
3023
|
-
}
|
|
3024
|
-
if (response.status === 404) {
|
|
3025
|
-
return await this.classifyNotFound(response, key);
|
|
3026
|
-
}
|
|
3027
|
-
const errorText = await response.text();
|
|
3028
|
-
if (response.status === 412) {
|
|
3029
|
-
return err(serviceError(
|
|
3030
|
-
ErrorCodes.KV_PRECONDITION_FAILED,
|
|
3031
|
-
`KV precondition failed for key ${JSON.stringify(key)}`,
|
|
3032
|
-
"kv",
|
|
3033
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3034
|
-
));
|
|
3035
|
-
}
|
|
3036
|
-
if (response.status === 503 && options?.ifMatch !== void 0) {
|
|
3037
|
-
return err(serviceError(
|
|
3038
|
-
ErrorCodes.KV_CONFLICT,
|
|
3039
|
-
`Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
|
|
3040
|
-
"kv",
|
|
3041
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3042
|
-
));
|
|
3043
|
-
}
|
|
3044
|
-
return err(
|
|
3045
|
-
serviceError(
|
|
3046
|
-
ErrorCodes.NETWORK_ERROR,
|
|
3047
|
-
`Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
3048
|
-
"kv",
|
|
3049
|
-
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3050
|
-
)
|
|
3051
|
-
);
|
|
3052
|
-
}
|
|
3053
|
-
return ok({
|
|
3054
|
-
data: void 0,
|
|
3055
|
-
headers: this.createResponseHeaders(response.headers)
|
|
3174
|
+
const network = () => this.deleteFromNetwork(key, path, options, request.signal);
|
|
3175
|
+
if (this.readThrough === null) return await network();
|
|
3176
|
+
return await this.readThrough.write({
|
|
3177
|
+
op: "delete",
|
|
3178
|
+
space: this.context.session.spaceId,
|
|
3179
|
+
entries: [{ path }],
|
|
3180
|
+
signal: request.signal,
|
|
3181
|
+
conditional: options?.ifMatch !== void 0,
|
|
3182
|
+
network
|
|
3056
3183
|
});
|
|
3057
3184
|
} catch (error) {
|
|
3058
3185
|
return err(wrapError("kv", error));
|
|
@@ -3061,6 +3188,54 @@ var KVService = class extends BaseService {
|
|
|
3061
3188
|
}
|
|
3062
3189
|
});
|
|
3063
3190
|
}
|
|
3191
|
+
async deleteFromNetwork(key, path, options, signal) {
|
|
3192
|
+
const response = await this.invokeOperation(
|
|
3193
|
+
path,
|
|
3194
|
+
KVAction.DELETE,
|
|
3195
|
+
void 0,
|
|
3196
|
+
signal,
|
|
3197
|
+
options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
|
|
3198
|
+
);
|
|
3199
|
+
if (!response.ok) {
|
|
3200
|
+
if (response.status === 401 || response.status === 403) {
|
|
3201
|
+
return this.authorizationFailure(
|
|
3202
|
+
`Failed to delete key ${JSON.stringify(key)}`,
|
|
3203
|
+
response,
|
|
3204
|
+
await this.readAuthorizationText(response),
|
|
3205
|
+
[path],
|
|
3206
|
+
KVAction.DELETE
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
if (response.status === 404) return this.classifyNotFound(response, key);
|
|
3210
|
+
const errorText = await response.text();
|
|
3211
|
+
if (response.status === 412) {
|
|
3212
|
+
return err(serviceError(
|
|
3213
|
+
ErrorCodes.KV_PRECONDITION_FAILED,
|
|
3214
|
+
`KV precondition failed for key ${JSON.stringify(key)}`,
|
|
3215
|
+
"kv",
|
|
3216
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3217
|
+
));
|
|
3218
|
+
}
|
|
3219
|
+
if (response.status === 503 && options?.ifMatch !== void 0) {
|
|
3220
|
+
return err(serviceError(
|
|
3221
|
+
ErrorCodes.KV_CONFLICT,
|
|
3222
|
+
`Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,
|
|
3223
|
+
"kv",
|
|
3224
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3225
|
+
));
|
|
3226
|
+
}
|
|
3227
|
+
return err(serviceError(
|
|
3228
|
+
ErrorCodes.NETWORK_ERROR,
|
|
3229
|
+
`Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
|
|
3230
|
+
"kv",
|
|
3231
|
+
{ meta: { status: response.status, statusText: response.statusText } }
|
|
3232
|
+
));
|
|
3233
|
+
}
|
|
3234
|
+
return ok({
|
|
3235
|
+
data: void 0,
|
|
3236
|
+
headers: this.createResponseHeaders(response.headers)
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3064
3239
|
/**
|
|
3065
3240
|
* Get metadata for a key without retrieving the value.
|
|
3066
3241
|
*/
|
|
@@ -3228,6 +3403,65 @@ var KVService = class extends BaseService {
|
|
|
3228
3403
|
*/
|
|
3229
3404
|
KVService.serviceName = "kv";
|
|
3230
3405
|
|
|
3406
|
+
// src/kv/replication/constants.ts
|
|
3407
|
+
var CLEAR_PENDING_WARNING = "Clearing pending writes surrenders read-your-writes protection for the cleared keys. If a write whose record was cleared had an unknown outcome or never finished, it may commit now or later; this device can serve an older value until the next sync after that commit, as it would for a write from another device.";
|
|
3408
|
+
|
|
3409
|
+
// src/kv/replication/identity.ts
|
|
3410
|
+
var SPACE_ID = /^(tinycloud:pkh:eip155:\d+:)([^:]+)(:.+)$/;
|
|
3411
|
+
var PRINCIPAL_DID = /^(did:pkh:eip155:\d+:)([^:]+)$/;
|
|
3412
|
+
function canonicalReplicationIdentity(raw) {
|
|
3413
|
+
if (!raw || typeof raw.host !== "string" || typeof raw.space !== "string" || typeof raw.principal !== "string") {
|
|
3414
|
+
throw new TypeError(
|
|
3415
|
+
"Replication identity host, space, and principal must be strings"
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
const authority = /^https?:\/\/([^/?#]*)/i.exec(raw.host)?.[1];
|
|
3419
|
+
let url;
|
|
3420
|
+
try {
|
|
3421
|
+
url = new URL(raw.host);
|
|
3422
|
+
} catch {
|
|
3423
|
+
throw new TypeError(
|
|
3424
|
+
"Replication identity host must be an absolute HTTP(S) URL"
|
|
3425
|
+
);
|
|
3426
|
+
}
|
|
3427
|
+
if (!authority || authority.includes("@") || raw.host.includes("?") || raw.host.includes("#") || url.username || url.password) {
|
|
3428
|
+
throw new TypeError(
|
|
3429
|
+
"Replication identity host must be HTTP(S) without credentials, query, or fragment"
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
const space = SPACE_ID.exec(raw.space);
|
|
3433
|
+
const principal = PRINCIPAL_DID.exec(raw.principal);
|
|
3434
|
+
if (!space || !principal) {
|
|
3435
|
+
throw new TypeError(
|
|
3436
|
+
"Replication identity requires a full TinyCloud space id and a pkh principal DID"
|
|
3437
|
+
);
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
host: `${url.origin}${url.pathname.replace(/\/+$/, "")}`,
|
|
3441
|
+
space: `${space[1]}${space[2].toLowerCase()}${space[3]}`,
|
|
3442
|
+
principal: `${principal[1]}${principal[2].toLowerCase()}`
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3445
|
+
function replicationIdentityKey(id) {
|
|
3446
|
+
const canonical = canonicalReplicationIdentity(id);
|
|
3447
|
+
return `tc-replication/v1
|
|
3448
|
+
${canonical.host}
|
|
3449
|
+
${canonical.space}
|
|
3450
|
+
${canonical.principal}`;
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// src/kv/replication/prefix.ts
|
|
3454
|
+
function kvPrefixCovers(prefix, key) {
|
|
3455
|
+
return prefix === "/" || key.startsWith(prefix);
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
// src/kv/replication/scope.ts
|
|
3459
|
+
function requiresSecretsOptIn(space, prefix) {
|
|
3460
|
+
if (prefix === "/") return true;
|
|
3461
|
+
if (space === "secrets" || space.endsWith(":secrets")) return true;
|
|
3462
|
+
return prefix.split("/", 1)[0] === "vault";
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3231
3465
|
// src/responseErrors.ts
|
|
3232
3466
|
function parseServiceErrorBody(errorText) {
|
|
3233
3467
|
try {
|
|
@@ -3404,7 +3638,7 @@ var SQLService = class extends BaseService {
|
|
|
3404
3638
|
request.signal
|
|
3405
3639
|
);
|
|
3406
3640
|
if (!response.ok) {
|
|
3407
|
-
return this.handleErrorResponse(response, "query", dbName, [action]);
|
|
3641
|
+
return await this.handleErrorResponse(response, "query", dbName, [action]);
|
|
3408
3642
|
}
|
|
3409
3643
|
const data = await response.json();
|
|
3410
3644
|
return ok(data);
|
|
@@ -3444,7 +3678,7 @@ var SQLService = class extends BaseService {
|
|
|
3444
3678
|
request.signal
|
|
3445
3679
|
);
|
|
3446
3680
|
if (!response.ok) {
|
|
3447
|
-
return this.handleErrorResponse(response, "execute", dbName, requestedActions);
|
|
3681
|
+
return await this.handleErrorResponse(response, "execute", dbName, requestedActions);
|
|
3448
3682
|
}
|
|
3449
3683
|
const data = await response.json();
|
|
3450
3684
|
return ok(data);
|
|
@@ -3470,7 +3704,7 @@ var SQLService = class extends BaseService {
|
|
|
3470
3704
|
request.signal
|
|
3471
3705
|
);
|
|
3472
3706
|
if (!response.ok) {
|
|
3473
|
-
return this.handleErrorResponse(response, "batch", dbName, requestedActions);
|
|
3707
|
+
return await this.handleErrorResponse(response, "batch", dbName, requestedActions);
|
|
3474
3708
|
}
|
|
3475
3709
|
const data = await response.json();
|
|
3476
3710
|
return ok(data);
|
|
@@ -3495,7 +3729,7 @@ var SQLService = class extends BaseService {
|
|
|
3495
3729
|
request.signal
|
|
3496
3730
|
);
|
|
3497
3731
|
if (!response.ok) {
|
|
3498
|
-
return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
|
|
3732
|
+
return await this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
|
|
3499
3733
|
}
|
|
3500
3734
|
const data = await response.json();
|
|
3501
3735
|
return ok(data);
|
|
@@ -3520,7 +3754,7 @@ var SQLService = class extends BaseService {
|
|
|
3520
3754
|
request.signal
|
|
3521
3755
|
);
|
|
3522
3756
|
if (!response.ok) {
|
|
3523
|
-
return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
|
|
3757
|
+
return await this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
|
|
3524
3758
|
}
|
|
3525
3759
|
const resp = response;
|
|
3526
3760
|
if (typeof resp.blob === "function") {
|
|
@@ -3896,7 +4130,7 @@ var DuckDbService = class extends BaseService {
|
|
|
3896
4130
|
request.signal
|
|
3897
4131
|
);
|
|
3898
4132
|
if (!response.ok) {
|
|
3899
|
-
return this.handleErrorResponse(response, "query");
|
|
4133
|
+
return await this.handleErrorResponse(response, "query");
|
|
3900
4134
|
}
|
|
3901
4135
|
const data = await response.json();
|
|
3902
4136
|
return ok(data);
|
|
@@ -3922,7 +4156,7 @@ var DuckDbService = class extends BaseService {
|
|
|
3922
4156
|
{ Accept: "application/vnd.apache.arrow.stream" }
|
|
3923
4157
|
);
|
|
3924
4158
|
if (!response.ok) {
|
|
3925
|
-
return this.handleErrorResponse(response, "queryArrow");
|
|
4159
|
+
return await this.handleErrorResponse(response, "queryArrow");
|
|
3926
4160
|
}
|
|
3927
4161
|
const buffer = await response.arrayBuffer();
|
|
3928
4162
|
return ok(buffer);
|
|
@@ -3955,7 +4189,7 @@ var DuckDbService = class extends BaseService {
|
|
|
3955
4189
|
request.signal
|
|
3956
4190
|
);
|
|
3957
4191
|
if (!response.ok) {
|
|
3958
|
-
return this.handleErrorResponse(response, "execute");
|
|
4192
|
+
return await this.handleErrorResponse(response, "execute");
|
|
3959
4193
|
}
|
|
3960
4194
|
const data = await response.json();
|
|
3961
4195
|
return ok(data);
|
|
@@ -3987,7 +4221,7 @@ var DuckDbService = class extends BaseService {
|
|
|
3987
4221
|
request.signal
|
|
3988
4222
|
);
|
|
3989
4223
|
if (!response.ok) {
|
|
3990
|
-
return this.handleErrorResponse(response, "batch");
|
|
4224
|
+
return await this.handleErrorResponse(response, "batch");
|
|
3991
4225
|
}
|
|
3992
4226
|
const data = await response.json();
|
|
3993
4227
|
return ok(data);
|
|
@@ -4012,7 +4246,7 @@ var DuckDbService = class extends BaseService {
|
|
|
4012
4246
|
request.signal
|
|
4013
4247
|
);
|
|
4014
4248
|
if (!response.ok) {
|
|
4015
|
-
return this.handleErrorResponse(response, "executeStatement");
|
|
4249
|
+
return await this.handleErrorResponse(response, "executeStatement");
|
|
4016
4250
|
}
|
|
4017
4251
|
const data = await response.json();
|
|
4018
4252
|
return ok(data);
|
|
@@ -4037,7 +4271,7 @@ var DuckDbService = class extends BaseService {
|
|
|
4037
4271
|
request.signal
|
|
4038
4272
|
);
|
|
4039
4273
|
if (!response.ok) {
|
|
4040
|
-
return this.handleErrorResponse(response, "describe");
|
|
4274
|
+
return await this.handleErrorResponse(response, "describe");
|
|
4041
4275
|
}
|
|
4042
4276
|
const data = await response.json();
|
|
4043
4277
|
return ok(data);
|
|
@@ -4062,7 +4296,7 @@ var DuckDbService = class extends BaseService {
|
|
|
4062
4296
|
request.signal
|
|
4063
4297
|
);
|
|
4064
4298
|
if (!response.ok) {
|
|
4065
|
-
return this.handleErrorResponse(response, "export");
|
|
4299
|
+
return await this.handleErrorResponse(response, "export");
|
|
4066
4300
|
}
|
|
4067
4301
|
const blob = await response.blob();
|
|
4068
4302
|
return ok(blob);
|
|
@@ -4097,7 +4331,7 @@ var DuckDbService = class extends BaseService {
|
|
|
4097
4331
|
signal: request.signal
|
|
4098
4332
|
});
|
|
4099
4333
|
if (!response.ok) {
|
|
4100
|
-
return this.handleErrorResponse(response, "import");
|
|
4334
|
+
return await this.handleErrorResponse(response, "import");
|
|
4101
4335
|
}
|
|
4102
4336
|
return ok(void 0);
|
|
4103
4337
|
} catch (error) {
|
|
@@ -5809,7 +6043,8 @@ var DataVaultService = class extends BaseService {
|
|
|
5809
6043
|
}
|
|
5810
6044
|
try {
|
|
5811
6045
|
const valueResult = await this.tc.kv.get(`vault/${key}`, {
|
|
5812
|
-
raw: true
|
|
6046
|
+
raw: true,
|
|
6047
|
+
...options?.source === void 0 ? {} : { source: options.source }
|
|
5813
6048
|
});
|
|
5814
6049
|
if (!valueResult.ok) {
|
|
5815
6050
|
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
@@ -5854,7 +6089,7 @@ var DataVaultService = class extends BaseService {
|
|
|
5854
6089
|
}
|
|
5855
6090
|
let valueResult;
|
|
5856
6091
|
try {
|
|
5857
|
-
valueResult = await this.tc.kv.get(`vault/${key}`, { raw: true });
|
|
6092
|
+
valueResult = await this.tc.kv.get(`vault/${key}`, { raw: true, source: "network" });
|
|
5858
6093
|
} catch {
|
|
5859
6094
|
return { status: "node_unreachable" };
|
|
5860
6095
|
}
|
|
@@ -5923,7 +6158,8 @@ var DataVaultService = class extends BaseService {
|
|
|
5923
6158
|
}
|
|
5924
6159
|
try {
|
|
5925
6160
|
const valueResult = await this.tc.kv.get(`vault/${key}`, {
|
|
5926
|
-
raw: true
|
|
6161
|
+
raw: true,
|
|
6162
|
+
source: "network"
|
|
5927
6163
|
});
|
|
5928
6164
|
if (!valueResult.ok) {
|
|
5929
6165
|
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
@@ -6057,7 +6293,8 @@ var DataVaultService = class extends BaseService {
|
|
|
6057
6293
|
}
|
|
6058
6294
|
try {
|
|
6059
6295
|
const keyResult = await this.tc.kv.get(`keys/${key}`, {
|
|
6060
|
-
raw: true
|
|
6296
|
+
raw: true,
|
|
6297
|
+
...options?.source === void 0 ? {} : { source: options.source }
|
|
6061
6298
|
});
|
|
6062
6299
|
if (!keyResult.ok) {
|
|
6063
6300
|
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
@@ -6066,7 +6303,8 @@ var DataVaultService = class extends BaseService {
|
|
|
6066
6303
|
const keyBlobBytes = base64Decode(keyEnvelope.key);
|
|
6067
6304
|
const entryKey = this.crypto.decrypt(this.masterKey, keyBlobBytes);
|
|
6068
6305
|
const valueResult = await this.tc.kv.get(`vault/${key}`, {
|
|
6069
|
-
raw: true
|
|
6306
|
+
raw: true,
|
|
6307
|
+
...options?.source === void 0 ? {} : { source: options.source }
|
|
6070
6308
|
});
|
|
6071
6309
|
if (!valueResult.ok) {
|
|
6072
6310
|
return vaultError({ code: "KEY_NOT_FOUND", key });
|
|
@@ -6176,6 +6414,7 @@ var DataVaultService = class extends BaseService {
|
|
|
6176
6414
|
prefix: listPrefix,
|
|
6177
6415
|
removePrefix: true,
|
|
6178
6416
|
...options?.limit === void 0 ? {} : { limit: options.limit },
|
|
6417
|
+
...options?.source === void 0 ? {} : { source: options.source },
|
|
6179
6418
|
...options?.cursor === void 0 ? {} : { cursor: options.cursor }
|
|
6180
6419
|
});
|
|
6181
6420
|
if (!listResult.ok) {
|
|
@@ -6900,7 +7139,9 @@ var SecretsService = class {
|
|
|
6900
7139
|
async get(name, options) {
|
|
6901
7140
|
const secretPath = resolveSecretPathResult(name, options);
|
|
6902
7141
|
if ("ok" in secretPath) return secretPath;
|
|
6903
|
-
const result = await this.vault.get(secretPath.vaultKey)
|
|
7142
|
+
const result = options?.source === void 0 ? await this.vault.get(secretPath.vaultKey) : await this.vault.get(secretPath.vaultKey, {
|
|
7143
|
+
source: options.source
|
|
7144
|
+
});
|
|
6904
7145
|
if (!result.ok) {
|
|
6905
7146
|
return result;
|
|
6906
7147
|
}
|
|
@@ -6934,7 +7175,8 @@ var SecretsService = class {
|
|
|
6934
7175
|
}
|
|
6935
7176
|
const result = await this.vault.list({
|
|
6936
7177
|
prefix,
|
|
6937
|
-
removePrefix: true
|
|
7178
|
+
removePrefix: true,
|
|
7179
|
+
...options?.source === void 0 ? {} : { source: options.source }
|
|
6938
7180
|
});
|
|
6939
7181
|
if (!result.ok) {
|
|
6940
7182
|
return result;
|
|
@@ -6953,6 +7195,7 @@ var SecretsService = class {
|
|
|
6953
7195
|
prefix: "secrets/",
|
|
6954
7196
|
removePrefix: true,
|
|
6955
7197
|
limit: 1e3,
|
|
7198
|
+
source: "network",
|
|
6956
7199
|
...cursor === void 0 ? {} : { cursor }
|
|
6957
7200
|
});
|
|
6958
7201
|
if (!result.ok) {
|
|
@@ -7945,6 +8188,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
7945
8188
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7946
8189
|
0 && (module.exports = {
|
|
7947
8190
|
BaseService,
|
|
8191
|
+
CLEAR_PENDING_WARNING,
|
|
7948
8192
|
DECRYPT_ACTION,
|
|
7949
8193
|
DECRYPT_FACT_TYPE,
|
|
7950
8194
|
DECRYPT_RESULT_TYPE,
|
|
@@ -8012,6 +8256,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8012
8256
|
buildDecryptInvocation,
|
|
8013
8257
|
buildNetworkId,
|
|
8014
8258
|
canonicalHashHex,
|
|
8259
|
+
canonicalReplicationIdentity,
|
|
8015
8260
|
canonicalSignedResponse,
|
|
8016
8261
|
canonicalizeEncryptionJson,
|
|
8017
8262
|
canonicalizeSecretScope,
|
|
@@ -8038,6 +8283,7 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8038
8283
|
installTinyCloudDebugGlobals,
|
|
8039
8284
|
isNetworkId,
|
|
8040
8285
|
isStorageFullError,
|
|
8286
|
+
kvPrefixCovers,
|
|
8041
8287
|
networkDiscoveryKey,
|
|
8042
8288
|
networkError,
|
|
8043
8289
|
notFoundError,
|
|
@@ -8051,6 +8297,8 @@ var import_decrypt_transport_response_error3 = require("@tinycloud/sdk-services/
|
|
|
8051
8297
|
parseSecretCatalogKey,
|
|
8052
8298
|
parseStorageQuotaBytes,
|
|
8053
8299
|
permissionDeniedError,
|
|
8300
|
+
replicationIdentityKey,
|
|
8301
|
+
requiresSecretsOptIn,
|
|
8054
8302
|
resolveSecretListPrefix,
|
|
8055
8303
|
resolveSecretPath,
|
|
8056
8304
|
serviceError,
|