@pikku/core 0.12.107 → 0.12.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +344 -0
- package/dist/analytics/analytics.d.ts +6 -0
- package/dist/analytics/analytics.js +58 -0
- package/dist/analytics/analytics.types.d.ts +156 -0
- package/dist/analytics/analytics.types.js +1 -0
- package/dist/analytics/anonymous-analytics-identity.d.ts +23 -0
- package/dist/analytics/anonymous-analytics-identity.js +27 -0
- package/dist/analytics/compose-analytics-identity.d.ts +14 -0
- package/dist/analytics/compose-analytics-identity.js +33 -0
- package/dist/analytics/cookie-analytics-identity.d.ts +24 -0
- package/dist/analytics/cookie-analytics-identity.js +37 -0
- package/dist/analytics/define-analytics-events.d.ts +22 -0
- package/dist/analytics/define-analytics-events.js +9 -0
- package/dist/analytics/fan-out-analytics.d.ts +15 -0
- package/dist/analytics/fan-out-analytics.js +36 -0
- package/dist/analytics/index.d.ts +13 -0
- package/dist/analytics/index.js +8 -0
- package/dist/analytics/logger-analytics-service.d.ts +14 -0
- package/dist/analytics/logger-analytics-service.js +23 -0
- package/dist/analytics/mint-cookie.d.ts +44 -0
- package/dist/analytics/mint-cookie.js +55 -0
- package/dist/errors/errors.d.ts +7 -0
- package/dist/errors/errors.js +17 -0
- package/dist/function/function-meta.types.d.ts +2 -0
- package/dist/function/function-runner.js +29 -0
- package/dist/function/functions.types.d.ts +22 -3
- package/dist/middleware/auth-apikey.d.ts +1 -1
- package/dist/pikku-state.d.ts +1 -1
- package/dist/scopes.d.ts +11 -0
- package/dist/scopes.js +17 -0
- package/dist/services/credential-wire-service.d.ts +1 -1
- package/dist/services/feature-flag-service.d.ts +100 -0
- package/dist/services/feature-flag-service.js +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/meta-service.d.ts +16 -0
- package/dist/services/meta-service.js +19 -0
- package/dist/services/webhook-service.d.ts +7 -2
- package/dist/services/webhook-service.js +11 -6
- package/dist/testing/service-tests/agent-run-service-tests.d.ts +2 -2
- package/dist/testing/service-tests/agent-storage-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/channel-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/credential-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/deployment-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/event-hub-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/secret-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/session-store-tests.d.ts +1 -1
- package/dist/testing/service-tests/workflow-run-service-tests.d.ts +1 -1
- package/dist/testing/service-tests/workflow-service-tests.d.ts +1 -1
- package/dist/types/core.types.d.ts +23 -0
- package/dist/wirings/agent/agent-rpc.d.ts +1 -1
- package/dist/wirings/agent/agent-turn.d.ts +1 -1
- package/dist/wirings/agent-scorer/agent-scorer-live.d.ts +1 -1
- package/dist/wirings/channel/channel-common.d.ts +1 -1
- package/dist/wirings/channel/channel-rpc-validators.d.ts +2 -2
- package/dist/wirings/channel/channel-runner.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-runner.d.ts +1 -1
- package/dist/wirings/channel/serverless/serverless-channel-runner.d.ts +1 -1
- package/dist/wirings/cli/channel/cli-approval.d.ts +2 -2
- package/dist/wirings/flag/assert-feature-available.d.ts +15 -0
- package/dist/wirings/flag/assert-feature-available.js +23 -0
- package/dist/wirings/flag/cached-flag-source.d.ts +73 -0
- package/dist/wirings/flag/cached-flag-source.js +96 -0
- package/dist/wirings/flag/define-feature-flags.d.ts +33 -0
- package/dist/wirings/flag/define-feature-flags.js +32 -0
- package/dist/wirings/flag/flag.types.d.ts +97 -0
- package/dist/wirings/flag/flag.types.js +1 -0
- package/dist/wirings/flag/index.d.ts +7 -0
- package/dist/wirings/flag/index.js +5 -0
- package/dist/wirings/flag/resolve-flag.d.ts +52 -0
- package/dist/wirings/flag/resolve-flag.js +90 -0
- package/dist/wirings/flag/validate-flag-definitions.d.ts +26 -0
- package/dist/wirings/flag/validate-flag-definitions.js +103 -0
- package/dist/wirings/virtual-user/virtual-user-scaffold.d.ts +8 -8
- package/dist/wirings/virtual-user/virtual-user-schedule.d.ts +1 -1
- package/dist/wirings/workflow/workflow-queue-routing.d.ts +1 -1
- package/package.json +11 -5
- package/run-tests.sh +17 -9
- package/src/public-surface.json +26 -0
- package/src/agent-naming.test.ts +0 -68
- package/src/api-report.test.ts +0 -138
- package/src/app-alias-surface.test.ts +0 -179
- package/src/app-leaf-surface.test.ts +0 -222
- package/src/bootstrap-compat/root.ts +0 -20
- package/src/classification/column-form.test.ts +0 -97
- package/src/classification/column-form.ts +0 -55
- package/src/classification/data-classification.ts +0 -73
- package/src/classification/index.ts +0 -40
- package/src/classification/key-ids.ts +0 -2
- package/src/classification/secret-value.test.ts +0 -204
- package/src/classification/secret-value.ts +0 -106
- package/src/crypto-utils.test.ts +0 -668
- package/src/crypto-utils.ts +0 -460
- package/src/dev/hot-reload.test.ts +0 -587
- package/src/dev/hot-reload.ts +0 -225
- package/src/dev/module-runner.test.ts +0 -235
- package/src/dev/module-runner.ts +0 -138
- package/src/dev/reload-meta.test.ts +0 -183
- package/src/dev/reload-meta.ts +0 -144
- package/src/ecosystem-tier-removed.test.ts +0 -72
- package/src/env.ts +0 -1
- package/src/errors/error-handler.ts +0 -77
- package/src/errors/error.test.ts +0 -229
- package/src/errors/errors.ts +0 -393
- package/src/errors/index.ts +0 -11
- package/src/errors/serialized-error.ts +0 -12
- package/src/factory-functions.test.ts +0 -111
- package/src/function/abort-scope.test.ts +0 -97
- package/src/function/abort-scope.ts +0 -80
- package/src/function/function-meta.types.ts +0 -151
- package/src/function/function-runner.test.ts +0 -1619
- package/src/function/function-runner.ts +0 -499
- package/src/function/functions.types.ts +0 -319
- package/src/function/index.ts +0 -40
- package/src/function/list.types.test.ts +0 -100
- package/src/function/list.types.ts +0 -69
- package/src/function/pikku-request.test.ts +0 -35
- package/src/function/pikku-request.ts +0 -17
- package/src/gopass-secrets-removed.test.ts +0 -51
- package/src/handle-error.test.ts +0 -532
- package/src/handle-error.ts +0 -64
- package/src/middleware/auth-apikey.test.ts +0 -362
- package/src/middleware/auth-apikey.ts +0 -39
- package/src/middleware/auth-bearer.test.ts +0 -566
- package/src/middleware/auth-bearer.ts +0 -83
- package/src/middleware/auth-cookie.test.ts +0 -572
- package/src/middleware/auth-cookie.ts +0 -107
- package/src/middleware/cors.test.ts +0 -579
- package/src/middleware/cors.ts +0 -103
- package/src/middleware/index.ts +0 -30
- package/src/middleware/middleware-factories.ts +0 -76
- package/src/middleware/middleware.types.ts +0 -61
- package/src/middleware/remote-auth.test.ts +0 -547
- package/src/middleware/remote-auth.ts +0 -95
- package/src/middleware/remote-jobs-secret.ts +0 -35
- package/src/middleware/require-origin.test.ts +0 -115
- package/src/middleware/require-origin.ts +0 -79
- package/src/middleware/telemetry.ts +0 -83
- package/src/middleware-runner.test.ts +0 -578
- package/src/middleware-runner.ts +0 -232
- package/src/no-any-casts.test.ts +0 -57
- package/src/no-root-barrel.test.ts +0 -122
- package/src/permissions.test.ts +0 -256
- package/src/permissions.ts +0 -217
- package/src/pikku-state.test.ts +0 -198
- package/src/pikku-state.ts +0 -206
- package/src/production-barrels-stay-lean.test.ts +0 -110
- package/src/public-surface.test.ts +0 -133
- package/src/remote.test.ts +0 -173
- package/src/remote.ts +0 -56
- package/src/removed-legacy-exports.test.ts +0 -66
- package/src/run-tests-script.test.ts +0 -67
- package/src/schema.test.ts +0 -430
- package/src/schema.ts +0 -204
- package/src/scopes.test.ts +0 -203
- package/src/scopes.ts +0 -67
- package/src/services/agent-run-state-service.ts +0 -37
- package/src/services/agent-runner-service.ts +0 -238
- package/src/services/agent-storage-service.ts +0 -39
- package/src/services/ai-embedding-service.ts +0 -9
- package/src/services/audit-service.test.ts +0 -44
- package/src/services/audit-service.ts +0 -280
- package/src/services/content-service.ts +0 -65
- package/src/services/credential-service.ts +0 -20
- package/src/services/credential-wire-service.test.ts +0 -235
- package/src/services/credential-wire-service.ts +0 -120
- package/src/services/deployment-service.ts +0 -32
- package/src/services/email-service.test.ts +0 -100
- package/src/services/email-service.ts +0 -60
- package/src/services/email-template.test.ts +0 -311
- package/src/services/email-template.ts +0 -254
- package/src/services/file-scenario-run-store.test.ts +0 -349
- package/src/services/file-scenario-run-store.ts +0 -282
- package/src/services/gateway-service.ts +0 -5
- package/src/services/http-personas-converse.test.ts +0 -274
- package/src/services/http-personas.test.ts +0 -379
- package/src/services/http-personas.ts +0 -508
- package/src/services/in-memory-agent-run-state-service.ts +0 -87
- package/src/services/in-memory-queue-service.test.ts +0 -162
- package/src/services/in-memory-queue-service.ts +0 -78
- package/src/services/in-memory-session-store.test.ts +0 -58
- package/src/services/in-memory-session-store.ts +0 -20
- package/src/services/in-memory-trigger-service.ts +0 -47
- package/src/services/in-memory-workflow-service.test.ts +0 -430
- package/src/services/in-memory-workflow-service.ts +0 -595
- package/src/services/index.ts +0 -192
- package/src/services/istanbul-coverage-service.test.ts +0 -66
- package/src/services/istanbul-coverage-service.ts +0 -59
- package/src/services/jwt-service.ts +0 -15
- package/src/services/local-content-request-handler.test.ts +0 -240
- package/src/services/local-content-request-handler.ts +0 -295
- package/src/services/local-content.test.ts +0 -186
- package/src/services/local-content.ts +0 -242
- package/src/services/local-credential-service.ts +0 -63
- package/src/services/local-email-service.test.ts +0 -182
- package/src/services/local-email-service.ts +0 -43
- package/src/services/local-gateway-service.ts +0 -51
- package/src/services/local-secrets.test.ts +0 -95
- package/src/services/local-secrets.ts +0 -71
- package/src/services/local-variables.test.ts +0 -61
- package/src/services/local-variables.ts +0 -45
- package/src/services/logger-console.test.ts +0 -117
- package/src/services/logger-console.ts +0 -174
- package/src/services/logger.ts +0 -48
- package/src/services/meta-service.test.ts +0 -75
- package/src/services/meta-service.ts +0 -767
- package/src/services/persona-actor-secret.test.ts +0 -68
- package/src/services/persona-actor-secret.ts +0 -70
- package/src/services/persona-sign-in.test.ts +0 -219
- package/src/services/persona-sign-in.ts +0 -238
- package/src/services/personas-service.ts +0 -191
- package/src/services/pikku-user-id.test.ts +0 -62
- package/src/services/pikku-user-id.ts +0 -13
- package/src/services/queue-webhook-service.test.ts +0 -409
- package/src/services/queue-webhook-service.ts +0 -150
- package/src/services/remote-jobs.test.ts +0 -106
- package/src/services/remote-jobs.ts +0 -92
- package/src/services/scheduler-service.ts +0 -37
- package/src/services/schema-service.ts +0 -10
- package/src/services/scope-service.ts +0 -91
- package/src/services/scoped-credential-service.test.ts +0 -86
- package/src/services/scoped-credential-service.ts +0 -63
- package/src/services/scoped-secret-service.test.ts +0 -93
- package/src/services/scoped-secret-service.ts +0 -40
- package/src/services/secret-host-binding.test.ts +0 -138
- package/src/services/secret-host-binding.ts +0 -51
- package/src/services/secret-service.ts +0 -25
- package/src/services/secretless.test.ts +0 -54
- package/src/services/secretless.ts +0 -29
- package/src/services/session-store.ts +0 -9
- package/src/services/stub-tracker.test.ts +0 -104
- package/src/services/stub-tracker.ts +0 -175
- package/src/services/system-role-guard.test.ts +0 -96
- package/src/services/system-role-guard.ts +0 -71
- package/src/services/temporary-file-service.ts +0 -76
- package/src/services/trigger-service.ts +0 -5
- package/src/services/typed-credential-service.ts +0 -83
- package/src/services/typed-secret-service.test.ts +0 -204
- package/src/services/typed-secret-service.ts +0 -132
- package/src/services/typed-variables-service.test.ts +0 -166
- package/src/services/typed-variables-service.ts +0 -166
- package/src/services/user-session-service.test.ts +0 -147
- package/src/services/user-session-service.ts +0 -85
- package/src/services/v8-coverage-service.test.ts +0 -69
- package/src/services/v8-coverage-service.ts +0 -163
- package/src/services/variables-service.ts +0 -13
- package/src/services/webhook-service.ts +0 -147
- package/src/services/workflow-service.ts +0 -120
- package/src/side-effects-are-declared.test.ts +0 -84
- package/src/source-files-stay-composable.test.ts +0 -41
- package/src/template-alias-surface.test.ts +0 -240
- package/src/test-utils.ts +0 -38
- package/src/testing/index.ts +0 -3
- package/src/testing/service-tests/agent-run-service-tests.ts +0 -98
- package/src/testing/service-tests/agent-storage-service-tests.ts +0 -379
- package/src/testing/service-tests/channel-store-tests.ts +0 -98
- package/src/testing/service-tests/credential-service-tests.ts +0 -143
- package/src/testing/service-tests/deployment-service-tests.ts +0 -33
- package/src/testing/service-tests/event-hub-store-tests.ts +0 -48
- package/src/testing/service-tests/secret-service-tests.ts +0 -105
- package/src/testing/service-tests/session-store-tests.ts +0 -62
- package/src/testing/service-tests/workflow-run-service-tests.ts +0 -59
- package/src/testing/service-tests/workflow-service-tests.ts +0 -308
- package/src/testing/service-tests.ts +0 -95
- package/src/time-utils.test.ts +0 -56
- package/src/time-utils.ts +0 -89
- package/src/types/core.types.ts +0 -446
- package/src/types/index.ts +0 -25
- package/src/types/state.types.ts +0 -226
- package/src/types/typed-credentials.test.ts +0 -72
- package/src/utils/hash.test.ts +0 -68
- package/src/utils/hash.ts +0 -26
- package/src/utils/hmac.ts +0 -21
- package/src/utils/node-host-resolver.ts +0 -20
- package/src/utils/safe-fetch.test.ts +0 -515
- package/src/utils/safe-fetch.ts +0 -347
- package/src/utils.test.ts +0 -359
- package/src/utils.ts +0 -165
- package/src/version.test.ts +0 -80
- package/src/version.ts +0 -25
- package/src/wirings/actor-flow/actor-flow.types.ts +0 -40
- package/src/wirings/actor-flow/index.ts +0 -10
- package/src/wirings/actor-flow/run-conversation.test.ts +0 -187
- package/src/wirings/actor-flow/run-conversation.ts +0 -280
- package/src/wirings/addon/addon-auth-tags.test.ts +0 -219
- package/src/wirings/addon/addon-runner.ts +0 -236
- package/src/wirings/addon/addon-scopes.test.ts +0 -225
- package/src/wirings/addon/addon-secrets.test.ts +0 -391
- package/src/wirings/addon/index.ts +0 -12
- package/src/wirings/addon/remote-addon-auth.ts +0 -65
- package/src/wirings/addon/wire-addon.test.ts +0 -102
- package/src/wirings/addon/wire-addon.ts +0 -202
- package/src/wirings/addon/wire-remote-addon.ts +0 -41
- package/src/wirings/agent/agent-agui.test.ts +0 -1213
- package/src/wirings/agent/agent-agui.ts +0 -405
- package/src/wirings/agent/agent-authorization.test.ts +0 -208
- package/src/wirings/agent/agent-finalize.test.ts +0 -187
- package/src/wirings/agent/agent-finalize.ts +0 -197
- package/src/wirings/agent/agent-helpers.test.ts +0 -215
- package/src/wirings/agent/agent-helpers.ts +0 -96
- package/src/wirings/agent/agent-interrupt.test.ts +0 -833
- package/src/wirings/agent/agent-interrupt.ts +0 -400
- package/src/wirings/agent/agent-memory.test.ts +0 -397
- package/src/wirings/agent/agent-memory.ts +0 -496
- package/src/wirings/agent/agent-middleware.types.test.ts +0 -41
- package/src/wirings/agent/agent-model-config.test.ts +0 -103
- package/src/wirings/agent/agent-model-config.ts +0 -59
- package/src/wirings/agent/agent-prepare.test.ts +0 -977
- package/src/wirings/agent/agent-prepare.ts +0 -1083
- package/src/wirings/agent/agent-registry.test.ts +0 -455
- package/src/wirings/agent/agent-registry.ts +0 -99
- package/src/wirings/agent/agent-resume-authorization.test.ts +0 -206
- package/src/wirings/agent/agent-rpc.ts +0 -116
- package/src/wirings/agent/agent-runner.test.ts +0 -1229
- package/src/wirings/agent/agent-runner.ts +0 -858
- package/src/wirings/agent/agent-stream-delegate.test.ts +0 -381
- package/src/wirings/agent/agent-stream-output-hooks.test.ts +0 -353
- package/src/wirings/agent/agent-stream.test.ts +0 -1800
- package/src/wirings/agent/agent-stream.ts +0 -1639
- package/src/wirings/agent/agent-thread-ownership.test.ts +0 -297
- package/src/wirings/agent/agent-turn.test.ts +0 -65
- package/src/wirings/agent/agent-turn.ts +0 -122
- package/src/wirings/agent/agent-utils.ts +0 -20
- package/src/wirings/agent/agent.types.ts +0 -676
- package/src/wirings/agent/index.ts +0 -58
- package/src/wirings/agent/voice-input.test.ts +0 -220
- package/src/wirings/agent/voice-input.ts +0 -180
- package/src/wirings/agent/voice-output.test.ts +0 -510
- package/src/wirings/agent/voice-output.ts +0 -311
- package/src/wirings/agent-scorer/agent-scorer-grade.test.ts +0 -106
- package/src/wirings/agent-scorer/agent-scorer-grade.ts +0 -55
- package/src/wirings/agent-scorer/agent-scorer-judge.test.ts +0 -285
- package/src/wirings/agent-scorer/agent-scorer-judge.ts +0 -180
- package/src/wirings/agent-scorer/agent-scorer-live.test.ts +0 -174
- package/src/wirings/agent-scorer/agent-scorer-live.ts +0 -56
- package/src/wirings/agent-scorer/agent-scorer-registry.ts +0 -66
- package/src/wirings/agent-scorer/agent-scorer-sampling.test.ts +0 -34
- package/src/wirings/agent-scorer/agent-scorer-sampling.ts +0 -36
- package/src/wirings/agent-scorer/agent-scorer-snapshots.test.ts +0 -49
- package/src/wirings/agent-scorer/agent-scorer-snapshots.ts +0 -46
- package/src/wirings/agent-scorer/agent-scorer-worker.test.ts +0 -122
- package/src/wirings/agent-scorer/agent-scorer-worker.ts +0 -71
- package/src/wirings/agent-scorer/agent-scorer.ts +0 -102
- package/src/wirings/agent-scorer/agent-scorer.types.ts +0 -128
- package/src/wirings/agent-scorer/index.ts +0 -23
- package/src/wirings/channel/channel-common.ts +0 -102
- package/src/wirings/channel/channel-handler-shapes.test.ts +0 -72
- package/src/wirings/channel/channel-handler.test.ts +0 -322
- package/src/wirings/channel/channel-handler.ts +0 -259
- package/src/wirings/channel/channel-host-rpc.test.ts +0 -150
- package/src/wirings/channel/channel-host-rpc.ts +0 -69
- package/src/wirings/channel/channel-middleware-runner.test.ts +0 -188
- package/src/wirings/channel/channel-middleware-runner.ts +0 -126
- package/src/wirings/channel/channel-rpc-registry.ts +0 -116
- package/src/wirings/channel/channel-rpc-responder.ts +0 -117
- package/src/wirings/channel/channel-rpc-service.ts +0 -132
- package/src/wirings/channel/channel-rpc-validators.ts +0 -65
- package/src/wirings/channel/channel-rpc.test.ts +0 -841
- package/src/wirings/channel/channel-rpc.ts +0 -5
- package/src/wirings/channel/channel-rpc.types.ts +0 -160
- package/src/wirings/channel/channel-runner.ts +0 -164
- package/src/wirings/channel/channel-store.ts +0 -35
- package/src/wirings/channel/channel.types.ts +0 -193
- package/src/wirings/channel/define-channel-routes.ts +0 -5
- package/src/wirings/channel/eventhub-service.ts +0 -16
- package/src/wirings/channel/eventhub-store.ts +0 -13
- package/src/wirings/channel/index.ts +0 -40
- package/src/wirings/channel/local/index.ts +0 -3
- package/src/wirings/channel/local/local-channel-handler.ts +0 -98
- package/src/wirings/channel/local/local-channel-runner.test.ts +0 -339
- package/src/wirings/channel/local/local-channel-runner.ts +0 -271
- package/src/wirings/channel/local/local-eventhub-service.test.ts +0 -77
- package/src/wirings/channel/local/local-eventhub-service.ts +0 -69
- package/src/wirings/channel/log-channels.ts +0 -16
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +0 -135
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +0 -60
- package/src/wirings/channel/serverless/index.ts +0 -6
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +0 -328
- package/src/wirings/cli/channel/cli-approval.test.ts +0 -177
- package/src/wirings/cli/channel/cli-approval.ts +0 -135
- package/src/wirings/cli/channel/cli-channel-runner.ts +0 -118
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +0 -192
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +0 -148
- package/src/wirings/cli/channel/cli-raw-client-runner.test.ts +0 -480
- package/src/wirings/cli/channel/cli-raw-client-runner.ts +0 -188
- package/src/wirings/cli/channel/index.ts +0 -6
- package/src/wirings/cli/cli-runner.test.ts +0 -969
- package/src/wirings/cli/cli-runner.ts +0 -538
- package/src/wirings/cli/cli.types.ts +0 -388
- package/src/wirings/cli/command-parser.test.ts +0 -1120
- package/src/wirings/cli/command-parser.ts +0 -713
- package/src/wirings/cli/define-cli-commands.ts +0 -13
- package/src/wirings/cli/format-cli-error.test.ts +0 -91
- package/src/wirings/cli/format-cli-error.ts +0 -96
- package/src/wirings/cli/index.ts +0 -19
- package/src/wirings/credential/credential.types.ts +0 -37
- package/src/wirings/credential/define-credential.ts +0 -49
- package/src/wirings/credential/index.ts +0 -7
- package/src/wirings/credential/validate-credential-definitions.ts +0 -63
- package/src/wirings/gateway/gateway-authorization.test.ts +0 -575
- package/src/wirings/gateway/gateway-channel-meta.test.ts +0 -44
- package/src/wirings/gateway/gateway-runner.test.ts +0 -581
- package/src/wirings/gateway/gateway-runner.ts +0 -411
- package/src/wirings/gateway/gateway.types.ts +0 -137
- package/src/wirings/gateway/index.ts +0 -15
- package/src/wirings/http/http-routes.test.ts +0 -318
- package/src/wirings/http/http-routes.ts +0 -111
- package/src/wirings/http/http-runner-addon-ref.test.ts +0 -238
- package/src/wirings/http/http-runner.test.ts +0 -503
- package/src/wirings/http/http-runner.ts +0 -462
- package/src/wirings/http/http.types.ts +0 -307
- package/src/wirings/http/index.ts +0 -27
- package/src/wirings/http/log-http-routes.ts +0 -18
- package/src/wirings/http/pikku-fetch-http-request.test.ts +0 -318
- package/src/wirings/http/pikku-fetch-http-request.ts +0 -260
- package/src/wirings/http/pikku-fetch-http-response.test.ts +0 -123
- package/src/wirings/http/pikku-fetch-http-response.ts +0 -149
- package/src/wirings/http/routers/http-router.ts +0 -13
- package/src/wirings/http/routers/path-to-regex.test.ts +0 -306
- package/src/wirings/http/routers/path-to-regex.ts +0 -115
- package/src/wirings/http/web-request.test.ts +0 -382
- package/src/wirings/http/web-request.ts +0 -162
- package/src/wirings/mcp/index.ts +0 -26
- package/src/wirings/mcp/mcp-endpoint-registry.test.ts +0 -388
- package/src/wirings/mcp/mcp-endpoint-registry.ts +0 -159
- package/src/wirings/mcp/mcp-runner.test.ts +0 -588
- package/src/wirings/mcp/mcp-runner.ts +0 -330
- package/src/wirings/mcp/mcp.types.ts +0 -199
- package/src/wirings/node/index.ts +0 -2
- package/src/wirings/node/node.types.ts +0 -23
- package/src/wirings/oauth2/index.ts +0 -1
- package/src/wirings/oauth2/oauth2.types.ts +0 -12
- package/src/wirings/persona/define-personas.ts +0 -33
- package/src/wirings/persona/index.ts +0 -75
- package/src/wirings/persona/persona-app-scopes.test.ts +0 -47
- package/src/wirings/persona/persona-app-scopes.ts +0 -74
- package/src/wirings/persona/persona-email.ts +0 -87
- package/src/wirings/persona/persona-environments.test.ts +0 -194
- package/src/wirings/persona/persona-environments.ts +0 -138
- package/src/wirings/persona/persona-mailbox.ts +0 -156
- package/src/wirings/persona/persona.test.ts +0 -230
- package/src/wirings/persona/persona.types.ts +0 -156
- package/src/wirings/persona/validate-personas.ts +0 -137
- package/src/wirings/queue/index.ts +0 -29
- package/src/wirings/queue/queue-identity.test.ts +0 -454
- package/src/wirings/queue/queue-identity.ts +0 -176
- package/src/wirings/queue/queue-runner.test.ts +0 -705
- package/src/wirings/queue/queue-runner.ts +0 -181
- package/src/wirings/queue/queue.types.ts +0 -161
- package/src/wirings/queue/register-queue-helper.ts +0 -46
- package/src/wirings/queue/signed-queue-service.ts +0 -60
- package/src/wirings/queue/validate-worker-config.test.ts +0 -108
- package/src/wirings/queue/validate-worker-config.ts +0 -90
- package/src/wirings/role/define-system-role.ts +0 -33
- package/src/wirings/role/index.ts +0 -13
- package/src/wirings/role/role.test.ts +0 -104
- package/src/wirings/role/role.types.ts +0 -47
- package/src/wirings/role/validate-role-definitions.ts +0 -93
- package/src/wirings/rpc/index.ts +0 -8
- package/src/wirings/rpc/rpc-runner.test.ts +0 -1130
- package/src/wirings/rpc/rpc-runner.ts +0 -569
- package/src/wirings/rpc/rpc-types.ts +0 -87
- package/src/wirings/scheduler/index.ts +0 -11
- package/src/wirings/scheduler/log-schedulers.ts +0 -16
- package/src/wirings/scheduler/scheduler-runner.test.ts +0 -835
- package/src/wirings/scheduler/scheduler-runner.ts +0 -138
- package/src/wirings/scheduler/scheduler.types.ts +0 -42
- package/src/wirings/scope/define-scope.ts +0 -33
- package/src/wirings/scope/index.ts +0 -15
- package/src/wirings/scope/scope.test.ts +0 -159
- package/src/wirings/scope/scope.types.ts +0 -42
- package/src/wirings/scope/validate-scope-definitions.ts +0 -107
- package/src/wirings/secret/index.ts +0 -9
- package/src/wirings/secret/secret.types.ts +0 -70
- package/src/wirings/secret/validate-secret-definitions.test.ts +0 -232
- package/src/wirings/secret/validate-secret-definitions.ts +0 -88
- package/src/wirings/trigger/index.ts +0 -9
- package/src/wirings/trigger/pikku-trigger-service.ts +0 -107
- package/src/wirings/trigger/trigger-runner.test.ts +0 -78
- package/src/wirings/trigger/trigger-runner.ts +0 -113
- package/src/wirings/trigger/trigger.types.ts +0 -100
- package/src/wirings/variable/index.ts +0 -8
- package/src/wirings/variable/validate-variable-definitions.test.ts +0 -115
- package/src/wirings/variable/validate-variable-definitions.ts +0 -71
- package/src/wirings/variable/variable.types.ts +0 -40
- package/src/wirings/virtual-user/index.ts +0 -107
- package/src/wirings/virtual-user/prepare-virtual-user-run.test.ts +0 -115
- package/src/wirings/virtual-user/prepare-virtual-user-run.ts +0 -95
- package/src/wirings/virtual-user/run-virtual-user.test.ts +0 -774
- package/src/wirings/virtual-user/run-virtual-user.ts +0 -684
- package/src/wirings/virtual-user/virtual-user-agents.test.ts +0 -72
- package/src/wirings/virtual-user/virtual-user-agents.ts +0 -62
- package/src/wirings/virtual-user/virtual-user-catalogue.test.ts +0 -215
- package/src/wirings/virtual-user/virtual-user-catalogue.ts +0 -184
- package/src/wirings/virtual-user/virtual-user-derive.test.ts +0 -426
- package/src/wirings/virtual-user/virtual-user-derive.ts +0 -161
- package/src/wirings/virtual-user/virtual-user-dispositions.test.ts +0 -68
- package/src/wirings/virtual-user/virtual-user-dispositions.ts +0 -210
- package/src/wirings/virtual-user/virtual-user-intents.test.ts +0 -218
- package/src/wirings/virtual-user/virtual-user-intents.ts +0 -185
- package/src/wirings/virtual-user/virtual-user-rng.test.ts +0 -72
- package/src/wirings/virtual-user/virtual-user-rng.ts +0 -50
- package/src/wirings/virtual-user/virtual-user-run-store.ts +0 -131
- package/src/wirings/virtual-user/virtual-user-scaffold.test.ts +0 -854
- package/src/wirings/virtual-user/virtual-user-scaffold.ts +0 -664
- package/src/wirings/virtual-user/virtual-user-schedule-store.ts +0 -93
- package/src/wirings/virtual-user/virtual-user-schedule.test.ts +0 -280
- package/src/wirings/virtual-user/virtual-user-schedule.ts +0 -156
- package/src/wirings/virtual-user/virtual-user-target.ts +0 -47
- package/src/wirings/virtual-user/virtual-user.types.ts +0 -215
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +0 -709
- package/src/wirings/workflow/dsl/workflow-runner.ts +0 -25
- package/src/wirings/workflow/feature.test.ts +0 -131
- package/src/wirings/workflow/feature.ts +0 -59
- package/src/wirings/workflow/graph/graph-foreach-queued.test.ts +0 -569
- package/src/wirings/workflow/graph/graph-foreach.test.ts +0 -626
- package/src/wirings/workflow/graph/graph-node.ts +0 -115
- package/src/wirings/workflow/graph/graph-runner.test.ts +0 -1049
- package/src/wirings/workflow/graph/graph-runner.ts +0 -1376
- package/src/wirings/workflow/graph/graph-validation.test.ts +0 -27
- package/src/wirings/workflow/graph/graph-validation.ts +0 -41
- package/src/wirings/workflow/graph/template.test.ts +0 -49
- package/src/wirings/workflow/graph/template.ts +0 -42
- package/src/wirings/workflow/graph/wire-workflow-graph.ts +0 -39
- package/src/wirings/workflow/graph/workflow-graph.types.ts +0 -75
- package/src/wirings/workflow/index.ts +0 -107
- package/src/wirings/workflow/pikku-scenario-service.ts +0 -1013
- package/src/wirings/workflow/pikku-workflow-service.test.ts +0 -963
- package/src/wirings/workflow/pikku-workflow-service.ts +0 -1993
- package/src/wirings/workflow/run-timeline.test.ts +0 -199
- package/src/wirings/workflow/run-timeline.ts +0 -185
- package/src/wirings/workflow/scenario-cookie-jar.test.ts +0 -107
- package/src/wirings/workflow/scenario-cookie-jar.ts +0 -57
- package/src/wirings/workflow/scenario-expectations.test.ts +0 -238
- package/src/wirings/workflow/scenario-hooks.test.ts +0 -259
- package/src/wirings/workflow/scenario-poll.test.ts +0 -64
- package/src/wirings/workflow/scenario-poll.ts +0 -27
- package/src/wirings/workflow/scenario-prose.test.ts +0 -275
- package/src/wirings/workflow/scenario-prose.ts +0 -86
- package/src/wirings/workflow/scenario-run.types.ts +0 -193
- package/src/wirings/workflow/scenario-service.test.ts +0 -147
- package/src/wirings/workflow/scenario-step-guards.ts +0 -23
- package/src/wirings/workflow/scenario-step.test.ts +0 -800
- package/src/wirings/workflow/scenario-step.types.ts +0 -262
- package/src/wirings/workflow/scenario-surface.test.ts +0 -146
- package/src/wirings/workflow/scenario-surface.ts +0 -71
- package/src/wirings/workflow/scenario.types.ts +0 -63
- package/src/wirings/workflow/workflow-approval-audit.ts +0 -47
- package/src/wirings/workflow/workflow-approval-policy.test.ts +0 -594
- package/src/wirings/workflow/workflow-approval-policy.ts +0 -68
- package/src/wirings/workflow/workflow-approval.ts +0 -290
- package/src/wirings/workflow/workflow-child-run-session.test.ts +0 -168
- package/src/wirings/workflow/workflow-constants.ts +0 -66
- package/src/wirings/workflow/workflow-dispatch-durability.test.ts +0 -116
- package/src/wirings/workflow/workflow-dispatch-payload.test.ts +0 -55
- package/src/wirings/workflow/workflow-dispatch-relay.test.ts +0 -128
- package/src/wirings/workflow/workflow-errors.ts +0 -132
- package/src/wirings/workflow/workflow-inline-authority.test.ts +0 -175
- package/src/wirings/workflow/workflow-invocation-id.test.ts +0 -51
- package/src/wirings/workflow/workflow-invocation-id.ts +0 -28
- package/src/wirings/workflow/workflow-meta-resolver.ts +0 -45
- package/src/wirings/workflow/workflow-mirror.test.ts +0 -171
- package/src/wirings/workflow/workflow-missing-meta.test.ts +0 -92
- package/src/wirings/workflow/workflow-on-error.test.ts +0 -145
- package/src/wirings/workflow/workflow-queue-routing.ts +0 -129
- package/src/wirings/workflow/workflow-queue-wiring.ts +0 -149
- package/src/wirings/workflow/workflow-queue-workers.test.ts +0 -131
- package/src/wirings/workflow/workflow-queue-workers.ts +0 -66
- package/src/wirings/workflow/workflow-recovery.ts +0 -192
- package/src/wirings/workflow/workflow-replay-snapshot.test.ts +0 -140
- package/src/wirings/workflow/workflow-retry-policy.test.ts +0 -113
- package/src/wirings/workflow/workflow-run-authority.test.ts +0 -212
- package/src/wirings/workflow/workflow-run-context.test.ts +0 -172
- package/src/wirings/workflow/workflow-run-engine.types.ts +0 -92
- package/src/wirings/workflow/workflow-run-ownership.ts +0 -37
- package/src/wirings/workflow/workflow-run-polling.test.ts +0 -127
- package/src/wirings/workflow/workflow-stalled-recovery.test.ts +0 -152
- package/src/wirings/workflow/workflow-status-stream.test.ts +0 -354
- package/src/wirings/workflow/workflow-status-stream.ts +0 -144
- package/src/wirings/workflow/workflow-step-claim.ts +0 -46
- package/src/wirings/workflow/workflow-step-ordinal.test.ts +0 -94
- package/src/wirings/workflow/workflow-step-session.test.ts +0 -87
- package/src/wirings/workflow/workflow-suspend.ts +0 -61
- package/src/wirings/workflow/workflow-terminal-run-guard.test.ts +0 -105
- package/src/wirings/workflow/workflow.types.ts +0 -268
- package/src/wirings-stay-decoupled.test.ts +0 -130
- package/tsconfig.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.type-tests.json +0 -15
|
@@ -1,1376 +0,0 @@
|
|
|
1
|
-
import type { PikkuWorkflowService } from '../pikku-workflow-service.js'
|
|
2
|
-
import {
|
|
3
|
-
WorkflowAsyncException,
|
|
4
|
-
WorkflowSuspendedException,
|
|
5
|
-
} from '../workflow-errors.js'
|
|
6
|
-
import { DEFAULT_STEP_RETRIES } from '../workflow-constants.js'
|
|
7
|
-
import type { GraphWireState, PikkuGraphWire } from './workflow-graph.types.js'
|
|
8
|
-
import { pikkuState, getSingletonServices } from '../../../pikku-state.js'
|
|
9
|
-
import type { WorkflowRuntimeMeta, WorkflowRunWire } from '../workflow.types.js'
|
|
10
|
-
import { RPCNotFoundError } from '../../rpc/rpc-runner.js'
|
|
11
|
-
|
|
12
|
-
export class ChildWorkflowStartedException extends Error {
|
|
13
|
-
name = 'ChildWorkflowStartedException'
|
|
14
|
-
constructor(
|
|
15
|
-
public parentRunId: string,
|
|
16
|
-
public stepId: string,
|
|
17
|
-
public childRunId: string
|
|
18
|
-
) {
|
|
19
|
-
super(`Child workflow started: ${childRunId}`)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function buildTemplateRegex(nodeId: string): RegExp | null {
|
|
24
|
-
if (!nodeId.includes('${')) return null
|
|
25
|
-
const escaped = nodeId
|
|
26
|
-
.split(/\$\{[^}]+\}/)
|
|
27
|
-
.map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
28
|
-
.join('.+')
|
|
29
|
-
return new RegExp(`^${escaped}$`)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const FANOUT_INSTANCE = /^(.+)\[(\d+)\]$/
|
|
33
|
-
|
|
34
|
-
function fanoutInstanceKey(base: string, index: number): string {
|
|
35
|
-
return `${base}[${index}]`
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function splitFanoutInstance(
|
|
39
|
-
name: string
|
|
40
|
-
): { base: string; index: number } | null {
|
|
41
|
-
const match = FANOUT_INSTANCE.exec(name)
|
|
42
|
-
if (!match) return null
|
|
43
|
-
return { base: match[1]!, index: Number(match[2]!) }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function stripInstanceOrdinal(name: string): string {
|
|
47
|
-
const fanout = splitFanoutInstance(name)
|
|
48
|
-
const withoutItem = fanout ? fanout.base : name
|
|
49
|
-
const hash = withoutItem.lastIndexOf('#')
|
|
50
|
-
if (hash <= 0) return withoutItem
|
|
51
|
-
return /^\d+$/.test(withoutItem.slice(hash + 1))
|
|
52
|
-
? withoutItem.slice(0, hash)
|
|
53
|
-
: withoutItem
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function remapStepNamesToNodeIds(
|
|
57
|
-
stepNames: string[],
|
|
58
|
-
nodes: Record<string, any>,
|
|
59
|
-
graphName: string
|
|
60
|
-
): string[] {
|
|
61
|
-
const templatePatterns = new Map<string, RegExp>()
|
|
62
|
-
for (const nodeId of Object.keys(nodes)) {
|
|
63
|
-
const regex = buildTemplateRegex(nodeId)
|
|
64
|
-
if (regex) templatePatterns.set(nodeId, regex)
|
|
65
|
-
}
|
|
66
|
-
return stepNames.map((name) => {
|
|
67
|
-
if (nodes[name]) return name
|
|
68
|
-
const base = stripInstanceOrdinal(name)
|
|
69
|
-
if (base !== name && nodes[base]) return base
|
|
70
|
-
const matches: string[] = []
|
|
71
|
-
for (const [nodeId, regex] of templatePatterns) {
|
|
72
|
-
if (regex.test(base)) matches.push(nodeId)
|
|
73
|
-
}
|
|
74
|
-
if (matches.length > 1) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
`Workflow graph '${graphName}': ambiguous template node match for '${name}' (${matches.join(', ')})`
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
if (matches.length === 1) {
|
|
80
|
-
return matches[0]!
|
|
81
|
-
}
|
|
82
|
-
return name
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const ENTRY_FROM = '__entry__'
|
|
87
|
-
|
|
88
|
-
function closesCycle(
|
|
89
|
-
source: string,
|
|
90
|
-
target: string,
|
|
91
|
-
nodes: Record<string, any>
|
|
92
|
-
): boolean {
|
|
93
|
-
const seen = new Set<string>()
|
|
94
|
-
const stack = [target]
|
|
95
|
-
while (stack.length) {
|
|
96
|
-
const cur = stack.pop()!
|
|
97
|
-
if (cur === source) return true
|
|
98
|
-
if (seen.has(cur)) continue
|
|
99
|
-
seen.add(cur)
|
|
100
|
-
for (const next of normalizeNodeTargets(nodes[cur]?.next)) stack.push(next)
|
|
101
|
-
}
|
|
102
|
-
return false
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
interface GraphFireInstruction {
|
|
106
|
-
logical: string
|
|
107
|
-
instanceKey: string
|
|
108
|
-
fromStepName?: string
|
|
109
|
-
itemIndex?: number
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function planGraphTransitions(
|
|
113
|
-
nodes: Record<string, any>,
|
|
114
|
-
instances: Array<{ stepName: string; status: string; fromStepName?: string }>,
|
|
115
|
-
branchByStep: Record<string, string>,
|
|
116
|
-
entryNodeIds: string[],
|
|
117
|
-
graphName: string,
|
|
118
|
-
fanoutWidths: Record<string, number> = {}
|
|
119
|
-
): {
|
|
120
|
-
toFire: GraphFireInstruction[]
|
|
121
|
-
hasInFlight: boolean
|
|
122
|
-
blockedWaiting: boolean
|
|
123
|
-
} {
|
|
124
|
-
const toLogical = (name: string) =>
|
|
125
|
-
remapStepNamesToNodeIds([name], nodes, graphName)[0]!
|
|
126
|
-
|
|
127
|
-
const countByLogical: Record<string, number> = {}
|
|
128
|
-
const instancesByLogical: Record<
|
|
129
|
-
string,
|
|
130
|
-
Array<{ stepName: string; status: string }>
|
|
131
|
-
> = {}
|
|
132
|
-
const consumed = new Set<string>()
|
|
133
|
-
for (const inst of instances) {
|
|
134
|
-
const logical = toLogical(inst.stepName)
|
|
135
|
-
countByLogical[logical] = (countByLogical[logical] ?? 0) + 1
|
|
136
|
-
;(instancesByLogical[logical] ??= []).push(inst)
|
|
137
|
-
consumed.add(`${inst.fromStepName ?? ENTRY_FROM}->${logical}`)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const isFanned = (nodeId: string) => Boolean(nodes[nodeId]?.forEach)
|
|
141
|
-
const fanoutComplete = (nodeId: string) => {
|
|
142
|
-
const width = fanoutWidths[nodeId]
|
|
143
|
-
if (width === undefined) return false
|
|
144
|
-
const succeeded = (instancesByLogical[nodeId] ?? []).filter(
|
|
145
|
-
(i) => i.status === 'succeeded'
|
|
146
|
-
).length
|
|
147
|
-
return succeeded >= width
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const completed = instances.filter((i) => i.status === 'succeeded')
|
|
151
|
-
const completedLogical = new Set<string>()
|
|
152
|
-
for (const inst of completed) {
|
|
153
|
-
const logical = toLogical(inst.stepName)
|
|
154
|
-
if (!isFanned(logical)) completedLogical.add(logical)
|
|
155
|
-
}
|
|
156
|
-
for (const nodeId of Object.keys(fanoutWidths)) {
|
|
157
|
-
if (isFanned(nodeId) && fanoutComplete(nodeId)) completedLogical.add(nodeId)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const edges: Array<{
|
|
161
|
-
from?: string
|
|
162
|
-
fromKey: string
|
|
163
|
-
fromLogical?: string
|
|
164
|
-
target: string
|
|
165
|
-
}> = []
|
|
166
|
-
for (const entryId of entryNodeIds) {
|
|
167
|
-
edges.push({ fromKey: ENTRY_FROM, target: entryId })
|
|
168
|
-
}
|
|
169
|
-
const fannedEdgesEmitted = new Set<string>()
|
|
170
|
-
for (const inst of completed) {
|
|
171
|
-
const fromLogical = toLogical(inst.stepName)
|
|
172
|
-
const node = nodes[fromLogical]
|
|
173
|
-
if (!node?.next) continue
|
|
174
|
-
let from = inst.stepName
|
|
175
|
-
let branchKey = branchByStep[inst.stepName]
|
|
176
|
-
if (isFanned(fromLogical)) {
|
|
177
|
-
if (!completedLogical.has(fromLogical)) continue
|
|
178
|
-
if (fannedEdgesEmitted.has(fromLogical)) continue
|
|
179
|
-
fannedEdgesEmitted.add(fromLogical)
|
|
180
|
-
from = fromLogical
|
|
181
|
-
branchKey = branchByStep[fromLogical] ?? branchByStep[inst.stepName]
|
|
182
|
-
}
|
|
183
|
-
for (const target of resolveNextFromConfig(node.next, branchKey)) {
|
|
184
|
-
edges.push({ from, fromKey: from, fromLogical, target })
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// A zero-width fanout produces no instances at all, so its outgoing edges
|
|
188
|
-
// have no completed instance to hang off — emit them from the logical node.
|
|
189
|
-
for (const [nodeId, width] of Object.entries(fanoutWidths)) {
|
|
190
|
-
if (width !== 0 || fannedEdgesEmitted.has(nodeId)) continue
|
|
191
|
-
const node = nodes[nodeId]
|
|
192
|
-
if (!node?.next) continue
|
|
193
|
-
fannedEdgesEmitted.add(nodeId)
|
|
194
|
-
for (const target of resolveNextFromConfig(
|
|
195
|
-
node.next,
|
|
196
|
-
branchByStep[nodeId]
|
|
197
|
-
)) {
|
|
198
|
-
edges.push({ from: nodeId, fromKey: nodeId, fromLogical: nodeId, target })
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const toFire: GraphFireInstruction[] = []
|
|
203
|
-
const plannedFanout = new Set<string>()
|
|
204
|
-
let blockedWaiting = false
|
|
205
|
-
for (const edge of edges) {
|
|
206
|
-
const target = edge.target
|
|
207
|
-
const edgeKey = `${edge.fromKey}->${target}`
|
|
208
|
-
|
|
209
|
-
if (isFanned(target)) {
|
|
210
|
-
if (plannedFanout.has(target)) continue
|
|
211
|
-
plannedFanout.add(target)
|
|
212
|
-
if (!areDependenciesSatisfied(nodes[target] ?? {}, completedLogical)) {
|
|
213
|
-
blockedWaiting = true
|
|
214
|
-
continue
|
|
215
|
-
}
|
|
216
|
-
const width = fanoutWidths[target]
|
|
217
|
-
if (width === undefined) {
|
|
218
|
-
blockedWaiting = true
|
|
219
|
-
continue
|
|
220
|
-
}
|
|
221
|
-
const existing = instancesByLogical[target] ?? []
|
|
222
|
-
const existingIndexes = new Set(
|
|
223
|
-
existing
|
|
224
|
-
.map((i) => splitFanoutInstance(i.stepName)?.index)
|
|
225
|
-
.filter((index): index is number => index !== undefined)
|
|
226
|
-
)
|
|
227
|
-
const pending: number[] = []
|
|
228
|
-
for (let index = 0; index < width; index++) {
|
|
229
|
-
if (!existingIndexes.has(index)) pending.push(index)
|
|
230
|
-
}
|
|
231
|
-
if (pending.length === 0) continue
|
|
232
|
-
const sequential = nodes[target]?.mode === 'sequential'
|
|
233
|
-
const anyInFlight = existing.some((i) => i.status !== 'succeeded')
|
|
234
|
-
const emit = sequential
|
|
235
|
-
? anyInFlight
|
|
236
|
-
? []
|
|
237
|
-
: pending.slice(0, 1)
|
|
238
|
-
: pending
|
|
239
|
-
for (const index of emit) {
|
|
240
|
-
toFire.push({
|
|
241
|
-
logical: target,
|
|
242
|
-
instanceKey: fanoutInstanceKey(target, index),
|
|
243
|
-
fromStepName: edge.from,
|
|
244
|
-
itemIndex: index,
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
if (emit.length === 0) blockedWaiting = true
|
|
248
|
-
continue
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (consumed.has(edgeKey)) continue
|
|
252
|
-
const visits = countByLogical[target] ?? 0
|
|
253
|
-
const isBackEdge =
|
|
254
|
-
edge.fromLogical !== undefined &&
|
|
255
|
-
closesCycle(edge.fromLogical, target, nodes)
|
|
256
|
-
if (!isBackEdge && visits > 0) {
|
|
257
|
-
consumed.add(edgeKey)
|
|
258
|
-
continue
|
|
259
|
-
}
|
|
260
|
-
if (!areDependenciesSatisfied(nodes[target] ?? {}, completedLogical)) {
|
|
261
|
-
blockedWaiting = true
|
|
262
|
-
continue
|
|
263
|
-
}
|
|
264
|
-
toFire.push({
|
|
265
|
-
logical: target,
|
|
266
|
-
instanceKey: visits === 0 ? target : `${target}#${visits}`,
|
|
267
|
-
fromStepName: edge.from,
|
|
268
|
-
})
|
|
269
|
-
countByLogical[target] = visits + 1
|
|
270
|
-
consumed.add(edgeKey)
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
toFire,
|
|
275
|
-
hasInFlight: instances.some((i) => i.status !== 'succeeded'),
|
|
276
|
-
blockedWaiting,
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function remapBranchKeys(
|
|
281
|
-
branchKeys: Record<string, string>,
|
|
282
|
-
nodes: Record<string, any>,
|
|
283
|
-
graphName: string
|
|
284
|
-
): Record<string, string> {
|
|
285
|
-
const templatePatterns = new Map<string, RegExp>()
|
|
286
|
-
for (const nodeId of Object.keys(nodes)) {
|
|
287
|
-
const regex = buildTemplateRegex(nodeId)
|
|
288
|
-
if (regex) templatePatterns.set(nodeId, regex)
|
|
289
|
-
}
|
|
290
|
-
if (templatePatterns.size === 0) return branchKeys
|
|
291
|
-
const remapped: Record<string, string> = {}
|
|
292
|
-
for (const [key, value] of Object.entries(branchKeys)) {
|
|
293
|
-
let mappedKey = key
|
|
294
|
-
if (!nodes[key]) {
|
|
295
|
-
const matches: string[] = []
|
|
296
|
-
for (const [nodeId, regex] of templatePatterns) {
|
|
297
|
-
if (regex.test(key)) matches.push(nodeId)
|
|
298
|
-
}
|
|
299
|
-
if (matches.length > 1) {
|
|
300
|
-
throw new Error(
|
|
301
|
-
`Workflow graph '${graphName}': ambiguous template branch key match for '${key}' (${matches.join(', ')})`
|
|
302
|
-
)
|
|
303
|
-
}
|
|
304
|
-
if (matches.length === 1) {
|
|
305
|
-
mappedKey = matches[0]!
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
remapped[mappedKey] = value
|
|
309
|
-
}
|
|
310
|
-
return remapped
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function isDataRef(value: unknown): value is { $ref: string; path?: string } {
|
|
314
|
-
return (
|
|
315
|
-
typeof value === 'object' &&
|
|
316
|
-
value !== null &&
|
|
317
|
-
'$ref' in value &&
|
|
318
|
-
typeof (value as { $ref?: unknown }).$ref === 'string'
|
|
319
|
-
)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
interface TemplateValue {
|
|
323
|
-
$template: {
|
|
324
|
-
parts: string[]
|
|
325
|
-
expressions: Array<{ $ref: string; path?: string }>
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function isTemplate(value: unknown): value is TemplateValue {
|
|
330
|
-
return (
|
|
331
|
-
typeof value === 'object' &&
|
|
332
|
-
value !== null &&
|
|
333
|
-
'$template' in value &&
|
|
334
|
-
typeof (value as { $template?: unknown }).$template === 'object'
|
|
335
|
-
)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function getWorkflowMeta(name: string): WorkflowRuntimeMeta | undefined {
|
|
339
|
-
const rootMeta = pikkuState(null, 'workflows', 'meta')
|
|
340
|
-
if (rootMeta[name]) return rootMeta[name]
|
|
341
|
-
|
|
342
|
-
const colonIndex = name.indexOf(':')
|
|
343
|
-
if (colonIndex !== -1) {
|
|
344
|
-
const namespace = name.substring(0, colonIndex)
|
|
345
|
-
const localName = name.substring(colonIndex + 1)
|
|
346
|
-
const addons = pikkuState(null, 'addons', 'packages')
|
|
347
|
-
const pkgConfig = addons?.get(namespace)
|
|
348
|
-
if (pkgConfig) {
|
|
349
|
-
const addonMeta = pikkuState(pkgConfig.package, 'workflows', 'meta')
|
|
350
|
-
if (addonMeta?.[localName]) return addonMeta[localName]
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return undefined
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function resolveNextFromConfig(next: unknown, branchKey?: string): string[] {
|
|
358
|
-
if (!next) return []
|
|
359
|
-
|
|
360
|
-
if (typeof next === 'string') return [next]
|
|
361
|
-
if (Array.isArray(next)) return next
|
|
362
|
-
|
|
363
|
-
if (typeof next === 'object' && next !== null) {
|
|
364
|
-
if (!branchKey || !(branchKey in next)) return []
|
|
365
|
-
const branchNext = (next as Record<string, string | string[]>)[branchKey]!
|
|
366
|
-
return Array.isArray(branchNext) ? branchNext : [branchNext]
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return []
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function getValueAtPath(obj: any, path: string): any {
|
|
373
|
-
if (!path) return obj
|
|
374
|
-
const parts = path.split('.')
|
|
375
|
-
let current = obj
|
|
376
|
-
for (const part of parts) {
|
|
377
|
-
if (current == null) return undefined
|
|
378
|
-
current = current[part]
|
|
379
|
-
}
|
|
380
|
-
return current
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function resolveTemplate(
|
|
384
|
-
template: TemplateValue,
|
|
385
|
-
nodeResults: Record<string, any>
|
|
386
|
-
): string {
|
|
387
|
-
const { parts, expressions } = template.$template
|
|
388
|
-
let result = ''
|
|
389
|
-
for (let i = 0; i < parts.length; i++) {
|
|
390
|
-
result += parts[i]
|
|
391
|
-
if (i < expressions.length) {
|
|
392
|
-
const expr = expressions[i]!
|
|
393
|
-
const nodeResult = nodeResults[expr.$ref]
|
|
394
|
-
const value = expr.path
|
|
395
|
-
? getValueAtPath(nodeResult, expr.path)
|
|
396
|
-
: nodeResult
|
|
397
|
-
result += String(value ?? '')
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return result
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function resolveValue(value: unknown, nodeResults: Record<string, any>): any {
|
|
404
|
-
if (isDataRef(value)) {
|
|
405
|
-
if (value.$ref === '$item' && !('$item' in nodeResults)) {
|
|
406
|
-
return value
|
|
407
|
-
}
|
|
408
|
-
const source = nodeResults[value.$ref]
|
|
409
|
-
return value.path ? getValueAtPath(source, value.path) : source
|
|
410
|
-
}
|
|
411
|
-
if (isTemplate(value)) {
|
|
412
|
-
return resolveTemplate(value, nodeResults)
|
|
413
|
-
}
|
|
414
|
-
if (Array.isArray(value)) {
|
|
415
|
-
return value.map((item) => resolveValue(item, nodeResults))
|
|
416
|
-
}
|
|
417
|
-
if (typeof value === 'object' && value !== null) {
|
|
418
|
-
const resolved: Record<string, any> = {}
|
|
419
|
-
for (const [k, v] of Object.entries(value)) {
|
|
420
|
-
resolved[k] = resolveValue(v, nodeResults)
|
|
421
|
-
}
|
|
422
|
-
return resolved
|
|
423
|
-
}
|
|
424
|
-
return value
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function resolveSerializedInput(
|
|
428
|
-
input: Record<string, unknown> | undefined,
|
|
429
|
-
nodeResults: Record<string, any>
|
|
430
|
-
): Record<string, any> {
|
|
431
|
-
if (!input || Object.keys(input).length === 0) return {}
|
|
432
|
-
|
|
433
|
-
const resolved: Record<string, any> = {}
|
|
434
|
-
for (const [key, value] of Object.entries(input)) {
|
|
435
|
-
resolved[key] = resolveValue(value, nodeResults)
|
|
436
|
-
}
|
|
437
|
-
return resolved
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function collectReferencedNodeIds(value: unknown, nodeIds: string[]): void {
|
|
441
|
-
if (isDataRef(value)) {
|
|
442
|
-
nodeIds.push(value.$ref)
|
|
443
|
-
} else if (isTemplate(value)) {
|
|
444
|
-
for (const expr of value.$template.expressions) {
|
|
445
|
-
nodeIds.push(expr.$ref)
|
|
446
|
-
}
|
|
447
|
-
} else if (Array.isArray(value)) {
|
|
448
|
-
for (const item of value) {
|
|
449
|
-
collectReferencedNodeIds(item, nodeIds)
|
|
450
|
-
}
|
|
451
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
452
|
-
for (const v of Object.values(value)) {
|
|
453
|
-
collectReferencedNodeIds(v, nodeIds)
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function extractReferencedNodeIds(
|
|
459
|
-
input: Record<string, unknown> | undefined
|
|
460
|
-
): string[] {
|
|
461
|
-
if (!input) return []
|
|
462
|
-
const nodeIds: string[] = []
|
|
463
|
-
for (const value of Object.values(input)) {
|
|
464
|
-
collectReferencedNodeIds(value, nodeIds)
|
|
465
|
-
}
|
|
466
|
-
return [...new Set(nodeIds)]
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const IGNORED_REFS = new Set(['trigger', '$item', 'unknown'])
|
|
470
|
-
|
|
471
|
-
const FOREACH_MODES = new Set(['parallel', 'sequential'])
|
|
472
|
-
|
|
473
|
-
function normalizeNodeTargets(value: unknown): string[] {
|
|
474
|
-
if (!value) return []
|
|
475
|
-
if (typeof value === 'string') return [value]
|
|
476
|
-
if (Array.isArray(value)) return value.filter((v) => typeof v === 'string')
|
|
477
|
-
if (typeof value === 'object') {
|
|
478
|
-
const targets: string[] = []
|
|
479
|
-
for (const branchTarget of Object.values(
|
|
480
|
-
value as Record<string, unknown>
|
|
481
|
-
)) {
|
|
482
|
-
targets.push(...normalizeNodeTargets(branchTarget))
|
|
483
|
-
}
|
|
484
|
-
return targets
|
|
485
|
-
}
|
|
486
|
-
return []
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function validateGraphReferences(
|
|
490
|
-
graphName: string,
|
|
491
|
-
nodes: Record<string, any>,
|
|
492
|
-
entryNodes: string[]
|
|
493
|
-
): void {
|
|
494
|
-
const nodeIds = new Set(Object.keys(nodes))
|
|
495
|
-
|
|
496
|
-
for (const entryId of entryNodes) {
|
|
497
|
-
if (!nodeIds.has(entryId)) {
|
|
498
|
-
throw new Error(
|
|
499
|
-
`Workflow graph '${graphName}': entry node '${entryId}' is not defined`
|
|
500
|
-
)
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
505
|
-
const inputRefs = extractReferencedNodeIds(node.input).filter(
|
|
506
|
-
(id) => !IGNORED_REFS.has(id)
|
|
507
|
-
)
|
|
508
|
-
for (const refId of inputRefs) {
|
|
509
|
-
if (!nodeIds.has(refId)) {
|
|
510
|
-
throw new Error(
|
|
511
|
-
`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${refId}' in input`
|
|
512
|
-
)
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (node.forEach !== undefined) {
|
|
517
|
-
const source = forEachSource(node)
|
|
518
|
-
if (!source) {
|
|
519
|
-
throw new Error(
|
|
520
|
-
`Workflow graph '${graphName}': node '${nodeId}' forEach must reference a node`
|
|
521
|
-
)
|
|
522
|
-
}
|
|
523
|
-
if (!IGNORED_REFS.has(source.$ref) && !nodeIds.has(source.$ref)) {
|
|
524
|
-
throw new Error(
|
|
525
|
-
`Workflow graph '${graphName}': node '${nodeId}' references unknown node '${source.$ref}' in forEach`
|
|
526
|
-
)
|
|
527
|
-
}
|
|
528
|
-
if (node.mode !== undefined && !FOREACH_MODES.has(node.mode)) {
|
|
529
|
-
throw new Error(
|
|
530
|
-
`Workflow graph '${graphName}': node '${nodeId}' has an unknown forEach mode '${node.mode}'`
|
|
531
|
-
)
|
|
532
|
-
}
|
|
533
|
-
} else if (node.mode !== undefined) {
|
|
534
|
-
throw new Error(
|
|
535
|
-
`Workflow graph '${graphName}': node '${nodeId}' sets mode '${node.mode}' without a forEach`
|
|
536
|
-
)
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const nextTargets = normalizeNodeTargets(node.next)
|
|
540
|
-
for (const nextId of nextTargets) {
|
|
541
|
-
if (!nodeIds.has(nextId)) {
|
|
542
|
-
throw new Error(
|
|
543
|
-
`Workflow graph '${graphName}': node '${nodeId}' routes to unknown node '${nextId}'`
|
|
544
|
-
)
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
const errorTargets = normalizeNodeTargets(node.onError)
|
|
549
|
-
for (const errorId of errorTargets) {
|
|
550
|
-
if (!nodeIds.has(errorId)) {
|
|
551
|
-
throw new Error(
|
|
552
|
-
`Workflow graph '${graphName}': node '${nodeId}' onError targets unknown node '${errorId}'`
|
|
553
|
-
)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
function forEachSource(node: {
|
|
560
|
-
forEach?: unknown
|
|
561
|
-
}): { $ref: string; path?: string } | undefined {
|
|
562
|
-
return isDataRef(node.forEach) ? node.forEach : undefined
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
function nodeDependencies(node: {
|
|
566
|
-
input?: Record<string, unknown>
|
|
567
|
-
forEach?: unknown
|
|
568
|
-
}): string[] {
|
|
569
|
-
const deps = extractReferencedNodeIds(node.input)
|
|
570
|
-
const source = forEachSource(node)
|
|
571
|
-
if (source) deps.push(source.$ref)
|
|
572
|
-
return [...new Set(deps)].filter((id) => !IGNORED_REFS.has(id))
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function describeValue(value: unknown): string {
|
|
576
|
-
if (value === null) return 'null'
|
|
577
|
-
if (value === undefined) return 'undefined'
|
|
578
|
-
return typeof value
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
function resolveForEachItems(
|
|
582
|
-
graphName: string,
|
|
583
|
-
nodeId: string,
|
|
584
|
-
node: { forEach?: unknown },
|
|
585
|
-
nodeResults: Record<string, any>
|
|
586
|
-
): unknown[] {
|
|
587
|
-
const source = forEachSource(node)
|
|
588
|
-
if (!source) {
|
|
589
|
-
throw new Error(
|
|
590
|
-
`Workflow graph '${graphName}': node '${nodeId}' has an unusable forEach configuration`
|
|
591
|
-
)
|
|
592
|
-
}
|
|
593
|
-
const raw = nodeResults[source.$ref]
|
|
594
|
-
const value = source.path ? getValueAtPath(raw, source.path) : raw
|
|
595
|
-
if (!Array.isArray(value)) {
|
|
596
|
-
const label = source.path ? `${source.$ref}.${source.path}` : source.$ref
|
|
597
|
-
throw new Error(
|
|
598
|
-
`Workflow graph '${graphName}': node '${nodeId}' forEach source '${label}' resolved to ${describeValue(
|
|
599
|
-
value
|
|
600
|
-
)}, expected an array`
|
|
601
|
-
)
|
|
602
|
-
}
|
|
603
|
-
return value
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
function areDependenciesSatisfied(
|
|
607
|
-
node: { input?: Record<string, unknown>; forEach?: unknown },
|
|
608
|
-
completedNodeIds: Set<string>
|
|
609
|
-
): boolean {
|
|
610
|
-
return nodeDependencies(node).every((dep) => completedNodeIds.has(dep))
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
interface GraphResultReader {
|
|
614
|
-
/** Per-item source arrays, keyed by fanned node id, for every ready fanout. */
|
|
615
|
-
fanoutItems: Record<string, unknown[]>
|
|
616
|
-
fanoutWidths: Record<string, number>
|
|
617
|
-
/** Reads node results, aggregating a fanned node into its ordered item array. */
|
|
618
|
-
read: (nodeIds: string[]) => Promise<Record<string, any>>
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
async function createGraphResultReader(
|
|
622
|
-
workflowService: PikkuWorkflowService,
|
|
623
|
-
runId: string,
|
|
624
|
-
graphName: string,
|
|
625
|
-
nodes: Record<string, any>,
|
|
626
|
-
triggerInput: any,
|
|
627
|
-
instances: Array<{ stepName: string; status: string }>
|
|
628
|
-
): Promise<GraphResultReader> {
|
|
629
|
-
const succeeded = new Set(
|
|
630
|
-
instances.filter((i) => i.status === 'succeeded').map((i) => i.stepName)
|
|
631
|
-
)
|
|
632
|
-
const fanoutItems: Record<string, unknown[]> = {}
|
|
633
|
-
const fanoutWidths: Record<string, number> = {}
|
|
634
|
-
const resultCache = new Map<string, any>()
|
|
635
|
-
const resolving = new Set<string>()
|
|
636
|
-
|
|
637
|
-
const readResult = async (nodeId: string): Promise<any> => {
|
|
638
|
-
if (nodeId === 'trigger') return triggerInput
|
|
639
|
-
if (resultCache.has(nodeId)) return resultCache.get(nodeId)
|
|
640
|
-
if (resolving.has(nodeId)) return undefined
|
|
641
|
-
resolving.add(nodeId)
|
|
642
|
-
try {
|
|
643
|
-
let value: any
|
|
644
|
-
if (nodes[nodeId]?.forEach) {
|
|
645
|
-
const items = await readFanoutItems(nodeId)
|
|
646
|
-
if (items === undefined) return undefined
|
|
647
|
-
const keys = items.map((_, index) => fanoutInstanceKey(nodeId, index))
|
|
648
|
-
if (keys.some((key) => !succeeded.has(key))) return undefined
|
|
649
|
-
const fetched = keys.length
|
|
650
|
-
? await workflowService.getNodeResults(runId, keys)
|
|
651
|
-
: {}
|
|
652
|
-
value = keys.map((key) => fetched[key])
|
|
653
|
-
} else {
|
|
654
|
-
if (!succeeded.has(nodeId)) return undefined
|
|
655
|
-
const fetched = await workflowService.getNodeResults(runId, [nodeId])
|
|
656
|
-
value = fetched[nodeId]
|
|
657
|
-
}
|
|
658
|
-
resultCache.set(nodeId, value)
|
|
659
|
-
return value
|
|
660
|
-
} finally {
|
|
661
|
-
resolving.delete(nodeId)
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
const readFanoutItems = async (
|
|
666
|
-
nodeId: string
|
|
667
|
-
): Promise<unknown[] | undefined> => {
|
|
668
|
-
if (fanoutItems[nodeId]) return fanoutItems[nodeId]
|
|
669
|
-
const node = nodes[nodeId]
|
|
670
|
-
const source = forEachSource(node)
|
|
671
|
-
if (!source) return undefined
|
|
672
|
-
const sourceValue = await readResult(source.$ref)
|
|
673
|
-
if (sourceValue === undefined) return undefined
|
|
674
|
-
const items = resolveForEachItems(graphName, nodeId, node, {
|
|
675
|
-
[source.$ref]: sourceValue,
|
|
676
|
-
})
|
|
677
|
-
fanoutItems[nodeId] = items
|
|
678
|
-
fanoutWidths[nodeId] = items.length
|
|
679
|
-
return items
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
for (const nodeId of Object.keys(nodes)) {
|
|
683
|
-
if (nodes[nodeId]?.forEach) await readFanoutItems(nodeId)
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
return {
|
|
687
|
-
fanoutItems,
|
|
688
|
-
fanoutWidths,
|
|
689
|
-
read: async (nodeIds: string[]) => {
|
|
690
|
-
const results: Record<string, any> = {}
|
|
691
|
-
for (const nodeId of nodeIds) {
|
|
692
|
-
const value = await readResult(nodeId)
|
|
693
|
-
if (value !== undefined) results[nodeId] = value
|
|
694
|
-
}
|
|
695
|
-
return results
|
|
696
|
-
},
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
async function queueGraphNode(
|
|
701
|
-
workflowService: PikkuWorkflowService,
|
|
702
|
-
runId: string,
|
|
703
|
-
_graphName: string,
|
|
704
|
-
nodeId: string,
|
|
705
|
-
rpcName: string,
|
|
706
|
-
input: any,
|
|
707
|
-
nodeConfig?: { retries?: number; retryDelay?: string | number },
|
|
708
|
-
fromStepName?: string
|
|
709
|
-
): Promise<void> {
|
|
710
|
-
const stepOptions = {
|
|
711
|
-
retries: nodeConfig?.retries ?? DEFAULT_STEP_RETRIES,
|
|
712
|
-
retryDelay: nodeConfig?.retryDelay,
|
|
713
|
-
}
|
|
714
|
-
await workflowService.insertStepState(
|
|
715
|
-
runId,
|
|
716
|
-
nodeId,
|
|
717
|
-
rpcName,
|
|
718
|
-
input,
|
|
719
|
-
stepOptions,
|
|
720
|
-
fromStepName
|
|
721
|
-
)
|
|
722
|
-
await workflowService.queueStepWorker(
|
|
723
|
-
runId,
|
|
724
|
-
nodeId,
|
|
725
|
-
rpcName,
|
|
726
|
-
input,
|
|
727
|
-
stepOptions,
|
|
728
|
-
fromStepName
|
|
729
|
-
)
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
export async function continueGraph(
|
|
733
|
-
workflowService: PikkuWorkflowService,
|
|
734
|
-
runId: string,
|
|
735
|
-
graphName: string,
|
|
736
|
-
overrideMeta?: WorkflowRuntimeMeta
|
|
737
|
-
): Promise<void> {
|
|
738
|
-
const meta = overrideMeta ?? getWorkflowMeta(graphName)
|
|
739
|
-
if (!meta?.nodes) {
|
|
740
|
-
throw new Error(`Workflow graph meta '${graphName}' not found`)
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
const nodes = meta.nodes
|
|
744
|
-
validateGraphReferences(graphName, nodes, meta.entryNodeIds ?? [])
|
|
745
|
-
|
|
746
|
-
const {
|
|
747
|
-
completedNodeIds: rawCompleted,
|
|
748
|
-
failedNodeIds: rawFailed,
|
|
749
|
-
branchKeys: branchByStep,
|
|
750
|
-
} = await workflowService.getCompletedGraphState(runId)
|
|
751
|
-
remapStepNamesToNodeIds(rawCompleted, nodes, graphName)
|
|
752
|
-
remapBranchKeys(branchByStep, nodes, graphName)
|
|
753
|
-
const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
|
|
754
|
-
|
|
755
|
-
if (failedNodeIds.length > 0) {
|
|
756
|
-
const failedNode = failedNodeIds[0]!
|
|
757
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
758
|
-
message: `Graph node '${failedNode}' failed after exhausting retries`,
|
|
759
|
-
stack: '',
|
|
760
|
-
code: 'GRAPH_NODE_FAILED',
|
|
761
|
-
})
|
|
762
|
-
return
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
const currentRun = await workflowService.getRun(runId)
|
|
766
|
-
if (currentRun?.status === 'suspended') {
|
|
767
|
-
return
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
const instances = await workflowService.getStepInstances(runId)
|
|
771
|
-
const triggerInput = currentRun?.input
|
|
772
|
-
const reader = await createGraphResultReader(
|
|
773
|
-
workflowService,
|
|
774
|
-
runId,
|
|
775
|
-
graphName,
|
|
776
|
-
nodes,
|
|
777
|
-
triggerInput,
|
|
778
|
-
instances
|
|
779
|
-
)
|
|
780
|
-
const plan = planGraphTransitions(
|
|
781
|
-
nodes,
|
|
782
|
-
instances,
|
|
783
|
-
branchByStep,
|
|
784
|
-
meta.entryNodeIds ?? [],
|
|
785
|
-
graphName,
|
|
786
|
-
reader.fanoutWidths
|
|
787
|
-
)
|
|
788
|
-
|
|
789
|
-
if (plan.toFire.length === 0) {
|
|
790
|
-
if (!plan.hasInFlight && !plan.blockedWaiting) {
|
|
791
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
792
|
-
}
|
|
793
|
-
return
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
for (const fire of plan.toFire) {
|
|
797
|
-
const node = nodes[fire.logical]
|
|
798
|
-
if (!node?.rpcName) continue
|
|
799
|
-
|
|
800
|
-
const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
|
|
801
|
-
(id) => !IGNORED_REFS.has(id)
|
|
802
|
-
)
|
|
803
|
-
const fetchedResults = await reader.read(referencedNodeIds)
|
|
804
|
-
const nodeResults: Record<string, any> = {
|
|
805
|
-
trigger: triggerInput,
|
|
806
|
-
...fetchedResults,
|
|
807
|
-
}
|
|
808
|
-
if (fire.itemIndex !== undefined) {
|
|
809
|
-
nodeResults['$item'] = reader.fanoutItems[fire.logical]?.[fire.itemIndex]
|
|
810
|
-
}
|
|
811
|
-
const resolvedInput = resolveSerializedInput(node.input, nodeResults)
|
|
812
|
-
|
|
813
|
-
await queueGraphNode(
|
|
814
|
-
workflowService,
|
|
815
|
-
runId,
|
|
816
|
-
graphName,
|
|
817
|
-
fire.instanceKey,
|
|
818
|
-
node.rpcName,
|
|
819
|
-
resolvedInput,
|
|
820
|
-
node,
|
|
821
|
-
fire.fromStepName
|
|
822
|
-
)
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
async function invokeGraphNodeRpc(
|
|
827
|
-
workflowService: PikkuWorkflowService,
|
|
828
|
-
rpcService: any,
|
|
829
|
-
runId: string,
|
|
830
|
-
stepId: string,
|
|
831
|
-
nodeId: string,
|
|
832
|
-
rpcName: string,
|
|
833
|
-
input: any,
|
|
834
|
-
graphName: string
|
|
835
|
-
): Promise<any> {
|
|
836
|
-
const wireState: GraphWireState = {}
|
|
837
|
-
const graphWire: PikkuGraphWire = {
|
|
838
|
-
runId,
|
|
839
|
-
graphName,
|
|
840
|
-
nodeId,
|
|
841
|
-
branch: (key: string) => {
|
|
842
|
-
wireState.branchKey = key
|
|
843
|
-
},
|
|
844
|
-
setState: (name: string, value: unknown) =>
|
|
845
|
-
workflowService.updateRunState(runId, name, value),
|
|
846
|
-
getState: () => workflowService.getRunState(runId),
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
const result = await rpcService.rpcWithWire(rpcName, input, {
|
|
850
|
-
graph: graphWire,
|
|
851
|
-
workflow: workflowService.createWorkflowWire(graphName, runId, rpcService),
|
|
852
|
-
})
|
|
853
|
-
|
|
854
|
-
if (wireState.branchKey) {
|
|
855
|
-
await workflowService.setBranchTaken(stepId, wireState.branchKey)
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
return result
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
export async function executeGraphStep(
|
|
862
|
-
workflowService: PikkuWorkflowService,
|
|
863
|
-
rpcService: any,
|
|
864
|
-
runId: string,
|
|
865
|
-
stepId: string,
|
|
866
|
-
nodeId: string,
|
|
867
|
-
rpcName: string,
|
|
868
|
-
data: any,
|
|
869
|
-
graphName: string
|
|
870
|
-
): Promise<any> {
|
|
871
|
-
try {
|
|
872
|
-
let result: any
|
|
873
|
-
|
|
874
|
-
const subWorkflowMeta = pikkuState(null, 'workflows', 'meta')[rpcName]
|
|
875
|
-
const agentMeta = subWorkflowMeta
|
|
876
|
-
? undefined
|
|
877
|
-
: pikkuState(null, 'agent', 'agentsMeta')[rpcName]
|
|
878
|
-
if (subWorkflowMeta) {
|
|
879
|
-
const childWire: WorkflowRunWire = {
|
|
880
|
-
type: 'workflow',
|
|
881
|
-
id: rpcName,
|
|
882
|
-
parentRunId: runId,
|
|
883
|
-
parentStepId: stepId,
|
|
884
|
-
}
|
|
885
|
-
const shouldInline = !getSingletonServices()?.queueService
|
|
886
|
-
const { runId: childRunId } = await workflowService.startWorkflow(
|
|
887
|
-
rpcName,
|
|
888
|
-
data,
|
|
889
|
-
childWire,
|
|
890
|
-
rpcService,
|
|
891
|
-
{ inline: shouldInline }
|
|
892
|
-
)
|
|
893
|
-
await workflowService.setStepChildRunId(stepId, childRunId)
|
|
894
|
-
|
|
895
|
-
if (shouldInline) {
|
|
896
|
-
const childRun = await workflowService.getRun(childRunId)
|
|
897
|
-
if (childRun?.status === 'failed') {
|
|
898
|
-
throw new Error(childRun.error?.message || 'Sub-workflow failed')
|
|
899
|
-
}
|
|
900
|
-
if (childRun?.status === 'cancelled') {
|
|
901
|
-
throw new Error('Sub-workflow was cancelled')
|
|
902
|
-
}
|
|
903
|
-
result = childRun?.output
|
|
904
|
-
} else {
|
|
905
|
-
throw new ChildWorkflowStartedException(runId, stepId, childRunId)
|
|
906
|
-
}
|
|
907
|
-
} else if (agentMeta) {
|
|
908
|
-
const agentRun = await rpcService.agent.run(rpcName, data)
|
|
909
|
-
result = agentRun.result
|
|
910
|
-
} else {
|
|
911
|
-
result = await invokeGraphNodeRpc(
|
|
912
|
-
workflowService,
|
|
913
|
-
rpcService,
|
|
914
|
-
runId,
|
|
915
|
-
stepId,
|
|
916
|
-
nodeId,
|
|
917
|
-
rpcName,
|
|
918
|
-
data,
|
|
919
|
-
graphName
|
|
920
|
-
)
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
return result
|
|
924
|
-
} catch (error) {
|
|
925
|
-
if (
|
|
926
|
-
error instanceof WorkflowAsyncException ||
|
|
927
|
-
error instanceof WorkflowSuspendedException
|
|
928
|
-
) {
|
|
929
|
-
throw error
|
|
930
|
-
}
|
|
931
|
-
if (error instanceof ChildWorkflowStartedException) {
|
|
932
|
-
throw error
|
|
933
|
-
}
|
|
934
|
-
if (error instanceof RPCNotFoundError) {
|
|
935
|
-
await workflowService.updateRunStatus(runId, 'suspended', undefined, {
|
|
936
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
937
|
-
code: 'RPC_NOT_FOUND',
|
|
938
|
-
})
|
|
939
|
-
throw error
|
|
940
|
-
}
|
|
941
|
-
const meta = getWorkflowMeta(graphName)
|
|
942
|
-
if (meta?.nodes) {
|
|
943
|
-
const node = meta.nodes[nodeId]
|
|
944
|
-
if (node?.onError) {
|
|
945
|
-
const errorNodes = Array.isArray(node.onError)
|
|
946
|
-
? node.onError
|
|
947
|
-
: [node.onError]
|
|
948
|
-
for (const errorNodeId of errorNodes) {
|
|
949
|
-
const errorNode = meta.nodes[errorNodeId]
|
|
950
|
-
if (errorNode) {
|
|
951
|
-
await queueGraphNode(
|
|
952
|
-
workflowService,
|
|
953
|
-
runId,
|
|
954
|
-
graphName,
|
|
955
|
-
errorNodeId,
|
|
956
|
-
errorNode.rpcName,
|
|
957
|
-
{ error: { message: (error as Error).message } },
|
|
958
|
-
errorNode
|
|
959
|
-
)
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
throw error
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
throw error
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
export async function runFromMeta(
|
|
970
|
-
workflowService: PikkuWorkflowService,
|
|
971
|
-
runId: string,
|
|
972
|
-
meta: WorkflowRuntimeMeta,
|
|
973
|
-
_rpcService: any
|
|
974
|
-
): Promise<void> {
|
|
975
|
-
await continueGraph(workflowService, runId, meta.name, meta)
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
async function executeGraphNodeInline(
|
|
979
|
-
workflowService: PikkuWorkflowService,
|
|
980
|
-
rpcService: any,
|
|
981
|
-
runId: string,
|
|
982
|
-
graphName: string,
|
|
983
|
-
nodeId: string,
|
|
984
|
-
instanceKey: string,
|
|
985
|
-
input: any,
|
|
986
|
-
nodes: Record<string, any>,
|
|
987
|
-
fromStepName?: string
|
|
988
|
-
): Promise<void> {
|
|
989
|
-
const node = nodes[nodeId]
|
|
990
|
-
if (!node) return
|
|
991
|
-
|
|
992
|
-
const rpcName = node.rpcName
|
|
993
|
-
|
|
994
|
-
const stepState = await workflowService.insertStepState(
|
|
995
|
-
runId,
|
|
996
|
-
instanceKey,
|
|
997
|
-
rpcName,
|
|
998
|
-
input,
|
|
999
|
-
{ retries: node.retries ?? 0, retryDelay: node.retryDelay },
|
|
1000
|
-
fromStepName
|
|
1001
|
-
)
|
|
1002
|
-
|
|
1003
|
-
await workflowService.setStepRunning(stepState.stepId)
|
|
1004
|
-
|
|
1005
|
-
try {
|
|
1006
|
-
let result: any
|
|
1007
|
-
|
|
1008
|
-
const subWorkflowMeta = pikkuState(null, 'workflows', 'meta')[rpcName]
|
|
1009
|
-
const agentMeta = subWorkflowMeta
|
|
1010
|
-
? undefined
|
|
1011
|
-
: pikkuState(null, 'agent', 'agentsMeta')[rpcName]
|
|
1012
|
-
if (subWorkflowMeta) {
|
|
1013
|
-
const childWire: WorkflowRunWire = {
|
|
1014
|
-
type: 'workflow',
|
|
1015
|
-
id: rpcName,
|
|
1016
|
-
parentRunId: runId,
|
|
1017
|
-
parentStepId: stepState.stepId,
|
|
1018
|
-
}
|
|
1019
|
-
const { runId: childRunId } = await workflowService.startWorkflow(
|
|
1020
|
-
rpcName,
|
|
1021
|
-
input,
|
|
1022
|
-
childWire,
|
|
1023
|
-
rpcService,
|
|
1024
|
-
{ inline: true }
|
|
1025
|
-
)
|
|
1026
|
-
await workflowService.setStepChildRunId(stepState.stepId, childRunId)
|
|
1027
|
-
const childRun = await workflowService.getRun(childRunId)
|
|
1028
|
-
if (childRun?.status === 'failed') {
|
|
1029
|
-
throw new Error(childRun.error?.message || 'Sub-workflow failed')
|
|
1030
|
-
}
|
|
1031
|
-
if (childRun?.status === 'cancelled') {
|
|
1032
|
-
throw new Error('Sub-workflow was cancelled')
|
|
1033
|
-
}
|
|
1034
|
-
result = childRun?.output
|
|
1035
|
-
} else if (agentMeta) {
|
|
1036
|
-
const agentRun = await rpcService.agent.run(rpcName, input)
|
|
1037
|
-
result = agentRun.result
|
|
1038
|
-
} else {
|
|
1039
|
-
result = await invokeGraphNodeRpc(
|
|
1040
|
-
workflowService,
|
|
1041
|
-
rpcService,
|
|
1042
|
-
runId,
|
|
1043
|
-
stepState.stepId,
|
|
1044
|
-
nodeId,
|
|
1045
|
-
rpcName,
|
|
1046
|
-
input,
|
|
1047
|
-
graphName
|
|
1048
|
-
)
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
await workflowService.setStepResult(stepState.stepId, result)
|
|
1052
|
-
} catch (error) {
|
|
1053
|
-
if (
|
|
1054
|
-
error instanceof WorkflowAsyncException ||
|
|
1055
|
-
error instanceof WorkflowSuspendedException
|
|
1056
|
-
) {
|
|
1057
|
-
throw error
|
|
1058
|
-
}
|
|
1059
|
-
if (error instanceof RPCNotFoundError) {
|
|
1060
|
-
await workflowService.setStepError(stepState.stepId, error as Error)
|
|
1061
|
-
await workflowService.updateRunStatus(runId, 'suspended', undefined, {
|
|
1062
|
-
message: `RPC '${rpcName}' not found. Deploy the missing function and resume.`,
|
|
1063
|
-
code: 'RPC_NOT_FOUND',
|
|
1064
|
-
})
|
|
1065
|
-
throw new WorkflowSuspendedException(runId, 'RPC_NOT_FOUND')
|
|
1066
|
-
}
|
|
1067
|
-
await workflowService.setStepError(stepState.stepId, error as Error)
|
|
1068
|
-
|
|
1069
|
-
if (node?.onError) {
|
|
1070
|
-
const errorNodes = Array.isArray(node.onError)
|
|
1071
|
-
? node.onError
|
|
1072
|
-
: [node.onError]
|
|
1073
|
-
await Promise.all(
|
|
1074
|
-
errorNodes.map((errorNodeId: string) =>
|
|
1075
|
-
executeGraphNodeInline(
|
|
1076
|
-
workflowService,
|
|
1077
|
-
rpcService,
|
|
1078
|
-
runId,
|
|
1079
|
-
graphName,
|
|
1080
|
-
errorNodeId,
|
|
1081
|
-
errorNodeId,
|
|
1082
|
-
{ error: { message: (error as Error).message } },
|
|
1083
|
-
nodes,
|
|
1084
|
-
nodeId
|
|
1085
|
-
)
|
|
1086
|
-
)
|
|
1087
|
-
)
|
|
1088
|
-
return
|
|
1089
|
-
}
|
|
1090
|
-
throw error
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
async function continueGraphInline(
|
|
1095
|
-
workflowService: PikkuWorkflowService,
|
|
1096
|
-
rpcService: any,
|
|
1097
|
-
runId: string,
|
|
1098
|
-
graphName: string,
|
|
1099
|
-
nodes: Record<string, any>,
|
|
1100
|
-
triggerInput: any,
|
|
1101
|
-
entryNodeIds: string[]
|
|
1102
|
-
): Promise<void> {
|
|
1103
|
-
while (true) {
|
|
1104
|
-
const {
|
|
1105
|
-
failedNodeIds: rawFailed,
|
|
1106
|
-
branchKeys: branchByStep,
|
|
1107
|
-
completedNodeIds: rawCompleted,
|
|
1108
|
-
} = await workflowService.getCompletedGraphState(runId)
|
|
1109
|
-
remapStepNamesToNodeIds(rawCompleted, nodes, graphName)
|
|
1110
|
-
remapBranchKeys(branchByStep, nodes, graphName)
|
|
1111
|
-
const failedNodeIds = remapStepNamesToNodeIds(rawFailed, nodes, graphName)
|
|
1112
|
-
|
|
1113
|
-
if (failedNodeIds.length > 0) {
|
|
1114
|
-
const failedNode = failedNodeIds[0]!
|
|
1115
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
1116
|
-
message: `Graph node '${failedNode}' failed after exhausting retries`,
|
|
1117
|
-
stack: '',
|
|
1118
|
-
code: 'GRAPH_NODE_FAILED',
|
|
1119
|
-
})
|
|
1120
|
-
return
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
const run = await workflowService.getRun(runId)
|
|
1124
|
-
if (run?.status === 'suspended') {
|
|
1125
|
-
return
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
const instances = await workflowService.getStepInstances(runId)
|
|
1129
|
-
const reader = await createGraphResultReader(
|
|
1130
|
-
workflowService,
|
|
1131
|
-
runId,
|
|
1132
|
-
graphName,
|
|
1133
|
-
nodes,
|
|
1134
|
-
triggerInput,
|
|
1135
|
-
instances
|
|
1136
|
-
)
|
|
1137
|
-
const plan = planGraphTransitions(
|
|
1138
|
-
nodes,
|
|
1139
|
-
instances,
|
|
1140
|
-
branchByStep,
|
|
1141
|
-
entryNodeIds,
|
|
1142
|
-
graphName,
|
|
1143
|
-
reader.fanoutWidths
|
|
1144
|
-
)
|
|
1145
|
-
|
|
1146
|
-
if (plan.toFire.length === 0) {
|
|
1147
|
-
if (!plan.hasInFlight && !plan.blockedWaiting) {
|
|
1148
|
-
await workflowService.updateRunStatus(runId, 'completed')
|
|
1149
|
-
}
|
|
1150
|
-
return
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
let executed = 0
|
|
1154
|
-
await Promise.all(
|
|
1155
|
-
plan.toFire.map(async (fire) => {
|
|
1156
|
-
const node = nodes[fire.logical]
|
|
1157
|
-
if (!node?.rpcName) return
|
|
1158
|
-
|
|
1159
|
-
const referencedNodeIds = extractReferencedNodeIds(node.input).filter(
|
|
1160
|
-
(id) => !IGNORED_REFS.has(id)
|
|
1161
|
-
)
|
|
1162
|
-
const fetchedResults = await reader.read(referencedNodeIds)
|
|
1163
|
-
const nodeResults: Record<string, any> = {
|
|
1164
|
-
trigger: triggerInput,
|
|
1165
|
-
...fetchedResults,
|
|
1166
|
-
}
|
|
1167
|
-
if (fire.itemIndex !== undefined) {
|
|
1168
|
-
nodeResults['$item'] =
|
|
1169
|
-
reader.fanoutItems[fire.logical]?.[fire.itemIndex]
|
|
1170
|
-
}
|
|
1171
|
-
const resolvedInput = resolveSerializedInput(node.input, nodeResults)
|
|
1172
|
-
|
|
1173
|
-
executed++
|
|
1174
|
-
await executeGraphNodeInline(
|
|
1175
|
-
workflowService,
|
|
1176
|
-
rpcService,
|
|
1177
|
-
runId,
|
|
1178
|
-
graphName,
|
|
1179
|
-
fire.logical,
|
|
1180
|
-
fire.instanceKey,
|
|
1181
|
-
resolvedInput,
|
|
1182
|
-
nodes,
|
|
1183
|
-
fire.fromStepName
|
|
1184
|
-
)
|
|
1185
|
-
})
|
|
1186
|
-
)
|
|
1187
|
-
if (executed === 0) return
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
function planEntryFirings(
|
|
1192
|
-
graphName: string,
|
|
1193
|
-
nodeId: string,
|
|
1194
|
-
node: Record<string, any>,
|
|
1195
|
-
triggerNodeResults: Record<string, any>,
|
|
1196
|
-
triggerInput: any
|
|
1197
|
-
): Array<{ instanceKey: string; input: any }> {
|
|
1198
|
-
if (!node.forEach) {
|
|
1199
|
-
const input =
|
|
1200
|
-
node.input && Object.keys(node.input).length > 0
|
|
1201
|
-
? resolveSerializedInput(node.input, triggerNodeResults)
|
|
1202
|
-
: triggerInput
|
|
1203
|
-
return [{ instanceKey: nodeId, input }]
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
const items = resolveForEachItems(graphName, nodeId, node, triggerNodeResults)
|
|
1207
|
-
const width = node.mode === 'sequential' ? Math.min(items.length, 1) : items.length
|
|
1208
|
-
const firings: Array<{ instanceKey: string; input: any }> = []
|
|
1209
|
-
for (let index = 0; index < width; index++) {
|
|
1210
|
-
firings.push({
|
|
1211
|
-
instanceKey: fanoutInstanceKey(nodeId, index),
|
|
1212
|
-
input: resolveSerializedInput(node.input, {
|
|
1213
|
-
...triggerNodeResults,
|
|
1214
|
-
$item: items[index],
|
|
1215
|
-
}),
|
|
1216
|
-
})
|
|
1217
|
-
}
|
|
1218
|
-
return firings
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
export async function runWorkflowGraph(
|
|
1222
|
-
workflowService: PikkuWorkflowService,
|
|
1223
|
-
graphName: string,
|
|
1224
|
-
triggerInput: any,
|
|
1225
|
-
rpcService?: any,
|
|
1226
|
-
inline?: boolean,
|
|
1227
|
-
startNode?: string,
|
|
1228
|
-
wire?: WorkflowRunWire,
|
|
1229
|
-
overrideMeta?: WorkflowRuntimeMeta
|
|
1230
|
-
): Promise<{ runId: string }> {
|
|
1231
|
-
const meta = overrideMeta ?? getWorkflowMeta(graphName)
|
|
1232
|
-
if (!meta?.nodes) {
|
|
1233
|
-
throw new Error(`Workflow graph '${graphName}' not found`)
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
const nodes = meta.nodes
|
|
1237
|
-
const declaredEntryNodes = meta.entryNodeIds ?? []
|
|
1238
|
-
// knowledge: decisions/security/a-graph-run-starts-at-an-entry-node-the-graph-declared.md
|
|
1239
|
-
if (startNode && !declaredEntryNodes.includes(startNode)) {
|
|
1240
|
-
throw new Error(
|
|
1241
|
-
`Workflow graph '${graphName}': '${startNode}' is not an entry node`
|
|
1242
|
-
)
|
|
1243
|
-
}
|
|
1244
|
-
const entryNodes: string[] = startNode ? [startNode] : declaredEntryNodes
|
|
1245
|
-
validateGraphReferences(graphName, nodes, entryNodes)
|
|
1246
|
-
|
|
1247
|
-
if (entryNodes.length === 0) {
|
|
1248
|
-
throw new Error(
|
|
1249
|
-
`Workflow graph '${graphName}': no entry nodes found in meta or startNode`
|
|
1250
|
-
)
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
const readyEntryNodes = entryNodes.filter((nodeId) => {
|
|
1254
|
-
const node = nodes[nodeId]
|
|
1255
|
-
return node && areDependenciesSatisfied(node, new Set())
|
|
1256
|
-
})
|
|
1257
|
-
|
|
1258
|
-
if (readyEntryNodes.length === 0) {
|
|
1259
|
-
throw new Error(
|
|
1260
|
-
`Workflow graph '${graphName}': no entry nodes have satisfied dependencies`
|
|
1261
|
-
)
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
if (!meta.graphHash) {
|
|
1265
|
-
throw new Error(`Workflow graph '${graphName}': missing graphHash in meta`)
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
const runId = await workflowService.createRun(
|
|
1269
|
-
graphName,
|
|
1270
|
-
triggerInput,
|
|
1271
|
-
inline ?? false,
|
|
1272
|
-
meta.graphHash,
|
|
1273
|
-
wire ?? { type: 'unknown' },
|
|
1274
|
-
{
|
|
1275
|
-
deterministic: meta.deterministic,
|
|
1276
|
-
plannedSteps: meta.plannedSteps,
|
|
1277
|
-
}
|
|
1278
|
-
)
|
|
1279
|
-
|
|
1280
|
-
if (inline) {
|
|
1281
|
-
workflowService.registerInlineRun(runId)
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
const triggerNodeResults = { trigger: triggerInput }
|
|
1285
|
-
|
|
1286
|
-
if (inline && rpcService) {
|
|
1287
|
-
const executeInline = async () => {
|
|
1288
|
-
try {
|
|
1289
|
-
await Promise.all(
|
|
1290
|
-
readyEntryNodes.map(async (nodeId) => {
|
|
1291
|
-
const node = nodes[nodeId]
|
|
1292
|
-
if (!node?.rpcName) return
|
|
1293
|
-
|
|
1294
|
-
const firings = planEntryFirings(
|
|
1295
|
-
graphName,
|
|
1296
|
-
nodeId,
|
|
1297
|
-
node,
|
|
1298
|
-
triggerNodeResults,
|
|
1299
|
-
triggerInput
|
|
1300
|
-
)
|
|
1301
|
-
|
|
1302
|
-
await Promise.all(
|
|
1303
|
-
firings.map((firing) =>
|
|
1304
|
-
executeGraphNodeInline(
|
|
1305
|
-
workflowService,
|
|
1306
|
-
rpcService,
|
|
1307
|
-
runId,
|
|
1308
|
-
graphName,
|
|
1309
|
-
nodeId,
|
|
1310
|
-
firing.instanceKey,
|
|
1311
|
-
firing.input,
|
|
1312
|
-
nodes
|
|
1313
|
-
)
|
|
1314
|
-
)
|
|
1315
|
-
)
|
|
1316
|
-
})
|
|
1317
|
-
)
|
|
1318
|
-
|
|
1319
|
-
await continueGraphInline(
|
|
1320
|
-
workflowService,
|
|
1321
|
-
rpcService,
|
|
1322
|
-
runId,
|
|
1323
|
-
graphName,
|
|
1324
|
-
nodes,
|
|
1325
|
-
triggerInput,
|
|
1326
|
-
entryNodes
|
|
1327
|
-
)
|
|
1328
|
-
} catch (error) {
|
|
1329
|
-
if (
|
|
1330
|
-
error instanceof WorkflowAsyncException ||
|
|
1331
|
-
error instanceof WorkflowSuspendedException
|
|
1332
|
-
) {
|
|
1333
|
-
return
|
|
1334
|
-
}
|
|
1335
|
-
await workflowService.updateRunStatus(runId, 'failed', undefined, {
|
|
1336
|
-
message: (error as Error).message,
|
|
1337
|
-
stack: (error as Error).stack || '',
|
|
1338
|
-
code: 'GRAPH_NODE_FAILED',
|
|
1339
|
-
})
|
|
1340
|
-
} finally {
|
|
1341
|
-
workflowService.unregisterInlineRun(runId)
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
await executeInline()
|
|
1345
|
-
} else {
|
|
1346
|
-
for (const nodeId of readyEntryNodes) {
|
|
1347
|
-
const node = nodes[nodeId]
|
|
1348
|
-
if (!node?.rpcName) continue
|
|
1349
|
-
|
|
1350
|
-
const firings = planEntryFirings(
|
|
1351
|
-
graphName,
|
|
1352
|
-
nodeId,
|
|
1353
|
-
node,
|
|
1354
|
-
triggerNodeResults,
|
|
1355
|
-
triggerInput
|
|
1356
|
-
)
|
|
1357
|
-
|
|
1358
|
-
for (const firing of firings) {
|
|
1359
|
-
await queueGraphNode(
|
|
1360
|
-
workflowService,
|
|
1361
|
-
runId,
|
|
1362
|
-
graphName,
|
|
1363
|
-
firing.instanceKey,
|
|
1364
|
-
node.rpcName,
|
|
1365
|
-
firing.input,
|
|
1366
|
-
node
|
|
1367
|
-
)
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
if (inline) {
|
|
1371
|
-
workflowService.unregisterInlineRun(runId)
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
return { runId }
|
|
1376
|
-
}
|