@voltro/runtime 0.86.0 → 0.88.0
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/CHANGELOG.md +5484 -0
- package/README.md +43 -0
- package/THIRD-PARTY-NOTICES.md +1511 -276
- package/dist/aggregate.js +1 -2
- package/dist/aggregateExtrema.js +1 -2
- package/dist/aggregateIvm.js +1 -2
- package/dist/analyticsMirror.js +1 -2
- package/dist/analyticsSink.js +9 -16
- package/dist/apiKeyUsage.js +1 -2
- package/dist/apiKeys.js +83 -80
- package/dist/apiKeysWiring.js +1 -2
- package/dist/apiRoleBucketSampler.js +63 -0
- package/dist/apiRoleWorkloadHistory.js +55 -0
- package/dist/apiRoleWorkloadMetrics.js +77 -0
- package/dist/apiRoleWorkloadStorage.js +94 -0
- package/dist/approvalBuiltins.js +1 -2
- package/dist/approvals.js +68 -76
- package/dist/authMiddlewareLayer.js +8 -8
- package/dist/bindEvent.js +49 -30
- package/dist/boot-refusal.d.ts +10 -0
- package/dist/{bootRefusal.js → boot-refusal.js} +1 -2
- package/dist/bootListener.js +203 -47
- package/dist/bootStoreCodec.js +39 -39
- package/dist/businessRules.js +1 -2
- package/dist/changeClaimsTable.js +1 -2
- package/dist/circuitBreaker.js +1 -72
- package/dist/columnOnUpdate.js +1 -2
- package/dist/composeAnalytics.js +32 -12
- package/dist/computedQuery.js +1 -2
- package/dist/computedQueryCache.js +289 -0
- package/dist/connectionBuiltins.js +1 -2
- package/dist/connectionCredentials.js +1 -2
- package/dist/connectionState.js +1 -2
- package/dist/connectionVault.js +1 -2
- package/dist/connectionsFacade.js +33 -32
- package/dist/coordinatedSchedule.js +85 -80
- package/dist/crdtHistory.js +1 -2
- package/dist/crdtMerge.js +1 -2
- package/dist/crdtVersionsTable.js +1 -2
- package/dist/credentialBound.js +1 -2
- package/dist/crud.js +1 -2
- package/dist/dataStore.js +155 -123
- package/dist/dataloader.js +1 -2
- package/dist/declaredTablesGuard.js +3 -3
- package/dist/defineExecutor.js +1 -2
- package/dist/dependencyGraph.js +1 -2
- package/dist/devices.js +1 -2
- package/dist/devicesWiring.js +1 -2
- package/dist/dialectReplicationAdapter.js +1 -2
- package/dist/dispatcher.js +404 -330
- package/dist/effectStore.js +3 -4
- package/dist/engineClients.js +23 -0
- package/dist/eventBus.js +1 -2
- package/dist/eventPublish.js +1 -2
- package/dist/events.js +1 -2
- package/dist/expectation.js +1 -2
- package/dist/expectationEvaluator.js +2 -3
- package/dist/experiment.js +1 -2
- package/dist/experimentEvaluator.js +1 -2
- package/dist/externalizedTasks.js +20 -8
- package/dist/fieldCipher.js +110 -42
- package/dist/fieldKeyRegistry.js +294 -0
- package/dist/fieldKeyRekey.js +285 -0
- package/dist/fieldKeyScan.js +135 -0
- package/dist/fieldKeyring.js +76 -0
- package/dist/fingerprint.js +1 -2
- package/dist/finops.js +1 -2
- package/dist/finopsAccountant.js +1 -2
- package/dist/handlerBody.js +1 -2
- package/dist/healthProbes.js +1 -2
- package/dist/hot.d.ts +56 -0
- package/dist/hot.js +3 -0
- package/dist/hotDataStore.js +147 -0
- package/dist/hotDataStoreSnapshot.js +97 -0
- package/dist/hotDataStoreTables.js +17 -0
- package/dist/http-range.d.ts +53 -0
- package/dist/http-range.js +51 -0
- package/dist/httpClientSsrf.js +13 -39
- package/dist/httpClientTracing.js +13 -11
- package/dist/httpCompression.js +18 -19
- package/dist/httpInputStream.js +81 -0
- package/dist/httpKeepAlive.js +1 -2
- package/dist/httpRouteMounts.js +2 -2
- package/dist/idempotencyStore.js +93 -54
- package/dist/idleDetector.js +1 -2
- package/dist/index.d.ts +1185 -452
- package/dist/index.js +144 -134
- package/dist/insertRequiredColumns.js +1 -2
- package/dist/instanceMembership.js +1 -2
- package/dist/ivm.js +1 -2
- package/dist/kvDataStore.js +1 -2
- package/dist/kvFacade.js +24 -19
- package/dist/matcher.js +1 -2
- package/dist/memoryColumnDefaults.js +1 -2
- package/dist/memoryQueryJoins.js +17 -18
- package/dist/memorySetOperations.js +42 -0
- package/dist/metrics.js +39 -26
- package/dist/observability.js +2 -3
- package/dist/observedGraph.js +1 -2
- package/dist/originGuard.js +1 -2
- package/dist/outbox.js +2 -3
- package/dist/pluginRefRules.js +1 -2
- package/dist/policyGuardResolver.js +1 -2
- package/dist/preview.js +1 -2
- package/dist/projectedRows.js +1 -2
- package/dist/provenance.js +1 -2
- package/dist/rangeIndex.js +1 -2
- package/dist/rawReadScope.js +2 -3
- package/dist/reaction.js +1 -2
- package/dist/reactiveConfig.js +1 -2
- package/dist/readClassification.js +1 -2
- package/dist/readRecorder.js +36 -36
- package/dist/readScopePredicate.js +11 -0
- package/dist/rebac.js +1 -2
- package/dist/rebacInterceptor.js +1 -2
- package/dist/registry.js +1 -2
- package/dist/relevantFields.js +1 -2
- package/dist/replicaHealth.js +1 -2
- package/dist/replicaLocality.js +1 -2
- package/dist/replicaObservations.js +1 -2
- package/dist/replicaSelection.js +1 -2
- package/dist/replicatedDataStore.js +23 -6
- package/dist/requestLoader.js +1 -2
- package/dist/resumeCensus.js +1 -2
- package/dist/revalidation.js +1 -2
- package/dist/rowFilter.js +57 -41
- package/dist/rowStateBytes.js +1 -2
- package/dist/rpcBodyCap.js +1 -2
- package/dist/rpcConnections.js +9 -10
- package/dist/rpcDefectDiagnostics.js +1 -2
- package/dist/rpcServer.js +523 -451
- package/dist/rpcWorkloadAdmission.js +49 -0
- package/dist/runAsSystem.js +1 -2
- package/dist/rywPositionStore.js +83 -49
- package/dist/schedule.js +1 -2
- package/dist/scheduleBackfillRequests.js +1 -2
- package/dist/scheduleClaimsTable.js +1 -2
- package/dist/scheduleRunsTable.js +1 -2
- package/dist/scheduler.js +7 -3
- package/dist/schemaRegistry.js +1 -2
- package/dist/secretsResolver.js +1 -2
- package/dist/securityHeaders.js +1 -2
- package/dist/servePipeline.js +238 -154
- package/dist/serverOnlyAudit.js +1 -2
- package/dist/serverTracer.js +1 -2
- package/dist/serviceCredentials.js +1 -2
- package/dist/setReadScopes.js +27 -0
- package/dist/singleNodeOrchestrator.js +1 -2
- package/dist/snapshotValueCodec.js +45 -0
- package/dist/sourceResolution.js +35 -26
- package/dist/storeMiddleware.js +213 -212
- package/dist/storeQuery.js +1 -2
- package/dist/storeRead.js +1 -2
- package/dist/subscriber.js +2 -2
- package/dist/subscriptionOutbox.js +1 -2
- package/dist/subscriptionRegistry.js +1 -2
- package/dist/subscriptionResume.js +1 -2
- package/dist/subscriptionSocketMetrics.js +1 -2
- package/dist/systemSubject.js +1 -2
- package/dist/tenantScope.js +9 -22
- package/dist/tenantSelection.js +1 -2
- package/dist/timeline.js +1 -2
- package/dist/traceContext.js +1 -2
- package/dist/traceSink.js +1 -2
- package/dist/traceSpansTable.js +1 -2
- package/dist/traceparent.js +9 -21
- package/dist/transactionSubject.js +1 -2
- package/dist/trustedProxy.js +2 -3
- package/dist/undo.js +1 -2
- package/dist/undoBuiltins.js +1 -2
- package/dist/undoCapture.js +1 -2
- package/dist/undoLogTable.js +1 -2
- package/dist/upsertBinding.js +1 -2
- package/dist/wakeOrchestrator.js +1 -2
- package/dist/wakeRegistry.js +1 -2
- package/dist/wakeRouter.js +1 -2
- package/dist/wakeSupervisor.js +1 -2
- package/dist/wakeupsTable.js +1 -2
- package/dist/webSocketGatewayRuntime.js +112 -73
- package/dist/webhookVerification.js +1 -2
- package/dist/wireColumnStrip.js +1 -2
- package/dist/workflowReadBuiltins.js +1 -2
- package/dist/workflowRuntime.js +10 -11
- package/package.json +36 -17
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { FIELD_KEYS_TABLE as e, FIELD_KEY_HOLDERS_TABLE as t } from "./fieldKeyRegistry.js";
|
|
2
|
+
import { Effect as n } from "effect";
|
|
3
|
+
import { ciphertextKeyId as r, encryptedColumnsOf as i, quoteIdent as a } from "@voltro/database";
|
|
4
|
+
import { SqlClient as o } from "effect/sql";
|
|
5
|
+
import { introspectSchema as s } from "@voltro/database/sql";
|
|
6
|
+
// src/fieldKeyScan.ts
|
|
7
|
+
var c = /enc:v[12]:[A-Za-z0-9+/=:_-]+/g, l = 500, u = /* @__PURE__ */ new Set(["text", "json"]), d = (e, t, n) => e === "postgres" ? `CAST(${t} AS TEXT)` : n === "json" && (e === "mysql" || e === "mariadb") ? `CAST(${t} AS CHAR)` : t, f = (e) => {
|
|
8
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
9
|
+
for (let n = e?.cause; n instanceof Error; n = n.cause) t = n.message;
|
|
10
|
+
return t;
|
|
11
|
+
}, p = (e) => typeof e == "number" || typeof e == "bigint" ? String(e) : `'${String(e).replaceAll("'", "''")}'`, m = (e) => typeof e == "string" ? e : e instanceof Uint8Array ? new TextDecoder().decode(e) : e == null ? "" : JSON.stringify(e), h = (e, t) => n.gen(function* () {
|
|
12
|
+
let r = yield* o.SqlClient, i = (t) => a(t, e), c = yield* s(r), l = [], m = [];
|
|
13
|
+
for (let a of c.tables) {
|
|
14
|
+
if (a.name === "_voltro_field_keys" || a.name === "_voltro_field_key_holders") continue;
|
|
15
|
+
let o = a.indexes.find((e) => e.name === `${a.name}_pkey`)?.columns, s = o?.length === 1 ? o[0] : void 0;
|
|
16
|
+
for (let o of a.columns) {
|
|
17
|
+
let c = t.get(a.name)?.has(o.name) === !0;
|
|
18
|
+
if (!c && !u.has(o.type)) continue;
|
|
19
|
+
let h = d(e, `${i("__t")}.${i(o.name)}`, o.type), g = `${h} LIKE '%enc:v%'`, _ = `${i(a.name)} ${i("__t")}`, v = yield* r.unsafe(`SELECT COUNT(*) AS n FROM ${_} WHERE ${g}`).unprepared.pipe(n.map((e) => Number(e[0]?.n ?? 0)), n.catch((e) => n.sync(() => {
|
|
20
|
+
m.push({
|
|
21
|
+
table: a.name,
|
|
22
|
+
column: o.name,
|
|
23
|
+
reason: f(e)
|
|
24
|
+
});
|
|
25
|
+
})));
|
|
26
|
+
v === void 0 || v === 0 && !c || l.push({
|
|
27
|
+
table: a.name,
|
|
28
|
+
column: o.name,
|
|
29
|
+
type: o.type,
|
|
30
|
+
declared: c,
|
|
31
|
+
key: s,
|
|
32
|
+
found: v,
|
|
33
|
+
asText: d(e, i(o.name), o.type),
|
|
34
|
+
page: (t, n) => {
|
|
35
|
+
if (s === void 0) return `SELECT ${h} AS ${i("__v")}, ${i("__t")}.* FROM ${_} WHERE ${g}`;
|
|
36
|
+
let r = `${i("__t")}.${i(s)} AS ${i("__k")}, ${h} AS ${i("__v")}, ${i("__t")}.*`, a = `WHERE ${g}${t === void 0 ? "" : ` AND ${i("__t")}.${i(s)} > ${p(t)}`} ORDER BY ${i("__t")}.${i(s)}`;
|
|
37
|
+
return e === "mssql" ? `SELECT TOP ${n} ${r} FROM ${_} ${a}` : `SELECT ${r} FROM ${_} ${a} LIMIT ${n}`;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
columns: l,
|
|
44
|
+
unreadable: m,
|
|
45
|
+
tables: new Set(c.tables.map((e) => e.name))
|
|
46
|
+
};
|
|
47
|
+
}), g = (e) => {
|
|
48
|
+
let t = /* @__PURE__ */ new Map();
|
|
49
|
+
for (let n of e) {
|
|
50
|
+
let e = n;
|
|
51
|
+
if (typeof e != "object" || !e || typeof e.fields != "object" || e.tableName === void 0) continue;
|
|
52
|
+
let r = i(e);
|
|
53
|
+
r.length > 0 && t.set(e.tableName, new Set(r));
|
|
54
|
+
}
|
|
55
|
+
return t;
|
|
56
|
+
}, _ = (e) => {
|
|
57
|
+
let t = /* @__PURE__ */ new Map();
|
|
58
|
+
for (let n of e) {
|
|
59
|
+
let e = n;
|
|
60
|
+
if (typeof e != "object" || !e || typeof e.fields != "object" || e.tableName === void 0) continue;
|
|
61
|
+
let r = /* @__PURE__ */ new Map();
|
|
62
|
+
for (let t of i(e)) {
|
|
63
|
+
let n = e.fields[t]?.renamedFrom;
|
|
64
|
+
n !== void 0 && r.set(t, n);
|
|
65
|
+
}
|
|
66
|
+
(e.previousTableName !== void 0 || r.size > 0) && t.set(e.tableName, {
|
|
67
|
+
...e.previousTableName === void 0 ? {} : { previousTable: e.previousTableName },
|
|
68
|
+
columns: r
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return t;
|
|
72
|
+
}, v = (e, t, n = 1) => {
|
|
73
|
+
e[t] = (e[t] ?? 0) + n;
|
|
74
|
+
}, y = (i, s) => n.gen(function* () {
|
|
75
|
+
let u = yield* o.SqlClient, d = (e) => a(e, i), p = (e) => u.unsafe(e).unprepared, { columns: _, unreadable: y, tables: b } = yield* h(i, g(s)), x = [...y], S = [];
|
|
76
|
+
for (let e of _) {
|
|
77
|
+
let t = yield* n.gen(function* () {
|
|
78
|
+
let t = e.declared ? Number((yield* p(`SELECT COUNT(*) AS n FROM ${d(e.table)} WHERE ${d(e.column)} IS NOT NULL AND ${e.asText} NOT LIKE 'enc:v%'`))[0]?.n ?? 0) : 0, n = {}, i = 0, a = 0, o = (e) => {
|
|
79
|
+
let t = m(e).match(c) ?? [];
|
|
80
|
+
t.length > 0 && (a += 1);
|
|
81
|
+
for (let e of t) {
|
|
82
|
+
let t = r(e);
|
|
83
|
+
t === void 0 ? i += 1 : v(n, t);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
if (e.found > 0) {
|
|
87
|
+
let t;
|
|
88
|
+
for (;;) {
|
|
89
|
+
let n = yield* p(e.page(t, l));
|
|
90
|
+
for (let e of n) o(e.__v);
|
|
91
|
+
if (e.key === void 0 || n.length < l) break;
|
|
92
|
+
t = n[n.length - 1].__k;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
table: e.table,
|
|
97
|
+
column: e.column,
|
|
98
|
+
declared: e.declared,
|
|
99
|
+
cells: a,
|
|
100
|
+
byKey: n,
|
|
101
|
+
unnamed: i,
|
|
102
|
+
plaintext: t
|
|
103
|
+
};
|
|
104
|
+
}).pipe(n.catch((t) => n.sync(() => {
|
|
105
|
+
x.push({
|
|
106
|
+
table: e.table,
|
|
107
|
+
column: e.column,
|
|
108
|
+
reason: f(t)
|
|
109
|
+
});
|
|
110
|
+
})));
|
|
111
|
+
t !== void 0 && S.push(t);
|
|
112
|
+
}
|
|
113
|
+
let C = (e) => e == null ? null : e instanceof Date ? e.toISOString() : String(e), w = b.has("_voltro_field_keys") ? (yield* p(`SELECT ${d("keyId")} AS k, ${d("state")} AS s, ${d("activatedAt")} AS a, ${d("requestedAt")} AS r FROM ${d(e)}`)).map((e) => ({
|
|
114
|
+
keyId: String(e.k),
|
|
115
|
+
state: String(e.s),
|
|
116
|
+
activatedAt: C(e.a),
|
|
117
|
+
requestedAt: C(e.r)
|
|
118
|
+
})).sort((e, t) => e.keyId.localeCompare(t.keyId)) : [], T = b.has("_voltro_field_key_holders") ? (yield* p(`SELECT ${d("holderId")} AS h, ${d("keyIds")} AS k, ${d("activeKeyId")} AS a, ${d("expiresAt")} AS e FROM ${d(t)}`)).map((e) => ({
|
|
119
|
+
holderId: String(e.h),
|
|
120
|
+
keyIds: String(e.k).split(","),
|
|
121
|
+
activeKeyId: String(e.a),
|
|
122
|
+
expiresAt: C(e.e) ?? ""
|
|
123
|
+
})).sort((e, t) => e.holderId.localeCompare(t.holderId)) : [], E = {};
|
|
124
|
+
for (let e of S) for (let [t, n] of Object.entries(e.byKey)) v(E, t, n);
|
|
125
|
+
return S.sort((e, t) => e.table.localeCompare(t.table) || e.column.localeCompare(t.column)), {
|
|
126
|
+
keys: w,
|
|
127
|
+
holders: T,
|
|
128
|
+
locations: S,
|
|
129
|
+
totals: E,
|
|
130
|
+
unreadable: x,
|
|
131
|
+
unnamed: S.reduce((e, t) => e + t.unnamed, 0),
|
|
132
|
+
plaintext: S.reduce((e, t) => e + t.plaintext, 0)
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
export { c as ENVELOPE_PATTERN, m as cellText, g as declaredEncryptedColumns, _ as declaredRenames, h as discoverSealedColumns, y as fieldKeyStatus, f as reasonOf };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { FieldKeyringConfigError as e, deriveKey as t, fieldKeyIdOf as n } from "./fieldCipher.js";
|
|
2
|
+
// src/fieldKeyring.ts
|
|
3
|
+
var r = "VOLTRO_FIELD_ENCRYPTION_KEYS", i = "VOLTRO_FIELD_ENCRYPTION_ACTIVE", a = "VOLTRO_FIELD_ENCRYPTION_KEY", o = "VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS", s = [
|
|
4
|
+
r,
|
|
5
|
+
i,
|
|
6
|
+
a,
|
|
7
|
+
o
|
|
8
|
+
], c = (e) => e !== void 0 && e.trim() !== "", l = (t) => {
|
|
9
|
+
let n = t.trim();
|
|
10
|
+
if (n.startsWith("{")) {
|
|
11
|
+
let t;
|
|
12
|
+
try {
|
|
13
|
+
t = JSON.parse(n);
|
|
14
|
+
} catch {
|
|
15
|
+
throw new e(`${r} starts like JSON but is not valid JSON`);
|
|
16
|
+
}
|
|
17
|
+
if (typeof t != "object" || !t || Array.isArray(t)) throw new e(`${r} as JSON must be an object of id → key`);
|
|
18
|
+
return Object.entries(t).map(([t, n]) => {
|
|
19
|
+
if (typeof n != "string") throw new e(`${r}: key "${t}" is not a string`);
|
|
20
|
+
return {
|
|
21
|
+
id: t,
|
|
22
|
+
secret: n
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return n.split(",").map((e) => e.trim()).filter((e) => e !== "").map((t) => {
|
|
27
|
+
let n = t.indexOf("=");
|
|
28
|
+
if (n <= 0) throw new e(`${r}: every entry is id=key, and one is not`);
|
|
29
|
+
return {
|
|
30
|
+
id: t.slice(0, n).trim(),
|
|
31
|
+
secret: t.slice(n + 1).trim()
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}, u = (e, n) => t(e).equals(t(n)), d = (t) => {
|
|
35
|
+
let s = t[r], d = t[a], p = t[o], m = t[i]?.trim();
|
|
36
|
+
if (c(s)) {
|
|
37
|
+
let t = l(s);
|
|
38
|
+
for (let [n, i] of [[a, d], [o, p]]) if (c(i) && !t.some((e) => u(e.secret, i))) throw new e(`${n} is set beside ${r} but is not one of its keys — put it into ${r} or remove it`);
|
|
39
|
+
if (t.length === 0) throw new e(`${r} is set but holds no key`);
|
|
40
|
+
let n = c(d) ? t.find((e) => u(e.secret, d))?.id : void 0, h = c(m) ? m : t.length === 1 ? t[0].id : n;
|
|
41
|
+
if (h === void 0) throw new e(`${r} holds ${t.length} keys: name the one new values are sealed under in ${i}`);
|
|
42
|
+
return f({
|
|
43
|
+
active: h,
|
|
44
|
+
keys: t
|
|
45
|
+
}), {
|
|
46
|
+
active: h,
|
|
47
|
+
keys: t
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (!c(d)) {
|
|
51
|
+
if (c(p)) throw new e(`${o} is set without ${a}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (c(p) && u(d, p)) throw new e(`${a} and ${o} are the same key — a rotation needs a NEW key in ${a}`);
|
|
55
|
+
let h = [{
|
|
56
|
+
id: n(d),
|
|
57
|
+
secret: d
|
|
58
|
+
}, ...c(p) ? [{
|
|
59
|
+
id: n(p),
|
|
60
|
+
secret: p
|
|
61
|
+
}] : []], g = {
|
|
62
|
+
active: h[0].id,
|
|
63
|
+
keys: h
|
|
64
|
+
};
|
|
65
|
+
return f(g), g;
|
|
66
|
+
}, f = (t) => {
|
|
67
|
+
let n = /* @__PURE__ */ new Set();
|
|
68
|
+
for (let r of t.keys) {
|
|
69
|
+
if (!/^[a-z0-9][a-z0-9-]{0,15}$/.test(r.id)) throw new e(`field encryption: "${r.id}" is not a key id — use a short lowercase slug (letters, digits, "-", at most 16)`);
|
|
70
|
+
if (n.has(r.id)) throw new e(`field encryption: key id "${r.id}" is configured twice`);
|
|
71
|
+
n.add(r.id);
|
|
72
|
+
}
|
|
73
|
+
for (let n = 0; n < t.keys.length; n++) for (let r = n + 1; r < t.keys.length; r++) if (u(t.keys[n].secret, t.keys[r].secret)) throw new e(`field encryption: keys "${t.keys[n].id}" and "${t.keys[r].id}" are the same key — a rotation needs a NEW key, and two ids for one key make every count per key wrong`);
|
|
74
|
+
if (!n.has(t.active)) throw new e(`field encryption: the active key "${t.active}" is not one of the configured keys (${[...n].join(", ")})`);
|
|
75
|
+
};
|
|
76
|
+
export { i as FIELD_ENCRYPTION_ACTIVE_ENV, r as FIELD_ENCRYPTION_KEYS_ENV, a as FIELD_ENCRYPTION_KEY_ENV, o as FIELD_ENCRYPTION_PREVIOUS_KEY_ENV, s as FIELD_KEYRING_ENV_NAMES, d as resolveFieldKeyring };
|
package/dist/fingerprint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash as e } from "node:crypto";
|
|
2
|
-
|
|
2
|
+
// src/fingerprint.ts
|
|
3
3
|
var t = (e) => {
|
|
4
4
|
if (e == null) return null;
|
|
5
5
|
if (Array.isArray(e)) return e.map(t);
|
|
@@ -17,5 +17,4 @@ var t = (e) => {
|
|
|
17
17
|
});
|
|
18
18
|
return e("sha256").update(JSON.stringify(a)).digest("hex").slice(0, 16);
|
|
19
19
|
};
|
|
20
|
-
//#endregion
|
|
21
20
|
export { t as canonicalize, n as fingerprintFor };
|
package/dist/finops.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context as e, Effect as t } from "effect";
|
|
2
|
-
|
|
2
|
+
// src/finops.ts
|
|
3
3
|
var n = /* @__PURE__ */ new Set([
|
|
4
4
|
"info",
|
|
5
5
|
"warn",
|
|
@@ -38,5 +38,4 @@ var n = /* @__PURE__ */ new Set([
|
|
|
38
38
|
}, s = (e) => typeof e == "object" && !!e && e._voltroCostBudget === !0, c = class extends e.Service()("@voltro/CostRegistry") {}, l = (e, n) => t.gen(function* () {
|
|
39
39
|
return (yield* c).budget(e.name, n);
|
|
40
40
|
});
|
|
41
|
-
//#endregion
|
|
42
41
|
export { c as CostRegistry, o as defineCostBudget, s as isCostBudgetDefinition, l as useCostBudget };
|
package/dist/finopsAccountant.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/finopsAccountant.ts
|
|
2
2
|
var e = class {
|
|
3
3
|
total = 0;
|
|
4
4
|
byUnit = /* @__PURE__ */ new Map();
|
|
@@ -212,5 +212,4 @@ var e = class {
|
|
|
212
212
|
runTick: u
|
|
213
213
|
};
|
|
214
214
|
};
|
|
215
|
-
//#endregion
|
|
216
215
|
export { a as CostAccountant, o as attachFinops };
|
package/dist/handlerBody.js
CHANGED
package/dist/healthProbes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/healthProbes.ts
|
|
2
2
|
var e = "/internal", t = (t, n) => ({
|
|
3
3
|
path: (t?.path ?? e).replace(/\/+$/, "") || e,
|
|
4
4
|
liveness: t?.liveness ?? (() => !0),
|
|
@@ -51,5 +51,4 @@ var e = "/internal", t = (t, n) => ({
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
//#endregion
|
|
55
54
|
export { r as answerProbe, n as probeFor, t as resolveHealth };
|
package/dist/hot.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ColumnBuilder } from '@voltro/database';
|
|
2
|
+
import { DataStore } from '@voltro/database';
|
|
3
|
+
import { DialectId } from '@voltro/database';
|
|
4
|
+
import { FieldDefinitions } from '@voltro/database';
|
|
5
|
+
import { HotStoreShape } from '@voltro/kv/hot/contract';
|
|
6
|
+
import { Table } from '@voltro/database';
|
|
7
|
+
|
|
8
|
+
/** Small-deployment SQL backend. One database lock serializes complete batches.
|
|
9
|
+
* The lock row contains no application key, tenant identity or payload. All
|
|
10
|
+
* application rows, including sorted members, are physically removed on delete.
|
|
11
|
+
* SQL stores own transaction retries; callbacks perform no external side effects.
|
|
12
|
+
*/
|
|
13
|
+
export declare const dataStoreHotStore: (store: DataStore, options: HotDataStoreOptions) => HotStoreShape;
|
|
14
|
+
|
|
15
|
+
export declare interface HotDataStoreOptions {
|
|
16
|
+
/** Actual SQL dialect: SQLite-family writer admission differs from row locks. */
|
|
17
|
+
readonly dialect: DialectId;
|
|
18
|
+
readonly namespace: string;
|
|
19
|
+
/** Trusted server/test time, sampled after acquiring the transaction lock. */
|
|
20
|
+
readonly now?: () => number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export declare const hotDataStoreTables: readonly [ Table<"_voltro_hot_kv", FieldDefinitions<{
|
|
24
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
25
|
+
readonly keyHash: ColumnBuilder<string, "text", boolean>;
|
|
26
|
+
readonly namespace: ColumnBuilder<string | null, "text", boolean>;
|
|
27
|
+
readonly key: ColumnBuilder<string | null, "text", boolean>;
|
|
28
|
+
readonly payload: ColumnBuilder<string | null, "text", boolean>;
|
|
29
|
+
readonly expiresAtMs: ColumnBuilder<number | null, "real", boolean>;
|
|
30
|
+
}>, false, never>, Table<"_voltro_hot_z", FieldDefinitions<{
|
|
31
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
32
|
+
readonly keyHash: ColumnBuilder<string, "text", boolean>;
|
|
33
|
+
readonly memberHash: ColumnBuilder<string, "text", boolean>;
|
|
34
|
+
readonly member: ColumnBuilder<string, "text", boolean>;
|
|
35
|
+
readonly score: ColumnBuilder<number, "real", boolean>;
|
|
36
|
+
}>, false, never>];
|
|
37
|
+
|
|
38
|
+
/** Activated with the database HotStore; importing definitions performs no I/O. */
|
|
39
|
+
export declare const hotKvTable: Table<"_voltro_hot_kv", FieldDefinitions<{
|
|
40
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
41
|
+
readonly keyHash: ColumnBuilder<string, "text", boolean>;
|
|
42
|
+
readonly namespace: ColumnBuilder<string | null, "text", boolean>;
|
|
43
|
+
readonly key: ColumnBuilder<string | null, "text", boolean>;
|
|
44
|
+
readonly payload: ColumnBuilder<string | null, "text", boolean>;
|
|
45
|
+
readonly expiresAtMs: ColumnBuilder<number | null, "real", boolean>;
|
|
46
|
+
}>, false, never>;
|
|
47
|
+
|
|
48
|
+
export declare const hotZTable: Table<"_voltro_hot_z", FieldDefinitions<{
|
|
49
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
50
|
+
readonly keyHash: ColumnBuilder<string, "text", boolean>;
|
|
51
|
+
readonly memberHash: ColumnBuilder<string, "text", boolean>;
|
|
52
|
+
readonly member: ColumnBuilder<string, "text", boolean>;
|
|
53
|
+
readonly score: ColumnBuilder<number, "real", boolean>;
|
|
54
|
+
}>, false, never>;
|
|
55
|
+
|
|
56
|
+
export { }
|
package/dist/hot.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { decodeHotSnapshot as e, encodeHotSnapshot as t } from "./hotDataStoreSnapshot.js";
|
|
2
|
+
import { hotKvTable as n, hotZTable as r } from "./hotDataStoreTables.js";
|
|
3
|
+
import { Effect as i, Stream as a } from "effect";
|
|
4
|
+
import { and as o, eq as s, generateId as c, gt as l, isNull as u, isSqliteFamily as d, or as f, queryFor as p, startsWith as m } from "@voltro/database";
|
|
5
|
+
import { createHash as h } from "node:crypto";
|
|
6
|
+
import { HotError as g, applyHotOperations as _, compareHotMembers as v, hotStoreFromRunner as y, validateHotOp as b } from "@voltro/kv/hot/contract";
|
|
7
|
+
// src/hotDataStore.ts
|
|
8
|
+
var x = n.tableName, S = r.tableName, C = "lock", w = (e) => h("sha256").update(e).digest("hex"), T = (h, T) => {
|
|
9
|
+
let { namespace: E } = T;
|
|
10
|
+
if (!E || !E.isWellFormed() || /[{}]/.test(E)) throw new g({
|
|
11
|
+
operation: "connect",
|
|
12
|
+
key: "",
|
|
13
|
+
reason: "invalid"
|
|
14
|
+
});
|
|
15
|
+
let D = T.now ?? Date.now, O = (e) => w(JSON.stringify([E, e])), k, A = () => k ??= h.transactional(async (e) => {
|
|
16
|
+
let t = {
|
|
17
|
+
id: c({
|
|
18
|
+
kind: "typeid",
|
|
19
|
+
prefix: "hot"
|
|
20
|
+
}, x),
|
|
21
|
+
keyHash: C,
|
|
22
|
+
namespace: null,
|
|
23
|
+
key: null,
|
|
24
|
+
payload: null,
|
|
25
|
+
expiresAtMs: null
|
|
26
|
+
};
|
|
27
|
+
T.dialect === "mssql" ? await e.upsert(x, t, {
|
|
28
|
+
conflictColumns: ["keyHash"],
|
|
29
|
+
update: []
|
|
30
|
+
}) : await e.insertIgnore(x, t, { conflictColumns: ["keyHash"] });
|
|
31
|
+
}).catch((e) => {
|
|
32
|
+
throw k = void 0, e;
|
|
33
|
+
}), j = async (t, i) => {
|
|
34
|
+
let a = O(i), o = (await t.query(p(n).where(s("keyHash", a)).descriptor))[0];
|
|
35
|
+
if (o === void 0) return;
|
|
36
|
+
if (o.namespace !== E || o.key !== i || typeof o.payload != "string") throw new g({
|
|
37
|
+
operation: "read",
|
|
38
|
+
key: i,
|
|
39
|
+
reason: "unavailable"
|
|
40
|
+
});
|
|
41
|
+
let c = o.expiresAtMs;
|
|
42
|
+
if (c !== null && (typeof c != "number" || !Number.isFinite(c))) throw new g({
|
|
43
|
+
operation: "read",
|
|
44
|
+
key: i,
|
|
45
|
+
reason: "unavailable"
|
|
46
|
+
});
|
|
47
|
+
let l = await t.query(p(r).where(s("keyHash", a)).descriptor), u = [];
|
|
48
|
+
for (let e of l) {
|
|
49
|
+
let t = e.member, n = e.score;
|
|
50
|
+
if (typeof t != "string" || typeof n != "number" || !Number.isFinite(n)) throw new g({
|
|
51
|
+
operation: "read",
|
|
52
|
+
key: i,
|
|
53
|
+
reason: "unavailable"
|
|
54
|
+
});
|
|
55
|
+
u.push([t, n]);
|
|
56
|
+
}
|
|
57
|
+
return e(o.payload, c === null ? Infinity : c, u);
|
|
58
|
+
}, M = async (e, n, r) => {
|
|
59
|
+
let i = O(n);
|
|
60
|
+
if (await e.deleteMany(S, { where: s("keyHash", i) }), r === void 0) await e.deleteMany(x, { where: s("keyHash", i) });
|
|
61
|
+
else if (await e.upsert(x, {
|
|
62
|
+
id: c({
|
|
63
|
+
kind: "typeid",
|
|
64
|
+
prefix: "hot"
|
|
65
|
+
}, x),
|
|
66
|
+
keyHash: i,
|
|
67
|
+
namespace: E,
|
|
68
|
+
key: n,
|
|
69
|
+
payload: t(r),
|
|
70
|
+
expiresAtMs: r.expiresAt === Infinity ? null : r.expiresAt
|
|
71
|
+
}, {
|
|
72
|
+
conflictColumns: ["keyHash"],
|
|
73
|
+
update: [
|
|
74
|
+
"namespace",
|
|
75
|
+
"key",
|
|
76
|
+
"payload",
|
|
77
|
+
"expiresAtMs"
|
|
78
|
+
]
|
|
79
|
+
}), r.kind === "z") for (let [t, n] of r.values) await e.insert(S, {
|
|
80
|
+
id: c({
|
|
81
|
+
kind: "typeid",
|
|
82
|
+
prefix: "hotz"
|
|
83
|
+
}, S),
|
|
84
|
+
keyHash: i,
|
|
85
|
+
memberHash: w(t),
|
|
86
|
+
member: t,
|
|
87
|
+
score: n
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
return y((e) => i.try({
|
|
91
|
+
try: () => {
|
|
92
|
+
e.forEach(b);
|
|
93
|
+
},
|
|
94
|
+
catch: (e) => e instanceof g ? e : new g({
|
|
95
|
+
operation: "multi",
|
|
96
|
+
key: "",
|
|
97
|
+
reason: "invalid"
|
|
98
|
+
})
|
|
99
|
+
}).pipe(i.flatMap(() => i.tryPromise({
|
|
100
|
+
try: async () => e.length === 0 ? [] : (await A(), h.transactional(async (t) => {
|
|
101
|
+
if (d(T.dialect) && await t.updateMany(x, { payload: null }, { where: s("keyHash", C) }), (await t.query(p(n).where(s("keyHash", C)).forUpdate().descriptor)).length !== 1) throw new g({
|
|
102
|
+
operation: "multi",
|
|
103
|
+
key: "",
|
|
104
|
+
reason: "unavailable"
|
|
105
|
+
});
|
|
106
|
+
let r = D();
|
|
107
|
+
if (!Number.isFinite(r)) throw new g({
|
|
108
|
+
operation: "multi",
|
|
109
|
+
key: "",
|
|
110
|
+
reason: "invalid"
|
|
111
|
+
});
|
|
112
|
+
let i = /* @__PURE__ */ new Map(), a = [...new Set(e.map((e) => e.key))];
|
|
113
|
+
for (let e of a) {
|
|
114
|
+
let n = await j(t, e);
|
|
115
|
+
n !== void 0 && i.set(e, n);
|
|
116
|
+
}
|
|
117
|
+
let o = new Map(i), c = _(i, e, r);
|
|
118
|
+
for (let e of a) i.get(e) !== o.get(e) && await M(t, e, i.get(e));
|
|
119
|
+
return c;
|
|
120
|
+
})),
|
|
121
|
+
catch: (e) => e instanceof g ? e : new g({
|
|
122
|
+
operation: "multi",
|
|
123
|
+
key: "",
|
|
124
|
+
reason: "unavailable"
|
|
125
|
+
})
|
|
126
|
+
}))), (e) => a.unwrap(i.tryPromise({
|
|
127
|
+
try: async () => {
|
|
128
|
+
if (!e.isWellFormed()) throw new g({
|
|
129
|
+
operation: "scanPrefix",
|
|
130
|
+
key: e,
|
|
131
|
+
reason: "invalid"
|
|
132
|
+
});
|
|
133
|
+
let t = D(), r = (await h.query(p(n).where(o(s("namespace", E), m("key", e), f(u("expiresAtMs"), l("expiresAtMs", t)))).descriptor)).flatMap((t) => t.namespace === E && typeof t.key == "string" && t.key.startsWith(e) && typeof t.payload == "string" ? [t.key] : []);
|
|
134
|
+
return a.fromIterable(r.sort(v));
|
|
135
|
+
},
|
|
136
|
+
catch: (t) => t instanceof g ? t : new g({
|
|
137
|
+
operation: "scanPrefix",
|
|
138
|
+
key: e,
|
|
139
|
+
reason: "unavailable"
|
|
140
|
+
})
|
|
141
|
+
})), {
|
|
142
|
+
atomicMulti: !0,
|
|
143
|
+
crossNode: !0,
|
|
144
|
+
persistent: !0
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
export { T as dataStoreHotStore };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
// src/hotDataStoreSnapshot.ts
|
|
3
|
+
var t = e.Number.pipe(e.check(e.makeFilter(Number.isFinite))), n = e.Union([
|
|
4
|
+
e.Struct({
|
|
5
|
+
kind: e.Literal("fixed"),
|
|
6
|
+
windowStart: t,
|
|
7
|
+
count: t
|
|
8
|
+
}),
|
|
9
|
+
e.Struct({
|
|
10
|
+
kind: e.Literal("sliding"),
|
|
11
|
+
currStart: t,
|
|
12
|
+
currCount: t,
|
|
13
|
+
prevCount: t
|
|
14
|
+
}),
|
|
15
|
+
e.Struct({
|
|
16
|
+
kind: e.Literal("token"),
|
|
17
|
+
tokens: t,
|
|
18
|
+
lastRefill: t
|
|
19
|
+
})
|
|
20
|
+
]), r = e.Union([
|
|
21
|
+
e.Struct({ kind: e.Literal("z") }),
|
|
22
|
+
e.Struct({
|
|
23
|
+
kind: e.Literal("h"),
|
|
24
|
+
values: e.Array(e.Tuple([e.String, e.String]))
|
|
25
|
+
}),
|
|
26
|
+
e.Struct({
|
|
27
|
+
kind: e.Literal("s"),
|
|
28
|
+
values: e.Array(e.String)
|
|
29
|
+
}),
|
|
30
|
+
e.Struct({
|
|
31
|
+
kind: e.Literal("counter"),
|
|
32
|
+
value: t
|
|
33
|
+
}),
|
|
34
|
+
e.Struct({
|
|
35
|
+
kind: e.Literal("lease"),
|
|
36
|
+
value: e.String
|
|
37
|
+
}),
|
|
38
|
+
e.Struct({
|
|
39
|
+
kind: e.Literal("bucket"),
|
|
40
|
+
tokens: t,
|
|
41
|
+
updatedAt: t
|
|
42
|
+
}),
|
|
43
|
+
e.Struct({
|
|
44
|
+
kind: e.Literal("rateLimit"),
|
|
45
|
+
state: n
|
|
46
|
+
})
|
|
47
|
+
]), i = e.decodeUnknownSync(r), a = (e) => {
|
|
48
|
+
switch (e.kind) {
|
|
49
|
+
case "z": return JSON.stringify({ kind: "z" });
|
|
50
|
+
case "h":
|
|
51
|
+
case "s": return JSON.stringify({
|
|
52
|
+
kind: e.kind,
|
|
53
|
+
values: [...e.values]
|
|
54
|
+
});
|
|
55
|
+
case "counter": return JSON.stringify({
|
|
56
|
+
kind: e.kind,
|
|
57
|
+
value: e.value
|
|
58
|
+
});
|
|
59
|
+
case "lease": return JSON.stringify({
|
|
60
|
+
kind: e.kind,
|
|
61
|
+
value: e.value
|
|
62
|
+
});
|
|
63
|
+
case "bucket": return JSON.stringify({
|
|
64
|
+
kind: e.kind,
|
|
65
|
+
tokens: e.tokens,
|
|
66
|
+
updatedAt: e.updatedAt
|
|
67
|
+
});
|
|
68
|
+
case "rateLimit": return JSON.stringify({
|
|
69
|
+
kind: e.kind,
|
|
70
|
+
state: e.state
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}, o = (e, t, n) => {
|
|
74
|
+
let r = i(JSON.parse(e));
|
|
75
|
+
switch (r.kind) {
|
|
76
|
+
case "z": return {
|
|
77
|
+
kind: "z",
|
|
78
|
+
values: new Map(n),
|
|
79
|
+
expiresAt: t
|
|
80
|
+
};
|
|
81
|
+
case "h": return {
|
|
82
|
+
kind: "h",
|
|
83
|
+
values: new Map(r.values),
|
|
84
|
+
expiresAt: t
|
|
85
|
+
};
|
|
86
|
+
case "s": return {
|
|
87
|
+
kind: "s",
|
|
88
|
+
values: new Set(r.values),
|
|
89
|
+
expiresAt: t
|
|
90
|
+
};
|
|
91
|
+
default: return {
|
|
92
|
+
...r,
|
|
93
|
+
expiresAt: t
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
export { o as decodeHotSnapshot, a as encodeHotSnapshot };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { id as e, real as t, table as n, text as r } from "@voltro/database";
|
|
2
|
+
// src/hotDataStoreTables.ts
|
|
3
|
+
var i = n("_voltro_hot_kv", {
|
|
4
|
+
id: e({ prefix: "hot" }),
|
|
5
|
+
keyHash: r().maxLength(64),
|
|
6
|
+
namespace: r().nullable(),
|
|
7
|
+
key: r().nullable(),
|
|
8
|
+
payload: r().nullable(),
|
|
9
|
+
expiresAtMs: t().nullable()
|
|
10
|
+
}).unique("hotKeyHash", ["keyHash"]).index(["expiresAtMs"]).nonReactive(), a = n("_voltro_hot_z", {
|
|
11
|
+
id: e({ prefix: "hotz" }),
|
|
12
|
+
keyHash: r().maxLength(64),
|
|
13
|
+
memberHash: r().maxLength(64),
|
|
14
|
+
member: r(),
|
|
15
|
+
score: t()
|
|
16
|
+
}).unique("hotMemberHash", ["keyHash", "memberHash"]).index(["keyHash", "score"]).nonReactive(), o = [i, a];
|
|
17
|
+
export { o as hotDataStoreTables, i as hotKvTable, a as hotZTable };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The outcome of a `Range` header against a representation of `size` bytes. */
|
|
2
|
+
export declare type ByteRangeRequest =
|
|
3
|
+
/** Absent, malformed, another unit, or several ranges: serve the whole thing. */
|
|
4
|
+
{
|
|
5
|
+
readonly _tag: 'ignored';
|
|
6
|
+
}
|
|
7
|
+
/** Syntactically valid, but no byte of it exists: 416 `bytes *\/size`. */
|
|
8
|
+
| {
|
|
9
|
+
readonly _tag: 'unsatisfiable';
|
|
10
|
+
}
|
|
11
|
+
/** Serve bytes `start..end`, both inclusive: 206 `bytes start-end/size`. */
|
|
12
|
+
| {
|
|
13
|
+
readonly _tag: 'satisfiable';
|
|
14
|
+
readonly start: number;
|
|
15
|
+
readonly end: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* RFC 9110 §13.1.5: may a `Range` be honoured given this `If-Range`?
|
|
20
|
+
*
|
|
21
|
+
* `true` when there is no `If-Range`, or when it names the current
|
|
22
|
+
* representation by a STRONG validator: an identical strong entity tag, or
|
|
23
|
+
* the exact `Last-Modified` second — and only once that second is over
|
|
24
|
+
* (`nowMs`), because a file written twice within one second would otherwise
|
|
25
|
+
* keep a date that no longer names its bytes. A weak tag, an unparsable
|
|
26
|
+
* value, or a validator the representation does not have never matches: the
|
|
27
|
+
* client then gets the whole representation, never bytes of a different one.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ifRangeAllowsRange: (ifRange: string | undefined, current: RangeValidators, nowMs?: number) => boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a `Range` header against a representation of `size` bytes
|
|
33
|
+
* (a non-negative safe integer, normally from `stat`).
|
|
34
|
+
*/
|
|
35
|
+
export declare const parseByteRange: (header: string | undefined, size: number) => ByteRangeRequest;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An HTTP-date (RFC 9110 §5.6.7) in epoch milliseconds, `undefined` when the
|
|
39
|
+
* value is none. All three historical forms are accepted. asctime carries no
|
|
40
|
+
* zone and HTTP dates are always GMT, so it is read as GMT — `Date.parse`
|
|
41
|
+
* alone would read it in the server's local time zone.
|
|
42
|
+
*/
|
|
43
|
+
export declare const parseHttpDate: (value: string) => number | undefined;
|
|
44
|
+
|
|
45
|
+
/** The current validators of the representation a range would be cut from. */
|
|
46
|
+
export declare interface RangeValidators {
|
|
47
|
+
/** The representation's entity tag as sent in `ETag`, quotes included. */
|
|
48
|
+
readonly etag?: string | undefined;
|
|
49
|
+
/** Last modification time in epoch milliseconds (as sent in `Last-Modified`). */
|
|
50
|
+
readonly lastModifiedMs?: number | undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { }
|