@workglow/test-contract 0.6.1

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 (149) hide show
  1. package/README.md +232 -0
  2. package/dist/ai-provider/assertions/capabilityHonesty.d.ts +7 -0
  3. package/dist/ai-provider/assertions/dispose.d.ts +7 -0
  4. package/dist/ai-provider/assertions/inferAdvertisesRegistered.d.ts +15 -0
  5. package/dist/ai-provider/assertions/inferServesInferred.d.ts +20 -0
  6. package/dist/ai-provider/assertions/pricingMatchesModality.d.ts +36 -0
  7. package/dist/ai-provider/assertions/registryCoverage.d.ts +13 -0
  8. package/dist/ai-provider/assertions/sessionReuse.d.ts +7 -0
  9. package/dist/ai-provider/assertions/signalHonoring.d.ts +7 -0
  10. package/dist/ai-provider/assertions/structuredGeneration.d.ts +7 -0
  11. package/dist/ai-provider/assertions/textGenerationSmoke.d.ts +7 -0
  12. package/dist/ai-provider/assertions/toolCallAccumulator.d.ts +7 -0
  13. package/dist/ai-provider/assertions/toolCallMultiTurn.d.ts +7 -0
  14. package/dist/ai-provider/assertions/usageNormalization.d.ts +61 -0
  15. package/dist/ai-provider/fixtures.d.ts +8 -0
  16. package/dist/ai-provider/runAiProviderConformance.d.ts +7 -0
  17. package/dist/ai-provider/types.d.ts +75 -0
  18. package/dist/ai-provider.d.ts +25 -0
  19. package/dist/ai-provider.js +876 -0
  20. package/dist/ai-provider.js.map +26 -0
  21. package/dist/browser-context/ConformanceMockContext.d.ts +54 -0
  22. package/dist/browser-context/assertions/ariaRoundTrip.d.ts +7 -0
  23. package/dist/browser-context/assertions/capabilityHonesty.d.ts +7 -0
  24. package/dist/browser-context/assertions/itExpectFail.d.ts +8 -0
  25. package/dist/browser-context/assertions/networkIntrospection.d.ts +7 -0
  26. package/dist/browser-context/assertions/tabsLifecycle.d.ts +7 -0
  27. package/dist/browser-context/fixtures.d.ts +8 -0
  28. package/dist/browser-context/runIBrowserContextConformance.d.ts +7 -0
  29. package/dist/browser-context/types.d.ts +48 -0
  30. package/dist/browser-context.d.ts +18 -0
  31. package/dist/browser-context.js +567 -0
  32. package/dist/browser-context.js.map +17 -0
  33. package/dist/creditExhaustedSkip.d.ts +39 -0
  34. package/dist/creditExhaustedSkip.js +163 -0
  35. package/dist/creditExhaustedSkip.js.map +10 -0
  36. package/dist/entitlement-profile/assertions/denialShape.d.ts +13 -0
  37. package/dist/entitlement-profile/assertions/dispose.d.ts +16 -0
  38. package/dist/entitlement-profile/assertions/hierarchyHonoring.d.ts +11 -0
  39. package/dist/entitlement-profile/assertions/optionalNeverDenied.d.ts +11 -0
  40. package/dist/entitlement-profile/assertions/requestEntitlementShape.d.ts +12 -0
  41. package/dist/entitlement-profile/assertions/resourceScoping.d.ts +16 -0
  42. package/dist/entitlement-profile/assertions/subscribeGrant.d.ts +11 -0
  43. package/dist/entitlement-profile/assertions/subscribeReload.d.ts +11 -0
  44. package/dist/entitlement-profile/assertions/subscribeRevocation.d.ts +12 -0
  45. package/dist/entitlement-profile/assertions/surfaceCoverage.d.ts +7 -0
  46. package/dist/entitlement-profile/assertions/unsubscribeIdempotent.d.ts +7 -0
  47. package/dist/entitlement-profile/fixtures.d.ts +21 -0
  48. package/dist/entitlement-profile/runEntitlementProfileConformance.d.ts +7 -0
  49. package/dist/entitlement-profile/types.d.ts +45 -0
  50. package/dist/entitlement-profile.d.ts +23 -0
  51. package/dist/entitlement-profile.js +436 -0
  52. package/dist/entitlement-profile.js.map +22 -0
  53. package/dist/human-connector/MockHumanConnector.d.ts +29 -0
  54. package/dist/human-connector/assertions/abort.d.ts +7 -0
  55. package/dist/human-connector/assertions/capabilityHonesty.d.ts +7 -0
  56. package/dist/human-connector/assertions/concurrentIsolation.d.ts +7 -0
  57. package/dist/human-connector/assertions/multiTurnFollowUp.d.ts +7 -0
  58. package/dist/human-connector/assertions/notifyDisplayFastResolve.d.ts +7 -0
  59. package/dist/human-connector/assertions/roundtrip.d.ts +7 -0
  60. package/dist/human-connector/fixtures.d.ts +8 -0
  61. package/dist/human-connector/runHumanConnectorConformance.d.ts +9 -0
  62. package/dist/human-connector/types.d.ts +91 -0
  63. package/dist/human-connector.d.ts +19 -0
  64. package/dist/human-connector.js +843 -0
  65. package/dist/human-connector.js.map +20 -0
  66. package/dist/itExpectFail.d.ts +15 -0
  67. package/dist/itExpectFail.js +180 -0
  68. package/dist/itExpectFail.js.map +11 -0
  69. package/dist/storage-migrations/assertions/appliesAndRecords.d.ts +7 -0
  70. package/dist/storage-migrations/assertions/concurrentRunsSerialize.d.ts +18 -0
  71. package/dist/storage-migrations/assertions/ensureBookkeepingIdempotent.d.ts +7 -0
  72. package/dist/storage-migrations/assertions/failedMigrationLeavesNoPartialSchema.d.ts +14 -0
  73. package/dist/storage-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
  74. package/dist/storage-migrations/assertions/idempotentRun.d.ts +7 -0
  75. package/dist/storage-migrations/assertions/incrementalApplication.d.ts +7 -0
  76. package/dist/storage-migrations/runMigrationRunnerContract.d.ts +9 -0
  77. package/dist/storage-migrations/types.d.ts +78 -0
  78. package/dist/storage-migrations.d.ts +18 -0
  79. package/dist/storage-migrations.js +473 -0
  80. package/dist/storage-migrations.js.map +20 -0
  81. package/dist/tabular-migrations/assertions/addAndDropIndex.d.ts +7 -0
  82. package/dist/tabular-migrations/assertions/addColumn.d.ts +7 -0
  83. package/dist/tabular-migrations/assertions/backfill.d.ts +7 -0
  84. package/dist/tabular-migrations/assertions/dropColumn.d.ts +7 -0
  85. package/dist/tabular-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
  86. package/dist/tabular-migrations/assertions/freshDbFastPath.d.ts +7 -0
  87. package/dist/tabular-migrations/assertions/incrementalApplication.d.ts +7 -0
  88. package/dist/tabular-migrations/assertions/renameColumn.d.ts +7 -0
  89. package/dist/tabular-migrations/runTabularMigrationContract.d.ts +8 -0
  90. package/dist/tabular-migrations/types.d.ts +28 -0
  91. package/dist/tabular-migrations.d.ts +19 -0
  92. package/dist/tabular-migrations.js +257 -0
  93. package/dist/tabular-migrations.js.map +18 -0
  94. package/dist/tabular-storage/assertions/countMatchesQuery.d.ts +7 -0
  95. package/dist/tabular-storage/assertions/guardParity.d.ts +25 -0
  96. package/dist/tabular-storage/assertions/inListCriterion.d.ts +14 -0
  97. package/dist/tabular-storage/assertions/joinBoundedLeftRead.d.ts +29 -0
  98. package/dist/tabular-storage/assertions/joinPushdown.d.ts +13 -0
  99. package/dist/tabular-storage/assertions/notInListCriterion.d.ts +15 -0
  100. package/dist/tabular-storage/assertions/strategyParity.d.ts +31 -0
  101. package/dist/tabular-storage/assertions/subscribeToChanges.d.ts +21 -0
  102. package/dist/tabular-storage/assertions/vectorColumnFormat.d.ts +7 -0
  103. package/dist/tabular-storage/assertions/withConnectionTransaction.d.ts +7 -0
  104. package/dist/tabular-storage/assertions/withTransactionRollback.d.ts +7 -0
  105. package/dist/tabular-storage/fixtures.d.ts +33 -0
  106. package/dist/tabular-storage/joinFixtures.d.ts +66 -0
  107. package/dist/tabular-storage/runTabularJoinContract.d.ts +24 -0
  108. package/dist/tabular-storage/runTabularStorageContract.d.ts +9 -0
  109. package/dist/tabular-storage/types.d.ts +102 -0
  110. package/dist/tabular-storage.d.ts +25 -0
  111. package/dist/tabular-storage.js +1477 -0
  112. package/dist/tabular-storage.js.map +27 -0
  113. package/dist/vector-storage/assertions/dimensionValidation.d.ts +17 -0
  114. package/dist/vector-storage/assertions/legacyEncoding.d.ts +16 -0
  115. package/dist/vector-storage/assertions/metadataFilter.d.ts +16 -0
  116. package/dist/vector-storage/assertions/searchOptions.d.ts +16 -0
  117. package/dist/vector-storage/assertions/shared.d.ts +23 -0
  118. package/dist/vector-storage/assertions/similarityRanking.d.ts +18 -0
  119. package/dist/vector-storage/assertions/similaritySearchEvent.d.ts +16 -0
  120. package/dist/vector-storage/assertions/vectorRoundTrip.d.ts +17 -0
  121. package/dist/vector-storage/runVectorStorageContract.d.ts +20 -0
  122. package/dist/vector-storage/types.d.ts +87 -0
  123. package/dist/vector-storage.d.ts +19 -0
  124. package/dist/vector-storage.js +412 -0
  125. package/dist/vector-storage.js.map +21 -0
  126. package/dist/web-search/assertions/capabilityAgreement.d.ts +23 -0
  127. package/dist/web-search/assertions/credentialNaming.d.ts +21 -0
  128. package/dist/web-search/assertions/dateFiltering.d.ts +22 -0
  129. package/dist/web-search/assertions/domainNormalization.d.ts +24 -0
  130. package/dist/web-search/assertions/maxResultsCeiling.d.ts +22 -0
  131. package/dist/web-search/assertions/reportedQuery.d.ts +18 -0
  132. package/dist/web-search/assertions/signalThreading.d.ts +20 -0
  133. package/dist/web-search/fixtures.d.ts +40 -0
  134. package/dist/web-search/runWebSearchProviderConformance.d.ts +24 -0
  135. package/dist/web-search/types.d.ts +79 -0
  136. package/dist/web-search.d.ts +19 -0
  137. package/dist/web-search.js +560 -0
  138. package/dist/web-search.js.map +18 -0
  139. package/dist/worker-proxy/assertions/backlogOrdering.d.ts +7 -0
  140. package/dist/worker-proxy/assertions/disposeTerminatesWorker.d.ts +8 -0
  141. package/dist/worker-proxy/assertions/errorPropagation.d.ts +7 -0
  142. package/dist/worker-proxy/assertions/providerCallHelpers.d.ts +23 -0
  143. package/dist/worker-proxy/browserOnlyStub.d.ts +7 -0
  144. package/dist/worker-proxy/runWorkerProxyBoundary.d.ts +7 -0
  145. package/dist/worker-proxy/types.d.ts +47 -0
  146. package/dist/worker-proxy.d.ts +16 -0
  147. package/dist/worker-proxy.js +389 -0
  148. package/dist/worker-proxy.js.map +17 -0
  149. package/package.json +120 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularMigrationContractHandle, TabularMigrationContractOpts } from "../types";
