@workflow/core 5.0.0-beta.5 → 5.0.0-beta.50
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/dist/attribute-changes.d.ts +5 -0
- package/dist/attribute-changes.d.ts.map +1 -0
- package/dist/attribute-changes.js +25 -0
- package/dist/capabilities.d.ts +35 -2
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +74 -5
- package/dist/capture-stack.d.ts +1 -1
- package/dist/capture-stack.js +2 -2
- package/dist/class-serialization.d.ts +32 -0
- package/dist/class-serialization.d.ts.map +1 -1
- package/dist/class-serialization.js +37 -1
- package/dist/classify-error.d.ts +19 -3
- package/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +98 -6
- package/dist/context-violation-error.d.ts +5 -5
- package/dist/context-violation-error.d.ts.map +1 -1
- package/dist/context-violation-error.js +8 -6
- package/dist/create-hook.d.ts +72 -7
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.js +2 -2
- package/dist/describe-error.d.ts +7 -7
- package/dist/describe-error.d.ts.map +1 -1
- package/dist/describe-error.js +97 -12
- package/dist/encryption.d.ts +38 -4
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -14
- package/dist/events-consumer.d.ts +271 -0
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +646 -42
- package/dist/flushable-stream.d.ts +56 -1
- package/dist/flushable-stream.d.ts.map +1 -1
- package/dist/flushable-stream.js +123 -11
- package/dist/global.d.ts +36 -2
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +21 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/log-format.d.ts +2 -2
- package/dist/log-format.js +7 -7
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -6
- package/dist/private.d.ts +226 -9
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +626 -7
- package/dist/replay-payload-cache.d.ts +64 -0
- package/dist/replay-payload-cache.d.ts.map +1 -0
- package/dist/replay-payload-cache.js +158 -0
- package/dist/runtime/compute-instance.d.ts +13 -0
- package/dist/runtime/compute-instance.d.ts.map +1 -0
- package/dist/runtime/compute-instance.js +14 -0
- package/dist/runtime/constants.d.ts +315 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +502 -16
- package/dist/runtime/count-step-started-events.d.ts +52 -0
- package/dist/runtime/count-step-started-events.d.ts.map +1 -0
- package/dist/runtime/count-step-started-events.js +72 -0
- package/dist/runtime/deployment-guard.d.ts +99 -0
- package/dist/runtime/deployment-guard.d.ts.map +1 -0
- package/dist/runtime/deployment-guard.js +154 -0
- package/dist/runtime/get-port-lazy.d.ts +15 -0
- package/dist/runtime/get-port-lazy.d.ts.map +1 -1
- package/dist/runtime/get-port-lazy.js +75 -6
- package/dist/runtime/get-world-lazy.d.ts +5 -14
- package/dist/runtime/get-world-lazy.d.ts.map +1 -1
- package/dist/runtime/get-world-lazy.js +19 -29
- package/dist/runtime/helpers.d.ts +341 -29
- package/dist/runtime/helpers.d.ts.map +1 -1
- package/dist/runtime/helpers.js +559 -57
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +136 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1686 -0
- package/dist/runtime/quickjs-runtime.d.ts +244 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +2427 -0
- package/dist/runtime/quickjs-serde.d.ts +107 -0
- package/dist/runtime/quickjs-serde.d.ts.map +1 -0
- package/dist/runtime/quickjs-serde.js +2159 -0
- package/dist/runtime/replay-budget.d.ts +97 -0
- package/dist/runtime/replay-budget.d.ts.map +1 -0
- package/dist/runtime/replay-budget.js +139 -0
- package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
- package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
- package/dist/runtime/replay-recovery-reporter.js +64 -0
- package/dist/runtime/resume-hook.d.ts +91 -8
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +512 -89
- package/dist/runtime/resume-latency.d.ts +150 -0
- package/dist/runtime/resume-latency.d.ts.map +1 -0
- package/dist/runtime/resume-latency.js +155 -0
- package/dist/runtime/run-id-time.d.ts +19 -0
- package/dist/runtime/run-id-time.d.ts.map +1 -0
- package/dist/runtime/run-id-time.js +42 -0
- package/dist/runtime/run.d.ts +72 -7
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +280 -53
- package/dist/runtime/runs.d.ts +54 -3
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +126 -16
- package/dist/runtime/start.d.ts +101 -2
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +352 -61
- package/dist/runtime/step-executor.d.ts +205 -5
- package/dist/runtime/step-executor.d.ts.map +1 -1
- package/dist/runtime/step-executor.js +760 -127
- package/dist/runtime/step-latency.d.ts +220 -0
- package/dist/runtime/step-latency.d.ts.map +1 -0
- package/dist/runtime/step-latency.js +215 -0
- package/dist/runtime/step-ownership.d.ts +72 -0
- package/dist/runtime/step-ownership.d.ts.map +1 -0
- package/dist/runtime/step-ownership.js +114 -0
- package/dist/runtime/step-single-flight.d.ts +12 -0
- package/dist/runtime/step-single-flight.d.ts.map +1 -0
- package/dist/runtime/step-single-flight.js +75 -0
- package/dist/runtime/suspension-handler.d.ts +269 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +1253 -125
- package/dist/runtime/unserializable-step.d.ts +34 -0
- package/dist/runtime/unserializable-step.d.ts.map +1 -0
- package/dist/runtime/unserializable-step.js +52 -0
- package/dist/runtime/vm-mode.d.ts +44 -0
- package/dist/runtime/vm-mode.d.ts.map +1 -0
- package/dist/runtime/vm-mode.js +62 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
- package/dist/runtime/vm-serde-bundle.generated.js +16 -0
- package/dist/runtime/wait-continuation.d.ts +106 -0
- package/dist/runtime/wait-continuation.d.ts.map +1 -0
- package/dist/runtime/wait-continuation.js +136 -0
- package/dist/runtime/wait-until.d.ts +18 -0
- package/dist/runtime/wait-until.d.ts.map +1 -0
- package/dist/runtime/wait-until.js +42 -0
- package/dist/runtime/world-compatibility.d.ts +32 -0
- package/dist/runtime/world-compatibility.d.ts.map +1 -0
- package/dist/runtime/world-compatibility.js +44 -0
- package/dist/runtime/world-init.d.ts +17 -42
- package/dist/runtime/world-init.d.ts.map +1 -1
- package/dist/runtime/world-init.js +18 -43
- package/dist/runtime/world.d.ts +14 -2
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +75 -19
- package/dist/runtime.d.ts +10 -6
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3741 -490
- package/dist/sealed-box.d.ts +167 -0
- package/dist/sealed-box.d.ts.map +1 -0
- package/dist/sealed-box.js +571 -0
- package/dist/serialization/client.d.ts +3 -3
- package/dist/serialization/client.d.ts.map +1 -1
- package/dist/serialization/client.js +8 -4
- package/dist/serialization/codec-devalue-vm.d.ts +16 -0
- package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
- package/dist/serialization/codec-devalue-vm.js +148 -0
- package/dist/serialization/codec-devalue.d.ts +1 -1
- package/dist/serialization/codec-devalue.d.ts.map +1 -1
- package/dist/serialization/codec-devalue.js +24 -6
- package/dist/serialization/codec.d.ts +37 -2
- package/dist/serialization/codec.d.ts.map +1 -1
- package/dist/serialization/codec.js +2 -2
- package/dist/serialization/compression.d.ts +105 -0
- package/dist/serialization/compression.d.ts.map +1 -0
- package/dist/serialization/compression.js +261 -0
- package/dist/serialization/encryption.d.ts +119 -17
- package/dist/serialization/encryption.d.ts.map +1 -1
- package/dist/serialization/encryption.js +152 -17
- package/dist/serialization/errors.d.ts +13 -0
- package/dist/serialization/errors.d.ts.map +1 -1
- package/dist/serialization/errors.js +20 -2
- package/dist/serialization/format.d.ts +6 -6
- package/dist/serialization/format.js +7 -7
- package/dist/serialization/hardened.d.ts +156 -0
- package/dist/serialization/hardened.d.ts.map +1 -0
- package/dist/serialization/hardened.js +538 -0
- package/dist/serialization/index.d.ts +10 -8
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +10 -8
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/class.d.ts.map +1 -1
- package/dist/serialization/reducers/class.js +11 -6
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +616 -0
- package/dist/serialization/reducers/common.d.ts +1 -1
- package/dist/serialization/reducers/common.d.ts.map +1 -1
- package/dist/serialization/reducers/common.js +202 -62
- package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
- package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function-vm.js +97 -0
- package/dist/serialization/reducers/step-function.d.ts.map +1 -1
- package/dist/serialization/reducers/step-function.js +25 -9
- package/dist/serialization/step.d.ts +4 -4
- package/dist/serialization/step.d.ts.map +1 -1
- package/dist/serialization/step.js +9 -5
- package/dist/serialization/types.d.ts +80 -1
- package/dist/serialization/types.d.ts.map +1 -1
- package/dist/serialization/types.js +15 -1
- package/dist/serialization/workflow-vm.d.ts +29 -0
- package/dist/serialization/workflow-vm.d.ts.map +1 -0
- package/dist/serialization/workflow-vm.js +74 -0
- package/dist/serialization-format.d.ts +64 -8
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +225 -73
- package/dist/serialization.d.ts +262 -34
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +1879 -274
- package/dist/set-attributes.d.ts +13 -0
- package/dist/set-attributes.d.ts.map +1 -0
- package/dist/set-attributes.js +60 -0
- package/dist/source-map.d.ts +25 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +154 -10
- package/dist/step/context-storage.d.ts +61 -2
- package/dist/step/context-storage.d.ts.map +1 -1
- package/dist/step/context-storage.js +7 -5
- package/dist/step/writable-stream.d.ts +3 -3
- package/dist/step/writable-stream.d.ts.map +1 -1
- package/dist/step/writable-stream.js +72 -9
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +148 -27
- package/dist/symbols.d.ts +50 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +51 -1
- package/dist/telemetry/semantic-conventions.d.ts +397 -2
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +297 -3
- package/dist/telemetry.d.ts +91 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +244 -17
- package/dist/test-support/orchestrator-context.d.ts +25 -0
- package/dist/test-support/orchestrator-context.d.ts.map +1 -0
- package/dist/test-support/orchestrator-context.js +101 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -1
- package/dist/util.d.ts +0 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -15
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/vm/index.d.ts.map +1 -1
- package/dist/vm/index.js +84 -14
- package/dist/vm/script-cache.d.ts +26 -0
- package/dist/vm/script-cache.d.ts.map +1 -0
- package/dist/vm/script-cache.js +131 -0
- package/dist/vm/uint8array-base64.js +1 -1
- package/dist/workflow/abort-controller.d.ts +2 -2
- package/dist/workflow/abort-controller.d.ts.map +1 -1
- package/dist/workflow/abort-controller.js +88 -21
- package/dist/workflow/attribute-dispatcher.d.ts +6 -0
- package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
- package/dist/workflow/attribute-dispatcher.js +45 -0
- package/dist/workflow/create-hook.d.ts.map +1 -1
- package/dist/workflow/create-hook.js +24 -3
- package/dist/workflow/get-workflow-metadata.js +2 -2
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +304 -47
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -1
- package/dist/workflow/set-attributes.d.ts +68 -0
- package/dist/workflow/set-attributes.d.ts.map +1 -0
- package/dist/workflow/set-attributes.js +60 -0
- package/dist/workflow/sleep.d.ts.map +1 -1
- package/dist/workflow/sleep.js +58 -12
- package/dist/workflow/world-init-stub.d.ts +3 -3
- package/dist/workflow/world-init-stub.js +4 -4
- package/dist/workflow.d.ts +95 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +895 -561
- package/docs/api-reference/create-hook.mdx +89 -10
- package/docs/api-reference/create-webhook.mdx +16 -15
- package/docs/api-reference/define-hook.mdx +35 -33
- package/docs/api-reference/fatal-error.mdx +30 -8
- package/docs/api-reference/fetch.mdx +14 -10
- package/docs/api-reference/get-step-metadata.mdx +2 -2
- package/docs/api-reference/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/get-writable.mdx +7 -7
- package/docs/api-reference/index.mdx +4 -1
- package/docs/api-reference/retryable-error.mdx +1 -1
- package/docs/api-reference/set-attributes.mdx +61 -0
- package/docs/api-reference/sleep.mdx +4 -4
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +107 -59
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +98 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/package.json +24 -15
- package/dist/runtime/step-handler.d.ts +0 -2
- package/dist/runtime/step-handler.d.ts.map +0 -1
- package/dist/runtime/step-handler.js +0 -776
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Event } from '@workflow/world';
|
|
2
|
+
/**
|
|
3
|
+
* Scope for {@link countStepStartedEvents}:
|
|
4
|
+
* - `{ type: 'ownedBy', messageId }` counts only starts whose
|
|
5
|
+
* `eventData.ownerMessageId` matches, i.e. attempts performed by that
|
|
6
|
+
* owning queue message (inline lazy starts and owned-recovery re-stamps).
|
|
7
|
+
* - `{ type: 'totalAttempts' }` estimates the step's genuine attempt total
|
|
8
|
+
* across its whole lifecycle: unstamped (bare) starts, which only real
|
|
9
|
+
* queue-dispatched background deliveries write, plus the starts of the
|
|
10
|
+
* single most-started owner. A genuine lifecycle has at most one inline
|
|
11
|
+
* owner phase (ownership is claimed atomically at step creation and
|
|
12
|
+
* lapses permanently at `step_retrying` or on any bare start), so taking
|
|
13
|
+
* the max over owners counts that phase's real attempts while invocations
|
|
14
|
+
* racing on the same batch, which stamp their own one-off message IDs,
|
|
15
|
+
* contribute at most their single largest count instead of accumulating.
|
|
16
|
+
*/
|
|
17
|
+
export type StepStartScope = {
|
|
18
|
+
type: 'ownedBy';
|
|
19
|
+
messageId: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'totalAttempts';
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Number of `step_started` events already recorded for a step, used as the
|
|
25
|
+
* authoritative attempt count for the maxRetries ceiling (the only bound for
|
|
26
|
+
* steps that time out without writing a `step_failed`).
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT: the raw (unscoped) count is NOT a reliable attempt number.
|
|
29
|
+
* Concurrent invocations racing on the same pending batch (stale replays,
|
|
30
|
+
* wake replays, a step message dispatched by a replay that lost the
|
|
31
|
+
* create-claim race, ...) can each write a `step_started` for the same
|
|
32
|
+
* logical attempt. Worlds without an atomic start guard (world-local) let
|
|
33
|
+
* all of them through. Counting those duplicates as "attempts" made the
|
|
34
|
+
* maxRetries ceiling fire on healthy runs and fail them with a false
|
|
35
|
+
* "exceeded max retries" (see workflow#3069).
|
|
36
|
+
*
|
|
37
|
+
* Callers therefore scope the count to the starts their ceiling is actually
|
|
38
|
+
* about via `scope`:
|
|
39
|
+
* - the inline owned-recovery ceiling counts only THIS message's starts
|
|
40
|
+
* (`ownedBy`): each real (re)delivery of the owning message stamps its
|
|
41
|
+
* `ownerMessageId` on the start it writes, while racing invocations stamp
|
|
42
|
+
* their own IDs (or none), so they no longer inflate the count;
|
|
43
|
+
* - the background-step ceiling counts the lifecycle attempt total
|
|
44
|
+
* (`totalAttempts`: bare starts plus the largest single owner's starts),
|
|
45
|
+
* so a step that exhausted part of its budget under inline ownership
|
|
46
|
+
* before transitioning to queued/bare retries still trips the combined
|
|
47
|
+
* ceiling, while racers' one-off stamped duplicates don't accumulate, and
|
|
48
|
+
* throttle/too-early redeliveries (which write no start at all) stay
|
|
49
|
+
* excluded as before.
|
|
50
|
+
*/
|
|
51
|
+
export declare function countStepStartedEvents(events: Event[] | null | undefined, stepId: string, scope?: StepStartScope): number;
|
|
52
|
+
//# sourceMappingURL=count-step-started-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-step-started-events.d.ts","sourceRoot":"","sources":["../../src/runtime/count-step-started-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAU9B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,cAAc,GACrB,MAAM,CAmCR"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const ownerOf = (e) => 'eventData' in e &&
|
|
2
|
+
e.eventData &&
|
|
3
|
+
'ownerMessageId' in e.eventData &&
|
|
4
|
+
typeof e.eventData.ownerMessageId === 'string'
|
|
5
|
+
? e.eventData.ownerMessageId
|
|
6
|
+
: undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Number of `step_started` events already recorded for a step, used as the
|
|
9
|
+
* authoritative attempt count for the maxRetries ceiling (the only bound for
|
|
10
|
+
* steps that time out without writing a `step_failed`).
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: the raw (unscoped) count is NOT a reliable attempt number.
|
|
13
|
+
* Concurrent invocations racing on the same pending batch (stale replays,
|
|
14
|
+
* wake replays, a step message dispatched by a replay that lost the
|
|
15
|
+
* create-claim race, ...) can each write a `step_started` for the same
|
|
16
|
+
* logical attempt. Worlds without an atomic start guard (world-local) let
|
|
17
|
+
* all of them through. Counting those duplicates as "attempts" made the
|
|
18
|
+
* maxRetries ceiling fire on healthy runs and fail them with a false
|
|
19
|
+
* "exceeded max retries" (see workflow#3069).
|
|
20
|
+
*
|
|
21
|
+
* Callers therefore scope the count to the starts their ceiling is actually
|
|
22
|
+
* about via `scope`:
|
|
23
|
+
* - the inline owned-recovery ceiling counts only THIS message's starts
|
|
24
|
+
* (`ownedBy`): each real (re)delivery of the owning message stamps its
|
|
25
|
+
* `ownerMessageId` on the start it writes, while racing invocations stamp
|
|
26
|
+
* their own IDs (or none), so they no longer inflate the count;
|
|
27
|
+
* - the background-step ceiling counts the lifecycle attempt total
|
|
28
|
+
* (`totalAttempts`: bare starts plus the largest single owner's starts),
|
|
29
|
+
* so a step that exhausted part of its budget under inline ownership
|
|
30
|
+
* before transitioning to queued/bare retries still trips the combined
|
|
31
|
+
* ceiling, while racers' one-off stamped duplicates don't accumulate, and
|
|
32
|
+
* throttle/too-early redeliveries (which write no start at all) stay
|
|
33
|
+
* excluded as before.
|
|
34
|
+
*/
|
|
35
|
+
export function countStepStartedEvents(events, stepId, scope) {
|
|
36
|
+
if (!events) {
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
let bare = 0;
|
|
40
|
+
const byOwner = new Map();
|
|
41
|
+
for (const e of events) {
|
|
42
|
+
if (e.eventType !== 'step_started' || e.correlationId !== stepId) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const owner = ownerOf(e);
|
|
46
|
+
if (owner === undefined) {
|
|
47
|
+
bare++;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
byOwner.set(owner, (byOwner.get(owner) ?? 0) + 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (scope === undefined) {
|
|
54
|
+
let total = bare;
|
|
55
|
+
for (const count of byOwner.values()) {
|
|
56
|
+
total += count;
|
|
57
|
+
}
|
|
58
|
+
return total;
|
|
59
|
+
}
|
|
60
|
+
if (scope.type === 'ownedBy') {
|
|
61
|
+
return byOwner.get(scope.messageId) ?? 0;
|
|
62
|
+
}
|
|
63
|
+
// totalAttempts
|
|
64
|
+
let maxOwner = 0;
|
|
65
|
+
for (const count of byOwner.values()) {
|
|
66
|
+
if (count > maxOwner) {
|
|
67
|
+
maxOwner = count;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return bare + maxOwner;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY291bnQtc3RlcC1zdGFydGVkLWV2ZW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2NvdW50LXN0ZXAtc3RhcnRlZC1ldmVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBcUJBLE1BQU0sT0FBTyxHQUFHLENBQUMsQ0FBUSxFQUFzQixFQUFFLENBQy9DLFdBQVcsSUFBSSxDQUFDO0lBQ2hCLENBQUMsQ0FBQyxTQUFTO0lBQ1gsZ0JBQWdCLElBQUksQ0FBQyxDQUFDLFNBQVM7SUFDL0IsT0FBTyxDQUFDLENBQUMsU0FBUyxDQUFDLGNBQWMsS0FBSyxRQUFRO0lBQzVDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLGNBQWM7SUFDNUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUVoQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUNwQyxNQUFrQyxFQUNsQyxNQUFjLEVBQ2QsS0FBc0I7SUFFdEIsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1osT0FBTyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBQ0QsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDO0lBQ2IsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDMUMsS0FBSyxNQUFNLENBQUMsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsQ0FBQyxTQUFTLEtBQUssY0FBYyxJQUFJLENBQUMsQ0FBQyxhQUFhLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDakUsU0FBUztRQUNYLENBQUM7UUFDRCxNQUFNLEtBQUssR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDekIsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDeEIsSUFBSSxFQUFFLENBQUM7UUFDVCxDQUFDO2FBQU0sQ0FBQztZQUNOLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNwRCxDQUFDO0lBQ0gsQ0FBQztJQUNELElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3hCLElBQUksS0FBSyxHQUFHLElBQUksQ0FBQztRQUNqQixLQUFLLE1BQU0sS0FBSyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO1lBQ3JDLEtBQUssSUFBSSxLQUFLLENBQUM7UUFDakIsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUM3QixPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBQ0QsZ0JBQWdCO0lBQ2hCLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztJQUNqQixLQUFLLE1BQU0sS0FBSyxJQUFJLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO1FBQ3JDLElBQUksS0FBSyxHQUFHLFFBQVEsRUFBRSxDQUFDO1lBQ3JCLFFBQVEsR0FBRyxLQUFLLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLElBQUksR0FBRyxRQUFRLENBQUM7QUFDekIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgRXZlbnQgfSBmcm9tICdAd29ya2Zsb3cvd29ybGQnO1xuXG4vKipcbiAqIFNjb3BlIGZvciB7QGxpbmsgY291bnRTdGVwU3RhcnRlZEV2ZW50c306XG4gKiAtIGB7IHR5cGU6ICdvd25lZEJ5JywgbWVzc2FnZUlkIH1gIGNvdW50cyBvbmx5IHN0YXJ0cyB3aG9zZVxuICogICBgZXZlbnREYXRhLm93bmVyTWVzc2FnZUlkYCBtYXRjaGVzLCBpLmUuIGF0dGVtcHRzIHBlcmZvcm1lZCBieSB0aGF0XG4gKiAgIG93bmluZyBxdWV1ZSBtZXNzYWdlIChpbmxpbmUgbGF6eSBzdGFydHMgYW5kIG93bmVkLXJlY292ZXJ5IHJlLXN0YW1wcykuXG4gKiAtIGB7IHR5cGU6ICd0b3RhbEF0dGVtcHRzJyB9YCBlc3RpbWF0ZXMgdGhlIHN0ZXAncyBnZW51aW5lIGF0dGVtcHQgdG90YWxcbiAqICAgYWNyb3NzIGl0cyB3aG9sZSBsaWZlY3ljbGU6IHVuc3RhbXBlZCAoYmFyZSkgc3RhcnRzLCB3aGljaCBvbmx5IHJlYWxcbiAqICAgcXVldWUtZGlzcGF0Y2hlZCBiYWNrZ3JvdW5kIGRlbGl2ZXJpZXMgd3JpdGUsIHBsdXMgdGhlIHN0YXJ0cyBvZiB0aGVcbiAqICAgc2luZ2xlIG1vc3Qtc3RhcnRlZCBvd25lci4gQSBnZW51aW5lIGxpZmVjeWNsZSBoYXMgYXQgbW9zdCBvbmUgaW5saW5lXG4gKiAgIG93bmVyIHBoYXNlIChvd25lcnNoaXAgaXMgY2xhaW1lZCBhdG9taWNhbGx5IGF0IHN0ZXAgY3JlYXRpb24gYW5kXG4gKiAgIGxhcHNlcyBwZXJtYW5lbnRseSBhdCBgc3RlcF9yZXRyeWluZ2Agb3Igb24gYW55IGJhcmUgc3RhcnQpLCBzbyB0YWtpbmdcbiAqICAgdGhlIG1heCBvdmVyIG93bmVycyBjb3VudHMgdGhhdCBwaGFzZSdzIHJlYWwgYXR0ZW1wdHMgd2hpbGUgaW52b2NhdGlvbnNcbiAqICAgcmFjaW5nIG9uIHRoZSBzYW1lIGJhdGNoLCB3aGljaCBzdGFtcCB0aGVpciBvd24gb25lLW9mZiBtZXNzYWdlIElEcyxcbiAqICAgY29udHJpYnV0ZSBhdCBtb3N0IHRoZWlyIHNpbmdsZSBsYXJnZXN0IGNvdW50IGluc3RlYWQgb2YgYWNjdW11bGF0aW5nLlxuICovXG5leHBvcnQgdHlwZSBTdGVwU3RhcnRTY29wZSA9XG4gIHwgeyB0eXBlOiAnb3duZWRCeSc7IG1lc3NhZ2VJZDogc3RyaW5nIH1cbiAgfCB7IHR5cGU6ICd0b3RhbEF0dGVtcHRzJyB9O1xuXG5jb25zdCBvd25lck9mID0gKGU6IEV2ZW50KTogc3RyaW5nIHwgdW5kZWZpbmVkID0+XG4gICdldmVudERhdGEnIGluIGUgJiZcbiAgZS5ldmVudERhdGEgJiZcbiAgJ293bmVyTWVzc2FnZUlkJyBpbiBlLmV2ZW50RGF0YSAmJlxuICB0eXBlb2YgZS5ldmVudERhdGEub3duZXJNZXNzYWdlSWQgPT09ICdzdHJpbmcnXG4gICAgPyBlLmV2ZW50RGF0YS5vd25lck1lc3NhZ2VJZFxuICAgIDogdW5kZWZpbmVkO1xuXG4vKipcbiAqIE51bWJlciBvZiBgc3RlcF9zdGFydGVkYCBldmVudHMgYWxyZWFkeSByZWNvcmRlZCBmb3IgYSBzdGVwLCB1c2VkIGFzIHRoZVxuICogYXV0aG9yaXRhdGl2ZSBhdHRlbXB0IGNvdW50IGZvciB0aGUgbWF4UmV0cmllcyBjZWlsaW5nICh0aGUgb25seSBib3VuZCBmb3JcbiAqIHN0ZXBzIHRoYXQgdGltZSBvdXQgd2l0aG91dCB3cml0aW5nIGEgYHN0ZXBfZmFpbGVkYCkuXG4gKlxuICogSU1QT1JUQU5UOiB0aGUgcmF3ICh1bnNjb3BlZCkgY291bnQgaXMgTk9UIGEgcmVsaWFibGUgYXR0ZW1wdCBudW1iZXIuXG4gKiBDb25jdXJyZW50IGludm9jYXRpb25zIHJhY2luZyBvbiB0aGUgc2FtZSBwZW5kaW5nIGJhdGNoIChzdGFsZSByZXBsYXlzLFxuICogd2FrZSByZXBsYXlzLCBhIHN0ZXAgbWVzc2FnZSBkaXNwYXRjaGVkIGJ5IGEgcmVwbGF5IHRoYXQgbG9zdCB0aGVcbiAqIGNyZWF0ZS1jbGFpbSByYWNlLCAuLi4pIGNhbiBlYWNoIHdyaXRlIGEgYHN0ZXBfc3RhcnRlZGAgZm9yIHRoZSBzYW1lXG4gKiBsb2dpY2FsIGF0dGVtcHQuIFdvcmxkcyB3aXRob3V0IGFuIGF0b21pYyBzdGFydCBndWFyZCAod29ybGQtbG9jYWwpIGxldFxuICogYWxsIG9mIHRoZW0gdGhyb3VnaC4gQ291bnRpbmcgdGhvc2UgZHVwbGljYXRlcyBhcyBcImF0dGVtcHRzXCIgbWFkZSB0aGVcbiAqIG1heFJldHJpZXMgY2VpbGluZyBmaXJlIG9uIGhlYWx0aHkgcnVucyBhbmQgZmFpbCB0aGVtIHdpdGggYSBmYWxzZVxuICogXCJleGNlZWRlZCBtYXggcmV0cmllc1wiIChzZWUgd29ya2Zsb3cjMzA2OSkuXG4gKlxuICogQ2FsbGVycyB0aGVyZWZvcmUgc2NvcGUgdGhlIGNvdW50IHRvIHRoZSBzdGFydHMgdGhlaXIgY2VpbGluZyBpcyBhY3R1YWxseVxuICogYWJvdXQgdmlhIGBzY29wZWA6XG4gKiAtIHRoZSBpbmxpbmUgb3duZWQtcmVjb3ZlcnkgY2VpbGluZyBjb3VudHMgb25seSBUSElTIG1lc3NhZ2UncyBzdGFydHNcbiAqICAgKGBvd25lZEJ5YCk6IGVhY2ggcmVhbCAocmUpZGVsaXZlcnkgb2YgdGhlIG93bmluZyBtZXNzYWdlIHN0YW1wcyBpdHNcbiAqICAgYG93bmVyTWVzc2FnZUlkYCBvbiB0aGUgc3RhcnQgaXQgd3JpdGVzLCB3aGlsZSByYWNpbmcgaW52b2NhdGlvbnMgc3RhbXBcbiAqICAgdGhlaXIgb3duIElEcyAob3Igbm9uZSksIHNvIHRoZXkgbm8gbG9uZ2VyIGluZmxhdGUgdGhlIGNvdW50O1xuICogLSB0aGUgYmFja2dyb3VuZC1zdGVwIGNlaWxpbmcgY291bnRzIHRoZSBsaWZlY3ljbGUgYXR0ZW1wdCB0b3RhbFxuICogICAoYHRvdGFsQXR0ZW1wdHNgOiBiYXJlIHN0YXJ0cyBwbHVzIHRoZSBsYXJnZXN0IHNpbmdsZSBvd25lcidzIHN0YXJ0cyksXG4gKiAgIHNvIGEgc3RlcCB0aGF0IGV4aGF1c3RlZCBwYXJ0IG9mIGl0cyBidWRnZXQgdW5kZXIgaW5saW5lIG93bmVyc2hpcFxuICogICBiZWZvcmUgdHJhbnNpdGlvbmluZyB0byBxdWV1ZWQvYmFyZSByZXRyaWVzIHN0aWxsIHRyaXBzIHRoZSBjb21iaW5lZFxuICogICBjZWlsaW5nLCB3aGlsZSByYWNlcnMnIG9uZS1vZmYgc3RhbXBlZCBkdXBsaWNhdGVzIGRvbid0IGFjY3VtdWxhdGUsIGFuZFxuICogICB0aHJvdHRsZS90b28tZWFybHkgcmVkZWxpdmVyaWVzICh3aGljaCB3cml0ZSBubyBzdGFydCBhdCBhbGwpIHN0YXlcbiAqICAgZXhjbHVkZWQgYXMgYmVmb3JlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY291bnRTdGVwU3RhcnRlZEV2ZW50cyhcbiAgZXZlbnRzOiBFdmVudFtdIHwgbnVsbCB8IHVuZGVmaW5lZCxcbiAgc3RlcElkOiBzdHJpbmcsXG4gIHNjb3BlPzogU3RlcFN0YXJ0U2NvcGVcbik6IG51bWJlciB7XG4gIGlmICghZXZlbnRzKSB7XG4gICAgcmV0dXJuIDA7XG4gIH1cbiAgbGV0IGJhcmUgPSAwO1xuICBjb25zdCBieU93bmVyID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgZm9yIChjb25zdCBlIG9mIGV2ZW50cykge1xuICAgIGlmIChlLmV2ZW50VHlwZSAhPT0gJ3N0ZXBfc3RhcnRlZCcgfHwgZS5jb3JyZWxhdGlvbklkICE9PSBzdGVwSWQpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBjb25zdCBvd25lciA9IG93bmVyT2YoZSk7XG4gICAgaWYgKG93bmVyID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGJhcmUrKztcbiAgICB9IGVsc2Uge1xuICAgICAgYnlPd25lci5zZXQob3duZXIsIChieU93bmVyLmdldChvd25lcikgPz8gMCkgKyAxKTtcbiAgICB9XG4gIH1cbiAgaWYgKHNjb3BlID09PSB1bmRlZmluZWQpIHtcbiAgICBsZXQgdG90YWwgPSBiYXJlO1xuICAgIGZvciAoY29uc3QgY291bnQgb2YgYnlPd25lci52YWx1ZXMoKSkge1xuICAgICAgdG90YWwgKz0gY291bnQ7XG4gICAgfVxuICAgIHJldHVybiB0b3RhbDtcbiAgfVxuICBpZiAoc2NvcGUudHlwZSA9PT0gJ293bmVkQnknKSB7XG4gICAgcmV0dXJuIGJ5T3duZXIuZ2V0KHNjb3BlLm1lc3NhZ2VJZCkgPz8gMDtcbiAgfVxuICAvLyB0b3RhbEF0dGVtcHRzXG4gIGxldCBtYXhPd25lciA9IDA7XG4gIGZvciAoY29uc3QgY291bnQgb2YgYnlPd25lci52YWx1ZXMoKSkge1xuICAgIGlmIChjb3VudCA+IG1heE93bmVyKSB7XG4gICAgICBtYXhPd25lciA9IGNvdW50O1xuICAgIH1cbiAgfVxuICByZXR1cm4gYmFyZSArIG1heE93bmVyO1xufVxuIl19
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type WorkflowRun, type World } from '@workflow/world';
|
|
2
|
+
/**
|
|
3
|
+
* How the guard resolved a delivery. Both non-`continue` outcomes mean the
|
|
4
|
+
* caller must ack the message and return without executing anything.
|
|
5
|
+
*/
|
|
6
|
+
export type DeploymentAffinityOutcome =
|
|
7
|
+
/** The run belongs here (or this world has no deployment affinity). */
|
|
8
|
+
'continue'
|
|
9
|
+
/** Misrouted; re-enqueued at the run's own deployment. */
|
|
10
|
+
| 'rerouted'
|
|
11
|
+
/** Misrouted and out of budget (or unroutable); `run_failed` recorded. */
|
|
12
|
+
| 'failed';
|
|
13
|
+
/**
|
|
14
|
+
* `spanAttributes` is present only on a misrouted delivery: mismatches are
|
|
15
|
+
* rare, so the presence of `workflow.deployment.pinned_id` on a span *is* the
|
|
16
|
+
* signal that one happened, and `recovered` separates the ones a re-route fixed
|
|
17
|
+
* from the ones that kept misrouting.
|
|
18
|
+
*/
|
|
19
|
+
export interface DeploymentAffinityResult {
|
|
20
|
+
outcome: DeploymentAffinityOutcome;
|
|
21
|
+
spanAttributes?: Record<string, string | number | boolean>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Arguments handed to a call site's re-enqueue closure. The guard owns the
|
|
25
|
+
* policy (counting, backoff, logging, telemetry, escalation) and the call
|
|
26
|
+
* site owns only the shape of the message it needs to put back on the queue.
|
|
27
|
+
*/
|
|
28
|
+
export interface ReenqueueArgs {
|
|
29
|
+
/** The deployment to target: the one the run is pinned to. */
|
|
30
|
+
deploymentId: string;
|
|
31
|
+
/**
|
|
32
|
+
* The run's spec version, so the world picks the right queue transport
|
|
33
|
+
* (CBOR vs JSON). Undefined on legacy runs that predate the field; worlds
|
|
34
|
+
* then fall back to their current default.
|
|
35
|
+
*/
|
|
36
|
+
specVersion: number | undefined;
|
|
37
|
+
deploymentMismatchRetryCount: number;
|
|
38
|
+
/** Backoff before the re-routed message becomes visible. */
|
|
39
|
+
delaySeconds: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Guards deployment affinity: a run may only execute on the deployment it is
|
|
43
|
+
* pinned to.
|
|
44
|
+
*
|
|
45
|
+
* A run's `deploymentId` is fixed when it starts: the deployment that called
|
|
46
|
+
* `start()`, or whatever `start({ deploymentId })` resolved to (an explicit id
|
|
47
|
+
* or `'latest'`). No replay or step execution may happen anywhere else:
|
|
48
|
+
* the bundles here may not match the run's persisted history, and any step
|
|
49
|
+
* dispatched from here derives the per-run encryption key from the wrong
|
|
50
|
+
* deployment's master key, which surfaces as a `RuntimeDecryptionError` the
|
|
51
|
+
* queue retry callback swallows into a blank "exceeded max retries".
|
|
52
|
+
*
|
|
53
|
+
* A misrouted delivery is not treated as permanent. Rather than failing
|
|
54
|
+
* immediately, re-enqueue the message *explicitly targeted* at the run's own
|
|
55
|
+
* deployment; that send is strictly better-addressed than the one that
|
|
56
|
+
* misrouted, which inherited the producing deployment's ambient id. Fail only
|
|
57
|
+
* once the budget (`WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES`, default 3) is
|
|
58
|
+
* spent, mirroring how a replay divergence gets bounded recovery replays before
|
|
59
|
+
* being recorded as a corrupted event log.
|
|
60
|
+
*
|
|
61
|
+
* Callers must pass a run entity they already have in hand (every call site
|
|
62
|
+
* loads the run for other reasons), so the guard costs no extra round trip.
|
|
63
|
+
* (`world.getDeploymentId()` reads the ambient deployment id, e.g.
|
|
64
|
+
* `VERCEL_DEPLOYMENT_ID`, and does not call the backend either.)
|
|
65
|
+
*
|
|
66
|
+
* The failure is recorded **without resolving the run's encryption key**: the
|
|
67
|
+
* key is fetched from the pinned deployment's API, often unavailable once that
|
|
68
|
+
* deployment is past its retention window, so depending on it would throw here
|
|
69
|
+
* instead of recording the failure. The payload is written unencrypted (it
|
|
70
|
+
* holds only deployment ids, nothing sensitive), and the plaintext `errorCode`
|
|
71
|
+
* is the signal observability and the UI key off.
|
|
72
|
+
*/
|
|
73
|
+
export declare function guardDeploymentAffinity({ world, run, requestId, retryCount, reenqueue, isDeploymentUnavailableError, beforeStop, }: {
|
|
74
|
+
world: World;
|
|
75
|
+
run: Pick<WorkflowRun, 'runId' | 'deploymentId' | 'specVersion'>;
|
|
76
|
+
requestId?: string;
|
|
77
|
+
/** `deploymentMismatchRetryCount` from the incoming message, if any. */
|
|
78
|
+
retryCount?: number;
|
|
79
|
+
/**
|
|
80
|
+
* Puts this delivery's message back on the queue, targeted at the run's own
|
|
81
|
+
* deployment. Omitted by callers that cannot reconstruct their message, which
|
|
82
|
+
* makes the guard fail-fast.
|
|
83
|
+
*/
|
|
84
|
+
reenqueue?: (args: ReenqueueArgs) => Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Classifies a re-enqueue failure as definitive proof that the pinned
|
|
87
|
+
* deployment cannot receive the message. Unclassified failures are retried
|
|
88
|
+
* by rejecting the handler and leaving the current queue message unacked.
|
|
89
|
+
*/
|
|
90
|
+
isDeploymentUnavailableError?: (error: unknown) => boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Ordering barrier awaited once a mismatch is confirmed, before either
|
|
93
|
+
* stopping action. Under turbo the `run_started` write is backgrounded, and
|
|
94
|
+
* both outcomes hand the run off (to a `run_failed` here, or to the pinned
|
|
95
|
+
* deployment's own `run_started`), so that write must have landed first.
|
|
96
|
+
*/
|
|
97
|
+
beforeStop?: () => Promise<void>;
|
|
98
|
+
}): Promise<DeploymentAffinityResult>;
|
|
99
|
+
//# sourceMappingURL=deployment-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment-guard.d.ts","sourceRoot":"","sources":["../../src/runtime/deployment-guard.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,KAAK,EACX,MAAM,iBAAiB,CAAC;AASzB;;;GAGG;AACH,MAAM,MAAM,yBAAyB;AACnC,uEAAuE;AACrE,UAAU;AACZ,0DAA0D;GACxD,UAAU;AACZ,0EAA0E;GACxE,QAAQ,CAAC;AAEb;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,yBAAyB,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAC5D;AAwBD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,4BAA4B,EAAE,MAAM,CAAC;IACrC,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,uBAAuB,CAAC,EAC5C,KAAK,EACL,GAAG,EACH,SAAS,EACT,UAAc,EACd,SAAS,EACT,4BAA4B,EAC5B,UAAU,GACX,EAAE;IACD,KAAK,EAAE,KAAK,CAAC;IACb,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,cAAc,GAAG,aAAa,CAAC,CAAC;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD;;;;OAIG;IACH,4BAA4B,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAC3D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAoIpC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { EntityConflictError, RUN_ERROR_CODES, RunExpiredError, WorkflowDeploymentMismatchError, } from '@workflow/errors';
|
|
2
|
+
import { SPEC_VERSION_CURRENT, SPEC_VERSION_SUPPORTS_COMPRESSION, } from '@workflow/world';
|
|
3
|
+
import { runtimeLogger } from '../logger.js';
|
|
4
|
+
import { dehydrateRunError } from '../serialization.js';
|
|
5
|
+
import * as Attribute from '../telemetry/semantic-conventions.js';
|
|
6
|
+
import { getDeploymentMismatchMaxRetries } from './constants.js';
|
|
7
|
+
/** Cap on the re-route backoff, in seconds. */
|
|
8
|
+
const MAX_REROUTE_DELAY_SECONDS = 8;
|
|
9
|
+
/** The common case, allocation-free. */
|
|
10
|
+
const CONTINUE = { outcome: 'continue' };
|
|
11
|
+
/**
|
|
12
|
+
* Builds a stopping result. `attempts` is the re-route count *including* this
|
|
13
|
+
* invocation's own attempt, so the span attribute reads as "attempts so far".
|
|
14
|
+
*/
|
|
15
|
+
function result(outcome, pinnedDeploymentId, attempts) {
|
|
16
|
+
return {
|
|
17
|
+
outcome,
|
|
18
|
+
spanAttributes: {
|
|
19
|
+
...Attribute.WorkflowRunPinnedDeploymentId(pinnedDeploymentId),
|
|
20
|
+
...Attribute.WorkflowDeploymentMismatchRetryCount(attempts),
|
|
21
|
+
...Attribute.WorkflowDeploymentMismatchRecovered(outcome === 'rerouted'),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Guards deployment affinity: a run may only execute on the deployment it is
|
|
27
|
+
* pinned to.
|
|
28
|
+
*
|
|
29
|
+
* A run's `deploymentId` is fixed when it starts: the deployment that called
|
|
30
|
+
* `start()`, or whatever `start({ deploymentId })` resolved to (an explicit id
|
|
31
|
+
* or `'latest'`). No replay or step execution may happen anywhere else:
|
|
32
|
+
* the bundles here may not match the run's persisted history, and any step
|
|
33
|
+
* dispatched from here derives the per-run encryption key from the wrong
|
|
34
|
+
* deployment's master key, which surfaces as a `RuntimeDecryptionError` the
|
|
35
|
+
* queue retry callback swallows into a blank "exceeded max retries".
|
|
36
|
+
*
|
|
37
|
+
* A misrouted delivery is not treated as permanent. Rather than failing
|
|
38
|
+
* immediately, re-enqueue the message *explicitly targeted* at the run's own
|
|
39
|
+
* deployment; that send is strictly better-addressed than the one that
|
|
40
|
+
* misrouted, which inherited the producing deployment's ambient id. Fail only
|
|
41
|
+
* once the budget (`WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES`, default 3) is
|
|
42
|
+
* spent, mirroring how a replay divergence gets bounded recovery replays before
|
|
43
|
+
* being recorded as a corrupted event log.
|
|
44
|
+
*
|
|
45
|
+
* Callers must pass a run entity they already have in hand (every call site
|
|
46
|
+
* loads the run for other reasons), so the guard costs no extra round trip.
|
|
47
|
+
* (`world.getDeploymentId()` reads the ambient deployment id, e.g.
|
|
48
|
+
* `VERCEL_DEPLOYMENT_ID`, and does not call the backend either.)
|
|
49
|
+
*
|
|
50
|
+
* The failure is recorded **without resolving the run's encryption key**: the
|
|
51
|
+
* key is fetched from the pinned deployment's API, often unavailable once that
|
|
52
|
+
* deployment is past its retention window, so depending on it would throw here
|
|
53
|
+
* instead of recording the failure. The payload is written unencrypted (it
|
|
54
|
+
* holds only deployment ids, nothing sensitive), and the plaintext `errorCode`
|
|
55
|
+
* is the signal observability and the UI key off.
|
|
56
|
+
*/
|
|
57
|
+
export async function guardDeploymentAffinity({ world, run, requestId, retryCount = 0, reenqueue, isDeploymentUnavailableError, beforeStop, }) {
|
|
58
|
+
// Deployment affinity is only meaningful in worlds whose deployments are
|
|
59
|
+
// atomic and immutable, which the World declares (world-vercel does) and
|
|
60
|
+
// capabilities default to unsupported. Local and self-hosted worlds report a
|
|
61
|
+
// synthetic deployment id (e.g. `dpl_local@<sdk-version>`) that legitimately
|
|
62
|
+
// differs across SDK versions within one logical environment, so guarding
|
|
63
|
+
// them would fail ordinary local runs after a version bump.
|
|
64
|
+
if (world.capabilities?.deploymentAffinity !== true) {
|
|
65
|
+
return CONTINUE;
|
|
66
|
+
}
|
|
67
|
+
const currentDeploymentId = await world.getDeploymentId();
|
|
68
|
+
if (run.deploymentId === currentDeploymentId) {
|
|
69
|
+
return CONTINUE;
|
|
70
|
+
}
|
|
71
|
+
await beforeStop?.();
|
|
72
|
+
const maxRetries = getDeploymentMismatchMaxRetries();
|
|
73
|
+
const logContext = {
|
|
74
|
+
workflowRunId: run.runId,
|
|
75
|
+
pinnedDeploymentId: run.deploymentId,
|
|
76
|
+
actualDeploymentId: currentDeploymentId,
|
|
77
|
+
retryCount,
|
|
78
|
+
maxRetries,
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Records the terminal `run_failed`. Mirrors `recordFatalRunError`: it
|
|
82
|
+
* records and returns rather than throwing, so the caller acks the message
|
|
83
|
+
* instead of letting the queue redeliver a run that is now terminal.
|
|
84
|
+
*/
|
|
85
|
+
const failRun = async (recoveryAttempts, cause) => {
|
|
86
|
+
const error = new WorkflowDeploymentMismatchError(run.runId, run.deploymentId, currentDeploymentId, { recoveryAttempts, cause });
|
|
87
|
+
try {
|
|
88
|
+
await world.events.create(run.runId, {
|
|
89
|
+
eventType: 'run_failed',
|
|
90
|
+
specVersion: SPEC_VERSION_CURRENT,
|
|
91
|
+
eventData: {
|
|
92
|
+
// Unencrypted (undefined key): see the note on
|
|
93
|
+
// `guardDeploymentAffinity`. dehydrate/hydrate are self-describing,
|
|
94
|
+
// so this reads back without a key.
|
|
95
|
+
error: await dehydrateRunError(error, run.runId, undefined, undefined, (run.specVersion ?? 0) >= SPEC_VERSION_SUPPORTS_COMPRESSION),
|
|
96
|
+
errorCode: RUN_ERROR_CODES.DEPLOYMENT_MISMATCH,
|
|
97
|
+
},
|
|
98
|
+
}, { requestId });
|
|
99
|
+
}
|
|
100
|
+
catch (failError) {
|
|
101
|
+
// Run already reached a terminal state (a concurrent writer failed it, or
|
|
102
|
+
// it was canceled/expired), so still stop. Anything else is a transient
|
|
103
|
+
// persistence failure: rethrow so the queue redelivers and we try again.
|
|
104
|
+
if (!EntityConflictError.is(failError) &&
|
|
105
|
+
!RunExpiredError.is(failError)) {
|
|
106
|
+
throw failError;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result('failed', run.deploymentId, recoveryAttempts);
|
|
110
|
+
};
|
|
111
|
+
if (reenqueue && retryCount < maxRetries) {
|
|
112
|
+
const attempt = retryCount + 1;
|
|
113
|
+
// Exponential, capped: 1s, 2s, 4s. Cheap insurance against a hot
|
|
114
|
+
// re-enqueue loop, and time for a mid-flight routing change to settle.
|
|
115
|
+
const delaySeconds = Math.min(2 ** retryCount, MAX_REROUTE_DELAY_SECONDS);
|
|
116
|
+
try {
|
|
117
|
+
await reenqueue({
|
|
118
|
+
deploymentId: run.deploymentId,
|
|
119
|
+
specVersion: run.specVersion,
|
|
120
|
+
deploymentMismatchRetryCount: attempt,
|
|
121
|
+
delaySeconds,
|
|
122
|
+
});
|
|
123
|
+
runtimeLogger.warn('Workflow run received by a deployment it is not pinned to; re-routing to its own deployment', { ...logContext, attempt, delaySeconds });
|
|
124
|
+
return result('rerouted', run.deploymentId, attempt);
|
|
125
|
+
}
|
|
126
|
+
catch (reenqueueError) {
|
|
127
|
+
// A generic send failure can be transient (429, 5xx, connect/DNS). Do not
|
|
128
|
+
// turn one infrastructure blip into a terminal run failure: reject the
|
|
129
|
+
// handler so the current message remains unacked and is redelivered.
|
|
130
|
+
if (!isDeploymentUnavailableError?.(reenqueueError)) {
|
|
131
|
+
throw reenqueueError;
|
|
132
|
+
}
|
|
133
|
+
// The World explicitly classified the target as unavailable: deleted,
|
|
134
|
+
// aged out of its retention window, or otherwise undiscoverable. Burning
|
|
135
|
+
// the rest of the budget on a deployment that provably cannot be reached
|
|
136
|
+
// only delays the failure, so fail now.
|
|
137
|
+
runtimeLogger.error('Cannot re-route a misrouted workflow run to its own deployment; failing the run', {
|
|
138
|
+
...logContext,
|
|
139
|
+
attempt,
|
|
140
|
+
errorMessage: reenqueueError instanceof Error
|
|
141
|
+
? reenqueueError.message
|
|
142
|
+
: String(reenqueueError),
|
|
143
|
+
});
|
|
144
|
+
// Recovery never actually happened, so report 0 attempts and let `cause`
|
|
145
|
+
// explain why.
|
|
146
|
+
return failRun(0, reenqueueError);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
runtimeLogger.error(retryCount > 0
|
|
150
|
+
? 'Workflow run kept arriving at a deployment it is not pinned to after re-routing; failing the run'
|
|
151
|
+
: 'Workflow run received by a deployment it is not pinned to; failing the run', logContext);
|
|
152
|
+
return failRun(retryCount);
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwbG95bWVudC1ndWFyZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2RlcGxveW1lbnQtZ3VhcmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLG1CQUFtQixFQUNuQixlQUFlLEVBQ2YsZUFBZSxFQUNmLCtCQUErQixHQUNoQyxNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsaUNBQWlDLEdBR2xDLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM3QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUN4RCxPQUFPLEtBQUssU0FBUyxNQUFNLHNDQUFzQyxDQUFDO0FBQ2xFLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWpFLCtDQUErQztBQUMvQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsQ0FBQztBQXlCcEMsd0NBQXdDO0FBQ3hDLE1BQU0sUUFBUSxHQUE2QixFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQztBQUVuRTs7O0dBR0c7QUFDSCxTQUFTLE1BQU0sQ0FDYixPQUE4QixFQUM5QixrQkFBMEIsRUFDMUIsUUFBZ0I7SUFFaEIsT0FBTztRQUNMLE9BQU87UUFDUCxjQUFjLEVBQUU7WUFDZCxHQUFHLFNBQVMsQ0FBQyw2QkFBNkIsQ0FBQyxrQkFBa0IsQ0FBQztZQUM5RCxHQUFHLFNBQVMsQ0FBQyxvQ0FBb0MsQ0FBQyxRQUFRLENBQUM7WUFDM0QsR0FBRyxTQUFTLENBQUMsbUNBQW1DLENBQUMsT0FBTyxLQUFLLFVBQVUsQ0FBQztTQUN6RTtLQUNGLENBQUM7QUFDSixDQUFDO0FBcUJEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBK0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSx1QkFBdUIsQ0FBQyxFQUM1QyxLQUFLLEVBQ0wsR0FBRyxFQUNILFNBQVMsRUFDVCxVQUFVLEdBQUcsQ0FBQyxFQUNkLFNBQVMsRUFDVCw0QkFBNEIsRUFDNUIsVUFBVSxHQTBCWDtJQUNDLHlFQUF5RTtJQUN6RSx5RUFBeUU7SUFDekUsNkVBQTZFO0lBQzdFLDZFQUE2RTtJQUM3RSwwRUFBMEU7SUFDMUUsNERBQTREO0lBQzVELElBQUksS0FBSyxDQUFDLFlBQVksRUFBRSxrQkFBa0IsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNwRCxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0lBRUQsTUFBTSxtQkFBbUIsR0FBRyxNQUFNLEtBQUssQ0FBQyxlQUFlLEVBQUUsQ0FBQztJQUMxRCxJQUFJLEdBQUcsQ0FBQyxZQUFZLEtBQUssbUJBQW1CLEVBQUUsQ0FBQztRQUM3QyxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0lBRUQsTUFBTSxVQUFVLEVBQUUsRUFBRSxDQUFDO0lBRXJCLE1BQU0sVUFBVSxHQUFHLCtCQUErQixFQUFFLENBQUM7SUFDckQsTUFBTSxVQUFVLEdBQUc7UUFDakIsYUFBYSxFQUFFLEdBQUcsQ0FBQyxLQUFLO1FBQ3hCLGtCQUFrQixFQUFFLEdBQUcsQ0FBQyxZQUFZO1FBQ3BDLGtCQUFrQixFQUFFLG1CQUFtQjtRQUN2QyxVQUFVO1FBQ1YsVUFBVTtLQUNYLENBQUM7SUFFRjs7OztPQUlHO0lBQ0gsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUNuQixnQkFBd0IsRUFDeEIsS0FBZSxFQUNvQixFQUFFO1FBQ3JDLE1BQU0sS0FBSyxHQUFHLElBQUksK0JBQStCLENBQy9DLEdBQUcsQ0FBQyxLQUFLLEVBQ1QsR0FBRyxDQUFDLFlBQVksRUFDaEIsbUJBQW1CLEVBQ25CLEVBQUUsZ0JBQWdCLEVBQUUsS0FBSyxFQUFFLENBQzVCLENBQUM7UUFFRixJQUFJLENBQUM7WUFDSCxNQUFNLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUN2QixHQUFHLENBQUMsS0FBSyxFQUNUO2dCQUNFLFNBQVMsRUFBRSxZQUFZO2dCQUN2QixXQUFXLEVBQUUsb0JBQW9CO2dCQUNqQyxTQUFTLEVBQUU7b0JBQ1QsK0NBQStDO29CQUMvQyxvRUFBb0U7b0JBQ3BFLG9DQUFvQztvQkFDcEMsS0FBSyxFQUFFLE1BQU0saUJBQWlCLENBQzVCLEtBQUssRUFDTCxHQUFHLENBQUMsS0FBSyxFQUNULFNBQVMsRUFDVCxTQUFTLEVBQ1QsQ0FBQyxHQUFHLENBQUMsV0FBVyxJQUFJLENBQUMsQ0FBQyxJQUFJLGlDQUFpQyxDQUM1RDtvQkFDRCxTQUFTLEVBQUUsZUFBZSxDQUFDLG1CQUFtQjtpQkFDL0M7YUFDRixFQUNELEVBQUUsU0FBUyxFQUFFLENBQ2QsQ0FBQztRQUNKLENBQUM7UUFBQyxPQUFPLFNBQVMsRUFBRSxDQUFDO1lBQ25CLDBFQUEwRTtZQUMxRSx3RUFBd0U7WUFDeEUseUVBQXlFO1lBQ3pFLElBQ0UsQ0FBQyxtQkFBbUIsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDO2dCQUNsQyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLEVBQzlCLENBQUM7Z0JBQ0QsTUFBTSxTQUFTLENBQUM7WUFDbEIsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLFlBQVksRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO0lBQzlELENBQUMsQ0FBQztJQUVGLElBQUksU0FBUyxJQUFJLFVBQVUsR0FBRyxVQUFVLEVBQUUsQ0FBQztRQUN6QyxNQUFNLE9BQU8sR0FBRyxVQUFVLEdBQUcsQ0FBQyxDQUFDO1FBQy9CLGlFQUFpRTtRQUNqRSx1RUFBdUU7UUFDdkUsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksVUFBVSxFQUFFLHlCQUF5QixDQUFDLENBQUM7UUFDMUUsSUFBSSxDQUFDO1lBQ0gsTUFBTSxTQUFTLENBQUM7Z0JBQ2QsWUFBWSxFQUFFLEdBQUcsQ0FBQyxZQUFZO2dCQUM5QixXQUFXLEVBQUUsR0FBRyxDQUFDLFdBQVc7Z0JBQzVCLDRCQUE0QixFQUFFLE9BQU87Z0JBQ3JDLFlBQVk7YUFDYixDQUFDLENBQUM7WUFDSCxhQUFhLENBQUMsSUFBSSxDQUNoQiw2RkFBNkYsRUFDN0YsRUFBRSxHQUFHLFVBQVUsRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLENBQ3pDLENBQUM7WUFDRixPQUFPLE1BQU0sQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDLFlBQVksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN2RCxDQUFDO1FBQUMsT0FBTyxjQUFjLEVBQUUsQ0FBQztZQUN4QiwwRUFBMEU7WUFDMUUsdUVBQXVFO1lBQ3ZFLHFFQUFxRTtZQUNyRSxJQUFJLENBQUMsNEJBQTRCLEVBQUUsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO2dCQUNwRCxNQUFNLGNBQWMsQ0FBQztZQUN2QixDQUFDO1lBRUQsc0VBQXNFO1lBQ3RFLHlFQUF5RTtZQUN6RSx5RUFBeUU7WUFDekUsd0NBQXdDO1lBQ3hDLGFBQWEsQ0FBQyxLQUFLLENBQ2pCLGlGQUFpRixFQUNqRjtnQkFDRSxHQUFHLFVBQVU7Z0JBQ2IsT0FBTztnQkFDUCxZQUFZLEVBQ1YsY0FBYyxZQUFZLEtBQUs7b0JBQzdCLENBQUMsQ0FBQyxjQUFjLENBQUMsT0FBTztvQkFDeEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUM7YUFDN0IsQ0FDRixDQUFDO1lBQ0YseUVBQXlFO1lBQ3pFLGVBQWU7WUFDZixPQUFPLE9BQU8sQ0FBQyxDQUFDLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDcEMsQ0FBQztJQUNILENBQUM7SUFFRCxhQUFhLENBQUMsS0FBSyxDQUNqQixVQUFVLEdBQUcsQ0FBQztRQUNaLENBQUMsQ0FBQyxrR0FBa0c7UUFDcEcsQ0FBQyxDQUFDLDRFQUE0RSxFQUNoRixVQUFVLENBQ1gsQ0FBQztJQUNGLE9BQU8sT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0FBQzdCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBFbnRpdHlDb25mbGljdEVycm9yLFxuICBSVU5fRVJST1JfQ09ERVMsXG4gIFJ1bkV4cGlyZWRFcnJvcixcbiAgV29ya2Zsb3dEZXBsb3ltZW50TWlzbWF0Y2hFcnJvcixcbn0gZnJvbSAnQHdvcmtmbG93L2Vycm9ycyc7XG5pbXBvcnQge1xuICBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgU1BFQ19WRVJTSU9OX1NVUFBPUlRTX0NPTVBSRVNTSU9OLFxuICB0eXBlIFdvcmtmbG93UnVuLFxuICB0eXBlIFdvcmxkLFxufSBmcm9tICdAd29ya2Zsb3cvd29ybGQnO1xuaW1wb3J0IHsgcnVudGltZUxvZ2dlciB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBkZWh5ZHJhdGVSdW5FcnJvciB9IGZyb20gJy4uL3NlcmlhbGl6YXRpb24uanMnO1xuaW1wb3J0ICogYXMgQXR0cmlidXRlIGZyb20gJy4uL3RlbGVtZXRyeS9zZW1hbnRpYy1jb252ZW50aW9ucy5qcyc7XG5pbXBvcnQgeyBnZXREZXBsb3ltZW50TWlzbWF0Y2hNYXhSZXRyaWVzIH0gZnJvbSAnLi9jb25zdGFudHMuanMnO1xuXG4vKiogQ2FwIG9uIHRoZSByZS1yb3V0ZSBiYWNrb2ZmLCBpbiBzZWNvbmRzLiAqL1xuY29uc3QgTUFYX1JFUk9VVEVfREVMQVlfU0VDT05EUyA9IDg7XG5cbi8qKlxuICogSG93IHRoZSBndWFyZCByZXNvbHZlZCBhIGRlbGl2ZXJ5LiBCb3RoIG5vbi1gY29udGludWVgIG91dGNvbWVzIG1lYW4gdGhlXG4gKiBjYWxsZXIgbXVzdCBhY2sgdGhlIG1lc3NhZ2UgYW5kIHJldHVybiB3aXRob3V0IGV4ZWN1dGluZyBhbnl0aGluZy5cbiAqL1xuZXhwb3J0IHR5cGUgRGVwbG95bWVudEFmZmluaXR5T3V0Y29tZSA9XG4gIC8qKiBUaGUgcnVuIGJlbG9uZ3MgaGVyZSAob3IgdGhpcyB3b3JsZCBoYXMgbm8gZGVwbG95bWVudCBhZmZpbml0eSkuICovXG4gIHwgJ2NvbnRpbnVlJ1xuICAvKiogTWlzcm91dGVkOyByZS1lbnF1ZXVlZCBhdCB0aGUgcnVuJ3Mgb3duIGRlcGxveW1lbnQuICovXG4gIHwgJ3Jlcm91dGVkJ1xuICAvKiogTWlzcm91dGVkIGFuZCBvdXQgb2YgYnVkZ2V0IChvciB1bnJvdXRhYmxlKTsgYHJ1bl9mYWlsZWRgIHJlY29yZGVkLiAqL1xuICB8ICdmYWlsZWQnO1xuXG4vKipcbiAqIGBzcGFuQXR0cmlidXRlc2AgaXMgcHJlc2VudCBvbmx5IG9uIGEgbWlzcm91dGVkIGRlbGl2ZXJ5OiBtaXNtYXRjaGVzIGFyZVxuICogcmFyZSwgc28gdGhlIHByZXNlbmNlIG9mIGB3b3JrZmxvdy5kZXBsb3ltZW50LnBpbm5lZF9pZGAgb24gYSBzcGFuICppcyogdGhlXG4gKiBzaWduYWwgdGhhdCBvbmUgaGFwcGVuZWQsIGFuZCBgcmVjb3ZlcmVkYCBzZXBhcmF0ZXMgdGhlIG9uZXMgYSByZS1yb3V0ZSBmaXhlZFxuICogZnJvbSB0aGUgb25lcyB0aGF0IGtlcHQgbWlzcm91dGluZy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEZXBsb3ltZW50QWZmaW5pdHlSZXN1bHQge1xuICBvdXRjb21lOiBEZXBsb3ltZW50QWZmaW5pdHlPdXRjb21lO1xuICBzcGFuQXR0cmlidXRlcz86IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IG51bWJlciB8IGJvb2xlYW4+O1xufVxuXG4vKiogVGhlIGNvbW1vbiBjYXNlLCBhbGxvY2F0aW9uLWZyZWUuICovXG5jb25zdCBDT05USU5VRTogRGVwbG95bWVudEFmZmluaXR5UmVzdWx0ID0geyBvdXRjb21lOiAnY29udGludWUnIH07XG5cbi8qKlxuICogQnVpbGRzIGEgc3RvcHBpbmcgcmVzdWx0LiBgYXR0ZW1wdHNgIGlzIHRoZSByZS1yb3V0ZSBjb3VudCAqaW5jbHVkaW5nKiB0aGlzXG4gKiBpbnZvY2F0aW9uJ3Mgb3duIGF0dGVtcHQsIHNvIHRoZSBzcGFuIGF0dHJpYnV0ZSByZWFkcyBhcyBcImF0dGVtcHRzIHNvIGZhclwiLlxuICovXG5mdW5jdGlvbiByZXN1bHQoXG4gIG91dGNvbWU6ICdyZXJvdXRlZCcgfCAnZmFpbGVkJyxcbiAgcGlubmVkRGVwbG95bWVudElkOiBzdHJpbmcsXG4gIGF0dGVtcHRzOiBudW1iZXJcbik6IERlcGxveW1lbnRBZmZpbml0eVJlc3VsdCB7XG4gIHJldHVybiB7XG4gICAgb3V0Y29tZSxcbiAgICBzcGFuQXR0cmlidXRlczoge1xuICAgICAgLi4uQXR0cmlidXRlLldvcmtmbG93UnVuUGlubmVkRGVwbG95bWVudElkKHBpbm5lZERlcGxveW1lbnRJZCksXG4gICAgICAuLi5BdHRyaWJ1dGUuV29ya2Zsb3dEZXBsb3ltZW50TWlzbWF0Y2hSZXRyeUNvdW50KGF0dGVtcHRzKSxcbiAgICAgIC4uLkF0dHJpYnV0ZS5Xb3JrZmxvd0RlcGxveW1lbnRNaXNtYXRjaFJlY292ZXJlZChvdXRjb21lID09PSAncmVyb3V0ZWQnKSxcbiAgICB9LFxuICB9O1xufVxuXG4vKipcbiAqIEFyZ3VtZW50cyBoYW5kZWQgdG8gYSBjYWxsIHNpdGUncyByZS1lbnF1ZXVlIGNsb3N1cmUuIFRoZSBndWFyZCBvd25zIHRoZVxuICogcG9saWN5IChjb3VudGluZywgYmFja29mZiwgbG9nZ2luZywgdGVsZW1ldHJ5LCBlc2NhbGF0aW9uKSBhbmQgdGhlIGNhbGxcbiAqIHNpdGUgb3ducyBvbmx5IHRoZSBzaGFwZSBvZiB0aGUgbWVzc2FnZSBpdCBuZWVkcyB0byBwdXQgYmFjayBvbiB0aGUgcXVldWUuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVlbnF1ZXVlQXJncyB7XG4gIC8qKiBUaGUgZGVwbG95bWVudCB0byB0YXJnZXQ6IHRoZSBvbmUgdGhlIHJ1biBpcyBwaW5uZWQgdG8uICovXG4gIGRlcGxveW1lbnRJZDogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIHJ1bidzIHNwZWMgdmVyc2lvbiwgc28gdGhlIHdvcmxkIHBpY2tzIHRoZSByaWdodCBxdWV1ZSB0cmFuc3BvcnRcbiAgICogKENCT1IgdnMgSlNPTikuIFVuZGVmaW5lZCBvbiBsZWdhY3kgcnVucyB0aGF0IHByZWRhdGUgdGhlIGZpZWxkOyB3b3JsZHNcbiAgICogdGhlbiBmYWxsIGJhY2sgdG8gdGhlaXIgY3VycmVudCBkZWZhdWx0LlxuICAgKi9cbiAgc3BlY1ZlcnNpb246IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgZGVwbG95bWVudE1pc21hdGNoUmV0cnlDb3VudDogbnVtYmVyO1xuICAvKiogQmFja29mZiBiZWZvcmUgdGhlIHJlLXJvdXRlZCBtZXNzYWdlIGJlY29tZXMgdmlzaWJsZS4gKi9cbiAgZGVsYXlTZWNvbmRzOiBudW1iZXI7XG59XG5cbi8qKlxuICogR3VhcmRzIGRlcGxveW1lbnQgYWZmaW5pdHk6IGEgcnVuIG1heSBvbmx5IGV4ZWN1dGUgb24gdGhlIGRlcGxveW1lbnQgaXQgaXNcbiAqIHBpbm5lZCB0by5cbiAqXG4gKiBBIHJ1bidzIGBkZXBsb3ltZW50SWRgIGlzIGZpeGVkIHdoZW4gaXQgc3RhcnRzOiB0aGUgZGVwbG95bWVudCB0aGF0IGNhbGxlZFxuICogYHN0YXJ0KClgLCBvciB3aGF0ZXZlciBgc3RhcnQoeyBkZXBsb3ltZW50SWQgfSlgIHJlc29sdmVkIHRvIChhbiBleHBsaWNpdCBpZFxuICogb3IgYCdsYXRlc3QnYCkuIE5vIHJlcGxheSBvciBzdGVwIGV4ZWN1dGlvbiBtYXkgaGFwcGVuIGFueXdoZXJlIGVsc2U6XG4gKiB0aGUgYnVuZGxlcyBoZXJlIG1heSBub3QgbWF0Y2ggdGhlIHJ1bidzIHBlcnNpc3RlZCBoaXN0b3J5LCBhbmQgYW55IHN0ZXBcbiAqIGRpc3BhdGNoZWQgZnJvbSBoZXJlIGRlcml2ZXMgdGhlIHBlci1ydW4gZW5jcnlwdGlvbiBrZXkgZnJvbSB0aGUgd3JvbmdcbiAqIGRlcGxveW1lbnQncyBtYXN0ZXIga2V5LCB3aGljaCBzdXJmYWNlcyBhcyBhIGBSdW50aW1lRGVjcnlwdGlvbkVycm9yYCB0aGVcbiAqIHF1ZXVlIHJldHJ5IGNhbGxiYWNrIHN3YWxsb3dzIGludG8gYSBibGFuayBcImV4Y2VlZGVkIG1heCByZXRyaWVzXCIuXG4gKlxuICogQSBtaXNyb3V0ZWQgZGVsaXZlcnkgaXMgbm90IHRyZWF0ZWQgYXMgcGVybWFuZW50LiBSYXRoZXIgdGhhbiBmYWlsaW5nXG4gKiBpbW1lZGlhdGVseSwgcmUtZW5xdWV1ZSB0aGUgbWVzc2FnZSAqZXhwbGljaXRseSB0YXJnZXRlZCogYXQgdGhlIHJ1bidzIG93blxuICogZGVwbG95bWVudDsgdGhhdCBzZW5kIGlzIHN0cmljdGx5IGJldHRlci1hZGRyZXNzZWQgdGhhbiB0aGUgb25lIHRoYXRcbiAqIG1pc3JvdXRlZCwgd2hpY2ggaW5oZXJpdGVkIHRoZSBwcm9kdWNpbmcgZGVwbG95bWVudCdzIGFtYmllbnQgaWQuIEZhaWwgb25seVxuICogb25jZSB0aGUgYnVkZ2V0IChgV09SS0ZMT1dfREVQTE9ZTUVOVF9NSVNNQVRDSF9NQVhfUkVUUklFU2AsIGRlZmF1bHQgMykgaXNcbiAqIHNwZW50LCBtaXJyb3JpbmcgaG93IGEgcmVwbGF5IGRpdmVyZ2VuY2UgZ2V0cyBib3VuZGVkIHJlY292ZXJ5IHJlcGxheXMgYmVmb3JlXG4gKiBiZWluZyByZWNvcmRlZCBhcyBhIGNvcnJ1cHRlZCBldmVudCBsb2cuXG4gKlxuICogQ2FsbGVycyBtdXN0IHBhc3MgYSBydW4gZW50aXR5IHRoZXkgYWxyZWFkeSBoYXZlIGluIGhhbmQgKGV2ZXJ5IGNhbGwgc2l0ZVxuICogbG9hZHMgdGhlIHJ1biBmb3Igb3RoZXIgcmVhc29ucyksIHNvIHRoZSBndWFyZCBjb3N0cyBubyBleHRyYSByb3VuZCB0cmlwLlxuICogKGB3b3JsZC5nZXREZXBsb3ltZW50SWQoKWAgcmVhZHMgdGhlIGFtYmllbnQgZGVwbG95bWVudCBpZCwgZS5nLlxuICogYFZFUkNFTF9ERVBMT1lNRU5UX0lEYCwgYW5kIGRvZXMgbm90IGNhbGwgdGhlIGJhY2tlbmQgZWl0aGVyLilcbiAqXG4gKiBUaGUgZmFpbHVyZSBpcyByZWNvcmRlZCAqKndpdGhvdXQgcmVzb2x2aW5nIHRoZSBydW4ncyBlbmNyeXB0aW9uIGtleSoqOiB0aGVcbiAqIGtleSBpcyBmZXRjaGVkIGZyb20gdGhlIHBpbm5lZCBkZXBsb3ltZW50J3MgQVBJLCBvZnRlbiB1bmF2YWlsYWJsZSBvbmNlIHRoYXRcbiAqIGRlcGxveW1lbnQgaXMgcGFzdCBpdHMgcmV0ZW50aW9uIHdpbmRvdywgc28gZGVwZW5kaW5nIG9uIGl0IHdvdWxkIHRocm93IGhlcmVcbiAqIGluc3RlYWQgb2YgcmVjb3JkaW5nIHRoZSBmYWlsdXJlLiBUaGUgcGF5bG9hZCBpcyB3cml0dGVuIHVuZW5jcnlwdGVkIChpdFxuICogaG9sZHMgb25seSBkZXBsb3ltZW50IGlkcywgbm90aGluZyBzZW5zaXRpdmUpLCBhbmQgdGhlIHBsYWludGV4dCBgZXJyb3JDb2RlYFxuICogaXMgdGhlIHNpZ25hbCBvYnNlcnZhYmlsaXR5IGFuZCB0aGUgVUkga2V5IG9mZi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGd1YXJkRGVwbG95bWVudEFmZmluaXR5KHtcbiAgd29ybGQsXG4gIHJ1bixcbiAgcmVxdWVzdElkLFxuICByZXRyeUNvdW50ID0gMCxcbiAgcmVlbnF1ZXVlLFxuICBpc0RlcGxveW1lbnRVbmF2YWlsYWJsZUVycm9yLFxuICBiZWZvcmVTdG9wLFxufToge1xuICB3b3JsZDogV29ybGQ7XG4gIHJ1bjogUGljazxXb3JrZmxvd1J1biwgJ3J1bklkJyB8ICdkZXBsb3ltZW50SWQnIHwgJ3NwZWNWZXJzaW9uJz47XG4gIHJlcXVlc3RJZD86IHN0cmluZztcbiAgLyoqIGBkZXBsb3ltZW50TWlzbWF0Y2hSZXRyeUNvdW50YCBmcm9tIHRoZSBpbmNvbWluZyBtZXNzYWdlLCBpZiBhbnkuICovXG4gIHJldHJ5Q291bnQ/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBQdXRzIHRoaXMgZGVsaXZlcnkncyBtZXNzYWdlIGJhY2sgb24gdGhlIHF1ZXVlLCB0YXJnZXRlZCBhdCB0aGUgcnVuJ3Mgb3duXG4gICAqIGRlcGxveW1lbnQuIE9taXR0ZWQgYnkgY2FsbGVycyB0aGF0IGNhbm5vdCByZWNvbnN0cnVjdCB0aGVpciBtZXNzYWdlLCB3aGljaFxuICAgKiBtYWtlcyB0aGUgZ3VhcmQgZmFpbC1mYXN0LlxuICAgKi9cbiAgcmVlbnF1ZXVlPzogKGFyZ3M6IFJlZW5xdWV1ZUFyZ3MpID0+IFByb21pc2U8dm9pZD47XG4gIC8qKlxuICAgKiBDbGFzc2lmaWVzIGEgcmUtZW5xdWV1ZSBmYWlsdXJlIGFzIGRlZmluaXRpdmUgcHJvb2YgdGhhdCB0aGUgcGlubmVkXG4gICAqIGRlcGxveW1lbnQgY2Fubm90IHJlY2VpdmUgdGhlIG1lc3NhZ2UuIFVuY2xhc3NpZmllZCBmYWlsdXJlcyBhcmUgcmV0cmllZFxuICAgKiBieSByZWplY3RpbmcgdGhlIGhhbmRsZXIgYW5kIGxlYXZpbmcgdGhlIGN1cnJlbnQgcXVldWUgbWVzc2FnZSB1bmFja2VkLlxuICAgKi9cbiAgaXNEZXBsb3ltZW50VW5hdmFpbGFibGVFcnJvcj86IChlcnJvcjogdW5rbm93bikgPT4gYm9vbGVhbjtcbiAgLyoqXG4gICAqIE9yZGVyaW5nIGJhcnJpZXIgYXdhaXRlZCBvbmNlIGEgbWlzbWF0Y2ggaXMgY29uZmlybWVkLCBiZWZvcmUgZWl0aGVyXG4gICAqIHN0b3BwaW5nIGFjdGlvbi4gVW5kZXIgdHVyYm8gdGhlIGBydW5fc3RhcnRlZGAgd3JpdGUgaXMgYmFja2dyb3VuZGVkLCBhbmRcbiAgICogYm90aCBvdXRjb21lcyBoYW5kIHRoZSBydW4gb2ZmICh0byBhIGBydW5fZmFpbGVkYCBoZXJlLCBvciB0byB0aGUgcGlubmVkXG4gICAqIGRlcGxveW1lbnQncyBvd24gYHJ1bl9zdGFydGVkYCksIHNvIHRoYXQgd3JpdGUgbXVzdCBoYXZlIGxhbmRlZCBmaXJzdC5cbiAgICovXG4gIGJlZm9yZVN0b3A/OiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xufSk6IFByb21pc2U8RGVwbG95bWVudEFmZmluaXR5UmVzdWx0PiB7XG4gIC8vIERlcGxveW1lbnQgYWZmaW5pdHkgaXMgb25seSBtZWFuaW5nZnVsIGluIHdvcmxkcyB3aG9zZSBkZXBsb3ltZW50cyBhcmVcbiAgLy8gYXRvbWljIGFuZCBpbW11dGFibGUsIHdoaWNoIHRoZSBXb3JsZCBkZWNsYXJlcyAod29ybGQtdmVyY2VsIGRvZXMpIGFuZFxuICAvLyBjYXBhYmlsaXRpZXMgZGVmYXVsdCB0byB1bnN1cHBvcnRlZC4gTG9jYWwgYW5kIHNlbGYtaG9zdGVkIHdvcmxkcyByZXBvcnQgYVxuICAvLyBzeW50aGV0aWMgZGVwbG95bWVudCBpZCAoZS5nLiBgZHBsX2xvY2FsQDxzZGstdmVyc2lvbj5gKSB0aGF0IGxlZ2l0aW1hdGVseVxuICAvLyBkaWZmZXJzIGFjcm9zcyBTREsgdmVyc2lvbnMgd2l0aGluIG9uZSBsb2dpY2FsIGVudmlyb25tZW50LCBzbyBndWFyZGluZ1xuICAvLyB0aGVtIHdvdWxkIGZhaWwgb3JkaW5hcnkgbG9jYWwgcnVucyBhZnRlciBhIHZlcnNpb24gYnVtcC5cbiAgaWYgKHdvcmxkLmNhcGFiaWxpdGllcz8uZGVwbG95bWVudEFmZmluaXR5ICE9PSB0cnVlKSB7XG4gICAgcmV0dXJuIENPTlRJTlVFO1xuICB9XG5cbiAgY29uc3QgY3VycmVudERlcGxveW1lbnRJZCA9IGF3YWl0IHdvcmxkLmdldERlcGxveW1lbnRJZCgpO1xuICBpZiAocnVuLmRlcGxveW1lbnRJZCA9PT0gY3VycmVudERlcGxveW1lbnRJZCkge1xuICAgIHJldHVybiBDT05USU5VRTtcbiAgfVxuXG4gIGF3YWl0IGJlZm9yZVN0b3A/LigpO1xuXG4gIGNvbnN0IG1heFJldHJpZXMgPSBnZXREZXBsb3ltZW50TWlzbWF0Y2hNYXhSZXRyaWVzKCk7XG4gIGNvbnN0IGxvZ0NvbnRleHQgPSB7XG4gICAgd29ya2Zsb3dSdW5JZDogcnVuLnJ1bklkLFxuICAgIHBpbm5lZERlcGxveW1lbnRJZDogcnVuLmRlcGxveW1lbnRJZCxcbiAgICBhY3R1YWxEZXBsb3ltZW50SWQ6IGN1cnJlbnREZXBsb3ltZW50SWQsXG4gICAgcmV0cnlDb3VudCxcbiAgICBtYXhSZXRyaWVzLFxuICB9O1xuXG4gIC8qKlxuICAgKiBSZWNvcmRzIHRoZSB0ZXJtaW5hbCBgcnVuX2ZhaWxlZGAuIE1pcnJvcnMgYHJlY29yZEZhdGFsUnVuRXJyb3JgOiBpdFxuICAgKiByZWNvcmRzIGFuZCByZXR1cm5zIHJhdGhlciB0aGFuIHRocm93aW5nLCBzbyB0aGUgY2FsbGVyIGFja3MgdGhlIG1lc3NhZ2VcbiAgICogaW5zdGVhZCBvZiBsZXR0aW5nIHRoZSBxdWV1ZSByZWRlbGl2ZXIgYSBydW4gdGhhdCBpcyBub3cgdGVybWluYWwuXG4gICAqL1xuICBjb25zdCBmYWlsUnVuID0gYXN5bmMgKFxuICAgIHJlY292ZXJ5QXR0ZW1wdHM6IG51bWJlcixcbiAgICBjYXVzZT86IHVua25vd25cbiAgKTogUHJvbWlzZTxEZXBsb3ltZW50QWZmaW5pdHlSZXN1bHQ+ID0+IHtcbiAgICBjb25zdCBlcnJvciA9IG5ldyBXb3JrZmxvd0RlcGxveW1lbnRNaXNtYXRjaEVycm9yKFxuICAgICAgcnVuLnJ1bklkLFxuICAgICAgcnVuLmRlcGxveW1lbnRJZCxcbiAgICAgIGN1cnJlbnREZXBsb3ltZW50SWQsXG4gICAgICB7IHJlY292ZXJ5QXR0ZW1wdHMsIGNhdXNlIH1cbiAgICApO1xuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHdvcmxkLmV2ZW50cy5jcmVhdGUoXG4gICAgICAgIHJ1bi5ydW5JZCxcbiAgICAgICAge1xuICAgICAgICAgIGV2ZW50VHlwZTogJ3J1bl9mYWlsZWQnLFxuICAgICAgICAgIHNwZWNWZXJzaW9uOiBTUEVDX1ZFUlNJT05fQ1VSUkVOVCxcbiAgICAgICAgICBldmVudERhdGE6IHtcbiAgICAgICAgICAgIC8vIFVuZW5jcnlwdGVkICh1bmRlZmluZWQga2V5KTogc2VlIHRoZSBub3RlIG9uXG4gICAgICAgICAgICAvLyBgZ3VhcmREZXBsb3ltZW50QWZmaW5pdHlgLiBkZWh5ZHJhdGUvaHlkcmF0ZSBhcmUgc2VsZi1kZXNjcmliaW5nLFxuICAgICAgICAgICAgLy8gc28gdGhpcyByZWFkcyBiYWNrIHdpdGhvdXQgYSBrZXkuXG4gICAgICAgICAgICBlcnJvcjogYXdhaXQgZGVoeWRyYXRlUnVuRXJyb3IoXG4gICAgICAgICAgICAgIGVycm9yLFxuICAgICAgICAgICAgICBydW4ucnVuSWQsXG4gICAgICAgICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAocnVuLnNwZWNWZXJzaW9uID8/IDApID49IFNQRUNfVkVSU0lPTl9TVVBQT1JUU19DT01QUkVTU0lPTlxuICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIGVycm9yQ29kZTogUlVOX0VSUk9SX0NPREVTLkRFUExPWU1FTlRfTUlTTUFUQ0gsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgICAgeyByZXF1ZXN0SWQgfVxuICAgICAgKTtcbiAgICB9IGNhdGNoIChmYWlsRXJyb3IpIHtcbiAgICAgIC8vIFJ1biBhbHJlYWR5IHJlYWNoZWQgYSB0ZXJtaW5hbCBzdGF0ZSAoYSBjb25jdXJyZW50IHdyaXRlciBmYWlsZWQgaXQsIG9yXG4gICAgICAvLyBpdCB3YXMgY2FuY2VsZWQvZXhwaXJlZCksIHNvIHN0aWxsIHN0b3AuIEFueXRoaW5nIGVsc2UgaXMgYSB0cmFuc2llbnRcbiAgICAgIC8vIHBlcnNpc3RlbmNlIGZhaWx1cmU6IHJldGhyb3cgc28gdGhlIHF1ZXVlIHJlZGVsaXZlcnMgYW5kIHdlIHRyeSBhZ2Fpbi5cbiAgICAgIGlmIChcbiAgICAgICAgIUVudGl0eUNvbmZsaWN0RXJyb3IuaXMoZmFpbEVycm9yKSAmJlxuICAgICAgICAhUnVuRXhwaXJlZEVycm9yLmlzKGZhaWxFcnJvcilcbiAgICAgICkge1xuICAgICAgICB0aHJvdyBmYWlsRXJyb3I7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQoJ2ZhaWxlZCcsIHJ1bi5kZXBsb3ltZW50SWQsIHJlY292ZXJ5QXR0ZW1wdHMpO1xuICB9O1xuXG4gIGlmIChyZWVucXVldWUgJiYgcmV0cnlDb3VudCA8IG1heFJldHJpZXMpIHtcbiAgICBjb25zdCBhdHRlbXB0ID0gcmV0cnlDb3VudCArIDE7XG4gICAgLy8gRXhwb25lbnRpYWwsIGNhcHBlZDogMXMsIDJzLCA0cy4gQ2hlYXAgaW5zdXJhbmNlIGFnYWluc3QgYSBob3RcbiAgICAvLyByZS1lbnF1ZXVlIGxvb3AsIGFuZCB0aW1lIGZvciBhIG1pZC1mbGlnaHQgcm91dGluZyBjaGFuZ2UgdG8gc2V0dGxlLlxuICAgIGNvbnN0IGRlbGF5U2Vjb25kcyA9IE1hdGgubWluKDIgKiogcmV0cnlDb3VudCwgTUFYX1JFUk9VVEVfREVMQVlfU0VDT05EUyk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHJlZW5xdWV1ZSh7XG4gICAgICAgIGRlcGxveW1lbnRJZDogcnVuLmRlcGxveW1lbnRJZCxcbiAgICAgICAgc3BlY1ZlcnNpb246IHJ1bi5zcGVjVmVyc2lvbixcbiAgICAgICAgZGVwbG95bWVudE1pc21hdGNoUmV0cnlDb3VudDogYXR0ZW1wdCxcbiAgICAgICAgZGVsYXlTZWNvbmRzLFxuICAgICAgfSk7XG4gICAgICBydW50aW1lTG9nZ2VyLndhcm4oXG4gICAgICAgICdXb3JrZmxvdyBydW4gcmVjZWl2ZWQgYnkgYSBkZXBsb3ltZW50IGl0IGlzIG5vdCBwaW5uZWQgdG87IHJlLXJvdXRpbmcgdG8gaXRzIG93biBkZXBsb3ltZW50JyxcbiAgICAgICAgeyAuLi5sb2dDb250ZXh0LCBhdHRlbXB0LCBkZWxheVNlY29uZHMgfVxuICAgICAgKTtcbiAgICAgIHJldHVybiByZXN1bHQoJ3Jlcm91dGVkJywgcnVuLmRlcGxveW1lbnRJZCwgYXR0ZW1wdCk7XG4gICAgfSBjYXRjaCAocmVlbnF1ZXVlRXJyb3IpIHtcbiAgICAgIC8vIEEgZ2VuZXJpYyBzZW5kIGZhaWx1cmUgY2FuIGJlIHRyYW5zaWVudCAoNDI5LCA1eHgsIGNvbm5lY3QvRE5TKS4gRG8gbm90XG4gICAgICAvLyB0dXJuIG9uZSBpbmZyYXN0cnVjdHVyZSBibGlwIGludG8gYSB0ZXJtaW5hbCBydW4gZmFpbHVyZTogcmVqZWN0IHRoZVxuICAgICAgLy8gaGFuZGxlciBzbyB0aGUgY3VycmVudCBtZXNzYWdlIHJlbWFpbnMgdW5hY2tlZCBhbmQgaXMgcmVkZWxpdmVyZWQuXG4gICAgICBpZiAoIWlzRGVwbG95bWVudFVuYXZhaWxhYmxlRXJyb3I/LihyZWVucXVldWVFcnJvcikpIHtcbiAgICAgICAgdGhyb3cgcmVlbnF1ZXVlRXJyb3I7XG4gICAgICB9XG5cbiAgICAgIC8vIFRoZSBXb3JsZCBleHBsaWNpdGx5IGNsYXNzaWZpZWQgdGhlIHRhcmdldCBhcyB1bmF2YWlsYWJsZTogZGVsZXRlZCxcbiAgICAgIC8vIGFnZWQgb3V0IG9mIGl0cyByZXRlbnRpb24gd2luZG93LCBvciBvdGhlcndpc2UgdW5kaXNjb3ZlcmFibGUuIEJ1cm5pbmdcbiAgICAgIC8vIHRoZSByZXN0IG9mIHRoZSBidWRnZXQgb24gYSBkZXBsb3ltZW50IHRoYXQgcHJvdmFibHkgY2Fubm90IGJlIHJlYWNoZWRcbiAgICAgIC8vIG9ubHkgZGVsYXlzIHRoZSBmYWlsdXJlLCBzbyBmYWlsIG5vdy5cbiAgICAgIHJ1bnRpbWVMb2dnZXIuZXJyb3IoXG4gICAgICAgICdDYW5ub3QgcmUtcm91dGUgYSBtaXNyb3V0ZWQgd29ya2Zsb3cgcnVuIHRvIGl0cyBvd24gZGVwbG95bWVudDsgZmFpbGluZyB0aGUgcnVuJyxcbiAgICAgICAge1xuICAgICAgICAgIC4uLmxvZ0NvbnRleHQsXG4gICAgICAgICAgYXR0ZW1wdCxcbiAgICAgICAgICBlcnJvck1lc3NhZ2U6XG4gICAgICAgICAgICByZWVucXVldWVFcnJvciBpbnN0YW5jZW9mIEVycm9yXG4gICAgICAgICAgICAgID8gcmVlbnF1ZXVlRXJyb3IubWVzc2FnZVxuICAgICAgICAgICAgICA6IFN0cmluZyhyZWVucXVldWVFcnJvciksXG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgICAvLyBSZWNvdmVyeSBuZXZlciBhY3R1YWxseSBoYXBwZW5lZCwgc28gcmVwb3J0IDAgYXR0ZW1wdHMgYW5kIGxldCBgY2F1c2VgXG4gICAgICAvLyBleHBsYWluIHdoeS5cbiAgICAgIHJldHVybiBmYWlsUnVuKDAsIHJlZW5xdWV1ZUVycm9yKTtcbiAgICB9XG4gIH1cblxuICBydW50aW1lTG9nZ2VyLmVycm9yKFxuICAgIHJldHJ5Q291bnQgPiAwXG4gICAgICA/ICdXb3JrZmxvdyBydW4ga2VwdCBhcnJpdmluZyBhdCBhIGRlcGxveW1lbnQgaXQgaXMgbm90IHBpbm5lZCB0byBhZnRlciByZS1yb3V0aW5nOyBmYWlsaW5nIHRoZSBydW4nXG4gICAgICA6ICdXb3JrZmxvdyBydW4gcmVjZWl2ZWQgYnkgYSBkZXBsb3ltZW50IGl0IGlzIG5vdCBwaW5uZWQgdG87IGZhaWxpbmcgdGhlIHJ1bicsXG4gICAgbG9nQ29udGV4dFxuICApO1xuICByZXR1cm4gZmFpbFJ1bihyZXRyeUNvdW50KTtcbn1cbiJdfQ==
|
|
@@ -7,4 +7,19 @@
|
|
|
7
7
|
* dependency is invisible to bundler static analysis.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getPortLazy(): Promise<number | undefined>;
|
|
10
|
+
/**
|
|
11
|
+
* Resets the per-process port cache. Intended for tests; not used on the hot
|
|
12
|
+
* path. Callers must let any in-flight lookup settle (await the pending
|
|
13
|
+
* `getPortLazy()` call) before resetting: clearing `portState.inFlight` here does not
|
|
14
|
+
* cancel an already-scheduled resolution, so a late `.then` could otherwise
|
|
15
|
+
* repopulate `portState.cachedPort` after the reset and bleed into the next test.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resetPortCacheForTesting(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Installs a fake port resolver and clears the cache. Test-only seam: the real
|
|
20
|
+
* resolver is loaded via `createRequire` with a runtime-built specifier (to
|
|
21
|
+
* hide it from bundlers), which also defeats module mocking, so injection is
|
|
22
|
+
* the only deterministic way to exercise the caching contract.
|
|
23
|
+
*/
|
|
24
|
+
export declare function setPortResolverForTesting(fn: () => Promise<number | undefined>): void;
|
|
10
25
|
//# sourceMappingURL=get-port-lazy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-port-lazy.d.ts","sourceRoot":"","sources":["../../src/runtime/get-port-lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"get-port-lazy.d.ts","sourceRoot":"","sources":["../../src/runtime/get-port-lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA8BH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA4C/D;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAI/C;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GACpC,IAAI,CAIN"}
|
|
@@ -8,9 +8,39 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
10
|
import { pathToFileURL } from 'node:url';
|
|
11
|
-
|
|
11
|
+
import { globalSingleton } from '@workflow/utils';
|
|
12
|
+
// All three live on `globalThis` (see `globalSingleton`) rather than at module
|
|
13
|
+
// scope. The resolved port is a fact about the process, not about one copy of
|
|
14
|
+
// this module, and per-copy caches would each pay the ~60ms `lsof` discovery
|
|
15
|
+
// and could each pin their own answer.
|
|
16
|
+
const portState = globalSingleton('@workflow/core//devServerPort', 1, () => ({
|
|
17
|
+
getPort: undefined,
|
|
18
|
+
cachedPort: undefined,
|
|
19
|
+
inFlight: undefined,
|
|
20
|
+
}));
|
|
21
|
+
// Per-process cache of the resolved port. The workflow server listens on a
|
|
22
|
+
// stable port for the lifetime of the process, but `getPort()` rediscovers it
|
|
23
|
+
// on every call by querying the OS for the process's listening sockets. On
|
|
24
|
+
// macOS that shells out to `lsof` (~60ms), which the runtime pays on EVERY
|
|
25
|
+
// workflow replay or step invocation. Since the port does not change within a
|
|
26
|
+
// process, resolve it once and reuse it. `portState.inFlight`
|
|
27
|
+
// dedupes concurrent first calls so discovery never runs more than once.
|
|
28
|
+
//
|
|
29
|
+
// The first concrete port is pinned for the lifetime of the process: there is
|
|
30
|
+
// no per-call re-resolution. This is safe because the runtime only runs inside
|
|
31
|
+
// the already-listening dev-server process, and `getPort()` -> `getAllPorts()`
|
|
32
|
+
// returns a deterministic order, so repeated calls would resolve the same port
|
|
33
|
+
// anyway.
|
|
12
34
|
export async function getPortLazy() {
|
|
13
|
-
|
|
35
|
+
// Fast path: already resolved a concrete port for this process.
|
|
36
|
+
if (portState.cachedPort !== undefined) {
|
|
37
|
+
return portState.cachedPort;
|
|
38
|
+
}
|
|
39
|
+
// A discovery is already running, so share it rather than starting a second.
|
|
40
|
+
if (portState.inFlight) {
|
|
41
|
+
return portState.inFlight;
|
|
42
|
+
}
|
|
43
|
+
if (!portState.getPort) {
|
|
14
44
|
try {
|
|
15
45
|
// Construct specifier at runtime to defeat bundler static analysis.
|
|
16
46
|
const spec = ['@workflow/utils', 'get-port'].join('/');
|
|
@@ -18,13 +48,52 @@ export async function getPortLazy() {
|
|
|
18
48
|
// import.meta.url is unavailable in CJS re-bundled outputs.
|
|
19
49
|
const _require = createRequire(pathToFileURL(process.cwd() + '/package.json').href);
|
|
20
50
|
const mod = _require(spec);
|
|
21
|
-
|
|
51
|
+
portState.getPort = mod.getPort;
|
|
22
52
|
}
|
|
23
53
|
catch {
|
|
24
54
|
// Module not available (e.g., in a browser or minimal bundle)
|
|
25
|
-
|
|
55
|
+
portState.getPort = async () => undefined;
|
|
26
56
|
}
|
|
27
57
|
}
|
|
28
|
-
|
|
58
|
+
// `portState.getPort` is always assigned by the block above; the fallback keeps the
|
|
59
|
+
// type non-nullable without a non-null assertion.
|
|
60
|
+
const resolver = portState.getPort ?? (async () => undefined);
|
|
61
|
+
portState.inFlight = resolver()
|
|
62
|
+
.then((port) => {
|
|
63
|
+
// Only cache a concrete port. A transient `undefined` (e.g. the server is
|
|
64
|
+
// not listening yet on the first replay) must not poison the cache:
|
|
65
|
+
// leaving it unset lets the next call retry discovery.
|
|
66
|
+
if (typeof port === 'number') {
|
|
67
|
+
portState.cachedPort = port;
|
|
68
|
+
}
|
|
69
|
+
return port;
|
|
70
|
+
})
|
|
71
|
+
.finally(() => {
|
|
72
|
+
portState.inFlight = undefined;
|
|
73
|
+
});
|
|
74
|
+
return portState.inFlight;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resets the per-process port cache. Intended for tests; not used on the hot
|
|
78
|
+
* path. Callers must let any in-flight lookup settle (await the pending
|
|
79
|
+
* `getPortLazy()` call) before resetting: clearing `portState.inFlight` here does not
|
|
80
|
+
* cancel an already-scheduled resolution, so a late `.then` could otherwise
|
|
81
|
+
* repopulate `portState.cachedPort` after the reset and bleed into the next test.
|
|
82
|
+
*/
|
|
83
|
+
export function resetPortCacheForTesting() {
|
|
84
|
+
portState.getPort = undefined;
|
|
85
|
+
portState.cachedPort = undefined;
|
|
86
|
+
portState.inFlight = undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Installs a fake port resolver and clears the cache. Test-only seam: the real
|
|
90
|
+
* resolver is loaded via `createRequire` with a runtime-built specifier (to
|
|
91
|
+
* hide it from bundlers), which also defeats module mocking, so injection is
|
|
92
|
+
* the only deterministic way to exercise the caching contract.
|
|
93
|
+
*/
|
|
94
|
+
export function setPortResolverForTesting(fn) {
|
|
95
|
+
portState.getPort = fn;
|
|
96
|
+
portState.cachedPort = undefined;
|
|
97
|
+
portState.inFlight = undefined;
|
|
29
98
|
}
|
|
30
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LXBvcnQtbGF6eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2dldC1wb3J0LWxhenkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDNUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFbEQsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw2RUFBNkU7QUFDN0UsdUNBQXVDO0FBQ3ZDLE1BQU0sU0FBUyxHQUFHLGVBQWUsQ0FBQywrQkFBK0IsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUMzRSxPQUFPLEVBQUUsU0FBNEQ7SUFDckUsVUFBVSxFQUFFLFNBQStCO0lBQzNDLFFBQVEsRUFBRSxTQUFvRDtDQUMvRCxDQUFDLENBQUMsQ0FBQztBQUVKLDJFQUEyRTtBQUMzRSw4RUFBOEU7QUFDOUUsMkVBQTJFO0FBQzNFLDJFQUEyRTtBQUMzRSw4RUFBOEU7QUFDOUUsOERBQThEO0FBQzlELHlFQUF5RTtBQUN6RSxFQUFFO0FBQ0YsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSwrRUFBK0U7QUFDL0UsK0VBQStFO0FBQy9FLFVBQVU7QUFFVixNQUFNLENBQUMsS0FBSyxVQUFVLFdBQVc7SUFDL0IsZ0VBQWdFO0lBQ2hFLElBQUksU0FBUyxDQUFDLFVBQVUsS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN2QyxPQUFPLFNBQVMsQ0FBQyxVQUFVLENBQUM7SUFDOUIsQ0FBQztJQUNELDZFQUE2RTtJQUM3RSxJQUFJLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN2QixPQUFPLFNBQVMsQ0FBQyxRQUFRLENBQUM7SUFDNUIsQ0FBQztJQUVELElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDO1lBQ0gsb0VBQW9FO1lBQ3BFLE1BQU0sSUFBSSxHQUFHLENBQUMsaUJBQWlCLEVBQUUsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZELG1FQUFtRTtZQUNuRSw0REFBNEQ7WUFDNUQsTUFBTSxRQUFRLEdBQUcsYUFBYSxDQUM1QixhQUFhLENBQUMsT0FBTyxDQUFDLEdBQUcsRUFBRSxHQUFHLGVBQWUsQ0FBQyxDQUFDLElBQUksQ0FDcEQsQ0FBQztZQUNGLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUMzQixTQUFTLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUM7UUFDbEMsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLDhEQUE4RDtZQUM5RCxTQUFTLENBQUMsT0FBTyxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUMsU0FBUyxDQUFDO1FBQzVDLENBQUM7SUFDSCxDQUFDO0lBRUQsb0ZBQW9GO0lBQ3BGLGtEQUFrRDtJQUNsRCxNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsT0FBTyxJQUFJLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUM5RCxTQUFTLENBQUMsUUFBUSxHQUFHLFFBQVEsRUFBRTtTQUM1QixJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRTtRQUNiLDBFQUEwRTtRQUMxRSxvRUFBb0U7UUFDcEUsdURBQXVEO1FBQ3ZELElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDN0IsU0FBUyxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDOUIsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQyxDQUFDO1NBQ0QsT0FBTyxDQUFDLEdBQUcsRUFBRTtRQUNaLFNBQVMsQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFDO0lBQ2pDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsT0FBTyxTQUFTLENBQUMsUUFBUSxDQUFDO0FBQzVCLENBQUM7QUFFRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLFVBQVUsd0JBQXdCO0lBQ3RDLFNBQVMsQ0FBQyxPQUFPLEdBQUcsU0FBUyxDQUFDO0lBQzlCLFNBQVMsQ0FBQyxVQUFVLEdBQUcsU0FBUyxDQUFDO0lBQ2pDLFNBQVMsQ0FBQyxRQUFRLEdBQUcsU0FBUyxDQUFDO0FBQ2pDLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSx5QkFBeUIsQ0FDdkMsRUFBcUM7SUFFckMsU0FBUyxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7SUFDdkIsU0FBUyxDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUM7SUFDakMsU0FBUyxDQUFDLFFBQVEsR0FBRyxTQUFTLENBQUM7QUFDakMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTGF6eSBsb2FkZXIgZm9yIGdldFBvcnQgdGhhdCBwcmV2ZW50cyBidW5kbGVycyAoVHVyYm9wYWNrLCBlc2J1aWxkKVxuICogZnJvbSBzdGF0aWNhbGx5IHRyYWNpbmcgQHdvcmtmbG93L3V0aWxzL2dldC1wb3J0IGFuZCBpdHMgZmlsZXN5c3RlbVxuICogb3BlcmF0aW9ucyAocmVhZGRpciwgcmVhZEZpbGUsIHJlYWRsaW5rKSBpbnRvIHRoZSBmbG93IHJvdXRlIGJ1bmRsZS5cbiAqXG4gKiBVc2VzIGNyZWF0ZVJlcXVpcmUgd2l0aCBhIGR5bmFtaWNhbGx5IGNvbnN0cnVjdGVkIHNwZWNpZmllciBzbyB0aGVcbiAqIGRlcGVuZGVuY3kgaXMgaW52aXNpYmxlIHRvIGJ1bmRsZXIgc3RhdGljIGFuYWx5c2lzLlxuICovXG5cbmltcG9ydCB7IGNyZWF0ZVJlcXVpcmUgfSBmcm9tICdub2RlOm1vZHVsZSc7XG5pbXBvcnQgeyBwYXRoVG9GaWxlVVJMIH0gZnJvbSAnbm9kZTp1cmwnO1xuaW1wb3J0IHsgZ2xvYmFsU2luZ2xldG9uIH0gZnJvbSAnQHdvcmtmbG93L3V0aWxzJztcblxuLy8gQWxsIHRocmVlIGxpdmUgb24gYGdsb2JhbFRoaXNgIChzZWUgYGdsb2JhbFNpbmdsZXRvbmApIHJhdGhlciB0aGFuIGF0IG1vZHVsZVxuLy8gc2NvcGUuIFRoZSByZXNvbHZlZCBwb3J0IGlzIGEgZmFjdCBhYm91dCB0aGUgcHJvY2Vzcywgbm90IGFib3V0IG9uZSBjb3B5IG9mXG4vLyB0aGlzIG1vZHVsZSwgYW5kIHBlci1jb3B5IGNhY2hlcyB3b3VsZCBlYWNoIHBheSB0aGUgfjYwbXMgYGxzb2ZgIGRpc2NvdmVyeVxuLy8gYW5kIGNvdWxkIGVhY2ggcGluIHRoZWlyIG93biBhbnN3ZXIuXG5jb25zdCBwb3J0U3RhdGUgPSBnbG9iYWxTaW5nbGV0b24oJ0B3b3JrZmxvdy9jb3JlLy9kZXZTZXJ2ZXJQb3J0JywgMSwgKCkgPT4gKHtcbiAgZ2V0UG9ydDogdW5kZWZpbmVkIGFzICgoKSA9PiBQcm9taXNlPG51bWJlciB8IHVuZGVmaW5lZD4pIHwgdW5kZWZpbmVkLFxuICBjYWNoZWRQb3J0OiB1bmRlZmluZWQgYXMgbnVtYmVyIHwgdW5kZWZpbmVkLFxuICBpbkZsaWdodDogdW5kZWZpbmVkIGFzIFByb21pc2U8bnVtYmVyIHwgdW5kZWZpbmVkPiB8IHVuZGVmaW5lZCxcbn0pKTtcblxuLy8gUGVyLXByb2Nlc3MgY2FjaGUgb2YgdGhlIHJlc29sdmVkIHBvcnQuIFRoZSB3b3JrZmxvdyBzZXJ2ZXIgbGlzdGVucyBvbiBhXG4vLyBzdGFibGUgcG9ydCBmb3IgdGhlIGxpZmV0aW1lIG9mIHRoZSBwcm9jZXNzLCBidXQgYGdldFBvcnQoKWAgcmVkaXNjb3ZlcnMgaXRcbi8vIG9uIGV2ZXJ5IGNhbGwgYnkgcXVlcnlpbmcgdGhlIE9TIGZvciB0aGUgcHJvY2VzcydzIGxpc3RlbmluZyBzb2NrZXRzLiBPblxuLy8gbWFjT1MgdGhhdCBzaGVsbHMgb3V0IHRvIGBsc29mYCAofjYwbXMpLCB3aGljaCB0aGUgcnVudGltZSBwYXlzIG9uIEVWRVJZXG4vLyB3b3JrZmxvdyByZXBsYXkgb3Igc3RlcCBpbnZvY2F0aW9uLiBTaW5jZSB0aGUgcG9ydCBkb2VzIG5vdCBjaGFuZ2Ugd2l0aGluIGFcbi8vIHByb2Nlc3MsIHJlc29sdmUgaXQgb25jZSBhbmQgcmV1c2UgaXQuIGBwb3J0U3RhdGUuaW5GbGlnaHRgXG4vLyBkZWR1cGVzIGNvbmN1cnJlbnQgZmlyc3QgY2FsbHMgc28gZGlzY292ZXJ5IG5ldmVyIHJ1bnMgbW9yZSB0aGFuIG9uY2UuXG4vL1xuLy8gVGhlIGZpcnN0IGNvbmNyZXRlIHBvcnQgaXMgcGlubmVkIGZvciB0aGUgbGlmZXRpbWUgb2YgdGhlIHByb2Nlc3M6IHRoZXJlIGlzXG4vLyBubyBwZXItY2FsbCByZS1yZXNvbHV0aW9uLiBUaGlzIGlzIHNhZmUgYmVjYXVzZSB0aGUgcnVudGltZSBvbmx5IHJ1bnMgaW5zaWRlXG4vLyB0aGUgYWxyZWFkeS1saXN0ZW5pbmcgZGV2LXNlcnZlciBwcm9jZXNzLCBhbmQgYGdldFBvcnQoKWAgLT4gYGdldEFsbFBvcnRzKClgXG4vLyByZXR1cm5zIGEgZGV0ZXJtaW5pc3RpYyBvcmRlciwgc28gcmVwZWF0ZWQgY2FsbHMgd291bGQgcmVzb2x2ZSB0aGUgc2FtZSBwb3J0XG4vLyBhbnl3YXkuXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRQb3J0TGF6eSgpOiBQcm9taXNlPG51bWJlciB8IHVuZGVmaW5lZD4ge1xuICAvLyBGYXN0IHBhdGg6IGFscmVhZHkgcmVzb2x2ZWQgYSBjb25jcmV0ZSBwb3J0IGZvciB0aGlzIHByb2Nlc3MuXG4gIGlmIChwb3J0U3RhdGUuY2FjaGVkUG9ydCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHBvcnRTdGF0ZS5jYWNoZWRQb3J0O1xuICB9XG4gIC8vIEEgZGlzY292ZXJ5IGlzIGFscmVhZHkgcnVubmluZywgc28gc2hhcmUgaXQgcmF0aGVyIHRoYW4gc3RhcnRpbmcgYSBzZWNvbmQuXG4gIGlmIChwb3J0U3RhdGUuaW5GbGlnaHQpIHtcbiAgICByZXR1cm4gcG9ydFN0YXRlLmluRmxpZ2h0O1xuICB9XG5cbiAgaWYgKCFwb3J0U3RhdGUuZ2V0UG9ydCkge1xuICAgIHRyeSB7XG4gICAgICAvLyBDb25zdHJ1Y3Qgc3BlY2lmaWVyIGF0IHJ1bnRpbWUgdG8gZGVmZWF0IGJ1bmRsZXIgc3RhdGljIGFuYWx5c2lzLlxuICAgICAgY29uc3Qgc3BlYyA9IFsnQHdvcmtmbG93L3V0aWxzJywgJ2dldC1wb3J0J10uam9pbignLycpO1xuICAgICAgLy8gVXNlIHByb2Nlc3MuY3dkKCktYmFzZWQgY3JlYXRlUmVxdWlyZSBmb3IgQ0pTL0VTTSBjb21wYXRpYmlsaXR5LlxuICAgICAgLy8gaW1wb3J0Lm1ldGEudXJsIGlzIHVuYXZhaWxhYmxlIGluIENKUyByZS1idW5kbGVkIG91dHB1dHMuXG4gICAgICBjb25zdCBfcmVxdWlyZSA9IGNyZWF0ZVJlcXVpcmUoXG4gICAgICAgIHBhdGhUb0ZpbGVVUkwocHJvY2Vzcy5jd2QoKSArICcvcGFja2FnZS5qc29uJykuaHJlZlxuICAgICAgKTtcbiAgICAgIGNvbnN0IG1vZCA9IF9yZXF1aXJlKHNwZWMpO1xuICAgICAgcG9ydFN0YXRlLmdldFBvcnQgPSBtb2QuZ2V0UG9ydDtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIE1vZHVsZSBub3QgYXZhaWxhYmxlIChlLmcuLCBpbiBhIGJyb3dzZXIgb3IgbWluaW1hbCBidW5kbGUpXG4gICAgICBwb3J0U3RhdGUuZ2V0UG9ydCA9IGFzeW5jICgpID0+IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICAvLyBgcG9ydFN0YXRlLmdldFBvcnRgIGlzIGFsd2F5cyBhc3NpZ25lZCBieSB0aGUgYmxvY2sgYWJvdmU7IHRoZSBmYWxsYmFjayBrZWVwcyB0aGVcbiAgLy8gdHlwZSBub24tbnVsbGFibGUgd2l0aG91dCBhIG5vbi1udWxsIGFzc2VydGlvbi5cbiAgY29uc3QgcmVzb2x2ZXIgPSBwb3J0U3RhdGUuZ2V0UG9ydCA/PyAoYXN5bmMgKCkgPT4gdW5kZWZpbmVkKTtcbiAgcG9ydFN0YXRlLmluRmxpZ2h0ID0gcmVzb2x2ZXIoKVxuICAgIC50aGVuKChwb3J0KSA9PiB7XG4gICAgICAvLyBPbmx5IGNhY2hlIGEgY29uY3JldGUgcG9ydC4gQSB0cmFuc2llbnQgYHVuZGVmaW5lZGAgKGUuZy4gdGhlIHNlcnZlciBpc1xuICAgICAgLy8gbm90IGxpc3RlbmluZyB5ZXQgb24gdGhlIGZpcnN0IHJlcGxheSkgbXVzdCBub3QgcG9pc29uIHRoZSBjYWNoZTpcbiAgICAgIC8vIGxlYXZpbmcgaXQgdW5zZXQgbGV0cyB0aGUgbmV4dCBjYWxsIHJldHJ5IGRpc2NvdmVyeS5cbiAgICAgIGlmICh0eXBlb2YgcG9ydCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgcG9ydFN0YXRlLmNhY2hlZFBvcnQgPSBwb3J0O1xuICAgICAgfVxuICAgICAgcmV0dXJuIHBvcnQ7XG4gICAgfSlcbiAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICBwb3J0U3RhdGUuaW5GbGlnaHQgPSB1bmRlZmluZWQ7XG4gICAgfSk7XG4gIHJldHVybiBwb3J0U3RhdGUuaW5GbGlnaHQ7XG59XG5cbi8qKlxuICogUmVzZXRzIHRoZSBwZXItcHJvY2VzcyBwb3J0IGNhY2hlLiBJbnRlbmRlZCBmb3IgdGVzdHM7IG5vdCB1c2VkIG9uIHRoZSBob3RcbiAqIHBhdGguIENhbGxlcnMgbXVzdCBsZXQgYW55IGluLWZsaWdodCBsb29rdXAgc2V0dGxlIChhd2FpdCB0aGUgcGVuZGluZ1xuICogYGdldFBvcnRMYXp5KClgIGNhbGwpIGJlZm9yZSByZXNldHRpbmc6IGNsZWFyaW5nIGBwb3J0U3RhdGUuaW5GbGlnaHRgIGhlcmUgZG9lcyBub3RcbiAqIGNhbmNlbCBhbiBhbHJlYWR5LXNjaGVkdWxlZCByZXNvbHV0aW9uLCBzbyBhIGxhdGUgYC50aGVuYCBjb3VsZCBvdGhlcndpc2VcbiAqIHJlcG9wdWxhdGUgYHBvcnRTdGF0ZS5jYWNoZWRQb3J0YCBhZnRlciB0aGUgcmVzZXQgYW5kIGJsZWVkIGludG8gdGhlIG5leHQgdGVzdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc2V0UG9ydENhY2hlRm9yVGVzdGluZygpOiB2b2lkIHtcbiAgcG9ydFN0YXRlLmdldFBvcnQgPSB1bmRlZmluZWQ7XG4gIHBvcnRTdGF0ZS5jYWNoZWRQb3J0ID0gdW5kZWZpbmVkO1xuICBwb3J0U3RhdGUuaW5GbGlnaHQgPSB1bmRlZmluZWQ7XG59XG5cbi8qKlxuICogSW5zdGFsbHMgYSBmYWtlIHBvcnQgcmVzb2x2ZXIgYW5kIGNsZWFycyB0aGUgY2FjaGUuIFRlc3Qtb25seSBzZWFtOiB0aGUgcmVhbFxuICogcmVzb2x2ZXIgaXMgbG9hZGVkIHZpYSBgY3JlYXRlUmVxdWlyZWAgd2l0aCBhIHJ1bnRpbWUtYnVpbHQgc3BlY2lmaWVyICh0b1xuICogaGlkZSBpdCBmcm9tIGJ1bmRsZXJzKSwgd2hpY2ggYWxzbyBkZWZlYXRzIG1vZHVsZSBtb2NraW5nLCBzbyBpbmplY3Rpb24gaXNcbiAqIHRoZSBvbmx5IGRldGVybWluaXN0aWMgd2F5IHRvIGV4ZXJjaXNlIHRoZSBjYWNoaW5nIGNvbnRyYWN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0UG9ydFJlc29sdmVyRm9yVGVzdGluZyhcbiAgZm46ICgpID0+IFByb21pc2U8bnVtYmVyIHwgdW5kZWZpbmVkPlxuKTogdm9pZCB7XG4gIHBvcnRTdGF0ZS5nZXRQb3J0ID0gZm47XG4gIHBvcnRTdGF0ZS5jYWNoZWRQb3J0ID0gdW5kZWZpbmVkO1xuICBwb3J0U3RhdGUuaW5GbGlnaHQgPSB1bmRlZmluZWQ7XG59XG4iXX0=
|
|
@@ -9,23 +9,14 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Resolution order, in priority:
|
|
11
11
|
*
|
|
12
|
-
* 1. `globalThis[WorldCacheKey]
|
|
12
|
+
* 1. `globalThis[WorldCacheKey]`: populated by a successful prior
|
|
13
13
|
* `getWorld()` call. This is the steady-state hot path.
|
|
14
|
-
* 2. `globalThis[GetWorldFnKey]
|
|
14
|
+
* 2. `globalThis[GetWorldFnKey]`: populated by the module-load side
|
|
15
15
|
* effect at the bottom of `./world.ts`. Fires on every server bundle
|
|
16
|
-
* that reaches this file via `workflow/api` (which
|
|
16
|
+
* that reaches this file via `workflow` or `workflow/api` (which import
|
|
17
17
|
* `./world-init.ts` for its side effect; see that file for the full
|
|
18
|
-
* rationale). This is the cold-start path for routes that consume
|
|
19
|
-
*
|
|
20
|
-
* 3. Dynamic `import('./world.js')` — last-resort fallback for
|
|
21
|
-
* environments where neither (1) nor (2) is available (CJS test
|
|
22
|
-
* runners, scripts that import deeply into `@workflow/core` without
|
|
23
|
-
* going through `workflow/api`, future bundlers we haven't validated).
|
|
24
|
-
* The specifier is built at runtime so esbuild can't trace it into
|
|
25
|
-
* step bundles. Note: this branch is unreliable in webpack-bundled
|
|
26
|
-
* routes because webpack inlines this module into the route file and
|
|
27
|
-
* the relative path resolves against the bundle location — paths (1)
|
|
28
|
-
* and (2) cover those cases instead.
|
|
18
|
+
* rationale). This is the cold-start path for routes that consume host
|
|
19
|
+
* helpers without any prior workflow run.
|
|
29
20
|
*/
|
|
30
21
|
import type { World } from '@workflow/world';
|
|
31
22
|
export declare function getWorldLazy(): Promise<World>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-world-lazy.d.ts","sourceRoot":"","sources":["../../src/runtime/get-world-lazy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"get-world-lazy.d.ts","sourceRoot":"","sources":["../../src/runtime/get-world-lazy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAa7C,wBAAsB,YAAY,IAAI,OAAO,CAAC,KAAK,CAAC,CAsBnD"}
|