@geekmidas/testkit 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{Factory-C6W78ulZ.d.mts → Factory-CVR3GdkW.d.mts} +2 -2
  3. package/dist/{Factory-C6W78ulZ.d.mts.map → Factory-CVR3GdkW.d.mts.map} +1 -1
  4. package/dist/{Factory-D28yjUj5.d.cts → Factory-Cwzho3c8.d.cts} +2 -2
  5. package/dist/{Factory-D28yjUj5.d.cts.map → Factory-Cwzho3c8.d.cts.map} +1 -1
  6. package/dist/Factory.d.cts +2 -2
  7. package/dist/Factory.d.mts +2 -2
  8. package/dist/{KyselyFactory-Cc2UmOJk.d.mts → KyselyFactory-DRlMv-WT.d.mts} +3 -3
  9. package/dist/{KyselyFactory-Cc2UmOJk.d.mts.map → KyselyFactory-DRlMv-WT.d.mts.map} +1 -1
  10. package/dist/{KyselyFactory-OUH03l2q.d.cts → KyselyFactory-hMcVtvJq.d.cts} +3 -3
  11. package/dist/{KyselyFactory-OUH03l2q.d.cts.map → KyselyFactory-hMcVtvJq.d.cts.map} +1 -1
  12. package/dist/KyselyFactory.d.cts +3 -3
  13. package/dist/KyselyFactory.d.mts +3 -3
  14. package/dist/{ObjectionFactory-B1WkcfZY.d.cts → ObjectionFactory-DW-qwnqO.d.cts} +3 -3
  15. package/dist/{ObjectionFactory-B1WkcfZY.d.cts.map → ObjectionFactory-DW-qwnqO.d.cts.map} +1 -1
  16. package/dist/{ObjectionFactory-BYnPr9ZP.d.mts → ObjectionFactory-DkJUf-uM.d.mts} +3 -3
  17. package/dist/{ObjectionFactory-BYnPr9ZP.d.mts.map → ObjectionFactory-DkJUf-uM.d.mts.map} +1 -1
  18. package/dist/ObjectionFactory.d.cts +3 -3
  19. package/dist/ObjectionFactory.d.mts +3 -3
  20. package/dist/{PostgresKyselyMigrator-B4pScubb.mjs → PostgresKyselyMigrator-BIKd7G5C.mjs} +16 -3
  21. package/dist/PostgresKyselyMigrator-BIKd7G5C.mjs.map +1 -0
  22. package/dist/{PostgresKyselyMigrator-CBltSOq5.d.cts → PostgresKyselyMigrator-CTNftoZK.d.cts} +16 -2
  23. package/dist/PostgresKyselyMigrator-CTNftoZK.d.cts.map +1 -0
  24. package/dist/{PostgresKyselyMigrator-C7ljZYvq.cjs → PostgresKyselyMigrator-CqNs3qX8.cjs} +16 -3
  25. package/dist/PostgresKyselyMigrator-CqNs3qX8.cjs.map +1 -0
  26. package/dist/{PostgresKyselyMigrator-DrVWncqd.d.mts → PostgresKyselyMigrator-x0uvWs3U.d.mts} +16 -2
  27. package/dist/PostgresKyselyMigrator-x0uvWs3U.d.mts.map +1 -0
  28. package/dist/PostgresKyselyMigrator.cjs +2 -2
  29. package/dist/PostgresKyselyMigrator.d.cts +2 -2
  30. package/dist/PostgresKyselyMigrator.d.mts +2 -2
  31. package/dist/PostgresKyselyMigrator.mjs +2 -2
  32. package/dist/{PostgresMigrator-Bres0U6E.d.cts → PostgresMigrator-C_QQ6q35.d.mts} +15 -2
  33. package/dist/PostgresMigrator-C_QQ6q35.d.mts.map +1 -0
  34. package/dist/{PostgresMigrator-S-YYosAC.d.mts → PostgresMigrator-CeYy-eHF.d.cts} +15 -2
  35. package/dist/PostgresMigrator-CeYy-eHF.d.cts.map +1 -0
  36. package/dist/{PostgresMigrator-DcP1o-T6.mjs → PostgresMigrator-DVAY04qN.mjs} +16 -2
  37. package/dist/PostgresMigrator-DVAY04qN.mjs.map +1 -0
  38. package/dist/{PostgresMigrator-CHiBYEg_.cjs → PostgresMigrator-M9jpzOvN.cjs} +16 -2
  39. package/dist/PostgresMigrator-M9jpzOvN.cjs.map +1 -0
  40. package/dist/PostgresMigrator.cjs +1 -1
  41. package/dist/PostgresMigrator.d.cts +1 -1
  42. package/dist/PostgresMigrator.d.mts +1 -1
  43. package/dist/PostgresMigrator.mjs +1 -1
  44. package/dist/{PostgresObjectionMigrator-CPfBAP7r.d.cts → PostgresObjectionMigrator-1j6YIB1c.d.mts} +2 -2
  45. package/dist/{PostgresObjectionMigrator-CPfBAP7r.d.cts.map → PostgresObjectionMigrator-1j6YIB1c.d.mts.map} +1 -1
  46. package/dist/{PostgresObjectionMigrator-DVEqB5tp.d.mts → PostgresObjectionMigrator-DHVC9h_P.d.cts} +2 -2
  47. package/dist/{PostgresObjectionMigrator-DVEqB5tp.d.mts.map → PostgresObjectionMigrator-DHVC9h_P.d.cts.map} +1 -1
  48. package/dist/{PostgresObjectionMigrator-BXLAVVwm.cjs → PostgresObjectionMigrator-DSaPhwjY.cjs} +2 -2
  49. package/dist/{PostgresObjectionMigrator-BXLAVVwm.cjs.map → PostgresObjectionMigrator-DSaPhwjY.cjs.map} +1 -1
  50. package/dist/{PostgresObjectionMigrator-BJ5X48U8.mjs → PostgresObjectionMigrator-DjPKdUbm.mjs} +2 -2
  51. package/dist/{PostgresObjectionMigrator-BJ5X48U8.mjs.map → PostgresObjectionMigrator-DjPKdUbm.mjs.map} +1 -1
  52. package/dist/PostgresObjectionMigrator.cjs +2 -2
  53. package/dist/PostgresObjectionMigrator.d.cts +2 -2
  54. package/dist/PostgresObjectionMigrator.d.mts +2 -2
  55. package/dist/PostgresObjectionMigrator.mjs +2 -2
  56. package/dist/better-auth.d.cts +2 -2
  57. package/dist/better-auth.d.mts +2 -2
  58. package/dist/{directory-CVrfTq1I.d.mts → directory-CYXmolu1.d.mts} +3 -3
  59. package/dist/{directory-CVrfTq1I.d.mts.map → directory-CYXmolu1.d.mts.map} +1 -1
  60. package/dist/{directory-DGOcVlKD.d.cts → directory-DAnMWi50.d.cts} +3 -3
  61. package/dist/{directory-DGOcVlKD.d.cts.map → directory-DAnMWi50.d.cts.map} +1 -1
  62. package/dist/{faker-CbYiF-8_.d.cts → faker-Dg3trU4a.d.cts} +3 -3
  63. package/dist/{faker-CbYiF-8_.d.cts.map → faker-Dg3trU4a.d.cts.map} +1 -1
  64. package/dist/{faker-D9gz7KjY.d.mts → faker-DsYCplsG.d.mts} +3 -3
  65. package/dist/{faker-D9gz7KjY.d.mts.map → faker-DsYCplsG.d.mts.map} +1 -1
  66. package/dist/faker.d.cts +1 -1
  67. package/dist/faker.d.mts +1 -1
  68. package/dist/initScript.cjs +95 -0
  69. package/dist/initScript.cjs.map +1 -0
  70. package/dist/initScript.d.cts +45 -0
  71. package/dist/initScript.d.cts.map +1 -0
  72. package/dist/initScript.d.mts +45 -0
  73. package/dist/initScript.d.mts.map +1 -0
  74. package/dist/initScript.mjs +93 -0
  75. package/dist/initScript.mjs.map +1 -0
  76. package/dist/kysely.cjs +2 -2
  77. package/dist/kysely.d.cts +5 -5
  78. package/dist/kysely.d.mts +5 -5
  79. package/dist/kysely.mjs +2 -2
  80. package/dist/objection.cjs +2 -2
  81. package/dist/objection.d.cts +5 -5
  82. package/dist/objection.d.mts +5 -5
  83. package/dist/objection.mjs +2 -2
  84. package/dist/os/directory.d.cts +1 -1
  85. package/dist/os/directory.d.mts +1 -1
  86. package/dist/os/index.d.cts +1 -1
  87. package/dist/os/index.d.mts +1 -1
  88. package/package.json +6 -1
  89. package/src/PostgresKyselyMigrator.ts +15 -1
  90. package/src/PostgresMigrator.ts +19 -2
  91. package/src/__tests__/PostgresKyselyMigrator.spec.ts +104 -0
  92. package/src/__tests__/PostgresMigrator.spec.ts +109 -0
  93. package/src/__tests__/initScript.spec.ts +308 -0
  94. package/src/initScript.ts +119 -0
  95. package/dist/PostgresKyselyMigrator-B4pScubb.mjs.map +0 -1
  96. package/dist/PostgresKyselyMigrator-C7ljZYvq.cjs.map +0 -1
  97. package/dist/PostgresKyselyMigrator-CBltSOq5.d.cts.map +0 -1
  98. package/dist/PostgresKyselyMigrator-DrVWncqd.d.mts.map +0 -1
  99. package/dist/PostgresMigrator-Bres0U6E.d.cts.map +0 -1
  100. package/dist/PostgresMigrator-CHiBYEg_.cjs.map +0 -1
  101. package/dist/PostgresMigrator-DcP1o-T6.mjs.map +0 -1
  102. package/dist/PostgresMigrator-S-YYosAC.d.mts.map +0 -1
