@fragno-dev/test 1.0.1 → 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 +54 -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 +13 -12
  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
@@ -1 +1 @@
1
- {"version":3,"file":"db-test.js","names":["#adapter","#fragments","schemaConfigs: SchemaConfig[]","builderConfigs: Array<{ config: any; routes: any; options: any }>","schema: AnySchema | undefined","namespace: string | undefined","providedServicesByName: Record<string, { service: any; orm: any }>","fragmentResults: any[]","serviceImplementations: Record<string, any>","fragmentResults"],"sources":["../src/db-test.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n RequestThisContext,\n FragnoPublicConfig,\n FragmentInstantiationBuilder,\n FragnoInstantiatedFragment,\n FragmentDefinition,\n} from \"@fragno-dev/core\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"@fragno-dev/core/route\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type AdapterContext,\n type SchemaConfig,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { BaseTestContext } from \".\";\n\n// BoundServices is an internal type that strips 'this' parameters from service methods\n// It's used to represent services after they've been bound to a context\ntype BoundServices<T> = {\n [K in keyof T]: T[K] extends (this: any, ...args: infer A) => infer R // eslint-disable-line @typescript-eslint/no-explicit-any\n ? (...args: A) => R\n : T[K] extends Record<string, unknown>\n ? BoundServices<T[K]>\n : T[K];\n};\n\n// Extract the schema type from database fragment dependencies\n// Database fragments have ImplicitDatabaseDependencies<TSchema> which includes `schema: TSchema`\ntype ExtractSchemaFromDeps<TDeps> = TDeps extends { schema: infer TSchema extends AnySchema }\n ? TSchema\n : AnySchema;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyLinkedFragments = Record<string, any>;\n\n// Forward declarations for recursive type references\ninterface FragmentResult<\n TDeps,\n TServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any\n TSchema extends AnySchema,\n TLinkedFragments extends AnyLinkedFragments = {},\n> {\n fragment: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >;\n services: TServices;\n deps: TDeps;\n callRoute: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >[\"callRoute\"];\n db: SimpleQueryInterface<TSchema>;\n}\n\n// Safe: Catch-all for any fragment result type\ntype AnyFragmentResult = FragmentResult<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n// Safe: Catch-all for any fragment builder config type\ntype AnyFragmentBuilderConfig = FragmentBuilderConfig<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n/**\n * Configuration for a single fragment in the test builder\n */\ninterface FragmentBuilderConfig<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n> {\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >;\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >;\n migrateToVersion?: number;\n}\n\n/**\n * Test context combining base and adapter-specific functionality\n */\ntype TestContext<\n T extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> = BaseTestContext &\n AdapterContext<T> & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n /**\n * Execute a callback within the first fragment's request context.\n * This is useful for calling services outside of route handlers in tests.\n */\n inContext<TResult>(callback: (this: TFirstFragmentThisContext) => TResult): TResult;\n inContext<TResult>(\n callback: (this: TFirstFragmentThisContext) => Promise<TResult>,\n ): Promise<TResult>;\n };\n\n/**\n * Result of building the database fragments test\n */\ninterface DatabaseFragmentsTestResult<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n fragments: TFragments;\n test: TestContext<TAdapter, TFirstFragmentThisContext>;\n}\n\n/**\n * Internal storage for fragment configurations\n */\ntype FragmentConfigMap = Map<string, AnyFragmentBuilderConfig>;\n\n/**\n * Builder for creating multiple database fragments for testing\n */\nexport class DatabaseFragmentsTestBuilder<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter | undefined = undefined,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n #adapter?: SupportedAdapter;\n #fragments: FragmentConfigMap = new Map();\n\n /**\n * Set the test adapter configuration\n */\n withTestAdapter<TNewAdapter extends SupportedAdapter>(\n adapter: TNewAdapter,\n ): DatabaseFragmentsTestBuilder<TFragments, TNewAdapter, TFirstFragmentThisContext> {\n this.#adapter = adapter;\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Add a fragment to the test setup\n *\n * @param name - Unique name for the fragment\n * @param builder - Pre-configured instantiation builder\n * @param options - Additional options (optional)\n */\n withFragment<\n TName extends string,\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n >(\n name: TName,\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >,\n options?: {\n migrateToVersion?: number;\n },\n ): DatabaseFragmentsTestBuilder<\n TFragments & {\n [K in TName]: FragmentResult<\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FlattenRouteFactories<TRoutesOrFactories>,\n ExtractSchemaFromDeps<TDeps>, // Extract actual schema type from deps\n TLinkedFragments\n >;\n },\n TAdapter,\n // If this is the first fragment (TFragments is empty {}), use THandlerThisContext; otherwise keep existing\n keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext\n > {\n this.#fragments.set(name, {\n definition: builder.definition,\n builder,\n migrateToVersion: options?.migrateToVersion,\n });\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Build the test setup and return fragments and test context\n */\n async build(): Promise<\n TAdapter extends SupportedAdapter\n ? DatabaseFragmentsTestResult<TFragments, TAdapter, TFirstFragmentThisContext>\n : never\n > {\n if (!this.#adapter) {\n throw new Error(\"Test adapter must be set using withTestAdapter()\");\n }\n\n if (this.#fragments.size === 0) {\n throw new Error(\"At least one fragment must be added using withFragment()\");\n }\n\n const adapterConfig = this.#adapter;\n\n // Extract fragment names and configs\n const fragmentNames = Array.from(this.#fragments.keys());\n const fragmentConfigs = Array.from(this.#fragments.values());\n\n // Extract schemas from definitions and prepare schema configs\n const schemaConfigs: SchemaConfig[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const builderConfigs: Array<{ config: any; routes: any; options: any }> = [];\n\n for (const fragmentConfig of fragmentConfigs) {\n const builder = fragmentConfig.builder;\n const definition = builder.definition;\n\n // Extract schema and namespace from definition by calling dependencies with a mock adapter\n let schema: AnySchema | undefined;\n let namespace: string | undefined;\n\n if (definition.dependencies) {\n try {\n // Create a mock adapter to extract the schema\n const mockAdapter = {\n createQueryEngine: () => ({ schema: null }),\n contextStorage: { run: (_data: unknown, fn: () => unknown) => fn() },\n };\n\n // Use the actual config from the builder instead of an empty mock\n // This ensures dependencies can be properly initialized (e.g., Stripe with API keys)\n const actualConfig = builder.config ?? {};\n\n const deps = definition.dependencies({\n config: actualConfig,\n options: {\n databaseAdapter: mockAdapter as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n } as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n });\n\n // The schema and namespace are in deps for database fragments\n if (deps && typeof deps === \"object\" && \"schema\" in deps) {\n schema = (deps as any).schema; // eslint-disable-line @typescript-eslint/no-explicit-any\n namespace = (deps as any).namespace; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n } catch (error) {\n // If extraction fails, provide a helpful error message\n const errorMessage =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"Unknown error\";\n\n throw new Error(\n `Failed to extract schema from fragment '${definition.name}'.\\n` +\n `Original error: ${errorMessage}\\n\\n` +\n `Make sure the fragment is a database fragment using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n }\n\n if (!schema) {\n throw new Error(\n `Fragment '${definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n\n if (!namespace) {\n throw new Error(\n `Fragment '${definition.name}' does not have a namespace in dependencies. ` +\n `This should be automatically provided by withDatabase().`,\n );\n }\n\n schemaConfigs.push({\n schema,\n namespace,\n migrateToVersion: fragmentConfig.migrateToVersion,\n });\n\n // Extract config, routes, and options from builder using public getters\n builderConfigs.push({\n config: builder.config ?? {},\n routes: builder.routes ?? [],\n options: builder.options ?? {},\n });\n }\n\n // Create adapter with all schemas\n const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);\n\n // Helper to create fragments with service wiring\n const createFragments = () => {\n // First pass: create fragments without service dependencies to extract provided services\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providedServicesByName: Record<string, { service: any; orm: any }> = {};\n const fragmentResults: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Instantiate fragment using the builder\n const fragment = fragmentConfig.builder.withOptions(mergedOptions).build();\n\n // Extract provided services based on serviceDependencies metadata\n // Note: serviceDependencies lists services this fragment USES, not provides\n // For provided services, we need to check what's actually in fragment.services\n // and match against other fragments' service dependencies\n\n // Store all services as potentially provided\n for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) {\n providedServicesByName[serviceName] = {\n service: serviceImpl,\n orm,\n };\n }\n\n // Store the fragment result\n const deps = fragment.$internal?.deps;\n\n fragmentResults.push({\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n });\n }\n\n // Second pass: rebuild fragments with service dependencies wired up\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const definition = fragmentConfig.builder.definition;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Build service implementations for services this fragment uses\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const serviceImplementations: Record<string, any> = {};\n const serviceDependencies = definition.serviceDependencies;\n\n if (serviceDependencies) {\n for (const serviceName of Object.keys(serviceDependencies)) {\n if (providedServicesByName[serviceName]) {\n serviceImplementations[serviceName] = providedServicesByName[serviceName]!.service;\n }\n }\n }\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Rebuild the fragment with service implementations using the builder\n const fragment = fragmentConfig.builder\n .withOptions(mergedOptions)\n .withServices(serviceImplementations as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .build();\n\n // Update the result\n // Access deps from the internal property\n const deps = fragment.$internal?.deps;\n\n fragmentResults[i] = {\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n };\n }\n\n return fragmentResults;\n };\n\n const fragmentResults = createFragments();\n\n // Wrap resetDatabase to also recreate all fragments\n const originalResetDatabase = testContext.resetDatabase;\n const resetDatabase = async () => {\n await originalResetDatabase();\n\n // Recreate all fragments with service wiring\n const newFragmentResults = createFragments();\n\n // Update the result objects\n newFragmentResults.forEach((newResult, index) => {\n const result = fragmentResults[index]!;\n result.fragment = newResult.fragment;\n result.services = newResult.services;\n result.deps = newResult.deps;\n result.callRoute = newResult.callRoute;\n result._orm = newResult._orm;\n });\n };\n\n // Get the first fragment's inContext method\n const firstFragment = fragmentResults[0]?.fragment;\n if (!firstFragment) {\n throw new Error(\"At least one fragment must be added\");\n }\n\n const finalTestContext = {\n ...testContext,\n resetDatabase,\n adapter,\n inContext: firstFragment.inContext.bind(firstFragment),\n };\n\n // Build result object with named fragments\n const fragmentsObject = Object.fromEntries(\n fragmentNames.map((name, index) => [name, fragmentResults[index]]),\n );\n\n // Safe cast: We've already validated that adapterConfig is SupportedAdapter at the beginning of build()\n // TypeScript can't infer this through the conditional return type, so we use 'as any'\n return {\n fragments: fragmentsObject as TFragments,\n test: finalTestContext,\n } as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n}\n\n/**\n * Create a builder for setting up multiple database fragments for testing.\n * This is the new builder-based API that works with the new fragment instantiation builders.\n *\n * @example\n * ```typescript\n * const userFragmentDef = defineFragment(\"user\")\n * .extend(withDatabase(userSchema))\n * .withDependencies(...)\n * .build();\n *\n * const postFragmentDef = defineFragment(\"post\")\n * .extend(withDatabase(postSchema))\n * .withDependencies(...)\n * .build();\n *\n * const { fragments, test } = await buildDatabaseFragmentsTest()\n * .withTestAdapter({ type: \"kysely-sqlite\" })\n * .withFragment(\"user\",\n * instantiate(userFragmentDef)\n * .withConfig({ ... })\n * .withRoutes([...])\n * )\n * .withFragment(\"post\",\n * instantiate(postFragmentDef)\n * .withRoutes([...])\n * )\n * .build();\n *\n * // Access fragments by name\n * await fragments.user.services.createUser(...);\n * await fragments.post.services.createPost(...);\n *\n * // Access dependencies directly\n * const userDeps = fragments.user.deps;\n *\n * // Shared test context\n * await test.resetDatabase();\n * await test.cleanup();\n * const adapter = test.adapter; // Access the database adapter\n * ```\n */\nexport function buildDatabaseFragmentsTest(): DatabaseFragmentsTestBuilder<\n {},\n undefined,\n RequestThisContext\n> {\n return new DatabaseFragmentsTestBuilder();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"db-test.js","names":["#adapter","#fragments","schemaConfigs: SchemaConfig[]","builderConfigs: Array<{ config: any; routes: any; options: any }>","schema: AnySchema | undefined","namespace: string | null | undefined","providedServicesByName: Record<string, { service: any; orm: any }>","fragmentResults: any[]","serviceImplementations: Record<string, any>","fragmentResults","drainError: unknown","cleanupError: unknown"],"sources":["../src/db-test.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n RequestThisContext,\n FragnoPublicConfig,\n FragmentInstantiationBuilder,\n FragnoInstantiatedFragment,\n FragmentDefinition,\n} from \"@fragno-dev/core\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"@fragno-dev/core/route\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type AdapterContext,\n type SchemaConfig,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\nimport type { BaseTestContext } from \".\";\nimport { drainDurableHooks } from \"./durable-hooks\";\n\n// BoundServices is an internal type that strips 'this' parameters from service methods\n// It's used to represent services after they've been bound to a context\ntype BoundServices<T> = {\n [K in keyof T]: T[K] extends (this: any, ...args: infer A) => infer R // eslint-disable-line @typescript-eslint/no-explicit-any\n ? (...args: A) => R\n : T[K] extends Record<string, unknown>\n ? BoundServices<T[K]>\n : T[K];\n};\n\n// Extract the schema type from database fragment dependencies\n// Database fragments have ImplicitDatabaseDependencies<TSchema> which includes `schema: TSchema`\ntype ExtractSchemaFromDeps<TDeps> = TDeps extends { schema: infer TSchema extends AnySchema }\n ? TSchema\n : AnySchema;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyLinkedFragments = Record<string, any>;\n\n// Forward declarations for recursive type references\ninterface FragmentResult<\n TDeps,\n TServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any\n TSchema extends AnySchema,\n TLinkedFragments extends AnyLinkedFragments = {},\n> {\n fragment: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >;\n services: TServices;\n deps: TDeps;\n callRoute: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >[\"callRoute\"];\n db: SimpleQueryInterface<TSchema>;\n}\n\n// Safe: Catch-all for any fragment result type\ntype AnyFragmentResult = FragmentResult<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n// Safe: Catch-all for any fragment builder config type\ntype AnyFragmentBuilderConfig = FragmentBuilderConfig<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n/**\n * Configuration for a single fragment in the test builder\n */\ninterface FragmentBuilderConfig<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n> {\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >;\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >;\n migrateToVersion?: number;\n}\n\n/**\n * Test context combining base and adapter-specific functionality\n */\ntype TestContext<\n T extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> = BaseTestContext &\n AdapterContext<T> & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n /**\n * Execute a callback within the first fragment's request context.\n * This is useful for calling services outside of route handlers in tests.\n */\n inContext<TResult>(callback: (this: TFirstFragmentThisContext) => TResult): TResult;\n inContext<TResult>(\n callback: (this: TFirstFragmentThisContext) => Promise<TResult>,\n ): Promise<TResult>;\n };\n\n/**\n * Result of building the database fragments test\n */\ninterface DatabaseFragmentsTestResult<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n fragments: TFragments;\n test: TestContext<TAdapter, TFirstFragmentThisContext>;\n}\n\n/**\n * Internal storage for fragment configurations\n */\ntype FragmentConfigMap = Map<string, AnyFragmentBuilderConfig>;\n\n/**\n * Builder for creating multiple database fragments for testing\n */\nexport class DatabaseFragmentsTestBuilder<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter | undefined = undefined,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n #adapter?: SupportedAdapter;\n #fragments: FragmentConfigMap = new Map();\n\n /**\n * Set the test adapter configuration\n */\n withTestAdapter<TNewAdapter extends SupportedAdapter>(\n adapter: TNewAdapter,\n ): DatabaseFragmentsTestBuilder<TFragments, TNewAdapter, TFirstFragmentThisContext> {\n this.#adapter = adapter;\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Add a fragment to the test setup\n *\n * @param name - Unique name for the fragment\n * @param builder - Pre-configured instantiation builder\n * @param options - Additional options (optional)\n */\n withFragment<\n TName extends string,\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n >(\n name: TName,\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >,\n options?: {\n migrateToVersion?: number;\n },\n ): DatabaseFragmentsTestBuilder<\n TFragments & {\n [K in TName]: FragmentResult<\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FlattenRouteFactories<TRoutesOrFactories>,\n ExtractSchemaFromDeps<TDeps>, // Extract actual schema type from deps\n TLinkedFragments\n >;\n },\n TAdapter,\n // If this is the first fragment (TFragments is empty {}), use THandlerThisContext; otherwise keep existing\n keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext\n > {\n this.#fragments.set(name, {\n definition: builder.definition,\n builder,\n migrateToVersion: options?.migrateToVersion,\n });\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Build the test setup and return fragments and test context\n */\n async build(): Promise<\n TAdapter extends SupportedAdapter\n ? DatabaseFragmentsTestResult<TFragments, TAdapter, TFirstFragmentThisContext>\n : never\n > {\n if (!this.#adapter) {\n throw new Error(\"Test adapter must be set using withTestAdapter()\");\n }\n\n if (this.#fragments.size === 0) {\n throw new Error(\"At least one fragment must be added using withFragment()\");\n }\n\n const adapterConfig = this.#adapter;\n\n // Extract fragment names and configs\n const fragmentNames = Array.from(this.#fragments.keys());\n const fragmentConfigs = Array.from(this.#fragments.values());\n\n // Extract schemas from definitions and prepare schema configs\n const schemaConfigs: SchemaConfig[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const builderConfigs: Array<{ config: any; routes: any; options: any }> = [];\n\n for (const fragmentConfig of fragmentConfigs) {\n const builder = fragmentConfig.builder;\n const definition = builder.definition;\n\n // Extract schema and namespace from definition by calling dependencies with a mock adapter\n let schema: AnySchema | undefined;\n let namespace: string | null | undefined;\n\n if (definition.dependencies) {\n try {\n // Create a mock adapter to extract the schema\n const mockAdapter = {\n createQueryEngine: () => ({ schema: null }),\n getSchemaVersion: async () => undefined,\n namingStrategy: {\n namespaceScope: \"suffix\",\n namespaceToSchema: (value: string) => value,\n tableName: (logicalTable: string, ns: string | null) =>\n ns ? `${logicalTable}_${ns}` : logicalTable,\n columnName: (logicalColumn: string) => logicalColumn,\n indexName: (logicalIndex: string) => logicalIndex,\n uniqueIndexName: (logicalIndex: string) => logicalIndex,\n foreignKeyName: ({ referenceName }: { referenceName: string }) => referenceName,\n },\n contextStorage: { run: (_data: unknown, fn: () => unknown) => fn() },\n close: async () => {},\n };\n\n // Use the actual config from the builder instead of an empty mock\n // This ensures dependencies can be properly initialized (e.g., Stripe with API keys)\n const actualConfig = builder.config ?? {};\n\n const deps = definition.dependencies({\n config: actualConfig,\n options: {\n databaseAdapter: mockAdapter as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n } as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n });\n\n // The schema and namespace are in deps for database fragments\n if (deps && typeof deps === \"object\" && \"schema\" in deps) {\n schema = (deps as any).schema; // eslint-disable-line @typescript-eslint/no-explicit-any\n namespace = (deps as any).namespace; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n } catch (error) {\n // If extraction fails, provide a helpful error message\n const errorMessage =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"Unknown error\";\n\n throw new Error(\n `Failed to extract schema from fragment '${definition.name}'.\\n` +\n `Original error: ${errorMessage}\\n\\n` +\n `Make sure the fragment is a database fragment using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n }\n\n if (!schema) {\n throw new Error(\n `Fragment '${definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n\n if (namespace === undefined) {\n throw new Error(\n `Fragment '${definition.name}' does not have a namespace in dependencies. ` +\n `This should be automatically provided by withDatabase().`,\n );\n }\n\n schemaConfigs.push({\n schema,\n namespace,\n migrateToVersion: fragmentConfig.migrateToVersion,\n });\n\n // Extract config, routes, and options from builder using public getters\n builderConfigs.push({\n config: builder.config ?? {},\n routes: builder.routes ?? [],\n options: builder.options ?? {},\n });\n }\n\n const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);\n\n // Helper to create fragments with service wiring\n const createFragments = () => {\n // First pass: create fragments without service dependencies to extract provided services\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providedServicesByName: Record<string, { service: any; orm: any }> = {};\n const fragmentResults: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Instantiate fragment using the builder\n const fragment = fragmentConfig.builder.withOptions(mergedOptions).build();\n\n // Extract provided services based on serviceDependencies metadata\n // Note: serviceDependencies lists services this fragment USES, not provides\n // For provided services, we need to check what's actually in fragment.services\n // and match against other fragments' service dependencies\n\n // Store all services as potentially provided\n for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) {\n providedServicesByName[serviceName] = {\n service: serviceImpl,\n orm,\n };\n }\n\n // Store the fragment result\n const deps = fragment.$internal?.deps;\n\n fragmentResults.push({\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n });\n }\n\n // Second pass: rebuild fragments with service dependencies wired up\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const definition = fragmentConfig.builder.definition;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Build service implementations for services this fragment uses\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const serviceImplementations: Record<string, any> = {};\n const serviceDependencies = definition.serviceDependencies;\n\n if (serviceDependencies) {\n for (const serviceName of Object.keys(serviceDependencies)) {\n if (providedServicesByName[serviceName]) {\n serviceImplementations[serviceName] = providedServicesByName[serviceName]!.service;\n }\n }\n }\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Rebuild the fragment with service implementations using the builder\n const fragment = fragmentConfig.builder\n .withOptions(mergedOptions)\n .withServices(serviceImplementations as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .build();\n\n // Update the result\n // Access deps from the internal property\n const deps = fragment.$internal?.deps;\n\n fragmentResults[i] = {\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n };\n }\n\n return fragmentResults;\n };\n\n const fragmentResults = createFragments();\n\n // Wrap resetDatabase to also recreate all fragments\n const originalResetDatabase = testContext.resetDatabase;\n const resetDatabase = async () => {\n await originalResetDatabase();\n\n // Recreate all fragments with service wiring\n const newFragmentResults = createFragments();\n\n // Update the result objects\n newFragmentResults.forEach((newResult, index) => {\n const result = fragmentResults[index]!;\n result.fragment = newResult.fragment;\n result.services = newResult.services;\n result.deps = newResult.deps;\n result.callRoute = newResult.callRoute;\n result._orm = newResult._orm;\n });\n };\n\n // Get the first fragment's inContext method\n const firstFragment = fragmentResults[0]?.fragment;\n if (!firstFragment) {\n throw new Error(\"At least one fragment must be added\");\n }\n\n const originalCleanup = testContext.cleanup;\n const cleanup = async () => {\n let drainError: unknown;\n let cleanupError: unknown;\n\n for (const result of fragmentResults) {\n try {\n await drainDurableHooks(result.fragment);\n } catch (error) {\n if (!drainError) {\n drainError = error;\n }\n }\n }\n\n try {\n await originalCleanup();\n } catch (error) {\n cleanupError = error;\n }\n\n if (drainError && cleanupError) {\n throw new AggregateError(\n [drainError, cleanupError],\n \"Failed to drain durable hooks and clean up test context\",\n );\n }\n\n if (drainError) {\n throw drainError;\n }\n\n if (cleanupError) {\n throw cleanupError;\n }\n };\n\n const finalTestContext = {\n ...testContext,\n resetDatabase,\n cleanup,\n adapter,\n inContext: firstFragment.inContext.bind(firstFragment),\n };\n\n // Build result object with named fragments\n const fragmentsObject = Object.fromEntries(\n fragmentNames.map((name, index) => [name, fragmentResults[index]]),\n );\n\n // Safe cast: We've already validated that adapterConfig is SupportedAdapter at the beginning of build()\n // TypeScript can't infer this through the conditional return type, so we use 'as any'\n return {\n fragments: fragmentsObject as TFragments,\n test: finalTestContext,\n } as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n}\n\n/**\n * Create a builder for setting up multiple database fragments for testing.\n * This is the new builder-based API that works with the new fragment instantiation builders.\n *\n * @example\n * ```typescript\n * const userFragmentDef = defineFragment(\"user\")\n * .extend(withDatabase(userSchema))\n * .withDependencies(...)\n * .build();\n *\n * const postFragmentDef = defineFragment(\"post\")\n * .extend(withDatabase(postSchema))\n * .withDependencies(...)\n * .build();\n *\n * const { fragments, test } = await buildDatabaseFragmentsTest()\n * .withTestAdapter({ type: \"kysely-sqlite\" })\n * .withFragment(\"user\",\n * instantiate(userFragmentDef)\n * .withConfig({ ... })\n * .withRoutes([...])\n * )\n * .withFragment(\"post\",\n * instantiate(postFragmentDef)\n * .withRoutes([...])\n * )\n * .build();\n *\n * // Access fragments by name\n * await fragments.user.services.createUser(...);\n * await fragments.post.services.createPost(...);\n *\n * // Access dependencies directly\n * const userDeps = fragments.user.deps;\n *\n * // Shared test context\n * await test.resetDatabase();\n * await test.cleanup();\n * const adapter = test.adapter; // Access the database adapter\n * ```\n */\nexport function buildDatabaseFragmentsTest(): DatabaseFragmentsTestBuilder<\n {},\n undefined,\n RequestThisContext\n> {\n return new DatabaseFragmentsTestBuilder();\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { AnyFragnoInstantiatedFragment } from "@fragno-dev/core";
2
+
3
+ //#region src/durable-hooks.d.ts
4
+ declare function drainDurableHooks(fragment: AnyFragnoInstantiatedFragment): Promise<void>;
5
+ //#endregion
6
+ export { drainDurableHooks };
7
+ //# sourceMappingURL=durable-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-hooks.d.ts","names":[],"sources":["../src/durable-hooks.ts"],"sourcesContent":[],"mappings":";;;iBAMsB,iBAAA,WAA4B,gCAAgC"}
@@ -0,0 +1,12 @@
1
+ import { createDurableHooksProcessor } from "@fragno-dev/db";
2
+
3
+ //#region src/durable-hooks.ts
4
+ async function drainDurableHooks(fragment) {
5
+ const processor = createDurableHooksProcessor(fragment);
6
+ if (!processor) return;
7
+ await processor.drain();
8
+ }
9
+
10
+ //#endregion
11
+ export { drainDurableHooks };
12
+ //# sourceMappingURL=durable-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-hooks.js","names":[],"sources":["../src/durable-hooks.ts"],"sourcesContent":["import {\n createDurableHooksProcessor,\n type AnyFragnoInstantiatedDatabaseFragment,\n} from \"@fragno-dev/db\";\nimport type { AnyFragnoInstantiatedFragment } from \"@fragno-dev/core\";\n\nexport async function drainDurableHooks(fragment: AnyFragnoInstantiatedFragment): Promise<void> {\n const processor = createDurableHooksProcessor(fragment as AnyFragnoInstantiatedDatabaseFragment);\n if (!processor) {\n return;\n }\n await processor.drain();\n}\n"],"mappings":";;;AAMA,eAAsB,kBAAkB,UAAwD;CAC9F,MAAM,YAAY,4BAA4B,SAAkD;AAChG,KAAI,CAAC,UACH;AAEF,OAAM,UAAU,OAAO"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- import { AdapterContext, DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter } from "./adapters.js";
1
+ import { AdapterContext, DrizzlePgliteAdapter, InMemoryAdapterConfig, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter } from "./adapters.js";
2
2
  import { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest } from "./db-test.js";
3
+ import { drainDurableHooks } from "./durable-hooks.js";
4
+ import { ModelCheckerBounds, ModelCheckerConfig, ModelCheckerMode, ModelCheckerRunResult, ModelCheckerScheduleResult, ModelCheckerStep, ModelCheckerTrace, ModelCheckerTraceEvent, ModelCheckerTraceHashMode, ModelCheckerTraceHasher, ModelCheckerTraceRecorder, NormalizedMutationOperation, RawUowMutateContext, RawUowTransaction, RawUowTransactionBuilder, RawUowTransactionContext, createRawUowTransaction, defaultStateHasher, defaultTraceHasher, runModelChecker } from "./model-checker.js";
5
+ import { ModelCheckerAdapter, ModelCheckerScheduler } from "./model-checker-adapter.js";
6
+ import { ModelCheckerActor, ModelCheckerActorsConfig, ModelCheckerInvariant, ModelCheckerInvariantContext, runModelCheckerWithActors } from "./model-checker-actors.js";
3
7
  import { CreateFragmentForTestOptions, RouteHandlerInputOptions, createFragmentForTest } from "@fragno-dev/core/test";
4
8
  import { DatabaseAdapter } from "@fragno-dev/db";
5
9
  import { AnySchema } from "@fragno-dev/db/schema";
@@ -19,17 +23,17 @@ interface BaseTestContext {
19
23
  * Internal interface with getOrm for adapter implementations
20
24
  */
21
25
  interface InternalTestContextMethods {
22
- getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;
26
+ getOrm: <TSchema extends AnySchema>(namespace: string | null) => SimpleQueryInterface<TSchema>;
23
27
  }
24
28
  /**
25
29
  * Helper to create common test context methods from an ORM map
26
30
  * This is used internally by adapter implementations to avoid code duplication
27
31
  */
28
- declare function createCommonTestContextMethods(ormMap: Map<string, SimpleQueryInterface<any>>): InternalTestContextMethods;
32
+ declare function createCommonTestContextMethods(ormMap: Map<string | null, SimpleQueryInterface<any>>): InternalTestContextMethods;
29
33
  /**
30
34
  * Complete test context combining base and adapter-specific functionality
31
35
  */
32
36
  type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;
33
37
  //#endregion
34
- export { type AdapterContext, BaseTestContext, type CreateFragmentForTestOptions, DatabaseFragmentsTestBuilder, type DrizzlePgliteAdapter, InternalTestContextMethods, type KyselyPgliteAdapter, type KyselySqliteAdapter, type RouteHandlerInputOptions, type SupportedAdapter, TestContext, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest };
38
+ export { type AdapterContext, BaseTestContext, type CreateFragmentForTestOptions, DatabaseFragmentsTestBuilder, type DrizzlePgliteAdapter, type InMemoryAdapterConfig, InternalTestContextMethods, type KyselyPgliteAdapter, type KyselySqliteAdapter, type ModelCheckerActor, type ModelCheckerActorsConfig, ModelCheckerAdapter, type ModelCheckerBounds, type ModelCheckerConfig, type ModelCheckerInvariant, type ModelCheckerInvariantContext, type ModelCheckerMode, type ModelCheckerRunResult, type ModelCheckerScheduleResult, type ModelCheckerScheduler, type ModelCheckerStep, type ModelCheckerTrace, type ModelCheckerTraceEvent, type ModelCheckerTraceHashMode, type ModelCheckerTraceHasher, type ModelCheckerTraceRecorder, type NormalizedMutationOperation, type RawUowMutateContext, type RawUowTransaction, type RawUowTransactionBuilder, type RawUowTransactionContext, type RouteHandlerInputOptions, type SupportedAdapter, TestContext, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest, createRawUowTransaction, defaultStateHasher, defaultTraceHasher, drainDurableHooks, runModelChecker, runModelCheckerWithActors };
35
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiCA;;AAGuB,UAHN,eAAA,CAGM;EACN,SAAA,OAAA,EAFG,eAEH,CAAA,GAAA,CAAA;EAAO,aAAA,EAAA,GAAA,GADD,OACC,CAAA,IAAA,CAAA;EAMP,OAAA,EAAA,GAAA,GANA,OAMA,CAAA,IAAA,CAAA;;;;;AAQD,UARC,0BAAA,CAQ6B;EAExB,MAAA,EAAA,CAAA,gBATK,SASL,CAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GATsC,oBAStC,CAT2D,OAS3D,CAAA;;;;AAgBtB;;AAAsD,iBAlBtC,8BAAA,CAkBsC,MAAA,EAhB5C,GAgB4C,CAAA,MAAA,EAhBhC,oBAgBgC,CAAA,GAAA,CAAA,CAAA,CAAA,EAfnD,0BAemD;;;;KAA1C,sBAAsB,oBAAoB,kBAAkB,eAAe"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAoEA;;AAGuB,UAHN,eAAA,CAGM;EACN,SAAA,OAAA,EAFG,eAEH,CAAA,GAAA,CAAA;EAAO,aAAA,EAAA,GAAA,GADD,OACC,CAAA,IAAA,CAAA;EAMP,OAAA,EAAA,GAAA,GANA,OAMA,CAAA,IAAA,CAAA;;;;;AAQD,UARC,0BAAA,CAQ6B;EAEjB,MAAA,EAAA,CAAA,gBATF,SASE,CAAA,CAAA,SAAA,EAAA,MAAA,GAAA,IAAA,EAAA,GATsC,oBAStC,CAT2D,OAS3D,CAAA;;;;AAgB7B;;AAAsD,iBAlBtC,8BAAA,CAkBsC,MAAA,EAhB5C,GAgB4C,CAAA,MAAA,GAAA,IAAA,EAhBzB,oBAgByB,CAAA,GAAA,CAAA,CAAA,CAAA,EAfnD,0BAemD;;;;KAA1C,sBAAsB,oBAAoB,kBAAkB,eAAe"}
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
+ import { ModelCheckerAdapter } from "./model-checker-adapter.js";
2
+ import { drainDurableHooks } from "./durable-hooks.js";
1
3
  import { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest } from "./db-test.js";
4
+ import { createRawUowTransaction, defaultStateHasher, defaultTraceHasher, runModelChecker } from "./model-checker.js";
5
+ import { runModelCheckerWithActors } from "./model-checker-actors.js";
2
6
  import { createFragmentForTest } from "@fragno-dev/core/test";
3
7
 
4
8
  //#region src/index.ts
@@ -9,11 +13,11 @@ import { createFragmentForTest } from "@fragno-dev/core/test";
9
13
  function createCommonTestContextMethods(ormMap) {
10
14
  return { getOrm: (namespace) => {
11
15
  const orm = ormMap.get(namespace);
12
- if (!orm) throw new Error(`No ORM found for namespace: ${namespace}`);
16
+ if (!orm) throw new Error(`No ORM found for namespace: ${String(namespace)}`);
13
17
  return orm;
14
18
  } };
15
19
  }
16
20
 
17
21
  //#endregion
18
- export { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest };
22
+ export { DatabaseFragmentsTestBuilder, ModelCheckerAdapter, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest, createRawUowTransaction, defaultStateHasher, defaultTraceHasher, drainDurableHooks, runModelChecker, runModelCheckerWithActors };
19
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n SupportedAdapter,\n AdapterContext,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n} from \"@fragno-dev/core/test\";\n\n// Re-export adapter types\nexport type {\n SupportedAdapter,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n AdapterContext,\n} from \"./adapters\";\n\n// Re-export new builder-based database test utilities\nexport { buildDatabaseFragmentsTest, DatabaseFragmentsTestBuilder } from \"./db-test\";\n\n/**\n * Base test context with common functionality across all adapters\n */\nexport interface BaseTestContext {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>;\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n}\n\n/**\n * Internal interface with getOrm for adapter implementations\n */\nexport interface InternalTestContextMethods {\n getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;\n}\n\n/**\n * Helper to create common test context methods from an ORM map\n * This is used internally by adapter implementations to avoid code duplication\n */\nexport function createCommonTestContextMethods(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string, SimpleQueryInterface<any>>,\n): InternalTestContextMethods {\n return {\n getOrm: <TSchema extends AnySchema>(namespace: string) => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${namespace}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n };\n}\n\n/**\n * Complete test context combining base and adapter-specific functionality\n */\nexport type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;\n"],"mappings":";;;;;;;;AAmDA,SAAgB,+BAEd,QAC4B;AAC5B,QAAO,EACL,SAAoC,cAAsB;EACxD,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,YAAY;AAE7D,SAAO;IAEV"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n SupportedAdapter,\n AdapterContext,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n InMemoryAdapterConfig,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n} from \"@fragno-dev/core/test\";\n\n// Re-export adapter types\nexport type {\n SupportedAdapter,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n InMemoryAdapterConfig,\n AdapterContext,\n} from \"./adapters\";\n\n// Re-export new builder-based database test utilities\nexport { buildDatabaseFragmentsTest, DatabaseFragmentsTestBuilder } from \"./db-test\";\nexport { drainDurableHooks } from \"./durable-hooks\";\nexport {\n runModelChecker,\n defaultStateHasher,\n defaultTraceHasher,\n type ModelCheckerConfig,\n type ModelCheckerBounds,\n type ModelCheckerRunResult,\n type ModelCheckerScheduleResult,\n type ModelCheckerMode,\n type ModelCheckerStep,\n type ModelCheckerTraceEvent,\n type ModelCheckerTrace,\n type ModelCheckerTraceRecorder,\n type ModelCheckerTraceHasher,\n type ModelCheckerTraceHashMode,\n type NormalizedMutationOperation,\n type RawUowTransaction,\n type RawUowTransactionBuilder,\n type RawUowTransactionContext,\n type RawUowMutateContext,\n createRawUowTransaction,\n} from \"./model-checker\";\n\nexport {\n runModelCheckerWithActors,\n type ModelCheckerActor,\n type ModelCheckerInvariant,\n type ModelCheckerInvariantContext,\n type ModelCheckerActorsConfig,\n} from \"./model-checker-actors\";\n\nexport { ModelCheckerAdapter, type ModelCheckerScheduler } from \"./model-checker-adapter\";\n\n/**\n * Base test context with common functionality across all adapters\n */\nexport interface BaseTestContext {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>;\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n}\n\n/**\n * Internal interface with getOrm for adapter implementations\n */\nexport interface InternalTestContextMethods {\n getOrm: <TSchema extends AnySchema>(namespace: string | null) => SimpleQueryInterface<TSchema>;\n}\n\n/**\n * Helper to create common test context methods from an ORM map\n * This is used internally by adapter implementations to avoid code duplication\n */\nexport function createCommonTestContextMethods(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string | null, SimpleQueryInterface<any>>,\n): InternalTestContextMethods {\n return {\n getOrm: <TSchema extends AnySchema>(namespace: string | null) => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${String(namespace)}`);\n }\n return orm as SimpleQueryInterface<TSchema>;\n },\n };\n}\n\n/**\n * Complete test context combining base and adapter-specific functionality\n */\nexport type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;\n"],"mappings":";;;;;;;;;;;;AAsFA,SAAgB,+BAEd,QAC4B;AAC5B,QAAO,EACL,SAAoC,cAA6B;EAC/D,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,OAAO,UAAU,GAAG;AAErE,SAAO;IAEV"}
@@ -0,0 +1,41 @@
1
+ import { ModelCheckerBounds, ModelCheckerHistory, ModelCheckerMode, ModelCheckerRunResult, ModelCheckerStateHasherContext, ModelCheckerStep } from "./model-checker.js";
2
+ import { ModelCheckerAdapter } from "./model-checker-adapter.js";
3
+ import { AnySchema } from "@fragno-dev/db/schema";
4
+ import { SimpleQueryInterface } from "@fragno-dev/db/query";
5
+
6
+ //#region src/model-checker-actors.d.ts
7
+ type ModelCheckerActor<TContext> = {
8
+ name?: string;
9
+ run: (ctx: TContext) => Promise<void> | void;
10
+ };
11
+ type ModelCheckerInvariantContext<TSchema extends AnySchema, TUowConfig, TContext> = {
12
+ schema: TSchema;
13
+ queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;
14
+ context: TContext;
15
+ schedule: ModelCheckerStep[];
16
+ stepIndex: number;
17
+ };
18
+ type ModelCheckerInvariant<TSchema extends AnySchema, TUowConfig, TContext> = (ctx: ModelCheckerInvariantContext<TSchema, TUowConfig, TContext>) => Promise<void> | void;
19
+ type ModelCheckerActorsConfig<TSchema extends AnySchema, TUowConfig, TContext> = {
20
+ schema: TSchema;
21
+ mode?: ModelCheckerMode;
22
+ seed?: number;
23
+ maxSchedules?: number;
24
+ bounds?: ModelCheckerBounds;
25
+ history?: ModelCheckerHistory;
26
+ stopWhenFrontierExhausted?: boolean;
27
+ stateHasher?: (ctx: ModelCheckerStateHasherContext<TSchema, TUowConfig>) => Promise<string>;
28
+ createContext: () => Promise<{
29
+ adapter: ModelCheckerAdapter<TUowConfig>;
30
+ ctx: TContext;
31
+ queryEngine: SimpleQueryInterface<TSchema, TUowConfig>;
32
+ cleanup?: () => Promise<void>;
33
+ }>;
34
+ setup?: (ctx: TContext) => Promise<void>;
35
+ buildActors: (ctx: TContext) => ModelCheckerActor<TContext>[];
36
+ invariants?: ModelCheckerInvariant<TSchema, TUowConfig, TContext>[];
37
+ };
38
+ declare const runModelCheckerWithActors: <TSchema extends AnySchema, TUowConfig, TContext>(config: ModelCheckerActorsConfig<TSchema, TUowConfig, TContext>) => Promise<ModelCheckerRunResult>;
39
+ //#endregion
40
+ export { ModelCheckerActor, ModelCheckerActorsConfig, ModelCheckerInvariant, ModelCheckerInvariantContext, runModelCheckerWithActors };
41
+ //# sourceMappingURL=model-checker-actors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-checker-actors.d.ts","names":[],"sources":["../src/model-checker-actors.ts"],"sourcesContent":[],"mappings":";;;;;;KA+SY;;EAAA,GAAA,EAAA,CAAA,GAAA,EAEC,QAFD,EAAiB,GAEH,OAFG,CAAA,IAAA,CAAA,GAEhB,IAAA;AAGb,CAAA;AAAyD,KAA7C,4BAA6C,CAAA,gBAAA,SAAA,EAAA,UAAA,EAAA,QAAA,CAAA,GAAA;EAC/C,MAAA,EAAA,OAAA;EAC0B,WAAA,EAArB,oBAAqB,CAAA,OAAA,EAAS,UAAT,CAAA;EAAS,OAAA,EAClC,QADkC;EAA9B,QAAA,EAEH,gBAFG,EAAA;EACJ,SAAA,EAAA,MAAA;CACC;AAAgB,KAIhB,qBAJgB,CAAA,gBAIsB,SAJtB,EAAA,UAAA,EAAA,QAAA,CAAA,GAAA,CAAA,GAAA,EAKrB,4BALqB,CAKQ,OALR,EAKiB,UALjB,EAK6B,QAL7B,CAAA,EAAA,GAMvB,OANuB,CAAA,IAAA,CAAA,GAAA,IAAA;AAIhB,KAIA,wBAJqB,CAAA,gBAIoB,SAJpB,EAAA,UAAA,EAAA,QAAA,CAAA,GAAA;EAAiB,MAAA,EAKxC,OALwC;EACd,IAAA,CAAA,EAK3B,gBAL2B;EAAS,IAAA,CAAA,EAAA,MAAA;EAAY,YAAA,CAAA,EAAA,MAAA;EAAlD,MAAA,CAAA,EAQI,kBARJ;EACF,OAAA,CAAA,EAQO,mBARP;EAAO,yBAAA,CAAA,EAAA,OAAA;EAEA,WAAA,CAAA,EAAA,CAAA,GAAA,EAQU,8BARc,CAQiB,OARjB,EAQ0B,UAR1B,CAAA,EAAA,GAQ0C,OAR1C,CAAA,MAAA,CAAA;EAAiB,aAAA,EAAA,GAAA,GAS9B,OAT8B,CAAA;IAC3C,OAAA,EASG,mBATH,CASuB,UATvB,CAAA;IACD,GAAA,EASA,QATA;IAGE,WAAA,EAOM,oBAPN,CAO2B,OAP3B,EAOoC,UAPpC,CAAA;IACC,OAAA,CAAA,EAAA,GAAA,GAOQ,OAPR,CAAA,IAAA,CAAA;EAEyC,CAAA,CAAA;EAAS,KAAA,CAAA,EAAA,CAAA,GAAA,EAO9C,QAP8C,EAAA,GAOjC,OAPiC,CAAA,IAAA,CAAA;EAAxC,WAAA,EAAA,CAAA,GAAA,EAQD,QARC,EAAA,GAQY,iBARZ,CAQ8B,QAR9B,CAAA,EAAA;EAAwD,UAAA,CAAA,EAS/D,qBAT+D,CASzC,OATyC,EAShC,UATgC,EASpB,QAToB,CAAA,EAAA;CAE7C;AAApB,cAyNA,yBAzNA,EAAA,CAAA,gBAyNmD,SAzNnD,EAAA,UAAA,EAAA,QAAA,CAAA,CAAA,MAAA,EA0NH,wBA1NG,CA0NsB,OA1NtB,EA0N+B,UA1N/B,EA0N2C,QA1N3C,CAAA,EAAA,GA2NV,OA3NU,CA2NF,qBA3NE,CAAA"}