@blokjs/runner 0.2.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 (307) hide show
  1. package/dist/Blok.d.ts +19 -0
  2. package/dist/Blok.js +184 -0
  3. package/dist/Blok.js.map +1 -0
  4. package/dist/BlokResponse.d.ts +16 -0
  5. package/dist/BlokResponse.js +28 -0
  6. package/dist/BlokResponse.js.map +1 -0
  7. package/dist/Configuration.d.ts +37 -0
  8. package/dist/Configuration.js +248 -0
  9. package/dist/Configuration.js.map +1 -0
  10. package/dist/ConfigurationResolver.d.ts +7 -0
  11. package/dist/ConfigurationResolver.js +15 -0
  12. package/dist/ConfigurationResolver.js.map +1 -0
  13. package/dist/DefaultLogger.d.ts +65 -0
  14. package/dist/DefaultLogger.js +101 -0
  15. package/dist/DefaultLogger.js.map +1 -0
  16. package/dist/LocalStorage.d.ts +7 -0
  17. package/dist/LocalStorage.js +56 -0
  18. package/dist/LocalStorage.js.map +1 -0
  19. package/dist/MemoryUsage.d.ts +22 -0
  20. package/dist/MemoryUsage.js +83 -0
  21. package/dist/MemoryUsage.js.map +1 -0
  22. package/dist/NodeMap.d.ts +7 -0
  23. package/dist/NodeMap.js +13 -0
  24. package/dist/NodeMap.js.map +1 -0
  25. package/dist/ResolverBase.d.ts +8 -0
  26. package/dist/ResolverBase.js +18 -0
  27. package/dist/ResolverBase.js.map +1 -0
  28. package/dist/Runner.d.ts +25 -0
  29. package/dist/Runner.js +32 -0
  30. package/dist/Runner.js.map +1 -0
  31. package/dist/RunnerNode.d.ts +9 -0
  32. package/dist/RunnerNode.js +8 -0
  33. package/dist/RunnerNode.js.map +1 -0
  34. package/dist/RunnerNodeBase.d.ts +4 -0
  35. package/dist/RunnerNodeBase.js +3 -0
  36. package/dist/RunnerNodeBase.js.map +1 -0
  37. package/dist/RunnerSteps.d.ts +14 -0
  38. package/dist/RunnerSteps.js +110 -0
  39. package/dist/RunnerSteps.js.map +1 -0
  40. package/dist/RuntimeAdapterNode.d.ts +19 -0
  41. package/dist/RuntimeAdapterNode.js +87 -0
  42. package/dist/RuntimeAdapterNode.js.map +1 -0
  43. package/dist/RuntimeRegistry.d.ts +61 -0
  44. package/dist/RuntimeRegistry.js +87 -0
  45. package/dist/RuntimeRegistry.js.map +1 -0
  46. package/dist/TriggerBase.d.ts +119 -0
  47. package/dist/TriggerBase.js +413 -0
  48. package/dist/TriggerBase.js.map +1 -0
  49. package/dist/adapters/BunRuntimeAdapter.d.ts +38 -0
  50. package/dist/adapters/BunRuntimeAdapter.js +169 -0
  51. package/dist/adapters/BunRuntimeAdapter.js.map +1 -0
  52. package/dist/adapters/DockerRuntimeAdapter.d.ts +85 -0
  53. package/dist/adapters/DockerRuntimeAdapter.js +298 -0
  54. package/dist/adapters/DockerRuntimeAdapter.js.map +1 -0
  55. package/dist/adapters/HttpRuntimeAdapter.d.ts +58 -0
  56. package/dist/adapters/HttpRuntimeAdapter.js +152 -0
  57. package/dist/adapters/HttpRuntimeAdapter.js.map +1 -0
  58. package/dist/adapters/NodeJsRuntimeAdapter.d.ts +23 -0
  59. package/dist/adapters/NodeJsRuntimeAdapter.js +67 -0
  60. package/dist/adapters/NodeJsRuntimeAdapter.js.map +1 -0
  61. package/dist/adapters/RuntimeAdapter.d.ts +42 -0
  62. package/dist/adapters/RuntimeAdapter.js +2 -0
  63. package/dist/adapters/RuntimeAdapter.js.map +1 -0
  64. package/dist/adapters/WasmRuntimeAdapter.d.ts +69 -0
  65. package/dist/adapters/WasmRuntimeAdapter.js +279 -0
  66. package/dist/adapters/WasmRuntimeAdapter.js.map +1 -0
  67. package/dist/cache/NodeResultCache.d.ts +286 -0
  68. package/dist/cache/NodeResultCache.js +499 -0
  69. package/dist/cache/NodeResultCache.js.map +1 -0
  70. package/dist/cache/index.d.ts +1 -0
  71. package/dist/cache/index.js +2 -0
  72. package/dist/cache/index.js.map +1 -0
  73. package/dist/cost/CostEstimator.d.ts +57 -0
  74. package/dist/cost/CostEstimator.js +171 -0
  75. package/dist/cost/CostEstimator.js.map +1 -0
  76. package/dist/cost/index.d.ts +4 -0
  77. package/dist/cost/index.js +3 -0
  78. package/dist/cost/index.js.map +1 -0
  79. package/dist/cost/pricing.d.ts +24 -0
  80. package/dist/cost/pricing.js +169 -0
  81. package/dist/cost/pricing.js.map +1 -0
  82. package/dist/defineNode.d.ts +155 -0
  83. package/dist/defineNode.js +191 -0
  84. package/dist/defineNode.js.map +1 -0
  85. package/dist/graphql/GraphQLSchemaGenerator.d.ts +129 -0
  86. package/dist/graphql/GraphQLSchemaGenerator.js +425 -0
  87. package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -0
  88. package/dist/hmr/FileWatcher.d.ts +62 -0
  89. package/dist/hmr/FileWatcher.js +185 -0
  90. package/dist/hmr/FileWatcher.js.map +1 -0
  91. package/dist/hmr/HmrDevConsole.d.ts +13 -0
  92. package/dist/hmr/HmrDevConsole.js +46 -0
  93. package/dist/hmr/HmrDevConsole.js.map +1 -0
  94. package/dist/hmr/HotReloadManager.d.ts +84 -0
  95. package/dist/hmr/HotReloadManager.js +195 -0
  96. package/dist/hmr/HotReloadManager.js.map +1 -0
  97. package/dist/hmr/index.d.ts +39 -0
  98. package/dist/hmr/index.js +38 -0
  99. package/dist/hmr/index.js.map +1 -0
  100. package/dist/index.d.ts +107 -0
  101. package/dist/index.js +107 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/integrations/APMIntegration.d.ts +141 -0
  104. package/dist/integrations/APMIntegration.js +212 -0
  105. package/dist/integrations/APMIntegration.js.map +1 -0
  106. package/dist/integrations/AzureMonitorIntegration.d.ts +118 -0
  107. package/dist/integrations/AzureMonitorIntegration.js +254 -0
  108. package/dist/integrations/AzureMonitorIntegration.js.map +1 -0
  109. package/dist/integrations/CloudWatchIntegration.d.ts +135 -0
  110. package/dist/integrations/CloudWatchIntegration.js +293 -0
  111. package/dist/integrations/CloudWatchIntegration.js.map +1 -0
  112. package/dist/integrations/SentryIntegration.d.ts +153 -0
  113. package/dist/integrations/SentryIntegration.js +200 -0
  114. package/dist/integrations/SentryIntegration.js.map +1 -0
  115. package/dist/integrations/index.d.ts +19 -0
  116. package/dist/integrations/index.js +16 -0
  117. package/dist/integrations/index.js.map +1 -0
  118. package/dist/marketplace/RuntimeAutoScaler.d.ts +148 -0
  119. package/dist/marketplace/RuntimeAutoScaler.js +366 -0
  120. package/dist/marketplace/RuntimeAutoScaler.js.map +1 -0
  121. package/dist/marketplace/RuntimeCatalog.d.ts +174 -0
  122. package/dist/marketplace/RuntimeCatalog.js +339 -0
  123. package/dist/marketplace/RuntimeCatalog.js.map +1 -0
  124. package/dist/marketplace/RuntimeDiscovery.d.ts +86 -0
  125. package/dist/marketplace/RuntimeDiscovery.js +219 -0
  126. package/dist/marketplace/RuntimeDiscovery.js.map +1 -0
  127. package/dist/marketplace/RuntimeHealthMonitor.d.ts +100 -0
  128. package/dist/marketplace/RuntimeHealthMonitor.js +241 -0
  129. package/dist/marketplace/RuntimeHealthMonitor.js.map +1 -0
  130. package/dist/marketplace/RuntimeMetricsDashboard.d.ts +113 -0
  131. package/dist/marketplace/RuntimeMetricsDashboard.js +293 -0
  132. package/dist/marketplace/RuntimeMetricsDashboard.js.map +1 -0
  133. package/dist/monitoring/CircuitBreaker.d.ts +107 -0
  134. package/dist/monitoring/CircuitBreaker.js +238 -0
  135. package/dist/monitoring/CircuitBreaker.js.map +1 -0
  136. package/dist/monitoring/DistributedTracer.d.ts +125 -0
  137. package/dist/monitoring/DistributedTracer.js +230 -0
  138. package/dist/monitoring/DistributedTracer.js.map +1 -0
  139. package/dist/monitoring/HealthCheck.d.ts +54 -0
  140. package/dist/monitoring/HealthCheck.js +102 -0
  141. package/dist/monitoring/HealthCheck.js.map +1 -0
  142. package/dist/monitoring/PerformanceProfiler.d.ts +63 -0
  143. package/dist/monitoring/PerformanceProfiler.js +229 -0
  144. package/dist/monitoring/PerformanceProfiler.js.map +1 -0
  145. package/dist/monitoring/PrometheusBootstrap.d.ts +30 -0
  146. package/dist/monitoring/PrometheusBootstrap.js +71 -0
  147. package/dist/monitoring/PrometheusBootstrap.js.map +1 -0
  148. package/dist/monitoring/PrometheusMetricsBridge.d.ts +60 -0
  149. package/dist/monitoring/PrometheusMetricsBridge.js +216 -0
  150. package/dist/monitoring/PrometheusMetricsBridge.js.map +1 -0
  151. package/dist/monitoring/RateLimiter.d.ts +58 -0
  152. package/dist/monitoring/RateLimiter.js +128 -0
  153. package/dist/monitoring/RateLimiter.js.map +1 -0
  154. package/dist/monitoring/StructuredLogger.d.ts +131 -0
  155. package/dist/monitoring/StructuredLogger.js +207 -0
  156. package/dist/monitoring/StructuredLogger.js.map +1 -0
  157. package/dist/monitoring/TracingBootstrap.d.ts +69 -0
  158. package/dist/monitoring/TracingBootstrap.js +129 -0
  159. package/dist/monitoring/TracingBootstrap.js.map +1 -0
  160. package/dist/monitoring/TriggerMetricsCollector.d.ts +94 -0
  161. package/dist/monitoring/TriggerMetricsCollector.js +174 -0
  162. package/dist/monitoring/TriggerMetricsCollector.js.map +1 -0
  163. package/dist/monitoring/index.d.ts +9 -0
  164. package/dist/monitoring/index.js +10 -0
  165. package/dist/monitoring/index.js.map +1 -0
  166. package/dist/openapi/OpenAPIGenerator.d.ts +192 -0
  167. package/dist/openapi/OpenAPIGenerator.js +373 -0
  168. package/dist/openapi/OpenAPIGenerator.js.map +1 -0
  169. package/dist/openapi/index.d.ts +20 -0
  170. package/dist/openapi/index.js +20 -0
  171. package/dist/openapi/index.js.map +1 -0
  172. package/dist/security/ABAC.d.ts +224 -0
  173. package/dist/security/ABAC.js +380 -0
  174. package/dist/security/ABAC.js.map +1 -0
  175. package/dist/security/AuditLogger.d.ts +242 -0
  176. package/dist/security/AuditLogger.js +317 -0
  177. package/dist/security/AuditLogger.js.map +1 -0
  178. package/dist/security/AuthMiddleware.d.ts +163 -0
  179. package/dist/security/AuthMiddleware.js +274 -0
  180. package/dist/security/AuthMiddleware.js.map +1 -0
  181. package/dist/security/EncryptionAtRest.d.ts +206 -0
  182. package/dist/security/EncryptionAtRest.js +236 -0
  183. package/dist/security/EncryptionAtRest.js.map +1 -0
  184. package/dist/security/OAuthProvider.d.ts +334 -0
  185. package/dist/security/OAuthProvider.js +719 -0
  186. package/dist/security/OAuthProvider.js.map +1 -0
  187. package/dist/security/PIIDetector.d.ts +233 -0
  188. package/dist/security/PIIDetector.js +354 -0
  189. package/dist/security/PIIDetector.js.map +1 -0
  190. package/dist/security/RBAC.d.ts +143 -0
  191. package/dist/security/RBAC.js +285 -0
  192. package/dist/security/RBAC.js.map +1 -0
  193. package/dist/security/SecretManager.d.ts +652 -0
  194. package/dist/security/SecretManager.js +1146 -0
  195. package/dist/security/SecretManager.js.map +1 -0
  196. package/dist/security/TLSConfig.d.ts +305 -0
  197. package/dist/security/TLSConfig.js +550 -0
  198. package/dist/security/TLSConfig.js.map +1 -0
  199. package/dist/security/index.d.ts +79 -0
  200. package/dist/security/index.js +80 -0
  201. package/dist/security/index.js.map +1 -0
  202. package/dist/testing/TestHarness.d.ts +189 -0
  203. package/dist/testing/TestHarness.js +272 -0
  204. package/dist/testing/TestHarness.js.map +1 -0
  205. package/dist/testing/TestLogger.d.ts +103 -0
  206. package/dist/testing/TestLogger.js +153 -0
  207. package/dist/testing/TestLogger.js.map +1 -0
  208. package/dist/testing/WorkflowTestRunner.d.ts +172 -0
  209. package/dist/testing/WorkflowTestRunner.js +355 -0
  210. package/dist/testing/WorkflowTestRunner.js.map +1 -0
  211. package/dist/testing/index.d.ts +21 -0
  212. package/dist/testing/index.js +22 -0
  213. package/dist/testing/index.js.map +1 -0
  214. package/dist/tracing/InMemoryRunStore.d.ts +44 -0
  215. package/dist/tracing/InMemoryRunStore.js +341 -0
  216. package/dist/tracing/InMemoryRunStore.js.map +1 -0
  217. package/dist/tracing/PostgresRunStore.d.ts +82 -0
  218. package/dist/tracing/PostgresRunStore.js +640 -0
  219. package/dist/tracing/PostgresRunStore.js.map +1 -0
  220. package/dist/tracing/RunStore.d.ts +38 -0
  221. package/dist/tracing/RunStore.js +2 -0
  222. package/dist/tracing/RunStore.js.map +1 -0
  223. package/dist/tracing/RunTracker.d.ts +75 -0
  224. package/dist/tracing/RunTracker.js +374 -0
  225. package/dist/tracing/RunTracker.js.map +1 -0
  226. package/dist/tracing/SqliteRunStore.d.ts +53 -0
  227. package/dist/tracing/SqliteRunStore.js +703 -0
  228. package/dist/tracing/SqliteRunStore.js.map +1 -0
  229. package/dist/tracing/TraceRouter.d.ts +47 -0
  230. package/dist/tracing/TraceRouter.js +904 -0
  231. package/dist/tracing/TraceRouter.js.map +1 -0
  232. package/dist/tracing/TracingLogger.d.ts +21 -0
  233. package/dist/tracing/TracingLogger.js +62 -0
  234. package/dist/tracing/TracingLogger.js.map +1 -0
  235. package/dist/tracing/createStore.d.ts +30 -0
  236. package/dist/tracing/createStore.js +75 -0
  237. package/dist/tracing/createStore.js.map +1 -0
  238. package/dist/tracing/index.d.ts +13 -0
  239. package/dist/tracing/index.js +9 -0
  240. package/dist/tracing/index.js.map +1 -0
  241. package/dist/tracing/sanitize.d.ts +7 -0
  242. package/dist/tracing/sanitize.js +95 -0
  243. package/dist/tracing/sanitize.js.map +1 -0
  244. package/dist/tracing/types.d.ts +178 -0
  245. package/dist/tracing/types.js +3 -0
  246. package/dist/tracing/types.js.map +1 -0
  247. package/dist/types/Average.d.ts +11 -0
  248. package/dist/types/Average.js +2 -0
  249. package/dist/types/Average.js.map +1 -0
  250. package/dist/types/Condition.d.ts +8 -0
  251. package/dist/types/Condition.js +2 -0
  252. package/dist/types/Condition.js.map +1 -0
  253. package/dist/types/Conditions.d.ts +5 -0
  254. package/dist/types/Conditions.js +2 -0
  255. package/dist/types/Conditions.js.map +1 -0
  256. package/dist/types/Config.d.ts +12 -0
  257. package/dist/types/Config.js +2 -0
  258. package/dist/types/Config.js.map +1 -0
  259. package/dist/types/Flow.d.ts +5 -0
  260. package/dist/types/Flow.js +2 -0
  261. package/dist/types/Flow.js.map +1 -0
  262. package/dist/types/GlobalOptions.d.ts +11 -0
  263. package/dist/types/GlobalOptions.js +2 -0
  264. package/dist/types/GlobalOptions.js.map +1 -0
  265. package/dist/types/Inputs.d.ts +5 -0
  266. package/dist/types/Inputs.js +2 -0
  267. package/dist/types/Inputs.js.map +1 -0
  268. package/dist/types/JsonLikeObject.d.ts +3 -0
  269. package/dist/types/JsonLikeObject.js +2 -0
  270. package/dist/types/JsonLikeObject.js.map +1 -0
  271. package/dist/types/Mapper.d.ts +5 -0
  272. package/dist/types/Mapper.js +2 -0
  273. package/dist/types/Mapper.js.map +1 -0
  274. package/dist/types/Node.d.ts +10 -0
  275. package/dist/types/Node.js +2 -0
  276. package/dist/types/Node.js.map +1 -0
  277. package/dist/types/ParamsDictionary.d.ts +3 -0
  278. package/dist/types/ParamsDictionary.js +2 -0
  279. package/dist/types/ParamsDictionary.js.map +1 -0
  280. package/dist/types/Properties.d.ts +5 -0
  281. package/dist/types/Properties.js +2 -0
  282. package/dist/types/Properties.js.map +1 -0
  283. package/dist/types/Targets.d.ts +5 -0
  284. package/dist/types/Targets.js +2 -0
  285. package/dist/types/Targets.js.map +1 -0
  286. package/dist/types/Trigger.d.ts +5 -0
  287. package/dist/types/Trigger.js +2 -0
  288. package/dist/types/Trigger.js.map +1 -0
  289. package/dist/types/TriggerHttp.d.ts +7 -0
  290. package/dist/types/TriggerHttp.js +2 -0
  291. package/dist/types/TriggerHttp.js.map +1 -0
  292. package/dist/types/TriggerResponse.d.ts +6 -0
  293. package/dist/types/TriggerResponse.js +2 -0
  294. package/dist/types/TriggerResponse.js.map +1 -0
  295. package/dist/types/Triggers.d.ts +5 -0
  296. package/dist/types/Triggers.js +2 -0
  297. package/dist/types/Triggers.js.map +1 -0
  298. package/dist/types/TryCatch.d.ts +6 -0
  299. package/dist/types/TryCatch.js +2 -0
  300. package/dist/types/TryCatch.js.map +1 -0
  301. package/dist/visualization/NodeDependencyGraph.d.ts +76 -0
  302. package/dist/visualization/NodeDependencyGraph.js +418 -0
  303. package/dist/visualization/NodeDependencyGraph.js.map +1 -0
  304. package/dist/visualization/WorkflowVisualizer.d.ts +144 -0
  305. package/dist/visualization/WorkflowVisualizer.js +446 -0
  306. package/dist/visualization/WorkflowVisualizer.js.map +1 -0
  307. package/package.json +95 -0
