@geekmidas/testkit 0.4.0 → 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.
Files changed (209) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/Factory-BFVnMMCC.mjs.map +1 -1
  3. package/dist/{Factory-c16c27Y6.d.cts → Factory-BOX312yd.d.cts} +3 -2
  4. package/dist/Factory-BOX312yd.d.cts.map +1 -0
  5. package/dist/Factory-BhjUOBWN.cjs.map +1 -1
  6. package/dist/{Factory-BcGJjLc8.d.mts → Factory-SFupxRC2.d.mts} +2 -1
  7. package/dist/Factory-SFupxRC2.d.mts.map +1 -0
  8. package/dist/Factory.d.cts +2 -2
  9. package/dist/Factory.d.mts +1 -1
  10. package/dist/KyselyFactory-BFqVIn_0.cjs.map +1 -1
  11. package/dist/KyselyFactory-DMswpwji.mjs.map +1 -1
  12. package/dist/{KyselyFactory-uZ45h7YU.d.cts → KyselyFactory-Dy5zzV4B.d.cts} +4 -3
  13. package/dist/KyselyFactory-Dy5zzV4B.d.cts.map +1 -0
  14. package/dist/{KyselyFactory-Cj-EultY.d.mts → KyselyFactory-vAxYodck.d.mts} +3 -2
  15. package/dist/KyselyFactory-vAxYodck.d.mts.map +1 -0
  16. package/dist/KyselyFactory.d.cts +3 -3
  17. package/dist/KyselyFactory.d.mts +2 -2
  18. package/dist/{ObjectionFactory-DL4qkuF1.d.mts → ObjectionFactory-BWjB49-i.d.mts} +3 -2
  19. package/dist/ObjectionFactory-BWjB49-i.d.mts.map +1 -0
  20. package/dist/ObjectionFactory-BeFBYcan.cjs.map +1 -1
  21. package/dist/{ObjectionFactory-CdhzKs4f.d.cts → ObjectionFactory-CD-WFuMJ.d.cts} +4 -3
  22. package/dist/ObjectionFactory-CD-WFuMJ.d.cts.map +1 -0
  23. package/dist/ObjectionFactory-QCJ7u0Ql.mjs.map +1 -1
  24. package/dist/ObjectionFactory.d.cts +3 -3
  25. package/dist/ObjectionFactory.d.mts +2 -2
  26. package/dist/{PostgresKyselyMigrator-upT-hmrz.mjs → PostgresKyselyMigrator-6sE1KOni.mjs} +2 -2
  27. package/dist/PostgresKyselyMigrator-6sE1KOni.mjs.map +1 -0
  28. package/dist/{PostgresKyselyMigrator-CIx3AFSR.d.mts → PostgresKyselyMigrator-CBltSOq5.d.cts} +3 -2
  29. package/dist/PostgresKyselyMigrator-CBltSOq5.d.cts.map +1 -0
  30. package/dist/{PostgresKyselyMigrator-CfytARcA.cjs → PostgresKyselyMigrator-D6IbPq8t.cjs} +2 -2
  31. package/dist/PostgresKyselyMigrator-D6IbPq8t.cjs.map +1 -0
  32. package/dist/{PostgresKyselyMigrator-CQ3aUoy_.d.cts → PostgresKyselyMigrator-DrVWncqd.d.mts} +3 -2
  33. package/dist/PostgresKyselyMigrator-DrVWncqd.d.mts.map +1 -0
  34. package/dist/PostgresKyselyMigrator.cjs +2 -2
  35. package/dist/PostgresKyselyMigrator.d.cts +2 -2
  36. package/dist/PostgresKyselyMigrator.d.mts +2 -2
  37. package/dist/PostgresKyselyMigrator.mjs +2 -2
  38. package/dist/{PostgresMigrator-DbuJGAVy.mjs → PostgresMigrator-BjjenqSd.mjs} +2 -2
  39. package/dist/PostgresMigrator-BjjenqSd.mjs.map +1 -0
  40. package/dist/{PostgresMigrator-D5UkK1_K.d.cts → PostgresMigrator-Bres0U6E.d.cts} +2 -1
  41. package/dist/PostgresMigrator-Bres0U6E.d.cts.map +1 -0
  42. package/dist/{PostgresMigrator-DFcNdCvD.cjs → PostgresMigrator-D6dQn0x2.cjs} +2 -2
  43. package/dist/PostgresMigrator-D6dQn0x2.cjs.map +1 -0
  44. package/dist/{PostgresMigrator-DQaRxoaY.d.mts → PostgresMigrator-S-YYosAC.d.mts} +2 -1
  45. package/dist/PostgresMigrator-S-YYosAC.d.mts.map +1 -0
  46. package/dist/PostgresMigrator.cjs +1 -1
  47. package/dist/PostgresMigrator.d.cts +1 -1
  48. package/dist/PostgresMigrator.d.mts +1 -1
  49. package/dist/PostgresMigrator.mjs +1 -1
  50. package/dist/{PostgresObjectionMigrator-CZHHcCOv.d.cts → PostgresObjectionMigrator-CPfBAP7r.d.cts} +3 -2
  51. package/dist/PostgresObjectionMigrator-CPfBAP7r.d.cts.map +1 -0
  52. package/dist/{PostgresObjectionMigrator-BG6ymgnt.cjs → PostgresObjectionMigrator-DK8ODIHQ.cjs} +2 -2
  53. package/dist/PostgresObjectionMigrator-DK8ODIHQ.cjs.map +1 -0
  54. package/dist/{PostgresObjectionMigrator-D_hCcrQu.d.mts → PostgresObjectionMigrator-DVEqB5tp.d.mts} +3 -2
  55. package/dist/PostgresObjectionMigrator-DVEqB5tp.d.mts.map +1 -0
  56. package/dist/{PostgresObjectionMigrator-DPj2pOpX.mjs → PostgresObjectionMigrator-D_QxXbIN.mjs} +2 -2
  57. package/dist/PostgresObjectionMigrator-D_QxXbIN.mjs.map +1 -0
  58. package/dist/PostgresObjectionMigrator.cjs +2 -2
  59. package/dist/PostgresObjectionMigrator.d.cts +2 -2
  60. package/dist/PostgresObjectionMigrator.d.mts +2 -2
  61. package/dist/PostgresObjectionMigrator.mjs +2 -2
  62. package/dist/{VitestKyselyTransactionIsolator-D3EZZhjZ.d.cts → VitestKyselyTransactionIsolator-CduJlHoT.d.cts} +4 -3
  63. package/dist/VitestKyselyTransactionIsolator-CduJlHoT.d.cts.map +1 -0
  64. package/dist/{VitestKyselyTransactionIsolator-Dxlp1u0f.d.mts → VitestKyselyTransactionIsolator-Cswnnj0k.d.mts} +4 -3
  65. package/dist/VitestKyselyTransactionIsolator-Cswnnj0k.d.mts.map +1 -0
  66. package/dist/{VitestKyselyTransactionIsolator-EvDLk5zg.cjs → VitestKyselyTransactionIsolator-D7RRXOBa.cjs} +2 -2
  67. package/dist/VitestKyselyTransactionIsolator-D7RRXOBa.cjs.map +1 -0
  68. package/dist/{VitestKyselyTransactionIsolator-CNURW8y6.mjs → VitestKyselyTransactionIsolator-DceyIqr4.mjs} +2 -2
  69. package/dist/VitestKyselyTransactionIsolator-DceyIqr4.mjs.map +1 -0
  70. package/dist/VitestKyselyTransactionIsolator.cjs +1 -1
  71. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  72. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  73. package/dist/VitestKyselyTransactionIsolator.mjs +1 -1
  74. package/dist/{VitestObjectionTransactionIsolator-1TpsPqfG.d.cts → VitestObjectionTransactionIsolator-BXoR6xdG.d.cts} +4 -3
  75. package/dist/VitestObjectionTransactionIsolator-BXoR6xdG.d.cts.map +1 -0
  76. package/dist/{VitestObjectionTransactionIsolator-CM5KTAFA.cjs → VitestObjectionTransactionIsolator-CdLRrzNf.cjs} +2 -2
  77. package/dist/VitestObjectionTransactionIsolator-CdLRrzNf.cjs.map +1 -0
  78. package/dist/{VitestObjectionTransactionIsolator-jQFaCz0u.mjs → VitestObjectionTransactionIsolator-OF2osYY5.mjs} +2 -2
  79. package/dist/VitestObjectionTransactionIsolator-OF2osYY5.mjs.map +1 -0
  80. package/dist/{VitestObjectionTransactionIsolator-i9jIgU8Q.d.mts → VitestObjectionTransactionIsolator-x6hY5j4u.d.mts} +4 -3
  81. package/dist/VitestObjectionTransactionIsolator-x6hY5j4u.d.mts.map +1 -0
  82. package/dist/VitestObjectionTransactionIsolator.cjs +1 -1
  83. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  84. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  85. package/dist/VitestObjectionTransactionIsolator.mjs +1 -1
  86. package/dist/{VitestTransactionIsolator-BvR19bYn.d.mts → VitestTransactionIsolator-BNWJqh9f.d.mts} +3 -2
  87. package/dist/VitestTransactionIsolator-BNWJqh9f.d.mts.map +1 -0
  88. package/dist/VitestTransactionIsolator-CMfJXZP8.cjs.map +1 -1
  89. package/dist/{VitestTransactionIsolator-CwQaxZLP.d.cts → VitestTransactionIsolator-CSroc7Df.d.cts} +3 -2
  90. package/dist/VitestTransactionIsolator-CSroc7Df.d.cts.map +1 -0
  91. package/dist/VitestTransactionIsolator-DQ7tLqgV.mjs.map +1 -1
  92. package/dist/VitestTransactionIsolator.d.cts +1 -1
  93. package/dist/VitestTransactionIsolator.d.mts +1 -1
  94. package/dist/aws.cjs.map +1 -1
  95. package/dist/aws.d.cts +2 -0
  96. package/dist/aws.d.cts.map +1 -0
  97. package/dist/aws.d.mts +2 -0
  98. package/dist/aws.d.mts.map +1 -0
  99. package/dist/aws.mjs.map +1 -1
  100. package/dist/benchmark.cjs.map +1 -1
  101. package/dist/benchmark.d.cts +1 -0
  102. package/dist/benchmark.d.cts.map +1 -0
  103. package/dist/benchmark.d.mts +1 -0
  104. package/dist/benchmark.d.mts.map +1 -0
  105. package/dist/benchmark.mjs.map +1 -1
  106. package/dist/better-auth.cjs +29 -30
  107. package/dist/better-auth.cjs.map +1 -1
  108. package/dist/better-auth.d.cts +2 -2
  109. package/dist/better-auth.d.cts.map +1 -0
  110. package/dist/better-auth.d.mts.map +1 -0
  111. package/dist/better-auth.mjs +29 -30
  112. package/dist/better-auth.mjs.map +1 -1
  113. package/dist/directory-B-Ozljzk.mjs.map +1 -1
  114. package/dist/directory-BVC8g7cX.cjs.map +1 -1
  115. package/dist/{directory-BXavAeJZ.d.mts → directory-CVrfTq1I.d.mts} +2 -1
  116. package/dist/directory-CVrfTq1I.d.mts.map +1 -0
  117. package/dist/{directory-Mi7tdOuD.d.cts → directory-DAnMWi50.d.cts} +2 -1
  118. package/dist/directory-DAnMWi50.d.cts.map +1 -0
  119. package/dist/faker-B14IEMIN.cjs.map +1 -1
  120. package/dist/faker-BGKYFoCT.mjs.map +1 -1
  121. package/dist/{faker-DvxiCtxc.d.cts → faker-Cg76aFNO.d.cts} +3 -3
  122. package/dist/faker-Cg76aFNO.d.cts.map +1 -0
  123. package/dist/faker-DHh7xs4u.d.mts.map +1 -0
  124. package/dist/faker.d.cts +1 -1
  125. package/dist/helpers.cjs.map +1 -1
  126. package/dist/helpers.d.cts +1 -0
  127. package/dist/helpers.d.cts.map +1 -0
  128. package/dist/helpers.d.mts +1 -0
  129. package/dist/helpers.d.mts.map +1 -0
  130. package/dist/helpers.mjs.map +1 -1
  131. package/dist/kysely.cjs +3 -3
  132. package/dist/kysely.cjs.map +1 -1
  133. package/dist/kysely.d.cts +8 -7
  134. package/dist/kysely.d.cts.map +1 -0
  135. package/dist/kysely.d.mts +7 -6
  136. package/dist/kysely.d.mts.map +1 -0
  137. package/dist/kysely.mjs +3 -3
  138. package/dist/kysely.mjs.map +1 -1
  139. package/dist/logger.cjs.map +1 -1
  140. package/dist/logger.d.cts +1 -0
  141. package/dist/logger.d.cts.map +1 -0
  142. package/dist/logger.d.mts +1 -0
  143. package/dist/logger.d.mts.map +1 -0
  144. package/dist/logger.mjs.map +1 -1
  145. package/dist/objection.cjs +3 -3
  146. package/dist/objection.cjs.map +1 -1
  147. package/dist/objection.d.cts +8 -7
  148. package/dist/objection.d.cts.map +1 -0
  149. package/dist/objection.d.mts +7 -6
  150. package/dist/objection.d.mts.map +1 -0
  151. package/dist/objection.mjs +3 -3
  152. package/dist/objection.mjs.map +1 -1
  153. package/dist/os/directory.d.cts +1 -1
  154. package/dist/os/directory.d.mts +1 -1
  155. package/dist/os/index.d.cts +1 -1
  156. package/dist/os/index.d.mts +1 -1
  157. package/dist/timer.cjs.map +1 -1
  158. package/dist/timer.d.cts +2 -0
  159. package/dist/timer.d.cts.map +1 -0
  160. package/dist/timer.d.mts +2 -0
  161. package/dist/timer.d.mts.map +1 -0
  162. package/dist/timer.mjs.map +1 -1
  163. package/package.json +5 -5
  164. package/src/Factory.ts +72 -72
  165. package/src/KyselyFactory.ts +330 -330
  166. package/src/ObjectionFactory.ts +354 -355
  167. package/src/PostgresKyselyMigrator.ts +37 -37
  168. package/src/PostgresMigrator.ts +107 -107
  169. package/src/PostgresObjectionMigrator.ts +91 -91
  170. package/src/VitestKyselyTransactionIsolator.ts +27 -27
  171. package/src/VitestObjectionTransactionIsolator.ts +39 -39
  172. package/src/VitestTransactionIsolator.ts +196 -195
  173. package/src/__tests__/Factory.spec.ts +163 -155
  174. package/src/__tests__/KyselyFactory.spec.ts +443 -439
  175. package/src/__tests__/ObjectionFactory.spec.ts +563 -557
  176. package/src/__tests__/PostgresKyselyMigrator.spec.ts +641 -641
  177. package/src/__tests__/PostgresMigrator.spec.ts +341 -341
  178. package/src/__tests__/PostgresObjectionMigrator.spec.ts +578 -578
  179. package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +114 -114
  180. package/src/__tests__/benchmark.spec.ts +140 -0
  181. package/src/__tests__/better-auth.spec.ts +15 -15
  182. package/src/__tests__/faker.spec.ts +226 -137
  183. package/src/__tests__/integration.spec.ts +597 -597
  184. package/src/__tests__/utilities.spec.ts +211 -0
  185. package/src/aws.ts +104 -104
  186. package/src/benchmark.ts +12 -12
  187. package/src/better-auth.ts +286 -301
  188. package/src/faker.ts +153 -153
  189. package/src/helpers.ts +6 -6
  190. package/src/kysely.ts +33 -33
  191. package/src/logger.ts +10 -10
  192. package/src/objection.ts +31 -31
  193. package/src/os/directory.ts +11 -10
  194. package/src/timer.ts +1 -1
  195. package/test/globalSetup.ts +45 -45
  196. package/test/helpers.ts +189 -189
  197. package/test/migrations/1749664623372_user.ts +13 -13
  198. package/tsconfig.json +9 -0
  199. package/vitest.config.ts +4 -4
  200. package/dist/PostgresKyselyMigrator-CfytARcA.cjs.map +0 -1
  201. package/dist/PostgresKyselyMigrator-upT-hmrz.mjs.map +0 -1
  202. package/dist/PostgresMigrator-DFcNdCvD.cjs.map +0 -1
  203. package/dist/PostgresMigrator-DbuJGAVy.mjs.map +0 -1
  204. package/dist/PostgresObjectionMigrator-BG6ymgnt.cjs.map +0 -1
  205. package/dist/PostgresObjectionMigrator-DPj2pOpX.mjs.map +0 -1
  206. package/dist/VitestKyselyTransactionIsolator-CNURW8y6.mjs.map +0 -1
  207. package/dist/VitestKyselyTransactionIsolator-EvDLk5zg.cjs.map +0 -1
  208. package/dist/VitestObjectionTransactionIsolator-CM5KTAFA.cjs.map +0 -1
  209. package/dist/VitestObjectionTransactionIsolator-jQFaCz0u.mjs.map +0 -1
