@joakimbugge/nest-typeorm-seeder 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # @joakimbugge/nest-typeorm-seeder
2
+
3
+ NestJS module for [@joakimbugge/typeorm-seeder](../typeorm-seeder). Runs your `@Seeder` classes automatically on application bootstrap — once per seeder by default, tracked in a database table so watch-mode restarts do not re-seed.
4
+
5
+ > [!TIP]
6
+ > This package handles the NestJS integration. The seeding itself — `@Seed()`, `@Seeder`, `seed()`, entity factories — is all defined in [@joakimbugge/typeorm-seeder](../typeorm-seeder/README.md). Familiarity with that package will make this one much easier to use.
7
+
8
+ ---
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install @joakimbugge/nest-typeorm-seeder @joakimbugge/typeorm-seeder
14
+ ```
15
+
16
+ The peer dependencies (`@nestjs/common`, `@nestjs/core`, `typeorm`, `reflect-metadata`) are required, but if you are adding this to an existing NestJS + TypeORM project they are already present.
17
+
18
+ ---
19
+
20
+ ## Basic usage
21
+
22
+ Import `SeederModule` in your root module. It auto-detects the `DataSource` registered by `TypeOrmModule`, so no extra wiring is needed in the common case:
23
+
24
+ ```ts
25
+ import { Module } from '@nestjs/common'
26
+ import { TypeOrmModule } from '@nestjs/typeorm'
27
+ import { SeederModule } from '@joakimbugge/nest-typeorm-seeder'
28
+ import { UserSeeder } from './seeders/UserSeeder.js'
29
+
30
+ @Module({
31
+ imports: [
32
+ TypeOrmModule.forRoot({ ... }),
33
+ SeederModule.forRoot({ seeders: [UserSeeder] }),
34
+ ],
35
+ })
36
+ export class AppModule {}
37
+ ```
38
+
39
+ If your seeders declare dependencies on each other via the `@Seeder` decorator, they are sorted and executed in the correct order automatically — see [@joakimbugge/typeorm-seeder](../typeorm-seeder#seeder-suites) for details.
40
+
41
+ ### Providing a DataSource explicitly
42
+
43
+ If you manage the `DataSource` yourself rather than through `TypeOrmModule`, pass it directly:
44
+
45
+ ```ts
46
+ SeederModule.forRoot({
47
+ seeders: [UserSeeder],
48
+ dataSource: myDataSource,
49
+ })
50
+ ```
51
+
52
+ > [!IMPORTANT]
53
+ > A DataSource passed this way must already be initialized before the app bootstraps. The module does not call `initialize()` on it.
54
+
55
+ ### Async configuration
56
+
57
+ Use `forRootAsync` to resolve options from the DI container — useful when the `DataSource` or environment config is injected:
58
+
59
+ ```ts
60
+ import { ConfigService } from '@nestjs/config'
61
+
62
+ SeederModule.forRootAsync({
63
+ imports: [ConfigModule],
64
+ inject: [ConfigService],
65
+ useFactory: (config: ConfigService) => ({
66
+ seeders: [UserSeeder],
67
+ enabled: config.get('SEED') === 'true',
68
+ }),
69
+ })
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Running once
75
+
76
+ By default (`runOnce: true`) each seeder is tracked in a `seeders` table — similar to how TypeORM tracks migrations. On the next boot — including watch-mode restarts — seeders already recorded in that table are skipped.
77
+
78
+ ### In combination with `dropSchema`
79
+
80
+ When TypeORM's `dropSchema: true` is set, the entire schema — including the `seeders` table — is dropped on every start, so all seeders run regardless of `runOnce`.
81
+
82
+ | `dropSchema` | `runOnce` | What happens |
83
+ |---|---|---|
84
+ | `true` | `true` | Tracking table dropped → all seeders run (fresh schema needs fresh seeds) |
85
+ | `true` | `false` | Always runs |
86
+ | `false` | `true` | Tracking table persists → already-run seeders are skipped |
87
+ | `false` | `false` | Always runs (tracking disabled) |
88
+
89
+ `dropSchema: true` is typical in development when you want a clean slate on every restart — seeding every run is exactly what you want in that scenario. `runOnce: true` with a persistent schema is the right default for staging and production, where duplicate data is the concern.
90
+
91
+ ### Re-seeding
92
+
93
+ To force a seeder to run again, delete its row from the `seeders` table:
94
+
95
+ ```sql
96
+ DELETE FROM "seeders" WHERE name = 'UserSeeder';
97
+ ```
98
+
99
+ To reset everything and re-run all seeders on next boot:
100
+
101
+ ```sql
102
+ DELETE FROM "seeders";
103
+ ```
104
+
105
+ > [!NOTE]
106
+ > If your factories use random data (e.g. via Faker), setting `runOnce: false` means you will get a completely different dataset on every restart. This can be useful in some development setups, but is usually not what you want.
107
+
108
+ ---
109
+
110
+ ## API reference
111
+
112
+ ### `SeederModule.forRoot(options)`
113
+
114
+ Registers the module with static options.
115
+
116
+ ### `SeederModule.forRootAsync(options)`
117
+
118
+ Registers the module with options resolved from the DI container.
119
+
120
+ **`SeederModuleAsyncOptions`**
121
+
122
+ | Property | Type | Description |
123
+ |---|---|---|
124
+ | `imports` | `any[]?` | Modules to import for the factory's dependencies. |
125
+ | `inject` | `any[]?` | Providers to inject into the factory. |
126
+ | `useFactory` | `(...args) => SeederModuleOptions \| Promise<SeederModuleOptions>` | Factory that returns the module options. |
127
+
128
+ ---
129
+
130
+ ### `SeederModuleOptions`
131
+
132
+ | Property | Type | Default | Description |
133
+ |---|---|---|---|
134
+ | `seeders` | `SeederCtor[]` | — | Seeder classes to run. Transitive dependencies are resolved automatically. |
135
+ | `dataSource` | `DataSource?` | — | Explicit DataSource. When omitted, the module resolves the DataSource registered by `TypeOrmModule`. |
136
+ | `relations` | `boolean?` | `true` | Passed through to `runSeeders`. Set to `false` to skip relation seeding. |
137
+ | `enabled` | `boolean?` | `true` | When `false`, seeding is skipped entirely. Useful for gating on an environment variable. |
138
+ | `runOnce` | `boolean?` | `true` | Track executed seeders in the database and skip them on subsequent boots. |
139
+ | `historyTableName` | `string?` | `'seeders'` | Name of the table used to track which seeders have run. |
140
+
141
+ ---
142
+
143
+ ## License
144
+
145
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,122 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _nestjs_common = require("@nestjs/common");
3
+ let _nestjs_core = require("@nestjs/core");
4
+ let _joakimbugge_typeorm_seeder = require("@joakimbugge/typeorm-seeder");
5
+ let typeorm = require("typeorm");
6
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorateMetadata.js
7
+ function __decorateMetadata(k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ }
10
+ //#endregion
11
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorateParam.js
12
+ function __decorateParam(paramIndex, decorator) {
13
+ return function(target, key) {
14
+ decorator(target, key, paramIndex);
15
+ };
16
+ }
17
+ //#endregion
18
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorate.js
19
+ function __decorate(decorators, target, key, desc) {
20
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
21
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
22
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
23
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
24
+ }
25
+ //#endregion
26
+ //#region src/SeederRunnerService.ts
27
+ var _ref, _SeederRunnerService;
28
+ const SEEDER_MODULE_OPTIONS = Symbol("SEEDER_MODULE_OPTIONS");
29
+ const DEFAULT_HISTORY_TABLE = "seeders";
30
+ let SeederRunnerService = _SeederRunnerService = class SeederRunnerService {
31
+ constructor(options, moduleRef) {
32
+ this.options = options;
33
+ this.moduleRef = moduleRef;
34
+ this.logger = new _nestjs_common.Logger(_SeederRunnerService.name);
35
+ }
36
+ async onApplicationBootstrap() {
37
+ if (this.options.enabled === false) return;
38
+ const dataSource = await this.resolveDataSource();
39
+ const runOnce = this.options.runOnce ?? true;
40
+ const tableName = this.options.historyTableName ?? DEFAULT_HISTORY_TABLE;
41
+ let executedSeeders = /* @__PURE__ */ new Set();
42
+ if (runOnce) {
43
+ await this.ensureHistoryTable(dataSource, tableName);
44
+ executedSeeders = await this.getExecutedSeeders(dataSource, tableName);
45
+ }
46
+ await (0, _joakimbugge_typeorm_seeder.runSeeders)(this.options.seeders, {
47
+ dataSource,
48
+ relations: this.options.relations,
49
+ logging: false,
50
+ skip: (seeder) => {
51
+ const shouldSkip = executedSeeders.has(seeder.name);
52
+ if (shouldSkip) this.logger.log(`[${seeder.name}] Skipping (already run)`);
53
+ return shouldSkip;
54
+ },
55
+ onBefore: (seeder) => this.logger.log(`[${seeder.name}] Starting...`),
56
+ onAfter: async (seeder, durationMs) => {
57
+ if (runOnce) await this.recordRun(dataSource, tableName, seeder.name);
58
+ this.logger.log(`[${seeder.name}] Done in ${durationMs}ms`);
59
+ },
60
+ onError: (seeder, error) => this.logger.error(`[${seeder.name}] Failed`, error instanceof Error ? error.stack : String(error))
61
+ });
62
+ }
63
+ async ensureHistoryTable(dataSource, tableName) {
64
+ await dataSource.query(`CREATE TABLE IF NOT EXISTS "${tableName}" (name VARCHAR(255) PRIMARY KEY NOT NULL, executed_at VARCHAR(32) NOT NULL)`);
65
+ }
66
+ async getExecutedSeeders(dataSource, tableName) {
67
+ const rows = await dataSource.query(`SELECT name FROM "${tableName}"`);
68
+ return new Set(rows.map((r) => r.name));
69
+ }
70
+ async recordRun(dataSource, tableName, name) {
71
+ const executedAt = (/* @__PURE__ */ new Date()).toISOString();
72
+ await dataSource.query(`INSERT INTO "${tableName}" (name, executed_at) VALUES ('${name}', '${executedAt}')`);
73
+ }
74
+ async resolveDataSource() {
75
+ if (this.options.dataSource) return this.options.dataSource;
76
+ try {
77
+ return this.moduleRef.get(typeorm.DataSource, { strict: false });
78
+ } catch {
79
+ throw new Error("SeederModule could not resolve a DataSource. Either import TypeOrmModule or provide a dataSource in SeederModule options.");
80
+ }
81
+ }
82
+ };
83
+ SeederRunnerService = _SeederRunnerService = __decorate([
84
+ (0, _nestjs_common.Injectable)(),
85
+ __decorateParam(0, (0, _nestjs_common.Inject)(SEEDER_MODULE_OPTIONS)),
86
+ __decorateMetadata("design:paramtypes", [Object, typeof (_ref = typeof _nestjs_core.ModuleRef !== "undefined" && _nestjs_core.ModuleRef) === "function" ? _ref : Object])
87
+ ], SeederRunnerService);
88
+ //#endregion
89
+ //#region src/SeederModule.ts
90
+ var _SeederModule;
91
+ let SeederModule = _SeederModule = class SeederModule {
92
+ static forRoot(options) {
93
+ return {
94
+ module: _SeederModule,
95
+ providers: [{
96
+ provide: SEEDER_MODULE_OPTIONS,
97
+ useValue: options
98
+ }, SeederRunnerService]
99
+ };
100
+ }
101
+ static forRootAsync(options) {
102
+ return {
103
+ module: _SeederModule,
104
+ imports: options.imports ?? [],
105
+ providers: [{
106
+ provide: SEEDER_MODULE_OPTIONS,
107
+ inject: options.inject ?? [],
108
+ useFactory: options.useFactory
109
+ }, SeederRunnerService]
110
+ };
111
+ }
112
+ };
113
+ SeederModule = _SeederModule = __decorate([(0, _nestjs_common.Module)({})], SeederModule);
114
+ //#endregion
115
+ Object.defineProperty(exports, "SeederModule", {
116
+ enumerable: true,
117
+ get: function() {
118
+ return SeederModule;
119
+ }
120
+ });
121
+
122
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["Logger","DataSource"],"sources":["../src/SeederRunnerService.ts","../src/SeederModule.ts"],"sourcesContent":["import { Inject, Injectable, Logger, type OnApplicationBootstrap } from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\nimport { runSeeders } from '@joakimbugge/typeorm-seeder';\nimport { DataSource } from 'typeorm';\nimport type { SeederModuleOptions } from './SeederModule.js';\n\nexport const SEEDER_MODULE_OPTIONS = Symbol('SEEDER_MODULE_OPTIONS');\n\nconst DEFAULT_HISTORY_TABLE = 'seeders';\n\n@Injectable()\nexport class SeederRunnerService implements OnApplicationBootstrap {\n private readonly logger = new Logger(SeederRunnerService.name);\n\n constructor(\n @Inject(SEEDER_MODULE_OPTIONS) private readonly options: SeederModuleOptions,\n private readonly moduleRef: ModuleRef,\n ) {}\n\n async onApplicationBootstrap(): Promise<void> {\n if (this.options.enabled === false) {\n return;\n }\n\n const dataSource = await this.resolveDataSource();\n const runOnce = this.options.runOnce ?? true;\n const tableName = this.options.historyTableName ?? DEFAULT_HISTORY_TABLE;\n\n let executedSeeders = new Set<string>();\n\n if (runOnce) {\n await this.ensureHistoryTable(dataSource, tableName);\n executedSeeders = await this.getExecutedSeeders(dataSource, tableName);\n }\n\n await runSeeders(this.options.seeders, {\n dataSource,\n relations: this.options.relations,\n logging: false,\n skip: (seeder) => {\n const shouldSkip = executedSeeders.has(seeder.name);\n\n if (shouldSkip) {\n this.logger.log(`[${seeder.name}] Skipping (already run)`);\n }\n\n return shouldSkip;\n },\n onBefore: (seeder) => this.logger.log(`[${seeder.name}] Starting...`),\n onAfter: async (seeder, durationMs) => {\n if (runOnce) {\n await this.recordRun(dataSource, tableName, seeder.name);\n }\n\n this.logger.log(`[${seeder.name}] Done in ${durationMs}ms`);\n },\n onError: (seeder, error) =>\n this.logger.error(\n `[${seeder.name}] Failed`,\n error instanceof Error ? error.stack : String(error),\n ),\n });\n }\n\n private async ensureHistoryTable(dataSource: DataSource, tableName: string): Promise<void> {\n await dataSource.query(\n `CREATE TABLE IF NOT EXISTS \"${tableName}\" (name VARCHAR(255) PRIMARY KEY NOT NULL, executed_at VARCHAR(32) NOT NULL)`,\n );\n }\n\n private async getExecutedSeeders(\n dataSource: DataSource,\n tableName: string,\n ): Promise<Set<string>> {\n const rows: { name: string }[] = await dataSource.query(`SELECT name FROM \"${tableName}\"`);\n\n return new Set(rows.map((r) => r.name));\n }\n\n private async recordRun(dataSource: DataSource, tableName: string, name: string): Promise<void> {\n const executedAt = new Date().toISOString();\n\n await dataSource.query(\n `INSERT INTO \"${tableName}\" (name, executed_at) VALUES ('${name}', '${executedAt}')`,\n );\n }\n\n private async resolveDataSource(): Promise<DataSource> {\n if (this.options.dataSource) {\n return this.options.dataSource;\n }\n\n try {\n return this.moduleRef.get(DataSource, { strict: false });\n } catch {\n throw new Error(\n 'SeederModule could not resolve a DataSource. Either import TypeOrmModule or provide a dataSource in SeederModule options.',\n );\n }\n }\n}\n","import { type DynamicModule, Module } from '@nestjs/common';\nimport type { SeederInterface } from '@joakimbugge/typeorm-seeder';\nimport type { DataSource } from 'typeorm';\nimport { SeederRunnerService, SEEDER_MODULE_OPTIONS } from './SeederRunnerService.js';\n\nexport type SeederCtor = new () => SeederInterface;\n\nexport interface SeederModuleOptions {\n seeders: SeederCtor[];\n dataSource?: DataSource;\n relations?: boolean;\n /**\n * When `false`, seeding is skipped entirely. Useful for gating on an env var.\n *\n * @default true\n */\n enabled?: boolean;\n /**\n * Track executed seeders in a database table and skip them on subsequent boots.\n * Set to `false` to always run every seeder regardless.\n *\n * When TypeORM's `dropSchema` is `true`, the history table is dropped with the\n * rest of the schema on every start, so all seeders run regardless of this setting.\n *\n * @default true\n */\n runOnce?: boolean;\n /**\n * Name of the table used to track which seeders have run.\n *\n * @default 'seeders'\n */\n historyTableName?: string;\n}\n\nexport interface SeederModuleAsyncOptions {\n imports?: any[];\n inject?: any[];\n useFactory: (...args: any[]) => SeederModuleOptions | Promise<SeederModuleOptions>;\n}\n\n@Module({})\nexport class SeederModule {\n static forRoot(options: SeederModuleOptions): DynamicModule {\n return {\n module: SeederModule,\n providers: [{ provide: SEEDER_MODULE_OPTIONS, useValue: options }, SeederRunnerService],\n };\n }\n\n static forRootAsync(options: SeederModuleAsyncOptions): DynamicModule {\n return {\n module: SeederModule,\n imports: options.imports ?? [],\n providers: [\n {\n provide: SEEDER_MODULE_OPTIONS,\n inject: options.inject ?? [],\n useFactory: options.useFactory,\n },\n SeederRunnerService,\n ],\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAa,wBAAwB,OAAO,wBAAwB;AAEpE,MAAM,wBAAwB;AAGvB,IAAA,sBAAA,uBAAA,MAAM,oBAAsD;CAGjE,YACE,SACA,WACA;AAFgD,OAAA,UAAA;AAC/B,OAAA,YAAA;gBAJO,IAAIA,eAAAA,OAAAA,qBAA2B,KAAK;;CAO9D,MAAM,yBAAwC;AAC5C,MAAI,KAAK,QAAQ,YAAY,MAC3B;EAGF,MAAM,aAAa,MAAM,KAAK,mBAAmB;EACjD,MAAM,UAAU,KAAK,QAAQ,WAAW;EACxC,MAAM,YAAY,KAAK,QAAQ,oBAAoB;EAEnD,IAAI,kCAAkB,IAAI,KAAa;AAEvC,MAAI,SAAS;AACX,SAAM,KAAK,mBAAmB,YAAY,UAAU;AACpD,qBAAkB,MAAM,KAAK,mBAAmB,YAAY,UAAU;;AAGxE,SAAA,GAAA,4BAAA,YAAiB,KAAK,QAAQ,SAAS;GACrC;GACA,WAAW,KAAK,QAAQ;GACxB,SAAS;GACT,OAAO,WAAW;IAChB,MAAM,aAAa,gBAAgB,IAAI,OAAO,KAAK;AAEnD,QAAI,WACF,MAAK,OAAO,IAAI,IAAI,OAAO,KAAK,0BAA0B;AAG5D,WAAO;;GAET,WAAW,WAAW,KAAK,OAAO,IAAI,IAAI,OAAO,KAAK,eAAe;GACrE,SAAS,OAAO,QAAQ,eAAe;AACrC,QAAI,QACF,OAAM,KAAK,UAAU,YAAY,WAAW,OAAO,KAAK;AAG1D,SAAK,OAAO,IAAI,IAAI,OAAO,KAAK,YAAY,WAAW,IAAI;;GAE7D,UAAU,QAAQ,UAChB,KAAK,OAAO,MACV,IAAI,OAAO,KAAK,WAChB,iBAAiB,QAAQ,MAAM,QAAQ,OAAO,MAAM,CACrD;GACJ,CAAC;;CAGJ,MAAc,mBAAmB,YAAwB,WAAkC;AACzF,QAAM,WAAW,MACf,+BAA+B,UAAU,8EAC1C;;CAGH,MAAc,mBACZ,YACA,WACsB;EACtB,MAAM,OAA2B,MAAM,WAAW,MAAM,qBAAqB,UAAU,GAAG;AAE1F,SAAO,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,KAAK,CAAC;;CAGzC,MAAc,UAAU,YAAwB,WAAmB,MAA6B;EAC9F,MAAM,8BAAa,IAAI,MAAM,EAAC,aAAa;AAE3C,QAAM,WAAW,MACf,gBAAgB,UAAU,iCAAiC,KAAK,MAAM,WAAW,IAClF;;CAGH,MAAc,oBAAyC;AACrD,MAAI,KAAK,QAAQ,WACf,QAAO,KAAK,QAAQ;AAGtB,MAAI;AACF,UAAO,KAAK,UAAU,IAAIC,QAAAA,YAAY,EAAE,QAAQ,OAAO,CAAC;UAClD;AACN,SAAM,IAAI,MACR,4HACD;;;;;iCAvFM;+CAKD,sBAAsB,CAAA;;;;;;AC2B3B,IAAA,eAAA,gBAAA,MAAM,aAAa;CACxB,OAAO,QAAQ,SAA6C;AAC1D,SAAO;GACL,QAAA;GACA,WAAW,CAAC;IAAE,SAAS;IAAuB,UAAU;IAAS,EAAE,oBAAoB;GACxF;;CAGH,OAAO,aAAa,SAAkD;AACpE,SAAO;GACL,QAAA;GACA,SAAS,QAAQ,WAAW,EAAE;GAC9B,WAAW,CACT;IACE,SAAS;IACT,QAAQ,QAAQ,UAAU,EAAE;IAC5B,YAAY,QAAQ;IACrB,EACD,oBACD;GACF;;;sEArBG,EAAE,CAAC,CAAA,EAAA,aAAA"}
@@ -0,0 +1,45 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import { SeederInterface } from "@joakimbugge/typeorm-seeder";
3
+ import { DataSource } from "typeorm";
4
+
5
+ //#region src/SeederModule.d.ts
6
+ type SeederCtor = new () => SeederInterface;
7
+ interface SeederModuleOptions {
8
+ seeders: SeederCtor[];
9
+ dataSource?: DataSource;
10
+ relations?: boolean;
11
+ /**
12
+ * When `false`, seeding is skipped entirely. Useful for gating on an env var.
13
+ *
14
+ * @default true
15
+ */
16
+ enabled?: boolean;
17
+ /**
18
+ * Track executed seeders in a database table and skip them on subsequent boots.
19
+ * Set to `false` to always run every seeder regardless.
20
+ *
21
+ * When TypeORM's `dropSchema` is `true`, the history table is dropped with the
22
+ * rest of the schema on every start, so all seeders run regardless of this setting.
23
+ *
24
+ * @default true
25
+ */
26
+ runOnce?: boolean;
27
+ /**
28
+ * Name of the table used to track which seeders have run.
29
+ *
30
+ * @default 'seeders'
31
+ */
32
+ historyTableName?: string;
33
+ }
34
+ interface SeederModuleAsyncOptions {
35
+ imports?: any[];
36
+ inject?: any[];
37
+ useFactory: (...args: any[]) => SeederModuleOptions | Promise<SeederModuleOptions>;
38
+ }
39
+ declare class SeederModule {
40
+ static forRoot(options: SeederModuleOptions): DynamicModule;
41
+ static forRootAsync(options: SeederModuleAsyncOptions): DynamicModule;
42
+ }
43
+ //#endregion
44
+ export { type SeederCtor, SeederModule, type SeederModuleAsyncOptions, type SeederModuleOptions };
45
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/SeederModule.ts"],"mappings":";;;;;KAKY,UAAA,aAAuB,eAAA;AAAA,UAElB,mBAAA;EACf,OAAA,EAAS,UAAA;EACT,UAAA,GAAa,UAAA;EACb,SAAA;EALiC;;AAEnC;;;EASE,OAAA;EARA;;;;;;;;;EAkBA,OAAA;EASe;;;;;EAHf,gBAAA;AAAA;AAAA,UAGe,wBAAA;EACf,OAAA;EACA,MAAA;EACA,UAAA,MAAgB,IAAA,YAAgB,mBAAA,GAAsB,OAAA,CAAQ,mBAAA;AAAA;AAAA,cAInD,YAAA;EAAA,OACJ,OAAA,CAAQ,OAAA,EAAS,mBAAA,GAAsB,aAAA;EAAA,OAOvC,YAAA,CAAa,OAAA,EAAS,wBAAA,GAA2B,aAAA;AAAA"}
@@ -0,0 +1,45 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import { SeederInterface } from "@joakimbugge/typeorm-seeder";
3
+ import { DataSource } from "typeorm";
4
+
5
+ //#region src/SeederModule.d.ts
6
+ type SeederCtor = new () => SeederInterface;
7
+ interface SeederModuleOptions {
8
+ seeders: SeederCtor[];
9
+ dataSource?: DataSource;
10
+ relations?: boolean;
11
+ /**
12
+ * When `false`, seeding is skipped entirely. Useful for gating on an env var.
13
+ *
14
+ * @default true
15
+ */
16
+ enabled?: boolean;
17
+ /**
18
+ * Track executed seeders in a database table and skip them on subsequent boots.
19
+ * Set to `false` to always run every seeder regardless.
20
+ *
21
+ * When TypeORM's `dropSchema` is `true`, the history table is dropped with the
22
+ * rest of the schema on every start, so all seeders run regardless of this setting.
23
+ *
24
+ * @default true
25
+ */
26
+ runOnce?: boolean;
27
+ /**
28
+ * Name of the table used to track which seeders have run.
29
+ *
30
+ * @default 'seeders'
31
+ */
32
+ historyTableName?: string;
33
+ }
34
+ interface SeederModuleAsyncOptions {
35
+ imports?: any[];
36
+ inject?: any[];
37
+ useFactory: (...args: any[]) => SeederModuleOptions | Promise<SeederModuleOptions>;
38
+ }
39
+ declare class SeederModule {
40
+ static forRoot(options: SeederModuleOptions): DynamicModule;
41
+ static forRootAsync(options: SeederModuleAsyncOptions): DynamicModule;
42
+ }
43
+ //#endregion
44
+ export { type SeederCtor, SeederModule, type SeederModuleAsyncOptions, type SeederModuleOptions };
45
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/SeederModule.ts"],"mappings":";;;;;KAKY,UAAA,aAAuB,eAAA;AAAA,UAElB,mBAAA;EACf,OAAA,EAAS,UAAA;EACT,UAAA,GAAa,UAAA;EACb,SAAA;EALiC;;AAEnC;;;EASE,OAAA;EARA;;;;;;;;;EAkBA,OAAA;EASe;;;;;EAHf,gBAAA;AAAA;AAAA,UAGe,wBAAA;EACf,OAAA;EACA,MAAA;EACA,UAAA,MAAgB,IAAA,YAAgB,mBAAA,GAAsB,OAAA,CAAQ,mBAAA;AAAA;AAAA,cAInD,YAAA;EAAA,OACJ,OAAA,CAAQ,OAAA,EAAS,mBAAA,GAAsB,aAAA;EAAA,OAOvC,YAAA,CAAa,OAAA,EAAS,wBAAA,GAA2B,aAAA;AAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,116 @@
1
+ import { Inject, Injectable, Logger, Module } from "@nestjs/common";
2
+ import { ModuleRef } from "@nestjs/core";
3
+ import { runSeeders } from "@joakimbugge/typeorm-seeder";
4
+ import { DataSource } from "typeorm";
5
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorateMetadata.js
6
+ function __decorateMetadata(k, v) {
7
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
8
+ }
9
+ //#endregion
10
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorateParam.js
11
+ function __decorateParam(paramIndex, decorator) {
12
+ return function(target, key) {
13
+ decorator(target, key, paramIndex);
14
+ };
15
+ }
16
+ //#endregion
17
+ //#region \0@oxc-project+runtime@0.121.0/helpers/decorate.js
18
+ function __decorate(decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ }
24
+ //#endregion
25
+ //#region src/SeederRunnerService.ts
26
+ var _ref, _SeederRunnerService;
27
+ const SEEDER_MODULE_OPTIONS = Symbol("SEEDER_MODULE_OPTIONS");
28
+ const DEFAULT_HISTORY_TABLE = "seeders";
29
+ let SeederRunnerService = _SeederRunnerService = class SeederRunnerService {
30
+ constructor(options, moduleRef) {
31
+ this.options = options;
32
+ this.moduleRef = moduleRef;
33
+ this.logger = new Logger(_SeederRunnerService.name);
34
+ }
35
+ async onApplicationBootstrap() {
36
+ if (this.options.enabled === false) return;
37
+ const dataSource = await this.resolveDataSource();
38
+ const runOnce = this.options.runOnce ?? true;
39
+ const tableName = this.options.historyTableName ?? DEFAULT_HISTORY_TABLE;
40
+ let executedSeeders = /* @__PURE__ */ new Set();
41
+ if (runOnce) {
42
+ await this.ensureHistoryTable(dataSource, tableName);
43
+ executedSeeders = await this.getExecutedSeeders(dataSource, tableName);
44
+ }
45
+ await runSeeders(this.options.seeders, {
46
+ dataSource,
47
+ relations: this.options.relations,
48
+ logging: false,
49
+ skip: (seeder) => {
50
+ const shouldSkip = executedSeeders.has(seeder.name);
51
+ if (shouldSkip) this.logger.log(`[${seeder.name}] Skipping (already run)`);
52
+ return shouldSkip;
53
+ },
54
+ onBefore: (seeder) => this.logger.log(`[${seeder.name}] Starting...`),
55
+ onAfter: async (seeder, durationMs) => {
56
+ if (runOnce) await this.recordRun(dataSource, tableName, seeder.name);
57
+ this.logger.log(`[${seeder.name}] Done in ${durationMs}ms`);
58
+ },
59
+ onError: (seeder, error) => this.logger.error(`[${seeder.name}] Failed`, error instanceof Error ? error.stack : String(error))
60
+ });
61
+ }
62
+ async ensureHistoryTable(dataSource, tableName) {
63
+ await dataSource.query(`CREATE TABLE IF NOT EXISTS "${tableName}" (name VARCHAR(255) PRIMARY KEY NOT NULL, executed_at VARCHAR(32) NOT NULL)`);
64
+ }
65
+ async getExecutedSeeders(dataSource, tableName) {
66
+ const rows = await dataSource.query(`SELECT name FROM "${tableName}"`);
67
+ return new Set(rows.map((r) => r.name));
68
+ }
69
+ async recordRun(dataSource, tableName, name) {
70
+ const executedAt = (/* @__PURE__ */ new Date()).toISOString();
71
+ await dataSource.query(`INSERT INTO "${tableName}" (name, executed_at) VALUES ('${name}', '${executedAt}')`);
72
+ }
73
+ async resolveDataSource() {
74
+ if (this.options.dataSource) return this.options.dataSource;
75
+ try {
76
+ return this.moduleRef.get(DataSource, { strict: false });
77
+ } catch {
78
+ throw new Error("SeederModule could not resolve a DataSource. Either import TypeOrmModule or provide a dataSource in SeederModule options.");
79
+ }
80
+ }
81
+ };
82
+ SeederRunnerService = _SeederRunnerService = __decorate([
83
+ Injectable(),
84
+ __decorateParam(0, Inject(SEEDER_MODULE_OPTIONS)),
85
+ __decorateMetadata("design:paramtypes", [Object, typeof (_ref = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref : Object])
86
+ ], SeederRunnerService);
87
+ //#endregion
88
+ //#region src/SeederModule.ts
89
+ var _SeederModule;
90
+ let SeederModule = _SeederModule = class SeederModule {
91
+ static forRoot(options) {
92
+ return {
93
+ module: _SeederModule,
94
+ providers: [{
95
+ provide: SEEDER_MODULE_OPTIONS,
96
+ useValue: options
97
+ }, SeederRunnerService]
98
+ };
99
+ }
100
+ static forRootAsync(options) {
101
+ return {
102
+ module: _SeederModule,
103
+ imports: options.imports ?? [],
104
+ providers: [{
105
+ provide: SEEDER_MODULE_OPTIONS,
106
+ inject: options.inject ?? [],
107
+ useFactory: options.useFactory
108
+ }, SeederRunnerService]
109
+ };
110
+ }
111
+ };
112
+ SeederModule = _SeederModule = __decorate([Module({})], SeederModule);
113
+ //#endregion
114
+ export { SeederModule };
115
+
116
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/SeederRunnerService.ts","../src/SeederModule.ts"],"sourcesContent":["import { Inject, Injectable, Logger, type OnApplicationBootstrap } from '@nestjs/common';\nimport { ModuleRef } from '@nestjs/core';\nimport { runSeeders } from '@joakimbugge/typeorm-seeder';\nimport { DataSource } from 'typeorm';\nimport type { SeederModuleOptions } from './SeederModule.js';\n\nexport const SEEDER_MODULE_OPTIONS = Symbol('SEEDER_MODULE_OPTIONS');\n\nconst DEFAULT_HISTORY_TABLE = 'seeders';\n\n@Injectable()\nexport class SeederRunnerService implements OnApplicationBootstrap {\n private readonly logger = new Logger(SeederRunnerService.name);\n\n constructor(\n @Inject(SEEDER_MODULE_OPTIONS) private readonly options: SeederModuleOptions,\n private readonly moduleRef: ModuleRef,\n ) {}\n\n async onApplicationBootstrap(): Promise<void> {\n if (this.options.enabled === false) {\n return;\n }\n\n const dataSource = await this.resolveDataSource();\n const runOnce = this.options.runOnce ?? true;\n const tableName = this.options.historyTableName ?? DEFAULT_HISTORY_TABLE;\n\n let executedSeeders = new Set<string>();\n\n if (runOnce) {\n await this.ensureHistoryTable(dataSource, tableName);\n executedSeeders = await this.getExecutedSeeders(dataSource, tableName);\n }\n\n await runSeeders(this.options.seeders, {\n dataSource,\n relations: this.options.relations,\n logging: false,\n skip: (seeder) => {\n const shouldSkip = executedSeeders.has(seeder.name);\n\n if (shouldSkip) {\n this.logger.log(`[${seeder.name}] Skipping (already run)`);\n }\n\n return shouldSkip;\n },\n onBefore: (seeder) => this.logger.log(`[${seeder.name}] Starting...`),\n onAfter: async (seeder, durationMs) => {\n if (runOnce) {\n await this.recordRun(dataSource, tableName, seeder.name);\n }\n\n this.logger.log(`[${seeder.name}] Done in ${durationMs}ms`);\n },\n onError: (seeder, error) =>\n this.logger.error(\n `[${seeder.name}] Failed`,\n error instanceof Error ? error.stack : String(error),\n ),\n });\n }\n\n private async ensureHistoryTable(dataSource: DataSource, tableName: string): Promise<void> {\n await dataSource.query(\n `CREATE TABLE IF NOT EXISTS \"${tableName}\" (name VARCHAR(255) PRIMARY KEY NOT NULL, executed_at VARCHAR(32) NOT NULL)`,\n );\n }\n\n private async getExecutedSeeders(\n dataSource: DataSource,\n tableName: string,\n ): Promise<Set<string>> {\n const rows: { name: string }[] = await dataSource.query(`SELECT name FROM \"${tableName}\"`);\n\n return new Set(rows.map((r) => r.name));\n }\n\n private async recordRun(dataSource: DataSource, tableName: string, name: string): Promise<void> {\n const executedAt = new Date().toISOString();\n\n await dataSource.query(\n `INSERT INTO \"${tableName}\" (name, executed_at) VALUES ('${name}', '${executedAt}')`,\n );\n }\n\n private async resolveDataSource(): Promise<DataSource> {\n if (this.options.dataSource) {\n return this.options.dataSource;\n }\n\n try {\n return this.moduleRef.get(DataSource, { strict: false });\n } catch {\n throw new Error(\n 'SeederModule could not resolve a DataSource. Either import TypeOrmModule or provide a dataSource in SeederModule options.',\n );\n }\n }\n}\n","import { type DynamicModule, Module } from '@nestjs/common';\nimport type { SeederInterface } from '@joakimbugge/typeorm-seeder';\nimport type { DataSource } from 'typeorm';\nimport { SeederRunnerService, SEEDER_MODULE_OPTIONS } from './SeederRunnerService.js';\n\nexport type SeederCtor = new () => SeederInterface;\n\nexport interface SeederModuleOptions {\n seeders: SeederCtor[];\n dataSource?: DataSource;\n relations?: boolean;\n /**\n * When `false`, seeding is skipped entirely. Useful for gating on an env var.\n *\n * @default true\n */\n enabled?: boolean;\n /**\n * Track executed seeders in a database table and skip them on subsequent boots.\n * Set to `false` to always run every seeder regardless.\n *\n * When TypeORM's `dropSchema` is `true`, the history table is dropped with the\n * rest of the schema on every start, so all seeders run regardless of this setting.\n *\n * @default true\n */\n runOnce?: boolean;\n /**\n * Name of the table used to track which seeders have run.\n *\n * @default 'seeders'\n */\n historyTableName?: string;\n}\n\nexport interface SeederModuleAsyncOptions {\n imports?: any[];\n inject?: any[];\n useFactory: (...args: any[]) => SeederModuleOptions | Promise<SeederModuleOptions>;\n}\n\n@Module({})\nexport class SeederModule {\n static forRoot(options: SeederModuleOptions): DynamicModule {\n return {\n module: SeederModule,\n providers: [{ provide: SEEDER_MODULE_OPTIONS, useValue: options }, SeederRunnerService],\n };\n }\n\n static forRootAsync(options: SeederModuleAsyncOptions): DynamicModule {\n return {\n module: SeederModule,\n imports: options.imports ?? [],\n providers: [\n {\n provide: SEEDER_MODULE_OPTIONS,\n inject: options.inject ?? [],\n useFactory: options.useFactory,\n },\n SeederRunnerService,\n ],\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,MAAa,wBAAwB,OAAO,wBAAwB;AAEpE,MAAM,wBAAwB;AAGvB,IAAA,sBAAA,uBAAA,MAAM,oBAAsD;CAGjE,YACE,SACA,WACA;AAFgD,OAAA,UAAA;AAC/B,OAAA,YAAA;gBAJO,IAAI,OAAA,qBAA2B,KAAK;;CAO9D,MAAM,yBAAwC;AAC5C,MAAI,KAAK,QAAQ,YAAY,MAC3B;EAGF,MAAM,aAAa,MAAM,KAAK,mBAAmB;EACjD,MAAM,UAAU,KAAK,QAAQ,WAAW;EACxC,MAAM,YAAY,KAAK,QAAQ,oBAAoB;EAEnD,IAAI,kCAAkB,IAAI,KAAa;AAEvC,MAAI,SAAS;AACX,SAAM,KAAK,mBAAmB,YAAY,UAAU;AACpD,qBAAkB,MAAM,KAAK,mBAAmB,YAAY,UAAU;;AAGxE,QAAM,WAAW,KAAK,QAAQ,SAAS;GACrC;GACA,WAAW,KAAK,QAAQ;GACxB,SAAS;GACT,OAAO,WAAW;IAChB,MAAM,aAAa,gBAAgB,IAAI,OAAO,KAAK;AAEnD,QAAI,WACF,MAAK,OAAO,IAAI,IAAI,OAAO,KAAK,0BAA0B;AAG5D,WAAO;;GAET,WAAW,WAAW,KAAK,OAAO,IAAI,IAAI,OAAO,KAAK,eAAe;GACrE,SAAS,OAAO,QAAQ,eAAe;AACrC,QAAI,QACF,OAAM,KAAK,UAAU,YAAY,WAAW,OAAO,KAAK;AAG1D,SAAK,OAAO,IAAI,IAAI,OAAO,KAAK,YAAY,WAAW,IAAI;;GAE7D,UAAU,QAAQ,UAChB,KAAK,OAAO,MACV,IAAI,OAAO,KAAK,WAChB,iBAAiB,QAAQ,MAAM,QAAQ,OAAO,MAAM,CACrD;GACJ,CAAC;;CAGJ,MAAc,mBAAmB,YAAwB,WAAkC;AACzF,QAAM,WAAW,MACf,+BAA+B,UAAU,8EAC1C;;CAGH,MAAc,mBACZ,YACA,WACsB;EACtB,MAAM,OAA2B,MAAM,WAAW,MAAM,qBAAqB,UAAU,GAAG;AAE1F,SAAO,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,KAAK,CAAC;;CAGzC,MAAc,UAAU,YAAwB,WAAmB,MAA6B;EAC9F,MAAM,8BAAa,IAAI,MAAM,EAAC,aAAa;AAE3C,QAAM,WAAW,MACf,gBAAgB,UAAU,iCAAiC,KAAK,MAAM,WAAW,IAClF;;CAGH,MAAc,oBAAyC;AACrD,MAAI,KAAK,QAAQ,WACf,QAAO,KAAK,QAAQ;AAGtB,MAAI;AACF,UAAO,KAAK,UAAU,IAAI,YAAY,EAAE,QAAQ,OAAO,CAAC;UAClD;AACN,SAAM,IAAI,MACR,4HACD;;;;;CAvFN,YAAY;oBAKR,OAAO,sBAAsB,CAAA;;;;;;AC2B3B,IAAA,eAAA,gBAAA,MAAM,aAAa;CACxB,OAAO,QAAQ,SAA6C;AAC1D,SAAO;GACL,QAAA;GACA,WAAW,CAAC;IAAE,SAAS;IAAuB,UAAU;IAAS,EAAE,oBAAoB;GACxF;;CAGH,OAAO,aAAa,SAAkD;AACpE,SAAO;GACL,QAAA;GACA,SAAS,QAAQ,WAAW,EAAE;GAC9B,WAAW,CACT;IACE,SAAS;IACT,QAAQ,QAAQ,UAAU,EAAE;IAC5B,YAAY,QAAQ;IACrB,EACD,oBACD;GACF;;;2CArBJ,OAAO,EAAE,CAAC,CAAA,EAAA,aAAA"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@joakimbugge/nest-typeorm-seeder",
3
+ "version": "0.2.0",
4
+ "description": "NestJS module for @joakimbugge/typeorm-seeder",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/joakimbugge/to-seeder.git"
8
+ },
9
+ "homepage": "https://github.com/joakimbugge/to-seeder#readme",
10
+ "bugs": "https://github.com/joakimbugge/to-seeder/issues",
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ ".": {
17
+ "import": {
18
+ "types": "./dist/index.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ },
21
+ "require": {
22
+ "types": "./dist/index.d.cts",
23
+ "default": "./dist/index.cjs"
24
+ }
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsdown",
32
+ "prepublishOnly": "pnpm run build",
33
+ "dev": "tsdown --watch",
34
+ "typecheck": "tsc --noEmit",
35
+ "test": "vitest",
36
+ "test:run": "vitest run",
37
+ "lint": "oxlint src tests",
38
+ "lint:fix": "oxlint --fix src tests",
39
+ "fmt": "oxfmt src tests",
40
+ "fmt:check": "oxfmt --check src tests"
41
+ },
42
+ "keywords": [
43
+ "typeorm",
44
+ "seeder",
45
+ "decorator",
46
+ "database",
47
+ "seed",
48
+ "nestjs"
49
+ ],
50
+ "license": "MIT",
51
+ "engines": {
52
+ "node": ">=16.13.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@nestjs/common": ">=10.0.0",
56
+ "@nestjs/core": ">=10.0.0",
57
+ "reflect-metadata": ">=0.1.0",
58
+ "typeorm": ">=0.3.0"
59
+ },
60
+ "devDependencies": {
61
+ "@faker-js/faker": "^10.4.0",
62
+ "@nestjs/common": "^11.0.0",
63
+ "@nestjs/core": "^11.0.0",
64
+ "@nestjs/testing": "^11.0.0",
65
+ "@types/better-sqlite3": "^7.6.13",
66
+ "better-sqlite3": "^12.8.0",
67
+ "reflect-metadata": "^0.2.2",
68
+ "typeorm": "1.0.0-beta.1"
69
+ },
70
+ "dependencies": {
71
+ "@joakimbugge/typeorm-seeder": "workspace:*"
72
+ }
73
+ }