@fragno-dev/test 1.0.2 → 2.0.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.
Files changed (42) hide show
  1. package/.turbo/turbo-build.log +31 -15
  2. package/CHANGELOG.md +43 -0
  3. package/dist/adapters.d.ts +21 -3
  4. package/dist/adapters.d.ts.map +1 -1
  5. package/dist/adapters.js +125 -31
  6. package/dist/adapters.js.map +1 -1
  7. package/dist/db-test.d.ts.map +1 -1
  8. package/dist/db-test.js +33 -2
  9. package/dist/db-test.js.map +1 -1
  10. package/dist/durable-hooks.d.ts +7 -0
  11. package/dist/durable-hooks.d.ts.map +1 -0
  12. package/dist/durable-hooks.js +12 -0
  13. package/dist/durable-hooks.js.map +1 -0
  14. package/dist/index.d.ts +8 -4
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/model-checker-actors.d.ts +41 -0
  19. package/dist/model-checker-actors.d.ts.map +1 -0
  20. package/dist/model-checker-actors.js +406 -0
  21. package/dist/model-checker-actors.js.map +1 -0
  22. package/dist/model-checker-adapter.d.ts +32 -0
  23. package/dist/model-checker-adapter.d.ts.map +1 -0
  24. package/dist/model-checker-adapter.js +109 -0
  25. package/dist/model-checker-adapter.js.map +1 -0
  26. package/dist/model-checker.d.ts +128 -0
  27. package/dist/model-checker.d.ts.map +1 -0
  28. package/dist/model-checker.js +443 -0
  29. package/dist/model-checker.js.map +1 -0
  30. package/package.json +12 -11
  31. package/src/adapter-conformance.test.ts +322 -0
  32. package/src/adapters.ts +199 -36
  33. package/src/db-test.test.ts +2 -2
  34. package/src/db-test.ts +53 -3
  35. package/src/durable-hooks.ts +13 -0
  36. package/src/index.test.ts +84 -7
  37. package/src/index.ts +39 -4
  38. package/src/model-checker-actors.test.ts +78 -0
  39. package/src/model-checker-actors.ts +642 -0
  40. package/src/model-checker-adapter.ts +200 -0
  41. package/src/model-checker.test.ts +399 -0
  42. package/src/model-checker.ts +799 -0