@@ -1,11 +1,11 @@
1
1
  import type {
2
- ControlledTransaction,
3
- Insertable,
4
- Kysely,
5
- Selectable,
2
+ ControlledTransaction,
3
+ Insertable,
4
+ Kysely,
5
+ Selectable,
6
6
  } from 'kysely';
7
- import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory.ts';
8
- import { type FakerFactory, faker } from './faker.ts';
7
+ import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory';
8
+ import { type FakerFactory, faker } from './faker';
9
9
 
10
10
  /**
11
11
  * Factory implementation for Kysely ORM, providing test data creation utilities.
@@ -46,346 +46,346 @@ import { type FakerFactory, faker } from './faker.ts';
46
46
  * ```
47
47
  */
48
48
  export class KyselyFactory<
49
- DB,
50
- Builders extends Record<string, any>,
51
- Seeds extends Record<string, any>,
49
+ DB,
50
+ Builders extends Record<string, any>,
51
+ Seeds extends Record<string, any>,
52
52
  > extends Factory<Builders, Seeds> {
53
- /**
54
- * Creates a typed seed function with proper type inference.
55
- * Inherits from the base Factory class implementation.
56
- *
57
- * @template Seed - The seed function type
58
- * @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
59
- * @returns The same seed function with proper typing
60
- *
61
- * @example
62
- * ```typescript
63
- * const seeds = {
64
- * userWithPosts: KyselyFactory.createSeed(
65
- * async ({ attrs, factory }) => {
66
- * const user = await factory.insert('user', attrs);
67
- * await factory.insertMany(3, 'post', { userId: user.id });
68
- * return user;
69
- * },
70
- * ),
71
- * };
72
- * ```
73
- */
74
- static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
75
- return Factory.createSeed(seedFn);
76
- }
53
+ /**
54
+ * Creates a typed seed function with proper type inference.
55
+ * Inherits from the base Factory class implementation.
56
+ *
57
+ * @template Seed - The seed function type
58
+ * @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
59
+ * @returns The same seed function with proper typing
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const seeds = {
64
+ * userWithPosts: KyselyFactory.createSeed(
65
+ * async ({ attrs, factory }) => {
66
+ * const user = await factory.insert('user', attrs);
67
+ * await factory.insertMany(3, 'post', { userId: user.id });
68
+ * return user;
69
+ * },
70
+ * ),
71
+ * };
72
+ * ```
73
+ */
74
+ static override createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
75
+ return Factory.createSeed(seedFn);
76
+ }
77
77
 
