@friggframework/core 2.0.0-next.12 → 2.0.0-next.120

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 (410) hide show
  1. package/.eslintrc.json +18 -1
  2. package/CLAUDE.md +830 -0
  3. package/README.md +996 -50
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
  9. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
  10. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  12. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  13. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  14. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  15. package/application/commands/README.md +451 -0
  16. package/application/commands/admin-script-commands.js +359 -0
  17. package/application/commands/credential-commands.js +262 -0
  18. package/application/commands/entity-commands.js +294 -0
  19. package/application/commands/integration-commands.js +419 -0
  20. package/application/commands/integration-mapping-commands.js +25 -0
  21. package/application/commands/process-commands.js +135 -0
  22. package/application/commands/report-commands.js +188 -0
  23. package/application/commands/scheduler-commands.js +263 -0
  24. package/application/commands/usage-commands.js +56 -0
  25. package/application/commands/user-commands.js +312 -0
  26. package/application/index.js +83 -0
  27. package/artifacts/repositories/artifact-repository-factory.js +19 -0
  28. package/artifacts/repositories/artifact-repository-interface.js +27 -0
  29. package/artifacts/repositories/artifact-repository-local.js +42 -0
  30. package/artifacts/repositories/artifact-repository-s3.js +61 -0
  31. package/assertions/index.js +0 -3
  32. package/core/CLAUDE.md +715 -0
  33. package/core/Worker.js +55 -24
  34. package/core/create-handler.js +121 -38
  35. package/core/index.js +8 -1
  36. package/core/invocation-scope.js +225 -0
  37. package/core/parameters-to-env.js +257 -0
  38. package/core/secrets-to-env.js +7 -6
  39. package/core/ssm-preload.mjs +34 -0
  40. package/credential/repositories/credential-active-type.js +32 -0
  41. package/credential/repositories/credential-repository-documentdb.js +355 -0
  42. package/credential/repositories/credential-repository-factory.js +54 -0
  43. package/credential/repositories/credential-repository-interface.js +113 -0
  44. package/credential/repositories/credential-repository-mongo.js +294 -0
  45. package/credential/repositories/credential-repository-postgres.js +312 -0
  46. package/credential/repositories/credential-repository.js +300 -0
  47. package/credential/use-cases/get-credential-for-user.js +25 -0
  48. package/credential/use-cases/update-authentication-status.js +15 -0
  49. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  50. package/database/adapters/lambda-invoker.js +97 -0
  51. package/database/config.js +147 -0
  52. package/database/documentdb-encryption-service.js +334 -0
  53. package/database/documentdb-utils.js +196 -0
  54. package/database/encryption/README.md +863 -0
  55. package/database/encryption/documentdb-encryption-service.md +3575 -0
  56. package/database/encryption/encryption-schema-registry.js +404 -0
  57. package/database/encryption/field-encryption-service.js +254 -0
  58. package/database/encryption/integration-mapping-encryption.js +97 -0
  59. package/database/encryption/logger.js +82 -0
  60. package/database/encryption/prisma-encryption-extension.js +241 -0
  61. package/database/index.js +21 -21
  62. package/database/prisma.js +182 -0
  63. package/database/repositories/health-check-repository-documentdb.js +138 -0
  64. package/database/repositories/health-check-repository-factory.js +48 -0
  65. package/database/repositories/health-check-repository-interface.js +82 -0
  66. package/database/repositories/health-check-repository-mongodb.js +89 -0
  67. package/database/repositories/health-check-repository-postgres.js +82 -0
  68. package/database/repositories/migration-status-repository-s3.js +137 -0
  69. package/database/use-cases/check-database-health-use-case.js +29 -0
  70. package/database/use-cases/check-database-state-use-case.js +81 -0
  71. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  72. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  73. package/database/use-cases/get-migration-status-use-case.js +93 -0
  74. package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
  75. package/database/use-cases/run-database-migration-use-case.js +139 -0
  76. package/database/use-cases/test-encryption-use-case.js +253 -0
  77. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  78. package/database/utils/mongodb-collection-utils.js +94 -0
  79. package/database/utils/mongodb-schema-init.js +108 -0
  80. package/database/utils/prisma-runner.js +491 -0
  81. package/database/utils/prisma-schema-parser.js +182 -0
  82. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  83. package/encrypt/Cryptor.js +34 -168
  84. package/encrypt/index.js +1 -2
  85. package/errors/client-safe-error.js +26 -0
  86. package/errors/fetch-error.js +59 -57
  87. package/errors/index.js +6 -0
  88. package/errors/mapping-already-exists-error.js +14 -0
  89. package/generated/prisma-mongodb/client.d.ts +1 -0
  90. package/generated/prisma-mongodb/client.js +5 -0
  91. package/generated/prisma-mongodb/default.d.ts +1 -0
  92. package/generated/prisma-mongodb/default.js +5 -0
  93. package/generated/prisma-mongodb/edge.d.ts +1 -0
  94. package/generated/prisma-mongodb/edge.js +386 -0
  95. package/generated/prisma-mongodb/index-browser.js +368 -0
  96. package/generated/prisma-mongodb/index.d.ts +27065 -0
  97. package/generated/prisma-mongodb/index.js +411 -0
  98. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  99. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  100. package/generated/prisma-mongodb/package.json +183 -0
  101. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  102. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  103. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  104. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  105. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  106. package/generated/prisma-mongodb/runtime/library.js +147 -0
  107. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  108. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  109. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  110. package/generated/prisma-mongodb/schema.prisma +455 -0
  111. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  112. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  113. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  114. package/generated/prisma-mongodb/wasm.js +393 -0
  115. package/generated/prisma-postgresql/client.d.ts +1 -0
  116. package/generated/prisma-postgresql/client.js +5 -0
  117. package/generated/prisma-postgresql/default.d.ts +1 -0
  118. package/generated/prisma-postgresql/default.js +5 -0
  119. package/generated/prisma-postgresql/edge.d.ts +1 -0
  120. package/generated/prisma-postgresql/edge.js +407 -0
  121. package/generated/prisma-postgresql/index-browser.js +389 -0
  122. package/generated/prisma-postgresql/index.d.ts +29904 -0
  123. package/generated/prisma-postgresql/index.js +432 -0
  124. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  125. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  126. package/generated/prisma-postgresql/package.json +183 -0
  127. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  128. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  129. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  130. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  131. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  132. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  133. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  134. package/generated/prisma-postgresql/runtime/library.js +147 -0
  135. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  136. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  137. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  138. package/generated/prisma-postgresql/schema.prisma +436 -0
  139. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  140. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  141. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  142. package/generated/prisma-postgresql/wasm.js +414 -0
  143. package/handlers/WEBHOOKS.md +682 -0
  144. package/handlers/app-definition-loader.js +103 -0
  145. package/handlers/app-handler-helpers.js +20 -6
  146. package/handlers/backend-utils.js +272 -46
  147. package/handlers/database-migration-handler.js +236 -0
  148. package/handlers/integration-event-dispatcher.js +68 -0
  149. package/handlers/middleware/admin-auth.js +73 -0
  150. package/handlers/routers/HEALTHCHECK.md +342 -0
  151. package/handlers/routers/auth.js +4 -15
  152. package/handlers/routers/db-migration.handler.js +29 -0
  153. package/handlers/routers/db-migration.js +329 -0
  154. package/handlers/routers/health.js +518 -0
  155. package/handlers/routers/integration-defined-routers.js +85 -10
  156. package/handlers/routers/integration-webhook-routers.js +87 -0
  157. package/handlers/routers/user.js +27 -5
  158. package/handlers/routers/websocket.js +31 -9
  159. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  160. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  161. package/handlers/workers/db-migration.js +438 -0
  162. package/handlers/workers/dlq-processor.js +89 -0
  163. package/handlers/workers/integration-defined-workers.js +13 -7
  164. package/index.js +123 -47
  165. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  166. package/infrastructure/scheduler/index.js +33 -0
  167. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  168. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  169. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  170. package/integrations/EXTENSIONS.md +240 -0
  171. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  172. package/integrations/extension.js +254 -0
  173. package/integrations/index.js +20 -10
  174. package/integrations/integration-base.js +777 -74
  175. package/integrations/integration-router.js +399 -180
  176. package/integrations/options.js +1 -1
  177. package/integrations/repositories/config-patch-shared.js +43 -0
  178. package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
  179. package/integrations/repositories/integration-mapping-query.js +193 -0
  180. package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
  181. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  182. package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
  183. package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
  184. package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
  185. package/integrations/repositories/integration-mapping-repository.js +185 -0
  186. package/integrations/repositories/integration-repository-documentdb.js +354 -0
  187. package/integrations/repositories/integration-repository-factory.js +51 -0
  188. package/integrations/repositories/integration-repository-interface.js +219 -0
  189. package/integrations/repositories/integration-repository-mongo.js +502 -0
  190. package/integrations/repositories/integration-repository-postgres.js +550 -0
  191. package/integrations/repositories/process-repository-documentdb.js +311 -0
  192. package/integrations/repositories/process-repository-factory.js +53 -0
  193. package/integrations/repositories/process-repository-interface.js +136 -0
  194. package/integrations/repositories/process-repository-mongo.js +262 -0
  195. package/integrations/repositories/process-repository-postgres.js +380 -0
  196. package/integrations/repositories/process-update-ops-shared.js +112 -0
  197. package/integrations/repositories/report-id.js +13 -0
  198. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  199. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  200. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  201. package/integrations/use-cases/add-entities-to-integration.js +92 -0
  202. package/integrations/use-cases/create-integration.js +253 -0
  203. package/integrations/use-cases/create-process.js +130 -0
  204. package/integrations/use-cases/delete-integration-for-user.js +130 -0
  205. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  206. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  207. package/integrations/use-cases/get-integration-for-user.js +78 -0
  208. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  209. package/integrations/use-cases/get-integration-instance.js +83 -0
  210. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  211. package/integrations/use-cases/get-possible-integrations.js +27 -0
  212. package/integrations/use-cases/get-process.js +89 -0
  213. package/integrations/use-cases/index.js +19 -0
  214. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  215. package/integrations/use-cases/load-integration-context.js +71 -0
  216. package/integrations/use-cases/patch-integration-config.js +39 -0
  217. package/integrations/use-cases/process-errors.js +28 -0
  218. package/integrations/use-cases/remove-entities-from-integration.js +70 -0
  219. package/integrations/use-cases/update-integration-config.js +32 -0
  220. package/integrations/use-cases/update-integration-messages.js +44 -0
  221. package/integrations/use-cases/update-integration-status.js +32 -0
  222. package/integrations/use-cases/update-integration.js +92 -0
  223. package/integrations/use-cases/update-process-metrics.js +222 -0
  224. package/integrations/use-cases/update-process-state.js +163 -0
  225. package/integrations/utils/assert-entity-ids-input.js +42 -0
  226. package/integrations/utils/map-integration-dto.js +37 -0
  227. package/jest-global-setup-noop.js +3 -0
  228. package/jest-global-teardown-noop.js +3 -0
  229. package/jest.config.js +2 -0
  230. package/logs/.eslintrc.json +26 -1
  231. package/logs/__fixtures__/events.js +82 -0
  232. package/logs/__fixtures__/matchers.js +45 -0
  233. package/logs/__fixtures__/secrets.js +59 -0
  234. package/logs/__fixtures__/vectors.js +329 -0
  235. package/logs/__fixtures__/with-env.js +45 -0
  236. package/logs/context.js +109 -0
  237. package/logs/contract-keys.js +67 -0
  238. package/logs/debug-shims.js +46 -0
  239. package/logs/denied-keys.js +21 -0
  240. package/logs/index.js +14 -3
  241. package/logs/jest-logger-setup.js +25 -0
  242. package/logs/levels.js +71 -0
  243. package/logs/logger-runtime.js +137 -0
  244. package/logs/logger.js +134 -46
  245. package/logs/record.js +276 -0
  246. package/logs/redact.js +328 -0
  247. package/logs/serialize.js +329 -0
  248. package/logs/sinks.js +93 -0
  249. package/logs/summarize-event.js +163 -0
  250. package/{module-plugin → modules}/index.js +0 -10
  251. package/modules/module-factory.js +56 -0
  252. package/modules/module.js +320 -0
  253. package/modules/repositories/module-repository-documentdb.js +350 -0
  254. package/modules/repositories/module-repository-factory.js +40 -0
  255. package/modules/repositories/module-repository-interface.js +145 -0
  256. package/modules/repositories/module-repository-mongo.js +436 -0
  257. package/modules/repositories/module-repository-postgres.js +481 -0
  258. package/modules/repositories/module-repository.js +369 -0
  259. package/modules/requester/api-key.js +52 -0
  260. package/modules/requester/oauth-2.js +561 -0
  261. package/modules/requester/requester.js +605 -0
  262. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  263. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  264. package/modules/tests/doubles/test-module-factory.js +16 -0
  265. package/modules/tests/doubles/test-module-repository.js +39 -0
  266. package/modules/use-cases/get-entities-for-user.js +32 -0
  267. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  268. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  269. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  270. package/modules/use-cases/get-module.js +74 -0
  271. package/modules/use-cases/process-authorization-callback.js +294 -0
  272. package/modules/use-cases/refresh-entity-options.js +72 -0
  273. package/modules/use-cases/test-module-auth.js +72 -0
  274. package/modules/utils/map-module-dto.js +20 -0
  275. package/modules/utils/to-credential-id.js +14 -0
  276. package/package.json +91 -52
  277. package/prisma-mongodb/schema.prisma +455 -0
  278. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  279. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  280. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  281. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  282. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  283. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  284. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  285. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  286. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  287. package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
  288. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  289. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  290. package/prisma-postgresql/schema.prisma +436 -0
  291. package/queues/queue-delivery.js +41 -0
  292. package/queues/queuer-util.js +89 -21
  293. package/reporting/README.md +154 -0
  294. package/reporting/builtin-reports.js +6 -0
  295. package/reporting/index.js +13 -0
  296. package/reporting/report-base.js +49 -0
  297. package/reporting/reports/integrations-report.js +221 -0
  298. package/syncs/manager.js +468 -443
  299. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  300. package/syncs/repositories/sync-repository-factory.js +43 -0
  301. package/syncs/repositories/sync-repository-interface.js +109 -0
  302. package/syncs/repositories/sync-repository-mongo.js +239 -0
  303. package/syncs/repositories/sync-repository-postgres.js +319 -0
  304. package/syncs/sync.js +0 -1
  305. package/telemetry/README.md +331 -0
  306. package/telemetry/bind-telemetry-context.js +77 -0
  307. package/telemetry/canonical-counters.js +52 -0
  308. package/telemetry/exporters.js +85 -0
  309. package/telemetry/index.js +26 -0
  310. package/telemetry/instrument-handler.js +104 -0
  311. package/telemetry/no-op-telemetry.js +71 -0
  312. package/telemetry/north-star.js +103 -0
  313. package/telemetry/otel-telemetry.js +232 -0
  314. package/telemetry/plugin-subscribers.js +77 -0
  315. package/telemetry/telemetry-config.js +120 -0
  316. package/telemetry/telemetry-context.js +51 -0
  317. package/telemetry/telemetry-event-bus.js +58 -0
  318. package/telemetry/telemetry-runtime.js +164 -0
  319. package/telemetry/telemetry-service.js +52 -0
  320. package/telemetry/usage-rollup-subscriber.js +116 -0
  321. package/token/repositories/token-repository-documentdb.js +137 -0
  322. package/token/repositories/token-repository-factory.js +40 -0
  323. package/token/repositories/token-repository-interface.js +131 -0
  324. package/token/repositories/token-repository-mongo.js +219 -0
  325. package/token/repositories/token-repository-postgres.js +264 -0
  326. package/token/repositories/token-repository.js +219 -0
  327. package/types/associations/index.d.ts +0 -17
  328. package/types/core/index.d.ts +12 -4
  329. package/types/database/index.d.ts +10 -2
  330. package/types/encrypt/index.d.ts +5 -3
  331. package/types/errors/index.d.ts +24 -10
  332. package/types/integrations/index.d.ts +3 -8
  333. package/types/logs/index.d.ts +61 -2
  334. package/types/module-plugin/index.d.ts +20 -69
  335. package/types/syncs/index.d.ts +0 -17
  336. package/usage/README.md +54 -0
  337. package/usage/index.js +17 -0
  338. package/usage/repositories/usage-repository-documentdb.js +194 -0
  339. package/usage/repositories/usage-repository-factory.js +25 -0
  340. package/usage/repositories/usage-repository-interface.js +37 -0
  341. package/usage/repositories/usage-repository-prisma.js +146 -0
  342. package/usage/tracked-metrics.js +38 -0
  343. package/usage/usage-windows.js +24 -0
  344. package/user/repositories/user-repository-documentdb.js +426 -0
  345. package/user/repositories/user-repository-factory.js +52 -0
  346. package/user/repositories/user-repository-interface.js +214 -0
  347. package/user/repositories/user-repository-mongo.js +323 -0
  348. package/user/repositories/user-repository-postgres.js +377 -0
  349. package/user/tests/doubles/test-user-repository.js +72 -0
  350. package/user/use-cases/authenticate-user.js +143 -0
  351. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  352. package/user/use-cases/create-individual-user.js +61 -0
  353. package/user/use-cases/create-organization-user.js +47 -0
  354. package/user/use-cases/create-token-for-user-id.js +30 -0
  355. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  356. package/user/use-cases/get-user-from-bearer-token.js +82 -0
  357. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  358. package/user/use-cases/login-user.js +122 -0
  359. package/user/user.js +125 -0
  360. package/utils/backend-path.js +38 -0
  361. package/utils/index.js +6 -0
  362. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  363. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  364. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  365. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  366. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  367. package/websocket/repositories/websocket-connection-repository.js +161 -0
  368. package/assertions/is-equal.js +0 -17
  369. package/associations/model.js +0 -54
  370. package/database/models/IndividualUser.js +0 -76
  371. package/database/models/OrganizationUser.js +0 -29
  372. package/database/models/State.js +0 -9
  373. package/database/models/Token.js +0 -70
  374. package/database/models/UserModel.js +0 -7
  375. package/database/models/WebsocketConnection.js +0 -49
  376. package/database/mongo.js +0 -45
  377. package/database/mongoose.js +0 -5
  378. package/encrypt/Cryptor.test.js +0 -32
  379. package/encrypt/encrypt.js +0 -132
  380. package/encrypt/encrypt.test.js +0 -1069
  381. package/encrypt/test-encrypt.js +0 -107
  382. package/errors/base-error.test.js +0 -32
  383. package/errors/fetch-error.test.js +0 -79
  384. package/errors/halt-error.test.js +0 -11
  385. package/errors/validation-errors.test.js +0 -120
  386. package/handlers/routers/middleware/loadUser.js +0 -15
  387. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  388. package/integrations/create-frigg-backend.js +0 -31
  389. package/integrations/integration-factory.js +0 -251
  390. package/integrations/integration-mapping.js +0 -43
  391. package/integrations/integration-model.js +0 -46
  392. package/integrations/integration-user.js +0 -144
  393. package/integrations/test/integration-base.test.js +0 -144
  394. package/lambda/TimeoutCatcher.test.js +0 -68
  395. package/logs/logger.test.js +0 -76
  396. package/module-plugin/auther.js +0 -393
  397. package/module-plugin/credential.js +0 -22
  398. package/module-plugin/entity-manager.js +0 -70
  399. package/module-plugin/entity.js +0 -46
  400. package/module-plugin/manager.js +0 -169
  401. package/module-plugin/module-factory.js +0 -61
  402. package/module-plugin/requester/api-key.js +0 -36
  403. package/module-plugin/requester/oauth-2.js +0 -219
  404. package/module-plugin/requester/requester.js +0 -165
  405. package/module-plugin/requester/requester.test.js +0 -28
  406. package/module-plugin/test/auther.test.js +0 -97
  407. package/syncs/model.js +0 -62
  408. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  409. /package/{module-plugin → modules}/requester/basic.js +0 -0
  410. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,406 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const { MappingAlreadyExistsError } = require('../../errors');
