@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.
Files changed (310) hide show
  1. package/dist/attribute-changes.d.ts +5 -0
  2. package/dist/attribute-changes.d.ts.map +1 -0
  3. package/dist/attribute-changes.js +25 -0
  4. package/dist/capabilities.d.ts +35 -2
  5. package/dist/capabilities.d.ts.map +1 -1
  6. package/dist/capabilities.js +74 -5
  7. package/dist/capture-stack.d.ts +1 -1
  8. package/dist/capture-stack.js +2 -2
  9. package/dist/class-serialization.d.ts +32 -0
  10. package/dist/class-serialization.d.ts.map +1 -1
  11. package/dist/class-serialization.js +37 -1
  12. package/dist/classify-error.d.ts +19 -3
  13. package/dist/classify-error.d.ts.map +1 -1
  14. package/dist/classify-error.js +98 -6
  15. package/dist/context-violation-error.d.ts +5 -5
  16. package/dist/context-violation-error.d.ts.map +1 -1
  17. package/dist/context-violation-error.js +8 -6
  18. package/dist/create-hook.d.ts +72 -7
  19. package/dist/create-hook.d.ts.map +1 -1
  20. package/dist/create-hook.js +1 -1
  21. package/dist/define-hook.js +2 -2
  22. package/dist/describe-error.d.ts +7 -7
  23. package/dist/describe-error.d.ts.map +1 -1
  24. package/dist/describe-error.js +97 -12
  25. package/dist/encryption.d.ts +38 -4
  26. package/dist/encryption.d.ts.map +1 -1
  27. package/dist/encryption.js +96 -14
  28. package/dist/events-consumer.d.ts +271 -0
  29. package/dist/events-consumer.d.ts.map +1 -1
  30. package/dist/events-consumer.js +646 -42
  31. package/dist/flushable-stream.d.ts +56 -1
  32. package/dist/flushable-stream.d.ts.map +1 -1
  33. package/dist/flushable-stream.js +123 -11
  34. package/dist/global.d.ts +36 -2
  35. package/dist/global.d.ts.map +1 -1
  36. package/dist/global.js +21 -6
  37. package/dist/index.d.ts +2 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +3 -2
  40. package/dist/log-format.d.ts +2 -2
  41. package/dist/log-format.js +7 -7
  42. package/dist/logger.d.ts +1 -0
  43. package/dist/logger.d.ts.map +1 -1
  44. package/dist/logger.js +10 -6
  45. package/dist/private.d.ts +226 -9
  46. package/dist/private.d.ts.map +1 -1
  47. package/dist/private.js +626 -7
  48. package/dist/replay-payload-cache.d.ts +64 -0
  49. package/dist/replay-payload-cache.d.ts.map +1 -0
  50. package/dist/replay-payload-cache.js +158 -0
  51. package/dist/runtime/compute-instance.d.ts +13 -0
  52. package/dist/runtime/compute-instance.d.ts.map +1 -0
  53. package/dist/runtime/compute-instance.js +14 -0
  54. package/dist/runtime/constants.d.ts +315 -0
  55. package/dist/runtime/constants.d.ts.map +1 -1
  56. package/dist/runtime/constants.js +502 -16
  57. package/dist/runtime/count-step-started-events.d.ts +52 -0
  58. package/dist/runtime/count-step-started-events.d.ts.map +1 -0
  59. package/dist/runtime/count-step-started-events.js +72 -0
  60. package/dist/runtime/deployment-guard.d.ts +99 -0
  61. package/dist/runtime/deployment-guard.d.ts.map +1 -0
  62. package/dist/runtime/deployment-guard.js +154 -0
  63. package/dist/runtime/get-port-lazy.d.ts +15 -0
  64. package/dist/runtime/get-port-lazy.d.ts.map +1 -1
  65. package/dist/runtime/get-port-lazy.js +75 -6
  66. package/dist/runtime/get-world-lazy.d.ts +5 -14
  67. package/dist/runtime/get-world-lazy.d.ts.map +1 -1
  68. package/dist/runtime/get-world-lazy.js +19 -29
  69. package/dist/runtime/helpers.d.ts +341 -29
  70. package/dist/runtime/helpers.d.ts.map +1 -1
  71. package/dist/runtime/helpers.js +559 -57
  72. package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
  73. package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
  74. package/dist/runtime/quickjs-assets.generated.js +30 -0
  75. package/dist/runtime/quickjs-entrypoint.d.ts +136 -0
  76. package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
  77. package/dist/runtime/quickjs-entrypoint.js +1686 -0
  78. package/dist/runtime/quickjs-runtime.d.ts +244 -0
  79. package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
  80. package/dist/runtime/quickjs-runtime.js +2427 -0
  81. package/dist/runtime/quickjs-serde.d.ts +107 -0
  82. package/dist/runtime/quickjs-serde.d.ts.map +1 -0
  83. package/dist/runtime/quickjs-serde.js +2159 -0
  84. package/dist/runtime/replay-budget.d.ts +97 -0
  85. package/dist/runtime/replay-budget.d.ts.map +1 -0
  86. package/dist/runtime/replay-budget.js +139 -0
  87. package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
  88. package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
  89. package/dist/runtime/replay-recovery-reporter.js +64 -0
  90. package/dist/runtime/resume-hook.d.ts +91 -8
  91. package/dist/runtime/resume-hook.d.ts.map +1 -1
  92. package/dist/runtime/resume-hook.js +512 -89
  93. package/dist/runtime/resume-latency.d.ts +150 -0
  94. package/dist/runtime/resume-latency.d.ts.map +1 -0
  95. package/dist/runtime/resume-latency.js +155 -0
  96. package/dist/runtime/run-id-time.d.ts +19 -0
  97. package/dist/runtime/run-id-time.d.ts.map +1 -0
  98. package/dist/runtime/run-id-time.js +42 -0
  99. package/dist/runtime/run.d.ts +72 -7
  100. package/dist/runtime/run.d.ts.map +1 -1
  101. package/dist/runtime/run.js +280 -53
  102. package/dist/runtime/runs.d.ts +54 -3
  103. package/dist/runtime/runs.d.ts.map +1 -1
  104. package/dist/runtime/runs.js +126 -16
  105. package/dist/runtime/start.d.ts +101 -2
  106. package/dist/runtime/start.d.ts.map +1 -1
  107. package/dist/runtime/start.js +352 -61
  108. package/dist/runtime/step-executor.d.ts +205 -5
  109. package/dist/runtime/step-executor.d.ts.map +1 -1
  110. package/dist/runtime/step-executor.js +760 -127
  111. package/dist/runtime/step-latency.d.ts +220 -0
  112. package/dist/runtime/step-latency.d.ts.map +1 -0
  113. package/dist/runtime/step-latency.js +215 -0
  114. package/dist/runtime/step-ownership.d.ts +72 -0
  115. package/dist/runtime/step-ownership.d.ts.map +1 -0
  116. package/dist/runtime/step-ownership.js +114 -0
  117. package/dist/runtime/step-single-flight.d.ts +12 -0
  118. package/dist/runtime/step-single-flight.d.ts.map +1 -0
  119. package/dist/runtime/step-single-flight.js +75 -0
  120. package/dist/runtime/suspension-handler.d.ts +269 -7
  121. package/dist/runtime/suspension-handler.d.ts.map +1 -1
  122. package/dist/runtime/suspension-handler.js +1253 -125
  123. package/dist/runtime/unserializable-step.d.ts +34 -0
  124. package/dist/runtime/unserializable-step.d.ts.map +1 -0
  125. package/dist/runtime/unserializable-step.js +52 -0
  126. package/dist/runtime/vm-mode.d.ts +44 -0
  127. package/dist/runtime/vm-mode.d.ts.map +1 -0
  128. package/dist/runtime/vm-mode.js +62 -0
  129. package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
  130. package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
  131. package/dist/runtime/vm-serde-bundle.generated.js +16 -0
  132. package/dist/runtime/wait-continuation.d.ts +106 -0
  133. package/dist/runtime/wait-continuation.d.ts.map +1 -0
  134. package/dist/runtime/wait-continuation.js +136 -0
  135. package/dist/runtime/wait-until.d.ts +18 -0
  136. package/dist/runtime/wait-until.d.ts.map +1 -0
  137. package/dist/runtime/wait-until.js +42 -0
  138. package/dist/runtime/world-compatibility.d.ts +32 -0
  139. package/dist/runtime/world-compatibility.d.ts.map +1 -0
  140. package/dist/runtime/world-compatibility.js +44 -0
  141. package/dist/runtime/world-init.d.ts +17 -42
  142. package/dist/runtime/world-init.d.ts.map +1 -1
  143. package/dist/runtime/world-init.js +18 -43
  144. package/dist/runtime/world.d.ts +14 -2
  145. package/dist/runtime/world.d.ts.map +1 -1
  146. package/dist/runtime/world.js +75 -19
  147. package/dist/runtime.d.ts +10 -6
  148. package/dist/runtime.d.ts.map +1 -1
  149. package/dist/runtime.js +3741 -490
  150. package/dist/sealed-box.d.ts +167 -0
  151. package/dist/sealed-box.d.ts.map +1 -0
  152. package/dist/sealed-box.js +571 -0
  153. package/dist/serialization/client.d.ts +3 -3
  154. package/dist/serialization/client.d.ts.map +1 -1
  155. package/dist/serialization/client.js +8 -4
  156. package/dist/serialization/codec-devalue-vm.d.ts +16 -0
  157. package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
  158. package/dist/serialization/codec-devalue-vm.js +148 -0
  159. package/dist/serialization/codec-devalue.d.ts +1 -1
  160. package/dist/serialization/codec-devalue.d.ts.map +1 -1
  161. package/dist/serialization/codec-devalue.js +24 -6
  162. package/dist/serialization/codec.d.ts +37 -2
  163. package/dist/serialization/codec.d.ts.map +1 -1
  164. package/dist/serialization/codec.js +2 -2
  165. package/dist/serialization/compression.d.ts +105 -0
  166. package/dist/serialization/compression.d.ts.map +1 -0
  167. package/dist/serialization/compression.js +261 -0
  168. package/dist/serialization/encryption.d.ts +119 -17
  169. package/dist/serialization/encryption.d.ts.map +1 -1
  170. package/dist/serialization/encryption.js +152 -17
  171. package/dist/serialization/errors.d.ts +13 -0
  172. package/dist/serialization/errors.d.ts.map +1 -1
  173. package/dist/serialization/errors.js +20 -2
  174. package/dist/serialization/format.d.ts +6 -6
  175. package/dist/serialization/format.js +7 -7
  176. package/dist/serialization/hardened.d.ts +156 -0
  177. package/dist/serialization/hardened.d.ts.map +1 -0
  178. package/dist/serialization/hardened.js +538 -0
  179. package/dist/serialization/index.d.ts +10 -8
  180. package/dist/serialization/index.d.ts.map +1 -1
  181. package/dist/serialization/index.js +10 -8
  182. package/dist/serialization/reducers/class-vm.d.ts +20 -0
  183. package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
  184. package/dist/serialization/reducers/class-vm.js +77 -0
  185. package/dist/serialization/reducers/class.d.ts.map +1 -1
  186. package/dist/serialization/reducers/class.js +11 -6
  187. package/dist/serialization/reducers/common-vm.d.ts +15 -0
  188. package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
  189. package/dist/serialization/reducers/common-vm.js +616 -0
  190. package/dist/serialization/reducers/common.d.ts +1 -1
  191. package/dist/serialization/reducers/common.d.ts.map +1 -1
  192. package/dist/serialization/reducers/common.js +202 -62
  193. package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
  194. package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
  195. package/dist/serialization/reducers/step-function-vm.js +97 -0
  196. package/dist/serialization/reducers/step-function.d.ts.map +1 -1
  197. package/dist/serialization/reducers/step-function.js +25 -9
  198. package/dist/serialization/step.d.ts +4 -4
  199. package/dist/serialization/step.d.ts.map +1 -1
  200. package/dist/serialization/step.js +9 -5
  201. package/dist/serialization/types.d.ts +80 -1
  202. package/dist/serialization/types.d.ts.map +1 -1
  203. package/dist/serialization/types.js +15 -1
  204. package/dist/serialization/workflow-vm.d.ts +29 -0
  205. package/dist/serialization/workflow-vm.d.ts.map +1 -0
  206. package/dist/serialization/workflow-vm.js +74 -0
  207. package/dist/serialization-format.d.ts +64 -8
  208. package/dist/serialization-format.d.ts.map +1 -1
  209. package/dist/serialization-format.js +225 -73
  210. package/dist/serialization.d.ts +262 -34
  211. package/dist/serialization.d.ts.map +1 -1
  212. package/dist/serialization.js +1879 -274
  213. package/dist/set-attributes.d.ts +13 -0
  214. package/dist/set-attributes.d.ts.map +1 -0
  215. package/dist/set-attributes.js +60 -0
  216. package/dist/source-map.d.ts +25 -0
  217. package/dist/source-map.d.ts.map +1 -1
  218. package/dist/source-map.js +154 -10
  219. package/dist/step/context-storage.d.ts +61 -2
  220. package/dist/step/context-storage.d.ts.map +1 -1
  221. package/dist/step/context-storage.js +7 -5
  222. package/dist/step/writable-stream.d.ts +3 -3
  223. package/dist/step/writable-stream.d.ts.map +1 -1
  224. package/dist/step/writable-stream.js +72 -9
  225. package/dist/step.d.ts.map +1 -1
  226. package/dist/step.js +148 -27
  227. package/dist/symbols.d.ts +50 -0
  228. package/dist/symbols.d.ts.map +1 -1
  229. package/dist/symbols.js +51 -1
  230. package/dist/telemetry/semantic-conventions.d.ts +397 -2
  231. package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
  232. package/dist/telemetry/semantic-conventions.js +297 -3
  233. package/dist/telemetry.d.ts +91 -0
  234. package/dist/telemetry.d.ts.map +1 -1
  235. package/dist/telemetry.js +244 -17
  236. package/dist/test-support/orchestrator-context.d.ts +25 -0
  237. package/dist/test-support/orchestrator-context.d.ts.map +1 -0
  238. package/dist/test-support/orchestrator-context.js +101 -0
  239. package/dist/types.d.ts +6 -0
  240. package/dist/types.d.ts.map +1 -1
  241. package/dist/types.js +23 -1
  242. package/dist/util.d.ts +0 -5
  243. package/dist/util.d.ts.map +1 -1
  244. package/dist/util.js +1 -15
  245. package/dist/version.d.ts +1 -1
  246. package/dist/version.d.ts.map +1 -1
  247. package/dist/version.js +2 -2
  248. package/dist/vm/index.d.ts.map +1 -1
  249. package/dist/vm/index.js +84 -14
  250. package/dist/vm/script-cache.d.ts +26 -0
  251. package/dist/vm/script-cache.d.ts.map +1 -0
  252. package/dist/vm/script-cache.js +131 -0
  253. package/dist/vm/uint8array-base64.js +1 -1
  254. package/dist/workflow/abort-controller.d.ts +2 -2
  255. package/dist/workflow/abort-controller.d.ts.map +1 -1
  256. package/dist/workflow/abort-controller.js +88 -21
  257. package/dist/workflow/attribute-dispatcher.d.ts +6 -0
  258. package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
  259. package/dist/workflow/attribute-dispatcher.js +45 -0
  260. package/dist/workflow/create-hook.d.ts.map +1 -1
  261. package/dist/workflow/create-hook.js +24 -3
  262. package/dist/workflow/get-workflow-metadata.js +2 -2
  263. package/dist/workflow/hook.d.ts.map +1 -1
  264. package/dist/workflow/hook.js +304 -47
  265. package/dist/workflow/index.d.ts +1 -0
  266. package/dist/workflow/index.d.ts.map +1 -1
  267. package/dist/workflow/index.js +2 -1
  268. package/dist/workflow/set-attributes.d.ts +68 -0
  269. package/dist/workflow/set-attributes.d.ts.map +1 -0
  270. package/dist/workflow/set-attributes.js +60 -0
  271. package/dist/workflow/sleep.d.ts.map +1 -1
  272. package/dist/workflow/sleep.js +58 -12
  273. package/dist/workflow/world-init-stub.d.ts +3 -3
  274. package/dist/workflow/world-init-stub.js +4 -4
  275. package/dist/workflow.d.ts +95 -3
  276. package/dist/workflow.d.ts.map +1 -1
  277. package/dist/workflow.js +895 -561
  278. package/docs/api-reference/create-hook.mdx +89 -10
  279. package/docs/api-reference/create-webhook.mdx +16 -15
  280. package/docs/api-reference/define-hook.mdx +35 -33
  281. package/docs/api-reference/fatal-error.mdx +30 -8
  282. package/docs/api-reference/fetch.mdx +14 -10
  283. package/docs/api-reference/get-step-metadata.mdx +2 -2
  284. package/docs/api-reference/get-workflow-metadata.mdx +3 -3
  285. package/docs/api-reference/get-writable.mdx +7 -7
  286. package/docs/api-reference/index.mdx +4 -1
  287. package/docs/api-reference/retryable-error.mdx +1 -1
  288. package/docs/api-reference/set-attributes.mdx +61 -0
  289. package/docs/api-reference/sleep.mdx +4 -4
  290. package/docs/foundations/cancellation.mdx +31 -32
  291. package/docs/foundations/errors-and-retries.mdx +42 -11
  292. package/docs/foundations/hooks.mdx +64 -35
  293. package/docs/foundations/idempotency.mdx +244 -12
  294. package/docs/foundations/index.mdx +1 -23
  295. package/docs/foundations/meta.json +2 -1
  296. package/docs/foundations/serialization.mdx +21 -22
  297. package/docs/foundations/starting-workflows.mdx +106 -30
  298. package/docs/foundations/streaming.mdx +107 -59
  299. package/docs/foundations/versioning.mdx +263 -0
  300. package/docs/foundations/workflows-and-steps.mdx +9 -9
  301. package/docs/how-it-works/cancellation.mdx +63 -63
  302. package/docs/how-it-works/code-transform.mdx +83 -67
  303. package/docs/how-it-works/encryption.mdx +30 -26
  304. package/docs/how-it-works/event-sourcing.mdx +98 -34
  305. package/docs/how-it-works/framework-integrations.mdx +96 -337
  306. package/docs/how-it-works/understanding-directives.mdx +22 -22
  307. package/package.json +24 -15
  308. package/dist/runtime/step-handler.d.ts +0 -2
  309. package/dist/runtime/step-handler.d.ts.map +0 -1
  310. 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;AAOH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAkB/D"}
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
- let _getPort;
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
- if (!_getPort) {
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
- _getPort = mod.getPort;
51
+ portState.getPort = mod.getPort;
22
52
  }
