nexus-ai-pro 2.0.0 → 2.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 (282) hide show
  1. package/API_STABILITY.md +65 -0
  2. package/CHANGELOG.md +267 -1
  3. package/MIGRATING.md +1 -1
  4. package/README.md +17 -8
  5. package/dist/agent/create-agent.d.ts +9 -1
  6. package/dist/agent/create-agent.js +40 -1
  7. package/dist/cache/memory-cache.d.ts +8 -0
  8. package/dist/cache/memory-cache.js +9 -0
  9. package/dist/cache/semantic-cache.d.ts +6 -3
  10. package/dist/cache/semantic-cache.js +5 -2
  11. package/dist/cli/args.js +3 -0
  12. package/dist/cli/db.d.ts +16 -4
  13. package/dist/cli/db.js +169 -22
  14. package/dist/cli/doctor.d.ts +17 -0
  15. package/dist/cli/doctor.js +53 -0
  16. package/dist/cli/graph.d.ts +9 -0
  17. package/dist/cli/graph.js +47 -0
  18. package/dist/cli.js +21 -2
  19. package/dist/core/lifecycle.js +11 -6
  20. package/dist/core/nexus.d.ts +12 -2
  21. package/dist/core/nexus.js +106 -23
  22. package/dist/doctor/index.d.ts +90 -0
  23. package/dist/doctor/index.js +237 -0
  24. package/dist/evals/judge.d.ts +11 -0
  25. package/dist/evals/judge.js +33 -0
  26. package/dist/evaluate/index.d.ts +1 -0
  27. package/dist/evaluate/index.js +1 -0
  28. package/dist/evaluate/provenance.d.ts +22 -0
  29. package/dist/evaluate/provenance.js +10 -0
  30. package/dist/evaluate/run.js +20 -4
  31. package/dist/graph/durability.d.ts +27 -0
  32. package/dist/graph/durability.js +66 -0
  33. package/dist/graph/errors.d.ts +30 -4
  34. package/dist/graph/errors.js +18 -2
  35. package/dist/graph/event-stream.d.ts +75 -0
  36. package/dist/graph/event-stream.js +289 -0
  37. package/dist/graph/functional.d.ts +80 -7
  38. package/dist/graph/functional.js +138 -35
  39. package/dist/graph/graph.d.ts +56 -8
  40. package/dist/graph/graph.js +331 -49
  41. package/dist/graph/index.d.ts +4 -2
  42. package/dist/graph/index.js +3 -1
  43. package/dist/graph/lint.d.ts +57 -0
  44. package/dist/graph/lint.js +141 -0
  45. package/dist/graph/run-control.d.ts +28 -0
  46. package/dist/graph/run-control.js +26 -0
  47. package/dist/images/evals.js +4 -2
  48. package/dist/operations/adapters.d.ts +52 -4
  49. package/dist/operations/adapters.js +194 -7
  50. package/dist/operations/runner.js +14 -15
  51. package/dist/operations/stats.js +8 -1
  52. package/dist/operations/store.d.ts +5 -1
  53. package/dist/operations/store.js +7 -0
  54. package/dist/ops/circuit-breaker.d.ts +52 -0
  55. package/dist/ops/circuit-breaker.js +4 -0
  56. package/dist/ops/circuit-store.d.ts +13 -2
  57. package/dist/ops/circuit-store.js +68 -0
  58. package/dist/ops/circuit-sync.d.ts +17 -0
  59. package/dist/ops/circuit-sync.js +86 -0
  60. package/dist/ops/health.d.ts +24 -0
  61. package/dist/ops/health.js +38 -10
  62. package/dist/ops/index.d.ts +1 -1
  63. package/dist/ops/index.js +1 -1
  64. package/dist/ops/rate-limit-adapters.d.ts +23 -0
  65. package/dist/ops/rate-limit-adapters.js +34 -0
  66. package/dist/ops/rate-limiter.d.ts +14 -2
  67. package/dist/ops/rate-limiter.js +31 -0
  68. package/dist/postgres/circuits.d.ts +21 -4
  69. package/dist/postgres/circuits.js +65 -4
  70. package/dist/postgres/client.d.ts +0 -2
  71. package/dist/postgres/client.js +0 -4
  72. package/dist/postgres/evaluate.d.ts +16 -4
  73. package/dist/postgres/evaluate.js +17 -5
  74. package/dist/postgres/index.d.ts +32 -11
  75. package/dist/postgres/index.js +65 -31
  76. package/dist/postgres/migrations.d.ts +60 -0
  77. package/dist/postgres/migrations.js +121 -0
  78. package/dist/postgres/operations.d.ts +14 -3
  79. package/dist/postgres/operations.js +45 -15
  80. package/dist/postgres/prompts.d.ts +11 -2
  81. package/dist/postgres/prompts.js +14 -3
  82. package/dist/postgres/rollups.d.ts +35 -0
  83. package/dist/postgres/rollups.js +129 -0
  84. package/dist/postgres/store.d.ts +11 -2
  85. package/dist/postgres/store.js +14 -3
  86. package/dist/postgres/traces.d.ts +13 -3
  87. package/dist/postgres/traces.js +25 -3
  88. package/dist/postgres/vectors.d.ts +11 -2
  89. package/dist/postgres/vectors.js +14 -3
  90. package/dist/router/auto-router.d.ts +34 -7
  91. package/dist/router/auto-router.js +201 -68
  92. package/dist/server/assistant.d.ts +44 -0
  93. package/dist/server/assistant.js +39 -0
  94. package/dist/server/errors.d.ts +12 -0
  95. package/dist/server/errors.js +7 -0
  96. package/dist/server/index.d.ts +1 -1
  97. package/dist/server/index.js +1 -1
  98. package/dist/server/runs.d.ts +28 -1
  99. package/dist/server/runs.js +110 -14
  100. package/dist/sqlite/index.d.ts +16 -3
  101. package/dist/sqlite/index.js +16 -3
  102. package/dist/sqlite/migrations.d.ts +26 -0
  103. package/dist/sqlite/migrations.js +63 -0
  104. package/dist/sqlite/operations.d.ts +14 -3
  105. package/dist/sqlite/operations.js +42 -15
  106. package/dist/sqlite/store.d.ts +9 -2
  107. package/dist/sqlite/store.js +13 -3
  108. package/dist/sqlite/vectors.d.ts +11 -2
  109. package/dist/sqlite/vectors.js +13 -2
  110. package/dist/store/tenant.d.ts +12 -0
  111. package/dist/store/tenant.js +29 -0
  112. package/dist/tenancy/index.d.ts +113 -0
  113. package/dist/tenancy/index.js +233 -0
  114. package/dist/testing/record.d.ts +42 -0
  115. package/dist/testing/record.js +57 -5
  116. package/dist/tracing/index.d.ts +3 -3
  117. package/dist/tracing/index.js +2 -2
  118. package/dist/tracing/instrument.js +30 -0
  119. package/dist/tracing/otlp.d.ts +91 -0
  120. package/dist/tracing/otlp.js +124 -0
  121. package/dist/tracing/rollups.d.ts +100 -0
  122. package/dist/tracing/rollups.js +139 -0
  123. package/dist/tracing/stores.d.ts +17 -0
  124. package/dist/tracing/stores.js +23 -0
  125. package/dist/tracing/tracer.d.ts +50 -4
  126. package/dist/tracing/tracer.js +85 -6
  127. package/dist/types/config.d.ts +33 -2
  128. package/dist/types/evaluate.d.ts +37 -0
  129. package/dist/types/graph.d.ts +354 -9
  130. package/dist/types/graph.js +3 -1
  131. package/dist/types/messages.d.ts +5 -0
  132. package/dist/types/operations.d.ts +22 -2
  133. package/dist/types/response.d.ts +20 -0
  134. package/dist/types/server.d.ts +18 -0
  135. package/dist/types/tracing.d.ts +11 -0
  136. package/dist/utils/schema-migrations.d.ts +97 -0
  137. package/dist/utils/schema-migrations.js +91 -0
  138. package/dist/utils/signals.d.ts +17 -0
  139. package/dist/utils/signals.js +26 -0
  140. package/dist/utils/tenant.d.ts +14 -0
  141. package/dist/utils/tenant.js +16 -0
  142. package/dist/version.d.ts +5 -0
  143. package/dist/version.js +1 -0
  144. package/dist-cjs/agent/create-agent.d.ts +9 -1
  145. package/dist-cjs/agent/create-agent.js +40 -1
  146. package/dist-cjs/cache/memory-cache.d.ts +8 -0
  147. package/dist-cjs/cache/memory-cache.js +9 -0
  148. package/dist-cjs/cache/semantic-cache.d.ts +6 -3
  149. package/dist-cjs/cache/semantic-cache.js +5 -2
  150. package/dist-cjs/cli/args.js +3 -0
  151. package/dist-cjs/cli/db.d.ts +16 -4
  152. package/dist-cjs/cli/db.js +200 -20
  153. package/dist-cjs/cli/doctor.d.ts +17 -0
  154. package/dist-cjs/cli/doctor.js +56 -0
  155. package/dist-cjs/cli/graph.d.ts +9 -0
  156. package/dist-cjs/cli/graph.js +50 -0
  157. package/dist-cjs/core/lifecycle.js +11 -6
  158. package/dist-cjs/core/nexus.d.ts +12 -2
  159. package/dist-cjs/core/nexus.js +106 -23
  160. package/dist-cjs/doctor/index.d.ts +90 -0
  161. package/dist-cjs/doctor/index.js +276 -0
  162. package/dist-cjs/evals/judge.d.ts +11 -0
  163. package/dist-cjs/evals/judge.js +33 -0
  164. package/dist-cjs/evaluate/index.d.ts +1 -0
  165. package/dist-cjs/evaluate/index.js +3 -1
  166. package/dist-cjs/evaluate/provenance.d.ts +22 -0
  167. package/dist-cjs/evaluate/provenance.js +14 -0
  168. package/dist-cjs/evaluate/run.js +20 -4
  169. package/dist-cjs/graph/durability.d.ts +27 -0
  170. package/dist-cjs/graph/durability.js +70 -0
  171. package/dist-cjs/graph/errors.d.ts +30 -4
  172. package/dist-cjs/graph/errors.js +20 -3
  173. package/dist-cjs/graph/event-stream.d.ts +75 -0
  174. package/dist-cjs/graph/event-stream.js +295 -0
  175. package/dist-cjs/graph/functional.d.ts +80 -7
  176. package/dist-cjs/graph/functional.js +137 -34
  177. package/dist-cjs/graph/graph.d.ts +56 -8
  178. package/dist-cjs/graph/graph.js +330 -48
  179. package/dist-cjs/graph/index.d.ts +4 -2
  180. package/dist-cjs/graph/index.js +7 -1
  181. package/dist-cjs/graph/lint.d.ts +57 -0
  182. package/dist-cjs/graph/lint.js +144 -0
  183. package/dist-cjs/graph/run-control.d.ts +28 -0
  184. package/dist-cjs/graph/run-control.js +30 -0
  185. package/dist-cjs/images/evals.js +4 -2
  186. package/dist-cjs/operations/adapters.d.ts +52 -4
  187. package/dist-cjs/operations/adapters.js +193 -6
  188. package/dist-cjs/operations/runner.js +14 -15
  189. package/dist-cjs/operations/stats.js +8 -1
  190. package/dist-cjs/operations/store.d.ts +5 -1
  191. package/dist-cjs/operations/store.js +7 -0
  192. package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
  193. package/dist-cjs/ops/circuit-breaker.js +4 -0
  194. package/dist-cjs/ops/circuit-store.d.ts +13 -2
  195. package/dist-cjs/ops/circuit-store.js +68 -0
  196. package/dist-cjs/ops/circuit-sync.d.ts +17 -0
  197. package/dist-cjs/ops/circuit-sync.js +87 -0
  198. package/dist-cjs/ops/health.d.ts +24 -0
  199. package/dist-cjs/ops/health.js +38 -10
  200. package/dist-cjs/ops/index.d.ts +1 -1
  201. package/dist-cjs/ops/rate-limit-adapters.d.ts +23 -0
  202. package/dist-cjs/ops/rate-limit-adapters.js +35 -1
  203. package/dist-cjs/ops/rate-limiter.d.ts +14 -2
  204. package/dist-cjs/ops/rate-limiter.js +32 -0
  205. package/dist-cjs/postgres/circuits.d.ts +21 -4
  206. package/dist-cjs/postgres/circuits.js +98 -3
  207. package/dist-cjs/postgres/client.d.ts +0 -2
  208. package/dist-cjs/postgres/client.js +0 -5
  209. package/dist-cjs/postgres/evaluate.d.ts +16 -4
  210. package/dist-cjs/postgres/evaluate.js +50 -4
  211. package/dist-cjs/postgres/index.d.ts +32 -11
  212. package/dist-cjs/postgres/index.js +69 -20
  213. package/dist-cjs/postgres/migrations.d.ts +60 -0
  214. package/dist-cjs/postgres/migrations.js +129 -0
  215. package/dist-cjs/postgres/operations.d.ts +14 -3
  216. package/dist-cjs/postgres/operations.js +78 -14
  217. package/dist-cjs/postgres/prompts.d.ts +11 -2
  218. package/dist-cjs/postgres/prompts.js +47 -2
  219. package/dist-cjs/postgres/rollups.d.ts +35 -0
  220. package/dist-cjs/postgres/rollups.js +168 -0
  221. package/dist-cjs/postgres/store.d.ts +11 -2
  222. package/dist-cjs/postgres/store.js +47 -2
  223. package/dist-cjs/postgres/traces.d.ts +13 -3
  224. package/dist-cjs/postgres/traces.js +58 -2
  225. package/dist-cjs/postgres/vectors.d.ts +11 -2
  226. package/dist-cjs/postgres/vectors.js +47 -2
  227. package/dist-cjs/router/auto-router.d.ts +34 -7
  228. package/dist-cjs/router/auto-router.js +201 -68
  229. package/dist-cjs/server/assistant.d.ts +44 -0
  230. package/dist-cjs/server/assistant.js +39 -0
  231. package/dist-cjs/server/errors.d.ts +12 -0
  232. package/dist-cjs/server/errors.js +9 -1
  233. package/dist-cjs/server/index.d.ts +1 -1
  234. package/dist-cjs/server/index.js +2 -1
  235. package/dist-cjs/server/runs.d.ts +28 -1
  236. package/dist-cjs/server/runs.js +109 -13
  237. package/dist-cjs/sqlite/index.d.ts +16 -3
  238. package/dist-cjs/sqlite/index.js +31 -10
  239. package/dist-cjs/sqlite/migrations.d.ts +26 -0
  240. package/dist-cjs/sqlite/migrations.js +71 -0
  241. package/dist-cjs/sqlite/operations.d.ts +14 -3
  242. package/dist-cjs/sqlite/operations.js +76 -15
  243. package/dist-cjs/sqlite/store.d.ts +9 -2
  244. package/dist-cjs/sqlite/store.js +47 -3
  245. package/dist-cjs/sqlite/vectors.d.ts +11 -2
  246. package/dist-cjs/sqlite/vectors.js +47 -2
  247. package/dist-cjs/store/tenant.d.ts +12 -0
  248. package/dist-cjs/store/tenant.js +33 -0
  249. package/dist-cjs/tenancy/index.d.ts +113 -0
  250. package/dist-cjs/tenancy/index.js +250 -0
  251. package/dist-cjs/testing/record.d.ts +42 -0
  252. package/dist-cjs/testing/record.js +57 -5
  253. package/dist-cjs/tracing/index.d.ts +3 -3
  254. package/dist-cjs/tracing/index.js +4 -1
  255. package/dist-cjs/tracing/instrument.js +30 -0
  256. package/dist-cjs/tracing/otlp.d.ts +91 -0
  257. package/dist-cjs/tracing/otlp.js +129 -0
  258. package/dist-cjs/tracing/rollups.d.ts +100 -0
  259. package/dist-cjs/tracing/rollups.js +147 -0
  260. package/dist-cjs/tracing/stores.d.ts +17 -0
  261. package/dist-cjs/tracing/stores.js +24 -0
  262. package/dist-cjs/tracing/tracer.d.ts +50 -4
  263. package/dist-cjs/tracing/tracer.js +87 -6
  264. package/dist-cjs/types/config.d.ts +33 -2
  265. package/dist-cjs/types/evaluate.d.ts +37 -0
  266. package/dist-cjs/types/graph.d.ts +354 -9
  267. package/dist-cjs/types/graph.js +3 -1
  268. package/dist-cjs/types/messages.d.ts +5 -0
  269. package/dist-cjs/types/operations.d.ts +22 -2
  270. package/dist-cjs/types/response.d.ts +20 -0
  271. package/dist-cjs/types/server.d.ts +18 -0
  272. package/dist-cjs/types/tracing.d.ts +11 -0
  273. package/dist-cjs/utils/schema-migrations.d.ts +97 -0
  274. package/dist-cjs/utils/schema-migrations.js +102 -0
  275. package/dist-cjs/utils/signals.d.ts +17 -0
  276. package/dist-cjs/utils/signals.js +29 -0
  277. package/dist-cjs/utils/tenant.d.ts +14 -0
  278. package/dist-cjs/utils/tenant.js +22 -0
  279. package/dist-cjs/version.d.ts +5 -0
  280. package/dist-cjs/version.js +4 -0
  281. package/llms.txt +4 -3
  282. package/package.json +110 -2
