@fragno-dev/test 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +41 -31
- package/CHANGELOG.md +69 -0
- package/dist/adapters.d.ts +4 -7
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +18 -302
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +120 -18
- package/dist/db-test.d.ts.map +1 -1
- package/dist/db-test.js +203 -55
- package/dist/db-test.js.map +1 -1
- package/dist/durable-hooks.d.ts +6 -2
- package/dist/durable-hooks.d.ts.map +1 -1
- package/dist/durable-hooks.js +10 -5
- package/dist/durable-hooks.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-checker-actors.d.ts.map +1 -1
- package/dist/model-checker-actors.js +1 -1
- package/dist/model-checker-actors.js.map +1 -1
- package/dist/model-checker-adapter.d.ts +1 -1
- package/dist/model-checker-adapter.d.ts.map +1 -1
- package/dist/model-checker-adapter.js.map +1 -1
- package/dist/model-checker.d.ts.map +1 -1
- package/dist/model-checker.js.map +1 -1
- package/dist/test-adapters/drizzle-pglite.js +116 -0
- package/dist/test-adapters/drizzle-pglite.js.map +1 -0
- package/dist/test-adapters/in-memory.js +39 -0
- package/dist/test-adapters/in-memory.js.map +1 -0
- package/dist/test-adapters/kysely-pglite.js +105 -0
- package/dist/test-adapters/kysely-pglite.js.map +1 -0
- package/dist/test-adapters/kysely-sqlite.js +87 -0
- package/dist/test-adapters/kysely-sqlite.js.map +1 -0
- package/dist/test-adapters/model-checker.js +41 -0
- package/dist/test-adapters/model-checker.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +32 -33
- package/src/adapter-conformance.test.ts +3 -1
- package/src/adapters.ts +24 -455
- package/src/db-roundtrip-guard.test.ts +206 -0
- package/src/db-test.test.ts +131 -77
- package/src/db-test.ts +530 -96
- package/src/durable-hooks.test.ts +58 -0
- package/src/durable-hooks.ts +23 -8
- package/src/index.test.ts +188 -104
- package/src/index.ts +6 -2
- package/src/model-checker-actors.test.ts +5 -2
- package/src/model-checker-actors.ts +2 -1
- package/src/model-checker-adapter.ts +3 -2
- package/src/model-checker.test.ts +4 -1
- package/src/model-checker.ts +4 -3
- package/src/test-adapters/drizzle-pglite.ts +162 -0
- package/src/test-adapters/in-memory.ts +56 -0
- package/src/test-adapters/kysely-pglite.ts +151 -0
- package/src/test-adapters/kysely-sqlite.ts +119 -0
- package/src/test-adapters/model-checker.ts +58 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +1 -0
package/src/db-test.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
import type { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/route";
|
|
2
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
1
3
|
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
4
|
+
|
|
2
5
|
import type {
|
|
3
6
|
RequestThisContext,
|
|
4
7
|
FragnoPublicConfig,
|
|
5
8
|
FragmentInstantiationBuilder,
|
|
6
9
|
FragnoInstantiatedFragment,
|
|
10
|
+
AnyFragnoInstantiatedFragment,
|
|
7
11
|
FragmentDefinition,
|
|
8
12
|
} from "@fragno-dev/core";
|
|
9
|
-
import type {
|
|
13
|
+
import type { DatabaseAdapter, FragnoPublicConfigWithDatabase } from "@fragno-dev/db";
|
|
14
|
+
|
|
15
|
+
import type { BaseTestContext } from ".";
|
|
10
16
|
import {
|
|
11
17
|
createAdapter,
|
|
12
18
|
type SupportedAdapter,
|
|
13
19
|
type AdapterContext,
|
|
14
20
|
type SchemaConfig,
|
|
15
21
|
} from "./adapters";
|
|
16
|
-
import type { DatabaseAdapter } from "@fragno-dev/db";
|
|
17
|
-
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
18
|
-
import type { BaseTestContext } from ".";
|
|
19
22
|
import { drainDurableHooks } from "./durable-hooks";
|
|
20
23
|
|
|
21
24
|
// BoundServices is an internal type that strips 'this' parameters from service methods
|
|
@@ -28,15 +31,126 @@ type BoundServices<T> = {
|
|
|
28
31
|
: T[K];
|
|
29
32
|
};
|
|
30
33
|
|
|
34
|
+
type FragmentFactoryContext = {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
adapter: DatabaseAdapter<any>;
|
|
37
|
+
test: BaseTestContext & AdapterContext<SupportedAdapter>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const disableAutoSchedule = <TOptions extends FragnoPublicConfig>(options: TOptions) => {
|
|
41
|
+
const durableHooks = (options as { durableHooks?: Record<string, unknown> }).durableHooks ?? {};
|
|
42
|
+
return {
|
|
43
|
+
...options,
|
|
44
|
+
durableHooks: {
|
|
45
|
+
...durableHooks,
|
|
46
|
+
autoSchedule: false,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type FragmentFactoryResult =
|
|
52
|
+
| FragmentInstantiationBuilder<
|
|
53
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
54
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
55
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
56
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
57
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
58
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
59
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
60
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
61
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
62
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
63
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
64
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
65
|
+
>
|
|
66
|
+
| FragnoInstantiatedFragment<
|
|
67
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
68
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
69
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
70
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
71
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
72
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
73
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
74
|
+
>;
|
|
75
|
+
|
|
76
|
+
type HandlerThisContextFromFactoryResult<T> =
|
|
77
|
+
T extends FragmentInstantiationBuilder<
|
|
78
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
79
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
80
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
81
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
82
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
83
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
84
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
85
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
86
|
+
infer THandlerThisContext,
|
|
87
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
88
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
89
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
90
|
+
>
|
|
91
|
+
? THandlerThisContext
|
|
92
|
+
: T extends FragnoInstantiatedFragment<
|
|
93
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
94
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
95
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
96
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
97
|
+
infer THandlerThisContext,
|
|
98
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
99
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
100
|
+
>
|
|
101
|
+
? THandlerThisContext
|
|
102
|
+
: RequestThisContext;
|
|
103
|
+
|
|
104
|
+
type FragmentResultFromFactoryResult<T> =
|
|
105
|
+
T extends FragmentInstantiationBuilder<
|
|
106
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
107
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
108
|
+
infer TDeps,
|
|
109
|
+
infer TBaseServices,
|
|
110
|
+
infer TServices,
|
|
111
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
112
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
113
|
+
infer TServiceThisContext,
|
|
114
|
+
infer THandlerThisContext,
|
|
115
|
+
infer TRequestStorage,
|
|
116
|
+
infer TRoutesOrFactories,
|
|
117
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
118
|
+
>
|
|
119
|
+
? FragmentResult<
|
|
120
|
+
TDeps,
|
|
121
|
+
BoundServices<TBaseServices & TServices>,
|
|
122
|
+
TServiceThisContext,
|
|
123
|
+
THandlerThisContext,
|
|
124
|
+
TRequestStorage,
|
|
125
|
+
FlattenRouteFactories<TRoutesOrFactories>,
|
|
126
|
+
ExtractSchemaFromDeps<TDeps>
|
|
127
|
+
>
|
|
128
|
+
: T extends FragnoInstantiatedFragment<
|
|
129
|
+
infer TRoutes,
|
|
130
|
+
infer TDeps,
|
|
131
|
+
infer TServices,
|
|
132
|
+
infer TServiceThisContext,
|
|
133
|
+
infer THandlerThisContext,
|
|
134
|
+
infer TRequestStorage,
|
|
135
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
136
|
+
>
|
|
137
|
+
? FragmentResult<
|
|
138
|
+
TDeps,
|
|
139
|
+
BoundServices<TServices>,
|
|
140
|
+
TServiceThisContext,
|
|
141
|
+
THandlerThisContext,
|
|
142
|
+
TRequestStorage,
|
|
143
|
+
TRoutes,
|
|
144
|
+
ExtractSchemaFromDeps<TDeps>
|
|
145
|
+
>
|
|
146
|
+
: never;
|
|
147
|
+
|
|
31
148
|
// Extract the schema type from database fragment dependencies
|
|
32
149
|
// Database fragments have ImplicitDatabaseDependencies<TSchema> which includes `schema: TSchema`
|
|
33
150
|
type ExtractSchemaFromDeps<TDeps> = TDeps extends { schema: infer TSchema extends AnySchema }
|
|
34
151
|
? TSchema
|
|
35
152
|
: AnySchema;
|
|
36
153
|
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
-
type AnyLinkedFragments = Record<string, any>;
|
|
39
|
-
|
|
40
154
|
// Forward declarations for recursive type references
|
|
41
155
|
interface FragmentResult<
|
|
42
156
|
TDeps,
|
|
@@ -46,7 +160,6 @@ interface FragmentResult<
|
|
|
46
160
|
TRequestStorage,
|
|
47
161
|
TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
48
162
|
TSchema extends AnySchema,
|
|
49
|
-
TLinkedFragments extends AnyLinkedFragments = {},
|
|
50
163
|
> {
|
|
51
164
|
fragment: FragnoInstantiatedFragment<
|
|
52
165
|
TRoutes,
|
|
@@ -55,8 +168,7 @@ interface FragmentResult<
|
|
|
55
168
|
TServiceThisContext,
|
|
56
169
|
THandlerThisContext,
|
|
57
170
|
TRequestStorage,
|
|
58
|
-
FragnoPublicConfig
|
|
59
|
-
TLinkedFragments
|
|
171
|
+
FragnoPublicConfig
|
|
60
172
|
>;
|
|
61
173
|
services: TServices;
|
|
62
174
|
deps: TDeps;
|
|
@@ -67,15 +179,13 @@ interface FragmentResult<
|
|
|
67
179
|
TServiceThisContext,
|
|
68
180
|
THandlerThisContext,
|
|
69
181
|
TRequestStorage,
|
|
70
|
-
FragnoPublicConfig
|
|
71
|
-
TLinkedFragments
|
|
182
|
+
FragnoPublicConfig
|
|
72
183
|
>["callRoute"];
|
|
73
184
|
db: SimpleQueryInterface<TSchema>;
|
|
74
185
|
}
|
|
75
186
|
|
|
76
187
|
// Safe: Catch-all for any fragment result type
|
|
77
|
-
type AnyFragmentResult = FragmentResult<
|
|
78
|
-
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
188
|
+
export type AnyFragmentResult = FragmentResult<
|
|
79
189
|
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
80
190
|
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
81
191
|
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -116,8 +226,9 @@ interface FragmentBuilderConfig<
|
|
|
116
226
|
THandlerThisContext extends RequestThisContext,
|
|
117
227
|
TRequestStorage,
|
|
118
228
|
TRoutesOrFactories extends readonly AnyRouteOrFactory[],
|
|
119
|
-
|
|
229
|
+
TInternalRoutes extends readonly AnyRouteOrFactory[],
|
|
120
230
|
> {
|
|
231
|
+
kind: "builder";
|
|
121
232
|
definition: FragmentDefinition<
|
|
122
233
|
TConfig,
|
|
123
234
|
TOptions,
|
|
@@ -129,7 +240,7 @@ interface FragmentBuilderConfig<
|
|
|
129
240
|
TServiceThisContext,
|
|
130
241
|
THandlerThisContext,
|
|
131
242
|
TRequestStorage,
|
|
132
|
-
|
|
243
|
+
TInternalRoutes
|
|
133
244
|
>;
|
|
134
245
|
builder: FragmentInstantiationBuilder<
|
|
135
246
|
TConfig,
|
|
@@ -143,11 +254,48 @@ interface FragmentBuilderConfig<
|
|
|
143
254
|
THandlerThisContext,
|
|
144
255
|
TRequestStorage,
|
|
145
256
|
TRoutesOrFactories,
|
|
146
|
-
|
|
257
|
+
TInternalRoutes
|
|
258
|
+
>;
|
|
259
|
+
migrateToVersion?: number;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Configuration for a pre-built fragment instance
|
|
264
|
+
*/
|
|
265
|
+
interface FragmentInstanceConfig<
|
|
266
|
+
TDeps,
|
|
267
|
+
TServices extends Record<string, unknown>,
|
|
268
|
+
TServiceThisContext extends RequestThisContext,
|
|
269
|
+
THandlerThisContext extends RequestThisContext,
|
|
270
|
+
TRequestStorage,
|
|
271
|
+
TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
272
|
+
> {
|
|
273
|
+
kind: "instance";
|
|
274
|
+
fragment: FragnoInstantiatedFragment<
|
|
275
|
+
TRoutes,
|
|
276
|
+
TDeps,
|
|
277
|
+
TServices,
|
|
278
|
+
TServiceThisContext,
|
|
279
|
+
THandlerThisContext,
|
|
280
|
+
TRequestStorage,
|
|
281
|
+
FragnoPublicConfig
|
|
147
282
|
>;
|
|
148
283
|
migrateToVersion?: number;
|
|
149
284
|
}
|
|
150
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Configuration for a fragment factory
|
|
288
|
+
*/
|
|
289
|
+
interface FragmentFactoryConfig {
|
|
290
|
+
kind: "factory";
|
|
291
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
292
|
+
definition: FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>;
|
|
293
|
+
factory: (context: FragmentFactoryContext) => FragmentFactoryResult;
|
|
294
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
295
|
+
config?: any;
|
|
296
|
+
migrateToVersion?: number;
|
|
297
|
+
}
|
|
298
|
+
|
|
151
299
|
/**
|
|
152
300
|
* Test context combining base and adapter-specific functionality
|
|
153
301
|
*/
|
|
@@ -183,7 +331,23 @@ interface DatabaseFragmentsTestResult<
|
|
|
183
331
|
/**
|
|
184
332
|
* Internal storage for fragment configurations
|
|
185
333
|
*/
|
|
186
|
-
type
|
|
334
|
+
type AnyFragmentInstanceConfig = FragmentInstanceConfig<
|
|
335
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
336
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
337
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
338
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
339
|
+
any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
340
|
+
any // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
341
|
+
>;
|
|
342
|
+
|
|
343
|
+
type AnyFragmentFactoryConfig = FragmentFactoryConfig;
|
|
344
|
+
|
|
345
|
+
type AnyFragmentConfig =
|
|
346
|
+
| AnyFragmentBuilderConfig
|
|
347
|
+
| AnyFragmentInstanceConfig
|
|
348
|
+
| AnyFragmentFactoryConfig;
|
|
349
|
+
|
|
350
|
+
type FragmentConfigMap = Map<string, AnyFragmentConfig>;
|
|
187
351
|
|
|
188
352
|
/**
|
|
189
353
|
* Builder for creating multiple database fragments for testing
|
|
@@ -195,6 +359,7 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
195
359
|
> {
|
|
196
360
|
#adapter?: SupportedAdapter;
|
|
197
361
|
#fragments: FragmentConfigMap = new Map();
|
|
362
|
+
#dbRoundtripGuard?: FragnoPublicConfigWithDatabase["dbRoundtripGuard"] = true;
|
|
198
363
|
|
|
199
364
|
/**
|
|
200
365
|
* Set the test adapter configuration
|
|
@@ -206,6 +371,17 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
206
371
|
return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
207
372
|
}
|
|
208
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Opt out of the default roundtrip guard (enabled by default), or override its configuration.
|
|
376
|
+
* Useful for allowing multi-roundtrip routes in tests.
|
|
377
|
+
*/
|
|
378
|
+
withDbRoundtripGuard(
|
|
379
|
+
guard: FragnoPublicConfigWithDatabase["dbRoundtripGuard"] = false,
|
|
380
|
+
): DatabaseFragmentsTestBuilder<TFragments, TAdapter, TFirstFragmentThisContext> {
|
|
381
|
+
this.#dbRoundtripGuard = guard;
|
|
382
|
+
return this;
|
|
383
|
+
}
|
|
384
|
+
|
|
209
385
|
/**
|
|
210
386
|
* Add a fragment to the test setup
|
|
211
387
|
*
|
|
@@ -226,7 +402,7 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
226
402
|
THandlerThisContext extends RequestThisContext,
|
|
227
403
|
TRequestStorage,
|
|
228
404
|
TRoutesOrFactories extends readonly AnyRouteOrFactory[],
|
|
229
|
-
|
|
405
|
+
TInternalRoutes extends readonly AnyRouteOrFactory[],
|
|
230
406
|
>(
|
|
231
407
|
name: TName,
|
|
232
408
|
builder: FragmentInstantiationBuilder<
|
|
@@ -241,7 +417,7 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
241
417
|
THandlerThisContext,
|
|
242
418
|
TRequestStorage,
|
|
243
419
|
TRoutesOrFactories,
|
|
244
|
-
|
|
420
|
+
TInternalRoutes
|
|
245
421
|
>,
|
|
246
422
|
options?: {
|
|
247
423
|
migrateToVersion?: number;
|
|
@@ -255,8 +431,7 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
255
431
|
THandlerThisContext,
|
|
256
432
|
TRequestStorage,
|
|
257
433
|
FlattenRouteFactories<TRoutesOrFactories>,
|
|
258
|
-
ExtractSchemaFromDeps<TDeps
|
|
259
|
-
TLinkedFragments
|
|
434
|
+
ExtractSchemaFromDeps<TDeps> // Extract actual schema type from deps
|
|
260
435
|
>;
|
|
261
436
|
},
|
|
262
437
|
TAdapter,
|
|
@@ -264,6 +439,7 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
264
439
|
keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext
|
|
265
440
|
> {
|
|
266
441
|
this.#fragments.set(name, {
|
|
442
|
+
kind: "builder",
|
|
267
443
|
definition: builder.definition,
|
|
268
444
|
builder,
|
|
269
445
|
migrateToVersion: options?.migrateToVersion,
|
|
@@ -271,6 +447,94 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
271
447
|
return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
272
448
|
}
|
|
273
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Add a pre-built fragment instance to the test setup
|
|
452
|
+
*
|
|
453
|
+
* @param name - Unique name for the fragment
|
|
454
|
+
* @param fragment - Already-built fragment instance
|
|
455
|
+
*/
|
|
456
|
+
withFragmentInstance<
|
|
457
|
+
TName extends string,
|
|
458
|
+
TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
459
|
+
TDeps,
|
|
460
|
+
TServices extends Record<string, unknown>,
|
|
461
|
+
TServiceThisContext extends RequestThisContext,
|
|
462
|
+
THandlerThisContext extends RequestThisContext,
|
|
463
|
+
TRequestStorage,
|
|
464
|
+
>(
|
|
465
|
+
name: TName,
|
|
466
|
+
fragment: FragnoInstantiatedFragment<
|
|
467
|
+
TRoutes,
|
|
468
|
+
TDeps,
|
|
469
|
+
TServices,
|
|
470
|
+
TServiceThisContext,
|
|
471
|
+
THandlerThisContext,
|
|
472
|
+
TRequestStorage,
|
|
473
|
+
FragnoPublicConfig
|
|
474
|
+
>,
|
|
475
|
+
options?: { migrateToVersion?: number },
|
|
476
|
+
): DatabaseFragmentsTestBuilder<
|
|
477
|
+
TFragments & {
|
|
478
|
+
[K in TName]: FragmentResult<
|
|
479
|
+
TDeps,
|
|
480
|
+
BoundServices<TServices>,
|
|
481
|
+
TServiceThisContext,
|
|
482
|
+
THandlerThisContext,
|
|
483
|
+
TRequestStorage,
|
|
484
|
+
TRoutes,
|
|
485
|
+
ExtractSchemaFromDeps<TDeps>
|
|
486
|
+
>;
|
|
487
|
+
},
|
|
488
|
+
TAdapter,
|
|
489
|
+
keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext
|
|
490
|
+
> {
|
|
491
|
+
this.#fragments.set(name, {
|
|
492
|
+
kind: "instance",
|
|
493
|
+
fragment,
|
|
494
|
+
migrateToVersion: options?.migrateToVersion,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Add a fragment factory to the test setup.
|
|
502
|
+
* The factory runs after the adapter is created.
|
|
503
|
+
*
|
|
504
|
+
* @param name - Unique name for the fragment
|
|
505
|
+
* @param definition - Fragment definition (used to extract schema/namespace)
|
|
506
|
+
* @param factory - Factory that returns a builder or a pre-built fragment
|
|
507
|
+
*/
|
|
508
|
+
withFragmentFactory<TName extends string, TFactoryResult extends FragmentFactoryResult>(
|
|
509
|
+
name: TName,
|
|
510
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
511
|
+
definition: FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>,
|
|
512
|
+
factory: (context: FragmentFactoryContext) => TFactoryResult,
|
|
513
|
+
options?: {
|
|
514
|
+
migrateToVersion?: number;
|
|
515
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
516
|
+
config?: any;
|
|
517
|
+
},
|
|
518
|
+
): DatabaseFragmentsTestBuilder<
|
|
519
|
+
TFragments & {
|
|
520
|
+
[K in TName]: FragmentResultFromFactoryResult<TFactoryResult>;
|
|
521
|
+
},
|
|
522
|
+
TAdapter,
|
|
523
|
+
keyof TFragments extends never
|
|
524
|
+
? HandlerThisContextFromFactoryResult<TFactoryResult>
|
|
525
|
+
: TFirstFragmentThisContext
|
|
526
|
+
> {
|
|
527
|
+
this.#fragments.set(name, {
|
|
528
|
+
kind: "factory",
|
|
529
|
+
definition,
|
|
530
|
+
factory,
|
|
531
|
+
config: options?.config,
|
|
532
|
+
migrateToVersion: options?.migrateToVersion,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
536
|
+
}
|
|
537
|
+
|
|
274
538
|
/**
|
|
275
539
|
* Build the test setup and return fragments and test context
|
|
276
540
|
*/
|
|
@@ -284,25 +548,47 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
284
548
|
}
|
|
285
549
|
|
|
286
550
|
if (this.#fragments.size === 0) {
|
|
287
|
-
throw new Error(
|
|
551
|
+
throw new Error(
|
|
552
|
+
"At least one fragment must be added using withFragment(), withFragmentFactory(), or withFragmentInstance().",
|
|
553
|
+
);
|
|
288
554
|
}
|
|
289
555
|
|
|
290
556
|
const adapterConfig = this.#adapter;
|
|
291
557
|
|
|
292
558
|
// Extract fragment names and configs
|
|
293
|
-
const
|
|
294
|
-
const
|
|
559
|
+
const fragmentEntries = Array.from(this.#fragments.entries());
|
|
560
|
+
const fragmentNames = fragmentEntries.map(([name]) => name);
|
|
295
561
|
|
|
296
562
|
// Extract schemas from definitions and prepare schema configs
|
|
297
563
|
const schemaConfigs: SchemaConfig[] = [];
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
564
|
+
const fragmentPlans: Array<{
|
|
565
|
+
name: string;
|
|
566
|
+
kind: "builder" | "instance" | "factory";
|
|
567
|
+
schema: AnySchema;
|
|
568
|
+
namespace: string | null;
|
|
569
|
+
migrateToVersion?: number;
|
|
570
|
+
builderConfig?: {
|
|
571
|
+
builder: AnyFragmentBuilderConfig["builder"];
|
|
572
|
+
definition: AnyFragmentBuilderConfig["definition"];
|
|
573
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
574
|
+
config: any;
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
576
|
+
routes: any;
|
|
577
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
578
|
+
options: any;
|
|
579
|
+
};
|
|
580
|
+
factory?: FragmentFactoryConfig["factory"];
|
|
581
|
+
cachedFactoryResult?: FragmentFactoryResult;
|
|
582
|
+
fragment?: AnyFragnoInstantiatedFragment;
|
|
583
|
+
}> = [];
|
|
304
584
|
|
|
305
|
-
|
|
585
|
+
const extractSchemaFromDefinition = (
|
|
586
|
+
definition: FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
587
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
588
|
+
actualConfig: any,
|
|
589
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
590
|
+
actualOptions?: any,
|
|
591
|
+
) => {
|
|
306
592
|
let schema: AnySchema | undefined;
|
|
307
593
|
let namespace: string | null | undefined;
|
|
308
594
|
|
|
@@ -326,24 +612,19 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
326
612
|
close: async () => {},
|
|
327
613
|
};
|
|
328
614
|
|
|
329
|
-
// Use the actual config from the builder instead of an empty mock
|
|
330
|
-
// This ensures dependencies can be properly initialized (e.g., Stripe with API keys)
|
|
331
|
-
const actualConfig = builder.config ?? {};
|
|
332
|
-
|
|
333
615
|
const deps = definition.dependencies({
|
|
334
|
-
config: actualConfig,
|
|
616
|
+
config: actualConfig ?? {},
|
|
335
617
|
options: {
|
|
618
|
+
...actualOptions,
|
|
336
619
|
databaseAdapter: mockAdapter as any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
337
620
|
} as any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
338
621
|
});
|
|
339
622
|
|
|
340
|
-
// The schema and namespace are in deps for database fragments
|
|
341
623
|
if (deps && typeof deps === "object" && "schema" in deps) {
|
|
342
624
|
schema = (deps as any).schema; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
343
625
|
namespace = (deps as any).namespace; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
344
626
|
}
|
|
345
627
|
} catch (error) {
|
|
346
|
-
// If extraction fails, provide a helpful error message
|
|
347
628
|
const errorMessage =
|
|
348
629
|
error instanceof Error
|
|
349
630
|
? error.message
|
|
@@ -373,51 +654,199 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
373
654
|
);
|
|
374
655
|
}
|
|
375
656
|
|
|
657
|
+
return { schema, namespace };
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
for (const [name, fragmentConfig] of fragmentEntries) {
|
|
661
|
+
if (fragmentConfig.kind === "builder") {
|
|
662
|
+
const builder = fragmentConfig.builder;
|
|
663
|
+
const definition = builder.definition;
|
|
664
|
+
const { schema, namespace } = extractSchemaFromDefinition(
|
|
665
|
+
definition,
|
|
666
|
+
builder.config ?? {},
|
|
667
|
+
builder.options ?? {},
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
schemaConfigs.push({
|
|
671
|
+
schema,
|
|
672
|
+
namespace,
|
|
673
|
+
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
fragmentPlans.push({
|
|
677
|
+
name,
|
|
678
|
+
kind: "builder",
|
|
679
|
+
schema,
|
|
680
|
+
namespace,
|
|
681
|
+
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
682
|
+
builderConfig: {
|
|
683
|
+
builder: fragmentConfig.builder,
|
|
684
|
+
definition: fragmentConfig.definition,
|
|
685
|
+
config: builder.config ?? {},
|
|
686
|
+
routes: builder.routes ?? [],
|
|
687
|
+
options: builder.options ?? {},
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (fragmentConfig.kind === "factory") {
|
|
694
|
+
const definition = fragmentConfig.definition;
|
|
695
|
+
const { schema, namespace } = extractSchemaFromDefinition(
|
|
696
|
+
definition,
|
|
697
|
+
fragmentConfig.config ?? {},
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
schemaConfigs.push({
|
|
701
|
+
schema,
|
|
702
|
+
namespace,
|
|
703
|
+
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
fragmentPlans.push({
|
|
707
|
+
name,
|
|
708
|
+
kind: "factory",
|
|
709
|
+
schema,
|
|
710
|
+
namespace,
|
|
711
|
+
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
712
|
+
factory: fragmentConfig.factory,
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const fragment = fragmentConfig.fragment;
|
|
719
|
+
const deps = fragment.$internal?.deps as
|
|
720
|
+
| {
|
|
721
|
+
schema?: AnySchema;
|
|
722
|
+
namespace?: string | null;
|
|
723
|
+
}
|
|
724
|
+
| undefined;
|
|
725
|
+
|
|
726
|
+
if (!deps?.schema) {
|
|
727
|
+
throw new Error(
|
|
728
|
+
`Fragment '${name}' does not have a database schema in deps. ` +
|
|
729
|
+
`Make sure you're using defineFragment().extend(withDatabase(schema)).`,
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
376
733
|
schemaConfigs.push({
|
|
377
|
-
schema,
|
|
378
|
-
namespace,
|
|
734
|
+
schema: deps.schema,
|
|
735
|
+
namespace: deps.namespace ?? null,
|
|
379
736
|
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
380
737
|
});
|
|
381
738
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
739
|
+
fragmentPlans.push({
|
|
740
|
+
name,
|
|
741
|
+
kind: "instance",
|
|
742
|
+
schema: deps.schema,
|
|
743
|
+
namespace: deps.namespace ?? null,
|
|
744
|
+
migrateToVersion: fragmentConfig.migrateToVersion,
|
|
745
|
+
fragment,
|
|
387
746
|
});
|
|
388
747
|
}
|
|
389
748
|
|
|
390
749
|
const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);
|
|
391
750
|
|
|
751
|
+
const resolveDbRoundtripGuardOption = (options: unknown) => {
|
|
752
|
+
if (options && typeof options === "object") {
|
|
753
|
+
if (Object.prototype.hasOwnProperty.call(options, "dbRoundtripGuard")) {
|
|
754
|
+
return (options as { dbRoundtripGuard?: unknown }).dbRoundtripGuard as
|
|
755
|
+
| FragnoPublicConfigWithDatabase["dbRoundtripGuard"]
|
|
756
|
+
| undefined;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return this.#dbRoundtripGuard;
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const mergeBuilderOptions = (options: unknown) => {
|
|
763
|
+
const resolvedOptions = disableAutoSchedule((options ?? {}) as FragnoPublicConfig);
|
|
764
|
+
const resolvedOptionsRecord = resolvedOptions as unknown as Record<string, unknown>;
|
|
765
|
+
const merged = {
|
|
766
|
+
...resolvedOptionsRecord,
|
|
767
|
+
databaseAdapter: adapter,
|
|
768
|
+
} as Record<string, unknown>;
|
|
769
|
+
const guardOption = resolveDbRoundtripGuardOption(resolvedOptions);
|
|
770
|
+
if (guardOption !== undefined) {
|
|
771
|
+
merged["dbRoundtripGuard"] = guardOption;
|
|
772
|
+
}
|
|
773
|
+
return merged;
|
|
774
|
+
};
|
|
775
|
+
|
|
392
776
|
// Helper to create fragments with service wiring
|
|
393
777
|
const createFragments = () => {
|
|
778
|
+
const resolveBuilderConfig = (builder: AnyFragmentBuilderConfig["builder"]) => ({
|
|
779
|
+
builder,
|
|
780
|
+
definition: builder.definition,
|
|
781
|
+
config: builder.config ?? {},
|
|
782
|
+
routes: builder.routes ?? [],
|
|
783
|
+
options: builder.options ?? {},
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
const isBuilder = (
|
|
787
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
788
|
+
value: any,
|
|
789
|
+
): value is AnyFragmentBuilderConfig["builder"] =>
|
|
790
|
+
Boolean(value) && typeof value === "object" && "build" in value && "definition" in value;
|
|
791
|
+
|
|
394
792
|
// First pass: create fragments without service dependencies to extract provided services
|
|
395
793
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
396
794
|
const providedServicesByName: Record<string, { service: any; orm: any }> = {};
|
|
397
|
-
const
|
|
795
|
+
const instanceResults = new Map<string, any>(); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
796
|
+
const builderConfigs = new Map<string, ReturnType<typeof resolveBuilderConfig>>();
|
|
398
797
|
|
|
399
|
-
for (
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const schema = schemaConfigs[i]!.schema;
|
|
404
|
-
const orm = testContext.getOrm(namespace);
|
|
798
|
+
for (const plan of fragmentPlans) {
|
|
799
|
+
const orm = testContext.getOrm(plan.namespace);
|
|
800
|
+
let fragment: AnyFragnoInstantiatedFragment | undefined;
|
|
801
|
+
let builderConfig = plan.builderConfig;
|
|
405
802
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
803
|
+
if (plan.kind === "factory") {
|
|
804
|
+
const result =
|
|
805
|
+
plan.cachedFactoryResult ??
|
|
806
|
+
plan.factory!({
|
|
807
|
+
adapter,
|
|
808
|
+
test: testContext,
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
if (!plan.cachedFactoryResult) {
|
|
812
|
+
plan.cachedFactoryResult = result;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (isBuilder(result)) {
|
|
816
|
+
builderConfig = resolveBuilderConfig(result);
|
|
817
|
+
} else {
|
|
818
|
+
fragment = result;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const usesBuilder = plan.kind === "builder" || !!builderConfig;
|
|
411
823
|
|
|
412
|
-
|
|
413
|
-
|
|
824
|
+
if (usesBuilder) {
|
|
825
|
+
const resolvedBuilderConfig = builderConfig ?? plan.builderConfig!;
|
|
826
|
+
const mergedOptions = mergeBuilderOptions(resolvedBuilderConfig.options);
|
|
414
827
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
828
|
+
fragment = resolvedBuilderConfig.builder.withOptions(mergedOptions).build();
|
|
829
|
+
builderConfigs.set(plan.name, resolvedBuilderConfig);
|
|
830
|
+
} else {
|
|
831
|
+
fragment = fragment ?? plan.fragment!;
|
|
832
|
+
|
|
833
|
+
const deps = fragment.$internal?.deps as
|
|
834
|
+
| { databaseAdapter?: DatabaseAdapter<unknown> }
|
|
835
|
+
| undefined;
|
|
836
|
+
if (deps?.databaseAdapter && deps.databaseAdapter !== adapter) {
|
|
837
|
+
throw new Error(
|
|
838
|
+
`Fragment '${plan.name}' was built with a different database adapter instance. ` +
|
|
839
|
+
`Use withFragment() or ensure the fragment uses the same adapter instance as the test builder.`,
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
if (!fragment) {
|
|
845
|
+
throw new Error(
|
|
846
|
+
`Fragment '${plan.name}' did not return a valid fragment instance from its factory.`,
|
|
847
|
+
);
|
|
848
|
+
}
|
|
419
849
|
|
|
420
|
-
// Store all services as potentially provided
|
|
421
850
|
for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) {
|
|
422
851
|
providedServicesByName[serviceName] = {
|
|
423
852
|
service: serviceImpl,
|
|
@@ -425,30 +854,40 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
425
854
|
};
|
|
426
855
|
}
|
|
427
856
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
857
|
+
if (!usesBuilder) {
|
|
858
|
+
const deps = fragment.$internal?.deps;
|
|
859
|
+
instanceResults.set(plan.name, {
|
|
860
|
+
fragment,
|
|
861
|
+
services: fragment.services,
|
|
862
|
+
deps: deps || {},
|
|
863
|
+
callRoute: fragment.callRoute.bind(fragment),
|
|
864
|
+
get db() {
|
|
865
|
+
return orm;
|
|
866
|
+
},
|
|
867
|
+
_orm: orm,
|
|
868
|
+
_schema: plan.schema,
|
|
869
|
+
});
|
|
870
|
+
}
|
|
442
871
|
}
|
|
443
872
|
|
|
444
873
|
// Second pass: rebuild fragments with service dependencies wired up
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
874
|
+
const fragmentResults: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
875
|
+
|
|
876
|
+
for (const plan of fragmentPlans) {
|
|
877
|
+
const orm = testContext.getOrm(plan.namespace);
|
|
878
|
+
|
|
879
|
+
if (instanceResults.has(plan.name)) {
|
|
880
|
+
fragmentResults.push(instanceResults.get(plan.name));
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const builderConfig = builderConfigs.get(plan.name);
|
|
885
|
+
if (!builderConfig) {
|
|
886
|
+
throw new Error(
|
|
887
|
+
`Fragment '${plan.name}' was expected to produce a builder for service wiring.`,
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
const definition = builderConfig.definition;
|
|
452
891
|
|
|
453
892
|
// Build service implementations for services this fragment uses
|
|
454
893
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -464,22 +903,17 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
464
903
|
}
|
|
465
904
|
|
|
466
905
|
// Merge builder options with database adapter
|
|
467
|
-
const mergedOptions =
|
|
468
|
-
...builderConfig.options,
|
|
469
|
-
databaseAdapter: adapter,
|
|
470
|
-
};
|
|
906
|
+
const mergedOptions = mergeBuilderOptions(builderConfig.options);
|
|
471
907
|
|
|
472
908
|
// Rebuild the fragment with service implementations using the builder
|
|
473
|
-
const fragment =
|
|
909
|
+
const fragment = builderConfig.builder
|
|
474
910
|
.withOptions(mergedOptions)
|
|
475
911
|
.withServices(serviceImplementations as any) // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
476
912
|
.build();
|
|
477
913
|
|
|
478
|
-
// Update the result
|
|
479
|
-
// Access deps from the internal property
|
|
480
914
|
const deps = fragment.$internal?.deps;
|
|
481
915
|
|
|
482
|
-
fragmentResults
|
|
916
|
+
fragmentResults.push({
|
|
483
917
|
fragment,
|
|
484
918
|
services: fragment.services,
|
|
485
919
|
deps: deps || {},
|
|
@@ -488,8 +922,8 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
488
922
|
return orm;
|
|
489
923
|
},
|
|
490
924
|
_orm: orm,
|
|
491
|
-
_schema: schema,
|
|
492
|
-
};
|
|
925
|
+
_schema: plan.schema,
|
|
926
|
+
});
|
|
493
927
|
}
|
|
494
928
|
|
|
495
929
|
return fragmentResults;
|