@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,112 @@
1
+ /**
2
+ * Shared helpers for ProcessRepository.applyProcessUpdate() validation.
3
+ *
4
+ * These utilities are backend-agnostic: they enforce invariants on the
5
+ * `ProcessUpdateOps` shape BEFORE each adapter emits any SQL or database
6
+ * command. Keeping validation here means any bug we fix (e.g. tighter
7
+ * path regex, size cap) fixes all three adapters in one place.
8
+ *
9
+ * Imported by the Postgres, MongoDB, and DocumentDB adapters.
10
+ */
11
+
12
+ /**
13
+ * Allowed dot-path shape. Root must be `context` or `results`, and each
14
+ * segment after the first must be a JS-identifier-style token. Numeric
15
+ * segments (array indices) and bracket syntax are intentionally
16
+ * disallowed — array element mutation is exclusively handled via
17
+ * `pushSlice`, which targets a whole array at a path.
18
+ */
19
+ const PATH_REGEX = /^(context|results)(\.[a-zA-Z_][a-zA-Z0-9_]*)+$/;
20
+
21
+ /**
22
+ * Normalizes and validates a `ProcessUpdateOps` object. Returns a frozen
23
+ * copy with defaults applied and every key pre-validated. Throws synchronously
24
+ * on any shape error so adapters can fail fast before touching the DB.
25
+ *
26
+ * @param {Object} ops
27
+ * @returns {{
28
+ * increment: Record<string, number>,
29
+ * set: Record<string, unknown>,
30
+ * pushSlice: Record<string, { values: unknown[]; keepLast: number }>,
31
+ * newState: string|null,
32
+ * }}
33
+ */
34
+ function validateOps(ops) {
35
+ if (!ops || typeof ops !== 'object' || Array.isArray(ops)) {
36
+ throw new Error('applyProcessUpdate: ops must be an object');
37
+ }
38
+
39
+ const increment = ops.increment || {};
40
+ const set = ops.set || {};
41
+ const pushSlice = ops.pushSlice || {};
42
+ const newState = ops.newState ?? null;
43
+
44
+ for (const [path, delta] of Object.entries(increment)) {
45
+ assertPath(path, 'increment');
46
+ if (typeof delta !== 'number' || !Number.isFinite(delta)) {
47
+ throw new Error(
48
+ `applyProcessUpdate: increment['${path}'] must be a finite number, got ${typeof delta}`
49
+ );
50
+ }
51
+ }
52
+
53
+ for (const path of Object.keys(set)) {
54
+ assertPath(path, 'set');
55
+ }
56
+
57
+ for (const [path, spec] of Object.entries(pushSlice)) {
58
+ assertPath(path, 'pushSlice');
59
+ if (
60
+ !spec ||
61
+ typeof spec !== 'object' ||
62
+ !Array.isArray(spec.values) ||
63
+ typeof spec.keepLast !== 'number' ||
64
+ !Number.isInteger(spec.keepLast) ||
65
+ spec.keepLast <= 0
66
+ ) {
67
+ throw new Error(
68
+ `applyProcessUpdate: pushSlice['${path}'] must be { values: [], keepLast: positive integer }`
69
+ );
70
+ }
71
+ }
72
+
73
+ if (newState !== null && typeof newState !== 'string') {
74
+ throw new Error('applyProcessUpdate: newState must be a string');
75
+ }
76
+
77
+ const hasAnyOp =
78
+ Object.keys(increment).length > 0 ||
79
+ Object.keys(set).length > 0 ||
80
+ Object.keys(pushSlice).length > 0 ||
81
+ newState !== null;
82
+ if (!hasAnyOp) {
83
+ throw new Error(
84
+ 'applyProcessUpdate: at least one of increment/set/pushSlice/newState must be provided'
85
+ );
86
+ }
87
+
88
+ return Object.freeze({ increment, set, pushSlice, newState });
89
+ }
90
+
91
+ function assertPath(path, opName) {
92
+ if (!PATH_REGEX.test(path)) {
93
+ throw new Error(
94
+ `applyProcessUpdate: invalid path '${path}' in ${opName} (must match ${PATH_REGEX})`
95
+ );
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Splits a validated path into `{ column, segments }`.
101
+ * `'context.pagination.pageCount'` → `{ column: 'context', segments: ['pagination', 'pageCount'] }`.
102
+ */
103
+ function splitPath(path) {
104
+ const [column, ...segments] = path.split('.');
105
+ return { column, segments };
106
+ }
107
+
108
+ module.exports = {
109
+ PATH_REGEX,
110
+ validateOps,
111
+ splitPath,
112
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Coerces an id to an integer, rejecting partially-numeric input ('12abc',
3
+ * '12.9') that parseInt would silently truncate to 12 and read the wrong record.
4
+ */
5
+ function strictIntId(id) {
6
+ const str = String(id).trim();
7
+ if (!/^-?\d+$/.test(str)) {
8
+ throw new TypeError(`Invalid ID: ${id} cannot be converted to integer`);
9
+ }
10
+ return Number.parseInt(str, 10);
11
+ }
12
+
13
+ module.exports = { strictIntId };
@@ -0,0 +1,81 @@
1
+ const { IntegrationBase } = require('../../integration-base');
2
+
3
+ class ConfigCapturingModule {
4
+ static definition = {
5
+ getName: () => 'config-capturing-module'
6
+ };
7
+ }
8
+
9
+ class ConfigCapturingIntegration extends IntegrationBase {
10
+ static Definition = {
11
+ name: 'config-capturing',
12
+ version: '1.0.0',
13
+ modules: {
14
+ primary: ConfigCapturingModule
15
+ },
16
+ display: {
17
+ label: 'Config Capturing Integration',
18
+ description: 'Test double for capturing config state during updates',
19
+ detailsUrl: 'https://example.com',
20
+ icon: 'test-icon'
21
+ }
22
+ };
23
+
24
+ static _capturedOnUpdateState = null;
25
+
26
+ static resetCaptures() {
27
+ this._capturedOnUpdateState = null;
28
+ }
29
+
30
+ static getCapturedOnUpdateState() {
31
+ return this._capturedOnUpdateState;
32
+ }
33
+
34
+ constructor(params) {
35
+ super(params);
36
+ this.integrationRepository = {
37
+ updateIntegrationById: jest.fn().mockResolvedValue({}),
38
+ findIntegrationById: jest.fn().mockResolvedValue({}),
39
+ };
40
+ this.updateIntegrationStatus = {
41
+ execute: jest.fn().mockResolvedValue({})
42
+ };
43
+ this.updateIntegrationMessages = {
44
+ execute: jest.fn().mockResolvedValue({})
45
+ };
46
+ }
47
+
48
+ async initialize() {
49
+ this.registerEventHandlers();
50
+ }
51
+
52
+ async onUpdate(params) {
53
+ ConfigCapturingIntegration._capturedOnUpdateState = {
54
+ thisConfig: JSON.parse(JSON.stringify(this.config)),
55
+ paramsConfig: params.config
56
+ };
57
+
58
+ this.config = this._deepMerge(this.config, params.config);
59
+ }
60
+
61
+ _deepMerge(target, source) {
62
+ const result = { ...target };
63
+ for (const key of Object.keys(source)) {
64
+ if (
65
+ source[key] !== null &&
66
+ typeof source[key] === 'object' &&
67
+ !Array.isArray(source[key]) &&
68
+ target[key] !== null &&
69
+ typeof target[key] === 'object' &&
70
+ !Array.isArray(target[key])
71
+ ) {
72
+ result[key] = this._deepMerge(target[key], source[key]);
73
+ } else {
74
+ result[key] = source[key];
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ }
80
+
81
+ module.exports = { ConfigCapturingIntegration };
@@ -0,0 +1,113 @@
1
+ const { IntegrationBase } = require('../../integration-base');
2
+
3
+ class DummyModule {
4
+ static definition = {
5
+ getName: () => 'dummy'
6
+ };
7
+ }
8
+
9
+ class DummyIntegration extends IntegrationBase {
10
+ static Definition = {
11
+ name: 'dummy',
12
+ version: '1.0.0',
13
+ modules: {
14
+ dummy: DummyModule
15
+ },
16
+ display: {
17
+ label: 'Dummy Integration',
18
+ description: 'A dummy integration for testing',
19
+ detailsUrl: 'https://example.com',
20
+ icon: 'dummy-icon'
21
+ }
22
+ };
23
+
24
+ static getOptionDetails() {
25
+ return {
26
+ name: this.Definition.name,
27
+ version: this.Definition.version,
28
+ display: this.Definition.display
29
+ };
30
+ }
31
+
32
+ constructor(params) {
33
+ super(params);
34
+ this.sendSpy = jest.fn();
35
+ this.testAuthSpy = jest.fn();
36
+ this.eventCallHistory = [];
37
+ this.events = {};
38
+
39
+ this.integrationRepository = {
40
+ updateIntegrationById: jest.fn().mockResolvedValue({}),
41
+ findIntegrationById: jest.fn().mockResolvedValue({}),
42
+ };
43
+
44
+ this.updateIntegrationStatus = {
45
+ execute: jest.fn().mockResolvedValue({})
46
+ };
47
+
48
+ this.updateIntegrationMessages = {
49
+ execute: jest.fn().mockResolvedValue({})
50
+ };
51
+ }
52
+
53
+ async loadDynamicUserActions() {
54
+ return {};
55
+ }
56
+
57
+ async send(event, data) {
58
+ this.sendSpy(event, data);
59
+ this.eventCallHistory.push({ event, data, timestamp: Date.now() });
60
+ if (event === 'ON_UPDATE') {
61
+ await this.onUpdate(data);
62
+ }
63
+ if (event === 'ON_DELETE') {
64
+ await this.onDelete(data);
65
+ }
66
+ return { event, data };
67
+ }
68
+
69
+ async initialize() {
70
+ return;
71
+ }
72
+
73
+ async testAuth() {
74
+ this.testAuthSpy();
75
+ }
76
+
77
+ async onCreate({ integrationId }) {
78
+ return;
79
+ }
80
+
81
+ async onUpdate(params) {
82
+ this.config = this._deepMerge(this.config, params.config);
83
+ }
84
+
85
+ _deepMerge(target, source) {
86
+ const result = { ...target };
87
+ for (const key of Object.keys(source)) {
88
+ if (
89
+ source[key] !== null &&
90
+ typeof source[key] === 'object' &&
91
+ !Array.isArray(source[key]) &&
92
+ target[key] !== null &&
93
+ typeof target[key] === 'object' &&
94
+ !Array.isArray(target[key])
95
+ ) {
96
+ result[key] = this._deepMerge(target[key], source[key]);
97
+ } else {
98
+ result[key] = source[key];
99
+ }
100
+ }
101
+ return result;
102
+ }
103
+
104
+ async onDelete(params) {
105
+ return;
106
+ }
107
+
108
+ getConfig() {
109
+ return this.config || {};
110
+ }
111
+ }
112
+
113
+ module.exports = { DummyIntegration };
@@ -0,0 +1,146 @@
1
+ const { v4: uuid } = require('uuid');
2
+ const { validateConfigPatch } = require('../../repositories/config-patch-shared');
3
+
4
+ class TestIntegrationRepository {
5
+ constructor() {
6
+ this.store = new Map();
7
+ this.operationHistory = [];
8
+ }
9
+
10
+ async createIntegration(entities, userId, config) {
11
+ const id = uuid();
12
+ const record = {
13
+ id,
14
+ _id: id,
15
+ entitiesIds: entities,
16
+ userId: userId,
17
+ config,
18
+ version: '0.0.0',
19
+ status: 'IN_CREATION',
20
+ messages: {},
21
+ createdAt: new Date(),
22
+ };
23
+ this.store.set(id, record);
24
+ this.operationHistory.push({ operation: 'create', id, userId, config });
25
+ return record;
26
+ }
27
+
28
+ async findIntegrationById(id) {
29
+ const rec = this.store.get(id);
30
+ this.operationHistory.push({ operation: 'findById', id, found: !!rec });
31
+ if (!rec) return null;
32
+ return rec;
33
+ }
34
+
35
+ async findIntegrationsByUserId(userId) {
36
+ const results = Array.from(this.store.values()).filter(r => r.userId === userId);
37
+ this.operationHistory.push({ operation: 'findByUserId', userId, count: results.length });
38
+ return results;
39
+ }
40
+
41
+ async findIntegrations(filter = {}) {
42
+ const { type, status } = filter;
43
+ const results = Array.from(this.store.values()).filter((r) => {
44
+ if (type && r.config?.type !== type) return false;
45
+ if (status && r.status !== status) return false;
46
+ return true;
47
+ });
48
+ this.operationHistory.push({ operation: 'findAll', count: results.length });
49
+ return results;
50
+ }
51
+
52
+ async findIntegrationByUserId(userId) {
53
+ const record = Array.from(this.store.values()).find((r) => r.userId === userId);
54
+ this.operationHistory.push({
55
+ operation: 'findSingleByUserId',
56
+ userId,
57
+ found: !!record,
58
+ });
59
+ return record || null;
60
+ }
61
+
62
+ async findIntegrationsByEntityId(entityId) {
63
+ const target = String(entityId);
64
+ const results = Array.from(this.store.values()).filter((r) =>
65
+ (r.entitiesIds || []).map(String).includes(target)
66
+ );
67
+ this.operationHistory.push({
68
+ operation: 'findByEntityId',
69
+ entityId: target,
70
+ count: results.length,
71
+ });
72
+ return results;
73
+ }
74
+
75
+ async updateIntegrationMessages(id, type, title, body, timestamp) {
76
+ const rec = this.store.get(id);
77
+ if (!rec) {
78
+ this.operationHistory.push({ operation: 'updateMessages', id, success: false });
79
+ return false;
80
+ }
81
+ if (!rec.messages[type]) rec.messages[type] = [];
82
+ rec.messages[type].push({ title, message: body, timestamp });
83
+ this.operationHistory.push({ operation: 'updateMessages', id, type, success: true });
84
+ return true;
85
+ }
86
+
87
+ async updateIntegrationConfig(id, config) {
88
+ if (config === null || config === undefined) {
89
+ throw new Error('Config parameter is required');
90
+ }
91
+ const rec = this.store.get(id);
92
+ if (!rec) {
93
+ this.operationHistory.push({ operation: 'updateConfig', id, success: false });
94
+ throw new Error(`Integration with id ${id} not found`);
95
+ }
96
+ rec.config = config;
97
+ this.operationHistory.push({ operation: 'updateConfig', id, success: true });
98
+ return rec;
99
+ }
100
+
101
+ async patchIntegrationConfig(id, patch) {
102
+ validateConfigPatch(patch);
103
+ if (!this.store.has(id)) {
104
+ this.operationHistory.push({ operation: 'patchConfig', id, success: false });
105
+ throw new Error(`Integration with id ${id} not found`);
106
+ }
107
+ // Simulates the DB round trip real adapters make: the merge reads
108
+ // the record fresh at write time rather than off a snapshot taken
109
+ // before the await, so concurrent patches to the same id can't lose
110
+ // each other's keys — same guarantee as `config || $1::jsonb` in
111
+ // postgres or a mongo/documentdb findAndModify.
112
+ await Promise.resolve();
113
+ const rec = this.store.get(id);
114
+ rec.config = { ...rec.config, ...patch };
115
+ this.operationHistory.push({ operation: 'patchConfig', id, success: true });
116
+ return rec;
117
+ }
118
+
119
+ async deleteIntegrationById(id) {
120
+ const existed = this.store.has(id);
121
+ const result = this.store.delete(id);
122
+ this.operationHistory.push({ operation: 'delete', id, existed, success: result });
123
+ return result;
124
+ }
125
+
126
+ async updateIntegrationStatus(id, status) {
127
+ const rec = this.store.get(id);
128
+ if (rec) {
129
+ rec.status = status;
130
+ this.operationHistory.push({ operation: 'updateStatus', id, status, success: true });
131
+ } else {
132
+ this.operationHistory.push({ operation: 'updateStatus', id, status, success: false });
133
+ }
134
+ return !!rec;
135
+ }
136
+
137
+ getOperationHistory() {
138
+ return [...this.operationHistory];
139
+ }
140
+
141
+ clearHistory() {
142
+ this.operationHistory = [];
143
+ }
144
+ }
145
+
146
+ module.exports = { TestIntegrationRepository };
@@ -0,0 +1,92 @@
1
+ const {
2
+ assertEntityIdsInput,
3
+ integrationNotFound,
4
+ loadIntegration,
5
+ } = require('../utils/assert-entity-ids-input');
6
+
7
+ /**
8
+ * Attaches entities to an existing integration, e.g. when a user grants
9
+ * access to more accounts after the integration was created. Every entity
10
+ * must exist, belong to the integration's user, and be of a module the
11
+ * integration already holds (so hydration can load it). Entities that are
12
+ * already attached are left as they are.
13
+ * @class AddEntitiesToIntegration
14
+ */
15
+ class AddEntitiesToIntegration {
16
+ /**
17
+ * @param {Object} params
18
+ * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository
19
+ * @param {import('../../modules/repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
20
+ */
21
+ constructor({ integrationRepository, moduleRepository }) {
22
+ this.integrationRepository = integrationRepository;
23
+ this.moduleRepository = moduleRepository;
24
+ }
25
+
26
+ /**
27
+ * @param {string} integrationId
28
+ * @param {string[]} entityIds
29
+ * @returns {Promise<Object>} The updated integration record.
30
+ */
31
+ async execute(integrationId, entityIds) {
32
+ assertEntityIdsInput(integrationId, entityIds);
33
+
34
+ const integration = await loadIntegration(
35
+ this.integrationRepository,
36
+ integrationId
37
+ );
38
+
39
+ const entities = await this.moduleRepository.findEntitiesByIds(
40
+ entityIds
41
+ );
42
+ const foundIds = new Set(entities.map((e) => String(e.id)));
43
+ const missing = entityIds.filter((id) => !foundIds.has(String(id)));
44
+ if (missing.length > 0) {
45
+ const error = new Error(
46
+ `Entities not found: ${missing.join(', ')}`
47
+ );
48
+ error.code = 'ENTITIES_NOT_FOUND';
49
+ throw error;
50
+ }
51
+
52
+ const foreign = entities.filter(
53
+ (e) => String(e.userId) !== String(integration.userId)
54
+ );
55
+ if (foreign.length > 0) {
56
+ const error = new Error(
57
+ `Entities do not belong to the integration's user: ${foreign
58
+ .map((e) => e.id)
59
+ .join(', ')}`
60
+ );
61
+ error.code = 'ENTITY_USER_MISMATCH';
62
+ throw error;
63
+ }
64
+
65
+ const attached = await this.moduleRepository.findEntitiesByIds(
66
+ integration.entitiesIds ?? []
67
+ );
68
+ const heldModules = new Set(attached.map((e) => e.moduleName));
69
+ const otherModules = entities.filter(
70
+ (e) => !heldModules.has(e.moduleName)
71
+ );
72
+ if (otherModules.length > 0) {
73
+ const error = new Error(
74
+ `Entities belong to a module the integration does not hold: ${otherModules
75
+ .map((e) => `${e.id} (${e.moduleName})`)
76
+ .join(', ')}`
77
+ );
78
+ error.code = 'ENTITY_MODULE_MISMATCH';
79
+ throw error;
80
+ }
81
+
82
+ const updated =
83
+ await this.integrationRepository.addEntitiesToIntegration(
84
+ integrationId,
85
+ entityIds
86
+ );
87
+ if (!updated) throw integrationNotFound(integrationId);
88
+ return updated;
89
+ }
90
+ }
91
+
92
+ module.exports = { AddEntitiesToIntegration };