@@ -0,0 +1,93 @@
1
+ import pg from "pg";
2
+ import { readFileSync } from "node:fs";
3
+
4
+ //#region src/initScript.ts
5
+ const { Client } = pg;
6
+ /**
7
+ * Parse a shell init script (like docker/postgres/init.sh) and extract
8
+ * SQL blocks from heredoc sections (<<-EOSQL ... EOSQL).
9
+ *
10
+ * @param content - The shell script content
11
+ * @param env - Environment variables to substitute ($VAR_NAME references)
12
+ * @returns Array of SQL strings ready to execute
13
+ * @internal Exported for testing
14
+ */
15
+ function parseInitScript(content, env) {
16
+ const blocks = [];
17
+ const lines = content.split("\n");
18
+ let inHeredoc = false;
19
+ let currentBlock = [];
20
+ for (const line of lines) if (inHeredoc) if (/^\s*EOSQL\s*$/.test(line)) {
21
+ const sql = substituteEnvVars(currentBlock.join("\n"), env);
22
+ blocks.push(sql);
23
+ currentBlock = [];
24
+ inHeredoc = false;
25
+ } else currentBlock.push(line);
26
+ else if (line.includes("<<-EOSQL") || line.includes("<< EOSQL") || line.includes("<<EOSQL")) {
27
+ inHeredoc = true;
28
+ currentBlock = [];
29
+ }
30
+ return blocks;
31
+ }
32
+ /**
33
+ * Replace shell variable references ($VAR_NAME and ${VAR_NAME})
34
+ * with values from the provided env object.
35
+ */
36
+ function substituteEnvVars(sql, env) {
37
+ let result = sql.replace(/\$\{(\w+)\}/g, (_, name) => env[name] ?? "");
38
+ result = result.replace(/\$(\w+)/g, (_, name) => env[name] ?? "");
39
+ return result;
40
+ }
41
+ /**
42
+ * Read a postgres init script, parse out the SQL blocks,
43
+ * substitute environment variables, and execute against a database.
44
+ *
45
+ * This is intended to run `docker/postgres/init.sh` against a test database
46
+ * so that per-app users and schemas are created (matching what Docker does
47
+ * on first volume initialization).
48
+ *
49
+ * Uses `CREATE ... IF NOT EXISTS` and `DO $$ ... END $$` wrappers where
50
+ * needed so the script is idempotent.
51
+ *
52
+ * @param scriptPath - Path to the init.sh file
53
+ * @param databaseUrl - PostgreSQL connection URL (should point to the test database)
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * // In your globalSetup.ts
58
+ * import { runInitScript } from '@geekmidas/testkit/postgres';
59
+ * import { Credentials } from '@geekmidas/envkit/credentials';
60
+ *
61
+ * const cleanup = await migrator.start();
62
+ *
63
+ * // Create per-app users in the test database
64
+ * await runInitScript('docker/postgres/init.sh', Credentials.DATABASE_URL, {
65
+ * ...process.env,
66
+ * ...Credentials,
67
+ * });
68
+ * ```
69
+ */
70
+ async function runInitScript(scriptPath, databaseUrl, env) {
71
+ const content = readFileSync(scriptPath, "utf-8");
72
+ const resolvedEnv = env ?? { ...process.env };
73
+ const blocks = parseInitScript(content, resolvedEnv);
74
+ if (blocks.length === 0) return;
75
+ const url = new URL(databaseUrl);
76
+ const client = new Client({
77
+ user: url.username,
78
+ password: decodeURIComponent(url.password),
79
+ host: url.hostname,
80
+ port: parseInt(url.port, 10),
81
+ database: url.pathname.slice(1)
82
+ });
83
+ try {
84
+ await client.connect();
85
+ for (const sql of blocks) await client.query(sql);
86
+ } finally {
87
+ await client.end();
88
+ }
89
+ }
90
+
91
+ //#endregion
92
+ export { parseInitScript, runInitScript };
93
+ //# sourceMappingURL=initScript.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initScript.mjs","names":["content: string","env: Record<string, string>","blocks: string[]","currentBlock: string[]","sql: string","scriptPath: string","databaseUrl: string","env?: Record<string, string>"],"sources":["../src/initScript.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport pg from 'pg';\n\nconst { Client } = pg;\n\n/**\n * Parse a shell init script (like docker/postgres/init.sh) and extract\n * SQL blocks from heredoc sections (<<-EOSQL ... EOSQL).\n *\n * @param content - The shell script content\n * @param env - Environment variables to substitute ($VAR_NAME references)\n * @returns Array of SQL strings ready to execute\n * @internal Exported for testing\n */\nexport function parseInitScript(\n\tcontent: string,\n\tenv: Record<string, string>,\n): string[] {\n\tconst blocks: string[] = [];\n\tconst lines = content.split('\\n');\n\tlet inHeredoc = false;\n\tlet currentBlock: string[] = [];\n\n\tfor (const line of lines) {\n\t\tif (inHeredoc) {\n\t\t\t// Check for heredoc terminator (EOSQL at start of line, with optional leading whitespace)\n\t\t\tif (/^\\s*EOSQL\\s*$/.test(line)) {\n\t\t\t\tconst sql = substituteEnvVars(currentBlock.join('\\n'), env);\n\t\t\t\tblocks.push(sql);\n\t\t\t\tcurrentBlock = [];\n\t\t\t\tinHeredoc = false;\n\t\t\t} else {\n\t\t\t\tcurrentBlock.push(line);\n\t\t\t}\n\t\t} else if (\n\t\t\tline.includes('<<-EOSQL') ||\n\t\t\tline.includes('<< EOSQL') ||\n\t\t\tline.includes('<<EOSQL')\n\t\t) {\n\t\t\tinHeredoc = true;\n\t\t\tcurrentBlock = [];\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\n/**\n * Replace shell variable references ($VAR_NAME and ${VAR_NAME})\n * with values from the provided env object.\n */\nfunction substituteEnvVars(sql: string, env: Record<string, string>): string {\n\t// Replace ${VAR_NAME} syntax\n\tlet result = sql.replace(/\\$\\{(\\w+)\\}/g, (_, name) => env[name] ?? '');\n\t// Replace $VAR_NAME syntax (word boundary after)\n\tresult = result.replace(/\\$(\\w+)/g, (_, name) => env[name] ?? '');\n\treturn result;\n}\n\n/**\n * Read a postgres init script, parse out the SQL blocks,\n * substitute environment variables, and execute against a database.\n *\n * This is intended to run `docker/postgres/init.sh` against a test database\n * so that per-app users and schemas are created (matching what Docker does\n * on first volume initialization).\n *\n * Uses `CREATE ... IF NOT EXISTS` and `DO $$ ... END $$` wrappers where\n * needed so the script is idempotent.\n *\n * @param scriptPath - Path to the init.sh file\n * @param databaseUrl - PostgreSQL connection URL (should point to the test database)\n *\n * @example\n * ```typescript\n * // In your globalSetup.ts\n * import { runInitScript } from '@geekmidas/testkit/postgres';\n * import { Credentials } from '@geekmidas/envkit/credentials';\n *\n * const cleanup = await migrator.start();\n *\n * // Create per-app users in the test database\n * await runInitScript('docker/postgres/init.sh', Credentials.DATABASE_URL, {\n * ...process.env,\n * ...Credentials,\n * });\n * ```\n */\nexport async function runInitScript(\n\tscriptPath: string,\n\tdatabaseUrl: string,\n\tenv?: Record<string, string>,\n): Promise<void> {\n\tconst content = readFileSync(scriptPath, 'utf-8');\n\tconst resolvedEnv = env ?? ({ ...process.env } as Record<string, string>);\n\tconst blocks = parseInitScript(content, resolvedEnv);\n\n\tif (blocks.length === 0) {\n\t\treturn;\n\t}\n\n\tconst url = new URL(databaseUrl);\n\tconst client = new Client({\n\t\tuser: url.username,\n\t\tpassword: decodeURIComponent(url.password),\n\t\thost: url.hostname,\n\t\tport: parseInt(url.port, 10),\n\t\tdatabase: url.pathname.slice(1),\n\t});\n\n\ttry {\n\t\tawait client.connect();\n\t\tfor (const sql of blocks) {\n\t\t\tawait client.query(sql);\n\t\t}\n\t} finally {\n\t\tawait client.end();\n\t}\n}\n"],"mappings":";;;;AAGA,MAAM,EAAE,QAAQ,GAAG;;;;;;;;;;AAWnB,SAAgB,gBACfA,SACAC,KACW;CACX,MAAMC,SAAmB,CAAE;CAC3B,MAAM,QAAQ,QAAQ,MAAM,KAAK;CACjC,IAAI,YAAY;CAChB,IAAIC,eAAyB,CAAE;AAE/B,MAAK,MAAM,QAAQ,MAClB,KAAI,UAEH,KAAI,gBAAgB,KAAK,KAAK,EAAE;EAC/B,MAAM,MAAM,kBAAkB,aAAa,KAAK,KAAK,EAAE,IAAI;AAC3D,SAAO,KAAK,IAAI;AAChB,iBAAe,CAAE;AACjB,cAAY;CACZ,MACA,cAAa,KAAK,KAAK;UAGxB,KAAK,SAAS,WAAW,IACzB,KAAK,SAAS,WAAW,IACzB,KAAK,SAAS,UAAU,EACvB;AACD,cAAY;AACZ,iBAAe,CAAE;CACjB;AAGF,QAAO;AACP;;;;;AAMD,SAAS,kBAAkBC,KAAaH,KAAqC;CAE5E,IAAI,SAAS,IAAI,QAAQ,gBAAgB,CAAC,GAAG,SAAS,IAAI,SAAS,GAAG;AAEtE,UAAS,OAAO,QAAQ,YAAY,CAAC,GAAG,SAAS,IAAI,SAAS,GAAG;AACjE,QAAO;AACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,eAAsB,cACrBI,YACAC,aACAC,KACgB;CAChB,MAAM,UAAU,aAAa,YAAY,QAAQ;CACjD,MAAM,cAAc,OAAQ,EAAE,GAAG,QAAQ,IAAK;CAC9C,MAAM,SAAS,gBAAgB,SAAS,YAAY;AAEpD,KAAI,OAAO,WAAW,EACrB;CAGD,MAAM,MAAM,IAAI,IAAI;CACpB,MAAM,SAAS,IAAI,OAAO;EACzB,MAAM,IAAI;EACV,UAAU,mBAAmB,IAAI,SAAS;EAC1C,MAAM,IAAI;EACV,MAAM,SAAS,IAAI,MAAM,GAAG;EAC5B,UAAU,IAAI,SAAS,MAAM,EAAE;CAC/B;AAED,KAAI;AACH,QAAM,OAAO,SAAS;AACtB,OAAK,MAAM,OAAO,OACjB,OAAM,OAAO,MAAM,IAAI;CAExB,UAAS;AACT,QAAM,OAAO,KAAK;CAClB;AACD"}
package/dist/kysely.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  require('./Factory-BhjUOBWN.cjs');
2
2
  const require_faker = require('./faker-B14IEMIN.cjs');
