@shipfox/api-definitions 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +215 -0
- package/LICENSE +21 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +35 -0
- package/dist/config.js.map +1 -0
- package/dist/core/entities/index.d.ts +5 -0
- package/dist/core/entities/index.d.ts.map +1 -0
- package/dist/core/entities/index.js +6 -0
- package/dist/core/entities/index.js.map +1 -0
- package/dist/core/entities/integration-context.d.ts +11 -0
- package/dist/core/entities/integration-context.d.ts.map +1 -0
- package/dist/core/entities/integration-context.js +3 -0
- package/dist/core/entities/integration-context.js.map +1 -0
- package/dist/core/entities/sync-state.d.ts +19 -0
- package/dist/core/entities/sync-state.d.ts.map +1 -0
- package/dist/core/entities/sync-state.js +20 -0
- package/dist/core/entities/sync-state.js.map +1 -0
- package/dist/core/entities/workflow-definition.d.ts +35 -0
- package/dist/core/entities/workflow-definition.d.ts.map +1 -0
- package/dist/core/entities/workflow-definition.js +3 -0
- package/dist/core/entities/workflow-definition.js.map +1 -0
- package/dist/core/entities/workflow-model.d.ts +138 -0
- package/dist/core/entities/workflow-model.d.ts.map +1 -0
- package/dist/core/entities/workflow-model.js +3 -0
- package/dist/core/entities/workflow-model.js.map +1 -0
- package/dist/core/errors.d.ts +10 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +14 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/has-agent-step-integrations.d.ts +3 -0
- package/dist/core/has-agent-step-integrations.d.ts.map +1 -0
- package/dist/core/has-agent-step-integrations.js +5 -0
- package/dist/core/has-agent-step-integrations.js.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +9 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parse-definition.d.ts +9 -0
- package/dist/core/parse-definition.d.ts.map +1 -0
- package/dist/core/parse-definition.js +17 -0
- package/dist/core/parse-definition.js.map +1 -0
- package/dist/core/sync-definitions.d.ts +44 -0
- package/dist/core/sync-definitions.d.ts.map +1 -0
- package/dist/core/sync-definitions.js +140 -0
- package/dist/core/sync-definitions.js.map +1 -0
- package/dist/core/validate-definition.d.ts +20 -0
- package/dist/core/validate-definition.d.ts.map +1 -0
- package/dist/core/validate-definition.js +61 -0
- package/dist/core/validate-definition.js.map +1 -0
- package/dist/core/workflow-model/constants.d.ts +2 -0
- package/dist/core/workflow-model/constants.d.ts.map +1 -0
- package/dist/core/workflow-model/constants.js +3 -0
- package/dist/core/workflow-model/constants.js.map +1 -0
- package/dist/core/workflow-model/cron-trigger.d.ts +16 -0
- package/dist/core/workflow-model/cron-trigger.d.ts.map +1 -0
- package/dist/core/workflow-model/cron-trigger.js +85 -0
- package/dist/core/workflow-model/cron-trigger.js.map +1 -0
- package/dist/core/workflow-model/index.d.ts +4 -0
- package/dist/core/workflow-model/index.d.ts.map +1 -0
- package/dist/core/workflow-model/index.js +5 -0
- package/dist/core/workflow-model/index.js.map +1 -0
- package/dist/core/workflow-model/invalid-workflow-model-error.d.ts +15 -0
- package/dist/core/workflow-model/invalid-workflow-model-error.d.ts.map +1 -0
- package/dist/core/workflow-model/invalid-workflow-model-error.js +9 -0
- package/dist/core/workflow-model/invalid-workflow-model-error.js.map +1 -0
- package/dist/core/workflow-model/map-job-ids.d.ts +4 -0
- package/dist/core/workflow-model/map-job-ids.d.ts.map +1 -0
- package/dist/core/workflow-model/map-job-ids.js +33 -0
- package/dist/core/workflow-model/map-job-ids.js.map +1 -0
- package/dist/core/workflow-model/normalize-agent-integrations.d.ts +12 -0
- package/dist/core/workflow-model/normalize-agent-integrations.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-agent-integrations.js +157 -0
- package/dist/core/workflow-model/normalize-agent-integrations.js.map +1 -0
- package/dist/core/workflow-model/normalize-dependencies.d.ts +6 -0
- package/dist/core/workflow-model/normalize-dependencies.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-dependencies.js +119 -0
- package/dist/core/workflow-model/normalize-dependencies.js.map +1 -0
- package/dist/core/workflow-model/normalize-env.d.ts +17 -0
- package/dist/core/workflow-model/normalize-env.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-env.js +40 -0
- package/dist/core/workflow-model/normalize-env.js.map +1 -0
- package/dist/core/workflow-model/normalize-if-condition.d.ts +14 -0
- package/dist/core/workflow-model/normalize-if-condition.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-if-condition.js +75 -0
- package/dist/core/workflow-model/normalize-if-condition.js.map +1 -0
- package/dist/core/workflow-model/normalize-job-checkout.d.ts +5 -0
- package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-job-checkout.js +16 -0
- package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -0
- package/dist/core/workflow-model/normalize-job-listening.d.ts +10 -0
- package/dist/core/workflow-model/normalize-job-listening.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-job-listening.js +125 -0
- package/dist/core/workflow-model/normalize-job-listening.js.map +1 -0
- package/dist/core/workflow-model/normalize-job-success.d.ts +11 -0
- package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-job-success.js +23 -0
- package/dist/core/workflow-model/normalize-job-success.js.map +1 -0
- package/dist/core/workflow-model/normalize-jobs.d.ts +12 -0
- package/dist/core/workflow-model/normalize-jobs.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-jobs.js +935 -0
- package/dist/core/workflow-model/normalize-jobs.js.map +1 -0
- package/dist/core/workflow-model/normalize-needs.d.ts +2 -0
- package/dist/core/workflow-model/normalize-needs.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-needs.js +16 -0
- package/dist/core/workflow-model/normalize-needs.js.map +1 -0
- package/dist/core/workflow-model/normalize-step-gate.d.ts +15 -0
- package/dist/core/workflow-model/normalize-step-gate.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-step-gate.js +89 -0
- package/dist/core/workflow-model/normalize-step-gate.js.map +1 -0
- package/dist/core/workflow-model/normalize-step-outputs.d.ts +10 -0
- package/dist/core/workflow-model/normalize-step-outputs.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-step-outputs.js +32 -0
- package/dist/core/workflow-model/normalize-step-outputs.js.map +1 -0
- package/dist/core/workflow-model/normalize-triggers.d.ts +11 -0
- package/dist/core/workflow-model/normalize-triggers.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-triggers.js +128 -0
- package/dist/core/workflow-model/normalize-triggers.js.map +1 -0
- package/dist/core/workflow-model/normalize-workflow-document.d.ts +11 -0
- package/dist/core/workflow-model/normalize-workflow-document.d.ts.map +1 -0
- package/dist/core/workflow-model/normalize-workflow-document.js +43 -0
- package/dist/core/workflow-model/normalize-workflow-document.js.map +1 -0
- package/dist/core/workflow-model/parse-duration-ms.d.ts +11 -0
- package/dist/core/workflow-model/parse-duration-ms.d.ts.map +1 -0
- package/dist/core/workflow-model/parse-duration-ms.js +47 -0
- package/dist/core/workflow-model/parse-duration-ms.js.map +1 -0
- package/dist/core/workflow-model/parse-interpolation-field.d.ts +14 -0
- package/dist/core/workflow-model/parse-interpolation-field.d.ts.map +1 -0
- package/dist/core/workflow-model/parse-interpolation-field.js +377 -0
- package/dist/core/workflow-model/parse-interpolation-field.js.map +1 -0
- package/dist/core/workflow-model/stable-id.d.ts +2 -0
- package/dist/core/workflow-model/stable-id.d.ts.map +1 -0
- package/dist/core/workflow-model/stable-id.js +8 -0
- package/dist/core/workflow-model/stable-id.js.map +1 -0
- package/dist/core/workflow-model/validate-job-references.d.ts +10 -0
- package/dist/core/workflow-model/validate-job-references.d.ts.map +1 -0
- package/dist/core/workflow-model/validate-job-references.js +42 -0
- package/dist/core/workflow-model/validate-job-references.js.map +1 -0
- package/dist/core/workflow-model/validate-predicate-expression.d.ts +14 -0
- package/dist/core/workflow-model/validate-predicate-expression.d.ts.map +1 -0
- package/dist/core/workflow-model/validate-predicate-expression.js +273 -0
- package/dist/core/workflow-model/validate-predicate-expression.js.map +1 -0
- package/dist/core/workflow-model/validation-issue.d.ts +8 -0
- package/dist/core/workflow-model/validation-issue.d.ts.map +1 -0
- package/dist/core/workflow-model/validation-issue.js +17 -0
- package/dist/core/workflow-model/validation-issue.js.map +1 -0
- package/dist/core/workflow-model/workflow-field-label.d.ts +3 -0
- package/dist/core/workflow-model/workflow-field-label.d.ts.map +1 -0
- package/dist/core/workflow-model/workflow-field-label.js +47 -0
- package/dist/core/workflow-model/workflow-field-label.js.map +1 -0
- package/dist/core/workflow-yaml/index.d.ts +3 -0
- package/dist/core/workflow-yaml/index.d.ts.map +1 -0
- package/dist/core/workflow-yaml/index.js +4 -0
- package/dist/core/workflow-yaml/index.js.map +1 -0
- package/dist/core/workflow-yaml/invalid-workflow-yaml-error.d.ts +16 -0
- package/dist/core/workflow-yaml/invalid-workflow-yaml-error.d.ts.map +1 -0
- package/dist/core/workflow-yaml/invalid-workflow-yaml-error.js +12 -0
- package/dist/core/workflow-yaml/invalid-workflow-yaml-error.js.map +1 -0
- package/dist/core/workflow-yaml/parse-workflow-yaml.d.ts +9 -0
- package/dist/core/workflow-yaml/parse-workflow-yaml.d.ts.map +1 -0
- package/dist/core/workflow-yaml/parse-workflow-yaml.js +52 -0
- package/dist/core/workflow-yaml/parse-workflow-yaml.js.map +1 -0
- package/dist/core/workflow-yaml/source-locations.d.ts +3 -0
- package/dist/core/workflow-yaml/source-locations.d.ts.map +1 -0
- package/dist/core/workflow-yaml/source-locations.js +48 -0
- package/dist/core/workflow-yaml/source-locations.js.map +1 -0
- package/dist/db/db.d.ts +1277 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +22 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/definition-triggers.d.ts +4 -0
- package/dist/db/definition-triggers.d.ts.map +1 -0
- package/dist/db/definition-triggers.js +17 -0
- package/dist/db/definition-triggers.js.map +1 -0
- package/dist/db/definitions.d.ts +63 -0
- package/dist/db/definitions.d.ts.map +1 -0
- package/dist/db/definitions.js +208 -0
- package/dist/db/definitions.js.map +1 -0
- package/dist/db/index.d.ts +8 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/definitions.d.ts +237 -0
- package/dist/db/schema/definitions.d.ts.map +1 -0
- package/dist/db/schema/definitions.js +63 -0
- package/dist/db/schema/definitions.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/sync-states.d.ts +218 -0
- package/dist/db/schema/sync-states.d.ts.map +1 -0
- package/dist/db/schema/sync-states.js +68 -0
- package/dist/db/schema/sync-states.js.map +1 -0
- package/dist/db/sync-states.d.ts +21 -0
- package/dist/db/sync-states.d.ts.map +1 -0
- package/dist/db/sync-states.js +47 -0
- package/dist/db/sync-states.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/presentation/dto/definition.d.ts +6 -0
- package/dist/presentation/dto/definition.d.ts.map +1 -0
- package/dist/presentation/dto/definition.js +35 -0
- package/dist/presentation/dto/definition.js.map +1 -0
- package/dist/presentation/dto/index.d.ts +2 -0
- package/dist/presentation/dto/index.d.ts.map +1 -0
- package/dist/presentation/dto/index.js +3 -0
- package/dist/presentation/dto/index.js.map +1 -0
- package/dist/presentation/index.d.ts +2 -0
- package/dist/presentation/index.d.ts.map +1 -0
- package/dist/presentation/index.js +3 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/presentation/routes/create-definition.d.ts +9 -0
- package/dist/presentation/routes/create-definition.d.ts.map +1 -0
- package/dist/presentation/routes/create-definition.js +76 -0
- package/dist/presentation/routes/create-definition.js.map +1 -0
- package/dist/presentation/routes/get-definition.d.ts +2 -0
- package/dist/presentation/routes/get-definition.d.ts.map +1 -0
- package/dist/presentation/routes/get-definition.js +43 -0
- package/dist/presentation/routes/get-definition.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +7 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +22 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/list-definitions.d.ts +2 -0
- package/dist/presentation/routes/list-definitions.d.ts.map +1 -0
- package/dist/presentation/routes/list-definitions.js +56 -0
- package/dist/presentation/routes/list-definitions.js.map +1 -0
- package/dist/presentation/routes/validate-definition.d.ts +2 -0
- package/dist/presentation/routes/validate-definition.d.ts.map +1 -0
- package/dist/presentation/routes/validate-definition.js +43 -0
- package/dist/presentation/routes/validate-definition.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +3 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -0
- package/dist/presentation/subscribers/index.js +4 -0
- package/dist/presentation/subscribers/index.js.map +1 -0
- package/dist/presentation/subscribers/on-project-source-bound.d.ts +3 -0
- package/dist/presentation/subscribers/on-project-source-bound.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-project-source-bound.js +11 -0
- package/dist/presentation/subscribers/on-project-source-bound.js.map +1 -0
- package/dist/presentation/subscribers/on-project-source-commit-observed.d.ts +3 -0
- package/dist/presentation/subscribers/on-project-source-commit-observed.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-project-source-commit-observed.js +13 -0
- package/dist/presentation/subscribers/on-project-source-commit-observed.js.map +1 -0
- package/dist/presentation/subscribers/start-definition-sync.d.ts +10 -0
- package/dist/presentation/subscribers/start-definition-sync.d.ts.map +1 -0
- package/dist/presentation/subscribers/start-definition-sync.js +26 -0
- package/dist/presentation/subscribers/start-definition-sync.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +3 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +3 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/sync-activities.d.ts +47 -0
- package/dist/temporal/activities/sync-activities.d.ts.map +1 -0
- package/dist/temporal/activities/sync-activities.js +128 -0
- package/dist/temporal/activities/sync-activities.js.map +1 -0
- package/dist/temporal/constants.d.ts +3 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +4 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/index.d.ts +3 -0
- package/dist/temporal/index.d.ts.map +1 -0
- package/dist/temporal/index.js +4 -0
- package/dist/temporal/index.js.map +1 -0
- package/dist/temporal/workflows/definition-sync.d.ts +20 -0
- package/dist/temporal/workflows/definition-sync.d.ts.map +1 -0
- package/dist/temporal/workflows/definition-sync.js +95 -0
- package/dist/temporal/workflows/definition-sync.js.map +1 -0
- package/dist/temporal/workflows/index.d.ts +2 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +3 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +64 -0
- package/drizzle/0001_definition_list_pagination.sql +1 -0
- package/drizzle/meta/0000_snapshot.json +527 -0
- package/drizzle/meta/_journal.json +20 -0
- package/drizzle.config.ts +11 -0
- package/package.json +89 -0
- package/src/config.test.ts +23 -0
- package/src/config.ts +50 -0
- package/src/core/entities/index.ts +4 -0
- package/src/core/entities/integration-context.ts +21 -0
- package/src/core/entities/sync-state.ts +40 -0
- package/src/core/entities/workflow-definition.ts +38 -0
- package/src/core/entities/workflow-model.ts +162 -0
- package/src/core/errors.ts +21 -0
- package/src/core/has-agent-step-integrations.ts +7 -0
- package/src/core/index.ts +23 -0
- package/src/core/parse-definition.test.ts +200 -0
- package/src/core/parse-definition.ts +28 -0
- package/src/core/sync-definitions.test.ts +390 -0
- package/src/core/sync-definitions.ts +216 -0
- package/src/core/validate-definition.test.ts +152 -0
- package/src/core/validate-definition.ts +72 -0
- package/src/core/workflow-model/README.md +129 -0
- package/src/core/workflow-model/constants.ts +1 -0
- package/src/core/workflow-model/cron-trigger.test.ts +29 -0
- package/src/core/workflow-model/cron-trigger.ts +83 -0
- package/src/core/workflow-model/index.ts +9 -0
- package/src/core/workflow-model/invalid-workflow-model-error.ts +71 -0
- package/src/core/workflow-model/map-job-ids.ts +33 -0
- package/src/core/workflow-model/normalize-agent-integrations.ts +208 -0
- package/src/core/workflow-model/normalize-dependencies.ts +139 -0
- package/src/core/workflow-model/normalize-env.ts +48 -0
- package/src/core/workflow-model/normalize-if-condition.ts +126 -0
- package/src/core/workflow-model/normalize-job-checkout.ts +19 -0
- package/src/core/workflow-model/normalize-job-listening.ts +121 -0
- package/src/core/workflow-model/normalize-job-success.ts +28 -0
- package/src/core/workflow-model/normalize-jobs.ts +1010 -0
- package/src/core/workflow-model/normalize-needs.ts +12 -0
- package/src/core/workflow-model/normalize-step-gate.ts +90 -0
- package/src/core/workflow-model/normalize-step-outputs.ts +36 -0
- package/src/core/workflow-model/normalize-triggers.ts +130 -0
- package/src/core/workflow-model/normalize-workflow-document.test.ts +4463 -0
- package/src/core/workflow-model/normalize-workflow-document.ts +63 -0
- package/src/core/workflow-model/parse-duration-ms.ts +58 -0
- package/src/core/workflow-model/parse-interpolation-field.ts +566 -0
- package/src/core/workflow-model/stable-id.ts +12 -0
- package/src/core/workflow-model/validate-job-references.ts +54 -0
- package/src/core/workflow-model/validate-predicate-expression.test.ts +161 -0
- package/src/core/workflow-model/validate-predicate-expression.ts +406 -0
- package/src/core/workflow-model/validation-issue.ts +27 -0
- package/src/core/workflow-model/workflow-field-label.ts +50 -0
- package/src/core/workflow-yaml/README.md +77 -0
- package/src/core/workflow-yaml/index.ts +11 -0
- package/src/core/workflow-yaml/invalid-workflow-yaml-error.ts +23 -0
- package/src/core/workflow-yaml/parse-workflow-yaml.test.ts +71 -0
- package/src/core/workflow-yaml/parse-workflow-yaml.ts +74 -0
- package/src/core/workflow-yaml/source-locations.ts +62 -0
- package/src/db/db.ts +18 -0
- package/src/db/definition-triggers.test.ts +95 -0
- package/src/db/definition-triggers.ts +17 -0
- package/src/db/definitions.test.ts +1309 -0
- package/src/db/definitions.ts +357 -0
- package/src/db/index.ts +31 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/definitions.test.ts +36 -0
- package/src/db/schema/definitions.ts +76 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/sync-states.ts +79 -0
- package/src/db/sync-states.test.ts +90 -0
- package/src/db/sync-states.ts +86 -0
- package/src/index.ts +104 -0
- package/src/presentation/dto/definition.test.ts +59 -0
- package/src/presentation/dto/definition.ts +41 -0
- package/src/presentation/dto/index.ts +1 -0
- package/src/presentation/index.ts +1 -0
- package/src/presentation/routes/create-definition.test.ts +356 -0
- package/src/presentation/routes/create-definition.ts +101 -0
- package/src/presentation/routes/get-definition.test.ts +79 -0
- package/src/presentation/routes/get-definition.ts +37 -0
- package/src/presentation/routes/index.test.ts +41 -0
- package/src/presentation/routes/index.ts +28 -0
- package/src/presentation/routes/list-definitions.test.ts +195 -0
- package/src/presentation/routes/list-definitions.ts +49 -0
- package/src/presentation/routes/validate-definition.test.ts +63 -0
- package/src/presentation/routes/validate-definition.ts +46 -0
- package/src/presentation/subscribers/index.ts +2 -0
- package/src/presentation/subscribers/on-project-source-bound.test.ts +65 -0
- package/src/presentation/subscribers/on-project-source-bound.ts +11 -0
- package/src/presentation/subscribers/on-project-source-commit-observed.test.ts +56 -0
- package/src/presentation/subscribers/on-project-source-commit-observed.ts +15 -0
- package/src/presentation/subscribers/start-definition-sync.ts +38 -0
- package/src/temporal/activities/index.ts +10 -0
- package/src/temporal/activities/sync-activities.test.ts +337 -0
- package/src/temporal/activities/sync-activities.ts +206 -0
- package/src/temporal/constants.ts +2 -0
- package/src/temporal/index.ts +2 -0
- package/src/temporal/workflows/definition-sync.test.ts +59 -0
- package/src/temporal/workflows/definition-sync.ts +116 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/test/env.ts +7 -0
- package/test/factories/definition.ts +66 -0
- package/test/fixtures/invalid-cycle.yml +13 -0
- package/test/fixtures/invalid-missing-name.yml +4 -0
- package/test/fixtures/invalid-yaml-syntax.yml +6 -0
- package/test/fixtures/valid-dag.yml +25 -0
- package/test/fixtures/valid-listening-job.yml +21 -0
- package/test/fixtures/valid-simple.yml +18 -0
- package/test/fixtures/workflow-yaml/invalid/invalid-yaml-syntax.yaml +6 -0
- package/test/fixtures/workflow-yaml/invalid/missing-step-run.yaml +5 -0
- package/test/fixtures/workflow-yaml/valid/simple-build.document.json +26 -0
- package/test/fixtures/workflow-yaml/valid/simple-build.yaml +14 -0
- package/test/globalSetup.ts +25 -0
- package/test/index.ts +1 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
analyzeContextRootKeyAccess,
|
|
3
|
+
type WorkflowExpression,
|
|
4
|
+
type WorkflowInterpolationField,
|
|
5
|
+
type WorkflowPredicateField,
|
|
6
|
+
} from '@shipfox/expression';
|
|
7
|
+
import type {
|
|
8
|
+
WorkflowModelValidationIssue,
|
|
9
|
+
WorkflowModelValidationIssuePathSegment,
|
|
10
|
+
} from './invalid-workflow-model-error.js';
|
|
11
|
+
import {issue} from './validation-issue.js';
|
|
12
|
+
import {workflowFieldLabel} from './workflow-field-label.js';
|
|
13
|
+
|
|
14
|
+
export function validateDirectJobReferences(params: {
|
|
15
|
+
source: string;
|
|
16
|
+
expression: WorkflowExpression;
|
|
17
|
+
field: WorkflowInterpolationField | WorkflowPredicateField;
|
|
18
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
19
|
+
allowedJobReferences: ReadonlySet<string>;
|
|
20
|
+
}): WorkflowModelValidationIssue | undefined {
|
|
21
|
+
const access = analyzeContextRootKeyAccess(params.expression, ['jobs']);
|
|
22
|
+
const [computed] = access.violations;
|
|
23
|
+
if (computed !== undefined) {
|
|
24
|
+
return issue({
|
|
25
|
+
code: 'computed-context-key',
|
|
26
|
+
message: `${workflowFieldLabel(params.field)} must reference jobs with a literal dot key.`,
|
|
27
|
+
path: params.path,
|
|
28
|
+
details: {
|
|
29
|
+
field: params.field,
|
|
30
|
+
source: params.source,
|
|
31
|
+
expression: computed.source,
|
|
32
|
+
contextRoots: ['jobs'],
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const missing = access.references.find(
|
|
38
|
+
(reference) => !params.allowedJobReferences.has(reference.key),
|
|
39
|
+
);
|
|
40
|
+
if (missing === undefined) return undefined;
|
|
41
|
+
|
|
42
|
+
return issue({
|
|
43
|
+
code: 'missing-job-needs-edge',
|
|
44
|
+
message: `${workflowFieldLabel(params.field)} references job "${missing.key}" without a direct needs edge.`,
|
|
45
|
+
path: params.path,
|
|
46
|
+
details: {
|
|
47
|
+
field: params.field,
|
|
48
|
+
source: params.source,
|
|
49
|
+
expression: params.expression.source,
|
|
50
|
+
job: missing.key,
|
|
51
|
+
allowedJobs: [...params.allowedJobReferences],
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type {ExpressionTypeEnvironment} from '@shipfox/expression';
|
|
2
|
+
import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
|
|
3
|
+
import {validatePredicateExpression} from './validate-predicate-expression.js';
|
|
4
|
+
|
|
5
|
+
function validate(params: {
|
|
6
|
+
source: string;
|
|
7
|
+
field: Parameters<typeof validatePredicateExpression>[0]['field'];
|
|
8
|
+
site: Parameters<typeof validatePredicateExpression>[0]['site'];
|
|
9
|
+
allowedJobReferences?: ReadonlySet<string>;
|
|
10
|
+
typeOverlay?: ExpressionTypeEnvironment;
|
|
11
|
+
}): {
|
|
12
|
+
readonly expression: ReturnType<typeof validatePredicateExpression>;
|
|
13
|
+
readonly issues: WorkflowModelValidationIssue[];
|
|
14
|
+
} {
|
|
15
|
+
const issues: WorkflowModelValidationIssue[] = [];
|
|
16
|
+
const expression = validatePredicateExpression({
|
|
17
|
+
field: params.field,
|
|
18
|
+
source: params.source,
|
|
19
|
+
site: params.site,
|
|
20
|
+
path: ['predicate'],
|
|
21
|
+
invalidCode: 'invalid-job-success',
|
|
22
|
+
invalidMessage: 'Predicate must be a valid CEL boolean expression.',
|
|
23
|
+
issues,
|
|
24
|
+
...(params.allowedJobReferences === undefined
|
|
25
|
+
? {}
|
|
26
|
+
: {allowedJobReferences: params.allowedJobReferences}),
|
|
27
|
+
...(params.typeOverlay === undefined ? {} : {typeOverlay: params.typeOverlay}),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return {expression, issues};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe('validatePredicateExpression', () => {
|
|
34
|
+
it.each([
|
|
35
|
+
['event.ref == "refs/heads/main"', 'syntax'],
|
|
36
|
+
['trigger.event == "push"', 'typed'],
|
|
37
|
+
['has(event.ref)', 'syntax'],
|
|
38
|
+
] as const)('accepts trigger filters at ingest: %s', (source, check) => {
|
|
39
|
+
const result = validate({field: 'trigger.filter', source, site: 'ingest'});
|
|
40
|
+
|
|
41
|
+
expect(result.issues).toEqual([]);
|
|
42
|
+
expect(result.expression).toMatchObject({source, check});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each([
|
|
46
|
+
['event.ref', 'Predicate source must be boolean-shaped.'],
|
|
47
|
+
['trigger.event', 'must return bool'],
|
|
48
|
+
])('rejects non-boolean trigger filters: %s', (source, reason) => {
|
|
49
|
+
const result = validate({field: 'trigger.filter', source, site: 'ingest'});
|
|
50
|
+
|
|
51
|
+
expect(result.expression).toBeUndefined();
|
|
52
|
+
expect(result.issues).toEqual([
|
|
53
|
+
expect.objectContaining({
|
|
54
|
+
code: 'invalid-job-success',
|
|
55
|
+
details: expect.objectContaining({
|
|
56
|
+
source,
|
|
57
|
+
reason: expect.stringContaining(reason),
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it.each([
|
|
64
|
+
'run.id == "run-1"',
|
|
65
|
+
'inputs.env == "prod"',
|
|
66
|
+
'jobs.build.status == "succeeded"',
|
|
67
|
+
])('rejects trigger filter roots that are unavailable at ingest: %s', (source) => {
|
|
68
|
+
const result = validate({field: 'trigger.filter', source, site: 'ingest'});
|
|
69
|
+
|
|
70
|
+
expect(result.expression).toBeUndefined();
|
|
71
|
+
expect(result.issues).toEqual([
|
|
72
|
+
expect.objectContaining({
|
|
73
|
+
code: 'context-unavailable-at-predicate-site',
|
|
74
|
+
details: expect.objectContaining({field: 'trigger.filter', source, site: 'ingest'}),
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it.each([
|
|
80
|
+
['runner.os == "linux"', 'runner-context-in-server-predicate'],
|
|
81
|
+
['vars.ENV == "prod"', 'vars-context-in-server-predicate'],
|
|
82
|
+
])('rejects forbidden server predicate roots: %s', (source, code) => {
|
|
83
|
+
const result = validate({field: 'trigger.filter', source, site: 'ingest'});
|
|
84
|
+
|
|
85
|
+
expect(result.expression).toBeUndefined();
|
|
86
|
+
expect(result.issues).toEqual([
|
|
87
|
+
expect.objectContaining({
|
|
88
|
+
code,
|
|
89
|
+
details: expect.objectContaining({field: 'trigger.filter', source}),
|
|
90
|
+
}),
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.each([
|
|
95
|
+
['listener.on', 'event.action == "created"', undefined],
|
|
96
|
+
['listener.on', 'run.id == "run-1"', undefined],
|
|
97
|
+
['listener.until', 'inputs.target == event.issue.number', undefined],
|
|
98
|
+
['listener.until', 'job.key == "await-review"', undefined],
|
|
99
|
+
['listener.on', 'executions.all(execution, execution.status != "")', undefined],
|
|
100
|
+
['listener.until', 'execution.status == "waiting"', undefined],
|
|
101
|
+
['listener.on', 'matrix.os == "linux"', undefined],
|
|
102
|
+
['listener.until', 'jobs.build.outputs.pr_number == event.issue.number', new Set(['build'])],
|
|
103
|
+
['listener.until', 'has(jobs.build.outputs.pr_number)', new Set(['build'])],
|
|
104
|
+
] as const)('accepts listener filters at job activation: %s %s', (field, source, allowedJobs) => {
|
|
105
|
+
const result = validate({
|
|
106
|
+
field,
|
|
107
|
+
source,
|
|
108
|
+
site: 'job-activation',
|
|
109
|
+
...(allowedJobs === undefined ? {} : {allowedJobReferences: allowedJobs}),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.issues).toEqual([]);
|
|
113
|
+
expect(result.expression).toMatchObject({source});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it.each([
|
|
117
|
+
'step.status == "succeeded"',
|
|
118
|
+
'steps.build.outputs.sha == "abc"',
|
|
119
|
+
])('rejects listener roots that are unavailable at job activation: %s', (source) => {
|
|
120
|
+
const result = validate({field: 'listener.on', source, site: 'job-activation'});
|
|
121
|
+
|
|
122
|
+
expect(result.expression).toBeUndefined();
|
|
123
|
+
expect(result.issues).toEqual([
|
|
124
|
+
expect.objectContaining({
|
|
125
|
+
code: 'context-unavailable-at-predicate-site',
|
|
126
|
+
details: expect.objectContaining({field: 'listener.on', source, site: 'job-activation'}),
|
|
127
|
+
}),
|
|
128
|
+
]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('rejects listener job references without a direct needs edge', () => {
|
|
132
|
+
const result = validate({
|
|
133
|
+
field: 'listener.on',
|
|
134
|
+
source: 'jobs.build.outputs.pr_number == event.issue.number',
|
|
135
|
+
site: 'job-activation',
|
|
136
|
+
allowedJobReferences: new Set(['test']),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(result.expression).toBeUndefined();
|
|
140
|
+
expect(result.issues).toEqual([
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
code: 'missing-job-needs-edge',
|
|
143
|
+
details: expect.objectContaining({field: 'listener.on', job: 'build'}),
|
|
144
|
+
}),
|
|
145
|
+
]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('keeps existing job success syntax-only behavior unchanged', () => {
|
|
149
|
+
const result = validate({
|
|
150
|
+
field: 'job.success',
|
|
151
|
+
source: 'jobs.build.outputs.ready',
|
|
152
|
+
site: 'job-resolution',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(result.issues).toEqual([]);
|
|
156
|
+
expect(result.expression).toMatchObject({
|
|
157
|
+
source: 'jobs.build.outputs.ready',
|
|
158
|
+
check: 'syntax',
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AvailabilitySite,
|
|
3
|
+
analyzeContextRootKeyAccess,
|
|
4
|
+
availabilitySites,
|
|
5
|
+
createWorkflowExpression,
|
|
6
|
+
type ExpressionTypeEnvironment,
|
|
7
|
+
extractExactContextRoots,
|
|
8
|
+
getWorkflowContextDefinition,
|
|
9
|
+
getWorkflowContextTypeEnvironment,
|
|
10
|
+
InvalidWorkflowExpressionError,
|
|
11
|
+
predicateSourceIsBooleanShaped,
|
|
12
|
+
resolveContextRootAvailability,
|
|
13
|
+
resolveContextRootHost,
|
|
14
|
+
routeExpression,
|
|
15
|
+
validateServerEvaluable,
|
|
16
|
+
type WorkflowContextName,
|
|
17
|
+
type WorkflowExpression,
|
|
18
|
+
type WorkflowPredicateField,
|
|
19
|
+
workflowContextNames,
|
|
20
|
+
} from '@shipfox/expression';
|
|
21
|
+
import type {
|
|
22
|
+
WorkflowModelValidationIssue,
|
|
23
|
+
WorkflowModelValidationIssueCode,
|
|
24
|
+
WorkflowModelValidationIssuePathSegment,
|
|
25
|
+
} from './invalid-workflow-model-error.js';
|
|
26
|
+
import {validateDirectJobReferences} from './validate-job-references.js';
|
|
27
|
+
import {issue} from './validation-issue.js';
|
|
28
|
+
import {workflowFieldLabel} from './workflow-field-label.js';
|
|
29
|
+
|
|
30
|
+
export function validatePredicateExpression(params: {
|
|
31
|
+
field: WorkflowPredicateField;
|
|
32
|
+
source: string;
|
|
33
|
+
site: AvailabilitySite;
|
|
34
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
35
|
+
invalidCode: WorkflowModelValidationIssueCode;
|
|
36
|
+
invalidMessage: string;
|
|
37
|
+
issues: WorkflowModelValidationIssue[];
|
|
38
|
+
allowedJobReferences?: ReadonlySet<string>;
|
|
39
|
+
typeOverlay?: ExpressionTypeEnvironment | undefined;
|
|
40
|
+
}): WorkflowExpression | undefined {
|
|
41
|
+
const syntaxExpression = createSyntaxExpression(params);
|
|
42
|
+
if (syntaxExpression === undefined) return undefined;
|
|
43
|
+
|
|
44
|
+
const contextRoots = extractExactContextRoots(syntaxExpression.source);
|
|
45
|
+
const knownRoots = contextRoots.filter((root) => resolveContextRootHost(root) !== undefined);
|
|
46
|
+
const unknownRoots = contextRoots.filter((root) => resolveContextRootHost(root) === undefined);
|
|
47
|
+
|
|
48
|
+
if (unknownRoots.length > 0) {
|
|
49
|
+
params.issues.push(invalidPredicateIssue({...params, contextRoots}));
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const serverEvaluability = validateServerEvaluable(syntaxExpression);
|
|
54
|
+
if (!serverEvaluability.ok) {
|
|
55
|
+
params.issues.push(
|
|
56
|
+
...serverEvaluability.violations.map((violation) =>
|
|
57
|
+
runnerContextInServerPredicateIssue({
|
|
58
|
+
...params,
|
|
59
|
+
contextRoots,
|
|
60
|
+
runnerRoots: violation.runnerRoots,
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const varsRoots = knownRoots.filter((root) => root === 'vars');
|
|
68
|
+
if (varsRoots.length > 0) {
|
|
69
|
+
params.issues.push(varsContextInServerPredicateIssue({...params, contextRoots}));
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const unavailableRoots = knownRoots.filter((root) => !isRootAvailableAt(root, params.site));
|
|
74
|
+
if (unavailableRoots.length > 0) {
|
|
75
|
+
params.issues.push(
|
|
76
|
+
unavailablePredicateContextIssue({...params, contextRoots, unavailableRoots}),
|
|
77
|
+
);
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const route = routeExpression(syntaxExpression);
|
|
82
|
+
if (route.fillTarget !== 'runner-fill' && !isSiteAtOrAfter(params.site, route.fillTarget)) {
|
|
83
|
+
const unavailableRoots = unavailableRouteReferences(syntaxExpression);
|
|
84
|
+
params.issues.push(
|
|
85
|
+
unavailableRoots.length > 0
|
|
86
|
+
? unavailablePredicateContextIssue({
|
|
87
|
+
...params,
|
|
88
|
+
contextRoots,
|
|
89
|
+
unavailableRoots,
|
|
90
|
+
})
|
|
91
|
+
: routeUnavailablePredicateContextIssue({
|
|
92
|
+
...params,
|
|
93
|
+
contextRoots,
|
|
94
|
+
fillTarget: route.fillTarget,
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const invalidJobReferenceIssue =
|
|
101
|
+
params.allowedJobReferences === undefined
|
|
102
|
+
? undefined
|
|
103
|
+
: validateDirectJobReferences({
|
|
104
|
+
source: params.source,
|
|
105
|
+
expression: syntaxExpression,
|
|
106
|
+
field: params.field,
|
|
107
|
+
path: params.path,
|
|
108
|
+
allowedJobReferences: params.allowedJobReferences,
|
|
109
|
+
});
|
|
110
|
+
if (invalidJobReferenceIssue !== undefined) {
|
|
111
|
+
params.issues.push(invalidJobReferenceIssue);
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (params.typeOverlay === undefined && knownRoots.some((root) => hasSyntaxOnlyCheckMode(root))) {
|
|
116
|
+
if (
|
|
117
|
+
isWorkflowFilterPredicateField(params.field) &&
|
|
118
|
+
!predicateSourceIsBooleanShaped(syntaxExpression.source)
|
|
119
|
+
) {
|
|
120
|
+
params.issues.push(
|
|
121
|
+
invalidPredicateIssue({
|
|
122
|
+
...params,
|
|
123
|
+
contextRoots,
|
|
124
|
+
reason: 'Predicate source must be boolean-shaped.',
|
|
125
|
+
}),
|
|
126
|
+
);
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return syntaxExpression;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
return createWorkflowExpression({
|
|
135
|
+
source: params.source,
|
|
136
|
+
check: {
|
|
137
|
+
mode: 'typed',
|
|
138
|
+
// `undefined` preserves the legacy syntax-only path above. `{}` means
|
|
139
|
+
// callers intentionally requested typed checking with the standard roots.
|
|
140
|
+
typeEnvironment: mergeTypeEnvironments(knownRoots, params.typeOverlay),
|
|
141
|
+
expectedResultType: 'bool',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
params.issues.push(
|
|
146
|
+
invalidPredicateIssue({
|
|
147
|
+
...params,
|
|
148
|
+
contextRoots,
|
|
149
|
+
reason:
|
|
150
|
+
error instanceof InvalidWorkflowExpressionError
|
|
151
|
+
? error.reason
|
|
152
|
+
: 'Expression source did not parse or type-check.',
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function createSyntaxExpression(params: {
|
|
160
|
+
field: WorkflowPredicateField;
|
|
161
|
+
source: string;
|
|
162
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
163
|
+
invalidCode: WorkflowModelValidationIssueCode;
|
|
164
|
+
invalidMessage: string;
|
|
165
|
+
issues: WorkflowModelValidationIssue[];
|
|
166
|
+
}): WorkflowExpression | undefined {
|
|
167
|
+
try {
|
|
168
|
+
return createWorkflowExpression({
|
|
169
|
+
source: params.source,
|
|
170
|
+
check: {mode: 'syntax'},
|
|
171
|
+
});
|
|
172
|
+
} catch (error) {
|
|
173
|
+
params.issues.push(
|
|
174
|
+
invalidPredicateIssue({
|
|
175
|
+
...params,
|
|
176
|
+
contextRoots: [],
|
|
177
|
+
reason:
|
|
178
|
+
error instanceof InvalidWorkflowExpressionError
|
|
179
|
+
? error.reason
|
|
180
|
+
: 'Expression source did not parse or type-check.',
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function invalidPredicateIssue(params: {
|
|
188
|
+
field: WorkflowPredicateField;
|
|
189
|
+
source: string;
|
|
190
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
191
|
+
invalidCode: WorkflowModelValidationIssueCode;
|
|
192
|
+
invalidMessage: string;
|
|
193
|
+
contextRoots: readonly string[];
|
|
194
|
+
reason?: string;
|
|
195
|
+
}): WorkflowModelValidationIssue {
|
|
196
|
+
return issue({
|
|
197
|
+
code: params.invalidCode,
|
|
198
|
+
message: params.invalidMessage,
|
|
199
|
+
path: params.path,
|
|
200
|
+
details: {
|
|
201
|
+
field: params.field,
|
|
202
|
+
source: params.source,
|
|
203
|
+
contextRoots: params.contextRoots,
|
|
204
|
+
reason: params.reason ?? 'Expression source did not parse or type-check.',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const availabilitySiteLabels = {
|
|
210
|
+
ingest: 'ingest',
|
|
211
|
+
'run-creation': 'run creation',
|
|
212
|
+
'execution-creation': 'execution creation',
|
|
213
|
+
'job-activation': 'job activation',
|
|
214
|
+
'step-dispatch': 'step dispatch',
|
|
215
|
+
'step-report': 'step reporting',
|
|
216
|
+
'execution-resolution': 'execution resolution',
|
|
217
|
+
'job-resolution': 'job resolution',
|
|
218
|
+
} as const satisfies Record<AvailabilitySite, string>;
|
|
219
|
+
|
|
220
|
+
function runnerContextInServerPredicateIssue(params: {
|
|
221
|
+
field: WorkflowPredicateField;
|
|
222
|
+
source: string;
|
|
223
|
+
site: AvailabilitySite;
|
|
224
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
225
|
+
contextRoots: readonly string[];
|
|
226
|
+
runnerRoots: readonly string[];
|
|
227
|
+
}): WorkflowModelValidationIssue {
|
|
228
|
+
return issue({
|
|
229
|
+
code: 'runner-context-in-server-predicate',
|
|
230
|
+
message: `${fieldLabel(params.field)} cannot reference runner context ${formatList(
|
|
231
|
+
params.runnerRoots,
|
|
232
|
+
)} because it is evaluated on the server at ${describeAvailabilitySite(params.site)}.`,
|
|
233
|
+
path: params.path,
|
|
234
|
+
details: {
|
|
235
|
+
field: params.field,
|
|
236
|
+
source: params.source,
|
|
237
|
+
contextRoots: params.contextRoots,
|
|
238
|
+
runnerRoots: params.runnerRoots,
|
|
239
|
+
site: params.site,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function varsContextInServerPredicateIssue(params: {
|
|
245
|
+
field: WorkflowPredicateField;
|
|
246
|
+
source: string;
|
|
247
|
+
site: AvailabilitySite;
|
|
248
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
249
|
+
contextRoots: readonly string[];
|
|
250
|
+
}): WorkflowModelValidationIssue {
|
|
251
|
+
return issue({
|
|
252
|
+
code: 'vars-context-in-server-predicate',
|
|
253
|
+
message: `${fieldLabel(params.field)} cannot reference vars because predicate evaluation does not include workflow variables.`,
|
|
254
|
+
path: params.path,
|
|
255
|
+
details: {
|
|
256
|
+
field: params.field,
|
|
257
|
+
source: params.source,
|
|
258
|
+
contextRoots: params.contextRoots,
|
|
259
|
+
rejectedRoots: ['vars'],
|
|
260
|
+
site: params.site,
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function unavailablePredicateContextIssue(params: {
|
|
266
|
+
field: WorkflowPredicateField;
|
|
267
|
+
source: string;
|
|
268
|
+
site: AvailabilitySite;
|
|
269
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
270
|
+
contextRoots: readonly string[];
|
|
271
|
+
unavailableRoots: readonly string[];
|
|
272
|
+
}): WorkflowModelValidationIssue {
|
|
273
|
+
return issue({
|
|
274
|
+
code: 'context-unavailable-at-predicate-site',
|
|
275
|
+
message: `${fieldLabel(params.field)} references ${contextNoun(
|
|
276
|
+
params.unavailableRoots,
|
|
277
|
+
)} ${formatList(params.unavailableRoots)} that ${availabilityVerb(
|
|
278
|
+
params.unavailableRoots,
|
|
279
|
+
)} not available at ${describeAvailabilitySite(params.site)}. ${params.unavailableRoots
|
|
280
|
+
.map(unavailableRootAvailabilityMessage)
|
|
281
|
+
.join(' ')}`,
|
|
282
|
+
path: params.path,
|
|
283
|
+
details: {
|
|
284
|
+
field: params.field,
|
|
285
|
+
source: params.source,
|
|
286
|
+
contextRoots: params.contextRoots,
|
|
287
|
+
unavailableRoots: params.unavailableRoots,
|
|
288
|
+
site: params.site,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function routeUnavailablePredicateContextIssue(params: {
|
|
294
|
+
field: WorkflowPredicateField;
|
|
295
|
+
source: string;
|
|
296
|
+
site: AvailabilitySite;
|
|
297
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
298
|
+
contextRoots: readonly string[];
|
|
299
|
+
fillTarget: AvailabilitySite;
|
|
300
|
+
}): WorkflowModelValidationIssue {
|
|
301
|
+
return issue({
|
|
302
|
+
code: 'context-unavailable-at-predicate-site',
|
|
303
|
+
message: `${fieldLabel(params.field)} requires context filled at ${describeAvailabilitySite(
|
|
304
|
+
params.fillTarget,
|
|
305
|
+
)}, but it is evaluated at ${describeAvailabilitySite(params.site)}.`,
|
|
306
|
+
path: params.path,
|
|
307
|
+
details: {
|
|
308
|
+
field: params.field,
|
|
309
|
+
source: params.source,
|
|
310
|
+
contextRoots: params.contextRoots,
|
|
311
|
+
fillTarget: params.fillTarget,
|
|
312
|
+
site: params.site,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function isRootAvailableAt(root: string, site: AvailabilitySite): boolean {
|
|
318
|
+
const availability = resolveContextRootAvailability(root);
|
|
319
|
+
if (availability === undefined) return false;
|
|
320
|
+
|
|
321
|
+
return availabilitySites.indexOf(availability) <= availabilitySites.indexOf(site);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function unavailableRootAvailabilityMessage(root: string): string {
|
|
325
|
+
if (root === 'execution.failed') {
|
|
326
|
+
return '"execution.failed" becomes available at step dispatch.';
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const availability = resolveContextRootAvailability(root);
|
|
330
|
+
if (availability === undefined) return `"${root}" is not available at any server site.`;
|
|
331
|
+
return `"${root}" becomes available at ${describeAvailabilitySite(availability)}.`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function unavailableRouteReferences(expression: WorkflowExpression): readonly string[] {
|
|
335
|
+
const access = analyzeContextRootKeyAccess(expression, ['execution']);
|
|
336
|
+
return access.references.some((reference) => reference.key === 'failed')
|
|
337
|
+
? ['execution.failed']
|
|
338
|
+
: [];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function hasSyntaxOnlyCheckMode(root: string): boolean {
|
|
342
|
+
if (!isWorkflowContextName(root)) return resolveContextRootHost(root) === 'server';
|
|
343
|
+
return isWorkflowContextName(root) && getWorkflowContextDefinition(root).checkMode === 'syntax';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function mergeTypeEnvironments(
|
|
347
|
+
roots: readonly string[],
|
|
348
|
+
typeOverlay?: ExpressionTypeEnvironment,
|
|
349
|
+
): ExpressionTypeEnvironment {
|
|
350
|
+
const typeEnvironment: Record<string, ExpressionTypeEnvironment[string]> = {};
|
|
351
|
+
|
|
352
|
+
for (const root of roots) {
|
|
353
|
+
const overlayType = typeOverlay?.[root];
|
|
354
|
+
if (overlayType !== undefined) {
|
|
355
|
+
typeEnvironment[root] = overlayType;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!isWorkflowContextName(root)) {
|
|
360
|
+
if (typeOverlay !== undefined) typeEnvironment[root] = {kind: 'map'};
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const contextTypeEnvironment = getWorkflowContextTypeEnvironment(root);
|
|
365
|
+
if (contextTypeEnvironment === undefined) {
|
|
366
|
+
if (typeOverlay !== undefined) typeEnvironment[root] = {kind: 'map'};
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
Object.assign(typeEnvironment, contextTypeEnvironment);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return typeEnvironment;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function isWorkflowContextName(root: string): root is WorkflowContextName {
|
|
377
|
+
return (workflowContextNames as readonly string[]).includes(root);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function isWorkflowFilterPredicateField(field: WorkflowPredicateField): boolean {
|
|
381
|
+
return field === 'trigger.filter' || field === 'listener.on' || field === 'listener.until';
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function fieldLabel(field: WorkflowPredicateField): string {
|
|
385
|
+
return workflowFieldLabel(field);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function contextNoun(roots: readonly string[]): 'context' | 'contexts' {
|
|
389
|
+
return roots.length === 1 ? 'context' : 'contexts';
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function availabilityVerb(roots: readonly string[]): 'is' | 'are' {
|
|
393
|
+
return roots.length === 1 ? 'is' : 'are';
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function describeAvailabilitySite(site: AvailabilitySite): string {
|
|
397
|
+
return availabilitySiteLabels[site];
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function isSiteAtOrAfter(site: AvailabilitySite, fillTarget: AvailabilitySite): boolean {
|
|
401
|
+
return availabilitySites.indexOf(site) >= availabilitySites.indexOf(fillTarget);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function formatList(values: readonly string[]): string {
|
|
405
|
+
return values.map((value) => `"${value}"`).join(', ');
|
|
406
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkflowModelValidationIssue,
|
|
3
|
+
WorkflowModelValidationIssueCode,
|
|
4
|
+
WorkflowModelValidationIssuePathSegment,
|
|
5
|
+
} from './invalid-workflow-model-error.js';
|
|
6
|
+
|
|
7
|
+
export function issue(params: {
|
|
8
|
+
code: WorkflowModelValidationIssueCode;
|
|
9
|
+
message: string;
|
|
10
|
+
path: readonly WorkflowModelValidationIssuePathSegment[];
|
|
11
|
+
details?: Readonly<Record<string, unknown>>;
|
|
12
|
+
}): WorkflowModelValidationIssue {
|
|
13
|
+
if (params.details === undefined) {
|
|
14
|
+
return {
|
|
15
|
+
code: params.code,
|
|
16
|
+
message: params.message,
|
|
17
|
+
path: params.path,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
code: params.code,
|
|
23
|
+
message: params.message,
|
|
24
|
+
path: params.path,
|
|
25
|
+
details: params.details,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type {WorkflowInterpolationField, WorkflowPredicateField} from '@shipfox/expression';
|
|
2
|
+
|
|
3
|
+
export function workflowFieldLabel(
|
|
4
|
+
field: WorkflowInterpolationField | WorkflowPredicateField,
|
|
5
|
+
): string {
|
|
6
|
+
switch (field) {
|
|
7
|
+
case 'run':
|
|
8
|
+
return 'Run command interpolation';
|
|
9
|
+
case 'env.value':
|
|
10
|
+
return 'Env value interpolation';
|
|
11
|
+
case 'agent.prompt':
|
|
12
|
+
return 'Agent prompt interpolation';
|
|
13
|
+
case 'agent.model':
|
|
14
|
+
return 'Agent model interpolation';
|
|
15
|
+
case 'agent.provider':
|
|
16
|
+
return 'Agent provider interpolation';
|
|
17
|
+
case 'agent.thinking':
|
|
18
|
+
return 'Agent thinking';
|
|
19
|
+
case 'job.runner':
|
|
20
|
+
return 'Job runner interpolation';
|
|
21
|
+
case 'job.outputs':
|
|
22
|
+
return 'Job outputs mapping';
|
|
23
|
+
case 'job.name':
|
|
24
|
+
return 'Job name interpolation';
|
|
25
|
+
case 'step.name':
|
|
26
|
+
return 'Step name interpolation';
|
|
27
|
+
case 'step.success':
|
|
28
|
+
return 'Step gate success';
|
|
29
|
+
case 'step.feedback':
|
|
30
|
+
return 'Step feedback';
|
|
31
|
+
case 'job.success':
|
|
32
|
+
return 'Job success';
|
|
33
|
+
case 'trigger.filter':
|
|
34
|
+
return 'Trigger filter';
|
|
35
|
+
case 'listener.on':
|
|
36
|
+
return 'Listener on filter';
|
|
37
|
+
case 'listener.until':
|
|
38
|
+
return 'Listener until filter';
|
|
39
|
+
case 'job.if':
|
|
40
|
+
return 'Job if';
|
|
41
|
+
case 'step.if':
|
|
42
|
+
return 'Step if';
|
|
43
|
+
default:
|
|
44
|
+
return assertNever(field);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function assertNever(value: never): never {
|
|
49
|
+
throw new Error(`Unhandled workflow field: ${value}`);
|
|
50
|
+
}
|