@oomol-lab/open-flow 0.1.0-beta.72 → 0.1.0-beta.74
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/LICENSE +36 -0
- package/NOTICE +7 -0
- package/README.md +40 -0
- package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-DI9pL-Yr.js} +2 -2
- package/dist/browser/{dialog-CUy_fwx9.js → dialog-BYJGvItc.js} +54 -19
- package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-RL-H4CjD.js} +306 -48
- package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-BkDsETj0.js} +20745 -20186
- package/dist/browser/preview.css +73 -3
- package/dist/browser/preview.js +21410 -20607
- package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
- package/dist/browser/ui.css +72 -2
- package/dist/browser/workbench.css +73 -3
- package/dist/browser/workbench.js +14 -76
- package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-BcWPWGzw.js} +1 -1
- package/dist/command/index.js +34 -34
- package/dist/common/authoring.d.ts +1 -1
- package/dist/common/authoringFields.d.ts +1 -1
- package/dist/common/authoringSchema.d.ts +28 -4
- package/dist/common/connector-proxy.d.ts +1 -1
- package/dist/common/control-api-conformance.d.ts +1 -1
- package/dist/common/control-api-conformance.js +61 -24
- package/dist/common/control-api.d.ts +4 -3
- package/dist/common/control-api.js +90 -42
- package/dist/common/control-requests.d.ts +2 -1
- package/dist/common/control-requests.js +204 -42
- package/dist/common/cron-trigger.d.ts +1 -1
- package/dist/common/decision.d.ts +1 -1
- package/dist/common/draftOperations.d.ts +1 -1
- package/dist/common/engine-contract.d.ts +3 -2
- package/dist/common/event-source-definition.d.ts +1 -1
- package/dist/common/flow-agent.d.ts +1 -1
- package/dist/{browser → common}/flow-authoring-node.d.ts +3 -3
- package/dist/{browser → common}/flow-authoring.js +75 -23
- package/dist/{browser → common}/flow-change-schema.d.ts +177 -6
- package/dist/{browser → common}/flow-change.d.ts +35 -15
- package/dist/{browser → common}/flow-change.js +77 -25
- package/dist/common/flow-connectionUsage.d.ts +1 -1
- package/dist/common/flow-encoding.d.ts +1 -1
- package/dist/common/flow-encoding.js +34 -16
- package/dist/common/flow-errorHandling.d.ts +1 -1
- package/dist/common/flow-graph.d.ts +1 -1
- package/dist/common/flow-modules.d.ts +1 -1
- package/dist/common/flow-promptTemplate.d.ts +1 -1
- package/dist/common/flow-retry.d.ts +1 -1
- package/dist/common/flow-semantics.d.ts +1 -1
- package/dist/common/flow-semantics.js +151 -32
- package/dist/common/flow-workflowFile.d.ts +1 -1
- package/dist/common/flowInspection.d.ts +3 -3
- package/dist/common/integration-trigger.d.ts +1 -1
- package/dist/common/licenses.md +118 -1
- package/dist/common/liveDetails.d.ts +1 -1
- package/dist/common/mcp.d.ts +2 -1
- package/dist/common/mcp.js +59 -22
- package/dist/common/openapi.d.ts +1 -1
- package/dist/common/poll-trigger.d.ts +1 -1
- package/dist/common/run-results.d.ts +8 -1
- package/dist/common/runtime-contract.d.ts +8 -2
- package/dist/common/runtime-contract.js +22 -5
- package/dist/common/scheduler-checkpoint.d.ts +1 -1
- package/dist/common/scheduler-dependencies.d.ts +1 -1
- package/dist/common/scheduler-node-execution.d.ts +1 -1
- package/dist/common/scheduler.d.ts +2 -2
- package/dist/common/scheduler.js +31 -12
- package/dist/common/trigger-catalog.d.ts +1 -1
- package/dist/common/trigger-config-options.d.ts +1 -1
- package/dist/common/trigger-config.d.ts +1 -1
- package/dist/common/trigger-contract.d.ts +2 -2
- package/dist/common/trigger-localization.d.ts +1 -1
- package/dist/common/webhook-response.d.ts +1 -1
- package/dist/common/webhook-trigger.d.ts +1 -1
- package/dist/common/workflow.d.ts +17 -1
- package/dist/common/workflow.js +68 -22
- package/dist/index.d.ts +6 -3
- package/package.json +7 -6
- package/src/base/common/json.ts +90 -0
- package/src/canvas/common/commands.ts +15 -0
- package/src/connector/common/actionSchema.ts +168 -0
- package/src/connector/common/proxy.ts +44 -0
- package/src/control/common/api.ts +1505 -0
- package/src/control/common/authoring.ts +912 -0
- package/src/control/common/authoringCondition.ts +110 -0
- package/src/control/common/authoringDiagnosticFields.ts +136 -0
- package/src/control/common/authoringExamples.ts +192 -0
- package/src/control/common/authoringFields.ts +53 -0
- package/src/control/common/authoringLlm.ts +70 -0
- package/src/control/common/authoringOpenApi.ts +129 -0
- package/src/control/common/authoringSchema.ts +404 -0
- package/src/control/common/authoringTools.ts +123 -0
- package/src/control/common/authoringTriggers.ts +26 -0
- package/src/control/common/checkDecoders.ts +65 -0
- package/src/control/common/conformance.ts +1989 -0
- package/src/control/common/connectorDecoders.ts +370 -0
- package/src/control/common/connectorQueries.ts +61 -0
- package/src/control/common/decoding.ts +60 -0
- package/src/control/common/draftOperations.ts +61 -0
- package/src/control/common/errors.ts +114 -0
- package/src/control/common/eventSourceSchemas.ts +13 -0
- package/src/control/common/eventSources.ts +186 -0
- package/src/control/common/flowDecoders.ts +153 -0
- package/src/control/common/flowInspection.ts +181 -0
- package/src/control/common/flowNotifications.ts +37 -0
- package/src/control/common/liveDetails.ts +124 -0
- package/src/control/common/mcp.ts +241 -0
- package/src/control/common/mcpConformance.ts +142 -0
- package/src/control/common/providerAccess.ts +28 -0
- package/src/control/common/providerIconSprite.ts +42 -0
- package/src/control/common/publicationDecoders.ts +98 -0
- package/src/control/common/random.ts +16 -0
- package/src/control/common/requests.ts +112 -0
- package/src/control/common/resultQuery.ts +8 -0
- package/src/control/common/results.ts +176 -0
- package/src/control/common/revisionDecoders.ts +52 -0
- package/src/control/common/runDecoders.ts +240 -0
- package/src/control/common/triggerCatalog.ts +89 -0
- package/src/control/common/triggerDecoders.ts +175 -0
- package/src/decision/common/decision.ts +168 -0
- package/src/event-source/common/catalog.generated.json +65 -0
- package/src/event-source/common/definition.ts +66 -0
- package/src/event-source/common/index.ts +99 -0
- package/src/execution/common/checkpoint.ts +311 -0
- package/src/execution/common/dependencies.ts +440 -0
- package/src/execution/common/engineContract.ts +80 -0
- package/src/execution/common/events.ts +328 -0
- package/src/execution/common/node-execution.ts +467 -0
- package/src/execution/common/runLifecycle.ts +250 -0
- package/src/execution/common/runtime.ts +563 -0
- package/src/execution/common/scheduler.ts +696 -0
- package/src/execution/common/wait.ts +36 -0
- package/src/flow/common/agent.ts +305 -0
- package/src/flow/common/authoring.ts +30 -0
- package/src/flow/common/change.ts +1036 -0
- package/src/flow/common/changeSchema.ts +783 -0
- package/src/flow/common/codePermissions.ts +2 -0
- package/src/flow/common/condition.ts +265 -0
- package/src/flow/common/connectionUsage.ts +131 -0
- package/src/flow/common/edgeChanges.ts +35 -0
- package/src/flow/common/encoding.ts +437 -0
- package/src/flow/common/errorHandling.ts +34 -0
- package/src/flow/common/graph.ts +892 -0
- package/src/flow/common/inputValue.ts +16 -0
- package/src/flow/common/json.ts +8 -0
- package/src/flow/common/moduleChanges.ts +31 -0
- package/src/flow/common/modules.ts +292 -0
- package/src/flow/common/nodeChanges.ts +762 -0
- package/src/flow/common/portChanges.ts +36 -0
- package/src/flow/common/promptTemplate.ts +15 -0
- package/src/flow/common/retry.ts +20 -0
- package/src/flow/common/schema.ts +162 -0
- package/src/flow/common/schemaCompare.ts +176 -0
- package/src/flow/common/schemaComparer.ts +178 -0
- package/src/flow/common/schemaKeywords.ts +53 -0
- package/src/flow/common/semantics.ts +474 -0
- package/src/flow/common/sourceField.ts +35 -0
- package/src/flow/common/triggerScheduleSchema.ts +8 -0
- package/src/flow/common/wait.ts +30 -0
- package/src/flow/common/webhookMethod.ts +7 -0
- package/src/flow/common/webhookResponse.ts +75 -0
- package/src/flow/common/workflowFile.ts +97 -0
- package/src/json-schema-subset/checker/array.ts +142 -0
- package/src/json-schema-subset/checker/cache.ts +57 -0
- package/src/json-schema-subset/checker/checker.ts +230 -0
- package/src/json-schema-subset/checker/enumAndConst.ts +113 -0
- package/src/json-schema-subset/checker/equals.ts +93 -0
- package/src/json-schema-subset/checker/numeric/checker.ts +50 -0
- package/src/json-schema-subset/checker/numeric/float.ts +121 -0
- package/src/json-schema-subset/checker/numeric/index.ts +1 -0
- package/src/json-schema-subset/checker/numeric/integer.ts +119 -0
- package/src/json-schema-subset/checker/numeric/integerNormalizer.ts +72 -0
- package/src/json-schema-subset/checker/object.ts +354 -0
- package/src/json-schema-subset/checker/range.ts +91 -0
- package/src/json-schema-subset/checker/string.ts +60 -0
- package/src/json-schema-subset/checker/tools/index.ts +3 -0
- package/src/json-schema-subset/checker/tools/mark.ts +82 -0
- package/src/json-schema-subset/checker/tools/quantum.ts +106 -0
- package/src/json-schema-subset/checker/tools/range.ts +210 -0
- package/src/json-schema-subset/checker/validator.ts +23 -0
- package/src/json-schema-subset/compiler/array.ts +163 -0
- package/src/json-schema-subset/compiler/combination.ts +109 -0
- package/src/json-schema-subset/compiler/common.ts +99 -0
- package/src/json-schema-subset/compiler/compiler.ts +188 -0
- package/src/json-schema-subset/compiler/enumAndConst.ts +75 -0
- package/src/json-schema-subset/compiler/index.ts +11 -0
- package/src/json-schema-subset/compiler/object.ts +411 -0
- package/src/json-schema-subset/compiler/primitive.ts +144 -0
- package/src/json-schema-subset/compiler/utils.ts +24 -0
- package/src/json-schema-subset/compiler/wrapper.ts +192 -0
- package/src/json-schema-subset/error.ts +55 -0
- package/src/json-schema-subset/expression/calculator.ts +442 -0
- package/src/json-schema-subset/expression/combination.ts +49 -0
- package/src/json-schema-subset/expression/expression.ts +191 -0
- package/src/json-schema-subset/expression/index.ts +25 -0
- package/src/json-schema-subset/expression/result.ts +88 -0
- package/src/json-schema-subset/expression/types.ts +36 -0
- package/src/json-schema-subset/extends/extendsContext.ts +89 -0
- package/src/json-schema-subset/extends/index.ts +2 -0
- package/src/json-schema-subset/extends/types.ts +12 -0
- package/src/json-schema-subset/global.ts +60 -0
- package/src/json-schema-subset/index.ts +8 -0
- package/src/json-schema-subset/preprocessing.ts +74 -0
- package/src/json-schema-subset/types.ts +8 -0
- package/src/json-schema-subset/utils.ts +95 -0
- package/src/localization/common/languages.ts +118 -0
- package/src/localization/common/timeZone.ts +25 -0
- package/src/openapi/common/authBindings.ts +12 -0
- package/src/openapi/common/openapi.ts +459 -0
- package/src/trigger/common/config.ts +56 -0
- package/src/trigger/common/configOptions.ts +15 -0
- package/src/trigger/common/configSchema.ts +8 -0
- package/src/trigger/common/contract.ts +132 -0
- package/src/trigger/common/cron.ts +151 -0
- package/src/trigger/common/integration.ts +498 -0
- package/src/trigger/common/poll.ts +187 -0
- package/src/trigger/common/webhook.ts +412 -0
- package/src/trigger/providers/airtable/locales/fr.json +18 -0
- package/src/trigger/providers/airtable/locales/ja.json +18 -0
- package/src/trigger/providers/airtable/locales/ko.json +18 -0
- package/src/trigger/providers/airtable/locales/ru.json +18 -0
- package/src/trigger/providers/airtable/locales/zh-CN.json +18 -0
- package/src/trigger/providers/airtable/locales/zh-TW.json +18 -0
- package/src/trigger/providers/asana/locales/fr.json +15 -0
- package/src/trigger/providers/asana/locales/ja.json +15 -0
- package/src/trigger/providers/asana/locales/ko.json +15 -0
- package/src/trigger/providers/asana/locales/ru.json +15 -0
- package/src/trigger/providers/asana/locales/zh-CN.json +15 -0
- package/src/trigger/providers/asana/locales/zh-TW.json +15 -0
- package/src/trigger/providers/calendly/locales/fr.json +16 -0
- package/src/trigger/providers/calendly/locales/ja.json +16 -0
- package/src/trigger/providers/calendly/locales/ko.json +16 -0
- package/src/trigger/providers/calendly/locales/ru.json +16 -0
- package/src/trigger/providers/calendly/locales/zh-CN.json +16 -0
- package/src/trigger/providers/calendly/locales/zh-TW.json +16 -0
- package/src/trigger/providers/catalog.generated.json +4996 -0
- package/src/trigger/providers/clickup/locales/fr.json +16 -0
- package/src/trigger/providers/clickup/locales/ja.json +16 -0
- package/src/trigger/providers/clickup/locales/ko.json +16 -0
- package/src/trigger/providers/clickup/locales/ru.json +16 -0
- package/src/trigger/providers/clickup/locales/zh-CN.json +16 -0
- package/src/trigger/providers/clickup/locales/zh-TW.json +16 -0
- package/src/trigger/providers/definitions.ts +19 -0
- package/src/trigger/providers/feishu/locales/fr.json +20 -0
- package/src/trigger/providers/feishu/locales/ja.json +20 -0
- package/src/trigger/providers/feishu/locales/ko.json +20 -0
- package/src/trigger/providers/feishu/locales/ru.json +20 -0
- package/src/trigger/providers/feishu/locales/zh-CN.json +20 -0
- package/src/trigger/providers/feishu/locales/zh-TW.json +20 -0
- package/src/trigger/providers/fieldDescriptions.ts +392 -0
- package/src/trigger/providers/fieldLabels.ts +256 -0
- package/src/trigger/providers/fireflies/locales/fr.json +17 -0
- package/src/trigger/providers/fireflies/locales/ja.json +17 -0
- package/src/trigger/providers/fireflies/locales/ko.json +17 -0
- package/src/trigger/providers/fireflies/locales/ru.json +17 -0
- package/src/trigger/providers/fireflies/locales/zh-CN.json +17 -0
- package/src/trigger/providers/fireflies/locales/zh-TW.json +17 -0
- package/src/trigger/providers/github/locales/fr.json +30 -0
- package/src/trigger/providers/github/locales/ja.json +30 -0
- package/src/trigger/providers/github/locales/ko.json +30 -0
- package/src/trigger/providers/github/locales/ru.json +30 -0
- package/src/trigger/providers/github/locales/zh-CN.json +30 -0
- package/src/trigger/providers/github/locales/zh-TW.json +30 -0
- package/src/trigger/providers/gitlab/locales/fr.json +18 -0
- package/src/trigger/providers/gitlab/locales/ja.json +18 -0
- package/src/trigger/providers/gitlab/locales/ko.json +18 -0
- package/src/trigger/providers/gitlab/locales/ru.json +18 -0
- package/src/trigger/providers/gitlab/locales/zh-CN.json +18 -0
- package/src/trigger/providers/gitlab/locales/zh-TW.json +18 -0
- package/src/trigger/providers/gmail/locales/fr.json +18 -0
- package/src/trigger/providers/gmail/locales/ja.json +18 -0
- package/src/trigger/providers/gmail/locales/ko.json +18 -0
- package/src/trigger/providers/gmail/locales/ru.json +18 -0
- package/src/trigger/providers/gmail/locales/zh-CN.json +18 -0
- package/src/trigger/providers/gmail/locales/zh-TW.json +18 -0
- package/src/trigger/providers/google-calendar/locales/fr.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ja.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ko.json +15 -0
- package/src/trigger/providers/google-calendar/locales/ru.json +15 -0
- package/src/trigger/providers/google-calendar/locales/zh-CN.json +15 -0
- package/src/trigger/providers/google-calendar/locales/zh-TW.json +15 -0
- package/src/trigger/providers/google-drive/locales/fr.json +48 -0
- package/src/trigger/providers/google-drive/locales/ja.json +48 -0
- package/src/trigger/providers/google-drive/locales/ko.json +48 -0
- package/src/trigger/providers/google-drive/locales/ru.json +48 -0
- package/src/trigger/providers/google-drive/locales/zh-CN.json +48 -0
- package/src/trigger/providers/google-drive/locales/zh-TW.json +48 -0
- package/src/trigger/providers/google-sheets/locales/fr.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ja.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ko.json +19 -0
- package/src/trigger/providers/google-sheets/locales/ru.json +19 -0
- package/src/trigger/providers/google-sheets/locales/zh-CN.json +19 -0
- package/src/trigger/providers/google-sheets/locales/zh-TW.json +19 -0
- package/src/trigger/providers/intercom/locales/fr.json +17 -0
- package/src/trigger/providers/intercom/locales/ja.json +17 -0
- package/src/trigger/providers/intercom/locales/ko.json +17 -0
- package/src/trigger/providers/intercom/locales/ru.json +17 -0
- package/src/trigger/providers/intercom/locales/zh-CN.json +17 -0
- package/src/trigger/providers/intercom/locales/zh-TW.json +17 -0
- package/src/trigger/providers/jira/locales/fr.json +17 -0
- package/src/trigger/providers/jira/locales/ja.json +17 -0
- package/src/trigger/providers/jira/locales/ko.json +17 -0
- package/src/trigger/providers/jira/locales/ru.json +17 -0
- package/src/trigger/providers/jira/locales/zh-CN.json +17 -0
- package/src/trigger/providers/jira/locales/zh-TW.json +17 -0
- package/src/trigger/providers/linear/locales/fr.json +13 -0
- package/src/trigger/providers/linear/locales/ja.json +13 -0
- package/src/trigger/providers/linear/locales/ko.json +13 -0
- package/src/trigger/providers/linear/locales/ru.json +13 -0
- package/src/trigger/providers/linear/locales/zh-CN.json +13 -0
- package/src/trigger/providers/linear/locales/zh-TW.json +13 -0
- package/src/trigger/providers/locales/fr.ts +71 -0
- package/src/trigger/providers/locales/ja.ts +71 -0
- package/src/trigger/providers/locales/ko.ts +71 -0
- package/src/trigger/providers/locales/ru.ts +71 -0
- package/src/trigger/providers/locales/zh-CN.ts +71 -0
- package/src/trigger/providers/locales/zh-TW.ts +71 -0
- package/src/trigger/providers/localization.ts +59 -0
- package/src/trigger/providers/notion/locales/fr.json +16 -0
- package/src/trigger/providers/notion/locales/ja.json +16 -0
- package/src/trigger/providers/notion/locales/ko.json +16 -0
- package/src/trigger/providers/notion/locales/ru.json +16 -0
- package/src/trigger/providers/notion/locales/zh-CN.json +16 -0
- package/src/trigger/providers/notion/locales/zh-TW.json +16 -0
- package/src/trigger/providers/one-drive/locales/fr.json +16 -0
- package/src/trigger/providers/one-drive/locales/ja.json +16 -0
- package/src/trigger/providers/one-drive/locales/ko.json +16 -0
- package/src/trigger/providers/one-drive/locales/ru.json +16 -0
- package/src/trigger/providers/one-drive/locales/zh-CN.json +16 -0
- package/src/trigger/providers/one-drive/locales/zh-TW.json +16 -0
- package/src/trigger/providers/outlook/locales/fr.json +18 -0
- package/src/trigger/providers/outlook/locales/ja.json +18 -0
- package/src/trigger/providers/outlook/locales/ko.json +18 -0
- package/src/trigger/providers/outlook/locales/ru.json +18 -0
- package/src/trigger/providers/outlook/locales/zh-CN.json +18 -0
- package/src/trigger/providers/outlook/locales/zh-TW.json +18 -0
- package/src/trigger/providers/pandadoc/locales/fr.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ja.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ko.json +12 -0
- package/src/trigger/providers/pandadoc/locales/ru.json +12 -0
- package/src/trigger/providers/pandadoc/locales/zh-CN.json +12 -0
- package/src/trigger/providers/pandadoc/locales/zh-TW.json +12 -0
- package/src/trigger/providers/pipedrive/locales/fr.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ja.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ko.json +15 -0
- package/src/trigger/providers/pipedrive/locales/ru.json +15 -0
- package/src/trigger/providers/pipedrive/locales/zh-CN.json +15 -0
- package/src/trigger/providers/pipedrive/locales/zh-TW.json +15 -0
- package/src/trigger/providers/remote.ts +163 -0
- package/src/trigger/providers/resend/locales/fr.json +16 -0
- package/src/trigger/providers/resend/locales/ja.json +16 -0
- package/src/trigger/providers/resend/locales/ko.json +16 -0
- package/src/trigger/providers/resend/locales/ru.json +16 -0
- package/src/trigger/providers/resend/locales/zh-CN.json +16 -0
- package/src/trigger/providers/resend/locales/zh-TW.json +16 -0
- package/src/trigger/providers/sentry/locales/fr.json +19 -0
- package/src/trigger/providers/sentry/locales/ja.json +19 -0
- package/src/trigger/providers/sentry/locales/ko.json +19 -0
- package/src/trigger/providers/sentry/locales/ru.json +19 -0
- package/src/trigger/providers/sentry/locales/zh-CN.json +19 -0
- package/src/trigger/providers/sentry/locales/zh-TW.json +19 -0
- package/src/trigger/providers/shopify/locales/fr.json +18 -0
- package/src/trigger/providers/shopify/locales/ja.json +18 -0
- package/src/trigger/providers/shopify/locales/ko.json +18 -0
- package/src/trigger/providers/shopify/locales/ru.json +18 -0
- package/src/trigger/providers/shopify/locales/zh-CN.json +18 -0
- package/src/trigger/providers/shopify/locales/zh-TW.json +18 -0
- package/src/trigger/providers/slack/locales/fr.json +18 -0
- package/src/trigger/providers/slack/locales/ja.json +18 -0
- package/src/trigger/providers/slack/locales/ko.json +18 -0
- package/src/trigger/providers/slack/locales/ru.json +18 -0
- package/src/trigger/providers/slack/locales/zh-CN.json +18 -0
- package/src/trigger/providers/slack/locales/zh-TW.json +18 -0
- package/src/trigger/providers/slackbot/locales/fr.json +23 -0
- package/src/trigger/providers/slackbot/locales/ja.json +23 -0
- package/src/trigger/providers/slackbot/locales/ko.json +23 -0
- package/src/trigger/providers/slackbot/locales/ru.json +23 -0
- package/src/trigger/providers/slackbot/locales/zh-CN.json +23 -0
- package/src/trigger/providers/slackbot/locales/zh-TW.json +23 -0
- package/src/trigger/providers/source-events.ts +25 -0
- package/src/trigger/providers/stripe/locales/fr.json +17 -0
- package/src/trigger/providers/stripe/locales/ja.json +17 -0
- package/src/trigger/providers/stripe/locales/ko.json +17 -0
- package/src/trigger/providers/stripe/locales/ru.json +17 -0
- package/src/trigger/providers/stripe/locales/zh-CN.json +17 -0
- package/src/trigger/providers/stripe/locales/zh-TW.json +17 -0
- package/src/trigger/providers/tally/locales/fr.json +12 -0
- package/src/trigger/providers/tally/locales/ja.json +12 -0
- package/src/trigger/providers/tally/locales/ko.json +12 -0
- package/src/trigger/providers/tally/locales/ru.json +12 -0
- package/src/trigger/providers/tally/locales/zh-CN.json +12 -0
- package/src/trigger/providers/tally/locales/zh-TW.json +12 -0
- package/src/trigger/providers/telegram/locales/fr.json +17 -0
- package/src/trigger/providers/telegram/locales/ja.json +17 -0
- package/src/trigger/providers/telegram/locales/ko.json +17 -0
- package/src/trigger/providers/telegram/locales/ru.json +17 -0
- package/src/trigger/providers/telegram/locales/zh-CN.json +17 -0
- package/src/trigger/providers/telegram/locales/zh-TW.json +17 -0
- package/src/trigger/providers/typeform/locales/fr.json +12 -0
- package/src/trigger/providers/typeform/locales/ja.json +12 -0
- package/src/trigger/providers/typeform/locales/ko.json +12 -0
- package/src/trigger/providers/typeform/locales/ru.json +12 -0
- package/src/trigger/providers/typeform/locales/zh-CN.json +12 -0
- package/src/trigger/providers/typeform/locales/zh-TW.json +12 -0
- package/src/trigger/providers/vercel/locales/fr.json +18 -0
- package/src/trigger/providers/vercel/locales/ja.json +18 -0
- package/src/trigger/providers/vercel/locales/ko.json +18 -0
- package/src/trigger/providers/vercel/locales/ru.json +18 -0
- package/src/trigger/providers/vercel/locales/zh-CN.json +18 -0
- package/src/trigger/providers/vercel/locales/zh-TW.json +18 -0
- package/src/trigger/providers/woocommerce/locales/fr.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ja.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ko.json +19 -0
- package/src/trigger/providers/woocommerce/locales/ru.json +19 -0
- package/src/trigger/providers/woocommerce/locales/zh-CN.json +19 -0
- package/src/trigger/providers/woocommerce/locales/zh-TW.json +19 -0
- package/src/trigger/providers/zendesk/locales/fr.json +15 -0
- package/src/trigger/providers/zendesk/locales/ja.json +15 -0
- package/src/trigger/providers/zendesk/locales/ko.json +15 -0
- package/src/trigger/providers/zendesk/locales/ru.json +15 -0
- package/src/trigger/providers/zendesk/locales/zh-CN.json +15 -0
- package/src/trigger/providers/zendesk/locales/zh-TW.json +15 -0
- package/src/trigger/providers/zoom/locales/fr.json +17 -0
- package/src/trigger/providers/zoom/locales/ja.json +17 -0
- package/src/trigger/providers/zoom/locales/ko.json +17 -0
- package/src/trigger/providers/zoom/locales/ru.json +17 -0
- package/src/trigger/providers/zoom/locales/zh-CN.json +17 -0
- package/src/trigger/providers/zoom/locales/zh-TW.json +17 -0
- package/src/types/index.ts +112 -0
- package/src/workflow/common/workflow.ts +273 -0
- package/dist/browser/lib-DrKav3HG.js +0 -11403
- /package/dist/{browser → common}/flow-authoring-edge.d.ts +0 -0
- /package/dist/{browser → common}/flow-authoring-module.d.ts +0 -0
- /package/dist/{browser → common}/flow-authoring.d.ts +0 -0
- /package/dist/{browser → common}/flow-input-value.d.ts +0 -0
- /package/dist/{browser → common}/webhook-method.d.ts +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
export { resolveTriggerConfig, triggerConfigValues } from './config.ts'
|
|
2
|
+
import type { ConnectorProxy } from '../../connector/common/proxy.ts'
|
|
3
|
+
import type { JsonValue, TriggerKeySnapshot, TriggerSchedule } from '../../flow/common/change.ts'
|
|
4
|
+
import type { TriggerConfigOption, TriggerConfigOptionsContext } from './configOptions.ts'
|
|
5
|
+
|
|
6
|
+
const encoder = new TextEncoder()
|
|
7
|
+
|
|
8
|
+
export const maximumPollCheckpointBytes = 64 * 1024
|
|
9
|
+
export const maximumPollDedupeKeyBytes = 1024
|
|
10
|
+
export const maximumPollEventsPerPage = 100
|
|
11
|
+
|
|
12
|
+
export interface PollEvent {
|
|
13
|
+
readonly dedupeKey: string
|
|
14
|
+
readonly payload: Readonly<Record<string, JsonValue>>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PollResult {
|
|
18
|
+
readonly checkpoint: JsonValue
|
|
19
|
+
readonly events: readonly PollEvent[]
|
|
20
|
+
readonly filtered?: number
|
|
21
|
+
readonly hasMore?: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PollContext {
|
|
25
|
+
readonly checkpoint: JsonValue
|
|
26
|
+
/** Fixed inputs resolved with resolveTriggerConfig before invoking the Provider. */
|
|
27
|
+
readonly config: Readonly<Record<string, JsonValue>>
|
|
28
|
+
readonly connector: ConnectorProxy
|
|
29
|
+
readonly now: Date
|
|
30
|
+
readonly signal?: AbortSignal
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function eventsPollOutputs(events: readonly PollEvent[]): Readonly<Record<string, JsonValue>> {
|
|
34
|
+
return { events: events.map((event) => event.payload) }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface PollDefinition {
|
|
38
|
+
/** Constructs one Run's outputs from a nonempty batch of fresh, deduplicated events. */
|
|
39
|
+
readonly buildOutputs: (events: readonly PollEvent[]) => Readonly<Record<string, JsonValue>>
|
|
40
|
+
readonly configOptions?: (context: TriggerConfigOptionsContext) => Promise<readonly TriggerConfigOption[]>
|
|
41
|
+
readonly poll: (context: PollContext) => Promise<PollResult>
|
|
42
|
+
readonly snapshot: TriggerKeySnapshot & { readonly type: 'poll' }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class PollConnectionError extends Error {
|
|
46
|
+
override readonly name = 'PollConnectionError'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class PermanentPollError extends Error {
|
|
50
|
+
override readonly name = 'PermanentPollError'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class TransientPollError extends Error {
|
|
54
|
+
override readonly name = 'TransientPollError'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function pollPageClaimId(bindingId: string, runtimeVersion: number, rootOccurrenceId: string, page: number): Promise<string> {
|
|
58
|
+
if (!Number.isSafeInteger(page) || page <= 0) throw new RangeError('Poll continuation page must be a positive integer.')
|
|
59
|
+
return await digest(`${bindingId}\0${runtimeVersion}\0${rootOccurrenceId}\0${page}`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function providerEventId(bindingId: string, triggerKey: string, dedupeKey: string): Promise<string> {
|
|
63
|
+
if (encoder.encode(dedupeKey).byteLength > maximumPollDedupeKeyBytes) throw new RangeError('Poll event dedupe key exceeds 1024 bytes.')
|
|
64
|
+
return await digest(`${bindingId}\0${triggerKey}\0${dedupeKey}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface PollConformanceFixture {
|
|
68
|
+
readonly config: Readonly<Record<string, JsonValue>>
|
|
69
|
+
readonly connectionId: string
|
|
70
|
+
readonly publishedAt: string
|
|
71
|
+
readonly rules: readonly TriggerSchedule[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PollConformanceState {
|
|
75
|
+
readonly calls: number
|
|
76
|
+
readonly checkpoint: JsonValue
|
|
77
|
+
readonly health: 'healthy' | 'initializing'
|
|
78
|
+
readonly payloads: readonly JsonValue[]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface PollConformanceHarness {
|
|
82
|
+
dispose(): Promise<void>
|
|
83
|
+
replayLast(): Promise<void>
|
|
84
|
+
republish(at: string, change?: { readonly config?: Readonly<Record<string, JsonValue>>; readonly connectionId?: string }): Promise<void>
|
|
85
|
+
restart(): Promise<void>
|
|
86
|
+
retire(at: string): Promise<void>
|
|
87
|
+
state(): Promise<PollConformanceState>
|
|
88
|
+
tick(at: string, pages: readonly PollResult[]): Promise<void>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface PollConformanceCase {
|
|
92
|
+
readonly fixture: PollConformanceFixture
|
|
93
|
+
readonly name: string
|
|
94
|
+
verify(harness: PollConformanceHarness): Promise<void>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const minutely: readonly TriggerSchedule[] = [{ type: 'every', unit: 'minute', value: 1 }]
|
|
98
|
+
const fixture: PollConformanceFixture = {
|
|
99
|
+
config: { source: 'primary' },
|
|
100
|
+
connectionId: 'connection-primary',
|
|
101
|
+
publishedAt: '2026-08-21T00:00:30.000Z',
|
|
102
|
+
rules: minutely,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const pollConformanceCases: readonly PollConformanceCase[] = [
|
|
106
|
+
{
|
|
107
|
+
fixture,
|
|
108
|
+
name: 'baselines every continuation page before becoming healthy',
|
|
109
|
+
async verify(harness) {
|
|
110
|
+
await harness.tick('2026-08-21T00:01:00.000Z', [
|
|
111
|
+
{ checkpoint: { cursor: 'baseline-1' }, events: [{ dedupeKey: 'old-1', payload: { value: 'old-1' } }], hasMore: true },
|
|
112
|
+
{ checkpoint: { cursor: 'baseline-2' }, events: [{ dedupeKey: 'old-2', payload: { value: 'old-2' } }] },
|
|
113
|
+
])
|
|
114
|
+
equal(await harness.state(), { calls: 2, checkpoint: { cursor: 'baseline-2' }, health: 'healthy', payloads: [] }, 'Baseline state')
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
fixture,
|
|
119
|
+
name: 'batches fresh page events and suppresses duplicate identities',
|
|
120
|
+
async verify(harness) {
|
|
121
|
+
await harness.tick('2026-08-21T00:01:00.000Z', [{ checkpoint: { cursor: 'baseline' }, events: [] }])
|
|
122
|
+
const event = { dedupeKey: 'event-1', payload: { value: 'first' } } as const
|
|
123
|
+
await harness.tick('2026-08-21T00:02:00.000Z', [
|
|
124
|
+
{ checkpoint: { cursor: 'next' }, events: [event, event, { dedupeKey: 'event-2', payload: { value: 'second' } }] },
|
|
125
|
+
])
|
|
126
|
+
await harness.tick('2026-08-21T00:03:00.000Z', [{ checkpoint: { cursor: 'last' }, events: [event] }])
|
|
127
|
+
equal(
|
|
128
|
+
await harness.state(),
|
|
129
|
+
{
|
|
130
|
+
calls: 3,
|
|
131
|
+
checkpoint: { cursor: 'last' },
|
|
132
|
+
health: 'healthy',
|
|
133
|
+
payloads: [{ events: [{ value: 'first' }, { value: 'second' }] }],
|
|
134
|
+
},
|
|
135
|
+
'Deduplicated state',
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
fixture,
|
|
141
|
+
name: 'replays a completed root across restart without polling again',
|
|
142
|
+
async verify(harness) {
|
|
143
|
+
await harness.tick('2026-08-21T00:01:00.000Z', [{ checkpoint: { cursor: 'baseline' }, events: [] }])
|
|
144
|
+
await harness.tick('2026-08-21T00:02:00.000Z', [{ checkpoint: { cursor: 'event' }, events: [{ dedupeKey: 'event-1', payload: { value: 'first' } }] }])
|
|
145
|
+
await harness.restart()
|
|
146
|
+
await harness.replayLast()
|
|
147
|
+
equal(
|
|
148
|
+
await harness.state(),
|
|
149
|
+
{
|
|
150
|
+
calls: 2,
|
|
151
|
+
checkpoint: { cursor: 'event' },
|
|
152
|
+
health: 'healthy',
|
|
153
|
+
payloads: [{ events: [{ value: 'first' }] }],
|
|
154
|
+
},
|
|
155
|
+
'Replayed state',
|
|
156
|
+
)
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
fixture,
|
|
161
|
+
name: 'preserves an unchanged checkpoint and baselines changed semantics',
|
|
162
|
+
async verify(harness) {
|
|
163
|
+
await harness.tick('2026-08-21T00:01:00.000Z', [{ checkpoint: { cursor: 'baseline' }, events: [] }])
|
|
164
|
+
await harness.republish('2026-08-21T00:01:30.000Z')
|
|
165
|
+
equal(await harness.state(), { calls: 1, checkpoint: { cursor: 'baseline' }, health: 'healthy', payloads: [] }, 'Unchanged publication state')
|
|
166
|
+
await harness.republish('2026-08-21T00:01:40.000Z', { config: { source: 'secondary' } })
|
|
167
|
+
await harness.tick('2026-08-21T00:02:00.000Z', [
|
|
168
|
+
{ checkpoint: { cursor: 'secondary-baseline' }, events: [{ dedupeKey: 'old-secondary', payload: { value: 'old' } }] },
|
|
169
|
+
])
|
|
170
|
+
equal(await harness.state(), { calls: 2, checkpoint: { cursor: 'secondary-baseline' }, health: 'healthy', payloads: [] }, 'Changed publication state')
|
|
171
|
+
await harness.retire('2026-08-21T00:02:30.000Z')
|
|
172
|
+
await harness.tick('2026-08-21T00:03:00.000Z', [{ checkpoint: { cursor: 'retired' }, events: [] }])
|
|
173
|
+
equal(await harness.state(), { calls: 2, checkpoint: { cursor: 'secondary-baseline' }, health: 'healthy', payloads: [] }, 'Retired state')
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
async function digest(value: string): Promise<string> {
|
|
179
|
+
const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', encoder.encode(value)))
|
|
180
|
+
return `sha256:${Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')}`
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function equal(actual: unknown, expected: unknown, message: string): void {
|
|
184
|
+
if (JSON.stringify(actual) != JSON.stringify(expected)) {
|
|
185
|
+
throw new Error(`${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}.`)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import type { JsonValue, WebhookBodyField, WebhookMethod, WebhookOptions } from '../../flow/common/change.ts'
|
|
2
|
+
|
|
3
|
+
import { dequal } from 'dequal/lite'
|
|
4
|
+
import { isJsonObject } from '../../base/common/json.ts'
|
|
5
|
+
import { webhookHeaderNameIssue, validWebhookStatus, webhookHeaderValues, encodeWebhookBody } from '../../flow/common/webhookResponse.ts'
|
|
6
|
+
|
|
7
|
+
export { webhookSupportsBody } from './contract.ts'
|
|
8
|
+
export { webhookResponseAllowsBody, webhookResponseTimeout } from '../../flow/common/webhookResponse.ts'
|
|
9
|
+
|
|
10
|
+
export type { WebhookResponseSnapshot } from '../../flow/common/webhookResponse.ts'
|
|
11
|
+
|
|
12
|
+
export const maximumWebhookBodyBytes = 64 * 1024
|
|
13
|
+
|
|
14
|
+
/** Serialize only declared workflow business outputs, never node logs or execution metadata. */
|
|
15
|
+
export function webhookResponseBody(
|
|
16
|
+
result: unknown,
|
|
17
|
+
source: Extract<NonNullable<WebhookOptions['responseBody']>, { kind: 'workflow-outputs' }>,
|
|
18
|
+
): { readonly body: string; readonly contentType: string } | undefined {
|
|
19
|
+
const value = workflowOutputValue(result, source.output)
|
|
20
|
+
if (value === undefined) return
|
|
21
|
+
return encodeWebhookBody(value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function webhookResponseStatusCode(result: unknown, output: string): number | undefined {
|
|
25
|
+
const value = workflowOutputValue(result, output)
|
|
26
|
+
if (validWebhookStatus(value)) return value
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Project business headers before the HTTP layer applies its deployment-owned header policy. */
|
|
30
|
+
export function webhookResponseHeaders(result: unknown, options: WebhookOptions): Readonly<Record<string, string>> | undefined {
|
|
31
|
+
const whole = options.responseHeadersSource
|
|
32
|
+
const source = whole == null ? (options.responseHeaders ?? {}) : workflowOutputValue(result, whole.output)
|
|
33
|
+
if (!isJsonObject(source)) return
|
|
34
|
+
const values = new Map<string, { name: string; value: unknown }>()
|
|
35
|
+
for (const [name, value] of Object.entries(source)) {
|
|
36
|
+
if (
|
|
37
|
+
webhookHeaderNameIssue(
|
|
38
|
+
name,
|
|
39
|
+
[...values.values()].map((entry) => entry.name),
|
|
40
|
+
) != null
|
|
41
|
+
)
|
|
42
|
+
return
|
|
43
|
+
values.set(name.toLowerCase(), { name, value })
|
|
44
|
+
}
|
|
45
|
+
if (whole == null) {
|
|
46
|
+
const names: string[] = []
|
|
47
|
+
for (const [name, output] of Object.entries(options.responseHeaderOutputs ?? {})) {
|
|
48
|
+
if (webhookHeaderNameIssue(name, names) != null) return
|
|
49
|
+
names.push(name)
|
|
50
|
+
values.set(name.toLowerCase(), { name, value: workflowOutputValue(result, output) })
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return webhookHeaderValues(Object.fromEntries([...values.values()].map(({ name, value }) => [name, value])))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function workflowOutputValue(result: unknown, output?: string): JsonValue | undefined {
|
|
57
|
+
if (!isJsonObject(result) || result.kind != 'node-results' || !isJsonObject(result.outputs)) return
|
|
58
|
+
return output == null ? result.outputs : Object.hasOwn(result.outputs, output) ? result.outputs[output] : undefined
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const endpointPattern = /^\/v1\/webhooks\/(endpoint_[0-9a-f]{32})$/
|
|
62
|
+
const encoder = new TextEncoder()
|
|
63
|
+
|
|
64
|
+
export function webhookEndpointId(url: URL): string | undefined {
|
|
65
|
+
return endpointPattern.exec(url.pathname)?.[1]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function webhookOccurrenceId(endpointId: string, runtimeVersion: number, key: string | null): Promise<string | undefined> {
|
|
69
|
+
if (key == null) return `webhook_${crypto.randomUUID().replaceAll('-', '')}`
|
|
70
|
+
if (key.trim().length == 0 || key.length > 256) return
|
|
71
|
+
const source = JSON.stringify([2, 'webhook-occurrence', endpointId, runtimeVersion, key])
|
|
72
|
+
const bytes = new Uint8Array(await crypto.subtle.digest('SHA-256', encoder.encode(source)))
|
|
73
|
+
return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface WebhookConformanceFixture {
|
|
77
|
+
readonly bodyFields: readonly WebhookBodyField[]
|
|
78
|
+
readonly method: WebhookMethod
|
|
79
|
+
readonly options?: WebhookOptions
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WebhookConformanceHarness {
|
|
83
|
+
readonly endpointUrl: string
|
|
84
|
+
dispose(): Promise<void>
|
|
85
|
+
outputs(): Promise<readonly Readonly<Record<string, JsonValue>>[]>
|
|
86
|
+
republish(): Promise<void>
|
|
87
|
+
request(request: Request): Promise<Response>
|
|
88
|
+
retire(): Promise<void>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface WebhookConformanceCase {
|
|
92
|
+
readonly fixture: WebhookConformanceFixture
|
|
93
|
+
readonly name: string
|
|
94
|
+
verify(harness: WebhookConformanceHarness): Promise<void>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function equal(actual: unknown, expected: unknown, message: string): void {
|
|
98
|
+
if (!dequal(actual, expected)) {
|
|
99
|
+
throw new Error(`${message}: expected ${JSON.stringify(expected)}, received ${JSON.stringify(actual)}.`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function response(
|
|
104
|
+
actual: Response,
|
|
105
|
+
expected: { readonly body?: string; readonly headers?: Readonly<Record<string, string | null>>; readonly status: number },
|
|
106
|
+
message: string,
|
|
107
|
+
): Promise<void> {
|
|
108
|
+
equal(actual.status, expected.status, `${message} status`)
|
|
109
|
+
if (expected.body != null) equal(await actual.text(), expected.body, `${message} body`)
|
|
110
|
+
for (const [name, value] of Object.entries(expected.headers ?? {})) equal(actual.headers.get(name), value, `${message} header ${name}`)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function call(
|
|
114
|
+
harness: WebhookConformanceHarness,
|
|
115
|
+
input: { readonly body?: string; readonly headers?: Headers | Readonly<Record<string, string>>; readonly method?: string; readonly url?: string } = {},
|
|
116
|
+
): Promise<Response> {
|
|
117
|
+
return harness.request(
|
|
118
|
+
new Request(input.url ?? harness.endpointUrl, {
|
|
119
|
+
...(input.body == null ? {} : { body: input.body }),
|
|
120
|
+
headers: input.headers,
|
|
121
|
+
method: input.method ?? 'POST',
|
|
122
|
+
}),
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const noInputs: readonly WebhookBodyField[] = []
|
|
127
|
+
const messageInputs: readonly WebhookBodyField[] = [
|
|
128
|
+
{
|
|
129
|
+
handle: 'event',
|
|
130
|
+
jsonSchema: {
|
|
131
|
+
additionalProperties: false,
|
|
132
|
+
properties: { message: { type: 'string' } },
|
|
133
|
+
required: ['message'],
|
|
134
|
+
type: 'object',
|
|
135
|
+
},
|
|
136
|
+
nullable: false,
|
|
137
|
+
},
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
const messagePayload = { event: { message: 'hello' } } as const
|
|
141
|
+
|
|
142
|
+
export const webhookConformanceCases: readonly WebhookConformanceCase[] = [
|
|
143
|
+
{
|
|
144
|
+
fixture: { bodyFields: messageInputs, method: 'POST' },
|
|
145
|
+
name: 'stores complete outputs and compares query and body for idempotency',
|
|
146
|
+
async verify(harness) {
|
|
147
|
+
const url = `${harness.endpointUrl}?tag=a&tag=b&single=x&__proto__=safe&constructor=value`
|
|
148
|
+
const input = {
|
|
149
|
+
url,
|
|
150
|
+
body: JSON.stringify(messagePayload),
|
|
151
|
+
headers: { 'Idempotency-Key': 'output-contract', 'X-Request-ID': 'first', 'Content-Type': 'application/json' },
|
|
152
|
+
}
|
|
153
|
+
const replies = await Promise.all([call(harness, input), call(harness, input)])
|
|
154
|
+
equal(
|
|
155
|
+
replies.map((reply) => reply.status),
|
|
156
|
+
[200, 200],
|
|
157
|
+
'Concurrent retry statuses',
|
|
158
|
+
)
|
|
159
|
+
const stored = await harness.outputs()
|
|
160
|
+
equal(stored.length, 1, 'One admitted Run')
|
|
161
|
+
const output = stored[0]!
|
|
162
|
+
equal(Object.keys(output).toSorted(), ['body', 'headers', 'query', 'webhookUrl'], 'Exact output handles')
|
|
163
|
+
equal(output.body, messagePayload, 'Body')
|
|
164
|
+
equal(output.query, JSON.parse('{"tag":["a","b"],"single":"x","__proto__":"safe","constructor":"value"}'), 'Query')
|
|
165
|
+
equal(output.webhookUrl, harness.endpointUrl, 'Endpoint without query')
|
|
166
|
+
equal((output.headers as Record<string, JsonValue>)['x-request-id'], 'first', 'Lowercase headers')
|
|
167
|
+
await response(await call(harness, { ...input, headers: { ...input.headers, 'X-Request-ID': 'retry' } }), { status: 200 }, 'Changed header retry')
|
|
168
|
+
await response(await call(harness, { ...input, url: url.replace(new URL(url).host, 'retry.example.com') }), { status: 200 }, 'Changed request URL host')
|
|
169
|
+
equal(await harness.outputs(), stored, 'First outputs remain unchanged')
|
|
170
|
+
await response(await call(harness, { ...input, method: 'PUT' }), { headers: { allow: 'POST' }, status: 405 }, 'Rejected method')
|
|
171
|
+
await response(await call(harness, { ...input, url: `${url}&extra=1` }), { status: 409 }, 'Changed query')
|
|
172
|
+
await response(await call(harness, { ...input, body: JSON.stringify({ event: { message: 'changed' } }) }), { status: 409 }, 'Changed body')
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
fixture: { bodyFields: noInputs, method: 'POST' },
|
|
177
|
+
name: 'rejects malformed and unknown endpoint identities',
|
|
178
|
+
async verify(harness) {
|
|
179
|
+
const endpoint = new URL(harness.endpointUrl)
|
|
180
|
+
await response(
|
|
181
|
+
await call(harness, { url: new URL('/v1/webhooks/not-an-endpoint', endpoint).href }),
|
|
182
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 404 },
|
|
183
|
+
'Malformed endpoint',
|
|
184
|
+
)
|
|
185
|
+
await response(
|
|
186
|
+
await call(harness, { url: new URL('/v1/webhooks/endpoint_00000000000000000000000000000000', endpoint).href }),
|
|
187
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 404 },
|
|
188
|
+
'Unknown endpoint',
|
|
189
|
+
)
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
fixture: { bodyFields: noInputs, method: 'POST' },
|
|
194
|
+
name: 'uses configured POST and decodes an empty body as an empty object',
|
|
195
|
+
async verify(harness) {
|
|
196
|
+
await response(await call(harness, { method: 'GET' }), { headers: { 'allow': 'POST', 'cache-control': 'no-store' }, status: 405 }, 'Default method')
|
|
197
|
+
await response(await call(harness), { body: '', headers: { 'cache-control': 'no-store' }, status: 200 }, 'Empty payload')
|
|
198
|
+
equal(
|
|
199
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
200
|
+
[{}],
|
|
201
|
+
'Decoded empty payloads',
|
|
202
|
+
)
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
fixture: {
|
|
207
|
+
bodyFields: noInputs,
|
|
208
|
+
method: 'PUT',
|
|
209
|
+
options: {
|
|
210
|
+
allowedOrigins: ['https://client.example'],
|
|
211
|
+
responseData: 'accepted',
|
|
212
|
+
responseHeaders: { 'x-webhook-response': 'configured' },
|
|
213
|
+
responseStatusCode: 202,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
name: 'applies configured methods, CORS, preflight, and success response',
|
|
217
|
+
async verify(harness) {
|
|
218
|
+
await response(
|
|
219
|
+
await call(harness, {
|
|
220
|
+
headers: {
|
|
221
|
+
'access-control-request-headers': 'content-type, idempotency-key',
|
|
222
|
+
'access-control-request-method': 'PUT',
|
|
223
|
+
'origin': 'https://client.example',
|
|
224
|
+
},
|
|
225
|
+
method: 'OPTIONS',
|
|
226
|
+
}),
|
|
227
|
+
{
|
|
228
|
+
body: '',
|
|
229
|
+
headers: {
|
|
230
|
+
'access-control-allow-headers': 'content-type, idempotency-key',
|
|
231
|
+
'access-control-allow-methods': 'PUT',
|
|
232
|
+
'access-control-allow-origin': 'https://client.example',
|
|
233
|
+
'access-control-max-age': '600',
|
|
234
|
+
},
|
|
235
|
+
status: 204,
|
|
236
|
+
},
|
|
237
|
+
'Preflight',
|
|
238
|
+
)
|
|
239
|
+
await response(
|
|
240
|
+
await call(harness, { body: '{}', headers: { origin: 'https://client.example' }, method: 'PUT' }),
|
|
241
|
+
{
|
|
242
|
+
body: 'accepted',
|
|
243
|
+
headers: {
|
|
244
|
+
'access-control-allow-origin': 'https://client.example',
|
|
245
|
+
'cache-control': 'no-store',
|
|
246
|
+
'x-webhook-response': 'configured',
|
|
247
|
+
},
|
|
248
|
+
status: 202,
|
|
249
|
+
},
|
|
250
|
+
'Configured success',
|
|
251
|
+
)
|
|
252
|
+
await response(
|
|
253
|
+
await call(harness, { body: '{}', headers: { origin: 'https://other.example' }, method: 'PUT' }),
|
|
254
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 403 },
|
|
255
|
+
'Disallowed origin',
|
|
256
|
+
)
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
fixture: {
|
|
261
|
+
bodyFields: noInputs,
|
|
262
|
+
method: 'POST',
|
|
263
|
+
options: {
|
|
264
|
+
responseData: '<script>globalThis.compromised = true</script>',
|
|
265
|
+
responseHeaders: {
|
|
266
|
+
'access-control-allow-credentials': 'true',
|
|
267
|
+
'content-security-policy': "default-src * 'unsafe-inline'",
|
|
268
|
+
'content-type': 'text/html',
|
|
269
|
+
'location': 'https://attacker.example',
|
|
270
|
+
'set-cookie': 'open_flow_operator_session=forged',
|
|
271
|
+
'x-accel-redirect': '/auth/session',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
name: 'prevents Flow responses from becoming executable or controlling deployment headers',
|
|
276
|
+
async verify(harness) {
|
|
277
|
+
await response(
|
|
278
|
+
await call(harness),
|
|
279
|
+
{
|
|
280
|
+
body: '<script>globalThis.compromised = true</script>',
|
|
281
|
+
headers: {
|
|
282
|
+
'access-control-allow-credentials': null,
|
|
283
|
+
'content-security-policy': "default-src 'none'; frame-ancestors 'none'; sandbox",
|
|
284
|
+
'content-type': 'text/html',
|
|
285
|
+
'location': null,
|
|
286
|
+
'referrer-policy': 'no-referrer',
|
|
287
|
+
'set-cookie': null,
|
|
288
|
+
'x-accel-redirect': null,
|
|
289
|
+
'x-content-type-options': 'nosniff',
|
|
290
|
+
'x-frame-options': 'DENY',
|
|
291
|
+
},
|
|
292
|
+
status: 200,
|
|
293
|
+
},
|
|
294
|
+
'Safe configured response',
|
|
295
|
+
)
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
fixture: { bodyFields: messageInputs, method: 'POST' },
|
|
300
|
+
name: 'rejects invalid JSON, invalid payloads, invalid idempotency keys, and oversized bodies',
|
|
301
|
+
async verify(harness) {
|
|
302
|
+
await response(await call(harness, { body: '{' }), { body: '', headers: { 'cache-control': 'no-store' }, status: 400 }, 'Invalid JSON')
|
|
303
|
+
await response(
|
|
304
|
+
await call(harness, { body: JSON.stringify({ event: {} }) }),
|
|
305
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 400 },
|
|
306
|
+
'Invalid payload',
|
|
307
|
+
)
|
|
308
|
+
await response(
|
|
309
|
+
await call(harness, { body: JSON.stringify(messagePayload), headers: { 'idempotency-key': ' ' } }),
|
|
310
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 400 },
|
|
311
|
+
'Empty key',
|
|
312
|
+
)
|
|
313
|
+
await response(
|
|
314
|
+
await call(harness, { body: JSON.stringify(messagePayload), headers: { 'idempotency-key': 'x'.repeat(257) } }),
|
|
315
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 400 },
|
|
316
|
+
'Oversized key',
|
|
317
|
+
)
|
|
318
|
+
await response(
|
|
319
|
+
await call(harness, { body: 'x'.repeat(maximumWebhookBodyBytes + 1) }),
|
|
320
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 413 },
|
|
321
|
+
'Oversized body',
|
|
322
|
+
)
|
|
323
|
+
equal(
|
|
324
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
325
|
+
[],
|
|
326
|
+
'Rejected payloads',
|
|
327
|
+
)
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
fixture: { bodyFields: messageInputs, method: 'POST' },
|
|
332
|
+
name: 'replays one idempotent occurrence and rejects conflicting payloads',
|
|
333
|
+
async verify(harness) {
|
|
334
|
+
const headers = { 'idempotency-key': 'delivery-1' }
|
|
335
|
+
await response(await call(harness, { body: JSON.stringify(messagePayload), headers }), { status: 200 }, 'Initial delivery')
|
|
336
|
+
await response(await call(harness, { body: JSON.stringify(messagePayload), headers }), { status: 200 }, 'Repeated delivery')
|
|
337
|
+
await response(
|
|
338
|
+
await call(harness, { body: JSON.stringify({ event: { message: 'different' } }), headers }),
|
|
339
|
+
{ body: '', headers: { 'cache-control': 'no-store' }, status: 409 },
|
|
340
|
+
'Conflicting delivery',
|
|
341
|
+
)
|
|
342
|
+
equal(
|
|
343
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
344
|
+
[messagePayload],
|
|
345
|
+
'Idempotent payloads',
|
|
346
|
+
)
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
fixture: { bodyFields: messageInputs, method: 'POST' },
|
|
351
|
+
name: 'admits requests without an idempotency key as separate occurrences',
|
|
352
|
+
async verify(harness) {
|
|
353
|
+
const body = JSON.stringify(messagePayload)
|
|
354
|
+
await response(await call(harness, { body }), { status: 200 }, 'First unkeyed delivery')
|
|
355
|
+
await response(await call(harness, { body }), { status: 200 }, 'Second unkeyed delivery')
|
|
356
|
+
equal(
|
|
357
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
358
|
+
[messagePayload, messagePayload],
|
|
359
|
+
'Unkeyed payloads',
|
|
360
|
+
)
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
fixture: { bodyFields: messageInputs, method: 'POST' },
|
|
365
|
+
name: 'scopes occurrence identity to the current runtime version',
|
|
366
|
+
async verify(harness) {
|
|
367
|
+
const body = JSON.stringify(messagePayload)
|
|
368
|
+
const headers = { 'idempotency-key': 'delivery-across-publish' }
|
|
369
|
+
await response(await call(harness, { body, headers }), { status: 200 }, 'Delivery before publish')
|
|
370
|
+
const endpointUrl = harness.endpointUrl
|
|
371
|
+
await harness.republish()
|
|
372
|
+
equal(harness.endpointUrl, endpointUrl, 'Endpoint after publish')
|
|
373
|
+
await response(await call(harness, { body, headers }), { status: 200 }, 'Delivery after publish')
|
|
374
|
+
equal(
|
|
375
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
376
|
+
[messagePayload, messagePayload],
|
|
377
|
+
'Payloads across publish',
|
|
378
|
+
)
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
fixture: { bodyFields: noInputs, method: 'POST' },
|
|
383
|
+
name: 'fails closed after its trigger is retired',
|
|
384
|
+
async verify(harness) {
|
|
385
|
+
await harness.retire()
|
|
386
|
+
await response(await call(harness, { body: '{}' }), { body: '', headers: { 'cache-control': 'no-store' }, status: 404 }, 'Retired endpoint')
|
|
387
|
+
equal(
|
|
388
|
+
(await harness.outputs()).map((outputs) => outputs.body),
|
|
389
|
+
[],
|
|
390
|
+
'Payloads after retirement',
|
|
391
|
+
)
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
fixture: { bodyFields: noInputs, method: 'POST', options: { responseData: 'ignored', responseStatusCode: 204 } },
|
|
396
|
+
name: 'omits a configured response body for null-body statuses',
|
|
397
|
+
async verify(harness) {
|
|
398
|
+
await response(await call(harness, { body: '{}' }), { body: '', status: 204 }, 'Null-body status')
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
fixture: { bodyFields: noInputs, method: 'GET', options: { responseData: 'ignored' } },
|
|
403
|
+
name: 'omits the body output for GET requests',
|
|
404
|
+
async verify(harness) {
|
|
405
|
+
await response(await call(harness, { method: 'GET' }), { body: 'ignored', status: 200 }, 'GET response')
|
|
406
|
+
const outputs = await harness.outputs()
|
|
407
|
+
equal(outputs.length, 1, 'One GET run')
|
|
408
|
+
equal(Object.keys(outputs[0]!).toSorted(), ['headers', 'query', 'webhookUrl'], 'GET outputs omit body')
|
|
409
|
+
await response(await call(harness, { method: 'HEAD' }), { body: '', headers: { allow: 'GET' }, status: 405 }, 'HEAD rejected')
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"airtable.on_record_changed": {
|
|
3
|
+
"displayName": "Création ou mise à jour d’un enregistrement",
|
|
4
|
+
"description": "Interroge périodiquement une table Airtable et se déclenche à la création ou à la mise à jour d’un enregistrement, dans l’ordre d’un champ temporel.",
|
|
5
|
+
"configInputs": {
|
|
6
|
+
"baseId": "ID de la base Airtable à surveiller.",
|
|
7
|
+
"fields": "Champs à inclure dans chaque enregistrement retourné. Laissez vide pour inclure tous les champs.",
|
|
8
|
+
"formula": "Formule Airtable utilisée pour filtrer les enregistrements. Laissez vide pour inclure tous les enregistrements.",
|
|
9
|
+
"maxRecordsPerPoll": "Nombre maximal d'enregistrements traités par interrogation.",
|
|
10
|
+
"tableIdOrName": "ID ou nom de la table Airtable à surveiller.",
|
|
11
|
+
"triggerField": "Champ de date ou d'heure utilisé pour détecter les enregistrements créés ou mis à jour.",
|
|
12
|
+
"view": "Vue Airtable utilisée pour filtrer et trier les enregistrements. Laissez vide pour utiliser la vue par défaut de la table."
|
|
13
|
+
},
|
|
14
|
+
"outputs": {
|
|
15
|
+
"events": "Enregistrements créés ou mis à jour depuis la précédente interrogation."
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"airtable.on_record_changed": {
|
|
3
|
+
"displayName": "レコードの作成・更新",
|
|
4
|
+
"description": "Airtable のテーブルを定期的に確認し、時刻フィールド順にレコードの作成や更新を検出すると実行します。",
|
|
5
|
+
"configInputs": {
|
|
6
|
+
"baseId": "監視対象のAirtableベースのID。",
|
|
7
|
+
"fields": "返される各レコードに含めるフィールド。空欄にするとすべてのフィールドを含みます。",
|
|
8
|
+
"formula": "レコードをフィルタリングするために使用するAirtableの式。空欄にするとすべてのレコードを含みます。",
|
|
9
|
+
"maxRecordsPerPoll": "1回のポーリングで処理する最大レコード数。",
|
|
10
|
+
"tableIdOrName": "監視対象のAirtableテーブルのIDまたは名前。",
|
|
11
|
+
"triggerField": "作成または更新されたレコードを検出するために使用する日付または時刻のフィールド。",
|
|
12
|
+
"view": "レコードをフィルタリングおよび並べ替えるために使用するAirtableビュー。空欄にするとテーブルのデフォルトを使用します。"
|
|
13
|
+
},
|
|
14
|
+
"outputs": {
|
|
15
|
+
"events": "前のポーリング以降に作成または更新されたレコード。"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"airtable.on_record_changed": {
|
|
3
|
+
"displayName": "레코드 생성 또는 업데이트",
|
|
4
|
+
"description": "Airtable 테이블을 주기적으로 확인하고 시간 필드 순서에 따라 레코드가 생성되거나 업데이트되면 실행합니다.",
|
|
5
|
+
"configInputs": {
|
|
6
|
+
"baseId": "모니터링할 Airtable 베이스 ID.",
|
|
7
|
+
"fields": "반환되는 각 레코드에 포함할 필드. 모든 필드를 포함하려면 비워 두세요.",
|
|
8
|
+
"formula": "레코드를 필터링하는 Airtable 수식. 모든 레코드를 포함하려면 비워 두세요.",
|
|
9
|
+
"maxRecordsPerPoll": "각 폴링에서 처리할 최대 레코드 수.",
|
|
10
|
+
"tableIdOrName": "모니터링할 Airtable 테이블 ID 또는 이름.",
|
|
11
|
+
"triggerField": "생성 또는 업데이트된 레코드를 감지하는 데 사용되는 날짜 또는 시간 필드.",
|
|
12
|
+
"view": "레코드를 필터링하고 정렬하는 데 사용되는 Airtable 보기. 테이블 기본값을 사용하려면 비워 두세요."
|
|
13
|
+
},
|
|
14
|
+
"outputs": {
|
|
15
|
+
"events": "이전 폴링 이후 생성되거나 업데이트된 레코드."
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"airtable.on_record_changed": {
|
|
3
|
+
"displayName": "Создание или изменение записи",
|
|
4
|
+
"description": "Периодически проверяет таблицу Airtable и срабатывает при создании или изменении записи, упорядочивая записи по полю времени.",
|
|
5
|
+
"configInputs": {
|
|
6
|
+
"baseId": "ID базы Airtable, которую нужно отслеживать.",
|
|
7
|
+
"fields": "Поля, которые должны быть включены в каждую возвращаемую запись. Оставьте пустым, чтобы включить все поля.",
|
|
8
|
+
"formula": "Формула Airtable, используемая для фильтрации записей. Оставьте пустым, чтобы включить все записи.",
|
|
9
|
+
"maxRecordsPerPoll": "Максимальное количество записей, обрабатываемых за одно опрос.",
|
|
10
|
+
"tableIdOrName": "ID или название таблицы Airtable, которую нужно отслеживать.",
|
|
11
|
+
"triggerField": "Поле даты или времени, используемое для определения созданных или обновленных записей.",
|
|
12
|
+
"view": "Вид Airtable, используемый для фильтрации и сортировки записей. Оставьте пустым, чтобы использовать настройку по умолчанию для таблицы."
|
|
13
|
+
},
|
|
14
|
+
"outputs": {
|
|
15
|
+
"events": "Записи, созданные или обновленные с момента предыдущего опроса."
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|