3
3
  const require_KyselyFactory = require('./KyselyFactory-BFqVIn_0.cjs');
4
- require('./PostgresMigrator-CHiBYEg_.cjs');
5
- const require_PostgresKyselyMigrator = require('./PostgresKyselyMigrator-C7ljZYvq.cjs');
4
+ require('./PostgresMigrator-M9jpzOvN.cjs');
5
+ const require_PostgresKyselyMigrator = require('./PostgresKyselyMigrator-CqNs3qX8.cjs');
6
6
  const require_VitestTransactionIsolator = require('./VitestTransactionIsolator-CMfJXZP8.cjs');
7
7
  const require_VitestKyselyTransactionIsolator = require('./VitestKyselyTransactionIsolator-D7RRXOBa.cjs');
8
8
 
package/dist/kysely.d.cts CHANGED
@@ -1,8 +1,8 @@
1
- import { FakerFactory, faker } from "./faker-CbYiF-8_.cjs";
2
- import { ExtractSeedAttrs, FactorySeed } from "./Factory-D28yjUj5.cjs";
3
- import { KyselyFactory } from "./KyselyFactory-OUH03l2q.cjs";
4
- import "./PostgresMigrator-Bres0U6E.cjs";
5
- import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-CBltSOq5.cjs";
1
+ import { FakerFactory, faker } from "./faker-Dg3trU4a.cjs";
2
+ import { ExtractSeedAttrs, FactorySeed } from "./Factory-Cwzho3c8.cjs";
3
+ import { KyselyFactory } from "./KyselyFactory-hMcVtvJq.cjs";
4
+ import "./PostgresMigrator-CeYy-eHF.cjs";
5
+ import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-CTNftoZK.cjs";
6
6
  import { DatabaseConnection, DatabaseFixtures, ExtendedDatabaseFixtures, FixtureCreators, IsolationLevel, TestWithExtendedFixtures, TransactionWrapperOptions } from "./VitestTransactionIsolator-CSroc7Df.cjs";
