spexcode 0.6.7 → 0.6.8

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 (205) hide show
  1. package/README.md +12 -7
  2. package/node_modules/@spexcode/session-application/dist/index.d.ts +17 -0
  3. package/node_modules/@spexcode/session-application/dist/index.js +35 -0
  4. package/node_modules/@spexcode/session-application/dist/migration.d.ts +51 -0
  5. package/node_modules/@spexcode/session-application/dist/migration.js +694 -0
  6. package/node_modules/@spexcode/session-application/dist/production.d.ts +109 -0
  7. package/node_modules/@spexcode/session-application/dist/production.js +438 -0
  8. package/node_modules/@spexcode/session-application/dist/schema.d.ts +2 -0
  9. package/node_modules/@spexcode/session-application/dist/schema.js +39 -0
  10. package/node_modules/@spexcode/session-application/package.json +29 -0
  11. package/node_modules/@spexcode/session-events/dist/errors.d.ts +6 -0
  12. package/node_modules/@spexcode/session-events/dist/errors.js +11 -0
  13. package/node_modules/@spexcode/session-events/dist/index.d.ts +46 -0
  14. package/node_modules/@spexcode/session-events/dist/index.js +245 -0
  15. package/node_modules/@spexcode/session-events/dist/schema.d.ts +3 -0
  16. package/node_modules/@spexcode/session-events/dist/schema.js +44 -0
  17. package/node_modules/@spexcode/session-events/package.json +24 -0
  18. package/node_modules/@spexcode/session-protocol/dist/canonical.d.ts +36 -0
  19. package/node_modules/@spexcode/session-protocol/dist/canonical.js +139 -0
  20. package/node_modules/@spexcode/session-protocol/dist/engine.d.ts +22 -0
  21. package/node_modules/@spexcode/session-protocol/dist/engine.js +478 -0
  22. package/node_modules/@spexcode/session-protocol/dist/errors.d.ts +8 -0
  23. package/node_modules/@spexcode/session-protocol/dist/errors.js +39 -0
  24. package/node_modules/@spexcode/session-protocol/dist/index.d.ts +60 -0
  25. package/node_modules/@spexcode/session-protocol/dist/index.js +4 -0
  26. package/node_modules/@spexcode/session-protocol/dist/schema.d.ts +36 -0
  27. package/node_modules/@spexcode/session-protocol/dist/schema.js +192 -0
  28. package/node_modules/@spexcode/{session-core → session-protocol}/package.json +4 -8
  29. package/node_modules/@spexcode/session-runtime/dist/errors.d.ts +6 -0
  30. package/node_modules/@spexcode/session-runtime/dist/errors.js +11 -0
  31. package/node_modules/@spexcode/session-runtime/dist/index.d.ts +33 -0
  32. package/node_modules/@spexcode/session-runtime/dist/index.js +193 -0
  33. package/node_modules/@spexcode/session-runtime/dist/schema.d.ts +2 -0
  34. package/node_modules/@spexcode/session-runtime/dist/schema.js +37 -0
  35. package/node_modules/@spexcode/session-runtime/package.json +24 -0
  36. package/node_modules/@spexcode/session-selflaunch/bin/spex-session.mjs +4 -0
  37. package/node_modules/@spexcode/session-selflaunch/dist/cli.d.ts +25 -0
  38. package/node_modules/@spexcode/session-selflaunch/dist/cli.js +156 -0
  39. package/node_modules/@spexcode/session-selflaunch/dist/index.d.ts +28 -0
  40. package/node_modules/@spexcode/session-selflaunch/dist/index.js +25 -0
  41. package/node_modules/@spexcode/session-selflaunch/dist/locality.d.ts +20 -0
  42. package/node_modules/@spexcode/session-selflaunch/dist/locality.js +83 -0
  43. package/node_modules/@spexcode/session-selflaunch/dist/path.d.ts +13 -0
  44. package/node_modules/@spexcode/session-selflaunch/dist/path.js +45 -0
  45. package/node_modules/@spexcode/session-selflaunch/package.json +35 -0
  46. package/node_modules/@spexcode/session-topology/dist/errors.d.ts +6 -0
  47. package/node_modules/@spexcode/session-topology/dist/errors.js +11 -0
  48. package/node_modules/@spexcode/session-topology/dist/index.d.ts +24 -0
  49. package/node_modules/@spexcode/session-topology/dist/index.js +173 -0
  50. package/node_modules/@spexcode/session-topology/dist/schema.d.ts +3 -0
  51. package/node_modules/@spexcode/session-topology/dist/schema.js +27 -0
  52. package/node_modules/@spexcode/session-topology/package.json +24 -0
  53. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +56 -19
  54. package/node_modules/@spexcode/spec-cli/dist/cli.js +102 -59
  55. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +1 -3
  56. package/node_modules/@spexcode/spec-cli/dist/client.js +49 -30
  57. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +11 -0
  58. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +46 -9
  59. package/node_modules/@spexcode/spec-cli/dist/delivery-lock.d.ts +2 -0
  60. package/node_modules/@spexcode/spec-cli/dist/delivery-lock.js +58 -0
  61. package/node_modules/@spexcode/spec-cli/dist/doctor.js +53 -11
  62. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +1 -0
  63. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +2 -2
  64. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +2 -1
  65. package/node_modules/@spexcode/spec-cli/dist/gateway.js +6 -3
  66. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +32 -2
  67. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +57 -2
  68. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +2 -0
  69. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +83 -3
  70. package/node_modules/@spexcode/spec-cli/dist/guide.js +20 -7
  71. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +16 -3
  72. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +15 -3
  73. package/node_modules/@spexcode/spec-cli/dist/harness.js +331 -50
  74. package/node_modules/@spexcode/spec-cli/dist/help.js +11 -8
  75. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +8 -0
  76. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +29 -8
  77. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +7 -0
  78. package/node_modules/@spexcode/spec-cli/dist/host.js +93 -0
  79. package/node_modules/@spexcode/spec-cli/dist/index.js +324 -22
  80. package/node_modules/@spexcode/spec-cli/dist/init.js +1 -1
  81. package/node_modules/@spexcode/spec-cli/dist/lint.js +70 -35
  82. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +3 -2
  83. package/node_modules/@spexcode/spec-cli/dist/listen.js +14 -2
  84. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +1 -1
  85. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +2 -2
  86. package/node_modules/@spexcode/spec-cli/dist/materialize.js +176 -35
  87. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +14 -14
  88. package/node_modules/@spexcode/spec-cli/dist/reviews.js +12 -7
  89. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +11 -0
  90. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +79 -1
  91. package/node_modules/@spexcode/spec-cli/dist/session-application.d.ts +23 -0
  92. package/node_modules/@spexcode/spec-cli/dist/session-application.js +189 -0
  93. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +13 -1
  94. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +6 -0
  95. package/node_modules/@spexcode/spec-cli/dist/session-files.js +13 -1
  96. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +39 -22
  97. package/node_modules/@spexcode/{session-core/dist/record-lock.d.ts → spec-cli/dist/session-record-lock.d.ts} +0 -4
  98. package/node_modules/@spexcode/spec-cli/dist/session-record-lock.js +94 -0
  99. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.d.ts +44 -0
  100. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.js +37 -0
  101. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +25 -2
  102. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +68 -11
  103. package/node_modules/@spexcode/spec-cli/dist/session-web.js +4 -4
  104. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +108 -15
  105. package/node_modules/@spexcode/spec-cli/dist/sessions.js +1465 -744
  106. package/node_modules/@spexcode/spec-cli/dist/source-list.d.ts +13 -0
  107. package/node_modules/@spexcode/spec-cli/dist/source-list.js +99 -0
  108. package/node_modules/@spexcode/spec-cli/dist/source-read.d.ts +16 -0
  109. package/node_modules/@spexcode/spec-cli/dist/source-read.js +84 -0
  110. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.d.ts +7 -0
  111. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.js +89 -0
  112. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.d.ts +23 -0
  113. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.js +138 -0
  114. package/node_modules/@spexcode/spec-cli/dist/supervise.js +15 -6
  115. package/node_modules/@spexcode/spec-cli/dist/transcript-reader.d.ts +36 -0
  116. package/node_modules/@spexcode/spec-cli/dist/transcript-reader.js +251 -0
  117. package/node_modules/@spexcode/spec-cli/hooks/dispatch.sh +19 -31
  118. package/node_modules/@spexcode/spec-cli/hooks/harness.sh +6 -6
  119. package/node_modules/@spexcode/spec-cli/package.json +6 -6
  120. package/node_modules/@spexcode/spec-cli/templates/hooks/post-checkout +4 -2
  121. package/node_modules/@spexcode/spec-cli/templates/hooks/post-merge +2 -1
  122. package/node_modules/@spexcode/spec-cli/templates/hooks/pre-commit +5 -3
  123. package/node_modules/@spexcode/spec-cli/templates/hooks/reference-transaction +5 -3
  124. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/spec.md +2 -7
  125. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/idle.sh +4 -10
  126. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/spec.md +1 -1
  127. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/mark-active.sh +22 -24
  128. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/spec.md +10 -2
  129. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/fail.sh +8 -7
  130. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/spec.md +3 -1
  131. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/session-listen.sh +133 -0
  132. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/spec.md +36 -0
  133. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +2 -0
  134. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/spec.md +1 -1
  135. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +17 -20
  136. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/merge/spec.md +33 -0
  137. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/spec.md +2 -6
  138. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/spec.md +7 -0
  139. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +13 -2
  140. package/node_modules/@spexcode/spec-core/dist/anchors.js +311 -15
  141. package/node_modules/@spexcode/spec-core/dist/git.d.ts +4 -0
  142. package/node_modules/@spexcode/spec-core/dist/git.js +9 -9
  143. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +12 -0
  144. package/node_modules/@spexcode/spec-core/dist/layout.js +4 -2
  145. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +1 -0
  146. package/node_modules/@spexcode/spec-core/dist/resilience.js +8 -0
  147. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +1 -0
  148. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +7 -3
  149. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +1 -0
  150. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +4 -0
  151. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +2 -0
  152. package/node_modules/@spexcode/spec-core/dist/specs.js +30 -0
  153. package/node_modules/@spexcode/spec-core/package.json +4 -1
  154. package/node_modules/@spexcode/spec-eval/dist/cli.js +1 -1
  155. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +1 -0
  156. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +6 -1
  157. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +1 -1
  158. package/node_modules/@spexcode/spec-eval/dist/freshness.js +12 -12
  159. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +2 -1
  160. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +2 -1
  161. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +20 -3
  162. package/node_modules/@spexcode/spec-eval/package.json +2 -2
  163. package/node_modules/@spexcode/spec-forge/package.json +2 -2
  164. package/node_modules/@vscode/tree-sitter-wasm/LICENSE +21 -0
  165. package/node_modules/@vscode/tree-sitter-wasm/README.md +36 -0
  166. package/node_modules/@vscode/tree-sitter-wasm/SECURITY.md +41 -0
  167. package/node_modules/@vscode/tree-sitter-wasm/cgmanifest.json +16 -0
  168. package/node_modules/@vscode/tree-sitter-wasm/package.json +42 -0
  169. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-bash.wasm +0 -0
  170. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-c-sharp.wasm +0 -0
  171. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-cpp.wasm +0 -0
  172. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm +0 -0
  173. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-go.wasm +0 -0
  174. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +0 -0
  175. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-java.wasm +0 -0
  176. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-javascript.wasm +0 -0
  177. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-php.wasm +0 -0
  178. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-powershell.wasm +0 -0
  179. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-python.wasm +0 -0
  180. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +0 -0
  181. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ruby.wasm +0 -0
  182. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-rust.wasm +0 -0
  183. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-tsx.wasm +0 -0
  184. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +0 -0
  185. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.js +4075 -0
  186. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.wasm +0 -0
  187. package/node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts +1027 -0
  188. package/package.json +9 -3
  189. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +0 -24
  190. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +0 -206
  191. package/node_modules/@spexcode/session-core/dist/index.d.ts +0 -5
  192. package/node_modules/@spexcode/session-core/dist/index.js +0 -5
  193. package/node_modules/@spexcode/session-core/dist/internal.d.ts +0 -3
  194. package/node_modules/@spexcode/session-core/dist/internal.js +0 -3
  195. package/node_modules/@spexcode/session-core/dist/message.d.ts +0 -22
  196. package/node_modules/@spexcode/session-core/dist/message.js +0 -53
  197. package/node_modules/@spexcode/session-core/dist/record-lock.js +0 -152
  198. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +0 -62
  199. package/node_modules/@spexcode/session-core/dist/runtime-session.js +0 -326
  200. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +0 -14
  201. package/node_modules/@spexcode/session-core/dist/session-cursors.js +0 -82
  202. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +0 -47
  203. package/node_modules/@spexcode/session-core/dist/session-timeline.js +0 -216
  204. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -53
  205. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-sed-v0.fixture +0 -46
