@friggframework/core 2.0.0-next.11 → 2.0.0-next.110

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 (375) hide show
  1. package/CLAUDE.md +702 -0
  2. package/README.md +999 -50
  3. package/admin-scripts/index.js +52 -0
  4. package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
  5. package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
  6. package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
  7. package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
  8. package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
  9. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  10. package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
  11. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  12. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  13. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  14. package/application/commands/README.md +451 -0
  15. package/application/commands/admin-script-commands.js +359 -0
  16. package/application/commands/credential-commands.js +262 -0
  17. package/application/commands/entity-commands.js +336 -0
  18. package/application/commands/integration-commands.js +356 -0
  19. package/application/commands/integration-mapping-commands.js +25 -0
  20. package/application/commands/process-commands.js +135 -0
  21. package/application/commands/report-commands.js +188 -0
  22. package/application/commands/scheduler-commands.js +263 -0
  23. package/application/commands/usage-commands.js +56 -0
  24. package/application/commands/user-commands.js +312 -0
  25. package/application/index.js +83 -0
  26. package/artifacts/repositories/artifact-repository-factory.js +19 -0
  27. package/artifacts/repositories/artifact-repository-interface.js +27 -0
  28. package/artifacts/repositories/artifact-repository-local.js +42 -0
  29. package/artifacts/repositories/artifact-repository-s3.js +61 -0
  30. package/assertions/index.js +0 -3
  31. package/core/CLAUDE.md +700 -0
  32. package/core/Worker.js +60 -24
  33. package/core/create-handler.js +189 -5
  34. package/core/parameters-to-env.js +257 -0
  35. package/core/ssm-preload.mjs +34 -0
  36. package/credential/repositories/credential-active-type.js +32 -0
  37. package/credential/repositories/credential-repository-documentdb.js +355 -0
  38. package/credential/repositories/credential-repository-factory.js +54 -0
  39. package/credential/repositories/credential-repository-interface.js +113 -0
  40. package/credential/repositories/credential-repository-mongo.js +294 -0
  41. package/credential/repositories/credential-repository-postgres.js +312 -0
  42. package/credential/repositories/credential-repository.js +300 -0
  43. package/credential/use-cases/get-credential-for-user.js +25 -0
  44. package/credential/use-cases/update-authentication-status.js +15 -0
  45. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  46. package/database/adapters/lambda-invoker.js +97 -0
  47. package/database/config.js +154 -0
  48. package/database/documentdb-encryption-service.js +330 -0
  49. package/database/documentdb-utils.js +192 -0
  50. package/database/encryption/README.md +839 -0
  51. package/database/encryption/documentdb-encryption-service.md +3575 -0
  52. package/database/encryption/encryption-schema-registry.js +401 -0
  53. package/database/encryption/field-encryption-service.js +254 -0
  54. package/database/encryption/logger.js +79 -0
  55. package/database/encryption/prisma-encryption-extension.js +230 -0
  56. package/database/index.js +21 -21
  57. package/database/prisma.js +182 -0
  58. package/database/repositories/health-check-repository-documentdb.js +138 -0
  59. package/database/repositories/health-check-repository-factory.js +48 -0
  60. package/database/repositories/health-check-repository-interface.js +82 -0
  61. package/database/repositories/health-check-repository-mongodb.js +89 -0
  62. package/database/repositories/health-check-repository-postgres.js +82 -0
  63. package/database/repositories/migration-status-repository-s3.js +137 -0
  64. package/database/use-cases/check-database-health-use-case.js +29 -0
  65. package/database/use-cases/check-database-state-use-case.js +81 -0
  66. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  67. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  68. package/database/use-cases/get-migration-status-use-case.js +93 -0
  69. package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
  70. package/database/use-cases/run-database-migration-use-case.js +139 -0
  71. package/database/use-cases/test-encryption-use-case.js +253 -0
  72. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  73. package/database/utils/mongodb-collection-utils.js +94 -0
  74. package/database/utils/mongodb-schema-init.js +108 -0
  75. package/database/utils/prisma-runner.js +491 -0
  76. package/database/utils/prisma-schema-parser.js +182 -0
  77. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  78. package/encrypt/Cryptor.js +34 -168
  79. package/encrypt/index.js +1 -2
  80. package/errors/client-safe-error.js +26 -0
  81. package/errors/fetch-error.js +15 -7
  82. package/errors/index.js +2 -0
  83. package/generated/prisma-mongodb/client.d.ts +1 -0
  84. package/generated/prisma-mongodb/client.js +5 -0
  85. package/generated/prisma-mongodb/default.d.ts +1 -0
  86. package/generated/prisma-mongodb/default.js +5 -0
  87. package/generated/prisma-mongodb/edge.d.ts +1 -0
  88. package/generated/prisma-mongodb/edge.js +386 -0
  89. package/generated/prisma-mongodb/index-browser.js +368 -0
  90. package/generated/prisma-mongodb/index.d.ts +27065 -0
  91. package/generated/prisma-mongodb/index.js +411 -0
  92. package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  93. package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  94. package/generated/prisma-mongodb/package.json +183 -0
  95. package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
  96. package/generated/prisma-mongodb/runtime/edge.js +35 -0
  97. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  98. package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
  99. package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
  100. package/generated/prisma-mongodb/runtime/library.js +147 -0
  101. package/generated/prisma-mongodb/runtime/react-native.js +84 -0
  102. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
  103. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
  104. package/generated/prisma-mongodb/schema.prisma +455 -0
  105. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
  106. package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
  107. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  108. package/generated/prisma-mongodb/wasm.js +393 -0
  109. package/generated/prisma-postgresql/client.d.ts +1 -0
  110. package/generated/prisma-postgresql/client.js +5 -0
  111. package/generated/prisma-postgresql/default.d.ts +1 -0
  112. package/generated/prisma-postgresql/default.js +5 -0
  113. package/generated/prisma-postgresql/edge.d.ts +1 -0
  114. package/generated/prisma-postgresql/edge.js +407 -0
  115. package/generated/prisma-postgresql/index-browser.js +389 -0
  116. package/generated/prisma-postgresql/index.d.ts +29904 -0
  117. package/generated/prisma-postgresql/index.js +432 -0
  118. package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  119. package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
  120. package/generated/prisma-postgresql/package.json +183 -0
  121. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  122. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  123. package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
  124. package/generated/prisma-postgresql/runtime/edge.js +35 -0
  125. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  126. package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
  127. package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
  128. package/generated/prisma-postgresql/runtime/library.js +147 -0
  129. package/generated/prisma-postgresql/runtime/react-native.js +84 -0
  130. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
  131. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
  132. package/generated/prisma-postgresql/schema.prisma +436 -0
  133. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
  134. package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
  135. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  136. package/generated/prisma-postgresql/wasm.js +414 -0
  137. package/handlers/WEBHOOKS.md +653 -0
  138. package/handlers/app-definition-loader.js +67 -0
  139. package/handlers/app-handler-helpers.js +3 -3
  140. package/handlers/backend-utils.js +271 -42
  141. package/handlers/database-migration-handler.js +227 -0
  142. package/handlers/integration-event-dispatcher.js +68 -0
  143. package/handlers/middleware/admin-auth.js +73 -0
  144. package/handlers/routers/HEALTHCHECK.md +342 -0
  145. package/handlers/routers/auth.js +4 -15
  146. package/handlers/routers/db-migration.handler.js +29 -0
  147. package/handlers/routers/db-migration.js +329 -0
  148. package/handlers/routers/health.js +518 -0
  149. package/handlers/routers/integration-defined-routers.js +85 -10
  150. package/handlers/routers/integration-webhook-routers.js +80 -0
  151. package/handlers/routers/user.js +27 -5
  152. package/handlers/routers/websocket.js +5 -3
  153. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  154. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  155. package/handlers/workers/db-migration.js +427 -0
  156. package/handlers/workers/dlq-processor.js +63 -0
  157. package/handlers/workers/integration-defined-workers.js +13 -7
  158. package/index.js +102 -46
  159. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
  160. package/infrastructure/scheduler/index.js +33 -0
  161. package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
  162. package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
  163. package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
  164. package/integrations/EXTENSIONS.md +240 -0
  165. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  166. package/integrations/extension.js +254 -0
  167. package/integrations/index.js +20 -10
  168. package/integrations/integration-base.js +730 -74
  169. package/integrations/integration-router.js +398 -180
  170. package/integrations/options.js +1 -1
  171. package/integrations/repositories/config-patch-shared.js +43 -0
  172. package/integrations/repositories/integration-mapping-repository-documentdb.js +303 -0
  173. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  174. package/integrations/repositories/integration-mapping-repository-interface.js +120 -0
  175. package/integrations/repositories/integration-mapping-repository-mongo.js +183 -0
  176. package/integrations/repositories/integration-mapping-repository-postgres.js +255 -0
  177. package/integrations/repositories/integration-mapping-repository.js +156 -0
  178. package/integrations/repositories/integration-repository-documentdb.js +322 -0
  179. package/integrations/repositories/integration-repository-factory.js +51 -0
  180. package/integrations/repositories/integration-repository-interface.js +189 -0
  181. package/integrations/repositories/integration-repository-mongo.js +450 -0
  182. package/integrations/repositories/integration-repository-postgres.js +492 -0
  183. package/integrations/repositories/process-repository-documentdb.js +311 -0
  184. package/integrations/repositories/process-repository-factory.js +53 -0
  185. package/integrations/repositories/process-repository-interface.js +136 -0
  186. package/integrations/repositories/process-repository-mongo.js +262 -0
  187. package/integrations/repositories/process-repository-postgres.js +380 -0
  188. package/integrations/repositories/process-update-ops-shared.js +112 -0
  189. package/integrations/repositories/report-id.js +13 -0
  190. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  191. package/integrations/tests/doubles/dummy-integration-class.js +113 -0
  192. package/integrations/tests/doubles/test-integration-repository.js +146 -0
  193. package/integrations/use-cases/create-integration.js +215 -0
  194. package/integrations/use-cases/create-process.js +130 -0
  195. package/integrations/use-cases/delete-integration-for-user.js +119 -0
  196. package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
  197. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
  198. package/integrations/use-cases/get-integration-for-user.js +78 -0
  199. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  200. package/integrations/use-cases/get-integration-instance.js +83 -0
  201. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  202. package/integrations/use-cases/get-possible-integrations.js +27 -0
  203. package/integrations/use-cases/get-process.js +89 -0
  204. package/integrations/use-cases/index.js +19 -0
  205. package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
  206. package/integrations/use-cases/load-integration-context.js +71 -0
  207. package/integrations/use-cases/patch-integration-config.js +39 -0
  208. package/integrations/use-cases/process-errors.js +28 -0
  209. package/integrations/use-cases/update-integration-config.js +32 -0
  210. package/integrations/use-cases/update-integration-messages.js +44 -0
  211. package/integrations/use-cases/update-integration-status.js +32 -0
  212. package/integrations/use-cases/update-integration.js +92 -0
  213. package/integrations/use-cases/update-process-metrics.js +222 -0
  214. package/integrations/use-cases/update-process-state.js +163 -0
  215. package/integrations/utils/map-integration-dto.js +37 -0
  216. package/jest-global-setup-noop.js +3 -0
  217. package/jest-global-teardown-noop.js +3 -0
  218. package/logs/logger.js +0 -4
  219. package/{module-plugin → modules}/index.js +0 -10
  220. package/modules/module-factory.js +56 -0
  221. package/modules/module.js +307 -0
  222. package/modules/repositories/module-repository-documentdb.js +350 -0
  223. package/modules/repositories/module-repository-factory.js +40 -0
  224. package/modules/repositories/module-repository-interface.js +145 -0
  225. package/modules/repositories/module-repository-mongo.js +436 -0
  226. package/modules/repositories/module-repository-postgres.js +481 -0
  227. package/modules/repositories/module-repository.js +369 -0
  228. package/modules/requester/api-key.js +52 -0
  229. package/modules/requester/oauth-2.js +555 -0
  230. package/modules/requester/requester.js +542 -0
  231. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  232. package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
  233. package/modules/tests/doubles/test-module-factory.js +16 -0
  234. package/modules/tests/doubles/test-module-repository.js +39 -0
  235. package/modules/use-cases/get-entities-for-user.js +32 -0
  236. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  237. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  238. package/modules/use-cases/get-module-instance-from-type.js +34 -0
  239. package/modules/use-cases/get-module.js +74 -0
  240. package/modules/use-cases/process-authorization-callback.js +243 -0
  241. package/modules/use-cases/refresh-entity-options.js +72 -0
  242. package/modules/use-cases/test-module-auth.js +72 -0
  243. package/modules/utils/map-module-dto.js +18 -0
  244. package/package.json +92 -50
  245. package/prisma-mongodb/schema.prisma +455 -0
  246. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  247. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  248. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  249. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  250. package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
  251. package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
  252. package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
  253. package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
  254. package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
  255. package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
  256. package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
  257. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  258. package/prisma-postgresql/schema.prisma +436 -0
  259. package/queues/queuer-util.js +103 -21
  260. package/reporting/README.md +154 -0
  261. package/reporting/builtin-reports.js +6 -0
  262. package/reporting/index.js +13 -0
  263. package/reporting/report-base.js +49 -0
  264. package/reporting/reports/integrations-report.js +221 -0
  265. package/syncs/manager.js +468 -443
  266. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  267. package/syncs/repositories/sync-repository-factory.js +43 -0
  268. package/syncs/repositories/sync-repository-interface.js +109 -0
  269. package/syncs/repositories/sync-repository-mongo.js +239 -0
  270. package/syncs/repositories/sync-repository-postgres.js +319 -0
  271. package/syncs/sync.js +0 -1
  272. package/telemetry/README.md +331 -0
  273. package/telemetry/bind-telemetry-context.js +73 -0
  274. package/telemetry/canonical-counters.js +52 -0
  275. package/telemetry/exporters.js +85 -0
  276. package/telemetry/index.js +26 -0
  277. package/telemetry/instrument-handler.js +87 -0
  278. package/telemetry/no-op-telemetry.js +67 -0
  279. package/telemetry/north-star.js +103 -0
  280. package/telemetry/otel-telemetry.js +213 -0
  281. package/telemetry/plugin-subscribers.js +77 -0
  282. package/telemetry/telemetry-config.js +120 -0
  283. package/telemetry/telemetry-context.js +40 -0
  284. package/telemetry/telemetry-event-bus.js +58 -0
  285. package/telemetry/telemetry-runtime.js +147 -0
  286. package/telemetry/telemetry-service.js +51 -0
  287. package/telemetry/usage-rollup-subscriber.js +116 -0
  288. package/token/repositories/token-repository-documentdb.js +137 -0
  289. package/token/repositories/token-repository-factory.js +40 -0
  290. package/token/repositories/token-repository-interface.js +131 -0
  291. package/token/repositories/token-repository-mongo.js +219 -0
  292. package/token/repositories/token-repository-postgres.js +264 -0
  293. package/token/repositories/token-repository.js +219 -0
  294. package/types/associations/index.d.ts +0 -17
  295. package/types/core/index.d.ts +12 -4
  296. package/types/database/index.d.ts +10 -2
  297. package/types/encrypt/index.d.ts +5 -3
  298. package/types/integrations/index.d.ts +3 -8
  299. package/types/module-plugin/index.d.ts +20 -69
  300. package/types/syncs/index.d.ts +0 -17
  301. package/usage/README.md +54 -0
  302. package/usage/index.js +17 -0
  303. package/usage/repositories/usage-repository-documentdb.js +194 -0
  304. package/usage/repositories/usage-repository-factory.js +25 -0
  305. package/usage/repositories/usage-repository-interface.js +37 -0
  306. package/usage/repositories/usage-repository-prisma.js +146 -0
  307. package/usage/tracked-metrics.js +38 -0
  308. package/usage/usage-windows.js +24 -0
  309. package/user/repositories/user-repository-documentdb.js +458 -0
  310. package/user/repositories/user-repository-factory.js +52 -0
  311. package/user/repositories/user-repository-interface.js +214 -0
  312. package/user/repositories/user-repository-mongo.js +323 -0
  313. package/user/repositories/user-repository-postgres.js +377 -0
  314. package/user/tests/doubles/test-user-repository.js +72 -0
  315. package/user/use-cases/authenticate-user.js +127 -0
  316. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  317. package/user/use-cases/create-individual-user.js +61 -0
  318. package/user/use-cases/create-organization-user.js +47 -0
  319. package/user/use-cases/create-token-for-user-id.js +30 -0
  320. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  321. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  322. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  323. package/user/use-cases/login-user.js +122 -0
  324. package/user/user.js +125 -0
  325. package/utils/backend-path.js +38 -0
  326. package/utils/index.js +6 -0
  327. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  328. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  329. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  330. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  331. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  332. package/websocket/repositories/websocket-connection-repository.js +161 -0
  333. package/assertions/is-equal.js +0 -17
  334. package/associations/model.js +0 -54
  335. package/database/models/IndividualUser.js +0 -76
  336. package/database/models/OrganizationUser.js +0 -29
  337. package/database/models/State.js +0 -9
  338. package/database/models/Token.js +0 -70
  339. package/database/models/UserModel.js +0 -7
  340. package/database/models/WebsocketConnection.js +0 -49
  341. package/database/mongo.js +0 -45
  342. package/database/mongoose.js +0 -5
  343. package/encrypt/Cryptor.test.js +0 -32
  344. package/encrypt/encrypt.js +0 -132
  345. package/encrypt/encrypt.test.js +0 -1069
  346. package/encrypt/test-encrypt.js +0 -107
  347. package/errors/base-error.test.js +0 -32
  348. package/errors/fetch-error.test.js +0 -79
  349. package/errors/halt-error.test.js +0 -11
  350. package/errors/validation-errors.test.js +0 -120
  351. package/handlers/routers/middleware/loadUser.js +0 -15
  352. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  353. package/integrations/create-frigg-backend.js +0 -31
  354. package/integrations/integration-factory.js +0 -251
  355. package/integrations/integration-mapping.js +0 -43
  356. package/integrations/integration-model.js +0 -46
  357. package/integrations/integration-user.js +0 -144
  358. package/integrations/test/integration-base.test.js +0 -144
  359. package/lambda/TimeoutCatcher.test.js +0 -68
  360. package/logs/logger.test.js +0 -76
  361. package/module-plugin/auther.js +0 -393
  362. package/module-plugin/credential.js +0 -22
  363. package/module-plugin/entity-manager.js +0 -70
  364. package/module-plugin/entity.js +0 -46
  365. package/module-plugin/manager.js +0 -169
  366. package/module-plugin/module-factory.js +0 -61
  367. package/module-plugin/requester/api-key.js +0 -36
  368. package/module-plugin/requester/oauth-2.js +0 -219
  369. package/module-plugin/requester/requester.js +0 -165
  370. package/module-plugin/requester/requester.test.js +0 -28
  371. package/module-plugin/test/auther.test.js +0 -97
  372. package/syncs/model.js +0 -62
  373. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  374. /package/{module-plugin → modules}/requester/basic.js +0 -0
  375. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,154 @@
