@friggframework/core 2.0.0-next.44 → 2.0.0-next.46

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 (166) hide show
  1. package/README.md +28 -0
  2. package/application/commands/integration-commands.js +19 -0
  3. package/core/Worker.js +8 -21
  4. package/credential/repositories/credential-repository-mongo.js +14 -8
  5. package/credential/repositories/credential-repository-postgres.js +14 -8
  6. package/credential/repositories/credential-repository.js +3 -8
  7. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  8. package/database/adapters/lambda-invoker.js +97 -0
  9. package/database/config.js +11 -2
  10. package/database/models/WebsocketConnection.js +11 -10
  11. package/database/prisma.js +63 -3
  12. package/database/repositories/health-check-repository-mongodb.js +3 -0
  13. package/database/repositories/migration-status-repository-s3.js +137 -0
  14. package/database/use-cases/check-database-state-use-case.js +81 -0
  15. package/database/use-cases/check-encryption-health-use-case.js +3 -2
  16. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  17. package/database/use-cases/get-migration-status-use-case.js +93 -0
  18. package/database/use-cases/run-database-migration-use-case.js +137 -0
  19. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  20. package/database/utils/mongodb-collection-utils.js +91 -0
  21. package/database/utils/mongodb-schema-init.js +106 -0
  22. package/database/utils/prisma-runner.js +400 -0
  23. package/database/utils/prisma-schema-parser.js +182 -0
  24. package/encrypt/Cryptor.js +14 -16
  25. package/generated/prisma-mongodb/client.d.ts +1 -0
  26. package/generated/prisma-mongodb/client.js +4 -0
  27. package/generated/prisma-mongodb/default.d.ts +1 -0
  28. package/generated/prisma-mongodb/default.js +4 -0
  29. package/generated/prisma-mongodb/edge.d.ts +1 -0
  30. package/generated/prisma-mongodb/edge.js +334 -0
  31. package/generated/prisma-mongodb/index-browser.js +316 -0
  32. package/generated/prisma-mongodb/index.d.ts +22897 -0
  33. package/generated/prisma-mongodb/index.js +359 -0
  34. package/generated/prisma-mongodb/package.json +183 -0
  35. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  36. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  37. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  38. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  39. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  40. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  41. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  42. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  43. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  44. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  45. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  46. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  47. package/generated/prisma-mongodb/schema.prisma +362 -0
  48. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  49. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  50. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  51. package/generated/prisma-mongodb/wasm.js +341 -0
  52. package/generated/prisma-postgresql/client.d.ts +1 -0
  53. package/generated/prisma-postgresql/client.js +4 -0
  54. package/generated/prisma-postgresql/default.d.ts +1 -0
  55. package/generated/prisma-postgresql/default.js +4 -0
  56. package/generated/prisma-postgresql/edge.d.ts +1 -0
  57. package/generated/prisma-postgresql/edge.js +356 -0
  58. package/generated/prisma-postgresql/index-browser.js +338 -0
  59. package/generated/prisma-postgresql/index.d.ts +25071 -0
  60. package/generated/prisma-postgresql/index.js +381 -0
  61. package/generated/prisma-postgresql/package.json +183 -0
  62. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  63. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  64. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  65. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  66. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  67. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  68. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  69. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  70. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  71. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  72. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  73. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  74. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  75. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  76. package/generated/prisma-postgresql/schema.prisma +345 -0
  77. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  78. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  79. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  80. package/generated/prisma-postgresql/wasm.js +363 -0
  81. package/handlers/WEBHOOKS.md +653 -0
  82. package/handlers/backend-utils.js +118 -3
  83. package/handlers/database-migration-handler.js +227 -0
  84. package/handlers/routers/auth.js +1 -1
  85. package/handlers/routers/db-migration.handler.js +29 -0
  86. package/handlers/routers/db-migration.js +256 -0
  87. package/handlers/routers/health.js +41 -6
  88. package/handlers/routers/integration-webhook-routers.js +67 -0
  89. package/handlers/use-cases/check-integrations-health-use-case.js +22 -10
  90. package/handlers/workers/db-migration.js +352 -0
  91. package/index.js +28 -0
  92. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  93. package/integrations/integration-base.js +74 -3
  94. package/integrations/integration-router.js +60 -70
  95. package/integrations/repositories/integration-repository-interface.js +12 -0
  96. package/integrations/repositories/integration-repository-mongo.js +32 -0
  97. package/integrations/repositories/integration-repository-postgres.js +33 -0
  98. package/integrations/repositories/process-repository-postgres.js +43 -20
  99. package/integrations/tests/doubles/dummy-integration-class.js +1 -8
  100. package/integrations/tests/doubles/test-integration-repository.js +2 -2
  101. package/logs/logger.js +0 -4
  102. package/modules/entity.js +0 -1
  103. package/modules/repositories/module-repository-mongo.js +3 -12
  104. package/modules/repositories/module-repository-postgres.js +0 -11
  105. package/modules/repositories/module-repository.js +1 -12
  106. package/modules/use-cases/get-entity-options-by-id.js +1 -1
  107. package/modules/use-cases/get-module.js +1 -2
  108. package/modules/use-cases/refresh-entity-options.js +1 -1
  109. package/modules/use-cases/test-module-auth.js +1 -1
  110. package/package.json +82 -66
  111. package/prisma-mongodb/schema.prisma +21 -21
  112. package/prisma-postgresql/schema.prisma +15 -15
  113. package/queues/queuer-util.js +28 -15
  114. package/types/core/index.d.ts +2 -2
  115. package/types/module-plugin/index.d.ts +0 -2
  116. package/user/repositories/user-repository-mongo.js +53 -12
  117. package/user/repositories/user-repository-postgres.js +53 -14
  118. package/user/use-cases/authenticate-user.js +127 -0
  119. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  120. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  121. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  122. package/user/use-cases/login-user.js +1 -1
  123. package/user/user.js +18 -2
  124. package/websocket/repositories/websocket-connection-repository-mongo.js +11 -10
  125. package/websocket/repositories/websocket-connection-repository-postgres.js +11 -10
  126. package/websocket/repositories/websocket-connection-repository.js +11 -10
  127. package/application/commands/integration-commands.test.js +0 -123
  128. package/database/encryption/encryption-integration.test.js +0 -553
  129. package/database/encryption/encryption-schema-registry.test.js +0 -392
  130. package/database/encryption/field-encryption-service.test.js +0 -525
  131. package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
  132. package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
  133. package/database/encryption/postgres-relation-decryption.test.js +0 -245
  134. package/database/encryption/prisma-encryption-extension.test.js +0 -439
  135. package/errors/base-error.test.js +0 -32
  136. package/errors/fetch-error.test.js +0 -79
  137. package/errors/halt-error.test.js +0 -11
  138. package/errors/validation-errors.test.js +0 -120
  139. package/handlers/auth-flow.integration.test.js +0 -147
  140. package/handlers/integration-event-dispatcher.test.js +0 -141
  141. package/handlers/routers/health.test.js +0 -210
  142. package/integrations/tests/use-cases/create-integration.test.js +0 -131
  143. package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
  144. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
  145. package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
  146. package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
  147. package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
  148. package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
  149. package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
  150. package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
  151. package/integrations/tests/use-cases/update-integration.test.js +0 -141
  152. package/integrations/use-cases/create-process.test.js +0 -178
  153. package/integrations/use-cases/get-process.test.js +0 -190
  154. package/integrations/use-cases/load-integration-context-full.test.js +0 -329
  155. package/integrations/use-cases/load-integration-context.test.js +0 -114
  156. package/integrations/use-cases/update-process-metrics.test.js +0 -308
  157. package/integrations/use-cases/update-process-state.test.js +0 -256
  158. package/lambda/TimeoutCatcher.test.js +0 -68
  159. package/logs/logger.test.js +0 -76
  160. package/modules/module-hydration.test.js +0 -205
  161. package/modules/requester/requester.test.js +0 -28
  162. package/user/tests/use-cases/create-individual-user.test.js +0 -24
  163. package/user/tests/use-cases/create-organization-user.test.js +0 -28
  164. package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
  165. package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
  166. package/user/tests/use-cases/login-user.test.js +0 -140
