@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
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
# Integrations published as definitions (v0.1)
|
|
2
|
-
|
|
3
|
-
ADR-063, implemented to the point where a definition can be validated, compiled into Frigg events, executed, rendered
|
|
4
|
-
and versioned. An integration becomes a **document** (what it connects, what it syncs, which workflows and UI it has)
|
|
5
|
-
run by **one** runtime class, instead of a hand-written class per integration.
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
definition (JSON, immutable per version) ─┐
|
|
9
|
-
capability manifests (one per API module) ─┼─ validate ─ compile ─▶ this.events (same shape as hand-written)
|
|
10
|
-
installation (Integration.config) ─┘ │
|
|
11
|
-
event arrives ─▶ runWorkflow ─▶ steps ─▶ Api methods
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Layout
|
|
15
|
-
|
|
16
|
-
| Path | What |
|
|
17
|
-
|---|---|
|
|
18
|
-
| `schema/integration-definition.v0.1.schema.json` | The definition format (JSON Schema 2020-12) |
|
|
19
|
-
| `schema/capability-manifest.v0.1.schema.json` | What an API module declares it can do |
|
|
20
|
-
| `schema/installation.v0.1.schema.json` | `Integration.config` for an installation |
|
|
21
|
-
| `schema/records.md` | Database records: definition versions, installation, mappings, workflow runs |
|
|
22
|
-
| `capabilities/*.capabilities.json` | Capability manifests. Nine are checked against the real Api classes (`capabilities/AUDIT.md`); the other twelve are still mock-shaped |
|
|
23
|
-
| `scripts/check-manifests.js` | Checks every manifest `method` exists on the real module's Api class, with the arguments it takes |
|
|
24
|
-
| `examples/*.definition.json` | Example definitions; `examples/records/` has Phone ↔ HubSpot database records |
|
|
25
|
-
| `expressions.js` | `$.ref`, `{{template \| filter}}` and conditions. No eval. |
|
|
26
|
-
| `steps.js`, `interpreter.js`, `transforms.js` | The step catalogue and the interpreter |
|
|
27
|
-
| `validate.js` | Structure + cross-references against the manifests (steps, templates, views, settings, `$.install`) |
|
|
28
|
-
| `manifests.js` | `validateManifest`: the manifests themselves (kinds, option lists, account fields) |
|
|
29
|
-
| `compile.js` | Definition + installation → events, webhook routes, recurring triggers, user actions |
|
|
30
|
-
| `sync-engine.js` | Syncs: link → identity → search → create/update, master rules, backfill pages, polls |
|
|
31
|
-
| `runtime.js` | Ports (Api calls, link store), `runWorkflow`, `runQuery`, `renderSurface`, `runLifecycle`, `resolveOptions`, `installContext` |
|
|
32
|
-
| `versioning.js` | `diff`, `checkBump`, `planUpgrade` |
|
|
33
|
-
| `integration-runtime.js` | `fromDefinitionDocument` (was `defineIntegrationClass`), `definitionDocumentHost`, `embedDefinitionDocument`: the IntegrationBase subclasses that run it all. `defineIntegration` (`../define`) is the code front door to the same compile target. |
|
|
34
|
-
| `use-cases/definition-use-cases.js` | Publish, deprecate, catalogue, history, upgrade plan; `createDefinitionCatalog()` (the store the runtime reads) |
|
|
35
|
-
| `repositories/` | `IntegrationDefinitionVersion` and `WorkflowRun` rows (MongoDB, PostgreSQL, DocumentDB) |
|
|
36
|
-
| `router.js` | The publish, catalogue, upgrade and run-history routes |
|
|
37
|
-
|
|
38
|
-
## The pieces of a definition
|
|
39
|
-
|
|
40
|
-
- **modules** — module keys used everywhere else (`phone`, `hubspot`), each naming a capability manifest.
|
|
41
|
-
- **settings** — integration-level choices (`fromNumber`); workflows can declare their own. An option setting draws
|
|
42
|
-
its choices from a module's option list, and may pass it inputs read from earlier choices
|
|
43
|
-
(`{ "source": "asana.project", "with": { "workspaceGid": "$.settings.workspace" } }`); `resolveOptions` fills the
|
|
44
|
-
dropdown and returns nothing until those inputs are chosen.
|
|
45
|
-
- **syncs** — records kept as copies on both sides: identity keys, field mapping (with transforms and master rules),
|
|
46
|
-
backfill and ongoing mode. A sync maintains the link + identity index every workflow uses (`links.find`).
|
|
47
|
-
A definition can have no workflows at all (`pipedrive-hubspot` is sync only).
|
|
48
|
-
- **links** — link sets: records on two modules known to be the same thing, with no sync behind them (a Calendly
|
|
49
|
-
invitee and the Salesforce Lead it booked as). Same storage and identity index as a sync, addressed as
|
|
50
|
-
`links.find { link: "people", ... }`.
|
|
51
|
-
- **workflows** — trigger (`webhook`, `schedule`, `userAction`, `syncRecord`) + steps. Default on or off.
|
|
52
|
-
- **queries** — read-only steps that return data, used by UI surfaces.
|
|
53
|
-
- **ui** — a surface in a host app (`hubspot` `crm.record.sidebar`): a query for data and a view tree from a fixed
|
|
54
|
-
component set. A generic renderer deployed once per host draws it; changing the card is a publish, not a deploy.
|
|
55
|
-
- **templates** — message bodies. **lifecycle** — steps run on install / uninstall (webhook subscriptions). Each
|
|
56
|
-
`onInstall` step with an `id` keeps its result as `$.install.runtime.installed.<id>`, which is how `onUninstall`
|
|
57
|
-
deletes exactly what install created.
|
|
58
|
-
|
|
59
|
-
## How it plugs into Frigg
|
|
60
|
-
|
|
61
|
-
- **One kind of integration**: every published definition is an integration type named by its id. The shared class
|
|
62
|
-
is a *provider* (`static IntegrationProvider`, see `../integration-types.js`): wherever Frigg turns `config.type`
|
|
63
|
-
into a class (create, get, update, delete, the queue worker, `GET /api/integrations` options), a published id
|
|
64
|
-
resolves to a class of its own, with its own name, label and modules. Registered code classes win over a
|
|
65
|
-
definition with the same name. What is shared is the deployment underneath: published types queue and book one-time jobs
|
|
66
|
-
on the provider's queue (`IntegrationBase.getDeploymentName()`), their triggers run on the provider's tick, and their webhooks reach the provider's function.
|
|
67
|
-
Logs, telemetry, schedule job ids and installation webhook URLs carry each integration's own name.
|
|
68
|
-
- **Hydration**: Frigg's use cases call `initialize()` on every hydrated integration; the class loads its pinned
|
|
69
|
-
definition there.
|
|
70
|
-
- **Webhooks**: install steps register `$.install.webhookUrls.<key>` (the URL names the module). An installation's
|
|
71
|
-
webhooks arrive at its own type's URL (`/api/<id>-integration/webhooks/<integrationId>`), which the provider's
|
|
72
|
-
webhook function also serves (devtools adds `/api/{integrationType}/webhooks[/{integrationId}]` to it; literal
|
|
73
|
-
routes of registered integrations win). App-level webhooks use the provider's URL, one per vendor app. The manifest's
|
|
74
|
-
`webhook` block (`webhooks.js`) splits batches and reads each event's topic, id and account; `verify` must return
|
|
75
|
-
true; handshakes are answered at receipt. App-level webhooks (`webhook.scope: "app"`, one URL per provider app)
|
|
76
|
-
are fanned out to the installations connected to each account.
|
|
77
|
-
- **Routes**: Frigg's existing authenticated ones. `GET/POST /api/integrations/:id/config/options[/refresh]` is the
|
|
78
|
-
generated configure form (dependent dropdowns on refresh); `POST /api/integrations/:id/actions/:event` runs a
|
|
79
|
-
button workflow (`WF_*`), a backfill, or renders a UI surface (`UI_*`, body `{ record }`).
|
|
80
|
-
|
|
81
|
-
`$.install` is the same everywhere a definition runs: integration settings, `webhookUrl`, `integrationId`,
|
|
82
|
-
`runtime.installed`, and `accounts.<module key>.<field>` — values about the connected account that the module's
|
|
83
|
-
manifest declares under `account` (`$.install.accounts.quickbooks.realmId`). Nothing undeclared is exposed.
|
|
84
|
-
|
|
85
|
-
## Code integrations use the same runtime
|
|
86
|
-
|
|
87
|
-
A hand-written `IntegrationBase` subclass can embed a definition and get everything a published integration has:
|
|
88
|
-
workflows with delay and approval pauses, syncs, UI surfaces, the generated configure form, and run history.
|
|
89
|
-
|
|
90
|
-
```js
|
|
91
|
-
class PhoneReports extends IntegrationBase { static Definition = { name: 'phone-reports', modules, … }; async notifyTeam(args) { … } }
|
|
92
|
-
module.exports = embedDefinitionDocument(PhoneReports, { definition: require('./phone-reports.definition.json'), manifests });
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- The class keeps its name, modules, routes, schedules, lifecycle and handlers; the definition's id must be its name.
|
|
96
|
-
- A `code` step with `ref: "#notifyTeam"` calls that method on the integration (checked at class creation; a
|
|
97
|
-
published definition cannot use `#` refs).
|
|
98
|
-
- What the definition does not declare stays the code's: with no webhook-triggered workflow or sync, the class's
|
|
99
|
-
own webhook handling runs; with no settings, its own configure form; its own schedules run beside the compiled ones.
|
|
100
|
-
- Each queued event the class handles itself is recorded as a run (`triggerType: "code"`) in the same history as its
|
|
101
|
-
workflows (`IntegrationEventDispatcher.dispatchJob` → `runEvent`). The history never decides a handler's outcome.
|
|
102
|
-
- The definition ships with the code, so installations run the deployed version; `upgrade` says so and moves nothing.
|
|
103
|
-
|
|
104
|
-
## Publishing, installing, upgrading
|
|
105
|
-
|
|
106
|
-
Definitions live in the `IntegrationDefinitionVersion` table (Prisma models in both schemas, PostgreSQL migration
|
|
107
|
-
`20261018000000_add_data_defined_integrations`), and runs in `WorkflowRun`. Point the class at the store:
|
|
108
|
-
|
|
109
|
-
```js
|
|
110
|
-
const { definitionDocumentHost, createDefinitionCatalog } = require('@friggframework/core/integrations/definitions');
|
|
111
|
-
// A database store is read asynchronously, so it goes with the shared class (one class, every definition).
|
|
112
|
-
const IntegrationRuntime = definitionDocumentHost({ definitions: createDefinitionCatalog(), manifests, modules });
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
| Route | Auth | |
|
|
116
|
-
|---|---|---|
|
|
117
|
-
| `POST /admin/integrations/versions` `{ document, notes?, publish?, strict? }` | admin key | Validate (both layers) and publish, or save a draft with `publish: false` |
|
|
118
|
-
| `GET /admin/integrations/:id/versions` | admin key | Version history |
|
|
119
|
-
| `POST /admin/integrations/:id/versions/:v/deprecate` | admin key | Stop new installs of a version |
|
|
120
|
-
| `GET /api/integrations` | user | What can be installed: code and published integrations in one list of options |
|
|
121
|
-
| `GET /api/integration-definitions` | user | The published ones, with their counts |
|
|
122
|
-
| `POST /api/integrations` with `config: { type: '<id>' }` | user | Install; pins the latest published version and its checksum |
|
|
123
|
-
| `GET` / `POST /api/integrations/:id/upgrade` `{ to? }` | user | Plan / apply an upgrade (409 with the plan while something is needed) |
|
|
124
|
-
| `GET /api/integrations/:id/runs` | user | Run history, newest first |
|
|
125
|
-
| `GET /api/integrations/:id/syncs` | user | Each sync's backfill progress and poll marks |
|
|
126
|
-
| `POST /api/integrations/:id/runs/:runId/decision` `{ decision, note? }` | user | Decide a paused approval. The approval step's `approvers` may decide without owning the installation; anyone else gets a 404 |
|
|
127
|
-
|
|
128
|
-
A signed-in user can also build their own: `POST /api/integration-definitions` `{ document, notes?, publish? }` publishes
|
|
129
|
-
into their workspace (the user's Frigg id; `IntegrationDefinitionVersion.workspaceId`). It is then listed and
|
|
130
|
-
installable for that workspace like any other integration (its install option carries `editable: true`, the cue for
|
|
131
|
-
the editor views), and invisible to everyone else: not listed, not resolvable when they install, not readable.
|
|
132
|
-
`GET /api/integration-definitions/:id` gives its owner every version, drafts included, and the newest document for
|
|
133
|
-
the editor. An id has one owner. The app's catalogue's ids and a code integration's name are public, and refused with
|
|
134
|
-
409 `ID_TAKEN`; an id owned by another workspace answers 404 `NOT_FOUND`, byte for byte what reading it answers, so one
|
|
135
|
-
tenant cannot learn that another's id exists (ADR-052 section 3). (The admin route, which is not a tenant, still gets 409.)
|
|
136
|
-
A published definition whose name matches a registered code integration is ignored (code wins); that is logged once per
|
|
137
|
-
process as `frigg.integrations.definition.shadowed_by_code`. PostgreSQL migration `20261019120000_definition_workspace`.
|
|
138
|
-
|
|
139
|
-
The installation
|
|
140
|
-
routes answer for every integration, code included: one with no run history, syncs or other versions gets `[]`, or a
|
|
141
|
-
plan that says there is nothing to move to, never a 404 for how it was made.
|
|
142
|
-
|
|
143
|
-
Published versions cannot change. Publishing a document that is identical to a published one returns that row. A
|
|
144
|
-
different document at the same version is refused. So is a version that is not newer than the latest, and so is a
|
|
145
|
-
semver bump smaller than the changes. Publishing goes through a conditional write, so two concurrent publishes of one
|
|
146
|
-
version cannot both win. With `memoryDefinitions()` (definitions bundled in the app) the admin routes answer 501.
|
|
147
|
-
|
|
148
|
-
## Previews: in the app
|
|
149
|
-
|
|
150
|
-
An integration shows up inside the apps it connects: a HubSpot card, a Slack message, digest or approval request. A
|
|
151
|
-
preview renders exactly that from the current settings and sample or real records, with the same query, view and step
|
|
152
|
-
code production runs (`renderers/`):
|
|
153
|
-
|
|
154
|
-
| Function | Does |
|
|
155
|
-
|---|---|
|
|
156
|
-
| `previewSurface({ def, installation, surfaceId, record, ports, host? })` | `{ surface, view, html, slack?, hubspot? }`. `slack` is Block Kit (`{ blocks, text, truncated }`), `hubspot` a data description of `@hubspot/ui-extensions` components (names and props checked against 0.17.1; `props` are real component props, a userAction button carries our own node-level `action` for the card to post, an `open_url` button is a real `href`), `html` the same card framed as its host looks. `host` defaults to the surface's own |
|
|
157
|
-
| `previewWorkflow({ def, installation, workflowId, event?, record?, manifests, fixtures? })` | Dry run: `{ status, sends, calls, trace, waiting? }`. Each call is `{ stepId, module, capability, kind, args, output, rendered }`; `rendered` is `{ html, slack? }` for writes (a Slack post renders as the message, anything else as a "would call" card) |
|
|
158
|
-
| `recordingPorts({ def, manifests, fixtures })` | Ports that record and answer instead of calling an API; give them to `previewSurface` for sample data |
|
|
159
|
-
| `viewToSlack`, `viewToHubSpot`, `viewToHtml`, `slackMessageToHtml` | The renderers on their own. `decodeAction(value)` reads a Slack button click back to `{ integrationId?, workflow, surface }` |
|
|
160
|
-
|
|
161
|
-
Dry-run rules: nothing is performed (the base ports' API function throws, and the recorder sits in front of it).
|
|
162
|
-
`fixtures` answers calls, keyed `"<module key>.<capability>"` (a value, or `(args) => value`; `code:<ref>` for code
|
|
163
|
-
steps, which are never run), otherwise the manifest's own record shape with ids filled. A webhook workflow with no
|
|
164
|
-
`event` runs on the manifest's sample payload. A pausing step ends the preview with `waiting`.
|
|
165
|
-
|
|
166
|
-
Everything that is record data is escaped for its target (mrkdwn `& < >` plus fenced `* _ ~`, HTML, text children for
|
|
167
|
-
HubSpot), links are http(s) only, Block Kit limits are met by truncating with a marker (50 blocks, 3000/2000/150
|
|
168
|
-
characters, 25 buttons), and nothing throws on an odd tree. Text a *definition* writes into a Slack message (templates)
|
|
169
|
-
is sent as authored: a preview shows it as sent, including any raw record data a template interpolates.
|
|
170
|
-
|
|
171
|
-
## Syncs
|
|
172
|
-
|
|
173
|
-
`sync-engine.js` runs `syncs`: backfill (a button or `onInstall`, one queued page per message, progress at
|
|
174
|
-
`GET /api/integrations/:id/syncs`), webhook or polling updates, both directions for `two_way`, per-field `master`
|
|
175
|
-
rules, and echo suppression. How each object is listed, fetched, searched and written is declared once in its
|
|
176
|
-
manifest (`objects.<obj>.sync`); ADR-063 §15b has the rules.
|
|
177
|
-
|
|
178
|
-
## Schedules
|
|
179
|
-
|
|
180
|
-
A schedule-triggered workflow or a polling sync is a recurring trigger (ADR-055), the same mechanism hand-written
|
|
181
|
-
integrations declare as `Definition.triggers` (ADR-063 §15a). Nothing is scheduled per installation: the class
|
|
182
|
-
declares `instanceTriggers: { tick: '5m' }`, so devtools provisions one tick function for it, and each tick compiles
|
|
183
|
-
every ENABLED installation's pinned definition with its config and queues the runs that are due. Mark one
|
|
184
|
-
`configurable` to let installations pick their own timing, optionally with a floor:
|
|
185
|
-
|
|
186
|
-
```json
|
|
187
|
-
{
|
|
188
|
-
"trigger": { "type": "schedule", "cron": "0 16 * * 1-5", "timezone": "America/New_York", "configurable": { "min": "1h" } },
|
|
189
|
-
"ongoing": { "mode": "poll", "every": "15m", "configurable": true }
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
(`trigger` belongs to a workflow, `ongoing` to a sync.)
|
|
194
|
-
|
|
195
|
-
The choice goes in `workflows.<id>.schedule` / `syncs.<id>.schedule` (the configure form offers the field). An
|
|
196
|
-
interval runs on whole multiples of the 5-minute tick; a cron runs on the first tick after it fires. Create, update,
|
|
197
|
-
upgrade and delete need no scheduler call: the next tick reads the new state. A run is deduped on
|
|
198
|
-
`tick:<event>:<scheduledFor>`, so a redelivered run happens once.
|
|
199
|
-
|
|
200
|
-
## Steps (v0.1)
|
|
201
|
-
|
|
202
|
-
Built-ins: `links.find`, `links.upsert`, `map`, `merge`, `template`, `set`, `stop`, `foreach` (≤1000), `branch`, `code`,
|
|
203
|
-
and the pausing steps `delay` and `approval`.
|
|
204
|
-
|
|
205
|
-
- `delay` (`for: '2h'` or `until`) and `approval` (`prompt`, `approvers`, `timeout`, `onTimeout`) end the run for now with
|
|
206
|
-
status `waiting` and carry it on later from the next step (ADR-063 §15c). Top level of a workflow only. Decide with
|
|
207
|
-
`POST /api/integrations/:id/runs/:runId/decision`; pending ones are `GET …/runs?status=waiting`.
|
|
208
|
-
|
|
209
|
-
- `map` rows read a `source` field or write a fixed `value` (`{ "target": "Line[0].DetailType", "value": "SalesItemLineDetail" }`).
|
|
210
|
-
- `merge` deep-merges objects (later wins, lists replaced).
|
|
211
|
-
- `foreach` takes `where` (skip items, checked before the cap) and `collect` (the step's result becomes one value per
|
|
212
|
-
item, which may read that iteration's steps); `do` is optional when `collect` is set. A digest is
|
|
213
|
-
`collect` + `{{steps.lines | join: '\n'}}`, not a string grown with `set`.
|
|
214
|
-
- Template filters include arithmetic as named, total functions: `plus`, `minus`, `times`, `divided_by`, `round`,
|
|
215
|
-
`abs`, `number`, `to_cents` / `from_cents`, `to_minor_units` / `from_minor_units` (currency-aware: JPY has no minor
|
|
216
|
-
unit, KWD three), `from_unix` / `to_unix`, and `join`, `size`, `first`. Bad input gives blank, never a throw. A
|
|
217
|
-
string that is exactly one tag keeps the value's type, so `"{{item.Amount | to_cents}}"` is the number 2000.
|
|
218
|
-
Everything else is `<moduleKey>.<object>.<verb>`, a capability the module's manifest declares and maps to one Api
|
|
219
|
-
method. There is no http, script or eval step. `code` calls a named, versioned function shipped in reviewed code
|
|
220
|
-
(`@scope/pkg#fn@1.2.0`) for logic that does not fit steps. Per step: `when` (skip), `onError`
|
|
221
|
-
(`fail` → the queue retries the run, `continue`, `stop`).
|
|
222
|
-
|
|
223
|
-
## Versioning
|
|
224
|
-
|
|
225
|
-
Published versions are immutable and each installation pins one. `checkBump` refuses a release whose declared semver
|
|
226
|
-
bump is smaller than its changes (patch: steps/templates/view/mapping; minor: additive, including a new link set or
|
|
227
|
-
changed option-list inputs; major: removed workflow or link set, required setting, identity change, new module). `planUpgrade` lists what a person must supply first and what the
|
|
228
|
-
runtime does (enable/disable workflows, rebuild links, re-run install steps). Queued messages carry the version
|
|
229
|
-
they were routed under and finish on it.
|
|
230
|
-
|
|
231
|
-
## Running the tests
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
npx jest --config '{}' --rootDir packages/core/integrations/definitions tests
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
`tests/persistence.test.js` runs the repositories and the publish rules against real databases. Each leg runs only
|
|
238
|
-
when its URL is set. Point them at a migrated PostgreSQL database and a MongoDB replica set with the schema pushed:
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
INTEGRATION_DEFINITIONS_POSTGRES_URL=postgresql://… INTEGRATION_DEFINITIONS_MONGO_URL=mongodb://… npx jest … tests
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Validating
|
|
245
|
-
|
|
246
|
-
Validation is two layers, and a document is valid only when both pass (`schema-check.js`):
|
|
247
|
-
|
|
248
|
-
1. **JSON Schema** (`schema/*.schema.json`): the published shape.
|
|
249
|
-
2. **References** (`validate.js`, `manifests.js`): what a schema cannot see — steps name declared capabilities,
|
|
250
|
-
`$.steps.x` is an earlier step, view paths exist, option inputs are declared, `$.install` paths resolve.
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
npm run validate:definitions --workspace @friggframework/core # bundled manifests + examples
|
|
254
|
-
node packages/core/integrations/definitions/scripts/validate.js my.definition.json --strict
|
|
255
|
-
node packages/core/integrations/definitions/scripts/validate.js --manifests ./capabilities --json defs/
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
Exit codes: `0` valid, `1` invalid (or a warning with `--strict`), `2` could not run (unreadable JSON, missing file,
|
|
259
|
-
unknown option, `ajv` not installed). Manifests are checked first and an invalid manifest fails the run, because a
|
|
260
|
-
definition is only as trustworthy as the manifests it is checked against.
|
|
261
|
-
|
|
262
|
-
A manifest is only as real as its methods. `scripts/check-manifests.js <modules-dir>...` loads each module's Api class
|
|
263
|
-
(no network, nothing instantiated) and checks that every capability's `method` exists and is called the way it takes its
|
|
264
|
-
arguments (`args`: positional inputs, `"$rest"`, or a `{ "value" }` constant; no `args` means one object of all inputs).
|
|
265
|
-
A capability the real Api lacks is marked `"status": "unimplemented"` with a `note`: a definition that uses it validates
|
|
266
|
-
with a warning, not an error. `capabilities/AUDIT.md` has the table.
|
|
267
|
-
|
|
268
|
-
`ajv` comes from the monorepo root (`@friggframework/schemas` depends on it); core's runtime stays dependency-free and
|
|
269
|
-
loads it only for validation. `tests/schemas.test.js` runs both layers over every example and manifest and drives the
|
|
270
|
-
CLI as a child process; it fails, rather than skips, if `ajv` is missing. Core's `npm test`, and so CI's
|
|
271
|
-
`npm run test:all`, runs it on every change.
|
|
272
|
-
|
|
273
|
-
## Not in v0.1
|
|
274
|
-
|
|
275
|
-
- Per-module rate limits.
|
|
276
|
-
- The host renderers (the HubSpot app extension and Phone panel that draw `renderSurface` output).
|
|
277
|
-
- Rebuilding a link set's (`links`, not a sync's) identity index after its keys change: it has no backfill to
|
|
278
|
-
rebuild from, so `planUpgrade` reports `reindex`. A sync's `relink` is done by a backfill.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/** Canonical JSON (sorted keys, no whitespace) and its sha256: the checksum a published version is stored under. */
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
// Canonical order is by UTF-16 code unit (what a bare sort() does), never locale: the checksum must not depend on the host's locale.
|
|
4
|
-
const byCodeUnit = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
5
|
-
function canonical(v) {
|
|
6
|
-
if (Array.isArray(v)) return '[' + v.map(canonical).join(',') + ']';
|
|
7
|
-
if (v && typeof v === 'object') return '{' + Object.keys(v).sort(byCodeUnit).filter((k) => v[k] !== undefined).map((k) => JSON.stringify(k) + ':' + canonical(v[k])).join(',') + '}';
|
|
8
|
-
return JSON.stringify(v);
|
|
9
|
-
}
|
|
10
|
-
const checksum = (doc) => 'sha256:' + crypto.createHash('sha256').update(canonical(doc)).digest('hex');
|
|
11
|
-
module.exports = { canonical, checksum };
|