@secondlayer/sdk 9.0.2 → 10.1.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.
- package/AGENTS.md +85 -17
- package/README.md +98 -31
- package/dist/index.d.ts +234 -113
- package/dist/index.js +768 -436
- package/dist/index.js.map +19 -19
- package/dist/sinks/bun-sqlite.d.ts +18 -4
- package/dist/sinks/bun-sqlite.js +21 -20
- package/dist/sinks/bun-sqlite.js.map +5 -5
- package/dist/sinks/core.d.ts +30 -7
- package/dist/sinks/core.js +16 -19
- package/dist/sinks/core.js.map +4 -4
- package/dist/sinks/drizzle.d.ts +23 -6
- package/dist/sinks/drizzle.js +22 -21
- package/dist/sinks/drizzle.js.map +5 -5
- package/dist/sinks/kysely.d.ts +26 -11
- package/dist/sinks/kysely.js +23 -21
- package/dist/sinks/kysely.js.map +5 -5
- package/dist/sinks/testing.d.ts +4 -1
- package/dist/sinks/testing.js +14 -1
- package/dist/sinks/testing.js.map +3 -3
- package/dist/streams/index.d.ts +92 -23
- package/dist/streams/index.js +395 -183
- package/dist/streams/index.js.map +12 -12
- package/dist/subgraphs/index.d.ts +202 -94
- package/dist/subgraphs/index.js +764 -434
- package/dist/subgraphs/index.js.map +17 -17
- package/package.json +4 -4
package/dist/sinks/testing.js
CHANGED
|
@@ -96,6 +96,19 @@ function sinkConformanceProbes(harness) {
|
|
|
96
96
|
expectCursor("inclusive >=", await harness.readCursor(), "10:2147483647");
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
|
+
{
|
|
100
|
+
name: "rollback to genesis (null rewind) deletes every row and clears the checkpoint",
|
|
101
|
+
async run() {
|
|
102
|
+
const sink = await start();
|
|
103
|
+
await commit(sink, "11:0", [
|
|
104
|
+
[10, "a"],
|
|
105
|
+
[11, "b"]
|
|
106
|
+
]);
|
|
107
|
+
await sink.rollback(0, null);
|
|
108
|
+
expectRows("genesis rewind", await harness.readRows(), []);
|
|
109
|
+
expectCursor("genesis rewind", await harness.readCursor(), null);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
99
112
|
{
|
|
100
113
|
name: "rollback then re-read: canonical rows land cleanly after the rewind",
|
|
101
114
|
async run() {
|
|
@@ -190,5 +203,5 @@ export {
|
|
|
190
203
|
attachSinkConformance
|
|
191
204
|
};
|
|
192
205
|
|
|
193
|
-
//# debugId=
|
|
206
|
+
//# debugId=2A425478905AA1C864756E2164756E21
|
|
194
207
|
//# sourceMappingURL=testing.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["src/sinks/testing.ts"],
|
|
4
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"
|
|
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 to genesis (null rewind) deletes every row and clears the checkpoint\",\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\t// A fork at block 0 has no cursor to rewind to: the checkpoint row\n\t\t\t\t// goes away, so a restart resumes from the very beginning.\n\t\t\t\tawait sink.rollback(0, null);\n\t\t\t\texpectRows(\"genesis rewind\", await harness.readRows(), []);\n\t\t\t\texpectCursor(\"genesis rewind\", await harness.readCursor(), null);\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
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "2A425478905AA1C864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/streams/index.d.ts
CHANGED
|
@@ -115,8 +115,11 @@ interface ConsumerSink<Tx = unknown> {
|
|
|
115
115
|
* same `rewindCursor` (the lowest fork point) but its own
|
|
116
116
|
* `forkPointHeight` — scope the undo by `forkPointHeight` only (contract
|
|
117
117
|
* invariant #11), and expect re-application after a crash (#10).
|
|
118
|
+
*
|
|
119
|
+
* `rewindCursor` is `null` only for a fork at genesis: clear the
|
|
120
|
+
* checkpoint so the next run starts from the first event.
|
|
118
121
|
*/
|
|
119
|
-
rollback(forkPointHeight: number, rewindCursor: string): Promise<void>;
|
|
122
|
+
rollback(forkPointHeight: number, rewindCursor: string | null): Promise<void>;
|
|
120
123
|
}
|
|
121
124
|
/** `ctx` gains `tx` exactly when a sink is attached; without one the shape
|
|
122
125
|
* is unchanged (an intersection with `unknown` is an identity, so
|
|
@@ -356,11 +359,41 @@ type StreamsEventsSubscribeParams = {
|
|
|
356
359
|
filters?: StreamsFilterMap;
|
|
357
360
|
/** Abort to unsubscribe (the returned function does the same). */
|
|
358
361
|
signal?: AbortSignal;
|
|
359
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* Called for each pushed event, in order. The resume cursor advances only
|
|
364
|
+
* after this resolves, so a handler that throws sees the same event again
|
|
365
|
+
* on reconnect (at-least-once). Key durable writes by `cursor`.
|
|
366
|
+
*/
|
|
360
367
|
onEvent: (event: StreamsEvent) => void | Promise<void>;
|
|
361
|
-
/**
|
|
362
|
-
*
|
|
368
|
+
/**
|
|
369
|
+
* Called on every failure. Transport errors (dropped socket, 5xx, 429, a
|
|
370
|
+
* stale connection, a throwing handler) reconnect from the last handled
|
|
371
|
+
* cursor with exponential backoff. Errors that a retry cannot fix (401,
|
|
372
|
+
* 4xx, a bad signature) end the subscription and reject its `done`.
|
|
373
|
+
*/
|
|
363
374
|
onError?: (err: unknown) => void;
|
|
375
|
+
/**
|
|
376
|
+
* First reconnect pause in ms (default 1000). Doubles per consecutive
|
|
377
|
+
* failure up to 30 s with jitter, resets once a frame arrives, and never
|
|
378
|
+
* undercuts a `Retry-After`.
|
|
379
|
+
*/
|
|
380
|
+
reconnectDelayMs?: number;
|
|
381
|
+
/**
|
|
382
|
+
* Reconnect when no frame (pings included) arrives for this long (default
|
|
383
|
+
* 60000, three server heartbeats). Catches a half-open socket that never
|
|
384
|
+
* errors; set it above your instance's heartbeat interval.
|
|
385
|
+
*/
|
|
386
|
+
staleAfterMs?: number;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* Handle for a live subscription: call it to unsubscribe. `done` resolves
|
|
390
|
+
* after unsubscribe (or the signal aborting) and rejects with the error that
|
|
391
|
+
* ended the loop when a retry could not fix it, so a worker can `await`
|
|
392
|
+
* it instead of polling for silence.
|
|
393
|
+
*/
|
|
394
|
+
type StreamsSubscription = {
|
|
395
|
+
(): void;
|
|
396
|
+
done: Promise<void>;
|
|
364
397
|
};
|
|
365
398
|
/**
|
|
366
399
|
* The checkpoint the SDK computes for a batch. Persist `cursor` inside the same
|
|
@@ -425,8 +458,11 @@ type StreamsBatchContext = ConsumerBatchContext;
|
|
|
425
458
|
* The checkpoint for a reorg rollback. Persist `cursor` (the rewind position)
|
|
426
459
|
* inside the same transaction as your rollback so the two commit atomically.
|
|
427
460
|
*/
|
|
461
|
+
/** `cursor` is the rewind position the loop resumes from after `onReorg`:
|
|
462
|
+
* the foot of the fork point, or `null` for a fork at genesis. Persist it
|
|
463
|
+
* in the same transaction as the rollback. */
|
|
428
464
|
type StreamsReorgContext = {
|
|
429
|
-
cursor: string;
|
|
465
|
+
cursor: string | null;
|
|
430
466
|
};
|
|
431
467
|
type StreamsEventsConsumeParams<
|
|
432
468
|
TTx = never,
|
|
@@ -446,7 +482,8 @@ type StreamsEventsConsumeParams<
|
|
|
446
482
|
* the loop resumes from the sink's committed cursor, `onBatch` receives
|
|
447
483
|
* `ctx.tx` and must write ONLY through it (rows and cursor commit in one
|
|
448
484
|
* transaction), reorg rollback is automatic, and `onBatch`'s return value
|
|
449
|
-
* is ignored.
|
|
485
|
+
* is ignored. Folds (balances) invert in the sink's `onRollback`, not in
|
|
486
|
+
* `onReorg`.
|
|
450
487
|
*/
|
|
451
488
|
sink?: ConsumerSink<TTx>;
|
|
452
489
|
/** Fires once per page, before `onBatch` and before any early return. */
|
|
@@ -494,12 +531,23 @@ type StreamsEventsConsumeParams<
|
|
|
494
531
|
onBatch?: (events: D extends true ? IndexEvent[] : StreamsEvent[], envelope: StreamsEventsEnvelope, ctx: StreamsBatchContext & WithSinkTx<TTx>) => void | string | null | undefined | Promise<void> | Promise<string | null | undefined>;
|
|
495
532
|
/**
|
|
496
533
|
* Roll your projection back to `reorg.fork_point_height`, persisting
|
|
497
|
-
* `ctx.cursor` in the same transaction. Called once per *new* reorg
|
|
498
|
-
* (deduped in-memory, fork-ascending) before the SDK
|
|
499
|
-
* now-canonical events.
|
|
500
|
-
*
|
|
534
|
+
* `ctx.cursor` in the same transaction. Called once per *new* reorg at or
|
|
535
|
+
* below the checkpoint (deduped in-memory, fork-ascending) before the SDK
|
|
536
|
+
* rewinds and re-reads the now-canonical events. A reorg whose fork sits
|
|
537
|
+
* above the checkpoint is not a rollback (nothing past it was written) and
|
|
538
|
+
* is skipped. Omit it to ignore reorgs (events stay canonical, but stale
|
|
539
|
+
* rows from an orphaned fork are left in place).
|
|
540
|
+
*
|
|
541
|
+
* With `onReorg` or a `sink` attached, every empty page also polls
|
|
542
|
+
* `reorgs.list` (one extra request per idle poll) so a fork that lands
|
|
543
|
+
* while the consumer idles at the tip is rolled back too.
|
|
501
544
|
*/
|
|
502
545
|
onReorg?: (reorg: StreamsReorg, ctx: StreamsReorgContext) => Promise<void> | void;
|
|
546
|
+
/** Deepest rewind one reorg may make below the checkpoint before the loop
|
|
547
|
+
* refuses with `ValidationError`. Default 1000 blocks. The fork point is
|
|
548
|
+
* server supplied and drives a delete on every declared table, so raise
|
|
549
|
+
* this only for a source you trust. */
|
|
550
|
+
maxRollbackDepth?: number;
|
|
503
551
|
/** Page-fetch retries after the first failure (429/5xx/network only —
|
|
504
552
|
* 4xx and handler throws always propagate). Default 3; `0` disables. */
|
|
505
553
|
retryCount?: number;
|
|
@@ -562,8 +610,16 @@ type StreamsEventsReplayParams = {
|
|
|
562
610
|
* Called once per finalized dump file, in block order, before live tailing.
|
|
563
611
|
* Process the parquet with your own tooling (e.g. DuckDB) — the SDK does not
|
|
564
612
|
* decode parquet. Use `client.dumps.download(file)` to fetch + verify bytes.
|
|
613
|
+
*
|
|
614
|
+
* Delivery is file-granular and at-least-once: a file whose range straddles
|
|
615
|
+
* `from` is handed over whole. `ctx.from` is that cursor (`null` from
|
|
616
|
+
* genesis); skip rows at or below it, or key rows by `cursor` so a re-run
|
|
617
|
+
* is an idempotent no-op. Files ending at or below `from` are not handed
|
|
618
|
+
* over at all.
|
|
565
619
|
*/
|
|
566
|
-
onDumpFile: (file: StreamsDumpFile
|
|
620
|
+
onDumpFile: (file: StreamsDumpFile, ctx: {
|
|
621
|
+
from: string | null;
|
|
622
|
+
}) => Promise<void> | void;
|
|
567
623
|
/** Called per live page after the dump phase, like `consume`. */
|
|
568
624
|
onBatch: (events: StreamsEvent[], envelope: StreamsEventsEnvelope) => Promise<string | null | undefined> | string | null | undefined;
|
|
569
625
|
/**
|
|
@@ -699,9 +755,11 @@ type StreamsClient = {
|
|
|
699
755
|
* Subscribe to the real-time SSE push surface. Calls `onEvent` for each new
|
|
700
756
|
* canonical event as the server pushes it (chain cadence, not poll-bounded),
|
|
701
757
|
* and verifies each frame's inline ed25519 signature when the client was
|
|
702
|
-
* created with `verify`. Returns an unsubscribe function
|
|
758
|
+
* created with `verify`. Returns an unsubscribe function whose `done`
|
|
759
|
+
* settles when the loop ends. Not reorg-aware: an event delivered then
|
|
760
|
+
* orphaned is never retracted. Durable writers use `consume()`.
|
|
703
761
|
*/
|
|
704
|
-
subscribe(params: StreamsEventsSubscribeParams):
|
|
762
|
+
subscribe(params: StreamsEventsSubscribeParams): StreamsSubscription;
|
|
705
763
|
};
|
|
706
764
|
blocks: {
|
|
707
765
|
events(heightOrHash: number | string): Promise<StreamsEventsListEnvelope>;
|
|
@@ -717,6 +775,8 @@ type StreamsClient = {
|
|
|
717
775
|
type CreateStreamsClientOptions = {
|
|
718
776
|
apiKey?: string;
|
|
719
777
|
baseUrl?: string;
|
|
778
|
+
/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */
|
|
779
|
+
origin?: "cli" | "mcp" | "session";
|
|
720
780
|
fetchImpl?: FetchLike2;
|
|
721
781
|
/**
|
|
722
782
|
* Public base URL for bulk parquet dumps (the R2/CDN bucket root). Required
|
|
@@ -844,26 +904,30 @@ declare class ApiError extends SecondLayerError {
|
|
|
844
904
|
body?: unknown;
|
|
845
905
|
constructor(status: number, message: string, body?: unknown, code?: string, options?: SecondLayerErrorOptions);
|
|
846
906
|
}
|
|
847
|
-
/** Thrown on a 401
|
|
907
|
+
/** Thrown on a 401 by both the instance clients and Streams. Carries the
|
|
908
|
+
* server's `{error, code}` envelope when it sent one, so a revoked token reads
|
|
909
|
+
* differently from a missing one. */
|
|
848
910
|
declare class AuthError extends ApiError {
|
|
849
911
|
readonly status: 401;
|
|
850
|
-
constructor(message?: string);
|
|
912
|
+
constructor(message?: string, body?: unknown, code?: string);
|
|
851
913
|
}
|
|
852
|
-
/** Thrown on a 429
|
|
914
|
+
/** Thrown on a 429 by both the instance clients and Streams. `retryable`,
|
|
853
915
|
* with `retryAfterSeconds` parsed from the `Retry-After` header when sent. */
|
|
854
916
|
declare class RateLimitError extends ApiError {
|
|
855
917
|
/** Raw `Retry-After` header value (seconds or HTTP-date). */
|
|
856
918
|
readonly retryAfter?: string | undefined;
|
|
857
919
|
readonly status: 429;
|
|
858
|
-
constructor(message?: string, retryAfter?: string | undefined);
|
|
920
|
+
constructor(message?: string, retryAfter?: string | undefined, body?: unknown, code?: string);
|
|
859
921
|
}
|
|
860
|
-
/** Thrown on a 4xx the caller can fix (bad cursor, bad params). Never retried.
|
|
922
|
+
/** Thrown on a 4xx the caller can fix (bad cursor, bad params). Never retried.
|
|
923
|
+
* `code` is the server's machine-readable code when the envelope carried one. */
|
|
861
924
|
declare class ValidationError extends ApiError {
|
|
862
|
-
constructor(message: string, status: number, body?: unknown);
|
|
925
|
+
constructor(message: string, status: number, body?: unknown, code?: string);
|
|
863
926
|
}
|
|
864
|
-
/** Thrown on a 5xx from the Streams API
|
|
927
|
+
/** Thrown on a 5xx from the Streams API, and on a failed signing-key fetch.
|
|
928
|
+
* `retryable`: the page retry policy tries again. */
|
|
865
929
|
declare class StreamsServerError extends ApiError {
|
|
866
|
-
constructor(message: string, status: number, body?: unknown);
|
|
930
|
+
constructor(message: string, status: number, body?: unknown, code?: string);
|
|
867
931
|
}
|
|
868
932
|
/** Thrown when response signature verification is enabled and fails. */
|
|
869
933
|
declare class StreamsSignatureError extends SecondLayerError {
|
|
@@ -895,12 +959,17 @@ declare const Cursor: {
|
|
|
895
959
|
* every reorg. The sentinel is int4 max (the `event_index`/`tx_index` column
|
|
896
960
|
* type), larger than any real index, so nothing at `height - 1` survives the
|
|
897
961
|
* keyset and the next returned row is exactly `(height, 0)`.
|
|
962
|
+
*
|
|
963
|
+
* `atHeight(0)` is `null`: the pre-genesis position both consume loops
|
|
964
|
+
* accept as "read everything from the first event". There is no string
|
|
965
|
+
* cursor below `0:0`, and `0:0` itself is exclusive, so returning it here
|
|
966
|
+
* skipped the genesis event.
|
|
898
967
|
*/
|
|
899
|
-
atHeight(height: number): string;
|
|
968
|
+
atHeight(height: number): string | null;
|
|
900
969
|
/** Parse a `<block>:<index>` cursor. Throws `ValidationError` if malformed. */
|
|
901
970
|
parse(cursor: string): {
|
|
902
971
|
blockHeight: number;
|
|
903
972
|
eventIndex: number;
|
|
904
973
|
};
|
|
905
974
|
};
|
|
906
|
-
export { isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decode, createStreamsClient, ValidationError, StreamsTip, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsSubscribeParams, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsConsumeParams, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StreamsBatch, STREAMS_EVENT_TYPES, RateLimitError, NftTransferPayload2 as NftTransferPayload, NftTransferEvent, FtTransferPayload2 as FtTransferPayload, FtTransferEvent, FetchLike2 as FetchLike, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, Cursor, ConsumerBatchContext, AuthError };
|
|
975
|
+
export { isStxTransfer, isStxMint, isStxLock, isStxBurn, isPrint, isNftTransfer, isNftMint, isNftBurn, isFtTransfer, isFtMint, isFtBurn, decodeStxTransfer, decodeStxMint, decodeStxLock, decodeStxBurn, decodePrint, decodeNftTransfer, decodeNftMint, decodeNftBurn, decodeFtTransfer, decodeFtMint, decodeFtBurn, decode, createStreamsClient, ValidationError, StreamsTip, StreamsSubscription, StreamsSignatureError, StreamsServerError, StreamsReorgsListParams, StreamsReorgsListEnvelope, StreamsReorgContext, StreamsReorg, StreamsEventsSubscribeParams, StreamsEventsStreamParams, StreamsEventsListParams, StreamsEventsListEnvelope, StreamsEventsEnvelope, StreamsEventsConsumeResult, StreamsEventsConsumeParams, StreamsEventType, StreamsEventPayload, StreamsEvent, StreamsDumpsManifest, StreamsDumps, StreamsDumpFile, StreamsConsumeParams, StreamsClient, StreamsCanonicalBlock, StreamsBatchContext, StreamsBatch, STREAMS_EVENT_TYPES, RateLimitError, NftTransferPayload2 as NftTransferPayload, NftTransferEvent, FtTransferPayload2 as FtTransferPayload, FtTransferEvent, FetchLike2 as FetchLike, DecodedStxTransferPayload, DecodedStxTransfer, DecodedStxMintPayload, DecodedStxMint, DecodedStxLockPayload, DecodedStxLock, DecodedStxBurnPayload, DecodedStxBurn, DecodedPrintValue, DecodedPrintPayload, DecodedPrint, DecodedNftTransferPayload, DecodedNftTransfer, DecodedNftMintPayload, DecodedNftMint, DecodedNftBurnPayload, DecodedNftBurn, DecodedFtTransferPayload, DecodedFtTransfer, DecodedFtMintPayload, DecodedFtMint, DecodedFtBurnPayload, DecodedFtBurn, DecodedEventRow, DecodedEventColumns, Cursor, ConsumerBatchContext, AuthError };
|