@fragno-dev/test 0.1.4 → 0.1.6

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @fragno-dev/test@0.1.4 build /home/runner/work/fragno/fragno/packages/fragno-test
2
+ > @fragno-dev/test@0.1.6 build /home/runner/work/fragno/fragno/packages/fragno-test
3
3
  > tsdown
4
4
 
5
5
  ℹ tsdown v0.15.11 powered by rolldown v1.0.0-beta.45
@@ -7,9 +7,13 @@
7
7
  ℹ entry: src/index.ts
8
8
  ℹ tsconfig: tsconfig.json
9
9
  ℹ Build start
10
- ℹ dist/index.js 2.63 kB │ gzip: 0.89 kB
11
- ℹ dist/index.js.map 8.17 kB │ gzip: 2.51 kB
12
- ℹ dist/index.d.ts.map 1.28 kB │ gzip: 0.59 kB
13
- ℹ dist/index.d.ts 2.55 kB │ gzip: 0.80 kB
14
- ℹ 4 files, total: 14.63 kB
15
- ✔ Build complete in 3640ms
10
+ ℹ dist/index.js  2.55 kB │ gzip: 0.80 kB
11
+ ℹ dist/adapters.js.map 15.32 kB │ gzip: 3.52 kB
12
+ ℹ dist/index.js.map  8.80 kB │ gzip: 2.50 kB
13
+ ℹ dist/adapters.js  6.36 kB │ gzip: 1.52 kB
14
+ ℹ dist/index.d.ts.map  1.60 kB │ gzip: 0.76 kB
15
+ ℹ dist/adapters.d.ts.map  0.85 kB │ gzip: 0.40 kB
16
+ ℹ dist/index.d.ts  2.98 kB │ gzip: 0.82 kB
17
+ ℹ dist/adapters.d.ts  1.16 kB │ gzip: 0.40 kB
18
+ ℹ 8 files, total: 39.61 kB
19
+ ✔ Build complete in 3001ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @fragno-dev/test
2
2
 
3
+ ## 0.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [ad3e63b]
8
+ - @fragno-dev/db@0.1.10
9
+
10
+ ## 0.1.5
11
+
12
+ ### Patch Changes
13
+
14
+ - 7445a73: feat: Added support for testing using different adapters
15
+ - Updated dependencies [8fcceb6]
16
+ - @fragno-dev/db@0.1.9
17
+
3
18
  ## 0.1.4
4
19
 
5
20
  ### Patch Changes