7
7
  import { VitestKyselyTransactionIsolator } from "./VitestKyselyTransactionIsolator-CduJlHoT.cjs";
8
8
  import { Kysely, Transaction } from "kysely";
package/dist/kysely.d.mts CHANGED
@@ -1,8 +1,8 @@
1
- import { FakerFactory, faker } from "./faker-D9gz7KjY.mjs";
2
- import { ExtractSeedAttrs, FactorySeed } from "./Factory-C6W78ulZ.mjs";
3
- import { KyselyFactory } from "./KyselyFactory-Cc2UmOJk.mjs";
4
- import "./PostgresMigrator-S-YYosAC.mjs";
5
- import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-DrVWncqd.mjs";
1
+ import { FakerFactory, faker } from "./faker-DsYCplsG.mjs";
2
+ import { ExtractSeedAttrs, FactorySeed } from "./Factory-CVR3GdkW.mjs";
3
+ import { KyselyFactory } from "./KyselyFactory-DRlMv-WT.mjs";
4
+ import "./PostgresMigrator-C_QQ6q35.mjs";
5
+ import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-x0uvWs3U.mjs";
6
6
  import { DatabaseConnection, DatabaseFixtures, ExtendedDatabaseFixtures, FixtureCreators, IsolationLevel, TestWithExtendedFixtures, TransactionWrapperOptions } from "./VitestTransactionIsolator-BNWJqh9f.mjs";