@@ -0,0 +1,703 @@
1
+ import { createRequire } from "node:module";
2
+ const esmRequire = createRequire(import.meta.url);
3
+ const isBun = "Bun" in globalThis;
4
+ /**
5
+ * SQLite-backed RunStore supporting both bun:sqlite and better-sqlite3.
6
+ *
7
+ * When running under Bun, uses the built-in bun:sqlite module for
8
+ * optimal performance. Falls back to better-sqlite3 under Node.js.
9
+ *
10
+ * Provides persistent trace storage that survives process restarts.
11
+ * All operations are synchronous.
12
+ *
13
+ * Schema is auto-migrated on construction via a versioned migration system.
14
+ */
15
+ export class SqliteRunStore {
16
+ db;
17
+ // Prepared statements (lazy-initialized)
18
+ stmts = {};
19
+ constructor(dbPath = ".blok/trace.db") {
20
+ if (isBun) {
21
+ // Use Bun's built-in SQLite (3-6x faster than better-sqlite3)
22
+ const bunMod = "bun:sqlite";
23
+ const { Database } = esmRequire(bunMod);
24
+ this.db = new Database(dbPath);
25
+ }
26
+ else {
27
+ // Fallback to better-sqlite3 for Node.js
28
+ let Database;
29
+ try {
30
+ const mod = "better-sqlite3";
31
+ Database = esmRequire(mod);
32
+ }
33
+ catch {
34
+ throw new Error("SqliteRunStore requires 'better-sqlite3'. Install it:\n" +
35
+ " npm install better-sqlite3\n" +
36
+ " # or\n" +
37
+ " bun add better-sqlite3");
38
+ }
39
+ this.db = new Database(dbPath);
40
+ }
41
+ // Use exec for pragmas — works in both bun:sqlite and better-sqlite3
42
+ this.db.exec("PRAGMA journal_mode = WAL");
43
+ this.db.exec("PRAGMA synchronous = NORMAL");
44
+ this.db.exec("PRAGMA foreign_keys = ON");
45
+ this.migrate();
46
+ }
47
+ // === Schema Migration ===
48
+ migrate() {
49
+ this.db.exec(`
50
+ CREATE TABLE IF NOT EXISTS _trace_migrations (
51
+ version INTEGER PRIMARY KEY,
52
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
53
+ );
54
+ `);
55
+ const applied = new Set(this.db
56
+ .prepare("SELECT version FROM _trace_migrations")
57
+ .all()
58
+ .map((r) => r.version));
59
+ const migrations = [
60
+ {
61
+ version: 1,
62
+ sql: `
63
+ CREATE TABLE IF NOT EXISTS workflow_runs (
64
+ id TEXT PRIMARY KEY,
65
+ workflow_name TEXT NOT NULL,
66
+ workflow_path TEXT NOT NULL,
67
+ trigger_type TEXT NOT NULL,
68
+ trigger_summary TEXT NOT NULL,
69
+ status TEXT NOT NULL DEFAULT 'running',
70
+ started_at INTEGER NOT NULL,
71
+ finished_at INTEGER,
72
+ duration_ms INTEGER,
73
+ error_json TEXT,
74
+ tags_json TEXT DEFAULT '[]',
75
+ metadata_json TEXT,
76
+ node_count INTEGER NOT NULL DEFAULT 0,
77
+ completed_nodes INTEGER NOT NULL DEFAULT 0
78
+ );
79
+
80
+ CREATE INDEX IF NOT EXISTS idx_runs_workflow ON workflow_runs(workflow_name);
81
+ CREATE INDEX IF NOT EXISTS idx_runs_status ON workflow_runs(status);
82
+ CREATE INDEX IF NOT EXISTS idx_runs_started_at ON workflow_runs(started_at);
83
+
84
+ CREATE TABLE IF NOT EXISTS node_runs (
85
+ id TEXT PRIMARY KEY,
86
+ run_id TEXT NOT NULL,
87
+ node_name TEXT NOT NULL,
88
+ node_type TEXT NOT NULL,
89
+ runtime_kind TEXT,
90
+ status TEXT NOT NULL DEFAULT 'running',
91
+ started_at INTEGER NOT NULL,
92
+ finished_at INTEGER,
93
+ duration_ms INTEGER,
94
+ inputs_json TEXT,
95
+ outputs_json TEXT,
96
+ error_json TEXT,
97
+ parent_node_id TEXT,
98
+ depth INTEGER NOT NULL DEFAULT 0,
99
+ step_index INTEGER NOT NULL DEFAULT 0,
100
+ metrics_json TEXT,
101
+ FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE
102
+ );
103
+
104
+ CREATE INDEX IF NOT EXISTS idx_nodes_run ON node_runs(run_id);
105
+
106
+ CREATE TABLE IF NOT EXISTS run_events (
107
+ id TEXT PRIMARY KEY,
108
+ type TEXT NOT NULL,
109
+ run_id TEXT NOT NULL,
110
+ workflow_name TEXT NOT NULL,
111
+ timestamp INTEGER NOT NULL,
112
+ node_name TEXT,
113
+ node_id TEXT,
114
+ payload_json TEXT,
115
+ FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE
116
+ );
117
+
118
+ CREATE INDEX IF NOT EXISTS idx_events_run ON run_events(run_id);
119
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON run_events(timestamp);
120
+
121
+ CREATE TABLE IF NOT EXISTS log_entries (
122
+ id TEXT PRIMARY KEY,
123
+ run_id TEXT NOT NULL,
124
+ node_id TEXT,
125
+ node_name TEXT,
126
+ level TEXT NOT NULL,
127
+ message TEXT NOT NULL,
128
+ timestamp INTEGER NOT NULL,
129
+ data_json TEXT,
130
+ FOREIGN KEY (run_id) REFERENCES workflow_runs(id) ON DELETE CASCADE
131
+ );
132
+
133
+ CREATE INDEX IF NOT EXISTS idx_logs_run ON log_entries(run_id);
134
+ `,
135
+ },
136
+ {
137
+ version: 2,
138
+ sql: `
139
+ CREATE TABLE IF NOT EXISTS dashboards (
140
+ id TEXT PRIMARY KEY,
141
+ name TEXT NOT NULL,
142
+ description TEXT,
143
+ is_default INTEGER NOT NULL DEFAULT 0,
144
+ created_at INTEGER NOT NULL,
145
+ updated_at INTEGER NOT NULL,
146
+ widgets_json TEXT NOT NULL DEFAULT '[]'
147
+ );
148
+ `,
149
+ },
150
+ ];
151
+ const applyMigration = this.db.transaction((m) => {
152
+ this.db.exec(m.sql);
153
+ this.db.prepare("INSERT INTO _trace_migrations (version) VALUES (?)").run(m.version);
154
+ });
155
+ for (const m of migrations) {
156
+ if (!applied.has(m.version)) {
157
+ applyMigration(m);
158
+ }
159
+ }
160
+ }
161
+ // === Prepared Statement Helpers ===
162
+ stmt(key, sql) {
163
+ if (!this.stmts[key]) {
164
+ this.stmts[key] = this.db.prepare(sql);
165
+ }
166
+ return this.stmts[key];
167
+ }
168
+ // === Writes ===
169
+ saveRun(run) {
170
+ this.stmt("saveRun", `
171
+ INSERT OR REPLACE INTO workflow_runs
172
+ (id, workflow_name, workflow_path, trigger_type, trigger_summary,
173
+ status, started_at, finished_at, duration_ms, error_json,
174
+ tags_json, metadata_json, node_count, completed_nodes)
175
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
176
+ `).run(run.id, run.workflowName, run.workflowPath, run.triggerType, run.triggerSummary, run.status, run.startedAt, run.finishedAt ?? null, run.durationMs ?? null, run.error ? JSON.stringify(run.error) : null, JSON.stringify(run.tags || []), run.metadata ? JSON.stringify(run.metadata) : null, run.nodeCount, run.completedNodes);
177
+ }
178
+ updateRun(runId, updates) {
179
+ const setClauses = [];
180
+ const values = [];
181
+ if (updates.status !== undefined) {
182
+ setClauses.push("status = ?");
183
+ values.push(updates.status);
184
+ }
185
+ if (updates.finishedAt !== undefined) {
186
+ setClauses.push("finished_at = ?");
187
+ values.push(updates.finishedAt);
188
+ }
189
+ if (updates.durationMs !== undefined) {
190
+ setClauses.push("duration_ms = ?");
191
+ values.push(updates.durationMs);
192
+ }
193
+ if (updates.error !== undefined) {
194
+ setClauses.push("error_json = ?");
195
+ values.push(JSON.stringify(updates.error));
196
+ }
197
+ if (updates.tags !== undefined) {
198
+ setClauses.push("tags_json = ?");
199
+ values.push(JSON.stringify(updates.tags));
200
+ }
201
+ if (updates.completedNodes !== undefined) {
202
+ setClauses.push("completed_nodes = ?");
203
+ values.push(updates.completedNodes);
204
+ }
205
+ if (updates.metadata !== undefined) {
206
+ setClauses.push("metadata_json = ?");
207
+ values.push(JSON.stringify(updates.metadata));
208
+ }
209
+ if (setClauses.length === 0)
210
+ return;
211
+ values.push(runId);
212
+ this.db.prepare(`UPDATE workflow_runs SET ${setClauses.join(", ")} WHERE id = ?`).run(...values);
213
+ }
214
+ saveNodeRun(nodeRun) {
215
+ this.stmt("saveNodeRun", `
216
+ INSERT OR REPLACE INTO node_runs
217
+ (id, run_id, node_name, node_type, runtime_kind,
218
+ status, started_at, finished_at, duration_ms,
219
+ inputs_json, outputs_json, error_json,
220
+ parent_node_id, depth, step_index, metrics_json)
221
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
222
+ `).run(nodeRun.id, nodeRun.runId, nodeRun.nodeName, nodeRun.nodeType, nodeRun.runtimeKind ?? null, nodeRun.status, nodeRun.startedAt, nodeRun.finishedAt ?? null, nodeRun.durationMs ?? null, nodeRun.inputs !== undefined ? JSON.stringify(nodeRun.inputs) : null, nodeRun.outputs !== undefined ? JSON.stringify(nodeRun.outputs) : null, nodeRun.error ? JSON.stringify(nodeRun.error) : null, nodeRun.parentNodeId ?? null, nodeRun.depth, nodeRun.stepIndex, nodeRun.metrics ? JSON.stringify(nodeRun.metrics) : null);
223
+ }
224
+ updateNodeRun(nodeRunId, updates) {
225
+ const setClauses = [];
226
+ const values = [];
227
+ if (updates.status !== undefined) {
228
+ setClauses.push("status = ?");
229
+ values.push(updates.status);
230
+ }
231
+ if (updates.finishedAt !== undefined) {
232
+ setClauses.push("finished_at = ?");
233
+ values.push(updates.finishedAt);
234
+ }
235
+ if (updates.durationMs !== undefined) {
236
+ setClauses.push("duration_ms = ?");
237
+ values.push(updates.durationMs);
238
+ }
239
+ if (updates.outputs !== undefined) {
240
+ setClauses.push("outputs_json = ?");
241
+ values.push(JSON.stringify(updates.outputs));
242
+ }
243
+ if (updates.error !== undefined) {
244
+ setClauses.push("error_json = ?");
245
+ values.push(JSON.stringify(updates.error));
246
+ }
247
+ if (updates.metrics !== undefined) {
248
+ setClauses.push("metrics_json = ?");
249
+ values.push(JSON.stringify(updates.metrics));
250
+ }
251
+ if (setClauses.length === 0)
252
+ return;
253
+ values.push(nodeRunId);
254
+ this.db.prepare(`UPDATE node_runs SET ${setClauses.join(", ")} WHERE id = ?`).run(...values);
255
+ }
256
+ saveEvent(event) {
257
+ this.stmt("saveEvent", `
258
+ INSERT INTO run_events (id, type, run_id, workflow_name, timestamp, node_name, node_id, payload_json)
259
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
260
+ `).run(event.id, event.type, event.runId, event.workflowName, event.timestamp, event.nodeName ?? null, event.nodeId ?? null, event.payload !== undefined ? JSON.stringify(event.payload) : null);
261
+ }
262
+ saveLog(entry) {
263
+ this.stmt("saveLog", `
264
+ INSERT INTO log_entries (id, run_id, node_id, node_name, level, message, timestamp, data_json)
265
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
266
+ `).run(entry.id, entry.runId, entry.nodeId ?? null, entry.nodeName ?? null, entry.level, entry.message, entry.timestamp, entry.data ? JSON.stringify(entry.data) : null);
267
+ }
268
+ // === Reads ===
269
+ getRun(runId) {
270
+ const row = this.stmt("getRun", "SELECT * FROM workflow_runs WHERE id = ?").get(runId);
271
+ return row ? this.rowToRun(row) : undefined;
272
+ }
273
+ getRuns(opts) {
274
+ const conditions = [];
275
+ const params = [];
276
+ if (opts?.workflow) {
277
+ conditions.push("workflow_name = ?");
278
+ params.push(opts.workflow);
279
+ }
280
+ if (opts?.status) {
281
+ conditions.push("status = ?");
282
+ params.push(opts.status);
283
+ }
284
+ const tags = opts?.tags;
285
+ if (tags && tags.length > 0) {
286
+ // For each tag, check that it exists in the JSON array
287
+ for (const tag of tags) {
288
+ conditions.push("json_each.value = ?");
289
+ params.push(tag);
290
+ }
291
+ }
292
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
293
+ const sortDir = opts?.sort === "asc" ? "ASC" : "DESC";
294
+ const limit = opts?.limit ?? 50;
295
+ const offset = opts?.offset ?? 0;
296
+ let countSql;
297
+ let querySql;
298
+ if (tags && tags.length > 0) {
299
+ // Use json_each for tag filtering with GROUP BY + HAVING for AND semantics
300
+ countSql = `
301
+ SELECT COUNT(DISTINCT wr.id) as total
302
+ FROM workflow_runs wr, json_each(wr.tags_json)
303
+ ${where}
304
+ `;
305
+ // For multiple tags, use HAVING to require all tags match
306
+ if (tags.length > 1) {
307
+ const tagConditions = conditions.filter((c) => c !== "json_each.value = ?");
308
+ const tagParams = params.filter((_, i) => i < params.length - tags.length);
309
+ const baseWhere = tagConditions.length > 0 ? `WHERE ${tagConditions.join(" AND ")}` : "";
310
+ const tagPlaceholders = tags.map(() => "?").join(", ");
311
+ countSql = `
312
+ SELECT COUNT(*) as total FROM (
313
+ SELECT wr.id
314
+ FROM workflow_runs wr, json_each(wr.tags_json)
315
+ ${baseWhere} ${baseWhere ? "AND" : "WHERE"} json_each.value IN (${tagPlaceholders})
316
+ GROUP BY wr.id
317
+ HAVING COUNT(DISTINCT json_each.value) = ?
318
+ )
319
+ `;
320
+ querySql = `
321
+ SELECT wr.* FROM workflow_runs wr
322
+ WHERE wr.id IN (
323
+ SELECT wr2.id
324
+ FROM workflow_runs wr2, json_each(wr2.tags_json)
325
+ ${baseWhere.replace(/wr\./g, "wr2.")} ${baseWhere ? "AND" : "WHERE"} json_each.value IN (${tagPlaceholders})
326
+ GROUP BY wr2.id
327
+ HAVING COUNT(DISTINCT json_each.value) = ?
328
+ )
329
+ ORDER BY wr.started_at ${sortDir}
330
+ LIMIT ? OFFSET ?
331
+ `;
332
+ const allTagParams = [...tagParams, ...tags, tags.length];
333
+ const total = this.db.prepare(countSql).get(...allTagParams)?.total ?? 0;
334
+ const rows = this.db.prepare(querySql).all(...allTagParams, limit, offset);
335
+ return { runs: rows.map((r) => this.rowToRun(r)), total };
336
+ }
337
+ querySql = `
338
+ SELECT DISTINCT wr.*
339
+ FROM workflow_runs wr, json_each(wr.tags_json)
340
+ ${where}
341
+ ORDER BY wr.started_at ${sortDir}
342
+ LIMIT ? OFFSET ?
343
+ `;
344
+ }
345
+ else {
346
+ countSql = `SELECT COUNT(*) as total FROM workflow_runs ${where}`;
347
+ querySql = `SELECT * FROM workflow_runs ${where} ORDER BY started_at ${sortDir} LIMIT ? OFFSET ?`;
348
+ }
349
+ const total = this.db.prepare(countSql).get(...params)?.total ?? 0;
350
+ const rows = this.db.prepare(querySql).all(...params, limit, offset);
351
+ return { runs: rows.map((r) => this.rowToRun(r)), total };
352
+ }
353
+ getNodeRuns(runId) {
354
+ const rows = this.stmt("getNodeRuns", "SELECT * FROM node_runs WHERE run_id = ? ORDER BY step_index").all(runId);
355
+ return rows.map((r) => this.rowToNodeRun(r));
356
+ }
357
+ getNodeRun(nodeRunId) {
358
+ const row = this.stmt("getNodeRun", "SELECT * FROM node_runs WHERE id = ?").get(nodeRunId);
359
+ return row ? this.rowToNodeRun(row) : undefined;
360
+ }
361
+ getEvents(runId, since) {
362
+ if (since) {
363
+ return this.stmt("getEventsSince", "SELECT * FROM run_events WHERE run_id = ? AND timestamp > ? ORDER BY timestamp").all(runId, since).map((r) => this.rowToEvent(r));
364
+ }
365
+ return this.stmt("getEvents", "SELECT * FROM run_events WHERE run_id = ? ORDER BY timestamp").all(runId).map((r) => this.rowToEvent(r));
366
+ }
367
+ getLogs(runId, nodeId) {
368
+ if (nodeId) {
369
+ return this.stmt("getLogsNode", "SELECT * FROM log_entries WHERE run_id = ? AND node_id = ? ORDER BY timestamp").all(runId, nodeId).map((r) => this.rowToLog(r));
370
+ }
371
+ return this.stmt("getLogs", "SELECT * FROM log_entries WHERE run_id = ? ORDER BY timestamp").all(runId).map((r) => this.rowToLog(r));
372
+ }
373
+ // === Aggregations ===
374
+ getWorkflowSummaries() {
375
+ const rows = this.db
376
+ .prepare(`
377
+ SELECT
378
+ workflow_name,
379
+ workflow_path,
380
+ COUNT(*) as total_runs,
381
+ SUM(CASE WHEN started_at >= ? THEN 1 ELSE 0 END) as recent_runs,
382
+ MAX(started_at) as last_run_at,
383
+ SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as error_count,
384
+ AVG(CASE WHEN duration_ms IS NOT NULL THEN duration_ms END) as avg_duration,
385
+ GROUP_CONCAT(DISTINCT trigger_type) as trigger_types
386
+ FROM workflow_runs
387
+ GROUP BY workflow_name
388
+ `)
389
+ .all(Date.now() - 24 * 60 * 60 * 1000);
390
+ return rows.map((r) => {
391
+ // Get last run status
392
+ const lastRun = this.db
393
+ .prepare("SELECT status FROM workflow_runs WHERE workflow_name = ? ORDER BY started_at DESC LIMIT 1")
394
+ .get(r.workflow_name);
395
+ // Get p95 duration
396
+ const durations = this.db
397
+ .prepare("SELECT duration_ms FROM workflow_runs WHERE workflow_name = ? AND duration_ms IS NOT NULL ORDER BY duration_ms")
398
+ .all(r.workflow_name);
399
+ const p95Index = Math.floor(durations.length * 0.95);
400
+ const p95 = durations.length > 0 ? (durations[Math.min(p95Index, durations.length - 1)]?.duration_ms ?? 0) : 0;
401
+ return {
402
+ name: r.workflow_name,
403
+ path: r.workflow_path,
404
+ triggerTypes: r.trigger_types ? r.trigger_types.split(",") : [],
405
+ totalRuns: r.total_runs ?? 0,
406
+ recentRuns: r.recent_runs ?? 0,
407
+ lastRunAt: r.last_run_at ?? undefined,
408
+ lastRunStatus: lastRun?.status,
409
+ errorRate: (r.total_runs ?? 0) > 0 ? (r.error_count ?? 0) / (r.total_runs ?? 1) : 0,
410
+ avgDurationMs: r.avg_duration ?? 0,
411
+ p95DurationMs: p95,
412
+ };
413
+ });
414
+ }
415
+ getAllTags() {
416
+ const rows = this.db
417
+ .prepare(`
418
+ SELECT DISTINCT value as tag
419
+ FROM workflow_runs, json_each(workflow_runs.tags_json)
420
+ ORDER BY value
421
+ `)
422
+ .all();
423
+ return rows.map((r) => r.tag);
424
+ }
425
+ getActiveRunCount() {
426
+ const row = this.db.prepare("SELECT COUNT(*) as count FROM workflow_runs WHERE status = 'running'").get();
427
+ return row?.count ?? 0;
428
+ }
429
+ getMetrics(workflow) {
430
+ const where = workflow ? "WHERE workflow_name = ?" : "";
431
+ const params = workflow ? [workflow] : [];
432
+ // Basic stats
433
+ const stats = this.db
434
+ .prepare(`
435
+ SELECT
436
+ COUNT(*) as total_runs,
437
+ SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_runs,
438
+ SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed_runs,
439
+ AVG(CASE WHEN duration_ms IS NOT NULL THEN duration_ms END) as avg_duration
440
+ FROM workflow_runs ${where}
441
+ `)
442
+ .get(...params);
443
+ // Percentiles
444
+ const durations = this.db
445
+ .prepare(`SELECT duration_ms FROM workflow_runs ${where} ${where ? "AND" : "WHERE"} duration_ms IS NOT NULL ORDER BY duration_ms`)
446
+ .all(...params);
447
+ const durationValues = durations.map((d) => d.duration_ms);
448
+ const percentile = (arr, p) => {
449
+ if (arr.length === 0)
450
+ return 0;
451
+ const idx = Math.floor(arr.length * p);
452
+ return arr[Math.min(idx, arr.length - 1)];
453
+ };
454
+ // Execution timeline — hourly buckets for last 24h
455
+ const now = Date.now();
456
+ const bucketSize = 60 * 60 * 1000;
457
+ const bucketCount = 24;
458
+ const executionTimeline = [];
459
+ for (let i = bucketCount - 1; i >= 0; i--) {
460
+ const bucketStart = now - (i + 1) * bucketSize;
461
+ const bucketEnd = now - i * bucketSize;
462
+ const bucketStats = this.db
463
+ .prepare(`
464
+ SELECT
465
+ COUNT(*) as total,
466
+ SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
467
+ SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
468
+ FROM workflow_runs
469
+ ${where} ${where ? "AND" : "WHERE"} started_at >= ? AND started_at < ?
470
+ `)
471
+ .get(...params, bucketStart, bucketEnd);
472
+ executionTimeline.push({
473
+ bucket: new Date(bucketStart).toISOString(),
474
+ total: bucketStats?.total ?? 0,
475
+ completed: bucketStats?.completed ?? 0,
476
+ failed: bucketStats?.failed ?? 0,
477
+ });
478
+ }
479
+ // Duration distribution
480
+ const ranges = [
481
+ { range: "0-10ms", min: 0, max: 10 },
482
+ { range: "10-50ms", min: 10, max: 50 },
483
+ { range: "50-100ms", min: 50, max: 100 },
484
+ { range: "100-500ms", min: 100, max: 500 },
485
+ { range: "500ms-1s", min: 500, max: 1000 },
486
+ { range: "1-5s", min: 1000, max: 5000 },
487
+ { range: "5s+", min: 5000, max: Number.POSITIVE_INFINITY },
488
+ ];
489
+ const durationDistribution = ranges.map(({ range, min, max }) => ({
490
+ range,
491
+ count: durationValues.filter((d) => d >= min && d < max).length,
492
+ }));
493
+ // Workflow breakdown
494
+ const wfRows = this.db
495
+ .prepare(`
496
+ SELECT
497
+ workflow_name as name,
498
+ COUNT(*) as total_runs,
499
+ SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed,
500
+ AVG(CASE WHEN duration_ms IS NOT NULL THEN duration_ms END) as avg_duration
501
+ FROM workflow_runs ${where}
502
+ GROUP BY workflow_name
503
+ `)
504
+ .all(...params);
505
+ const workflowBreakdown = wfRows.map((r) => ({
506
+ name: r.name,
507
+ totalRuns: r.total_runs,
508
+ errorRate: r.total_runs > 0 ? r.failed / r.total_runs : 0,
509
+ avgDurationMs: r.avg_duration ?? 0,
510
+ }));
511
+ // Node performance
512
+ const runIdWhere = workflow ? "WHERE nr.run_id IN (SELECT id FROM workflow_runs WHERE workflow_name = ?)" : "";
513
+ const nodeRows = this.db
514
+ .prepare(`
515
+ SELECT
516
+ nr.node_name,
517
+ COUNT(*) as total,
518
+ SUM(CASE WHEN nr.status = 'failed' THEN 1 ELSE 0 END) as failed,
519
+ AVG(CASE WHEN nr.duration_ms IS NOT NULL THEN nr.duration_ms END) as avg_duration,
520
+ MAX(CASE WHEN nr.duration_ms IS NOT NULL THEN nr.duration_ms END) as max_duration
521
+ FROM node_runs nr
522
+ ${runIdWhere}
523
+ GROUP BY nr.node_name
524
+ `)
525
+ .all(...params);
526
+ const nodePerformance = nodeRows.map((r) => ({
527
+ nodeName: r.node_name,
528
+ avgDurationMs: r.avg_duration ?? 0,
529
+ maxDurationMs: r.max_duration ?? 0,
530
+ errorRate: r.total > 0 ? r.failed / r.total : 0,
531
+ executionCount: r.total,
532
+ }));
533
+ return {
534
+ totalRuns: stats?.total_runs ?? 0,
535
+ completedRuns: stats?.completed_runs ?? 0,
536
+ failedRuns: stats?.failed_runs ?? 0,
537
+ avgDurationMs: stats?.avg_duration ?? 0,
538
+ p50DurationMs: percentile(durationValues, 0.5),
539
+ p95DurationMs: percentile(durationValues, 0.95),
540
+ p99DurationMs: percentile(durationValues, 0.99),
541
+ executionTimeline,
542
+ durationDistribution,
543
+ workflowBreakdown,
544
+ nodePerformance,
545
+ };
546
+ }
547
+ // === Dashboards ===
548
+ saveDashboard(dashboard) {
549
+ this.stmt("saveDashboard", `
550
+ INSERT OR REPLACE INTO dashboards
551
+ (id, name, description, is_default, created_at, updated_at, widgets_json)
552
+ VALUES (?, ?, ?, ?, ?, ?, ?)
553
+ `).run(dashboard.id, dashboard.name, dashboard.description ?? null, dashboard.isDefault ? 1 : 0, dashboard.createdAt, dashboard.updatedAt, JSON.stringify(dashboard.widgets));
554
+ }
555
+ getDashboard(dashboardId) {
556
+ const row = this.stmt("getDashboard", "SELECT * FROM dashboards WHERE id = ?").get(dashboardId);
557
+ return row ? this.rowToDashboard(row) : undefined;
558
+ }
559
+ listDashboards() {
560
+ const rows = this.db
561
+ .prepare("SELECT * FROM dashboards ORDER BY updated_at DESC")
562
+ .all();
563
+ return rows.map((r) => this.rowToDashboard(r));
564
+ }
565
+ deleteDashboard(dashboardId) {
566
+ const result = this.stmt("deleteDashboard", "DELETE FROM dashboards WHERE id = ?").run(dashboardId);
567
+ return result.changes > 0;
568
+ }
569
+ updateDashboard(dashboardId, updates) {
570
+ const setClauses = ["updated_at = ?"];
571
+ const values = [Date.now()];
572
+ if (updates.name !== undefined) {
573
+ setClauses.push("name = ?");
574
+ values.push(updates.name);
575
+ }
576
+ if (updates.description !== undefined) {
577
+ setClauses.push("description = ?");
578
+ values.push(updates.description);
579
+ }
580
+ if (updates.isDefault !== undefined) {
581
+ setClauses.push("is_default = ?");
582
+ values.push(updates.isDefault ? 1 : 0);
583
+ }
584
+ if (updates.widgets !== undefined) {
585
+ setClauses.push("widgets_json = ?");
586
+ values.push(JSON.stringify(updates.widgets));
587
+ }
588
+ values.push(dashboardId);
589
+ this.db.prepare(`UPDATE dashboards SET ${setClauses.join(", ")} WHERE id = ?`).run(...values);
590
+ }
591
+ // === Cleanup ===
592
+ clearAll() {
593
+ const count = this.db.prepare("SELECT COUNT(*) as c FROM workflow_runs").get()?.c ?? 0;
594
+ this.db.exec("DELETE FROM log_entries");
595
+ this.db.exec("DELETE FROM run_events");
596
+ this.db.exec("DELETE FROM node_runs");
597
+ this.db.exec("DELETE FROM workflow_runs");
598
+ this.db.exec("DELETE FROM dashboards");
599
+ return count;
600
+ }
601
+ deleteRunsBefore(timestamp) {
602
+ // Foreign key CASCADE handles child tables
603
+ const result = this.db
604
+ .prepare("DELETE FROM workflow_runs WHERE started_at < ? AND status != 'running'")
605
+ .run(timestamp);
606
+ return result.changes;
607
+ }
608
+ evictOldRuns(maxRuns) {
609
+ const count = this.db.prepare("SELECT COUNT(*) as c FROM workflow_runs").get()?.c ?? 0;
610
+ if (count <= maxRuns)
611
+ return;
612
+ const toRemove = count - maxRuns;
613
+ this.db
614
+ .prepare(`
615
+ DELETE FROM workflow_runs WHERE id IN (
616
+ SELECT id FROM workflow_runs
617
+ WHERE status != 'running'
618
+ ORDER BY started_at ASC
619
+ LIMIT ?
620
+ )
621
+ `)
622
+ .run(toRemove);
623
+ }
624
+ close() {
625
+ this.stmts = {};
626
+ this.db.close();
627
+ }
628
+ // === Row → Object Mappers ===
629
+ rowToRun(row) {
630
+ return {
631
+ id: row.id,
632
+ workflowName: row.workflow_name,
633
+ workflowPath: row.workflow_path,
634
+ triggerType: row.trigger_type,
635
+ triggerSummary: row.trigger_summary,
636
+ status: row.status,
637
+ startedAt: row.started_at,
638
+ finishedAt: row.finished_at ?? undefined,
639
+ durationMs: row.duration_ms ?? undefined,
640
+ error: row.error_json ? JSON.parse(row.error_json) : undefined,
641
+ tags: row.tags_json ? JSON.parse(row.tags_json) : undefined,
642
+ metadata: row.metadata_json ? JSON.parse(row.metadata_json) : undefined,
643
+ nodeCount: row.node_count,
644
+ completedNodes: row.completed_nodes,
645
+ };
646
+ }
647
+ rowToNodeRun(row) {
648
+ return {
649
+ id: row.id,
650
+ runId: row.run_id,
651
+ nodeName: row.node_name,
652
+ nodeType: row.node_type,
653
+ runtimeKind: row.runtime_kind ?? undefined,
654
+ status: row.status,
655
+ startedAt: row.started_at,
656
+ finishedAt: row.finished_at ?? undefined,
657
+ durationMs: row.duration_ms ?? undefined,
658
+ inputs: row.inputs_json ? JSON.parse(row.inputs_json) : undefined,
659
+ outputs: row.outputs_json ? JSON.parse(row.outputs_json) : undefined,
660
+ error: row.error_json ? JSON.parse(row.error_json) : undefined,
661
+ parentNodeId: row.parent_node_id ?? undefined,
662
+ depth: row.depth,
663
+ stepIndex: row.step_index,
664
+ metrics: row.metrics_json ? JSON.parse(row.metrics_json) : undefined,
665
+ };
666
+ }
667
+ rowToEvent(row) {
668
+ return {
669
+ id: row.id,
670
+ type: row.type,
671
+ runId: row.run_id,
672
+ workflowName: row.workflow_name,
673
+ timestamp: row.timestamp,
674
+ nodeName: row.node_name ?? undefined,
675
+ nodeId: row.node_id ?? undefined,
676
+ payload: row.payload_json ? JSON.parse(row.payload_json) : undefined,
677
+ };
678
+ }
679
+ rowToLog(row) {
680
+ return {
681
+ id: row.id,
682
+ runId: row.run_id,
683
+ nodeId: row.node_id ?? undefined,
684
+ nodeName: row.node_name ?? undefined,
685
+ level: row.level,
686
+ message: row.message,
687
+ timestamp: row.timestamp,
688
+ data: row.data_json ? JSON.parse(row.data_json) : undefined,
689
+ };
690
+ }
691
+ rowToDashboard(row) {
692
+ return {
693
+ id: row.id,
694
+ name: row.name,
695
+ description: row.description ?? undefined,
696
+ isDefault: row.is_default === 1,
697
+ createdAt: row.created_at,
698
+ updatedAt: row.updated_at,
699
+ widgets: row.widgets_json ? JSON.parse(row.widgets_json) : [],
700
+ };
701
+ }
702
+ }
703
+ //# sourceMappingURL=SqliteRunStore.js.map