@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,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Expressions for integrations published as definitions (v0.1).
|
|
3
|
-
*
|
|
4
|
-
* Three forms, and no others (nothing is ever passed to eval):
|
|
5
|
-
* 1. Reference "$.event.contact.id" A string that is exactly a path. Resolves to the value, keeping its type.
|
|
6
|
-
* 2. Template "Call with {{event.from}}" A string with {{path | filter}} parts. Resolves to a string, except
|
|
7
|
-
* that a string which is exactly one tag ("{{record.amount | to_cents}}") keeps the value's type, so
|
|
8
|
-
* a filter can produce a number or a list for an API argument.
|
|
9
|
-
* 3. Condition "$.steps.contact == null && $.event.duration > 30" Used by `when` and trigger filters.
|
|
10
|
-
*
|
|
11
|
-
* Paths are read from a context object: { event, steps, settings, install, vars, item, record, now }.
|
|
12
|
-
* A missing segment resolves to undefined; it never throws.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const PATH_RE = /^\$\.[A-Za-z_][\w-]*(\.[\w-]+|\[\d+\])*$/;
|
|
16
|
-
|
|
17
|
-
function getPath(ctx, path) {
|
|
18
|
-
const clean = path.replace(/^\$\./, '');
|
|
19
|
-
let cur = ctx;
|
|
20
|
-
for (const seg of clean.split(/\.|(?=\[)/)) {
|
|
21
|
-
if (cur == null) return undefined;
|
|
22
|
-
const m = /^\[(\d+)\]$/.exec(seg);
|
|
23
|
-
cur = m ? cur[Number(m[1])] : cur[seg];
|
|
24
|
-
}
|
|
25
|
-
return cur;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const num = (v) => (v == null || v === '' ? null : Number(v));
|
|
29
|
-
const arith = (f) => (v, arg) => { const a = num(v), b = num(arg); return a == null || b == null || Number.isNaN(a) || Number.isNaN(b) ? null : f(a, b); };
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Template filters. Each is a named, total function of (value, argument): no filter loops, calls out or throws on
|
|
33
|
-
* bad input (a non-number gives null, which renders as ''). Arithmetic is here, as named filters, instead of an
|
|
34
|
-
* expression language: {{item.amount | to_cents}}, {{record.created | from_unix}}.
|
|
35
|
-
*/
|
|
36
|
-
// ISO 4217 minor-unit exponents that are not 2 (Stripe's zero-decimal and three-decimal lists).
|
|
37
|
-
const MINOR_EXPONENT = { BIF: 0, CLP: 0, DJF: 0, GNF: 0, JPY: 0, KMF: 0, KRW: 0, MGA: 0, PYG: 0, RWF: 0, UGX: 0, VND: 0, VUV: 0, XAF: 0, XOF: 0, XPF: 0, BHD: 3, JOD: 3, KWD: 3, OMR: 3, TND: 3 };
|
|
38
|
-
const exponentOf = (currency) => MINOR_EXPONENT[String(currency || '').toUpperCase()] ?? 2;
|
|
39
|
-
/** a * 10^e, rounded half-up in decimal (19.995 -> 1999.5 -> 2000; 19.995 * 100 is 1999.4999... in binary). */
|
|
40
|
-
const shiftRound = (a, e) => Math.round(Number(`${a}e${e}`));
|
|
41
|
-
|
|
42
|
-
const FILTERS = {
|
|
43
|
-
upper: (v) => String(v ?? '').toUpperCase(),
|
|
44
|
-
lower: (v) => String(v ?? '').toLowerCase(),
|
|
45
|
-
minutes: (v) => (v == null ? '' : String(Math.round(Number(v) / 60000))),
|
|
46
|
-
seconds: (v) => (v == null ? '' : String(Math.round(Number(v) / 1000))),
|
|
47
|
-
date: (v) => (v ? new Date(v).toISOString().slice(0, 10) : ''),
|
|
48
|
-
json: (v) => JSON.stringify(v),
|
|
49
|
-
// Record data inside a Slack message: escaped so a name like "*x* <!channel>" neither formats nor pings.
|
|
50
|
-
mrkdwn: (v) => require('./renderers/slack').escapeMrkdwn(v),
|
|
51
|
-
default: (v, arg) => (v == null || v === '' ? arg : v),
|
|
52
|
-
truncate: (v, arg) => { const s = String(v ?? ''); const n = Number(arg) || 80; return s.length > n ? s.slice(0, n - 1) + '…' : s; },
|
|
53
|
-
// lists
|
|
54
|
-
join: (v, arg) => (Array.isArray(v) ? v.filter((x) => x != null && x !== '').join(arg ?? ', ') : v),
|
|
55
|
-
size: (v) => (Array.isArray(v) || typeof v === 'string' ? v.length : v && typeof v === 'object' ? Object.keys(v).length : 0),
|
|
56
|
-
first: (v) => (Array.isArray(v) ? v[0] : v),
|
|
57
|
-
last: (v) => (Array.isArray(v) ? v[v.length - 1] : v),
|
|
58
|
-
split: (v, arg) => (typeof v === 'string' ? v.split(arg ?? ',') : v),
|
|
59
|
-
// arithmetic
|
|
60
|
-
plus: arith((a, b) => a + b),
|
|
61
|
-
minus: arith((a, b) => a - b),
|
|
62
|
-
times: arith((a, b) => a * b),
|
|
63
|
-
divided_by: arith((a, b) => (b === 0 ? null : a / b)),
|
|
64
|
-
// Rounded in decimal-exponent form, so 1.005 | round: 2 is 1.01 (1.005 * 100 is 100.49999... in binary floating point).
|
|
65
|
-
round: (v, arg) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; const d = Math.max(0, Math.min(10, Number(arg) || 0)); return Number(`${shiftRound(a, d)}e-${d}`); },
|
|
66
|
-
abs: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : Math.abs(a); },
|
|
67
|
-
number: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : a; },
|
|
68
|
-
// money in minor units, rounded so 19.99 -> 1999 (19.99 * 100 is 1998.9999999999998)
|
|
69
|
-
to_cents: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : shiftRound(a, 2); },
|
|
70
|
-
from_cents: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : Math.round(a) / 100; },
|
|
71
|
-
// the same for any currency: {{record.amount | from_minor_units: record.currency}} (JPY has no minor unit, KWD three)
|
|
72
|
-
to_minor_units: (v, cur) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; return shiftRound(a, exponentOf(cur)); },
|
|
73
|
-
from_minor_units: (v, cur) => { const a = num(v); if (a == null || Number.isNaN(a)) return null; const e = exponentOf(cur); return Math.round(a) / 10 ** e; },
|
|
74
|
-
// unix seconds <-> ISO 8601
|
|
75
|
-
from_unix: (v) => { const a = num(v); return a == null || Number.isNaN(a) ? null : new Date(a * 1000).toISOString(); },
|
|
76
|
-
to_unix: (v) => { const t = v ? Date.parse(v) : NaN; return Number.isNaN(t) ? null : Math.floor(t / 1000); },
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/** A filter argument is a quoted literal ('x'), a number, or a path read from the context (steps.full.owner). */
|
|
80
|
-
function filterArg(raw, ctx) {
|
|
81
|
-
if (raw == null) return undefined;
|
|
82
|
-
const a = raw.trim();
|
|
83
|
-
if (/^'.*'$/.test(a)) return a.slice(1, -1).replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
|
84
|
-
if (/^-?\d+(\.\d+)?$/.test(a)) return a;
|
|
85
|
-
return getPath(ctx, '$.' + a.replace(/^\$\./, ''));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const TAG_RE = /\{\{\s*([^}]+?)\s*\}\}/g;
|
|
89
|
-
const SINGLE_TAG_RE = /^\{\{\s*([^}]+?)\s*\}\}$/;
|
|
90
|
-
|
|
91
|
-
/** Split one tag's expression into its path and filters: "item.amount | times: 100" -> { path, filters: [{ name, arg }] }. */
|
|
92
|
-
function parseTag(expr) {
|
|
93
|
-
const [p, ...filters] = expr.split('|').map((x) => x.trim());
|
|
94
|
-
return { path: p.replace(/^\$\./, ''), filters: filters.map((f) => { const m = /^(\w+)(?::\s*(.*))?$/.exec(f); return m ? { name: m[1], arg: m[2] } : { name: f, arg: undefined, bad: true }; }) };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function evalTag(expr, ctx) {
|
|
98
|
-
const { path, filters } = parseTag(expr);
|
|
99
|
-
let v = getPath(ctx, '$.' + path);
|
|
100
|
-
for (const f of filters) {
|
|
101
|
-
const fn = !f.bad && FILTERS[f.name];
|
|
102
|
-
if (!fn) throw new Error(`Unknown template filter "${f.name}"`);
|
|
103
|
-
v = fn(v, filterArg(f.arg, ctx));
|
|
104
|
-
}
|
|
105
|
-
return v;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function renderTemplate(str, ctx) {
|
|
109
|
-
const single = SINGLE_TAG_RE.exec(str);
|
|
110
|
-
if (single) return evalTag(single[1], ctx) ?? '';
|
|
111
|
-
return str.replace(TAG_RE, (_, expr) => {
|
|
112
|
-
const v = evalTag(expr, ctx);
|
|
113
|
-
return v == null ? '' : typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Every tag in a value (deep), for validation: [{ path, filters }]. */
|
|
118
|
-
function templateTags(value, acc = []) {
|
|
119
|
-
if (typeof value === 'string') for (const m of value.matchAll(TAG_RE)) acc.push(parseTag(m[1]));
|
|
120
|
-
else if (Array.isArray(value)) value.forEach((v) => templateTags(v, acc));
|
|
121
|
-
else if (value && typeof value === 'object') Object.values(value).forEach((v) => templateTags(v, acc));
|
|
122
|
-
return acc;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** Resolve every reference and template inside a value (deep). Non-string scalars pass through. */
|
|
126
|
-
function resolve(value, ctx) {
|
|
127
|
-
if (typeof value === 'string') {
|
|
128
|
-
if (PATH_RE.test(value)) return getPath(ctx, value);
|
|
129
|
-
if (value.includes('{{')) return renderTemplate(value, ctx);
|
|
130
|
-
return value;
|
|
131
|
-
}
|
|
132
|
-
if (Array.isArray(value)) return value.map((v) => resolve(v, ctx));
|
|
133
|
-
if (value && typeof value === 'object') {
|
|
134
|
-
const out = {};
|
|
135
|
-
for (const [k, v] of Object.entries(value)) out[k] = resolve(v, ctx);
|
|
136
|
-
return out;
|
|
137
|
-
}
|
|
138
|
-
return value;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ---------- conditions ----------
|
|
142
|
-
const TOKEN_RE = /\s*(\$\.[A-Za-z_][\w.\-[\]]*|'(?:[^'\\]|\\.)*'|-?\d+(?:\.\d+)?|==|!=|>=|<=|&&|\|\||[()!<>]|\bin\b|\bcontains\b|\bexists\b|\btrue\b|\bfalse\b|\bnull\b)/y;
|
|
143
|
-
|
|
144
|
-
function tokenize(src) {
|
|
145
|
-
const out = [];
|
|
146
|
-
TOKEN_RE.lastIndex = 0;
|
|
147
|
-
let pos = 0;
|
|
148
|
-
while (pos < src.length) {
|
|
149
|
-
if (/^\s*$/.test(src.slice(pos))) break;
|
|
150
|
-
TOKEN_RE.lastIndex = pos;
|
|
151
|
-
const m = TOKEN_RE.exec(src);
|
|
152
|
-
if (!m) throw new Error(`Cannot parse condition near "${src.slice(pos, pos + 20)}"`);
|
|
153
|
-
out.push(m[1]);
|
|
154
|
-
pos = TOKEN_RE.lastIndex;
|
|
155
|
-
}
|
|
156
|
-
return out;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** Parse a condition into a small AST. Grammar: or := and ('||' and)* ; and := not ('&&' not)* ; not := '!' not | cmp ; cmp := atom (op atom | 'exists')? */
|
|
160
|
-
function parseCondition(src) {
|
|
161
|
-
const t = tokenize(src);
|
|
162
|
-
let i = 0;
|
|
163
|
-
const peek = () => t[i];
|
|
164
|
-
const next = () => t[i++];
|
|
165
|
-
function atom() {
|
|
166
|
-
const tok = next();
|
|
167
|
-
if (i > t.length) throw new Error(`Condition ends early: "${src}"`);
|
|
168
|
-
if (tok === '(') { const e = or(); if (next() !== ')') throw new Error(`Missing ) in "${src}"`); return e; }
|
|
169
|
-
if (tok.startsWith('$.')) return { ref: tok };
|
|
170
|
-
if (tok.startsWith("'")) return { lit: tok.slice(1, -1).replace(/\\'/g, "'") };
|
|
171
|
-
if (tok === 'true' || tok === 'false') return { lit: tok === 'true' };
|
|
172
|
-
if (tok === 'null') return { lit: null };
|
|
173
|
-
if (/^-?\d/.test(tok)) return { lit: Number(tok) };
|
|
174
|
-
throw new Error(`Unexpected "${tok}" in "${src}"`);
|
|
175
|
-
}
|
|
176
|
-
function cmp() {
|
|
177
|
-
const left = atom();
|
|
178
|
-
const op = peek();
|
|
179
|
-
if (op === 'exists') { next(); return { op: 'exists', left }; }
|
|
180
|
-
if (['==', '!=', '>', '>=', '<', '<=', 'in', 'contains'].includes(op)) { next(); return { op, left, right: atom() }; }
|
|
181
|
-
return { op: 'truthy', left };
|
|
182
|
-
}
|
|
183
|
-
function not() { if (peek() === '!') { next(); return { op: '!', left: not() }; } return cmp(); }
|
|
184
|
-
function and() { let l = not(); while (peek() === '&&') { next(); l = { op: '&&', left: l, right: not() }; } return l; }
|
|
185
|
-
function or() { let l = and(); while (peek() === '||') { next(); l = { op: '||', left: l, right: and() }; } return l; }
|
|
186
|
-
const ast = or();
|
|
187
|
-
if (i < t.length) throw new Error(`Unexpected "${t[i]}" in "${src}"`);
|
|
188
|
-
return ast;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function evalAst(n, ctx) {
|
|
192
|
-
if ('ref' in n) return getPath(ctx, n.ref);
|
|
193
|
-
if ('lit' in n) return n.lit;
|
|
194
|
-
const L = () => evalAst(n.left, ctx), R = () => evalAst(n.right, ctx);
|
|
195
|
-
switch (n.op) {
|
|
196
|
-
case 'truthy': return Boolean(L());
|
|
197
|
-
case 'exists': return L() != null;
|
|
198
|
-
case '!': return !evalAst(n.left, ctx);
|
|
199
|
-
case '&&': return Boolean(L()) && Boolean(R());
|
|
200
|
-
case '||': return Boolean(L()) || Boolean(R());
|
|
201
|
-
case '==': return L() == R(); // eslint-disable-line eqeqeq -- null == undefined is intended
|
|
202
|
-
case '!=': return L() != R(); // eslint-disable-line eqeqeq
|
|
203
|
-
case '>': return L() > R();
|
|
204
|
-
case '>=': return L() >= R();
|
|
205
|
-
case '<': return L() < R();
|
|
206
|
-
case '<=': return L() <= R();
|
|
207
|
-
case 'in': { const r = R(); return Array.isArray(r) ? r.includes(L()) : typeof r === 'string' && r.includes(String(L())); }
|
|
208
|
-
case 'contains': { const l = L(); return Array.isArray(l) ? l.includes(R()) : String(l ?? '').includes(String(R())); }
|
|
209
|
-
default: throw new Error(`Unknown operator ${n.op}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function evaluate(condition, ctx) {
|
|
214
|
-
if (condition == null) return true;
|
|
215
|
-
return Boolean(evalAst(parseCondition(condition), ctx));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** Every $.root referenced by an expression-bearing value (for validation). */
|
|
219
|
-
function referencedPaths(value, acc = []) {
|
|
220
|
-
if (typeof value === 'string') {
|
|
221
|
-
if (PATH_RE.test(value)) acc.push(value);
|
|
222
|
-
for (const m of value.matchAll(/\{\{\s*([^}|]+?)\s*(\|[^}]*)?\}\}/g)) acc.push('$.' + m[1].replace(/^\$\./, ''));
|
|
223
|
-
for (const m of value.matchAll(/\$\.[A-Za-z_][\w.\-[\]]*/g)) if (!acc.includes(m[0])) acc.push(m[0]);
|
|
224
|
-
} else if (Array.isArray(value)) value.forEach((v) => referencedPaths(v, acc));
|
|
225
|
-
else if (value && typeof value === 'object') Object.values(value).forEach((v) => referencedPaths(v, acc));
|
|
226
|
-
return acc;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
module.exports = { getPath, resolve, renderTemplate, evaluate, parseCondition, referencedPaths, templateTags, FILTERS };
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integrations published as definitions, v0.1 (ADR-063). An integration as a document: modules, syncs, workflows, queries,
|
|
3
|
-
* UI surfaces and templates, run by one runtime class on the existing Frigg infrastructure.
|
|
4
|
-
*/
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const { validate } = require('./validate');
|
|
8
|
-
const { compile, routeWebhook, eventName } = require('./compile');
|
|
9
|
-
const { execute } = require('./interpreter');
|
|
10
|
-
const { makePorts, runWorkflow, runQuery, renderSurface, resolveView, runLifecycle, resolveOptions } = require('./runtime');
|
|
11
|
-
const { diff, checkBump, planUpgrade } = require('./versioning');
|
|
12
|
-
const { fromDefinitionDocument, definitionDocumentHost, embedDefinitionDocument, defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass, memoryDefinitions } = require('./integration-runtime');
|
|
13
|
-
const { BUILTINS } = require('./steps');
|
|
14
|
-
const { TRANSFORMS, applyMapping } = require('./transforms');
|
|
15
|
-
const { FILTERS } = require('./expressions');
|
|
16
|
-
const { validateManifest } = require('./manifests');
|
|
17
|
-
const { checkDefinition, checkManifest, checkInstallation } = require('./schema-check');
|
|
18
|
-
const { viewToSlack, escapeMrkdwn } = require('./renderers/slack');
|
|
19
|
-
const { viewToHubSpot } = require('./renderers/hubspot');
|
|
20
|
-
const { viewToHtml, slackMessageToHtml } = require('./renderers/html');
|
|
21
|
-
const { previewSurface, previewWorkflow, recordingPorts } = require('./renderers/preview');
|
|
22
|
-
const { decodeAction } = require('./renderers/common');
|
|
23
|
-
const useCases = require('./use-cases/definition-use-cases');
|
|
24
|
-
|
|
25
|
-
/** Load every capabilities/*.capabilities.json (bundled mocks; real modules ship their own). */
|
|
26
|
-
function loadManifests(dir = path.join(__dirname, 'capabilities')) {
|
|
27
|
-
const out = {};
|
|
28
|
-
for (const f of fs.readdirSync(dir)) if (f.endsWith('.capabilities.json')) { const m = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')); out[m.module] = m; }
|
|
29
|
-
return out;
|
|
30
|
-
}
|
|
31
|
-
function loadExamples(dir = path.join(__dirname, 'examples')) {
|
|
32
|
-
return fs.readdirSync(dir).filter((f) => f.endsWith('.definition.json')).map((f) => JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = {
|
|
36
|
-
SCHEMA_VERSION: '0.1',
|
|
37
|
-
validate, validateManifest, checkDefinition, checkManifest, checkInstallation, compile, routeWebhook, eventName, execute,
|
|
38
|
-
makePorts, runWorkflow, runQuery, renderSurface, resolveView, runLifecycle,
|
|
39
|
-
diff, checkBump, planUpgrade,
|
|
40
|
-
// the document front door (ADR-063); `defineIntegration` (../define) is the code front door. Same compile target.
|
|
41
|
-
fromDefinitionDocument, definitionDocumentHost, embedDefinitionDocument, memoryDefinitions,
|
|
42
|
-
// deprecated aliases, removed before Stack B merges
|
|
43
|
-
defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass,
|
|
44
|
-
BUILTINS, TRANSFORMS, FILTERS, applyMapping, resolveOptions,
|
|
45
|
-
loadManifests, loadExamples,
|
|
46
|
-
viewToSlack, escapeMrkdwn, viewToHubSpot, viewToHtml, slackMessageToHtml, previewSurface, previewWorkflow, recordingPorts, decodeAction,
|
|
47
|
-
...useCases,
|
|
48
|
-
};
|