@structure-ai/eventsourcing-nisshi 0.0.10

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.
package/src/sidecar.ts ADDED
@@ -0,0 +1,81 @@
1
+ import * as SqlClient from "@effect/sql/SqlClient";
2
+ import type { SqlError } from "@effect/sql/SqlError";
3
+ import { Effect } from "effect";
4
+
5
+ /** Options shared by every adapter in this package. */
6
+ export interface SidecarOptions {
7
+ /** Prefix prepended to every sidecar table name (default: none). */
8
+ readonly tablePrefix?: string;
9
+ }
10
+
11
+ /** Resolved sidecar table names for one `tablePrefix`. */
12
+ export interface SidecarTables {
13
+ /** Optimistic-concurrency ledger: stream → last reserved version. */
14
+ readonly streams: string;
15
+ /** Events reserved in the ledger but not yet confirmed in the topic. */
16
+ readonly pending: string;
17
+ readonly snapshots: string;
18
+ readonly checkpoints: string;
19
+ readonly inbox: string;
20
+ }
21
+
22
+ /** Table names for the given options (default: unprefixed). */
23
+ export const sidecarTables = (options?: SidecarOptions): SidecarTables => {
24
+ const prefix = options?.tablePrefix ?? "";
25
+ return {
26
+ streams: `${prefix}nisshi_streams`,
27
+ pending: `${prefix}nisshi_pending`,
28
+ snapshots: `${prefix}nisshi_snapshots`,
29
+ checkpoints: `${prefix}nisshi_checkpoints`,
30
+ inbox: `${prefix}nisshi_inbox`,
31
+ };
32
+ };
33
+
34
+ /**
35
+ * Creates every sidecar table, idempotently. Dialect is deliberately kept to
36
+ * the common SQL subset so the same statements run on bun:sqlite and
37
+ * PostgreSQL `SqlClient`s. Positions are BIGINT (they are Kafka offsets + 1).
38
+ */
39
+ export const migrate = (
40
+ options?: SidecarOptions,
41
+ ): Effect.Effect<void, SqlError, SqlClient.SqlClient> =>
42
+ Effect.gen(function* () {
43
+ const sql = yield* SqlClient.SqlClient;
44
+ const tables = sidecarTables(options);
45
+ yield* sql`
46
+ CREATE TABLE IF NOT EXISTS ${sql(tables.streams)} (
47
+ stream_name TEXT PRIMARY KEY,
48
+ last_version BIGINT NOT NULL
49
+ )
50
+ `;
51
+ yield* sql`
52
+ CREATE TABLE IF NOT EXISTS ${sql(tables.pending)} (
53
+ stream_name TEXT NOT NULL,
54
+ version BIGINT NOT NULL,
55
+ topic TEXT NOT NULL,
56
+ record_value TEXT NOT NULL,
57
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
58
+ PRIMARY KEY (stream_name, version)
59
+ )
60
+ `;
61
+ yield* sql`
62
+ CREATE TABLE IF NOT EXISTS ${sql(tables.snapshots)} (
63
+ stream_name TEXT PRIMARY KEY,
64
+ state TEXT NOT NULL,
65
+ version BIGINT NOT NULL
66
+ )
67
+ `;
68
+ yield* sql`
69
+ CREATE TABLE IF NOT EXISTS ${sql(tables.checkpoints)} (
70
+ name TEXT PRIMARY KEY,
71
+ position BIGINT NOT NULL
72
+ )
73
+ `;
74
+ yield* sql`
75
+ CREATE TABLE IF NOT EXISTS ${sql(tables.inbox)} (
76
+ consumer_id TEXT NOT NULL,
77
+ message_id TEXT NOT NULL,
78
+ PRIMARY KEY (consumer_id, message_id)
79
+ )
80
+ `;
81
+ });