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

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 +238 -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 +92 -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
package/CLAUDE.md ADDED
@@ -0,0 +1,830 @@
1
+ # CLAUDE.md - Frigg Framework Core Package
2
+
3
+ This file provides guidance to Claude Code when working with the Frigg Framework's core package (`@friggframework/core`).
4
+
5
+ ## Critical Context (Read First)
6
+
7
+ - **Package Purpose**: Core framework functionality for building enterprise serverless integrations
8
+ - **Main Architecture**: Hexagonal/DDD architecture with clear separation of adapters, use cases, and repositories
9
+ - **Key Technologies**: Node.js, Express, AWS Lambda, MongoDB/PostgreSQL (Prisma), AWS KMS encryption
10
+ - **Core Value**: Provides building blocks for integration developers - they extend IntegrationBase and use framework services
11
+ - **Security Model**: Field-level encryption, OAuth2 flows, signature validation, VPC deployment
12
+ - **DO NOT**: Bypass architectural layers, skip encryption for sensitive data, expose internal errors to users
13
+
14
+ ## Table of Contents
15
+
16
+ 1. [Package Overview](#package-overview)
17
+ 2. [Architecture Principles](#architecture-principles)
18
+ 3. [Essential Commands](#essential-commands)
19
+ 4. [Directory Structure](#directory-structure)
20
+ 5. [Core Components](#core-components)
21
+ 6. [Development Workflow](#development-workflow)
22
+ 7. [Testing Strategy](#testing-strategy)
23
+ 8. [Anti-Patterns](#anti-patterns)
24
+
25
+ ## Package Overview
26
+
27
+ `@friggframework/core` is the foundational package of the Frigg Framework, providing:
28
+
29
+ - **IntegrationBase**: Base class all integrations extend
30
+ - **Database Layer**: Multi-database support (MongoDB, DocumentDB, PostgreSQL) with Prisma ORM
31
+ - **Encryption**: Transparent field-level encryption with AWS KMS or AES
32
+ - **User Management**: Individual and organizational user support
33
+ - **Module System**: API module loading and credential management
34
+ - **Lambda Runtime**: Handler factory, worker base class, timeout management
35
+ - **Error Handling**: Standardized error types with proper HTTP status codes
36
+ - **Event System**: Integration lifecycle events and user actions
37
+
38
+ ## Architecture Principles
39
+
40
+ ### Hexagonal Architecture (Ports and Adapters)
41
+
42
+ The core package strictly follows hexagonal architecture:
43
+
44
+ ```
45
+ ┌─────────────────────────────────────────────────────────┐
46
+ │ Adapters (Inbound) │
47
+ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
48
+ │ │ HTTP/REST │ │ Lambda │ │ SQS Workers │ │
49
+ │ │ (handlers/) │ │ (core/) │ │ (queues/) │ │
50
+ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
51
+ └─────────┼─────────────────┼─────────────────┼───────────┘
52
+ │ │ │
53
+ ┌─────────▼─────────────────▼─────────────────▼───────────┐
54
+ │ Application Layer (Use Cases) │
55
+ │ ┌────────────────────────────────────────────────┐ │
56
+ │ │ CreateIntegration, UpdateIntegration, │ │
57
+ │ │ LoginUser, ProcessAttachmentJob, etc. │ │
58
+ │ └────────────────────┬───────────────────────────┘ │
59
+ └───────────────────────┼─────────────────────────────────┘
60
+ │ calls
61
+ ┌───────────────────────▼─────────────────────────────────┐
62
+ │ Domain Layer (Entities) │
63
+ │ ┌────────────────────────────────────────────────┐ │
64
+ │ │ IntegrationBase, User, Credential, Entity │ │
65
+ │ └────────────────────┬───────────────────────────┘ │
66
+ └───────────────────────┼─────────────────────────────────┘
67
+ │ persisted by
68
+ ┌───────────────────────▼─────────────────────────────────┐
69
+ │ Infrastructure Layer (Repositories) │
70
+ │ ┌────────────────────────────────────────────────┐ │
71
+ │ │ IntegrationRepository, UserRepository, │ │
72
+ │ │ CredentialRepository, ModuleRepository │ │
73
+ │ └────────────────────┬───────────────────────────┘ │
74
+ └───────────────────────┼─────────────────────────────────┘
75
+ │ accesses
76
+ ┌───────────────────────▼─────────────────────────────────┐
77
+ │ External Systems │
78
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
79
+ │ │ MongoDB │ │ Postgres │ │ AWS KMS │ │
80
+ │ └──────────┘ └──────────┘ └──────────┘ │
81
+ └─────────────────────────────────────────────────────────┘
82
+ ```
83
+
84
+ ### Golden Rules
85
+
86
+ 1. **Handlers NEVER call repositories directly** - Always go through use cases
87
+ 2. **Use cases contain business logic** - Not repositories or handlers
88
+ 3. **Repositories are pure data access** - No business logic or orchestration
89
+ 4. **Domain entities have behavior** - Not just data bags
90
+ 5. **Encryption is transparent** - Application code works with plain data
91
+
92
+ ## Essential Commands
93
+
94
+ ### Development
95
+
96
+ ```bash
97
+ # Install dependencies
98
+ npm install
99
+
100
+ # Generate Prisma clients (both MongoDB and PostgreSQL)
101
+ npm run prisma:generate
102
+
103
+ # Format and lint code
104
+ npm run lint:fix
105
+
106
+ # Run tests
107
+ npm test
108
+
109
+ # Run specific test file
110
+ npm test -- path/to/test.test.js
111
+
112
+ # Run tests for specific pattern
113
+ npm test -- --testPathPattern="encryption"
114
+ ```
115
+
116
+ ### Prisma Database Operations
117
+
118
+ ```bash
119
+ # Generate clients
120
+ npm run prisma:generate:mongo # MongoDB only
121
+ npm run prisma:generate:postgres # PostgreSQL only
122
+ npm run prisma:generate # Both databases
123
+
124
+ # Database migrations
125
+ npm run prisma:push:mongo # Push MongoDB schema
126
+ npm run prisma:migrate:postgres # Run PostgreSQL migrations
127
+ ```
128
+
129
+ ### Testing
130
+
131
+ ```bash
132
+ # All tests
133
+ npm test
134
+
135
+ # Specific test categories
136
+ npm test -- database/encryption/ # Encryption tests
137
+ npm test -- integrations/ # Integration tests
138
+ npm test -- handlers/ # Handler tests
139
+ ```
140
+
141
+ ## Directory Structure
142
+
143
+ ```
144
+ packages/core/
145
+ ├── application/ # Application-level commands and initialization
146
+ │ └── commands/ # Command pattern implementations
147
+ ├── assertions/ # Validation and assertion utilities
148
+ ├── associations/ # Entity association management
149
+ ├── core/ # Runtime system (Lambda, Workers, Delegates)
150
+ │ └── CLAUDE.md # Detailed core runtime documentation
151
+ ├── credential/ # Credential management
152
+ │ ├── repositories/ # Credential data access
153
+ │ └── use-cases/ # Credential business logic
154
+ ├── database/ # Database layer and encryption
155
+ │ ├── encryption/ # Field-level encryption system
156
+ │ │ └── README.md # Comprehensive encryption documentation
157
+ │ ├── models/ # Mongoose models
158
+ │ ├── repositories/ # Database repositories
159
+ │ └── use-cases/ # Database health and management
160
+ ├── encrypt/ # Cryptor adapter for AWS KMS and AES
161
+ ├── errors/ # Error type definitions
162
+ ├── handlers/ # HTTP/Lambda request handlers
163
+ │ └── routers/ # Express routers
164
+ ├── integrations/ # Integration domain and lifecycle
165
+ │ ├── integration-base.js # Base class for all integrations
166
+ │ ├── repositories/ # Integration data access
167
+ │ ├── tests/ # Integration tests
168
+ │ └── use-cases/ # Integration business logic
169
+ ├── lambda/ # AWS Lambda utilities
170
+ ├── logs/ # Logging system
171
+ ├── modules/ # API module system
172
+ │ ├── requester/ # HTTP client implementations
173
+ │ └── repositories/ # Module data access
174
+ ├── prisma-mongodb/ # MongoDB Prisma schema
175
+ ├── prisma-postgresql/ # PostgreSQL Prisma schema
176
+ ├── queues/ # SQS job queue management
177
+ ├── syncs/ # Data synchronization
178
+ ├── token/ # Token management
179
+ │ └── repositories/ # Token data access
180
+ ├── types/ # TypeScript type definitions
181
+ ├── user/ # User management
182
+ │ ├── repositories/ # User data access
183
+ │ └── use-cases/ # User business logic
184
+ ├── utils/ # Utility functions
185
+ ├── websocket/ # WebSocket connection management
186
+ │ └── repositories/ # WebSocket data access
187
+ ├── index.js # Main export file
188
+ ├── package.json # Package configuration
189
+ └── README.md # Package documentation
190
+ ```
191
+
192
+ ## Core Components
193
+
194
+ ### 1. Integration System (`/integrations`)
195
+
196
+ **Purpose**: Foundation for building integrations between external systems.
197
+
198
+ **Key Files**:
199
+ - `integration-base.js` - Base class all integrations extend
200
+ - `integration.js` - Integration domain aggregate using Proxy pattern
201
+ - `options.js` - Integration configuration and options
202
+
203
+ **Use Cases**:
204
+ - `create-integration.js` - Create new integration instance
205
+ - `update-integration.js` - Update integration configuration
206
+ - `delete-integration-for-user.js` - Remove integration
207
+ - `get-integration-instance.js` - Load integration with modules
208
+ - `load-integration-context.js` - Full integration context loading
209
+ - `add-entities-to-integration.js` / `remove-entities-from-integration.js` - Attach or detach entities on an existing integration
210
+
211
+ **Repositories**:
212
+ - `integration-repository-factory.js` - Creates database-specific repositories
213
+ - `integration-repository-mongo.js` - MongoDB implementation
214
+ - `integration-repository-postgres.js` - PostgreSQL implementation
215
+ - `addEntitiesToIntegration(integrationId, entityIds)` /
216
+ `removeEntitiesFromIntegration(integrationId, entityIds)` change the entity
217
+ set of an existing integration (all three adapters; `null` for an unknown
218
+ integration). Reach them through the `addEntitiesToIntegration` /
219
+ `removeEntitiesFromIntegration` commands. The add use case
220
+ (`add-entities-to-integration.js`) rejects unknown entities
221
+ (`ENTITIES_NOT_FOUND`, 404), entities of another user
222
+ (`ENTITY_USER_MISMATCH`, 403) and entities of a module the integration does
223
+ not already hold (`ENTITY_MODULE_MISMATCH`, 400). The remove use case
224
+ (`remove-entities-from-integration.js`) refuses to leave a module with no
225
+ entity (`LAST_ENTITY_OF_MODULE`, 409). Re-adding an attached entity or
226
+ removing a detached one changes nothing; a detached entity is not deleted.
227
+ `integration-repository-entities-parity.test.js` runs every adapter against
228
+ real databases when `INTEGRATION_ENTITIES_PARITY_MONGO_URL` /
229
+ `INTEGRATION_ENTITIES_PARITY_POSTGRES_URL` are set. The DocumentDB adapter
230
+ writes only `Integration.entityIds` (as its `createIntegration` does), not
231
+ `Entity.integrationIds`.
232
+ **One credential, many accounts**: when one credential covers several
233
+ provider accounts, give each account its own entity (externalId = account
234
+ id) and attach them all to one integration; webhooks then route with
235
+ `findIntegrationByEntityExternalId`. Traps:
236
+ (1) `IntegrationBase` keys modules by module name, so `this.<moduleKey>`
237
+ holds only one entity's module, and on the queue/webhook path
238
+ (`get-integration-instance.js`) `this.entities` holds entity **ids**, not
239
+ records. For per-account work (a webhook for one account, teardown of every
240
+ account in `onDelete`), load
241
+ `this.commands.loadIntegrationContextById(this.id)` and use
242
+ `context.modules`: one module per entity, each with its record on
243
+ `module.entity` (pick by `module.entity.externalId`).
244
+ (2) `findIntegrationByEntityExternalId` counts every entity with that
245
+ externalId, attached or not, across users: reuse an existing entity
246
+ (`findEntity`) when an account comes back, or delete it when it is revoked,
247
+ never create a second one.
248
+ (3) `createIntegration` reuses an existing integration of the same user
249
+ and type whose entities include all requested ones (an exact match wins),
250
+ so a reconnect that sends the original entities finds the integration that
251
+ now holds more.
252
+ (4) Every module's delegate is the integration: an auth failure on any
253
+ account marks the shared credential invalid and the integration ERROR.
254
+ - `integration-mapping-repository-*.js` - Mapping data persistence.
255
+ `queryMappings(integrationId, { where, orderBy, skip, take, omit })`
256
+ returns `{ mappings, total }`: one filtered, ordered page of an
257
+ integration's mappings, for callers that cannot load every row through
258
+ `findMappingsByIntegration`. `where` is an array of ANDed conditions
259
+ (at most 20); an entry may be `{ anyOf: [...] }`, ORed, one level deep.
260
+ Conditions are `{ path: 'mapping.<segment>...', op: 'exists' | 'notExists' }`
261
+ (JSON null counts as absent), `{ path: 'mapping.…', op: 'in', value: string[] }`
262
+ (1–500 strings) and `{ path: 'sourceId', op: 'notStartsWith', value }`
263
+ (a NULL sourceId matches). `orderBy` is `{ path: 'mapping.…', direction:
264
+ 'asc' | 'desc' }`, nulls last, ties broken by id in the same direction;
265
+ without it rows come in id order. `take` is 1–500. `omit` lists top-level
266
+ mapping keys to leave out of the rows; never write such rows back. Path
267
+ segments must match `^[A-Za-z_][A-Za-z0-9_]*$`. The PostgreSQL, MongoDB and
268
+ DocumentDB adapters give the same pages and totals;
269
+ `integration-mapping-repository-query-parity.test.js` checks that against
270
+ real databases when `QUERY_MAPPINGS_PARITY_MONGO_URL` /
271
+ `QUERY_MAPPINGS_PARITY_POSTGRES_URL` are set. Two orderings still differ:
272
+ strings compare by the database collation on PostgreSQL and by code point
273
+ on MongoDB and DocumentDB, and arrays or objects at the sort path order
274
+ among themselves only on PostgreSQL (the other adapters fall back to id).
275
+ The legacy `IntegrationMappingRepository` inherits the port's
276
+ "not supported by this database adapter yet" error. Every adapter refuses
277
+ to run while field-level encryption still encrypts
278
+ `IntegrationMapping.mapping` on write (see `database/encryption/README.md`),
279
+ and returns rows decrypted like `findMappingsByIntegration`. PostgreSQL and
280
+ MongoDB decrypt through `decryptQueriedMappings`
281
+ (`database/encryption/integration-mapping-encryption.js`), a no-op while
282
+ encryption is off or lists no `IntegrationMapping` field besides `mapping`.
283
+ Validation lives in `integration-mapping-query.js`; a new operator is one
284
+ entry in its `OPERATORS` table, one in the Postgres adapter's
285
+ `CONDITION_SQL` and one in `CONDITION_EXPRESSIONS` in
286
+ `integration-mapping-query-pipeline.js`, the aggregation stages both
287
+ MongoDB-protocol adapters share. Those stages may only use what Amazon
288
+ DocumentDB 4.0 and 5.0 support (no `$facet`, `$getField`, `$set` or
289
+ `$unset`).
290
+ **Cost**: PostgreSQL answers in one SQL statement, which reads every row
291
+ of the integration and evaluates the JSON paths per row, because no JSON
292
+ index exists. With ~4 KB mappings on PostgreSQL 16 that is about 0.3 s per
293
+ call at 10⁴ rows per integration and 2.5–3 s at 10⁵. MongoDB answers in one
294
+ aggregate: the `integrationId` index narrows `$match` to the integration,
295
+ the `$expr` is then evaluated per document, and `$facet` returns the page
296
+ with its `$count`, so a page (after `omit`) must fit the 16 MB document
297
+ limit. DocumentDB has no `$facet`: the page and the count are two
298
+ concurrent aggregates, so the total does not come from the same snapshot
299
+ as the page. Both sort in memory on a computed key (`allowDiskUse`). With
300
+ ~4 KB mappings on MongoDB 7 that is about 0.05–0.1 s per call at 10⁴ rows
301
+ per integration and 0.5–0.7 s at 10⁵ (the DocumentDB pipeline, run on
302
+ MongoDB, 0.6–0.8 s). On every adapter a deep offset or an empty page past
303
+ the end costs about the same as the first page.
304
+ - `process-repository-*.js` - Process (long-running job) persistence.
305
+ Implements `applyProcessUpdate(processId, ops)` — a race-safe alternative
306
+ to `update(id, patch)` that routes increments, sets, and bounded-array
307
+ pushes through each backend's native atomic primitive (PostgreSQL
308
+ `jsonb_set` / MongoDB `$inc`/`$set`/`$push`). Use this method any time
309
+ multiple queue workers may concurrently mutate the same Process row
310
+ (counters, flags, error history). The legacy `update(id, patch)`
311
+ remains available but is clobber-prone under concurrency.
312
+
313
+ **Integration developers extend IntegrationBase**:
314
+
315
+ ```javascript
316
+ const { IntegrationBase } = require('@friggframework/core');
317
+
318
+ class MyIntegration extends IntegrationBase {
319
+ static Definition = {
320
+ name: 'my-integration',
321
+ version: '1.0.0',
322
+ modules: {
323
+ serviceA: 'service-a',
324
+ serviceB: 'service-b'
325
+ }
326
+ };
327
+
328
+ async onCreate({ integrationId }) {
329
+ // Setup logic
330
+ await super.onCreate({ integrationId });
331
+ }
332
+ }
333
+ ```
334
+
335
+ ### 2. Database Layer (`/database`)
336
+
337
+ **Purpose**: Multi-database support with transparent encryption.
338
+
339
+ **Key Components**:
340
+ - `prisma.js` - Prisma client initialization with encryption extension
341
+ - `mongo.js` - Mongoose connection management (legacy)
342
+ - `models/` - Mongoose model definitions
343
+
344
+ **Encryption System** (`/database/encryption`):
345
+ - **Transparent encryption**: Application code never sees encrypted data
346
+ - **Database-agnostic**: Works with MongoDB and PostgreSQL
347
+ - **AWS KMS or AES**: Production KMS, development AES
348
+ - **Configurable**: Via environment variables and app definition
349
+
350
+ **See**: `database/encryption/README.md` for comprehensive documentation
351
+
352
+ **Repositories**:
353
+ - `health-check-repository.js` - Database health monitoring
354
+ - `token-repository.js` - Authentication tokens
355
+ - `websocket-connection-repository.js` - WebSocket connections
356
+ - DocumentDB-enabled adapters mirror the MongoDB APIs but execute raw commands (`$runCommandRaw`, `$aggregateRaw`) for compatibility; encrypted models (e.g., credentials) still delegate reads to Prisma so the encryption extension can decrypt secrets transparently.
357
+
358
+ **Use Cases**:
359
+ - `check-database-health-use-case.js` - Database health checks
360
+ - `test-encryption-use-case.js` - Encryption verification
361
+
362
+ ### 3. User Management (`/user`)
363
+
364
+ **Purpose**: Individual and organizational user authentication.
365
+
366
+ **User Types**:
367
+ - **Individual Users**: Personal accounts with email/password
368
+ - **Organization Users**: Business accounts with organization-level access
369
+ - **Hybrid**: Support both simultaneously
370
+
371
+ **Authentication Methods**:
372
+ - Password-based (bcrypt hashed)
373
+ - Token-based (Bearer tokens)
374
+ - App-based (external app user IDs)
375
+
376
+ **Use Cases**:
377
+ - `login-user.js` - User authentication
378
+ - `create-individual-user.js` - Create personal account
379
+ - `create-organization-user.js` - Create business account
380
+ - `get-user-from-bearer-token.js` - Token authentication
381
+
382
+ **Repositories**:
383
+ - `user-repository-factory.js` - Creates database-specific repositories
384
+ - `user-repository-mongo.js` - MongoDB implementation
385
+ - `user-repository-postgres.js` - PostgreSQL implementation
386
+
387
+ **Configuration** (in app definition):
388
+
389
+ ```javascript
390
+ {
391
+ user: {
392
+ usePassword: true, // Enable password auth
393
+ primary: 'individual', // Primary user type
394
+ individualUserRequired: true, // Require individual user
395
+ organizationUserRequired: false // Optional org user
396
+ }
397
+ }
398
+ ```
399
+
400
+ ### 4. Module System (`/modules`)
401
+
402
+ **Purpose**: API module loading, credential management, and HTTP clients.
403
+
404
+ **Key Classes**:
405
+ - `Credential` - API credentials domain entity
406
+ - `Entity` - External service entity (account, workspace, etc.)
407
+ - `Requester` - Base HTTP client class
408
+ - `OAuth2Requester` - OAuth 2.0 flow implementation
409
+ - `ApiKeyRequester` - API key authentication
410
+ - `BasicAuthRequester` - Basic authentication
411
+
412
+ **Module Factory**:
413
+ - `ModuleFactory` - Creates and configures API module instances
414
+ - Handles credential injection
415
+ - Manages module lifecycle
416
+
417
+ **Repositories**:
418
+ - `module-repository.js` - Module data access
419
+ - `credential-repository.js` - Credential persistence (encrypted)
420
+
421
+ ### 5. Core Runtime System (`/core`)
422
+
423
+ **Purpose**: Lambda-optimized runtime with handlers, workers, and delegates.
424
+
425
+ **See**: `core/CLAUDE.md` for comprehensive documentation
426
+
427
+ **Key Components**:
428
+ - `create-handler.js` - Lambda handler factory
429
+ - `Worker.js` - SQS job processing base class
430
+ - `Delegate.js` - Observer/delegation pattern
431
+ - `load-installed-modules.js` - Dynamic module loading
432
+
433
+ **Handler Pattern**:
434
+
435
+ ```javascript
436
+ const { createHandler } = require('@friggframework/core');
437
+
438
+ const handler = createHandler({
439
+ eventName: 'MyIntegration',
440
+ isUserFacingResponse: true, // Sanitize errors
441
+ shouldUseDatabase: true, // Connect to DB
442
+ method: async (event, context) => {
443
+ // Your logic here
444
+ return { statusCode: 200, body: 'Success' };
445
+ }
446
+ });
447
+ ```
448
+
449
+ **Worker Pattern**:
450
+
451
+ ```javascript
452
+ const { Worker } = require('@friggframework/core');
453
+
454
+ class MyWorker extends Worker {
455
+ _validateParams(params) {
456
+ this._verifyParamExists(params, 'requiredField');
457
+ }
458
+
459
+ async _run(params, context) {
460
+ // Process SQS message
461
+ }
462
+ }
463
+ ```
464
+
465
+ ### 6. Encryption System (`/encrypt`)
466
+
467
+ **Purpose**: Cryptor adapter for AWS KMS and AES encryption.
468
+
469
+ **Key Class**: `Cryptor.js`
470
+ - Envelope encryption pattern
471
+ - AWS KMS integration
472
+ - AES-256-GCM fallback
473
+ - Key rotation support
474
+
475
+ **Usage**:
476
+
477
+ ```javascript
478
+ const { Cryptor } = require('@friggframework/core');
479
+
480
+ const cryptor = new Cryptor({
481
+ shouldUseAws: process.env.KMS_KEY_ARN ? true : false
482
+ });
483
+
484
+ const encrypted = await cryptor.encrypt('sensitive-data');
485
+ const decrypted = await cryptor.decrypt(encrypted);
486
+ ```
487
+
488
+ ### 7. Handlers & Routers (`/handlers`)
489
+
490
+ **Purpose**: HTTP/Lambda request handling and routing.
491
+
492
+ **Key Routers**:
493
+ - `integration-router.js` - Integration CRUD operations
494
+ - `auth.js` - Authentication endpoints
495
+ - `health.js` - Health check endpoints with encryption verification
496
+
497
+ **Handler Types**:
498
+ - **User-facing**: Sanitize errors, friendly responses
499
+ - **Server-to-server**: Full error details for debugging
500
+ - **Background workers**: SQS message processing
501
+
502
+ **Event Dispatcher**:
503
+ - `integration-event-dispatcher.js` - Routes events to integration handlers
504
+ - Supports lifecycle events and user actions
505
+
506
+ **Queue handler delivery**: a handler dispatched from the integration queue
507
+ (any `this.events` entry reached through SQS, including `ON_WEBHOOK` and
508
+ scheduled jobs) receives `{ data, context, delivery }`. HTTP-dispatched
509
+ handlers (`{ req, res, next }`) and `this.on` events do not.
510
+
511
+ | Field | Type | Value |
512
+ |---|---|---|
513
+ | `delivery.receiveCount` | `number \| undefined` | SQS `ApproximateReceiveCount` of this delivery |
514
+ | `delivery.maxReceiveCount` | `number \| undefined` | Receives allowed before SQS moves the message to the DLQ (`FRIGG_QUEUE_MAX_RECEIVE_COUNT`) |
515
+ | `delivery.isLastAttempt` | `boolean` | `true` only when both counts are known and `receiveCount >= maxReceiveCount` |
516
+
517
+ `isLastAttempt` is `false` when either count is unknown: a local or non-SQS
518
+ invocation, or a queue whose redrive policy the stack does not own
519
+ (`ownership.queue: 'external'`). The value is information only: core still
520
+ rethrows retryable errors and discards halt errors (4xx except 408/429).
521
+
522
+ Use it to end a run or count lost work on the final try: when a retryable
523
+ error (429, 5xx, network) is about to be rethrown and `isLastAttempt` is
524
+ `true`, the message goes to the DLQ next, so mark the run failed or count the
525
+ message's records as failed before rethrowing.
526
+
527
+ ```javascript
528
+ async processBatch({ data, delivery }) {
529
+ try {
530
+ await this.syncPage(data);
531
+ } catch (error) {
532
+ if (delivery?.isLastAttempt) await this.failRun(data.processId, error);
533
+ throw error;
534
+ }
535
+ }
536
+ ```
537
+
538
+ The single source of the max receive count is
539
+ `INTEGRATION_QUEUE_MAX_RECEIVE_COUNT` in `queues/queue-delivery.js`. The
540
+ devtools integration builder uses it for the queue's `RedrivePolicy` and sets
541
+ it as `FRIGG_QUEUE_MAX_RECEIVE_COUNT` on the queue worker function.
542
+
543
+ ### 8. Error Handling (`/errors`)
544
+
545
+ **Purpose**: Standardized error types with proper HTTP semantics.
546
+
547
+ **Error Types**:
548
+ - `BaseError` - Base error class
549
+ - `FetchError` - HTTP request failures
550
+ - `HaltError` - Stop processing without retry
551
+ - `RequiredPropertyError` - Missing required parameters
552
+ - `ParameterTypeError` - Invalid parameter type
553
+
554
+ **Usage**:
555
+
556
+ ```javascript
557
+ const { RequiredPropertyError } = require('@friggframework/core');
558
+
559
+ if (!userId) {
560
+ throw new RequiredPropertyError('userId is required');
561
+ }
562
+ ```
563
+
564
+ ### 9. Logging System (`/logs`)
565
+
566
+ **Purpose**: One redacted JSON record per line to stdout (ADR-048). See `docs/guides/LOGGING.md`.
567
+
568
+ **Rules**:
569
+ - `logs/` is a leaf: it requires only Node built-ins and sibling `logs/` files.
570
+ - Integrations and API modules use `this.logger`; core uses one `getLogger('frigg.<area>')` per module.
571
+ - Fixed message text; ids and counts go into fields. `frigg.*` records at WARN and above need an `eventName` (`<logger name>.<action>`); tests fail without it.
572
+ - Log an error one time, at the boundary (`createHandler`, the express middleware, `Worker.run`, the DLQ processor). Inner code throws with `cause`.
573
+ - Use `statusCode`, not `status` (reserved key). `body`, `payload`, `response` are dropped at INFO and above.
574
+ - Tests assert on `createMemorySink()` records by `eventName`, not on console spies.
575
+ - `debug`, `initDebugLog`, `flushDebugLog` are deprecated shims.
576
+
577
+ **Usage**:
578
+
579
+ ```javascript
580
+ const { getLogger } = require('../logs');
581
+ const log = getLogger('frigg.core.sync');
582
+
583
+ log.warn('Sync skipped', { eventName: 'frigg.core.sync.skipped', processId });
584
+ ```
585
+
586
+ ### 10. Lambda Utilities (`/lambda`)
587
+
588
+ **Purpose**: AWS Lambda-specific utilities.
589
+
590
+ **Key Classes**:
591
+ - `TimeoutCatcher` - Detect approaching Lambda timeout
592
+ - Graceful shutdown handling
593
+
594
+ **Usage**:
595
+
596
+ ```javascript
597
+ const { TimeoutCatcher } = require('@friggframework/core');
598
+
599
+ exports.handler = async (event, context) => {
600
+ const timeoutCatcher = new TimeoutCatcher(context);
601
+
602
+ if (timeoutCatcher.isNearTimeout()) {
603
+ // Save state and exit gracefully
604
+ }
605
+ };
606
+ ```
607
+
608
+ ## Development Workflow
609
+
610
+ ### Adding a New Use Case
611
+
612
+ 1. **Create use case file** in appropriate `use-cases/` directory:
613
+
614
+ ```javascript
615
+ // integrations/use-cases/my-new-use-case.js
616
+ class MyNewUseCase {
617
+ constructor({ integrationRepository, userRepository }) {
618
+ this.integrationRepo = integrationRepository;
619
+ this.userRepo = userRepository;
620
+ }
621
+
622
+ async execute(userId, integrationId) {
623
+ // Business logic here
624
+ const user = await this.userRepo.findById(userId);
625
+ const integration = await this.integrationRepo.findById(integrationId);
626
+
627
+ // Validate, orchestrate, coordinate
628
+ // Return result
629
+ }
630
+ }
631
+
632
+ module.exports = { MyNewUseCase };
633
+ ```
634
+
635
+ 2. **Add tests** in corresponding `tests/` directory
636
+ 3. **Export** from parent index.js if needed
637
+ 4. **Use in handler** - handlers call use cases, not repositories
638
+
639
+ ### Adding Encrypted Fields
640
+
641
+ **For custom models** (integration developers):
642
+
643
+ In `backend/index.js`:
644
+
645
+ ```javascript
646
+ const appDefinition = {
647
+ encryption: {
648
+ schema: {
649
+ MyCustomModel: {
650
+ fields: ['secretData', 'data.apiKey']
651
+ }
652
+ }
653
+ }
654
+ };
655
+ ```
656
+
657
+ **For core models** (framework developers):
658
+
659
+ Edit `database/encryption/encryption-schema-registry.js`:
660
+
661
+ ```javascript
662
+ const ENCRYPTION_SCHEMA = {
663
+ MyModel: {
664
+ fields: ['sensitiveField']
665
+ }
666
+ };
667
+ ```
668
+
669
+ ### Database Migrations
670
+
671
+ **MongoDB** (Prisma push):
672
+
673
+ ```bash
674
+ npm run prisma:push:mongo
675
+ ```
676
+
677
+ **PostgreSQL** (Prisma migrate):
678
+
679
+ ```bash
680
+ npm run prisma:migrate:postgres
681
+ ```
682
+
683
+ ### Integration Development
684
+
685
+ 1. **Extend IntegrationBase** in your app
686
+ 2. **Define static Definition** with name, version, modules
687
+ 3. **Implement lifecycle methods**: `onCreate`, `onUpdate`, `onDelete`
688
+ 4. **Add event handlers** for webhooks and user actions
689
+ 5. **Use framework services**: repositories, encryption, logging
690
+
691
+ ## Testing Strategy
692
+
693
+ ### Test Categories
694
+
695
+ 1. **Unit Tests**: Use cases with mocked repositories
696
+ 2. **Integration Tests**: Full flow with real dependencies
697
+ 3. **Repository Tests**: Database operations
698
+ 4. **Handler Tests**: HTTP/Lambda response testing
699
+
700
+ ### Test Structure
701
+
702
+ ```javascript
703
+ describe('MyUseCase', () => {
704
+ let useCase;
705
+ let mockRepository;
706
+
707
+ beforeEach(() => {
708
+ mockRepository = {
709
+ findById: jest.fn(),
710
+ save: jest.fn()
711
+ };
712
+ useCase = new MyUseCase({ repository: mockRepository });
713
+ });
714
+
715
+ it('executes successfully', async () => {
716
+ mockRepository.findById.mockResolvedValue({ id: '123' });
717
+
718
+ const result = await useCase.execute('123');
719
+
720
+ expect(result).toBeDefined();
721
+ expect(mockRepository.findById).toHaveBeenCalledWith('123');
722
+ });
723
+ });
724
+ ```
725
+
726
+ ### Running Tests
727
+
728
+ ```bash
729
+ # All tests
730
+ npm test
731
+
732
+ # Specific file
733
+ npm test -- path/to/test.test.js
734
+
735
+ # Pattern matching
736
+ npm test -- --testPathPattern="encryption"
737
+
738
+ # With coverage
739
+ npm test -- --coverage
740
+ ```
741
+
742
+ ### Test Doubles
743
+
744
+ Use test doubles from `@friggframework/test` package for consistent mocking.
745
+
746
+ ## Anti-Patterns
747
+
748
+ ### Architecture Anti-Patterns
749
+
750
+ ❌ **Don't call repositories from handlers** - Always use use cases
751
+ ❌ **Don't put business logic in repositories** - Repositories are pure data access
752
+ ❌ **Don't put HTTP concerns in use cases** - Use cases are protocol-agnostic
753
+ ❌ **Don't bypass encryption** - Sensitive data must be encrypted
754
+ ❌ **Don't expose internal errors to users** - Use `isUserFacingResponse: true`
755
+ ❌ **Don't skip connection pooling** - Set `callbackWaitsForEmptyEventLoop = false`
756
+
757
+ ### Development Anti-Patterns
758
+
759
+ ❌ **Don't modify node_modules** - Extend through proper patterns
760
+ ❌ **Don't hardcode credentials** - Use environment variables
761
+ ❌ **Don't skip tests** - Maintain test coverage
762
+ ❌ **Don't commit secrets** - Use .gitignore and AWS Secrets Manager
763
+ ❌ **Don't ignore linting errors** - Run `npm run lint:fix`
764
+
765
+ ### Integration Development Anti-Patterns
766
+
767
+ ❌ **Don't bypass IntegrationBase** - Always extend the base class
768
+ ❌ **Don't ignore lifecycle methods** - Implement onCreate, onUpdate, onDelete
769
+ ❌ **Don't skip signature validation** - Validate all webhooks
770
+ ❌ **Don't sync operations in handlers** - Use background workers for long tasks
771
+ ❌ **Don't ignore errors** - Proper error handling and logging
772
+
773
+ ## Environment Variables
774
+
775
+ ### Required
776
+
777
+ - `AWS_REGION` - AWS region for services
778
+ - `DATABASE_URL` - Database connection string (auto-set)
779
+ - `DB_TYPE` - Database type: 'mongodb' or 'postgresql'
780
+
781
+ ### Encryption
782
+
783
+ - `KMS_KEY_ARN` - AWS KMS key ARN (production)
784
+ - `AES_KEY_ID` - AES key ID (development)
785
+ - `AES_KEY` - AES encryption key (development)
786
+ - `STAGE` - Environment stage (dev, test, local bypass encryption)
787
+
788
+ ### Optional
789
+
790
+ - `SECRET_ARN` - AWS Secrets Manager ARN for auto-injection
791
+ - `FRIGG_LOG_LEVEL` - Minimum log level (`TRACE` … `FATAL`, default `INFO`; `DEBUG` on local runs)
792
+ - `FRIGG_QUEUE_MAX_RECEIVE_COUNT` - Set by devtools on integration queue workers whose queue the stack owns; feeds `delivery.maxReceiveCount`
793
+
794
+ ## Version Information
795
+
796
+ - **Current Version**: 2.0.0-next.0 (pre-release)
797
+ - **Node.js**: >=18 required
798
+ - **Dependencies**: See package.json for full list
799
+
800
+ ## Support and Documentation
801
+
802
+ - **Main Framework CLAUDE.md**: See root Frigg CLAUDE.md for framework-wide guidance
803
+ - **Core Runtime**: See `core/CLAUDE.md` for Lambda/Worker patterns
804
+ - **Encryption**: See `database/encryption/README.md` for encryption details
805
+ - **Package README**: See `README.md` for API reference
806
+
807
+ ## Recent Important Changes
808
+
809
+ ### Field-Level Encryption JSON Object Support
810
+
811
+ **Date**: 2025-01-06
812
+
813
+ **Problem**: The `FieldEncryptionService` was converting objects to the string `"[object Object]"` before encrypting, corrupting JSON fields like `IntegrationMapping.mapping`.
814
+
815
+ **Solution**: Added `_serializeForEncryption()` and `_deserializeAfterDecryption()` methods:
816
+ - Objects are now JSON.stringify'd before encryption
817
+ - Decrypted strings are JSON.parse'd back to objects
818
+ - Plain strings work as before
819
+
820
+ **Files Changed**:
821
+ - `database/encryption/field-encryption-service.js`
822
+ - `database/encryption/field-encryption-service.test.js`
823
+
824
+ **Test Coverage**: All 40 tests pass, including new object encryption test.
825
+
826
+ **Impact**: `IntegrationMapping.mapping` and other JSON fields now correctly round-trip through encryption.
827
+
828
+ ---
829
+
830
+ **Built with ❤️ by the Frigg Framework team**