@@ -0,0 +1,400 @@
1
+ const { execSync, spawn } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const chalk = require('chalk');
5
+
6
+ /**
7
+ * Prisma Command Runner Utility
8
+ * Handles execution of Prisma CLI commands for database setup
9
+ */
10
+
11
+ /**
12
+ * Gets the path to the Prisma schema file for the database type
13
+ * @param {'mongodb'|'postgresql'} dbType - Database type
14
+ * @param {string} projectRoot - Project root directory
15
+ * @returns {string} Absolute path to schema file
16
+ * @throws {Error} If schema file doesn't exist
17
+ */
18
+ function getPrismaSchemaPath(dbType, projectRoot = process.cwd()) {
19
+ // Try multiple locations for the schema file
20
+ // Priority order:
21
+ // 1. Lambda layer path (where the schema actually exists in deployed Lambda)
22
+ // 2. Local node_modules (where @friggframework/core is installed - production scenario)
23
+ // 3. Parent node_modules (workspace/monorepo setup)
24
+ const possiblePaths = [
25
+ // Lambda layer path - this is where the schema actually exists in deployed Lambda
26
+ `/opt/nodejs/node_modules/generated/prisma-${dbType}/schema.prisma`,
27
+ // Check where Frigg is installed via npm (production scenario)
28
+ path.join(projectRoot, 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma'),
29
+ path.join(projectRoot, '..', 'node_modules', '@friggframework', 'core', `prisma-${dbType}`, 'schema.prisma')
30
+ ];
31
+
32
+ for (const schemaPath of possiblePaths) {
33
+ if (fs.existsSync(schemaPath)) {
34
+ return schemaPath;
35
+ }
36
+ }
37
+
38
+ // If not found in any location, throw error
39
+ throw new Error(
40
+ `Prisma schema not found at:\n${possiblePaths.join('\n')}\n\n` +
41
+ 'Ensure @friggframework/core is installed.'
42
+ );
43
+ }
44
+
45
+ /**
46
+ * Runs prisma generate for the specified database type
47
+ * @param {'mongodb'|'postgresql'} dbType - Database type
48
+ * @param {boolean} verbose - Enable verbose output
49
+ * @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
50
+ */
51
+ async function runPrismaGenerate(dbType, verbose = false) {
52
+ try {
53
+ const schemaPath = getPrismaSchemaPath(dbType);
54
+
55
+ // Check if Prisma client already exists (e.g., in Lambda or pre-generated)
56
+ const generatedClientPath = path.join(path.dirname(path.dirname(schemaPath)), 'generated', `prisma-${dbType}`, 'client.js');
57
+ const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
58
+
59
+ // In Lambda, also check the layer path (/opt/nodejs/node_modules)
60
+ const lambdaLayerClientPath = `/opt/nodejs/node_modules/generated/prisma-${dbType}/client.js`;
61
+
62
+ const clientExists = fs.existsSync(generatedClientPath) || (isLambdaEnvironment && fs.existsSync(lambdaLayerClientPath));
63
+
64
+ if (clientExists) {
65
+ const foundPath = fs.existsSync(generatedClientPath) ? generatedClientPath : lambdaLayerClientPath;
66
+ if (verbose) {
67
+ console.log(chalk.gray(`✓ Prisma client already generated at: ${foundPath}`));
68
+ }
69
+ if (isLambdaEnvironment) {
70
+ if (verbose) {
71
+ console.log(chalk.gray('Skipping generation in Lambda environment (using pre-generated client)'));
72
+ }
73
+ return {
74
+ success: true,
75
+ output: 'Using pre-generated Prisma client (Lambda environment)'
76
+ };
77
+ }
78
+ }
79
+
80
+ if (verbose) {
81
+ console.log(chalk.gray(`Running: npx prisma generate --schema=${schemaPath}`));
82
+ }
83
+
84
+ const output = execSync(
85
+ `npx prisma generate --schema=${schemaPath}`,
86
+ {
87
+ encoding: 'utf8',
88
+ stdio: verbose ? 'inherit' : 'pipe',
89
+ env: {
90
+ ...process.env,
91
+ // Suppress Prisma telemetry prompts
92
+ PRISMA_HIDE_UPDATE_MESSAGE: '1'
93
+ }
94
+ }
95
+ );
96
+
97
+ return {
98
+ success: true,
99
+ output: verbose ? 'Generated successfully' : output
100
+ };
101
+
102
+ } catch (error) {
103
+ return {
104
+ success: false,
105
+ error: error.message,
106
+ output: error.stdout?.toString() || error.stderr?.toString()
107
+ };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Checks database migration status
113
+ * @param {'mongodb'|'postgresql'} dbType - Database type
114
+ * @returns {Promise<Object>} { upToDate: boolean, pendingMigrations?: number, error?: string }
115
+ */
116
+ async function checkDatabaseState(dbType) {
117
+ try {
118
+ // Only applicable for PostgreSQL (MongoDB uses db push)
119
+ if (dbType !== 'postgresql') {
120
+ return { upToDate: true };
121
+ }
122
+
123
+ const schemaPath = getPrismaSchemaPath(dbType);
124
+ const prismaBin = getPrismaBinaryPath();
125
+
126
+ // Use direct path instead of npx to avoid WASM file resolution issues
127
+ const isDirectBinary = prismaBin !== 'npx prisma';
128
+ const command = isDirectBinary
129
+ ? `${prismaBin} migrate status --schema=${schemaPath}`
130
+ : `npx prisma migrate status --schema=${schemaPath}`;
131
+
132
+ const output = execSync(
133
+ command,
134
+ {
135
+ encoding: 'utf8',
136
+ stdio: 'pipe',
137
+ env: {
138
+ ...process.env,
139
+ PRISMA_HIDE_UPDATE_MESSAGE: '1'
140
+ }
141
+ }
142
+ );
143
+
144
+ if (output.includes('Database schema is up to date')) {
145
+ return { upToDate: true };
146
+ }
147
+
148
+ // Parse pending migrations count
149
+ const pendingMatch = output.match(/(\d+) migration/);
150
+ const pendingMigrations = pendingMatch ? parseInt(pendingMatch[1]) : 0;
151
+
152
+ return {
153
+ upToDate: false,
154
+ pendingMigrations
155
+ };
156
+
157
+ } catch (error) {
158
+ // If migrate status fails, database might not be initialized
159
+ return {
160
+ upToDate: false,
161
+ error: error.message
162
+ };
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Gets the path to the Prisma CLI entry point
168
+ *
169
+ * IMPORTANT: We invoke prisma/build/index.js directly instead of .bin/prisma
170
+ * because .bin/prisma uses __dirname to find WASM files, and when the symlink
171
+ * is resolved during Lambda packaging, __dirname points to .bin/ instead of
172
+ * prisma/build/, causing WASM files to not be found.
173
+ *
174
+ * @returns {string} Command to run Prisma CLI (e.g., 'node /path/to/index.js' or 'npx prisma')
175
+ */
176
+ function getPrismaBinaryPath() {
177
+ const fs = require('fs');
178
+
179
+ // Check function's bundled Prisma (Lambda) - use actual CLI location
180
+ const functionPrisma = '/var/task/node_modules/prisma/build/index.js';
181
+ if (fs.existsSync(functionPrisma)) {
182
+ return `node ${functionPrisma}`;
183
+ }
184
+
185
+ // Check Lambda layer path - use actual CLI location
186
+ const layerPrisma = '/opt/nodejs/node_modules/prisma/build/index.js';
187
+ if (fs.existsSync(layerPrisma)) {
188
+ return `node ${layerPrisma}`;
189
+ }
190
+
191
+ // Check local node_modules - use actual CLI location
192
+ const localPrisma = path.join(process.cwd(), 'node_modules', 'prisma', 'build', 'index.js');
193
+ if (fs.existsSync(localPrisma)) {
194
+ return `node ${localPrisma}`;
195
+ }
196
+
197
+ // Fallback to npx (local dev)
198
+ return 'npx prisma';
199
+ }
200
+
201
+ /**
202
+ * Runs Prisma migrate for PostgreSQL
203
+ * @param {'dev'|'deploy'} command - Migration command (dev or deploy)
204
+ * @param {boolean} verbose - Enable verbose output
205
+ * @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
206
+ */
207
+ async function runPrismaMigrate(command = 'dev', verbose = false) {
208
+ return new Promise((resolve) => {
209
+ try {
210
+ const schemaPath = getPrismaSchemaPath('postgresql');
211
+
212
+ // Get Prisma binary path (checks multiple locations)
213
+ const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
214
+ const prismaBin = getPrismaBinaryPath();
215
+
216
+ // Determine args based on whether we're using direct binary or npx
217
+ // Direct binary (e.g., /var/task/node_modules/.bin/prisma): ['migrate', command, ...]
218
+ // npx (local dev or fallback): ['prisma', 'migrate', command, ...]
219
+ const isDirectBinary = prismaBin !== 'npx';
220
+ const args = isDirectBinary
221
+ ? ['migrate', command, '--schema', schemaPath]
222
+ : ['prisma', 'migrate', command, '--schema', schemaPath];
223
+
224
+ if (verbose) {
225
+ const displayCmd = isDirectBinary
226
+ ? `${prismaBin} ${args.join(' ')}`
227
+ : `npx ${args.join(' ')}`;
228
+ console.log(chalk.gray(`Running: ${displayCmd}`));
229
+ }
230
+
231
+ // Execute the command (prismaBin might be 'node /path/to/index.js' or 'npx prisma')
232
+ const [executable, ...executableArgs] = prismaBin.split(' ');
233
+ const fullArgs = [...executableArgs, ...args];
234
+
235
+ const proc = spawn(executable, fullArgs, {
236
+ stdio: 'inherit',
237
+ env: {
238
+ ...process.env,
239
+ PRISMA_HIDE_UPDATE_MESSAGE: '1'
240
+ }
241
+ });
242
+
243
+ proc.on('error', (error) => {
244
+ resolve({
245
+ success: false,
246
+ error: error.message
247
+ });
248
+ });
249
+
250
+ proc.on('close', (code) => {
251
+ if (code === 0) {
252
+ resolve({
253
+ success: true,
254
+ output: 'Migration completed successfully'
255
+ });
256
+ } else {
257
+ resolve({
258
+ success: false,
259
+ error: `Migration process exited with code ${code}`
260
+ });
261
+ }
262
+ });
263
+
264
+ } catch (error) {
265
+ resolve({
266
+ success: false,
267
+ error: error.message
268
+ });
269
+ }
270
+ });
271
+ }
272
+
273
+ /**
274
+ * Runs Prisma db push for MongoDB
275
+ * @param {boolean} verbose - Enable verbose output
276
+ * @param {boolean} nonInteractive - Run in non-interactive mode (accepts data loss, for Lambda/CI)
277
+ * @returns {Promise<Object>} { success: boolean, output?: string, error?: string }
278
+ */
279
+ async function runPrismaDbPush(verbose = false, nonInteractive = false) {
280
+ return new Promise((resolve) => {
281
+ try {
282
+ const schemaPath = getPrismaSchemaPath('mongodb');
283
+
284
+ const args = [
285
+ 'prisma',
286
+ 'db',
287
+ 'push',
288
+ '--schema',
289
+ schemaPath,
290
+ '--skip-generate' // We generate separately
291
+ ];
292
+
293
+ // Add non-interactive flag for Lambda/CI environments
294
+ if (nonInteractive) {
295
+ args.push('--accept-data-loss');
296
+ }
297
+
298
+ if (verbose) {
299
+ console.log(chalk.gray(`Running: npx ${args.join(' ')}`));
300
+ }
301
+
302
+ if (nonInteractive) {
303
+ console.log(chalk.yellow('⚠️ Non-interactive mode: Data loss will be automatically accepted'));
304
+ } else {
305
+ console.log(chalk.yellow('⚠️ Interactive mode: You may be prompted if schema changes cause data loss'));
306
+ }
307
+
308
+ const proc = spawn('npx', args, {
309
+ stdio: nonInteractive ? 'pipe' : 'inherit', // Use pipe for non-interactive to capture output
310
+ env: {
311
+ ...process.env,
312
+ PRISMA_HIDE_UPDATE_MESSAGE: '1'
313
+ }
314
+ });
315
+
316
+ let stdout = '';
317
+ let stderr = '';
318
+
319
+ // Capture output in non-interactive mode
320
+ if (nonInteractive) {
321
+ if (proc.stdout) {
322
+ proc.stdout.on('data', (data) => {
323
+ stdout += data.toString();
324
+ if (verbose) {
325
+ process.stdout.write(data);
326
+ }
327
+ });
328
+ }
329
+ if (proc.stderr) {
330
+ proc.stderr.on('data', (data) => {
331
+ stderr += data.toString();
332
+ if (verbose) {
333
+ process.stderr.write(data);
334
+ }
335
+ });
336
+ }
337
+ }
338
+
339
+ proc.on('error', (error) => {
340
+ resolve({
341
+ success: false,
342
+ error: error.message
343
+ });
344
+ });
345
+
346
+ proc.on('close', (code) => {
347
+ if (code === 0) {
348
+ resolve({
349
+ success: true,
350
+ output: nonInteractive ? stdout || 'Database push completed successfully' : 'Database push completed successfully'
351
+ });
352
+ } else {
353
+ resolve({
354
+ success: false,
355
+ error: `Database push process exited with code ${code}`,
356
+ output: stderr || stdout
357
+ });
358
+ }
359
+ });
360
+
361
+ } catch (error) {
362
+ resolve({
363
+ success: false,
364
+ error: error.message
365
+ });
366
+ }
367
+ });
368
+ }
369
+
370
+ /**
371
+ * Determines migration command based on STAGE environment variable
372
+ * @param {string} stage - Stage from CLI option or environment
373
+ * @returns {'dev'|'deploy'}
374
+ */
375
+ function getMigrationCommand(stage) {
376
+ // Always use 'deploy' in Lambda environment (it's non-interactive and doesn't create migrations)
377
+ const isLambdaEnvironment = !!process.env.AWS_LAMBDA_FUNCTION_NAME || !!process.env.LAMBDA_TASK_ROOT;
378
+ if (isLambdaEnvironment) {
379
+ return 'deploy';
380
+ }
381
+
382
+ const normalizedStage = (stage || process.env.STAGE || 'development').toLowerCase();
383
+
384
+ const developmentStages = ['dev', 'local', 'test', 'development'];
385
+
386
+ if (developmentStages.includes(normalizedStage)) {
387
+ return 'dev';
388
+ }
389
+
390
+ return 'deploy';
391
+ }
392
+
393
+ module.exports = {
394
+ getPrismaSchemaPath,
395
+ runPrismaGenerate,
396
+ checkDatabaseState,
397
+ runPrismaMigrate,
398
+ runPrismaDbPush,
399
+ getMigrationCommand
400
+ };
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Prisma Schema Parser for MongoDB Collections
3
+ *
4
+ * Dynamically parses the Prisma schema file to extract MongoDB collection names.
5
+ * This ensures collection names stay in sync with the schema without hardcoding.
6
+ *
7
+ * Handles:
8
+ * - @@map() directives (custom collection names)
9
+ * - Models without @@map() (uses model name)
10
+ * - Comments and whitespace
11
+ * - Multiple schema file locations
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ /**
18
+ * Parse Prisma schema file to extract collection names
19
+ *
20
+ * Reads the schema.prisma file and extracts all model definitions,
21
+ * returning the actual MongoDB collection names (from @@map directives).
22
+ *
23
+ * @param {string} schemaPath - Path to schema.prisma file
24
+ * @returns {Promise<string[]>} Array of collection names
25
+ *
26
+ * @example
27
+ * ```js
28
+ * const collections = await parseCollectionsFromSchema('./prisma/schema.prisma');
29
+ * // Returns: ['User', 'Token', 'Credential', ...]
30
+ * ```
31
+ */
32
+ async function parseCollectionsFromSchema(schemaPath) {
33
+ try {
34
+ const schemaContent = await fs.promises.readFile(schemaPath, 'utf-8');
35
+ return extractCollectionNames(schemaContent);
36
+ } catch (error) {
37
+ throw new Error(
38
+ `Failed to parse Prisma schema at ${schemaPath}: ${error.message}`
39
+ );
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Synchronous version of parseCollectionsFromSchema
45
+ *
46
+ * @param {string} schemaPath - Path to schema.prisma file
47
+ * @returns {string[]} Array of collection names
48
+ */
49
+ function parseCollectionsFromSchemaSync(schemaPath) {
50
+ try {
51
+ const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
52
+ return extractCollectionNames(schemaContent);
53
+ } catch (error) {
54
+ throw new Error(
55
+ `Failed to parse Prisma schema at ${schemaPath}: ${error.message}`
56
+ );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Extract collection names from Prisma schema content
62
+ *
63
+ * Parses the schema content to find:
64
+ * 1. All model definitions
65
+ * 2. Their @@map() directives (if present)
66
+ * 3. Falls back to model name if no @@map()
67
+ *
68
+ * @param {string} schemaContent - Content of schema.prisma file
69
+ * @returns {string[]} Array of collection names
70
+ * @private
71
+ */
72
+ function extractCollectionNames(schemaContent) {
73
+ const collections = [];
74
+
75
+ // Match model blocks: "model ModelName { ... }"
76
+ // Using non-greedy match to handle multiple models
77
+ const modelRegex = /model\s+(\w+)\s*\{([^}]+)\}/g;
78
+
79
+ let match;
80
+ while ((match = modelRegex.exec(schemaContent)) !== null) {
81
+ const modelName = match[1];
82
+ const modelBody = match[2];
83
+
84
+ // Look for @@map("CollectionName") directive
85
+ const mapMatch = modelBody.match(/@@map\s*\(\s*["'](\w+)["']\s*\)/);
86
+
87
+ if (mapMatch) {
88
+ // Use mapped collection name
89
+ collections.push(mapMatch[1]);
90
+ } else {
91
+ // Use model name as collection name (Prisma default)
92
+ collections.push(modelName);
93
+ }
94
+ }
95
+
96
+ return collections;
97
+ }
98
+
99
+ /**
100
+ * Find Prisma MongoDB schema file
101
+ *
102
+ * Searches for the schema.prisma file in common locations:
103
+ * 1. prisma-mongodb/schema.prisma (Frigg convention)
104
+ * 2. prisma/schema.prisma (Prisma default)
105
+ * 3. schema.prisma (root)
106
+ *
107
+ * @param {string} startDir - Directory to start searching from
108
+ * @returns {string|null} Path to schema file, or null if not found
109
+ */
110
+ function findMongoDBSchemaFile(startDir = __dirname) {
111
+ // Start from database directory and work up
112
+ const baseDir = path.resolve(startDir, '../..');
113
+
114
+ const searchPaths = [
115
+ path.join(baseDir, 'prisma-mongodb', 'schema.prisma'),
116
+ path.join(baseDir, 'prisma', 'schema.prisma'),
117
+ path.join(baseDir, 'schema.prisma'),
118
+ ];
119
+
120
+ for (const schemaPath of searchPaths) {
121
+ if (fs.existsSync(schemaPath)) {
122
+ return schemaPath;
123
+ }
124
+ }
125
+
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Get MongoDB collection names from Prisma schema
131
+ *
132
+ * Convenience function that finds and parses the schema automatically.
133
+ *
134
+ * @returns {Promise<string[]>} Array of collection names
135
+ * @throws {Error} If schema file not found or parsing fails
136
+ *
137
+ * @example
138
+ * ```js
139
+ * const collections = await getCollectionsFromSchema();
140
+ * await ensureCollectionsExist(collections);
141
+ * ```
142
+ */
143
+ async function getCollectionsFromSchema() {
144
+ const schemaPath = findMongoDBSchemaFile();
145
+
146
+ if (!schemaPath) {
147
+ throw new Error(
148
+ 'Could not find Prisma MongoDB schema file. ' +
149
+ 'Searched: prisma-mongodb/schema.prisma, prisma/schema.prisma, schema.prisma'
150
+ );
151
+ }
152
+
153
+ return await parseCollectionsFromSchema(schemaPath);
154
+ }
155
+
156
+ /**
157
+ * Synchronous version of getCollectionsFromSchema
158
+ *
159
+ * @returns {string[]} Array of collection names
160
+ * @throws {Error} If schema file not found or parsing fails
161
+ */
162
+ function getCollectionsFromSchemaSync() {
163
+ const schemaPath = findMongoDBSchemaFile();
164
+
165
+ if (!schemaPath) {
166
+ throw new Error(
167
+ 'Could not find Prisma MongoDB schema file. ' +
168
+ 'Searched: prisma-mongodb/schema.prisma, prisma/schema.prisma, schema.prisma'
169
+ );
170
+ }
171
+
172
+ return parseCollectionsFromSchemaSync(schemaPath);
173
+ }
174
+
175
+ module.exports = {
176
+ parseCollectionsFromSchema,
177
+ parseCollectionsFromSchemaSync,
178
+ extractCollectionNames,
179
+ findMongoDBSchemaFile,
180
+ getCollectionsFromSchema,
181
+ getCollectionsFromSchemaSync,
182
+ };
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  const crypto = require('crypto');
20
- const AWS = require('aws-sdk');
20
+ const { KMSClient, GenerateDataKeyCommand, DecryptCommand } = require('@aws-sdk/client-kms');
21
21
  const aes = require('./aes');
22
22
 
23
23
  class Cryptor {
@@ -27,16 +27,15 @@ class Cryptor {
27
27
 
28
28
  async generateDataKey() {
29
29
  if (this.shouldUseAws) {
30
- const kmsClient = new AWS.KMS();
31
- const dataKey = await kmsClient
32
- .generateDataKey({
33
- KeyId: process.env.KMS_KEY_ARN,
34
- KeySpec: 'AES_256',
35
- })
36
- .promise();
30
+ const kmsClient = new KMSClient({});
31
+ const command = new GenerateDataKeyCommand({
32
+ KeyId: process.env.KMS_KEY_ARN,
33
+ KeySpec: 'AES_256',
34
+ });
35
+ const dataKey = await kmsClient.send(command);
37
36
 
38
37
  const keyId = Buffer.from(dataKey.KeyId).toString('base64');
39
- const encryptedKey = dataKey.CiphertextBlob.toString('base64');
38
+ const encryptedKey = Buffer.from(dataKey.CiphertextBlob).toString('base64');
40
39
  const plaintext = dataKey.Plaintext;
41
40
  return { keyId, encryptedKey, plaintext };
42
41
  }
@@ -70,13 +69,12 @@ class Cryptor {
70
69
 
71
70
  async decryptDataKey(keyId, encryptedKey) {
72
71
  if (this.shouldUseAws) {
73
- const kmsClient = new AWS.KMS();
74
- const dataKey = await kmsClient
75
- .decrypt({
76
- KeyId: keyId,
77
- CiphertextBlob: encryptedKey,
78
- })
79
- .promise();
72
+ const kmsClient = new KMSClient({});
73
+ const command = new DecryptCommand({
74
+ KeyId: keyId,
75
+ CiphertextBlob: encryptedKey,
76
+ });
77
+ const dataKey = await kmsClient.send(command);
80
78
 
81
79
  return dataKey.Plaintext;
82
80
  }
@@ -0,0 +1 @@
1
+ export * from "./index"
@@ -0,0 +1,4 @@
1
+
2
+ /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
+ /* eslint-disable */
4
+ module.exports = { ...require('.') }
@@ -0,0 +1 @@
1
+ export * from "./index"
@@ -0,0 +1,4 @@
1
+
2
+ /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
+ /* eslint-disable */
4
+ module.exports = { ...require('#main-entry-point') }
@@ -0,0 +1 @@
1
+ export * from "./default"