@smthrs/journal 0.0.0-stage → 1.0.0-rc.3

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 (257) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/LICENSE +21 -0
  3. package/README.md +206 -2
  4. package/dist/cjs/CallFact.d.ts +129 -0
  5. package/dist/cjs/CallFact.d.ts.map +1 -0
  6. package/dist/cjs/CallFact.js +106 -0
  7. package/dist/cjs/CallFact.js.map +7 -0
  8. package/dist/cjs/Consensus.d.ts +440 -0
  9. package/dist/cjs/Consensus.d.ts.map +1 -0
  10. package/dist/cjs/Consensus.js +251 -0
  11. package/dist/cjs/Consensus.js.map +7 -0
  12. package/dist/cjs/EngineEvent.d.ts +890 -0
  13. package/dist/cjs/EngineEvent.d.ts.map +1 -0
  14. package/dist/cjs/EngineEvent.js +383 -0
  15. package/dist/cjs/EngineEvent.js.map +7 -0
  16. package/dist/cjs/ExecutionFact.d.ts +216 -0
  17. package/dist/cjs/ExecutionFact.d.ts.map +1 -0
  18. package/dist/cjs/ExecutionFact.js +303 -0
  19. package/dist/cjs/ExecutionFact.js.map +7 -0
  20. package/dist/cjs/Journal.d.ts +617 -0
  21. package/dist/cjs/Journal.d.ts.map +1 -0
  22. package/dist/cjs/Journal.js +190 -0
  23. package/dist/cjs/Journal.js.map +7 -0
  24. package/dist/cjs/JournalEvent.d.ts +421 -0
  25. package/dist/cjs/JournalEvent.d.ts.map +1 -0
  26. package/dist/cjs/JournalEvent.js +281 -0
  27. package/dist/cjs/JournalEvent.js.map +7 -0
  28. package/dist/cjs/JournalGeneration.d.ts +34 -0
  29. package/dist/cjs/JournalGeneration.d.ts.map +1 -0
  30. package/dist/cjs/JournalGeneration.js +72 -0
  31. package/dist/cjs/JournalGeneration.js.map +7 -0
  32. package/dist/cjs/JournalMetrics.d.ts +72 -0
  33. package/dist/cjs/JournalMetrics.d.ts.map +1 -0
  34. package/dist/cjs/JournalMetrics.js +55 -0
  35. package/dist/cjs/JournalMetrics.js.map +7 -0
  36. package/dist/cjs/Migrations.d.ts +42 -0
  37. package/dist/cjs/Migrations.d.ts.map +1 -0
  38. package/dist/cjs/Migrations.js +58 -0
  39. package/dist/cjs/Migrations.js.map +7 -0
  40. package/dist/cjs/OwnerId.d.ts +40 -0
  41. package/dist/cjs/OwnerId.d.ts.map +1 -0
  42. package/dist/cjs/OwnerId.js +40 -0
  43. package/dist/cjs/OwnerId.js.map +7 -0
  44. package/dist/cjs/Projection.d.ts +29 -0
  45. package/dist/cjs/Projection.d.ts.map +1 -0
  46. package/dist/cjs/Projection.js +25 -0
  47. package/dist/cjs/Projection.js.map +7 -0
  48. package/dist/cjs/RedactedLogger.d.ts +121 -0
  49. package/dist/cjs/RedactedLogger.d.ts.map +1 -0
  50. package/dist/cjs/RedactedLogger.js +235 -0
  51. package/dist/cjs/RedactedLogger.js.map +7 -0
  52. package/dist/cjs/Redaction.d.ts +350 -0
  53. package/dist/cjs/Redaction.d.ts.map +1 -0
  54. package/dist/cjs/Redaction.js +928 -0
  55. package/dist/cjs/Redaction.js.map +7 -0
  56. package/dist/cjs/SqlConsensus.d.ts +44 -0
  57. package/dist/cjs/SqlConsensus.d.ts.map +1 -0
  58. package/dist/cjs/SqlConsensus.js +243 -0
  59. package/dist/cjs/SqlConsensus.js.map +7 -0
  60. package/dist/cjs/SqlJournal.d.ts +173 -0
  61. package/dist/cjs/SqlJournal.d.ts.map +1 -0
  62. package/dist/cjs/SqlJournal.js +1568 -0
  63. package/dist/cjs/SqlJournal.js.map +7 -0
  64. package/dist/cjs/StepFact.d.ts +66 -0
  65. package/dist/cjs/StepFact.d.ts.map +1 -0
  66. package/dist/cjs/StepFact.js +68 -0
  67. package/dist/cjs/StepFact.js.map +7 -0
  68. package/dist/cjs/index.d.ts +95 -0
  69. package/dist/cjs/index.d.ts.map +1 -0
  70. package/dist/cjs/index.js +63 -0
  71. package/dist/cjs/index.js.map +7 -0
  72. package/dist/cjs/internal/JournalInternalFault.d.ts +37 -0
  73. package/dist/cjs/internal/JournalInternalFault.d.ts.map +1 -0
  74. package/dist/cjs/internal/JournalInternalFault.js +43 -0
  75. package/dist/cjs/internal/JournalInternalFault.js.map +7 -0
  76. package/dist/cjs/migrations/0001_initial.d.ts +15 -0
  77. package/dist/cjs/migrations/0001_initial.d.ts.map +1 -0
  78. package/dist/cjs/migrations/0001_initial.js +54 -0
  79. package/dist/cjs/migrations/0001_initial.js.map +7 -0
  80. package/dist/cjs/migrations/0002_checkpoints.d.ts +27 -0
  81. package/dist/cjs/migrations/0002_checkpoints.d.ts.map +1 -0
  82. package/dist/cjs/migrations/0002_checkpoints.js +48 -0
  83. package/dist/cjs/migrations/0002_checkpoints.js.map +7 -0
  84. package/dist/cjs/migrations/0003_startup_index.d.ts +15 -0
  85. package/dist/cjs/migrations/0003_startup_index.d.ts.map +1 -0
  86. package/dist/cjs/migrations/0003_startup_index.js +40 -0
  87. package/dist/cjs/migrations/0003_startup_index.js.map +7 -0
  88. package/dist/cjs/migrations/0004_dedup.d.ts +15 -0
  89. package/dist/cjs/migrations/0004_dedup.d.ts.map +1 -0
  90. package/dist/cjs/migrations/0004_dedup.js +57 -0
  91. package/dist/cjs/migrations/0004_dedup.js.map +7 -0
  92. package/dist/cjs/migrations/0005_run_event_type.d.ts +15 -0
  93. package/dist/cjs/migrations/0005_run_event_type.d.ts.map +1 -0
  94. package/dist/cjs/migrations/0005_run_event_type.js +40 -0
  95. package/dist/cjs/migrations/0005_run_event_type.js.map +7 -0
  96. package/dist/cjs/migrations/0006_consensus.d.ts +22 -0
  97. package/dist/cjs/migrations/0006_consensus.d.ts.map +1 -0
  98. package/dist/cjs/migrations/0006_consensus.js +84 -0
  99. package/dist/cjs/migrations/0006_consensus.js.map +7 -0
  100. package/dist/cjs/package.json +1 -0
  101. package/dist/cjs/test/Notifying.d.ts +65 -0
  102. package/dist/cjs/test/Notifying.d.ts.map +1 -0
  103. package/dist/cjs/test/Notifying.js +62 -0
  104. package/dist/cjs/test/Notifying.js.map +7 -0
  105. package/dist/cjs/test/TestJournal.d.ts +43 -0
  106. package/dist/cjs/test/TestJournal.d.ts.map +1 -0
  107. package/dist/cjs/test/TestJournal.js +43 -0
  108. package/dist/cjs/test/TestJournal.js.map +7 -0
  109. package/dist/esm/CallFact.d.ts +129 -0
  110. package/dist/esm/CallFact.d.ts.map +1 -0
  111. package/dist/esm/CallFact.js +96 -0
  112. package/dist/esm/CallFact.js.map +1 -0
  113. package/dist/esm/Consensus.d.ts +440 -0
  114. package/dist/esm/Consensus.d.ts.map +1 -0
  115. package/dist/esm/Consensus.js +454 -0
  116. package/dist/esm/Consensus.js.map +1 -0
  117. package/dist/esm/EngineEvent.d.ts +890 -0
  118. package/dist/esm/EngineEvent.d.ts.map +1 -0
  119. package/dist/esm/EngineEvent.js +614 -0
  120. package/dist/esm/EngineEvent.js.map +1 -0
  121. package/dist/esm/ExecutionFact.d.ts +216 -0
  122. package/dist/esm/ExecutionFact.d.ts.map +1 -0
  123. package/dist/esm/ExecutionFact.js +374 -0
  124. package/dist/esm/ExecutionFact.js.map +1 -0
  125. package/dist/esm/Journal.d.ts +617 -0
  126. package/dist/esm/Journal.d.ts.map +1 -0
  127. package/dist/esm/Journal.js +323 -0
  128. package/dist/esm/Journal.js.map +1 -0
  129. package/dist/esm/JournalEvent.d.ts +421 -0
  130. package/dist/esm/JournalEvent.d.ts.map +1 -0
  131. package/dist/esm/JournalEvent.js +517 -0
  132. package/dist/esm/JournalEvent.js.map +1 -0
  133. package/dist/esm/JournalGeneration.d.ts +34 -0
  134. package/dist/esm/JournalGeneration.d.ts.map +1 -0
  135. package/dist/esm/JournalGeneration.js +65 -0
  136. package/dist/esm/JournalGeneration.js.map +1 -0
  137. package/dist/esm/JournalMetrics.d.ts +72 -0
  138. package/dist/esm/JournalMetrics.d.ts.map +1 -0
  139. package/dist/esm/JournalMetrics.js +79 -0
  140. package/dist/esm/JournalMetrics.js.map +1 -0
  141. package/dist/esm/Migrations.d.ts +42 -0
  142. package/dist/esm/Migrations.d.ts.map +1 -0
  143. package/dist/esm/Migrations.js +59 -0
  144. package/dist/esm/Migrations.js.map +1 -0
  145. package/dist/esm/OwnerId.d.ts +40 -0
  146. package/dist/esm/OwnerId.d.ts.map +1 -0
  147. package/dist/esm/OwnerId.js +33 -0
  148. package/dist/esm/OwnerId.js.map +1 -0
  149. package/dist/esm/Projection.d.ts +29 -0
  150. package/dist/esm/Projection.d.ts.map +1 -0
  151. package/dist/esm/Projection.js +13 -0
  152. package/dist/esm/Projection.js.map +1 -0
  153. package/dist/esm/RedactedLogger.d.ts +121 -0
  154. package/dist/esm/RedactedLogger.d.ts.map +1 -0
  155. package/dist/esm/RedactedLogger.js +445 -0
  156. package/dist/esm/RedactedLogger.js.map +1 -0
  157. package/dist/esm/Redaction.d.ts +350 -0
  158. package/dist/esm/Redaction.d.ts.map +1 -0
  159. package/dist/esm/Redaction.js +1584 -0
  160. package/dist/esm/Redaction.js.map +1 -0
  161. package/dist/esm/SqlConsensus.d.ts +44 -0
  162. package/dist/esm/SqlConsensus.d.ts.map +1 -0
  163. package/dist/esm/SqlConsensus.js +252 -0
  164. package/dist/esm/SqlConsensus.js.map +1 -0
  165. package/dist/esm/SqlJournal.d.ts +173 -0
  166. package/dist/esm/SqlJournal.d.ts.map +1 -0
  167. package/dist/esm/SqlJournal.js +1858 -0
  168. package/dist/esm/SqlJournal.js.map +1 -0
  169. package/dist/esm/StepFact.d.ts +66 -0
  170. package/dist/esm/StepFact.d.ts.map +1 -0
  171. package/dist/esm/StepFact.js +50 -0
  172. package/dist/esm/StepFact.js.map +1 -0
  173. package/dist/esm/index.d.ts +95 -0
  174. package/dist/esm/index.d.ts.map +1 -0
  175. package/dist/esm/index.js +95 -0
  176. package/dist/esm/index.js.map +1 -0
  177. package/dist/esm/internal/JournalInternalFault.d.ts +37 -0
  178. package/dist/esm/internal/JournalInternalFault.d.ts.map +1 -0
  179. package/dist/esm/internal/JournalInternalFault.js +28 -0
  180. package/dist/esm/internal/JournalInternalFault.js.map +1 -0
  181. package/dist/esm/migrations/0001_initial.d.ts +15 -0
  182. package/dist/esm/migrations/0001_initial.d.ts.map +1 -0
  183. package/dist/esm/migrations/0001_initial.js +32 -0
  184. package/dist/esm/migrations/0001_initial.js.map +1 -0
  185. package/dist/esm/migrations/0002_checkpoints.d.ts +27 -0
  186. package/dist/esm/migrations/0002_checkpoints.d.ts.map +1 -0
  187. package/dist/esm/migrations/0002_checkpoints.js +38 -0
  188. package/dist/esm/migrations/0002_checkpoints.js.map +1 -0
  189. package/dist/esm/migrations/0003_startup_index.d.ts +15 -0
  190. package/dist/esm/migrations/0003_startup_index.d.ts.map +1 -0
  191. package/dist/esm/migrations/0003_startup_index.js +18 -0
  192. package/dist/esm/migrations/0003_startup_index.js.map +1 -0
  193. package/dist/esm/migrations/0004_dedup.d.ts +15 -0
  194. package/dist/esm/migrations/0004_dedup.d.ts.map +1 -0
  195. package/dist/esm/migrations/0004_dedup.js +38 -0
  196. package/dist/esm/migrations/0004_dedup.js.map +1 -0
  197. package/dist/esm/migrations/0005_run_event_type.d.ts +15 -0
  198. package/dist/esm/migrations/0005_run_event_type.d.ts.map +1 -0
  199. package/dist/esm/migrations/0005_run_event_type.js +18 -0
  200. package/dist/esm/migrations/0005_run_event_type.js.map +1 -0
  201. package/dist/esm/migrations/0006_consensus.d.ts +22 -0
  202. package/dist/esm/migrations/0006_consensus.d.ts.map +1 -0
  203. package/dist/esm/migrations/0006_consensus.js +69 -0
  204. package/dist/esm/migrations/0006_consensus.js.map +1 -0
  205. package/dist/esm/test/Notifying.d.ts +65 -0
  206. package/dist/esm/test/Notifying.d.ts.map +1 -0
  207. package/dist/esm/test/Notifying.js +62 -0
  208. package/dist/esm/test/Notifying.js.map +1 -0
  209. package/dist/esm/test/TestJournal.d.ts +43 -0
  210. package/dist/esm/test/TestJournal.d.ts.map +1 -0
  211. package/dist/esm/test/TestJournal.js +27 -0
  212. package/dist/esm/test/TestJournal.js.map +1 -0
  213. package/docs/README.md +164 -0
  214. package/docs/api.md +639 -0
  215. package/docs/concepts/compaction.md +157 -0
  216. package/docs/concepts/idempotency.md +127 -0
  217. package/docs/concepts/owner-fence.md +124 -0
  218. package/docs/concepts/redaction.md +218 -0
  219. package/docs/concepts/state-event-authority.md +159 -0
  220. package/docs/concepts/two-channels.md +167 -0
  221. package/docs/guides/commit-state-and-entry.md +114 -0
  222. package/docs/guides/compact-a-run.md +134 -0
  223. package/docs/guides/fold-a-projection.md +106 -0
  224. package/docs/guides/read-a-run.md +139 -0
  225. package/docs/guides/redact-log-output.md +94 -0
  226. package/docs/guides/testing.md +124 -0
  227. package/docs/guides/write-lifecycle-events.md +110 -0
  228. package/docs/installation.md +133 -0
  229. package/docs/quickstart.md +143 -0
  230. package/docs/troubleshooting.md +214 -0
  231. package/package.json +263 -3
  232. package/src/CallFact.ts +117 -0
  233. package/src/Consensus.ts +683 -0
  234. package/src/EngineEvent.ts +823 -0
  235. package/src/ExecutionFact.ts +433 -0
  236. package/src/Journal.ts +688 -0
  237. package/src/JournalEvent.ts +654 -0
  238. package/src/JournalGeneration.ts +77 -0
  239. package/src/JournalMetrics.ts +89 -0
  240. package/src/Migrations.ts +62 -0
  241. package/src/OwnerId.ts +42 -0
  242. package/src/Projection.ts +31 -0
  243. package/src/RedactedLogger.ts +483 -0
  244. package/src/Redaction.ts +1784 -0
  245. package/src/SqlConsensus.ts +344 -0
  246. package/src/SqlJournal.ts +2732 -0
  247. package/src/StepFact.ts +69 -0
  248. package/src/index.ts +109 -0
  249. package/src/internal/JournalInternalFault.ts +40 -0
  250. package/src/migrations/0001_initial.ts +41 -0
  251. package/src/migrations/0002_checkpoints.ts +46 -0
  252. package/src/migrations/0003_startup_index.ts +19 -0
  253. package/src/migrations/0004_dedup.ts +43 -0
  254. package/src/migrations/0005_run_event_type.ts +19 -0
  255. package/src/migrations/0006_consensus.ts +83 -0
  256. package/src/test/Notifying.ts +101 -0
  257. package/src/test/TestJournal.ts +52 -0