3
+ const {
4
+ toObjectId,
5
+ fromObjectId,
6
+ findMany,
7
+ findOne,
8
+ insertOne,
9
+ updateOne,
10
+ deleteOne,
11
+ deleteMany,
12
+ aggregate,
13
+ aggregateDrained,
14
+ } = require('../../database/documentdb-utils');
15
+ const {
16
+ IntegrationMappingRepositoryInterface,
17
+ } = require('./integration-mapping-repository-interface');
18
+ const {
19
+ DocumentDBEncryptionService,
20
+ } = require('../../database/documentdb-encryption-service');
21
+ const {
22
+ assertMappingWrittenUnencrypted,
23
+ } = require('../../database/encryption/integration-mapping-encryption');
24
+ const { validateMappingQuery } = require('./integration-mapping-query');
25
+ const {
26
+ buildMappingQueryStages,
27
+ } = require('./integration-mapping-query-pipeline');
28
+
29
+ function storedIntegrationId(id) {
30
+ if (!['string', 'number'].includes(typeof id) || id === '') {
31
+ throw new TypeError(`Invalid ID: ${id}`);
32
+ }
33
+ return String(id);
34
+ }
35
+
36
+ class IntegrationMappingRepositoryDocumentDB extends IntegrationMappingRepositoryInterface {
37
+ constructor() {
38
+ super();
39
+ this.prisma = prisma;
40
+ this.encryptionService = new DocumentDBEncryptionService();
41
+ }
42
+
43
+ /**
44
+ * integrationId is stored as a string in DocumentDB, so ids are matched as
45
+ * strings (an ObjectId $in would never match). Drains the grouped cursor so
46
+ * a deployment-wide count is not truncated at the first batch.
47
+ */
48
+ async countByIntegrationIds(ids = []) {
49
+ const counts = new Map();
50
+ if (!ids || ids.length === 0) return counts;
51
+
52
+ const stringIds = ids.map(String);
53
+ const rows = await aggregateDrained(this.prisma, 'IntegrationMapping', [
54
+ { $match: { integrationId: { $in: stringIds } } },
55
+ { $group: { _id: '$integrationId', count: { $sum: 1 } } },
56
+ ]);
57
+
58
+ for (const row of rows) {
59
+ counts.set(String(row?._id), row?.count ?? 0);
60
+ }
61
+ return counts;
62
+ }
63
+
64
+ async findMappingBy(integrationId, sourceId) {
65
+ const filter = this._compositeFilter(integrationId, sourceId);
66
+ const doc = await findOne(this.prisma, 'IntegrationMapping', filter);
67
+ if (!doc) return null;
68
+
69
+ const decryptedMapping = await this.encryptionService.decryptFields(
70
+ 'IntegrationMapping',
71
+ doc
72
+ );
73
+ return this._mapMapping(decryptedMapping);
74
+ }
75
+
76
+ async upsertMapping(integrationId, sourceId, mapping) {
77
+ const filter = this._compositeFilter(integrationId, sourceId);
78
+ const existing = await findOne(
79
+ this.prisma,
80
+ 'IntegrationMapping',
81
+ filter
82
+ );
83
+ const now = new Date();
84
+
85
+ if (existing) {
86
+ const decryptedExisting =
87
+ await this.encryptionService.decryptFields(
88
+ 'IntegrationMapping',
89
+ existing
90
+ );
91
+
92
+ const updateDocument = {
93
+ mapping,
94
+ updatedAt: now,
95
+ };
96
+
97
+ const encryptedUpdate = await this.encryptionService.encryptFields(
98
+ 'IntegrationMapping',
99
+ { mapping: updateDocument.mapping }
100
+ );
101
+
102
+ await updateOne(
103
+ this.prisma,
104
+ 'IntegrationMapping',
105
+ { _id: existing._id },
106
+ {
107
+ $set: {
108
+ mapping: encryptedUpdate.mapping,
109
+ updatedAt: updateDocument.updatedAt,
110
+ },
111
+ }
112
+ );
113
+
114
+ const updated = await findOne(this.prisma, 'IntegrationMapping', {
115
+ _id: existing._id,
116
+ });
117
+ if (!updated) {
118
+ console.error(
119
+ '[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
120
+ {
121
+ mappingId: fromObjectId(existing._id),
122
+ integrationId,
123
+ sourceId,
124
+ }
125
+ );
126
+ throw new Error(
127
+ 'Failed to update mapping: Document not found after update. ' +
128
+ 'This indicates a database consistency issue.'
129
+ );
130
+ }
131
+ const decryptedMapping = await this.encryptionService.decryptFields(
132
+ 'IntegrationMapping',
133
+ updated
134
+ );
135
+ return this._mapMapping(decryptedMapping);
136
+ }
137
+
138
+ const plainDocument = {
139
+ integrationId: integrationId,
140
+ sourceId:
141
+ sourceId === null || sourceId === undefined
142
+ ? null
143
+ : String(sourceId),
144
+ mapping,
145
+ createdAt: now,
146
+ updatedAt: now,
147
+ };
148
+
149
+ const encryptedDocument = await this.encryptionService.encryptFields(
150
+ 'IntegrationMapping',
151
+ plainDocument
152
+ );
153
+
154
+ const insertedId = await insertOne(
155
+ this.prisma,
156
+ 'IntegrationMapping',
157
+ encryptedDocument
158
+ );
159
+
160
+ const created = await findOne(this.prisma, 'IntegrationMapping', {
161
+ _id: insertedId,
162
+ });
163
+ if (!created) {
164
+ console.error(
165
+ '[IntegrationMappingRepositoryDocumentDB] Mapping not found after insert',
166
+ {
167
+ insertedId: fromObjectId(insertedId),
168
+ integrationId,
169
+ sourceId,
170
+ }
171
+ );
172
+ throw new Error(
173
+ 'Failed to create mapping: Document not found after insert. ' +
174
+ 'This indicates a database consistency issue.'
175
+ );
176
+ }
177
+ const decryptedMapping = await this.encryptionService.decryptFields(
178
+ 'IntegrationMapping',
179
+ created
180
+ );
181
+ return this._mapMapping(decryptedMapping);
182
+ }
183
+
184
+ async createMapping(integrationId, sourceId, mapping) {
185
+ const now = new Date();
186
+ const encryptedDocument = await this.encryptionService.encryptFields(
187
+ 'IntegrationMapping',
188
+ {
189
+ integrationId,
190
+ sourceId:
191
+ sourceId === null || sourceId === undefined
192
+ ? null
193
+ : String(sourceId),
194
+ mapping,
195
+ createdAt: now,
196
+ updatedAt: now,
197
+ }
198
+ );
199
+
200
+ let insertedId;
201
+ try {
202
+ insertedId = await insertOne(
203
+ this.prisma,
204
+ 'IntegrationMapping',
205
+ encryptedDocument
206
+ );
207
+ } catch (error) {
208
+ if (error.code === 11000) {
209
+ throw new MappingAlreadyExistsError(integrationId, sourceId, {
210
+ cause: error,
211
+ });
212
+ }
213
+ throw error;
214
+ }
215
+
216
+ const created = await findOne(this.prisma, 'IntegrationMapping', {
217
+ _id: insertedId,
218
+ });
219
+ if (!created) {
220
+ throw new Error(
221
+ 'Failed to create mapping: Document not found after insert. ' +
222
+ 'This indicates a database consistency issue.'
223
+ );
224
+ }
225
+ return this._mapMapping(
226
+ await this.encryptionService.decryptFields(
227
+ 'IntegrationMapping',
228
+ created
229
+ )
230
+ );
231
+ }
232
+
233
+ async findMappingsByIntegration(integrationId) {
234
+ const filter = {};
235
+ if (integrationId) filter.integrationId = integrationId;
236
+ const docs = await findMany(this.prisma, 'IntegrationMapping', filter);
237
+
238
+ const decryptedDocs = await Promise.all(
239
+ docs.map((doc) =>
240
+ this.encryptionService.decryptFields('IntegrationMapping', doc)
241
+ )
242
+ );
243
+
244
+ return decryptedDocs.map((doc) => this._mapMapping(doc));
245
+ }
246
+
247
+ /**
248
+ * @param {string} integrationId
249
+ * @param {Object} query - See IntegrationMappingRepositoryInterface.queryMappings
250
+ * @returns {Promise<{mappings: Array<Object>, total: number}>}
251
+ */
252
+ async queryMappings(integrationId, query) {
253
+ const validated = validateMappingQuery(query);
254
+ const stored = storedIntegrationId(integrationId);
255
+ assertMappingWrittenUnencrypted();
256
+ const { match, page, sort } = buildMappingQueryStages(
257
+ stored,
258
+ validated
259
+ );
260
+
261
+ const [docs, counts] = await Promise.all([
262
+ aggregateDrained(
263
+ this.prisma,
264
+ 'IntegrationMapping',
265
+ [match, ...page, sort],
266
+ { allowDiskUse: true }
267
+ ),
268
+ aggregate(this.prisma, 'IntegrationMapping', [
269
+ match,
270
+ { $count: 'total' },
271
+ ]),
272
+ ]);
273
+ const decryptedDocs = await Promise.all(
274
+ docs.map((doc) =>
275
+ this.encryptionService.decryptFields('IntegrationMapping', doc)
276
+ )
277
+ );
278
+
279
+ return {
280
+ mappings: decryptedDocs.map((doc) => this._mapMapping(doc)),
281
+ total: counts[0]?.total ?? 0,
282
+ };
283
+ }
284
+
285
+ async deleteMapping(integrationId, sourceId) {
286
+ const filter = this._compositeFilter(integrationId, sourceId);
287
+ const result = await deleteOne(
288
+ this.prisma,
289
+ 'IntegrationMapping',
290
+ filter
291
+ );
292
+ const deleted = result?.n ?? 0;
293
+ return { acknowledged: true, deletedCount: deleted };
294
+ }
295
+
296
+ async deleteMappingsByIntegration(integrationId) {
297
+ if (!integrationId) {
298
+ return { acknowledged: true, deletedCount: 0 };
299
+ }
300
+ const result = await deleteMany(this.prisma, 'IntegrationMapping', {
301
+ integrationId: integrationId,
302
+ });
303
+ const deleted = result?.n ?? 0;
304
+ return { acknowledged: true, deletedCount: deleted };
305
+ }
306
+
307
+ async findMappingById(id) {
308
+ const objectId = toObjectId(id);
309
+ if (!objectId) return null;
310
+ const doc = await findOne(this.prisma, 'IntegrationMapping', {
311
+ _id: objectId,
312
+ });
313
+ if (!doc) return null;
314
+
315
+ const decryptedMapping = await this.encryptionService.decryptFields(
316
+ 'IntegrationMapping',
317
+ doc
318
+ );
319
+ return this._mapMapping(decryptedMapping);
320
+ }
321
+
322
+ async updateMapping(id, updates) {
323
+ const objectId = toObjectId(id);
324
+ if (!objectId) return null;
325
+
326
+ const existing = await findOne(this.prisma, 'IntegrationMapping', {
327
+ _id: objectId,
328
+ });
329
+ if (!existing) return null;
330
+
331
+ const decryptedExisting = await this.encryptionService.decryptFields(
332
+ 'IntegrationMapping',
333
+ existing
334
+ );
335
+
336
+ const mergedMapping =
337
+ updates.mapping !== undefined
338
+ ? updates.mapping
339
+ : decryptedExisting.mapping;
340
+
341
+ const updateDocument = {
342
+ ...updates,
343
+ updatedAt: new Date(),
344
+ };
345
+
346
+ if (mergedMapping !== undefined) {
347
+ const encryptedUpdate = await this.encryptionService.encryptFields(
348
+ 'IntegrationMapping',
349
+ { mapping: mergedMapping }
350
+ );
351
+ updateDocument.mapping = encryptedUpdate.mapping;
352
+ }
353
+
354
+ await updateOne(
355
+ this.prisma,
356
+ 'IntegrationMapping',
357
+ { _id: objectId },
358
+ {
359
+ $set: updateDocument,
360
+ }
361
+ );
362
+
363
+ const updated = await findOne(this.prisma, 'IntegrationMapping', {
364
+ _id: objectId,
365
+ });
366
+ if (!updated) {
367
+ console.error(
368
+ '[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
369
+ {
370
+ mappingId: fromObjectId(objectId),
371
+ }
372
+ );
373
+ throw new Error(
374
+ 'Failed to update mapping: Document not found after update. ' +
375
+ 'This indicates a database consistency issue.'
376
+ );
377
+ }
378
+ const decryptedMapping = await this.encryptionService.decryptFields(
379
+ 'IntegrationMapping',
380
+ updated
381
+ );
382
+ return this._mapMapping(decryptedMapping);
383
+ }
384
+
385
+ _compositeFilter(integrationId, sourceId) {
386
+ const filter = {};
387
+ if (integrationId) filter.integrationId = integrationId;
388
+ if (sourceId !== undefined) {
389
+ filter.sourceId = sourceId === null ? null : String(sourceId);
390
+ }
391
+ return filter;
392
+ }
393
+
394
+ _mapMapping(doc) {
395
+ return {
396
+ id: fromObjectId(doc?._id),
397
+ integrationId: doc?.integrationId ?? null,
398
+ sourceId: doc?.sourceId ?? null,
399
+ mapping: doc?.mapping ?? null,
400
+ createdAt: doc?.createdAt,
401
+ updatedAt: doc?.updatedAt,
402
+ };
403
+ }
404
+ }
405
+
406
+ module.exports = { IntegrationMappingRepositoryDocumentDB };
@@ -0,0 +1,57 @@
1
+ const {
2
+ IntegrationMappingRepositoryMongo,
3
+ } = require('./integration-mapping-repository-mongo');
4
+ const {
5
+ IntegrationMappingRepositoryPostgres,
6
+ } = require('./integration-mapping-repository-postgres');
7
+ const {
8
+ IntegrationMappingRepositoryDocumentDB,
9
+ } = require('./integration-mapping-repository-documentdb');
10
+ const config = require('../../database/config');
11
+
12
+ /**
13
+ * Integration Mapping Repository Factory
14
+ * Creates the appropriate repository adapter based on database type
15
+ *
16
+ * Database-specific implementations:
17
+ * - MongoDB: Uses String IDs (ObjectId), no conversion needed
18
+ * - PostgreSQL: Uses Int IDs, converts String ↔ Int
19
+ *
20
+ * All repository methods return String IDs regardless of database type,
21
+ * ensuring application layer consistency.
22
+ *
23
+ * Usage:
24
+ * ```javascript
25
+ * const repository = createIntegrationMappingRepository();
26
+ * const mapping = await repository.findMappingBy(integrationId, sourceId);
27
+ * ```
28
+ *
29
+ * @returns {IntegrationMappingRepositoryInterface} Configured repository adapter
30
+ */
31
+ function createIntegrationMappingRepository() {
32
+ const dbType = config.DB_TYPE;
33
+
34
+ switch (dbType) {
35
+ case 'mongodb':
36
+ return new IntegrationMappingRepositoryMongo();
37
+
38
+ case 'postgresql':
39
+ return new IntegrationMappingRepositoryPostgres();
40
+
41
+ case 'documentdb':
42
+ return new IntegrationMappingRepositoryDocumentDB();
43
+
44
+ default:
45
+ throw new Error(
46
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
47
+ );
48
+ }
49
+ }
50
+
51
+ module.exports = {
52
+ createIntegrationMappingRepository,
53
+ // Export adapters for direct testing
54
+ IntegrationMappingRepositoryMongo,
55
+ IntegrationMappingRepositoryPostgres,
56
+ IntegrationMappingRepositoryDocumentDB,
57
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Integration Mapping Repository Interface
3
+ * Abstract base class defining the contract for integration mapping persistence adapters
4
+ *
5
+ * This follows the Port in Hexagonal Architecture:
6
+ * - Domain layer depends on this abstraction
7
+ * - Concrete adapters implement this interface
8
+ * - Use cases receive repositories via dependency injection
9
+ *
10
+ * Note: Currently, IntegrationMapping model has identical structure across MongoDB and PostgreSQL,
11
+ * so IntegrationMappingRepository serves both. This interface exists for consistency and
12
+ * future-proofing if database-specific implementations become needed.
13
+ *
14
+ * @abstract
15
+ */
16
+ class IntegrationMappingRepositoryInterface {
17
+ /**
18
+ * Find mapping by integration ID and source ID
19
+ *
20
+ * @param {string|number} integrationId - The integration ID
21
+ * @param {string} sourceId - The source ID for lookup
22
+ * @returns {Promise<Object|null>} The mapping object or null
23
+ * @abstract
24
+ */
25
+ async findMappingBy(integrationId, sourceId) {
26
+ throw new Error('Method findMappingBy must be implemented by subclass');
27
+ }
28
+
29
+ /**
30
+ * Create or update a mapping
31
+ *
32
+ * @param {string|number} integrationId - The integration ID
33
+ * @param {string} sourceId - The source ID for lookup
34
+ * @param {Object} mapping - The mapping data
35
+ * @returns {Promise<Object>} The created or updated mapping document
36
+ * @abstract
37
+ */
38
+ async upsertMapping(integrationId, sourceId, mapping) {
39
+ throw new Error('Method upsertMapping must be implemented by subclass');
40
+ }
41
+
42
+ /**
43
+ * Insert a mapping, failing if one already exists for the
44
+ * (integrationId, sourceId) pair. Atomic: usable as a claim/lock row.
45
+ *
46
+ * @param {string|number} integrationId - The integration ID
47
+ * @param {string} sourceId - The source ID for lookup
48
+ * @param {Object} mapping - The mapping data
49
+ * @returns {Promise<Object>} The created mapping document
50
+ * @throws {MappingAlreadyExistsError} If the pair already exists
51
+ * @abstract
52
+ */
53
+ async createMapping(integrationId, sourceId, mapping) {
54
+ throw new Error('createMapping must be implemented by subclass');
55
+ }
56
+
57
+ /**
58
+ * Find all mappings for an integration
59
+ *
60
+ * @param {string|number} integrationId - The integration ID
61
+ * @returns {Promise<Array>} Array of mapping objects
62
+ * @abstract
63
+ */
64
+ async findMappingsByIntegration(integrationId) {
65
+ throw new Error(
66
+ 'Method findMappingsByIntegration must be implemented by subclass'
67
+ );
68
+ }
69
+
70
+ /**
71
+ * Query one filtered, ordered page of an integration's mappings without
72
+ * loading every row. Rows have the same shape as findMappingsByIntegration
73
+ * and are decrypted the same way.
74
+ *
75
+ * Paths address the `mapping` JSON by identifier-only segments
76
+ * (`'mapping.outbound.status'`), or the `sourceId` column. Conditions:
77
+ * - `{ path: 'mapping.…', op: 'exists' | 'notExists' }` — JSON null counts
78
+ * as absent; notExists is the exact negation of exists.
79
+ * - `{ path: 'mapping.…', op: 'in', value: string[] }` — matches JSON
80
+ * strings; 1–500 values.
81
+ * - `{ path: 'sourceId', op: 'notStartsWith', value: string }` — a NULL
82
+ * sourceId matches.
83
+ *
84
+ * Only rows whose `mapping` is a JSON object can match, so rows whose
85
+ * whole `mapping` is still ciphertext from before an encryption opt-out
86
+ * never do. A nested path still encrypted from before its opt-out comes
87
+ * back plain, but conditions and orderBy on it see the ciphertext.
88
+ * Adapters refuse to run while field-level encryption is enabled
89
+ * and still encrypts `mapping`, or a path inside it, on write; opt out with
90
+ * `appDefinition.encryption.disable = { IntegrationMapping: ['mapping'] }`
91
+ * plus any nested `mapping.…` path a custom schema encrypts.
92
+ *
93
+ * @param {string|number} integrationId - The integration ID
94
+ * @param {Object} query
95
+ * @param {Array<Object>} [query.where=[]] - Conditions ANDed together; an
96
+ * entry may be `{ anyOf: Condition[] }` (one level, ORed). At most 20
97
+ * conditions, anyOf members included.
98
+ * @param {{path: string, direction: 'asc'|'desc'}} [query.orderBy] - A
99
+ * mapping path; nulls last, ties broken by id in the same direction.
100
+ * Values order string < number < boolean < array < object. Strings
101
+ * compare by the database collation on PostgreSQL and by code point on
102
+ * MongoDB and DocumentDB; arrays and objects order among themselves
103
+ * only on PostgreSQL. Without it rows are ordered by id ascending.
104
+ * @param {number} [query.skip=0] - Rows to skip (integer ≥ 0)
105
+ * @param {number} query.take - Page size (integer 1–500)
106
+ * @param {string[]} [query.omit=[]] - Top-level mapping keys to leave out of
107
+ * the returned rows; such projected rows must not be written back
108
+ * @returns {Promise<{mappings: Array<Object>, total: number}>} The page, and
109
+ * the number of rows matching `where` (counted by a separate command on
110
+ * DocumentDB, so not from the page's snapshot)
111
+ */
112
+ async queryMappings(integrationId, query) {
113
+ throw new Error(
114
+ 'queryMappings is not supported by this database adapter yet'
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Delete a specific mapping
120
+ *
121
+ * @param {string|number} integrationId - The integration ID
122
+ * @param {string} sourceId - The source ID
123
+ * @returns {Promise<Object>} Deletion result
124
+ * @abstract
125
+ */
126
+ async deleteMapping(integrationId, sourceId) {
127
+ throw new Error('Method deleteMapping must be implemented by subclass');
128
+ }
129
+
130
+ /**
131
+ * Delete all mappings for an integration
132
+ *
133
+ * @param {string|number} integrationId - The integration ID
134
+ * @returns {Promise<Object>} Deletion result
135
+ * @abstract
136
+ */
137
+ async deleteMappingsByIntegration(integrationId) {
138
+ throw new Error(
139
+ 'Method deleteMappingsByIntegration must be implemented by subclass'
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Count mappings grouped by integration id, for a bounded set of ids.
145
+ * Adapters must drain the full grouped result (a deployment can have more
146
+ * than one first-batch of distinct integration ids).
147
+ *
148
+ * @returns {Promise<Map<string, number>>} Map of integrationId (string) → count
149
+ * @abstract
150
+ */
151
+ async countByIntegrationIds(ids) {
152
+ throw new Error(
153
+ 'Method countByIntegrationIds must be implemented by subclass'
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Find mapping by ID
159
+ *
160
+ * @param {string|number} id - The mapping ID
161
+ * @returns {Promise<Object|null>} The mapping object or null
162
+ * @abstract
163
+ */
164
+ async findMappingById(id) {
165
+ throw new Error(
166
+ 'Method findMappingById must be implemented by subclass'
167
+ );
168
+ }
169
+
170
+ /**
171
+ * Update a mapping by ID
172
+ *
173
+ * @param {string|number} id - The mapping ID
174
+ * @param {Object} updates - Fields to update
175
+ * @returns {Promise<Object>} Updated mapping object
176
+ * @abstract
177
+ */
178
+ async updateMapping(id, updates) {
179
+ throw new Error('Method updateMapping must be implemented by subclass');
180
+ }
181
+ }
182
+
183
+ module.exports = { IntegrationMappingRepositoryInterface };