@lenne.tech/nest-server 11.3.0 → 11.4.1

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 (35) hide show
  1. package/bin/migrate.js +40 -0
  2. package/dist/core/modules/migrate/cli/migrate-cli.d.ts +3 -0
  3. package/dist/core/modules/migrate/cli/migrate-cli.js +221 -0
  4. package/dist/core/modules/migrate/cli/migrate-cli.js.map +1 -0
  5. package/dist/core/modules/migrate/helpers/migration.helper.d.ts +12 -0
  6. package/dist/core/modules/migrate/helpers/migration.helper.js +57 -0
  7. package/dist/core/modules/migrate/helpers/migration.helper.js.map +1 -0
  8. package/dist/core/modules/migrate/helpers/ts-compiler.d.ts +2 -0
  9. package/dist/core/modules/migrate/helpers/ts-compiler.js +3 -0
  10. package/dist/core/modules/migrate/helpers/ts-compiler.js.map +1 -0
  11. package/dist/core/modules/migrate/index.d.ts +4 -0
  12. package/dist/core/modules/migrate/index.js +21 -0
  13. package/dist/core/modules/migrate/index.js.map +1 -0
  14. package/dist/core/modules/migrate/migration-runner.d.ts +26 -0
  15. package/dist/core/modules/migrate/migration-runner.js +124 -0
  16. package/dist/core/modules/migrate/migration-runner.js.map +1 -0
  17. package/dist/core/modules/migrate/mongo-state-store.d.ts +30 -0
  18. package/dist/core/modules/migrate/mongo-state-store.js +105 -0
  19. package/dist/core/modules/migrate/mongo-state-store.js.map +1 -0
  20. package/dist/core/modules/migrate/templates/migration-project.template.ts +53 -0
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +1 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/tsconfig.build.tsbuildinfo +1 -1
  25. package/package.json +9 -3
  26. package/src/core/modules/migrate/MIGRATION_FROM_NODEPIT.md +298 -0
  27. package/src/core/modules/migrate/README.md +453 -0
  28. package/src/core/modules/migrate/cli/migrate-cli.ts +319 -0
  29. package/src/core/modules/migrate/helpers/migration.helper.ts +117 -0
  30. package/src/core/modules/migrate/helpers/ts-compiler.js +14 -0
  31. package/src/core/modules/migrate/index.ts +41 -0
  32. package/src/core/modules/migrate/migration-runner.ts +230 -0
  33. package/src/core/modules/migrate/mongo-state-store.ts +283 -0
  34. package/src/core/modules/migrate/templates/migration-project.template.ts +53 -0
  35. package/src/index.ts +9 -3
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Migration CLI - Compatible with `migrate` package CLI
5
+ *
6
+ * This provides a drop-in replacement for the `migrate` CLI without requiring
7
+ * the external `migrate` package as a dependency.
8
+ *
9
+ * Usage (same as migrate package):
10
+ * migrate create <name> [options]
11
+ * migrate up [name] [options]
12
+ * migrate down [name] [options]
13
+ * migrate list [options]
14
+ *
15
+ * Options:
16
+ * --migrations-dir, -d Directory containing migrations (default: ./migrations)
17
+ * --store, -s Path to state store module
18
+ * --compiler, -c Compiler to use (e.g., ts:./path/to/ts-compiler.js)
19
+ * --template-file, -t Template file for creating migrations
20
+ */
21
+
22
+ /* eslint-disable no-console */
23
+ // Console output is required for CLI functionality
24
+
25
+ import * as fs from 'fs';
26
+ import * as path from 'path';
27
+
28
+ import { MigrationRunner } from '../migration-runner';
29
+ import { MongoStateStore } from '../mongo-state-store';
30
+
31
+ interface CliOptions {
32
+ compiler?: string;
33
+ migrationsDir: string;
34
+ store?: string;
35
+ templateFile?: string;
36
+ }
37
+
38
+ /**
39
+ * Create a new migration file
40
+ */
41
+ async function createMigration(name: string, options: CliOptions) {
42
+ if (!name) {
43
+ console.error('Error: Migration name is required');
44
+ console.log('Usage: migrate create <name> [options]');
45
+ process.exit(1);
46
+ }
47
+
48
+ const timestamp = Date.now();
49
+ const fileName = `${timestamp}-${name}.ts`;
50
+ const filePath = path.join(options.migrationsDir, fileName);
51
+
52
+ // Create migrations directory if it doesn't exist
53
+ if (!fs.existsSync(options.migrationsDir)) {
54
+ fs.mkdirSync(options.migrationsDir, { recursive: true });
55
+ }
56
+
57
+ let template: string;
58
+
59
+ // Use custom template if provided
60
+ if (options.templateFile) {
61
+ const templatePath = path.resolve(process.cwd(), options.templateFile);
62
+ template = fs.readFileSync(templatePath, 'utf-8');
63
+ } else {
64
+ // Default template
65
+ template = `/**
66
+ * Migration: ${name}
67
+ * Created: ${new Date().toISOString()}
68
+ */
69
+
70
+ export const up = async () => {
71
+ // TODO: Implement migration
72
+ console.log('Running migration: ${name}');
73
+ };
74
+
75
+ export const down = async () => {
76
+ // TODO: Implement rollback
77
+ console.log('Rolling back migration: ${name}');
78
+ };
79
+ `;
80
+ }
81
+
82
+ fs.writeFileSync(filePath, template, 'utf-8');
83
+ console.log(`Created migration: ${fileName}`);
84
+ }
85
+
86
+ /**
87
+ * List migration status
88
+ */
89
+ async function listMigrations(options: CliOptions) {
90
+ registerCompiler(options.compiler);
91
+ const stateStore = loadStateStore(options.store);
92
+
93
+ const runner = new MigrationRunner({
94
+ migrationsDirectory: options.migrationsDir,
95
+ stateStore,
96
+ });
97
+
98
+ const status = await runner.status();
99
+
100
+ console.log('\nMigration Status:');
101
+ console.log('=================\n');
102
+
103
+ if (status.completed.length > 0) {
104
+ console.log('Completed:');
105
+ status.completed.forEach((name) => {
106
+ console.log(` ✓ ${name}`);
107
+ });
108
+ console.log('');
109
+ }
110
+
111
+ if (status.pending.length > 0) {
112
+ console.log('Pending:');
113
+ status.pending.forEach((name) => {
114
+ console.log(` ⋯ ${name}`);
115
+ });
116
+ console.log('');
117
+ }
118
+
119
+ if (status.completed.length === 0 && status.pending.length === 0) {
120
+ console.log('No migrations found\n');
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Load state store from module
126
+ */
127
+ function loadStateStore(storePath: string | undefined): MongoStateStore {
128
+ if (!storePath) {
129
+ throw new Error('--store option is required for up/down/list commands');
130
+ }
131
+
132
+ const absolutePath = path.resolve(process.cwd(), storePath);
133
+
134
+ const StoreClass = require(absolutePath);
135
+
136
+ // Handle different export patterns
137
+ if (StoreClass.default) {
138
+ return new StoreClass.default();
139
+ } else if (typeof StoreClass === 'function') {
140
+ return new StoreClass();
141
+ } else {
142
+ throw new Error(`Invalid state store module at ${storePath}`);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Main CLI entry point
148
+ */
149
+ async function main() {
150
+ const { command, name, options } = parseArgs();
151
+
152
+ try {
153
+ switch (command) {
154
+ case '--help':
155
+
156
+ case '-h':
157
+
158
+ case 'help':
159
+ showHelp();
160
+ break;
161
+
162
+ case 'create':
163
+ await createMigration(name!, options);
164
+ break;
165
+ case 'down':
166
+ await runDown(options);
167
+ break;
168
+
169
+ case 'list':
170
+ case 'status':
171
+ await listMigrations(options);
172
+ break;
173
+ case 'up':
174
+ await runUp(options);
175
+ break;
176
+
177
+ default:
178
+ if (!command) {
179
+ showHelp();
180
+ } else {
181
+ console.error(`Unknown command: ${command}`);
182
+ console.log('Run "migrate --help" for usage information');
183
+ process.exit(1);
184
+ }
185
+ }
186
+ } catch (error) {
187
+ console.error('Error:', (error as Error).message);
188
+ if (process.env.DEBUG) {
189
+ console.error((error as Error).stack);
190
+ }
191
+ process.exit(1);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Parse command line arguments
197
+ */
198
+ function parseArgs(): { command: string; name?: string; options: CliOptions } {
199
+ const args = process.argv.slice(2);
200
+ const command = args[0];
201
+ let name: string | undefined;
202
+ const options: CliOptions = {
203
+ migrationsDir: './migrations',
204
+ };
205
+
206
+ // Check if second arg is a name (not a flag)
207
+ if (args[1] && !args[1].startsWith('-')) {
208
+ name = args[1];
209
+ }
210
+
211
+ // Parse flags
212
+ for (let i = 1; i < args.length; i++) {
213
+ const arg = args[i];
214
+
215
+ if (arg === '--migrations-dir' || arg === '-d') {
216
+ options.migrationsDir = args[++i];
217
+ } else if (arg === '--store' || arg === '-s') {
218
+ options.store = args[++i];
219
+ } else if (arg === '--compiler' || arg === '-c') {
220
+ options.compiler = args[++i];
221
+ } else if (arg === '--template-file' || arg === '-t') {
222
+ options.templateFile = args[++i];
223
+ }
224
+ }
225
+
226
+ return { command, name, options };
227
+ }
228
+
229
+ /**
230
+ * Register TypeScript compiler if specified
231
+ */
232
+ function registerCompiler(compiler: string | undefined) {
233
+ if (!compiler) {
234
+ return;
235
+ }
236
+
237
+ // Format: "ts:./path/to/compiler.js"
238
+ const [type, compilerPath] = compiler.split(':');
239
+
240
+ if (type === 'ts' && compilerPath) {
241
+ const absolutePath = path.resolve(process.cwd(), compilerPath);
242
+
243
+ const register = require(absolutePath);
244
+ if (typeof register === 'function') {
245
+ register();
246
+ }
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Run migrations down
252
+ */
253
+ async function runDown(options: CliOptions) {
254
+ registerCompiler(options.compiler);
255
+ const stateStore = loadStateStore(options.store);
256
+
257
+ const runner = new MigrationRunner({
258
+ migrationsDirectory: options.migrationsDir,
259
+ stateStore,
260
+ });
261
+
262
+ await runner.down();
263
+ }
264
+
265
+ /**
266
+ * Run migrations up
267
+ */
268
+ async function runUp(options: CliOptions) {
269
+ registerCompiler(options.compiler);
270
+ const stateStore = loadStateStore(options.store);
271
+
272
+ const runner = new MigrationRunner({
273
+ migrationsDirectory: options.migrationsDir,
274
+ stateStore,
275
+ });
276
+
277
+ await runner.up();
278
+ }
279
+
280
+ /**
281
+ * Show help
282
+ */
283
+ function showHelp() {
284
+ console.log(`
285
+ Migration CLI - Compatible with migrate package
286
+
287
+ Usage:
288
+ migrate create <name> [options] Create a new migration
289
+ migrate up [options] Run all pending migrations
290
+ migrate down [options] Rollback the last migration
291
+ migrate list [options] List migration status
292
+
293
+ Options:
294
+ --migrations-dir, -d <path> Directory containing migrations (default: ./migrations)
295
+ --store, -s <path> Path to state store module
296
+ --compiler, -c <compiler> Compiler to use (e.g., ts:./path/to/ts-compiler.js)
297
+ --template-file, -t <path> Template file for creating migrations
298
+
299
+ Examples:
300
+ migrate create add-user-email
301
+ migrate create add-user-email --template-file ./migrations-utils/template.ts
302
+ migrate up --store ./migrations-utils/migrate.js --migrations-dir ./migrations
303
+ migrate down --store ./migrations-utils/migrate.js
304
+ migrate list --store ./migrations-utils/migrate.js
305
+
306
+ Environment Variables:
307
+ NODE_ENV Set environment (e.g., development, production)
308
+ `);
309
+ }
310
+
311
+ // Run CLI if executed directly
312
+ if (require.main === module) {
313
+ main().catch((error) => {
314
+ console.error('Fatal error:', error);
315
+ process.exit(1);
316
+ });
317
+ }
318
+
319
+ export { main };
@@ -0,0 +1,117 @@
1
+ import * as fs from 'fs';
2
+ import { Db, GridFSBucket, MongoClient, ObjectId } from 'mongodb';
3
+ import * as path from 'path';
4
+
5
+ /**
6
+ * Migration helper functions for database operations
7
+ */
8
+
9
+ /**
10
+ * Get database connection
11
+ *
12
+ * @param mongoUrl - MongoDB connection URI
13
+ * @returns Promise with database instance
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const db = await getDb('mongodb://localhost/mydb');
18
+ * await db.collection('users').updateMany(...);
19
+ * ```
20
+ */
21
+ export const getDb = async (mongoUrl: string): Promise<Db> => {
22
+ const client: MongoClient = await MongoClient.connect(mongoUrl);
23
+ return client.db();
24
+ };
25
+
26
+ /**
27
+ * Upload file to GridFS
28
+ *
29
+ * @param mongoUrl - MongoDB connection URI
30
+ * @param relativePath - Relative path to the file
31
+ * @param options - Optional bucket name and filename
32
+ * @returns Promise with ObjectId of uploaded file
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const fileId = await uploadFileToGridFS(
37
+ * 'mongodb://localhost/mydb',
38
+ * '../assets/image.png',
39
+ * { bucketName: 'images', filename: 'logo.png' }
40
+ * );
41
+ * ```
42
+ */
43
+ export const uploadFileToGridFS = async (
44
+ mongoUrl: string,
45
+ relativePath: string,
46
+ options?: { bucketName?: string; filename?: string },
47
+ ): Promise<ObjectId> => {
48
+ if (!relativePath) {
49
+ throw new Error('relativePath is required');
50
+ }
51
+
52
+ const { bucketName, filename } = {
53
+ bucketName: 'fs',
54
+ filename: relativePath.split('/')[relativePath.split('/').length - 1],
55
+ ...options,
56
+ };
57
+
58
+ return new Promise<ObjectId>(async (resolve, reject) => {
59
+ let client: MongoClient | undefined;
60
+ try {
61
+ client = await MongoClient.connect(mongoUrl);
62
+ const db = client.db();
63
+ const bucket = new GridFSBucket(db, { bucketName });
64
+ const writeStream = bucket.openUploadStream(filename);
65
+
66
+ const rs = fs.createReadStream(path.resolve(__dirname, relativePath)).pipe(writeStream);
67
+
68
+ rs.on('finish', () => {
69
+ resolve(writeStream.id as ObjectId);
70
+ });
71
+
72
+ rs.on('error', (err) => {
73
+ reject(err);
74
+ });
75
+ } catch (err) {
76
+ reject(err);
77
+ } finally {
78
+ // Note: Connection will be closed when stream finishes
79
+ // but we keep the client reference for proper error handling
80
+ }
81
+ });
82
+ };
83
+
84
+ /**
85
+ * Create a migration state store factory
86
+ *
87
+ * @param mongoUrl - MongoDB connection URI
88
+ * @param collectionName - Optional collection name (default: 'migrations')
89
+ * @param lockCollectionName - Optional lock collection name for cluster environments
90
+ * @returns MongoStateStore class that can be used with migrate CLI
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * // In migrations-utils/migrate.js:
95
+ * const { createMigrationStore } = require('@lenne.tech/nest-server');
96
+ * const config = require('../src/config.env');
97
+ *
98
+ * module.exports = createMigrationStore(config.default.mongoose.uri);
99
+ * ```
100
+ */
101
+ export const createMigrationStore = (
102
+ mongoUrl: string,
103
+ collectionName: string = 'migrations',
104
+ lockCollectionName?: string,
105
+ ) => {
106
+ const { MongoStateStore } = require('../mongo-state-store');
107
+
108
+ return class MigrationStateStore extends MongoStateStore {
109
+ constructor() {
110
+ super({
111
+ collectionName,
112
+ lockCollectionName,
113
+ uri: mongoUrl,
114
+ });
115
+ }
116
+ };
117
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * TypeScript compiler for migrate CLI
3
+ *
4
+ * This file registers ts-node to allow TypeScript migrations to be executed.
5
+ *
6
+ * Usage with migrate CLI:
7
+ * migrate --compiler="ts:./node_modules/@lenne.tech/nest-server/dist/core/modules/migrate/helpers/ts-compiler.js"
8
+ *
9
+ * Or copy this file to your project's migrations-utils folder.
10
+ */
11
+
12
+ const tsNode = require('ts-node');
13
+
14
+ module.exports = tsNode.register;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Migration module exports
3
+ *
4
+ * This module provides MongoDB-based state storage for migration frameworks.
5
+ * It is compatible with the @nodepit/migrate-state-store-mongodb package
6
+ * and supports MongoDB 6+.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { MongoStateStore, synchronizedUp, createMigrationStore } from '@lenne.tech/nest-server';
11
+ *
12
+ * // Basic usage
13
+ * const stateStore = new MongoStateStore('mongodb://localhost/mydb');
14
+ *
15
+ * // With custom collection names
16
+ * const stateStore = new MongoStateStore({
17
+ * uri: 'mongodb://localhost/mydb',
18
+ * collectionName: 'my_migrations',
19
+ * lockCollectionName: 'migration_lock'
20
+ * });
21
+ *
22
+ * // Using synchronized migration for clustered environments
23
+ * await synchronizedUp({
24
+ * stateStore: new MongoStateStore({
25
+ * uri: 'mongodb://localhost/mydb',
26
+ * lockCollectionName: 'migration_lock'
27
+ * })
28
+ * });
29
+ *
30
+ * // Create a migration store for use with migrate CLI
31
+ * // In your project's migrations-utils/migrate.js:
32
+ * const { createMigrationStore } = require('@lenne.tech/nest-server');
33
+ * const config = require('../src/config.env');
34
+ * module.exports = createMigrationStore(config.default.mongoose.uri);
35
+ * ```
36
+ */
37
+
38
+ export * from './cli/migrate-cli';
39
+ export * from './helpers/migration.helper';
40
+ export * from './migration-runner';
41
+ export * from './mongo-state-store';