78
- /**
79
- * Creates a new KyselyFactory instance.
80
- *
81
- * @param builders - Record of builder functions for creating individual entities
82
- * @param seeds - Record of seed functions for creating complex test scenarios
83
- * @param db - Kysely database instance or controlled transaction
84
- */
85
- constructor(
86
- private builders: Builders,
87
- private seeds: Seeds,
88
- private db: Kysely<DB> | ControlledTransaction<DB, []>,
89
- ) {
90
- super();
91
- }
78
+ /**
79
+ * Creates a new KyselyFactory instance.
80
+ *
81
+ * @param builders - Record of builder functions for creating individual entities
82
+ * @param seeds - Record of seed functions for creating complex test scenarios
83
+ * @param db - Kysely database instance or controlled transaction
84
+ */
85
+ constructor(
86
+ private builders: Builders,
87
+ private seeds: Seeds,
88
+ private db: Kysely<DB> | ControlledTransaction<DB, []>,
89
+ ) {
90
+ super();
91
+ }
92
92
 
93
- /**
94
- * Creates a typed builder function for a specific database table.
95
- * This is a utility method that helps create builders with proper type inference for Kysely.
96
- *
97
- * @template DB - The database schema type
98
- * @template TableName - The name of the table (must be a key of DB)
99
- * @template Attrs - The attributes type for the builder (defaults to Partial<Insertable>)
100
- * @template Factory - The factory instance type
101
- * @template Result - The result type (defaults to Selectable of the table)
102
- *
103
- * @param table - The name of the database table
104
- * @param defaults - Optional function to provide default values (receives destructured context)
105
- * @param autoInsert - Whether to automatically insert the record (default: true)
106
- * @returns A builder function that creates and optionally inserts records
107
- *
108
- * @example
109
- * ```typescript
110
- * // Create a simple builder with defaults - destructure only what you need
111
- * const userBuilder = KyselyFactory.createBuilder<DB, 'users'>('users',
112
- * ({ attrs, faker }) => ({
113
- * id: faker.string.uuid(),
114
- * name: faker.person.fullName(),
115
- * email: faker.internet.email(),
116
- * createdAt: new Date(),
117
- * ...attrs
118
- * })
119
- * );
120
- *
121
- * // Only need faker? Just destructure that
122
- * const leaveTypeBuilder = KyselyFactory.createBuilder<DB, 'leaveTypes'>('leaveTypes',
123
- * ({ faker }) => ({
124
- * name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
125
- * code: faker.string.alpha({ length: 3, casing: 'upper' }),
126
- * })
127
- * );
128
- *
129
- * // Create a builder that doesn't auto-insert (useful for nested inserts)
130
- * const addressBuilder = KyselyFactory.createBuilder<DB, 'addresses'>('addresses',
131
- * ({ attrs }) => ({
132
- * street: '123 Main St',
133
- * city: 'Anytown',
134
- * ...attrs
135
- * }),
136
- * false // Don't auto-insert
137
- * );
138
- * ```
139
- */
140
- static createBuilder<
141
- DB,
142
- TableName extends keyof DB & string,
143
- Attrs extends Partial<Insertable<DB[TableName]>> = Partial<
144
- Insertable<DB[TableName]>
145
- >,
146
- Factory = any,
147
- Result = Selectable<DB[TableName]>,
148
- >(
149
- table: TableName,
150
- defaults?: (context: {
151
- attrs: Attrs;
152
- factory: Factory;
153
- db: Kysely<DB>;
154
- faker: FakerFactory;
155
- }) =>
156
- | Partial<Insertable<DB[TableName]>>
157
- | Promise<Partial<Insertable<DB[TableName]>>>,
158
- autoInsert?: boolean,
159
- ): (
160
- attrs: Attrs,
161
- factory: Factory,
162
- db: Kysely<DB>,
163
- faker: FakerFactory,
164
- ) => Promise<Result> {
165
- return async (
166
- attrs: Attrs,
167
- factory: Factory,
168
- db: Kysely<DB>,
169
- fakerInstance: FakerFactory,
170
- ) => {
171
- // Start with attributes
172
- let data: Partial<Insertable<DB[TableName]>> = { ...attrs };
93
+ /**
94
+ * Creates a typed builder function for a specific database table.
95
+ * This is a utility method that helps create builders with proper type inference for Kysely.
96
+ *
97
+ * @template DB - The database schema type
98
+ * @template TableName - The name of the table (must be a key of DB)
99
+ * @template Attrs - The attributes type for the builder (defaults to Partial<Insertable>)
100
+ * @template Factory - The factory instance type
101
+ * @template Result - The result type (defaults to Selectable of the table)
102
+ *
103
+ * @param table - The name of the database table
104
+ * @param defaults - Optional function to provide default values (receives destructured context)
105
+ * @param autoInsert - Whether to automatically insert the record (default: true)
106
+ * @returns A builder function that creates and optionally inserts records
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * // Create a simple builder with defaults - destructure only what you need
111
+ * const userBuilder = KyselyFactory.createBuilder<DB, 'users'>('users',
112
+ * ({ attrs, faker }) => ({
113
+ * id: faker.string.uuid(),
114
+ * name: faker.person.fullName(),
115
+ * email: faker.internet.email(),
116
+ * createdAt: new Date(),
117
+ * ...attrs
118
+ * })
119
+ * );
120
+ *
121
+ * // Only need faker? Just destructure that
122
+ * const leaveTypeBuilder = KyselyFactory.createBuilder<DB, 'leaveTypes'>('leaveTypes',
123
+ * ({ faker }) => ({
124
+ * name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
125
+ * code: faker.string.alpha({ length: 3, casing: 'upper' }),
126
+ * })
127
+ * );
128
+ *
129
+ * // Create a builder that doesn't auto-insert (useful for nested inserts)
130
+ * const addressBuilder = KyselyFactory.createBuilder<DB, 'addresses'>('addresses',
131
+ * ({ attrs }) => ({
132
+ * street: '123 Main St',
133
+ * city: 'Anytown',
134
+ * ...attrs
135
+ * }),
136
+ * false // Don't auto-insert
137
+ * );
138
+ * ```
139
+ */
140
+ static createBuilder<
141
+ DB,
142
+ TableName extends keyof DB & string,
143
+ Attrs extends Partial<Insertable<DB[TableName]>> = Partial<
144
+ Insertable<DB[TableName]>
145
+ >,
146
+ Factory = any,
147
+ Result = Selectable<DB[TableName]>,
148
+ >(
149
+ table: TableName,
150
+ defaults?: (context: {
151
+ attrs: Attrs;
152
+ factory: Factory;
153
+ db: Kysely<DB>;
154
+ faker: FakerFactory;
155
+ }) =>
156
+ | Partial<Insertable<DB[TableName]>>
157
+ | Promise<Partial<Insertable<DB[TableName]>>>,
158
+ autoInsert?: boolean,
159
+ ): (
160
+ attrs: Attrs,
161
+ factory: Factory,
162
+ db: Kysely<DB>,
163
+ faker: FakerFactory,
164
+ ) => Promise<Result> {
165
+ return async (
166
+ attrs: Attrs,
167
+ factory: Factory,
168
+ db: Kysely<DB>,
169
+ fakerInstance: FakerFactory,
170
+ ) => {
171
+ // Start with attributes
172
+ let data: Partial<Insertable<DB[TableName]>> = { ...attrs };
173
173
 
174
- // Apply defaults
175
- if (defaults) {
176
- const defaultValues = await defaults({
177
- attrs,
178
- factory,
179
- db,
180
- faker: fakerInstance,
181
- });
182
- data = { ...defaultValues, ...data };
183
- }
174
+ // Apply defaults
175
+ if (defaults) {
176
+ const defaultValues = await defaults({
177
+ attrs,
178
+ factory,
179
+ db,
180
+ faker: fakerInstance,
181
+ });
182
+ data = { ...defaultValues, ...data };
183
+ }
184
184
 
185
- // Handle insertion based on autoInsert flag
186
- if (autoInsert !== false) {
187
- // Auto insert is enabled by default
188
- const result = await db
189
- .insertInto(table)
190
- .values(data as Insertable<DB[TableName]>)
191
- .returningAll()
192
- .executeTakeFirst();
185
+ // Handle insertion based on autoInsert flag
186
+ if (autoInsert !== false) {
187
+ // Auto insert is enabled by default
188
+ const result = await db
189
+ .insertInto(table)
190
+ .values(data as Insertable<DB[TableName]>)
191
+ .returningAll()
192
+ .executeTakeFirst();
193
193
 
194
- if (!result) {
195
- throw new Error(`Failed to insert into ${table}`);
196
- }
194
+ if (!result) {
195
+ throw new Error(`Failed to insert into ${table}`);
196
+ }
197
197
 
198
- return result as Result;
199
- } else {
200
- // Return object for factory to handle insertion
201
- return { table, data } as any;
202
- }
203
- };
204
- }
198
+ return result as Result;
199
+ } else {
200
+ // Return object for factory to handle insertion
201
+ return { table, data } as any;
202
+ }
203
+ };
204
+ }
205
205
 
