@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 (237) hide show
  1. package/CHANGELOG.md +66 -67
  2. package/CLAUDE.md +148 -124
  3. package/README.md +165 -130
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
  8. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
  9. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
  10. package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
  12. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  13. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  14. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  15. package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
  16. package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
  17. package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
  18. package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
  19. package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
  20. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  21. package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
  22. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  23. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  24. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  25. package/application/commands/admin-script-commands.js +348 -0
  26. package/application/commands/integration-commands.js +12 -6
  27. package/application/index.js +5 -3
  28. package/assertions/get.js +17 -7
  29. package/associations/association.js +59 -57
  30. package/core/CLAUDE.md +222 -156
  31. package/core/Worker.js +33 -12
  32. package/credential/credential-router.js +262 -0
  33. package/credential/repositories/credential-repository-documentdb.js +71 -1
  34. package/credential/repositories/credential-repository-factory.js +4 -6
  35. package/credential/repositories/credential-repository-interface.js +8 -2
  36. package/credential/repositories/credential-repository-mongo.js +45 -4
  37. package/credential/repositories/credential-repository-postgres.js +44 -3
  38. package/credential/repositories/credential-repository.js +3 -1
  39. package/credential/use-cases/delete-credential-for-user.js +50 -0
  40. package/credential/use-cases/get-credential-for-user.js +21 -3
  41. package/credential/use-cases/list-credentials-for-user.js +31 -0
  42. package/credential/use-cases/reauthorize-credential.js +103 -0
  43. package/credential/use-cases/update-authentication-status.js +5 -2
  44. package/database/MONGODB_TRANSACTION_FIX.md +72 -52
  45. package/database/config.js +13 -59
  46. package/database/documentdb-encryption-service.js +29 -13
  47. package/database/documentdb-utils.js +21 -12
  48. package/database/encryption/README.md +53 -36
  49. package/database/encryption/encryption-schema-registry.js +29 -23
  50. package/database/encryption/field-encryption-service.js +15 -4
  51. package/database/encryption/logger.js +3 -2
  52. package/database/encryption/prisma-encryption-extension.js +5 -8
  53. package/database/prisma.js +22 -14
  54. package/database/repositories/health-check-repository-factory.js +7 -5
  55. package/database/repositories/health-check-repository-interface.js +15 -5
  56. package/database/repositories/health-check-repository-postgres.js +1 -1
  57. package/database/use-cases/check-database-health-use-case.js +3 -2
  58. package/database/use-cases/check-database-state-use-case.js +8 -7
  59. package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
  60. package/database/use-cases/get-migration-status-use-case.js +4 -2
  61. package/database/use-cases/run-database-migration-use-case.js +41 -9
  62. package/database/use-cases/test-encryption-use-case.js +4 -5
  63. package/database/use-cases/trigger-database-migration-use-case.js +9 -8
  64. package/database/utils/mongodb-collection-utils.js +11 -3
  65. package/database/utils/mongodb-schema-init.js +10 -4
  66. package/database/utils/prisma-runner.js +144 -74
  67. package/database/utils/prisma-schema-parser.js +2 -2
  68. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
  69. package/encrypt/Cryptor.js +54 -49
  70. package/encrypt/aes-encryption-key-provider.js +82 -0
  71. package/encrypt/encryption-key-provider-interface.js +50 -0
  72. package/errors/fetch-error.js +1 -1
  73. package/generated/prisma-mongodb/edge.js +59 -4
  74. package/generated/prisma-mongodb/index-browser.js +49 -1
  75. package/generated/prisma-mongodb/index.d.ts +4023 -61
  76. package/generated/prisma-mongodb/index.js +59 -4
  77. package/generated/prisma-mongodb/package.json +1 -1
  78. package/generated/prisma-mongodb/schema.prisma +87 -0
  79. package/generated/prisma-mongodb/wasm.js +59 -4
  80. package/generated/prisma-postgresql/edge.js +58 -4
  81. package/generated/prisma-postgresql/index-browser.js +48 -1
  82. package/generated/prisma-postgresql/index.d.ts +4738 -54
  83. package/generated/prisma-postgresql/index.js +58 -4
  84. package/generated/prisma-postgresql/package.json +1 -1
  85. package/generated/prisma-postgresql/schema.prisma +85 -0
  86. package/generated/prisma-postgresql/wasm.js +58 -4
  87. package/handlers/WEBHOOKS.md +104 -81
  88. package/handlers/app-definition-loader.js +53 -10
  89. package/handlers/app-handler-helpers.js +14 -3
  90. package/handlers/backend-utils.js +0 -1
  91. package/handlers/database-migration-handler.js +64 -45
  92. package/handlers/middleware/admin-auth.js +53 -0
  93. package/handlers/routers/HEALTHCHECK.md +170 -135
  94. package/handlers/routers/admin.js +474 -0
  95. package/handlers/routers/auth.js +51 -9
  96. package/handlers/routers/db-migration.handler.js +2 -3
  97. package/handlers/routers/db-migration.js +102 -66
  98. package/handlers/routers/docs.js +180 -0
  99. package/handlers/routers/health.js +84 -283
  100. package/handlers/routers/integration-defined-routers.js +44 -33
  101. package/handlers/routers/integration-webhook-routers.js +73 -52
  102. package/handlers/routers/middleware/loadUser.js +39 -0
  103. package/handlers/routers/middleware/requireAdmin.js +43 -0
  104. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  105. package/handlers/routers/user.js +30 -14
  106. package/handlers/routers/websocket.js +18 -4
  107. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  108. package/handlers/workers/db-migration.js +74 -35
  109. package/handlers/workers/integration-defined-workers.js +31 -20
  110. package/index.js +50 -16
  111. package/infrastructure/scheduler/index.js +15 -11
  112. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  113. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  114. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  115. package/integrations/index.js +4 -0
  116. package/integrations/integration-base.js +9 -6
  117. package/integrations/integration-router.js +1828 -157
  118. package/integrations/options.js +47 -20
  119. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  120. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  121. package/integrations/repositories/integration-repository-factory.js +6 -6
  122. package/integrations/repositories/integration-repository-interface.js +27 -9
  123. package/integrations/repositories/integration-repository-postgres.js +3 -3
  124. package/integrations/repositories/process-repository-factory.js +4 -7
  125. package/integrations/repositories/process-repository-interface.js +9 -5
  126. package/integrations/repositories/process-repository-mongo.js +7 -3
  127. package/integrations/repositories/process-repository-postgres.js +2 -2
  128. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  129. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  130. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  131. package/integrations/use-cases/create-integration.js +36 -32
  132. package/integrations/use-cases/create-process.js +20 -9
  133. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  134. package/integrations/use-cases/execute-proxy-request.js +564 -0
  135. package/integrations/use-cases/get-integration-for-user.js +25 -10
  136. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  137. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  138. package/integrations/use-cases/get-possible-integrations.js +1 -1
  139. package/integrations/use-cases/get-process.js +5 -6
  140. package/integrations/use-cases/index.js +3 -1
  141. package/integrations/use-cases/load-integration-context.js +1 -5
  142. package/integrations/use-cases/update-process-state.js +10 -8
  143. package/integrations/utils/map-integration-dto.js +9 -3
  144. package/jest-setup.js +1 -1
  145. package/jest-teardown.js +1 -1
  146. package/lambda/TimeoutCatcher.js +32 -30
  147. package/lambda/index.js +1 -1
  148. package/logs/index.js +3 -3
  149. package/modules/domain/entities/AuthorizationSession.js +131 -0
  150. package/modules/domain/entities/index.js +10 -0
  151. package/modules/module.js +4 -9
  152. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  153. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  154. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  155. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  156. package/modules/repositories/module-repository-documentdb.js +89 -41
  157. package/modules/repositories/module-repository-factory.js +4 -6
  158. package/modules/repositories/module-repository-interface.js +13 -0
  159. package/modules/repositories/module-repository-mongo.js +40 -5
  160. package/modules/repositories/module-repository-postgres.js +42 -5
  161. package/modules/repositories/module-repository.js +28 -0
  162. package/modules/requester/api-key.js +0 -2
  163. package/modules/requester/basic.js +0 -1
  164. package/modules/requester/oauth-2.js +3 -14
  165. package/modules/test/mock-api/definition.js +15 -10
  166. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  167. package/modules/tests/doubles/test-module-factory.js +10 -4
  168. package/modules/tests/doubles/test-module-repository.js +1 -1
  169. package/modules/use-cases/delete-module-entity.js +23 -0
  170. package/modules/use-cases/get-authorization-requirements.js +110 -0
  171. package/modules/use-cases/get-entities-for-user.js +1 -1
  172. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  173. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  174. package/modules/use-cases/get-module.js +12 -9
  175. package/modules/use-cases/process-authorization-callback.js +12 -6
  176. package/modules/use-cases/process-authorization-step.js +160 -0
  177. package/modules/use-cases/refresh-entity-options.js +8 -6
  178. package/modules/use-cases/start-authorization-session.js +86 -0
  179. package/modules/use-cases/test-module-auth.js +8 -6
  180. package/modules/use-cases/update-module-entity.js +27 -0
  181. package/modules/utils/map-module-dto.js +2 -2
  182. package/openapi/openapi-spec-generator.js +370 -0
  183. package/openapi/openapi-v1.yaml +1360 -0
  184. package/openapi/openapi-v2.yaml +1535 -0
  185. package/package.json +7 -9
  186. package/prisma-mongodb/schema.prisma +87 -0
  187. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  188. package/prisma-postgresql/schema.prisma +85 -0
  189. package/providers/index.js +13 -0
  190. package/providers/resolve-provider.js +93 -0
  191. package/queues/index.js +19 -0
  192. package/queues/providers/index.js +7 -0
  193. package/queues/providers/netlify-background-provider.js +76 -0
  194. package/queues/providers/qstash-queue-provider.js +122 -0
  195. package/queues/queue-client-interface.js +55 -0
  196. package/queues/queue-provider-factory.js +93 -0
  197. package/queues/queue-provider.js +46 -0
  198. package/queues/queuer-util.js +33 -25
  199. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  200. package/syncs/repositories/sync-repository-factory.js +4 -4
  201. package/syncs/repositories/sync-repository-interface.js +6 -2
  202. package/syncs/repositories/sync-repository-postgres.js +19 -13
  203. package/syncs/sync.js +104 -100
  204. package/token/repositories/token-repository-factory.js +4 -6
  205. package/types/assertions/index.d.ts +72 -72
  206. package/types/core/index.d.ts +98 -53
  207. package/types/errors/index.d.ts +61 -54
  208. package/types/eslint-config/index.d.ts +40 -40
  209. package/types/lambda/index.d.ts +26 -26
  210. package/types/logs/index.d.ts +4 -4
  211. package/types/prettier-config/index.d.ts +5 -5
  212. package/types/test-environment/index.d.ts +14 -14
  213. package/types/tsconfig.json +95 -93
  214. package/user/repositories/user-repository-factory.js +4 -4
  215. package/user/tests/doubles/test-user-repository.js +5 -2
  216. package/user/use-cases/authenticate-user.js +2 -4
  217. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  218. package/user/use-cases/create-individual-user.js +8 -7
  219. package/user/use-cases/create-organization-user.js +1 -1
  220. package/user/use-cases/create-token-for-user-id.js +1 -1
  221. package/user/use-cases/delete-user.js +47 -0
  222. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  223. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  224. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  225. package/user/use-cases/login-user.js +8 -8
  226. package/user/user.js +18 -4
  227. package/utils/backend-path.js +2 -2
  228. package/utils/index.js +4 -1
  229. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  230. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  231. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  232. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  233. package/websocket/repositories/websocket-connection-repository.js +26 -20
  234. package/websocket/websocket-message-sender-interface.js +38 -0
  235. package/database/adapters/lambda-invoker.js +0 -97
  236. package/database/repositories/migration-status-repository-s3.js +0 -137
  237. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