@@ -0,0 +1,32 @@
1
+ import { Kysely } from "kysely";
2
+ import { drizzle } from "drizzle-orm/pglite";
3
+ import { AnySchema } from "@fragno-dev/db/schema";
4
+ import { DatabaseAdapter } from "@fragno-dev/db/adapters";
5
+
6
+ //#region src/adapters.d.ts
7
+ interface KyselySqliteAdapter {
8
+ type: "kysely-sqlite";
9
+ }
10
+ interface KyselyPgliteAdapter {
11
+ type: "kysely-pglite";
12
+ databasePath?: string;
13
+ }
14
+ interface DrizzlePgliteAdapter {
15
+ type: "drizzle-pglite";
16
+ databasePath?: string;
17
+ }
18
+ type SupportedAdapter = KyselySqliteAdapter | KyselyPgliteAdapter | DrizzlePgliteAdapter;
19
+ type TestContext<T extends SupportedAdapter> = T extends KyselySqliteAdapter | KyselyPgliteAdapter ? {
20
+ readonly kysely: Kysely<any>;
21
+ readonly adapter: DatabaseAdapter<any>;
22
+ resetDatabase: () => Promise<void>;
23
+ cleanup: () => Promise<void>;
24
+ } : T extends DrizzlePgliteAdapter ? {
25
+ readonly drizzle: ReturnType<typeof drizzle<any>>;
26
+ readonly adapter: DatabaseAdapter<any>;
27
+ resetDatabase: () => Promise<void>;
28
+ cleanup: () => Promise<void>;
29
+ } : never;
30
+ //#endregion
31
+ export { DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter, TestContext };
32
+ //# sourceMappingURL=adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.d.ts","names":[],"sources":["../src/adapters.ts"],"sourcesContent":[],"mappings":";;;;;;UAeiB,mBAAA;;AAAjB;AAIiB,UAAA,mBAAA,CAAmB;EAKnB,IAAA,EAAA,eAAA;EAKL,YAAA,CAAA,EAAA,MAAgB;;AAAyB,UALpC,oBAAA,CAKoC;EAAsB,IAAA,EAAA,gBAAA;EAAoB,YAAA,CAAA,EAAA,MAAA;AAG/F;AAAkC,KAHtB,gBAAA,GAAmB,mBAGG,GAHmB,mBAGnB,GAHyC,oBAGzC;AAAoB,KAA1C,WAA0C,CAAA,UAApB,gBAAoB,CAAA,GAAA,CAAA,SAClD,mBADkD,GAElD,mBAFkD,GAAA;EAClD,SAAA,MAAA,EAGmB,MAHnB,CAAA,GAAA,CAAA;EACA,SAAA,OAAA,EAGoB,eAHpB,CAAA,GAAA,CAAA;EAEmB,aAAA,EAAA,GAAA,GAEI,OAFJ,CAAA,IAAA,CAAA;EACC,OAAA,EAAA,GAAA,GAEH,OAFG,CAAA,IAAA,CAAA;CACG,GAGvB,CAHuB,SAGb,oBAHa,GAAA;EACN,SAAA,OAAA,EAIK,UAJL,CAAA,OAIuB,OAJvB,CAAA,GAAA,CAAA,CAAA;EAEjB,SAAA,OAAA,EAGsB,eAHtB,CAAA,GAAA,CAAA;EAAU,aAAA,EAAA,GAAA,GAIe,OAJf,CAAA,IAAA,CAAA;EAE8B,OAAA,EAAA,GAAA,GAGrB,OAHqB,CAAA,IAAA,CAAA;CAAlB,GAAA,KAAA"}
@@ -0,0 +1,204 @@
1
+ import { createRequire } from "node:module";
2
+ import { Kysely } from "kysely";
3
+ import { SQLocalKysely } from "sqlocal/kysely";
4
+ import { KyselyPGlite } from "kysely-pglite";
5
+ import { drizzle } from "drizzle-orm/pglite";
6
+ import { PGlite } from "@electric-sql/pglite";
7
+ import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
8
+ import { DrizzleAdapter } from "@fragno-dev/db/adapters/drizzle";
9
+ import { mkdir, rm, writeFile } from "node:fs/promises";
10
+ import { join } from "node:path";
11
+ import { existsSync } from "node:fs";
12
+
13
+ //#region src/adapters.ts
14
+ /**
15
+ * Create Kysely + SQLite adapter using SQLocalKysely (always in-memory)
16
+ */
17
+ async function createKyselySqliteAdapter(_config, schema, namespace, migrateToVersion) {
18
+ const createDatabase = async () => {
19
+ const { dialect } = new SQLocalKysely(":memory:");
20
+ const kysely$1 = new Kysely({ dialect });
21
+ const adapter$1 = new KyselyAdapter({
22
+ db: kysely$1,
23
+ provider: "sqlite"
24
+ });
25
+ const migrator = adapter$1.createMigrationEngine(schema, namespace);
26
+ await (migrateToVersion ? await migrator.prepareMigrationTo(migrateToVersion, { updateSettings: false }) : await migrator.prepareMigration({ updateSettings: false })).execute();
27
+ return {
28
+ kysely: kysely$1,
29
+ adapter: adapter$1
30
+ };
31
+ };
32
+ let { kysely, adapter } = await createDatabase();
33
+ const resetDatabase = async () => {
34
+ await kysely.destroy();
35
+ const newDb = await createDatabase();
36
+ kysely = newDb.kysely;
37
+ adapter = newDb.adapter;
38
+ };
39
+ const cleanup = async () => {
40
+ await kysely.destroy();
41
+ };
42
+ return {
43
+ testContext: {
44
+ get kysely() {
45
+ return kysely;
46
+ },
47
+ get adapter() {
48
+ return adapter;
49
+ },
50
+ resetDatabase,
51
+ cleanup
52
+ },
53
+ get adapter() {
54
+ return adapter;
55
+ }
56
+ };
57
+ }
58
+ /**
59
+ * Create Kysely + PGLite adapter using kysely-pglite
60
+ */
61
+ async function createKyselyPgliteAdapter(config, schema, namespace, migrateToVersion) {
62
+ const databasePath = config.databasePath;
63
+ const createDatabase = async () => {
64
+ const kyselyPglite$1 = await KyselyPGlite.create(databasePath);
65
+ const kysely$1 = new Kysely({ dialect: kyselyPglite$1.dialect });
66
+ const adapter$1 = new KyselyAdapter({
67
+ db: kysely$1,
68
+ provider: "postgresql"
69
+ });
70
+ const migrator = adapter$1.createMigrationEngine(schema, namespace);
71
+ await (migrateToVersion ? await migrator.prepareMigrationTo(migrateToVersion, { updateSettings: false }) : await migrator.prepareMigration({ updateSettings: false })).execute();
72
+ return {
73
+ kysely: kysely$1,
74
+ adapter: adapter$1,
75
+ kyselyPglite: kyselyPglite$1
76
+ };
77
+ };
78
+ let { kysely, adapter, kyselyPglite } = await createDatabase();
79
+ const resetDatabase = async () => {
80
+ await kysely.destroy();
81
+ try {
82
+ await kyselyPglite.client.close();
83
+ } catch {}
84
+ const newDb = await createDatabase();
85
+ kysely = newDb.kysely;
86
+ adapter = newDb.adapter;
87
+ kyselyPglite = newDb.kyselyPglite;
88
+ };
89
+ const cleanup = async () => {
90
+ await kysely.destroy();
91
+ try {
92
+ await kyselyPglite.client.close();
93
+ } catch {}
94
+ if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) await rm(databasePath, {
95
+ recursive: true,
96
+ force: true
97
+ });
98
+ };
99
+ return {
100
+ testContext: {
101
+ get kysely() {
102
+ return kysely;
103
+ },
104
+ get adapter() {
105
+ return adapter;
106
+ },
107
+ resetDatabase,
108
+ cleanup
109
+ },
110
+ get adapter() {
111
+ return adapter;
112
+ }
113
+ };
114
+ }
115
+ /**
116
+ * Create Drizzle + PGLite adapter using drizzle-orm/pglite
117
+ */
118
+ async function createDrizzlePgliteAdapter(config, schema, namespace, _migrateToVersion) {
119
+ const databasePath = config.databasePath;
120
+ const { generateDrizzleJson, generateMigration } = createRequire(import.meta.url)("drizzle-kit/api");
121
+ const { generateSchema } = await import("@fragno-dev/db/adapters/drizzle/generate");
122
+ const writeAndLoadSchema = async () => {
123
+ const testDir = join(import.meta.dirname, "_generated", "drizzle-test");
124
+ await mkdir(testDir, { recursive: true }).catch(() => {});
125
+ const schemaFilePath = join(testDir, `test-schema-${Date.now()}-${Math.random().toString(36).slice(2, 9)}.ts`);
126
+ await writeFile(schemaFilePath, generateSchema([{
127
+ namespace: namespace ?? "",
128
+ schema
129
+ }], "postgresql"), "utf-8");
130
+ const schemaModule = await import(`${schemaFilePath}?t=${Date.now()}`);
131
+ const cleanup$1 = async () => {
132
+ await rm(testDir, {
133
+ recursive: true,
134
+ force: true
135
+ });
136
+ };
137
+ return {
138
+ schemaModule,
139
+ cleanup: cleanup$1
140
+ };
141
+ };
142
+ const createDatabase = async () => {
143
+ const { schemaModule, cleanup: cleanup$1 } = await writeAndLoadSchema();
144
+ const pglite$1 = new PGlite(databasePath);
145
+ const db = drizzle(pglite$1, { schema: schemaModule });
146
+ const migrationStatements = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(schemaModule));
147
+ for (const statement of migrationStatements) await db.execute(statement);
148
+ return {
149
+ drizzle: db,
150
+ adapter: new DrizzleAdapter({
151
+ db: () => db,
152
+ provider: "postgresql"
153
+ }),
154
+ pglite: pglite$1,
155
+ cleanup: cleanup$1
156
+ };
157
+ };
158
+ let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup } = await createDatabase();
159
+ const resetDatabase = async () => {
160
+ await pglite.close();
161
+ await schemaCleanup();
162
+ const newDb = await createDatabase();
163
+ drizzleDb = newDb.drizzle;
164
+ adapter = newDb.adapter;
165
+ pglite = newDb.pglite;
166
+ schemaCleanup = newDb.cleanup;
167
+ };
168
+ const cleanup = async () => {
169
+ await pglite.close();
170
+ await schemaCleanup();
171
+ if (databasePath && databasePath !== ":memory:" && existsSync(databasePath)) await rm(databasePath, {
172
+ recursive: true,
173
+ force: true
174
+ });
175
+ };
176
+ return {
177
+ testContext: {
178
+ get drizzle() {
179
+ return drizzleDb;
180
+ },
181
+ get adapter() {
182
+ return adapter;
183
+ },
184
+ resetDatabase,
185
+ cleanup
186
+ },
187
+ get adapter() {
188
+ return adapter;
189
+ }
190
+ };
191
+ }
192
+ /**
193
+ * Create adapter based on configuration
194
+ */
195
+ async function createAdapter(adapterConfig, schema, namespace, migrateToVersion) {
196
+ if (adapterConfig.type === "kysely-sqlite") return createKyselySqliteAdapter(adapterConfig, schema, namespace, migrateToVersion);
197
+ else if (adapterConfig.type === "kysely-pglite") return createKyselyPgliteAdapter(adapterConfig, schema, namespace, migrateToVersion);
198
+ else if (adapterConfig.type === "drizzle-pglite") return createDrizzlePgliteAdapter(adapterConfig, schema, namespace, migrateToVersion);
199
+ throw new Error(`Unsupported adapter type: ${adapterConfig.type}`);
200
+ }
201
+
202
+ //#endregion
203
+ export { createAdapter };
204
+ //# sourceMappingURL=adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.js","names":["kysely","adapter","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 { createRequire } from \"node:module\";\nimport { mkdir, writeFile, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\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// Conditional return types based on adapter\nexport type TestContext<T extends SupportedAdapter> = T extends\n | KyselySqliteAdapter\n | KyselyPgliteAdapter\n ? {\n readonly kysely: Kysely<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : T extends DrizzlePgliteAdapter\n ? {\n readonly drizzle: ReturnType<typeof drizzle<any>>; // eslint-disable-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>; // eslint-disable-line @typescript-eslint/no-explicit-any\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n }\n : never;\n\n// Factory function return type\ninterface AdapterFactoryResult<T extends SupportedAdapter> {\n testContext: TestContext<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 */\nexport async function createKyselySqliteAdapter(\n _config: KyselySqliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselySqliteAdapter>> {\n // Helper to create a new database instance and run migrations\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\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 return { kysely, adapter };\n };\n\n // Create initial database\n let { kysely, adapter } = await createDatabase();\n\n // Reset database function - creates a fresh in-memory database and re-runs migrations\n const resetDatabase = async () => {\n // Destroy the old Kysely instance\n await kysely.destroy();\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\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 return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\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 */\nexport async function createKyselyPgliteAdapter(\n config: KyselyPgliteAdapter,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<KyselyPgliteAdapter>> {\n const databasePath = config.databasePath;\n\n // Helper to create a new database instance and run migrations\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\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 return { kysely, adapter, kyselyPglite };\n };\n\n // Create initial database\n let { kysely, adapter, kyselyPglite } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances\n await kysely.destroy();\n\n try {\n await kyselyPglite.client.close();\n } catch {\n // Ignore if already closed\n }\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n kyselyPglite = newDb.kyselyPglite;\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 return {\n testContext: {\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\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 */\nexport async function createDrizzlePgliteAdapter(\n config: DrizzlePgliteAdapter,\n schema: AnySchema,\n namespace: string,\n _migrateToVersion?: number,\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 // Generate and write the Drizzle schema to file\n const drizzleSchemaTs = generateSchema([{ namespace: namespace ?? \"\", schema }], \"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\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 return { drizzle: db, adapter, pglite, cleanup };\n };\n\n // Create initial database\n let { drizzle: drizzleDb, adapter, pglite, cleanup: schemaCleanup } = await createDatabase();\n\n // Reset database function - creates a fresh database and re-runs migrations\n const resetDatabase = async () => {\n // Close the old instances and cleanup\n await pglite.close();\n await schemaCleanup();\n\n // Create a new database instance\n const newDb = await createDatabase();\n drizzleDb = newDb.drizzle;\n adapter = newDb.adapter;\n pglite = newDb.pglite;\n schemaCleanup = newDb.cleanup;\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 return {\n testContext: {\n get drizzle() {\n return drizzleDb;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n cleanup,\n },\n get adapter() {\n return adapter;\n },\n };\n}\n\n/**\n * Create adapter based on configuration\n */\nexport async function createAdapter<T extends SupportedAdapter>(\n adapterConfig: T,\n schema: AnySchema,\n namespace: string,\n migrateToVersion?: number,\n): Promise<AdapterFactoryResult<T>> {\n if (adapterConfig.type === \"kysely-sqlite\") {\n return createKyselySqliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"kysely-pglite\") {\n return createKyselyPgliteAdapter(adapterConfig, schema, namespace, migrateToVersion) as Promise<\n AdapterFactoryResult<T>\n >;\n } else if (adapterConfig.type === \"drizzle-pglite\") {\n return createDrizzlePgliteAdapter(\n adapterConfig,\n schema,\n namespace,\n migrateToVersion,\n ) 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/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { Kysely } from "kysely";
1
+ import { DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter, TestContext } from "./adapters.js";
2
2
  import { CreateFragmentForTestOptions, CreateFragmentForTestOptions as CreateFragmentForTestOptions$1, FragmentForTest, FragmentForTest as FragmentForTest$1, InitRoutesOverrides, RouteHandlerInputOptions, TestResponse, createFragmentForTest } from "@fragno-dev/core/test";
