@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.
Files changed (130) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +43 -0
  3. package/src/JsonBounds.ts +6 -0
  4. package/src/SchemaRegistryEntry.ts +6 -0
  5. package/src/SqlMessageStorage.js +818 -0
  6. package/src/SqlMessageStorageEventHistoryQuery.ts +7 -0
  7. package/src/SqliteWriteRetryOptions.ts +7 -0
  8. package/src/adapter/AlertRow.ts +29 -0
  9. package/src/adapter/AlertSeverity.ts +2 -0
  10. package/src/adapter/AlertStatus.ts +2 -0
  11. package/src/adapter/ApprovalRow.ts +13 -0
  12. package/src/adapter/AttemptRow.ts +17 -0
  13. package/src/adapter/CacheRow.ts +12 -0
  14. package/src/adapter/DB_ALERT_ALLOWED_SEVERITIES.js +5 -0
  15. package/src/adapter/DB_ALERT_ALLOWED_STATUSES.js +6 -0
  16. package/src/adapter/DB_ALERT_ID_MAX_LENGTH.js +1 -0
  17. package/src/adapter/DB_ALERT_MESSAGE_MAX_LENGTH.js +1 -0
  18. package/src/adapter/DB_ALERT_POLICY_NAME_MAX_LENGTH.js +1 -0
  19. package/src/adapter/DB_RUN_ALLOWED_STATUSES.js +10 -0
  20. package/src/adapter/DB_RUN_ID_MAX_LENGTH.js +1 -0
  21. package/src/adapter/DB_RUN_WORKFLOW_NAME_MAX_LENGTH.js +1 -0
  22. package/src/adapter/EventHistoryQuery.ts +7 -0
  23. package/src/adapter/HumanRequestRow.ts +19 -0
  24. package/src/adapter/NodeDiffCacheRow.ts +9 -0
  25. package/src/adapter/NodeRow.ts +10 -0
  26. package/src/adapter/PendingHumanRequestRow.ts +7 -0
  27. package/src/adapter/RunAncestryRow.ts +5 -0
  28. package/src/adapter/RunRow.ts +21 -0
  29. package/src/adapter/SignalQuery.ts +6 -0
  30. package/src/adapter/SignalRow.ts +9 -0
  31. package/src/adapter/SmithersDb.js +2236 -0
  32. package/src/adapter/StaleRunRecord.ts +7 -0
  33. package/src/adapter/index.js +27 -0
  34. package/src/adapter.js +2359 -0
  35. package/src/assertJsonPayloadWithinBounds.js +94 -0
  36. package/src/assertMaxBytes.js +23 -0
  37. package/src/assertMaxJsonDepth.js +40 -0
  38. package/src/assertMaxStringLength.js +16 -0
  39. package/src/assertOptionalArrayMaxLength.js +16 -0
  40. package/src/assertOptionalStringMaxLength.js +11 -0
  41. package/src/assertPositiveFiniteInteger.js +14 -0
  42. package/src/assertPositiveFiniteNumber.js +12 -0
  43. package/src/buildHumanRequestId.js +9 -0
  44. package/src/cache/nodeDiffCache.js +124 -0
  45. package/src/ensure.js +18 -0
  46. package/src/ensureSqlMessageStorage.js +11 -0
  47. package/src/ensureSqlMessageStorageEffect.js +12 -0
  48. package/src/frame-codec/FRAME_KEYFRAME_INTERVAL.js +1 -0
  49. package/src/frame-codec/FrameDelta.ts +6 -0
  50. package/src/frame-codec/FrameDeltaOp.ts +20 -0
  51. package/src/frame-codec/FrameEncoding.ts +1 -0
  52. package/src/frame-codec/JsonPath.ts +3 -0
  53. package/src/frame-codec/JsonPathSegment.ts +1 -0
  54. package/src/frame-codec/applyFrameDelta.js +143 -0
  55. package/src/frame-codec/applyFrameDeltaJson.js +10 -0
  56. package/src/frame-codec/encodeFrameDelta.js +247 -0
  57. package/src/frame-codec/index.js +15 -0
  58. package/src/frame-codec/normalizeFrameEncoding.js +13 -0
  59. package/src/frame-codec/parseFrameDelta.js +27 -0
  60. package/src/frame-codec/serializeFrameDelta.js +9 -0
  61. package/src/frame-codec.js +409 -0
  62. package/src/getSqlMessageStorage.js +11 -0
  63. package/src/index.d.ts +5203 -0
  64. package/src/index.js +20 -0
  65. package/src/input-bounds.js +12 -0
  66. package/src/input.js +17 -0
  67. package/src/internal-schema/index.js +19 -0
  68. package/src/internal-schema/smithersAlerts.js +27 -0
  69. package/src/internal-schema/smithersApprovals.js +18 -0
  70. package/src/internal-schema/smithersAttempts.js +20 -0
  71. package/src/internal-schema/smithersCache.js +13 -0
  72. package/src/internal-schema/smithersCron.js +11 -0
  73. package/src/internal-schema/smithersEvents.js +10 -0
  74. package/src/internal-schema/smithersFrames.js +14 -0
  75. package/src/internal-schema/smithersHumanRequests.js +17 -0
  76. package/src/internal-schema/smithersNodeDiffs.js +12 -0
  77. package/src/internal-schema/smithersNodes.js +13 -0
  78. package/src/internal-schema/smithersRalph.js +10 -0
  79. package/src/internal-schema/smithersRuns.js +22 -0
  80. package/src/internal-schema/smithersSandboxes.js +16 -0
  81. package/src/internal-schema/smithersSignals.js +12 -0
  82. package/src/internal-schema/smithersTimeTravelAudit.js +12 -0
  83. package/src/internal-schema/smithersToolCalls.js +19 -0
  84. package/src/internal-schema/smithersVectors.js +12 -0
  85. package/src/internal-schema.js +245 -0
  86. package/src/isRetryableSqliteWriteError.js +53 -0
  87. package/src/loadInputEffect.js +28 -0
  88. package/src/loadOutputsEffect.js +87 -0
  89. package/src/output/OutputKey.ts +1 -0
  90. package/src/output/buildKeyWhere.js +17 -0
  91. package/src/output/buildOutputRow.js +34 -0
  92. package/src/output/describeSchemaShape.js +70 -0
  93. package/src/output/getAgentOutputSchema.js +13 -0
  94. package/src/output/getKeyColumns.js +19 -0
  95. package/src/output/index.js +14 -0
  96. package/src/output/selectOutputRowEffect.js +30 -0
  97. package/src/output/stripAutoColumns.js +10 -0
  98. package/src/output/upsertOutputRowEffect.js +38 -0
  99. package/src/output/validateExistingOutput.js +17 -0
  100. package/src/output/validateOutput.js +17 -0
  101. package/src/output-schema-descriptor.js +163 -0
  102. package/src/output.js +240 -0
  103. package/src/react-output.js +10 -0
  104. package/src/runState/ComputeRunStateOptions.ts +4 -0
  105. package/src/runState/DeriveRunStateInput.ts +10 -0
  106. package/src/runState/RUN_STATE_HEARTBEAT_STALE_MS.js +1 -0
  107. package/src/runState/ReasonBlocked.ts +10 -0
  108. package/src/runState/ReasonUnhealthy.ts +6 -0
  109. package/src/runState/RunState.ts +12 -0
  110. package/src/runState/RunStateView.ts +11 -0
  111. package/src/runState/computeRunState.js +22 -0
  112. package/src/runState/computeRunStateFromRow.js +102 -0
  113. package/src/runState/deriveRunState.js +109 -0
  114. package/src/runState/parseEventMeta.js +18 -0
  115. package/src/runState/parseTimerMeta.js +16 -0
  116. package/src/runState-types.ts +23 -0
  117. package/src/runState.js +7 -0
  118. package/src/schema-signature.js +22 -0
  119. package/src/snapshot.js +125 -0
  120. package/src/sql-message-storage.js +839 -0
  121. package/src/storage/InMemoryStorage.js +484 -0
  122. package/src/storage/StorageService.js +7 -0
  123. package/src/storage/StorageServiceShape.ts +122 -0
  124. package/src/storage/StorageServiceTypes.ts +150 -0
  125. package/src/unwrapZodType.js +17 -0
  126. package/src/utils/camelToSnake.js +6 -0
  127. package/src/withSqliteWriteRetryEffect.js +110 -0
  128. package/src/write-retry.js +49 -0
  129. package/src/zodToCreateTableSQL.js +41 -0
  130. package/src/zodToTable.js +60 -0
