@xacos/orm 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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -0
  3. package/dist/XModel.d.ts +42 -0
  4. package/dist/XModel.d.ts.map +1 -0
  5. package/dist/XModel.js +240 -0
  6. package/dist/XModel.js.map +1 -0
  7. package/dist/XModel.test.d.ts +2 -0
  8. package/dist/XModel.test.d.ts.map +1 -0
  9. package/dist/XModel.test.js +119 -0
  10. package/dist/XModel.test.js.map +1 -0
  11. package/dist/XMongoModel.d.ts +25 -0
  12. package/dist/XMongoModel.d.ts.map +1 -0
  13. package/dist/XMongoModel.js +86 -0
  14. package/dist/XMongoModel.js.map +1 -0
  15. package/dist/XMongoModel.test.d.ts +2 -0
  16. package/dist/XMongoModel.test.d.ts.map +1 -0
  17. package/dist/XMongoModel.test.js +14 -0
  18. package/dist/XMongoModel.test.js.map +1 -0
  19. package/dist/__tests__/sqlite.integration.test.d.ts +2 -0
  20. package/dist/__tests__/sqlite.integration.test.d.ts.map +1 -0
  21. package/dist/__tests__/sqlite.integration.test.js +106 -0
  22. package/dist/__tests__/sqlite.integration.test.js.map +1 -0
  23. package/dist/connection/db.d.ts +25 -0
  24. package/dist/connection/db.d.ts.map +1 -0
  25. package/dist/connection/db.js +100 -0
  26. package/dist/connection/db.js.map +1 -0
  27. package/dist/connection/drivers.d.ts +3 -0
  28. package/dist/connection/drivers.d.ts.map +1 -0
  29. package/dist/connection/drivers.js +58 -0
  30. package/dist/connection/drivers.js.map +1 -0
  31. package/dist/connection/mongoUri.d.ts +6 -0
  32. package/dist/connection/mongoUri.d.ts.map +1 -0
  33. package/dist/connection/mongoUri.js +22 -0
  34. package/dist/connection/mongoUri.js.map +1 -0
  35. package/dist/decorators.d.ts +47 -0
  36. package/dist/decorators.d.ts.map +1 -0
  37. package/dist/decorators.js +149 -0
  38. package/dist/decorators.js.map +1 -0
  39. package/dist/factories/Factory.d.ts +34 -0
  40. package/dist/factories/Factory.d.ts.map +1 -0
  41. package/dist/factories/Factory.js +48 -0
  42. package/dist/factories/Factory.js.map +1 -0
  43. package/dist/factories/Factory.test.d.ts +2 -0
  44. package/dist/factories/Factory.test.d.ts.map +1 -0
  45. package/dist/factories/Factory.test.js +16 -0
  46. package/dist/factories/Factory.test.js.map +1 -0
  47. package/dist/index.d.ts +16 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +16 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/migrations/Migration.d.ts +6 -0
  52. package/dist/migrations/Migration.d.ts.map +1 -0
  53. package/dist/migrations/Migration.js +3 -0
  54. package/dist/migrations/Migration.js.map +1 -0
  55. package/dist/migrations/MigrationRunner.d.ts +14 -0
  56. package/dist/migrations/MigrationRunner.d.ts.map +1 -0
  57. package/dist/migrations/MigrationRunner.js +142 -0
  58. package/dist/migrations/MigrationRunner.js.map +1 -0
  59. package/dist/migrations/MigrationRunner.test.d.ts +2 -0
  60. package/dist/migrations/MigrationRunner.test.d.ts.map +1 -0
  61. package/dist/migrations/MigrationRunner.test.js +26 -0
  62. package/dist/migrations/MigrationRunner.test.js.map +1 -0
  63. package/dist/migrations/Schema.d.ts +7 -0
  64. package/dist/migrations/Schema.d.ts.map +1 -0
  65. package/dist/migrations/Schema.js +17 -0
  66. package/dist/migrations/Schema.js.map +1 -0
  67. package/dist/migrations/columnHelpers.d.ts +9 -0
  68. package/dist/migrations/columnHelpers.d.ts.map +1 -0
  69. package/dist/migrations/columnHelpers.js +15 -0
  70. package/dist/migrations/columnHelpers.js.map +1 -0
  71. package/dist/seeders/Seeder.d.ts +8 -0
  72. package/dist/seeders/Seeder.d.ts.map +1 -0
  73. package/dist/seeders/Seeder.js +3 -0
  74. package/dist/seeders/Seeder.js.map +1 -0
  75. package/dist/seeders/SeederRunner.d.ts +20 -0
  76. package/dist/seeders/SeederRunner.d.ts.map +1 -0
  77. package/dist/seeders/SeederRunner.js +68 -0
  78. package/dist/seeders/SeederRunner.js.map +1 -0
  79. package/dist/seeders/SeederRunner.test.d.ts +2 -0
  80. package/dist/seeders/SeederRunner.test.d.ts.map +1 -0
  81. package/dist/seeders/SeederRunner.test.js +44 -0
  82. package/dist/seeders/SeederRunner.test.js.map +1 -0
  83. package/dist/utils/paginate.d.ts +14 -0
  84. package/dist/utils/paginate.d.ts.map +1 -0
  85. package/dist/utils/paginate.js +28 -0
  86. package/dist/utils/paginate.js.map +1 -0
  87. package/dist/utils/paginate.test.d.ts +2 -0
  88. package/dist/utils/paginate.test.d.ts.map +1 -0
  89. package/dist/utils/paginate.test.js +23 -0
  90. package/dist/utils/paginate.test.js.map +1 -0
  91. package/package.json +75 -0
  92. package/src/XModel.test.ts +147 -0
  93. package/src/XModel.ts +301 -0
  94. package/src/XMongoModel.test.ts +16 -0
  95. package/src/XMongoModel.ts +119 -0
  96. package/src/__tests__/sqlite.integration.test.ts +116 -0
  97. package/src/connection/db.ts +127 -0
  98. package/src/connection/drivers.ts +65 -0
  99. package/src/connection/mongoUri.ts +25 -0
  100. package/src/decorators.ts +200 -0
  101. package/src/factories/Factory.test.ts +18 -0
  102. package/src/factories/Factory.ts +61 -0
  103. package/src/index.ts +18 -0
  104. package/src/migrations/Migration.ts +8 -0
  105. package/src/migrations/MigrationRunner.test.ts +33 -0
  106. package/src/migrations/MigrationRunner.ts +171 -0
  107. package/src/migrations/Schema.ts +20 -0
  108. package/src/migrations/columnHelpers.ts +28 -0
  109. package/src/seeders/Seeder.ts +8 -0
  110. package/src/seeders/SeederRunner.test.ts +62 -0
  111. package/src/seeders/SeederRunner.ts +76 -0
  112. package/src/types/bun-test.d.ts +8 -0
  113. package/src/utils/paginate.test.ts +24 -0
  114. package/src/utils/paginate.ts +37 -0
