@friggframework/core 2.0.0-next.12 → 2.0.0-next.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -1
- package/CLAUDE.md +830 -0
- package/README.md +996 -50
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-script-execution-repository-interface.js +171 -0
- package/admin-scripts/repositories/admin-script-execution-repository-mongo.js +238 -0
- package/admin-scripts/repositories/admin-script-execution-repository-postgres.js +278 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/README.md +451 -0
- package/application/commands/admin-script-commands.js +359 -0
- package/application/commands/credential-commands.js +262 -0
- package/application/commands/entity-commands.js +294 -0
- package/application/commands/integration-commands.js +419 -0
- package/application/commands/integration-mapping-commands.js +25 -0
- package/application/commands/process-commands.js +135 -0
- package/application/commands/report-commands.js +188 -0
- package/application/commands/scheduler-commands.js +263 -0
- package/application/commands/usage-commands.js +56 -0
- package/application/commands/user-commands.js +312 -0
- package/application/index.js +83 -0
- package/artifacts/repositories/artifact-repository-factory.js +19 -0
- package/artifacts/repositories/artifact-repository-interface.js +27 -0
- package/artifacts/repositories/artifact-repository-local.js +42 -0
- package/artifacts/repositories/artifact-repository-s3.js +61 -0
- package/assertions/index.js +0 -3
- package/core/CLAUDE.md +715 -0
- package/core/Worker.js +55 -24
- package/core/create-handler.js +121 -38
- package/core/index.js +8 -1
- package/core/invocation-scope.js +225 -0
- package/core/parameters-to-env.js +257 -0
- package/core/secrets-to-env.js +7 -6
- package/core/ssm-preload.mjs +34 -0
- package/credential/repositories/credential-active-type.js +32 -0
- package/credential/repositories/credential-repository-documentdb.js +355 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +113 -0
- package/credential/repositories/credential-repository-mongo.js +294 -0
- package/credential/repositories/credential-repository-postgres.js +312 -0
- package/credential/repositories/credential-repository.js +300 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +147 -0
- package/database/documentdb-encryption-service.js +334 -0
- package/database/documentdb-utils.js +196 -0
- package/database/encryption/README.md +863 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +404 -0
- package/database/encryption/field-encryption-service.js +254 -0
- package/database/encryption/integration-mapping-encryption.js +97 -0
- package/database/encryption/logger.js +82 -0
- package/database/encryption/prisma-encryption-extension.js +241 -0
- package/database/index.js +21 -21
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +138 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/resolve-migration-via-worker-use-case.js +49 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +94 -0
- package/database/utils/mongodb-schema-init.js +108 -0
- package/database/utils/prisma-runner.js +491 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +59 -57
- package/errors/index.js +6 -0
- package/errors/mapping-already-exists-error.js +14 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +5 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +5 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +386 -0
- package/generated/prisma-mongodb/index-browser.js +368 -0
- package/generated/prisma-mongodb/index.d.ts +27065 -0
- package/generated/prisma-mongodb/index.js +411 -0
- package/generated/prisma-mongodb/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +35 -0
- package/generated/prisma-mongodb/runtime/edge.js +35 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +17 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3982 -0
- package/generated/prisma-mongodb/runtime/library.js +147 -0
- package/generated/prisma-mongodb/runtime/react-native.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-mongodb/schema.prisma +455 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +393 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +5 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +5 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +407 -0
- package/generated/prisma-postgresql/index-browser.js +389 -0
- package/generated/prisma-postgresql/index.d.ts +29904 -0
- package/generated/prisma-postgresql/index.js +432 -0
- package/generated/prisma-postgresql/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +35 -0
- package/generated/prisma-postgresql/runtime/edge.js +35 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +17 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3982 -0
- package/generated/prisma-postgresql/runtime/library.js +147 -0
- package/generated/prisma-postgresql/runtime/react-native.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +85 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +38 -0
- package/generated/prisma-postgresql/schema.prisma +436 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +5 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +414 -0
- package/handlers/WEBHOOKS.md +682 -0
- package/handlers/app-definition-loader.js +103 -0
- package/handlers/app-handler-helpers.js +20 -6
- package/handlers/backend-utils.js +272 -46
- package/handlers/database-migration-handler.js +236 -0
- package/handlers/integration-event-dispatcher.js +68 -0
- package/handlers/middleware/admin-auth.js +73 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +4 -15
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +329 -0
- package/handlers/routers/health.js +518 -0
- package/handlers/routers/integration-defined-routers.js +85 -10
- package/handlers/routers/integration-webhook-routers.js +87 -0
- package/handlers/routers/user.js +27 -5
- package/handlers/routers/websocket.js +31 -9
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +438 -0
- package/handlers/workers/dlq-processor.js +89 -0
- package/handlers/workers/integration-defined-workers.js +13 -7
- package/index.js +123 -47
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +184 -0
- package/infrastructure/scheduler/index.js +33 -0
- package/infrastructure/scheduler/mock-scheduler-adapter.js +143 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +73 -0
- package/infrastructure/scheduler/scheduler-service-interface.js +47 -0
- package/integrations/EXTENSIONS.md +240 -0
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/extension.js +254 -0
- package/integrations/index.js +20 -10
- package/integrations/integration-base.js +777 -74
- package/integrations/integration-router.js +399 -180
- package/integrations/options.js +1 -1
- package/integrations/repositories/config-patch-shared.js +43 -0
- package/integrations/repositories/integration-mapping-query-pipeline.js +152 -0
- package/integrations/repositories/integration-mapping-query.js +193 -0
- package/integrations/repositories/integration-mapping-repository-documentdb.js +406 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +183 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +284 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +403 -0
- package/integrations/repositories/integration-mapping-repository.js +185 -0
- package/integrations/repositories/integration-repository-documentdb.js +354 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +219 -0
- package/integrations/repositories/integration-repository-mongo.js +502 -0
- package/integrations/repositories/integration-repository-postgres.js +550 -0
- package/integrations/repositories/process-repository-documentdb.js +311 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +136 -0
- package/integrations/repositories/process-repository-mongo.js +262 -0
- package/integrations/repositories/process-repository-postgres.js +380 -0
- package/integrations/repositories/process-update-ops-shared.js +112 -0
- package/integrations/repositories/report-id.js +13 -0
- package/integrations/tests/doubles/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +113 -0
- package/integrations/tests/doubles/test-integration-repository.js +146 -0
- package/integrations/use-cases/add-entities-to-integration.js +92 -0
- package/integrations/use-cases/create-integration.js +253 -0
- package/integrations/use-cases/create-process.js +130 -0
- package/integrations/use-cases/delete-integration-for-user.js +130 -0
- package/integrations/use-cases/find-integration-by-entity-external-id.js +74 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +76 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +89 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/list-integrations-by-entity-external-id.js +46 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/patch-integration-config.js +39 -0
- package/integrations/use-cases/process-errors.js +28 -0
- package/integrations/use-cases/remove-entities-from-integration.js +70 -0
- package/integrations/use-cases/update-integration-config.js +32 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +92 -0
- package/integrations/use-cases/update-process-metrics.js +222 -0
- package/integrations/use-cases/update-process-state.js +163 -0
- package/integrations/utils/assert-entity-ids-input.js +42 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/jest.config.js +2 -0
- package/logs/.eslintrc.json +26 -1
- package/logs/__fixtures__/events.js +82 -0
- package/logs/__fixtures__/matchers.js +45 -0
- package/logs/__fixtures__/secrets.js +59 -0
- package/logs/__fixtures__/vectors.js +329 -0
- package/logs/__fixtures__/with-env.js +45 -0
- package/logs/context.js +109 -0
- package/logs/contract-keys.js +67 -0
- package/logs/debug-shims.js +46 -0
- package/logs/denied-keys.js +21 -0
- package/logs/index.js +14 -3
- package/logs/jest-logger-setup.js +25 -0
- package/logs/levels.js +71 -0
- package/logs/logger-runtime.js +137 -0
- package/logs/logger.js +134 -46
- package/logs/record.js +276 -0
- package/logs/redact.js +328 -0
- package/logs/serialize.js +329 -0
- package/logs/sinks.js +93 -0
- package/logs/summarize-event.js +163 -0
- package/{module-plugin → modules}/index.js +0 -10
- package/modules/module-factory.js +56 -0
- package/modules/module.js +320 -0
- package/modules/repositories/module-repository-documentdb.js +350 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +145 -0
- package/modules/repositories/module-repository-mongo.js +436 -0
- package/modules/repositories/module-repository-postgres.js +481 -0
- package/modules/repositories/module-repository.js +369 -0
- package/modules/requester/api-key.js +52 -0
- package/modules/requester/oauth-2.js +561 -0
- package/modules/requester/requester.js +605 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +14 -10
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +71 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +34 -0
- package/modules/use-cases/get-module.js +74 -0
- package/modules/use-cases/process-authorization-callback.js +294 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +20 -0
- package/modules/utils/to-credential-id.js +14 -0
- package/package.json +91 -52
- package/prisma-mongodb/schema.prisma +455 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/20260422120000_add_entity_data_column/migration.sql +10 -0
- package/prisma-postgresql/migrations/20260422120001_create_process_table/migration.sql +48 -0
- package/prisma-postgresql/migrations/20260625000000_add_user_organization_id_index/migration.sql +2 -0
- package/prisma-postgresql/migrations/20260703000000_add_integration_status_in_creation_in_deletion/migration.sql +11 -0
- package/prisma-postgresql/migrations/20260703000001_integration_status_default_in_creation/migration.sql +8 -0
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +26 -0
- package/prisma-postgresql/migrations/20260706000000_add_admin_script_execution_and_schedule/migration.sql +61 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +436 -0
- package/queues/queue-delivery.js +41 -0
- package/queues/queuer-util.js +89 -21
- package/reporting/README.md +154 -0
- package/reporting/builtin-reports.js +6 -0
- package/reporting/index.js +13 -0
- package/reporting/report-base.js +49 -0
- package/reporting/reports/integrations-report.js +221 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/telemetry/README.md +331 -0
- package/telemetry/bind-telemetry-context.js +77 -0
- package/telemetry/canonical-counters.js +52 -0
- package/telemetry/exporters.js +85 -0
- package/telemetry/index.js +26 -0
- package/telemetry/instrument-handler.js +104 -0
- package/telemetry/no-op-telemetry.js +71 -0
- package/telemetry/north-star.js +103 -0
- package/telemetry/otel-telemetry.js +232 -0
- package/telemetry/plugin-subscribers.js +77 -0
- package/telemetry/telemetry-config.js +120 -0
- package/telemetry/telemetry-context.js +51 -0
- package/telemetry/telemetry-event-bus.js +58 -0
- package/telemetry/telemetry-runtime.js +164 -0
- package/telemetry/telemetry-service.js +52 -0
- package/telemetry/usage-rollup-subscriber.js +116 -0
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/associations/index.d.ts +0 -17
- package/types/core/index.d.ts +12 -4
- package/types/database/index.d.ts +10 -2
- package/types/encrypt/index.d.ts +5 -3
- package/types/errors/index.d.ts +24 -10
- package/types/integrations/index.d.ts +3 -8
- package/types/logs/index.d.ts +61 -2
- package/types/module-plugin/index.d.ts +20 -69
- package/types/syncs/index.d.ts +0 -17
- package/usage/README.md +54 -0
- package/usage/index.js +17 -0
- package/usage/repositories/usage-repository-documentdb.js +194 -0
- package/usage/repositories/usage-repository-factory.js +25 -0
- package/usage/repositories/usage-repository-interface.js +37 -0
- package/usage/repositories/usage-repository-prisma.js +146 -0
- package/usage/tracked-metrics.js +38 -0
- package/usage/usage-windows.js +24 -0
- package/user/repositories/user-repository-documentdb.js +426 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +214 -0
- package/user/repositories/user-repository-mongo.js +323 -0
- package/user/repositories/user-repository-postgres.js +377 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +143 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +82 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/assertions/is-equal.js +0 -17
- package/associations/model.js +0 -54
- package/database/models/IndividualUser.js +0 -76
- package/database/models/OrganizationUser.js +0 -29
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/models/UserModel.js +0 -7
- package/database/models/WebsocketConnection.js +0 -49
- package/database/mongo.js +0 -45
- package/database/mongoose.js +0 -5
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/encrypt/test-encrypt.js +0 -107
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/entity.js +0 -46
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/requester/api-key.js +0 -36
- package/module-plugin/requester/oauth-2.js +0 -219
- package/module-plugin/requester/requester.js +0 -165
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- package/syncs/model.js +0 -62
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const { findNearestBackendPackageJson } = require('@friggframework/core/utils');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { resolveTelemetryConfig } = require('../telemetry/telemetry-config');
|
|
5
|
+
const { registerDeniedKeys } = require('../logs/denied-keys');
|
|
6
|
+
const {
|
|
7
|
+
extractCredentialFieldsFromModules,
|
|
8
|
+
} = require('../database/encryption/encryption-schema-registry');
|
|
9
|
+
const {
|
|
10
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
11
|
+
} = require('../integrations/utils/map-integration-dto');
|
|
12
|
+
|
|
13
|
+
// Handlers without a database never load the encryption registry, so the
|
|
14
|
+
// credential leaf keys are registered for redaction here too.
|
|
15
|
+
function registerCredentialLogKeys(appDefinition, integrations) {
|
|
16
|
+
try {
|
|
17
|
+
extractCredentialFieldsFromModules(
|
|
18
|
+
getModulesDefinitionFromIntegrationClasses(integrations)
|
|
19
|
+
);
|
|
20
|
+
} catch {
|
|
21
|
+
// An odd integration shape must not stop the app from loading.
|
|
22
|
+
}
|
|
23
|
+
const schema = appDefinition.encryption?.schema;
|
|
24
|
+
if (schema && typeof schema === 'object') {
|
|
25
|
+
for (const config of Object.values(schema)) {
|
|
26
|
+
registerDeniedKeys(config?.fields);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Loads the App definition from the nearest backend package
|
|
33
|
+
* @function loadAppDefinition
|
|
34
|
+
* @description Searches for the nearest backend package.json, loads the corresponding index.js file,
|
|
35
|
+
* and extracts the application definition containing integrations and user configuration.
|
|
36
|
+
* @returns {{integrations: Array<object>, userConfig: object | null, adminScripts: Array<object>, reports: Array<object>, admin: object, telemetry: object, logging: object | null}} An object containing the application definition.
|
|
37
|
+
* @throws {Error} Throws error if backend package.json cannot be found.
|
|
38
|
+
* @throws {Error} Throws error if index.js file cannot be found in the backend directory.
|
|
39
|
+
* @example
|
|
40
|
+
* const { integrations, userConfig, telemetry } = loadAppDefinition();
|
|
41
|
+
* console.log(`Found ${integrations.length} integrations`);
|
|
42
|
+
*/
|
|
43
|
+
function loadAppDefinition() {
|
|
44
|
+
const backendPath = findNearestBackendPackageJson();
|
|
45
|
+
if (!backendPath) {
|
|
46
|
+
throw new Error('Could not find backend package.json');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const backendDir = path.dirname(backendPath);
|
|
50
|
+
const backendFilePath = path.join(backendDir, 'index.js');
|
|
51
|
+
if (!fs.existsSync(backendFilePath)) {
|
|
52
|
+
throw new Error('Could not find index.js');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const backendJsFile = require(backendFilePath);
|
|
56
|
+
const appDefinition = backendJsFile.Definition;
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
integrations = [],
|
|
60
|
+
user: userConfig = null,
|
|
61
|
+
adminScripts = [],
|
|
62
|
+
reports = [],
|
|
63
|
+
admin = {},
|
|
64
|
+
logging = null,
|
|
65
|
+
} = appDefinition;
|
|
66
|
+
|
|
67
|
+
registerCredentialLogKeys(appDefinition, integrations);
|
|
68
|
+
|
|
69
|
+
// Degrade consistently: an invalid telemetry block must never take down a
|
|
70
|
+
// router bundle that loads the app definition at module scope (telemetry is
|
|
71
|
+
// never allowed to break a handler). The singletons apply the same
|
|
72
|
+
// fall-back, so all consumers behave identically.
|
|
73
|
+
let telemetry;
|
|
74
|
+
try {
|
|
75
|
+
telemetry = resolveTelemetryConfig(appDefinition);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.warn(
|
|
78
|
+
`[Frigg][telemetry] invalid telemetry config, defaulting to disabled: ${
|
|
79
|
+
error && error.message
|
|
80
|
+
}`
|
|
81
|
+
);
|
|
82
|
+
telemetry = {
|
|
83
|
+
exporter: { type: 'none' },
|
|
84
|
+
northStar: null,
|
|
85
|
+
sampleRatio: 1,
|
|
86
|
+
subscribers: [],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
integrations,
|
|
92
|
+
userConfig,
|
|
93
|
+
adminScripts,
|
|
94
|
+
reports,
|
|
95
|
+
admin,
|
|
96
|
+
telemetry,
|
|
97
|
+
logging,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
loadAppDefinition,
|
|
103
|
+
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
const { createHandler
|
|
1
|
+
const { createHandler } = require('@friggframework/core');
|
|
2
|
+
const { getLogger } = require('../logs');
|
|
3
|
+
const { summarizeExpressRequest } = require('../logs/summarize-event');
|
|
2
4
|
const express = require('express');
|
|
3
5
|
const bodyParser = require('body-parser');
|
|
4
6
|
const cors = require('cors');
|
|
5
7
|
const Boom = require('@hapi/boom');
|
|
6
|
-
const loadUserManager = require('./routers/middleware/loadUser');
|
|
7
8
|
const serverlessHttp = require('serverless-http');
|
|
8
9
|
|
|
10
|
+
const log = getLogger('frigg.http');
|
|
11
|
+
|
|
9
12
|
const createApp = (applyMiddleware) => {
|
|
10
13
|
const app = express();
|
|
11
14
|
|
|
@@ -14,15 +17,15 @@ const createApp = (applyMiddleware) => {
|
|
|
14
17
|
app.use(
|
|
15
18
|
cors({
|
|
16
19
|
origin: '*',
|
|
20
|
+
allowedHeaders: '*',
|
|
21
|
+
methods: '*',
|
|
17
22
|
credentials: true,
|
|
18
23
|
})
|
|
19
24
|
);
|
|
20
25
|
|
|
21
|
-
app.use(loadUserManager);
|
|
22
|
-
|
|
23
26
|
if (applyMiddleware) applyMiddleware(app);
|
|
24
27
|
|
|
25
|
-
//
|
|
28
|
+
// The express boundary: send the error response and log it one time.
|
|
26
29
|
app.use((err, req, res, next) => {
|
|
27
30
|
const boomError = err.isBoom ? err : Boom.boomify(err);
|
|
28
31
|
const {
|
|
@@ -30,9 +33,20 @@ const createApp = (applyMiddleware) => {
|
|
|
30
33
|
} = boomError;
|
|
31
34
|
|
|
32
35
|
if (statusCode >= 500) {
|
|
33
|
-
|
|
36
|
+
log.error('Request failed', {
|
|
37
|
+
eventName: 'frigg.http.request_failed',
|
|
38
|
+
statusCode,
|
|
39
|
+
invocation: summarizeExpressRequest(req),
|
|
40
|
+
error: boomError,
|
|
41
|
+
});
|
|
34
42
|
res.status(statusCode).json({ error: 'Internal Server Error' });
|
|
35
43
|
} else {
|
|
44
|
+
// A client error needs no stack; the logger scrubs the reason.
|
|
45
|
+
log.warn('Request rejected', {
|
|
46
|
+
eventName: 'frigg.http.request_rejected',
|
|
47
|
+
statusCode,
|
|
48
|
+
reason: boomError.message,
|
|
49
|
+
});
|
|
36
50
|
res.status(statusCode).json({ error: err.message });
|
|
37
51
|
}
|
|
38
52
|
});
|
|
@@ -1,38 +1,49 @@
|
|
|
1
|
-
const { createFriggBackend, Worker } = require('@friggframework/core');
|
|
2
|
-
const {
|
|
3
|
-
findNearestBackendPackageJson,
|
|
4
|
-
} = require('../../devtools/frigg-cli/utils/backend-path');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
|
|
8
|
-
const backendPath = findNearestBackendPackageJson();
|
|
9
|
-
if (!backendPath) {
|
|
10
|
-
throw new Error('Could not find backend package.json');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const backendDir = path.dirname(backendPath);
|
|
14
|
-
const backendFilePath = path.join(backendDir, 'index.js');
|
|
15
|
-
if (!fs.existsSync(backendFilePath)) {
|
|
16
|
-
throw new Error('Could not find index.js');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const backendJsFile = require(backendFilePath);
|
|
20
1
|
const { Router } = require('express');
|
|
21
|
-
const
|
|
2
|
+
const { runInContext, LOGGER_SCOPE_KEY } = require('../logs/context');
|
|
3
|
+
const { Worker } = require('@friggframework/core');
|
|
4
|
+
const {
|
|
5
|
+
IntegrationEventDispatcher,
|
|
6
|
+
} = require('./integration-event-dispatcher');
|
|
7
|
+
const {
|
|
8
|
+
GetIntegrationInstance,
|
|
9
|
+
} = require('../integrations/use-cases/get-integration-instance');
|
|
10
|
+
const { ModuleFactory } = require('../modules/module-factory');
|
|
11
|
+
const {
|
|
12
|
+
createProcessRepository,
|
|
13
|
+
} = require('../integrations/repositories/process-repository-factory');
|
|
14
|
+
const {
|
|
15
|
+
createIntegrationRepository,
|
|
16
|
+
} = require('../integrations/repositories/integration-repository-factory');
|
|
17
|
+
const {
|
|
18
|
+
createModuleRepository,
|
|
19
|
+
} = require('../modules/repositories/module-repository-factory');
|
|
20
|
+
const {
|
|
21
|
+
getModulesDefinitionFromIntegrationClasses,
|
|
22
|
+
} = require('../integrations/utils/map-integration-dto');
|
|
23
|
+
const { processNotFound } = require('../integrations/use-cases/process-errors');
|
|
22
24
|
|
|
23
|
-
const backend = createFriggBackend(appDefinition);
|
|
24
25
|
const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
25
26
|
const router = Router();
|
|
26
27
|
const { path, method, event } = routerObject;
|
|
28
|
+
|
|
27
29
|
console.log(
|
|
28
30
|
`Registering ${method} ${path} for ${IntegrationClass.Definition.name}`
|
|
29
31
|
);
|
|
32
|
+
|
|
30
33
|
router[method.toLowerCase()](path, async (req, res, next) => {
|
|
31
34
|
try {
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
const integrationInstance = new IntegrationClass();
|
|
36
|
+
// initialize() registers dynamic user actions AND merges any Tier 3
|
|
37
|
+
// Integration Extension events into instance.events before dispatch.
|
|
38
|
+
await integrationInstance.initialize();
|
|
39
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
40
|
+
integrationInstance
|
|
41
|
+
);
|
|
42
|
+
// Logs only: a route :integrationId is unauthenticated input.
|
|
43
|
+
const result = await runInContext(
|
|
44
|
+
{ [LOGGER_SCOPE_KEY]: { integrationId: req.params?.integrationId } },
|
|
45
|
+
() => dispatcher.dispatchHttp({ event, req, res, next })
|
|
46
|
+
);
|
|
36
47
|
res.json(result);
|
|
37
48
|
} catch (error) {
|
|
38
49
|
next(error);
|
|
@@ -41,38 +52,252 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
|
41
52
|
|
|
42
53
|
return router;
|
|
43
54
|
};
|
|
55
|
+
|
|
56
|
+
const initializeRepositories = () => {
|
|
57
|
+
const processRepository = createProcessRepository();
|
|
58
|
+
const integrationRepository = createIntegrationRepository();
|
|
59
|
+
const moduleRepository = createModuleRepository();
|
|
60
|
+
|
|
61
|
+
return { processRepository, integrationRepository, moduleRepository };
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const createModuleFactoryWithDefinitions = (
|
|
65
|
+
moduleRepository,
|
|
66
|
+
integrationClasses
|
|
67
|
+
) => {
|
|
68
|
+
const moduleDefinitions =
|
|
69
|
+
getModulesDefinitionFromIntegrationClasses(integrationClasses);
|
|
70
|
+
|
|
71
|
+
return new ModuleFactory({
|
|
72
|
+
moduleRepository,
|
|
73
|
+
moduleDefinitions,
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const loadIntegrationForWebhook = async (integrationId) => {
|
|
78
|
+
const { loadAppDefinition } = require('./app-definition-loader');
|
|
79
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
80
|
+
|
|
81
|
+
const { integrationRepository, moduleRepository } =
|
|
82
|
+
initializeRepositories();
|
|
83
|
+
|
|
84
|
+
const moduleFactory = createModuleFactoryWithDefinitions(
|
|
85
|
+
moduleRepository,
|
|
86
|
+
integrationClasses
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const getIntegrationInstance = new GetIntegrationInstance({
|
|
90
|
+
integrationRepository,
|
|
91
|
+
integrationClasses,
|
|
92
|
+
moduleFactory,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let integrationRecord;
|
|
96
|
+
try {
|
|
97
|
+
integrationRecord = await integrationRepository.findIntegrationById(
|
|
98
|
+
integrationId
|
|
99
|
+
);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error.message?.includes('not found')) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const instance = await getIntegrationInstance.execute(
|
|
108
|
+
integrationId,
|
|
109
|
+
integrationRecord.userId
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return instance;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Returns false only when the integration is confirmed gone; a transient
|
|
116
|
+
// lookup failure returns true so genuine errors keep their normal retry path.
|
|
117
|
+
const integrationExists = async (integrationId) => {
|
|
118
|
+
const integrationRepository = createIntegrationRepository();
|
|
119
|
+
try {
|
|
120
|
+
const record = await integrationRepository.findIntegrationById(
|
|
121
|
+
integrationId
|
|
122
|
+
);
|
|
123
|
+
return Boolean(record);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error.message?.includes('not found')) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const loadIntegrationForProcess = async (processId, integrationClass) => {
|
|
133
|
+
const { processRepository, integrationRepository, moduleRepository } =
|
|
134
|
+
initializeRepositories();
|
|
135
|
+
|
|
136
|
+
const moduleFactory = createModuleFactoryWithDefinitions(moduleRepository, [
|
|
137
|
+
integrationClass,
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
const getIntegrationInstance = new GetIntegrationInstance({
|
|
141
|
+
integrationRepository,
|
|
142
|
+
integrationClasses: [integrationClass],
|
|
143
|
+
moduleFactory,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!processId) {
|
|
147
|
+
throw new Error('processId is required in queue message data');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const process = await processRepository.findById(processId);
|
|
151
|
+
|
|
152
|
+
if (!process) {
|
|
153
|
+
throw processNotFound(`Process not found: ${processId}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const instance = await getIntegrationInstance.execute(
|
|
157
|
+
process.integrationId,
|
|
158
|
+
process.userId
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return instance;
|
|
162
|
+
};
|
|
163
|
+
|
|
44
164
|
const createQueueWorker = (integrationClass) => {
|
|
165
|
+
const integrationName = integrationClass.Definition.name;
|
|
166
|
+
|
|
45
167
|
class QueueWorker extends Worker {
|
|
46
|
-
async _run(params, context) {
|
|
168
|
+
async _run(params, context, delivery) {
|
|
169
|
+
const logCtx = {
|
|
170
|
+
integration: integrationName,
|
|
171
|
+
event: params.event,
|
|
172
|
+
processId: params.data?.processId,
|
|
173
|
+
integrationId: params.data?.integrationId,
|
|
174
|
+
};
|
|
175
|
+
|
|
47
176
|
try {
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
177
|
+
let integrationInstance;
|
|
178
|
+
|
|
179
|
+
// Prioritize processId first (for sync handler compatibility),
|
|
180
|
+
// then integrationId (for ANY event type that needs hydration),
|
|
181
|
+
// fallback to unhydrated instance
|
|
182
|
+
if (params.data?.processId) {
|
|
183
|
+
console.log(`[QueueWorker] hydrating by processId`, logCtx);
|
|
184
|
+
integrationInstance = await loadIntegrationForProcess(
|
|
185
|
+
params.data.processId,
|
|
186
|
+
integrationClass
|
|
187
|
+
);
|
|
188
|
+
console.log(`[QueueWorker] hydrated`, {
|
|
189
|
+
...logCtx,
|
|
190
|
+
integrationStatus: integrationInstance?.status,
|
|
191
|
+
hydratedIntegrationId: integrationInstance?.id,
|
|
192
|
+
});
|
|
193
|
+
if (
|
|
194
|
+
['DISABLED', 'ERROR', 'IN_DELETION'].includes(
|
|
195
|
+
integrationInstance?.status
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
console.warn(
|
|
199
|
+
`[${integrationName}] Integration for process ${params.data.processId} is ${integrationInstance.status}. Discarding ${params.event} message.`
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
} else if (params.data?.integrationId) {
|
|
54
204
|
console.log(
|
|
55
|
-
|
|
205
|
+
`[QueueWorker] hydrating by integrationId`,
|
|
206
|
+
logCtx
|
|
207
|
+
);
|
|
208
|
+
integrationInstance = await loadIntegrationForWebhook(
|
|
209
|
+
params.data.integrationId
|
|
56
210
|
);
|
|
211
|
+
if (!integrationInstance) {
|
|
212
|
+
console.warn(
|
|
213
|
+
`[${integrationName}] Integration ${params.data.integrationId} no longer exists. Discarding ${params.event} message.`
|
|
214
|
+
);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log(`[QueueWorker] hydrated`, {
|
|
218
|
+
...logCtx,
|
|
219
|
+
integrationStatus: integrationInstance?.status,
|
|
220
|
+
});
|
|
221
|
+
if (
|
|
222
|
+
['DISABLED', 'ERROR', 'IN_DELETION'].includes(
|
|
223
|
+
integrationInstance.status
|
|
224
|
+
)
|
|
225
|
+
) {
|
|
226
|
+
console.warn(
|
|
227
|
+
`[${integrationName}] Integration ${params.data.integrationId} is ${integrationInstance.status}. Discarding ${params.event} message.`
|
|
228
|
+
);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
57
231
|
} else {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
232
|
+
// Instantiates a DRY integration class without database records.
|
|
233
|
+
// There will be cases where we need to use helpers that the api modules can export.
|
|
234
|
+
// Like for HubSpot, the answer is to do a reverse lookup for the integration by the entity external ID (HubSpot Portal ID),
|
|
235
|
+
// and then you'll have the integration ID available to hydrate from.
|
|
62
236
|
console.log(
|
|
63
|
-
|
|
237
|
+
`[QueueWorker] no processId/integrationId — running dry instance`,
|
|
238
|
+
logCtx
|
|
64
239
|
);
|
|
240
|
+
integrationInstance = new integrationClass();
|
|
241
|
+
// Merge Tier 3 Integration Extension events into instance.events
|
|
242
|
+
// so extension-contributed queue events can be dispatched.
|
|
243
|
+
await integrationInstance.initialize();
|
|
65
244
|
}
|
|
66
|
-
|
|
245
|
+
|
|
246
|
+
const dispatcher = new IntegrationEventDispatcher(
|
|
247
|
+
integrationInstance
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
console.log(
|
|
251
|
+
`[QueueWorker] dispatching ${params.event}`,
|
|
252
|
+
logCtx
|
|
253
|
+
);
|
|
254
|
+
const result = await dispatcher.dispatchJob({
|
|
255
|
+
event: params.event,
|
|
67
256
|
data: params.data,
|
|
68
|
-
context,
|
|
257
|
+
context: context,
|
|
258
|
+
delivery,
|
|
69
259
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`Error in ${params.event} for ${integrationClass.Definition.name}:`,
|
|
74
|
-
error
|
|
260
|
+
console.log(
|
|
261
|
+
`[QueueWorker] ${params.event} dispatched ok`,
|
|
262
|
+
logCtx
|
|
75
263
|
);
|
|
264
|
+
return result;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
// Integration deleted mid-flight: no retry can succeed once
|
|
267
|
+
// it's gone, so discard instead of sending it to the DLQ.
|
|
268
|
+
if (
|
|
269
|
+
params.data?.integrationId &&
|
|
270
|
+
!(await integrationExists(params.data.integrationId))
|
|
271
|
+
) {
|
|
272
|
+
console.warn(
|
|
273
|
+
`[${integrationName}] Integration ${params.data.integrationId} was deleted mid-flight — discarding ${params.event} message (no retry)`
|
|
274
|
+
);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 4xx HTTP errors are permanent — the requester already
|
|
279
|
+
// attempted token refresh (401) and backoff (429/5xx).
|
|
280
|
+
// By the time a 4xx reaches here, retrying won't help.
|
|
281
|
+
// 408 (timeout) and 429 (rate limit) are excluded — both are transient.
|
|
282
|
+
const status = error.statusCode;
|
|
283
|
+
if (
|
|
284
|
+
status &&
|
|
285
|
+
status >= 400 &&
|
|
286
|
+
status < 500 &&
|
|
287
|
+
status !== 408 &&
|
|
288
|
+
status !== 429
|
|
289
|
+
) {
|
|
290
|
+
error.isHaltError = true;
|
|
291
|
+
console.warn(
|
|
292
|
+
`[${integrationName}] Permanent ${status} error for ${params.event} — message will be discarded (no retry)`,
|
|
293
|
+
{
|
|
294
|
+
...logCtx,
|
|
295
|
+
errorName: error.name,
|
|
296
|
+
errorMessage: error.message,
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
76
301
|
throw error;
|
|
77
302
|
}
|
|
78
303
|
}
|
|
@@ -81,7 +306,8 @@ const createQueueWorker = (integrationClass) => {
|
|
|
81
306
|
};
|
|
82
307
|
|
|
83
308
|
module.exports = {
|
|
84
|
-
...backend,
|
|
85
309
|
loadRouterFromObject,
|
|
86
310
|
createQueueWorker,
|
|
311
|
+
integrationExists,
|
|
312
|
+
loadIntegrationForWebhook,
|
|
87
313
|
};
|