206
- /**
207
- * Inserts a single record into the database using the specified builder.
208
- * The builder function is responsible for generating the record data with defaults
209
- * and the factory handles the actual database insertion.
210
- *
211
- * @template K - The builder name (must be a key of Builders)
212
- * @param builderName - The name of the builder to use
213
- * @param attrs - Optional attributes to override builder defaults
214
- * @returns A promise resolving to the inserted record
215
- * @throws Error if the specified builder doesn't exist
216
- *
217
- * @example
218
- * ```typescript
219
- * // Insert with defaults
220
- * const user = await factory.insert('user');
221
- *
222
- * // Insert with overrides
223
- * const adminUser = await factory.insert('user', {
224
- * email: 'admin@example.com',
225
- * role: 'admin'
226
- * });
227
- *
228
- * // Use the inserted record
229
- * const post = await factory.insert('post', {
230
- * userId: user.id,
231
- * title: 'My First Post'
232
- * });
233
- * ```
234
- */
235
- async insert<K extends keyof Builders>(
236
- builderName: K,
237
- attrs?: Parameters<Builders[K]>[0],
238
- ): Promise<Awaited<ReturnType<Builders[K]>>> {
239
- if (!(builderName in this.builders)) {
240
- throw new Error(
241
- `Factory "${
242
- builderName as string
243
- }" does not exist. Make sure it is correct and registered in src/test/setup.ts`,
244
- );
245
- }
206
+ /**
207
+ * Inserts a single record into the database using the specified builder.
208
+ * The builder function is responsible for generating the record data with defaults
209
+ * and the factory handles the actual database insertion.
210
+ *
211
+ * @template K - The builder name (must be a key of Builders)
212
+ * @param builderName - The name of the builder to use
213
+ * @param attrs - Optional attributes to override builder defaults
214
+ * @returns A promise resolving to the inserted record
215
+ * @throws Error if the specified builder doesn't exist
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * // Insert with defaults
220
+ * const user = await factory.insert('user');
221
+ *
222
+ * // Insert with overrides
223
+ * const adminUser = await factory.insert('user', {
224
+ * email: 'admin@example.com',
225
+ * role: 'admin'
226
+ * });
227
+ *
228
+ * // Use the inserted record
229
+ * const post = await factory.insert('post', {
230
+ * userId: user.id,
231
+ * title: 'My First Post'
232
+ * });
233
+ * ```
234
+ */
235
+ async insert<K extends keyof Builders>(
236
+ builderName: K,
237
+ attrs?: Parameters<Builders[K]>[0],
238
+ ): Promise<Awaited<ReturnType<Builders[K]>>> {
239
+ if (!(builderName in this.builders)) {
240
+ throw new Error(
241
+ `Factory "${
242
+ builderName as string
243
+ }" does not exist. Make sure it is correct and registered in src/test/setup.ts`,
244
+ );
245
+ }
246
246
 
247
- const result = await this.builders[builderName](
248
- attrs || {},
249
- this,
250
- this.db,
251
- faker,
252
- );
247
+ const result = await this.builders[builderName](
248
+ attrs || {},
249
+ this,
250
+ this.db,
251
+ faker,
252
+ );
253
253
 
254
- // For Kysely, we expect the builder to return an object with table and data properties
255
- // or to handle the insertion itself and return the inserted record
256
- if (
257
- result &&
258
- typeof result === 'object' &&
259
- 'table' in result &&
260
- 'data' in result
261
- ) {
262
- // If the builder returns {table: string, data: object}, we insert it
263
- const inserted = await this.db
264
- .insertInto(result.table)
265
- .values(result.data)
266
- .returningAll()
267
- .executeTakeFirst();
254
+ // For Kysely, we expect the builder to return an object with table and data properties
255
+ // or to handle the insertion itself and return the inserted record
256
+ if (
257
+ result &&
258
+ typeof result === 'object' &&
259
+ 'table' in result &&
260
+ 'data' in result
261
+ ) {
262
+ // If the builder returns {table: string, data: object}, we insert it
263
+ const inserted = await this.db
264
+ .insertInto(result.table)
265
+ .values(result.data)
266
+ .returningAll()
267
+ .executeTakeFirst();
268
268
 
269
- return inserted as any;
270
- }
269
+ return inserted as any;
270
+ }
271
271
 
272
- // Otherwise, assume the builder handled the insertion itself
273
- return result;
274
- }
272
+ // Otherwise, assume the builder handled the insertion itself
273
+ return result;
274
+ }
275
275
 