23
53
  catch {
24
54
  // Module not available (e.g., in a browser or minimal bundle)
25
- _getPort = async () => undefined;
55
+ portState.getPort = async () => undefined;
26
56
  }
27
57
  }
28
- return _getPort();
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LXBvcnQtbGF6eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydW50aW1lL2dldC1wb3J0LWxhenkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7R0FPRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDNUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUV6QyxJQUFJLFFBQXlELENBQUM7QUFFOUQsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXO0lBQy9CLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLElBQUksQ0FBQztZQUNILG9FQUFvRTtZQUNwRSxNQUFNLElBQUksR0FBRyxDQUFDLGlCQUFpQixFQUFFLFVBQVUsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN2RCxtRUFBbUU7WUFDbkUsNERBQTREO1lBQzVELE1BQU0sUUFBUSxHQUFHLGFBQWEsQ0FDNUIsYUFBYSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsR0FBRyxlQUFlLENBQUMsQ0FBQyxJQUFJLENBQ3BELENBQUM7WUFDRixNQUFNLEdBQUcsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDM0IsUUFBUSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUM7UUFDekIsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLDhEQUE4RDtZQUM5RCxRQUFRLEdBQUcsS0FBSyxJQUFJLEVBQUUsQ0FBQyxTQUFTLENBQUM7UUFDbkMsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLFFBQVMsRUFBRSxDQUFDO0FBQ3JCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIExhenkgbG9hZGVyIGZvciBnZXRQb3J0IHRoYXQgcHJldmVudHMgYnVuZGxlcnMgKFR1cmJvcGFjaywgZXNidWlsZClcbiAqIGZyb20gc3RhdGljYWxseSB0cmFjaW5nIEB3b3JrZmxvdy91dGlscy9nZXQtcG9ydCBhbmQgaXRzIGZpbGVzeXN0ZW1cbiAqIG9wZXJhdGlvbnMgKHJlYWRkaXIsIHJlYWRGaWxlLCByZWFkbGluaykgaW50byB0aGUgZmxvdyByb3V0ZSBidW5kbGUuXG4gKlxuICogVXNlcyBjcmVhdGVSZXF1aXJlIHdpdGggYSBkeW5hbWljYWxseSBjb25zdHJ1Y3RlZCBzcGVjaWZpZXIgc28gdGhlXG4gKiBkZXBlbmRlbmN5IGlzIGludmlzaWJsZSB0byBidW5kbGVyIHN0YXRpYyBhbmFseXNpcy5cbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbm9kZTptb2R1bGUnO1xuaW1wb3J0IHsgcGF0aFRvRmlsZVVSTCB9IGZyb20gJ25vZGU6dXJsJztcblxubGV0IF9nZXRQb3J0OiAoKCkgPT4gUHJvbWlzZTxudW1iZXIgfCB1bmRlZmluZWQ+KSB8IHVuZGVmaW5lZDtcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldFBvcnRMYXp5KCk6IFByb21pc2U8bnVtYmVyIHwgdW5kZWZpbmVkPiB7XG4gIGlmICghX2dldFBvcnQpIHtcbiAgICB0cnkge1xuICAgICAgLy8gQ29uc3RydWN0IHNwZWNpZmllciBhdCBydW50aW1lIHRvIGRlZmVhdCBidW5kbGVyIHN0YXRpYyBhbmFseXNpcy5cbiAgICAgIGNvbnN0IHNwZWMgPSBbJ0B3b3JrZmxvdy91dGlscycsICdnZXQtcG9ydCddLmpvaW4oJy8nKTtcbiAgICAgIC8vIFVzZSBwcm9jZXNzLmN3ZCgpLWJhc2VkIGNyZWF0ZVJlcXVpcmUgZm9yIENKUy9FU00gY29tcGF0aWJpbGl0eS5cbiAgICAgIC8vIGltcG9ydC5tZXRhLnVybCBpcyB1bmF2YWlsYWJsZSBpbiBDSlMgcmUtYnVuZGxlZCBvdXRwdXRzLlxuICAgICAgY29uc3QgX3JlcXVpcmUgPSBjcmVhdGVSZXF1aXJlKFxuICAgICAgICBwYXRoVG9GaWxlVVJMKHByb2Nlc3MuY3dkKCkgKyAnL3BhY2thZ2UuanNvbicpLmhyZWZcbiAgICAgICk7XG4gICAgICBjb25zdCBtb2QgPSBfcmVxdWlyZShzcGVjKTtcbiAgICAgIF9nZXRQb3J0ID0gbW9kLmdldFBvcnQ7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBNb2R1bGUgbm90IGF2YWlsYWJsZSAoZS5nLiwgaW4gYSBicm93c2VyIG9yIG1pbmltYWwgYnVuZGxlKVxuICAgICAgX2dldFBvcnQgPSBhc3luYyAoKSA9PiB1bmRlZmluZWQ7XG4gICAgfVxuICB9XG4gIHJldHVybiBfZ2V0UG9ydCEoKTtcbn1cbiJdfQ==
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]` populated by a successful prior
12
+ * 1. `globalThis[WorldCacheKey]`: populated by a successful prior
13
13
  * `getWorld()` call. This is the steady-state hot path.
14
- * 2. `globalThis[GetWorldFnKey]` populated by the module-load side
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 imports
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
- * `start` without any prior workflow run.
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAM7C,wBAAsB,YAAY,IAAI,OAAO,CAAC,KAAK,CAAC,CAoBnD"}
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"}