@@ -0,0 +1,478 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { statSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, isAbsolute, sep } from 'node:path';
5
+ import { normalizeMessage, requireSessionId } from './canonical.js';
6
+ import { classifySqliteError, fail, ProtocolError, rethrowProtocolError } from './errors.js';
7
+ import { MIN_SQLITE_VERSION, migrateComponent, migrateProtocol, registerMigrationTarget, } from './schema.js';
8
+ const require = createRequire(import.meta.url);
9
+ const { DatabaseSync } = require('node:sqlite');
10
+ const JOURNAL_MODE = 'delete';
11
+ const SELECT_COLUMNS = `enqueue_seq, message_id, target_session_id, sender_session_id, protocol_version,
12
+ kind, body, headers_json, idempotency_key, payload_hash, enqueued_at_ms, dequeued_at_ms`;
13
+ const SQL = {
14
+ session: 'SELECT session_id, created_at_ms, retired_at_ms FROM protocol_sessions WHERE session_id=?',
15
+ head: `SELECT ${SELECT_COLUMNS} FROM protocol_messages INDEXED BY protocol_messages_pending_fifo
16
+ WHERE target_session_id=? AND dequeued_at_ms IS NULL ORDER BY enqueue_seq LIMIT 1`,
17
+ pending: `SELECT ${SELECT_COLUMNS} FROM protocol_messages INDEXED BY protocol_messages_pending_fifo
18
+ WHERE target_session_id=? AND dequeued_at_ms IS NULL ORDER BY enqueue_seq`,
19
+ history: `SELECT ${SELECT_COLUMNS} FROM protocol_messages INDEXED BY protocol_messages_history
20
+ WHERE target_session_id=? AND enqueue_seq>? ORDER BY enqueue_seq`,
21
+ byKey: `SELECT ${SELECT_COLUMNS} FROM protocol_messages INDEXED BY protocol_messages_idempotency
22
+ WHERE target_session_id=? AND idempotency_key=?`,
23
+ };
24
+ const inspections = new WeakMap();
25
+ export function inspectProtocol(protocol) {
26
+ const inspection = inspections.get(protocol);
27
+ if (!inspection)
28
+ fail('PROTOCOL_TRANSACTION_INVALID', 'inspection requires a live protocol handle');
29
+ return inspection;
30
+ }
31
+ export function isSupportedSqliteVersion(version) {
32
+ if (typeof version !== 'string')
33
+ return false;
34
+ const parse = (value) => value.split('.').map(Number);
35
+ const [major, minor, patch] = parse(version);
36
+ if (![major, minor, patch].every(Number.isInteger))
37
+ return false;
38
+ const [minimumMajor, minimumMinor, minimumPatch] = parse(MIN_SQLITE_VERSION);
39
+ if (major !== minimumMajor)
40
+ return major > minimumMajor;
41
+ if (minor !== minimumMinor)
42
+ return minor > minimumMinor;
43
+ return patch >= minimumPatch;
44
+ }
45
+ function validateDatabasePath(databasePath) {
46
+ if (typeof databasePath !== 'string'
47
+ || databasePath.length === 0
48
+ || databasePath.startsWith('file:')
49
+ || !isAbsolute(databasePath)) {
50
+ fail('PROTOCOL_PATH_NOT_ABSOLUTE', 'databasePath must be an absolute filesystem path string');
51
+ }
52
+ if (databasePath.includes('\0'))
53
+ fail('PROTOCOL_PATH_INVALID', 'databasePath must not contain a NUL byte');
54
+ if (databasePath.endsWith(sep))
55
+ fail('PROTOCOL_PATH_INVALID', 'databasePath must name a file, not a directory');
56
+ }
57
+ function checkParentDirectory(databasePath) {
58
+ const directory = dirname(databasePath);
59
+ let stat;
60
+ try {
61
+ stat = statSync(directory);
62
+ }
63
+ catch (error) {
64
+ fail('PROTOCOL_PATH_PARENT_MISSING', `database parent directory does not exist: ${directory}`, error);
65
+ }
66
+ if (!stat.isDirectory()) {
67
+ fail('PROTOCOL_PATH_PARENT_MISSING', `database parent is not a directory: ${directory}`);
68
+ }
69
+ }
70
+ function normalizeBusyTimeout(value) {
71
+ const timeout = value ?? 5000;
72
+ if (typeof timeout !== 'number' || !Number.isFinite(timeout) || timeout < 0) {
73
+ fail('PROTOCOL_PRAGMA_UNSUPPORTED', 'busyTimeoutMs must be a non-negative finite number');
74
+ }
75
+ return Math.trunc(timeout);
76
+ }
77
+ function normalizeNow(value) {
78
+ if (value !== undefined && typeof value !== 'function') {
79
+ fail('PROTOCOL_TRANSACTION_INVALID', 'now must be a synchronous function');
80
+ }
81
+ const now = (value ?? (() => Date.now()));
82
+ return () => {
83
+ const timestamp = now();
84
+ if (!Number.isSafeInteger(timestamp) || timestamp < 0) {
85
+ fail('PROTOCOL_SQLITE_ERROR', 'now() must return a non-negative safe integer');
86
+ }
87
+ return timestamp;
88
+ };
89
+ }
90
+ function sleepSync(milliseconds) {
91
+ const buffer = new Int32Array(new SharedArrayBuffer(4));
92
+ Atomics.wait(buffer, 0, 0, milliseconds);
93
+ }
94
+ function retryWhileBusy(budgetMs, body) {
95
+ const deadline = Date.now() + Math.max(budgetMs, 1);
96
+ for (;;) {
97
+ try {
98
+ return body();
99
+ }
100
+ catch (error) {
101
+ const busy = error instanceof ProtocolError
102
+ ? error.code === 'PROTOCOL_DATABASE_BUSY'
103
+ : classifySqliteError(error) === 'PROTOCOL_DATABASE_BUSY';
104
+ if (!busy || Date.now() >= deadline)
105
+ throw error;
106
+ sleepSync(20);
107
+ }
108
+ }
109
+ }
110
+ function toMessage(row) {
111
+ return {
112
+ enqueueSeq: Number(row.enqueue_seq),
113
+ messageId: row.message_id,
114
+ targetSessionId: row.target_session_id,
115
+ senderSessionId: row.sender_session_id,
116
+ protocolVersion: Number(row.protocol_version),
117
+ kind: row.kind,
118
+ body: Buffer.from(row.body),
119
+ headers: JSON.parse(row.headers_json),
120
+ idempotencyKey: row.idempotency_key,
121
+ payloadHash: Buffer.from(row.payload_hash).toString('hex'),
122
+ enqueuedAtMs: Number(row.enqueued_at_ms),
123
+ dequeuedAtMs: row.dequeued_at_ms === null ? null : Number(row.dequeued_at_ms),
124
+ };
125
+ }
126
+ function requireSql(value) {
127
+ if (typeof value !== 'string' || value.length === 0) {
128
+ fail('PROTOCOL_TRANSACTION_INVALID', 'transaction SQL must be a non-empty string');
129
+ }
130
+ return value;
131
+ }
132
+ function requireSqlParams(params) {
133
+ for (const value of params) {
134
+ if (value !== null
135
+ && typeof value !== 'string'
136
+ && typeof value !== 'bigint'
137
+ && !(value instanceof Uint8Array)
138
+ && !(typeof value === 'number' && Number.isFinite(value))) {
139
+ fail('PROTOCOL_TRANSACTION_INVALID', 'transaction parameters must be SqlParam values');
140
+ }
141
+ }
142
+ }
143
+ function buildHandle(database, config) {
144
+ const { readOnly, now } = config;
145
+ let transactionActive = false;
146
+ let closed = false;
147
+ const ensureOpen = () => {
148
+ if (closed)
149
+ fail('PROTOCOL_SQLITE_ERROR', 'protocol handle is closed');
150
+ };
151
+ const rejectReentry = (operation) => {
152
+ if (transactionActive) {
153
+ fail('PROTOCOL_TRANSACTION_INVALID', `${operation} cannot be called inside withTransaction; use tx.enqueue for messages and tx.exec or tx.query for SQL`);
154
+ }
155
+ };
156
+ const write = (operation, body) => {
157
+ ensureOpen();
158
+ rejectReentry(operation);
159
+ if (readOnly)
160
+ fail('PROTOCOL_DATABASE_READONLY', 'this handle was opened read-only');
161
+ try {
162
+ database.exec('BEGIN IMMEDIATE');
163
+ }
164
+ catch (error) {
165
+ rethrowProtocolError(error);
166
+ }
167
+ try {
168
+ const value = body();
169
+ database.exec('COMMIT');
170
+ return value;
171
+ }
172
+ catch (error) {
173
+ try {
174
+ database.exec('ROLLBACK');
175
+ }
176
+ catch { }
177
+ rethrowProtocolError(error);
178
+ }
179
+ };
180
+ const read = (body) => {
181
+ ensureOpen();
182
+ try {
183
+ return body();
184
+ }
185
+ catch (error) {
186
+ rethrowProtocolError(error);
187
+ }
188
+ };
189
+ const requireAddress = (sessionId, allowRetired = false) => {
190
+ const row = database.prepare(SQL.session).get(sessionId);
191
+ if (!row)
192
+ fail('PROTOCOL_SESSION_UNKNOWN', `unknown protocol address: ${sessionId}`);
193
+ if (!allowRetired && row.retired_at_ms !== null) {
194
+ fail('PROTOCOL_SESSION_RETIRED', `protocol address is retired: ${sessionId}`);
195
+ }
196
+ return row;
197
+ };
198
+ const insertNormalizedMessage = (message) => {
199
+ const sessionId = message.targetSessionId;
200
+ requireAddress(sessionId);
201
+ if (message.idempotencyKey !== null) {
202
+ const prior = database.prepare(SQL.byKey).get(sessionId, message.idempotencyKey);
203
+ if (prior) {
204
+ if (Buffer.compare(Buffer.from(prior.payload_hash), Buffer.from(message.hash)) !== 0) {
205
+ fail('PROTOCOL_IDEMPOTENCY_CONFLICT', `idempotency key ${message.idempotencyKey} was reused with different message bytes`);
206
+ }
207
+ return toMessage(prior);
208
+ }
209
+ }
210
+ const messageId = randomBytes(16).toString('hex');
211
+ const enqueuedAtMs = now();
212
+ database.prepare(`INSERT INTO protocol_messages
213
+ (message_id, target_session_id, sender_session_id, protocol_version, kind, body,
214
+ headers_json, idempotency_key, payload_hash, enqueued_at_ms, dequeued_at_ms)
215
+ VALUES (?,?,?,?,?,?,?,?,?,?,NULL)`).run(messageId, sessionId, message.senderSessionId, message.protocolVersion, message.kind, message.body, message.headersJson, message.idempotencyKey, message.hash, enqueuedAtMs);
216
+ const row = database.prepare(`SELECT ${SELECT_COLUMNS} FROM protocol_messages WHERE message_id=?`)
217
+ .get(messageId);
218
+ if (!row)
219
+ fail('PROTOCOL_SQLITE_ERROR', 'inserted message could not be read back');
220
+ return toMessage(row);
221
+ };
222
+ const insertMessage = (sessionId, input) => insertNormalizedMessage(normalizeMessage(sessionId, input));
223
+ const transaction = {
224
+ exec(sql, ...params) {
225
+ requireSql(sql);
226
+ requireSqlParams(params);
227
+ try {
228
+ const result = database.prepare(sql).run(...params);
229
+ return { changes: Number(result.changes), lastInsertRowid: Number(result.lastInsertRowid) };
230
+ }
231
+ catch (error) {
232
+ rethrowProtocolError(error);
233
+ }
234
+ },
235
+ query(sql, ...params) {
236
+ requireSql(sql);
237
+ requireSqlParams(params);
238
+ try {
239
+ return database.prepare(sql).all(...params);
240
+ }
241
+ catch (error) {
242
+ rethrowProtocolError(error);
243
+ }
244
+ },
245
+ enqueue(sessionId, message) {
246
+ requireSessionId(sessionId);
247
+ return insertMessage(sessionId, message);
248
+ },
249
+ };
250
+ const handle = {
251
+ databasePath: config.databasePath,
252
+ readOnly,
253
+ initialize(sessionId) {
254
+ rejectReentry('protocol.initialize');
255
+ requireSessionId(sessionId);
256
+ return write('protocol.initialize', () => {
257
+ const existing = database.prepare(SQL.session).get(sessionId);
258
+ if (existing) {
259
+ if (existing.retired_at_ms !== null) {
260
+ fail('PROTOCOL_SESSION_RETIRED', `protocol address is retired and cannot be resurrected: ${sessionId}`);
261
+ }
262
+ return { sessionId, createdAtMs: Number(existing.created_at_ms), retiredAtMs: null };
263
+ }
264
+ const createdAtMs = now();
265
+ database.prepare('INSERT INTO protocol_sessions(session_id, created_at_ms, retired_at_ms) VALUES(?,?,NULL)')
266
+ .run(sessionId, createdAtMs);
267
+ return { sessionId, createdAtMs, retiredAtMs: null };
268
+ });
269
+ },
270
+ enqueue(sessionId, input) {
271
+ rejectReentry('protocol.enqueue');
272
+ requireSessionId(sessionId);
273
+ const normalized = normalizeMessage(sessionId, input);
274
+ return write('protocol.enqueue', () => insertNormalizedMessage(normalized));
275
+ },
276
+ dequeue(sessionId) {
277
+ rejectReentry('protocol.dequeue');
278
+ requireSessionId(sessionId);
279
+ return write('protocol.dequeue', () => {
280
+ requireAddress(sessionId);
281
+ const row = database.prepare(SQL.head).get(sessionId);
282
+ if (!row)
283
+ return null;
284
+ const dequeuedAtMs = now();
285
+ const result = database.prepare('UPDATE protocol_messages SET dequeued_at_ms=? WHERE enqueue_seq=? AND dequeued_at_ms IS NULL').run(dequeuedAtMs, row.enqueue_seq);
286
+ if (Number(result.changes) !== 1) {
287
+ fail('PROTOCOL_SQLITE_ERROR', 'FIFO head changed inside a write transaction');
288
+ }
289
+ return toMessage({ ...row, dequeued_at_ms: dequeuedAtMs });
290
+ });
291
+ },
292
+ listPending(sessionId) {
293
+ requireSessionId(sessionId);
294
+ return read(() => {
295
+ requireAddress(sessionId, true);
296
+ return database.prepare(SQL.pending).all(sessionId).map(row => toMessage(row));
297
+ });
298
+ },
299
+ readMessages(sessionId, afterSequence = 0) {
300
+ requireSessionId(sessionId);
301
+ if (!Number.isSafeInteger(afterSequence) || afterSequence < 0) {
302
+ fail('PROTOCOL_CURSOR_INVALID', 'afterSequence must be a non-negative safe integer');
303
+ }
304
+ return read(() => {
305
+ requireAddress(sessionId, true);
306
+ return database.prepare(SQL.history).all(sessionId, afterSequence).map(row => toMessage(row));
307
+ });
308
+ },
309
+ retire(sessionId) {
310
+ rejectReentry('protocol.retire');
311
+ requireSessionId(sessionId);
312
+ return write('protocol.retire', () => {
313
+ const row = requireAddress(sessionId, true);
314
+ if (row.retired_at_ms !== null) {
315
+ return {
316
+ sessionId,
317
+ createdAtMs: Number(row.created_at_ms),
318
+ retiredAtMs: Number(row.retired_at_ms),
319
+ };
320
+ }
321
+ const pending = database.prepare(`SELECT 1 AS present FROM protocol_messages INDEXED BY protocol_messages_pending_fifo
322
+ WHERE target_session_id=? AND dequeued_at_ms IS NULL LIMIT 1`).get(sessionId);
323
+ if (pending) {
324
+ fail('PROTOCOL_RETIRE_NON_EMPTY', `cannot retire an address with pending messages: ${sessionId}`);
325
+ }
326
+ const retiredAtMs = now();
327
+ database.prepare('UPDATE protocol_sessions SET retired_at_ms=? WHERE session_id=? AND retired_at_ms IS NULL')
328
+ .run(retiredAtMs, sessionId);
329
+ return { sessionId, createdAtMs: Number(row.created_at_ms), retiredAtMs };
330
+ });
331
+ },
332
+ withTransaction(body) {
333
+ rejectReentry('protocol.withTransaction');
334
+ ensureOpen();
335
+ if (typeof body !== 'function') {
336
+ fail('PROTOCOL_TRANSACTION_INVALID', 'a transaction body is required');
337
+ }
338
+ if (body.constructor?.name === 'AsyncFunction') {
339
+ fail('PROTOCOL_TRANSACTION_INVALID', 'a transaction body must be synchronous; use tx.enqueue inside it');
340
+ }
341
+ if (readOnly)
342
+ fail('PROTOCOL_DATABASE_READONLY', 'this handle was opened read-only');
343
+ try {
344
+ database.exec('BEGIN IMMEDIATE');
345
+ }
346
+ catch (error) {
347
+ rethrowProtocolError(error);
348
+ }
349
+ transactionActive = true;
350
+ let value;
351
+ try {
352
+ value = body(transaction);
353
+ if (value && typeof value.then === 'function') {
354
+ fail('PROTOCOL_TRANSACTION_INVALID', 'a transaction body must not return a promise; use tx.enqueue');
355
+ }
356
+ }
357
+ catch (error) {
358
+ transactionActive = false;
359
+ try {
360
+ database.exec('ROLLBACK');
361
+ }
362
+ catch { }
363
+ throw error;
364
+ }
365
+ transactionActive = false;
366
+ try {
367
+ database.exec('COMMIT');
368
+ return value;
369
+ }
370
+ catch (error) {
371
+ try {
372
+ database.exec('ROLLBACK');
373
+ }
374
+ catch { }
375
+ rethrowProtocolError(error);
376
+ }
377
+ },
378
+ dataVersion() {
379
+ return read(() => Number(database.prepare('PRAGMA data_version').get()?.data_version));
380
+ },
381
+ close() {
382
+ rejectReentry('protocol.close');
383
+ if (closed)
384
+ return;
385
+ database.close();
386
+ closed = true;
387
+ },
388
+ };
389
+ const inspection = {
390
+ pragmas: () => ({
391
+ journal_mode: String(database.prepare('PRAGMA journal_mode').get()?.journal_mode),
392
+ foreign_keys: Number(database.prepare('PRAGMA foreign_keys').get()?.foreign_keys),
393
+ synchronous: Number(database.prepare('PRAGMA synchronous').get()?.synchronous),
394
+ busy_timeout: Number(database.prepare('PRAGMA busy_timeout').get()?.timeout),
395
+ }),
396
+ counts: () => ({
397
+ sessions: Number(database.prepare('SELECT count(*) AS count FROM protocol_sessions').get()?.count),
398
+ messages: Number(database.prepare('SELECT count(*) AS count FROM protocol_messages').get()?.count),
399
+ }),
400
+ queryPlans: () => {
401
+ const plan = (sql, ...params) => database
402
+ .prepare(`EXPLAIN QUERY PLAN ${sql}`)
403
+ .all(...params)
404
+ .map(row => String(row.detail))
405
+ .join(' | ');
406
+ return {
407
+ dequeueHead: plan(SQL.head, 's'),
408
+ listPending: plan(SQL.pending, 's'),
409
+ readMessages: plan(SQL.history, 's', 0),
410
+ idempotencyLookup: plan(SQL.byKey, 's', 'k'),
411
+ };
412
+ },
413
+ };
414
+ inspections.set(handle, inspection);
415
+ registerMigrationTarget(handle, {
416
+ readOnly,
417
+ apply(component, migrations) {
418
+ ensureOpen();
419
+ rejectReentry('applyComponentMigrations');
420
+ try {
421
+ return migrateComponent(database, component, migrations, now);
422
+ }
423
+ catch (error) {
424
+ rethrowProtocolError(error);
425
+ }
426
+ },
427
+ });
428
+ return handle;
429
+ }
430
+ export function openProtocol(databasePath, options = {}) {
431
+ validateDatabasePath(databasePath);
432
+ checkParentDirectory(databasePath);
433
+ const readOnly = options.readOnly === true;
434
+ const busyTimeoutMs = normalizeBusyTimeout(options.busyTimeoutMs);
435
+ const now = normalizeNow(options.now);
436
+ let database;
437
+ try {
438
+ database = new DatabaseSync(databasePath, { readOnly });
439
+ }
440
+ catch (error) {
441
+ rethrowProtocolError(error);
442
+ }
443
+ try {
444
+ database.exec(`PRAGMA busy_timeout=${busyTimeoutMs}`);
445
+ if (Number(database.prepare('PRAGMA busy_timeout').get()?.timeout) !== busyTimeoutMs) {
446
+ fail('PROTOCOL_PRAGMA_UNSUPPORTED', 'busy_timeout did not read back the requested value');
447
+ }
448
+ const version = database.prepare('SELECT sqlite_version() AS version').get()?.version;
449
+ if (!isSupportedSqliteVersion(version)) {
450
+ fail('PROTOCOL_SQLITE_VERSION_UNSUPPORTED', `SQLite ${String(version)} is below ${MIN_SQLITE_VERSION}`);
451
+ }
452
+ database.exec('PRAGMA foreign_keys=ON');
453
+ if (Number(database.prepare('PRAGMA foreign_keys').get()?.foreign_keys) !== 1) {
454
+ fail('PROTOCOL_PRAGMA_UNSUPPORTED', 'foreign_keys could not be enabled');
455
+ }
456
+ const mode = String(database.prepare('PRAGMA journal_mode').get()?.journal_mode);
457
+ if (mode !== JOURNAL_MODE) {
458
+ fail('PROTOCOL_JOURNAL_MODE_UNSUPPORTED', `journal_mode is ${mode}, not ${JOURNAL_MODE}; this version does not convert a database`);
459
+ }
460
+ retryWhileBusy(busyTimeoutMs, () => {
461
+ if (!readOnly) {
462
+ database.exec('PRAGMA synchronous=FULL');
463
+ if (Number(database.prepare('PRAGMA synchronous').get()?.synchronous) !== 2) {
464
+ fail('PROTOCOL_PRAGMA_UNSUPPORTED', 'synchronous could not be set to FULL');
465
+ }
466
+ }
467
+ migrateProtocol(database, readOnly, now);
468
+ });
469
+ }
470
+ catch (error) {
471
+ try {
472
+ database.close();
473
+ }
474
+ catch { }
475
+ rethrowProtocolError(error);
476
+ }
477
+ return buildHandle(database, { databasePath, readOnly, now });
478
+ }
@@ -0,0 +1,8 @@
1
+ export type ProtocolErrorCode = 'PROTOCOL_PATH_NOT_ABSOLUTE' | 'PROTOCOL_PATH_INVALID' | 'PROTOCOL_PATH_PARENT_MISSING' | 'PROTOCOL_SQLITE_VERSION_UNSUPPORTED' | 'PROTOCOL_JOURNAL_MODE_UNSUPPORTED' | 'PROTOCOL_PRAGMA_UNSUPPORTED' | 'PROTOCOL_SCHEMA_CHECKSUM_MISMATCH' | 'PROTOCOL_SCHEMA_GENERATION_UNSUPPORTED' | 'PROTOCOL_SCHEMA_REGISTRY_INCONSISTENT' | 'PROTOCOL_SESSION_ID_INVALID' | 'PROTOCOL_SESSION_UNKNOWN' | 'PROTOCOL_SESSION_RETIRED' | 'PROTOCOL_RETIRE_NON_EMPTY' | 'PROTOCOL_IDEMPOTENCY_CONFLICT' | 'PROTOCOL_MESSAGE_INVALID' | 'PROTOCOL_CURSOR_INVALID' | 'PROTOCOL_TRANSACTION_INVALID' | 'PROTOCOL_DATABASE_BUSY' | 'PROTOCOL_DATABASE_READONLY' | 'PROTOCOL_DATABASE_UNAVAILABLE' | 'PROTOCOL_DATABASE_CORRUPT' | 'PROTOCOL_SQLITE_ERROR';
2
+ export declare class ProtocolError extends Error {
3
+ readonly code: ProtocolErrorCode;
4
+ constructor(code: ProtocolErrorCode, message: string, cause?: unknown);
5
+ }
6
+ export declare function fail(code: ProtocolErrorCode, message: string, cause?: unknown): never;
7
+ export declare function classifySqliteError(error: unknown): ProtocolErrorCode | null;
8
+ export declare function rethrowProtocolError(error: unknown): never;
@@ -0,0 +1,39 @@
1
+ export class ProtocolError extends Error {
2
+ code;
3
+ constructor(code, message, cause) {
4
+ super(message, { cause });
5
+ this.name = 'ProtocolError';
6
+ this.code = code;
7
+ }
8
+ }
9
+ export function fail(code, message, cause) {
10
+ throw new ProtocolError(code, message, cause);
11
+ }
12
+ export function classifySqliteError(error) {
13
+ const value = error;
14
+ const code = String(value?.code ?? '');
15
+ if (code === 'SQLITE_BUSY' || code === 'SQLITE_LOCKED')
16
+ return 'PROTOCOL_DATABASE_BUSY';
17
+ if (code === 'SQLITE_READONLY')
18
+ return 'PROTOCOL_DATABASE_READONLY';
19
+ if (code === 'SQLITE_CORRUPT' || code === 'SQLITE_NOTADB')
20
+ return 'PROTOCOL_DATABASE_CORRUPT';
21
+ if (code === 'SQLITE_CANTOPEN')
22
+ return 'PROTOCOL_DATABASE_UNAVAILABLE';
23
+ const text = String(value?.message ?? error);
24
+ if (/database is locked|database table is locked|SQLITE_BUSY/i.test(text))
25
+ return 'PROTOCOL_DATABASE_BUSY';
26
+ if (/readonly|read-only|attempt to write/i.test(text))
27
+ return 'PROTOCOL_DATABASE_READONLY';
28
+ if (/malformed|not a database|disk image|corrupt/i.test(text))
29
+ return 'PROTOCOL_DATABASE_CORRUPT';
30
+ if (/unable to open database/i.test(text))
31
+ return 'PROTOCOL_DATABASE_UNAVAILABLE';
32
+ return null;
33
+ }
34
+ export function rethrowProtocolError(error) {
35
+ if (error instanceof ProtocolError)
36
+ throw error;
37
+ const message = String(error?.message ?? error);
38
+ fail(classifySqliteError(error) ?? 'PROTOCOL_SQLITE_ERROR', message, error);
39
+ }
@@ -0,0 +1,60 @@
1
+ export type SqlParam = null | number | bigint | string | Uint8Array;
2
+ export interface MessageInput {
3
+ kind: string;
4
+ body: Uint8Array;
5
+ headers?: Record<string, string>;
6
+ senderSessionId?: string | null;
7
+ idempotencyKey?: string | null;
8
+ protocolVersion?: number;
9
+ }
10
+ export interface Message {
11
+ enqueueSeq: number;
12
+ messageId: string;
13
+ targetSessionId: string;
14
+ senderSessionId: string | null;
15
+ protocolVersion: number;
16
+ kind: string;
17
+ body: Uint8Array;
18
+ headers: Record<string, string>;
19
+ idempotencyKey: string | null;
20
+ payloadHash: string;
21
+ enqueuedAtMs: number;
22
+ dequeuedAtMs: number | null;
23
+ }
24
+ export interface SessionAddress {
25
+ sessionId: string;
26
+ createdAtMs: number;
27
+ retiredAtMs: number | null;
28
+ }
29
+ export interface ProtocolTransaction {
30
+ exec(sql: string, ...params: SqlParam[]): {
31
+ changes: number;
32
+ lastInsertRowid: number;
33
+ };
34
+ query(sql: string, ...params: SqlParam[]): Record<string, unknown>[];
35
+ enqueue(sessionId: string, message: MessageInput): Message;
36
+ }
37
+ export interface SessionProtocol {
38
+ readonly databasePath: string;
39
+ readonly readOnly: boolean;
40
+ initialize(sessionId: string): SessionAddress;
41
+ enqueue(sessionId: string, message: MessageInput): Message;
42
+ dequeue(sessionId: string): Message | null;
43
+ listPending(sessionId: string): Message[];
44
+ readMessages(sessionId: string, afterSequence?: number): Message[];
45
+ retire(sessionId: string): SessionAddress;
46
+ withTransaction<T>(body: (tx: ProtocolTransaction) => T): T;
47
+ dataVersion(): number;
48
+ close(): void;
49
+ }
50
+ export interface OpenOptions {
51
+ readOnly?: boolean;
52
+ busyTimeoutMs?: number;
53
+ now?: () => number;
54
+ }
55
+ export { canonicalPreimage, LIMITS, payloadHash, PROTOCOL_VERSION } from './canonical.js';
56
+ export { ProtocolError } from './errors.js';
57
+ export type { ProtocolErrorCode } from './errors.js';
58
+ export { openProtocol } from './engine.js';
59
+ export { applyComponentMigrations, MIN_SQLITE_VERSION } from './schema.js';
60
+ export type { ComponentMigration } from './schema.js';
@@ -0,0 +1,4 @@
1
+ export { canonicalPreimage, LIMITS, payloadHash, PROTOCOL_VERSION } from './canonical.js';
2
+ export { ProtocolError } from './errors.js';
3
+ export { openProtocol } from './engine.js';
4
+ export { applyComponentMigrations, MIN_SQLITE_VERSION } from './schema.js';
@@ -0,0 +1,36 @@
1
+ import type { SessionProtocol, SqlParam } from './index.js';
2
+ export interface ComponentMigration {
3
+ readonly version: number;
4
+ readonly sql: string;
5
+ }
6
+ interface StatementResult {
7
+ changes: number | bigint;
8
+ lastInsertRowid: number | bigint;
9
+ }
10
+ export interface StatementLike {
11
+ get(...params: SqlParam[]): Record<string, unknown> | undefined;
12
+ all(...params: SqlParam[]): Record<string, unknown>[];
13
+ run(...params: SqlParam[]): StatementResult;
14
+ }
15
+ export interface DatabaseLike {
16
+ exec(sql: string): void;
17
+ prepare(sql: string): StatementLike;
18
+ close(): void;
19
+ }
20
+ interface PreparedMigration extends ComponentMigration {
21
+ checksum: string;
22
+ }
23
+ interface MigrationTarget {
24
+ readonly readOnly: boolean;
25
+ apply(component: string, migrations: readonly ComponentMigration[]): number;
26
+ }
27
+ export declare const MIN_SQLITE_VERSION = "3.38.0";
28
+ export declare const PROTOCOL_COMPONENT = "session-protocol";
29
+ export declare const PROTOCOL_MIGRATION_SQL = "\nCREATE TABLE protocol_sessions (\n session_id TEXT NOT NULL PRIMARY KEY,\n created_at_ms INTEGER NOT NULL,\n retired_at_ms INTEGER,\n CHECK (length(session_id) BETWEEN 1 AND 256),\n CHECK (session_id NOT GLOB '*[^0-9A-Za-z_-]*'),\n CHECK (session_id NOT GLOB '-*'),\n CHECK (created_at_ms >= 0),\n CHECK (retired_at_ms IS NULL OR retired_at_ms >= 0)\n) STRICT;\n\nCREATE TABLE protocol_messages (\n enqueue_seq INTEGER PRIMARY KEY AUTOINCREMENT,\n message_id TEXT NOT NULL UNIQUE,\n target_session_id TEXT NOT NULL REFERENCES protocol_sessions(session_id),\n sender_session_id TEXT,\n protocol_version INTEGER NOT NULL,\n kind TEXT NOT NULL,\n body BLOB NOT NULL,\n headers_json TEXT NOT NULL,\n idempotency_key TEXT,\n payload_hash BLOB NOT NULL,\n enqueued_at_ms INTEGER NOT NULL,\n dequeued_at_ms INTEGER,\n CHECK (message_id GLOB '[0-9a-f]*' AND length(message_id) = 32),\n CHECK (protocol_version >= 1),\n CHECK (length(kind) BETWEEN 1 AND 64),\n CHECK (kind NOT GLOB '*[^0-9A-Za-z._-]*'),\n CHECK (length(body) <= 1048576),\n CHECK (json_valid(headers_json) AND json_type(headers_json) = 'object'),\n CHECK (length(CAST(headers_json AS BLOB)) <= 65536),\n CHECK (idempotency_key IS NULL OR length(idempotency_key) BETWEEN 1 AND 256),\n CHECK (length(payload_hash) = 32),\n CHECK (enqueued_at_ms >= 0),\n CHECK (dequeued_at_ms IS NULL OR dequeued_at_ms >= 0),\n CHECK (sender_session_id IS NULL OR length(sender_session_id) BETWEEN 1 AND 256)\n) STRICT;\n\nCREATE UNIQUE INDEX protocol_messages_idempotency\n ON protocol_messages (target_session_id, idempotency_key)\n WHERE idempotency_key IS NOT NULL;\n\nCREATE INDEX protocol_messages_pending_fifo\n ON protocol_messages (target_session_id, enqueue_seq)\n WHERE dequeued_at_ms IS NULL;\n\nCREATE INDEX protocol_messages_history\n ON protocol_messages (target_session_id, enqueue_seq);\n";
30
+ export declare const REGISTRY_SQL = "\nCREATE TABLE IF NOT EXISTS schema_migrations (\n component TEXT NOT NULL,\n version INTEGER NOT NULL,\n checksum TEXT NOT NULL,\n applied_at_ms INTEGER NOT NULL,\n PRIMARY KEY (component, version)\n) STRICT;\n";
31
+ export declare const PROTOCOL_MIGRATIONS: readonly PreparedMigration[];
32
+ export declare function registerMigrationTarget(protocol: SessionProtocol, target: MigrationTarget): void;
33
+ export declare function migrateProtocol(database: DatabaseLike, readOnly: boolean, now: () => number): number;
34
+ export declare function migrateComponent(database: DatabaseLike, component: string, migrations: readonly ComponentMigration[], now: () => number): number;
35
+ export declare function applyComponentMigrations(protocol: SessionProtocol, component: string, migrations: readonly ComponentMigration[]): number;
36
+ export {};