@@ -3,8 +3,10 @@ const { ObjectId } = require('bson');
3
3
  function toObjectId(value) {
4
4
  if (value === null || value === undefined || value === '') return undefined;
5
5
  if (value instanceof ObjectId) return value;
6
- if (typeof value === 'object' && value.$oid) return new ObjectId(value.$oid);
7
- if (typeof value === 'string') return ObjectId.isValid(value) ? new ObjectId(value) : undefined;
6
+ if (typeof value === 'object' && value.$oid)
7
+ return new ObjectId(value.$oid);
8
+ if (typeof value === 'string')
9
+ return ObjectId.isValid(value) ? new ObjectId(value) : undefined;
8
10
  return undefined;
9
11
  }
10
12
 
@@ -15,7 +17,8 @@ function toObjectIdArray(values) {
15
17
 
16
18
  function fromObjectId(value) {
17
19
  if (value instanceof ObjectId) return value.toHexString();
18
- if (typeof value === 'object' && value !== null && value.$oid) return value.$oid;
20
+ if (typeof value === 'object' && value !== null && value.$oid)
21
+ return value.$oid;
19
22
  if (typeof value === 'string') return value;
20
23
  return value === undefined || value === null ? value : String(value);
21
24
  }
@@ -30,7 +33,10 @@ async function findMany(client, collection, filter = {}, options = {}) {
30
33
  }
31
34
 
32
35
  async function findOne(client, collection, filter = {}, options = {}) {
33
- const docs = await findMany(client, collection, filter, { ...options, limit: 1 });
36
+ const docs = await findMany(client, collection, filter, {
37
+ ...options,
38
+ limit: 1,
39
+ });
34
40
  return docs[0] || null;
35
41
  }
36
42
 
@@ -47,7 +53,9 @@ async function insertOne(client, collection, document) {
47
53
  // Validate insert succeeded
48
54
  if (result.ok !== 1) {
49
55
  throw new Error(
50
- `Insert command failed for collection '${collection}': ${JSON.stringify(result)}`
56
+ `Insert command failed for collection '${collection}': ${JSON.stringify(
57
+ result
58
+ )}`
51
59
  );
52
60
  }
53
61
 
@@ -67,7 +75,7 @@ async function insertOne(client, collection, document) {
67
75
  if (result.n !== 1) {
68
76
  throw new Error(
69
77
  `Expected to insert 1 document into '${collection}', but inserted ${result.n}. ` +
70
- `Result: ${JSON.stringify(result)}`
78
+ `Result: ${JSON.stringify(result)}`
71
79
  );
72
80
  }
73
81
 
@@ -75,11 +83,13 @@ async function insertOne(client, collection, document) {
75
83
  }
76
84
 
77
85
  async function updateOne(client, collection, filter, update, options = {}) {
78
- const updates = [{
79
- q: filter,
80
- u: update,
81
- upsert: Boolean(options.upsert),
82
- }];
86
+ const updates = [
87
+ {
88
+ q: filter,
89
+ u: update,
90
+ upsert: Boolean(options.upsert),
91
+ },
92
+ ];
83
93
  if (options.arrayFilters) updates[0].arrayFilters = options.arrayFilters;
84
94
  const result = await client.$runCommandRaw({
85
95
  update: collection,
@@ -133,4 +143,3 @@ module.exports = {
133
143
  deleteMany,
134
144
  aggregate,
135
145
  };
136
-
@@ -125,10 +125,11 @@ Or simply don't configure any encryption keys. In Production field level encrypt
125
125
  Core and custom encrypted fields are defined in `encryption-schema-registry.js`. See that file for the current list of encrypted fields.
126
126
 
127
127
  **Core fields include**:
128
- - OAuth tokens: `access_token`, `refresh_token`, `id_token`
129
- - API keys: `api_key`, `apiKey`, `API_KEY_VALUE`
130
- - Basic auth: `password`
131
- - OAuth client credentials: `client_secret`
128
+
129
+ - OAuth tokens: `access_token`, `refresh_token`, `id_token`
130
+ - API keys: `api_key`, `apiKey`, `API_KEY_VALUE`
131
+ - Basic auth: `password`
132
+ - OAuth client credentials: `client_secret`
132
133
 
133
134
  **Note**: API modules should use `api_key` (snake_case) in their `apiPropertiesToPersist.credential` arrays for consistency with OAuth2Requester and BasicAuthRequester conventions.
134
135
 
@@ -137,34 +138,36 @@ Core and custom encrypted fields are defined in `encryption-schema-registry.js`.
137
138
  When creating API module definitions, use **snake_case** for credential property names to ensure automatic encryption:
138
139
 
139
140
  **✅ Recommended (automatically encrypted):**
141
+
140
142
  ```javascript
141
143
  // API Module Definition
142
144
  const Definition = {
143
145
  requiredAuthMethods: {
144
146
  apiPropertiesToPersist: {
145
147
  // For API key authentication
146
- credential: ['api_key'], // ✅ Automatically encrypted
148
+ credential: ['api_key'], // ✅ Automatically encrypted
147
149
  // or for OAuth authentication
148
- credential: ['access_token', 'refresh_token'], // ✅ OAuth - encrypted
150
+ credential: ['access_token', 'refresh_token'], // ✅ OAuth - encrypted
149
151
  // or for Basic authentication
150
- credential: ['username', 'password'], // ✅ Basic auth - encrypted
151
- }
152
- }
152
+ credential: ['username', 'password'], // ✅ Basic auth - encrypted
153
+ },
154
+ },
153
155
  };
154
156
 
155
157
  // API class (extends ApiKeyRequester)
156
158
  class MyApi extends ApiKeyRequester {
157
159
  constructor(params) {
158
160
  super(params);
159
- this.api_key = params.api_key; // ✅ snake_case convention
161
+ this.api_key = params.api_key; // ✅ snake_case convention
160
162
  }
161
163
  }
162
164
  ```
163
165
 
164
166
  **❌ Avoid (requires manual encryption schema):**
167
+
165
168
  ```javascript
166
169
  apiPropertiesToPersist: {
167
- credential: ['customToken', 'proprietaryKey'] // ❌ Not in core schema
170
+ credential: ['customToken', 'proprietaryKey']; // ❌ Not in core schema
168
171
  }
169
172
  ```
170
173
 
@@ -184,57 +187,61 @@ const Definition = {
184
187
 
185
188
  // Declare which credential fields need encryption
186
189
  encryption: {
187
- credentialFields: ['api_key', 'webhook_secret']
190
+ credentialFields: ['api_key', 'webhook_secret'],
188
191
  },
189
192
 
190
193
  requiredAuthMethods: {
191
194
  apiPropertiesToPersist: {
192
- credential: ['api_key', 'webhook_secret'], // These will be auto-encrypted
193
- entity: []
195
+ credential: ['api_key', 'webhook_secret'], // These will be auto-encrypted
196
+ entity: [],
194
197
  },
195
198
  // ... other methods
196
- }
199
+ },
197
200
  };
198
201
  ```
199
202
 
200
203
  **How it works**:
204
+
201
205
  1. Module declares `encryption.credentialFields` array
202
206
  2. Framework automatically adds `data.` prefix: `['api_key']` → `['data.api_key']`
203
207
  3. Fields are merged with core encryption schema on app startup
204
208
  4. All modules across all integrations are scanned and combined
205
209
 
206
210
  **Benefits**:
207
- - ✅ Module authors control their own security requirements
208
- - ✅ No need to modify core framework or app configuration
209
- - ✅ Automatic encryption for API key-based integrations
210
- - ✅ Works seamlessly with `apiPropertiesToPersist`
211
+
212
+ - ✅ Module authors control their own security requirements
213
+ - ✅ No need to modify core framework or app configuration
214
+ - ✅ Automatic encryption for API key-based integrations
215
+ - ✅ Works seamlessly with `apiPropertiesToPersist`
211
216
 
212
217
  **Example - API Key Module**:
218
+
213
219
  ```javascript
214
220
  // API Module Definition
215
221
  const Definition = {
216
222
  moduleName: 'axiscare',
217
223
  API: AxisCareApi,
218
224
  encryption: {
219
- credentialFields: ['api_key'] // Auto-encrypted as 'data.api_key'
225
+ credentialFields: ['api_key'], // Auto-encrypted as 'data.api_key'
220
226
  },
221
227
  requiredAuthMethods: {
222
228
  apiPropertiesToPersist: {
223
- credential: ['api_key'] // Will be encrypted automatically
224
- }
225
- }
229
+ credential: ['api_key'], // Will be encrypted automatically
230
+ },
231
+ },
226
232
  };
227
233
 
228
234
  // API Class (extends ApiKeyRequester)
229
235
  class AxisCareApi extends ApiKeyRequester {
230
236
  constructor(params) {
231
237
  super(params);
232
- this.api_key = params.api_key; // snake_case convention
238
+ this.api_key = params.api_key; // snake_case convention
233
239
  }
234
240
  }
235
241
  ```
236
242
 
237
243
  **Example - Custom Authentication**:
244
+
238
245
  ```javascript
239
246
  const Definition = {
240
247
  moduleName: 'customService',
@@ -242,16 +249,17 @@ const Definition = {
242
249
  credentialFields: [
243
250
  'signing_key',
244
251
  'webhook_secret',
245
- 'data.custom_nested_field' // Can specify data. prefix explicitly
246
- ]
247
- }
252
+ 'data.custom_nested_field', // Can specify data. prefix explicitly
253
+ ],
254
+ },
248
255
  };
249
256
  ```
250
257
 
251
258
  **Limitations**:
252
- - Only supports Credential model fields (stored in `credential.data`)
253
- - Cannot encrypt entity fields or custom models (use app-level schema for those)
254
- - Applied globally once - module schemas loaded at app startup
259
+
260
+ - Only supports Credential model fields (stored in `credential.data`)
261
+ - Cannot encrypt entity fields or custom models (use app-level schema for those)
262
+ - Applied globally once - module schemas loaded at app startup
255
263
 
256
264
  #### Option 2: App-Level Custom Schema (Integration Developers)
257
265
 
@@ -529,7 +537,9 @@ For DocumentDB repositories, use `DocumentDBEncryptionService` to manually encry
529
537
  #### Usage Example
530
538
 
531
539
  ```javascript
532
- const { DocumentDBEncryptionService } = require('../documentdb-encryption-service');
540
+ const {
541
+ DocumentDBEncryptionService,
542
+ } = require('../documentdb-encryption-service');
533
543
  const { insertOne, findOne } = require('../documentdb-utils');
534
544
 
535
545
  class MyRepositoryDocumentDB {
@@ -539,12 +549,18 @@ class MyRepositoryDocumentDB {
539
549
 
540
550
  async create(data) {
541
551
  // Encrypt before write
542
- const encrypted = await this.encryptionService.encryptFields('ModelName', data);
552
+ const encrypted = await this.encryptionService.encryptFields(
553
+ 'ModelName',
554
+ data
555
+ );
543
556
  const id = await insertOne(this.prisma, 'CollectionName', encrypted);
544
557
 
545
558
  // Decrypt after read
546
559
  const doc = await findOne(this.prisma, 'CollectionName', { _id: id });
547
- const decrypted = await this.encryptionService.decryptFields('ModelName', doc);
560
+ const decrypted = await this.encryptionService.decryptFields(
561
+ 'ModelName',
562
+ doc
563
+ );
548
564
 
549
565
  return decrypted;
550
566
  }
@@ -554,9 +570,10 @@ class MyRepositoryDocumentDB {
554
570
  #### Configuration
555
571
 
556
572
  Uses the same environment variables and Cryptor as the Prisma Extension:
557
- - `STAGE`: Bypasses encryption for dev/test/local
558
- - `KMS_KEY_ARN`: AWS KMS encryption (production)
559
- - `AES_KEY_ID` + `AES_KEY`: AES encryption (fallback)
573
+
574
+ - `STAGE`: Bypasses encryption for dev/test/local
575
+ - `KMS_KEY_ARN`: AWS KMS encryption (production)
576
+ - `AES_KEY_ID` + `AES_KEY`: AES encryption (fallback)
560
577
 
561
578
  ## Usage Examples
562
579
 
@@ -72,7 +72,9 @@ function validateCustomSchema(schema) {
72
72
 
73
73
  for (const fieldPath of config.fields) {
74
74
  if (typeof fieldPath !== 'string' || !fieldPath) {
75
- errors.push(`Model "${modelName}" has invalid field path: ${fieldPath}`);
75
+ errors.push(
76
+ `Model "${modelName}" has invalid field path: ${fieldPath}`
77
+ );
76
78
  }
77
79
 
78
80
  // Check if trying to override core fields
@@ -104,13 +106,17 @@ function registerCustomSchema(schema) {
104
106
  const validation = validateCustomSchema(schema);
105
107
  if (!validation.valid) {
106
108
  throw new Error(
107
- `Invalid custom encryption schema:\n- ${validation.errors.join('\n- ')}`
109
+ `Invalid custom encryption schema:\n- ${validation.errors.join(
110
+ '\n- '
111
+ )}`
108
112
  );
109
113
  }
110
114
 
111
115
  customSchema = { ...schema };
112
116
  logger.info(
113
- `Registered custom encryption schema for models: ${Object.keys(customSchema).join(', ')}`
117
+ `Registered custom encryption schema for models: ${Object.keys(
118
+ customSchema
119
+ ).join(', ')}`
114
120
  );
115
121
  }
116
122
 
@@ -133,7 +139,9 @@ function extractCredentialFieldsFromModules(moduleDefinitions) {
133
139
  }
134
140
 
135
141
  for (const field of credentialFields) {
136
- const prefixedField = field.startsWith('data.') ? field : `data.${field}`;
142
+ const prefixedField = field.startsWith('data.')
143
+ ? field
144
+ : `data.${field}`;
137
145
  fields.push(prefixedField);
138
146
  }
139
147
  }
@@ -160,10 +168,14 @@ function loadModuleEncryptionSchemas(integrations) {
160
168
  return;
161
169
  }
162
170
 
163
- const { getModulesDefinitionFromIntegrationClasses } = require('../integrations/utils/map-integration-dto');
171
+ const {
172
+ getModulesDefinitionFromIntegrationClasses,
173
+ } = require('../../integrations/utils/map-integration-dto');
164
174
 
165
- const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(integrations);
166
- const credentialFields = extractCredentialFieldsFromModules(moduleDefinitions);
175
+ const moduleDefinitions =
176
+ getModulesDefinitionFromIntegrationClasses(integrations);
177
+ const credentialFields =
178
+ extractCredentialFieldsFromModules(moduleDefinitions);
167
179
 
168
180
  if (credentialFields.length === 0) {
169
181
  return;
@@ -171,8 +183,8 @@ function loadModuleEncryptionSchemas(integrations) {
171
183
 
172
184
  const moduleSchema = {
173
185
  Credential: {
174
- fields: credentialFields
175
- }
186
+ fields: credentialFields,
187
+ },
176
188
  };
177
189
 
178
190
  logger.info(
@@ -193,20 +205,14 @@ function loadModuleEncryptionSchemas(integrations) {
193
205
  */
194
206
  function loadCustomEncryptionSchema() {
195
207
  try {
196
- // Lazy require to avoid circular dependency issues
197
- const path = require('node:path');
198
- const { findNearestBackendPackageJson } = require('../../utils');
199
-
200
- const backendPackagePath = findNearestBackendPackageJson();
201
- if (!backendPackagePath) {
202
- return; // No backend found, skip custom schema
203
- }
204
-
205
- const backendDir = path.dirname(backendPackagePath);
206
- const backendIndexPath = path.join(backendDir, 'index.js');
207
-
208
- const backendModule = require(backendIndexPath);
209
- const appDefinition = backendModule?.Definition;
208
+ // Uses loadAppDefinition() which respects setAppDefinition() cache.
209
+ // This is critical for platforms like Netlify where process.cwd()-based
210
+ // discovery fails at runtime (process.cwd() is /var/task, not the project root).
211
+ const {
212
+ loadAppDefinition,
213
+ } = require('../../handlers/app-definition-loader');
214
+
215
+ const { appDefinition } = loadAppDefinition();
210
216
 
211
217
  if (!appDefinition) {
212
218
  return; // No app definition found
@@ -35,7 +35,9 @@ class FieldEncryptionService {
35
35
 
36
36
  if (this._shouldEncrypt(value)) {
37
37
  const serializedValue = this._serializeForEncryption(value);
38
- const encryptedValue = await this.cryptor.encrypt(serializedValue);
38
+ const encryptedValue = await this.cryptor.encrypt(
39
+ serializedValue
40
+ );
39
41
  return { fieldPath, encryptedValue };
40
42
  }
41
43
  return null;
@@ -46,7 +48,11 @@ class FieldEncryptionService {
46
48
  // Apply encrypted values
47
49
  for (const result of results) {
48
50
  if (result) {
49
- this._setNestedValue(encrypted, result.fieldPath, result.encryptedValue);
51
+ this._setNestedValue(
52
+ encrypted,
53
+ result.fieldPath,
54
+ result.encryptedValue
55
+ );
50
56
  }
51
57
  }
52
58
 
@@ -71,7 +77,8 @@ class FieldEncryptionService {
71
77
 
72
78
  if (this._isEncrypted(value)) {
73
79
  const decryptedValue = await this.cryptor.decrypt(value);
74
- const deserializedValue = this._deserializeAfterDecryption(decryptedValue);
80
+ const deserializedValue =
81
+ this._deserializeAfterDecryption(decryptedValue);
75
82
  return { fieldPath, decryptedValue: deserializedValue };
76
83
  }
77
84
  return null;
@@ -82,7 +89,11 @@ class FieldEncryptionService {
82
89
  // Apply decrypted values
83
90
  for (const result of results) {
84
91
  if (result) {
85
- this._setNestedValue(decrypted, result.fieldPath, result.decryptedValue);
92
+ this._setNestedValue(
93
+ decrypted,
94
+ result.fieldPath,
95
+ result.decryptedValue
96
+ );
86
97
  }
87
98
  }
88
99
 
@@ -30,8 +30,9 @@ class EncryptionLogger {
30
30
  // Remove potential key material or encrypted data from logs
31
31
  if (typeof message === 'string') {
32
32
  // Truncate long base64 strings that might be keys or encrypted data
33
- return message.replace(/([A-Za-z0-9+/=]{50,})/g, (match) =>
34
- `${match.substring(0, 10)}...[${match.length} chars]`
33
+ return message.replace(
34
+ /([A-Za-z0-9+/=]{50,})/g,
35
+ (match) => `${match.substring(0, 10)}...[${match.length} chars]`
35
36
  );
36
37
  }
37
38
  return message;
@@ -12,9 +12,7 @@ function createEncryptionExtension({ cryptor, enabled = true }) {
12
12
  }
13
13
 
14
14
  if (!cryptor) {
15
- throw new Error(
16
- 'Cryptor instance required for encryption extension'
17
- );
15
+ throw new Error('Cryptor instance required for encryption extension');
18
16
  }
19
17
 
20
18
  const encryptionService = new FieldEncryptionService({
@@ -48,11 +46,10 @@ function createEncryptionExtension({ cryptor, enabled = true }) {
48
46
 
49
47
  async createMany({ model, args, query }) {
50
48
  if (args.data && Array.isArray(args.data)) {
51
- args.data =
52
- await encryptionService.encryptFieldsInBulk(
53
- model,
54
- args.data
55
- );
49
+ args.data = await encryptionService.encryptFieldsInBulk(
50
+ model,
51
+ args.data
52
+ );
56
53
  } else if (args.data) {
57
54
  args.data = await encryptionService.encryptFields(
58
55
  model,
@@ -1,7 +1,9 @@
1
1
  const {
2
2
  createEncryptionExtension,
3
3
  } = require('./encryption/prisma-encryption-extension');
4
- const { loadCustomEncryptionSchema } = require('./encryption/encryption-schema-registry');
4
+ const {
5
+ loadCustomEncryptionSchema,
6
+ } = require('./encryption/encryption-schema-registry');
5
7
  const { logger } = require('./encryption/logger');
6
8
  const { Cryptor } = require('../encrypt/Cryptor');
7
9
  const config = require('./config');
@@ -10,7 +12,7 @@ const config = require('./config');
10
12
  * Ensures DATABASE_URL is set for MongoDB connections
11
13
  * Falls back to MONGO_URI if DATABASE_URL is not set
12
14
  * Infrastructure layer concern - maps legacy MONGO_URI to Prisma's expected DATABASE_URL
13
- *
15
+ *
14
16
  * Note: This should only be called when DB_TYPE is 'mongodb' or 'documentdb'
15
17
  */
16
18
  function ensureMongoDbUrl() {
@@ -22,7 +24,9 @@ function ensureMongoDbUrl() {
22
24
  // Fallback to MONGO_URI for backwards compatibility with DocumentDB deployments
23
25
  if (process.env.MONGO_URI && process.env.MONGO_URI.trim()) {
24
26
  process.env.DATABASE_URL = process.env.MONGO_URI;
25
- logger.debug('Using MONGO_URI as DATABASE_URL for Mongo-compatible connection');
27
+ logger.debug(
28
+ 'Using MONGO_URI as DATABASE_URL for Mongo-compatible connection'
29
+ );
26
30
  return;
27
31
  }
28
32
 
@@ -48,7 +52,7 @@ function getEncryptionConfig() {
48
52
  if (!hasKMS && !hasAES) {
49
53
  logger.warn(
50
54
  'No encryption keys configured (KMS_KEY_ARN or AES_KEY_ID). ' +
51
- 'Field-level encryption disabled. Set STAGE=production and configure keys to enable.'
55
+ 'Field-level encryption disabled. Set STAGE=production and configure keys to enable.'
52
56
  );
53
57
  return { enabled: false };
54
58
  }
@@ -80,7 +84,9 @@ const prismaClientSingleton = () => {
80
84
  }
81
85
 
82
86
  throw new Error(
83
- `Cannot find Prisma client for ${dbType}. Tried paths: ${paths.join(', ')}`
87
+ `Cannot find Prisma client for ${dbType}. Tried paths: ${paths.join(
88
+ ', '
89
+ )}`
84
90
  );
85
91
  };
86
92
 
@@ -125,10 +131,7 @@ const prismaClientSingleton = () => {
125
131
  `Field-level encryption enabled using ${encryptionConfig.method.toUpperCase()}`
126
132
  );
127
133
  } catch (error) {
128
- logger.error(
129
- 'Failed to initialize encryption extension:',
130
- error
131
- );
134
+ logger.error('Failed to initialize encryption extension:', error);
132
135
  logger.warn('Continuing without encryption...');
133
136
  }
134
137
  } else {
@@ -149,11 +152,14 @@ function getPrismaClient() {
149
152
  }
150
153
 
151
154
  // Export a getter for lazy initialization
152
- const prisma = new Proxy({}, {
153
- get(target, prop) {
154
- return getPrismaClient()[prop];
155
+ const prisma = new Proxy(
156
+ {},
157
+ {
158
+ get(target, prop) {
159
+ return getPrismaClient()[prop];
160
+ },
155
161
  }
156
- });
162
+ );
157
163
 
158
164
  async function disconnectPrisma() {
159
165
  await getPrismaClient().$disconnect();
@@ -166,7 +172,9 @@ async function connectPrisma() {
166
172
  // Only run for MongoDB/DocumentDB (not PostgreSQL)
167
173
  // This prevents "Cannot create namespace in multi-document transaction" errors
168
174
  if (config.DB_TYPE === 'mongodb' || config.DB_TYPE === 'documentdb') {
169
- const { initializeMongoDBSchema } = require('./utils/mongodb-schema-init');
175
+ const {
176
+ initializeMongoDBSchema,
177
+ } = require('./utils/mongodb-schema-init');
170
178
  await initializeMongoDBSchema();
171
179
  }
172
180
 
@@ -1,6 +1,6 @@
1
- const { HealthCheckRepositoryMongoDB } = require('./health-check-repository-mongodb');
2
- const { HealthCheckRepositoryPostgreSQL } = require('./health-check-repository-postgres');
3
- const { HealthCheckRepositoryDocumentDB } = require('./health-check-repository-documentdb');
1
+ const {
2
+ HealthCheckRepositoryPostgreSQL,
3
+ } = require('./health-check-repository-postgres');
4
4
  const config = require('../config');
5
5
 
6
6
  /**
@@ -25,12 +25,14 @@ function createHealthCheckRepository({ prismaClient } = {}) {
25
25
 
26
26
  switch (dbType) {
27
27
  case 'mongodb':
28
+ const { HealthCheckRepositoryMongoDB } = require('./health-check-repository-mongodb');
28
29
  return new HealthCheckRepositoryMongoDB({ prismaClient });
29
30
 
30
31
  case 'postgresql':
31
32
  return new HealthCheckRepositoryPostgreSQL({ prismaClient });
32
33
 
33
34
  case 'documentdb':
35
+ const { HealthCheckRepositoryDocumentDB } = require('./health-check-repository-documentdb');
34
36
  return new HealthCheckRepositoryDocumentDB({ prismaClient });
35
37
 
36
38
  default:
@@ -42,7 +44,7 @@ function createHealthCheckRepository({ prismaClient } = {}) {
42
44
 
43
45
  module.exports = {
44
46
  createHealthCheckRepository,
45
- HealthCheckRepositoryMongoDB,
47
+ get HealthCheckRepositoryMongoDB() { return require('./health-check-repository-mongodb').HealthCheckRepositoryMongoDB; },
46
48
  HealthCheckRepositoryPostgreSQL,
47
- HealthCheckRepositoryDocumentDB,
49
+ get HealthCheckRepositoryDocumentDB() { return require('./health-check-repository-documentdb').HealthCheckRepositoryDocumentDB; },
48
50
  };
@@ -19,7 +19,9 @@ class HealthCheckRepositoryInterface {
19
19
  * @abstract
20
20
  */
21
21
  async getDatabaseConnectionState() {
22
- throw new Error('Method getDatabaseConnectionState must be implemented by subclass');
22
+ throw new Error(
23
+ 'Method getDatabaseConnectionState must be implemented by subclass'
24
+ );
23
25
  }
24
26
 
25
27
  /**
@@ -42,7 +44,9 @@ class HealthCheckRepositoryInterface {
42
44
  * @abstract
43
45
  */
44
46
  async createCredential(credentialData) {
45
- throw new Error('Method createCredential must be implemented by subclass');
47
+ throw new Error(
48
+ 'Method createCredential must be implemented by subclass'
49
+ );
46
50
  }
47
51
 
48
52
  /**
@@ -53,7 +57,9 @@ class HealthCheckRepositoryInterface {
53
57
  * @abstract
54
58
  */
55
59
  async findCredentialById(id) {
56
- throw new Error('Method findCredentialById must be implemented by subclass');
60
+ throw new Error(
61
+ 'Method findCredentialById must be implemented by subclass'
62
+ );
57
63
  }
58
64
 
59
65
  /**
@@ -64,7 +70,9 @@ class HealthCheckRepositoryInterface {
64
70
  * @abstract
65
71
  */
66
72
  async getRawCredentialById(id) {
67
- throw new Error('Method getRawCredentialById must be implemented by subclass');
73
+ throw new Error(
74
+ 'Method getRawCredentialById must be implemented by subclass'
75
+ );
68
76
  }
69
77
 
70
78
  /**
@@ -75,7 +83,9 @@ class HealthCheckRepositoryInterface {
75
83
  * @abstract
76
84
  */
77
85
  async deleteCredential(id) {
78
- throw new Error('Method deleteCredential must be implemented by subclass');
86
+ throw new Error(
87
+ 'Method deleteCredential must be implemented by subclass'
88
+ );
79
89
  }
80
90
  }
81
91
 
@@ -18,7 +18,7 @@ class HealthCheckRepositoryPostgreSQL extends HealthCheckRepositoryInterface {
18
18
  async getDatabaseConnectionState() {
19
19
  let isConnected = false;
20
20
  let stateName = 'unknown';
21
-
21
+
22
22
  try {
23
23
  await this.prisma.$queryRaw`SELECT 1`;
24
24
  isConnected = true;
@@ -11,7 +11,8 @@ class CheckDatabaseHealthUseCase {
11
11
  * @returns {Promise<{status: string, state: string, responseTime?: number}>}
12
12
  */
13
13
  async execute() {
14
- const { stateName, isConnected } = await this.repository.getDatabaseConnectionState();
14
+ const { stateName, isConnected } =
15
+ await this.repository.getDatabaseConnectionState();
15
16
 
16
17
  const result = {
17
18
  status: isConnected ? 'healthy' : 'unhealthy',
@@ -26,4 +27,4 @@ class CheckDatabaseHealthUseCase {
26
27
  }
27
28
  }
28
29
 
29
- module.exports = { CheckDatabaseHealthUseCase };
30
+ module.exports = { CheckDatabaseHealthUseCase };