spexcode 0.6.7 → 0.7.0-next.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 (247) 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 +31 -0
  42. package/node_modules/@spexcode/session-selflaunch/dist/locality.js +135 -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 +175 -21
  54. package/node_modules/@spexcode/spec-cli/dist/cli.js +121 -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/file-write.js +14 -2
  63. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +2 -1
  64. package/node_modules/@spexcode/spec-cli/dist/gateway.js +6 -3
  65. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +32 -2
  66. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +57 -2
  67. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +2 -0
  68. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +83 -3
  69. package/node_modules/@spexcode/spec-cli/dist/guide.js +29 -18
  70. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +16 -3
  71. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +24 -6
  72. package/node_modules/@spexcode/spec-cli/dist/harness.js +510 -72
  73. package/node_modules/@spexcode/spec-cli/dist/help.js +11 -8
  74. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +8 -0
  75. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +29 -8
  76. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +7 -0
  77. package/node_modules/@spexcode/spec-cli/dist/host.js +98 -1
  78. package/node_modules/@spexcode/spec-cli/dist/index.js +294 -36
  79. package/node_modules/@spexcode/spec-cli/dist/init.js +1 -1
  80. package/node_modules/@spexcode/spec-cli/dist/lint.js +70 -35
  81. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +3 -2
  82. package/node_modules/@spexcode/spec-cli/dist/listen.js +14 -2
  83. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +2 -1
  84. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +46 -15
  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/opencode.js +10 -2
  88. package/node_modules/@spexcode/spec-cli/dist/pi-harness.js +18 -5
  89. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +2 -0
  90. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +27 -0
  91. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +14 -14
  92. package/node_modules/@spexcode/spec-cli/dist/reviews.js +12 -7
  93. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +11 -0
  94. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +79 -1
  95. package/node_modules/@spexcode/spec-cli/dist/session-application.d.ts +23 -0
  96. package/node_modules/@spexcode/spec-cli/dist/session-application.js +189 -0
  97. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +13 -1
  98. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +6 -0
  99. package/node_modules/@spexcode/spec-cli/dist/session-files.js +13 -1
  100. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +39 -22
  101. package/node_modules/@spexcode/{session-core/dist/record-lock.d.ts → spec-cli/dist/session-record-lock.d.ts} +0 -4
  102. package/node_modules/@spexcode/spec-cli/dist/session-record-lock.js +94 -0
  103. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.d.ts +44 -0
  104. package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.js +37 -0
  105. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +25 -2
  106. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +68 -11
  107. package/node_modules/@spexcode/spec-cli/dist/session-transcript.d.ts +57 -0
  108. package/node_modules/@spexcode/spec-cli/dist/session-transcript.js +137 -0
  109. package/node_modules/@spexcode/spec-cli/dist/session-web.js +4 -4
  110. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +112 -15
  111. package/node_modules/@spexcode/spec-cli/dist/sessions.js +1553 -764
  112. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.js +14 -0
  113. package/node_modules/@spexcode/spec-cli/dist/source-list.d.ts +13 -0
  114. package/node_modules/@spexcode/spec-cli/dist/source-list.js +99 -0
  115. package/node_modules/@spexcode/spec-cli/dist/source-read.d.ts +16 -0
  116. package/node_modules/@spexcode/spec-cli/dist/source-read.js +84 -0
  117. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.d.ts +7 -0
  118. package/node_modules/@spexcode/spec-cli/dist/spec-attachments.js +89 -0
  119. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.d.ts +23 -0
  120. package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.js +138 -0
  121. package/node_modules/@spexcode/spec-cli/dist/supervise.js +15 -6
  122. package/node_modules/@spexcode/spec-cli/hooks/dispatch.sh +19 -31
  123. package/node_modules/@spexcode/spec-cli/hooks/harness.sh +6 -6
  124. package/node_modules/@spexcode/spec-cli/package.json +8 -6
  125. package/node_modules/@spexcode/spec-cli/templates/hooks/post-checkout +4 -2
  126. package/node_modules/@spexcode/spec-cli/templates/hooks/post-merge +2 -1
  127. package/node_modules/@spexcode/spec-cli/templates/hooks/pre-commit +5 -3
  128. package/node_modules/@spexcode/spec-cli/templates/hooks/reference-transaction +5 -3
  129. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/spec.md +2 -7
  130. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/idle.sh +4 -10
  131. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/spec.md +1 -1
  132. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/mark-active.sh +22 -24
  133. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/spec.md +10 -2
  134. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/fail.sh +8 -7
  135. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/spec.md +3 -1
  136. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/session-listen.sh +133 -0
  137. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/spec.md +36 -0
  138. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +2 -0
  139. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/spec.md +1 -1
  140. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +17 -20
  141. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/merge/spec.md +33 -0
  142. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/spec.md +2 -6
  143. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/spec.md +7 -0
  144. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +13 -2
  145. package/node_modules/@spexcode/spec-core/dist/anchors.js +311 -15
  146. package/node_modules/@spexcode/spec-core/dist/git.d.ts +4 -0
  147. package/node_modules/@spexcode/spec-core/dist/git.js +9 -9
  148. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +12 -1
  149. package/node_modules/@spexcode/spec-core/dist/layout.js +4 -2
  150. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +1 -0
  151. package/node_modules/@spexcode/spec-core/dist/resilience.js +8 -0
  152. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +1 -0
  153. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +7 -3
  154. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +1 -0
  155. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +4 -0
  156. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +2 -0
  157. package/node_modules/@spexcode/spec-core/dist/specs.js +30 -0
  158. package/node_modules/@spexcode/spec-core/package.json +4 -1
  159. package/node_modules/@spexcode/spec-core/templates/spexcode.json +36 -13
  160. package/node_modules/@spexcode/spec-eval/dist/cli.js +1 -1
  161. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +1 -0
  162. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +6 -1
  163. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +1 -1
  164. package/node_modules/@spexcode/spec-eval/dist/freshness.js +12 -12
  165. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +2 -1
  166. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +2 -1
  167. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +20 -3
  168. package/node_modules/@spexcode/spec-eval/package.json +2 -2
  169. package/node_modules/@spexcode/spec-forge/package.json +2 -2
  170. package/node_modules/@spexcode/transcript/dist/frames.d.ts +44 -0
  171. package/node_modules/@spexcode/transcript/dist/frames.js +85 -0
  172. package/node_modules/@spexcode/transcript/dist/index.d.ts +5 -0
  173. package/node_modules/@spexcode/transcript/dist/index.js +5 -0
  174. package/node_modules/@spexcode/transcript/dist/live.d.ts +16 -0
  175. package/node_modules/@spexcode/transcript/dist/live.js +65 -0
  176. package/node_modules/@spexcode/transcript/dist/parsers.d.ts +47 -0
  177. package/node_modules/@spexcode/transcript/dist/parsers.js +285 -0
  178. package/node_modules/@spexcode/transcript/dist/readers.d.ts +10 -0
  179. package/node_modules/@spexcode/transcript/dist/readers.js +296 -0
  180. package/node_modules/@spexcode/transcript/dist/turns.d.ts +42 -0
  181. package/node_modules/@spexcode/transcript/dist/turns.js +11 -0
  182. package/node_modules/@spexcode/transcript/package.json +30 -0
  183. package/node_modules/@vscode/tree-sitter-wasm/LICENSE +21 -0
  184. package/node_modules/@vscode/tree-sitter-wasm/README.md +36 -0
  185. package/node_modules/@vscode/tree-sitter-wasm/SECURITY.md +41 -0
  186. package/node_modules/@vscode/tree-sitter-wasm/cgmanifest.json +16 -0
  187. package/node_modules/@vscode/tree-sitter-wasm/package.json +42 -0
  188. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-bash.wasm +0 -0
  189. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-c-sharp.wasm +0 -0
  190. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-cpp.wasm +0 -0
  191. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm +0 -0
  192. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-go.wasm +0 -0
  193. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +0 -0
  194. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-java.wasm +0 -0
  195. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-javascript.wasm +0 -0
  196. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-php.wasm +0 -0
  197. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-powershell.wasm +0 -0
  198. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-python.wasm +0 -0
  199. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +0 -0
  200. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ruby.wasm +0 -0
  201. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-rust.wasm +0 -0
  202. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-tsx.wasm +0 -0
  203. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +0 -0
  204. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.js +4075 -0
  205. package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.wasm +0 -0
  206. package/node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts +1027 -0
  207. package/node_modules/smol-toml/LICENSE +24 -0
  208. package/node_modules/smol-toml/README.md +418 -0
  209. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  210. package/node_modules/smol-toml/dist/date.js +127 -0
  211. package/node_modules/smol-toml/dist/error.d.ts +38 -0
  212. package/node_modules/smol-toml/dist/error.js +63 -0
  213. package/node_modules/smol-toml/dist/extract.js +69 -0
  214. package/node_modules/smol-toml/dist/index.cjs +734 -0
  215. package/node_modules/smol-toml/dist/index.d.ts +43 -0
  216. package/node_modules/smol-toml/dist/index.js +33 -0
  217. package/node_modules/smol-toml/dist/parse.d.ts +36 -0
  218. package/node_modules/smol-toml/dist/parse.js +149 -0
  219. package/node_modules/smol-toml/dist/primitive.js +238 -0
  220. package/node_modules/smol-toml/dist/stringify.d.ts +31 -0
  221. package/node_modules/smol-toml/dist/stringify.js +181 -0
  222. package/node_modules/smol-toml/dist/struct.js +179 -0
  223. package/node_modules/smol-toml/dist/util.d.ts +38 -0
  224. package/node_modules/smol-toml/dist/util.js +89 -0
  225. package/node_modules/smol-toml/package.json +68 -0
  226. package/package.json +9 -3
  227. package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +0 -24
  228. package/node_modules/@spexcode/session-core/dist/delivery-queue.js +0 -206
  229. package/node_modules/@spexcode/session-core/dist/index.d.ts +0 -5
  230. package/node_modules/@spexcode/session-core/dist/index.js +0 -5
  231. package/node_modules/@spexcode/session-core/dist/internal.d.ts +0 -3
  232. package/node_modules/@spexcode/session-core/dist/internal.js +0 -3
  233. package/node_modules/@spexcode/session-core/dist/message.d.ts +0 -22
  234. package/node_modules/@spexcode/session-core/dist/message.js +0 -53
  235. package/node_modules/@spexcode/session-core/dist/record-lock.js +0 -152
  236. package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +0 -62
  237. package/node_modules/@spexcode/session-core/dist/runtime-session.js +0 -326
  238. package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +0 -14
  239. package/node_modules/@spexcode/session-core/dist/session-cursors.js +0 -82
  240. package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +0 -47
  241. package/node_modules/@spexcode/session-core/dist/session-timeline.js +0 -216
  242. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +0 -26
  243. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +0 -475
  244. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +0 -10
  245. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +0 -70
  246. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -53
  247. package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-sed-v0.fixture +0 -46