@@ -0,0 +1,128 @@
1
+ import { TypedUnitOfWork } from "@fragno-dev/db";
2
+ import { AnySchema } from "@fragno-dev/db/schema";
3
+ import { SimpleQueryInterface } from "@fragno-dev/db/query";
4
+ import { FragnoRuntime } from "@fragno-dev/core";
5
+
6
+ //#region src/model-checker.d.ts
7
+ type ModelCheckerMode = "exhaustive" | "bounded-exhaustive" | "random" | "infinite";
8
+ type ModelCheckerPhase = "retrieve" | "mutate";
9
+ type ModelCheckerStep = {
10
+ txId: number;
11
+ phase: ModelCheckerPhase;
12
+ };
13
+ type ModelCheckerTraceHashMode = "state" | "trace" | "state+trace";
14
+ type NormalizedMutationOperation = {
15
+ type: "create" | "update" | "delete" | "check";
16
+ table: string;
17
+ namespace?: string | null;
18
+ id?: unknown;
19
+ checkVersion?: boolean;
20
+ set?: Record<string, unknown>;
21
+ values?: Record<string, unknown>;
22
+ generatedExternalId?: string;
23
+ };
24
+ type ModelCheckerTraceEvent = {
25
+ type: "schedule-step";
26
+ step: ModelCheckerStep;
27
+ stepIndex: number;
28
+ } | {
29
+ type: "retrieve-output";
30
+ txId?: number;
31
+ uowName?: string;
32
+ output: unknown;
33
+ } | {
34
+ type: "mutation-input";
35
+ txId?: number;
36
+ uowName?: string;
37
+ operations: NormalizedMutationOperation[];
38
+ } | {
39
+ type: "mutation-result";
40
+ txId?: number;
41
+ uowName?: string;
42
+ success: boolean;
43
+ createdIds: unknown[];
44
+ error?: string;
45
+ } | {
46
+ type: "runtime";
47
+ operation: "time.now" | "random.float" | "random.uuid" | "random.cuid";
48
+ value: unknown;
49
+ } | {
50
+ type: "external";
51
+ name: string;
52
+ payload: unknown;
53
+ };
54
+ type ModelCheckerTrace = {
55
+ events: ModelCheckerTraceEvent[];
56
+ };
57
+ type ModelCheckerTraceRecorder = (event: ModelCheckerTraceEvent) => void;
58
+ type ModelCheckerTraceHasher = (trace: ModelCheckerTrace) => Promise<string> | string;
59
+ type RawUowTransactionContext<TSchema extends AnySchema, TUowConfig> = {
60
+ queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;
61
+ createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>;
62
+ runtime?: FragnoRuntime;
63
+ };
64
+ type RawUowMutateContext<TSchema extends AnySchema, TUowConfig, TRetrieve> = RawUowTransactionContext<TSchema, TUowConfig> & {
65
+ retrieveResult: TRetrieve;
66
+ };
67
+ interface RawUowTransaction<TRetrieve = unknown, TMutate = unknown, TSchema extends AnySchema = AnySchema, TUowConfig = void> {
68
+ name?: string;
69
+ retrieve(ctx: RawUowTransactionContext<TSchema, TUowConfig>): TRetrieve | Promise<TRetrieve>;
70
+ mutate?(ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>): TMutate | Promise<TMutate>;
71
+ }
72
+ type RawUowTransactionBuilder<TRetrieve = unknown, TMutate = unknown, TSchema extends AnySchema = AnySchema, TUowConfig = void> = {
73
+ name?: string;
74
+ config?: TUowConfig;
75
+ retrieve: (uow: TypedUnitOfWork<TSchema, [], unknown>, ctx: RawUowTransactionContext<TSchema, TUowConfig>) => TRetrieve | Promise<TRetrieve>;
76
+ mutate?: (uow: TypedUnitOfWork<TSchema, [], unknown>, ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>) => TMutate | Promise<TMutate>;
77
+ };
78
+ declare const createRawUowTransaction: <TRetrieve = unknown, TMutate = unknown, TSchema extends AnySchema = AnySchema, TUowConfig = void>(builder: RawUowTransactionBuilder<TRetrieve, TMutate, TSchema, TUowConfig>) => RawUowTransaction<TRetrieve, TMutate, TSchema, TUowConfig>;
79
+ type ModelCheckerExecutionContext<TSchema extends AnySchema, TUowConfig> = {
80
+ ctx: RawUowTransactionContext<TSchema, TUowConfig>;
81
+ cleanup?: () => Promise<void>;
82
+ };
83
+ type ModelCheckerHistory = false | {
84
+ type: "lru";
85
+ maxEntries: number;
86
+ } | {
87
+ type: "unbounded";
88
+ };
89
+ type ModelCheckerBounds = {
90
+ maxSteps?: number;
91
+ };
92
+ type ModelCheckerStateHasherContext<TSchema extends AnySchema, TUowConfig> = {
93
+ schema: TSchema;
94
+ queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;
95
+ };
96
+ type ModelCheckerConfig<TSchema extends AnySchema, TUowConfig = void> = {
97
+ schema: TSchema;
98
+ mode?: ModelCheckerMode;
99
+ seed?: number;
100
+ maxSchedules?: number;
101
+ bounds?: ModelCheckerBounds;
102
+ history?: ModelCheckerHistory;
103
+ stopWhenFrontierExhausted?: boolean;
104
+ stateHasher?: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;
105
+ traceRecorder?: ModelCheckerTraceRecorder;
106
+ traceHasher?: ModelCheckerTraceHasher;
107
+ traceHashMode?: ModelCheckerTraceHashMode;
108
+ runtime?: FragnoRuntime;
109
+ createContext: () => Promise<ModelCheckerExecutionContext<TSchema, TUowConfig>>;
110
+ setup?: (ctx: RawUowTransactionContext<TSchema, TUowConfig>) => Promise<void>;
111
+ buildTransactions: (ctx: RawUowTransactionContext<TSchema, TUowConfig>) => RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[];
112
+ };
113
+ type ModelCheckerScheduleResult = {
114
+ schedule: ModelCheckerStep[];
115
+ stateHash: string;
116
+ traceHash?: string;
117
+ trace?: ModelCheckerTrace;
118
+ };
119
+ type ModelCheckerRunResult = {
120
+ schedules: ModelCheckerScheduleResult[];
121
+ visitedPaths: number;
122
+ };
123
+ declare const defaultStateHasher: <TSchema extends AnySchema, TUowConfig>(ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;
124
+ declare const defaultTraceHasher: (trace: ModelCheckerTrace) => Promise<string>;
125
+ declare const runModelChecker: <TSchema extends AnySchema, TUowConfig>(config: ModelCheckerConfig<TSchema, TUowConfig>) => Promise<ModelCheckerRunResult>;
126
+ //#endregion
127
+ export { ModelCheckerBounds, ModelCheckerConfig, ModelCheckerHistory, ModelCheckerMode, ModelCheckerPhase, ModelCheckerRunResult, ModelCheckerScheduleResult, ModelCheckerStateHasherContext, ModelCheckerStep, ModelCheckerTrace, ModelCheckerTraceEvent, ModelCheckerTraceHashMode, ModelCheckerTraceHasher, ModelCheckerTraceRecorder, NormalizedMutationOperation, RawUowMutateContext, RawUowTransaction, RawUowTransactionBuilder, RawUowTransactionContext, createRawUowTransaction, defaultStateHasher, defaultTraceHasher, runModelChecker };
128
+ //# sourceMappingURL=model-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-checker.d.ts","names":[],"sources":["../src/model-checker.ts"],"sourcesContent":[],"mappings":"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"}
@@ -0,0 +1,443 @@
1
+ import { runWithTraceRecorder } from "@fragno-dev/core/internal/trace-context";
2
+ import { FragnoId, FragnoReference } from "@fragno-dev/db/schema";
3
+
4
+ //#region src/model-checker.ts
5
+ const createRawUowTransaction = (builder) => {
6
+ let uow = null;
7
+ const getUow = (ctx, phase) => {
8
+ if (!uow) {
9
+ if (phase === "mutate") throw new Error("Raw UOW mutate invoked before retrieve.");
10
+ uow = ctx.createUnitOfWork(builder.name, builder.config);
11
+ }
12
+ return uow;
13
+ };
14
+ const mutate = builder.mutate;
15
+ return {
16
+ name: builder.name,
17
+ retrieve: (ctx) => builder.retrieve(getUow(ctx, "retrieve"), ctx),
18
+ mutate: mutate ? (ctx) => mutate(getUow(ctx, "mutate"), ctx) : void 0
19
+ };
20
+ };
21
+ const defaultHistoryConfig = {
22
+ type: "lru",
23
+ maxEntries: 5e3
24
+ };
25
+ const mulberry32 = (seed) => {
26
+ let t = seed >>> 0;
27
+ return () => {
28
+ t += 1831565813;
29
+ let r = Math.imul(t ^ t >>> 15, t | 1);
30
+ r ^= r + Math.imul(r ^ r >>> 7, r | 61);
31
+ return ((r ^ r >>> 14) >>> 0) / 4294967296;
32
+ };
33
+ };
34
+ const serializeSchedulePrefix = (schedule) => schedule.map((step) => `${step.txId}:${step.phase}`).join("|");
35
+ const buildPathKey = (pathHash, prefix) => `${pathHash}::${serializeSchedulePrefix(prefix)}`;
36
+ const createHistoryTracker = (history) => {
37
+ if (!history) return null;
38
+ if (history.type === "unbounded") {
39
+ const set = /* @__PURE__ */ new Set();
40
+ return {
41
+ add: (key) => {
42
+ const has = set.has(key);
43
+ if (!has) set.add(key);
44
+ return !has;
45
+ },
46
+ size: () => set.size
47
+ };
48
+ }
49
+ const maxEntries = Math.max(history.maxEntries, 1);
50
+ const map = /* @__PURE__ */ new Map();
51
+ return {
52
+ add: (key) => {
53
+ const existed = map.delete(key);
54
+ map.set(key, true);
55
+ if (map.size > maxEntries) {
56
+ const first = map.keys().next().value;
57
+ if (first !== void 0) map.delete(first);
58
+ }
59
+ return !existed;
60
+ },
61
+ size: () => map.size
62
+ };
63
+ };
64
+ const normalizeForHash = (value) => {
65
+ if (value === null || value === void 0) return value;
66
+ if (typeof value === "bigint") return value.toString();
67
+ if (value instanceof Date) return value.toISOString();
68
+ if (value instanceof FragnoId) return {
69
+ externalId: value.externalId,
70
+ internalId: value.internalId?.toString(),
71
+ version: value.version
72
+ };
73
+ if (value instanceof FragnoReference) return value.internalId.toString();
74
+ if (Array.isArray(value)) return value.map((entry) => normalizeForHash(entry));
75
+ if (typeof value === "object") {
76
+ const record = value;
77
+ const sortedKeys = Object.keys(record).sort();
78
+ const normalized = {};
79
+ for (const key of sortedKeys) normalized[key] = normalizeForHash(record[key]);
80
+ return normalized;
81
+ }
82
+ return value;
83
+ };
84
+ const stableStringify = (value) => JSON.stringify(normalizeForHash(value));
85
+ const defaultStateHasher = async (ctx) => {
86
+ const tableNames = Object.keys(ctx.schema.tables).sort();
87
+ const snapshot = [];
88
+ for (const tableName of tableNames) {
89
+ const rows = await ctx.queryEngine.find(tableName, (b) => b.whereIndex("primary").orderByIndex("primary", "asc"));
90
+ snapshot.push({
91
+ table: tableName,
92
+ rows
93
+ });
94
+ }
95
+ return stableStringify(snapshot);
96
+ };
97
+ const defaultTraceHasher = async (trace) => stableStringify(trace.events);
98
+ const resolvePathHash = (stateHash, traceHash, mode) => {
99
+ if (mode === "state") return stateHash;
100
+ if (!traceHash) throw new Error("Trace hash mode requires traceHasher to be configured.");
101
+ if (mode === "trace") return traceHash;
102
+ return `${stateHash}::${traceHash}`;
103
+ };
104
+ const wrapRuntimeForTrace = (runtime, record) => ({
105
+ time: { now: () => {
106
+ const value = runtime.time.now();
107
+ record({
108
+ type: "runtime",
109
+ operation: "time.now",
110
+ value
111
+ });
112
+ return value;
113
+ } },
114
+ random: {
115
+ float: () => {
116
+ const value = runtime.random.float();
117
+ record({
118
+ type: "runtime",
119
+ operation: "random.float",
120
+ value
121
+ });
122
+ return value;
123
+ },
124
+ uuid: () => {
125
+ const value = runtime.random.uuid();
126
+ record({
127
+ type: "runtime",
128
+ operation: "random.uuid",
129
+ value
130
+ });
131
+ return value;
132
+ },
133
+ cuid: () => {
134
+ const value = runtime.random.cuid();
135
+ record({
136
+ type: "runtime",
137
+ operation: "random.cuid",
138
+ value
139
+ });
140
+ return value;
141
+ }
142
+ }
143
+ });
144
+ const captureCoreTrace = (record, event) => {
145
+ record({
146
+ type: "external",
147
+ name: event.type,
148
+ payload: event
149
+ });
150
+ };
151
+ const createTraceContext = (recorder, traceHasher) => {
152
+ const events = recorder || traceHasher ? [] : null;
153
+ const record = (event) => {
154
+ if (events) events.push(event);
155
+ recorder?.(event);
156
+ };
157
+ return {
158
+ events,
159
+ record
160
+ };
161
+ };
162
+ const normalizeMutationOperation = (op) => {
163
+ if (op.type === "create") return {
164
+ type: "create",
165
+ table: op.table,
166
+ namespace: op.namespace,
167
+ values: normalizeForHash(op.values),
168
+ generatedExternalId: op.generatedExternalId
169
+ };
170
+ if (op.type === "update") return {
171
+ type: "update",
172
+ table: op.table,
173
+ namespace: op.namespace,
174
+ id: normalizeForHash(op.id),
175
+ checkVersion: op.checkVersion,
176
+ set: normalizeForHash(op.set)
177
+ };
178
+ if (op.type === "delete") return {
179
+ type: "delete",
180
+ table: op.table,
181
+ namespace: op.namespace,
182
+ id: normalizeForHash(op.id),
183
+ checkVersion: op.checkVersion
184
+ };
185
+ return {
186
+ type: "check",
187
+ table: op.table,
188
+ namespace: op.namespace,
189
+ id: normalizeForHash(op.id)
190
+ };
191
+ };
192
+ const wrapCreateUnitOfWorkForTrace = (createUnitOfWork, getTxId, record) => {
193
+ return (name, config) => {
194
+ const uow = createUnitOfWork(name, config);
195
+ return new Proxy(uow, { get(target, prop, receiver) {
196
+ if (prop === "executeRetrieve") return async () => {
197
+ const result = await target.executeRetrieve();
198
+ record({
199
+ type: "retrieve-output",
200
+ txId: getTxId(),
201
+ uowName: target.name,
202
+ output: normalizeForHash(result)
203
+ });
204
+ return result;
205
+ };
206
+ if (prop === "executeMutations") return async () => {
207
+ const txId = getTxId();
208
+ record({
209
+ type: "mutation-input",
210
+ txId,
211
+ uowName: target.name,
212
+ operations: target.getMutationOperations().map(normalizeMutationOperation)
213
+ });
214
+ try {
215
+ const result = await target.executeMutations();
216
+ record({
217
+ type: "mutation-result",
218
+ txId,
219
+ uowName: target.name,
220
+ success: result.success,
221
+ createdIds: result.success ? target.getCreatedIds().map(normalizeForHash) : []
222
+ });
223
+ return result;
224
+ } catch (error) {
225
+ record({
226
+ type: "mutation-result",
227
+ txId,
228
+ uowName: target.name,
229
+ success: false,
230
+ createdIds: [],
231
+ error: error instanceof Error ? error.message : String(error)
232
+ });
233
+ throw error;
234
+ }
235
+ };
236
+ const value = Reflect.get(target, prop, receiver);
237
+ if (typeof value === "function") return value.bind(target);
238
+ return value;
239
+ } });
240
+ };
241
+ };
242
+ const planTransactions = (transactions) => {
243
+ if (transactions.length === 0) throw new Error("Model checker requires at least one transaction.");
244
+ return { stepsPerTransaction: transactions.map((tx) => {
245
+ if (!tx.retrieve) throw new Error("Model checker transactions must define a retrieve step.");
246
+ return tx.mutate ? 2 : 1;
247
+ }) };
248
+ };
249
+ const generateAllSchedules = (stepsPerTransaction) => {
250
+ const schedules = [];
251
+ const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);
252
+ const progress = stepsPerTransaction.map(() => 0);
253
+ const current = [];
254
+ const walk = () => {
255
+ if (current.length === totalSteps) {
256
+ schedules.push([...current]);
257
+ return;
258
+ }
259
+ for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {
260
+ if (progress[txId] >= stepsPerTransaction[txId]) continue;
261
+ const phase = progress[txId] === 0 ? "retrieve" : "mutate";
262
+ current.push({
263
+ txId,
264
+ phase
265
+ });
266
+ progress[txId] += 1;
267
+ walk();
268
+ progress[txId] -= 1;
269
+ current.pop();
270
+ }
271
+ };
272
+ walk();
273
+ return schedules;
274
+ };
275
+ const generateRandomSchedule = (stepsPerTransaction, rng) => {
276
+ const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);
277
+ const progress = stepsPerTransaction.map(() => 0);
278
+ const schedule = [];
279
+ for (let stepIndex = 0; stepIndex < totalSteps; stepIndex += 1) {
280
+ const candidates = [];
281
+ for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) if (progress[txId] < stepsPerTransaction[txId]) candidates.push(txId);
282
+ const pick = candidates[Math.floor(rng() * candidates.length)];
283
+ const phase = progress[pick] === 0 ? "retrieve" : "mutate";
284
+ schedule.push({
285
+ txId: pick,
286
+ phase
287
+ });
288
+ progress[pick] += 1;
289
+ }
290
+ return schedule;
291
+ };
292
+ const executeSchedule = async (schedule, config, history, stateHasher) => {
293
+ const traceHashMode = config.traceHashMode ?? "state";
294
+ const traceHasher = config.traceHasher ?? (traceHashMode === "state" ? void 0 : defaultTraceHasher);
295
+ const traceContext = createTraceContext(config.traceRecorder, traceHasher);
296
+ const traceEnabled = Boolean(traceContext.events);
297
+ const runtime = config.runtime && traceEnabled ? wrapRuntimeForTrace(config.runtime, traceContext.record) : config.runtime;
298
+ const { ctx, cleanup } = await config.createContext();
299
+ try {
300
+ let currentTxId;
301
+ const tracedContext = traceEnabled ? {
302
+ ...ctx,
303
+ createUnitOfWork: wrapCreateUnitOfWorkForTrace(ctx.createUnitOfWork, () => currentTxId, traceContext.record),
304
+ runtime
305
+ } : {
306
+ ...ctx,
307
+ runtime
308
+ };
309
+ if (config.setup) await config.setup(tracedContext);
310
+ const transactions = config.buildTransactions(tracedContext);
311
+ const transactionState = transactions.map(() => ({ retrieveResult: void 0 }));
312
+ let lastStateHash = null;
313
+ let newPathsAdded = false;
314
+ const runSchedule = async () => {
315
+ for (let stepIndex = 0; stepIndex < schedule.length; stepIndex += 1) {
316
+ const step = schedule[stepIndex];
317
+ if (!step) throw new Error("Schedule step is missing.");
318
+ const tx = transactions[step.txId];
319
+ if (!tx) throw new Error(`Transaction ${step.txId} is missing.`);
320
+ currentTxId = step.txId;
321
+ if (traceEnabled) traceContext.record({
322
+ type: "schedule-step",
323
+ step,
324
+ stepIndex
325
+ });
326
+ if (step.phase === "retrieve") transactionState[step.txId].retrieveResult = await tx.retrieve(tracedContext);
327
+ else {
328
+ if (!tx.mutate) throw new Error(`Transaction ${step.txId} does not define a mutate step.`);
329
+ await tx.mutate({
330
+ ...tracedContext,
331
+ retrieveResult: transactionState[step.txId].retrieveResult
332
+ });
333
+ }
334
+ if (history) {
335
+ lastStateHash = await stateHasher({
336
+ schema: config.schema,
337
+ queryEngine: tracedContext.queryEngine
338
+ });
339
+ const traceHash = traceHasher ? await traceHasher({ events: traceContext.events ?? [] }) : void 0;
340
+ const prefix = schedule.slice(0, stepIndex + 1);
341
+ if (history.add(buildPathKey(resolvePathHash(lastStateHash, traceHash, traceHashMode), prefix))) newPathsAdded = true;
342
+ }
343
+ }
344
+ currentTxId = void 0;
345
+ if (!lastStateHash) lastStateHash = await stateHasher({
346
+ schema: config.schema,
347
+ queryEngine: tracedContext.queryEngine
348
+ });
349
+ const finalTraceHash = traceHasher ? await traceHasher({ events: traceContext.events ?? [] }) : void 0;
350
+ return {
351
+ stateHash: lastStateHash,
352
+ newPathsAdded,
353
+ traceHash: finalTraceHash,
354
+ trace: traceContext.events ? { events: traceContext.events } : void 0
355
+ };
356
+ };
357
+ if (traceEnabled) return await runWithTraceRecorder((event) => captureCoreTrace(traceContext.record, event), runSchedule);
358
+ return await runSchedule();
359
+ } finally {
360
+ if (cleanup) await cleanup();
361
+ }
362
+ };
363
+ const getTransactionPlan = async (config) => {
364
+ const { ctx, cleanup } = await config.createContext();
365
+ try {
366
+ const context = config.runtime ? {
367
+ ...ctx,
368
+ runtime: config.runtime
369
+ } : ctx;
370
+ if (config.setup) await config.setup(context);
371
+ return planTransactions(config.buildTransactions(context));
372
+ } finally {
373
+ if (cleanup) await cleanup();
374
+ }
375
+ };
376
+ const runModelChecker = async (config) => {
377
+ const mode = config.mode ?? "exhaustive";
378
+ const history = createHistoryTracker(config.history ?? defaultHistoryConfig);
379
+ const stateHasher = config.stateHasher ?? defaultStateHasher;
380
+ const { stepsPerTransaction } = await getTransactionPlan(config);
381
+ const schedules = [];
382
+ const maxSchedules = config.maxSchedules;
383
+ const seed = config.seed ?? 1;
384
+ if (mode === "exhaustive" || mode === "bounded-exhaustive") {
385
+ if (mode === "bounded-exhaustive") {
386
+ const bounds = config.bounds;
387
+ if (bounds?.maxSteps !== void 0) {
388
+ const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);
389
+ if (totalSteps > bounds.maxSteps) throw new Error(`bounded-exhaustive requires maxSteps >= ${totalSteps}, but received ${bounds.maxSteps}`);
390
+ }
391
+ }
392
+ const allSchedules = generateAllSchedules(stepsPerTransaction);
393
+ for (const schedule of allSchedules) {
394
+ if (maxSchedules !== void 0 && schedules.length >= maxSchedules) break;
395
+ const result = await executeSchedule(schedule, config, history, stateHasher);
396
+ schedules.push({
397
+ schedule,
398
+ stateHash: result.stateHash,
399
+ traceHash: result.traceHash,
400
+ trace: result.trace
401
+ });
402
+ }
403
+ } else if (mode === "random") {
404
+ const rng = mulberry32(seed);
405
+ const total = maxSchedules ?? 1;
406
+ for (let i = 0; i < total; i += 1) {
407
+ const schedule = generateRandomSchedule(stepsPerTransaction, rng);
408
+ const result = await executeSchedule(schedule, config, history, stateHasher);
409
+ schedules.push({
410
+ schedule,
411
+ stateHash: result.stateHash,
412
+ traceHash: result.traceHash,
413
+ trace: result.trace
414
+ });
415
+ }
416
+ } else {
417
+ const rng = mulberry32(seed);
418
+ const stopWhenFrontierExhausted = config.stopWhenFrontierExhausted ?? false;
419
+ let iterations = 0;
420
+ let frontierExhausted = false;
421
+ while (!frontierExhausted) {
422
+ if (maxSchedules !== void 0 && iterations >= maxSchedules) break;
423
+ const schedule = generateRandomSchedule(stepsPerTransaction, rng);
424
+ const result = await executeSchedule(schedule, config, history, stateHasher);
425
+ schedules.push({
426
+ schedule,
427
+ stateHash: result.stateHash,
428
+ traceHash: result.traceHash,
429
+ trace: result.trace
430
+ });
431
+ iterations += 1;
432
+ if (stopWhenFrontierExhausted && history) frontierExhausted = !result.newPathsAdded;
433
+ }
434
+ }
435
+ return {
436
+ schedules,
437
+ visitedPaths: history?.size() ?? 0
438
+ };
439
+ };
440
+
441
+ //#endregion
442
+ export { createRawUowTransaction, defaultStateHasher, defaultTraceHasher, runModelChecker };
443
+ //# sourceMappingURL=model-checker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-checker.js","names":["uow: TypedUnitOfWork<TSchema, [], unknown> | null","defaultHistoryConfig: ModelCheckerHistory","normalized: Record<string, unknown>","snapshot: { table: string; rows: unknown[] }[]","schedules: ModelCheckerStep[][]","current: ModelCheckerStep[]","phase: ModelCheckerPhase","schedule: ModelCheckerStep[]","candidates: number[]","currentTxId: number | undefined","lastStateHash: string | null","schedules: ModelCheckerScheduleResult[]"],"sources":["../src/model-checker.ts"],"sourcesContent":["import type { FragnoRuntime } from \"@fragno-dev/core\";\nimport {\n runWithTraceRecorder,\n type FragnoCoreTraceEvent,\n} from \"@fragno-dev/core/internal/trace-context\";\nimport { FragnoId, FragnoReference, type AnySchema } from \"@fragno-dev/db/schema\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { TypedUnitOfWork } from \"@fragno-dev/db\";\nimport type { MutationOperation } from \"@fragno-dev/db/unit-of-work\";\n\nexport type ModelCheckerMode = \"exhaustive\" | \"bounded-exhaustive\" | \"random\" | \"infinite\";\nexport type ModelCheckerPhase = \"retrieve\" | \"mutate\";\n\nexport type ModelCheckerStep = {\n txId: number;\n phase: ModelCheckerPhase;\n};\n\nexport type ModelCheckerTraceHashMode = \"state\" | \"trace\" | \"state+trace\";\n\nexport type NormalizedMutationOperation = {\n type: \"create\" | \"update\" | \"delete\" | \"check\";\n table: string;\n namespace?: string | null;\n id?: unknown;\n checkVersion?: boolean;\n set?: Record<string, unknown>;\n values?: Record<string, unknown>;\n generatedExternalId?: string;\n};\n\nexport type ModelCheckerTraceEvent =\n | {\n type: \"schedule-step\";\n step: ModelCheckerStep;\n stepIndex: number;\n }\n | {\n type: \"retrieve-output\";\n txId?: number;\n uowName?: string;\n output: unknown;\n }\n | {\n type: \"mutation-input\";\n txId?: number;\n uowName?: string;\n operations: NormalizedMutationOperation[];\n }\n | {\n type: \"mutation-result\";\n txId?: number;\n uowName?: string;\n success: boolean;\n createdIds: unknown[];\n error?: string;\n }\n | {\n type: \"runtime\";\n operation: \"time.now\" | \"random.float\" | \"random.uuid\" | \"random.cuid\";\n value: unknown;\n }\n | {\n type: \"external\";\n name: string;\n payload: unknown;\n };\n\nexport type ModelCheckerTrace = {\n events: ModelCheckerTraceEvent[];\n};\n\nexport type ModelCheckerTraceRecorder = (event: ModelCheckerTraceEvent) => void;\n\nexport type ModelCheckerTraceHasher = (trace: ModelCheckerTrace) => Promise<string> | string;\n\nexport type RawUowTransactionContext<TSchema extends AnySchema, TUowConfig> = {\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>;\n runtime?: FragnoRuntime;\n};\n\nexport type RawUowMutateContext<\n TSchema extends AnySchema,\n TUowConfig,\n TRetrieve,\n> = RawUowTransactionContext<TSchema, TUowConfig> & {\n retrieveResult: TRetrieve;\n};\n\nexport interface RawUowTransaction<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> {\n name?: string;\n retrieve(ctx: RawUowTransactionContext<TSchema, TUowConfig>): TRetrieve | Promise<TRetrieve>;\n mutate?(ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>): TMutate | Promise<TMutate>;\n}\n\nexport type RawUowTransactionBuilder<\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n> = {\n name?: string;\n config?: TUowConfig;\n retrieve: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => TRetrieve | Promise<TRetrieve>;\n mutate?: (\n uow: TypedUnitOfWork<TSchema, [], unknown>,\n ctx: RawUowMutateContext<TSchema, TUowConfig, TRetrieve>,\n ) => TMutate | Promise<TMutate>;\n};\n\nexport const createRawUowTransaction = <\n TRetrieve = unknown,\n TMutate = unknown,\n TSchema extends AnySchema = AnySchema,\n TUowConfig = void,\n>(\n builder: RawUowTransactionBuilder<TRetrieve, TMutate, TSchema, TUowConfig>,\n): RawUowTransaction<TRetrieve, TMutate, TSchema, TUowConfig> => {\n let uow: TypedUnitOfWork<TSchema, [], unknown> | null = null;\n const getUow = (ctx: RawUowTransactionContext<TSchema, TUowConfig>, phase: ModelCheckerPhase) => {\n if (!uow) {\n if (phase === \"mutate\") {\n throw new Error(\"Raw UOW mutate invoked before retrieve.\");\n }\n uow = ctx.createUnitOfWork(builder.name, builder.config);\n }\n return uow;\n };\n\n const mutate = builder.mutate;\n return {\n name: builder.name,\n retrieve: (ctx) => builder.retrieve(getUow(ctx, \"retrieve\"), ctx),\n mutate: mutate ? (ctx) => mutate(getUow(ctx, \"mutate\"), ctx) : undefined,\n };\n};\n\nexport type ModelCheckerExecutionContext<TSchema extends AnySchema, TUowConfig> = {\n ctx: RawUowTransactionContext<TSchema, TUowConfig>;\n cleanup?: () => Promise<void>;\n};\n\nexport type ModelCheckerHistory =\n | false\n | { type: \"lru\"; maxEntries: number }\n | { type: \"unbounded\" };\n\nexport type ModelCheckerBounds = {\n maxSteps?: number;\n};\n\nexport type ModelCheckerStateHasherContext<TSchema extends AnySchema, TUowConfig> = {\n schema: TSchema;\n queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;\n};\n\nexport type ModelCheckerConfig<TSchema extends AnySchema, TUowConfig = void> = {\n schema: TSchema;\n mode?: ModelCheckerMode;\n seed?: number;\n maxSchedules?: number;\n bounds?: ModelCheckerBounds;\n history?: ModelCheckerHistory;\n stopWhenFrontierExhausted?: boolean;\n stateHasher?: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;\n traceRecorder?: ModelCheckerTraceRecorder;\n traceHasher?: ModelCheckerTraceHasher;\n traceHashMode?: ModelCheckerTraceHashMode;\n runtime?: FragnoRuntime;\n createContext: () => Promise<ModelCheckerExecutionContext<TSchema, TUowConfig>>;\n setup?: (ctx: RawUowTransactionContext<TSchema, TUowConfig>) => Promise<void>;\n buildTransactions: (\n ctx: RawUowTransactionContext<TSchema, TUowConfig>,\n ) => RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[];\n};\n\nexport type ModelCheckerScheduleResult = {\n schedule: ModelCheckerStep[];\n stateHash: string;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nexport type ModelCheckerRunResult = {\n schedules: ModelCheckerScheduleResult[];\n visitedPaths: number;\n};\n\ntype HistoryTracker = {\n add: (key: string) => boolean;\n size: () => number;\n};\n\ntype TransactionPlan = {\n stepsPerTransaction: number[];\n};\n\ntype ScheduleExecutionResult = {\n stateHash: string;\n newPathsAdded: boolean;\n traceHash?: string;\n trace?: ModelCheckerTrace;\n};\n\nconst defaultHistoryConfig: ModelCheckerHistory = { type: \"lru\", maxEntries: 5000 };\n\nconst mulberry32 = (seed: number) => {\n let t = seed >>> 0;\n return () => {\n t += 1831565813;\n let r = Math.imul(t ^ (t >>> 15), t | 1);\n r ^= r + Math.imul(r ^ (r >>> 7), r | 61);\n return ((r ^ (r >>> 14)) >>> 0) / 4294967296;\n };\n};\n\nconst serializeSchedulePrefix = (schedule: ModelCheckerStep[]): string =>\n schedule.map((step) => `${step.txId}:${step.phase}`).join(\"|\");\n\nconst buildPathKey = (pathHash: string, prefix: ModelCheckerStep[]): string =>\n `${pathHash}::${serializeSchedulePrefix(prefix)}`;\n\nconst createHistoryTracker = (history: ModelCheckerHistory): HistoryTracker | null => {\n if (!history) {\n return null;\n }\n\n if (history.type === \"unbounded\") {\n const set = new Set<string>();\n return {\n add: (key: string) => {\n const has = set.has(key);\n if (!has) {\n set.add(key);\n }\n return !has;\n },\n size: () => set.size,\n };\n }\n\n const maxEntries = Math.max(history.maxEntries, 1);\n const map = new Map<string, true>();\n return {\n add: (key: string) => {\n const existed = map.delete(key);\n map.set(key, true);\n if (map.size > maxEntries) {\n const first = map.keys().next().value;\n if (first !== undefined) {\n map.delete(first);\n }\n }\n return !existed;\n },\n size: () => map.size,\n };\n};\n\nconst normalizeForHash = (value: unknown): unknown => {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof FragnoId) {\n return {\n externalId: value.externalId,\n internalId: value.internalId?.toString(),\n version: value.version,\n };\n }\n\n if (value instanceof FragnoReference) {\n return value.internalId.toString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => normalizeForHash(entry));\n }\n\n if (typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const sortedKeys = Object.keys(record).sort();\n const normalized: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n normalized[key] = normalizeForHash(record[key]);\n }\n return normalized;\n }\n\n return value;\n};\n\nconst stableStringify = (value: unknown): string => JSON.stringify(normalizeForHash(value));\n\nexport const defaultStateHasher = async <TSchema extends AnySchema, TUowConfig>(\n ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>,\n): Promise<string> => {\n const tableNames = Object.keys(ctx.schema.tables).sort();\n const snapshot: { table: string; rows: unknown[] }[] = [];\n\n for (const tableName of tableNames) {\n const rows = await ctx.queryEngine.find(tableName, (b) =>\n b.whereIndex(\"primary\").orderByIndex(\"primary\", \"asc\"),\n );\n snapshot.push({ table: tableName, rows });\n }\n\n return stableStringify(snapshot);\n};\n\nexport const defaultTraceHasher = async (trace: ModelCheckerTrace): Promise<string> =>\n stableStringify(trace.events);\n\nconst resolvePathHash = (\n stateHash: string,\n traceHash: string | undefined,\n mode: ModelCheckerTraceHashMode,\n): string => {\n if (mode === \"state\") {\n return stateHash;\n }\n if (!traceHash) {\n throw new Error(\"Trace hash mode requires traceHasher to be configured.\");\n }\n if (mode === \"trace\") {\n return traceHash;\n }\n return `${stateHash}::${traceHash}`;\n};\n\nconst wrapRuntimeForTrace = (\n runtime: FragnoRuntime,\n record: (event: ModelCheckerTraceEvent) => void,\n): FragnoRuntime => ({\n time: {\n now: () => {\n const value = runtime.time.now();\n record({ type: \"runtime\", operation: \"time.now\", value });\n return value;\n },\n },\n random: {\n float: () => {\n const value = runtime.random.float();\n record({ type: \"runtime\", operation: \"random.float\", value });\n return value;\n },\n uuid: () => {\n const value = runtime.random.uuid();\n record({ type: \"runtime\", operation: \"random.uuid\", value });\n return value;\n },\n cuid: () => {\n const value = runtime.random.cuid();\n record({ type: \"runtime\", operation: \"random.cuid\", value });\n return value;\n },\n },\n});\n\nconst captureCoreTrace = (\n record: (event: ModelCheckerTraceEvent) => void,\n event: FragnoCoreTraceEvent,\n) => {\n record({ type: \"external\", name: event.type, payload: event });\n};\n\nconst createTraceContext = (\n recorder: ModelCheckerTraceRecorder | undefined,\n traceHasher: ModelCheckerTraceHasher | undefined,\n) => {\n const events = recorder || traceHasher ? ([] as ModelCheckerTraceEvent[]) : null;\n const record = (event: ModelCheckerTraceEvent) => {\n if (events) {\n events.push(event);\n }\n recorder?.(event);\n };\n\n return { events, record };\n};\n\nconst normalizeMutationOperation = (\n op: MutationOperation<AnySchema>,\n): NormalizedMutationOperation => {\n if (op.type === \"create\") {\n return {\n type: \"create\",\n table: op.table,\n namespace: op.namespace,\n values: normalizeForHash(op.values) as Record<string, unknown>,\n generatedExternalId: op.generatedExternalId,\n };\n }\n\n if (op.type === \"update\") {\n return {\n type: \"update\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n set: normalizeForHash(op.set) as Record<string, unknown>,\n };\n }\n\n if (op.type === \"delete\") {\n return {\n type: \"delete\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n checkVersion: op.checkVersion,\n };\n }\n\n return {\n type: \"check\",\n table: op.table,\n namespace: op.namespace,\n id: normalizeForHash(op.id),\n };\n};\n\nconst wrapCreateUnitOfWorkForTrace = <TSchema extends AnySchema, TUowConfig>(\n createUnitOfWork: (name?: string, config?: TUowConfig) => TypedUnitOfWork<TSchema, [], unknown>,\n getTxId: () => number | undefined,\n record: (event: ModelCheckerTraceEvent) => void,\n) => {\n return (name?: string, config?: TUowConfig) => {\n const uow = createUnitOfWork(name, config);\n return new Proxy(uow, {\n get(target, prop, receiver) {\n if (prop === \"executeRetrieve\") {\n return async () => {\n const result = await target.executeRetrieve();\n record({\n type: \"retrieve-output\",\n txId: getTxId(),\n uowName: target.name,\n output: normalizeForHash(result),\n });\n return result;\n };\n }\n\n if (prop === \"executeMutations\") {\n return async () => {\n const txId = getTxId();\n record({\n type: \"mutation-input\",\n txId,\n uowName: target.name,\n operations: target.getMutationOperations().map(normalizeMutationOperation),\n });\n try {\n const result = await target.executeMutations();\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: result.success,\n createdIds: result.success ? target.getCreatedIds().map(normalizeForHash) : [],\n });\n return result;\n } catch (error) {\n record({\n type: \"mutation-result\",\n txId,\n uowName: target.name,\n success: false,\n createdIds: [],\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n };\n }\n\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return value.bind(target);\n }\n return value;\n },\n });\n };\n};\n\nconst planTransactions = <TSchema extends AnySchema, TUowConfig>(\n transactions: RawUowTransaction<unknown, unknown, TSchema, TUowConfig>[],\n): TransactionPlan => {\n if (transactions.length === 0) {\n throw new Error(\"Model checker requires at least one transaction.\");\n }\n\n const stepsPerTransaction = transactions.map((tx) => {\n if (!tx.retrieve) {\n throw new Error(\"Model checker transactions must define a retrieve step.\");\n }\n return tx.mutate ? 2 : 1;\n });\n\n return { stepsPerTransaction };\n};\n\nconst generateAllSchedules = (stepsPerTransaction: number[]): ModelCheckerStep[][] => {\n const schedules: ModelCheckerStep[][] = [];\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const current: ModelCheckerStep[] = [];\n\n const walk = () => {\n if (current.length === totalSteps) {\n schedules.push([...current]);\n return;\n }\n\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] >= stepsPerTransaction[txId]) {\n continue;\n }\n const phase: ModelCheckerPhase = progress[txId] === 0 ? \"retrieve\" : \"mutate\";\n current.push({ txId, phase });\n progress[txId] += 1;\n walk();\n progress[txId] -= 1;\n current.pop();\n }\n };\n\n walk();\n return schedules;\n};\n\nconst generateRandomSchedule = (\n stepsPerTransaction: number[],\n rng: () => number,\n): ModelCheckerStep[] => {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n const progress = stepsPerTransaction.map(() => 0);\n const schedule: ModelCheckerStep[] = [];\n\n for (let stepIndex = 0; stepIndex < totalSteps; stepIndex += 1) {\n const candidates: number[] = [];\n for (let txId = 0; txId < stepsPerTransaction.length; txId += 1) {\n if (progress[txId] < stepsPerTransaction[txId]) {\n candidates.push(txId);\n }\n }\n const pick = candidates[Math.floor(rng() * candidates.length)];\n const phase: ModelCheckerPhase = progress[pick] === 0 ? \"retrieve\" : \"mutate\";\n schedule.push({ txId: pick, phase });\n progress[pick] += 1;\n }\n\n return schedule;\n};\n\nconst executeSchedule = async <TSchema extends AnySchema, TUowConfig>(\n schedule: ModelCheckerStep[],\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n history: HistoryTracker | null,\n stateHasher: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>,\n): Promise<ScheduleExecutionResult> => {\n const traceHashMode = config.traceHashMode ?? \"state\";\n const traceHasher =\n config.traceHasher ?? (traceHashMode === \"state\" ? undefined : defaultTraceHasher);\n const traceContext = createTraceContext(config.traceRecorder, traceHasher);\n const traceEnabled = Boolean(traceContext.events);\n const runtime =\n config.runtime && traceEnabled\n ? wrapRuntimeForTrace(config.runtime, traceContext.record)\n : config.runtime;\n\n const { ctx, cleanup } = await config.createContext();\n try {\n let currentTxId: number | undefined;\n const tracedContext = traceEnabled\n ? {\n ...ctx,\n createUnitOfWork: wrapCreateUnitOfWorkForTrace(\n ctx.createUnitOfWork,\n () => currentTxId,\n traceContext.record,\n ),\n runtime,\n }\n : { ...ctx, runtime };\n\n if (config.setup) {\n await config.setup(tracedContext);\n }\n\n const transactions = config.buildTransactions(tracedContext);\n const transactionState = transactions.map(() => ({ retrieveResult: undefined as unknown }));\n let lastStateHash: string | null = null;\n let newPathsAdded = false;\n\n const runSchedule = async () => {\n for (let stepIndex = 0; stepIndex < schedule.length; stepIndex += 1) {\n const step = schedule[stepIndex];\n if (!step) {\n throw new Error(\"Schedule step is missing.\");\n }\n const tx = transactions[step.txId];\n if (!tx) {\n throw new Error(`Transaction ${step.txId} is missing.`);\n }\n\n currentTxId = step.txId;\n if (traceEnabled) {\n traceContext.record({ type: \"schedule-step\", step, stepIndex });\n }\n\n if (step.phase === \"retrieve\") {\n transactionState[step.txId].retrieveResult = await tx.retrieve(tracedContext);\n } else {\n if (!tx.mutate) {\n throw new Error(`Transaction ${step.txId} does not define a mutate step.`);\n }\n await tx.mutate({\n ...tracedContext,\n retrieveResult: transactionState[step.txId].retrieveResult,\n });\n }\n\n if (history) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n const traceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n const prefix = schedule.slice(0, stepIndex + 1);\n const added = history.add(\n buildPathKey(resolvePathHash(lastStateHash, traceHash, traceHashMode), prefix),\n );\n if (added) {\n newPathsAdded = true;\n }\n }\n }\n\n currentTxId = undefined;\n\n if (!lastStateHash) {\n lastStateHash = await stateHasher({\n schema: config.schema,\n queryEngine: tracedContext.queryEngine,\n });\n }\n\n const finalTraceHash = traceHasher\n ? await traceHasher({ events: traceContext.events ?? [] })\n : undefined;\n\n return {\n stateHash: lastStateHash,\n newPathsAdded,\n traceHash: finalTraceHash,\n trace: traceContext.events ? { events: traceContext.events } : undefined,\n };\n };\n\n if (traceEnabled) {\n return await runWithTraceRecorder(\n (event) => captureCoreTrace(traceContext.record, event),\n runSchedule,\n );\n }\n\n return await runSchedule();\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nconst getTransactionPlan = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<TransactionPlan> => {\n const { ctx, cleanup } = await config.createContext();\n try {\n const context = config.runtime ? { ...ctx, runtime: config.runtime } : ctx;\n if (config.setup) {\n await config.setup(context);\n }\n const transactions = config.buildTransactions(context);\n return planTransactions(transactions);\n } finally {\n if (cleanup) {\n await cleanup();\n }\n }\n};\n\nexport const runModelChecker = async <TSchema extends AnySchema, TUowConfig>(\n config: ModelCheckerConfig<TSchema, TUowConfig>,\n): Promise<ModelCheckerRunResult> => {\n const mode = config.mode ?? \"exhaustive\";\n const historyConfig = config.history ?? defaultHistoryConfig;\n const history = createHistoryTracker(historyConfig);\n const stateHasher = config.stateHasher ?? defaultStateHasher;\n const { stepsPerTransaction } = await getTransactionPlan(config);\n const schedules: ModelCheckerScheduleResult[] = [];\n const maxSchedules = config.maxSchedules;\n const seed = config.seed ?? 1;\n\n if (mode === \"exhaustive\" || mode === \"bounded-exhaustive\") {\n if (mode === \"bounded-exhaustive\") {\n const bounds = config.bounds;\n if (bounds?.maxSteps !== undefined) {\n const totalSteps = stepsPerTransaction.reduce((sum, steps) => sum + steps, 0);\n if (totalSteps > bounds.maxSteps) {\n throw new Error(\n `bounded-exhaustive requires maxSteps >= ${totalSteps}, ` +\n `but received ${bounds.maxSteps}`,\n );\n }\n }\n }\n const allSchedules = generateAllSchedules(stepsPerTransaction);\n for (const schedule of allSchedules) {\n if (maxSchedules !== undefined && schedules.length >= maxSchedules) {\n break;\n }\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else if (mode === \"random\") {\n const rng = mulberry32(seed);\n const total = maxSchedules ?? 1;\n for (let i = 0; i < total; i += 1) {\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n }\n } else {\n const rng = mulberry32(seed);\n const stopWhenFrontierExhausted = config.stopWhenFrontierExhausted ?? false;\n let iterations = 0;\n let frontierExhausted = false;\n\n while (!frontierExhausted) {\n if (maxSchedules !== undefined && iterations >= maxSchedules) {\n break;\n }\n const schedule = generateRandomSchedule(stepsPerTransaction, rng);\n const result = await executeSchedule(schedule, config, history, stateHasher);\n schedules.push({\n schedule,\n stateHash: result.stateHash,\n traceHash: result.traceHash,\n trace: result.trace,\n });\n iterations += 1;\n\n if (stopWhenFrontierExhausted && history) {\n frontierExhausted = !result.newPathsAdded;\n }\n }\n }\n\n return {\n schedules,\n visitedPaths: history?.size() ?? 0,\n };\n};\n"],"mappings":"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"}