@friggframework/core 2.0.0--canary.690.08cd07e.0 → 2.0.0--canary.694.f66a64a.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 (267) hide show
  1. package/CLAUDE.md +0 -28
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +10 -20
  4. package/application/commands/scheduler-commands.js +7 -11
  5. package/application/commands/user-commands.js +5 -17
  6. package/application/index.js +2 -6
  7. package/core/Worker.js +7 -31
  8. package/core/create-handler.js +0 -9
  9. package/credential/repositories/credential-repository-documentdb.js +0 -34
  10. package/credential/repositories/credential-repository-interface.js +0 -15
  11. package/credential/repositories/credential-repository-mongo.js +0 -23
  12. package/credential/repositories/credential-repository-postgres.js +0 -28
  13. package/database/config.js +24 -48
  14. package/database/documentdb-utils.js +2 -2
  15. package/database/encryption/encryption-schema-registry.js +12 -93
  16. package/database/prisma.js +2 -4
  17. package/errors/index.js +4 -0
  18. package/errors/mapping-already-exists-error.js +14 -0
  19. package/generated/prisma-mongodb/edge.js +38 -33
  20. package/generated/prisma-mongodb/index-browser.js +35 -30
  21. package/generated/prisma-mongodb/index.d.ts +1880 -3191
  22. package/generated/prisma-mongodb/index.js +38 -33
  23. package/generated/prisma-mongodb/package.json +1 -1
  24. package/generated/prisma-mongodb/schema.prisma +50 -86
  25. package/generated/prisma-mongodb/wasm.js +38 -33
  26. package/generated/prisma-postgresql/edge.js +38 -33
  27. package/generated/prisma-postgresql/index-browser.js +35 -30
  28. package/generated/prisma-postgresql/index.d.ts +2050 -3390
  29. package/generated/prisma-postgresql/index.js +38 -33
  30. package/generated/prisma-postgresql/package.json +1 -1
  31. package/generated/prisma-postgresql/schema.prisma +48 -81
  32. package/generated/prisma-postgresql/wasm.js +38 -33
  33. package/handlers/WEBHOOKS.md +29 -0
  34. package/handlers/app-definition-loader.js +19 -128
  35. package/handlers/app-handler-helpers.js +3 -25
  36. package/handlers/backend-utils.js +25 -31
  37. package/handlers/middleware/admin-auth.js +1 -14
  38. package/handlers/routers/auth.js +10 -46
  39. package/handlers/routers/health.js +4 -13
  40. package/handlers/routers/integration-defined-routers.js +92 -144
  41. package/handlers/routers/integration-webhook-routers.js +64 -102
  42. package/handlers/workers/integration-defined-workers.js +2 -5
  43. package/handlers/workers/integration-triggers.js +25 -0
  44. package/index.js +3 -65
  45. package/infrastructure/scheduler/index.js +6 -19
  46. package/infrastructure/scheduler/recurrence.js +204 -0
  47. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  48. package/integrations/data-defined/README.md +185 -0
  49. package/integrations/data-defined/canonical.js +11 -0
  50. package/integrations/data-defined/capabilities/acmeerp.capabilities.json +608 -0
  51. package/integrations/data-defined/capabilities/asana.capabilities.json +265 -0
  52. package/integrations/data-defined/capabilities/calendly.capabilities.json +385 -0
  53. package/integrations/data-defined/capabilities/docusign.capabilities.json +314 -0
  54. package/integrations/data-defined/capabilities/fathom.capabilities.json +228 -0
  55. package/integrations/data-defined/capabilities/github.capabilities.json +563 -0
  56. package/integrations/data-defined/capabilities/hubspot.capabilities.json +715 -0
  57. package/integrations/data-defined/capabilities/jira.capabilities.json +376 -0
  58. package/integrations/data-defined/capabilities/klaviyo.capabilities.json +475 -0
  59. package/integrations/data-defined/capabilities/linear.capabilities.json +324 -0
  60. package/integrations/data-defined/capabilities/netsuite.capabilities.json +491 -0
  61. package/integrations/data-defined/capabilities/notion.capabilities.json +208 -0
  62. package/integrations/data-defined/capabilities/phone.capabilities.json +470 -0
  63. package/integrations/data-defined/capabilities/pipedrive.capabilities.json +388 -0
  64. package/integrations/data-defined/capabilities/quickbooks.capabilities.json +615 -0
  65. package/integrations/data-defined/capabilities/salesforce.capabilities.json +507 -0
  66. package/integrations/data-defined/capabilities/shopify.capabilities.json +1082 -0
  67. package/integrations/data-defined/capabilities/slack.capabilities.json +363 -0
  68. package/integrations/data-defined/capabilities/stripe.capabilities.json +580 -0
  69. package/integrations/data-defined/capabilities/webflow.capabilities.json +196 -0
  70. package/integrations/data-defined/capabilities/zendesk.capabilities.json +409 -0
  71. package/integrations/data-defined/compile.js +80 -0
  72. package/integrations/data-defined/config-form.js +137 -0
  73. package/integrations/data-defined/data-defined-integration.js +417 -0
  74. package/integrations/data-defined/examples/acme-erp-quickbooks.definition.json +467 -0
  75. package/integrations/data-defined/examples/asana-slack.definition.json +220 -0
  76. package/integrations/data-defined/examples/calendly-salesforce.definition.json +459 -0
  77. package/integrations/data-defined/examples/docusign-salesforce.definition.json +305 -0
  78. package/integrations/data-defined/examples/fathom-hubspot-notion.definition.json +575 -0
  79. package/integrations/data-defined/examples/github-linear.definition.json +316 -0
  80. package/integrations/data-defined/examples/hubspot-slack.definition.json +409 -0
  81. package/integrations/data-defined/examples/klaviyo-hubspot.definition.json +339 -0
  82. package/integrations/data-defined/examples/phone-hubspot.definition.json +637 -0
  83. package/integrations/data-defined/examples/pipedrive-hubspot.definition.json +136 -0
  84. package/integrations/data-defined/examples/pipedrive-phone.definition.json +340 -0
  85. package/integrations/data-defined/examples/records/phone-hubspot.definition-version.json +652 -0
  86. package/integrations/data-defined/examples/records/phone-hubspot.integration.json +86 -0
  87. package/integrations/data-defined/examples/records/phone-hubspot.mappings.json +39 -0
  88. package/integrations/data-defined/examples/records/phone-hubspot.workflow-run.json +62 -0
  89. package/integrations/data-defined/examples/shopify-klaviyo.definition.json +488 -0
  90. package/integrations/data-defined/examples/shopify-netsuite.definition.json +622 -0
  91. package/integrations/data-defined/examples/stripe-quickbooks.definition.json +476 -0
  92. package/integrations/data-defined/examples/webflow-hubspot-slack.definition.json +262 -0
  93. package/integrations/data-defined/examples/zendesk-jira.definition.json +604 -0
  94. package/integrations/data-defined/expressions.js +225 -0
  95. package/integrations/data-defined/index.js +39 -0
  96. package/integrations/data-defined/interpreter.js +89 -0
  97. package/integrations/data-defined/manifests.js +125 -0
  98. package/integrations/data-defined/repositories/definition-version-repository.js +67 -0
  99. package/integrations/data-defined/repositories/workflow-run-repository.js +59 -0
  100. package/integrations/data-defined/router.js +97 -0
  101. package/integrations/data-defined/runtime.js +208 -0
  102. package/integrations/data-defined/schema/capability-manifest.v0.1.schema.json +377 -0
  103. package/integrations/data-defined/schema/installation.v0.1.schema.json +130 -0
  104. package/integrations/data-defined/schema/integration-definition.v0.1.schema.json +902 -0
  105. package/integrations/data-defined/schema/records.md +98 -0
  106. package/integrations/data-defined/schema-check.js +58 -0
  107. package/integrations/data-defined/scripts/validate.js +66 -0
  108. package/integrations/data-defined/steps.js +89 -0
  109. package/integrations/data-defined/transforms.js +91 -0
  110. package/integrations/data-defined/use-cases/definition-use-cases.js +187 -0
  111. package/integrations/data-defined/validate.js +384 -0
  112. package/integrations/data-defined/versioning.js +176 -0
  113. package/integrations/data-defined/webhooks.js +119 -0
  114. package/integrations/index.js +0 -1
  115. package/integrations/integration-base.js +117 -0
  116. package/integrations/integration-router.js +101 -131
  117. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  118. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  119. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  120. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  121. package/integrations/repositories/integration-mapping-repository.js +29 -0
  122. package/integrations/repositories/integration-repository-documentdb.js +9 -37
  123. package/integrations/repositories/integration-repository-interface.js +4 -17
  124. package/integrations/repositories/integration-repository-mongo.js +96 -64
  125. package/integrations/repositories/integration-repository-postgres.js +81 -63
  126. package/integrations/repositories/process-repository-mongo.js +1 -1
  127. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  128. package/integrations/triggers.js +244 -0
  129. package/integrations/use-cases/create-integration.js +20 -55
  130. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  131. package/integrations/use-cases/index.js +1 -7
  132. package/integrations/use-cases/run-trigger-tick.js +132 -0
  133. package/integrations/use-cases/run-trigger.js +77 -0
  134. package/integrations/utils/map-integration-dto.js +14 -43
  135. package/modules/index.js +0 -14
  136. package/modules/module-factory.js +0 -12
  137. package/modules/module.js +2 -53
  138. package/modules/repositories/module-repository-documentdb.js +6 -51
  139. package/modules/repositories/module-repository-interface.js +2 -19
  140. package/modules/repositories/module-repository-mongo.js +74 -67
  141. package/modules/repositories/module-repository-postgres.js +74 -66
  142. package/modules/repositories/module-repository.js +1 -2
  143. package/modules/requester/requester.js +5 -35
  144. package/modules/use-cases/get-entities-for-user.js +13 -26
  145. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  146. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  147. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  148. package/modules/use-cases/get-module.js +4 -10
  149. package/modules/use-cases/process-authorization-callback.js +19 -53
  150. package/modules/use-cases/refresh-entity-options.js +4 -10
  151. package/modules/use-cases/test-module-auth.js +4 -10
  152. package/package.json +6 -6
  153. package/prisma-mongodb/schema.prisma +50 -86
  154. package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +66 -0
  155. package/prisma-postgresql/schema.prisma +48 -81
  156. package/queues/index.js +0 -25
  157. package/queues/queuer-util.js +9 -2
  158. package/types/index.d.ts +0 -2
  159. package/user/repositories/user-repository-documentdb.js +2 -27
  160. package/user/repositories/user-repository-interface.js +0 -18
  161. package/user/repositories/user-repository-mongo.js +0 -24
  162. package/user/repositories/user-repository-postgres.js +0 -18
  163. package/user/use-cases/authenticate-user.js +1 -7
  164. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  165. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  166. package/user/user.js +1 -64
  167. package/credential/use-cases/credential-operations-for-user.js +0 -202
  168. package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
  169. package/database/organization-where.js +0 -18
  170. package/database/unique-violation.js +0 -25
  171. package/extensions/bootstrap-runner.js +0 -43
  172. package/extensions/extension-loader.js +0 -187
  173. package/extensions/index.js +0 -41
  174. package/extensions/initialize-app.js +0 -123
  175. package/extensions/route-mounter.js +0 -103
  176. package/extensions/schema-composer.js +0 -188
  177. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
  178. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  179. package/integrations/repositories/integration-not-found.js +0 -14
  180. package/integrations/schedule-config.js +0 -80
  181. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  182. package/integrations/use-cases/integration-operations-for-user.js +0 -256
  183. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  184. package/management-api/create-management-api-router.js +0 -49
  185. package/management-api/error-response.js +0 -116
  186. package/management-api/management-api-config.js +0 -151
  187. package/management-api/meta-router.js +0 -76
  188. package/management-api/openapi/build-openapi.js +0 -144
  189. package/management-api/openapi/components.js +0 -415
  190. package/management-api/openapi/v1-operations.js +0 -83
  191. package/management-api/openapi/v2-operations.js +0 -264
  192. package/management-api/route-registry.js +0 -411
  193. package/management-api/test-support/http-client.js +0 -39
  194. package/management-api/use-cases/get-management-api-meta.js +0 -57
  195. package/management-api/v1-deprecation.js +0 -120
  196. package/management-api/v2/create-v2-router.js +0 -85
  197. package/management-api/v2/dependencies.js +0 -281
  198. package/management-api/v2/dto.js +0 -143
  199. package/management-api/v2/handlers/authorization.js +0 -94
  200. package/management-api/v2/handlers/credentials.js +0 -33
  201. package/management-api/v2/handlers/entities.js +0 -77
  202. package/management-api/v2/handlers/integrations.js +0 -128
  203. package/management-api/v2/handlers/proxy.js +0 -34
  204. package/management-api/v2/request.js +0 -85
  205. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  206. package/modules/domain/authorization-session.js +0 -144
  207. package/modules/entity-scope.js +0 -15
  208. package/modules/entity-type-registry.js +0 -311
  209. package/modules/proxy/proxy-bases.js +0 -169
  210. package/modules/proxy/proxy-errors.js +0 -31
  211. package/modules/proxy/proxy-path.js +0 -183
  212. package/modules/proxy/proxy-policy.js +0 -277
  213. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  214. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  215. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  216. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  217. package/modules/repositories/entity-not-found.js +0 -18
  218. package/modules/requester/proxy-request.js +0 -245
  219. package/modules/scope-config.js +0 -192
  220. package/modules/scope-policy.js +0 -112
  221. package/modules/scope-resolution.js +0 -288
  222. package/modules/scope-visibility.js +0 -247
  223. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  224. package/modules/tests/doubles/ownership-matching.js +0 -18
  225. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  226. package/modules/use-cases/create-entity-from-credential.js +0 -113
  227. package/modules/use-cases/entity-operations-for-user.js +0 -162
  228. package/modules/use-cases/entity-types.js +0 -139
  229. package/modules/use-cases/execute-entity-proxy-request.js +0 -331
  230. package/modules/use-cases/get-authorization-requirements.js +0 -100
  231. package/modules/use-cases/get-entity-module-for-user.js +0 -113
  232. package/modules/use-cases/manage-access.js +0 -21
  233. package/modules/use-cases/multi-step-authorization.js +0 -329
  234. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  235. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  236. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  237. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  238. package/providers/index.js +0 -23
  239. package/providers/resolve-provider.js +0 -93
  240. package/providers/runtime-detection.js +0 -124
  241. package/queues/providers/index.js +0 -11
  242. package/queues/providers/netlify-background-provider.js +0 -150
  243. package/queues/providers/qstash-queue-provider.js +0 -219
  244. package/queues/providers/sqs-queue-provider.js +0 -70
  245. package/queues/queue-client-interface.js +0 -55
  246. package/queues/queue-message-auth.js +0 -193
  247. package/queues/queue-provider-factory.js +0 -101
  248. package/queues/queue-provider.js +0 -65
  249. package/queues/sqs-queue-client.js +0 -81
  250. package/types/modules/index.d.ts +0 -214
  251. package/types/user/index.d.ts +0 -134
  252. package/user/adopter-jwt-config.js +0 -177
  253. package/user/adopter-jwt-errors.js +0 -59
  254. package/user/adopter-token-verifier.js +0 -122
  255. package/user/app-user-helpers.js +0 -97
  256. package/user/create-user-authentication.js +0 -64
  257. package/user/membership.js +0 -51
  258. package/user/repositories/organization-membership-record.js +0 -19
  259. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  260. package/user/repositories/organization-membership-repository-factory.js +0 -42
  261. package/user/repositories/organization-membership-repository-interface.js +0 -57
  262. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  263. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  264. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  265. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  266. package/user/use-cases/ensure-organization-membership.js +0 -92
  267. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -1,7 +1,5 @@