@@ -0,0 +1,46 @@
1
+ import type { ProtocolTransaction, SessionProtocol } from '@spexcode/session-protocol';
2
+ export type JsonPrimitive = null | boolean | number | string;
3
+ export type JsonValue = JsonPrimitive | JsonValue[] | {
4
+ [key: string]: JsonValue;
5
+ };
6
+ export interface NewSessionEvent {
7
+ eventId: string;
8
+ type: string;
9
+ schemaVersion: number;
10
+ subjectSessionId: string;
11
+ payload: Uint8Array;
12
+ occurredAtMs: number;
13
+ ignorable?: boolean;
14
+ }
15
+ export interface SessionEvent {
16
+ eventId: string;
17
+ eventSeq: number;
18
+ type: string;
19
+ schemaVersion: number;
20
+ subjectSessionId: string;
21
+ payload: Uint8Array;
22
+ occurredAtMs: number;
23
+ ignorable: boolean;
24
+ }
25
+ export interface EventReadOptions {
26
+ afterSequence?: number;
27
+ atSequence?: number;
28
+ }
29
+ export type SessionEventReducer<State> = (state: State, event: SessionEvent) => State;
30
+ export type SessionEventReducers<State> = Readonly<Record<string, SessionEventReducer<State>>>;
31
+ export interface ReplayOptions<State> extends EventReadOptions {
32
+ initialState: State;
33
+ reducers: SessionEventReducers<State>;
34
+ }
35
+ export interface SessionEventStore {
36
+ append(tx: ProtocolTransaction, input: NewSessionEvent): SessionEvent;
37
+ hasMessageEvent(tx: ProtocolTransaction, subjectSessionId: string, messageId: string): boolean;
38
+ read(subjectSessionId: string, options?: EventReadOptions, tx?: ProtocolTransaction): readonly SessionEvent[];
39
+ replay<State>(subjectSessionId: string, options: ReplayOptions<State>, tx?: ProtocolTransaction): State;
40
+ }
41
+ export declare function encodeEventJson(value: unknown): Uint8Array;
42
+ export declare function decodeEventJson(payload: Uint8Array): JsonValue;
43
+ export declare function openSessionEvents(protocol: SessionProtocol): SessionEventStore;
44
+ export { SessionEventError } from './errors.js';
45
+ export type { SessionEventErrorCode } from './errors.js';
46
+ export { SESSION_EVENTS_MIGRATION_SQL, SESSION_EVENTS_MIGRATIONS } from './schema.js';
@@ -0,0 +1,245 @@
1
+ import { applyComponentMigrations } from '@spexcode/session-protocol';
2
+ import { failSessionEvent, SessionEventError } from './errors.js';
3
+ import { SESSION_EVENTS_MIGRATIONS } from './schema.js';
4
+ const EVENT_ID = /^[0-9a-f]{32}$/;
5
+ const EVENT_TYPE = /^[0-9A-Za-z._:-]{1,128}$/;
6
+ const SESSION_ID = /^(?!-)[0-9A-Za-z_-]{1,256}$/;
7
+ const MAX_PAYLOAD_BYTES = 1_048_576;
8
+ const SELECT_COLUMNS = `subject_session_id, event_seq, event_id, event_type,
9
+ schema_version, ignorable, payload, occurred_at_ms`;
10
+ const requireTransaction = (tx) => {
11
+ if (!tx || typeof tx.exec !== 'function' || typeof tx.query !== 'function') {
12
+ failSessionEvent('EVENT_TRANSACTION_INVALID', 'a live protocol transaction context is required');
13
+ }
14
+ return tx;
15
+ };
16
+ const requireSessionId = (subjectSessionId) => {
17
+ if (typeof subjectSessionId !== 'string' || !SESSION_ID.test(subjectSessionId)) {
18
+ failSessionEvent('EVENT_SESSION_ID_INVALID', 'subject session id has an invalid grammar');
19
+ }
20
+ };
21
+ const requireSequence = (value, name, defaultValue) => {
22
+ const resolved = value ?? defaultValue;
23
+ if (!Number.isSafeInteger(resolved) || resolved < 0) {
24
+ failSessionEvent('EVENT_SEQUENCE_INVALID', `${name} must be a non-negative safe integer`);
25
+ }
26
+ return resolved;
27
+ };
28
+ const requireEnvelope = (input) => {
29
+ if (!input || typeof input !== 'object') {
30
+ failSessionEvent('EVENT_PAYLOAD_INVALID', 'event input must be an object');
31
+ }
32
+ if (typeof input.eventId !== 'string' || !EVENT_ID.test(input.eventId)) {
33
+ failSessionEvent('EVENT_ID_INVALID', 'event id must be exactly 32 lowercase hexadecimal characters');
34
+ }
35
+ if (typeof input.type !== 'string' || !EVENT_TYPE.test(input.type)) {
36
+ failSessionEvent('EVENT_TYPE_INVALID', 'event type has an invalid grammar');
37
+ }
38
+ if (!Number.isSafeInteger(input.schemaVersion) || input.schemaVersion < 1) {
39
+ failSessionEvent('EVENT_SCHEMA_VERSION_INVALID', 'schema version must be a positive safe integer');
40
+ }
41
+ requireSessionId(input.subjectSessionId);
42
+ if (!(input.payload instanceof Uint8Array) || input.payload.byteLength > MAX_PAYLOAD_BYTES) {
43
+ failSessionEvent('EVENT_PAYLOAD_INVALID', `payload must be Uint8Array at most ${MAX_PAYLOAD_BYTES} bytes`);
44
+ }
45
+ if (!Number.isSafeInteger(input.occurredAtMs) || input.occurredAtMs < 0) {
46
+ failSessionEvent('EVENT_TIMESTAMP_INVALID', 'occurredAtMs must be a non-negative safe integer');
47
+ }
48
+ if (input.ignorable !== undefined && typeof input.ignorable !== 'boolean') {
49
+ failSessionEvent('EVENT_PAYLOAD_INVALID', 'ignorable must be a boolean when present');
50
+ }
51
+ return new Uint8Array(input.payload);
52
+ };
53
+ const eventFromRow = (row) => Object.freeze({
54
+ eventId: String(row.event_id),
55
+ eventSeq: Number(row.event_seq),
56
+ type: String(row.event_type),
57
+ schemaVersion: Number(row.schema_version),
58
+ subjectSessionId: String(row.subject_session_id),
59
+ payload: new Uint8Array(row.payload),
60
+ occurredAtMs: Number(row.occurred_at_ms),
61
+ ignorable: Number(row.ignorable) === 1,
62
+ });
63
+ const snapshotJson = (value, path, active) => {
64
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
65
+ return value;
66
+ if (typeof value === 'number') {
67
+ if (!Number.isFinite(value) || Object.is(value, -0)) {
68
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains a non-lossless JSON number`);
69
+ }
70
+ return value;
71
+ }
72
+ if (typeof value !== 'object') {
73
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains a non-JSON value`);
74
+ }
75
+ if (active.has(value))
76
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains a cycle`);
77
+ active.add(value);
78
+ try {
79
+ if (Array.isArray(value)) {
80
+ const keys = Reflect.ownKeys(value);
81
+ if (keys.some(key => key !== 'length' && (typeof key !== 'string' || !/^(0|[1-9][0-9]*)$/.test(key)))) {
82
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains non-index array properties`);
83
+ }
84
+ const result = [];
85
+ for (let index = 0; index < value.length; index++) {
86
+ if (!Object.prototype.hasOwnProperty.call(value, index)) {
87
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains a sparse array`);
88
+ }
89
+ result.push(snapshotJson(value[index], `${path}[${index}]`, active));
90
+ }
91
+ return result;
92
+ }
93
+ const prototype = Object.getPrototypeOf(value);
94
+ if (prototype !== Object.prototype && prototype !== null) {
95
+ failSessionEvent('EVENT_JSON_INVALID', `${path} must contain only plain JSON objects`);
96
+ }
97
+ const descriptors = Object.getOwnPropertyDescriptors(value);
98
+ const symbols = Object.getOwnPropertySymbols(value);
99
+ if (symbols.length > 0)
100
+ failSessionEvent('EVENT_JSON_INVALID', `${path} contains symbol keys`);
101
+ const result = {};
102
+ for (const key of Object.keys(descriptors).sort()) {
103
+ const descriptor = descriptors[key];
104
+ if (!descriptor.enumerable || !('value' in descriptor)) {
105
+ failSessionEvent('EVENT_JSON_INVALID', `${path}.${key} is not an enumerable data property`);
106
+ }
107
+ result[key] = snapshotJson(descriptor.value, `${path}.${key}`, active);
108
+ }
109
+ return result;
110
+ }
111
+ finally {
112
+ active.delete(value);
113
+ }
114
+ };
115
+ export function encodeEventJson(value) {
116
+ const snapshot = snapshotJson(value, '$', new Set());
117
+ return new TextEncoder().encode(JSON.stringify(snapshot));
118
+ }
119
+ export function decodeEventJson(payload) {
120
+ if (!(payload instanceof Uint8Array)) {
121
+ failSessionEvent('EVENT_JSON_INVALID', 'JSON payload must be Uint8Array');
122
+ }
123
+ let text;
124
+ try {
125
+ text = new TextDecoder('utf-8', { fatal: true }).decode(payload);
126
+ }
127
+ catch (error) {
128
+ failSessionEvent('EVENT_JSON_INVALID', 'JSON payload is not valid UTF-8', error);
129
+ }
130
+ try {
131
+ return snapshotJson(JSON.parse(text), '$', new Set());
132
+ }
133
+ catch (error) {
134
+ if (error instanceof SessionEventError)
135
+ throw error;
136
+ failSessionEvent('EVENT_JSON_INVALID', 'payload is not valid JSON', error);
137
+ }
138
+ }
139
+ export function openSessionEvents(protocol) {
140
+ try {
141
+ applyComponentMigrations(protocol, 'session-events', SESSION_EVENTS_MIGRATIONS);
142
+ }
143
+ catch (error) {
144
+ failSessionEvent('EVENT_STORAGE', 'session event component migration failed', error);
145
+ }
146
+ const requireSubject = (tx, subjectSessionId) => {
147
+ const rows = tx.query('SELECT 1 AS present FROM protocol_sessions WHERE session_id=?', subjectSessionId);
148
+ if (rows.length === 0) {
149
+ failSessionEvent('EVENT_SESSION_UNKNOWN', `unknown protocol session: ${subjectSessionId}`);
150
+ }
151
+ };
152
+ const append = (txInput, input) => {
153
+ const tx = requireTransaction(txInput);
154
+ const payload = requireEnvelope(input);
155
+ requireSubject(tx, input.subjectSessionId);
156
+ if (tx.query('SELECT 1 AS present FROM session_events WHERE event_id=?', input.eventId).length > 0) {
157
+ failSessionEvent('EVENT_ID_EXISTS', `event id already exists: ${input.eventId}`);
158
+ }
159
+ const nextRows = tx.query('SELECT COALESCE(MAX(event_seq), 0) + 1 AS next_seq FROM session_events WHERE subject_session_id=?', input.subjectSessionId);
160
+ const eventSeq = Number(nextRows[0]?.next_seq);
161
+ if (!Number.isSafeInteger(eventSeq) || eventSeq < 1) {
162
+ failSessionEvent('EVENT_SEQUENCE_INVALID', 'next event sequence is not a positive safe integer');
163
+ }
164
+ try {
165
+ tx.exec(`INSERT INTO session_events
166
+ (subject_session_id,event_seq,event_id,event_type,schema_version,ignorable,payload,occurred_at_ms)
167
+ VALUES(?,?,?,?,?,?,?,?)`, input.subjectSessionId, eventSeq, input.eventId, input.type, input.schemaVersion, input.ignorable === true ? 1 : 0, payload, input.occurredAtMs);
168
+ }
169
+ catch (error) {
170
+ failSessionEvent('EVENT_STORAGE', 'event insert failed', error);
171
+ }
172
+ return Object.freeze({
173
+ eventId: input.eventId,
174
+ eventSeq,
175
+ type: input.type,
176
+ schemaVersion: input.schemaVersion,
177
+ subjectSessionId: input.subjectSessionId,
178
+ payload: new Uint8Array(payload),
179
+ occurredAtMs: input.occurredAtMs,
180
+ ignorable: input.ignorable === true,
181
+ });
182
+ };
183
+ const readInTransaction = (txInput, subjectSessionId, options) => {
184
+ const tx = requireTransaction(txInput);
185
+ requireSessionId(subjectSessionId);
186
+ const address = tx.query('SELECT 1 AS present FROM protocol_sessions WHERE session_id=?', subjectSessionId);
187
+ if (address.length === 0) {
188
+ failSessionEvent('EVENT_SESSION_UNKNOWN', `unknown protocol session: ${subjectSessionId}`);
189
+ }
190
+ const afterSequence = requireSequence(options.afterSequence, 'afterSequence', 0);
191
+ const atSequence = requireSequence(options.atSequence, 'atSequence', Number.MAX_SAFE_INTEGER);
192
+ if (atSequence < afterSequence) {
193
+ failSessionEvent('EVENT_SEQUENCE_INVALID', 'atSequence must not precede afterSequence');
194
+ }
195
+ const rows = tx.query(`SELECT ${SELECT_COLUMNS} FROM session_events
196
+ WHERE subject_session_id=? AND event_seq>? AND event_seq<=? ORDER BY event_seq`, subjectSessionId, afterSequence, atSequence);
197
+ const events = rows.map(eventFromRow);
198
+ for (let index = 0; index < events.length; index++) {
199
+ const expected = afterSequence + index + 1;
200
+ if (events[index].eventSeq !== expected) {
201
+ failSessionEvent('EVENT_SEQUENCE_INVALID', `subject ${subjectSessionId} has a sequence gap: expected ${expected}, found ${events[index].eventSeq}`);
202
+ }
203
+ }
204
+ return Object.freeze(events);
205
+ };
206
+ const hasMessageEvent = (txInput, subjectSessionId, messageId) => {
207
+ const tx = requireTransaction(txInput);
208
+ requireSubject(tx, subjectSessionId);
209
+ if (typeof messageId !== 'string' || messageId.length === 0)
210
+ return false;
211
+ // Message events are JSON envelopes. Keep the lookup in the event package so adopters do not scan and
212
+ // decode an entire session history merely to make a retry idempotent. json_valid makes malformed legacy
213
+ // payloads non-matching instead of turning a delivery check into a transaction failure.
214
+ return tx.query(`SELECT 1 AS present FROM session_events
215
+ WHERE subject_session_id=? AND event_type='session.message.sent.v1'
216
+ AND json_valid(CAST(payload AS TEXT))
217
+ AND json_extract(CAST(payload AS TEXT), '$.messageId')=? LIMIT 1`, subjectSessionId, messageId).length > 0;
218
+ };
219
+ const read = (subjectSessionId, options = {}, tx) => {
220
+ if (tx !== undefined)
221
+ return readInTransaction(tx, subjectSessionId, options);
222
+ return protocol.withTransaction(active => readInTransaction(active, subjectSessionId, options));
223
+ };
224
+ const replay = (subjectSessionId, options, tx) => {
225
+ if (!options || typeof options !== 'object' || !options.reducers || typeof options.reducers !== 'object') {
226
+ failSessionEvent('EVENT_TYPE_UNKNOWN', 'replay requires a reducer table');
227
+ }
228
+ let state = options.initialState;
229
+ for (const event of read(subjectSessionId, options, tx)) {
230
+ const reducer = Object.prototype.hasOwnProperty.call(options.reducers, event.type)
231
+ ? options.reducers[event.type]
232
+ : undefined;
233
+ if (typeof reducer !== 'function') {
234
+ if (event.ignorable)
235
+ continue;
236
+ failSessionEvent('EVENT_TYPE_UNKNOWN', `required event type has no reducer: ${event.type}`);
237
+ }
238
+ state = reducer(state, event);
239
+ }
240
+ return state;
241
+ };
242
+ return Object.freeze({ append, hasMessageEvent, read, replay });
243
+ }
244
+ export { SessionEventError } from './errors.js';
245
+ export { SESSION_EVENTS_MIGRATION_SQL, SESSION_EVENTS_MIGRATIONS } from './schema.js';
@@ -0,0 +1,3 @@
1
+ import type { ComponentMigration } from '@spexcode/session-protocol';
2
+ export declare const SESSION_EVENTS_MIGRATION_SQL = "\nCREATE TABLE session_events (\n subject_session_id TEXT NOT NULL REFERENCES protocol_sessions(session_id),\n event_seq INTEGER NOT NULL,\n event_id TEXT NOT NULL UNIQUE,\n event_type TEXT NOT NULL,\n schema_version INTEGER NOT NULL,\n ignorable INTEGER NOT NULL,\n payload BLOB NOT NULL,\n occurred_at_ms INTEGER NOT NULL,\n PRIMARY KEY (subject_session_id, event_seq),\n CHECK (event_seq >= 1),\n CHECK (length(event_id) = 32 AND event_id NOT GLOB '*[^0-9a-f]*'),\n CHECK (length(event_type) BETWEEN 1 AND 128),\n CHECK (event_type NOT GLOB '*[^0-9A-Za-z._:-]*'),\n CHECK (schema_version >= 1),\n CHECK (ignorable IN (0, 1)),\n CHECK (length(payload) <= 1048576),\n CHECK (occurred_at_ms >= 0)\n) STRICT;\n\nCREATE INDEX session_events_subject_history\n ON session_events (subject_session_id, event_seq);\n\nCREATE TRIGGER session_events_append_only_update\nBEFORE UPDATE ON session_events\nBEGIN\n SELECT RAISE(ABORT, 'session_events is append-only');\nEND;\n\nCREATE TRIGGER session_events_append_only_delete\nBEFORE DELETE ON session_events\nBEGIN\n SELECT RAISE(ABORT, 'session_events is append-only');\nEND;\n";
3
+ export declare const SESSION_EVENTS_MIGRATIONS: readonly ComponentMigration[];
@@ -0,0 +1,44 @@
1
+ export const SESSION_EVENTS_MIGRATION_SQL = `
2
+ CREATE TABLE session_events (
3
+ subject_session_id TEXT NOT NULL REFERENCES protocol_sessions(session_id),
4
+ event_seq INTEGER NOT NULL,
5
+ event_id TEXT NOT NULL UNIQUE,
6
+ event_type TEXT NOT NULL,
7
+ schema_version INTEGER NOT NULL,
8
+ ignorable INTEGER NOT NULL,
9
+ payload BLOB NOT NULL,
10
+ occurred_at_ms INTEGER NOT NULL,
11
+ PRIMARY KEY (subject_session_id, event_seq),
12
+ CHECK (event_seq >= 1),
13
+ CHECK (length(event_id) = 32 AND event_id NOT GLOB '*[^0-9a-f]*'),
14
+ CHECK (length(event_type) BETWEEN 1 AND 128),
15
+ CHECK (event_type NOT GLOB '*[^0-9A-Za-z._:-]*'),
16
+ CHECK (schema_version >= 1),
17
+ CHECK (ignorable IN (0, 1)),
18
+ CHECK (length(payload) <= 1048576),
19
+ CHECK (occurred_at_ms >= 0)
20
+ ) STRICT;
21
+
22
+ CREATE INDEX session_events_subject_history
23
+ ON session_events (subject_session_id, event_seq);
24
+
25
+ CREATE TRIGGER session_events_append_only_update
26
+ BEFORE UPDATE ON session_events
27
+ BEGIN
28
+ SELECT RAISE(ABORT, 'session_events is append-only');
29
+ END;
30
+
31
+ CREATE TRIGGER session_events_append_only_delete
32
+ BEFORE DELETE ON session_events
33
+ BEGIN
34
+ SELECT RAISE(ABORT, 'session_events is append-only');
35
+ END;
36
+ `;
37
+ export const SESSION_EVENTS_MIGRATIONS = [
38
+ { version: 1, sql: SESSION_EVENTS_MIGRATION_SQL },
39
+ { version: 2, sql: `
40
+ CREATE INDEX session_events_message_lookup
41
+ ON session_events (subject_session_id, event_type)
42
+ WHERE event_type = 'session.message.sent.v1';
43
+ ` },
44
+ ];
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@spexcode/session-events",
3
+ "version": "0.7.0-next.0",
4
+ "publishConfig": { "access": "public" },
5
+ "type": "module",
6
+ "description": "Append-only session facts and deterministic ordered replay inputs.",
7
+ "files": ["dist"],
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "engines": { "node": ">=22" },
13
+ "scripts": {
14
+ "build": "node ../../scripts/build-dist.mjs",
15
+ "prepublishOnly": "node ../../scripts/release-publish.mjs --from-package-publish",
16
+ "test": "npm run build && tsx --import ../../scripts/test-home.mjs --test src/*.test.ts"
17
+ },
18
+ "dependencies": { "@spexcode/session-protocol": "0.7.0-next.0" },
19
+ "devDependencies": {
20
+ "@types/node": "^20.16.0",
21
+ "tsx": "^4.19.2",
22
+ "typescript": "^5.6.3"
23
+ }
24
+ }
@@ -0,0 +1,36 @@
1
+ import type { MessageInput } from './index.js';
2
+ interface CanonicalMessage {
3
+ protocolVersion: number;
4
+ targetSessionId: string;
5
+ senderSessionId?: string | null;
6
+ kind: string;
7
+ headers?: Record<string, string>;
8
+ body: Uint8Array;
9
+ }
10
+ export interface NormalizedMessage {
11
+ protocolVersion: number;
12
+ targetSessionId: string;
13
+ senderSessionId: string | null;
14
+ kind: string;
15
+ headers: Record<string, string>;
16
+ headersJson: string;
17
+ body: Uint8Array;
18
+ idempotencyKey: string | null;
19
+ hash: Uint8Array;
20
+ }
21
+ export declare const PROTOCOL_VERSION = 1;
22
+ export declare const LIMITS: {
23
+ readonly sessionId: 256;
24
+ readonly messageKind: 64;
25
+ readonly idempotencyKey: 256;
26
+ readonly headerKey: 64;
27
+ readonly headerValueBytes: 4096;
28
+ readonly headerCount: 64;
29
+ readonly headersJsonBytes: 65536;
30
+ readonly bodyBytes: 1048576;
31
+ };
32
+ export declare function canonicalPreimage(message: CanonicalMessage): Uint8Array;
33
+ export declare function payloadHash(message: CanonicalMessage): Uint8Array;
34
+ export declare function requireSessionId(value: unknown, label?: string): string;
35
+ export declare function normalizeMessage(targetSessionId: string, input: MessageInput): NormalizedMessage;
36
+ export {};
@@ -0,0 +1,139 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { fail } from './errors.js';
3
+ export const PROTOCOL_VERSION = 1;
4
+ export const LIMITS = {
5
+ sessionId: 256,
6
+ messageKind: 64,
7
+ idempotencyKey: 256,
8
+ headerKey: 64,
9
+ headerValueBytes: 4096,
10
+ headerCount: 64,
11
+ headersJsonBytes: 65536,
12
+ bodyBytes: 1048576,
13
+ };
14
+ const SESSION_ID_RE = /^[0-9A-Za-z_][0-9A-Za-z_-]*$/;
15
+ const KIND_RE = /^[0-9A-Za-z._-]+$/;
16
+ const HEADER_KEY_RE = /^[0-9A-Za-z._-]+$/;
17
+ const u32 = (value) => {
18
+ const bytes = Buffer.alloc(4);
19
+ bytes.writeUInt32BE(value);
20
+ return bytes;
21
+ };
22
+ const u64 = (value) => {
23
+ const bytes = Buffer.alloc(8);
24
+ bytes.writeBigUInt64BE(BigInt(value));
25
+ return bytes;
26
+ };
27
+ const field = (value) => {
28
+ const bytes = Buffer.from(value, 'utf8');
29
+ return Buffer.concat([u32(bytes.length), bytes]);
30
+ };
31
+ const optionalField = (value) => value === null || value === undefined
32
+ ? Buffer.from([0])
33
+ : Buffer.concat([Buffer.from([1]), field(value)]);
34
+ const sortedHeaderKeys = (headers) => Object.keys(headers).sort((left, right) => Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8')));
35
+ export function canonicalPreimage(message) {
36
+ const headers = message.headers ?? {};
37
+ const keys = sortedHeaderKeys(headers);
38
+ const body = Buffer.from(message.body);
39
+ return Buffer.concat([
40
+ u32(message.protocolVersion),
41
+ field(message.targetSessionId),
42
+ optionalField(message.senderSessionId ?? null),
43
+ field(message.kind),
44
+ u32(keys.length),
45
+ ...keys.flatMap(key => [field(key), field(headers[key])]),
46
+ u64(body.length),
47
+ body,
48
+ ]);
49
+ }
50
+ export function payloadHash(message) {
51
+ return createHash('sha256').update(canonicalPreimage(message)).digest();
52
+ }
53
+ export function requireSessionId(value, label = 'sessionId') {
54
+ if (typeof value !== 'string')
55
+ fail('PROTOCOL_SESSION_ID_INVALID', `${label} must be a string`);
56
+ if (value.length < 1 || value.length > LIMITS.sessionId) {
57
+ fail('PROTOCOL_SESSION_ID_INVALID', `${label} must be 1..${LIMITS.sessionId} characters`);
58
+ }
59
+ if (!SESSION_ID_RE.test(value)) {
60
+ fail('PROTOCOL_SESSION_ID_INVALID', `${label} must match [0-9A-Za-z_][0-9A-Za-z_-]* (got ${JSON.stringify(value)})`);
61
+ }
62
+ return value;
63
+ }
64
+ export function normalizeMessage(targetSessionId, input) {
65
+ if (!input || typeof input !== 'object')
66
+ fail('PROTOCOL_MESSAGE_INVALID', 'message must be an object');
67
+ const record = input;
68
+ if ('messageId' in record) {
69
+ fail('PROTOCOL_MESSAGE_INVALID', 'messageId is generated by the protocol; producers must not supply one');
70
+ }
71
+ if ('targetSessionId' in record && record.targetSessionId !== targetSessionId) {
72
+ fail('PROTOCOL_MESSAGE_INVALID', 'targetSessionId must equal the enqueue address');
73
+ }
74
+ const protocolVersion = input.protocolVersion ?? PROTOCOL_VERSION;
75
+ if (!Number.isInteger(protocolVersion) || protocolVersion < 1 || protocolVersion > 0xffffffff) {
76
+ fail('PROTOCOL_MESSAGE_INVALID', 'protocolVersion must be a positive unsigned 32-bit integer');
77
+ }
78
+ const kind = input.kind;
79
+ if (typeof kind !== 'string'
80
+ || kind.length < 1
81
+ || kind.length > LIMITS.messageKind
82
+ || !KIND_RE.test(kind)) {
83
+ fail('PROTOCOL_MESSAGE_INVALID', `kind must match [0-9A-Za-z._-]{1,${LIMITS.messageKind}}`);
84
+ }
85
+ if (!(input.body instanceof Uint8Array)) {
86
+ fail('PROTOCOL_MESSAGE_INVALID', 'body must be a Uint8Array; encode strings before enqueue');
87
+ }
88
+ const body = Buffer.from(input.body);
89
+ if (body.length > LIMITS.bodyBytes) {
90
+ fail('PROTOCOL_MESSAGE_INVALID', `body exceeds ${LIMITS.bodyBytes} bytes`);
91
+ }
92
+ const senderSessionId = input.senderSessionId ?? null;
93
+ if (senderSessionId !== null)
94
+ requireSessionId(senderSessionId, 'senderSessionId');
95
+ const idempotencyKey = input.idempotencyKey ?? null;
96
+ if (idempotencyKey !== null
97
+ && (typeof idempotencyKey !== 'string'
98
+ || idempotencyKey.length < 1
99
+ || idempotencyKey.length > LIMITS.idempotencyKey)) {
100
+ fail('PROTOCOL_MESSAGE_INVALID', `idempotencyKey must be 1..${LIMITS.idempotencyKey} characters`);
101
+ }
102
+ const headersInput = input.headers ?? {};
103
+ const headerPrototype = typeof headersInput === 'object' && headersInput !== null
104
+ ? Object.getPrototypeOf(headersInput)
105
+ : undefined;
106
+ if (typeof headersInput !== 'object'
107
+ || headersInput === null
108
+ || Array.isArray(headersInput)
109
+ || (headerPrototype !== Object.prototype && headerPrototype !== null)) {
110
+ fail('PROTOCOL_MESSAGE_INVALID', 'headers must be a plain object');
111
+ }
112
+ const keys = Object.keys(headersInput);
113
+ if (keys.length > LIMITS.headerCount) {
114
+ fail('PROTOCOL_MESSAGE_INVALID', `at most ${LIMITS.headerCount} headers are allowed`);
115
+ }
116
+ for (const key of keys) {
117
+ if (key.length < 1 || key.length > LIMITS.headerKey || !HEADER_KEY_RE.test(key)) {
118
+ fail('PROTOCOL_MESSAGE_INVALID', `header key must match [0-9A-Za-z._-]{1,${LIMITS.headerKey}} (got ${JSON.stringify(key)})`);
119
+ }
120
+ const value = headersInput[key];
121
+ if (typeof value !== 'string')
122
+ fail('PROTOCOL_MESSAGE_INVALID', `header ${key} must be a string`);
123
+ if (Buffer.byteLength(value, 'utf8') > LIMITS.headerValueBytes) {
124
+ fail('PROTOCOL_MESSAGE_INVALID', `header ${key} exceeds ${LIMITS.headerValueBytes} bytes`);
125
+ }
126
+ }
127
+ const headers = Object.fromEntries(sortedHeaderKeys(headersInput).map(key => [key, headersInput[key]]));
128
+ const headersJson = JSON.stringify(headers);
129
+ if (Buffer.byteLength(headersJson, 'utf8') > LIMITS.headersJsonBytes) {
130
+ fail('PROTOCOL_MESSAGE_INVALID', `headers exceed ${LIMITS.headersJsonBytes} bytes`);
131
+ }
132
+ const envelope = { protocolVersion, targetSessionId, senderSessionId, kind, headers, body };
133
+ return {
134
+ ...envelope,
135
+ idempotencyKey,
136
+ headersJson,
137
+ hash: payloadHash(envelope),
138
+ };
139
+ }
@@ -0,0 +1,22 @@
1
+ import type { OpenOptions, SessionProtocol } from './index.js';
2
+ export interface ProtocolInspection {
3
+ pragmas(): {
4
+ journal_mode: string;
5
+ foreign_keys: number;
6
+ synchronous: number;
7
+ busy_timeout: number;
8
+ };
9
+ counts(): {
10
+ sessions: number;
11
+ messages: number;
12
+ };
13
+ queryPlans(): {
14
+ dequeueHead: string;
15
+ listPending: string;
16
+ readMessages: string;
17
+ idempotencyLookup: string;
18
+ };
19
+ }
20
+ export declare function inspectProtocol(protocol: SessionProtocol): ProtocolInspection;
21
+ export declare function isSupportedSqliteVersion(version: unknown): boolean;
22
+ export declare function openProtocol(databasePath: string, options?: OpenOptions): SessionProtocol;