7
+ export declare function freshDbFastPathBlock(opts: TabularMigrationContractOpts, getHandle: () => TabularMigrationContractHandle): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularMigrationContractHandle, TabularMigrationContractOpts } from "../types";
7
+ export declare function incrementalApplicationBlock(_opts: TabularMigrationContractOpts, getHandle: () => TabularMigrationContractHandle): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularMigrationContractHandle, TabularMigrationContractOpts } from "../types";
7
+ export declare function renameColumnBlock(_opts: TabularMigrationContractOpts, getHandle: () => TabularMigrationContractHandle): void;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularMigrationContractOpts } from "./types";
7
+ export declare function runTabularMigrationContract(opts: TabularMigrationContractOpts): void;
8
+ export type { TabularMigrationContractHandle, TabularMigrationContractOpts } from "./types";
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { AnyTabularStorage, ITabularMigration } from "@workglow/storage";
7
+ export interface TabularMigrationContractHandle {
8
+ /**
9
+ * Build a fresh storage at the given target schema, with the supplied
10
+ * migrations and an optional pre-populated set of rows. Calls
11
+ * `setupDatabase` before returning.
12
+ *
13
+ * The schema's properties at minimum include `id: string` (PK) plus
14
+ * whatever extra columns the test's migration ops mention.
15
+ */
16
+ makeStorage(properties: Record<string, unknown>, migrations: ReadonlyArray<ITabularMigration>, preExistingRows?: ReadonlyArray<Record<string, unknown>>): Promise<AnyTabularStorage>;
17
+ dispose(): Promise<void>;
18
+ }
19
+ export interface TabularMigrationContractOpts {
20
+ readonly name: string;
21
+ readonly factory: () => Promise<TabularMigrationContractHandle>;
22
+ readonly skip?: boolean;
23
+ readonly timeout?: number;
24
+ /** True for SQL backends: DDL is enforced; addColumn produces a real column. */
25
+ readonly enforcesDdl: boolean;
26
+ /** True for FsFolder, IDB, SQLite-on-disk: bookkeeping survives restart. */
27
+ readonly persistentBookkeeping?: boolean;
28
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Conformance suite for tabular schema migrations — add, drop, rename and
8
+ * backfill against a live table.
9
+ */
10
+ export * from "./tabular-migrations/runTabularMigrationContract";
11
+ export * from "./tabular-migrations/types";
12
+ export * from "./tabular-migrations/assertions/addAndDropIndex";
13
+ export * from "./tabular-migrations/assertions/addColumn";
14
+ export * from "./tabular-migrations/assertions/backfill";
15
+ export * from "./tabular-migrations/assertions/dropColumn";
16
+ export * from "./tabular-migrations/assertions/failedMigrationNotRecorded";
17
+ export * from "./tabular-migrations/assertions/freshDbFastPath";
18
+ export * from "./tabular-migrations/assertions/incrementalApplication";
19
+ export * from "./tabular-migrations/assertions/renameColumn";
@@ -0,0 +1,257 @@
1
+ // src/tabular-migrations/runTabularMigrationContract.ts
2
+ import { afterAll, beforeAll, describe } from "vitest";
3
+
4
+ // src/tabular-migrations/assertions/addAndDropIndex.ts
5
+ import { expect, it } from "vitest";
6
+ function addAndDropIndexBlock(_opts, getHandle) {
7
+ it("addIndex / dropIndex: query still returns correct rows", async () => {
8
+ const storage = await getHandle().makeStorage({ id: { type: "string" }, name: { type: "string" } }, [
9
+ {
10
+ version: 1,
11
+ ops: [
12
+ { kind: "addIndex", name: "idx_name", columns: ["name"] },
13
+ { kind: "dropIndex", name: "idx_name" }
14
+ ]
15
+ }
16
+ ], [
17
+ { id: "u1", name: "alice" },
18
+ { id: "u2", name: "bob" }
19
+ ]);
20
+ const rows = await storage.query({ name: "alice" }) ?? [];
21
+ expect(rows.map((r) => r.id)).toEqual(["u1"]);
22
+ });
23
+ }
24
+
25
+ // src/tabular-migrations/assertions/addColumn.ts
26
+ import { expect as expect2, it as it2 } from "vitest";
27
+ function addColumnBlock(_opts, getHandle) {
28
+ it2("addColumn: existing rows survive; new column readable", async () => {
29
+ const storage = await getHandle().makeStorage({
30
+ id: { type: "string" },
31
+ name: { type: "string" },
32
+ archived: { anyOf: [{ type: "boolean" }, { type: "null" }] }
33
+ }, [
34
+ {
35
+ version: 1,
36
+ ops: [
37
+ {
38
+ kind: "addColumn",
39
+ name: "archived",
40
+ schema: { anyOf: [{ type: "boolean" }, { type: "null" }] }
41
+ }
42
+ ]
43
+ }
44
+ ], [{ id: "u1", name: "alice" }]);
45
+ const row = await storage.get({ id: "u1" });
46
+ expect2(row).toBeDefined();
47
+ expect2(row.id).toBe("u1");
48
+ expect2(row.archived ?? null).toBeNull();
49
+ });
50
+ }
51
+
52
+ // src/tabular-migrations/assertions/backfill.ts
53
+ import { expect as expect3, it as it3 } from "vitest";
54
+ function backfillBlock(_opts, getHandle) {
55
+ it3("backfill: every existing row is rewritten", async () => {
56
+ const rows = Array.from({ length: 30 }, (_, i) => ({ id: `r${i}`, n: i }));
57
+ const storage = await getHandle().makeStorage({ id: { type: "string" }, n: { type: "integer" } }, [
58
+ {
59
+ version: 1,
60
+ ops: [
61
+ {
62
+ kind: "backfill",
63
+ batchSize: 7,
64
+ transform: (row) => ({ ...row, n: row.n * 2 })
65
+ }
66
+ ]
67
+ }
68
+ ], rows);
69
+ const all = await storage.getAll() ?? [];
70
+ const r3 = all.find((r) => r.id === "r3");
71
+ expect3(r3).toBeDefined();
72
+ expect3(r3.n).toBe(6);
73
+ expect3(all.length).toBe(30);
74
+ });
75
+ }
76
+
77
+ // src/tabular-migrations/assertions/dropColumn.ts
78
+ import { expect as expect4, it as it4 } from "vitest";
79
+ function dropColumnBlock(_opts, getHandle) {
80
+ it4("dropColumn: column removed; existing rows preserved (sans column)", async () => {
81
+ const storage = await getHandle().makeStorage({ id: { type: "string" }, name: { type: "string" } }, [
82
+ {
83
+ version: 1,
84
+ ops: [{ kind: "dropColumn", name: "removed_field" }]
85
+ }
86
+ ], [{ id: "u1", name: "alice", removed_field: "old" }]);
87
+ const row = await storage.get({ id: "u1" });
88
+ expect4(row).toBeDefined();
89
+ expect4(row.name).toBe("alice");
90
+ expect4(row.removed_field).toBeUndefined();
91
+ });
92
+ }
93
+
94
+ // src/tabular-migrations/assertions/failedMigrationNotRecorded.ts
95
+ import { expect as expect5, it as it5 } from "vitest";
96
+ function failedMigrationNotRecordedBlock(_opts, getHandle) {
97
+ it5("a failed migration is not recorded; subsequent run can succeed", async () => {
98
+ let failOnFirst = true;
99
+ const migrations = [
100
+ {
101
+ version: 1,
102
+ ops: [
103
+ {
104
+ kind: "backfill",
105
+ transform: (row) => {
106
+ if (failOnFirst) {
107
+ failOnFirst = false;
108
+ throw new Error("boom");
109
+ }
110
+ return row;
111
+ }
112
+ }
113
+ ]
114
+ }
115
+ ];
116
+ await expect5(getHandle().makeStorage({ id: { type: "string" } }, migrations, [{ id: "u1" }])).rejects.toThrow(/boom/);
117
+ const storage = await getHandle().makeStorage({ id: { type: "string" } }, migrations, [
118
+ { id: "u1" }
119
+ ]);
120
+ expect5(await storage.size()).toBe(1);
121
+ });
122
+ }
123
+
124
+ // src/tabular-migrations/assertions/freshDbFastPath.ts
125
+ import { expect as expect6, it as it6 } from "vitest";
126
+ function freshDbFastPathBlock(opts, getHandle) {
127
+ it6("fresh DB: backfill is NOT executed; bookkeeping marks all applied", async () => {
128
+ let transformCalls = 0;
129
+ const storage = await getHandle().makeStorage({ id: { type: "string" }, n: { type: "integer" } }, [
130
+ {
131
+ version: 1,
132
+ ops: [
133
+ {
134
+ kind: "backfill",
135
+ transform: (row) => {
136
+ transformCalls++;
137
+ return row;
138
+ }
139
+ }
140
+ ]
141
+ }
142
+ ], []);
143
+ expect6(transformCalls).toBe(0);
144
+ expect6(await storage.size()).toBe(0);
145
+ });
146
+ if (opts.enforcesDdl) {
147
+ it6("fresh DB: addColumn against target-schema does NOT execute (fast path)", async () => {
148
+ const storage = await getHandle().makeStorage({
149
+ id: { type: "string" },
150
+ name: { type: "string" },
151
+ archived: { anyOf: [{ type: "boolean" }, { type: "null" }] }
152
+ }, [
153
+ {
154
+ version: 1,
155
+ ops: [
156
+ {
157
+ kind: "addColumn",
158
+ name: "archived",
159
+ schema: { anyOf: [{ type: "boolean" }, { type: "null" }] }
160
+ }
161
+ ]
162
+ }
163
+ ], []);
164
+ expect6(await storage.size()).toBe(0);
165
+ });
166
+ }
167
+ }
168
+
169
+ // src/tabular-migrations/assertions/incrementalApplication.ts
170
+ import { expect as expect7, it as it7 } from "vitest";
171
+ function incrementalApplicationBlock(_opts, getHandle) {
172
+ it7("running migrations twice is idempotent (each version applied once)", async () => {
173
+ let backfillCalls = 0;
174
+ const migrations = [
175
+ {
176
+ version: 1,
177
+ ops: [
178
+ {
179
+ kind: "backfill",
180
+ transform: (row) => {
181
+ backfillCalls++;
182
+ return row;
183
+ }
184
+ }
185
+ ]
186
+ }
187
+ ];
188
+ await getHandle().makeStorage({ id: { type: "string" }, n: { type: "integer" } }, migrations, [
189
+ { id: "r1", n: 1 }
190
+ ]);
191
+ const firstCount = backfillCalls;
192
+ await getHandle().makeStorage({ id: { type: "string" }, n: { type: "integer" } }, migrations, [
193
+ { id: "r1", n: 1 }
194
+ ]);
195
+ expect7(backfillCalls).toBe(firstCount);
196
+ });
197
+ }
198
+
199
+ // src/tabular-migrations/assertions/renameColumn.ts
200
+ import { expect as expect8, it as it8 } from "vitest";
201
+ function renameColumnBlock(_opts, getHandle) {
202
+ it8("renameColumn: data preserved under new name", async () => {
203
+ const storage = await getHandle().makeStorage({ id: { type: "string" }, state: { type: "string" } }, [
204
+ {
205
+ version: 1,
206
+ ops: [{ kind: "renameColumn", from: "status", to: "state" }]
207
+ }
208
+ ], [{ id: "u1", status: "active" }]);
209
+ const row = await storage.get({ id: "u1" });
210
+ expect8(row).toBeDefined();
211
+ expect8(row.state).toBe("active");
212
+ });
213
+ }
214
+
215
+ // src/tabular-migrations/runTabularMigrationContract.ts
216
+ function runTabularMigrationContract(opts) {
217
+ describe.skipIf(opts.skip)(`Tabular migration contract: ${opts.name}`, () => {
218
+ let handle;
219
+ const getHandle = () => {
220
+ if (!handle)
221
+ throw new Error("handle not initialized");
222
+ return handle;
223
+ };
224
+ beforeAll(async () => {
225
+ handle = await opts.factory();
226
+ }, opts.timeout);
227
+ afterAll(async () => {
228
+ if (handle)
229
+ await handle.dispose();
230
+ });
231
+ if (opts.enforcesDdl) {
232
+ addColumnBlock(opts, getHandle);
233
+ dropColumnBlock(opts, getHandle);
234
+ renameColumnBlock(opts, getHandle);
235
+ addAndDropIndexBlock(opts, getHandle);
236
+ }
237
+ backfillBlock(opts, getHandle);
238
+ freshDbFastPathBlock(opts, getHandle);
239
+ if (opts.persistentBookkeeping !== false) {
240
+ incrementalApplicationBlock(opts, getHandle);
241
+ failedMigrationNotRecordedBlock(opts, getHandle);
242
+ }
243
+ });
244
+ }
245
+ export {
246
+ addAndDropIndexBlock,
247
+ addColumnBlock,
248
+ backfillBlock,
249
+ dropColumnBlock,
250
+ failedMigrationNotRecordedBlock,
251
+ freshDbFastPathBlock,
252
+ incrementalApplicationBlock,
253
+ renameColumnBlock,
254
+ runTabularMigrationContract
255
+ };
256
+
257
+ //# debugId=FAE6293E721A4E2664756E2164756E21
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/tabular-migrations/runTabularMigrationContract.ts", "../src/tabular-migrations/assertions/addAndDropIndex.ts", "../src/tabular-migrations/assertions/addColumn.ts", "../src/tabular-migrations/assertions/backfill.ts", "../src/tabular-migrations/assertions/dropColumn.ts", "../src/tabular-migrations/assertions/failedMigrationNotRecorded.ts", "../src/tabular-migrations/assertions/freshDbFastPath.ts", "../src/tabular-migrations/assertions/incrementalApplication.ts", "../src/tabular-migrations/assertions/renameColumn.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { afterAll, beforeAll, describe } from \"vitest\";\n\nimport { addAndDropIndexBlock } from \"./assertions/addAndDropIndex\";\nimport { addColumnBlock } from \"./assertions/addColumn\";\nimport { backfillBlock } from \"./assertions/backfill\";\nimport { dropColumnBlock } from \"./assertions/dropColumn\";\nimport { failedMigrationNotRecordedBlock } from \"./assertions/failedMigrationNotRecorded\";\nimport { freshDbFastPathBlock } from \"./assertions/freshDbFastPath\";\nimport { incrementalApplicationBlock } from \"./assertions/incrementalApplication\";\nimport { renameColumnBlock } from \"./assertions/renameColumn\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"./types\";\n\nexport function runTabularMigrationContract(opts: TabularMigrationContractOpts): void {\n describe.skipIf(opts.skip)(`Tabular migration contract: ${opts.name}`, () => {\n let handle: TabularMigrationContractHandle | undefined;\n const getHandle = (): TabularMigrationContractHandle => {\n if (!handle) throw new Error(\"handle not initialized\");\n return handle;\n };\n\n beforeAll(async () => {\n handle = await opts.factory();\n }, opts.timeout);\n afterAll(async () => {\n if (handle) await handle.dispose();\n });\n\n if (opts.enforcesDdl) {\n addColumnBlock(opts, getHandle);\n dropColumnBlock(opts, getHandle);\n renameColumnBlock(opts, getHandle);\n addAndDropIndexBlock(opts, getHandle);\n }\n backfillBlock(opts, getHandle);\n freshDbFastPathBlock(opts, getHandle);\n if (opts.persistentBookkeeping !== false) {\n incrementalApplicationBlock(opts, getHandle);\n failedMigrationNotRecordedBlock(opts, getHandle);\n }\n });\n}\n\nexport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"./types\";\n",
6
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function addAndDropIndexBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"addIndex / dropIndex: query still returns correct rows\", async () => {\n const storage = await getHandle().makeStorage(\n { id: { type: \"string\" }, name: { type: \"string\" } },\n [\n {\n version: 1,\n ops: [\n { kind: \"addIndex\", name: \"idx_name\", columns: [\"name\"] },\n { kind: \"dropIndex\", name: \"idx_name\" },\n ],\n },\n ],\n [\n { id: \"u1\", name: \"alice\" },\n { id: \"u2\", name: \"bob\" },\n ]\n );\n const rows = (await storage.query({ name: \"alice\" })) ?? [];\n expect(rows.map((r) => (r as { id: string }).id)).toEqual([\"u1\"]);\n });\n}\n",
7
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function addColumnBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"addColumn: existing rows survive; new column readable\", async () => {\n const storage = await getHandle().makeStorage(\n {\n id: { type: \"string\" },\n name: { type: \"string\" },\n archived: { anyOf: [{ type: \"boolean\" }, { type: \"null\" }] },\n },\n [\n {\n version: 1,\n ops: [\n {\n kind: \"addColumn\",\n name: \"archived\",\n schema: { anyOf: [{ type: \"boolean\" }, { type: \"null\" }] },\n },\n ],\n },\n ],\n [{ id: \"u1\", name: \"alice\" }]\n );\n const row = (await storage.get({ id: \"u1\" })) as\n | { id: string; name: string; archived?: boolean | null }\n | undefined;\n expect(row).toBeDefined();\n expect(row!.id).toBe(\"u1\");\n expect(row!.archived ?? null).toBeNull();\n });\n}\n",
8
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function backfillBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"backfill: every existing row is rewritten\", async () => {\n const rows = Array.from({ length: 30 }, (_, i) => ({ id: `r${i}`, n: i }));\n const storage = await getHandle().makeStorage(\n { id: { type: \"string\" }, n: { type: \"integer\" } },\n [\n {\n version: 1,\n ops: [\n {\n kind: \"backfill\",\n batchSize: 7,\n transform: (row) => ({ ...row, n: (row.n as number) * 2 }),\n },\n ],\n },\n ],\n rows\n );\n const all = (await storage.getAll()) ?? [];\n const r3 = all.find((r) => (r as { id: string }).id === \"r3\") as { n: number } | undefined;\n expect(r3).toBeDefined();\n expect(r3!.n).toBe(6);\n expect(all.length).toBe(30);\n });\n}\n",
9
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function dropColumnBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"dropColumn: column removed; existing rows preserved (sans column)\", async () => {\n const storage = await getHandle().makeStorage(\n { id: { type: \"string\" }, name: { type: \"string\" } },\n [\n {\n version: 1,\n ops: [{ kind: \"dropColumn\", name: \"removed_field\" }],\n },\n ],\n [{ id: \"u1\", name: \"alice\", removed_field: \"old\" }]\n );\n const row = (await storage.get({ id: \"u1\" })) as\n | { id: string; name: string; removed_field?: string }\n | undefined;\n expect(row).toBeDefined();\n expect(row!.name).toBe(\"alice\");\n expect((row as Record<string, unknown>).removed_field).toBeUndefined();\n });\n}\n",
10
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function failedMigrationNotRecordedBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"a failed migration is not recorded; subsequent run can succeed\", async () => {\n let failOnFirst = true;\n const migrations = [\n {\n version: 1,\n ops: [\n {\n kind: \"backfill\" as const,\n transform: (row: Record<string, unknown>) => {\n if (failOnFirst) {\n failOnFirst = false;\n throw new Error(\"boom\");\n }\n return row;\n },\n },\n ],\n },\n ];\n await expect(\n getHandle().makeStorage({ id: { type: \"string\" } }, migrations, [{ id: \"u1\" }])\n ).rejects.toThrow(/boom/);\n\n const storage = await getHandle().makeStorage({ id: { type: \"string\" } }, migrations, [\n { id: \"u1\" },\n ]);\n expect(await storage.size()).toBe(1);\n });\n}\n",
11
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function freshDbFastPathBlock(\n opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"fresh DB: backfill is NOT executed; bookkeeping marks all applied\", async () => {\n let transformCalls = 0;\n const storage = await getHandle().makeStorage(\n { id: { type: \"string\" }, n: { type: \"integer\" } },\n [\n {\n version: 1,\n ops: [\n {\n kind: \"backfill\",\n transform: (row) => {\n transformCalls++;\n return row;\n },\n },\n ],\n },\n ],\n [] // no pre-existing rows -> fresh DB\n );\n expect(transformCalls).toBe(0);\n expect(await storage.size()).toBe(0);\n });\n\n // Regression test for the fresh-DB fast path on SQL backends. If the\n // orchestrator's freshTable signal is wired up correctly, addColumn for\n // a column that already exists in the target schema must NOT run as a\n // real ALTER TABLE — which would throw \"duplicate column\" — because\n // the migration is recorded as already-applied without executing ops.\n // Only meaningful on backends that enforce DDL (i.e. SQL ones).\n if (opts.enforcesDdl) {\n it(\"fresh DB: addColumn against target-schema does NOT execute (fast path)\", async () => {\n // The target schema already includes `archived`. A v1 migration that\n // would `ALTER TABLE … ADD COLUMN archived` should be marked applied\n // without running on a brand-new DB.\n const storage = await getHandle().makeStorage(\n {\n id: { type: \"string\" },\n name: { type: \"string\" },\n archived: { anyOf: [{ type: \"boolean\" }, { type: \"null\" }] },\n },\n [\n {\n version: 1,\n ops: [\n {\n kind: \"addColumn\",\n name: \"archived\",\n schema: { anyOf: [{ type: \"boolean\" }, { type: \"null\" }] },\n },\n ],\n },\n ],\n [] // no pre-existing rows -> fresh DB\n );\n // If the fast path is broken, makeStorage above throws on the\n // duplicate-column error and we never reach here. Reaching this\n // assertion is itself the proof.\n expect(await storage.size()).toBe(0);\n });\n }\n}\n",
12
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function incrementalApplicationBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"running migrations twice is idempotent (each version applied once)\", async () => {\n let backfillCalls = 0;\n const migrations = [\n {\n version: 1,\n ops: [\n {\n kind: \"backfill\" as const,\n transform: (row: Record<string, unknown>) => {\n backfillCalls++;\n return row;\n },\n },\n ],\n },\n ];\n await getHandle().makeStorage({ id: { type: \"string\" }, n: { type: \"integer\" } }, migrations, [\n { id: \"r1\", n: 1 },\n ]);\n const firstCount = backfillCalls;\n await getHandle().makeStorage({ id: { type: \"string\" }, n: { type: \"integer\" } }, migrations, [\n { id: \"r1\", n: 1 },\n ]);\n expect(backfillCalls).toBe(firstCount);\n });\n}\n",
13
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { expect, it } from \"vitest\";\nimport type { TabularMigrationContractHandle, TabularMigrationContractOpts } from \"../types\";\n\nexport function renameColumnBlock(\n _opts: TabularMigrationContractOpts,\n getHandle: () => TabularMigrationContractHandle\n): void {\n it(\"renameColumn: data preserved under new name\", async () => {\n const storage = await getHandle().makeStorage(\n { id: { type: \"string\" }, state: { type: \"string\" } },\n [\n {\n version: 1,\n ops: [{ kind: \"renameColumn\", from: \"status\", to: \"state\" }],\n },\n ],\n [{ id: \"u1\", status: \"active\" }]\n );\n const row = (await storage.get({ id: \"u1\" })) as { id: string; state: string } | undefined;\n expect(row).toBeDefined();\n expect(row!.state).toBe(\"active\");\n });\n}\n"
14
+ ],
15
+ "mappings": "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",
16
+ "debugId": "FAE6293E721A4E2664756E2164756E21",
17
+ "names": []
18
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ export declare function countMatchesQueryBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ /**
8
+ * A guard on a public method is a guard on the transaction path.
9
+ *
10
+ * It was not always. `withTransaction` used to hand its callback a Proxy that
11
+ * resolved `tx.foo(...)` to `_fooInternal(...)` by name, skipping the public
12
+ * method, so a check written on `foo` was silently absent from `tx.foo`. That
13
+ * cost three fixes — `tx.deleteSearch({})` turning from a no-op into a `DELETE
14
+ * FROM t WHERE` syntax error and criteria excluding nothing into a truncated
15
+ * table; `tx.updateWhere` rewriting a row's identity where the same call
16
+ * outside a transaction threw; `tx.getBulk([])` reaching DuckDB as `IN ()` —
17
+ * and each could only restate the guard in the private method.
18
+ *
19
+ * `createTxView` now leaves the public methods in place and neutralizes only
20
+ * the mutex, so `tx.foo(args)` runs `foo`. Parity is a property of the routing
21
+ * rather than of anyone's diligence, and this block is what says so out loud:
22
+ * every guard below is written once, on the public method, and asserted on both
23
+ * paths, over a set of methods derived from the object rather than written down.
24
+ */
25
+ export declare function guardParityBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ /**
8
+ * The `{ operator: "in" }` criterion, which every backend renders differently —
9
+ * expanded placeholders on SQLite/DuckDB, a single `= ANY($n)` array parameter
10
+ * on Postgres, a PostgREST `in.()` filter on Supabase, and an in-memory
11
+ * membership test elsewhere. That divergence is exactly why the semantics are
12
+ * pinned here rather than per backend.
13
+ */
14
+ export declare function inListCriterionBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { AuthorStorage, PostStorage } from "../joinFixtures";
7
+ /**
8
+ * What the hash join reads off the left side, and when it is allowed to stop.
9
+ *
10
+ * `join`'s own docstring makes a promise with a condition attached: `limit` and
11
+ * `offset` apply to the joined rows, and the fallback "bounds the left read —
12
+ * and stops early — only when the joined order does not need a sort", meaning
13
+ * `orderBy` is absent or names left columns the left query can serve. An
14
+ * `orderBy` touching the right side is sorted in memory, and that sort needs
15
+ * every joined row, so such a join reads the whole left table however small its
16
+ * `limit`.
17
+ *
18
+ * Both halves matter and they fail in opposite directions. Losing the bound is
19
+ * a performance cliff a caller cannot see: over a remote backend a `limit: 2`
20
+ * join quietly puts the whole table on the wire. Applying it when the order is
21
+ * NOT already final is a wrong answer: the first N left rows in arbitrary order
22
+ * are sorted afterwards and returned as though they were the top N. Nothing
23
+ * else in this contract asserts either one — the pushdown is bounded by the
24
+ * database, so only the fallback can be caught getting this wrong.
25
+ */
26
+ export declare function joinBoundedLeftReadBlock(createPosts: () => Promise<PostStorage>, createAuthors: () => Promise<AuthorStorage>, seed: (posts: PostStorage, authors: AuthorStorage) => Promise<void>, opts: {
27
+ readonly expectSqlPushdown?: boolean;
28
+ readonly timeout?: number;
29
+ }): void;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ /**
8
+ * Which path a join takes. A backend that can share a connection handle must
9
+ * run a same-handle join as one statement — proven by the right side's `query`
10
+ * never being called — and must fall back to the hash join, which does call
11
+ * it, when the right side lives elsewhere.
12
+ */
13
+ export declare function joinPushdownBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ /**
8
+ * The `{ operator: "not-in" }` criterion. Pinned here for the same reason its
9
+ * complement is — `NOT IN (…)` on SQLite/DuckDB, `<> ALL($n)` on Postgres, a
10
+ * PostgREST `not.in.()` filter on Supabase, a JS predicate elsewhere — plus one
11
+ * of its own: an exclusion filter that silently matches nothing looks like an
12
+ * empty result set rather than a bug, so the empty-list and null cases need a
13
+ * test that says which way they are supposed to fall.
14
+ */
15
+ export declare function notInListCriterionBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { AuthorStorage, PostStorage } from "../joinFixtures";
7
+ /**
8
+ * The two join strategies answer the same question the same way.
9
+ *
10
+ * `ITabularStorage.join` has one documented semantics and two implementations —
11
+ * a single `JOIN` statement when both tables share a connection, and an
12
+ * application-side hash join otherwise — and which one runs is decided by the
13
+ * planner, not by the caller. So a caller cannot tell them apart, and any
14
+ * disagreement between them is a wrong answer that no error reports and that
15
+ * changes with where the right-hand storage happens to live.
16
+ *
17
+ * That has already happened once: a pushdown chosen against a right side
18
+ * enlisted in a transaction it could not see returned zero rows from an inner
19
+ * join where the same call through the fallback returned the row. The block
20
+ * that covered `join` at the time stayed green throughout, because it asserted
21
+ * which path ran and never compared what the two paths returned.
22
+ *
23
+ * This asserts the second thing. It runs only where both strategies are
24
+ * reachable — a pair the planner pushes down — and compares them over specs
25
+ * derived from the option space rather than a list, so an option added to
26
+ * `JoinSpec` is covered by construction.
27
+ */
28
+ export declare function joinStrategyParityBlock(createPosts: () => Promise<PostStorage>, createAuthors: () => Promise<AuthorStorage>, seed: (posts: PostStorage, authors: AuthorStorage) => Promise<void>, opts: {
29
+ readonly expectSqlPushdown?: boolean;
30
+ readonly timeout?: number;
31
+ }): void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ /**
8
+ * Subscription contract. Selection between the two blocks below is driven by
9
+ * `opts.usesPolling`:
10
+ *
11
+ * - `usesPolling: false` → `subscribeToChanges.eventDriven` — strict commit
12
+ * order (event-driven subscriptions like Postgres LISTEN/NOTIFY or the
13
+ * in-memory broadcast bus emit one event per write, in write order).
14
+ * - `usesPolling: true` → `subscribeToChanges.polling` — set equality plus
15
+ * event count (polling-based subscriptions diff a snapshot and have no
16
+ * way to preserve commit order).
17
+ *
18
+ * When `capabilities.supportsSubscriptions` is true, `usesPolling` is
19
+ * required on the contract opts; the type guarantees this at compile time.
20
+ */
21
+ export declare function subscribeToChangesBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ export declare function vectorColumnFormatBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ export declare function withConnectionTransactionBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { TabularStorageContractOpts } from "../types";
7
+ export declare function withTransactionRollbackBlock(opts: TabularStorageContractOpts): void;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * The row shape every tabular assertion in this contract reads and writes.
8
+ *
9
+ * A compound primary key (`name` + `type`) rather than a single column, because
10
+ * a backend that gets one key right and the pair wrong is the common failure —
11
+ * cursor encoding, `getBulk` chunking and the join's tuple fingerprint all
12
+ * behave differently once the key has two parts.
13
+ */
14
+ export declare const CompoundPrimaryKeyNames: readonly ["name", "type"];
15
+ export declare const CompoundSchema: {
16
+ readonly type: "object";
17
+ readonly properties: {
18
+ readonly name: {
19
+ readonly type: "string";
20
+ };
21
+ readonly type: {
22
+ readonly type: "string";
23
+ };
24
+ readonly option: {
25
+ readonly type: "string";
26
+ };
27
+ readonly success: {
28
+ readonly type: "boolean";
29
+ };
30
+ };
31
+ readonly required: readonly ["name", "type", "option", "success"];
32
+ readonly additionalProperties: false;
33
+ };