1
1
  const { prisma } = require('../../database/prisma');
2
- const { organizationWhere } = require('../../database/organization-where');
3
2
  const { ModuleRepositoryInterface } = require('./module-repository-interface');
4
- const { entityNotFoundError } = require('./entity-not-found');
5
3
 
6
4
  /**
7
5
  * MongoDB Module Repository Adapter
@@ -78,26 +76,6 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
78
76
  return credentialMap;
79
77
  }
80
78
 
81
- /**
82
- * The one place a stored entity row becomes the entity the application sees.
83
- * The provider's `data` is spread FIRST and every column is pinned after it:
84
- * a stored `id`, `userId` or `organizationId` in `data` must not decide who
85
- * owns the entity or who sees it.
86
- * @private
87
- */
88
- _mapEntity(row, credential) {
89
- return {
90
- ...(row.data || {}),
91
- id: row.id,
92
- credential,
93
- userId: row.userId,
94
- name: row.name,
95
- externalId: row.externalId,
96
- moduleName: row.moduleName,
97
- organizationId: row.organizationId ?? null,
98
- };
99
- }
100
-
101
79
  /**
102
80
  * Find entity by ID with credential
103
81
  * Replaces: Entity.findById(entityId).populate('credential')
@@ -112,12 +90,20 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
112
90
  });
113
91
 
114
92
  if (!entity) {
115
- throw entityNotFoundError(entityId);
93
+ throw new Error(`Entity ${entityId} not found`);
116
94
  }
117
95
 
118
96
  const credential = await this._fetchCredential(entity.credentialId);
119
97
 
120
- return this._mapEntity(entity, credential);
98
+ return {
99
+ id: entity.id,
100
+ credential,
101
+ userId: entity.userId,
102
+ name: entity.name,
103
+ externalId: entity.externalId,
104
+ moduleName: entity.moduleName,
105
+ ...(entity.data || {}),
106
+ };
121
107
  }
122
108
 
123
109
  /**
@@ -135,7 +121,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
135
121
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
136
122
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
137
123
 
138
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
124
+ return entities.map((e) => ({
125
+ id: e.id,
126
+ credential: credentialMap.get(e.credentialId) || null,
127
+ userId: e.userId,
128
+ name: e.name,
129
+ externalId: e.externalId,
130
+ moduleName: e.moduleName,
131
+ ...(e.data || {}),
132
+ }));
139
133
  }
140
134
 
141
135
  /**
@@ -153,7 +147,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
153
147
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
154
148
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
155
149
 
156
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
150
+ return entities.map((e) => ({
151
+ id: e.id,
152
+ credential: credentialMap.get(e.credentialId) || null,
153
+ userId: e.userId,
154
+ name: e.name,
155
+ externalId: e.externalId,
156
+ moduleName: e.moduleName,
157
+ ...(e.data || {}),
158
+ }));
157
159
  }
158
160
 
159
161
  /**
@@ -175,32 +177,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
175
177
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
176
178
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
177
179
 
178
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
179
- }
180
-
181
- /**
182
- * Find entities matching any of the owner/organization pairs, in one query.
183
- * `organizationId: null` matches rows with no organization. The caller
184
- * (a use case) decides which pairs; this only runs them.
185
- *
186
- * @param {Array<{userId: string, organizationId: (string|null)}>} clauses
187
- * @returns {Promise<Array>} Array of entity objects with string IDs
188
- */
189
- async findEntitiesByOwnership(clauses) {
190
- if (!Array.isArray(clauses) || clauses.length === 0) return [];
191
- const entities = await this.prisma.entity.findMany({
192
- where: {
193
- OR: clauses.map(({ userId, organizationId }) => ({
194
- userId,
195
- ...organizationWhere(organizationId),
196
- })),
197
- },
198
- });
199
-
200
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
201
- const credentialMap = await this._fetchCredentialsBulk(credentialIds);
202
-
203
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
180
+ return entities.map((e) => ({
181
+ id: e.id,
182
+ credential: credentialMap.get(e.credentialId) || null,
183
+ userId: e.userId,
184
+ name: e.name,
185
+ externalId: e.externalId,
186
+ moduleName: e.moduleName,
187
+ ...(e.data || {}),
188
+ }));
204
189
  }
