@friggframework/core 2.0.0--canary.703.12b980b.0 → 2.0.0--canary.707.df80bfd.0
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/CHANGELOG.md +11 -0
- package/CLAUDE.md +28 -0
- package/README.md +105 -0
- package/application/commands/integration-commands.js +20 -10
- package/application/commands/scheduler-commands.js +11 -7
- package/application/commands/user-commands.js +17 -5
- package/application/index.js +6 -2
- package/core/Worker.js +31 -7
- package/core/create-handler.js +9 -0
- package/credential/repositories/credential-repository-documentdb.js +34 -0
- package/credential/repositories/credential-repository-interface.js +15 -0
- package/credential/repositories/credential-repository-mongo.js +23 -0
- package/credential/repositories/credential-repository-postgres.js +28 -0
- package/credential/use-cases/credential-operations-for-user.js +202 -0
- package/credential/use-cases/reauthorize-credential-for-user.js +73 -0
- package/database/config.js +48 -24
- package/database/documentdb-encryption-service.js +9 -22
- package/database/documentdb-utils.js +2 -2
- package/database/encryption/README.md +78 -14
- package/database/encryption/documentdb-encryption-service.md +78 -1
- package/database/encryption/encryption-config.js +219 -0
- package/database/encryption/encryption-schema-registry.js +121 -24
- package/database/encryption/field-encryption-service.js +69 -5
- package/database/encryption/integration-mapping-encryption.js +1 -1
- package/database/encryption/test-support/fake-ciphertext.js +15 -0
- package/database/organization-where.js +18 -0
- package/database/prisma.js +15 -38
- package/database/unique-violation.js +25 -0
- package/database/use-cases/check-encryption-health-use-case.js +43 -52
- package/errors/index.js +0 -4
- package/extensions/bootstrap-runner.js +43 -0
- package/extensions/extension-loader.js +230 -0
- package/extensions/index.js +44 -0
- package/extensions/initialize-app.js +123 -0
- package/extensions/prisma-composer.js +270 -0
- package/extensions/route-mounter.js +103 -0
- package/extensions/schema-composer.js +188 -0
- package/generated/prisma-mongodb/edge.js +33 -42
- package/generated/prisma-mongodb/index-browser.js +30 -39
- package/generated/prisma-mongodb/index.d.ts +3187 -2005
- package/generated/prisma-mongodb/index.js +33 -42
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +86 -59
- package/generated/prisma-mongodb/wasm.js +33 -42
- package/generated/prisma-postgresql/edge.js +33 -42
- package/generated/prisma-postgresql/index-browser.js +30 -39
- package/generated/prisma-postgresql/index.d.ts +3380 -2177
- package/generated/prisma-postgresql/index.js +33 -42
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +81 -57
- package/generated/prisma-postgresql/wasm.js +33 -42
- package/handlers/WEBHOOKS.md +0 -29
- package/handlers/app-definition-loader.js +128 -19
- package/handlers/app-handler-helpers.js +27 -8
- package/handlers/backend-utils.js +31 -25
- package/handlers/integration-event-dispatcher.js +3 -7
- package/handlers/middleware/admin-auth.js +14 -1
- package/handlers/routers/auth.js +46 -10
- package/handlers/routers/health.js +13 -4
- package/handlers/routers/integration-defined-routers.js +144 -92
- package/handlers/routers/integration-webhook-routers.js +100 -50
- package/handlers/workers/integration-defined-workers.js +5 -2
- package/index.js +66 -16
- package/infrastructure/scheduler/index.js +19 -6
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +216 -0
- package/infrastructure/scheduler/scheduled-job-repository.js +118 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +55 -7
- package/integrations/index.js +1 -14
- package/integrations/integration-base.js +2 -163
- package/integrations/integration-router.js +132 -102
- package/integrations/repositories/integration-mapping-repository-documentdb.js +0 -50
- package/integrations/repositories/integration-mapping-repository-interface.js +0 -15
- package/integrations/repositories/integration-mapping-repository-mongo.js +0 -29
- package/integrations/repositories/integration-mapping-repository-postgres.js +0 -30
- package/integrations/repositories/integration-mapping-repository.js +0 -29
- package/integrations/repositories/integration-not-found.js +14 -0
- package/integrations/repositories/integration-repository-documentdb.js +37 -9
- package/integrations/repositories/integration-repository-interface.js +17 -4
- package/integrations/repositories/integration-repository-mongo.js +64 -96
- package/integrations/repositories/integration-repository-postgres.js +63 -81
- package/integrations/repositories/process-repository-mongo.js +1 -1
- package/integrations/schedule-config.js +80 -0
- package/integrations/tests/doubles/test-integration-repository.js +12 -1
- package/integrations/use-cases/create-integration.js +56 -31
- package/integrations/use-cases/delete-integration-for-user.js +4 -4
- package/integrations/use-cases/dispatch-ongoing-syncs.js +100 -0
- package/integrations/use-cases/get-integration-for-user.js +2 -4
- package/integrations/use-cases/get-integration-instance.js +4 -4
- package/integrations/use-cases/get-integrations-for-user.js +79 -39
- package/integrations/use-cases/get-possible-integrations.js +2 -7
- package/integrations/use-cases/index.js +7 -1
- package/integrations/use-cases/integration-operations-for-user.js +256 -0
- package/integrations/use-cases/resolve-integration-ownership.js +177 -0
- package/integrations/use-cases/update-integration.js +4 -4
- package/integrations/utils/map-integration-dto.js +43 -14
- package/management-api/create-management-api-router.js +49 -0
- package/management-api/error-response.js +116 -0
- package/management-api/management-api-config.js +151 -0
- package/management-api/meta-router.js +76 -0
- package/management-api/openapi/build-openapi.js +144 -0
- package/management-api/openapi/components.js +415 -0
- package/management-api/openapi/v1-operations.js +83 -0
- package/management-api/openapi/v2-operations.js +264 -0
- package/management-api/route-registry.js +411 -0
- package/management-api/test-support/http-client.js +39 -0
- package/management-api/use-cases/get-management-api-meta.js +57 -0
- package/management-api/v1-deprecation.js +120 -0
- package/management-api/v2/create-v2-router.js +85 -0
- package/management-api/v2/dependencies.js +281 -0
- package/management-api/v2/dto.js +143 -0
- package/management-api/v2/handlers/authorization.js +94 -0
- package/management-api/v2/handlers/credentials.js +33 -0
- package/management-api/v2/handlers/entities.js +77 -0
- package/management-api/v2/handlers/integrations.js +128 -0
- package/management-api/v2/handlers/proxy.js +34 -0
- package/management-api/v2/request.js +85 -0
- package/management-api/v2/test-support/fake-dependencies.js +103 -0
- package/modules/domain/authorization-session.js +144 -0
- package/modules/entity-scope.js +15 -0
- package/modules/entity-type-registry.js +311 -0
- package/modules/index.js +14 -0
- package/modules/module-factory.js +12 -0
- package/modules/module.js +53 -2
- package/modules/proxy/proxy-bases.js +169 -0
- package/modules/proxy/proxy-errors.js +31 -0
- package/modules/proxy/proxy-path.js +183 -0
- package/modules/proxy/proxy-policy.js +277 -0
- package/modules/repositories/authorization-session-repository-documentdb.js +112 -0
- package/modules/repositories/authorization-session-repository-factory.js +31 -0
- package/modules/repositories/authorization-session-repository-interface.js +47 -0
- package/modules/repositories/authorization-session-repository-prisma.js +87 -0
- package/modules/repositories/entity-not-found.js +18 -0
- package/modules/repositories/module-repository-documentdb.js +51 -6
- package/modules/repositories/module-repository-interface.js +19 -2
- package/modules/repositories/module-repository-mongo.js +67 -74
- package/modules/repositories/module-repository-postgres.js +66 -74
- package/modules/repositories/module-repository.js +2 -1
- package/modules/requester/proxy-request.js +245 -0
- package/modules/requester/requester.js +35 -5
- package/modules/scope-config.js +192 -0
- package/modules/scope-policy.js +112 -0
- package/modules/scope-resolution.js +288 -0
- package/modules/scope-visibility.js +247 -0
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +45 -0
- package/modules/tests/doubles/ownership-matching.js +18 -0
- package/modules/use-cases/authorize-entity-for-user.js +61 -0
- package/modules/use-cases/create-entity-from-credential.js +113 -0
- package/modules/use-cases/entity-operations-for-user.js +162 -0
- package/modules/use-cases/entity-types.js +139 -0
- package/modules/use-cases/execute-entity-proxy-request.js +331 -0
- package/modules/use-cases/get-authorization-requirements.js +100 -0
- package/modules/use-cases/get-entities-for-user.js +26 -13
- package/modules/use-cases/get-entity-module-for-user.js +113 -0
- package/modules/use-cases/get-entity-options-by-id.js +10 -4
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +10 -4
- package/modules/use-cases/manage-access.js +21 -0
- package/modules/use-cases/multi-step-authorization.js +329 -0
- package/modules/use-cases/process-authorization-callback.js +53 -19
- package/modules/use-cases/refresh-entity-options.js +10 -4
- package/modules/use-cases/test-module-auth.js +10 -4
- package/package.json +6 -7
- package/prisma-mongodb/schema.prisma +86 -59
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +28 -0
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +26 -0
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +25 -0
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +28 -0
- package/prisma-postgresql/schema.prisma +81 -57
- package/providers/index.js +23 -0
- package/providers/resolve-provider.js +93 -0
- package/providers/runtime-detection.js +124 -0
- package/queues/index.js +25 -0
- package/queues/providers/index.js +11 -0
- package/queues/providers/netlify-background-provider.js +150 -0
- package/queues/providers/qstash-queue-provider.js +219 -0
- package/queues/providers/sqs-queue-provider.js +70 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-message-auth.js +193 -0
- package/queues/queue-provider-factory.js +101 -0
- package/queues/queue-provider.js +65 -0
- package/queues/queuer-util.js +2 -9
- package/queues/sqs-queue-client.js +81 -0
- package/types/index.d.ts +2 -0
- package/types/modules/index.d.ts +214 -0
- package/types/user/index.d.ts +134 -0
- package/user/adopter-jwt-config.js +177 -0
- package/user/adopter-jwt-errors.js +59 -0
- package/user/adopter-token-verifier.js +122 -0
- package/user/app-user-helpers.js +97 -0
- package/user/create-user-authentication.js +64 -0
- package/user/membership.js +51 -0
- package/user/repositories/organization-membership-record.js +19 -0
- package/user/repositories/organization-membership-repository-documentdb.js +106 -0
- package/user/repositories/organization-membership-repository-factory.js +42 -0
- package/user/repositories/organization-membership-repository-interface.js +57 -0
- package/user/repositories/organization-membership-repository-mongo.js +13 -0
- package/user/repositories/organization-membership-repository-postgres.js +29 -0
- package/user/repositories/organization-membership-repository-prisma.js +81 -0
- package/user/repositories/user-repository-documentdb.js +27 -2
- package/user/repositories/user-repository-interface.js +18 -0
- package/user/repositories/user-repository-mongo.js +24 -0
- package/user/repositories/user-repository-postgres.js +18 -0
- package/user/tests/doubles/adopter-jwt-fixtures.js +220 -0
- package/user/use-cases/authenticate-user.js +7 -1
- package/user/use-cases/ensure-organization-membership.js +92 -0
- package/user/use-cases/find-individual-users-by-organization.js +54 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +95 -124
- package/user/use-cases/get-user-from-x-frigg-headers.js +83 -80
- package/user/user.js +64 -1
- package/errors/mapping-already-exists-error.js +0 -14
- package/handlers/workers/integration-triggers.js +0 -25
- package/infrastructure/scheduler/recurrence.js +0 -204
- package/integrations/define/DESIGN.md +0 -122
- package/integrations/define/config.js +0 -98
- package/integrations/define/define-app.js +0 -66
- package/integrations/define/define-extension.js +0 -154
- package/integrations/define/define-integration.js +0 -335
- package/integrations/define/examples/deal-alerts.js +0 -140
- package/integrations/define/extension-binding.js +0 -174
- package/integrations/define/index.js +0 -11
- package/integrations/define/json-schema-lite.js +0 -68
- package/integrations/define/subscriptions.js +0 -186
- package/integrations/define/tests/fixtures/hubspot.js +0 -109
- package/integrations/define/tests/fixtures/slack.js +0 -26
- package/integrations/define/use.js +0 -39
- package/integrations/definitions/README.md +0 -278
- package/integrations/definitions/canonical.js +0 -11
- package/integrations/definitions/capabilities/AUDIT.md +0 -480
- package/integrations/definitions/capabilities/acmeerp.capabilities.json +0 -633
- package/integrations/definitions/capabilities/asana.capabilities.json +0 -279
- package/integrations/definitions/capabilities/calendly.capabilities.json +0 -385
- package/integrations/definitions/capabilities/docusign.capabilities.json +0 -314
- package/integrations/definitions/capabilities/fathom.capabilities.json +0 -238
- package/integrations/definitions/capabilities/github.capabilities.json +0 -585
- package/integrations/definitions/capabilities/hubspot.capabilities.json +0 -822
- package/integrations/definitions/capabilities/jira.capabilities.json +0 -391
- package/integrations/definitions/capabilities/klaviyo.capabilities.json +0 -503
- package/integrations/definitions/capabilities/linear.capabilities.json +0 -378
- package/integrations/definitions/capabilities/netsuite.capabilities.json +0 -535
- package/integrations/definitions/capabilities/notion.capabilities.json +0 -208
- package/integrations/definitions/capabilities/phone.capabilities.json +0 -511
- package/integrations/definitions/capabilities/pipedrive.capabilities.json +0 -430
- package/integrations/definitions/capabilities/quickbooks.capabilities.json +0 -635
- package/integrations/definitions/capabilities/salesforce.capabilities.json +0 -568
- package/integrations/definitions/capabilities/shopify.capabilities.json +0 -1128
- package/integrations/definitions/capabilities/slack.capabilities.json +0 -373
- package/integrations/definitions/capabilities/stripe.capabilities.json +0 -630
- package/integrations/definitions/capabilities/webflow.capabilities.json +0 -196
- package/integrations/definitions/capabilities/zendesk.capabilities.json +0 -422
- package/integrations/definitions/compile.js +0 -80
- package/integrations/definitions/config-form.js +0 -137
- package/integrations/definitions/examples/acme-erp-quickbooks.definition.json +0 -467
- package/integrations/definitions/examples/asana-slack.definition.json +0 -219
- package/integrations/definitions/examples/calendly-salesforce.definition.json +0 -459
- package/integrations/definitions/examples/docusign-salesforce.definition.json +0 -305
- package/integrations/definitions/examples/fathom-hubspot-notion.definition.json +0 -575
- package/integrations/definitions/examples/github-linear.definition.json +0 -322
- package/integrations/definitions/examples/hubspot-slack.definition.json +0 -427
- package/integrations/definitions/examples/klaviyo-hubspot.definition.json +0 -336
- package/integrations/definitions/examples/phone-hubspot.definition.json +0 -633
- package/integrations/definitions/examples/pipedrive-hubspot.definition.json +0 -136
- package/integrations/definitions/examples/pipedrive-phone.definition.json +0 -340
- package/integrations/definitions/examples/records/phone-hubspot.definition-version.json +0 -652
- package/integrations/definitions/examples/records/phone-hubspot.integration.json +0 -86
- package/integrations/definitions/examples/records/phone-hubspot.mappings.json +0 -39
- package/integrations/definitions/examples/records/phone-hubspot.workflow-run.json +0 -62
- package/integrations/definitions/examples/shopify-klaviyo.definition.json +0 -488
- package/integrations/definitions/examples/shopify-netsuite.definition.json +0 -622
- package/integrations/definitions/examples/stripe-quickbooks.definition.json +0 -476
- package/integrations/definitions/examples/webflow-hubspot-slack.definition.json +0 -262
- package/integrations/definitions/examples/zendesk-jira.definition.json +0 -604
- package/integrations/definitions/expressions.js +0 -229
- package/integrations/definitions/index.js +0 -48
- package/integrations/definitions/integration-runtime.js +0 -779
- package/integrations/definitions/interpreter.js +0 -104
- package/integrations/definitions/manifests.js +0 -177
- package/integrations/definitions/renderers/common.js +0 -67
- package/integrations/definitions/renderers/html.js +0 -129
- package/integrations/definitions/renderers/hubspot.js +0 -59
- package/integrations/definitions/renderers/preview.js +0 -154
- package/integrations/definitions/renderers/slack.js +0 -146
- package/integrations/definitions/repositories/definition-version-repository.js +0 -67
- package/integrations/definitions/repositories/workflow-run-repository.js +0 -80
- package/integrations/definitions/router.js +0 -170
- package/integrations/definitions/runtime.js +0 -268
- package/integrations/definitions/schema/capability-manifest.v0.1.schema.json +0 -549
- package/integrations/definitions/schema/installation.v0.1.schema.json +0 -130
- package/integrations/definitions/schema/integration-definition.v0.1.schema.json +0 -909
- package/integrations/definitions/schema/records.md +0 -105
- package/integrations/definitions/schema-check.js +0 -58
- package/integrations/definitions/scripts/check-manifests.js +0 -220
- package/integrations/definitions/scripts/validate.js +0 -66
- package/integrations/definitions/steps.js +0 -123
- package/integrations/definitions/sync-engine.js +0 -297
- package/integrations/definitions/tests/helpers-fake-crm.js +0 -49
- package/integrations/definitions/transforms.js +0 -91
- package/integrations/definitions/use-cases/definition-use-cases.js +0 -245
- package/integrations/definitions/validate.js +0 -436
- package/integrations/definitions/versioning.js +0 -176
- package/integrations/definitions/webhooks.js +0 -119
- package/integrations/integration-types.js +0 -73
- package/integrations/triggers.js +0 -244
- package/integrations/use-cases/run-trigger-tick.js +0 -155
- package/integrations/use-cases/run-trigger.js +0 -77
- package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +0 -66
- package/prisma-postgresql/migrations/20261019000000_workflow_run_waits/migration.sql +0 -7
- package/prisma-postgresql/migrations/20261019120000_definition_workspace/migration.sql +0 -5
|
@@ -56,6 +56,14 @@ model User {
|
|
|
56
56
|
integrations Integration[]
|
|
57
57
|
processes Process[]
|
|
58
58
|
|
|
59
|
+
// Organization memberships (ADR-056 §3.6). `organizationId` above is the legacy
|
|
60
|
+
// single-org pointer: set on a person's first link, never moved afterwards, and
|
|
61
|
+
// read as an implicit membership when no row exists for it.
|
|
62
|
+
// As an individual: the organizations this person belongs to.
|
|
63
|
+
memberships OrganizationMembership[] @relation("IndividualMemberships")
|
|
64
|
+
// As an organization: the people who belong to it.
|
|
65
|
+
orgMembers OrganizationMembership[] @relation("OrganizationMemberships")
|
|
66
|
+
|
|
59
67
|
@@unique([username, appUserId])
|
|
60
68
|
@@index([type])
|
|
61
69
|
@@index([appUserId])
|
|
@@ -126,6 +134,14 @@ model Entity {
|
|
|
126
134
|
moduleName String?
|
|
127
135
|
externalId String?
|
|
128
136
|
|
|
137
|
+
// Organization this entity was connected in (ADR-056 §3.6): the active
|
|
138
|
+
// organization for `individual`, the owner for `organization`, null for
|
|
139
|
+
// `global`. Null on an `individual` entity is also what every row written
|
|
140
|
+
// before this column existed has, and what `individualAcross: 'all'` writes.
|
|
141
|
+
// Deliberately not a relation: a deleted organization must leave its rows
|
|
142
|
+
// invisible, not turn them into "unscoped" ones (SetNull would do that).
|
|
143
|
+
organizationId String? @db.ObjectId
|
|
144
|
+
|
|
129
145
|
data Json @default("{}")
|
|
130
146
|
|
|
131
147
|
createdAt DateTime @default(now())
|
|
@@ -142,6 +158,7 @@ model Entity {
|
|
|
142
158
|
associationObjects AssociationObject[]
|
|
143
159
|
|
|
144
160
|
@@index([userId])
|
|
161
|
+
@@index([userId, organizationId])
|
|
145
162
|
@@index([externalId])
|
|
146
163
|
@@index([moduleName])
|
|
147
164
|
@@index([credentialId])
|
|
@@ -159,6 +176,11 @@ model Integration {
|
|
|
159
176
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
160
177
|
status IntegrationStatus @default(IN_CREATION)
|
|
161
178
|
|
|
179
|
+
// Organization this integration was created in (ADR-056 §3.6): the active
|
|
180
|
+
// organization at create time. Null for rows that predate the column. Not a
|
|
181
|
+
// relation, for the same reason as Entity.organizationId.
|
|
182
|
+
organizationId String? @db.ObjectId
|
|
183
|
+
|
|
162
184
|
// Configuration and version
|
|
163
185
|
config Json? // Integration configuration object
|
|
164
186
|
version String?
|
|
@@ -181,9 +203,9 @@ model Integration {
|
|
|
181
203
|
syncs Sync[]
|
|
182
204
|
mappings IntegrationMapping[]
|
|
183
205
|
processes Process[]
|
|
184
|
-
workflowRuns WorkflowRun[]
|
|
185
206
|
|
|
186
207
|
@@index([userId])
|
|
208
|
+
@@index([userId, organizationId])
|
|
187
209
|
@@index([status])
|
|
188
210
|
@@map("Integration")
|
|
189
211
|
}
|
|
@@ -455,66 +477,71 @@ model UsageCounter {
|
|
|
455
477
|
@@map("UsageCounter")
|
|
456
478
|
}
|
|
457
479
|
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
480
|
+
// ============================================================================
|
|
481
|
+
// MANAGEMENT API MODELS
|
|
482
|
+
// ============================================================================
|
|
483
|
+
|
|
484
|
+
/// Multi-step authorization state for Management API v2 (/api/v2/authorize,
|
|
485
|
+
/// /api/v2/credentials/:id/reauthorize). Short-lived: expired documents are
|
|
486
|
+
/// removed opportunistically. stepData is encrypted at rest.
|
|
487
|
+
model AuthorizationSession {
|
|
488
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
489
|
+
sessionId String @unique
|
|
490
|
+
userId String
|
|
491
|
+
entityType String
|
|
492
|
+
credentialId String?
|
|
493
|
+
currentStep Int @default(1)
|
|
494
|
+
maxSteps Int
|
|
495
|
+
stepData Json @default("{}")
|
|
496
|
+
expiresAt DateTime
|
|
497
|
+
completed Boolean @default(false)
|
|
498
|
+
|
|
499
|
+
createdAt DateTime @default(now())
|
|
500
|
+
updatedAt DateTime @updatedAt
|
|
501
|
+
|
|
502
|
+
@@index([userId, entityType])
|
|
503
|
+
@@index([expiresAt])
|
|
504
|
+
@@map("AuthorizationSession")
|
|
464
505
|
}
|
|
465
506
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
@@
|
|
486
|
-
@@index([
|
|
487
|
-
@@map("
|
|
507
|
+
// ============================================================================
|
|
508
|
+
// ORGANIZATION MEMBERSHIP (ADR-056 §3.6)
|
|
509
|
+
// ============================================================================
|
|
510
|
+
|
|
511
|
+
/// One person's membership of one organization. Unique on the pair, so adding a
|
|
512
|
+
/// membership is an idempotent upsert. `orgRole` is the organization role the
|
|
513
|
+
/// adopter token carried when the membership was created (MEMBER when it named none). NoAction on delete
|
|
514
|
+
/// because MongoDB gives no referential integrity: delete a user's memberships
|
|
515
|
+
/// before the user.
|
|
516
|
+
model OrganizationMembership {
|
|
517
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
518
|
+
individualUserId String @db.ObjectId
|
|
519
|
+
organizationUserId String @db.ObjectId
|
|
520
|
+
orgRole String @default("MEMBER")
|
|
521
|
+
createdAt DateTime @default(now())
|
|
522
|
+
|
|
523
|
+
individualUser User @relation("IndividualMemberships", fields: [individualUserId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
|
524
|
+
organizationUser User @relation("OrganizationMemberships", fields: [organizationUserId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
|
525
|
+
|
|
526
|
+
@@unique([individualUserId, organizationUserId])
|
|
527
|
+
@@index([organizationUserId])
|
|
528
|
+
@@map("OrganizationMembership")
|
|
488
529
|
}
|
|
489
530
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
/// The runtime's run id (run_...): how routes and resume jobs address a run
|
|
507
|
-
runKey String?
|
|
508
|
-
/// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.
|
|
509
|
-
waitingFor Json?
|
|
510
|
-
resumeAt DateTime?
|
|
511
|
-
startedAt DateTime
|
|
512
|
-
endedAt DateTime?
|
|
513
|
-
createdAt DateTime @default(now())
|
|
514
|
-
|
|
515
|
-
@@index([integrationId, runKey])
|
|
516
|
-
@@index([integrationId, startedAt])
|
|
517
|
-
@@index([integrationId, workflowId, startedAt])
|
|
518
|
-
@@index([integrationId, status])
|
|
519
|
-
@@map("WorkflowRun")
|
|
531
|
+
/// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
|
|
532
|
+
/// Used by providers without native one-time scheduling APIs (e.g., Netlify).
|
|
533
|
+
/// A cron function queries for due jobs and dispatches them to a queue.
|
|
534
|
+
model ScheduledJob {
|
|
535
|
+
id String @id @default(auto()) @map("_id") @db.ObjectId
|
|
536
|
+
scheduleName String @unique
|
|
537
|
+
scheduledAt DateTime
|
|
538
|
+
queueResourceId String
|
|
539
|
+
payload Json?
|
|
540
|
+
state String @default("PENDING")
|
|
541
|
+
|
|
542
|
+
createdAt DateTime @default(now())
|
|
543
|
+
updatedAt DateTime @updatedAt
|
|
544
|
+
|
|
545
|
+
@@index([state, scheduledAt])
|
|
546
|
+
@@map("ScheduledJob")
|
|
520
547
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
-- Multi-step authorization state for Management API v2 (/api/v2/authorize).
|
|
3
|
+
-- Short-lived rows; "stepData" is encrypted at rest by the application.
|
|
4
|
+
CREATE TABLE "AuthorizationSession" (
|
|
5
|
+
"id" SERIAL NOT NULL,
|
|
6
|
+
"sessionId" TEXT NOT NULL,
|
|
7
|
+
"userId" TEXT NOT NULL,
|
|
8
|
+
"entityType" TEXT NOT NULL,
|
|
9
|
+
"credentialId" TEXT,
|
|
10
|
+
"currentStep" INTEGER NOT NULL DEFAULT 1,
|
|
11
|
+
"maxSteps" INTEGER NOT NULL,
|
|
12
|
+
"stepData" JSONB NOT NULL DEFAULT '{}',
|
|
13
|
+
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
14
|
+
"completed" BOOLEAN NOT NULL DEFAULT false,
|
|
15
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
16
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
17
|
+
|
|
18
|
+
CONSTRAINT "AuthorizationSession_pkey" PRIMARY KEY ("id")
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
-- CreateIndex
|
|
22
|
+
CREATE UNIQUE INDEX "AuthorizationSession_sessionId_key" ON "AuthorizationSession"("sessionId");
|
|
23
|
+
|
|
24
|
+
-- CreateIndex
|
|
25
|
+
CREATE INDEX "AuthorizationSession_userId_entityType_idx" ON "AuthorizationSession"("userId", "entityType");
|
|
26
|
+
|
|
27
|
+
-- CreateIndex
|
|
28
|
+
CREATE INDEX "AuthorizationSession_expiresAt_idx" ON "AuthorizationSession"("expiresAt");
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
-- Organization memberships (ADR-056 §3.6). One row per person per organization.
|
|
3
|
+
-- "User"."organizationId" stays as the legacy single-org pointer. It is NOT
|
|
4
|
+
-- backfilled into this table: the domain (user/membership.js) reads it as an
|
|
5
|
+
-- implicit membership when no row exists (see docs/guides/organization-memberships.md).
|
|
6
|
+
CREATE TABLE "OrganizationMembership" (
|
|
7
|
+
"id" SERIAL NOT NULL,
|
|
8
|
+
"individualUserId" INTEGER NOT NULL,
|
|
9
|
+
"organizationUserId" INTEGER NOT NULL,
|
|
10
|
+
"orgRole" TEXT NOT NULL DEFAULT 'MEMBER',
|
|
11
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
|
|
13
|
+
CONSTRAINT "OrganizationMembership_pkey" PRIMARY KEY ("id")
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- CreateIndex
|
|
17
|
+
CREATE INDEX "OrganizationMembership_organizationUserId_idx" ON "OrganizationMembership"("organizationUserId");
|
|
18
|
+
|
|
19
|
+
-- CreateIndex
|
|
20
|
+
CREATE UNIQUE INDEX "OrganizationMembership_individualUserId_organizationUserId_key" ON "OrganizationMembership"("individualUserId", "organizationUserId");
|
|
21
|
+
|
|
22
|
+
-- AddForeignKey
|
|
23
|
+
ALTER TABLE "OrganizationMembership" ADD CONSTRAINT "OrganizationMembership_individualUserId_fkey" FOREIGN KEY ("individualUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
|
24
|
+
|
|
25
|
+
-- AddForeignKey
|
|
26
|
+
ALTER TABLE "OrganizationMembership" ADD CONSTRAINT "OrganizationMembership_organizationUserId_fkey" FOREIGN KEY ("organizationUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Entity and Integration carry the organization they were created in (ADR-056 §3.6).
|
|
2
|
+
-- Nullable, no default and no backfill: a row written before this migration keeps
|
|
3
|
+
-- NULL, and the domain (modules/entity-scope.js) reads NULL as "predates scoping"
|
|
4
|
+
-- (docs/guides/entity-scopes.md). No scope column: the owner's user type says it.
|
|
5
|
+
-- Deliberately no foreign key: a deleted organization must leave its rows
|
|
6
|
+
-- invisible, and ON DELETE SET NULL would turn them into unscoped ones.
|
|
7
|
+
--
|
|
8
|
+
-- Release note: the two CREATE INDEX statements below are plain (not
|
|
9
|
+
-- CONCURRENTLY, which Prisma migrations cannot run inside a transaction). Each
|
|
10
|
+
-- takes a SHARE lock on its table while it builds, so writes to "Entity" and
|
|
11
|
+
-- "Integration" wait for it. Both tables are small in a typical Frigg
|
|
12
|
+
-- deployment; on a very large one, create the indexes by hand first with
|
|
13
|
+
-- CREATE INDEX CONCURRENTLY IF NOT EXISTS under the same names, then deploy.
|
|
14
|
+
|
|
15
|
+
-- AlterTable
|
|
16
|
+
ALTER TABLE "Entity" ADD COLUMN "organizationId" INTEGER;
|
|
17
|
+
|
|
18
|
+
-- AlterTable
|
|
19
|
+
ALTER TABLE "Integration" ADD COLUMN "organizationId" INTEGER;
|
|
20
|
+
|
|
21
|
+
-- CreateIndex
|
|
22
|
+
CREATE INDEX "Entity_userId_organizationId_idx" ON "Entity"("userId", "organizationId");
|
|
23
|
+
|
|
24
|
+
-- CreateIndex
|
|
25
|
+
CREATE INDEX "Integration_userId_organizationId_idx" ON "Integration"("userId", "organizationId");
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
-- One-time scheduled jobs for the poll-and-dispatch scheduler
|
|
2
|
+
-- (NetlifySchedulerAdapter, friggframework/frigg#545): a cron function reads
|
|
3
|
+
-- PENDING rows that are due and dispatches them to a queue.
|
|
4
|
+
--
|
|
5
|
+
-- IF NOT EXISTS on purpose: #545 shipped this model in the Prisma schema with
|
|
6
|
+
-- no migration, so an app that ran its canary may already have the table from
|
|
7
|
+
-- `prisma db push`. On such a database this migration is a no-op; on a fresh
|
|
8
|
+
-- one it is the SQL `prisma migrate diff` generates.
|
|
9
|
+
|
|
10
|
+
-- CreateTable
|
|
11
|
+
CREATE TABLE IF NOT EXISTS "ScheduledJob" (
|
|
12
|
+
"id" SERIAL NOT NULL,
|
|
13
|
+
"scheduleName" TEXT NOT NULL,
|
|
14
|
+
"scheduledAt" TIMESTAMP(3) NOT NULL,
|
|
15
|
+
"queueResourceId" TEXT NOT NULL,
|
|
16
|
+
"payload" JSONB,
|
|
17
|
+
"state" TEXT NOT NULL DEFAULT 'PENDING',
|
|
18
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
19
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
20
|
+
|
|
21
|
+
CONSTRAINT "ScheduledJob_pkey" PRIMARY KEY ("id")
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
-- CreateIndex
|
|
25
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "ScheduledJob_scheduleName_key" ON "ScheduledJob"("scheduleName");
|
|
26
|
+
|
|
27
|
+
-- CreateIndex
|
|
28
|
+
CREATE INDEX IF NOT EXISTS "ScheduledJob_state_scheduledAt_idx" ON "ScheduledJob"("state", "scheduledAt");
|
|
@@ -56,6 +56,14 @@ model User {
|
|
|
56
56
|
integrations Integration[]
|
|
57
57
|
processes Process[]
|
|
58
58
|
|
|
59
|
+
// Organization memberships (ADR-056 §3.6). `organizationId` above is the legacy
|
|
60
|
+
// single-org pointer: set on a person's first link, never moved afterwards, and
|
|
61
|
+
// read as an implicit membership when no row exists for it.
|
|
62
|
+
// As an individual: the organizations this person belongs to.
|
|
63
|
+
memberships OrganizationMembership[] @relation("IndividualMemberships")
|
|
64
|
+
// As an organization: the people who belong to it.
|
|
65
|
+
orgMembers OrganizationMembership[] @relation("OrganizationMemberships")
|
|
66
|
+
|
|
59
67
|
@@unique([username, appUserId])
|
|
60
68
|
@@index([type])
|
|
61
69
|
@@index([appUserId])
|
|
@@ -123,6 +131,14 @@ model Entity {
|
|
|
123
131
|
moduleName String?
|
|
124
132
|
externalId String?
|
|
125
133
|
|
|
134
|
+
// Organization this entity was connected in (ADR-056 §3.6): the active
|
|
135
|
+
// organization for `individual`, the owner for `organization`, null for
|
|
136
|
+
// `global`. Null on an `individual` entity is also what every row written
|
|
137
|
+
// before this column existed has, and what `individualAcross: 'all'` writes.
|
|
138
|
+
// Deliberately not a relation: a deleted organization must leave its rows
|
|
139
|
+
// invisible, not turn them into "unscoped" ones (SetNull would do that).
|
|
140
|
+
organizationId Int?
|
|
141
|
+
|
|
126
142
|
data Json @default("{}")
|
|
127
143
|
|
|
128
144
|
createdAt DateTime @default(now())
|
|
@@ -136,6 +152,7 @@ model Entity {
|
|
|
136
152
|
associationObjects AssociationObject[]
|
|
137
153
|
|
|
138
154
|
@@index([userId])
|
|
155
|
+
@@index([userId, organizationId])
|
|
139
156
|
@@index([externalId])
|
|
140
157
|
@@index([moduleName])
|
|
141
158
|
@@index([credentialId])
|
|
@@ -152,6 +169,11 @@ model Integration {
|
|
|
152
169
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
153
170
|
status IntegrationStatus @default(IN_CREATION)
|
|
154
171
|
|
|
172
|
+
// Organization this integration was created in (ADR-056 §3.6): the active
|
|
173
|
+
// organization at create time. Null for rows that predate the column. Not a
|
|
174
|
+
// relation, for the same reason as Entity.organizationId.
|
|
175
|
+
organizationId Int?
|
|
176
|
+
|
|
155
177
|
// Configuration and version
|
|
156
178
|
config Json? // Integration configuration object
|
|
157
179
|
version String?
|
|
@@ -173,9 +195,9 @@ model Integration {
|
|
|
173
195
|
syncs Sync[]
|
|
174
196
|
mappings IntegrationMapping[]
|
|
175
197
|
processes Process[]
|
|
176
|
-
workflowRuns WorkflowRun[]
|
|
177
198
|
|
|
178
199
|
@@index([userId])
|
|
200
|
+
@@index([userId, organizationId])
|
|
179
201
|
@@index([status])
|
|
180
202
|
}
|
|
181
203
|
|
|
@@ -436,64 +458,66 @@ model UsageCounter {
|
|
|
436
458
|
@@index([metric, integrationType, window])
|
|
437
459
|
}
|
|
438
460
|
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
461
|
+
// ============================================================================
|
|
462
|
+
// MANAGEMENT API MODELS
|
|
463
|
+
// ============================================================================
|
|
464
|
+
|
|
465
|
+
/// Multi-step authorization state for Management API v2 (/api/v2/authorize,
|
|
466
|
+
/// /api/v2/credentials/:id/reauthorize). Short-lived: expired rows are removed
|
|
467
|
+
/// opportunistically. stepData is encrypted at rest.
|
|
468
|
+
model AuthorizationSession {
|
|
469
|
+
id Int @id @default(autoincrement())
|
|
470
|
+
sessionId String @unique
|
|
471
|
+
userId String
|
|
472
|
+
entityType String
|
|
473
|
+
credentialId String?
|
|
474
|
+
currentStep Int @default(1)
|
|
475
|
+
maxSteps Int
|
|
476
|
+
stepData Json @default("{}")
|
|
477
|
+
expiresAt DateTime
|
|
478
|
+
completed Boolean @default(false)
|
|
479
|
+
|
|
480
|
+
createdAt DateTime @default(now())
|
|
481
|
+
updatedAt DateTime @updatedAt
|
|
482
|
+
|
|
483
|
+
@@index([userId, entityType])
|
|
484
|
+
@@index([expiresAt])
|
|
445
485
|
}
|
|
446
486
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
@@
|
|
466
|
-
@@index([definitionId, status])
|
|
467
|
-
@@index([workspaceId, status])
|
|
487
|
+
// ============================================================================
|
|
488
|
+
// ORGANIZATION MEMBERSHIP (ADR-056 §3.6)
|
|
489
|
+
// ============================================================================
|
|
490
|
+
|
|
491
|
+
/// One person's membership of one organization. Unique on the pair, so adding a
|
|
492
|
+
/// membership is an idempotent upsert. `orgRole` is the organization role the
|
|
493
|
+
/// adopter token carried when the membership was created (MEMBER when it named none).
|
|
494
|
+
model OrganizationMembership {
|
|
495
|
+
id Int @id @default(autoincrement())
|
|
496
|
+
individualUserId Int
|
|
497
|
+
organizationUserId Int
|
|
498
|
+
orgRole String @default("MEMBER")
|
|
499
|
+
createdAt DateTime @default(now())
|
|
500
|
+
|
|
501
|
+
individualUser User @relation("IndividualMemberships", fields: [individualUserId], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
502
|
+
organizationUser User @relation("OrganizationMemberships", fields: [organizationUserId], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
|
503
|
+
|
|
504
|
+
@@unique([individualUserId, organizationUserId])
|
|
505
|
+
@@index([organizationUserId])
|
|
468
506
|
}
|
|
469
507
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
trace Json @default("[]")
|
|
486
|
-
/// The runtime's run id (run_...): how routes and resume jobs address a run
|
|
487
|
-
runKey String?
|
|
488
|
-
/// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.
|
|
489
|
-
waitingFor Json?
|
|
490
|
-
resumeAt DateTime?
|
|
491
|
-
startedAt DateTime
|
|
492
|
-
endedAt DateTime?
|
|
493
|
-
createdAt DateTime @default(now())
|
|
494
|
-
|
|
495
|
-
@@index([integrationId, runKey])
|
|
496
|
-
@@index([integrationId, startedAt])
|
|
497
|
-
@@index([integrationId, workflowId, startedAt])
|
|
498
|
-
@@index([integrationId, status])
|
|
508
|
+
/// One-time scheduled jobs for poll-and-dispatch scheduling pattern.
|
|
509
|
+
/// Used by providers without native one-time scheduling APIs (e.g., Netlify).
|
|
510
|
+
/// A cron function queries for due jobs and dispatches them to a queue.
|
|
511
|
+
model ScheduledJob {
|
|
512
|
+
id Int @id @default(autoincrement())
|
|
513
|
+
scheduleName String @unique
|
|
514
|
+
scheduledAt DateTime
|
|
515
|
+
queueResourceId String
|
|
516
|
+
payload Json?
|
|
517
|
+
state String @default("PENDING")
|
|
518
|
+
|
|
519
|
+
createdAt DateTime @default(now())
|
|
520
|
+
updatedAt DateTime @updatedAt
|
|
521
|
+
|
|
522
|
+
@@index([state, scheduledAt])
|
|
499
523
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const {
|
|
2
|
+
resolveProvider,
|
|
3
|
+
determineProviderName,
|
|
4
|
+
providerPackageName,
|
|
5
|
+
KNOWN_PROVIDERS,
|
|
6
|
+
} = require('./resolve-provider');
|
|
7
|
+
const {
|
|
8
|
+
isDeployedRuntime,
|
|
9
|
+
isAwsLambdaRuntime,
|
|
10
|
+
isLocalRun,
|
|
11
|
+
resolveActiveProvider,
|
|
12
|
+
} = require('./runtime-detection');
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
resolveProvider,
|
|
16
|
+
determineProviderName,
|
|
17
|
+
providerPackageName,
|
|
18
|
+
KNOWN_PROVIDERS,
|
|
19
|
+
isDeployedRuntime,
|
|
20
|
+
isAwsLambdaRuntime,
|
|
21
|
+
isLocalRun,
|
|
22
|
+
resolveActiveProvider,
|
|
23
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves a provider name from the appDefinition into the corresponding
|
|
5
|
+
* @friggframework/provider-{name} package. This is the bridge between
|
|
6
|
+
* `appDefinition.provider` and the provider plugin interface.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order:
|
|
9
|
+
* 1. Explicit `providerName` argument
|
|
10
|
+
* 2. `appDefinition.provider`
|
|
11
|
+
* 3. `FRIGG_PROVIDER` environment variable
|
|
12
|
+
* 4. Default: 'aws'
|
|
13
|
+
*
|
|
14
|
+
* Package naming convention:
|
|
15
|
+
* provider: 'netlify' → require('@friggframework/provider-netlify')
|
|
16
|
+
* provider: 'aws' → require('@friggframework/provider-aws')
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const provider = resolveProvider({ provider: 'netlify' });
|
|
20
|
+
* // provider.name === 'netlify'
|
|
21
|
+
* // provider.deploy(appDefinition, options)
|
|
22
|
+
* // provider.createHandler(options)
|
|
23
|
+
* // provider.QueueProvider (class)
|
|
24
|
+
* // etc.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const KNOWN_PROVIDERS = ['aws', 'netlify'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Determine the provider name from available sources.
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} [appDefinition] - Frigg app definition
|
|
33
|
+
* @param {string} [providerName] - Explicit override
|
|
34
|
+
* @returns {string} Provider name (e.g. 'netlify', 'aws')
|
|
35
|
+
*/
|
|
36
|
+
function determineProviderName(appDefinition, providerName) {
|
|
37
|
+
if (providerName) {
|
|
38
|
+
return providerName;
|
|
39
|
+
}
|
|
40
|
+
if (appDefinition?.provider) {
|
|
41
|
+
return appDefinition.provider;
|
|
42
|
+
}
|
|
43
|
+
if (process.env.FRIGG_PROVIDER) {
|
|
44
|
+
return process.env.FRIGG_PROVIDER;
|
|
45
|
+
}
|
|
46
|
+
return 'aws';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a provider name to a package name.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} name - Provider name (e.g. 'netlify')
|
|
53
|
+
* @returns {string} npm package name (e.g. '@friggframework/provider-netlify')
|
|
54
|
+
*/
|
|
55
|
+
function providerPackageName(name) {
|
|
56
|
+
return `@friggframework/provider-${name}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve and load the provider plugin for the given app definition.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} [appDefinition] - Frigg app definition (reads .provider)
|
|
63
|
+
* @param {Object} [options]
|
|
64
|
+
* @param {string} [options.provider] - Explicit provider name override
|
|
65
|
+
* @returns {Object} Provider plugin (conforms to the provider plugin interface)
|
|
66
|
+
* @throws {Error} If the provider package is not installed
|
|
67
|
+
*/
|
|
68
|
+
function resolveProvider(appDefinition, options = {}) {
|
|
69
|
+
const name = determineProviderName(appDefinition, options.provider);
|
|
70
|
+
const packageName = providerPackageName(name);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
return require(packageName);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
76
|
+
const hint = KNOWN_PROVIDERS.includes(name)
|
|
77
|
+
? `Install it with: npm install ${packageName}`
|
|
78
|
+
: `Is '${name}' a valid Frigg provider? Known providers: ${KNOWN_PROVIDERS.join(', ')}`;
|
|
79
|
+
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Provider '${name}' specified in appDefinition but package '${packageName}' is not installed.\n ${hint}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
resolveProvider,
|
|
90
|
+
determineProviderName,
|
|
91
|
+
providerPackageName,
|
|
92
|
+
KNOWN_PROVIDERS,
|
|
93
|
+
};
|