@@ -0,0 +1,143 @@
1
+ ---
2
+ title: "Quickstart"
3
+ description: "Compose a SQLite journal, write on both channels, flush the lossy one, and read the run back: one file, no other Smithers package."
4
+ sidebar:
5
+ order: 2
6
+ ---
7
+
8
+ This quickstart writes one run's history and reads it back. It uses the
9
+ production `SqlJournal` layer over a real SQLite file, so nothing here is a
10
+ test double, and it needs no other Smithers package.
11
+
12
+ By the end you will have seen a durable receipt, a lossy receipt, the flush
13
+ barrier that makes the lossy entry readable, and a payload that arrives back
14
+ redacted.
15
+
16
+ ## Prerequisites
17
+
18
+ - Node.js 26.4.0 or later, where the built-in SQLite module lives.
19
+ - An ESM package (`"type": "module"`, because the program ends in a top-level
20
+ `await`) with the journal, the database it writes through, and `effect`
21
+ installed. See [Installation](./installation.md) for why `effect` is pinned.
22
+
23
+ Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
24
+
25
+ ## Compose the layer
26
+
27
+ Create `quickstart.ts`. `SqlJournal.layer` needs a SQL client and a durable
28
+ writer, and `Migrations.layer` creates the journal's tables before the journal
29
+ is exposed:
30
+
31
+ ```ts
32
+ import * as DurableWriter from "@smthrs/database/DurableWriter"
33
+ import * as NodeDatabase from "@smthrs/database/node/NodeDatabase"
34
+ import { Journal, JournalEvent, Migrations, SqlJournal } from "@smthrs/journal"
35
+ import * as Effect from "effect/Effect"
36
+ import * as Layer from "effect/Layer"
37
+
38
+ const database = Layer.provideMerge(
39
+ DurableWriter.layer(),
40
+ NodeDatabase.layer({ filename: "quickstart.db" })
41
+ )
42
+
43
+ const journalLayer = SqlJournal.layer({ capacity: 1024, overflow: "reject" }).pipe(
44
+ Layer.provide(Layer.provideMerge(Migrations.layer, database))
45
+ )
46
+ ```
47
+
48
+ `capacity` bounds two things at once: how many entries the lossy admission
49
+ queue holds, and how many the `changes` buffer keeps for a slow subscriber.
50
+ `overflow: "reject"` makes a full queue a typed `queue_overflow` failure rather
51
+ than a silent drop.
52
+
53
+ ## Write on both channels
54
+
55
+ Identifiers are branded, so cast the strings once at the edge:
56
+
57
+ ```ts
58
+ const runId = "run-1" as JournalEvent.RunId
59
+ const sourceId = "engine" as JournalEvent.SourceId
60
+
61
+ const program = Effect.gen(function*() {
62
+ const journal = yield* Journal.Journal
63
+
64
+ const created = yield* journal.emitDurableUnfenced({
65
+ runId,
66
+ sourceId,
67
+ eventType: "run.created",
68
+ payload: { flow: "build", token: "ghp_0123456789abcdefghijklmnopqrstuvwxyz" }
69
+ })
70
+ console.log("durable:", created._tag, "seq", created.seq)
71
+
72
+ yield* journal.emitLossy({
73
+ runId,
74
+ sourceId,
75
+ eventType: "step.progress",
76
+ payload: { step: "compile", percent: 40 }
77
+ })
78
+ yield* journal.flush
79
+
80
+ const page = yield* journal.entries({ runId, limit: 100 })
81
+ for (const entry of page.entries) {
82
+ console.log(entry.seq, entry.eventType, JSON.stringify(entry.payload))
83
+ }
84
+ console.log("hasMore:", page.hasMore)
85
+ })
86
+ ```
87
+
88
+ Two choices in that block are worth naming:
89
+
90
+ - `emitDurableUnfenced` is the durable write with no ownership fence. It is
91
+ correct here because this program owns no run. The fenced `emitDurable` is
92
+ what an engine calls, and it needs the run claimed and activated through the
93
+ journal's `Consensus` strategy, which
94
+ [`@smthrs/run-store`](/api/run-store) drives; see
95
+ [Write a fenced lifecycle event](./guides/write-lifecycle-events.md).
96
+ - `flush` is the barrier for the lossy channel only. The durable write was
97
+ already on disk when its receipt arrived; the lossy one is still in the queue
98
+ until `flush` returns.
99
+
100
+ ## Run it
101
+
102
+ ```ts
103
+ await Effect.runPromise(program.pipe(Effect.provide(journalLayer), Effect.orDie))
104
+ ```
105
+
106
+ Run it with Node, which strips the types itself on 26.4.0 and later:
107
+
108
+ ```bash
109
+ node quickstart.ts
110
+ ```
111
+
112
+ The output is:
113
+
114
+ ```text
115
+ durable: Accepted seq 0
116
+ 0 run.created {"flow":"build","token":"[REDACTED]"}
117
+ 1 step.progress {"percent":40,"step":"compile"}
118
+ hasMore: false
119
+ ```
120
+
121
+ ## Read the output
122
+
123
+ Three things in those four lines are the journal's contract, not incidental:
124
+
125
+ - **`token` came back as `[REDACTED]`.** The value was scrubbed before the row
126
+ was encoded, so the credential is not in the file and never was. See
127
+ [Redaction](./concepts/redaction.md).
128
+ - **The `step.progress` keys came back sorted.** The journal persists canonical
129
+ JSON with object keys sorted, because those bytes are what an idempotency
130
+ check compares. See
131
+ [Producer identity and idempotency](./concepts/idempotency.md).
132
+ - **Both entries share one `seq` line.** `seq` is the run's canonical replay
133
+ order across both channels, so the durable and lossy entries interleave in
134
+ one history rather than in two.
135
+
136
+ ## Next steps
137
+
138
+ - [The two channels](./concepts/two-channels.md): which write to reach for, and
139
+ what each receipt means.
140
+ - [Read and follow a run](./guides/read-a-run.md): paging, the lossless
141
+ follower, and the local tail.
142
+ - [Test against a real journal](./guides/testing.md): the same layer over an
143
+ in-memory database, in one call.
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: "Troubleshooting"
3
+ description: "Every JournalError code, the three failures that are not errors at all, what causes each, and what to change."
4
+ ---
5
+
6
+ Every journal operation fails with a `JournalError` carrying a stable `code`.
7
+ Find your code below. Three common problems produce no error at all, and they
8
+ are at the end.
9
+
10
+ The full error schema is in the [API reference](./api.md). Every Smithers error
11
+ code across every package, this one's included, is listed under
12
+ [`@smthrs/journal` error codes](/docs/reference/errors/#smthrsjournal).
13
+
14
+ ## sink_failed with "consensus guard failed"
15
+
16
+ **What happened.** A fenced write, meaning `emitDurable`, `checkpoint`, or
17
+ `compact`, asked the consensus strategy whether the owner still holds the run
18
+ and the strategy could not answer: with `SqlConsensus`, its
19
+ `flows_consensus_leases` table is missing or the database refused the read.
20
+
21
+ **What to change.** Run the journal's migration set before building the
22
+ journal, or take `@smthrs/engine-store/Migrations`'s `sets`, which is the
23
+ whole durable schema in dependency order. A strategy failure is never
24
+ reported as `fence_lost`. See
25
+ [Installation](./installation.md#what-a-fenced-write-needs).
26
+
27
+ This is a composition problem, not a race. It is `sink_failed` rather than
28
+ `fence_lost` precisely so the two cannot be confused.
29
+
30
+ ## fence_lost
31
+
32
+ **What happened.** The run no longer records the owner you supplied. Either
33
+ another process reclaimed it, or its status is no longer `running`.
34
+
35
+ **What to change.** Stop writing. `fence_lost` is ownership news, not a
36
+ transient failure: retrying with the same token cannot succeed, and reaching for
37
+ `emitDurableUnfenced` to get past it writes exactly the zombie entry the fence
38
+ exists to reject. See [The owner fence](./concepts/owner-fence.md).
39
+
40
+ ## invalid_event
41
+
42
+ **What happened.** The input violates the journal's contract before anything
43
+ was allocated. The common causes are:
44
+
45
+ - An identifier that is empty, longer than 1,024 UTF-16 code units, carrying an
46
+ unpaired surrogate, or carrying a NUL.
47
+ - An owner that is missing, null, or not an `OwnerId`, most often a fractional
48
+ or negative `pid`.
49
+ - A payload nested past `Redaction.maxDepth` (256 container edges).
50
+ - An encoded `payload` plus `meta` larger than the layer's `maxEntryBytes`, when
51
+ that option is set.
52
+
53
+ **What to change.** Fix the argument. A refused event costs no sequence and
54
+ leaves no gap, so nothing needs cleaning up. On a fenced call, note that
55
+ `invalid_event` rather than `fence_lost` means the problem is the token you
56
+ passed, not who owns the run.
57
+
58
+ ## idempotency_conflict
59
+
60
+ **What happened.** A producer identity `(runId, sourceId, sourceSeq)` was
61
+ re-emitted with different content, under the default `dedupe: "content"`.
62
+
63
+ **What to change.** Either stop reusing the sequence for a different event, or
64
+ declare `dedupe: "identity"` if the sequence is derived from the event's own
65
+ content and a collision genuinely is the same event observed twice.
66
+
67
+ If the failure surfaced from `flush` rather than from the emit, the original
68
+ entry had been evicted from the in-process index, so admission had no resident
69
+ entry to compare against. That changes where you see the failure, not whether
70
+ it happens. See
71
+ [Producer identity and idempotency](./concepts/idempotency.md).
72
+
73
+ ## Sequence exhaustion
74
+
75
+ **What happened.** `invalid_event` reports an exhausted canonical sequence
76
+ range. A queued admission can encounter this at `flush` if another writer
77
+ reaches the limit before the batch commits.
78
+
79
+ **What to change.** Start a new run. Ordinary overlapping writers do not cause
80
+ sequence conflicts: the queued writer advances an overtaken reservation above
81
+ the committed tail. See [Commit ordering](./concepts/two-channels.md#commit-ordering).
82
+
83
+ ## queue_overflow
84
+
85
+ **What happened.** The lossy admission queue is full and the layer's `overflow`
86
+ is `reject`.
87
+
88
+ **What to change.** Raise `capacity`, or choose a dropping policy if losing
89
+ telemetry is acceptable: `drop-newest` returns a `Dropped` receipt, and
90
+ `drop-oldest` returns `Accepted` with an `evicted` count. If the queue is full
91
+ because the writer is not draining, look for a `flush` or a long transaction
92
+ holding the write lock.
93
+
94
+ ## journal_closed
95
+
96
+ **What happened.** The operation ran against a closed journal. Either the
97
+ layer's scope was released and the admission queue is gone, or the composition
98
+ provided `Journal.layerNoop()`.
99
+
100
+ **What to change.** Keep the journal's scope open for as long as callers hold
101
+ the service, or provide a real layer where one is required.
102
+
103
+ ## sink_failed
104
+
105
+ **What happened.** The database failed on a write path. The cause carries the
106
+ underlying `DatabaseError`.
107
+
108
+ **What to change.** Read the cause first: a missing table is a composition
109
+ problem, and a locked or unreadable file is an operational one. A transient
110
+ outage is not permanent for the journal. The writer fiber survives a failed
111
+ batch, and `emitDurable` opens its own transaction inline, so the durable
112
+ channel works again as soon as the database is healthy.
113
+
114
+ ## read_failed
115
+
116
+ **What happened.** The database failed on a read path: `entries`, `stream`, or
117
+ the floor read behind them.
118
+
119
+ **What to change.** Treat it as a retryable storage condition rather than a
120
+ journal defect. It is a separate code from `unknown` for exactly that reason.
121
+
122
+ ## decode_failed
123
+
124
+ **What happened.** A persisted row no longer matches the schema that reads it.
125
+
126
+ **What to change.** This is schema drift, not a caller mistake. Check that the
127
+ migration set the database was built with matches the package version reading
128
+ it.
129
+
130
+ ## projection_failed
131
+
132
+ **What happened.** A projection's `reduce` failed, which ends the projection
133
+ stream.
134
+
135
+ **What to change.** Fix the reducer. The journal is unaffected: later emissions
136
+ still succeed, and a fresh `project` call folds the same history again from the
137
+ start. Make a reducer that meets an entry it does not recognize return the state
138
+ unchanged.
139
+
140
+ ## checkpoint_invalid
141
+
142
+ **What happened.** One of two things:
143
+
144
+ - `checkpoint` was given a `seq` that names no committed entry of the run, or
145
+ one at or below the run's compaction floor. In the second case the failure
146
+ carries the floor in `checkpointSeq`.
147
+ - `compact` found no checkpoint to truncate below, either at all or at the
148
+ `upTo` you named.
149
+
150
+ **What to change.** Checkpoint a committed sequence above the floor before
151
+ compacting to it. See [Compact a long-running run](./guides/compact-a-run.md).
152
+
153
+ ## reader_behind
154
+
155
+ **What happened.** `compact` refused because a live in-process `stream` still
156
+ needs a sequence the truncation would delete. `checkpointSeq` is the checkpoint
157
+ it declined to truncate below.
158
+
159
+ **What to change.** Let the reader catch up, or drop it, then compact again.
160
+ Readers in other processes cannot be seen from here, so they are protected by
161
+ the read-side `compacted` guard rather than by this refusal.
162
+
163
+ ## compacted
164
+
165
+ **What happened.** A read started below the run's compaction floor.
166
+ `checkpointSeq` carries the floor.
167
+
168
+ **What to change.** Resync from the checkpoint: read `latestCheckpoint(runId)`,
169
+ apply its `state`, and continue from
170
+ `stream({ runId, afterSequence: checkpoint.seq })`. The worked version is in
171
+ [Compact a long-running run](./guides/compact-a-run.md#resync-a-reader).
172
+
173
+ ## unknown
174
+
175
+ **What happened.** A journal defect that no other code classifies.
176
+
177
+ **What to change.** This code is reserved for genuinely unclassified failures.
178
+ Report it with the message and cause.
179
+
180
+ ## A stream or a projection never finishes
181
+
182
+ **What happened.** `stream` and `project` replay a run's history and then follow
183
+ its tail. Following is the point, so neither completes on its own, and a
184
+ `Stream.runCollect`, `Stream.runDrain`, or `Stream.runLast` with no bound hangs
185
+ forever.
186
+
187
+ **What to change.** Bound the stream: `Stream.take(n)` for a snapshot,
188
+ `Stream.takeUntil` for a condition, or a forked fiber you interrupt when the
189
+ view is torn down. See [Fold a run into a projection](./guides/fold-a-projection.md#bound-the-stream).
190
+
191
+ ## A write hangs inside transact
192
+
193
+ **What happened.** The body of a `transact` called `flush`, or did work that
194
+ waits on something outside the transaction.
195
+
196
+ **What to change.** Keep a `transact` body to storage work only: no flow
197
+ bodies, no host calls, no waits. `flush` in particular waits on the queued
198
+ lossy writer, which is waiting on the transaction you are holding. See
199
+ [Commit state and its entry together](./guides/commit-state-and-entry.md).
200
+
201
+ ## Entries are missing from a live tail
202
+
203
+ **What happened.** `changes` is a bounded sliding buffer sized by the layer's
204
+ `capacity`. A slow subscriber loses entries with no error and no gap signal.
205
+ `changes` also carries only the entries this process commits; another writer on
206
+ the same database publishes into its own buffer.
207
+
208
+ **What to change.** Use `stream` wherever a missing entry would be a
209
+ correctness bug. It is the lossless follower, it reports sink losses, and it
210
+ rechecks the durable tail rather than trusting one process's buffer. See
211
+ [Read and follow a run](./guides/read-a-run.md).
212
+
213
+ If a lossy entry is missing from a read rather than from a tail, `flush` first:
214
+ `emitLossy` returns before its entry is committed.
package/package.json CHANGED
@@ -1,6 +1,266 @@
1
1
  {
2
2
  "name": "@smthrs/journal",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "type": "module",
4
+ "version": "1.0.0-rc.3",
5
+ "license": "MIT",
6
+ "smthrs": {
7
+ "group": "engine"
8
+ },
9
+ "description": "Effect services for the Smithers event journal: the immutable run history and the owner fence on its durable channel",
10
+ "homepage": "https://github.com/smithersai/smithers",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/smithersai/smithers.git",
14
+ "directory": "packages/smithers/flows/journal"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/smithersai/smithers/issues"
18
+ },
19
+ "tags": [
20
+ "typescript",
21
+ "effect",
22
+ "journal",
23
+ "durability"
24
+ ],
25
+ "keywords": [
26
+ "typescript",
27
+ "effect",
28
+ "journal",
29
+ "durability"
30
+ ],
31
+ "engines": {
32
+ "node": ">=26.4.0"
33
+ },
34
+ "sideEffects": [],
35
+ "exports": {
36
+ "./package.json": "./package.json",
37
+ ".": {
38
+ "import": {
39
+ "types": "./dist/esm/index.d.ts",
40
+ "default": "./dist/esm/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/cjs/index.d.ts",
44
+ "default": "./dist/cjs/index.js"
45
+ }
46
+ },
47
+ "./internal/*": null,
48
+ "./migrations/*": null,
49
+ "./*/index": null,
50
+ "./Journal": {
51
+ "import": {
52
+ "types": "./dist/esm/Journal.d.ts",
53
+ "default": "./dist/esm/Journal.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/cjs/Journal.d.ts",
57
+ "default": "./dist/cjs/Journal.js"
58
+ }
59
+ },
60
+ "./JournalEvent": {
61
+ "import": {
62
+ "types": "./dist/esm/JournalEvent.d.ts",
63
+ "default": "./dist/esm/JournalEvent.js"
64
+ },
65
+ "require": {
66
+ "types": "./dist/cjs/JournalEvent.d.ts",
67
+ "default": "./dist/cjs/JournalEvent.js"
68
+ }
69
+ },
70
+ "./EngineEvent": {
71
+ "import": {
72
+ "types": "./dist/esm/EngineEvent.d.ts",
73
+ "default": "./dist/esm/EngineEvent.js"
74
+ },
75
+ "require": {
76
+ "types": "./dist/cjs/EngineEvent.d.ts",
77
+ "default": "./dist/cjs/EngineEvent.js"
78
+ }
79
+ },
80
+ "./JournalMetrics": {
81
+ "import": {
82
+ "types": "./dist/esm/JournalMetrics.d.ts",
83
+ "default": "./dist/esm/JournalMetrics.js"
84
+ },
85
+ "require": {
86
+ "types": "./dist/cjs/JournalMetrics.d.ts",
87
+ "default": "./dist/cjs/JournalMetrics.js"
88
+ }
89
+ },
90
+ "./Migrations": {
91
+ "import": {
92
+ "types": "./dist/esm/Migrations.d.ts",
93
+ "default": "./dist/esm/Migrations.js"
94
+ },
95
+ "require": {
96
+ "types": "./dist/cjs/Migrations.d.ts",
97
+ "default": "./dist/cjs/Migrations.js"
98
+ }
99
+ },
100
+ "./OwnerId": {
101
+ "import": {
102
+ "types": "./dist/esm/OwnerId.d.ts",
103
+ "default": "./dist/esm/OwnerId.js"
104
+ },
105
+ "require": {
106
+ "types": "./dist/cjs/OwnerId.d.ts",
107
+ "default": "./dist/cjs/OwnerId.js"
108
+ }
109
+ },
110
+ "./Projection": {
111
+ "import": {
112
+ "types": "./dist/esm/Projection.d.ts",
113
+ "default": "./dist/esm/Projection.js"
114
+ },
115
+ "require": {
116
+ "types": "./dist/cjs/Projection.d.ts",
117
+ "default": "./dist/cjs/Projection.js"
118
+ }
119
+ },
120
+ "./RedactedLogger": {
121
+ "import": {
122
+ "types": "./dist/esm/RedactedLogger.d.ts",
123
+ "default": "./dist/esm/RedactedLogger.js"
124
+ },
125
+ "require": {
126
+ "types": "./dist/cjs/RedactedLogger.d.ts",
127
+ "default": "./dist/cjs/RedactedLogger.js"
128
+ }
129
+ },
130
+ "./Redaction": {
131
+ "import": {
132
+ "types": "./dist/esm/Redaction.d.ts",
133
+ "default": "./dist/esm/Redaction.js"
134
+ },
135
+ "require": {
136
+ "types": "./dist/cjs/Redaction.d.ts",
137
+ "default": "./dist/cjs/Redaction.js"
138
+ }
139
+ },
140
+ "./SqlJournal": {
141
+ "import": {
142
+ "types": "./dist/esm/SqlJournal.d.ts",
143
+ "default": "./dist/esm/SqlJournal.js"
144
+ },
145
+ "require": {
146
+ "types": "./dist/cjs/SqlJournal.d.ts",
147
+ "default": "./dist/cjs/SqlJournal.js"
148
+ }
149
+ },
150
+ "./index": {
151
+ "import": {
152
+ "types": "./dist/esm/index.d.ts",
153
+ "default": "./dist/esm/index.js"
154
+ },
155
+ "require": {
156
+ "types": "./dist/cjs/index.d.ts",
157
+ "default": "./dist/cjs/index.js"
158
+ }
159
+ },
160
+ "./test/Notifying": {
161
+ "import": {
162
+ "types": "./dist/esm/test/Notifying.d.ts",
163
+ "default": "./dist/esm/test/Notifying.js"
164
+ },
165
+ "require": {
166
+ "types": "./dist/cjs/test/Notifying.d.ts",
167
+ "default": "./dist/cjs/test/Notifying.js"
168
+ }
169
+ },
170
+ "./test/TestJournal": {
171
+ "import": {
172
+ "types": "./dist/esm/test/TestJournal.d.ts",
173
+ "default": "./dist/esm/test/TestJournal.js"
174
+ },
175
+ "require": {
176
+ "types": "./dist/cjs/test/TestJournal.d.ts",
177
+ "default": "./dist/cjs/test/TestJournal.js"
178
+ }
179
+ },
180
+ "./JournalGeneration": {
181
+ "import": {
182
+ "types": "./dist/esm/JournalGeneration.d.ts",
183
+ "default": "./dist/esm/JournalGeneration.js"
184
+ },
185
+ "require": {
186
+ "types": "./dist/cjs/JournalGeneration.d.ts",
187
+ "default": "./dist/cjs/JournalGeneration.js"
188
+ }
189
+ },
190
+ "./Consensus": {
191
+ "import": {
192
+ "types": "./dist/esm/Consensus.d.ts",
193
+ "default": "./dist/esm/Consensus.js"
194
+ },
195
+ "require": {
196
+ "types": "./dist/cjs/Consensus.d.ts",
197
+ "default": "./dist/cjs/Consensus.js"
198
+ }
199
+ },
200
+ "./SqlConsensus": {
201
+ "import": {
202
+ "types": "./dist/esm/SqlConsensus.d.ts",
203
+ "default": "./dist/esm/SqlConsensus.js"
204
+ },
205
+ "require": {
206
+ "types": "./dist/cjs/SqlConsensus.d.ts",
207
+ "default": "./dist/cjs/SqlConsensus.js"
208
+ }
209
+ }
210
+ },
211
+ "files": [
212
+ "src/**/*.ts",
213
+ "dist/**/*.js",
214
+ "dist/**/*.js.map",
215
+ "dist/**/*.d.ts",
216
+ "dist/**/*.d.ts.map",
217
+ "dist/**/package.json",
218
+ "LICENSE",
219
+ "README.md",
220
+ "CHANGELOG.md",
221
+ "docs/"
222
+ ],
223
+ "publishConfig": {
224
+ "access": "public",
225
+ "provenance": true,
226
+ "tag": "next"
227
+ },
228
+ "dependencies": {
229
+ "@smthrs/database": "1.0.0-rc.3"
230
+ },
231
+ "devDependencies": {
232
+ "@effect/language-service": "0.87.2",
233
+ "@effect/sql-sqlite-node": "4.0.0-rc.115",
234
+ "@effect/vitest": "4.0.0-rc.115",
235
+ "@eslint/js": "9.39.5",
236
+ "@smthrs/flow": "1.0.0-rc.3",
237
+ "@types/node": "26.4.1",
238
+ "@vitest/coverage-v8": "5.0.0",
239
+ "dprint": "0.57.1",
240
+ "effect": "4.0.0-rc.115",
241
+ "esbuild": "0.28.2",
242
+ "eslint": "9.39.5",
243
+ "eslint-import-resolver-typescript": "4.4.5",
244
+ "eslint-plugin-import": "2.32.0",
245
+ "eslint-plugin-jsdoc": "64.3.4",
246
+ "eslint-plugin-unicorn": "65.0.0",
247
+ "madge": "8.0.0",
248
+ "typescript": "5.9.3",
249
+ "typescript-eslint": "8.69.0",
250
+ "vitest": "5.0.0",
251
+ "fast-check": "4.9.0"
252
+ },
253
+ "peerDependencies": {
254
+ "effect": "4.0.0-rc.115"
255
+ },
256
+ "scripts": {
257
+ "lint": "eslint src --max-warnings=0 && dprint check",
258
+ "format": "dprint fmt",
259
+ "build": "node scripts/build.mjs",
260
+ "check": "tsc -b tsconfig.json && tsc -p tsconfig.test.json --noEmit",
261
+ "circular": "node scripts/circular.mjs",
262
+ "test": "vitest",
263
+ "coverage": "node ../database/scripts/test-matrix.mjs",
264
+ "test:matrix": "node ../database/scripts/test-matrix.mjs"
265
+ }
6
266
  }