3
3
  import { AnySchema } from "@fragno-dev/db/schema";
4
- import { DatabaseAdapter } from "@fragno-dev/db/adapters";
5
4
  import { FragnoPublicConfig } from "@fragno-dev/core/api/fragment-instantiation";
6
5
  import { FragmentDefinition } from "@fragno-dev/core/api/fragment-builder";
7
6
 
@@ -10,27 +9,29 @@ import { FragmentDefinition } from "@fragno-dev/core/api/fragment-builder";
10
9
  /**
11
10
  * Options for creating a database fragment for testing
12
11
  */
13
- interface CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig> extends Omit<CreateFragmentForTestOptions$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>, "config"> {
14
- databasePath?: string;
12
+ interface CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig, TAdapter extends SupportedAdapter> extends Omit<CreateFragmentForTestOptions$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>, "config"> {
13
+ adapter: TAdapter;
15
14
  migrateToVersion?: number;
16
15
  config?: TConfig;
17
16
  }
18
17
  /**
19
- * Extended fragment test instance with database adapter and Kysely instance
18
+ * Result of creating a database fragment for testing
19
+ * All properties are getters that return the current fragment instance
20
20
  */
21
- interface DatabaseFragmentForTest<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig> extends FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions> {
22
- kysely: Kysely<any>;
23
- adapter: DatabaseAdapter<any>;
24
- /**
25
- * Resets the database by creating a fresh in-memory database instance and re-running migrations.
26
- * After calling this, you should re-initialize any routes to ensure they use the new database instance.
27
- */
28
- resetDatabase: () => Promise<void>;
21
+ interface DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext extends Record<string, unknown>, TOptions extends FragnoPublicConfig, TAdapter extends SupportedAdapter> {
22
+ readonly fragment: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>;
23
+ readonly services: TServices;
24
+ readonly initRoutes: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>["initRoutes"];
25
+ readonly handler: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions>["handler"];
26
+ readonly config: TConfig;
27
+ readonly deps: TDeps;
28
+ readonly additionalContext: TAdditionalContext;
29
+ test: TestContext<TAdapter>;
29
30
  }
30
- declare function createDatabaseFragmentForTest<const TConfig, const TDeps, const TServices extends Record<string, unknown>, const TAdditionalContext extends Record<string, unknown>, const TOptions extends FragnoPublicConfig, const TSchema extends AnySchema>(fragmentBuilder: {
31
+ declare function createDatabaseFragmentForTest<const TConfig, const TDeps, const TServices extends Record<string, unknown>, const TAdditionalContext extends Record<string, unknown>, const TOptions extends FragnoPublicConfig, const TSchema extends AnySchema, const TAdapter extends SupportedAdapter>(fragmentBuilder: {
31
32
  definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
32
33
  $requiredOptions: TOptions;
33
- }, options?: CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>): Promise<DatabaseFragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions>>;
34
+ }, options: CreateDatabaseFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>): Promise<DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>>;
34
35
  //#endregion
35
- export { CreateDatabaseFragmentForTestOptions, type CreateFragmentForTestOptions, DatabaseFragmentForTest, type FragmentForTest, type InitRoutesOverrides, type RouteHandlerInputOptions, type TestResponse, createDatabaseFragmentForTest, createFragmentForTest };
36
+ export { CreateDatabaseFragmentForTestOptions, type CreateFragmentForTestOptions, DatabaseFragmentTestResult, type DrizzlePgliteAdapter, type FragmentForTest, type InitRoutesOverrides, type KyselyPgliteAdapter, type KyselySqliteAdapter, type RouteHandlerInputOptions, type SupportedAdapter, type TestContext, type TestResponse, createDatabaseFragmentForTest, createFragmentForTest };
36
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AA0BA;;AAKmB,UALF,oCAKE,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BADU,MACV,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBAAA,kBAAA,CAAA,SACT,IADS,CAEf,8BAFe,CAEc,OAFd,EAEuB,KAFvB,EAE8B,SAF9B,EAEyC,kBAFzC,EAE6D,QAF7D,CAAA,EAAA,QAAA,CAAA,CAAA;EAEc,YAAA,CAAA,EAAA,MAAA;EAAS,gBAAA,CAAA,EAAA,MAAA;EAAO,MAAA,CAAA,EAKtC,OALsC;;;;;AADvC,UAYO,uBAZP,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BAgBmB,MAhBnB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBAiBS,kBAjBT,CAAA,SAkBA,iBAlBA,CAkBgB,OAlBhB,EAkByB,KAlBzB,EAkBgC,SAlBhC,EAkB2C,kBAlB3C,EAkB+D,QAlB/D,CAAA,CAAA;EAAI,MAAA,EAoBJ,MApBI,CAAA,GAAA,CAAA;EAYG,OAAA,EAUN,eAVM,CAAA,GAAuB,CAAA;EAIX;;;;EAEa,aAAA,EAAA,GAAA,GASnB,OATmB,CAAA,IAAA,CAAA;;AAA+B,iBAYnD,6BAZmD,CAAA,aAAA,EAAA,WAAA,EAAA,wBAe/C,MAf+C,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iCAgBtC,MAhBsC,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,uBAiBhD,kBAjBgD,EAAA,sBAkBjD,SAlBiD,CAAA,CAAA,eAAA,EAAA;EAE/D,UAAA,EAmBM,kBAnBN,CAmByB,OAnBzB,EAmBkC,KAnBlC,EAmByC,SAnBzC,EAmBoD,kBAnBpD,CAAA;EAEC,gBAAA,EAkBW,QAlBX;CAKY,EAAA,OAAA,CAAA,EAeX,oCAfW,CAgBnB,OAhBmB,EAiBnB,KAjBmB,EAkBnB,SAlBmB,EAmBnB,kBAnBmB,EAoBnB,QApBmB,CAAA,CAAA,EAsBpB,OAtBoB,CAsBZ,uBAtBY,CAsBY,OAtBZ,EAsBqB,KAtBrB,EAsB4B,SAtB5B,EAsBuC,kBAtBvC,EAsB2D,QAtB3D,CAAA,CAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuCA;;;AAMmB,UANF,oCAME,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BAFU,MAEV,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBADA,kBACA,EAAA,iBAAA,gBAAA,CAAA,SACT,IADS,CAEf,8BAFe,CAEc,OAFd,EAEuB,KAFvB,EAE8B,SAF9B,EAEyC,kBAFzC,EAE6D,QAF7D,CAAA,EAAA,QAAA,CAAA,CAAA;EAEc,OAAA,EAGtB,QAHsB;EAAS,gBAAA,CAAA,EAAA,MAAA;EAAO,MAAA,CAAA,EAKtC,OALsC;;;;;;AADvC,UAaO,0BAbP,CAAA,OAAA,EAAA,KAAA,EAAA,SAAA,EAAA,2BAiBmB,MAjBnB,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iBAkBS,kBAlBT,EAAA,iBAmBS,gBAnBT,CAAA,CAAA;EAAI,SAAA,QAAA,EAqBO,iBArBP,CAqBuB,OArBvB,EAqBgC,KArBhC,EAqBuC,SArBvC,EAqBkD,kBArBlD,EAqBsE,QArBtE,CAAA;EAaG,SAAA,QAAA,EASI,SATJ;EAIY,SAAA,UAAA,EAMN,iBANM,CAOzB,OAPyB,EAQzB,KARyB,EASzB,SATyB,EAUzB,kBAVyB,EAWzB,QAXyB,CAAA,CAAA,YAAA,CAAA;EACV,SAAA,OAAA,EAYC,iBAZD,CAaf,OAbe,EAcf,KAde,EAef,SAfe,EAgBf,kBAhBe,EAiBf,QAjBe,CAAA,CAAA,SAAA,CAAA;EACA,SAAA,MAAA,EAkBA,OAlBA;EAEkB,SAAA,IAAA,EAiBpB,KAjBoB;EAAS,SAAA,iBAAA,EAkBhB,kBAlBgB;EAAO,IAAA,EAmB7C,WAnB6C,CAmBjC,QAnBiC,CAAA;;AAA+B,iBAsB9D,6BAtB8D,CAAA,aAAA,EAAA,WAAA,EAAA,wBAyB1D,MAzB0D,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,iCA0BjD,MA1BiD,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,uBA2B3D,kBA3B2D,EAAA,sBA4B5D,SA5B4D,EAAA,uBA6B3D,gBA7B2D,CAAA,CAAA,eAAA,EAAA;EAA/D,UAAA,EAgCL,kBAhCK,CAgCc,OAhCd,EAgCuB,KAhCvB,EAgC8B,SAhC9B,EAgCyC,kBAhCzC,CAAA;EACA,gBAAA,EAgCC,QAhCD;CAEjB,EAAA,OAAA,EAgCO,oCAhCP,CAiCA,OAjCA,EAkCA,KAlCA,EAmCA,SAnCA,EAoCA,kBApCA,EAqCA,QArCA,EAsCA,QAtCA,CAAA,CAAA,EAwCD,OAxCC,CAyCF,0BAzCE,CAyCyB,OAzCzB,EAyCkC,KAzClC,EAyCyC,SAzCzC,EAyCoD,kBAzCpD,EAyCwE,QAzCxE,EAyCkF,QAzClF,CAAA,CAAA"}
package/dist/index.js CHANGED
@@ -1,30 +1,14 @@
1
- import { Kysely } from "kysely";
2
- import { SQLocalKysely } from "sqlocal/kysely";
3
- import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
1
+ import { createAdapter } from "./adapters.js";
4
2
  import { createFragmentForTest, createFragmentForTest as createFragmentForTest$1 } from "@fragno-dev/core/test";
5
3
 
6
4
  //#region src/index.ts
7
5
  async function createDatabaseFragmentForTest(fragmentBuilder, options) {
8
- const { databasePath = ":memory:", migrateToVersion, config, options: fragmentOptions, deps, services, additionalContext } = options ?? {};
6
+ const { adapter: adapterConfig, migrateToVersion, config, options: fragmentOptions, deps, services, additionalContext } = options;
9
7
  const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext;
10
8
  const schema = fragmentAdditionalContext?.databaseSchema;
11
9
  const namespace = fragmentAdditionalContext?.databaseNamespace ?? "";
12
10
  if (!schema) throw new Error(`Fragment '${fragmentBuilder.definition.name}' does not have a database schema. Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`);
13
- const createDatabase = async () => {
14
- const { dialect } = new SQLocalKysely(databasePath);
15
- const kysely$1 = new Kysely({ dialect });
16
- const adapter$1 = new KyselyAdapter({
17
- db: kysely$1,
18
- provider: "sqlite"
19
- });
20
- const migrator = adapter$1.createMigrationEngine(schema, namespace);
21
- await (migrateToVersion ? await migrator.prepareMigrationTo(migrateToVersion, { updateSettings: false }) : await migrator.prepareMigration({ updateSettings: false })).execute();
22
- return {
23
- kysely: kysely$1,
24
- adapter: adapter$1
25
- };
26
- };
27
- let { kysely, adapter } = await createDatabase();
11
+ const { testContext: originalTestContext, adapter } = await createAdapter(adapterConfig, schema, namespace, migrateToVersion);
28
12
  let mergedOptions = {
29
13
  ...fragmentOptions,
30
14
  databaseAdapter: adapter
@@ -36,24 +20,11 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
36
20
  services,
37
21
  additionalContext
38
22
  });
39
- const resetDatabase = async () => {
40
- await kysely.destroy();
41
- const newDb = await createDatabase();
42
- kysely = newDb.kysely;
43
- adapter = newDb.adapter;
44
- mergedOptions = {
45
- ...fragmentOptions,
46
- databaseAdapter: adapter
47
- };
48
- fragment = createFragmentForTest$1(fragmentBuilder, {
49
- config,
50
- options: mergedOptions,
51
- deps,
52
- services,
53
- additionalContext
54
- });
55
- };
23
+ const originalResetDatabase = originalTestContext.resetDatabase;
56
24
  return {
25
+ get fragment() {
26
+ return fragment;
27
+ },
57
28
  get services() {
58
29
  return fragment.services;
59
30
  },
@@ -72,13 +43,49 @@ async function createDatabaseFragmentForTest(fragmentBuilder, options) {
72
43
  get additionalContext() {
73
44
  return fragment.additionalContext;
74
45
  },
75
- get kysely() {
76
- return kysely;
77
- },
78
- get adapter() {
79
- return adapter;
80
- },
81
- resetDatabase
46
+ test: Object.create(originalTestContext, {
47
+ kysely: {
48
+ get() {
49
+ return originalTestContext.kysely;
50
+ },
51
+ enumerable: true
52
+ },
53
+ drizzle: {
54
+ get() {
55
+ return originalTestContext.drizzle;
56
+ },
57
+ enumerable: true
58
+ },
59
+ adapter: {
60
+ get() {
61
+ return originalTestContext.adapter;
62
+ },
63
+ enumerable: true
64
+ },
65
+ resetDatabase: {
66
+ value: async () => {
67
+ await originalResetDatabase();
68
+ mergedOptions = {
69
+ ...fragmentOptions,
70
+ databaseAdapter: originalTestContext.adapter
71
+ };
72
+ fragment = createFragmentForTest$1(fragmentBuilder, {
73
+ config,
74
+ options: mergedOptions,
75
+ deps,
76
+ services,
77
+ additionalContext
78
+ });
79
+ },
80
+ enumerable: true
81
+ },
82
+ cleanup: {
83
+ value: async () => {
84
+ await originalTestContext.cleanup();
85
+ },
86
+ enumerable: true
87
+ }
88
+ })
82
89
  };
83
90
  }
84
91
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["kysely","adapter","createFragmentForTest"],"sources":["../src/index.ts"],"sourcesContent":["import { Kysely } from \"kysely\";\nimport { SQLocalKysely } from \"sqlocal/kysely\";\nimport { KyselyAdapter } from \"@fragno-dev/db/adapters/kysely\";\nimport type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db/adapters\";\nimport {\n createFragmentForTest,\n type FragmentForTest,\n type CreateFragmentForTestOptions,\n} from \"@fragno-dev/core/test\";\nimport type { FragnoPublicConfig } from \"@fragno-dev/core/api/fragment-instantiation\";\nimport type { FragmentDefinition } from \"@fragno-dev/core/api/fragment-builder\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type TestResponse,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n type FragmentForTest,\n type InitRoutesOverrides,\n} from \"@fragno-dev/core/test\";\n\n/**\n * Options for creating a database fragment for testing\n */\nexport interface CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n> extends Omit<\n CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,\n \"config\"\n > {\n databasePath?: string;\n migrateToVersion?: number;\n config?: TConfig;\n}\n\n/**\n * Extended fragment test instance with database adapter and Kysely instance\n */\nexport interface DatabaseFragmentForTest<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n> extends FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n kysely: Kysely<any>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n /**\n * Resets the database by creating a fresh in-memory database instance and re-running migrations.\n * After calling this, you should re-initialize any routes to ensure they use the new database instance.\n */\n resetDatabase: () => Promise<void>;\n}\n\nexport async function createDatabaseFragmentForTest<\n const TConfig,\n const TDeps,\n const TServices extends Record<string, unknown>,\n const TAdditionalContext extends Record<string, unknown>,\n const TOptions extends FragnoPublicConfig,\n const TSchema extends AnySchema,\n>(\n fragmentBuilder: {\n definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;\n $requiredOptions: TOptions;\n },\n options?: CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions\n >,\n): Promise<DatabaseFragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions>> {\n const {\n databasePath = \":memory:\",\n migrateToVersion,\n config,\n options: fragmentOptions,\n deps,\n services,\n additionalContext,\n } = options ?? {};\n\n // Get schema and namespace from fragment definition's additionalContext\n // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext as any;\n const schema = fragmentAdditionalContext?.databaseSchema as TSchema | undefined;\n const namespace = (fragmentAdditionalContext?.databaseNamespace as string | undefined) ?? \"\";\n\n if (!schema) {\n throw new Error(\n `Fragment '${fragmentBuilder.definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,\n );\n }\n\n // Helper to create a new database instance and run migrations\n const createDatabase = async () => {\n // Create SQLocalKysely instance\n const { dialect } = new SQLocalKysely(databasePath);\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\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 return { kysely, adapter };\n };\n\n // Create initial database\n let { kysely, adapter } = await createDatabase();\n\n // Create fragment with database adapter in options\n // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions\n // The user's TOptions is constrained to FragnoPublicConfig (or a subtype), which we extend with databaseAdapter\n let mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: adapter,\n } as unknown as TOptions;\n\n // Safe cast: If config is not provided, we pass undefined as TConfig.\n // The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined\n // or if the fragment doesn't use config in its dependencies function, this will work correctly.\n let fragment = createFragmentForTest(fragmentBuilder, {\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n\n // Reset database function - creates a fresh in-memory database and re-runs migrations\n const resetDatabase = async () => {\n // Destroy the old Kysely instance\n await kysely.destroy();\n\n // Create a new database instance\n const newDb = await createDatabase();\n kysely = newDb.kysely;\n adapter = newDb.adapter;\n\n // Recreate the fragment with the new adapter\n mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: adapter,\n } as unknown as TOptions;\n\n fragment = createFragmentForTest(fragmentBuilder, {\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n };\n\n return {\n get services() {\n return fragment.services;\n },\n get initRoutes() {\n return fragment.initRoutes;\n },\n get handler() {\n return fragment.handler;\n },\n get config() {\n return fragment.config;\n },\n get deps() {\n return fragment.deps;\n },\n get additionalContext() {\n return fragment.additionalContext;\n },\n get kysely() {\n return kysely;\n },\n get adapter() {\n return adapter;\n },\n resetDatabase,\n };\n}\n"],"mappings":";;;;;;AA8DA,eAAsB,8BAQpB,iBAIA,SAO2F;CAC3F,MAAM,EACJ,eAAe,YACf,kBACA,QACA,SAAS,iBACT,MACA,UACA,sBACE,WAAW,EAAE;CAKjB,MAAM,4BAA4B,gBAAgB,WAAW;CAC7D,MAAM,SAAS,2BAA2B;CAC1C,MAAM,YAAa,2BAA2B,qBAA4C;AAE1F,KAAI,CAAC,OACH,OAAM,IAAI,MACR,aAAa,gBAAgB,WAAW,KAAK,8GAE9C;CAIH,MAAM,iBAAiB,YAAY;EAEjC,MAAM,EAAE,YAAY,IAAI,cAAc,aAAa;EAEnD,MAAMA,WAAS,IAAI,OAAY,EAC7B,SACD,CAAC;EAGF,MAAMC,YAAU,IAAI,cAAc;GAChC,IAAID;GACJ,UAAU;GACX,CAAC;EAGF,MAAM,WAAWC,UAAQ,sBAAsB,QAAQ,UAAU;AAQjE,SAP0B,mBACtB,MAAM,SAAS,mBAAmB,kBAAkB,EAClD,gBAAgB,OACjB,CAAC,GACF,MAAM,SAAS,iBAAiB,EAC9B,gBAAgB,OACjB,CAAC,EACkB,SAAS;AAEjC,SAAO;GAAE;GAAQ;GAAS;;CAI5B,IAAI,EAAE,QAAQ,YAAY,MAAM,gBAAgB;CAKhD,IAAI,gBAAgB;EAClB,GAAG;EACH,iBAAiB;EAClB;CAKD,IAAI,WAAWC,wBAAsB,iBAAiB;EAC5C;EACR,SAAS;EACT;EACA;EACA;EACD,CAAC;CAGF,MAAM,gBAAgB,YAAY;AAEhC,QAAM,OAAO,SAAS;EAGtB,MAAM,QAAQ,MAAM,gBAAgB;AACpC,WAAS,MAAM;AACf,YAAU,MAAM;AAGhB,kBAAgB;GACd,GAAG;GACH,iBAAiB;GAClB;AAED,aAAWA,wBAAsB,iBAAiB;GACxC;GACR,SAAS;GACT;GACA;GACA;GACD,CAAC;;AAGJ,QAAO;EACL,IAAI,WAAW;AACb,UAAO,SAAS;;EAElB,IAAI,aAAa;AACf,UAAO,SAAS;;EAElB,IAAI,UAAU;AACZ,UAAO,SAAS;;EAElB,IAAI,SAAS;AACX,UAAO,SAAS;;EAElB,IAAI,OAAO;AACT,UAAO,SAAS;;EAElB,IAAI,oBAAoB;AACtB,UAAO,SAAS;;EAElB,IAAI,SAAS;AACX,UAAO;;EAET,IAAI,UAAU;AACZ,UAAO;;EAET;EACD"}
1
+ {"version":3,"file":"index.js","names":["createFragmentForTest"],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport {\n createFragmentForTest,\n type FragmentForTest,\n type CreateFragmentForTestOptions,\n} from \"@fragno-dev/core/test\";\nimport type { FragnoPublicConfig } from \"@fragno-dev/core/api/fragment-instantiation\";\nimport type { FragmentDefinition } from \"@fragno-dev/core/api/fragment-builder\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type TestContext,\n type KyselySqliteAdapter,\n type KyselyPgliteAdapter,\n type DrizzlePgliteAdapter,\n} from \"./adapters\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type TestResponse,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n type FragmentForTest,\n type InitRoutesOverrides,\n} from \"@fragno-dev/core/test\";\n\n// Re-export adapter types\nexport type {\n SupportedAdapter,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n TestContext,\n} from \"./adapters\";\n\n/**\n * Options for creating a database fragment for testing\n */\nexport interface CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n TAdapter extends SupportedAdapter,\n> extends Omit<\n CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,\n \"config\"\n > {\n adapter: TAdapter;\n migrateToVersion?: number;\n config?: TConfig;\n}\n\n/**\n * Result of creating a database fragment for testing\n * All properties are getters that return the current fragment instance\n */\nexport interface DatabaseFragmentTestResult<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext extends Record<string, unknown>,\n TOptions extends FragnoPublicConfig,\n TAdapter extends SupportedAdapter,\n> {\n readonly fragment: FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions>;\n readonly services: TServices;\n readonly initRoutes: FragmentForTest<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions\n >[\"initRoutes\"];\n readonly handler: FragmentForTest<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions\n >[\"handler\"];\n readonly config: TConfig;\n readonly deps: TDeps;\n readonly additionalContext: TAdditionalContext;\n test: TestContext<TAdapter>;\n}\n\nexport async function createDatabaseFragmentForTest<\n const TConfig,\n const TDeps,\n const TServices extends Record<string, unknown>,\n const TAdditionalContext extends Record<string, unknown>,\n const TOptions extends FragnoPublicConfig,\n const TSchema extends AnySchema,\n const TAdapter extends SupportedAdapter,\n>(\n fragmentBuilder: {\n definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;\n $requiredOptions: TOptions;\n },\n options: CreateDatabaseFragmentForTestOptions<\n TConfig,\n TDeps,\n TServices,\n TAdditionalContext,\n TOptions,\n TAdapter\n >,\n): Promise<\n DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>\n> {\n const {\n adapter: adapterConfig,\n migrateToVersion,\n config,\n options: fragmentOptions,\n deps,\n services,\n additionalContext,\n } = options;\n\n // Get schema and namespace from fragment definition's additionalContext\n // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext as any;\n const schema = fragmentAdditionalContext?.databaseSchema as TSchema | undefined;\n const namespace = (fragmentAdditionalContext?.databaseNamespace as string | undefined) ?? \"\";\n\n if (!schema) {\n throw new Error(\n `Fragment '${fragmentBuilder.definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,\n );\n }\n\n // Create adapter using the factory\n const { testContext: originalTestContext, adapter } = await createAdapter(\n adapterConfig,\n schema,\n namespace,\n migrateToVersion,\n );\n\n // Create fragment with database adapter in options\n // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions\n // The user's TOptions is constrained to FragnoPublicConfig (or a subtype), which we extend with databaseAdapter\n let mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: adapter,\n } as unknown as TOptions;\n\n // Safe cast: If config is not provided, we pass undefined as TConfig.\n // The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined\n // or if the fragment doesn't use config in its dependencies function, this will work correctly.\n let fragment = createFragmentForTest(fragmentBuilder, {\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n\n // Wrap resetDatabase to also recreate the fragment with the new adapter\n const originalResetDatabase = originalTestContext.resetDatabase;\n\n // Create test context with getters that always return current values\n // We need to cast to any to avoid TypeScript errors when accessing kysely/drizzle properties\n // that may not exist depending on the adapter type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const testContext: any = Object.create(originalTestContext, {\n kysely: {\n get() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalTestContext as any).kysely;\n },\n enumerable: true,\n },\n drizzle: {\n get() {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (originalTestContext as any).drizzle;\n },\n enumerable: true,\n },\n adapter: {\n get() {\n return originalTestContext.adapter;\n },\n enumerable: true,\n },\n resetDatabase: {\n value: async () => {\n // Call the original reset database function\n await originalResetDatabase();\n\n // Recreate the fragment with the new adapter (which has been updated by the factory's resetDatabase)\n mergedOptions = {\n ...fragmentOptions,\n databaseAdapter: originalTestContext.adapter,\n } as unknown as TOptions;\n\n fragment = createFragmentForTest(fragmentBuilder, {\n config: config as TConfig,\n options: mergedOptions,\n deps,\n services,\n additionalContext,\n });\n },\n enumerable: true,\n },\n cleanup: {\n value: async () => {\n await originalTestContext.cleanup();\n },\n enumerable: true,\n },\n });\n\n // Return an object with getters for fragment properties so they always reference the current fragment\n return {\n get fragment() {\n return fragment;\n },\n get services() {\n return fragment.services;\n },\n get initRoutes() {\n return fragment.initRoutes;\n },\n get handler() {\n return fragment.handler;\n },\n get config() {\n return fragment.config;\n },\n get deps() {\n return fragment.deps;\n },\n get additionalContext() {\n return fragment.additionalContext;\n },\n test: testContext,\n };\n}\n"],"mappings":";;;;AAyFA,eAAsB,8BASpB,iBAIA,SAUA;CACA,MAAM,EACJ,SAAS,eACT,kBACA,QACA,SAAS,iBACT,MACA,UACA,sBACE;CAKJ,MAAM,4BAA4B,gBAAgB,WAAW;CAC7D,MAAM,SAAS,2BAA2B;CAC1C,MAAM,YAAa,2BAA2B,qBAA4C;AAE1F,KAAI,CAAC,OACH,OAAM,IAAI,MACR,aAAa,gBAAgB,WAAW,KAAK,8GAE9C;CAIH,MAAM,EAAE,aAAa,qBAAqB,YAAY,MAAM,cAC1D,eACA,QACA,WACA,iBACD;CAKD,IAAI,gBAAgB;EAClB,GAAG;EACH,iBAAiB;EAClB;CAKD,IAAI,WAAWA,wBAAsB,iBAAiB;EAC5C;EACR,SAAS;EACT;EACA;EACA;EACD,CAAC;CAGF,MAAM,wBAAwB,oBAAoB;AAyDlD,QAAO;EACL,IAAI,WAAW;AACb,UAAO;;EAET,IAAI,WAAW;AACb,UAAO,SAAS;;EAElB,IAAI,aAAa;AACf,UAAO,SAAS;;EAElB,IAAI,UAAU;AACZ,UAAO,SAAS;;EAElB,IAAI,SAAS;AACX,UAAO,SAAS;;EAElB,IAAI,OAAO;AACT,UAAO,SAAS;;EAElB,IAAI,oBAAoB;AACtB,UAAO,SAAS;;EAElB,MAzEuB,OAAO,OAAO,qBAAqB;GAC1D,QAAQ;IACN,MAAM;AAEJ,YAAQ,oBAA4B;;IAEtC,YAAY;IACb;GACD,SAAS;IACP,MAAM;AAEJ,YAAQ,oBAA4B;;IAEtC,YAAY;IACb;GACD,SAAS;IACP,MAAM;AACJ,YAAO,oBAAoB;;IAE7B,YAAY;IACb;GACD,eAAe;IACb,OAAO,YAAY;AAEjB,WAAM,uBAAuB;AAG7B,qBAAgB;MACd,GAAG;MACH,iBAAiB,oBAAoB;MACtC;AAED,gBAAWA,wBAAsB,iBAAiB;MACxC;MACR,SAAS;MACT;MACA;MACA;MACD,CAAC;;IAEJ,YAAY;IACb;GACD,SAAS;IACP,OAAO,YAAY;AACjB,WAAM,oBAAoB,SAAS;;IAErC,YAAY;IACb;GACF,CAAC;EA0BD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fragno-dev/test",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -16,15 +16,40 @@
16
16
  "kysely": "^0.28.7",
17
17
  "sqlocal": "^0.15.2",
18
18
  "@fragno-dev/core": "0.1.3",
19
- "@fragno-dev/db": "0.1.8"
19
+ "@fragno-dev/db": "0.1.10"
20
+ },
21
+ "peerDependencies": {
22
+ "@electric-sql/pglite": "^0.3.11",
23
+ "drizzle-kit": "^0.30.3",
24
+ "drizzle-orm": "^0.44.7",
25
+ "kysely-pglite": "^0.6.1"
26
+ },
27
+ "peerDependenciesMeta": {
28
+ "@electric-sql/pglite": {
29
+ "optional": true
30
+ },
31
+ "drizzle-kit": {
32
+ "optional": true
33
+ },
34
+ "drizzle-orm": {
35
+ "optional": true
36
+ },
37
+ "kysely-pglite": {
38
+ "optional": true
39
+ }
20
40
  },
21
41
  "devDependencies": {
42
+ "@electric-sql/pglite": "^0.3.11",
43
+ "@libsql/client": "^0.14.0",
22
44
  "@types/node": "^22",
23
45
  "@vitest/coverage-istanbul": "^3.2.4",
46
+ "drizzle-kit": "^0.30.3",
47
+ "drizzle-orm": "^0.44.7",
48
+ "kysely-pglite": "^0.6.1",
24
49
  "vitest": "^3.2.4",
25
50
  "zod": "^4.1.12",
26
- "@fragno-private/typescript-config": "0.0.1",
27
- "@fragno-private/vitest-config": "0.0.0"
51
+ "@fragno-private/vitest-config": "0.0.0",
52
+ "@fragno-private/typescript-config": "0.0.1"
28
53
  },
29
54
  "repository": {
30
55
  "type": "git",