205
190
 
206
191
  /**
@@ -238,7 +223,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
238
223
 
239
224
  const credential = await this._fetchCredential(entity.credentialId);
240
225
 
241
- return this._mapEntity(entity, credential);
226
+ return {
227
+ id: entity.id,
228
+ credential,
229
+ userId: entity.userId,
230
+ name: entity.name,
231
+ externalId: entity.externalId,
232
+ moduleName: entity.moduleName,
233
+ ...(entity.data || {}),
234
+ };
242
235
  }
243
236
 
244
237
  /**
@@ -258,7 +251,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
258
251
  .filter(Boolean);
259
252
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
260
253
 
261
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
254
+ return entities.map((e) => ({
255
+ id: e.id,
256
+ credential: credentialMap.get(e.credentialId) || null,
257
+ userId: e.userId,
258
+ name: e.name,
259
+ externalId: e.externalId,
260
+ moduleName: e.moduleName,
261
+ ...(e.data || {}),
262
+ }));
262
263
  }
263
264
 
264
265
  /**
@@ -266,13 +267,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
266
267
  * Replaces: Entity.create(entityData)
267
268
  *
268
269
  * @param {Object} entityData - Entity data
269
- * @param {{organizationId?: (string|null)}} [tenancy] - the organization the entity
270
- * belongs to (ADR-056 §3.6). A separate argument, not a field of `entityData`,
271
- * because `entityData` carries provider fields and one named `organizationId`
272
- * must not be taken for ours.
273
270
  * @returns {Promise<Object>} Created entity object with string IDs
274
271
  */