@@ -0,0 +1,48 @@
1
+ import { faker } from '@faker-js/faker';
2
+ class FactoryBuilder {
3
+ constructor(count) {
4
+ this.count = count;
5
+ }
6
+ /**
7
+ * Create `count` instances of a model using the provided definition function.
8
+ *
9
+ * @example
10
+ * await Factory.times(10).create(User, () => ({
11
+ * name: Factory.faker.person.fullName(),
12
+ * email: Factory.faker.internet.email(),
13
+ * }));
14
+ */
15
+ async create(ModelClass, definition) {
16
+ const results = [];
17
+ for (let i = 0; i < this.count; i++) {
18
+ const data = await definition();
19
+ const instance = await ModelClass.create(data);
20
+ results.push(instance);
21
+ }
22
+ return results;
23
+ }
24
+ /**
25
+ * Build plain objects without persisting to DB.
26
+ * Useful for unit tests that don't need a DB.
27
+ */
28
+ async build(definition) {
29
+ const results = [];
30
+ for (let i = 0; i < this.count; i++) {
31
+ results.push((await definition()));
32
+ }
33
+ return results;
34
+ }
35
+ }
36
+ export class Factory {
37
+ /** Access Faker directly: Factory.faker.person.fullName() */
38
+ static { this.faker = faker; }
39
+ static times(count) {
40
+ return new FactoryBuilder(count);
41
+ }
42
+ /** Shorthand for Factory.times(1).create(...) */
43
+ static async create(ModelClass, definition) {
44
+ const [result] = await Factory.times(1).create(ModelClass, definition);
45
+ return result;
46
+ }
47
+ }
48
+ //# sourceMappingURL=Factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../../src/factories/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAKxC,MAAM,cAAc;IAClB,YAAoB,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;IAAG,CAAC;IAErC;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CACV,UAAoE,EACpE,UAA2B;QAE3B,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,UAAU,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,IAAkB,CAAC,CAAC;YAC7D,OAAO,CAAC,IAAI,CAAC,QAAa,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAI,UAA2B;QACxC,MAAM,OAAO,GAAQ,EAAE,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,UAAU,EAAE,CAAM,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF;AAED,MAAM,OAAO,OAAO;IAClB,6DAA6D;aACtD,UAAK,GAAG,KAAK,CAAC;IAErB,MAAM,CAAC,KAAK,CAAC,KAAa;QACxB,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,iDAAiD;IACjD,MAAM,CAAC,KAAK,CAAC,MAAM,CACjB,UAAoE,EACpE,UAA2B;QAE3B,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACvE,OAAO,MAAO,CAAC;IACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Factory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Factory.test.d.ts","sourceRoot":"","sources":["../../src/factories/Factory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from 'bun:test';
2
+ import { Factory } from './Factory';
3
+ describe('Factory', () => {
4
+ it('builds N plain objects', async () => {
5
+ const items = await Factory.times(3).build(() => ({
6
+ name: Factory.faker.person.fullName(),
7
+ }));
8
+ expect(items).toHaveLength(3);
9
+ expect(typeof items[0]?.name).toBe('string');
10
+ });
11
+ it('times(0) returns empty array', async () => {
12
+ const items = await Factory.times(0).build(() => ({ x: 1 }));
13
+ expect(items).toHaveLength(0);
14
+ });
15
+ });
16
+ //# sourceMappingURL=Factory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Factory.test.js","sourceRoot":"","sources":["../../src/factories/Factory.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,EAAE,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;YAChD,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;SACtC,CAAC,CAAC,CAAC;QACJ,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7D,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ export { initDb, closeDb, getKnex, initMongo, initOrmFromEnv, getMongoose, isKnexInitialized, isMongooseConnected, type InitDbOptions, type InitMongoOptions } from "./connection/db";
2
+ export { buildKnexConfig } from "./connection/drivers";
3
+ export { buildMongoUri } from "./connection/mongoUri";
4
+ export { XModel, RelationScope } from "./XModel";
5
+ export { default } from "./XModel";
6
+ export { XMongoModel } from "./XMongoModel";
7
+ export * from "./decorators";
8
+ export { Migration } from "./migrations/Migration";
9
+ export { Schema } from "./migrations/Schema";
10
+ export { MigrationRunner } from "./migrations/MigrationRunner";
11
+ export { augmentTable, type XCreateTableBuilder } from "./migrations/columnHelpers";
12
+ export { Seeder } from "./seeders/Seeder";
13
+ export { SeederRunner } from "./seeders/SeederRunner";
14
+ export { Factory } from "./factories/Factory";
15
+ export { paginate } from "./utils/paginate";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACtL,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { initDb, closeDb, getKnex, initMongo, initOrmFromEnv, getMongoose, isKnexInitialized, isMongooseConnected } from "./connection/db";
2
+ export { buildKnexConfig } from "./connection/drivers";
3
+ export { buildMongoUri } from "./connection/mongoUri";
4
+ export { XModel, RelationScope } from "./XModel";
5
+ export { default } from "./XModel";
6
+ export { XMongoModel } from "./XMongoModel";
7
+ export * from "./decorators";
8
+ export { Migration } from "./migrations/Migration";
9
+ export { Schema } from "./migrations/Schema";
10
+ export { MigrationRunner } from "./migrations/MigrationRunner";
11
+ export { augmentTable } from "./migrations/columnHelpers";
12
+ export { Seeder } from "./seeders/Seeder";
13
+ export { SeederRunner } from "./seeders/SeederRunner";
14
+ export { Factory } from "./factories/Factory";
15
+ export { paginate } from "./utils/paginate";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAA6C,MAAM,iBAAiB,CAAC;AACtL,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAA4B,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Schema } from "./Schema";
2
+ export declare abstract class Migration {
3
+ abstract up(schema: Schema): Promise<void>;
4
+ abstract down(schema: Schema): Promise<void>;
5
+ }
6
+ //# sourceMappingURL=Migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Migration.d.ts","sourceRoot":"","sources":["../../src/migrations/Migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,8BAAsB,SAAS;IAC7B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1C,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAC7C"}
@@ -0,0 +1,3 @@
1
+ export class Migration {
2
+ }
3
+ //# sourceMappingURL=Migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Migration.js","sourceRoot":"","sources":["../../src/migrations/Migration.ts"],"names":[],"mappings":"AAEA,MAAM,OAAgB,SAAS;CAI9B"}
@@ -0,0 +1,14 @@
1
+ export declare class MigrationRunner {
2
+ private readonly schema;
3
+ private ensureMigrationsTable;
4
+ getPending(migrationsDir: string): Promise<string[]>;
5
+ run(migrationsDir: string): Promise<void>;
6
+ rollback(migrationsDir: string): Promise<void>;
7
+ fresh(migrationsDir: string): Promise<void>;
8
+ status(migrationsDir: string): Promise<Array<{
9
+ name: string;
10
+ ran: boolean;
11
+ batch?: number | undefined;
12
+ }>>;
13
+ }
14
+ //# sourceMappingURL=MigrationRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationRunner.d.ts","sourceRoot":"","sources":["../../src/migrations/MigrationRunner.ts"],"names":[],"mappings":"AA6CA,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;YAEzB,qBAAqB;IAa7B,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAqBpD,GAAG,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBzC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B9C,KAAK,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3C,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;CA0BhH"}
@@ -0,0 +1,142 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { getKnex } from "../connection/db";
5
+ import { Schema } from "./Schema";
6
+ async function listUserTables(knex) {
7
+ const client = knex.client.config.client;
8
+ if (client === "better-sqlite3" || client === "sqlite3") {
9
+ const rows = (await knex
10
+ .select("name")
11
+ .from("sqlite_master")
12
+ .where("type", "table")
13
+ .whereNotIn("name", ["sqlite_sequence", "sqlite_stat1"]));
14
+ return rows.map((row) => row.name);
15
+ }
16
+ if (client === "mysql2" || client === "mysql") {
17
+ const dbName = knex.client.config.connection.database;
18
+ if (!dbName)
19
+ return [];
20
+ const rows = (await knex
21
+ .select("TABLE_NAME")
22
+ .from("information_schema.tables")
23
+ .where({
24
+ table_schema: dbName,
25
+ table_type: "BASE TABLE",
26
+ }));
27
+ return rows.map((row) => row.TABLE_NAME);
28
+ }
29
+ if (client === "pg") {
30
+ const rows = (await knex
31
+ .select("tablename")
32
+ .from("pg_tables")
33
+ .where("schemaname", "public"));
34
+ return rows.map((row) => row.tablename);
35
+ }
36
+ throw new Error(`[XAOCS ORM] Unsupported SQL client for migrations: ${String(client)}`);
37
+ }
38
+ export class MigrationRunner {
39
+ constructor() {
40
+ this.schema = new Schema();
41
+ }
42
+ async ensureMigrationsTable() {
43
+ const knex = getKnex();
44
+ const exists = await knex.schema.hasTable("xacos_migrations");
45
+ if (exists)
46
+ return;
47
+ await knex.schema.createTable("xacos_migrations", (table) => {
48
+ table.increments("id").primary();
49
+ table.string("name", 255).notNullable();
50
+ table.integer("batch").notNullable();
51
+ table.timestamp("ran_at").defaultTo(knex.fn.now());
52
+ });
53
+ }
54
+ async getPending(migrationsDir) {
55
+ await this.ensureMigrationsTable();
56
+ const knex = getKnex();
57
+ const ranRows = (await knex.select("name").from("xacos_migrations"));
58
+ const ran = new Set(ranRows.map((row) => row.name));
59
+ let files;
60
+ try {
61
+ files = readdirSync(migrationsDir);
62
+ }
63
+ catch {
64
+ return [];
65
+ }
66
+ return files
67
+ .filter((file) => file.endsWith(".ts") || file.endsWith(".js"))
68
+ .filter((file) => !ran.has(file))
69
+ .sort((a, b) => a.localeCompare(b));
70
+ }
71
+ async run(migrationsDir) {
72
+ await this.ensureMigrationsTable();
73
+ const knex = getKnex();
74
+ const pending = await this.getPending(migrationsDir);
75
+ if (pending.length === 0)
76
+ return;
77
+ const lastBatchRow = await knex("xacos_migrations").max({ b: "batch" }).first();
78
+ const batch = Number(lastBatchRow?.b ?? 0) + 1;
79
+ for (const file of pending) {
80
+ const abs = join(migrationsDir, file);
81
+ const mod = (await import(pathToFileURL(abs).href));
82
+ const MigrationClass = mod.default;
83
+ if (!MigrationClass) {
84
+ throw new Error(`[XAOCS ORM] Migration file must default-export a Migration class: ${file}`);
85
+ }
86
+ const migration = new MigrationClass();
87
+ await migration.up(this.schema);
88
+ await knex("xacos_migrations").insert({ name: file, batch, ran_at: knex.fn.now() });
89
+ }
90
+ }
91
+ async rollback(migrationsDir) {
92
+ await this.ensureMigrationsTable();
93
+ const knex = getKnex();
94
+ const lastBatchRow = await knex("xacos_migrations").max({ b: "batch" }).first();
95
+ const lastBatch = Number(lastBatchRow?.b ?? 0);
96
+ if (!lastBatch)
97
+ return;
98
+ const rows = (await knex("xacos_migrations").where("batch", lastBatch).orderBy("id", "desc"));
99
+ const schema = new Schema();
100
+ for (const row of rows) {
101
+ const abs = join(migrationsDir, row.name);
102
+ const mod = (await import(pathToFileURL(abs).href));
103
+ const MigrationClass = mod.default;
104
+ if (!MigrationClass) {
105
+ throw new Error(`[XAOCS ORM] Migration file must default-export a Migration class: ${row.name}`);
106
+ }
107
+ const migration = new MigrationClass();
108
+ await migration.down(schema);
109
+ await knex("xacos_migrations").where("id", row.id).del();
110
+ }
111
+ }
112
+ async fresh(migrationsDir) {
113
+ const knex = getKnex();
114
+ const tables = await listUserTables(knex);
115
+ for (const name of tables) {
116
+ await knex.schema.dropTableIfExists(name);
117
+ }
118
+ await this.run(migrationsDir);
119
+ }
120
+ async status(migrationsDir) {
121
+ await this.ensureMigrationsTable();
122
+ const knex = getKnex();
123
+ const ranRows = (await knex.select("name", "batch").from("xacos_migrations"));
124
+ const ranMap = new Map(ranRows.map((row) => [row.name, row.batch]));
125
+ let files;
126
+ try {
127
+ files = readdirSync(migrationsDir);
128
+ }
129
+ catch {
130
+ files = [];
131
+ }
132
+ const migrationFiles = files
133
+ .filter((file) => file.endsWith(".ts") || file.endsWith(".js"))
134
+ .sort((a, b) => a.localeCompare(b));
135
+ return migrationFiles.map((file) => ({
136
+ name: file,
137
+ ran: ranMap.has(file),
138
+ batch: ranMap.get(file),
139
+ }));
140
+ }
141
+ }
142
+ //# sourceMappingURL=MigrationRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationRunner.js","sourceRoot":"","sources":["../../src/migrations/MigrationRunner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=MigrationRunner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationRunner.test.d.ts","sourceRoot":"","sources":["../../src/migrations/MigrationRunner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,26 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "bun:test";
2
+ import { join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { closeDb, getKnex, initDb, MigrationRunner } from "../index";
5
+ describe("MigrationRunner", () => {
6
+ const fixturesDir = join(fileURLToPath(new URL(".", import.meta.url)), "..", "..", "test-fixtures", "migrations");
7
+ beforeAll(() => {
8
+ initDb({ __memorySqlite: true });
9
+ });
10
+ afterAll(async () => {
11
+ await closeDb();
12
+ });
13
+ it("runs pending migrations, rolls back the last batch, and reapplies on fresh", async () => {
14
+ const runner = new MigrationRunner();
15
+ const knex = getKnex();
16
+ await runner.run(fixturesDir);
17
+ expect(await knex.schema.hasTable("migration_test")).toBe(true);
18
+ expect((await runner.getPending(fixturesDir)).length).toBe(0);
19
+ await runner.rollback(fixturesDir);
20
+ expect(await knex.schema.hasTable("migration_test")).toBe(false);
21
+ await runner.fresh(fixturesDir);
22
+ expect(await knex.schema.hasTable("migration_test")).toBe(true);
23
+ expect(await knex.schema.hasTable("xacos_migrations")).toBe(true);
24
+ });
25
+ });
26
+ //# sourceMappingURL=MigrationRunner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationRunner.test.js","sourceRoot":"","sources":["../../src/migrations/MigrationRunner.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAErE,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;IAElH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,OAAO,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QAEvB,MAAM,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC9B,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE9D,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEjE,MAAM,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type XCreateTableBuilder } from "./columnHelpers";
2
+ export declare class Schema {
3
+ createTable(name: string, callback: (table: XCreateTableBuilder) => void): Promise<void>;
4
+ dropTable(name: string): Promise<void>;
5
+ hasTable(name: string): Promise<boolean>;
6
+ }
7
+ //# sourceMappingURL=Schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../src/migrations/Schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,qBAAa,MAAM;IACX,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG/C"}
@@ -0,0 +1,17 @@
1
+ import { getKnex } from "../connection/db";
2
+ import { augmentTable } from "./columnHelpers";
3
+ export class Schema {
4
+ async createTable(name, callback) {
5
+ const knex = getKnex();
6
+ await knex.schema.createTable(name, (table) => {
7
+ callback(augmentTable(table, knex));
8
+ });
9
+ }
10
+ async dropTable(name) {
11
+ await getKnex().schema.dropTableIfExists(name);
12
+ }
13
+ async hasTable(name) {
14
+ return getKnex().schema.hasTable(name);
15
+ }
16
+ }
17
+ //# sourceMappingURL=Schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Schema.js","sourceRoot":"","sources":["../../src/migrations/Schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAA4B,MAAM,iBAAiB,CAAC;AAEzE,MAAM,OAAO,MAAM;IACjB,KAAK,CAAC,WAAW,CAAC,IAAY,EAAE,QAA8C;QAC5E,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YAC5C,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY;QAC1B,MAAM,OAAO,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,OAAO,OAAO,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { Knex } from "knex";
2
+ /** Augmented table builder: use `xacosTimestamps()` / `xacosSoftDeletes()` to avoid clashing with Knex's `timestamps()`. */
3
+ export type XCreateTableBuilder = Knex.CreateTableBuilder & {
4
+ id(): void;
5
+ xacosTimestamps(): void;
6
+ xacosSoftDeletes(): void;
7
+ };
8
+ export declare function augmentTable(table: Knex.CreateTableBuilder, knex: Knex): XCreateTableBuilder;
9
+ //# sourceMappingURL=columnHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columnHelpers.d.ts","sourceRoot":"","sources":["../../src/migrations/columnHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAEjC,4HAA4H;AAC5H,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,kBAAkB,GAAG;IAC1D,EAAE,IAAI,IAAI,CAAC;IACX,eAAe,IAAI,IAAI,CAAC;IACxB,gBAAgB,IAAI,IAAI,CAAC;CAC1B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,GAAG,mBAAmB,CAiB5F"}
@@ -0,0 +1,15 @@
1
+ export function augmentTable(table, knex) {
2
+ const extended = table;
3
+ extended.id = () => {
4
+ table.bigIncrements("id").primary();
5
+ };
6
+ extended.xacosTimestamps = () => {
7
+ table.timestamp("created_at").defaultTo(knex.fn.now()).notNullable();
8
+ table.timestamp("updated_at").defaultTo(knex.fn.now()).notNullable();
9
+ };
10
+ extended.xacosSoftDeletes = () => {
11
+ table.timestamp("deleted_at").nullable();
12
+ };
13
+ return extended;
14
+ }
15
+ //# sourceMappingURL=columnHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"columnHelpers.js","sourceRoot":"","sources":["../../src/migrations/columnHelpers.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,YAAY,CAAC,KAA8B,EAAE,IAAU;IACrE,MAAM,QAAQ,GAAG,KAA4B,CAAC;IAE9C,QAAQ,CAAC,EAAE,GAAG,GAAG,EAAE;QACjB,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IACtC,CAAC,CAAC;IAEF,QAAQ,CAAC,eAAe,GAAG,GAAG,EAAE;QAC9B,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACrE,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACvE,CAAC,CAAC;IAEF,QAAQ,CAAC,gBAAgB,GAAG,GAAG,EAAE;QAC/B,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3C,CAAC,CAAC;IAEF,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare abstract class Seeder {
2
+ /**
3
+ * Implement this method with your seed logic.
4
+ * Called by the SeederRunner when `xacos db:seed` is invoked.
5
+ */
6
+ abstract run(): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=Seeder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Seeder.d.ts","sourceRoot":"","sources":["../../src/seeders/Seeder.ts"],"names":[],"mappings":"AAAA,8BAAsB,MAAM;IAC1B;;;OAGG;IACH,QAAQ,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAC9B"}
@@ -0,0 +1,3 @@
1
+ export class Seeder {
2
+ }
3
+ //# sourceMappingURL=Seeder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Seeder.js","sourceRoot":"","sources":["../../src/seeders/Seeder.ts"],"names":[],"mappings":"AAAA,MAAM,OAAgB,MAAM;CAM3B"}
@@ -0,0 +1,20 @@
1
+ export declare class SeederRunner {
2
+ private seedersDir;
3
+ constructor(seedersDir: string);
4
+ /**
5
+ * Run a specific seeder by class name.
6
+ * e.g. runOne('UserSeeder')
7
+ */
8
+ runOne(name: string): Promise<void>;
9
+ /**
10
+ * Run all seeders in the seeders directory, in alphabetical order.
11
+ */
12
+ runAll(): Promise<void>;
13
+ /**
14
+ * Scan the seeders directory and return file names sorted correctly.
15
+ * Files prefixed with numbers sort numerically: 001_, 002_, etc.
16
+ * Files without prefix sort alphabetically after numbered ones.
17
+ */
18
+ private getSortedFiles;
19
+ }
20
+ //# sourceMappingURL=SeederRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeederRunner.d.ts","sourceRoot":"","sources":["../../src/seeders/SeederRunner.ts"],"names":[],"mappings":"AAIA,qBAAa,YAAY;IACX,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,MAAM;IAEtC;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BzC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B;;;;OAIG;IACH,OAAO,CAAC,cAAc;CAevB"}
@@ -0,0 +1,68 @@
1
+ import { resolve } from 'path';
2
+ import { existsSync, readdirSync } from 'fs';
3
+ export class SeederRunner {
4
+ constructor(seedersDir) {
5
+ this.seedersDir = seedersDir;
6
+ }
7
+ /**
8
+ * Run a specific seeder by class name.
9
+ * e.g. runOne('UserSeeder')
10
+ */
11
+ async runOne(name) {
12
+ const path = resolve(this.seedersDir, `${name}.ts`);
13
+ const jsPath = resolve(this.seedersDir, `${name}.js`);
14
+ let targetPath = path;
15
+ if (!existsSync(path)) {
16
+ if (existsSync(jsPath)) {
17
+ targetPath = jsPath;
18
+ }
19
+ else {
20
+ throw new Error(`[XAOCS] Seeder not found: ${name} at ${path}`);
21
+ }
22
+ }
23
+ const mod = await import(targetPath);
24
+ const SeederClass = mod.default ?? mod[name];
25
+ if (!SeederClass) {
26
+ throw new Error(`[XAOCS] Seeder class not exported from ${name}.ts`);
27
+ }
28
+ const instance = new SeederClass();
29
+ console.log(` ▶ Running seeder: ${name}`);
30
+ await instance.run();
31
+ console.log(` ✓ Done: ${name}`);
32
+ }
33
+ /**
34
+ * Run all seeders in the seeders directory, in alphabetical order.
35
+ */
36
+ async runAll() {
37
+ const files = this.getSortedFiles();
38
+ if (files.length === 0) {
39
+ console.log('[XAOCS] No seeder files found.');
40
+ return;
41
+ }
42
+ for (const file of files) {
43
+ const name = file.replace(/\.(ts|js)$/, '');
44
+ await this.runOne(name);
45
+ }
46
+ }
47
+ /**
48
+ * Scan the seeders directory and return file names sorted correctly.
49
+ * Files prefixed with numbers sort numerically: 001_, 002_, etc.
50
+ * Files without prefix sort alphabetically after numbered ones.
51
+ */
52
+ getSortedFiles() {
53
+ const fullPath = resolve(this.seedersDir);
54
+ if (!existsSync(fullPath))
55
+ return [];
56
+ return readdirSync(fullPath)
57
+ .filter((f) => f.endsWith('.ts') || f.endsWith('.js'))
58
+ .sort((a, b) => {
59
+ // Extract leading number if present: "001_UserSeeder.ts" → 1
60
+ const numA = parseInt(a.match(/^(\d+)/)?.[1] ?? 'Infinity', 10);
61
+ const numB = parseInt(b.match(/^(\d+)/)?.[1] ?? 'Infinity', 10);
62
+ if (numA !== numB)
63
+ return numA - numB;
64
+ return a.localeCompare(b);
65
+ });
66
+ }
67
+ }
68
+ //# sourceMappingURL=SeederRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeederRunner.js","sourceRoot":"","sources":["../../src/seeders/SeederRunner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=SeederRunner.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeederRunner.test.d.ts","sourceRoot":"","sources":["../../src/seeders/SeederRunner.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { SeederRunner } from './SeederRunner';
3
+ import { writeFileSync, mkdirSync, rmSync } from 'fs';
4
+ import { resolve } from 'path';
5
+ const TMP = resolve('./tmp-seeders-test');
6
+ describe('SeederRunner', () => {
7
+ beforeEach(() => {
8
+ mkdirSync(TMP, { recursive: true });
9
+ writeFileSync(`${TMP}/TestSeeder.ts`, `export default class TestSeeder { async run() { (global as any).__seederRan = true; } }`);
10
+ });
11
+ afterEach(() => rmSync(TMP, { recursive: true, force: true }));
12
+ it('throws when seeder file does not exist', async () => {
13
+ const runner = new SeederRunner(TMP);
14
+ await expect(runner.runOne('NonExistent')).rejects.toThrow('Seeder not found');
15
+ });
16
+ it('runs a single seeder', async () => {
17
+ const runner = new SeederRunner(TMP);
18
+ global.__seederRan = false;
19
+ await runner.runOne('TestSeeder');
20
+ expect(global.__seederRan).toBe(true);
21
+ });
22
+ it('runs all seeders in directory', async () => {
23
+ writeFileSync(`${TMP}/AnotherSeeder.ts`, `export default class AnotherSeeder { async run() { (global as any).__anotherRan = true; } }`);
24
+ const runner = new SeederRunner(TMP);
25
+ global.__seederRan = false;
26
+ global.__anotherRan = false;
27
+ await runner.runAll();
28
+ expect(global.__seederRan).toBe(true);
29
+ expect(global.__anotherRan).toBe(true);
30
+ });
31
+ it('sorts seeders: numbered before alpha', async () => {
32
+ // create files in reverse to ensure sorting matters
33
+ writeFileSync(`${TMP}/ZebraSeeder.ts`, 'export default class ZebraSeeder { async run() { (global as any).__order.push("zebra"); } }');
34
+ writeFileSync(`${TMP}/001_UserSeeder.ts`, 'export default class UserSeeder { async run() { (global as any).__order.push("user"); } }');
35
+ writeFileSync(`${TMP}/002_PostSeeder.ts`, 'export default class PostSeeder { async run() { (global as any).__order.push("post"); } }');
36
+ const runner = new SeederRunner(TMP);
37
+ global.__order = [];
38
+ // We need to clear out the default seeders created in beforeEach if they interfere
39
+ rmSync(resolve(TMP, 'TestSeeder.ts'));
40
+ await runner.runAll();
41
+ expect(global.__order).toEqual(['user', 'post', 'zebra']);
42
+ });
43
+ });
44
+ //# sourceMappingURL=SeederRunner.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeederRunner.test.js","sourceRoot":"","sources":["../../src/seeders/SeederRunner.test.ts"],"names":[],"mappings":"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"}