@oomol-lab/open-flow 0.1.0-beta.73 → 0.1.0-beta.75
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 +41 -0
- package/dist/browser/{ContentIcon-CUQHjQIw.js → ContentIcon-FCpKiwjp.js} +2 -1
- package/dist/browser/{FlowCanvas-Dtf8_cQp.js → FlowCanvas-dzRBnlUT.js} +1 -1
- package/dist/browser/{createResourceDialog-BWJkjiOO.js → createResourceDialog-BeYDdUl-.js} +2 -2
- package/dist/browser/{dialog-CUy_fwx9.js → dialog-ZtpsFbl4.js} +337 -31
- package/dist/browser/{exportSvg-BOoy7-Db.js → exportSvg-DqXw1yf-.js} +2 -2
- package/dist/browser/{flowChanges-CgSnSqqP.js → flowChanges-b_5UobFj.js} +586 -87
- package/dist/browser/{flowWorkspace-Bf7H-r0t.js → flowWorkspace-ItojZbO4.js} +21162 -20242
- package/dist/browser/preview.css +254 -179
- package/dist/browser/preview.js +22201 -20625
- package/dist/browser/{typeScriptSession-B0hC-C8y.js → typeScriptSession-CSRwWmZm.js} +1 -1
- package/dist/browser/ui.css +72 -2
- package/dist/browser/workbench.css +254 -179
- package/dist/browser/workbench.js +22 -78
- package/dist/browser/{workbenchSelect-C3e4J93X.js → workbenchSelect-M441Lz0d.js} +1 -1
- package/dist/command/index.js +42 -42
- package/dist/common/authoring.d.ts +5 -5
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringFields.d.ts +1 -1
- package/dist/common/authoringSchema.d.ts +95 -8
- 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 +119 -31
- package/dist/common/control-api.d.ts +4 -3
- package/dist/common/control-api.js +117 -29
- package/dist/common/control-requests.d.ts +5 -4
- package/dist/common/control-requests.js +437 -103
- 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 +4 -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 +131 -30
- 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 +133 -32
- package/dist/common/flow-connectionUsage.d.ts +1 -1
- package/dist/common/flow-encoding.d.ts +1 -1
- package/dist/common/flow-encoding.js +89 -23
- package/dist/common/flow-errorHandling.d.ts +1 -1
- package/dist/common/flow-graph.d.ts +3 -2
- 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 +2 -2
- package/dist/common/flow-semantics.d.ts +1 -1
- package/dist/common/flow-semantics.js +966 -614
- package/dist/common/flow-workflowFile.d.ts +1 -1
- package/dist/common/flowInspection.d.ts +3 -3
- package/dist/common/http-request.d.ts +156 -0
- package/dist/common/http-request.js +5254 -0
- 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 +5 -4
- package/dist/common/mcp.js +117 -29
- package/dist/common/openapi.d.ts +1 -1
- package/dist/common/poll-trigger.d.ts +1 -1
- package/dist/common/run-events.js +11 -9
- package/dist/common/run-results.d.ts +8 -1
- package/dist/common/runtime-contract.d.ts +8 -2
- package/dist/common/runtime-contract.js +25 -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 +3 -3
- package/dist/common/scheduler.js +101 -33
- 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 -3
- package/dist/common/webhook-trigger.d.ts +1 -1
- package/dist/common/webhook-trigger.js +12 -11
- 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 +11 -6
- package/skills/open-flow/references/flow-authoring.md +1 -0
- package/skills/open-flow/references/flow-n8n-conversion.md +19 -15
- 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 +1506 -0
- package/src/control/common/authoring.ts +923 -0
- package/src/control/common/authoringCondition.ts +110 -0
- package/src/control/common/authoringDiagnosticFields.ts +136 -0
- package/src/control/common/authoringExamples.ts +200 -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 +410 -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 +84 -0
- package/src/execution/common/events.ts +331 -0
- package/src/execution/common/node-execution.ts +469 -0
- package/src/execution/common/runLifecycle.ts +250 -0
- package/src/execution/common/runtime.ts +563 -0
- package/src/execution/common/scheduler.ts +697 -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 +1037 -0
- package/src/flow/common/changeSchema.ts +792 -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 +898 -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 +531 -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 +71 -0
- package/src/flow/common/workflowFile.ts +97 -0
- package/src/http/common/authBindings.ts +12 -0
- package/src/http/common/headers.ts +5 -0
- package/src/http/common/httpRequest.ts +251 -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/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 +413 -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,459 @@
|
|
|
1
|
+
import type { InputPort, JsonValue, ManagedTaskDefinition, Port } from '../../flow/common/change.ts'
|
|
2
|
+
|
|
3
|
+
import { dequal } from 'dequal/lite'
|
|
4
|
+
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
5
|
+
|
|
6
|
+
type ObjectValue = { [key: string]: JsonValue }
|
|
7
|
+
export interface OpenApiAuth {
|
|
8
|
+
readonly id: string
|
|
9
|
+
readonly type: 'bearer' | 'basic' | 'apiKey'
|
|
10
|
+
readonly name?: string
|
|
11
|
+
readonly in?: 'header' | 'query'
|
|
12
|
+
}
|
|
13
|
+
export interface OpenApiExecutor {
|
|
14
|
+
readonly kind: 'openapi'
|
|
15
|
+
readonly sourceUrl: string
|
|
16
|
+
readonly method: string
|
|
17
|
+
readonly path: string
|
|
18
|
+
readonly serverUrl: string
|
|
19
|
+
readonly document: JsonValue
|
|
20
|
+
readonly auth: readonly OpenApiAuth[]
|
|
21
|
+
}
|
|
22
|
+
export interface OpenApiOperation {
|
|
23
|
+
readonly method: string
|
|
24
|
+
readonly path: string
|
|
25
|
+
readonly label: string
|
|
26
|
+
readonly tag: string
|
|
27
|
+
}
|
|
28
|
+
const methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']
|
|
29
|
+
export function object(value: unknown): ObjectValue {
|
|
30
|
+
if (value == null || typeof value != 'object' || Array.isArray(value)) throw new Error('Expected an OpenAPI object.')
|
|
31
|
+
return value as ObjectValue
|
|
32
|
+
}
|
|
33
|
+
export function httpUrl(value: string, base?: string): string {
|
|
34
|
+
const url = new URL(value, base)
|
|
35
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash)
|
|
36
|
+
throw new Error('An HTTP(S) URL without credentials or fragment is required.')
|
|
37
|
+
return url.href
|
|
38
|
+
}
|
|
39
|
+
export function parseOpenApi(value: unknown): ObjectValue {
|
|
40
|
+
const doc = object(value)
|
|
41
|
+
if (typeof doc.openapi != 'string' || !/^3\.[01]\.\d+$/.test(doc.openapi)) throw new Error('Only OpenAPI 3.0 and 3.1 JSON documents are supported.')
|
|
42
|
+
object(doc.paths)
|
|
43
|
+
return doc
|
|
44
|
+
}
|
|
45
|
+
function pointer(doc: ObjectValue, ref: string): JsonValue {
|
|
46
|
+
if (!ref.startsWith('#/')) throw new Error('External OpenAPI references are not supported.')
|
|
47
|
+
let value: JsonValue = doc
|
|
48
|
+
for (const key of decodeURIComponent(ref.slice(2))
|
|
49
|
+
.split('/')
|
|
50
|
+
.map((part) => part.replace(/~1/g, '/').replace(/~0/g, '~'))) {
|
|
51
|
+
const source = object(value)
|
|
52
|
+
if (!Object.hasOwn(source, key)) throw new Error('An OpenAPI reference is missing.')
|
|
53
|
+
value = source[key]!
|
|
54
|
+
}
|
|
55
|
+
return value
|
|
56
|
+
}
|
|
57
|
+
function resolved(doc: ObjectValue, value: JsonValue): ObjectValue {
|
|
58
|
+
let result = object(value)
|
|
59
|
+
const seen = new Set<string>()
|
|
60
|
+
while (typeof result.$ref == 'string') {
|
|
61
|
+
if (seen.has(result.$ref)) throw new Error('Circular non-schema reference is unsupported.')
|
|
62
|
+
seen.add(result.$ref)
|
|
63
|
+
const { $ref, ...siblings } = result
|
|
64
|
+
result = { ...object(pointer(doc, $ref)), ...siblings }
|
|
65
|
+
}
|
|
66
|
+
return result
|
|
67
|
+
}
|
|
68
|
+
export function listOperations(documentValue: JsonValue): OpenApiOperation[] {
|
|
69
|
+
const doc = parseOpenApi(documentValue)
|
|
70
|
+
return Object.entries(object(doc.paths)).flatMap(([path, value]) => {
|
|
71
|
+
const item = resolved(doc, value)
|
|
72
|
+
return methods
|
|
73
|
+
.filter((method) => item[method] != null)
|
|
74
|
+
.map((method) => {
|
|
75
|
+
const op = object(item[method])
|
|
76
|
+
return {
|
|
77
|
+
method,
|
|
78
|
+
path,
|
|
79
|
+
label: String(op.summary ?? op.operationId ?? `${method.toUpperCase()} ${path}`),
|
|
80
|
+
tag: String((op.tags as JsonValue[] | undefined)?.[0] ?? ''),
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
function operation(doc: ObjectValue, path: string, method: string): { item: ObjectValue; op: ObjectValue } {
|
|
86
|
+
const item = resolved(doc, object(doc.paths)[path]!)
|
|
87
|
+
if (!methods.includes(method) || item[method] == null) throw new Error('The selected operation is no longer available.')
|
|
88
|
+
return { item, op: object(item[method]) }
|
|
89
|
+
}
|
|
90
|
+
export function servers(value: JsonValue, path: string, method: string): readonly ObjectValue[] {
|
|
91
|
+
const doc = parseOpenApi(value)
|
|
92
|
+
const { item, op } = operation(doc, path, method)
|
|
93
|
+
const entries = op.servers ?? item.servers ?? doc.servers
|
|
94
|
+
if (!Array.isArray(entries) || entries.length == 0) return [{ url: '/' }]
|
|
95
|
+
return entries.map(object)
|
|
96
|
+
}
|
|
97
|
+
export function serverAddress(server: ObjectValue, sourceUrl: string, variables: Readonly<Record<string, string>> = {}): string {
|
|
98
|
+
return httpUrl(
|
|
99
|
+
String(server.url).replace(/\{([^}]+)\}/g, (_, name: string) => {
|
|
100
|
+
const definition = object(object(server.variables)[name])
|
|
101
|
+
const value = variables[name] ?? definition.default
|
|
102
|
+
if (typeof value != 'string' || (Array.isArray(definition.enum) && !definition.enum.includes(value))) throw new Error('A server variable is invalid.')
|
|
103
|
+
return value
|
|
104
|
+
}),
|
|
105
|
+
sourceUrl,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
export function securityOptions(value: JsonValue, path: string, method: string): readonly { label: string; auth?: readonly OpenApiAuth[]; error?: string }[] {
|
|
109
|
+
const doc = parseOpenApi(value)
|
|
110
|
+
const { op } = operation(doc, path, method)
|
|
111
|
+
const requirements = op.security ?? doc.security ?? []
|
|
112
|
+
if (!Array.isArray(requirements)) throw new Error('Invalid security requirements.')
|
|
113
|
+
if (!requirements.length) return [{ label: 'None', auth: [] }]
|
|
114
|
+
return requirements.map((entry) => {
|
|
115
|
+
const names = Object.keys(object(entry))
|
|
116
|
+
const label = names.join(' + ') || 'None'
|
|
117
|
+
try {
|
|
118
|
+
if (!names.length) return { label, auth: [] }
|
|
119
|
+
const schemes = object(object(doc.components).securitySchemes)
|
|
120
|
+
return {
|
|
121
|
+
label,
|
|
122
|
+
auth: names.map((id): OpenApiAuth => {
|
|
123
|
+
const scheme = resolved(doc, schemes[id]!)
|
|
124
|
+
if (scheme.type == 'http' && ['bearer', 'basic'].includes(String(scheme.scheme).toLowerCase()))
|
|
125
|
+
return { id, type: String(scheme.scheme).toLowerCase() as 'bearer' | 'basic' }
|
|
126
|
+
if (scheme.type == 'apiKey' && (scheme.in == 'header' || scheme.in == 'query') && typeof scheme.name == 'string')
|
|
127
|
+
return { id, type: 'apiKey', name: scheme.name, in: scheme.in }
|
|
128
|
+
throw new Error('This security scheme is unsupported. Supply an access token using manual Bearer authentication if applicable.')
|
|
129
|
+
}),
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return { label, error: (error as Error).message }
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
// The miniature document retains only the selected operation and reachable references.
|
|
137
|
+
export function snapshot(documentValue: JsonValue, path: string, method: string): JsonValue {
|
|
138
|
+
const doc = parseOpenApi(documentValue)
|
|
139
|
+
const { item, op } = operation(doc, path, method)
|
|
140
|
+
const security = op.security ?? doc.security ?? []
|
|
141
|
+
const result: ObjectValue = {
|
|
142
|
+
openapi: doc.openapi!,
|
|
143
|
+
paths: { [path]: { [method]: op, ...(item.parameters == null ? {} : { parameters: item.parameters }) } },
|
|
144
|
+
servers: servers(doc, path, method) as JsonValue,
|
|
145
|
+
security,
|
|
146
|
+
}
|
|
147
|
+
const visited = new Set<string>()
|
|
148
|
+
function copy(ref: string): void {
|
|
149
|
+
if (visited.has(ref)) return
|
|
150
|
+
visited.add(ref)
|
|
151
|
+
const value = pointer(doc, ref)
|
|
152
|
+
const keys = decodeURIComponent(ref.slice(2))
|
|
153
|
+
.split('/')
|
|
154
|
+
.map((part) => part.replace(/~1/g, '/').replace(/~0/g, '~'))
|
|
155
|
+
if (keys.some((key) => ['__proto__', 'constructor', 'prototype'].includes(key))) throw new Error('Unsupported reference key.')
|
|
156
|
+
let target = result
|
|
157
|
+
for (const key of keys.slice(0, -1)) {
|
|
158
|
+
if (!Object.hasOwn(target, key)) target[key] = {}
|
|
159
|
+
target = object(target[key])
|
|
160
|
+
}
|
|
161
|
+
target[keys.at(-1)!] = structuredClone(value)
|
|
162
|
+
visit(value)
|
|
163
|
+
}
|
|
164
|
+
function visit(value: JsonValue): void {
|
|
165
|
+
if (value == null || typeof value != 'object') return
|
|
166
|
+
if (Array.isArray(value)) {
|
|
167
|
+
value.forEach(visit)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
if (typeof object(value).$ref == 'string') copy(object(value).$ref as string)
|
|
171
|
+
for (const [key, child] of Object.entries(value)) if (!['example', 'examples', 'default'].includes(key)) visit(child)
|
|
172
|
+
}
|
|
173
|
+
visit(result)
|
|
174
|
+
for (const entry of security as JsonValue[])
|
|
175
|
+
for (const name of Object.keys(object(entry))) copy(`#/components/securitySchemes/${name.replace(/~/g, '~0').replace(/\//g, '~1')}`)
|
|
176
|
+
return structuredClone(result)
|
|
177
|
+
}
|
|
178
|
+
function schema(doc: ObjectValue, schemaValue: JsonValue): JsonValue {
|
|
179
|
+
const defs: ObjectValue = {}
|
|
180
|
+
const ids = new Map<string, string>()
|
|
181
|
+
function convert(value: JsonValue): JsonValue {
|
|
182
|
+
if (typeof value == 'boolean') return value
|
|
183
|
+
const source = object(value)
|
|
184
|
+
if (source.format == 'binary' || source.$dynamicRef != null || source.$id != null) throw new Error('Binary and dynamically scoped schemas are unsupported.')
|
|
185
|
+
const result: ObjectValue = {}
|
|
186
|
+
for (const [key, child] of Object.entries(source)) {
|
|
187
|
+
if (['nullable', 'example', 'xml', 'discriminator', '__proto__'].includes(key)) continue
|
|
188
|
+
if (key == '$ref') {
|
|
189
|
+
if (typeof child != 'string') throw new Error('Invalid schema reference.')
|
|
190
|
+
let id = ids.get(child)
|
|
191
|
+
if (id == null) {
|
|
192
|
+
id = `s${ids.size}`
|
|
193
|
+
ids.set(child, id)
|
|
194
|
+
defs[id] = convert(pointer(doc, child))
|
|
195
|
+
}
|
|
196
|
+
result.$ref = `#/$defs/${id}`
|
|
197
|
+
} else if (['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas'].includes(key)) {
|
|
198
|
+
result[key] = Object.fromEntries(Object.entries(object(child)).map(([name, childSchema]) => [name, convert(childSchema)]))
|
|
199
|
+
} else if (['allOf', 'anyOf', 'oneOf', 'prefixItems'].includes(key)) {
|
|
200
|
+
if (!Array.isArray(child)) throw new Error('Invalid schema composition.')
|
|
201
|
+
result[key] = child.map(convert)
|
|
202
|
+
} else if (['items', 'additionalProperties', 'not', 'if', 'then', 'else', 'contains', 'propertyNames', 'unevaluatedProperties'].includes(key))
|
|
203
|
+
result[key] = convert(child)
|
|
204
|
+
else result[key] = child
|
|
205
|
+
}
|
|
206
|
+
if (source.nullable === true) return { anyOf: [result, { type: 'null' }] }
|
|
207
|
+
for (const key of ['minimum', 'maximum']) {
|
|
208
|
+
const exclusive = key == 'minimum' ? 'exclusiveMinimum' : 'exclusiveMaximum'
|
|
209
|
+
if (typeof result[exclusive] == 'boolean') {
|
|
210
|
+
if (result[exclusive] && typeof result[key] == 'number') {
|
|
211
|
+
result[exclusive] = result[key]!
|
|
212
|
+
delete result[key]
|
|
213
|
+
} else delete result[exclusive]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return result
|
|
217
|
+
}
|
|
218
|
+
let converted = convert(schemaValue)
|
|
219
|
+
if (typeof converted == 'object' && converted != null && typeof object(converted).$ref == 'string') {
|
|
220
|
+
const id = String(object(converted).$ref).slice('#/$defs/'.length)
|
|
221
|
+
const { $ref: _ref, ...siblings } = object(converted)
|
|
222
|
+
converted = { ...object(defs[id]), ...siblings }
|
|
223
|
+
}
|
|
224
|
+
if (typeof converted == 'boolean') return converted
|
|
225
|
+
return { $schema: 'https://json-schema.org/draft/2020-12/schema', ...object(converted), ...(Object.keys(defs).length ? { $defs: defs } : {}) }
|
|
226
|
+
}
|
|
227
|
+
function parameters(config: OpenApiExecutor): ObjectValue[] {
|
|
228
|
+
const doc = parseOpenApi(config.document)
|
|
229
|
+
const { item, op } = operation(doc, config.path, config.method)
|
|
230
|
+
const entries = new Map<string, ObjectValue>()
|
|
231
|
+
for (const value of [...((item.parameters as JsonValue[]) ?? []), ...((op.parameters as JsonValue[]) ?? [])]) {
|
|
232
|
+
const p = resolved(doc, value)
|
|
233
|
+
if (!['path', 'query', 'header'].includes(String(p.in)) || typeof p.name != 'string' || p.content != null)
|
|
234
|
+
throw new Error('Only schema-based path, query and header parameters are supported.')
|
|
235
|
+
if (p.allowReserved === true) throw new Error('allowReserved parameters are unsupported.')
|
|
236
|
+
const style = p.style ?? (p.in == 'query' ? 'form' : 'simple')
|
|
237
|
+
if (style != (p.in == 'query' ? 'form' : 'simple')) throw new Error('Only form query and simple path/header serialization are supported.')
|
|
238
|
+
entries.set(`${p.in}.${p.in == 'header' ? p.name.toLowerCase() : p.name}`, p)
|
|
239
|
+
}
|
|
240
|
+
return [...entries.values()]
|
|
241
|
+
}
|
|
242
|
+
function jsonContent(doc: ObjectValue, value: JsonValue): { schema: JsonValue; required: boolean; mediaType: string } {
|
|
243
|
+
const content = resolved(doc, value)
|
|
244
|
+
const media = object(content.content)
|
|
245
|
+
const mediaType = media['application/json'] != null ? 'application/json' : Object.keys(media).find((key) => /^application\/.+\+json$/.test(key))
|
|
246
|
+
const json = mediaType == null ? undefined : media[mediaType]
|
|
247
|
+
if (json == null) throw new Error('Only JSON request and response bodies are supported.')
|
|
248
|
+
return { schema: schema(doc, object(json).schema ?? {}), required: content.required === true, mediaType: mediaType! }
|
|
249
|
+
}
|
|
250
|
+
export function authHandles(auth: readonly OpenApiAuth[]): string[] {
|
|
251
|
+
return auth.flatMap((scheme) => (scheme.type == 'basic' ? [`auth.${scheme.id}.username`, `auth.${scheme.id}.password`] : [`auth.${scheme.id}.token`]))
|
|
252
|
+
}
|
|
253
|
+
export function openApiTask(config: OpenApiExecutor, name = 'OpenAPI'): ManagedTaskDefinition {
|
|
254
|
+
if (!config.path) return { name, executor: config, inputs: [], outputs: [] }
|
|
255
|
+
const doc = parseOpenApi(config.document)
|
|
256
|
+
const { op } = operation(doc, config.path, config.method)
|
|
257
|
+
const params = parameters(config)
|
|
258
|
+
const inputs: InputPort[] = params.map((p) => {
|
|
259
|
+
const jsonSchema = schema(doc, p.schema ?? {})
|
|
260
|
+
const defaultValue = typeof jsonSchema == 'boolean' ? undefined : object(jsonSchema).default
|
|
261
|
+
return {
|
|
262
|
+
handle: `${p.in}.${p.name}`,
|
|
263
|
+
jsonSchema,
|
|
264
|
+
nullable: (p.required !== true && p.in != 'path') || matchesSchema(null, jsonSchema),
|
|
265
|
+
...(typeof p.description == 'string' ? { description: p.description } : {}),
|
|
266
|
+
...(defaultValue === undefined ? {} : { value: defaultValue }),
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
if (op.requestBody != null) {
|
|
270
|
+
if (config.method == 'get' || config.method == 'head') throw new Error('GET and HEAD request bodies are unsupported.')
|
|
271
|
+
const body = jsonContent(doc, op.requestBody)
|
|
272
|
+
inputs.push({ handle: 'body', jsonSchema: body.schema, nullable: !body.required || matchesSchema(null, body.schema) })
|
|
273
|
+
}
|
|
274
|
+
const occupied = new Set(params.map((p) => `${p.in}.${p.in == 'header' ? String(p.name).toLowerCase() : p.name}`))
|
|
275
|
+
for (const auth of config.auth) {
|
|
276
|
+
const location = auth.type == 'apiKey' ? `${auth.in}.${auth.in == 'header' ? auth.name?.toLowerCase() : auth.name}` : 'header.authorization'
|
|
277
|
+
if (occupied.has(location)) throw new Error('Authentication conflicts with a parameter or another security scheme.')
|
|
278
|
+
if (auth.type == 'apiKey' && (!auth.name || !['header', 'query'].includes(auth.in ?? ''))) throw new Error('API Key requires a name and location.')
|
|
279
|
+
occupied.add(location)
|
|
280
|
+
}
|
|
281
|
+
inputs.push(...authHandles(config.auth).map((handle) => ({ handle, jsonSchema: { type: 'string' }, nullable: false })))
|
|
282
|
+
const responses = object(op.responses)
|
|
283
|
+
const schemas: JsonValue[] = []
|
|
284
|
+
for (const [status, response] of Object.entries(responses))
|
|
285
|
+
if (/^2\d\d$|^2XX$/.test(status) || status == 'default') {
|
|
286
|
+
const r = resolved(doc, response)
|
|
287
|
+
if (r.content == null) schemas.push({ type: 'null' })
|
|
288
|
+
else {
|
|
289
|
+
jsonContent(doc, r)
|
|
290
|
+
for (const [mediaType, media] of Object.entries(object(r.content)))
|
|
291
|
+
if (/^application\/(json|.+\+json)$/.test(mediaType)) schemas.push(schema(doc, object(media).schema ?? {}))
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (!schemas.length) throw new Error('A successful JSON response definition is required.')
|
|
295
|
+
// Each branch keeps its own reference scope when compiled separately at execution.
|
|
296
|
+
const bodySchema =
|
|
297
|
+
schemas.length == 1 ? schemas[0]! : { $schema: 'https://json-schema.org/draft/2020-12/schema', anyOf: schemas.map((s, index) => inlineSchema(s, index)) }
|
|
298
|
+
const outputs: Port[] = [
|
|
299
|
+
{ handle: 'body', jsonSchema: bodySchema, nullable: config.method == 'head' || schemas.some((item) => matchesSchema(null, item)) },
|
|
300
|
+
{ handle: 'statusCode', jsonSchema: { type: 'integer' }, nullable: false },
|
|
301
|
+
{ handle: 'headers', jsonSchema: { type: 'object', additionalProperties: { type: 'string' } }, nullable: false },
|
|
302
|
+
]
|
|
303
|
+
return { name, executor: config, inputs, outputs }
|
|
304
|
+
}
|
|
305
|
+
function inlineSchema(value: JsonValue, index: number): JsonValue {
|
|
306
|
+
if (Array.isArray(value)) return value.map((item) => inlineSchema(item, index))
|
|
307
|
+
if (value == null || typeof value != 'object') return value
|
|
308
|
+
return Object.fromEntries(
|
|
309
|
+
Object.entries(value).map(([key, child]) => [
|
|
310
|
+
key,
|
|
311
|
+
key == '$ref' && typeof child == 'string' && child.startsWith('#/') ? `#/anyOf/${index}/${child.slice(2)}` : inlineSchema(child, index),
|
|
312
|
+
]),
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
export interface OpenApiFieldIssue {
|
|
316
|
+
readonly field: 'sourceUrl' | 'operation' | 'serverUrl'
|
|
317
|
+
readonly message: string
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** The canvas validator and property panel share the same configuration contract. */
|
|
321
|
+
export function openApiFieldIssues(task: ManagedTaskDefinition): OpenApiFieldIssue[] {
|
|
322
|
+
if (task.executor.kind != 'openapi') return []
|
|
323
|
+
const config = task.executor
|
|
324
|
+
let field: OpenApiFieldIssue['field'] = 'sourceUrl'
|
|
325
|
+
try {
|
|
326
|
+
if (!config.sourceUrl.trim()) return [{ field, message: 'Enter an OpenAPI JSON document URL.' }]
|
|
327
|
+
httpUrl(config.sourceUrl)
|
|
328
|
+
field = 'operation'
|
|
329
|
+
if (!config.path) return [{ field, message: 'Select an OpenAPI operation.' }]
|
|
330
|
+
field = 'serverUrl'
|
|
331
|
+
httpUrl(config.serverUrl)
|
|
332
|
+
if (new URL(config.serverUrl).search) throw new Error('Server URLs must not contain a query string.')
|
|
333
|
+
field = 'operation'
|
|
334
|
+
if (!dequal(config.document, snapshot(config.document, config.path, config.method)))
|
|
335
|
+
return [{ field, message: 'Save only the selected operation and its dependencies.' }]
|
|
336
|
+
const expected = openApiTask(config, task.name)
|
|
337
|
+
if (!dequal(expected.inputs, task.inputs) || !dequal(expected.outputs, task.outputs))
|
|
338
|
+
return [{ field, message: 'OpenAPI ports must match the saved operation.' }]
|
|
339
|
+
return []
|
|
340
|
+
} catch (error) {
|
|
341
|
+
return [{ field, message: (error as Error).message }]
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function openApiIssues(task: ManagedTaskDefinition): string[] {
|
|
346
|
+
return openApiFieldIssues(task).map((issue) => issue.message)
|
|
347
|
+
}
|
|
348
|
+
export function selectOperation(value: JsonValue, sourceUrl: string, path: string, method: string): OpenApiExecutor {
|
|
349
|
+
const doc = snapshot(value, path, method)
|
|
350
|
+
const choices = securityOptions(doc, path, method)
|
|
351
|
+
const auth = choices.find((choice) => choice.auth != null)?.auth
|
|
352
|
+
if (auth == null) throw new Error(choices[0]?.error ?? 'Unsupported authentication.')
|
|
353
|
+
const config: OpenApiExecutor = {
|
|
354
|
+
kind: 'openapi',
|
|
355
|
+
sourceUrl: httpUrl(sourceUrl),
|
|
356
|
+
method,
|
|
357
|
+
path,
|
|
358
|
+
serverUrl: serverAddress(servers(doc, path, method)[0]!, sourceUrl),
|
|
359
|
+
document: doc,
|
|
360
|
+
auth,
|
|
361
|
+
}
|
|
362
|
+
openApiTask(config)
|
|
363
|
+
return config
|
|
364
|
+
}
|
|
365
|
+
function scalar(v: JsonValue): string {
|
|
366
|
+
if (v != null && typeof v == 'object') throw new Error('Nested parameter values are unsupported.')
|
|
367
|
+
return v == null ? '' : String(v)
|
|
368
|
+
}
|
|
369
|
+
export function buildRequest(config: OpenApiExecutor, input: Readonly<Record<string, JsonValue>>): { url: string; init: RequestInit } {
|
|
370
|
+
const task = openApiTask(config)
|
|
371
|
+
for (const port of task.inputs) {
|
|
372
|
+
if (!('handle' in port)) continue
|
|
373
|
+
const value = input[port.handle]
|
|
374
|
+
if (value === undefined) {
|
|
375
|
+
if (!port.nullable) throw new Error('A required input is missing.')
|
|
376
|
+
continue
|
|
377
|
+
}
|
|
378
|
+
if (!matchesSchema(value, port.jsonSchema)) throw new Error('A request input does not match its schema.')
|
|
379
|
+
}
|
|
380
|
+
let path = config.path
|
|
381
|
+
const query = new URLSearchParams()
|
|
382
|
+
const headers = new Headers({ accept: 'application/json' })
|
|
383
|
+
|
|
384
|
+
for (const p of parameters(config)) {
|
|
385
|
+
const value = input[`${p.in}.${p.name}`]
|
|
386
|
+
if (value === undefined) {
|
|
387
|
+
if (p.required === true || p.in == 'path') throw new Error('A required request parameter is missing.')
|
|
388
|
+
continue
|
|
389
|
+
}
|
|
390
|
+
const explode = p.explode ?? p.in == 'query'
|
|
391
|
+
const parts = Array.isArray(value)
|
|
392
|
+
? value.map(scalar)
|
|
393
|
+
: value != null && typeof value == 'object'
|
|
394
|
+
? Object.entries(value).flatMap(([k, v]) => (explode ? [`${k}=${scalar(v)}`] : [k, scalar(v)]))
|
|
395
|
+
: [scalar(value)]
|
|
396
|
+
if (p.in == 'path') {
|
|
397
|
+
const encoded =
|
|
398
|
+
value != null && typeof value == 'object' && !Array.isArray(value) && explode
|
|
399
|
+
? Object.entries(value)
|
|
400
|
+
.map(([key, entry]) => `${encodeURIComponent(key)}=${encodeURIComponent(scalar(entry))}`)
|
|
401
|
+
.join(',')
|
|
402
|
+
: parts.map(encodeURIComponent).join(',')
|
|
403
|
+
path = path.replaceAll(`{${p.name}}`, encoded)
|
|
404
|
+
} else if (p.in == 'header') headers.set(String(p.name), parts.join(','))
|
|
405
|
+
else if (Array.isArray(value) && explode) for (const part of parts) query.append(String(p.name), part)
|
|
406
|
+
else if (value != null && typeof value == 'object' && !Array.isArray(value) && explode)
|
|
407
|
+
for (const [k, v] of Object.entries(value)) query.append(k, scalar(v))
|
|
408
|
+
else query.append(String(p.name), parts.join(','))
|
|
409
|
+
}
|
|
410
|
+
if (/\{[^}]+\}/.test(path)) throw new Error('A path parameter is missing.')
|
|
411
|
+
const url = new URL(httpUrl(config.serverUrl).replace(/\/$/, '') + '/' + path.replace(/^\//, ''))
|
|
412
|
+
for (const [key, value] of query) url.searchParams.append(key, value)
|
|
413
|
+
for (const auth of config.auth) {
|
|
414
|
+
const token = (part: string): string => {
|
|
415
|
+
const value = input[`auth.${auth.id}.${part}`]
|
|
416
|
+
if (typeof value != 'string' || !value) throw new Error('Authentication source is missing.')
|
|
417
|
+
return value
|
|
418
|
+
}
|
|
419
|
+
if (auth.type == 'bearer') headers.set('authorization', `Bearer ${token('token')}`)
|
|
420
|
+
else if (auth.type == 'basic') {
|
|
421
|
+
const username = token('username')
|
|
422
|
+
if (username.includes(':')) throw new Error('Basic authentication username cannot contain a colon.')
|
|
423
|
+
const bytes = new TextEncoder().encode(`${username}:${token('password')}`)
|
|
424
|
+
headers.set('authorization', `Basic ${btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join(''))}`)
|
|
425
|
+
} else if (auth.in == 'header') headers.set(auth.name!, token('token'))
|
|
426
|
+
else {
|
|
427
|
+
if (url.searchParams.has(auth.name!)) throw new Error('Authentication conflicts with a query parameter.')
|
|
428
|
+
url.searchParams.set(auth.name!, token('token'))
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const { op } = operation(parseOpenApi(config.document), config.path, config.method)
|
|
432
|
+
let body: string | undefined
|
|
433
|
+
if (op.requestBody != null && input.body !== undefined) {
|
|
434
|
+
body = JSON.stringify(input.body)
|
|
435
|
+
headers.set('content-type', jsonContent(parseOpenApi(config.document), op.requestBody).mediaType)
|
|
436
|
+
} else if (op.requestBody != null && jsonContent(parseOpenApi(config.document), op.requestBody).required) throw new Error('The request body is required.')
|
|
437
|
+
return { url: url.href, init: { method: config.method.toUpperCase(), headers, body, redirect: 'manual' } }
|
|
438
|
+
}
|
|
439
|
+
export function parseResponse(config: OpenApiExecutor, status: number, headers: Headers, text: string): Record<string, JsonValue> {
|
|
440
|
+
if (status < 200 || status >= 300) throw new Error(`API request failed with HTTP ${status}.`)
|
|
441
|
+
const doc = parseOpenApi(config.document)
|
|
442
|
+
const responses = object(operation(doc, config.path, config.method).op.responses)
|
|
443
|
+
const raw = responses[String(status)] ?? responses['2XX'] ?? responses.default
|
|
444
|
+
if (raw == null) throw new Error('The response status is not declared in the saved definition.')
|
|
445
|
+
const response = resolved(doc, raw)
|
|
446
|
+
let body: JsonValue = null
|
|
447
|
+
if (text && config.method != 'head' && status != 204 && status != 205) {
|
|
448
|
+
const type = (headers.get('content-type') ?? '').split(';')[0]!.trim().toLowerCase()
|
|
449
|
+
const media = object(response.content)[type]
|
|
450
|
+
if (media == null || !/^application\/(json|.+\+json)$/.test(type)) throw new Error('The response media type is not supported by the saved definition.')
|
|
451
|
+
try {
|
|
452
|
+
body = JSON.parse(text) as JsonValue
|
|
453
|
+
} catch {
|
|
454
|
+
throw new Error('The API returned invalid JSON.')
|
|
455
|
+
}
|
|
456
|
+
if (!matchesSchema(body, schema(doc, object(media).schema ?? {}))) throw new Error('The API response does not match the saved schema.')
|
|
457
|
+
} else if (response.content != null && config.method != 'head' && status != 204 && status != 205) throw new Error('The API returned an empty JSON response.')
|
|
458
|
+
return { body, statusCode: status, headers: Object.fromEntries([...headers].filter(([name]) => name != 'set-cookie')) }
|
|
459
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Group, InputPort, InputValues, JsonValue } from '../../flow/common/change.ts'
|
|
2
|
+
|
|
3
|
+
import { inputValue } from '../../flow/common/inputValue.ts'
|
|
4
|
+
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
5
|
+
|
|
6
|
+
/** Deployment-bound selections owned by the Trigger configuration contract. */
|
|
7
|
+
export function clearTriggerExternalConfig(key: string, config: InputValues): InputValues {
|
|
8
|
+
const fields =
|
|
9
|
+
key == 'jira.on_issue_event' ||
|
|
10
|
+
key == 'intercom.on_conversation_event' ||
|
|
11
|
+
key == 'sentry.on_issue_alert' ||
|
|
12
|
+
key == 'zoom.on_recording_ready' ||
|
|
13
|
+
key == 'fireflies.on_meeting_ready'
|
|
14
|
+
? ['sourceId', 'eventTypes']
|
|
15
|
+
: key == 'slackbot.on_event'
|
|
16
|
+
? ['sourceId', 'eventTypes', 'channelIds']
|
|
17
|
+
: key == 'feishu_app_bot.on_event'
|
|
18
|
+
? ['sourceId', 'eventTypes', 'resource', 'chatIds']
|
|
19
|
+
: key == 'linear.on_issue_changed'
|
|
20
|
+
? ['teamId', 'stateIds']
|
|
21
|
+
: []
|
|
22
|
+
return { ...config, ...Object.fromEntries(fields.map((field) => [field, { kind: 'unset' as const }])) }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function triggerConfigValue(input: InputPort, config: InputValues): JsonValue | undefined {
|
|
26
|
+
return inputValue(config[input.handle], input.value)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Provider-specific controls consume values, never persistence wrappers. */
|
|
30
|
+
export function triggerConfigValues(inputs: readonly (InputPort | Group)[], config: InputValues): Readonly<Record<string, JsonValue>> {
|
|
31
|
+
return Object.fromEntries(inputs.flatMap((input) => ('handle' in input ? [[input.handle, triggerConfigValue(input, config) ?? null]] : [])))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function missingTriggerConfig(inputs: readonly (InputPort | Group)[], config: InputValues): readonly string[] {
|
|
35
|
+
return inputs.flatMap((input) => ('handle' in input && !input.nullable && triggerConfigValue(input, config) === undefined ? [input.handle] : []))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Resolves fixed inputs before a Provider sees them, using the same defaults/null semantics as node inputs. */
|
|
39
|
+
export function resolveTriggerConfig(inputs: readonly (InputPort | Group)[], config: InputValues): Readonly<Record<string, JsonValue>> {
|
|
40
|
+
const fields = inputs.filter((input): input is InputPort => 'handle' in input)
|
|
41
|
+
const handles = new Set(fields.map((input) => input.handle))
|
|
42
|
+
if (handles.size !== fields.length) throw new TypeError('Trigger config has duplicate input handles.')
|
|
43
|
+
const unknown = Object.keys(config).find((handle) => !handles.has(handle))
|
|
44
|
+
if (unknown != null) throw new TypeError(`Unknown Trigger config input "${unknown}".`)
|
|
45
|
+
const resolved = Object.fromEntries(
|
|
46
|
+
fields.map((input) => {
|
|
47
|
+
const value = triggerConfigValue(input, config) ?? null
|
|
48
|
+
if (!(value === null && input.nullable) && !matchesSchema(value, input.jsonSchema)) {
|
|
49
|
+
throw new TypeError(`Trigger config input "${input.handle}" does not match its declared schema.`)
|
|
50
|
+
}
|
|
51
|
+
return [input.handle, value]
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
if (new TextEncoder().encode(JSON.stringify(resolved)).byteLength > 64 * 1024) throw new TypeError('Trigger config exceeds the 65536-byte limit.')
|
|
55
|
+
return resolved
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ConnectorProxy } from '../../connector/common/proxy.ts'
|
|
2
|
+
import type { JsonValue } from '../../flow/common/change.ts'
|
|
3
|
+
|
|
4
|
+
export interface TriggerConfigOption {
|
|
5
|
+
readonly value: string
|
|
6
|
+
readonly label: string
|
|
7
|
+
readonly color?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TriggerConfigOptionsContext {
|
|
11
|
+
readonly field: string
|
|
12
|
+
readonly config: Readonly<Record<string, JsonValue>>
|
|
13
|
+
readonly connector: ConnectorProxy
|
|
14
|
+
readonly signal?: AbortSignal
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const configInputsSchema = z.array(
|
|
4
|
+
z.union([
|
|
5
|
+
z.strictObject({ handle: z.string().min(1), description: z.string().optional(), jsonSchema: z.json(), nullable: z.boolean(), value: z.json().optional() }),
|
|
6
|
+
z.strictObject({ group: z.string(), collapsed: z.boolean().optional() }),
|
|
7
|
+
]),
|
|
8
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { JsonValue, Port, PortDefinition, TriggerNode } from '../../flow/common/change.ts'
|
|
2
|
+
|
|
3
|
+
import { isJsonObject } from '../../base/common/json.ts'
|
|
4
|
+
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
5
|
+
import { webhookMethods, webhookSupportsBody } from '../../flow/common/webhookMethod.ts'
|
|
6
|
+
|
|
7
|
+
export { webhookMethods, webhookSupportsBody }
|
|
8
|
+
|
|
9
|
+
export const errorOutputs: readonly Port[] = [
|
|
10
|
+
{
|
|
11
|
+
handle: 'workflow',
|
|
12
|
+
nullable: false,
|
|
13
|
+
jsonSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
required: ['flowId', 'name', 'revisionId', 'publicationId'],
|
|
17
|
+
properties: { flowId: { type: 'string' }, name: { type: 'string' }, revisionId: { type: 'string' }, publicationId: { type: ['string', 'null'] } },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
handle: 'execution',
|
|
22
|
+
nullable: false,
|
|
23
|
+
jsonSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
required: ['runId', 'status', 'startedAt', 'finishedAt'],
|
|
27
|
+
properties: {
|
|
28
|
+
runId: { type: 'string' },
|
|
29
|
+
status: { enum: ['failed', 'indeterminate'] },
|
|
30
|
+
startedAt: { type: ['string', 'null'] },
|
|
31
|
+
finishedAt: { type: 'string' },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
handle: 'error',
|
|
37
|
+
nullable: false,
|
|
38
|
+
jsonSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
additionalProperties: false,
|
|
41
|
+
required: ['code', 'message'],
|
|
42
|
+
properties: {
|
|
43
|
+
code: { type: 'string' },
|
|
44
|
+
message: { type: 'string' },
|
|
45
|
+
nodeId: { type: 'string' },
|
|
46
|
+
jobId: { type: 'string' },
|
|
47
|
+
path: { type: 'array', items: { type: 'string' } },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
export const sampleErrorOutputs: Readonly<Record<string, JsonValue>> = {
|
|
54
|
+
workflow: { flowId: 'example-flow', name: 'Example workflow', revisionId: 'example-revision', publicationId: 'example-publication' },
|
|
55
|
+
execution: { runId: 'example-run', status: 'failed', startedAt: '2026-01-01T00:00:00.000Z', finishedAt: '2026-01-01T00:00:01.000Z' },
|
|
56
|
+
error: { code: 'node.failed', message: 'Example failure', nodeId: 'example-node', jobId: 'example-job', path: [] },
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const webhookOutputs: readonly Port[] = [
|
|
60
|
+
{ handle: 'headers', jsonSchema: { type: 'object', additionalProperties: { type: 'string' } }, nullable: false },
|
|
61
|
+
{ handle: 'query', jsonSchema: { type: 'object', additionalProperties: { type: ['string', 'array'], items: { type: 'string' } } }, nullable: false },
|
|
62
|
+
{ handle: 'body', jsonSchema: { type: 'object', additionalProperties: true }, nullable: false },
|
|
63
|
+
{ handle: 'webhookUrl', jsonSchema: { type: 'string' }, nullable: false },
|
|
64
|
+
]
|
|
65
|
+
const cronOutputs: readonly Port[] = [
|
|
66
|
+
{
|
|
67
|
+
handle: 'scheduledAt',
|
|
68
|
+
jsonSchema: { type: 'string', format: 'date-time' },
|
|
69
|
+
nullable: false,
|
|
70
|
+
},
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
export function triggerOutputDefinitions(trigger: TriggerNode): readonly Port[] {
|
|
74
|
+
switch (trigger.kind) {
|
|
75
|
+
case 'error':
|
|
76
|
+
return errorOutputs
|
|
77
|
+
case 'manual':
|
|
78
|
+
return []
|
|
79
|
+
case 'start':
|
|
80
|
+
return trigger.inputDefs
|
|
81
|
+
case 'cron':
|
|
82
|
+
return cronOutputs
|
|
83
|
+
case 'poll':
|
|
84
|
+
case 'integration':
|
|
85
|
+
return trigger.definition.outputs
|
|
86
|
+
case 'webhook':
|
|
87
|
+
return webhookOutputs
|
|
88
|
+
.filter((port) => port.handle !== 'body' || webhookSupportsBody(trigger.method))
|
|
89
|
+
.map((port) =>
|
|
90
|
+
port.handle === 'body'
|
|
91
|
+
? Object.assign({}, port, {
|
|
92
|
+
jsonSchema: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
additionalProperties: false,
|
|
95
|
+
properties: Object.fromEntries(trigger.bodyFields.map((field) => [field.handle, field.jsonSchema])),
|
|
96
|
+
required: trigger.bodyFields.filter((field) => !field.nullable && !Object.hasOwn(field, 'value')).map((field) => field.handle),
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
: port,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function triggerOutputPorts(trigger: TriggerNode): Readonly<Record<string, PortDefinition>> {
|
|
105
|
+
return Object.fromEntries(triggerOutputDefinitions(trigger).map(({ handle, ...port }) => [handle, port]))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function matchesTriggerOutputs(trigger: TriggerNode, outputs: unknown): outputs is Readonly<Record<string, JsonValue>> {
|
|
109
|
+
if (!isJsonObject(outputs)) return false
|
|
110
|
+
const ports = triggerOutputDefinitions(trigger)
|
|
111
|
+
return (
|
|
112
|
+
new Set(ports.map((port) => port.handle)).size === ports.length &&
|
|
113
|
+
Object.keys(outputs).length === ports.length &&
|
|
114
|
+
ports.every(
|
|
115
|
+
(port) =>
|
|
116
|
+
Object.hasOwn(outputs, port.handle) && ((outputs[port.handle] === null && port.nullable) || matchesSchema(outputs[port.handle]!, port.jsonSchema)),
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Resolve caller-supplied Start parameters into the complete output snapshot used by execution. */
|
|
122
|
+
export function normalizeTriggerOutputs(trigger: TriggerNode, outputs: unknown): Readonly<Record<string, JsonValue>> | undefined {
|
|
123
|
+
if (!isJsonObject(outputs)) return
|
|
124
|
+
const normalized =
|
|
125
|
+
trigger.kind == 'start'
|
|
126
|
+
? {
|
|
127
|
+
...outputs,
|
|
128
|
+
...Object.fromEntries(trigger.inputDefs.filter((port) => port.nullable && !Object.hasOwn(outputs, port.handle)).map((port) => [port.handle, null])),
|
|
129
|
+
}
|
|
130
|
+
: outputs
|
|
131
|
+
return matchesTriggerOutputs(trigger, normalized) ? normalized : undefined
|
|
132
|
+
}
|