@fragno-dev/test 0.1.14 → 1.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.
- package/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +19 -0
- package/dist/adapters.d.ts +1 -1
- package/dist/adapters.js +30 -47
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +3 -3
- package/dist/db-test.d.ts.map +1 -1
- package/dist/db-test.js +6 -2
- package/dist/db-test.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters.ts +54 -105
- package/src/db-test.ts +13 -5
- package/src/index.ts +4 -4
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @fragno-dev/test@0.
|
|
2
|
+
> @fragno-dev/test@1.0.0 build /home/runner/work/fragno/fragno/packages/fragno-test
|
|
3
3
|
> tsdown
|
|
4
4
|
|
|
5
5
|
[34mℹ[39m tsdown [2mv0.15.12[22m powered by rolldown [2mv1.0.0-beta.45[22m
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
9
9
|
[34mℹ[39m Build start
|
|
10
10
|
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m 0.71 kB[22m [2m│ gzip: 0.40 kB[22m
|
|
11
|
-
[34mℹ[39m [2mdist/[22mdb-test.js.map [
|
|
12
|
-
[34mℹ[39m [2mdist/[22madapters.js.map [
|
|
13
|
-
[34mℹ[39m [2mdist/[22madapters.js [2m 7.
|
|
14
|
-
[34mℹ[39m [2mdist/[22mdb-test.js [2m 7.
|
|
11
|
+
[34mℹ[39m [2mdist/[22mdb-test.js.map [2m24.19 kB[22m [2m│ gzip: 5.91 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22madapters.js.map [2m16.98 kB[22m [2m│ gzip: 3.34 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22madapters.js [2m 7.53 kB[22m [2m│ gzip: 1.45 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22mdb-test.js [2m 7.43 kB[22m [2m│ gzip: 2.18 kB[22m
|
|
15
15
|
[34mℹ[39m [2mdist/[22mdb-test.d.ts.map [2m 3.19 kB[22m [2m│ gzip: 1.47 kB[22m
|
|
16
|
-
[34mℹ[39m [2mdist/[22mindex.js.map [2m 2.
|
|
16
|
+
[34mℹ[39m [2mdist/[22mindex.js.map [2m 2.46 kB[22m [2m│ gzip: 0.96 kB[22m
|
|
17
17
|
[34mℹ[39m [2mdist/[22mindex.d.ts.map [2m 0.55 kB[22m [2m│ gzip: 0.31 kB[22m
|
|
18
18
|
[34mℹ[39m [2mdist/[22madapters.d.ts.map [2m 0.42 kB[22m [2m│ gzip: 0.27 kB[22m
|
|
19
|
-
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m 1.
|
|
20
|
-
[34mℹ[39m [2mdist/[22m[32mdb-test.d.ts[39m [2m 6.
|
|
21
|
-
[34mℹ[39m [2mdist/[22m[32madapters.d.ts[39m [2m 0.
|
|
22
|
-
[34mℹ[39m 12 files, total:
|
|
23
|
-
[32m✔[39m Build complete in [
|
|
19
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.ts[22m[39m [2m 1.81 kB[22m [2m│ gzip: 0.68 kB[22m
|
|
20
|
+
[34mℹ[39m [2mdist/[22m[32mdb-test.d.ts[39m [2m 6.68 kB[22m [2m│ gzip: 1.80 kB[22m
|
|
21
|
+
[34mℹ[39m [2mdist/[22m[32madapters.d.ts[39m [2m 0.96 kB[22m [2m│ gzip: 0.38 kB[22m
|
|
22
|
+
[34mℹ[39m 12 files, total: 72.92 kB
|
|
23
|
+
[32m✔[39m Build complete in [32m13104ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @fragno-dev/test
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f9ae2d3: fix: database namespace generation
|
|
8
|
+
- Updated dependencies [8429960]
|
|
9
|
+
- Updated dependencies [4d897c9]
|
|
10
|
+
- Updated dependencies [a46b59c]
|
|
11
|
+
- Updated dependencies [bc072dd]
|
|
12
|
+
- Updated dependencies [e46d2a7]
|
|
13
|
+
- Updated dependencies [fcce048]
|
|
14
|
+
- Updated dependencies [147bdd6]
|
|
15
|
+
- Updated dependencies [f9ae2d3]
|
|
16
|
+
- Updated dependencies [f3b7084]
|
|
17
|
+
- Updated dependencies [c3870ec]
|
|
18
|
+
- Updated dependencies [75e298f]
|
|
19
|
+
- @fragno-dev/db@0.2.0
|
|
20
|
+
- @fragno-dev/core@0.1.11
|
|
21
|
+
|
|
3
22
|
## 0.1.14
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/dist/adapters.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import "./index.js";
|
|
|
2
2
|
import { Kysely } from "kysely";
|
|
3
3
|
import { drizzle } from "drizzle-orm/pglite";
|
|
4
4
|
import { AnySchema } from "@fragno-dev/db/schema";
|
|
5
|
-
import {
|
|
5
|
+
import { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
6
6
|
|
|
7
7
|
//#region src/adapters.d.ts
|
|
8
8
|
interface KyselySqliteAdapter {
|
package/dist/adapters.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createCommonTestContextMethods } from "./index.js";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import { Kysely } from "kysely";
|
|
4
3
|
import { SQLocalKysely } from "sqlocal/kysely";
|
|
5
4
|
import { KyselyPGlite } from "kysely-pglite";
|
|
@@ -7,9 +6,10 @@ import { drizzle } from "drizzle-orm/pglite";
|
|
|
7
6
|
import { PGlite } from "@electric-sql/pglite";
|
|
8
7
|
import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
|
|
9
8
|
import { DrizzleAdapter } from "@fragno-dev/db/adapters/drizzle";
|
|
10
|
-
import {
|
|
11
|
-
import { join } from "node:path";
|
|
9
|
+
import { rm } from "node:fs/promises";
|
|
12
10
|
import { existsSync } from "node:fs";
|
|
11
|
+
import { PGLiteDriverConfig, SQLocalDriverConfig } from "@fragno-dev/db/drivers";
|
|
12
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
13
13
|
|
|
14
14
|
//#region src/adapters.ts
|
|
15
15
|
/**
|
|
@@ -21,13 +21,14 @@ async function createKyselySqliteAdapter(_config, schemas) {
|
|
|
21
21
|
const { dialect } = new SQLocalKysely(":memory:");
|
|
22
22
|
const kysely$1 = new Kysely({ dialect });
|
|
23
23
|
const adapter$1 = new KyselyAdapter({
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
dialect,
|
|
25
|
+
driverConfig: new SQLocalDriverConfig()
|
|
26
26
|
});
|
|
27
27
|
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
28
28
|
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
29
|
-
const
|
|
30
|
-
|
|
29
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
30
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
31
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
31
32
|
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
32
33
|
ormMap$1.set(namespace, orm);
|
|
33
34
|
}
|
|
@@ -77,13 +78,14 @@ async function createKyselyPgliteAdapter(config, schemas) {
|
|
|
77
78
|
const kyselyPglite$1 = await KyselyPGlite.create(databasePath);
|
|
78
79
|
const kysely$1 = new Kysely({ dialect: kyselyPglite$1.dialect });
|
|
79
80
|
const adapter$1 = new KyselyAdapter({
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
dialect: kyselyPglite$1.dialect,
|
|
82
|
+
driverConfig: new PGLiteDriverConfig()
|
|
82
83
|
});
|
|
83
84
|
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
84
85
|
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
85
|
-
const
|
|
86
|
-
|
|
86
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
87
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
88
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
87
89
|
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
88
90
|
ormMap$1.set(namespace, orm);
|
|
89
91
|
}
|
|
@@ -138,52 +140,34 @@ async function createKyselyPgliteAdapter(config, schemas) {
|
|
|
138
140
|
*/
|
|
139
141
|
async function createDrizzlePgliteAdapter(config, schemas) {
|
|
140
142
|
const databasePath = config.databasePath;
|
|
141
|
-
const { generateDrizzleJson, generateMigration } = createRequire(import.meta.url)("drizzle-kit/api");
|
|
142
|
-
const { generateSchema } = await import("@fragno-dev/db/adapters/drizzle/generate");
|
|
143
|
-
const writeAndLoadSchema = async () => {
|
|
144
|
-
const testDir = join(import.meta.dirname, "_generated", "drizzle-test");
|
|
145
|
-
await mkdir(testDir, { recursive: true }).catch(() => {});
|
|
146
|
-
const schemaFilePath = join(testDir, `test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`);
|
|
147
|
-
await writeFile(schemaFilePath, generateSchema(schemas.map(({ schema, namespace }) => ({
|
|
148
|
-
namespace: namespace ?? "",
|
|
149
|
-
schema
|
|
150
|
-
})), "postgresql"), "utf-8");
|
|
151
|
-
const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);
|
|
152
|
-
const cleanup$1 = async () => {
|
|
153
|
-
await rm(testDir, {
|
|
154
|
-
recursive: true,
|
|
155
|
-
force: true
|
|
156
|
-
});
|
|
157
|
-
};
|
|
158
|
-
return {
|
|
159
|
-
schemaModule,
|
|
160
|
-
cleanup: cleanup$1
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
143
|
const createDatabase = async () => {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const db = drizzle(pglite$1, { schema: schemaModule });
|
|
167
|
-
const migrationStatements = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(schemaModule));
|
|
168
|
-
for (const statement of migrationStatements) await db.execute(statement);
|
|
144
|
+
const pglite = new PGlite(databasePath);
|
|
145
|
+
const { dialect } = new KyselyPGlite(pglite);
|
|
169
146
|
const adapter$1 = new DrizzleAdapter({
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
dialect,
|
|
148
|
+
driverConfig: new PGLiteDriverConfig()
|
|
172
149
|
});
|
|
173
150
|
const ormMap$1 = /* @__PURE__ */ new Map();
|
|
174
|
-
|
|
151
|
+
const databaseDeps = internalFragmentDef.dependencies?.({
|
|
152
|
+
config: {},
|
|
153
|
+
options: { databaseAdapter: adapter$1 }
|
|
154
|
+
});
|
|
155
|
+
if (databaseDeps?.schema) await adapter$1.prepareMigrations(databaseDeps.schema, databaseDeps.namespace).executeWithDriver(adapter$1.driver, 0);
|
|
156
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
157
|
+
const preparedMigrations = adapter$1.prepareMigrations(schema, namespace);
|
|
158
|
+
if (migrateToVersion !== void 0) await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
159
|
+
else await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
175
160
|
const orm = adapter$1.createQueryEngine(schema, namespace);
|
|
176
161
|
ormMap$1.set(namespace, orm);
|
|
177
162
|
}
|
|
178
163
|
return {
|
|
179
|
-
drizzle:
|
|
164
|
+
drizzle: drizzle(pglite),
|
|
180
165
|
adapter: adapter$1,
|
|
181
|
-
pglite
|
|
182
|
-
cleanup: cleanup$1,
|
|
166
|
+
pglite,
|
|
183
167
|
ormMap: ormMap$1
|
|
184
168
|
};
|
|
185
169
|
};
|
|
186
|
-
const { drizzle: drizzleDb, adapter,
|
|
170
|
+
const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();
|
|
187
171
|
const resetDatabase = async () => {
|
|
188
172
|
if (databasePath && databasePath !== ":memory:") throw new Error("resetDatabase is only supported for in-memory databases");
|
|
189
173
|
for (const { schema, namespace } of schemas) {
|
|
@@ -195,8 +179,7 @@ async function createDrizzlePgliteAdapter(config, schemas) {
|
|
|
195
179
|
}
|
|
196
180
|
};
|
|
197
181
|
const cleanup = async () => {
|
|
198
|
-
await
|
|
199
|
-
await schemaCleanup();
|
|
182
|
+
await adapter.close();
|
|
200
183
|
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) await rm(databasePath, {
|
|
201
184
|
recursive: true,
|
|
202
185
|
force: true
|
package/dist/adapters.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapters.js","names":["kysely","adapter","ormMap","kyselyPglite","cleanup","pglite"],"sources":["../src/adapters.ts"],"sourcesContent":["import { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\nimport { KyselyPGlite } from \"kysely-pglite\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { PGlite } from \"@electric-sql/pglite\";\nimport { KyselyAdapter } from \"@fragno-dev/db/adapters/kysely\";\nimport { DrizzleAdapter } from \"@fragno-dev/db/adapters/drizzle\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db/adapters\";\nimport type { AbstractQuery } from \"@fragno-dev/db/query\";\nimport { createRequire } from \"node:module\";\nimport { mkdir, writeFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport type { BaseTestContext } from \".\";\nimport { createCommonTestContextMethods } from \".\";\n\n// Adapter configuration types\nexport interface KyselySqliteAdapter {\n type: \"kysely-sqlite\";\n}\n\nexport interface KyselyPgliteAdapter {\n type: \"kysely-pglite\";\n databasePath?: string;\n}\n\nexport interface DrizzlePgliteAdapter {\n type: \"drizzle-pglite\";\n databasePath?: string;\n}\n\nexport type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;\n\n// Schema configuration for multi-schema adapters\nexport interface SchemaConfig {\n schema: AnySchema;\n namespace: string;\n migrateToVersion?: number;\n}\n\n// Internal test context extends BaseTestContext with getOrm (not exposed publicly)\ninterface InternalTestContext extends BaseTestContext {\n getOrm: <TSchema extends AnySchema>(namespace: string) => AbstractQuery<TSchema>;\n}\n\n// Conditional return types based on adapter (adapter-specific properties only)\nexport type AdapterContext<T extends SupportedAdapter> = T extends\n | KyselySqliteAdapter\n | KyselyPgliteAdapter\n ? {\n readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n : T extends DrizzlePgliteAdapter\n ? {\n readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n : never;\n\n// Factory function return type\ninterface AdapterFactoryResult<T extends SupportedAdapter> {\n testContext: InternalTestContext & AdapterContext<T>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n}\n\n/**\n * Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)\n * Supports multiple schemas with separate namespaces\n */\nexport async function createKyselySqliteAdapter(\n _config: KyselySqliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n // Create SQLocalKysely instance (always in-memory for tests)\n const { dialect } = new SQLocalKysely(\":memory:\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"sqlite\",\n });\n\n // Run migrations for all schemas in order\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, AbstractQuery<any, any>>();\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n // Create ORM instance and store in map\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, ormMap };\n };\n\n // Create initial database\n let { kysely, adapter, ormMap } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n // For SQLite, truncate all tables by deleting rows\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await kysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n // Cleanup function - closes connections (no files to delete for in-memory)\n const cleanup = async () => {\n await kysely.destroy();\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Kysely + PGLite adapter using kysely-pglite\n * Supports multiple schemas with separate namespaces\n */\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n // Create KyselyPGlite instance\n const kyselyPglite = await KyselyPGlite.create(databasePath);\n\n // Create Kysely instance with PGlite dialect\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect: kyselyPglite.dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n db: kysely,\n provider: \"postgresql\",\n });\n\n // Run migrations for all schemas in order\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, AbstractQuery<any, any>>();\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n // Run migrations\n const migrator = adapter.createMigrationEngine(schema, namespace);\n const preparedMigration = migrateToVersion\n ? await migrator.prepareMigrationTo(migrateToVersion, {\n updateSettings: false,\n })\n : await migrator.prepareMigration({\n updateSettings: false,\n });\n await preparedMigration.execute();\n\n // Create ORM instance and store in map\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, kyselyPglite, ormMap };\n };\n\n // Create initial database\n const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n // Truncate all tables\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await kysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n // Cleanup function - closes connections and deletes database directory\n const cleanup = async () => {\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Drizzle + PGLite adapter using drizzle-orm/pglite\n * Supports multiple schemas with separate namespaces\n */\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Import drizzle-kit for migrations\n const require = createRequire(import.meta.url);\n const { generateDrizzleJson, generateMigration } =\n require(\"drizzle-kit/api\") as typeof import(\"drizzle-kit/api\");\n\n // Import generateSchema from the properly exported module\n const { generateSchema } = await import(\"@fragno-dev/db/adapters/drizzle/generate\");\n\n // Helper to write schema to file and dynamically import it\n const writeAndLoadSchema = async () => {\n const testDir = join(import.meta.dirname, \"_generated\", \"drizzle-test\");\n await mkdir(testDir, { recursive: true }).catch(() => {\n // Ignore error if directory already exists\n });\n\n const schemaFilePath = join(\n testDir,\n `test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`,\n );\n\n // Combine all schemas into a single Drizzle schema with namespaced tables\n const schemaConfigs = schemas.map(({ schema, namespace }) => ({\n namespace: namespace ?? \"\",\n schema,\n }));\n\n const drizzleSchemaTs = generateSchema(schemaConfigs, \"postgresql\");\n await writeFile(schemaFilePath, drizzleSchemaTs, \"utf-8\");\n\n // Dynamically import the generated schema (with cache busting)\n const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);\n\n const cleanup = async () => {\n await rm(testDir, { recursive: true, force: true });\n };\n\n return { schemaModule, cleanup };\n };\n\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n // Write schema to file and load it\n const { schemaModule, cleanup } = await writeAndLoadSchema();\n\n // Create PGlite instance\n const pglite = new PGlite(databasePath);\n\n // Create Drizzle instance with PGlite\n const db = drizzle(pglite, {\n schema: schemaModule,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) as any;\n\n // Generate and run migrations\n const migrationStatements = await generateMigration(\n generateDrizzleJson({}), // Empty schema (starting state)\n generateDrizzleJson(schemaModule), // Target schema\n );\n\n // Execute migration SQL\n for (const statement of migrationStatements) {\n await db.execute(statement);\n }\n\n // Create DrizzleAdapter\n const adapter = new DrizzleAdapter({\n db: () => db,\n provider: \"postgresql\",\n });\n\n // Create ORM instances for each schema and store in map\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, AbstractQuery<any, any>>();\n\n for (const { schema, namespace } of schemas) {\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { drizzle: db, adapter, pglite, cleanup, ormMap };\n };\n\n // Create initial database\n const {\n drizzle: drizzleDb,\n adapter,\n pglite,\n cleanup: schemaCleanup,\n ormMap,\n } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n // Truncate all tables by deleting rows\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await drizzleDb.execute(`DELETE FROM \"${physicalTableName}\"`);\n }\n }\n };\n\n // Cleanup function - closes connections and deletes generated files and database directory\n const cleanup = async () => {\n await pglite.close();\n await schemaCleanup();\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create adapter based on configuration\n * Supports multiple schemas with separate namespaces\n */\nexport async function createAdapter<T extends SupportedAdapter>(\n adapterConfig: T,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<T>> {\n if (adapterConfig.type === \"kysely-sqlite\") {\n return createKyselySqliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n } else if (adapterConfig.type === \"kysely-pglite\") {\n return createKyselyPgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n } else if (adapterConfig.type === \"drizzle-pglite\") {\n return createDrizzlePgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n }\n\n throw new Error(`Unsupported adapter type: ${(adapterConfig as SupportedAdapter).type}`);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"adapters.js","names":["kysely","adapter","ormMap","kyselyPglite"],"sources":["../src/adapters.ts"],"sourcesContent":["import { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\nimport { KyselyPGlite } from \"kysely-pglite\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { PGlite } from \"@electric-sql/pglite\";\nimport { KyselyAdapter } from \"@fragno-dev/db/adapters/kysely\";\nimport { DrizzleAdapter } from \"@fragno-dev/db/adapters/drizzle\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db/adapters\";\nimport { rm } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport type { BaseTestContext } from \".\";\nimport { createCommonTestContextMethods } from \".\";\nimport { PGLiteDriverConfig, SQLocalDriverConfig } from \"@fragno-dev/db/drivers\";\nimport { internalFragmentDef } from \"@fragno-dev/db\";\nimport type { SimpleQueryInterface } from \"@fragno-dev/db/query\";\n\n// Adapter configuration types\nexport interface KyselySqliteAdapter {\n type: \"kysely-sqlite\";\n}\n\nexport interface KyselyPgliteAdapter {\n type: \"kysely-pglite\";\n databasePath?: string;\n}\n\nexport interface DrizzlePgliteAdapter {\n type: \"drizzle-pglite\";\n databasePath?: string;\n}\n\nexport type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;\n\n// Schema configuration for multi-schema adapters\nexport interface SchemaConfig {\n schema: AnySchema;\n namespace: string;\n migrateToVersion?: number;\n}\n\n// Internal test context extends BaseTestContext with getOrm (not exposed publicly)\ninterface InternalTestContext extends BaseTestContext {\n getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;\n}\n\n// Conditional return types based on adapter (adapter-specific properties only)\nexport type AdapterContext<T extends SupportedAdapter> = T extends\n | KyselySqliteAdapter\n | KyselyPgliteAdapter\n ? {\n readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n : T extends DrizzlePgliteAdapter\n ? {\n readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n : never;\n\n// Factory function return type\ninterface AdapterFactoryResult<T extends SupportedAdapter> {\n testContext: InternalTestContext & AdapterContext<T>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n}\n\n/**\n * Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)\n * Supports multiple schemas with separate namespaces\n */\nexport async function createKyselySqliteAdapter(\n _config: KyselySqliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n // Create SQLocalKysely instance (always in-memory for tests)\n const { dialect } = new SQLocalKysely(\":memory:\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n dialect,\n driverConfig: new SQLocalDriverConfig(),\n });\n\n // Run migrations for all schemas in order\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, SimpleQueryInterface<any, any>>();\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n // Run migrations\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n // Create ORM instance and store in map\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, ormMap };\n };\n\n // Create initial database\n let { kysely, adapter, ormMap } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n // For SQLite, truncate all tables by deleting rows\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await kysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n // Cleanup function - closes connections (no files to delete for in-memory)\n const cleanup = async () => {\n await kysely.destroy();\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Kysely + PGLite adapter using kysely-pglite\n * Supports multiple schemas with separate namespaces\n */\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n // Create KyselyPGlite instance\n const kyselyPglite = await KyselyPGlite.create(databasePath);\n\n // Create Kysely instance with PGlite dialect\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const kysely = new Kysely<any>({\n dialect: kyselyPglite.dialect,\n });\n\n // Create KyselyAdapter\n const adapter = new KyselyAdapter({\n dialect: kyselyPglite.dialect,\n driverConfig: new PGLiteDriverConfig(),\n });\n\n // Run migrations for all schemas in order\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, SimpleQueryInterface<any, any>>();\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n // Run migrations\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n // Create ORM instance and store in map\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n return { kysely, adapter, kyselyPglite, ormMap };\n };\n\n // Create initial database\n const { kysely, adapter, kyselyPglite, ormMap } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n // Truncate all tables\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await kysely.deleteFrom(physicalTableName).execute();\n }\n }\n };\n\n // Cleanup function - closes connections and deletes database directory\n const cleanup = async () => {\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create Drizzle + PGLite adapter using drizzle-orm/pglite\n * Supports multiple schemas with separate namespaces\n */\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations for all schemas\n const createDatabase = async () => {\n const pglite = new PGlite(databasePath);\n\n const { dialect } = new KyselyPGlite(pglite);\n\n const adapter = new DrizzleAdapter({\n dialect,\n driverConfig: new PGLiteDriverConfig(),\n });\n\n // Run migrations for all schemas\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const ormMap = new Map<string, SimpleQueryInterface<any, any>>();\n\n const databaseDeps = internalFragmentDef.dependencies?.({\n config: {},\n options: { databaseAdapter: adapter },\n });\n if (databaseDeps?.schema) {\n const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);\n await migrations.executeWithDriver(adapter.driver, 0);\n }\n\n for (const { schema, namespace, migrateToVersion } of schemas) {\n const preparedMigrations = adapter.prepareMigrations(schema, namespace);\n if (migrateToVersion !== undefined) {\n await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });\n } else {\n await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });\n }\n\n // Create ORM instance and store in map\n const orm = adapter.createQueryEngine(schema, namespace);\n ormMap.set(namespace, orm);\n }\n\n // Create Drizzle instance for backward compatibility (if needed)\n const db = drizzle(pglite) as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n return { drizzle: db, adapter, pglite, ormMap };\n };\n\n // Create initial database\n const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();\n\n // Reset database function - truncates all tables (only supported for in-memory databases)\n const resetDatabase = async () => {\n if (databasePath && databasePath !== \":memory:\") {\n throw new Error(\"resetDatabase is only supported for in-memory databases\");\n }\n\n // Truncate all tables by deleting rows\n for (const { schema, namespace } of schemas) {\n const mapper = adapter.createTableNameMapper(namespace);\n for (const tableName of Object.keys(schema.tables)) {\n const physicalTableName = mapper.toPhysical(tableName);\n await drizzleDb.execute(`DELETE FROM \"${physicalTableName}\"`);\n }\n }\n };\n\n // Cleanup function - closes connections and deletes database directory\n const cleanup = async () => {\n // Close the adapter (which will handle closing the underlying database connection)\n await adapter.close();\n\n // Delete the database directory if it exists and is a file path\n if (databasePath && databasePath !== \":memory:\" && existsSync(databasePath)) {\n await rm(databasePath, { recursive: true, force: true });\n }\n };\n\n const commonMethods = createCommonTestContextMethods(ormMap);\n\n return {\n testContext: {\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n ...commonMethods,\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create adapter based on configuration\n * Supports multiple schemas with separate namespaces\n */\nexport async function createAdapter<T extends SupportedAdapter>(\n adapterConfig: T,\n schemas: SchemaConfig[],\n): Promise<AdapterFactoryResult<T>> {\n if (adapterConfig.type === \"kysely-sqlite\") {\n return createKyselySqliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n } else if (adapterConfig.type === \"kysely-pglite\") {\n return createKyselyPgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n } else if (adapterConfig.type === \"drizzle-pglite\") {\n return createDrizzlePgliteAdapter(adapterConfig, schemas) as Promise<AdapterFactoryResult<T>>;\n }\n\n throw new Error(`Unsupported adapter type: ${(adapterConfig as SupportedAdapter).type}`);\n}\n"],"mappings":"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"}
|
package/dist/db-test.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AdapterContext, SupportedAdapter } from "./adapters.js";
|
|
2
2
|
import { BaseTestContext } from "./index.js";
|
|
3
|
-
import { AnySchema } from "@fragno-dev/db/schema";
|
|
4
|
-
import { AbstractQuery } from "@fragno-dev/db/query";
|
|
5
3
|
import { DatabaseAdapter } from "@fragno-dev/db";
|
|
4
|
+
import { AnySchema } from "@fragno-dev/db/schema";
|
|
5
|
+
import { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
6
6
|
import { FragmentInstantiationBuilder, FragnoInstantiatedFragment, FragnoPublicConfig, RequestThisContext } from "@fragno-dev/core";
|
|
7
7
|
import { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/route";
|
|
8
8
|
|
|
@@ -19,7 +19,7 @@ TSchema$1 extends AnySchema, TLinkedFragments extends AnyLinkedFragments = {}> {
|
|
|
19
19
|
services: TServices;
|
|
20
20
|
deps: TDeps;
|
|
21
21
|
callRoute: FragnoInstantiatedFragment<TRoutes, TDeps, TServices, TServiceThisContext, THandlerThisContext, TRequestStorage, FragnoPublicConfig, TLinkedFragments>["callRoute"];
|
|
22
|
-
db:
|
|
22
|
+
db: SimpleQueryInterface<TSchema$1>;
|
|
23
23
|
}
|
|
24
24
|
type AnyFragmentResult = FragmentResult<any,
|
|
25
25
|
// eslint-disable-line @typescript-eslint/no-explicit-any
|
package/dist/db-test.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-test.d.ts","names":[],"sources":["../src/db-test.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAqBK,iCACS,IAAI,EAAE,kEACJ,MAAM,IAChB,EAAE,WAAW,0BACX,cAAc,EAAE,MAChB,EAAE,IAT+B;KAcpC,qBATS,CAAA,KAAA,CAAA,GASsB,KATtB,SAAA;EAAI,MAAA,EAAA,KAAA,iBASgE,SAThE;CAAE,GAUhB,OAVgB,GAWhB,SAXgB;KAcf,kBAAA,GAAqB,MAbV,CAAA,MAAA,EAAA,GAAA,CAAA;UAgBN,cAhBY,CAAA,KAAA,EAAA,kBAkBF,MAlBE,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,4BAmBQ,kBAnBR,EAAA,4BAoBQ,kBApBR,EAAA,eAAA,EAAA,gBAAA,SAAA,GAAA,EAAA;AAAA;kBAuBJ,SAtBZ,EAAA,yBAuBqB,kBAvBrB,GAAA,CAAA,CAAA,CAAA,CAAA;EAAE,QAAA,EAyBI,0BAzBJ,CA0BJ,OA1BI,EA2BJ,KA3BI,EA4BJ,SA5BI,EA6BJ,mBA7BI,EA8BJ,mBA9BI,EA+BJ,eA/BI,EAgCJ,kBAhCI,EAiCJ,gBAjCI,CAAA;EAAW,QAAA,EAmCP,SAnCO;EACG,IAAA,EAmCd,KAnCc;EAAE,SAAA,EAoCX,0BApCW,CAqCpB,OArCoB,EAsCpB,KAtCoB,EAuCpB,SAvCoB,EAwCpB,mBAxCoB,EAyCpB,mBAzCoB,EA0CpB,eA1CoB,EA2CpB,kBA3CoB,EA4CpB,gBA5CoB,CAAA,CAAA,WAAA,CAAA;EAAhB,EAAA,EA8CF,
|
|
1
|
+
{"version":3,"file":"db-test.d.ts","names":[],"sources":["../src/db-test.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/db-test.js
CHANGED
|
@@ -44,7 +44,7 @@ var DatabaseFragmentsTestBuilder = class {
|
|
|
44
44
|
const builder = fragmentConfig.builder;
|
|
45
45
|
const definition = builder.definition;
|
|
46
46
|
let schema;
|
|
47
|
-
|
|
47
|
+
let namespace;
|
|
48
48
|
if (definition.dependencies) try {
|
|
49
49
|
const mockAdapter = {
|
|
50
50
|
createQueryEngine: () => ({ schema: null }),
|
|
@@ -55,12 +55,16 @@ var DatabaseFragmentsTestBuilder = class {
|
|
|
55
55
|
config: actualConfig,
|
|
56
56
|
options: { databaseAdapter: mockAdapter }
|
|
57
57
|
});
|
|
58
|
-
if (deps && typeof deps === "object" && "schema" in deps)
|
|
58
|
+
if (deps && typeof deps === "object" && "schema" in deps) {
|
|
59
|
+
schema = deps.schema;
|
|
60
|
+
namespace = deps.namespace;
|
|
61
|
+
}
|
|
59
62
|
} catch (error) {
|
|
60
63
|
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
61
64
|
throw new Error(`Failed to extract schema from fragment '${definition.name}'.\nOriginal error: ${errorMessage}\n\nMake sure the fragment is a database fragment using defineFragment().extend(withDatabase(schema)).`);
|
|
62
65
|
}
|
|
63
66
|
if (!schema) throw new Error(`Fragment '${definition.name}' does not have a database schema. Make sure you're using defineFragment().extend(withDatabase(schema)).`);
|
|
67
|
+
if (!namespace) throw new Error(`Fragment '${definition.name}' does not have a namespace in dependencies. This should be automatically provided by withDatabase().`);
|
|
64
68
|
schemaConfigs.push({
|
|
65
69
|
schema,
|
|
66
70
|
namespace,
|
package/dist/db-test.js.map
CHANGED
|
@@ -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","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 { AbstractQuery } 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: AbstractQuery<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 from definition by calling dependencies with a mock adapter\n let schema: AnySchema | undefined;\n const namespace = definition.name + \"-db\";\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 is in deps.schema 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 }\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 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 | 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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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { AdapterContext, DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter } from "./adapters.js";
|
|
2
2
|
import { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest } from "./db-test.js";
|
|
3
3
|
import { CreateFragmentForTestOptions, RouteHandlerInputOptions, createFragmentForTest } from "@fragno-dev/core/test";
|
|
4
|
-
import { AnySchema } from "@fragno-dev/db/schema";
|
|
5
|
-
import { AbstractQuery } from "@fragno-dev/db/query";
|
|
6
4
|
import { DatabaseAdapter } from "@fragno-dev/db";
|
|
5
|
+
import { AnySchema } from "@fragno-dev/db/schema";
|
|
6
|
+
import { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
7
7
|
|
|
8
8
|
//#region src/index.d.ts
|
|
9
9
|
|
|
@@ -19,13 +19,13 @@ interface BaseTestContext {
|
|
|
19
19
|
* Internal interface with getOrm for adapter implementations
|
|
20
20
|
*/
|
|
21
21
|
interface InternalTestContextMethods {
|
|
22
|
-
getOrm: <TSchema extends AnySchema>(namespace: string) =>
|
|
22
|
+
getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* Helper to create common test context methods from an ORM map
|
|
26
26
|
* This is used internally by adapter implementations to avoid code duplication
|
|
27
27
|
*/
|
|
28
|
-
declare function createCommonTestContextMethods(ormMap: Map<string,
|
|
28
|
+
declare function createCommonTestContextMethods(ormMap: Map<string, SimpleQueryInterface<any>>): InternalTestContextMethods;
|
|
29
29
|
/**
|
|
30
30
|
* Complete test context combining base and adapter-specific functionality
|
|
31
31
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
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 {
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@electric-sql/pglite": "^0.3.11",
|
|
22
|
-
"drizzle-kit": "^0.
|
|
22
|
+
"drizzle-kit": "^0.31.7",
|
|
23
23
|
"drizzle-orm": "^0.44.7",
|
|
24
24
|
"kysely-pglite": "^0.6.1",
|
|
25
|
-
"@fragno-dev/core": "0.1.
|
|
26
|
-
"@fragno-dev/db": "0.
|
|
25
|
+
"@fragno-dev/core": "0.1.11",
|
|
26
|
+
"@fragno-dev/db": "0.2.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"@electric-sql/pglite": {
|
|
@@ -44,13 +44,13 @@
|
|
|
44
44
|
"@libsql/client": "^0.14.0",
|
|
45
45
|
"@types/node": "^22",
|
|
46
46
|
"@vitest/coverage-istanbul": "^3.2.4",
|
|
47
|
-
"drizzle-kit": "^0.
|
|
47
|
+
"drizzle-kit": "^0.31.7",
|
|
48
48
|
"drizzle-orm": "^0.44.7",
|
|
49
49
|
"kysely-pglite": "^0.6.1",
|
|
50
50
|
"vitest": "^3.2.4",
|
|
51
51
|
"zod": "^4.1.12",
|
|
52
|
-
"@fragno-dev/core": "0.1.
|
|
53
|
-
"@fragno-dev/db": "0.
|
|
52
|
+
"@fragno-dev/core": "0.1.11",
|
|
53
|
+
"@fragno-dev/db": "0.2.0",
|
|
54
54
|
"@fragno-private/typescript-config": "0.0.1",
|
|
55
55
|
"@fragno-private/vitest-config": "0.0.0"
|
|
56
56
|
},
|
package/src/adapters.ts
CHANGED
|
@@ -7,13 +7,13 @@ import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
|
|
|
7
7
|
import { DrizzleAdapter } from "@fragno-dev/db/adapters/drizzle";
|
|
8
8
|
import type { AnySchema } from "@fragno-dev/db/schema";
|
|
9
9
|
import type { DatabaseAdapter } from "@fragno-dev/db/adapters";
|
|
10
|
-
import
|
|
11
|
-
import { createRequire } from "node:module";
|
|
12
|
-
import { mkdir, writeFile, rm } from "node:fs/promises";
|
|
13
|
-
import { join } from "node:path";
|
|
10
|
+
import { rm } from "node:fs/promises";
|
|
14
11
|
import { existsSync } from "node:fs";
|
|
15
12
|
import type { BaseTestContext } from ".";
|
|
16
13
|
import { createCommonTestContextMethods } from ".";
|
|
14
|
+
import { PGLiteDriverConfig, SQLocalDriverConfig } from "@fragno-dev/db/drivers";
|
|
15
|
+
import { internalFragmentDef } from "@fragno-dev/db";
|
|
16
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
17
17
|
|
|
18
18
|
// Adapter configuration types
|
|
19
19
|
export interface KyselySqliteAdapter {
|
|
@@ -41,7 +41,7 @@ export interface SchemaConfig {
|
|
|
41
41
|
|
|
42
42
|
// Internal test context extends BaseTestContext with getOrm (not exposed publicly)
|
|
43
43
|
interface InternalTestContext extends BaseTestContext {
|
|
44
|
-
getOrm: <TSchema extends AnySchema>(namespace: string) =>
|
|
44
|
+
getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Conditional return types based on adapter (adapter-specific properties only)
|
|
@@ -83,25 +83,22 @@ export async function createKyselySqliteAdapter(
|
|
|
83
83
|
|
|
84
84
|
// Create KyselyAdapter
|
|
85
85
|
const adapter = new KyselyAdapter({
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
dialect,
|
|
87
|
+
driverConfig: new SQLocalDriverConfig(),
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
// Run migrations for all schemas in order
|
|
91
91
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
-
const ormMap = new Map<string,
|
|
92
|
+
const ormMap = new Map<string, SimpleQueryInterface<any, any>>();
|
|
93
93
|
|
|
94
94
|
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
95
95
|
// Run migrations
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
updateSettings: false,
|
|
103
|
-
});
|
|
104
|
-
await preparedMigration.execute();
|
|
96
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
97
|
+
if (migrateToVersion !== undefined) {
|
|
98
|
+
await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
99
|
+
} else {
|
|
100
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
101
|
+
}
|
|
105
102
|
|
|
106
103
|
// Create ORM instance and store in map
|
|
107
104
|
const orm = adapter.createQueryEngine(schema, namespace);
|
|
@@ -174,25 +171,22 @@ export async function createKyselyPgliteAdapter(
|
|
|
174
171
|
|
|
175
172
|
// Create KyselyAdapter
|
|
176
173
|
const adapter = new KyselyAdapter({
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
dialect: kyselyPglite.dialect,
|
|
175
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
179
176
|
});
|
|
180
177
|
|
|
181
178
|
// Run migrations for all schemas in order
|
|
182
179
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
183
|
-
const ormMap = new Map<string,
|
|
180
|
+
const ormMap = new Map<string, SimpleQueryInterface<any, any>>();
|
|
184
181
|
|
|
185
182
|
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
186
183
|
// Run migrations
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
updateSettings: false,
|
|
194
|
-
});
|
|
195
|
-
await preparedMigration.execute();
|
|
184
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
185
|
+
if (migrateToVersion !== undefined) {
|
|
186
|
+
await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
187
|
+
} else {
|
|
188
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
189
|
+
}
|
|
196
190
|
|
|
197
191
|
// Create ORM instance and store in map
|
|
198
192
|
const orm = adapter.createQueryEngine(schema, namespace);
|
|
@@ -267,96 +261,51 @@ export async function createDrizzlePgliteAdapter(
|
|
|
267
261
|
): Promise<AdapterFactoryResult<DrizzlePgliteAdapter>> {
|
|
268
262
|
const databasePath = config.databasePath;
|
|
269
263
|
|
|
270
|
-
// Import drizzle-kit for migrations
|
|
271
|
-
const require = createRequire(import.meta.url);
|
|
272
|
-
const { generateDrizzleJson, generateMigration } =
|
|
273
|
-
require("drizzle-kit/api") as typeof import("drizzle-kit/api");
|
|
274
|
-
|
|
275
|
-
// Import generateSchema from the properly exported module
|
|
276
|
-
const { generateSchema } = await import("@fragno-dev/db/adapters/drizzle/generate");
|
|
277
|
-
|
|
278
|
-
// Helper to write schema to file and dynamically import it
|
|
279
|
-
const writeAndLoadSchema = async () => {
|
|
280
|
-
const testDir = join(import.meta.dirname, "_generated", "drizzle-test");
|
|
281
|
-
await mkdir(testDir, { recursive: true }).catch(() => {
|
|
282
|
-
// Ignore error if directory already exists
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
const schemaFilePath = join(
|
|
286
|
-
testDir,
|
|
287
|
-
`test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`,
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// Combine all schemas into a single Drizzle schema with namespaced tables
|
|
291
|
-
const schemaConfigs = schemas.map(({ schema, namespace }) => ({
|
|
292
|
-
namespace: namespace ?? "",
|
|
293
|
-
schema,
|
|
294
|
-
}));
|
|
295
|
-
|
|
296
|
-
const drizzleSchemaTs = generateSchema(schemaConfigs, "postgresql");
|
|
297
|
-
await writeFile(schemaFilePath, drizzleSchemaTs, "utf-8");
|
|
298
|
-
|
|
299
|
-
// Dynamically import the generated schema (with cache busting)
|
|
300
|
-
const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);
|
|
301
|
-
|
|
302
|
-
const cleanup = async () => {
|
|
303
|
-
await rm(testDir, { recursive: true, force: true });
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
return { schemaModule, cleanup };
|
|
307
|
-
};
|
|
308
|
-
|
|
309
264
|
// Helper to create a new database instance and run migrations for all schemas
|
|
310
265
|
const createDatabase = async () => {
|
|
311
|
-
// Write schema to file and load it
|
|
312
|
-
const { schemaModule, cleanup } = await writeAndLoadSchema();
|
|
313
|
-
|
|
314
|
-
// Create PGlite instance
|
|
315
266
|
const pglite = new PGlite(databasePath);
|
|
316
267
|
|
|
317
|
-
|
|
318
|
-
const db = drizzle(pglite, {
|
|
319
|
-
schema: schemaModule,
|
|
320
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
321
|
-
}) as any;
|
|
322
|
-
|
|
323
|
-
// Generate and run migrations
|
|
324
|
-
const migrationStatements = await generateMigration(
|
|
325
|
-
generateDrizzleJson({}), // Empty schema (starting state)
|
|
326
|
-
generateDrizzleJson(schemaModule), // Target schema
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// Execute migration SQL
|
|
330
|
-
for (const statement of migrationStatements) {
|
|
331
|
-
await db.execute(statement);
|
|
332
|
-
}
|
|
268
|
+
const { dialect } = new KyselyPGlite(pglite);
|
|
333
269
|
|
|
334
|
-
// Create DrizzleAdapter
|
|
335
270
|
const adapter = new DrizzleAdapter({
|
|
336
|
-
|
|
337
|
-
|
|
271
|
+
dialect,
|
|
272
|
+
driverConfig: new PGLiteDriverConfig(),
|
|
338
273
|
});
|
|
339
274
|
|
|
340
|
-
//
|
|
275
|
+
// Run migrations for all schemas
|
|
341
276
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
-
const ormMap = new Map<string,
|
|
277
|
+
const ormMap = new Map<string, SimpleQueryInterface<any, any>>();
|
|
343
278
|
|
|
344
|
-
|
|
279
|
+
const databaseDeps = internalFragmentDef.dependencies?.({
|
|
280
|
+
config: {},
|
|
281
|
+
options: { databaseAdapter: adapter },
|
|
282
|
+
});
|
|
283
|
+
if (databaseDeps?.schema) {
|
|
284
|
+
const migrations = adapter.prepareMigrations(databaseDeps.schema, databaseDeps.namespace);
|
|
285
|
+
await migrations.executeWithDriver(adapter.driver, 0);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (const { schema, namespace, migrateToVersion } of schemas) {
|
|
289
|
+
const preparedMigrations = adapter.prepareMigrations(schema, namespace);
|
|
290
|
+
if (migrateToVersion !== undefined) {
|
|
291
|
+
await preparedMigrations.execute(0, migrateToVersion, { updateVersionInMigration: false });
|
|
292
|
+
} else {
|
|
293
|
+
await preparedMigrations.execute(0, schema.version, { updateVersionInMigration: false });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Create ORM instance and store in map
|
|
345
297
|
const orm = adapter.createQueryEngine(schema, namespace);
|
|
346
298
|
ormMap.set(namespace, orm);
|
|
347
299
|
}
|
|
348
300
|
|
|
349
|
-
|
|
301
|
+
// Create Drizzle instance for backward compatibility (if needed)
|
|
302
|
+
const db = drizzle(pglite) as any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
303
|
+
|
|
304
|
+
return { drizzle: db, adapter, pglite, ormMap };
|
|
350
305
|
};
|
|
351
306
|
|
|
352
307
|
// Create initial database
|
|
353
|
-
const {
|
|
354
|
-
drizzle: drizzleDb,
|
|
355
|
-
adapter,
|
|
356
|
-
pglite,
|
|
357
|
-
cleanup: schemaCleanup,
|
|
358
|
-
ormMap,
|
|
359
|
-
} = await createDatabase();
|
|
308
|
+
const { drizzle: drizzleDb, adapter, ormMap } = await createDatabase();
|
|
360
309
|
|
|
361
310
|
// Reset database function - truncates all tables (only supported for in-memory databases)
|
|
362
311
|
const resetDatabase = async () => {
|
|
@@ -374,10 +323,10 @@ export async function createDrizzlePgliteAdapter(
|
|
|
374
323
|
}
|
|
375
324
|
};
|
|
376
325
|
|
|
377
|
-
// Cleanup function - closes connections and deletes
|
|
326
|
+
// Cleanup function - closes connections and deletes database directory
|
|
378
327
|
const cleanup = async () => {
|
|
379
|
-
|
|
380
|
-
await
|
|
328
|
+
// Close the adapter (which will handle closing the underlying database connection)
|
|
329
|
+
await adapter.close();
|
|
381
330
|
|
|
382
331
|
// Delete the database directory if it exists and is a file path
|
|
383
332
|
if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) {
|
package/src/db-test.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type SchemaConfig,
|
|
15
15
|
} from "./adapters";
|
|
16
16
|
import type { DatabaseAdapter } from "@fragno-dev/db";
|
|
17
|
-
import type {
|
|
17
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
18
18
|
import type { BaseTestContext } from ".";
|
|
19
19
|
|
|
20
20
|
// BoundServices is an internal type that strips 'this' parameters from service methods
|
|
@@ -69,7 +69,7 @@ interface FragmentResult<
|
|
|
69
69
|
FragnoPublicConfig,
|
|
70
70
|
TLinkedFragments
|
|
71
71
|
>["callRoute"];
|
|
72
|
-
db:
|
|
72
|
+
db: SimpleQueryInterface<TSchema>;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Safe: Catch-all for any fragment result type
|
|
@@ -301,9 +301,9 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
301
301
|
const builder = fragmentConfig.builder;
|
|
302
302
|
const definition = builder.definition;
|
|
303
303
|
|
|
304
|
-
// Extract schema from definition by calling dependencies with a mock adapter
|
|
304
|
+
// Extract schema and namespace from definition by calling dependencies with a mock adapter
|
|
305
305
|
let schema: AnySchema | undefined;
|
|
306
|
-
|
|
306
|
+
let namespace: string | undefined;
|
|
307
307
|
|
|
308
308
|
if (definition.dependencies) {
|
|
309
309
|
try {
|
|
@@ -324,9 +324,10 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
324
324
|
} as any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
-
// The schema
|
|
327
|
+
// The schema and namespace are in deps for database fragments
|
|
328
328
|
if (deps && typeof deps === "object" && "schema" in deps) {
|
|
329
329
|
schema = (deps as any).schema; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
330
|
+
namespace = (deps as any).namespace; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
330
331
|
}
|
|
331
332
|
} catch (error) {
|
|
332
333
|
// If extraction fails, provide a helpful error message
|
|
@@ -352,6 +353,13 @@ export class DatabaseFragmentsTestBuilder<
|
|
|
352
353
|
);
|
|
353
354
|
}
|
|
354
355
|
|
|
356
|
+
if (!namespace) {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Fragment '${definition.name}' does not have a namespace in dependencies. ` +
|
|
359
|
+
`This should be automatically provided by withDatabase().`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
355
363
|
schemaConfigs.push({
|
|
356
364
|
schema,
|
|
357
365
|
namespace,
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
DrizzlePgliteAdapter,
|
|
8
8
|
} from "./adapters";
|
|
9
9
|
import type { DatabaseAdapter } from "@fragno-dev/db";
|
|
10
|
-
import type {
|
|
10
|
+
import type { SimpleQueryInterface } from "@fragno-dev/db/query";
|
|
11
11
|
|
|
12
12
|
// Re-export utilities from @fragno-dev/core/test
|
|
13
13
|
export {
|
|
@@ -42,7 +42,7 @@ export interface BaseTestContext {
|
|
|
42
42
|
* Internal interface with getOrm for adapter implementations
|
|
43
43
|
*/
|
|
44
44
|
export interface InternalTestContextMethods {
|
|
45
|
-
getOrm: <TSchema extends AnySchema>(namespace: string) =>
|
|
45
|
+
getOrm: <TSchema extends AnySchema>(namespace: string) => SimpleQueryInterface<TSchema>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|
|
@@ -51,7 +51,7 @@ export interface InternalTestContextMethods {
|
|
|
51
51
|
*/
|
|
52
52
|
export function createCommonTestContextMethods(
|
|
53
53
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
-
ormMap: Map<string,
|
|
54
|
+
ormMap: Map<string, SimpleQueryInterface<any>>,
|
|
55
55
|
): InternalTestContextMethods {
|
|
56
56
|
return {
|
|
57
57
|
getOrm: <TSchema extends AnySchema>(namespace: string) => {
|
|
@@ -59,7 +59,7 @@ export function createCommonTestContextMethods(
|
|
|
59
59
|
if (!orm) {
|
|
60
60
|
throw new Error(`No ORM found for namespace: ${namespace}`);
|
|
61
61
|
}
|
|
62
|
-
return orm as
|
|
62
|
+
return orm as SimpleQueryInterface<TSchema>;
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
}
|