275
- async createEntity(entityData, tenancy = {}) {
272
+ async createEntity(entityData) {
276
273
  const {
277
274
  user,
278
275
  userId,
@@ -290,7 +287,6 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
290
287
  name,
291
288
  moduleName,
292
289
  externalId,
293
- organizationId: tenancy.organizationId ?? null,
294
290
  data: dynamicData,
295
291
  };
296
292
 
@@ -300,7 +296,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
300
296
 
301
297
  const credentialObj = await this._fetchCredential(entity.credentialId);
302
298
 
303
- return this._mapEntity(entity, credentialObj);
299
+ return {
300
+ id: entity.id,
301
+ credential: credentialObj,
302
+ userId: entity.userId,
303
+ name: entity.name,
304
+ externalId: entity.externalId,
305
+ moduleName: entity.moduleName,
306
+ ...(entity.data || {}),
307
+ };
304
308
  }
305
309
 
306
310
  /**
@@ -354,7 +358,15 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
354
358
 
355
359
  const credentialObj = await this._fetchCredential(entity.credentialId);
356
360
 
357
- return this._mapEntity(entity, credentialObj);
361
+ return {
362
+ id: entity.id,
363
+ credential: credentialObj,
364
+ userId: entity.userId,
365
+ name: entity.name,
366
+ externalId: entity.externalId,
367
+ moduleName: entity.moduleName,
368
+ ...(entity.data || {}),
369
+ };
358
370
  } catch (error) {
359
371
  if (error.code === 'P2025') {
360
372
  return null;
@@ -417,11 +429,6 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
417
429
  if (filter.moduleName) where.moduleName = filter.moduleName;
418
430
  if (filter.externalId) where.externalId = this._toString(filter.externalId);
419
431
 
420
- // `null` means "no organization"; absent means "any".
421
- if (filter.organizationId !== undefined) {
422
- Object.assign(where, organizationWhere(filter.organizationId));
423
- }
424
-
425
432
  return where;
426
433
  }
427
434
  }
@@ -1,6 +1,5 @@
1
1
  const { prisma } = require('../../database/prisma');
2
2
  const { ModuleRepositoryInterface } = require('./module-repository-interface');
3
- const { entityNotFoundError } = require('./entity-not-found');
4
3
 
5
4
  /**
6
5
  * PostgreSQL Module Repository Adapter
@@ -109,26 +108,6 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
109
108
  return credentialMap;
110
109
  }
111
110
 
112
- /**
113
- * The one place a stored entity row becomes the entity the application sees.
114
- * The provider's `data` is spread FIRST and every column is pinned after it:
115
- * a stored `id`, `userId` or `organizationId` in `data` must not decide who
116
- * owns the entity or who sees it.
117
- * @private
118
- */
119
- _mapEntity(row, credential) {
120
- return {
121
- ...(row.data || {}),
122
- id: row.id.toString(),
123
- credential,
124
- userId: row.userId?.toString(),
125
- name: row.name,
126
- externalId: row.externalId,
127
- moduleName: row.moduleName,
128
- organizationId: row.organizationId?.toString() ?? null,
129
- };
130
- }
131
-
132
111
  /**
133
112
  * Find entity by ID with credential
134
113
  * Replaces: Entity.findById(entityId).populate('credential')
@@ -145,12 +124,20 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
145
124
  });
146
125
 
147
126
  if (!entity) {
148
- throw entityNotFoundError(entityId);
127
+ throw new Error(`Entity ${entityId} not found`);
149
128
  }
150
129
 
151
130
  const credential = await this._fetchCredential(entity.credentialId);
152
131
 
153
- return this._mapEntity(entity, credential);
132
+ return {
133
+ id: entity.id.toString(),
134
+ credential,
135
+ userId: entity.userId?.toString(),
136
+ name: entity.name,
137
+ externalId: entity.externalId,
138
+ moduleName: entity.moduleName,
139
+ ...(entity.data || {}),
140
+ };
154
141
  }
155
142
 
156
143
  /**
@@ -170,7 +157,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
170
157
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
171
158
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
172
159
 
173
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
160
+ return entities.map((e) => ({
161
+ id: e.id.toString(),
162
+ credential: credentialMap.get(e.credentialId) || null,
163
+ userId: e.userId?.toString(),
164
+ name: e.name,
165
+ externalId: e.externalId,
166
+ moduleName: e.moduleName,
167
+ ...(e.data || {}),
168
+ }));
174
169
  }
175
170
 
176
171
  /**
@@ -190,7 +185,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
190
185
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
191
186
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
192
187
 
193
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
188
+ return entities.map((e) => ({
189
+ id: e.id.toString(),
190
+ credential: credentialMap.get(e.credentialId) || null,
191
+ userId: e.userId?.toString(),
192
+ name: e.name,
193
+ externalId: e.externalId,
194
+ moduleName: e.moduleName,
195
+ ...(e.data || {}),
196
+ }));
194
197
  }
195
198
 
196
199
  /**
@@ -214,32 +217,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
214
217
  const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
215
218
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
216
219
 
217
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
218
- }
219
-
220
- /**
221
- * Find entities matching any of the owner/organization pairs, in one query.
222
- * `organizationId: null` matches rows with no organization. The caller
223
- * (a use case) decides which pairs; this only runs them.
224
- *
225
- * @param {Array<{userId: string, organizationId: (string|null)}>} clauses
226
- * @returns {Promise<Array>} Array of entity objects with string IDs
227
- */
228
- async findEntitiesByOwnership(clauses) {
229
- if (!Array.isArray(clauses) || clauses.length === 0) return [];
230
- const entities = await this.prisma.entity.findMany({
231
- where: {
232
- OR: clauses.map(({ userId, organizationId }) => ({
233
- userId: this._convertId(userId),
234
- organizationId: this._convertId(organizationId),
235
- })),
236
- },
237
- });
238
-
239
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
240
- const credentialMap = await this._fetchCredentialsBulk(credentialIds);
241
-
242
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
220
+ return entities.map((e) => ({
221
+ id: e.id.toString(),
222
+ credential: credentialMap.get(e.credentialId) || null,
223
+ userId: e.userId?.toString(),
224
+ name: e.name,
225
+ externalId: e.externalId,
226
+ moduleName: e.moduleName,
227
+ ...(e.data || {}),
228
+ }));
243
229
  }
244
230
 
245
231
  /**
@@ -278,7 +264,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
278
264
 
279
265
  const credential = await this._fetchCredential(entity.credentialId);
280
266
 
281
- return this._mapEntity(entity, credential);
267
+ return {
268
+ id: entity.id.toString(),
269
+ credential,
270
+ userId: entity.userId?.toString(),
271
+ name: entity.name,
272
+ externalId: entity.externalId,
273
+ moduleName: entity.moduleName,
274
+ ...(entity.data || {}),
275
+ };
282
276
  }
283
277
 
284
278
  /**
@@ -298,7 +292,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
298
292
  .filter(Boolean);
299
293
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
300
294
 
301
- return entities.map((e) => (this._mapEntity(e, credentialMap.get(e.credentialId) || null)));
295
+ return entities.map((e) => ({
296
+ id: e.id.toString(),
297
+ credential: credentialMap.get(e.credentialId) || null,
298
+ userId: e.userId?.toString(),
299
+ name: e.name,
300
+ externalId: e.externalId,
301
+ moduleName: e.moduleName,
302
+ ...(e.data || {}),
303
+ }));
302
304
  }
303
305
 
304
306
  /**
@@ -306,13 +308,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
306
308
  * Replaces: Entity.create(entityData)
307
309
  *
308
310
  * @param {Object} entityData - Entity data (with string IDs from application layer)
309
- * @param {{organizationId?: (string|null)}} [tenancy] - the organization the entity
310
- * belongs to (ADR-056 §3.6). A separate argument, not a field of `entityData`,
311
- * because `entityData` carries provider fields and one named `organizationId`
312
- * must not be taken for ours.
313
311
  * @returns {Promise<Object>} Created entity object with string IDs
314
312
  */
315
- async createEntity(entityData, tenancy = {}) {
313
+ async createEntity(entityData) {
316
314
  const {
317
315
  user,
318
316
  userId,
@@ -330,7 +328,6 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
330
328
  name,
331
329
  moduleName,
332
330
  externalId,
333
- organizationId: this._convertId(tenancy.organizationId ?? null),
334
331
  data: dynamicData,
335
332
  };
336
333
 
@@ -340,7 +337,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
340
337
 
341
338
  const credentialObj = await this._fetchCredential(entity.credentialId);
342
339
 
343
- return this._mapEntity(entity, credentialObj);
340
+ return {
341
+ id: entity.id.toString(),
342
+ credential: credentialObj,
343
+ userId: entity.userId?.toString(),
344
+ name: entity.name,
345
+ externalId: entity.externalId,
346
+ moduleName: entity.moduleName,
347
+ ...(entity.data || {}),
348
+ };
344
349
  }
345
350
 
346
351
  /**
@@ -396,7 +401,15 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
396
401
 
397
402
  const credentialObj = await this._fetchCredential(entity.credentialId);
398
403
 
399
- return this._mapEntity(entity, credentialObj);
404
+ return {
405
+ id: entity.id.toString(),
406
+ credential: credentialObj,
407
+ userId: entity.userId?.toString(),
408
+ name: entity.name,
409
+ externalId: entity.externalId,
410
+ moduleName: entity.moduleName,
411
+ ...(entity.data || {}),
412
+ };
400
413
  } catch (error) {
401
414
  if (error.code === 'P2025') {
402
415
  return null;
@@ -461,11 +474,6 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
461
474
  if (filter.moduleName) where.moduleName = filter.moduleName;
462
475
  if (filter.externalId) where.externalId = this._toString(filter.externalId);
463
476
 
464
- // `null` means "no organization"; absent means "any".
465
- if (filter.organizationId !== undefined) {
466
- where.organizationId = this._convertId(filter.organizationId);
467
- }
468
-
469
477
  return where;
470
478
  }
471
479
  }
@@ -1,6 +1,5 @@
1
1
  const { prisma } = require('../../database/prisma');
2
2
  const { ModuleRepositoryInterface } = require('./module-repository-interface');
3
- const { entityNotFoundError } = require('./entity-not-found');
4
3
 
5
4
  /**
6
5
  * Prisma-based Module Repository
@@ -38,7 +37,7 @@ class ModuleRepository extends ModuleRepositoryInterface {
38
37
  });
39
38
 
40
39
  if (!entity) {
41
- throw entityNotFoundError(entityId);
40
+ throw new Error(`Entity ${entityId} not found`);
42
41
  }
43
42
 
44
43
  return {
@@ -8,7 +8,6 @@ const { getLogger } = require('../../logs');
8
8
  const { redactUrl } = require('../../logs/redact');
9
9
  const { toSanitizedSurrogate } = require('../../logs/serialize');
10
10
  const { getLoggerScope } = require('../../logs/context');
11
- const { proxyRequest } = require('./proxy-request');
12
11
 
13
12
  const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
14
13
 
@@ -219,14 +218,9 @@ class Requester extends Delegate {
219
218
  if (options.query) {
220
219
  let queryBuild = '?';
221
220
  for (const key in options.query) {
222
- const value = options.query[key];
223
- // `fields[]: ['a', 'b']` -> `fields%5B%5D=a&fields%5B%5D=b`.
224
- // Any other array keeps String(value), i.e. comma-joined.
225
- const values =
226
- Array.isArray(value) && key.endsWith('[]') ? value : [value];
227
- for (const v of values) {
228
- queryBuild += `${encodeURIComponent(key)}=${encodeURIComponent(v)}&`;
229
- }
221
+ queryBuild += `${encodeURIComponent(key)}=${encodeURIComponent(
222
+ options.query[key]
223
+ )}&`;
230
224
  }
231
225
  encodedUrl += queryBuild.slice(0, -1);
232
226
  }
@@ -243,9 +237,7 @@ class Requester extends Delegate {
243
237
  // recursion (with its own backoff sleeps) always gets a clean
244
238
  // signal. Timer is cleared in the finally block regardless of
245
239
  // outcome.
246
- // options.timeoutMs lets one call use a tighter budget (the
247
- // Management API proxy passes what is left of its deadline).
248
- const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
240
+ const timeoutMs = this.requestTimeoutMs;
249
241
  const controller = timeoutMs > 0 ? new AbortController() : null;
250
242
  const timeoutHandle = controller
251
243
  ? setTimeout(() => controller.abort(), timeoutMs)
@@ -283,11 +275,7 @@ class Requester extends Delegate {
283
275
  // stall at batch scale.
284
276
  const isTimeout =
285
277
  e?.name === 'AbortError' || e?.type === 'aborted';
286
- if (
287
- e?.code === 'ECONNRESET' &&
288
- !options.noRetry &&
289
- attempt < this.backOff.length
290
- ) {
278
+ if (e?.code === 'ECONNRESET' && attempt < this.backOff.length) {
291
279
  clearRequestTimer();
292
280
  const delay = this.backOff[attempt] * 1000;
293
281
  await new Promise((resolve) => setTimeout(resolve, delay));
@@ -313,7 +301,6 @@ class Requester extends Delegate {
313
301
  // If the status is retriable and there are back off requests left, retry the request
314
302
  if (
315
303
  (status === 429 || status >= 500) &&
316
- !options.noRetry &&
317
304
  attempt < this.backOff.length
318
305
  ) {
319
306
  clearRequestTimer();
@@ -399,12 +386,6 @@ class Requester extends Delegate {
399
386
  // If the error wasn't retried, throw. FetchError.create reads
400
387
  // the response body (response.text()) — timer must still be
401
388
  // alive to catch a stalled body stream.
402
- // The proxy relays upstream errors to its caller instead of
403
- // throwing; 401 keeps the refresh / invalidate handling above.
404
- if (status >= 400 && options.returnErrorResponses) {
405
- return response;
406
- }
407
-
408
389
  if (status >= 400) {
409
390
  this._logRequestFailed(encodedUrl, options, status);
410
391
  const fetchError = await FetchError.create({
@@ -547,17 +528,6 @@ class Requester extends Delegate {
547
528
  return res;
548
529
  }
549
530
 
550
- /**
551
- * Generic, path-checked request for the Management API entity proxy
552
- * (ADR-052 §8). The URL is always the module base URL plus a validated
553
- * path; see ./proxy-request.js. Module methods are not involved.
554
- *
555
- * @param {Object} params - see proxyRequest()
556
- */
557
- async _proxyRequest(params) {
558
- return proxyRequest(this, params);
559
- }
560
-
561
531
  async _delete(options) {
562
532
  const fetchOptions = {
563
533
  method: 'DELETE',
@@ -1,11 +1,8 @@
1
1
  const { Module } = require('../module');
2
2
  const { mapModuleClassToModuleDTO } = require('../utils/map-module-dto');
3
- const { describeRecord, visibilityClauses } = require('../scope-visibility');
4
- const { DEFAULT_SCOPE_POLICY } = require('../scope-policy');
5
3
 
6
4
  class GetEntitiesForUser {
7
- constructor({ moduleRepository, moduleDefinitions, scopePolicy }) {
8
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
5
+ constructor({ moduleRepository, moduleDefinitions }) {
9
6
  this.moduleRepository = moduleRepository;
10
7
 
11
8
  this.definitionMap = new Map();
@@ -14,31 +11,21 @@ class GetEntitiesForUser {
14
11
  }
15
12
  }
16
13
 
17
- /**
18
- * The entities a `User` may see in the active organization (ADR-056 §3.6).
19
- * @param {Object} user - authenticated User
20
- */
21
- async execute(user) {
22
- const ctx = this.scopePolicy.accessContext(user);
23
- const found = await this.moduleRepository.findEntitiesByOwnership(
24
- visibilityClauses(ctx, 'entity')
25
- );
26
- const entities = (found || []).filter((entity) =>
27
- describeRecord(entity, ctx, 'entity')
14
+ async execute(userId) {
15
+ const entities = await this.moduleRepository.findEntitiesByUserId(
16
+ userId
28
17
  );
29
18
 
30
- return Promise.all(
31
- entities.map(async (entity) => {
32
- const definition = this.definitionMap.get(entity.moduleName);
19
+ return entities.map((entity) => {
20
+ const definition = this.definitionMap.get(entity.moduleName);
33
21
 
34
- const moduleInstance = await Module.create({
35
- userId: entity.userId,
36
- definition: definition,
37
- entity: entity,
38
- });
39
- return mapModuleClassToModuleDTO(moduleInstance);
40
- })
41
- );
22
+ const moduleInstance = new Module({
23
+ userId,
24
+ definition: definition,
25
+ entity: entity,
26
+ });
27
+ return mapModuleClassToModuleDTO(moduleInstance);
28
+ });
42
29
  }
43
30
  }
44
31
 
@@ -1,5 +1,4 @@
1
1
  const { Module } = require('../module');
2
- const { DEFAULT_SCOPE_POLICY } = require('../scope-policy');
3
2
 
4
3
  class GetEntityOptionsById {
5
4
  /**
@@ -7,11 +6,9 @@ class GetEntityOptionsById {
7
6
  * @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
8
7
  * @param {} params.moduleDefinitions
9
8
  */
10
- constructor({ moduleRepository, moduleDefinitions, scopePolicy }) {
9
+ constructor({ moduleRepository, moduleDefinitions }) {
11
10
  this.moduleRepository = moduleRepository;
12
11
  this.moduleDefinitions = moduleDefinitions;
13
- // Who may load an entity follows its scope (ADR-056 §3.6).
14
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
15
12
  }
16
13
 
17
14
  /**
@@ -38,7 +35,7 @@ class GetEntityOptionsById {
38
35
 
39
36
  // Validate entity ownership
40
37
  const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
41
- ? this.scopePolicy.canUseEntity(userIdOrUser, entity)
38
+ ? userIdOrUser.ownsUserId(entity.userId)
42
39
  : entity.userId?.toString() === userId?.toString();
43
40
 
44
41
  if (!isOwned) {
@@ -60,11 +57,8 @@ class GetEntityOptionsById {
60
57
  );
61
58
  }
62
59
 
63
- // Built for the entity's OWNER, not the caller: a caller may use an
64
- // organization's entity, and a token refresh during this call must
65
- // update the owner's credential, not create one under the caller.
66
- const module = await Module.create({
67
- userId: entity.userId ?? userId,
60
+ const module = new Module({
61
+ userId,
68
62
  entity,
69
63
  definition: moduleDefinition,
70
64
  });
@@ -21,7 +21,7 @@ class GetEntityOptionsByType {
21
21
  if (!moduleDefinition) {
22
22
  throw new Error(`Module definition not found for type: ${type}`);
23
23
  }
24
- const moduleInstance = await Module.create({
24
+ const moduleInstance = new Module({
25
25
  userId,
26
26
  definition: moduleDefinition,
27
27
  });