@secondlayer/sdk 6.47.0 → 6.48.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.
@@ -0,0 +1,194 @@
1
+ // src/sinks/testing.ts
2
+ function fail(probe, detail) {
3
+ throw new Error(`sink conformance — ${probe}: ${detail}`);
4
+ }
5
+ function sortRows(rows) {
6
+ return [...rows].sort((a, b) => a.height - b.height || a.key.localeCompare(b.key));
7
+ }
8
+ function expectRows(probe, actual, expected) {
9
+ const got = JSON.stringify(sortRows(actual));
10
+ const want = JSON.stringify(sortRows(expected));
11
+ if (got !== want)
12
+ fail(probe, `rows ${got}, expected ${want}`);
13
+ }
14
+ function expectCursor(probe, actual, expected) {
15
+ if (actual !== expected) {
16
+ fail(probe, `cursor ${JSON.stringify(actual)}, expected ${JSON.stringify(expected)}`);
17
+ }
18
+ }
19
+ async function expectRejects(probe, promise, why) {
20
+ let rejected = false;
21
+ try {
22
+ await promise;
23
+ } catch {
24
+ rejected = true;
25
+ }
26
+ if (!rejected)
27
+ fail(probe, why);
28
+ }
29
+ function sinkConformanceProbes(harness) {
30
+ async function start() {
31
+ await harness.reset();
32
+ const sink = await harness.makeSink();
33
+ await sink.loadCursor();
34
+ return sink;
35
+ }
36
+ const commit = (sink, cursor, rows) => sink.commitBatch(cursor, async (tx) => {
37
+ for (const [height, key] of rows)
38
+ await harness.insertRow(tx, height, key);
39
+ });
40
+ const probes = [
41
+ {
42
+ name: "fresh store: loadCursor initializes and returns null",
43
+ async run() {
44
+ await harness.reset();
45
+ const sink = await harness.makeSink();
46
+ const cursor = await sink.loadCursor();
47
+ expectCursor("fresh loadCursor", cursor, null);
48
+ }
49
+ },
50
+ {
51
+ name: "checkpoint persists: a restarted sink resumes from the committed cursor",
52
+ async run() {
53
+ const sink = await start();
54
+ await commit(sink, "10:0", [[10, "a"]]);
55
+ const restarted = await harness.makeSink();
56
+ const resumed = await restarted.loadCursor();
57
+ expectCursor("restart resume", resumed, "10:0");
58
+ }
59
+ },
60
+ {
61
+ name: "crash between rows and cursor: a throw aborts both (invariants #4/#5)",
62
+ async run() {
63
+ const sink = await start();
64
+ await expectRejects("torn batch", sink.commitBatch("10:0", async (tx) => {
65
+ await harness.insertRow(tx, 10, "a");
66
+ throw new Error("kill -9");
67
+ }), "commitBatch swallowed the handler throw — it must propagate (invariant #12)");
68
+ expectRows("torn batch", await harness.readRows(), []);
69
+ expectCursor("torn batch", await harness.readCursor(), null);
70
+ }
71
+ },
72
+ {
73
+ name: "replaying the same cursor is safe: cursor works as an idempotency key (#7)",
74
+ async run() {
75
+ const sink = await start();
76
+ await commit(sink, "10:0", [[10, "a"]]);
77
+ await commit(sink, "10:0", [[10, "a"]]);
78
+ expectRows("replay", await harness.readRows(), [
79
+ { height: 10, key: "a" }
80
+ ]);
81
+ expectCursor("replay", await harness.readCursor(), "10:0");
82
+ }
83
+ },
84
+ {
85
+ name: "rollback deletes at-or-above the fork — inclusive >= (#8/#9)",
86
+ async run() {
87
+ const sink = await start();
88
+ await commit(sink, "11:0", [
89
+ [10, "a"],
90
+ [11, "b"]
91
+ ]);
92
+ await sink.rollback(11, "10:2147483647");
93
+ expectRows("inclusive >=", await harness.readRows(), [
94
+ { height: 10, key: "a" }
95
+ ]);
96
+ expectCursor("inclusive >=", await harness.readCursor(), "10:2147483647");
97
+ }
98
+ },
99
+ {
100
+ name: "rollback then re-read: canonical rows land cleanly after the rewind",
101
+ async run() {
102
+ const sink = await start();
103
+ await commit(sink, "12:0", [
104
+ [10, "a"],
105
+ [11, "b"],
106
+ [12, "c"]
107
+ ]);
108
+ await sink.rollback(11, "10:2147483647");
109
+ expectRows("rollback", await harness.readRows(), [
110
+ { height: 10, key: "a" }
111
+ ]);
112
+ await commit(sink, "12:0", [
113
+ [11, "b2"],
114
+ [12, "c2"]
115
+ ]);
116
+ expectRows("re-read", await harness.readRows(), [
117
+ { height: 10, key: "a" },
118
+ { height: 11, key: "b2" },
119
+ { height: 12, key: "c2" }
120
+ ]);
121
+ expectCursor("re-read", await harness.readCursor(), "12:0");
122
+ }
123
+ },
124
+ {
125
+ name: "rollback is idempotent: re-application after a crash is a no-op (#10)",
126
+ async run() {
127
+ const sink = await start();
128
+ await commit(sink, "11:0", [
129
+ [10, "a"],
130
+ [11, "b"]
131
+ ]);
132
+ await sink.rollback(11, "10:2147483647");
133
+ await sink.rollback(11, "10:2147483647");
134
+ expectRows("idempotent rollback", await harness.readRows(), [
135
+ { height: 10, key: "a" }
136
+ ]);
137
+ expectCursor("idempotent rollback", await harness.readCursor(), "10:2147483647");
138
+ }
139
+ },
140
+ {
141
+ name: "rollback scopes deletion by forkPointHeight, never rewindCursor (#11)",
142
+ async run() {
143
+ const sink = await start();
144
+ await commit(sink, "13:0", [
145
+ [9, "a"],
146
+ [10, "b"],
147
+ [11, "c"],
148
+ [12, "d"],
149
+ [13, "e"]
150
+ ]);
151
+ await sink.rollback(12, "9:2147483647");
152
+ expectRows("scope by height", await harness.readRows(), [
153
+ { height: 9, key: "a" },
154
+ { height: 10, key: "b" },
155
+ { height: 11, key: "c" }
156
+ ]);
157
+ await sink.rollback(10, "9:2147483647");
158
+ expectRows("scope by height (second fork)", await harness.readRows(), [
159
+ { height: 9, key: "a" }
160
+ ]);
161
+ expectCursor("scope by height", await harness.readCursor(), "9:2147483647");
162
+ }
163
+ }
164
+ ];
165
+ if (harness.withLockHeld) {
166
+ const withLockHeld = harness.withLockHeld.bind(harness);
167
+ probes.push({
168
+ name: "lock contention: a second writer fails loudly, never interleaves (#13)",
169
+ async run() {
170
+ const sink = await start();
171
+ await withLockHeld(async () => {
172
+ await expectRejects("lock contention", sink.commitBatch("10:0", async (tx) => {
173
+ await harness.insertRow(tx, 10, "a");
174
+ }), "commitBatch succeeded while another writer held the lock — two replicas would interleave commits");
175
+ });
176
+ expectRows("lock contention", await harness.readRows(), []);
177
+ expectCursor("lock contention", await harness.readCursor(), null);
178
+ }
179
+ });
180
+ }
181
+ return probes;
182
+ }
183
+ function attachSinkConformance(test, harness) {
184
+ for (const probe of sinkConformanceProbes(harness)) {
185
+ test(probe.name, () => probe.run());
186
+ }
187
+ }
188
+ export {
189
+ sinkConformanceProbes,
190
+ attachSinkConformance
191
+ };
192
+
193
+ //# debugId=5D137D9410DE790C64756E2164756E21
194
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/sinks/testing.ts"],
4
+ "sourcesContent": [
5
+ "import type { ConsumerSink } from \"./types.ts\";\n\n/**\n * The sink conformance kit: executable probes for the {@link ConsumerSink}\n * contract invariants — the ones that are SILENT when violated in\n * production (torn batches, silent gaps, under-deleted forks) and only\n * surface weeks later as missing or duplicated rows.\n *\n * Run every sink you author through it, in CI, against the real store:\n *\n * ```ts\n * import { test } from \"bun:test\";\n * import { attachSinkConformance } from \"@secondlayer/sdk/sinks/testing\";\n *\n * attachSinkConformance(test, {\n * makeSink: () => mySink(db, { id: \"conformance\", tables: [\"rows\"], height: \"height\" }),\n * reset: () => db.deleteFrom(...)...,\n * insertRow: (tx, height, key) => ..., // MUST be replay-safe (upsert on key)\n * readRows: () => ...,\n * readCursor: () => ...,\n * });\n * ```\n *\n * Framework-agnostic: probes throw plain `Error`s, so they run under\n * bun:test, vitest, jest, or a bare loop.\n */\nexport interface SinkConformanceHarness<Tx = unknown> {\n\t/** A fresh sink instance bound to the SAME store and the SAME checkpoint\n\t * id each call — \"new instance\" is how the kit simulates a restart. */\n\tmakeSink(): ConsumerSink<Tx> | Promise<ConsumerSink<Tx>>;\n\t/** Wipe the store back to empty: projection rows AND this id's\n\t * checkpoint. Called before every probe. */\n\treset(): Promise<void> | void;\n\t/** Insert one projection row through the lent transaction, exactly as a\n\t * real `onBatch` handler would. MUST be replay-safe (upsert / insert-\n\t * on-conflict-do-nothing keyed on `key`) — the same requirement the\n\t * contract's at-least-once delivery puts on real handlers. */\n\tinsertRow(tx: Tx, height: number, key: string): Promise<void> | void;\n\t/** Every committed projection row, read OUTSIDE any transaction. */\n\treadRows(): Promise<Array<{ height: number; key: string }>>;\n\t/** The committed checkpoint cursor as visible to a restarted process, or\n\t * `null` when none was ever committed. */\n\treadCursor(): Promise<string | null>;\n\t/** Optional (sinks with a writer lock): hold the lock for this sink's id\n\t * — as a second live consumer would — while `during` runs. Enables the\n\t * lock-contention probe (invariant #13: fail loudly, never interleave). */\n\twithLockHeld?(during: () => Promise<void>): Promise<void>;\n}\n\n/** One executable contract probe; `run` throws on violation. */\nexport interface ConformanceProbe {\n\tname: string;\n\trun(): Promise<void>;\n}\n\nfunction fail(probe: string, detail: string): never {\n\tthrow new Error(`sink conformance — ${probe}: ${detail}`);\n}\n\nfunction sortRows(rows: Array<{ height: number; key: string }>) {\n\treturn [...rows].sort(\n\t\t(a, b) => a.height - b.height || a.key.localeCompare(b.key),\n\t);\n}\n\nfunction expectRows(\n\tprobe: string,\n\tactual: Array<{ height: number; key: string }>,\n\texpected: Array<{ height: number; key: string }>,\n): void {\n\tconst got = JSON.stringify(sortRows(actual));\n\tconst want = JSON.stringify(sortRows(expected));\n\tif (got !== want) fail(probe, `rows ${got}, expected ${want}`);\n}\n\nfunction expectCursor(\n\tprobe: string,\n\tactual: string | null,\n\texpected: string | null,\n): void {\n\tif (actual !== expected) {\n\t\tfail(\n\t\t\tprobe,\n\t\t\t`cursor ${JSON.stringify(actual)}, expected ${JSON.stringify(expected)}`,\n\t\t);\n\t}\n}\n\nasync function expectRejects(\n\tprobe: string,\n\tpromise: Promise<unknown>,\n\twhy: string,\n): Promise<void> {\n\tlet rejected = false;\n\ttry {\n\t\tawait promise;\n\t} catch {\n\t\trejected = true;\n\t}\n\tif (!rejected) fail(probe, why);\n}\n\n/** Build the probe list for a harness. Prefer {@link attachSinkConformance}\n * inside a test framework; use this directly for a bare runner. */\nexport function sinkConformanceProbes<Tx>(\n\tharness: SinkConformanceHarness<Tx>,\n): ConformanceProbe[] {\n\t// Every probe starts from a wiped store and an initialized fresh sink.\n\tasync function start(): Promise<ConsumerSink<Tx>> {\n\t\tawait harness.reset();\n\t\tconst sink = await harness.makeSink();\n\t\tawait sink.loadCursor();\n\t\treturn sink;\n\t}\n\tconst commit = (\n\t\tsink: ConsumerSink<Tx>,\n\t\tcursor: string,\n\t\trows: Array<[number, string]>,\n\t) =>\n\t\tsink.commitBatch(cursor, async (tx) => {\n\t\t\tfor (const [height, key] of rows)\n\t\t\t\tawait harness.insertRow(tx, height, key);\n\t\t});\n\n\tconst probes: ConformanceProbe[] = [\n\t\t{\n\t\t\tname: \"fresh store: loadCursor initializes and returns null\",\n\t\t\tasync run() {\n\t\t\t\tawait harness.reset();\n\t\t\t\tconst sink = await harness.makeSink();\n\t\t\t\tconst cursor = await sink.loadCursor();\n\t\t\t\texpectCursor(\"fresh loadCursor\", cursor, null);\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"checkpoint persists: a restarted sink resumes from the committed cursor\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"10:0\", [[10, \"a\"]]);\n\t\t\t\tconst restarted = await harness.makeSink();\n\t\t\t\tconst resumed = await restarted.loadCursor();\n\t\t\t\texpectCursor(\"restart resume\", resumed, \"10:0\");\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"crash between rows and cursor: a throw aborts both (invariants #4/#5)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait expectRejects(\n\t\t\t\t\t\"torn batch\",\n\t\t\t\t\tsink.commitBatch(\"10:0\", async (tx) => {\n\t\t\t\t\t\tawait harness.insertRow(tx, 10, \"a\");\n\t\t\t\t\t\tthrow new Error(\"kill -9\");\n\t\t\t\t\t}),\n\t\t\t\t\t\"commitBatch swallowed the handler throw — it must propagate (invariant #12)\",\n\t\t\t\t);\n\t\t\t\texpectRows(\"torn batch\", await harness.readRows(), []);\n\t\t\t\texpectCursor(\"torn batch\", await harness.readCursor(), null);\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"replaying the same cursor is safe: cursor works as an idempotency key (#7)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"10:0\", [[10, \"a\"]]);\n\t\t\t\t// At-least-once redelivery: same cursor, same rows (deterministic\n\t\t\t\t// replay). Neither duplicate rows nor a corrupted checkpoint.\n\t\t\t\tawait commit(sink, \"10:0\", [[10, \"a\"]]);\n\t\t\t\texpectRows(\"replay\", await harness.readRows(), [\n\t\t\t\t\t{ height: 10, key: \"a\" },\n\t\t\t\t]);\n\t\t\t\texpectCursor(\"replay\", await harness.readCursor(), \"10:0\");\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"rollback deletes at-or-above the fork — inclusive >= (#8/#9)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"11:0\", [\n\t\t\t\t\t[10, \"a\"],\n\t\t\t\t\t[11, \"b\"],\n\t\t\t\t]);\n\t\t\t\tawait sink.rollback(11, \"10:2147483647\");\n\t\t\t\t// The fork block itself (height 11) is deleted: the new canonical\n\t\t\t\t// chain re-supplies it.\n\t\t\t\texpectRows(\"inclusive >=\", await harness.readRows(), [\n\t\t\t\t\t{ height: 10, key: \"a\" },\n\t\t\t\t]);\n\t\t\t\texpectCursor(\n\t\t\t\t\t\"inclusive >=\",\n\t\t\t\t\tawait harness.readCursor(),\n\t\t\t\t\t\"10:2147483647\",\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"rollback then re-read: canonical rows land cleanly after the rewind\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"12:0\", [\n\t\t\t\t\t[10, \"a\"],\n\t\t\t\t\t[11, \"b\"],\n\t\t\t\t\t[12, \"c\"],\n\t\t\t\t]);\n\t\t\t\tawait sink.rollback(11, \"10:2147483647\");\n\t\t\t\texpectRows(\"rollback\", await harness.readRows(), [\n\t\t\t\t\t{ height: 10, key: \"a\" },\n\t\t\t\t]);\n\t\t\t\t// The re-read: the new canonical chain replaces the orphaned rows.\n\t\t\t\tawait commit(sink, \"12:0\", [\n\t\t\t\t\t[11, \"b2\"],\n\t\t\t\t\t[12, \"c2\"],\n\t\t\t\t]);\n\t\t\t\texpectRows(\"re-read\", await harness.readRows(), [\n\t\t\t\t\t{ height: 10, key: \"a\" },\n\t\t\t\t\t{ height: 11, key: \"b2\" },\n\t\t\t\t\t{ height: 12, key: \"c2\" },\n\t\t\t\t]);\n\t\t\t\texpectCursor(\"re-read\", await harness.readCursor(), \"12:0\");\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"rollback is idempotent: re-application after a crash is a no-op (#10)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"11:0\", [\n\t\t\t\t\t[10, \"a\"],\n\t\t\t\t\t[11, \"b\"],\n\t\t\t\t]);\n\t\t\t\tawait sink.rollback(11, \"10:2147483647\");\n\t\t\t\t// The loop dedups reorgs in memory only — a restart re-detects the\n\t\t\t\t// same fork and re-applies the same rollback.\n\t\t\t\tawait sink.rollback(11, \"10:2147483647\");\n\t\t\t\texpectRows(\"idempotent rollback\", await harness.readRows(), [\n\t\t\t\t\t{ height: 10, key: \"a\" },\n\t\t\t\t]);\n\t\t\t\texpectCursor(\n\t\t\t\t\t\"idempotent rollback\",\n\t\t\t\t\tawait harness.readCursor(),\n\t\t\t\t\t\"10:2147483647\",\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"rollback scopes deletion by forkPointHeight, never rewindCursor (#11)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait commit(sink, \"13:0\", [\n\t\t\t\t\t[9, \"a\"],\n\t\t\t\t\t[10, \"b\"],\n\t\t\t\t\t[11, \"c\"],\n\t\t\t\t\t[12, \"d\"],\n\t\t\t\t\t[13, \"e\"],\n\t\t\t\t]);\n\t\t\t\t// A multi-fork page: every call carries the SAME rewindCursor (the\n\t\t\t\t// lowest fork point) with its OWN forkPointHeight. This call's pair\n\t\t\t\t// is deliberately mismatched — fork at 12, rewind below 10. A sink\n\t\t\t\t// deriving the undo range from the cursor deletes 10-11 here.\n\t\t\t\tawait sink.rollback(12, \"9:2147483647\");\n\t\t\t\texpectRows(\"scope by height\", await harness.readRows(), [\n\t\t\t\t\t{ height: 9, key: \"a\" },\n\t\t\t\t\t{ height: 10, key: \"b\" },\n\t\t\t\t\t{ height: 11, key: \"c\" },\n\t\t\t\t]);\n\t\t\t\tawait sink.rollback(10, \"9:2147483647\");\n\t\t\t\texpectRows(\"scope by height (second fork)\", await harness.readRows(), [\n\t\t\t\t\t{ height: 9, key: \"a\" },\n\t\t\t\t]);\n\t\t\t\texpectCursor(\n\t\t\t\t\t\"scope by height\",\n\t\t\t\t\tawait harness.readCursor(),\n\t\t\t\t\t\"9:2147483647\",\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t];\n\n\tif (harness.withLockHeld) {\n\t\tconst withLockHeld = harness.withLockHeld.bind(harness);\n\t\tprobes.push({\n\t\t\tname: \"lock contention: a second writer fails loudly, never interleaves (#13)\",\n\t\t\tasync run() {\n\t\t\t\tconst sink = await start();\n\t\t\t\tawait withLockHeld(async () => {\n\t\t\t\t\tawait expectRejects(\n\t\t\t\t\t\t\"lock contention\",\n\t\t\t\t\t\tsink.commitBatch(\"10:0\", async (tx) => {\n\t\t\t\t\t\t\tawait harness.insertRow(tx, 10, \"a\");\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t\"commitBatch succeeded while another writer held the lock — two replicas would interleave commits\",\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t\texpectRows(\"lock contention\", await harness.readRows(), []);\n\t\t\t\texpectCursor(\"lock contention\", await harness.readCursor(), null);\n\t\t\t},\n\t\t});\n\t}\n\n\treturn probes;\n}\n\n/**\n * Register every conformance probe with a test framework: pass your\n * framework's `test` function (bun:test, vitest, jest — anything with a\n * `(name, fn)` signature). One probe becomes one test case.\n */\nexport function attachSinkConformance<Tx>(\n\ttest: (name: string, fn: () => Promise<void>) => void,\n\tharness: SinkConformanceHarness<Tx>,\n): void {\n\tfor (const probe of sinkConformanceProbes(harness)) {\n\t\ttest(probe.name, () => probe.run());\n\t}\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "5D137D9410DE790C64756E2164756E21",
9
+ "names": []
10
+ }