@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
@@ -0,0 +1,561 @@
1
+ const { Requester } = require('./requester');
2
+ const { get } = require('../../assertions');
3
+ const { ModuleConstants } = require('../ModuleConstants');
4
+
5
+ /**
6
+ * OAuth 2.0 Requester - Base class for API modules using OAuth 2.0 authentication.
7
+ *
8
+ * Supports multiple OAuth 2.0 grant types:
9
+ * - `authorization_code` (default): Standard OAuth flow with user consent
10
+ * - `client_credentials`: Server-to-server authentication without user
11
+ * - `password`: Resource Owner Password Credentials grant
12
+ *
13
+ * @extends Requester
14
+ *
15
+ * @example
16
+ * // Authorization Code flow (default)
17
+ * const api = new MyApi({ grant_type: 'authorization_code' });
18
+ * const authUrl = api.getAuthorizationUri();
19
+ * // After user authorizes...
20
+ * await api.getTokenFromCode(code);
21
+ *
22
+ * @example
23
+ * // Client Credentials flow
24
+ * const api = new MyApi({
25
+ * grant_type: 'client_credentials',
26
+ * client_id: process.env.CLIENT_ID,
27
+ * client_secret: process.env.CLIENT_SECRET,
28
+ * audience: 'https://api.example.com',
29
+ * });
30
+ * await api.getTokenFromClientCredentials();
31
+ */
32
+ class OAuth2Requester extends Requester {
33
+ static requesterType = ModuleConstants.authType.oauth2;
34
+
35
+ /**
36
+ * Creates an OAuth2Requester instance.
37
+ *
38
+ * @param {Object} params - Configuration parameters
39
+ * @param {string} [params.grant_type='authorization_code'] - OAuth grant type:
40
+ * 'authorization_code', 'client_credentials', or 'password'
41
+ * @param {string} [params.client_id] - OAuth client ID
42
+ * @param {string} [params.client_secret] - OAuth client secret
43
+ * @param {string} [params.redirect_uri] - OAuth redirect URI for authorization code flow
44
+ * @param {string} [params.scope] - OAuth scopes (space-separated)
45
+ * @param {string} [params.authorizationUri] - Authorization endpoint URL
46
+ * @param {string} [params.tokenUri] - Token endpoint URL for exchanging codes/credentials
47
+ * @param {string} [params.baseURL] - Base URL for API requests
48
+ * @param {string} [params.access_token] - Existing access token
49
+ * @param {string} [params.refresh_token] - Existing refresh token
50
+ * @param {Date} [params.accessTokenExpire] - Access token expiration date
51
+ * @param {Date} [params.refreshTokenExpire] - Refresh token expiration date
52
+ * @param {string} [params.audience] - Token audience (for client_credentials)
53
+ * @param {string} [params.username] - Username (for password grant)
54
+ * @param {string} [params.password] - Password (for password grant)
55
+ * @param {string} [params.state] - OAuth state parameter for CSRF protection
56
+ */
57
+ constructor(params) {
58
+ super(params);
59
+ /** @type {string} Delegate type for token update notifications */
60
+ this.DLGT_TOKEN_UPDATE = 'TOKEN_UPDATE';
61
+ /** @type {string} Delegate type for token deauthorization notifications */
62
+ this.DLGT_TOKEN_DEAUTHORIZED = 'TOKEN_DEAUTHORIZED';
63
+ /**
64
+ * @type {string} Delegate type that asks the Module for the stored
65
+ * credential. The requester can then adopt a concurrent invocation's
66
+ * refresh and does not race it. See _adoptNewerCredential.
67
+ */
68
+ this.DLGT_CREDENTIAL_RELOAD = 'CREDENTIAL_RELOAD';
69
+
70
+ this.delegateTypes.push(this.DLGT_TOKEN_UPDATE);
71
+ this.delegateTypes.push(this.DLGT_TOKEN_DEAUTHORIZED);
72
+ this.delegateTypes.push(this.DLGT_CREDENTIAL_RELOAD);
73
+
74
+ /**
75
+ * Re-read delays after an invalid_grant, in ms. The winner's write
76
+ * can arrive after the loser's rejection (716 ms measured in
77
+ * production). Wait between re-reads before you decide that the
78
+ * credential is dead. Tests can inject other values.
79
+ */
80
+ this.credentialReloadBackoffMs = params?.credentialReloadBackoffMs ?? [
81
+ 500, 1000, 1500,
82
+ ];
83
+
84
+ /** @type {string} OAuth grant type */
85
+ this.grant_type = get(params, 'grant_type', 'authorization_code');
86
+ /** @type {string|null} OAuth client ID */
87
+ this.client_id = get(params, 'client_id', null);
88
+ /** @type {string|null} OAuth client secret */
89
+ this.client_secret = get(params, 'client_secret', null);
90
+ /** @type {string|null} OAuth redirect URI */
91
+ this.redirect_uri = get(params, 'redirect_uri', null);
92
+ /** @type {string|null} OAuth scopes */
93
+ this.scope = get(params, 'scope', null);
94
+ /** @type {string|null} Authorization endpoint URL */
95
+ this.authorizationUri = get(params, 'authorizationUri', null);
96
+ /** @type {string|null} Token endpoint URL */
97
+ this.tokenUri = get(params, 'tokenUri', null);
98
+ /** @type {string|null} Base URL for API requests */
99
+ this.baseURL = get(params, 'baseURL', null);
100
+ /** @type {string|null} Current access token */
101
+ this.access_token = get(params, 'access_token', null);
102
+ /** @type {string|null} Current refresh token */
103
+ this.refresh_token = get(params, 'refresh_token', null);
104
+ /** @type {Date|null} Access token expiration */
105
+ this.accessTokenExpire = get(params, 'accessTokenExpire', null);
106
+ /** @type {Date|null} Refresh token expiration */
107
+ this.refreshTokenExpire = get(params, 'refreshTokenExpire', null);
108
+ /** @type {string|null} Token audience */
109
+ this.audience = get(params, 'audience', null);
110
+ /** @type {string|null} Username for password grant */
111
+ this.username = get(params, 'username', null);
112
+ /** @type {string|null} Password for password grant */
113
+ this.password = get(params, 'password', null);
114
+ /** @type {string|null} OAuth state for CSRF protection */
115
+ this.state = get(params, 'state', null);
116
+
117
+ /** @type {boolean} Whether this requester supports token refresh */
118
+ this.isRefreshable = true;
119
+ }
120
+
121
+ /**
122
+ * Sets OAuth tokens and calculates expiration times.
123
+ * Notifies delegates of token update via DLGT_TOKEN_UPDATE.
124
+ *
125
+ * @param {Object} params - Token response from OAuth server
126
+ * @param {string} params.access_token - The access token
127
+ * @param {string} [params.refresh_token] - The refresh token (if provided)
128
+ * @param {number} [params.expires_in] - Access token lifetime in seconds
129
+ * @param {number} [params.x_refresh_token_expires_in] - Refresh token lifetime in seconds
130
+ * @returns {Promise<void>}
131
+ */
132
+ async setTokens(params) {
133
+ this.access_token = get(params, 'access_token');
134
+ const newRefreshToken = get(params, 'refresh_token', null);
135
+ if (newRefreshToken !== null) {
136
+ this.refresh_token = newRefreshToken;
137
+ } else {
138
+ if (this.refresh_token) {
139
+ this.logger.debug('No refresh_token in response, preserving existing', {
140
+ eventName: `${this.logger.name}.refresh_token_preserved`,
141
+ });
142
+ } else {
143
+ this.logger.debug('No refresh_token in response and none held', {
144
+ eventName: `${this.logger.name}.refresh_token_absent`,
145
+ });
146
+ }
147
+ }
148
+ const accessExpiresIn = get(params, 'expires_in', null);
149
+ const refreshExpiresIn = get(
150
+ params,
151
+ 'x_refresh_token_expires_in',
152
+ null
153
+ );
154
+
155
+ this.accessTokenExpire = new Date(Date.now() + accessExpiresIn * 1000);
156
+ if (refreshExpiresIn !== null) {
157
+ this.refreshTokenExpire = new Date(
158
+ Date.now() + refreshExpiresIn * 1000
159
+ );
160
+ }
161
+
162
+ await this.notify(this.DLGT_TOKEN_UPDATE);
163
+ }
164
+
165
+ /**
166
+ * Gets the OAuth authorization URL for initiating the authorization code flow.
167
+ *
168
+ * @returns {string|null} The authorization URL
169
+ */
170
+ getAuthorizationUri() {
171
+ return this.authorizationUri;
172
+ }
173
+
174
+ /**
175
+ * Returns authorization requirements for this OAuth flow.
176
+ *
177
+ * @returns {{url: string|null, type: string}} Authorization requirements
178
+ */
179
+ getAuthorizationRequirements() {
180
+ return {
181
+ url: this.getAuthorizationUri(),
182
+ type: 'oauth2',
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Exchanges an authorization code for access and refresh tokens.
188
+ * Requires client_id, client_secret, redirect_uri, and tokenUri to be set.
189
+ *
190
+ * @param {string} code - The authorization code from the OAuth callback
191
+ * @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
192
+ */
193
+ async getTokenFromCode(code) {
194
+ const params = new URLSearchParams();
195
+ params.append('grant_type', 'authorization_code');
196
+ params.append('client_id', this.client_id);
197
+ params.append('client_secret', this.client_secret);
198
+ params.append('redirect_uri', this.redirect_uri);
199
+ params.append('scope', this.scope);
200
+ params.append('code', code);
201
+ const options = {
202
+ body: params,
203
+ headers: {
204
+ 'Content-Type': 'application/x-www-form-urlencoded',
205
+ },
206
+ url: this.tokenUri,
207
+ };
208
+ const response = await this._post(options, false);
209
+ await this.setTokens(response);
210
+ return response;
211
+ }
212
+
213
+ /**
214
+ * Exchanges an authorization code for tokens using Basic Auth header.
215
+ * Alternative to getTokenFromCode() for OAuth servers requiring Basic Auth.
216
+ * Override getTokenFromCode() in child class to use this instead.
217
+ *
218
+ * @param {string} code - The authorization code from the OAuth callback
219
+ * @returns {Promise<Object>} Token response containing access_token, refresh_token, etc.
220
+ */
221
+ async getTokenFromCodeBasicAuthHeader(code) {
222
+ const params = new URLSearchParams();
223
+ params.append('grant_type', 'authorization_code');
224
+ params.append('client_id', this.client_id);
225
+ params.append('redirect_uri', this.redirect_uri);
226
+ params.append('code', code);
227
+
228
+ const options = {
229
+ body: params,
230
+ headers: {
231
+ 'Content-Type': 'application/x-www-form-urlencoded',
232
+ Authorization: `Basic ${Buffer.from(
233
+ `${this.client_id}:${this.client_secret}`
234
+ ).toString('base64')}`,
235
+ },
236
+ url: this.tokenUri,
237
+ };
238
+
239
+ const response = await this._post(options, false);
240
+ await this.setTokens(response);
241
+ return response;
242
+ }
243
+
244
+ /**
245
+ * Refreshes the access token using the refresh token.
246
+ * Used for authorization_code and password grant types.
247
+ *
248
+ * @param {Object} refreshTokenObject - Object containing refresh_token
249
+ * @param {string} refreshTokenObject.refresh_token - The refresh token
250
+ * @returns {Promise<Object>} New token response
251
+ */
252
+ async refreshAccessToken(refreshTokenObject) {
253
+ this.access_token = undefined;
254
+ const params = new URLSearchParams();
255
+ params.append('grant_type', 'refresh_token');
256
+ params.append('client_id', this.client_id);
257
+ params.append('client_secret', this.client_secret);
258
+ params.append('refresh_token', refreshTokenObject.refresh_token);
259
+ params.append('redirect_uri', this.redirect_uri);
260
+
261
+ const options = {
262
+ body: params,
263
+ url: this.tokenUri,
264
+ headers: {
265
+ 'Content-Type': 'application/x-www-form-urlencoded',
266
+ },
267
+ };
268
+ this.logger.debug('Refreshing access token', {
269
+ eventName: `${this.logger.name}.token_refresh_request`,
270
+ });
271
+ const response = await this._post(options, false);
272
+ await this.setTokens(response);
273
+ return response;
274
+ }
275
+
276
+ /**
277
+ * Adds OAuth Bearer token to request headers.
278
+ * Clears any existing Authorization header first to prevent stale tokens
279
+ * from being reused after failed refresh attempts.
280
+ *
281
+ * @param {Object} headers - Headers object to modify
282
+ * @returns {Promise<Object>} Headers with Authorization added
283
+ */
284
+ async addAuthHeaders(headers) {
285
+ delete headers.Authorization;
286
+ if (this.access_token) {
287
+ headers.Authorization = `Bearer ${this.access_token}`;
288
+ }
289
+
290
+ return headers;
291
+ }
292
+
293
+ /**
294
+ * Checks if the requester has valid authentication.
295
+ *
296
+ * @returns {boolean} True if authenticated with valid tokens
297
+ */
298
+ isAuthenticated() {
299
+ return !!(
300
+ this.access_token !== null &&
301
+ this.refresh_token !== null &&
302
+ this.accessTokenExpire &&
303
+ this.refreshTokenExpire
304
+ );
305
+ }
306
+
307
+ /**
308
+ * Refreshes authentication based on the configured grant type.
309
+ * - For authorization_code/password: Uses refreshAccessToken() with refresh_token
310
+ * - For client_credentials: Uses getTokenFromClientCredentials() to get new token
311
+ *
312
+ * On failure, notifies delegates via DLGT_INVALID_AUTH.
313
+ *
314
+ * @returns {Promise<boolean>} True if refresh succeeded, false if failed
315
+ */
316
+ async refreshAuth() {
317
+ // The wrapper runs this check before it calls refreshAuth(). Callers
318
+ // that reach this method directly skip the wrapper, so the check runs
319
+ // here for them. The guard keeps it at one read per refresh.
320
+ if (
321
+ !this._isInsideRefreshFlow() &&
322
+ (await this._adoptNewerCredential())
323
+ ) {
324
+ return true;
325
+ }
326
+
327
+ try {
328
+ // Field names avoid the denied suffixes (token, secret), which
329
+ // would redact these booleans.
330
+ this.logger.debug('Starting token refresh', {
331
+ eventName: `${this.logger.name}.token_refresh_started`,
332
+ grantType: this.grant_type,
333
+ refreshTokenPresent: !!this.refresh_token,
334
+ clientIdPresent: !!this.client_id,
335
+ clientSecretPresent: !!this.client_secret,
336
+ tokenUri: this.tokenUri,
337
+ });
338
+
339
+ if (this.grant_type !== 'client_credentials') {
340
+ await this.refreshAccessToken({
341
+ refresh_token: this.refresh_token,
342
+ });
343
+ } else {
344
+ // getTokenFromClientCredentials() reports a failed token
345
+ // request itself and resolves to undefined. Without this
346
+ // check, the refresh counts as a success with no new token.
347
+ const tokenRes = await this.getTokenFromClientCredentials();
348
+ if (!tokenRes) return false;
349
+ }
350
+ this.logger.info('Token refresh succeeded', {
351
+ eventName: `${this.logger.name}.token_refreshed`,
352
+ });
353
+ return true;
354
+ } catch (error) {
355
+ const moduleName = this.delegate?.name ?? 'unknown module';
356
+ this.logger.debug('Token refresh failed', {
357
+ eventName: `${this.logger.name}.token_refresh_failed`,
358
+ statusCode:
359
+ error?.statusCode ?? error?.status ?? error?.response?.status,
360
+ reason: error?.message,
361
+ });
362
+
363
+ if (!this._isDefinitiveAuthRejection(error)) {
364
+ throw this._transportFailureError(error, moduleName);
365
+ }
366
+
367
+ if (await this._adoptNewerCredentialWithBackoff()) return true;
368
+
369
+ // The provider rejected the grant, and the store has nothing
370
+ // newer. The credential is dead.
371
+ this.telemetry?.count?.('frigg.auth.refresh_race_lost', 1, {
372
+ module: this._telemetryModuleLabel(),
373
+ });
374
+ // Send the status only. The refresh body contains the
375
+ // client_secret, and FetchError puts the body in its message
376
+ // outside prod.
377
+ await this.notify(this.DLGT_INVALID_AUTH, {
378
+ statusCode: error?.statusCode,
379
+ });
380
+ return false;
381
+ }
382
+ }
383
+
384
+ /**
385
+ * A timeout, a 429, or a 5xx from the token endpoint says nothing about
386
+ * the credential. The error must stay retryable: the caller fails loudly
387
+ * (worker throw → SQS retry → DLQ) and does not flag a healthy
388
+ * credential. This is a fresh Error on purpose. Outside prod, the
389
+ * original message can contain the request body, and the body carries
390
+ * the client_secret.
391
+ */
392
+ _transportFailureError(error, moduleName) {
393
+ const status =
394
+ error?.statusCode ?? error?.status ?? error?.response?.status;
395
+ const transportError = new Error(
396
+ `[Frigg] Token refresh transport failure for ${moduleName}` +
397
+ (status != null ? ` (status ${status})` : '')
398
+ );
399
+ transportError.statusCode = status;
400
+ transportError.isTokenRefreshTransportFailure = true;
401
+ return transportError;
402
+ }
403
+
404
+ /**
405
+ * A definitive rejection can mean that another invocation consumed this
406
+ * refresh token first. That invocation's write can be unreadable for a
407
+ * short time. Re-read with a bounded backoff before you decide that the
408
+ * credential is dead.
409
+ */
410
+ async _adoptNewerCredentialWithBackoff() {
411
+ for (const delayMs of this.credentialReloadBackoffMs) {
412
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
413
+ if (await this._adoptNewerCredential()) {
414
+ this.logger.info(
415
+ 'Adopted a newer credential after a refresh rejection',
416
+ { eventName: `${this.logger.name}.credential_adopted` }
417
+ );
418
+ this.telemetry?.count?.('frigg.auth.refresh_race_recovered', 1, {
419
+ module: this._telemetryModuleLabel(),
420
+ });
421
+ return true;
422
+ }
423
+ }
424
+ return false;
425
+ }
426
+
427
+ /**
428
+ * True when the token endpoint refused the grant. RFC 6749 §5.2 sets
429
+ * the status: 400, or 401 for invalid_client. A 429 or a 5xx is never a
430
+ * verdict on the credential. Body markers are only a fallback for SDK
431
+ * errors that have no status code. Production FetchErrors have a
432
+ * sanitized body, so a marker cannot be the primary signal.
433
+ */
434
+ _isDefinitiveAuthRejection(error) {
435
+ const status =
436
+ error?.statusCode ?? error?.status ?? error?.response?.status;
437
+ if (status !== undefined && status !== null) {
438
+ return status === 400 || status === 401;
439
+ }
440
+ const haystack = [
441
+ error?.message,
442
+ error?.body,
443
+ typeof error?.error === 'string' ? error.error : null,
444
+ error?.response?.data && JSON.stringify(error.response.data),
445
+ ]
446
+ .filter(Boolean)
447
+ .join(' ');
448
+ return /\b(invalid_grant|invalid_client)\b/i.test(haystack);
449
+ }
450
+
451
+ /**
452
+ * Adopts the credential stored in the database if it is newer than the
453
+ * credential from the instance. The refresh token decides "newer": a
454
+ * provider can rotate it and return an identical access-token string.
455
+ * The reload only reads. A reload failure is not fatal: a database blip
456
+ * must not change the auth behavior.
457
+ *
458
+ * @returns {Promise<boolean>} True if the module adopted a newer
459
+ * credential.
460
+ */
461
+ async _adoptNewerCredential() {
462
+ let stored = null;
463
+ try {
464
+ stored = await this.notify(this.DLGT_CREDENTIAL_RELOAD);
465
+ } catch (_) {
466
+ return false;
467
+ }
468
+ if (!stored?.refresh_token) return false;
469
+ if (stored.refresh_token === this.refresh_token) return false;
470
+
471
+ if (stored.access_token) {
472
+ this.access_token = stored.access_token;
473
+ }
474
+ this.refresh_token = stored.refresh_token;
475
+ if (stored.accessTokenExpire !== undefined) {
476
+ this.accessTokenExpire = stored.accessTokenExpire;
477
+ }
478
+ if (stored.refreshTokenExpire !== undefined) {
479
+ this.refreshTokenExpire = stored.refreshTokenExpire;
480
+ }
481
+ return true;
482
+ }
483
+
484
+ /**
485
+ * Obtains tokens using the Resource Owner Password Credentials grant.
486
+ * Requires username and password to be set.
487
+ *
488
+ * @returns {Promise<Object|undefined>} Token response or undefined on error
489
+ */
490
+ async getTokenFromUsernamePassword() {
491
+ try {
492
+ const url = this.tokenUri;
493
+
494
+ const body = {
495
+ username: this.username,
496
+ password: this.password,
497
+ grant_type: 'password',
498
+ };
499
+ const headers = {
500
+ 'Content-Type': 'application/json',
501
+ };
502
+
503
+ const tokenRes = await this._post({
504
+ url,
505
+ body,
506
+ headers,
507
+ });
508
+
509
+ await this.setTokens(tokenRes);
510
+ return tokenRes;
511
+ } catch (error) {
512
+ // Status only. This request's body holds the password or client
513
+ // secret, and FetchError embeds the body in its message outside
514
+ // prod, so forwarding the error itself would log the credential.
515
+ await this.notify(this.DLGT_INVALID_AUTH, {
516
+ statusCode: error?.statusCode,
517
+ });
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Obtains tokens using the Client Credentials grant.
523
+ * Used for server-to-server authentication without a user context.
524
+ * Requires client_id, client_secret, and optionally audience to be set.
525
+ *
526
+ * @returns {Promise<Object|undefined>} Token response or undefined on error
527
+ */
528
+ async getTokenFromClientCredentials() {
529
+ try {
530
+ const url = this.tokenUri;
531
+
532
+ const body = {
533
+ audience: this.audience,
534
+ client_id: this.client_id,
535
+ client_secret: this.client_secret,
536
+ grant_type: 'client_credentials',
537
+ };
538
+ const headers = {
539
+ 'Content-Type': 'application/json',
540
+ };
541
+
542
+ const tokenRes = await this._post({
543
+ url,
544
+ body,
545
+ headers,
546
+ });
547
+
548
+ await this.setTokens(tokenRes);
549
+ return tokenRes;
550
+ } catch (error) {
551
+ // Status only. This request's body holds the password or client
552
+ // secret, and FetchError embeds the body in its message outside
553
+ // prod, so forwarding the error itself would log the credential.
554
+ await this.notify(this.DLGT_INVALID_AUTH, {
555
+ statusCode: error?.statusCode,
556
+ });
557
+ }
558
+ }
559
+ }
560
+
561
+ module.exports = { OAuth2Requester };