@@ -12,6 +12,8 @@ const state_js_1 = require("./state.js");
12
12
  const THREADS = ['nexus', 'server', 'threads'];
13
13
  const RUNS = state_js_1.RUNS_NAMESPACE;
14
14
  const KIND_PREFIX = 'assistant:';
15
+ const THREAD_CLAIM = 'nexus-thread:';
16
+ const UNRECORDED_CLAIM_MS = 60_000;
15
17
  const DEFAULT_CONCURRENCY = 10;
16
18
  const DEFAULT_POLL_MS = 1_000;
17
19
  const DEFAULT_RECOVER_MS = 30_000;
@@ -29,6 +31,8 @@ class RunManager {
29
31
  store;
30
32
  local = new Map();
31
33
  slots = new Set();
34
+ controls = new Map();
35
+ drainedRuns = new Set();
32
36
  state;
33
37
  events;
34
38
  now;
@@ -121,12 +125,22 @@ class RunManager {
121
125
  }
122
126
  async start(options) {
123
127
  const base = this.assistant(options.assistant);
128
+ const runId = `run-${randomId()}`;
124
129
  let thread;
125
130
  if (options.threadId) {
126
131
  thread = await this.thread(options.threadId, options.principal);
127
- thread = await this.settleBusyThread(thread, options);
132
+ thread = await this.settleBusyThread(thread, options, runId);
128
133
  }
129
- const runId = `run-${randomId()}`;
134
+ try {
135
+ return await this.startClaimed(runId, base, thread, options);
136
+ }
137
+ catch (error) {
138
+ if (thread)
139
+ await this.releaseClaim(thread.id, runId).catch((cause) => this.report(cause, { runId }));
140
+ throw error;
141
+ }
142
+ }
143
+ async startClaimed(runId, base, thread, options) {
130
144
  let assistant = base;
131
145
  let revision;
132
146
  if (base.route) {
@@ -220,11 +234,18 @@ class RunManager {
220
234
  async drain(options = {}) {
221
235
  this.draining = true;
222
236
  this.stopWorking();
237
+ if (this.options.queue) {
238
+ for (const control of this.controls.values()) {
239
+ control.draining = true;
240
+ control.reason = 'replica draining';
241
+ }
242
+ }
223
243
  const running = [...this.slots];
224
244
  const deadline = Date.now() + (options.timeoutMs ?? 25_000);
225
245
  while (this.slots.size > 0 && Date.now() < deadline)
226
246
  await pause(Math.min(50, deadline - Date.now()));
227
- const released = [];
247
+ const released = [...this.drainedRuns];
248
+ this.drainedRuns.clear();
228
249
  if (this.options.queue) {
229
250
  for (const id of [...this.slots]) {
230
251
  if (this.runner.release(id))
@@ -344,6 +365,8 @@ class RunManager {
344
365
  acceptedId = (await this.runner.enqueue(submit)).id;
345
366
  }
346
367
  if (acceptedId !== runId) {
368
+ if (thread)
369
+ await this.releaseClaim(thread.id, runId);
347
370
  await this.state.delete(RUNS, runId);
348
371
  await this.releaseTenant(runId, options.principal?.tenantId);
349
372
  return (await this.state.get(RUNS, acceptedId)) ?? run;
@@ -377,14 +400,18 @@ class RunManager {
377
400
  async execute(plan, context) {
378
401
  const runId = context.operationId;
379
402
  let cost = 0;
403
+ const control = { draining: false };
380
404
  const runContext = {
381
405
  runId,
382
406
  threadId: plan.threadId,
383
407
  signal: context.signal,
408
+ control,
384
409
  principal: plan.principal,
385
410
  metadata: plan.metadata,
386
411
  attempt: context.attempt,
387
412
  ...(plan.revision ? { revision: plan.revision.id } : {}),
413
+ saveProgress: (details) => context.heartbeat(details),
414
+ ...(context.previousHeartbeat === undefined ? {} : { progress: context.previousHeartbeat }),
388
415
  recordCost: async (usd) => {
389
416
  if (!Number.isFinite(usd) || usd <= 0)
390
417
  return;
@@ -414,6 +441,7 @@ class RunManager {
414
441
  ? plan.assistant.resume(plan.threadId, plan.resume, runContext)
415
442
  : plan.assistant.stream(plan.input, runContext));
416
443
  let last;
444
+ this.controls.set(runId, control);
417
445
  try {
418
446
  for await (const event of stream) {
419
447
  last = event;
@@ -422,6 +450,8 @@ class RunManager {
422
450
  }
423
451
  }
424
452
  catch (error) {
453
+ if (control.draining && isDrained(error) && this.runner.release(runId))
454
+ this.drainedRuns.add(runId);
425
455
  if (context.signal.aborted && context.signal.reason instanceof errors_js_1.OperationReleasedError) {
426
456
  await this.record(runId, 'status', { status: 'queued', reason: 'handed to another worker' });
427
457
  await this.settle(runId, 'queued', { addCost: cost });
@@ -442,6 +472,9 @@ class RunManager {
442
472
  await this.releaseTenant(runId, plan.principal?.tenantId);
443
473
  throw error;
444
474
  }
475
+ finally {
476
+ this.controls.delete(runId);
477
+ }
445
478
  const interrupt = interruptOf(last);
446
479
  const status = interrupt ? 'awaiting_input' : 'succeeded';
447
480
  const output = outputOf(last);
@@ -520,21 +553,21 @@ class RunManager {
520
553
  const pollMs = this.options.queue?.pollMs ?? DEFAULT_POLL_MS;
521
554
  this.schedule(claimed > 0 && this.freeSlots() > 0 ? 0 : Math.min(pollMs, this.options.recoverEveryMs ?? pollMs));
522
555
  }
523
- async settleBusyThread(thread, options) {
556
+ async settleBusyThread(thread, options, runId) {
524
557
  const policy = options.onBusy ?? this.options.onBusy ?? 'reject';
525
558
  let current = thread;
526
- for (let attempt = 0; attempt < 2; attempt += 1) {
527
- const activeId = current.activeRunId;
559
+ const deadline = this.now().getTime() + (this.options.queueTimeoutMs ?? 30_000);
560
+ for (let attempt = 0; attempt < 4 || (policy === 'enqueue' && this.now().getTime() < deadline); attempt += 1) {
561
+ const activeId = await this.claimThread(current, runId);
528
562
  if (!activeId)
529
563
  return current;
530
564
  const active = await this.state.get(RUNS, activeId);
531
- if (!active || isFinished(active.status)) {
532
- current = { ...current, activeRunId: undefined };
533
- await this.state.put(THREADS, current.id, current);
534
- return current;
535
- }
536
565
  if (policy === 'reject')
537
566
  throw new errors_js_2.ThreadBusyError(current.id, activeId);
567
+ if (!active) {
568
+ await delay(50);
569
+ continue;
570
+ }
538
571
  if (policy === 'enqueue') {
539
572
  await this.waitForRun(activeId);
540
573
  current = await this.thread(current.id, options.principal);
@@ -551,9 +584,67 @@ class RunManager {
551
584
  }
552
585
  current = { ...current, activeRunId: undefined };
553
586
  await this.state.put(THREADS, current.id, current);
554
- return current;
555
587
  }
556
- return current;
588
+ throw new errors_js_2.ThreadBusyError(current.id, current.activeRunId ?? 'another run');
589
+ }
590
+ async claimThread(thread, runId) {
591
+ if (!this.store.findByIdempotencyKey) {
592
+ const activeId = thread.activeRunId;
593
+ return activeId && !(await this.runIsFree(activeId, undefined)) ? activeId : undefined;
594
+ }
595
+ const key = `${THREAD_CLAIM}${thread.id}`;
596
+ for (let attempt = 0; attempt < 8; attempt += 1) {
597
+ const claim = await this.store.findByIdempotencyKey(key);
598
+ const at = this.now().toISOString();
599
+ if (!claim) {
600
+ try {
601
+ await this.store.create({
602
+ id: `${key}:${runId}`,
603
+ status: 'succeeded',
604
+ attempt: 1,
605
+ maxAttempts: 1,
606
+ sequence: 0,
607
+ createdAt: at,
608
+ updatedAt: at,
609
+ kind: 'server.thread',
610
+ idempotencyKey: key,
611
+ result: { runId },
612
+ });
613
+ return undefined;
614
+ }
615
+ catch (error) {
616
+ if (error instanceof errors_js_1.OperationDuplicateError)
617
+ continue;
618
+ throw error;
619
+ }
620
+ }
621
+ const holder = claim.result?.runId;
622
+ if (holder === runId)
623
+ return undefined;
624
+ if (holder && !(await this.runIsFree(holder, claim.updatedAt)))
625
+ return holder;
626
+ const taken = await this.store.update({ ...claim, result: { runId }, sequence: claim.sequence + 1, updatedAt: at }, claim.sequence);
627
+ if (taken)
628
+ return undefined;
629
+ }
630
+ const holder = (await this.store.findByIdempotencyKey(key))?.result?.runId;
631
+ return holder ?? 'another run';
632
+ }
633
+ async runIsFree(runId, claimedAt) {
634
+ const run = await this.state.get(RUNS, runId);
635
+ if (run)
636
+ return isFinished(run.status);
637
+ if (claimedAt === undefined)
638
+ return true;
639
+ return this.now().getTime() - Date.parse(claimedAt) > UNRECORDED_CLAIM_MS;
640
+ }
641
+ async releaseClaim(threadId, runId) {
642
+ if (!this.store.findByIdempotencyKey)
643
+ return;
644
+ const claim = await this.store.findByIdempotencyKey(`${THREAD_CLAIM}${threadId}`);
645
+ if (!claim || claim.result?.runId !== runId)
646
+ return;
647
+ await this.store.update({ ...claim, result: {}, sequence: claim.sequence + 1, updatedAt: this.now().toISOString() }, claim.sequence);
557
648
  }
558
649
  async waitForRun(runId) {
559
650
  const deadline = this.now().getTime() + (this.options.queueTimeoutMs ?? 30_000);
@@ -570,6 +661,7 @@ class RunManager {
570
661
  async releaseThread(threadId, runId) {
571
662
  if (!threadId)
572
663
  return;
664
+ await this.releaseClaim(threadId, runId).catch((error) => this.report(error, { runId }));
573
665
  const thread = await this.state.get(THREADS, threadId);
574
666
  if (!thread || thread.activeRunId !== runId)
575
667
  return;
@@ -664,3 +756,7 @@ function delay(ms) {
664
756
  timer.unref?.();
665
757
  });
666
758
  }
759
+ function isDrained(error) {
760
+ const code = typeof error === 'object' && error !== null ? error.code : undefined;
761
+ return code === 'GRAPH_DRAINED' || code === 'RUN_HANDED_OFF';
762
+ }
@@ -1,4 +1,17 @@
1
+ import type { SchemaMigration } from '../utils/schema-migrations.js';
2
+ export { type AppliedMigration, type MigrationResult, type MigrationStatus, migrationChecksum, type SchemaMigration, SchemaMigrationError, } from '../utils/schema-migrations.js';
1
3
  export { fromLibsql, fromSqliteDatabase, type LibsqlLikeClient, type SqliteDatabaseLike, type SqliteLikeClient, type SqliteValue, } from './client.js';
2
- export { SqliteOperationStore, type SqliteOperationStoreOptions, sqliteOperationStoreMigration } from './operations.js';
3
- export { SqliteStore, type SqliteStoreOptions, sqliteStoreMigration } from './store.js';
4
- export { SqliteVectorStore, type SqliteVectorStoreOptions, sqliteVectorStoreMigration } from './vectors.js';
4
+ export { applySqliteMigrations, type SqliteMigrateOptions, sqliteMigrationStatus } from './migrations.js';
5
+ export { SqliteOperationStore, type SqliteOperationStoreOptions, sqliteOperationStoreMigration, sqliteOperationStoreMigrations, } from './operations.js';
6
+ export { SqliteStore, type SqliteStoreOptions, sqliteStoreMigration, sqliteStoreMigrations } from './store.js';
7
+ export { SqliteVectorStore, type SqliteVectorStoreOptions, sqliteVectorStoreMigration, sqliteVectorStoreMigrations, } from './vectors.js';
8
+ /** The SQLite adapters a migration can include. */
9
+ export type SqliteAdapter = 'operations' | 'store' | 'vectors';
10
+ /**
11
+ * The versioned migrations of the chosen adapters on their default tables, for
12
+ * `applySqliteMigrations()`, `sqliteMigrationStatus()`, and `nexus db migrate`. Defaults to every
13
+ * adapter. A store on a custom table migrates through its own `migrate()`.
14
+ */
15
+ export declare function sqliteMigrations(options?: {
16
+ adapters?: readonly SqliteAdapter[];
17
+ }): SchemaMigration[];
@@ -1,15 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sqliteVectorStoreMigration = exports.SqliteVectorStore = exports.sqliteStoreMigration = exports.SqliteStore = exports.sqliteOperationStoreMigration = exports.SqliteOperationStore = exports.fromSqliteDatabase = exports.fromLibsql = void 0;
3
+ exports.sqliteVectorStoreMigrations = exports.sqliteVectorStoreMigration = exports.SqliteVectorStore = exports.sqliteStoreMigrations = exports.sqliteStoreMigration = exports.SqliteStore = exports.sqliteOperationStoreMigrations = exports.sqliteOperationStoreMigration = exports.SqliteOperationStore = exports.sqliteMigrationStatus = exports.applySqliteMigrations = exports.fromSqliteDatabase = exports.fromLibsql = exports.SchemaMigrationError = exports.migrationChecksum = void 0;
4
+ exports.sqliteMigrations = sqliteMigrations;
5
+ const operations_js_1 = require("./operations.js");
6
+ const store_js_1 = require("./store.js");
7
+ const vectors_js_1 = require("./vectors.js");
8
+ var schema_migrations_js_1 = require("../utils/schema-migrations.js");
9
+ Object.defineProperty(exports, "migrationChecksum", { enumerable: true, get: function () { return schema_migrations_js_1.migrationChecksum; } });
10
+ Object.defineProperty(exports, "SchemaMigrationError", { enumerable: true, get: function () { return schema_migrations_js_1.SchemaMigrationError; } });
4
11
  var client_js_1 = require("./client.js");
5
12
  Object.defineProperty(exports, "fromLibsql", { enumerable: true, get: function () { return client_js_1.fromLibsql; } });
6
13
  Object.defineProperty(exports, "fromSqliteDatabase", { enumerable: true, get: function () { return client_js_1.fromSqliteDatabase; } });
7
- var operations_js_1 = require("./operations.js");
8
- Object.defineProperty(exports, "SqliteOperationStore", { enumerable: true, get: function () { return operations_js_1.SqliteOperationStore; } });
9
- Object.defineProperty(exports, "sqliteOperationStoreMigration", { enumerable: true, get: function () { return operations_js_1.sqliteOperationStoreMigration; } });
10
- var store_js_1 = require("./store.js");
11
- Object.defineProperty(exports, "SqliteStore", { enumerable: true, get: function () { return store_js_1.SqliteStore; } });
12
- Object.defineProperty(exports, "sqliteStoreMigration", { enumerable: true, get: function () { return store_js_1.sqliteStoreMigration; } });
13
- var vectors_js_1 = require("./vectors.js");
14
- Object.defineProperty(exports, "SqliteVectorStore", { enumerable: true, get: function () { return vectors_js_1.SqliteVectorStore; } });
15
- Object.defineProperty(exports, "sqliteVectorStoreMigration", { enumerable: true, get: function () { return vectors_js_1.sqliteVectorStoreMigration; } });
14
+ var migrations_js_1 = require("./migrations.js");
15
+ Object.defineProperty(exports, "applySqliteMigrations", { enumerable: true, get: function () { return migrations_js_1.applySqliteMigrations; } });
16
+ Object.defineProperty(exports, "sqliteMigrationStatus", { enumerable: true, get: function () { return migrations_js_1.sqliteMigrationStatus; } });
17
+ var operations_js_2 = require("./operations.js");
18
+ Object.defineProperty(exports, "SqliteOperationStore", { enumerable: true, get: function () { return operations_js_2.SqliteOperationStore; } });
19
+ Object.defineProperty(exports, "sqliteOperationStoreMigration", { enumerable: true, get: function () { return operations_js_2.sqliteOperationStoreMigration; } });
20
+ Object.defineProperty(exports, "sqliteOperationStoreMigrations", { enumerable: true, get: function () { return operations_js_2.sqliteOperationStoreMigrations; } });
21
+ var store_js_2 = require("./store.js");
22
+ Object.defineProperty(exports, "SqliteStore", { enumerable: true, get: function () { return store_js_2.SqliteStore; } });
23
+ Object.defineProperty(exports, "sqliteStoreMigration", { enumerable: true, get: function () { return store_js_2.sqliteStoreMigration; } });
24
+ Object.defineProperty(exports, "sqliteStoreMigrations", { enumerable: true, get: function () { return store_js_2.sqliteStoreMigrations; } });
25
+ var vectors_js_2 = require("./vectors.js");
26
+ Object.defineProperty(exports, "SqliteVectorStore", { enumerable: true, get: function () { return vectors_js_2.SqliteVectorStore; } });
27
+ Object.defineProperty(exports, "sqliteVectorStoreMigration", { enumerable: true, get: function () { return vectors_js_2.sqliteVectorStoreMigration; } });
28
+ Object.defineProperty(exports, "sqliteVectorStoreMigrations", { enumerable: true, get: function () { return vectors_js_2.sqliteVectorStoreMigrations; } });
29
+ function sqliteMigrations(options = {}) {
30
+ const adapters = new Set(options.adapters ?? ['operations', 'store', 'vectors']);
31
+ return [
32
+ ...(adapters.has('operations') ? (0, operations_js_1.sqliteOperationStoreMigrations)() : []),
33
+ ...(adapters.has('store') ? (0, store_js_1.sqliteStoreMigrations)() : []),
34
+ ...(adapters.has('vectors') ? (0, vectors_js_1.sqliteVectorStoreMigrations)() : []),
35
+ ];
36
+ }
@@ -0,0 +1,26 @@
1
+ import { type MigrationResult, type MigrationStatus, type SchemaMigration } from '../utils/schema-migrations.js';
2
+ import { type SqliteDatabaseLike, type SqliteLikeClient } from './client.js';
3
+ export { type AppliedMigration, type MigrationResult, type MigrationStatus, migrationChecksum, type SchemaMigration, SchemaMigrationError, } from '../utils/schema-migrations.js';
4
+ /** Options for applying or inspecting SQLite migrations. */
5
+ export interface SqliteMigrateOptions {
6
+ /** Plans without writing anything, not even the table that records migrations. */
7
+ dryRun?: boolean;
8
+ /** The table that records applied migrations. Defaults to `nexus_schema_migrations`. */
9
+ table?: string;
10
+ }
11
+ /**
12
+ * Where a SQLite database stands against a list of migrations. Reads only: on a database that has
13
+ * never been migrated, everything is pending.
14
+ */
15
+ export declare function sqliteMigrationStatus(database: SqliteLikeClient | SqliteDatabaseLike, migrations: readonly SchemaMigration[], options?: Pick<SqliteMigrateOptions, 'table'>): Promise<MigrationStatus>;
16
+ /**
17
+ * Applies the pending migrations in order, recording each with a checksum.
18
+ *
19
+ * Each migration runs inside `BEGIN IMMEDIATE`, which takes SQLite's write lock: its statements and
20
+ * the row recording it land together or not at all, and a second process migrating the same file
21
+ * waits for the first instead of interleaving with it. A migration whose statements changed after it
22
+ * ran is refused before anything is written.
23
+ */
24
+ export declare function applySqliteMigrations(database: SqliteLikeClient | SqliteDatabaseLike, migrations: readonly SchemaMigration[], options?: SqliteMigrateOptions): Promise<MigrationResult>;
25
+ /** The statement that creates the migrations table, which `migrate()` runs first. */
26
+ export declare function sqliteMigrationsTableStatement(table?: string): string;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaMigrationError = exports.migrationChecksum = void 0;
4
+ exports.sqliteMigrationStatus = sqliteMigrationStatus;
5
+ exports.applySqliteMigrations = applySqliteMigrations;
6
+ exports.sqliteMigrationsTableStatement = sqliteMigrationsTableStatement;
7
+ const schema_migrations_js_1 = require("../utils/schema-migrations.js");
8
+ const client_js_1 = require("./client.js");
9
+ var schema_migrations_js_2 = require("../utils/schema-migrations.js");
10
+ Object.defineProperty(exports, "migrationChecksum", { enumerable: true, get: function () { return schema_migrations_js_2.migrationChecksum; } });
11
+ Object.defineProperty(exports, "SchemaMigrationError", { enumerable: true, get: function () { return schema_migrations_js_2.SchemaMigrationError; } });
12
+ async function sqliteMigrationStatus(database, migrations, options = {}) {
13
+ return (0, schema_migrations_js_1.planMigrations)(migrations, await readApplied((0, client_js_1.toSqliteClient)(database), options.table ?? schema_migrations_js_1.MIGRATIONS_TABLE));
14
+ }
15
+ async function applySqliteMigrations(database, migrations, options = {}) {
16
+ const client = (0, client_js_1.toSqliteClient)(database);
17
+ const tableName = options.table ?? schema_migrations_js_1.MIGRATIONS_TABLE;
18
+ if (options.dryRun) {
19
+ const status = await sqliteMigrationStatus(client, migrations, options);
20
+ (0, schema_migrations_js_1.assertUnchanged)(status);
21
+ return { applied: status.pending, statements: status.pending.flatMap((m) => [...m.statements]), dryRun: true };
22
+ }
23
+ (0, schema_migrations_js_1.planMigrations)(migrations, []);
24
+ await client.exec(sqliteMigrationsTableStatement(tableName));
25
+ const status = (0, schema_migrations_js_1.planMigrations)(migrations, await readApplied(client, tableName));
26
+ (0, schema_migrations_js_1.assertUnchanged)(status);
27
+ const table = (0, client_js_1.quoteSqliteTable)(tableName);
28
+ const statements = [];
29
+ for (const migration of status.pending) {
30
+ const record = `INSERT OR IGNORE INTO ${table} (component, version, name, checksum, applied_at) VALUES (${(0, schema_migrations_js_1.sqlLiteral)(migration.component)}, ${migration.version}, ${(0, schema_migrations_js_1.sqlLiteral)(migration.name)}, ${(0, schema_migrations_js_1.sqlLiteral)((0, schema_migrations_js_1.migrationChecksum)(migration))}, ${(0, schema_migrations_js_1.sqlLiteral)(new Date().toISOString())})`;
31
+ const script = ['BEGIN IMMEDIATE', ...migration.statements, record, 'COMMIT'].join(';\n');
32
+ try {
33
+ await client.exec(`${script};`);
34
+ }
35
+ catch (error) {
36
+ try {
37
+ await client.exec('ROLLBACK');
38
+ }
39
+ catch {
40
+ }
41
+ throw error;
42
+ }
43
+ statements.push(...migration.statements);
44
+ }
45
+ return { applied: status.pending, statements, dryRun: false };
46
+ }
47
+ function sqliteMigrationsTableStatement(table = schema_migrations_js_1.MIGRATIONS_TABLE) {
48
+ return `CREATE TABLE IF NOT EXISTS ${(0, client_js_1.quoteSqliteTable)(table)} (
49
+ component TEXT NOT NULL,
50
+ version INTEGER NOT NULL,
51
+ name TEXT NOT NULL,
52
+ checksum TEXT NOT NULL,
53
+ applied_at TEXT NOT NULL,
54
+ PRIMARY KEY (component, version)
55
+ )`;
56
+ }
57
+ async function readApplied(client, table) {
58
+ const exists = (await client.all(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`, [
59
+ table,
60
+ ]));
61
+ if (exists.length === 0)
62
+ return [];
63
+ const rows = (await client.all(`SELECT component, version, name, checksum, applied_at FROM ${(0, client_js_1.quoteSqliteTable)(table)} ORDER BY applied_at, component, version`, []));
64
+ return rows.map((row) => ({
65
+ component: String(row.component),
66
+ version: Number(row.version),
67
+ name: String(row.name),
68
+ checksum: String(row.checksum),
69
+ appliedAt: String(row.applied_at),
70
+ }));
71
+ }
@@ -1,12 +1,20 @@
1
1
  import { type OperationRecord, type OperationStore, type OperationStoreFilter, type OperationStoreStats } from '../types/operations.js';
2
+ import type { MigrationResult, SchemaMigration } from '../utils/schema-migrations.js';
2
3
  import { type SqliteDatabaseLike, type SqliteLikeClient } from './client.js';
4
+ import type { SqliteMigrateOptions } from './migrations.js';
3
5
  /** Options for the SQLite operation store. */
4
6
  export interface SqliteOperationStoreOptions {
5
7
  /** Table name. Defaults to `nexus_operations`. */
6
8
  table?: string;
7
9
  }
8
- /** The schema, as statements. Applied by `migrate()`, or by the application's own tooling. */
10
+ /** The schema, as statements: every migration's, in order. For the application's own tooling. */
9
11
  export declare function sqliteOperationStoreMigration(options?: SqliteOperationStoreOptions): string[];
12
+ /**
13
+ * The versioned schema, which `migrate()` applies. Version 1 is the 2.0 table; version 2 indexes
14
+ * queued work by age, so a claim reads the oldest few rows instead of sorting the queue; version 3
15
+ * indexes queued work by tenant.
16
+ */
17
+ export declare function sqliteOperationStoreMigrations(options?: SqliteOperationStoreOptions): SchemaMigration[];
10
18
  /**
11
19
  * Operation records in SQLite: durable operations, and — through `OperationStoreCheckpointer` — graph
12
20
  * and workflow checkpoints, on one machine with no server to run.
@@ -20,8 +28,11 @@ export declare class SqliteOperationStore<TResult = unknown> implements Operatio
20
28
  private readonly client;
21
29
  private readonly table;
22
30
  constructor(database: SqliteLikeClient | SqliteDatabaseLike, options?: SqliteOperationStoreOptions);
23
- /** Creates the table and indexes if they do not exist. Never runs implicitly. */
24
- migrate(): Promise<void>;
31
+ /**
32
+ * Applies this store's pending migrations, recorded in `nexus_schema_migrations`, each inside
33
+ * `BEGIN IMMEDIATE`. Never runs implicitly. Safe on a file an older release created.
34
+ */
35
+ migrate(options?: SqliteMigrateOptions): Promise<MigrationResult>;
25
36
  /**
26
37
  * Inserts a new record. Throws `OperationDuplicateError` when its idempotency key is already
27
38
  * claimed. Refuses records carrying raw bytes.
@@ -1,7 +1,41 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.SqliteOperationStore = void 0;
4
37
  exports.sqliteOperationStoreMigration = sqliteOperationStoreMigration;
38
+ exports.sqliteOperationStoreMigrations = sqliteOperationStoreMigrations;
5
39
  const errors_js_1 = require("../operations/errors.js");
6
40
  const serialization_js_1 = require("../operations/serialization.js");
7
41
  const operations_js_1 = require("../types/operations.js");
@@ -9,8 +43,31 @@ const client_js_1 = require("./client.js");
9
43
  const DEFAULT_TABLE = 'nexus_operations';
10
44
  const TERMINAL = operations_js_1.TERMINAL_OPERATION_STATUSES.map((status) => `'${status}'`).join(', ');
11
45
  function sqliteOperationStoreMigration(options = {}) {
46
+ return sqliteOperationStoreMigrations(options).flatMap((migration) => [...migration.statements]);
47
+ }
48
+ function sqliteOperationStoreMigrations(options = {}) {
12
49
  const name = options.table ?? DEFAULT_TABLE;
13
50
  const table = (0, client_js_1.quoteSqliteTable)(name);
51
+ const component = `operations:${name}`;
52
+ return [
53
+ { component, version: 1, name: 'create the operations table', statements: createStatements(name, table) },
54
+ {
55
+ component,
56
+ version: 2,
57
+ name: 'index queued work by age',
58
+ statements: [`CREATE INDEX IF NOT EXISTS "${name}_queued" ON ${table} (status, created_at, id)`],
59
+ },
60
+ {
61
+ component,
62
+ version: 3,
63
+ name: 'index queued work by tenant',
64
+ statements: [
65
+ `CREATE INDEX IF NOT EXISTS "${name}_tenant_queued" ON ${table} (status, json_extract(doc, '$.tenantId'), created_at, id)`,
66
+ ],
67
+ },
68
+ ];
69
+ }
70
+ function createStatements(name, table) {
14
71
  return [
15
72
  `CREATE TABLE IF NOT EXISTS ${table} (
16
73
  id TEXT PRIMARY KEY,
@@ -36,9 +93,9 @@ class SqliteOperationStore {
36
93
  this.client = (0, client_js_1.toSqliteClient)(database);
37
94
  this.table = (0, client_js_1.quoteSqliteTable)(options.table ?? DEFAULT_TABLE);
38
95
  }
39
- async migrate() {
40
- for (const statement of sqliteOperationStoreMigration(this.options))
41
- await this.client.exec(statement);
96
+ async migrate(options = {}) {
97
+ const { applySqliteMigrations } = await Promise.resolve().then(() => __importStar(require('./migrations.js')));
98
+ return applySqliteMigrations(this.client, sqliteOperationStoreMigrations(this.options), options);
42
99
  }
43
100
  async create(record) {
44
101
  (0, serialization_js_1.assertSerializableRecord)(record);
@@ -103,29 +160,21 @@ class SqliteOperationStore {
103
160
  }
104
161
  async listQueued(limit, filter) {
105
162
  const params = [new Date().toISOString()];
106
- let kind = '';
107
- if (filter?.kindPrefix) {
108
- params.push(filter.kindPrefix, filter.kindPrefix);
109
- kind = ' AND substr(kind, 1, length(?)) = ?';
110
- }
163
+ const narrowed = filterSql(filter, params);
111
164
  params.push(limit);
112
165
  return this.many(`SELECT doc FROM ${this.table}
113
- WHERE status = 'queued' AND (lease_expires_at IS NULL OR lease_expires_at <= ?)${kind}
166
+ WHERE status = 'queued' AND (lease_expires_at IS NULL OR lease_expires_at <= ?)${narrowed}
114
167
  ORDER BY created_at, id
115
168
  LIMIT ?`, params);
116
169
  }
117
170
  async stats(now, filter) {
118
171
  const params = [now];
119
- let kind = '';
120
- if (filter?.kindPrefix) {
121
- params.push(filter.kindPrefix, filter.kindPrefix);
122
- kind = ' AND substr(kind, 1, length(?)) = ?';
123
- }
172
+ const narrowed = filterSql(filter, params);
124
173
  const rows = (await this.client.all(`SELECT status, count(*) AS count,
125
174
  min(CASE WHEN status = 'queued' THEN created_at END) AS oldest,
126
175
  sum(CASE WHEN status = 'running' AND (lease_expires_at IS NULL OR lease_expires_at <= ?) THEN 1 ELSE 0 END) AS lapsed
127
176
  FROM ${this.table}
128
- WHERE status NOT IN (${TERMINAL})${kind}
177
+ WHERE status NOT IN (${TERMINAL})${narrowed}
129
178
  GROUP BY status`, params));
130
179
  const byStatus = {};
131
180
  let oldestQueuedAt;
@@ -151,3 +200,15 @@ class SqliteOperationStore {
151
200
  }
152
201
  }
153
202
  exports.SqliteOperationStore = SqliteOperationStore;
203
+ function filterSql(filter, params) {
204
+ let sql = '';
205
+ if (filter?.kindPrefix) {
206
+ params.push(filter.kindPrefix, filter.kindPrefix);
207
+ sql += ' AND substr(kind, 1, length(?)) = ?';
208
+ }
209
+ if (filter?.tenantId !== undefined) {
210
+ params.push(filter.tenantId);
211
+ sql += " AND json_extract(doc, '$.tenantId') = ?";
212
+ }
213
+ return sql;
214
+ }
@@ -1,5 +1,7 @@
1
1
  import type { Store, StoreIndexOptions, StoreItem, StoreNamespace, StorePutOptions, StoreSearchOptions } from '../types/store.js';
2
+ import type { MigrationResult, SchemaMigration } from '../utils/schema-migrations.js';
2
3
  import { type SqliteDatabaseLike, type SqliteLikeClient } from './client.js';
4
+ import type { SqliteMigrateOptions } from './migrations.js';
3
5
  /** Options for the SQLite store. */
4
6
  export interface SqliteStoreOptions {
5
7
  /** Table name. Defaults to `nexus_store`. */
@@ -11,6 +13,8 @@ export interface SqliteStoreOptions {
11
13
  }
12
14
  /** The schema, as statements. */
13
15
  export declare function sqliteStoreMigration(options?: Pick<SqliteStoreOptions, 'table'>): string[];
16
+ /** The versioned schema, which `migrate()` applies. Version 1 is the 2.0 schema. */
17
+ export declare function sqliteStoreMigrations(options?: Pick<SqliteStoreOptions, 'table'>): SchemaMigration[];
14
18
  /**
15
19
  * Long-term memory in SQLite, for one machine that needs memory to survive a restart.
16
20
  *
@@ -25,8 +29,11 @@ export declare class SqliteStore implements Store {
25
29
  private readonly table;
26
30
  private readonly now;
27
31
  constructor(database: SqliteLikeClient | SqliteDatabaseLike, options?: SqliteStoreOptions);
28
- /** Creates the table and indexes if they do not exist. Never runs implicitly. */
29
- migrate(): Promise<void>;
32
+ /**
33
+ * Applies this store's pending migrations, recorded in `nexus_schema_migrations`, each inside
34
+ * `BEGIN IMMEDIATE`. Never runs implicitly. Safe on a file an older release created.
35
+ */
36
+ migrate(options?: SqliteMigrateOptions): Promise<MigrationResult>;
30
37
  /** Stores an item, keeping its original `createdAt` when it replaces one. */
31
38
  put<V>(namespace: StoreNamespace, key: string, value: V, options?: StorePutOptions): Promise<void>;
32
39
  /** Reads an item, or `undefined` when it does not exist or has expired. */