1
+ # Reporting
2
+
3
+ Deployment-wide reports for `@friggframework/core`, modelled as **admin
4
+ operations** (ADR-010) — a sibling of the Admin Script Runner (ADR-005) on the
5
+ same primitives: one registry, the shared admin API key, sync/async execution,
6
+ the isolated admin execution store, and EventBridge scheduling.
7
+
8
+ A report is a definition (`ReportBase`) whose output is its payload. **Core
9
+ ships built-in reports; adopters register their own** — both are discovered,
10
+ listed, and executed through the same runner. This package provides `ReportBase`,
11
+ the built-in reports, and `createReportCommands()`. The HTTP router, runner, and
12
+ executor live in `@friggframework/admin-scripts`; the infrastructure (queue,
13
+ executor Lambda, artifact bucket, scheduler) is generated by the devtools
14
+ `AdminScriptBuilder`.
15
+
16
+ ## Registering reports
17
+
18
+ ```js
19
+ // app definition — same shape as adminScripts
20
+ const Definition = {
21
+ name: 'my-app',
22
+ integrations: [HubSpotIntegration, SalesforceIntegration],
23
+ reports: [ConnectedAccountsActivity], // adopter-defined
24
+ admin: { includeBuiltinReports: true }, // + core built-ins (integrations, ...)
25
+ };
26
+ ```
27
+
28
+ A report extends `ReportBase` and reads only through the injected admin command
29
+ bundle (`frigg` / `this.context.commands`), never a repository directly:
30
+
31
+ ```js
32
+ const { ReportBase } = require('@friggframework/core');
33
+
34
+ class ConnectedAccountsActivity extends ReportBase {
35
+ static Definition = {
36
+ name: 'connected-accounts-activity',
37
+ version: '1.0.0',
38
+ runModes: ['snapshot', 'recorded', 'live'], // first is the default
39
+ inputSchema: { type: 'object', properties: {
40
+ windowDays: { type: 'integer', enum: [30, 60, 90], default: 30 } } },
41
+ output: { format: 'json' }, // 'csv'|'pdf'|'zip' → artifact storage
42
+ schedule: { enabled: true, cron: 'cron(0 6 * * ? *)', mode: 'snapshot' }, // default mode only; activate via PUT
43
+ };
44
+
45
+ async execute(frigg, params) {
46
+ const since = daysAgo(params.windowDays ?? 30);
47
+ const byType = await frigg.credentials.countActiveByType({ since });
48
+ return { windowDays: params.windowDays ?? 30, byType };
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Auth
54
+
55
+ All report endpoints use the **shared admin API key** (ADR-005): the
56
+ `x-frigg-admin-api-key` header, validated against `ADMIN_API_KEY`. The dedicated
57
+ reporting key (`x-frigg-reporting-api-key` / `REPORTING_API_KEY`) is **retired**.
58
+
59
+ ## Run modes
60
+
61
+ Each invocation picks a mode; persistence follows from it. All three run through
62
+ the one runner and the isolated admin execution store (`AdminScriptExecution`,
63
+ `type: 'REPORT'` — no user/integration FK, so a user-scoped query can never
64
+ return a report record).
65
+
66
+ | Mode | Persists | Use it for |
67
+ | --- | --- | --- |
68
+ | `live` | nothing — computes and returns inline | cheap, always-fresh queries |
69
+ | `recorded` | an execution record (input + results + logs) | audit trail, async polling, expensive scans |
70
+ | `snapshot` | a recorded run tagged into a named series | trends over time |
71
+
72
+ Non-JSON `output.format` (`csv`/`pdf`/`zip`) is written to artifact storage (S3,
73
+ private + signed URL) instead of inline; JSON stays inline.
74
+
75
+ ## Endpoints
76
+
77
+ | Method | Path | Description |
78
+ | --- | --- | --- |
79
+ | `GET` | `/api/v2/reports` | List registered report definitions. |
80
+ | `GET` | `/api/v2/reports/:name` | Report definition detail (run modes, input schema, schedule). |
81
+ | `POST` | `/api/v2/reports/:name/run` | Run `{ mode, params }`. `live` → `200` inline; `recorded`/`snapshot` → `202 { executionId }` (queued). |
82
+ | `GET` | `/api/v2/reports/:name/snapshots?from=&to=` | A report's snapshot series (trend); each point carries a signed `artifactUrl` when the run produced a non-JSON artifact. |
83
+ | `GET` | `/api/v2/reports/executions/:id` | Fetch one report execution (guarded to `type: 'REPORT'`); a stored artifact is returned as a signed `results.artifactUrl`. |
84
+ | `GET`/`PUT`/`DELETE` | `/api/v2/reports/:name/schedule` | Manage a report's recurring schedule. |
85
+ | `GET` | `/api/v2/reports/integrations` | Deprecated back-compat for PR #607 — runs the built-in `integrations` report in `live` mode and returns its payload directly. Prefer `POST /:name/run`. |
86
+
87
+ ## Built-in: `integrations`
88
+
89
+ Integrations by status and type, with per-type usage columns. Input query params
90
+ (all strings): `status` (an `IntegrationStatus`), `type` (`config.type` slug),
91
+ `userId`. Response shape (`schemaVersion: 1`):
92
+
93
+ ```jsonc
94
+ {
95
+ "schemaVersion": 1,
96
+ "service": "frigg-core-api",
97
+ "generatedAt": "2026-06-29T20:45:05.142Z",
98
+ "filters": { "status": null, "type": null, "userId": null },
99
+ "metrics": {
100
+ "total": 12,
101
+ "byStatus": { "ENABLED": 9, "ERROR": 2, "NEEDS_CONFIG": 1, "PROCESSING": 0, "DISABLED": 0 },
102
+ "byType": [
103
+ {
104
+ "type": "hubspot",
105
+ "label": "HubSpot CRM",
106
+ "total": 5,
107
+ "byStatus": { "ENABLED": 4, "ERROR": 1, "NEEDS_CONFIG": 0, "PROCESSING": 0, "DISABLED": 0 },
108
+ "usage": { "records.synced": 4200, "webhooks.received": 118 }
109
+ }
110
+ ],
111
+ "typeLabels": { "hubspot": "HubSpot CRM" },
112
+ "integrations": [ /* lightweight per-integration rows */ ]
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Field reference
118
+
119
+ - `schemaVersion` — contract version; branch on it. Additive changes do **not** bump it.
120
+ - `filters` — echoes the applied filters (nulls when omitted).
121
+ - `metrics.total` — count of integrations matching the filters.
122
+ - `metrics.byStatus` — counts keyed by `IntegrationStatus` value.
123
+ - `metrics.byType[]` — per `type` breakdown: `{ type, label, total, byStatus, usage? }`.
124
+ - `type` — the `config.type` slug. Integrations with no type bucket as `"unknown"`.
125
+ - `usage` — **additive** (ADR-011): present only when the deployment has the usage
126
+ store configured. A map of canonical counter → total for that type (`0` when a
127
+ type has no data). Read only from the Frigg usage store; a usage-store read
128
+ failure never fails the structural report. Populated by the counters an
129
+ integration opts into via `Definition.usage` — see [`../telemetry/README.md`](../telemetry/README.md).
130
+ - `label` — human-readable name from the integration class's `Definition.display.label`,
131
+ falling back to the `type` slug when no registered class supplies one.
132
+ - `metrics.typeLabels` — map of `type` slug → label, so callers label
133
+ `integrations[].type` rows without bloating each row.
134
+ - `metrics.integrations[]` — lightweight per-integration rows (`id`, `type`, `status`,
135
+ `userId`, `version`, `moduleCount`, `errorCount`, `mappedRecordCount`, `createdAt`,
136
+ `updatedAt`). Resolve display names via `metrics.typeLabels`.
137
+
138
+ The built-in report reads its data through the admin command bundle
139
+ (`frigg.integrations.listForReport`, `frigg.integrationMappings.countByIntegrationIds`,
140
+ `frigg.usage.getTotalsByDimension`) — the retired reporting repository triad's
141
+ logic now lives in the canonical integration/mapping repositories, so all
142
+ database adapters (PostgreSQL, MongoDB, DocumentDB) are served through one path.
143
+
144
+ ## Caveats
145
+
146
+ - Report output is sensitive (integration types, counts, user IDs, versions).
147
+ Treat the admin API key as an admin secret.
148
+ - The `integrations` back-compat alias is transitional; migrate to
149
+ `POST /api/v2/reports/integrations/run { "mode": "live" }`.
150
+ - Labels only appear once the app registers the integration classes.
151
+ - A `schedule` block in a report's Definition only supplies the default scheduled
152
+ **mode**. It does not create a recurring trigger on its own — activate the
153
+ schedule with `PUT /api/v2/reports/:name/schedule`; the DB schedule
154
+ (`ScriptSchedule`) is the single source of truth.
@@ -0,0 +1,6 @@
1
+ const { IntegrationsReport } = require('./reports/integrations-report');
2
+
3
+ // Registered by the admin-scripts bootstrap when the app definition sets `admin.includeBuiltinReports`.
4
+ const BUILTIN_REPORTS = [IntegrationsReport];
5
+
6
+ module.exports = { BUILTIN_REPORTS };
@@ -0,0 +1,13 @@
1
+ const { ReportBase } = require('./report-base');
2
+ const { IntegrationsReport } = require('./reports/integrations-report');
3
+ const { BUILTIN_REPORTS } = require('./builtin-reports');
4
+ const {
5
+ createReportCommands,
6
+ } = require('../application/commands/report-commands');
7
+
8
+ module.exports = {
9
+ ReportBase,
10
+ IntegrationsReport,
11
+ BUILTIN_REPORTS,
12
+ createReportCommands,
13
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ReportBase — a report is an admin operation whose output is its payload.
3
+ *
4
+ * A report Definition declares:
5
+ * - runModes: allowed modes; the first is the default.
6
+ * 'live' — compute and return inline; persist nothing.
7
+ * 'recorded' — persist an execution record (input + results + logs).
8
+ * 'snapshot' — a recorded run tagged as a point in a named series.
9
+ * - output.format: 'json' returns inline; 'csv'|'pdf'|'zip' go to artifact storage.
10
+ * - schedule: optional recurring run via EventBridge.
11
+ *
12
+ * execute(frigg, params): `frigg` is the admin command bundle
13
+ * (=== this.context.commands); do data reads through it, never a repository.
14
+ */
15
+ class ReportBase {
16
+ static Definition = {
17
+ name: 'Report Name',
18
+ version: '0.0.0',
19
+ description: 'What this report computes',
20
+ source: 'USER_DEFINED', // 'BUILTIN' | 'USER_DEFINED'
21
+
22
+ runModes: ['live', 'recorded', 'snapshot'],
23
+ inputSchema: null,
24
+ outputSchema: null,
25
+ output: { format: 'json' },
26
+ schedule: { enabled: false, cron: null, mode: 'snapshot' },
27
+
28
+ config: {
29
+ timeout: 300000,
30
+ },
31
+
32
+ display: {
33
+ category: 'reporting',
34
+ icon: null,
35
+ },
36
+ };
37
+
38
+ constructor(params = {}) {
39
+ this.context = params.context || null;
40
+ this.executionId = params.executionId || null;
41
+ this.integrationFactory = params.integrationFactory || null;
42
+ }
43
+
44
+ async execute(frigg, params) {
45
+ throw new Error('ReportBase.execute() must be implemented by subclass');
46
+ }
47
+ }
48
+
49
+ module.exports = { ReportBase };
@@ -0,0 +1,221 @@
1
+ const { ReportBase } = require('../report-base');
2
+ const { CANONICAL_COUNTERS } = require('../../telemetry/canonical-counters');
3
+ const { loadAppDefinition } = require('../../handlers/app-definition-loader');
4
+
5
+ const SCHEMA_VERSION = 1;
6
+ const SERVICE = 'frigg-core-api';
7
+
8
+ // Seeded so every known status appears in output even at count 0.
9
+ const KNOWN_STATUSES = [
10
+ 'IN_CREATION',
11
+ 'ENABLED',
12
+ 'ERROR',
13
+ 'NEEDS_CONFIG',
14
+ 'PROCESSING',
15
+ 'IN_DELETION',
16
+ 'DISABLED',
17
+ ];
18
+
19
+ // IntegrationBase.Definition default — skip it so the slug is used instead.
20
+ const PLACEHOLDER_DISPLAY_NAME = 'Integration Name';
21
+
22
+ class IntegrationsReport extends ReportBase {
23
+ static Definition = {
24
+ name: 'integrations',
25
+ version: '1.0.0',
26
+ description: 'Integrations by status and type, with per-type usage counts',
27
+ source: 'BUILTIN',
28
+ runModes: ['live', 'recorded', 'snapshot'],
29
+ inputSchema: {
30
+ type: 'object',
31
+ additionalProperties: false,
32
+ properties: {
33
+ status: { type: 'string' },
34
+ type: { type: 'string' },
35
+ userId: { type: 'string' },
36
+ },
37
+ },
38
+ output: { format: 'json' },
39
+ schedule: { enabled: false, cron: null, mode: 'snapshot' },
40
+ display: { category: 'reporting', icon: null },
41
+ };
42
+
43
+ async execute(frigg, params = {}) {
44
+ const { status, type, userId } = this._validateQuery(params);
45
+ const typeLabels = buildTypeLabels();
46
+
47
+ const rows = await frigg.integrations.listForReport({ status, userId });
48
+
49
+ // type lives in config.type, a JSON path not portably groupable across DBs, so filter here not in the query.
50
+ const filtered =
51
+ type === undefined
52
+ ? rows
53
+ : rows.filter((row) => (row.type ?? 'unknown') === type);
54
+
55
+ const ids = filtered.map((row) => row.id);
56
+ const mappingCounts = ids.length
57
+ ? await frigg.integrationMappings.countByIntegrationIds(ids)
58
+ : new Map();
59
+
60
+ const integrations = filtered.map((row) => ({
61
+ id: row.id,
62
+ type: row.type ?? 'unknown',
63
+ status: row.status ?? null,
64
+ userId: row.userId ?? null,
65
+ version: row.version ?? null,
66
+ moduleCount: row.moduleCount ?? 0,
67
+ errorCount: row.errorCount ?? 0,
68
+ mappedRecordCount: mappingCounts.get(row.id) ?? 0,
69
+ createdAt: toIso(row.createdAt),
70
+ updatedAt: toIso(row.updatedAt),
71
+ }));
72
+
73
+ const byStatus = emptyStatusCounts();
74
+ const byTypeMap = new Map();
75
+ for (const integration of integrations) {
76
+ // sentinel so a missing status never becomes a literal "null" key
77
+ const statusKey = integration.status ?? 'UNKNOWN';
78
+ byStatus[statusKey] = (byStatus[statusKey] ?? 0) + 1;
79
+
80
+ if (!byTypeMap.has(integration.type)) {
81
+ byTypeMap.set(integration.type, {
82
+ type: integration.type,
83
+ label: typeLabels[integration.type] || integration.type,
84
+ total: 0,
85
+ byStatus: emptyStatusCounts(),
86
+ });
87
+ }
88
+ const bucket = byTypeMap.get(integration.type);
89
+ bucket.total += 1;
90
+ bucket.byStatus[statusKey] = (bucket.byStatus[statusKey] ?? 0) + 1;
91
+ }
92
+
93
+ await this._attachUsageColumns(byTypeMap, frigg);
94
+
95
+ return {
96
+ schemaVersion: SCHEMA_VERSION,
97
+ service: SERVICE,
98
+ generatedAt: new Date().toISOString(),
99
+ filters: {
100
+ status: status ?? null,
101
+ type: type ?? null,
102
+ userId: userId ?? null,
103
+ },
104
+ metrics: {
105
+ total: integrations.length,
106
+ byStatus,
107
+ byType: Array.from(byTypeMap.values()),
108
+ typeLabels: { ...typeLabels },
109
+ integrations,
110
+ },
111
+ };
112
+ }
113
+
114
+ async _attachUsageColumns(byTypeMap, frigg) {
115
+ const metrics = Object.keys(CANONICAL_COUNTERS);
116
+ try {
117
+ const totalsByMetric = await Promise.all(
118
+ metrics.map(async (metric) => {
119
+ const totals = await frigg.usage.getTotalsByDimension({
120
+ metric,
121
+ groupBy: 'integrationType',
122
+ });
123
+ const map = new Map(
124
+ (totals || []).map((t) => [t.integrationType, t.value])
125
+ );
126
+ return [metric, map];
127
+ })
128
+ );
129
+
130
+ for (const bucket of byTypeMap.values()) {
131
+ bucket.usage = {};
132
+ for (const [metric, map] of totalsByMetric) {
133
+ bucket.usage[metric] = map.get(bucket.type) ?? 0;
134
+ }
135
+ }
136
+ } catch (error) {
137
+ console.warn(
138
+ `[Frigg][reporting] usage columns unavailable: ${
139
+ error && error.message
140
+ }`
141
+ );
142
+ }
143
+ }
144
+
145
+ _validateQuery({ status, type, userId } = {}) {
146
+ for (const [key, value] of Object.entries({ status, type, userId })) {
147
+ if (
148
+ value !== undefined &&
149
+ value !== null &&
150
+ typeof value !== 'string'
151
+ ) {
152
+ throw invalidInput(
153
+ `Invalid query parameter '${key}': expected a string`
154
+ );
155
+ }
156
+ }
157
+ const normalize = (value) => value || undefined;
158
+ const normalized = {
159
+ status: normalize(status),
160
+ type: normalize(type),
161
+ userId: normalize(userId),
162
+ };
163
+ if (normalized.status && !KNOWN_STATUSES.includes(normalized.status)) {
164
+ throw invalidInput(
165
+ `Invalid status '${
166
+ normalized.status
167
+ }'. Expected one of: ${KNOWN_STATUSES.join(', ')}`
168
+ );
169
+ }
170
+ return normalized;
171
+ }
172
+ }
173
+
174
+ // INVALID_INPUT keeps the report protocol-agnostic; the runner/router map it to a 400.
175
+ function invalidInput(message) {
176
+ const error = new Error(message);
177
+ error.code = 'INVALID_INPUT';
178
+ return error;
179
+ }
180
+
181
+ function emptyStatusCounts() {
182
+ return KNOWN_STATUSES.reduce((acc, status) => {
183
+ acc[status] = 0;
184
+ return acc;
185
+ }, {});
186
+ }
187
+
188
+ function toIso(value) {
189
+ if (!value) return null;
190
+ if (value instanceof Date) return value.toISOString();
191
+ // DocumentDB raw reads surface dates as { $date: ... }
192
+ if (typeof value === 'object' && value.$date) {
193
+ const date = new Date(value.$date);
194
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
195
+ }
196
+ return String(value);
197
+ }
198
+
199
+ function buildTypeLabels() {
200
+ try {
201
+ const { integrations = [] } = loadAppDefinition();
202
+ const labels = {};
203
+ for (const IntegrationClass of integrations) {
204
+ const def = IntegrationClass?.Definition;
205
+ if (!def?.name) continue;
206
+ const label = def.display?.label;
207
+ if (label && label !== PLACEHOLDER_DISPLAY_NAME) {
208
+ labels[def.name] = label;
209
+ }
210
+ }
211
+ return labels;
212
+ } catch (error) {
213
+ console.error(
214
+ 'Reporting: failed to load integration labels:',
215
+ error.message
216
+ );
217
+ return {};
218
+ }
219
+ }
220
+
221
+ module.exports = { IntegrationsReport, SCHEMA_VERSION };