@@ -0,0 +1,125 @@
1
+ import { eq, getTableName } from "drizzle-orm";
2
+ import { getTableColumns } from "drizzle-orm/utils";
3
+ import { Effect, Option } from "effect";
4
+ import { toSmithersError } from "@smithers-orchestrator/errors/toSmithersError";
5
+ import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
6
+ /** @typedef {import("@smithers-orchestrator/driver/OutputSnapshot").OutputSnapshot} OutputSnapshot */
7
+ /** @typedef {import("drizzle-orm/bun-sqlite").BunSQLiteDatabase} BunSQLiteDatabase */
8
+ /** @typedef {import("drizzle-orm").Table} _Table */
9
+
10
+ /**
11
+ * @param {_Table} table
12
+ * @returns {string[]}
13
+ */
14
+ function getBooleanColumnKeys(table) {
15
+ try {
16
+ const cols = getTableColumns(table);
17
+ const keys = [];
18
+ for (const [key, col] of Object.entries(cols)) {
19
+ const c = /** @type {Record<string, unknown> & { config?: { mode?: string }; mapFromDriverValue?: unknown }} */ (/** @type {unknown} */ (col));
20
+ const mapFn = /** @type {{ toString?: () => string } | undefined} */ (c?.mapFromDriverValue);
21
+ if (c?.columnType === "SQLiteBoolean" || c?.config?.mode === "boolean" || c?.mode === "boolean" || mapFn?.toString?.().includes("Boolean") || (c?.dataType === "boolean")) {
22
+ keys.push(key);
23
+ }
24
+ }
25
+ return keys;
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+ /**
31
+ * @param {ReadonlyArray<Record<string, unknown>>} rows
32
+ * @param {readonly string[]} boolKeys
33
+ * @returns {Array<Record<string, unknown>>}
34
+ */
35
+ function coerceBooleanColumns(rows, boolKeys) {
36
+ if (boolKeys.length === 0) return rows.slice();
37
+ return rows.map((row) => {
38
+ if (!row) return row;
39
+ /** @type {Record<string, unknown>} */
40
+ const patched = { ...row };
41
+ for (const key of boolKeys) {
42
+ if (key in patched && typeof patched[key] !== "boolean") {
43
+ patched[key] = Boolean(patched[key]);
44
+ }
45
+ }
46
+ return patched;
47
+ });
48
+ }
49
+ /**
50
+ * @param {BunSQLiteDatabase<Record<string, unknown>>} db
51
+ * @param {_Table} inputTable
52
+ * @param {string} runId
53
+ * @returns {Effect.Effect<Record<string, unknown> | undefined, SmithersError>}
54
+ */
55
+ export function loadInputEffect(db, inputTable, runId) {
56
+ const cols = getTableColumns(inputTable);
57
+ const runIdCol = cols.runId;
58
+ if (!runIdCol) {
59
+ throw new SmithersError("DB_MISSING_COLUMNS", "schema.input must include runId column");
60
+ }
61
+ return Effect.tryPromise({
62
+ try: () => db.select().from(inputTable).where(eq(runIdCol, runId)).limit(1),
63
+ catch: (cause) => toSmithersError(cause, "load input", {
64
+ code: "DB_QUERY_FAILED",
65
+ details: { runId },
66
+ }),
67
+ }).pipe(Effect.map((rows) => rows[0]), Effect.annotateLogs({ runId }), Effect.withLogSpan("db:load-input"));
68
+ }
69
+ /**
70
+ * @param {BunSQLiteDatabase<Record<string, unknown>>} db
71
+ * @param {_Table} inputTable
72
+ * @param {string} runId
73
+ * @returns {Promise<Record<string, unknown> | undefined>}
74
+ */
75
+ export function loadInput(db, inputTable, runId) {
76
+ return Effect.runPromise(loadInputEffect(db, inputTable, runId));
77
+ }
78
+ /**
79
+ * @param {BunSQLiteDatabase<Record<string, unknown>>} db
80
+ * @param {Record<string, _Table | unknown>} schema
81
+ * @param {string} runId
82
+ * @returns {Effect.Effect<OutputSnapshot, SmithersError>}
83
+ */
84
+ export function loadOutputsEffect(db, schema, runId) {
85
+ return Effect.gen(function* () {
86
+ /** @type {Record<string, ReadonlyArray<Record<string, unknown>>>} */
87
+ const out = {};
88
+ for (const [key, table] of Object.entries(schema)) {
89
+ if (!table || typeof table !== "object") continue;
90
+ if (key === "input") continue;
91
+ const colsOpt = yield* Effect.try({
92
+ try: () => getTableColumns(/** @type {_Table} */ (table)),
93
+ catch: (cause) => toSmithersError(cause, "get table columns", { code: "DB_QUERY_FAILED", details: { runId, schemaKey: key } }),
94
+ }).pipe(Effect.option);
95
+ if (Option.isNone(colsOpt)) continue;
96
+ const cols = colsOpt.value;
97
+ const runIdCol = cols.runId;
98
+ if (!runIdCol) continue;
99
+ const tableNameOpt = yield* Effect.try({
100
+ try: () => getTableName(/** @type {_Table} */ (table)),
101
+ catch: (cause) => toSmithersError(cause, "get table name", { code: "DB_QUERY_FAILED", details: { runId, schemaKey: key } }),
102
+ }).pipe(Effect.option);
103
+ if (Option.isNone(tableNameOpt)) continue;
104
+ const tableName = tableNameOpt.value;
105
+ const rawRows = yield* Effect.tryPromise({
106
+ try: () => db.select().from(/** @type {_Table} */ (table)).where(eq(runIdCol, runId)),
107
+ catch: (cause) => toSmithersError(cause, `load outputs ${tableName}`, { code: "DB_QUERY_FAILED", details: { runId, tableName } }),
108
+ });
109
+ const boolKeys = getBooleanColumnKeys(/** @type {_Table} */ (table));
110
+ const rows = coerceBooleanColumns(rawRows, boolKeys);
111
+ out[tableName] = rows;
112
+ out[key] = rows;
113
+ }
114
+ return /** @type {OutputSnapshot} */ (/** @type {unknown} */ (out));
115
+ }).pipe(Effect.annotateLogs({ runId }), Effect.withLogSpan("db:load-outputs"));
116
+ }
117
+ /**
118
+ * @param {BunSQLiteDatabase<Record<string, unknown>>} db
119
+ * @param {Record<string, _Table | unknown>} schema
120
+ * @param {string} runId
121
+ * @returns {Promise<OutputSnapshot>}
122
+ */
123
+ export function loadOutputs(db, schema, runId) {
124
+ return Effect.runPromise(loadOutputsEffect(db, schema, runId));
125
+ }