7
7
  import { VitestKyselyTransactionIsolator } from "./VitestKyselyTransactionIsolator-Cswnnj0k.mjs";
8
8
  import { Kysely, Transaction } from "kysely";
package/dist/kysely.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import "./Factory-BFVnMMCC.mjs";
2
2
  import { faker } from "./faker-BGKYFoCT.mjs";
3
3
  import { KyselyFactory } from "./KyselyFactory-DMswpwji.mjs";
4
- import "./PostgresMigrator-DcP1o-T6.mjs";
5
- import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-B4pScubb.mjs";
4
+ import "./PostgresMigrator-DVAY04qN.mjs";
5
+ import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-BIKd7G5C.mjs";
6
6
  import { IsolationLevel, extendWithFixtures } from "./VitestTransactionIsolator-DQ7tLqgV.mjs";
7
7
  import { VitestKyselyTransactionIsolator } from "./VitestKyselyTransactionIsolator-DceyIqr4.mjs";
8
8
 
@@ -1,8 +1,8 @@
1
1
  require('./Factory-BhjUOBWN.cjs');
2
2
  const require_faker = require('./faker-B14IEMIN.cjs');
3
3
  const require_ObjectionFactory = require('./ObjectionFactory-BeFBYcan.cjs');
4
- require('./PostgresMigrator-CHiBYEg_.cjs');
5
- const require_PostgresObjectionMigrator = require('./PostgresObjectionMigrator-BXLAVVwm.cjs');
4
+ require('./PostgresMigrator-M9jpzOvN.cjs');
5
+ const require_PostgresObjectionMigrator = require('./PostgresObjectionMigrator-DSaPhwjY.cjs');
6
6
  const require_VitestTransactionIsolator = require('./VitestTransactionIsolator-CMfJXZP8.cjs');
7
7
  const require_VitestObjectionTransactionIsolator = require('./VitestObjectionTransactionIsolator-CdLRrzNf.cjs');
8
8
 
