@smithers-orchestrator/db 0.16.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/LICENSE +21 -0
- package/package.json +43 -0
- package/src/JsonBounds.ts +6 -0
- package/src/SchemaRegistryEntry.ts +6 -0
- package/src/SqlMessageStorage.js +818 -0
- package/src/SqlMessageStorageEventHistoryQuery.ts +7 -0
- package/src/SqliteWriteRetryOptions.ts +7 -0
- package/src/adapter/AlertRow.ts +29 -0
- package/src/adapter/AlertSeverity.ts +2 -0
- package/src/adapter/AlertStatus.ts +2 -0
- package/src/adapter/ApprovalRow.ts +13 -0
- package/src/adapter/AttemptRow.ts +17 -0
- package/src/adapter/CacheRow.ts +12 -0
- package/src/adapter/DB_ALERT_ALLOWED_SEVERITIES.js +5 -0
- package/src/adapter/DB_ALERT_ALLOWED_STATUSES.js +6 -0
- package/src/adapter/DB_ALERT_ID_MAX_LENGTH.js +1 -0
- package/src/adapter/DB_ALERT_MESSAGE_MAX_LENGTH.js +1 -0
- package/src/adapter/DB_ALERT_POLICY_NAME_MAX_LENGTH.js +1 -0
- package/src/adapter/DB_RUN_ALLOWED_STATUSES.js +10 -0
- package/src/adapter/DB_RUN_ID_MAX_LENGTH.js +1 -0
- package/src/adapter/DB_RUN_WORKFLOW_NAME_MAX_LENGTH.js +1 -0
- package/src/adapter/EventHistoryQuery.ts +7 -0
- package/src/adapter/HumanRequestRow.ts +19 -0
- package/src/adapter/NodeDiffCacheRow.ts +9 -0
- package/src/adapter/NodeRow.ts +10 -0
- package/src/adapter/PendingHumanRequestRow.ts +7 -0
- package/src/adapter/RunAncestryRow.ts +5 -0
- package/src/adapter/RunRow.ts +21 -0
- package/src/adapter/SignalQuery.ts +6 -0
- package/src/adapter/SignalRow.ts +9 -0
- package/src/adapter/SmithersDb.js +2236 -0
- package/src/adapter/StaleRunRecord.ts +7 -0
- package/src/adapter/index.js +27 -0
- package/src/adapter.js +2359 -0
- package/src/assertJsonPayloadWithinBounds.js +94 -0
- package/src/assertMaxBytes.js +23 -0
- package/src/assertMaxJsonDepth.js +40 -0
- package/src/assertMaxStringLength.js +16 -0
- package/src/assertOptionalArrayMaxLength.js +16 -0
- package/src/assertOptionalStringMaxLength.js +11 -0
- package/src/assertPositiveFiniteInteger.js +14 -0
- package/src/assertPositiveFiniteNumber.js +12 -0
- package/src/buildHumanRequestId.js +9 -0
- package/src/cache/nodeDiffCache.js +124 -0
- package/src/ensure.js +18 -0
- package/src/ensureSqlMessageStorage.js +11 -0
- package/src/ensureSqlMessageStorageEffect.js +12 -0
- package/src/frame-codec/FRAME_KEYFRAME_INTERVAL.js +1 -0
- package/src/frame-codec/FrameDelta.ts +6 -0
- package/src/frame-codec/FrameDeltaOp.ts +20 -0
- package/src/frame-codec/FrameEncoding.ts +1 -0
- package/src/frame-codec/JsonPath.ts +3 -0
- package/src/frame-codec/JsonPathSegment.ts +1 -0
- package/src/frame-codec/applyFrameDelta.js +143 -0
- package/src/frame-codec/applyFrameDeltaJson.js +10 -0
- package/src/frame-codec/encodeFrameDelta.js +247 -0
- package/src/frame-codec/index.js +15 -0
- package/src/frame-codec/normalizeFrameEncoding.js +13 -0
- package/src/frame-codec/parseFrameDelta.js +27 -0
- package/src/frame-codec/serializeFrameDelta.js +9 -0
- package/src/frame-codec.js +409 -0
- package/src/getSqlMessageStorage.js +11 -0
- package/src/index.d.ts +5203 -0
- package/src/index.js +20 -0
- package/src/input-bounds.js +12 -0
- package/src/input.js +17 -0
- package/src/internal-schema/index.js +19 -0
- package/src/internal-schema/smithersAlerts.js +27 -0
- package/src/internal-schema/smithersApprovals.js +18 -0
- package/src/internal-schema/smithersAttempts.js +20 -0
- package/src/internal-schema/smithersCache.js +13 -0
- package/src/internal-schema/smithersCron.js +11 -0
- package/src/internal-schema/smithersEvents.js +10 -0
- package/src/internal-schema/smithersFrames.js +14 -0
- package/src/internal-schema/smithersHumanRequests.js +17 -0
- package/src/internal-schema/smithersNodeDiffs.js +12 -0
- package/src/internal-schema/smithersNodes.js +13 -0
- package/src/internal-schema/smithersRalph.js +10 -0
- package/src/internal-schema/smithersRuns.js +22 -0
- package/src/internal-schema/smithersSandboxes.js +16 -0
- package/src/internal-schema/smithersSignals.js +12 -0
- package/src/internal-schema/smithersTimeTravelAudit.js +12 -0
- package/src/internal-schema/smithersToolCalls.js +19 -0
- package/src/internal-schema/smithersVectors.js +12 -0
- package/src/internal-schema.js +245 -0
- package/src/isRetryableSqliteWriteError.js +53 -0
- package/src/loadInputEffect.js +28 -0
- package/src/loadOutputsEffect.js +87 -0
- package/src/output/OutputKey.ts +1 -0
- package/src/output/buildKeyWhere.js +17 -0
- package/src/output/buildOutputRow.js +34 -0
- package/src/output/describeSchemaShape.js +70 -0
- package/src/output/getAgentOutputSchema.js +13 -0
- package/src/output/getKeyColumns.js +19 -0
- package/src/output/index.js +14 -0
- package/src/output/selectOutputRowEffect.js +30 -0
- package/src/output/stripAutoColumns.js +10 -0
- package/src/output/upsertOutputRowEffect.js +38 -0
- package/src/output/validateExistingOutput.js +17 -0
- package/src/output/validateOutput.js +17 -0
- package/src/output-schema-descriptor.js +163 -0
- package/src/output.js +240 -0
- package/src/react-output.js +10 -0
- package/src/runState/ComputeRunStateOptions.ts +4 -0
- package/src/runState/DeriveRunStateInput.ts +10 -0
- package/src/runState/RUN_STATE_HEARTBEAT_STALE_MS.js +1 -0
- package/src/runState/ReasonBlocked.ts +10 -0
- package/src/runState/ReasonUnhealthy.ts +6 -0
- package/src/runState/RunState.ts +12 -0
- package/src/runState/RunStateView.ts +11 -0
- package/src/runState/computeRunState.js +22 -0
- package/src/runState/computeRunStateFromRow.js +102 -0
- package/src/runState/deriveRunState.js +109 -0
- package/src/runState/parseEventMeta.js +18 -0
- package/src/runState/parseTimerMeta.js +16 -0
- package/src/runState-types.ts +23 -0
- package/src/runState.js +7 -0
- package/src/schema-signature.js +22 -0
- package/src/snapshot.js +125 -0
- package/src/sql-message-storage.js +839 -0
- package/src/storage/InMemoryStorage.js +484 -0
- package/src/storage/StorageService.js +7 -0
- package/src/storage/StorageServiceShape.ts +122 -0
- package/src/storage/StorageServiceTypes.ts +150 -0
- package/src/unwrapZodType.js +17 -0
- package/src/utils/camelToSnake.js +6 -0
- package/src/withSqliteWriteRetryEffect.js +110 -0
- package/src/write-retry.js +49 -0
- package/src/zodToCreateTableSQL.js +41 -0
- package/src/zodToTable.js +60 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { canonicalizeXml, parseXmlJson } from "@smithers-orchestrator/graph/utils/xml";
|
|
2
|
+
/** @typedef {import("./frame-codec/FrameDelta.ts").FrameDelta} FrameDelta */
|
|
3
|
+
/** @typedef {import("./frame-codec/FrameDeltaOp.ts").FrameDeltaOp} FrameDeltaOp */
|
|
4
|
+
/** @typedef {import("./frame-codec/FrameEncoding.ts").FrameEncoding} FrameEncoding */
|
|
5
|
+
/** @typedef {import("./frame-codec/JsonPath.ts").JsonPath} JsonPath */
|
|
6
|
+
/** @typedef {import("./frame-codec/JsonPathSegment.ts").JsonPathSegment} JsonPathSegment */
|
|
7
|
+
|
|
8
|
+
export const FRAME_KEYFRAME_INTERVAL = 50;
|
|
9
|
+
const FRAME_DELTA_VERSION = 1;
|
|
10
|
+
/**
|
|
11
|
+
* @param {unknown} value
|
|
12
|
+
* @returns {FrameEncoding}
|
|
13
|
+
*/
|
|
14
|
+
export function normalizeFrameEncoding(value) {
|
|
15
|
+
if (value === "delta")
|
|
16
|
+
return "delta";
|
|
17
|
+
if (value === "keyframe")
|
|
18
|
+
return "keyframe";
|
|
19
|
+
return "full";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} deltaJson
|
|
23
|
+
* @returns {FrameDelta}
|
|
24
|
+
*/
|
|
25
|
+
export function parseFrameDelta(deltaJson) {
|
|
26
|
+
const parsed = JSON.parse(deltaJson);
|
|
27
|
+
if (!isRecord(parsed)) {
|
|
28
|
+
throw new Error("Invalid frame delta payload (not an object)");
|
|
29
|
+
}
|
|
30
|
+
if (parsed.version !== FRAME_DELTA_VERSION) {
|
|
31
|
+
throw new Error(`Unsupported frame delta version: ${String(parsed.version)}`);
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(parsed.ops)) {
|
|
34
|
+
throw new Error("Invalid frame delta payload (missing ops array)");
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @param {FrameDelta} delta
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
export function serializeFrameDelta(delta) {
|
|
43
|
+
return JSON.stringify(delta);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} previousXmlJson
|
|
47
|
+
* @param {string} nextXmlJson
|
|
48
|
+
* @returns {FrameDelta}
|
|
49
|
+
*/
|
|
50
|
+
export function encodeFrameDelta(previousXmlJson, nextXmlJson) {
|
|
51
|
+
const prev = parseXmlJson(previousXmlJson);
|
|
52
|
+
const next = parseXmlJson(nextXmlJson);
|
|
53
|
+
const ops = [];
|
|
54
|
+
diffValues(prev, next, [], ops, null);
|
|
55
|
+
return {
|
|
56
|
+
version: FRAME_DELTA_VERSION,
|
|
57
|
+
ops,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @param {string} previousXmlJson
|
|
62
|
+
* @param {FrameDelta} delta
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
export function applyFrameDelta(previousXmlJson, delta) {
|
|
66
|
+
const root = cloneValue(parseXmlJson(previousXmlJson));
|
|
67
|
+
const next = applyOps(root, delta.ops);
|
|
68
|
+
return canonicalizeXml(next);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} previousXmlJson
|
|
72
|
+
* @param {string} deltaJson
|
|
73
|
+
* @returns {string}
|
|
74
|
+
*/
|
|
75
|
+
export function applyFrameDeltaJson(previousXmlJson, deltaJson) {
|
|
76
|
+
return applyFrameDelta(previousXmlJson, parseFrameDelta(deltaJson));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @param {unknown} prev
|
|
80
|
+
* @param {unknown} next
|
|
81
|
+
* @param {JsonPath} path
|
|
82
|
+
* @param {FrameDeltaOp[]} ops
|
|
83
|
+
* @param {string | null} currentNodeId
|
|
84
|
+
*/
|
|
85
|
+
function diffValues(prev, next, path, ops, currentNodeId) {
|
|
86
|
+
if (deepEqual(prev, next))
|
|
87
|
+
return;
|
|
88
|
+
if (prev === undefined && next !== undefined) {
|
|
89
|
+
pushSet(ops, path, next, currentNodeId);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (next === undefined) {
|
|
93
|
+
pushRemove(ops, path, currentNodeId);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const prevIsObj = isRecord(prev);
|
|
97
|
+
const nextIsObj = isRecord(next);
|
|
98
|
+
if (Array.isArray(prev) && Array.isArray(next)) {
|
|
99
|
+
diffArrays(prev, next, path, ops, currentNodeId);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (prevIsObj && nextIsObj) {
|
|
103
|
+
const nodeId = inferNodeId(next, inferNodeId(prev, currentNodeId));
|
|
104
|
+
diffObjects(prev, next, path, ops, nodeId);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
pushSet(ops, path, next, currentNodeId);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @param {Record<string, unknown>} prev
|
|
111
|
+
* @param {Record<string, unknown>} next
|
|
112
|
+
* @param {JsonPath} path
|
|
113
|
+
* @param {FrameDeltaOp[]} ops
|
|
114
|
+
* @param {string | null} currentNodeId
|
|
115
|
+
*/
|
|
116
|
+
function diffObjects(prev, next, path, ops, currentNodeId) {
|
|
117
|
+
const keys = Array.from(new Set([...Object.keys(prev), ...Object.keys(next)])).sort();
|
|
118
|
+
for (const key of keys) {
|
|
119
|
+
const hasPrev = Object.prototype.hasOwnProperty.call(prev, key);
|
|
120
|
+
const hasNext = Object.prototype.hasOwnProperty.call(next, key);
|
|
121
|
+
const nextPath = [...path, key];
|
|
122
|
+
if (!hasNext && hasPrev) {
|
|
123
|
+
pushRemove(ops, nextPath, currentNodeId);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (hasNext && !hasPrev) {
|
|
127
|
+
pushSet(ops, nextPath, next[key], currentNodeId);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
diffValues(prev[key], next[key], nextPath, ops, currentNodeId);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* @param {unknown[]} prev
|
|
135
|
+
* @param {unknown[]} next
|
|
136
|
+
* @param {JsonPath} path
|
|
137
|
+
* @param {FrameDeltaOp[]} ops
|
|
138
|
+
* @param {string | null} currentNodeId
|
|
139
|
+
*/
|
|
140
|
+
function diffArrays(prev, next, path, ops, currentNodeId) {
|
|
141
|
+
let start = 0;
|
|
142
|
+
while (start < prev.length && start < next.length && deepEqual(prev[start], next[start])) {
|
|
143
|
+
start += 1;
|
|
144
|
+
}
|
|
145
|
+
let prevEnd = prev.length - 1;
|
|
146
|
+
let nextEnd = next.length - 1;
|
|
147
|
+
while (prevEnd >= start && nextEnd >= start && deepEqual(prev[prevEnd], next[nextEnd])) {
|
|
148
|
+
prevEnd -= 1;
|
|
149
|
+
nextEnd -= 1;
|
|
150
|
+
}
|
|
151
|
+
const prevCount = prevEnd - start + 1;
|
|
152
|
+
const nextCount = nextEnd - start + 1;
|
|
153
|
+
if (prevCount <= 0 && nextCount <= 0) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (prevCount <= 0) {
|
|
157
|
+
for (let i = 0; i < nextCount; i += 1) {
|
|
158
|
+
pushInsert(ops, [...path, start + i], next[start + i], currentNodeId);
|
|
159
|
+
}
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (nextCount <= 0) {
|
|
163
|
+
for (let i = prevEnd; i >= start; i -= 1) {
|
|
164
|
+
pushRemove(ops, [...path, i], currentNodeId);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (prevCount === nextCount) {
|
|
169
|
+
for (let i = 0; i < prevCount; i += 1) {
|
|
170
|
+
const prevValue = prev[start + i];
|
|
171
|
+
const nextValue = next[start + i];
|
|
172
|
+
const childNodeId = inferNodeId(nextValue, inferNodeId(prevValue, currentNodeId));
|
|
173
|
+
diffValues(prevValue, nextValue, [...path, start + i], ops, childNodeId);
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
for (let i = prevEnd; i >= start; i -= 1) {
|
|
178
|
+
pushRemove(ops, [...path, i], currentNodeId);
|
|
179
|
+
}
|
|
180
|
+
for (let i = 0; i < nextCount; i += 1) {
|
|
181
|
+
pushInsert(ops, [...path, start + i], next[start + i], currentNodeId);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* @param {FrameDeltaOp[]} ops
|
|
186
|
+
* @param {JsonPath} path
|
|
187
|
+
* @param {unknown} value
|
|
188
|
+
* @param {string | null} nodeId
|
|
189
|
+
*/
|
|
190
|
+
function pushSet(ops, path, value, nodeId) {
|
|
191
|
+
const op = {
|
|
192
|
+
op: "set",
|
|
193
|
+
path,
|
|
194
|
+
value: cloneValue(value),
|
|
195
|
+
...(nodeId ? { nodeId } : {}),
|
|
196
|
+
};
|
|
197
|
+
ops.push(op);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* @param {FrameDeltaOp[]} ops
|
|
201
|
+
* @param {JsonPath} path
|
|
202
|
+
* @param {unknown} value
|
|
203
|
+
* @param {string | null} nodeId
|
|
204
|
+
*/
|
|
205
|
+
function pushInsert(ops, path, value, nodeId) {
|
|
206
|
+
const op = {
|
|
207
|
+
op: "insert",
|
|
208
|
+
path,
|
|
209
|
+
value: cloneValue(value),
|
|
210
|
+
...(nodeId ? { nodeId } : {}),
|
|
211
|
+
};
|
|
212
|
+
ops.push(op);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* @param {FrameDeltaOp[]} ops
|
|
216
|
+
* @param {JsonPath} path
|
|
217
|
+
* @param {string | null} nodeId
|
|
218
|
+
*/
|
|
219
|
+
function pushRemove(ops, path, nodeId) {
|
|
220
|
+
const op = {
|
|
221
|
+
op: "remove",
|
|
222
|
+
path,
|
|
223
|
+
...(nodeId ? { nodeId } : {}),
|
|
224
|
+
};
|
|
225
|
+
ops.push(op);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @param {unknown} root
|
|
229
|
+
* @param {FrameDeltaOp[]} ops
|
|
230
|
+
* @returns {unknown}
|
|
231
|
+
*/
|
|
232
|
+
function applyOps(root, ops) {
|
|
233
|
+
let current = root;
|
|
234
|
+
for (const op of ops) {
|
|
235
|
+
if (op.op === "set") {
|
|
236
|
+
current = setAtPath(current, op.path, op.value);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (op.op === "insert") {
|
|
240
|
+
current = insertAtPath(current, op.path, op.value);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
current = removeAtPath(current, op.path);
|
|
244
|
+
}
|
|
245
|
+
return current;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* @param {unknown} root
|
|
249
|
+
* @param {JsonPath} path
|
|
250
|
+
* @param {unknown} value
|
|
251
|
+
* @returns {unknown}
|
|
252
|
+
*/
|
|
253
|
+
function setAtPath(root, path, value) {
|
|
254
|
+
if (path.length === 0) {
|
|
255
|
+
return cloneValue(value);
|
|
256
|
+
}
|
|
257
|
+
const { parent, key } = getParentAndKey(root, path);
|
|
258
|
+
if (Array.isArray(parent)) {
|
|
259
|
+
if (typeof key !== "number") {
|
|
260
|
+
throw new Error("Invalid array set path");
|
|
261
|
+
}
|
|
262
|
+
parent[key] = cloneValue(value);
|
|
263
|
+
return root;
|
|
264
|
+
}
|
|
265
|
+
if (!isRecord(parent) || typeof key !== "string") {
|
|
266
|
+
throw new Error("Invalid object set path");
|
|
267
|
+
}
|
|
268
|
+
parent[key] = cloneValue(value);
|
|
269
|
+
return root;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* @param {unknown} root
|
|
273
|
+
* @param {JsonPath} path
|
|
274
|
+
* @param {unknown} value
|
|
275
|
+
* @returns {unknown}
|
|
276
|
+
*/
|
|
277
|
+
function insertAtPath(root, path, value) {
|
|
278
|
+
if (path.length === 0) {
|
|
279
|
+
return cloneValue(value);
|
|
280
|
+
}
|
|
281
|
+
const { parent, key } = getParentAndKey(root, path);
|
|
282
|
+
if (!Array.isArray(parent) || typeof key !== "number") {
|
|
283
|
+
throw new Error("Invalid insert path");
|
|
284
|
+
}
|
|
285
|
+
parent.splice(key, 0, cloneValue(value));
|
|
286
|
+
return root;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* @param {unknown} root
|
|
290
|
+
* @param {JsonPath} path
|
|
291
|
+
* @returns {unknown}
|
|
292
|
+
*/
|
|
293
|
+
function removeAtPath(root, path) {
|
|
294
|
+
if (path.length === 0) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const { parent, key } = getParentAndKey(root, path);
|
|
298
|
+
if (Array.isArray(parent)) {
|
|
299
|
+
if (typeof key !== "number") {
|
|
300
|
+
throw new Error("Invalid array remove path");
|
|
301
|
+
}
|
|
302
|
+
parent.splice(key, 1);
|
|
303
|
+
return root;
|
|
304
|
+
}
|
|
305
|
+
if (!isRecord(parent) || typeof key !== "string") {
|
|
306
|
+
throw new Error("Invalid object remove path");
|
|
307
|
+
}
|
|
308
|
+
delete parent[key];
|
|
309
|
+
return root;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* @param {unknown} root
|
|
313
|
+
* @param {JsonPath} path
|
|
314
|
+
* @returns {{ parent: unknown; key: JsonPathSegment }}
|
|
315
|
+
*/
|
|
316
|
+
function getParentAndKey(root, path) {
|
|
317
|
+
let cursor = root;
|
|
318
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
319
|
+
const seg = path[i];
|
|
320
|
+
if (typeof seg === "number") {
|
|
321
|
+
if (!Array.isArray(cursor)) {
|
|
322
|
+
throw new Error("Invalid numeric path segment");
|
|
323
|
+
}
|
|
324
|
+
cursor = cursor[seg];
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (!isRecord(cursor)) {
|
|
328
|
+
throw new Error("Invalid object path segment");
|
|
329
|
+
}
|
|
330
|
+
cursor = cursor[seg];
|
|
331
|
+
}
|
|
332
|
+
return { parent: cursor, key: path[path.length - 1] };
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* @param {unknown} value
|
|
336
|
+
* @param {string | null} fallback
|
|
337
|
+
* @returns {string | null}
|
|
338
|
+
*/
|
|
339
|
+
function inferNodeId(value, fallback) {
|
|
340
|
+
if (!isRecord(value))
|
|
341
|
+
return fallback;
|
|
342
|
+
if (value.kind !== "element")
|
|
343
|
+
return fallback;
|
|
344
|
+
const props = value.props;
|
|
345
|
+
if (!isRecord(props))
|
|
346
|
+
return fallback;
|
|
347
|
+
const id = props.id;
|
|
348
|
+
return typeof id === "string" && id.length > 0 ? id : fallback;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* @param {unknown} a
|
|
352
|
+
* @param {unknown} b
|
|
353
|
+
* @returns {boolean}
|
|
354
|
+
*/
|
|
355
|
+
function deepEqual(a, b) {
|
|
356
|
+
if (Object.is(a, b))
|
|
357
|
+
return true;
|
|
358
|
+
if (typeof a !== typeof b)
|
|
359
|
+
return false;
|
|
360
|
+
if (a === null || b === null)
|
|
361
|
+
return a === b;
|
|
362
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
363
|
+
if (!Array.isArray(a) || !Array.isArray(b))
|
|
364
|
+
return false;
|
|
365
|
+
if (a.length !== b.length)
|
|
366
|
+
return false;
|
|
367
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
368
|
+
if (!deepEqual(a[i], b[i]))
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
if (isRecord(a) || isRecord(b)) {
|
|
374
|
+
if (!isRecord(a) || !isRecord(b))
|
|
375
|
+
return false;
|
|
376
|
+
const keysA = Object.keys(a).sort();
|
|
377
|
+
const keysB = Object.keys(b).sort();
|
|
378
|
+
if (keysA.length !== keysB.length)
|
|
379
|
+
return false;
|
|
380
|
+
for (let i = 0; i < keysA.length; i += 1) {
|
|
381
|
+
if (keysA[i] !== keysB[i])
|
|
382
|
+
return false;
|
|
383
|
+
const key = keysA[i];
|
|
384
|
+
if (!deepEqual(a[key], b[key]))
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* @param {unknown} value
|
|
393
|
+
* @returns {value is Record<string, unknown>}
|
|
394
|
+
*/
|
|
395
|
+
function isRecord(value) {
|
|
396
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* @template T
|
|
400
|
+
* @param {T} value
|
|
401
|
+
* @returns {T}
|
|
402
|
+
*/
|
|
403
|
+
function cloneValue(value) {
|
|
404
|
+
if (value === null)
|
|
405
|
+
return value;
|
|
406
|
+
if (typeof value !== "object")
|
|
407
|
+
return value;
|
|
408
|
+
return JSON.parse(JSON.stringify(value));
|
|
409
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { SqlMessageStorage } from "./SqlMessageStorage.js";
|
|
3
|
+
/** @typedef {import("drizzle-orm/bun-sqlite").BunSQLiteDatabase} BunSQLiteDatabase */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {BunSQLiteDatabase<any> | Database} db
|
|
7
|
+
* @returns {SqlMessageStorage}
|
|
8
|
+
*/
|
|
9
|
+
export function getSqlMessageStorage(db) {
|
|
10
|
+
return new SqlMessageStorage(db);
|
|
11
|
+
}
|