@tinycloud/sdk-services 3.2.0-beta.3 → 3.2.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseService-BDQXjWMx.d.cts → BaseService-4wwJq1Gn.d.cts} +1 -1
- package/dist/{BaseService-Di_hVfUV.d.ts → BaseService-BGr2slUz.d.ts} +1 -1
- package/dist/encryption/index.cjs +5 -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 +5 -1
- package/dist/encryption/index.js.map +1 -1
- package/dist/index.cjs +190 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +157 -6
- package/dist/index.d.ts +157 -6
- package/dist/index.js +184 -11
- 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 +56 -11
- package/dist/kv/index.cjs.map +1 -1
- package/dist/kv/index.d.cts +5 -4
- package/dist/kv/index.d.ts +5 -4
- package/dist/kv/index.js +56 -11
- package/dist/kv/index.js.map +1 -1
- package/dist/kv/replication/index.cjs +44 -19
- package/dist/kv/replication/index.cjs.map +1 -1
- package/dist/kv/replication/index.d.cts +4 -3
- package/dist/kv/replication/index.d.ts +4 -3
- package/dist/kv/replication/index.js +44 -19
- package/dist/kv/replication/index.js.map +1 -1
- 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-BmLxwyl-.d.ts → scope-CoKKmMiI.d.ts} +5 -5
- package/dist/{scope-D855kRDx.d.cts → scope-D7wmjbMH.d.cts} +5 -5
- package/dist/sql/index.cjs +44 -2
- 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 +44 -2
- package/dist/sql/index.js.map +1 -1
- package/dist/{types-7A5rHWRY.d.cts → types-D-9A3Sut.d.cts} +6 -0
- package/dist/{types-7A5rHWRY.d.ts → types-D-9A3Sut.d.ts} +6 -0
- package/package.json +14 -1
|
@@ -77,16 +77,17 @@ ${canonical.space}
|
|
|
77
77
|
${canonical.principal}`;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// src/kv/replication/
|
|
80
|
+
// src/kv/replication/prefix.ts
|
|
81
81
|
function kvPrefixCovers(prefix, key) {
|
|
82
|
-
|
|
83
|
-
if (prefix.endsWith("/")) return key.startsWith(prefix);
|
|
84
|
-
return key === prefix || key.startsWith(`${prefix}/`);
|
|
82
|
+
return prefix === "/" || key.startsWith(prefix);
|
|
85
83
|
}
|
|
84
|
+
|
|
85
|
+
// src/kv/replication/scope.ts
|
|
86
86
|
function listRangeCovers(path, key) {
|
|
87
|
-
return key === path || key.startsWith(path.endsWith("/") ? path : `${path}/`);
|
|
87
|
+
return path === "" || key === path || key.startsWith(path.endsWith("/") ? path : `${path}/`);
|
|
88
88
|
}
|
|
89
89
|
function requiresSecretsOptIn(space, prefix) {
|
|
90
|
+
if (prefix === "/") return true;
|
|
90
91
|
if (space === "secrets" || space.endsWith(":secrets")) return true;
|
|
91
92
|
return prefix.split("/", 1)[0] === "vault";
|
|
92
93
|
}
|
|
@@ -132,6 +133,8 @@ var ErrorCodes = {
|
|
|
132
133
|
// Storage quota errors
|
|
133
134
|
STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
|
|
134
135
|
STORAGE_LIMIT_REACHED: "STORAGE_LIMIT_REACHED",
|
|
136
|
+
/** The node answered the usage read without usage numbers (it predates the read). */
|
|
137
|
+
STORAGE_STATUS_UNAVAILABLE: "STORAGE_STATUS_UNAVAILABLE",
|
|
135
138
|
// DuckDB-specific errors
|
|
136
139
|
DUCKDB_ERROR: "DUCKDB_ERROR",
|
|
137
140
|
DUCKDB_PERMISSION_DENIED: "DUCKDB_PERMISSION_DENIED",
|
|
@@ -371,9 +374,9 @@ function cursorRestart() {
|
|
|
371
374
|
}
|
|
372
375
|
function localList(handle, space, path, limit, cursor, validateMeta) {
|
|
373
376
|
const after = cursor ? decodeTcr1(cursor)?.last : void 0;
|
|
374
|
-
const exact = !path.endsWith("/") && (after === void 0 || utf8Compare(path, after) > 0) ? awaitExact(handle, path, after, validateMeta) : Promise.resolve(false);
|
|
377
|
+
const exact = path !== "" && !path.endsWith("/") && (after === void 0 || utf8Compare(path, after) > 0) ? awaitExact(handle, path, after, validateMeta) : Promise.resolve(false);
|
|
375
378
|
return exact.then(async (hasExact) => {
|
|
376
|
-
const child = path.endsWith("/") ? path : `${path}/`;
|
|
379
|
+
const child = path === "" ? "" : path.endsWith("/") ? path : `${path}/`;
|
|
377
380
|
const want = limit === void 0 ? void 0 : limit + 1 - (hasExact ? 1 : 0);
|
|
378
381
|
const children = await handle.list({ prefix: child, after, limit: want });
|
|
379
382
|
validateMeta(children.meta);
|
|
@@ -390,9 +393,7 @@ async function awaitExact(handle, path, after, validateMeta) {
|
|
|
390
393
|
return result.keys[0] === path;
|
|
391
394
|
}
|
|
392
395
|
function listPathCovered(prefixes, path) {
|
|
393
|
-
|
|
394
|
-
const covering = prefixes.filter((prefix) => kvPrefixCovers(prefix, path) && kvPrefixCovers(prefix, descendant));
|
|
395
|
-
return covering.length === 1 ? covering[0] : void 0;
|
|
396
|
+
return prefixes.find((prefix) => kvPrefixCovers(prefix, path));
|
|
396
397
|
}
|
|
397
398
|
|
|
398
399
|
// src/kv/replication/events.ts
|
|
@@ -421,6 +422,7 @@ function raceSignal(job, signal) {
|
|
|
421
422
|
}
|
|
422
423
|
function createKVReplication(deps) {
|
|
423
424
|
const { options, mode, storage, identity, session, authority, pending, scheduler } = deps;
|
|
425
|
+
const emit = (event) => emitEvent(deps.emit, scheduler, event);
|
|
424
426
|
const replicas = /* @__PURE__ */ new Map();
|
|
425
427
|
const statusInspections = /* @__PURE__ */ new Set();
|
|
426
428
|
let isClosed = false;
|
|
@@ -429,7 +431,6 @@ function createKVReplication(deps) {
|
|
|
429
431
|
let previous = deps.previous;
|
|
430
432
|
let pinnedStateReported = false;
|
|
431
433
|
const inProcessProof = /* @__PURE__ */ new Set();
|
|
432
|
-
const emit = (event) => emitEvent(deps.emit, scheduler, event);
|
|
433
434
|
const configuredPrefix = (path) => options.prefixes.find((prefix) => kvPrefixCovers(prefix, path));
|
|
434
435
|
function recordRevocation(prefix, error) {
|
|
435
436
|
if (errorCode(error) !== "GRANT_REVOKED") return;
|
|
@@ -439,6 +440,20 @@ function createKVReplication(deps) {
|
|
|
439
440
|
state.lastError = "GRANT_REVOKED";
|
|
440
441
|
replicas.set(prefix, state);
|
|
441
442
|
}
|
|
443
|
+
function markRootUnsupported(prefix) {
|
|
444
|
+
const state = replicas.get(prefix) ?? {};
|
|
445
|
+
const firstRefusal = state.lastError !== "REPLICATION_ROOT_UNSUPPORTED";
|
|
446
|
+
state.reason = "replica_unavailable";
|
|
447
|
+
state.lastError = "REPLICATION_ROOT_UNSUPPORTED";
|
|
448
|
+
replicas.set(prefix, state);
|
|
449
|
+
if (firstRefusal) {
|
|
450
|
+
emit({ type: "replication.state", space: session.space, replica: prefix, state: "unavailable", code: "REPLICATION_ROOT_UNSUPPORTED" });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function unsupportedRoot(prefix) {
|
|
454
|
+
markRootUnsupported(prefix);
|
|
455
|
+
return Object.assign(new Error("This node doesn't support whole-space sync yet."), { code: "REPLICATION_ROOT_UNSUPPORTED" });
|
|
456
|
+
}
|
|
442
457
|
async function open(prefix) {
|
|
443
458
|
if (isClosed || purgeStarted) throw Object.assign(new Error("Replication controller is closed"), { code: "REPLICA_CLOSED" });
|
|
444
459
|
let state = replicas.get(prefix);
|
|
@@ -472,6 +487,9 @@ function createKVReplication(deps) {
|
|
|
472
487
|
state.strategy = "session";
|
|
473
488
|
}
|
|
474
489
|
const spec = { identity, space: session.space, prefix, allowSecrets: options.allowSecrets, ...device ? { device } : {}, ...deps.fetch ? { fetch: deps.fetch } : {} };
|
|
490
|
+
if (prefix === "/" && deps.nodeFeatures !== void 0 && !deps.nodeFeatures.includes("kv-sync-root")) {
|
|
491
|
+
throw unsupportedRoot(prefix);
|
|
492
|
+
}
|
|
475
493
|
const handle = await storage.open(spec);
|
|
476
494
|
state.handle = handle;
|
|
477
495
|
emit({ type: "replication.state", space: session.space, replica: prefix, state: "opened" });
|
|
@@ -521,7 +539,7 @@ function createKVReplication(deps) {
|
|
|
521
539
|
state.lastError = code;
|
|
522
540
|
state.failures = (state.failures ?? 0) + 1;
|
|
523
541
|
if (code === "RUNTIME_UNSUPPORTED") state.unsupported = true;
|
|
524
|
-
else if (code !== "REPLICA_CLOSED") state.retryAt = scheduler.now() + Math.min(3e5, 5e3 * 2 ** Math.min(state.failures - 1, 6));
|
|
542
|
+
else if (code !== "REPLICA_CLOSED" && code !== "REPLICATION_ROOT_UNSUPPORTED") state.retryAt = scheduler.now() + Math.min(3e5, 5e3 * 2 ** Math.min(state.failures - 1, 6));
|
|
525
543
|
state.handle?.close().catch(() => void 0);
|
|
526
544
|
state.handle = void 0;
|
|
527
545
|
throw error;
|
|
@@ -538,6 +556,9 @@ function createKVReplication(deps) {
|
|
|
538
556
|
}, options.syncIntervalMs);
|
|
539
557
|
}
|
|
540
558
|
async function syncPrefix(prefix, trigger, signal, context) {
|
|
559
|
+
if (prefix === "/" && deps.nodeFeatures !== void 0 && !deps.nodeFeatures.includes("kv-sync-root")) {
|
|
560
|
+
throw unsupportedRoot(prefix);
|
|
561
|
+
}
|
|
541
562
|
const handle = await open(prefix);
|
|
542
563
|
const state = replicas.get(prefix);
|
|
543
564
|
if (state.sync) return state.sync;
|
|
@@ -839,7 +860,7 @@ function createKVReplication(deps) {
|
|
|
839
860
|
observeNetworkRequested({ op: "get", space: r.space, path: r.path, reason: "NETWORK_REQUESTED", outcome, latencyMs: scheduler.now() - started });
|
|
840
861
|
}
|
|
841
862
|
}
|
|
842
|
-
if (
|
|
863
|
+
if (prefix === void 0) {
|
|
843
864
|
const value = await r.network();
|
|
844
865
|
readEvent("get", r.path, null, "network", "not_covered", value.ok ? "found" : value.error.code === ErrorCodes.KV_NOT_FOUND ? "not_found" : "error", started);
|
|
845
866
|
return value;
|
|
@@ -990,10 +1011,10 @@ function createKVReplication(deps) {
|
|
|
990
1011
|
return cursorRestart();
|
|
991
1012
|
};
|
|
992
1013
|
if (cursor && decoded && (decoded.space !== r.space || decoded.path !== r.listPath)) return restart();
|
|
993
|
-
if (
|
|
1014
|
+
if (prefix === void 0 || r.options?.raw) {
|
|
994
1015
|
if (cursor && decoded) return restart();
|
|
995
1016
|
const result = await r.network();
|
|
996
|
-
readEvent("list", r.listPath, prefix ?? null, "network", prefix ? "unsupported_option" : "not_covered", result.ok ? "found" : "error", started);
|
|
1017
|
+
readEvent("list", r.listPath, prefix ?? null, "network", prefix !== void 0 ? "unsupported_option" : "not_covered", result.ok ? "found" : "error", started);
|
|
997
1018
|
return result;
|
|
998
1019
|
}
|
|
999
1020
|
let handle;
|
|
@@ -1088,7 +1109,7 @@ function createKVReplication(deps) {
|
|
|
1088
1109
|
async function write(r) {
|
|
1089
1110
|
const covered = r.entries.flatMap((entry) => {
|
|
1090
1111
|
const prefix = configuredPrefix(entry.path);
|
|
1091
|
-
return prefix ? [{ key: entry.path, op: r.op === "delete" ? "delete" : "put" }] : [];
|
|
1112
|
+
return prefix !== void 0 ? [{ key: entry.path, op: r.op === "delete" ? "delete" : "put" }] : [];
|
|
1092
1113
|
});
|
|
1093
1114
|
if (covered.length === 0) return r.network();
|
|
1094
1115
|
await reportExistingPins();
|
|
@@ -1164,6 +1185,9 @@ function createKVReplication(deps) {
|
|
|
1164
1185
|
const persistedSnapshot = pending.readExisting ? await pending.readExisting() : await pending.read();
|
|
1165
1186
|
const snapshot = persistedSnapshot ?? emptyPendingState(identity);
|
|
1166
1187
|
const entries = await Promise.all(options.prefixes.map(async (prefix) => {
|
|
1188
|
+
if (prefix === "/" && deps.nodeFeatures !== void 0 && !deps.nodeFeatures.includes("kv-sync-root")) {
|
|
1189
|
+
markRootUnsupported(prefix);
|
|
1190
|
+
}
|
|
1167
1191
|
const records = snapshot.records.filter((r) => kvPrefixCovers(prefix, r.key));
|
|
1168
1192
|
const opened = replicas.get(prefix);
|
|
1169
1193
|
let local;
|
|
@@ -1178,12 +1202,13 @@ function createKVReplication(deps) {
|
|
|
1178
1202
|
local ?? (local = inspected.local);
|
|
1179
1203
|
if (inspected.missing && !opened?.reason && !opened?.authorityRevoked && !opened?.opening && !isClosed) return void 0;
|
|
1180
1204
|
const revoked = opened?.authorityRevoked === true || local?.authority.state === "revoked";
|
|
1181
|
-
const
|
|
1205
|
+
const rootUnsupported = opened?.lastError === "REPLICATION_ROOT_UNSUPPORTED";
|
|
1206
|
+
const state = isClosed ? "closed" : revoked ? "revoked" : rootUnsupported ? "unavailable" : opened?.reason === "grant_missing" ? "grant_missing" : local?.coverage === "complete" && local.authority.state === "valid" ? "ready" : opened?.reason || inspected.errorCode ? "unavailable" : "idle";
|
|
1182
1207
|
return {
|
|
1183
1208
|
prefix,
|
|
1184
1209
|
state,
|
|
1185
1210
|
...opened?.reason ? { reason: opened.reason } : inspected.errorCode ? { reason: "replica_unavailable" } : {},
|
|
1186
|
-
...inspected.errorCode ? { errorCode: inspected.errorCode } : {},
|
|
1211
|
+
...inspected.errorCode ?? opened?.lastError ? { errorCode: inspected.errorCode ?? opened?.lastError } : {},
|
|
1187
1212
|
...local ?? {},
|
|
1188
1213
|
...revoked ? { authority: { state: "revoked", expiresAt: local?.authority.expiresAt ?? null } } : {},
|
|
1189
1214
|
pending: {
|
|
@@ -1198,7 +1223,7 @@ function createKVReplication(deps) {
|
|
|
1198
1223
|
return entries.filter((entry) => entry !== void 0);
|
|
1199
1224
|
}
|
|
1200
1225
|
async function sync(o) {
|
|
1201
|
-
const prefixes = o?.prefix ? options.prefixes.filter((p) => p === o.prefix) : options.prefixes;
|
|
1226
|
+
const prefixes = o?.prefix !== void 0 ? options.prefixes.filter((p) => p === o.prefix) : options.prefixes;
|
|
1202
1227
|
await Promise.all(prefixes.map((prefix) => syncPrefix(prefix, "manual", o?.signal).then(() => void 0)));
|
|
1203
1228
|
}
|
|
1204
1229
|
async function clearPending2() {
|