@@ -1,8 +1,8 @@
1
- import { FakerFactory, faker } from "./faker-CbYiF-8_.cjs";
2
- import { ExtractSeedAttrs, FactorySeed } from "./Factory-D28yjUj5.cjs";
3
- import { ObjectionFactory } from "./ObjectionFactory-B1WkcfZY.cjs";
4
- import "./PostgresMigrator-Bres0U6E.cjs";
5
- import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-CPfBAP7r.cjs";
1
+ import { FakerFactory, faker } from "./faker-Dg3trU4a.cjs";
2
+ import { ExtractSeedAttrs, FactorySeed } from "./Factory-Cwzho3c8.cjs";
3
+ import { ObjectionFactory } from "./ObjectionFactory-DW-qwnqO.cjs";
4
+ import "./PostgresMigrator-CeYy-eHF.cjs";
5
+ import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-DHVC9h_P.cjs";
6
6
  import { DatabaseConnection, DatabaseFixtures, ExtendedDatabaseFixtures, FixtureCreators, IsolationLevel, TestWithExtendedFixtures, TransactionWrapperOptions } from "./VitestTransactionIsolator-CSroc7Df.cjs";
7
7
  import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-BXoR6xdG.cjs";
8
8
  import { Knex } from "knex";
@@ -1,8 +1,8 @@
1
- import { FakerFactory, faker } from "./faker-D9gz7KjY.mjs";
2
- import { ExtractSeedAttrs, FactorySeed } from "./Factory-C6W78ulZ.mjs";
3
- import { ObjectionFactory } from "./ObjectionFactory-BYnPr9ZP.mjs";
4
- import "./PostgresMigrator-S-YYosAC.mjs";
5
- import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-DVEqB5tp.mjs";
1
+ import { FakerFactory, faker } from "./faker-DsYCplsG.mjs";
2
+ import { ExtractSeedAttrs, FactorySeed } from "./Factory-CVR3GdkW.mjs";
3
+ import { ObjectionFactory } from "./ObjectionFactory-DkJUf-uM.mjs";
4
+ import "./PostgresMigrator-C_QQ6q35.mjs";
5
+ import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-1j6YIB1c.mjs";
6
6
  import { DatabaseConnection, DatabaseFixtures, ExtendedDatabaseFixtures, FixtureCreators, IsolationLevel, TestWithExtendedFixtures, TransactionWrapperOptions } from "./VitestTransactionIsolator-BNWJqh9f.mjs";
7
7
  import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-x6hY5j4u.mjs";
8
8
  import { TestAPI } from "vitest";
@@ -1,8 +1,8 @@
1
1
  import "./Factory-BFVnMMCC.mjs";
2
2
  import { faker } from "./faker-BGKYFoCT.mjs";
3
3
  import { ObjectionFactory } from "./ObjectionFactory-QCJ7u0Ql.mjs";
4
- import "./PostgresMigrator-DcP1o-T6.mjs";
5
- import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-BJ5X48U8.mjs";
4
+ import "./PostgresMigrator-DVAY04qN.mjs";
5
+ import { PostgresObjectionMigrator } from "./PostgresObjectionMigrator-DjPKdUbm.mjs";
6
6
  import { IsolationLevel, extendWithFixtures } from "./VitestTransactionIsolator-DQ7tLqgV.mjs";
7
7
  import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-OF2osYY5.mjs";
8
8
 
@@ -1,2 +1,2 @@
1
- import { DirectoryFixtures, itWithDir } from "../directory-DGOcVlKD.cjs";
1
+ import { DirectoryFixtures, itWithDir } from "../directory-DAnMWi50.cjs";
2
2
  export { DirectoryFixtures, itWithDir };
@@ -1,2 +1,2 @@
1
- import { DirectoryFixtures, itWithDir } from "../directory-CVrfTq1I.mjs";
1
+ import { DirectoryFixtures, itWithDir } from "../directory-CYXmolu1.mjs";
2
2
  export { DirectoryFixtures, itWithDir };
@@ -1,2 +1,2 @@
1
- import { itWithDir } from "../directory-DGOcVlKD.cjs";
1
+ import { itWithDir } from "../directory-DAnMWi50.cjs";
2
2
  export { itWithDir };
@@ -1,2 +1,2 @@
1
- import { itWithDir } from "../directory-CVrfTq1I.mjs";
1
+ import { itWithDir } from "../directory-CYXmolu1.mjs";
2
2
  export { itWithDir };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/testkit",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -48,6 +48,11 @@
48
48
  "types": "./dist/benchmark.d.ts",
49
49
  "import": "./dist/benchmark.mjs",
50
50
  "require": "./dist/benchmark.cjs"
51
+ },
52
+ "./postgres": {
53
+ "types": "./dist/initScript.d.ts",
54
+ "import": "./dist/initScript.mjs",
55
+ "require": "./dist/initScript.cjs"
51
56
  }
52
57
  },