276
- /**
277
- * Inserts multiple records into the database using the specified builder.
278
- * Supports both static attributes and dynamic attribute generation via a function.
279
- *
280
- * @template K - The builder name (must be a key of Builders)
281
- * @param count - The number of records to insert
282
- * @param builderName - The name of the builder to use
283
- * @param attrs - Static attributes or a function that generates attributes for each record
284
- * @returns A promise resolving to an array of inserted records
285
- * @throws Error if the specified builder doesn't exist
286
- *
287
- * @example
288
- * ```typescript
289
- * // Insert multiple with same attributes
290
- * const users = await factory.insertMany(5, 'user', { role: 'member' });
291
- *
292
- * // Insert multiple with dynamic attributes
293
- * const posts = await factory.insertMany(10, 'post', (idx, faker) => ({
294
- * title: `Post ${idx + 1}`,
295
- * content: faker.lorem.paragraph(),
296
- * publishedAt: faker.date.past()
297
- * }));
298
- *
299
- * // Create users with sequential emails
300
- * const admins = await factory.insertMany(3, 'user', (idx) => ({
301
- * email: `admin${idx + 1}@example.com`,
302
- * role: 'admin'
303
- * }));
304
- * ```
305
- */
306
- // Method overloads for better type inference
307
- async insertMany<K extends keyof Builders>(
308
- count: number,
309
- builderName: K,
310
- attrs?: Parameters<Builders[K]>[0],
311
- ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
312
- async insertMany<K extends keyof Builders>(
313
- count: number,
314
- builderName: K,
315
- attrs: (
316
- idx: number,
317
- faker: FakerFactory,
318
- ) => Parameters<Builders[K]>[0] | Promise<Parameters<Builders[K]>[0]>,
319
- ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
320
- async insertMany<K extends keyof Builders>(
321
- count: number,
322
- builderName: K,
323
- attrs?: any,
324
- ): Promise<Awaited<ReturnType<Builders[K]>>[]> {
325
- if (!(builderName in this.builders)) {
326
- throw new Error(
327
- `Builder "${
328
- builderName as string
329
- }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
330
- );
331
- }
276
+ /**
277
+ * Inserts multiple records into the database using the specified builder.
278
+ * Supports both static attributes and dynamic attribute generation via a function.
279
+ *
280
+ * @template K - The builder name (must be a key of Builders)
281
+ * @param count - The number of records to insert
282
+ * @param builderName - The name of the builder to use
283
+ * @param attrs - Static attributes or a function that generates attributes for each record
284
+ * @returns A promise resolving to an array of inserted records
285
+ * @throws Error if the specified builder doesn't exist
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * // Insert multiple with same attributes
290
+ * const users = await factory.insertMany(5, 'user', { role: 'member' });
291
+ *
292
+ * // Insert multiple with dynamic attributes
293
+ * const posts = await factory.insertMany(10, 'post', (idx, faker) => ({
294
+ * title: `Post ${idx + 1}`,
295
+ * content: faker.lorem.paragraph(),
296
+ * publishedAt: faker.date.past()
297
+ * }));
298
+ *
299
+ * // Create users with sequential emails
300
+ * const admins = await factory.insertMany(3, 'user', (idx) => ({
301
+ * email: `admin${idx + 1}@example.com`,
302
+ * role: 'admin'
303
+ * }));
304
+ * ```
305
+ */
306
+ // Method overloads for better type inference
307
+ async insertMany<K extends keyof Builders>(
308
+ count: number,
309
+ builderName: K,
310
+ attrs?: Parameters<Builders[K]>[0],
311
+ ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
312
+ async insertMany<K extends keyof Builders>(
313
+ count: number,
314
+ builderName: K,
315
+ attrs: (
316
+ idx: number,
317
+ faker: FakerFactory,
318
+ ) => Parameters<Builders[K]>[0] | Promise<Parameters<Builders[K]>[0]>,
319
+ ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
320
+ async insertMany<K extends keyof Builders>(
321
+ count: number,
322
+ builderName: K,
323
+ attrs?: any,
324
+ ): Promise<Awaited<ReturnType<Builders[K]>>[]> {
325
+ if (!(builderName in this.builders)) {
326
+ throw new Error(
327
+ `Builder "${
328
+ builderName as string
329
+ }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
330
+ );
331
+ }
332
332
 
333
- const promises: Promise<any>[] = [];
333
+ const promises: Promise<any>[] = [];
334
334
 
335
- for (let i = 0; i < count; i++) {
336
- const newAttrs =
337
- typeof attrs === 'function' ? await attrs(i, faker) : attrs;
338
- promises.push(this.insert(builderName, newAttrs));
339
- }
335
+ for (let i = 0; i < count; i++) {
336
+ const newAttrs =
337
+ typeof attrs === 'function' ? await attrs(i, faker) : attrs;
338
+ promises.push(this.insert(builderName, newAttrs));
339
+ }
340
340
 
341
- return Promise.all(promises);
342
- }
341
+ return Promise.all(promises);
342
+ }
343
343
 
344
- /**
345
- * Executes a seed function to create complex test scenarios with multiple related records.
346
- * Seeds are useful for setting up complete test environments with realistic data relationships.
347
- *
348
- * @template K - The seed name (must be a key of Seeds)
349
- * @param seedName - The name of the seed to execute
350
- * @param attrs - Optional configuration attributes for the seed
351
- * @returns The result of the seed function (typically the primary record created)
352
- * @throws Error if the specified seed doesn't exist
353
- *
354
- * @example
355
- * ```typescript
356
- * // Execute a simple seed
357
- * const user = await factory.seed('userWithProfile');
358
- *
359
- * // Execute a seed with configuration
360
- * const author = await factory.seed('authorWithBooks', {
361
- * bookCount: 5,
362
- * includeReviews: true
363
- * });
364
- *
365
- * // Use seed result in tests
366
- * const company = await factory.seed('companyWithDepartments', {
367
- * departmentCount: 3,
368
- * employeesPerDepartment: 10
369
- * });
370
- * expect(company.departments).toHaveLength(3);
371
- * ```
372
- */
373
- seed<K extends keyof Seeds>(
374
- seedName: K,
375
- attrs?: ExtractSeedAttrs<Seeds[K]>,
376
- ): ReturnType<Seeds[K]> {
377
- if (!(seedName in this.seeds)) {
378
- throw new Error(
379
- `Seed "${
380
- seedName as string
381
- }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
382
- );
383
- }
344
+ /**
345
+ * Executes a seed function to create complex test scenarios with multiple related records.
346
+ * Seeds are useful for setting up complete test environments with realistic data relationships.
347
+ *
348
+ * @template K - The seed name (must be a key of Seeds)
349
+ * @param seedName - The name of the seed to execute
350
+ * @param attrs - Optional configuration attributes for the seed
351
+ * @returns The result of the seed function (typically the primary record created)
352
+ * @throws Error if the specified seed doesn't exist
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * // Execute a simple seed
357
+ * const user = await factory.seed('userWithProfile');
358
+ *
359
+ * // Execute a seed with configuration
360
+ * const author = await factory.seed('authorWithBooks', {
361
+ * bookCount: 5,
362
+ * includeReviews: true
363
+ * });
364
+ *
365
+ * // Use seed result in tests
366
+ * const company = await factory.seed('companyWithDepartments', {
367
+ * departmentCount: 3,
368
+ * employeesPerDepartment: 10
369
+ * });
370
+ * expect(company.departments).toHaveLength(3);
371
+ * ```
372
+ */
373
+ seed<K extends keyof Seeds>(
374
+ seedName: K,
375
+ attrs?: ExtractSeedAttrs<Seeds[K]>,
376
+ ): ReturnType<Seeds[K]> {
377
+ if (!(seedName in this.seeds)) {
378
+ throw new Error(
379
+ `Seed "${
380
+ seedName as string
381
+ }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
382
+ );
383
+ }
384
384
 
385
- return this.seeds[seedName]({
386
- attrs: attrs || {},
387
- factory: this,
388
- db: this.db,
389
- });
390
- }
385
+ return this.seeds[seedName]({
386
+ attrs: attrs || {},
387
+ factory: this,
388
+ db: this.db,
389
+ });
390
+ }
391
391
  }