53
58
  "dependencies": {
@@ -40,6 +40,18 @@ const logger = console;
40
40
  * const cleanup = await migrator.start();
41
41
  * // Run tests...
42
42
  * await cleanup();
43
+ *
44
+ * // With afterCreate hook to create per-app users
45
+ * import { runInitScript } from '@geekmidas/testkit/postgres';
46
+ *
47
+ * const migrator = new PostgresKyselyMigrator({
48
+ * uri: 'postgresql://localhost:5432/test_db',
49
+ * db,
50
+ * provider,
51
+ * afterCreate: async (uri) => {
52
+ * await runInitScript('docker/postgres/init.sh', uri, env);
53
+ * },
54
+ * });
43
55
  * ```
44
56
  */
45
57
  export class PostgresKyselyMigrator extends PostgresMigrator {
@@ -50,15 +62,17 @@ export class PostgresKyselyMigrator extends PostgresMigrator {
50
62
  * @param options.uri - PostgreSQL connection URI
51
63
  * @param options.db - Kysely database instance
52
64
  * @param options.provider - Migration provider for locating migration files
65
+ * @param options.afterCreate - Optional hook called after database creation but before migrations
53
66
  */
54
67
  constructor(
55
68
  private options: {
56
69
  uri: string;
57
70
  db: Kysely<any>;
58
71
  provider: MigrationProvider;
72
+ afterCreate?: (uri: string) => Promise<void>;
59
73
  },
60
74
  ) {
61
- super(options.uri);
75
+ super(options.uri, options.afterCreate);
62
76
  }
63
77
 
64
78
  /**
@@ -57,6 +57,17 @@ const logger = console;
57
57
  * const migrator = new MyMigrator('postgresql://localhost:5432/test_db');
58
58
  * const cleanup = await migrator.start();
59
59
  *
60
+ * // With afterCreate hook (e.g. run init script to create per-app users)
61
+ * import { runInitScript } from '@geekmidas/testkit/postgres';
62
+ *
63
+ * const migrator = new MyMigrator(
64
+ * 'postgresql://localhost:5432/test_db',
65
+ * async (uri) => {
66
+ * await runInitScript('docker/postgres/init.sh', uri, env);
67
+ * },
68
+ * );
69
+ * const cleanup = await migrator.start();
70
+ *
60
71
  * // Run tests...
61
72
  *
62
73
  * // Clean up
@@ -68,8 +79,12 @@ export abstract class PostgresMigrator {
68
79
  * Creates a new PostgresMigrator instance.
69
80
  *
70
81
  * @param uri - PostgreSQL connection URI
82
+ * @param afterCreate - Optional hook called after database creation but before migrations
71
83
  */
72
- constructor(private uri: string) {}
84
+ constructor(
85
+ private uri: string,
86
+ private afterCreate?: (uri: string) => Promise<void>,
87
+ ) {}
73
88
 
74
89
  /**
75
90
  * Abstract method to be implemented by subclasses.
@@ -161,7 +176,9 @@ export abstract class PostgresMigrator {
161
176
  const { database, db } = await setupClient(this.uri);
162
177
  try {
163
178
  await PostgresMigrator.create(this.uri);
164
- // Implement migration logic here
179
+ if (this.afterCreate) {
180
+ await this.afterCreate(this.uri);
181
+ }
165
182
  await this.migrate();
166
183
  logger.log(`Migrating database: ${database}`);
167
184
  // Example: await db.query('CREATE TABLE example (id SERIAL PRIMARY KEY)');
@@ -577,6 +577,110 @@ describe('PostgresKyselyMigrator', () => {
577
577
  });
578
578
  });
579
579
 
580
+ describe('afterCreate hook', () => {
581
+ it('should call afterCreate before migrations run', async () => {
582
+ const hookDbName = `test_kysely_hook_${Date.now()}`;
583
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${hookDbName}`;
584
+ const callOrder: string[] = [];
585
+
586
+ const db = new Kysely<TestSchema>({
587
+ dialect: new PostgresDialect({
588
+ pool: new Pool({
589
+ host: 'localhost',
590
+ port: 5432,
591
+ user: 'geekmidas',
592
+ password: 'geekmidas',
593
+ database: hookDbName,
594
+ }),
595
+ }),
596
+ });
597
+
598
+ const provider = new TestMigrationProvider();
599
+ provider.addMigration('001_create_table', {
600
+ up: async (db) => {
601
+ callOrder.push('migrate');
602
+ await db.schema
603
+ .createTable('items')
604
+ .addColumn('id', 'serial', (col) => col.primaryKey())
605
+ .execute();
606
+ },
607
+ });
608
+
609
+ const migrator = new PostgresKyselyMigrator({
610
+ uri,
611
+ db,
612
+ provider,
613
+ afterCreate: async (receivedUri) => {
614
+ callOrder.push('afterCreate');
615
+ expect(receivedUri).toBe(uri);
616
+ },
617
+ });
618
+
619
+ const cleanup = await migrator.start();
620
+
621
+ expect(callOrder).toEqual(['afterCreate', 'migrate']);
622
+
623
+ await cleanup();
624
+ });
625
+
626
+ it('should allow creating users and schemas before migrations', async () => {
627
+ const schemaDbName = `test_kysely_schema_hook_${Date.now()}`;
628
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${schemaDbName}`;
629
+
630
+ const db = new Kysely<TestSchema>({
631
+ dialect: new PostgresDialect({
632
+ pool: new Pool({
633
+ host: 'localhost',
634
+ port: 5432,
635
+ user: 'geekmidas',
636
+ password: 'geekmidas',
637
+ database: schemaDbName,
638
+ }),
639
+ }),
640
+ });
641
+
642
+ const provider = new TestMigrationProvider();
643
+ provider.addMigration('001_use_schema', {
644
+ up: async (db) => {
645
+ // Verify schema exists (created by afterCreate)
646
+ const result =
647
+ await sql`SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'app'`.execute(
648
+ db,
649
+ );
650
+ if (result.rows.length === 0) {
651
+ throw new Error('Expected app schema to exist');
652
+ }
653
+ await db.schema
654
+ .createTable('items')
655
+ .addColumn('id', 'serial', (col) => col.primaryKey())
656
+ .execute();
657
+ },
658
+ });
659
+
660
+ const migrator = new PostgresKyselyMigrator({
661
+ uri,
662
+ db,
663
+ provider,
664
+ afterCreate: async () => {
665
+ const client = new Client({
666
+ host: 'localhost',
667
+ port: 5432,
668
+ user: 'geekmidas',
669
+ password: 'geekmidas',
670
+ database: schemaDbName,
671
+ });
672
+ await client.connect();
673
+ await client.query('CREATE SCHEMA IF NOT EXISTS app');
674
+ await client.end();
675
+ },
676
+ });
677
+
678
+ const cleanup = await migrator.start();
679
+
680
+ await cleanup();
681
+ });
682
+ });
683
+
580
684
  describe('error scenarios', () => {
581
685
  it('should handle provider errors', async () => {
582
686
  const providerErrorDbName = `test_provider_error_${Date.now()}`;
@@ -18,6 +18,10 @@ class TestPostgresMigrator extends PostgresMigrator {
18
18
  public migrateError?: Error;
19
19
  public customMigrations: Array<() => Promise<void>> = [];
20
20
 
21
+ constructor(uri: string, afterCreate?: (uri: string) => Promise<void>) {
22
+ super(uri, afterCreate);
23
+ }
24
+
21
25
  async migrate(): Promise<void> {
22
26
  this.migrateCalled = true;
23
27
  if (this.migrateError) {
@@ -285,6 +289,111 @@ describe('PostgresMigrator', () => {
285
289
  });
286
290
  });
287
291
 
292
+ describe('afterCreate hook', () => {
293
+ it('should call afterCreate after database creation and before migrate', async () => {
294
+ const hookDbName = `test_after_create_${Date.now()}`;
295
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${hookDbName}`;
296
+ const callOrder: string[] = [];
297
+
298
+ const migrator = new TestPostgresMigrator(uri, async (receivedUri) => {
299
+ callOrder.push('afterCreate');
300
+ expect(receivedUri).toBe(uri);
301
+ });
302
+
303
+ migrator.addMigration(async () => {
304
+ callOrder.push('migrate');
305
+ });
306
+
307
+ const cleanup = await migrator.start();
308
+
309
+ expect(callOrder).toEqual(['afterCreate', 'migrate']);
310
+
311
+ await cleanup();
312
+ });
313
+
314
+ it('should skip afterCreate when not provided', async () => {
315
+ const noHookDbName = `test_no_hook_${Date.now()}`;
316
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${noHookDbName}`;
317
+ const migrator = new TestPostgresMigrator(uri);
318
+
319
+ const cleanup = await migrator.start();
320
+
321
+ expect(migrator.migrateCalled).toBe(true);
322
+
323
+ await cleanup();
324
+ });
325
+
326
+ it('should propagate afterCreate errors and not run migrate', async () => {
327
+ const errorHookDbName = `test_hook_error_${Date.now()}`;
328
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${errorHookDbName}`;
329
+
330
+ const migrator = new TestPostgresMigrator(uri, async () => {
331
+ throw new Error('afterCreate failed');
332
+ });
333
+
334
+ await expect(migrator.start()).rejects.toThrow('afterCreate failed');
335
+ expect(migrator.migrateCalled).toBe(false);
336
+
337
+ // Cleanup the created database
338
+ const cleanupClient = new Client({
339
+ host: 'localhost',
340
+ port: 5432,
341
+ user: 'geekmidas',
342
+ password: 'geekmidas',
343
+ database: 'postgres',
344
+ });
345
+ try {
346
+ await cleanupClient.connect();
347
+ await cleanupClient.query(
348
+ `DROP DATABASE IF EXISTS "${errorHookDbName}"`,
349
+ );
350
+ } finally {
351
+ await cleanupClient.end();
352
+ }
353
+ });
354
+
355
+ it('should allow afterCreate to set up database objects before migrations', async () => {
356
+ const setupDbName = `test_hook_setup_${Date.now()}`;
357
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${setupDbName}`;
358
+
359
+ const migrator = new TestPostgresMigrator(uri, async () => {
360
+ const client = new Client({
361
+ host: 'localhost',
362
+ port: 5432,
363
+ user: 'geekmidas',
364
+ password: 'geekmidas',
365
+ database: setupDbName,
366
+ });
367
+ await client.connect();
368
+ await client.query(
369
+ "CREATE SCHEMA IF NOT EXISTS app; COMMENT ON SCHEMA app IS 'Created by afterCreate hook';",
370
+ );
371
+ await client.end();
372
+ });
373
+
374
+ migrator.addMigration(async () => {
375
+ // Verify schema exists during migration
376
+ const client = new Client({
377
+ host: 'localhost',
378
+ port: 5432,
379
+ user: 'geekmidas',
380
+ password: 'geekmidas',
381
+ database: setupDbName,
382
+ });
383
+ await client.connect();
384
+ const result = await client.query(
385
+ "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'app'",
386
+ );
387
+ expect(result.rowCount).toBe(1);
388
+ await client.end();
389
+ });
390
+
391
+ const cleanup = await migrator.start();
392
+
393
+ await cleanup();
394
+ });
395
+ });
396
+
288
397
  describe('abstract method', () => {
289
398
  it('should require concrete implementation of migrate method', () => {
290
399
  // TypeScript ensures abstract methods are implemented