@blokjs/runner 0.2.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/dist/Blok.js +32 -3
  2. package/dist/Blok.js.map +1 -1
  3. package/dist/Configuration.d.ts +59 -5
  4. package/dist/Configuration.js +366 -96
  5. package/dist/Configuration.js.map +1 -1
  6. package/dist/ForEachNode.d.ts +59 -0
  7. package/dist/ForEachNode.js +522 -0
  8. package/dist/ForEachNode.js.map +1 -0
  9. package/dist/LoopMaxIterationsError.d.ts +11 -0
  10. package/dist/LoopMaxIterationsError.js +18 -0
  11. package/dist/LoopMaxIterationsError.js.map +1 -0
  12. package/dist/LoopNode.d.ts +36 -0
  13. package/dist/LoopNode.js +182 -0
  14. package/dist/LoopNode.js.map +1 -0
  15. package/dist/PayloadTooLargeError.d.ts +19 -0
  16. package/dist/PayloadTooLargeError.js +29 -0
  17. package/dist/PayloadTooLargeError.js.map +1 -0
  18. package/dist/RunCancelledError.d.ts +17 -0
  19. package/dist/RunCancelledError.js +25 -0
  20. package/dist/RunCancelledError.js.map +1 -0
  21. package/dist/Runner.d.ts +11 -1
  22. package/dist/Runner.js +9 -2
  23. package/dist/Runner.js.map +1 -1
  24. package/dist/RunnerSteps.js +648 -44
  25. package/dist/RunnerSteps.js.map +1 -1
  26. package/dist/RuntimeAdapterNode.d.ts +2 -1
  27. package/dist/RuntimeAdapterNode.js +2 -2
  28. package/dist/RuntimeAdapterNode.js.map +1 -1
  29. package/dist/RuntimeRegistry.d.ts +23 -2
  30. package/dist/RuntimeRegistry.js +31 -2
  31. package/dist/RuntimeRegistry.js.map +1 -1
  32. package/dist/SubworkflowNode.d.ts +181 -0
  33. package/dist/SubworkflowNode.js +479 -0
  34. package/dist/SubworkflowNode.js.map +1 -0
  35. package/dist/SwitchNode.d.ts +37 -0
  36. package/dist/SwitchNode.js +153 -0
  37. package/dist/SwitchNode.js.map +1 -0
  38. package/dist/TriggerBase.d.ts +178 -0
  39. package/dist/TriggerBase.js +1032 -5
  40. package/dist/TriggerBase.js.map +1 -1
  41. package/dist/TryCatchNode.d.ts +32 -0
  42. package/dist/TryCatchNode.js +207 -0
  43. package/dist/TryCatchNode.js.map +1 -0
  44. package/dist/WaitDispatchRequest.d.ts +38 -0
  45. package/dist/WaitDispatchRequest.js +13 -0
  46. package/dist/WaitDispatchRequest.js.map +1 -0
  47. package/dist/WaitNode.d.ts +23 -0
  48. package/dist/WaitNode.js +26 -0
  49. package/dist/WaitNode.js.map +1 -0
  50. package/dist/adapters/grpc/GrpcCodec.js +2 -2
  51. package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +6 -4
  52. package/dist/adapters/grpc/GrpcRuntimeAdapter.js +6 -4
  53. package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
  54. package/dist/adapters/grpc/types.d.ts +7 -5
  55. package/dist/adapters/grpc/types.js.map +1 -1
  56. package/dist/adapters/transport.d.ts +12 -41
  57. package/dist/adapters/transport.js +21 -70
  58. package/dist/adapters/transport.js.map +1 -1
  59. package/dist/cache/NodeResultCache.js +7 -0
  60. package/dist/cache/NodeResultCache.js.map +1 -1
  61. package/dist/concurrency/ConcurrencyBackend.d.ts +61 -0
  62. package/dist/concurrency/ConcurrencyBackend.js +20 -0
  63. package/dist/concurrency/ConcurrencyBackend.js.map +1 -0
  64. package/dist/concurrency/ConcurrencyLimitError.d.ts +37 -0
  65. package/dist/concurrency/ConcurrencyLimitError.js +16 -0
  66. package/dist/concurrency/ConcurrencyLimitError.js.map +1 -0
  67. package/dist/concurrency/NatsKvConcurrencyBackend.d.ts +64 -0
  68. package/dist/concurrency/NatsKvConcurrencyBackend.js +310 -0
  69. package/dist/concurrency/NatsKvConcurrencyBackend.js.map +1 -0
  70. package/dist/concurrency/QueueExpiredError.d.ts +40 -0
  71. package/dist/concurrency/QueueExpiredError.js +15 -0
  72. package/dist/concurrency/QueueExpiredError.js.map +1 -0
  73. package/dist/concurrency/RedisConcurrencyBackend.d.ts +64 -0
  74. package/dist/concurrency/RedisConcurrencyBackend.js +374 -0
  75. package/dist/concurrency/RedisConcurrencyBackend.js.map +1 -0
  76. package/dist/concurrency/createConcurrencyBackend.d.ts +24 -0
  77. package/dist/concurrency/createConcurrencyBackend.js +38 -0
  78. package/dist/concurrency/createConcurrencyBackend.js.map +1 -0
  79. package/dist/concurrency/readConcurrencyConfig.d.ts +60 -0
  80. package/dist/concurrency/readConcurrencyConfig.js +60 -0
  81. package/dist/concurrency/readConcurrencyConfig.js.map +1 -0
  82. package/dist/defineNode.d.ts +8 -0
  83. package/dist/defineNode.js +25 -5
  84. package/dist/defineNode.js.map +1 -1
  85. package/dist/graphql/GraphQLSchemaGenerator.js +1 -1
  86. package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -1
  87. package/dist/idempotency/resolveIdempotencyKey.d.ts +20 -0
  88. package/dist/idempotency/resolveIdempotencyKey.js +37 -0
  89. package/dist/idempotency/resolveIdempotencyKey.js.map +1 -0
  90. package/dist/index.d.ts +30 -6
  91. package/dist/index.js +55 -6
  92. package/dist/index.js.map +1 -1
  93. package/dist/marketplace/RuntimeCatalog.d.ts +6 -0
  94. package/dist/marketplace/RuntimeCatalog.js.map +1 -1
  95. package/dist/marketplace/RuntimeDiscovery.d.ts +2 -2
  96. package/dist/marketplace/RuntimeDiscovery.js +18 -6
  97. package/dist/marketplace/RuntimeDiscovery.js.map +1 -1
  98. package/dist/monitoring/ConcurrencyMetrics.d.ts +82 -0
  99. package/dist/monitoring/ConcurrencyMetrics.js +139 -0
  100. package/dist/monitoring/ConcurrencyMetrics.js.map +1 -0
  101. package/dist/monitoring/ForEachWaitMetrics.d.ts +22 -0
  102. package/dist/monitoring/ForEachWaitMetrics.js +36 -0
  103. package/dist/monitoring/ForEachWaitMetrics.js.map +1 -0
  104. package/dist/monitoring/JanitorMetrics.d.ts +27 -0
  105. package/dist/monitoring/JanitorMetrics.js +48 -0
  106. package/dist/monitoring/JanitorMetrics.js.map +1 -0
  107. package/dist/openapi/OpenAPIGenerator.js +7 -2
  108. package/dist/openapi/OpenAPIGenerator.js.map +1 -1
  109. package/dist/runtime/PrimitiveStack.d.ts +64 -0
  110. package/dist/runtime/PrimitiveStack.js +92 -0
  111. package/dist/runtime/PrimitiveStack.js.map +1 -0
  112. package/dist/scheduling/DebounceBackend.d.ts +108 -0
  113. package/dist/scheduling/DebounceBackend.js +23 -0
  114. package/dist/scheduling/DebounceBackend.js.map +1 -0
  115. package/dist/scheduling/DebounceCoordinator.d.ts +141 -0
  116. package/dist/scheduling/DebounceCoordinator.js +362 -0
  117. package/dist/scheduling/DebounceCoordinator.js.map +1 -0
  118. package/dist/scheduling/DeferredDispatchSignal.d.ts +50 -0
  119. package/dist/scheduling/DeferredDispatchSignal.js +14 -0
  120. package/dist/scheduling/DeferredDispatchSignal.js.map +1 -0
  121. package/dist/scheduling/DeferredRunScheduler.d.ts +96 -0
  122. package/dist/scheduling/DeferredRunScheduler.js +256 -0
  123. package/dist/scheduling/DeferredRunScheduler.js.map +1 -0
  124. package/dist/scheduling/NatsKvDebounceBackend.d.ts +53 -0
  125. package/dist/scheduling/NatsKvDebounceBackend.js +334 -0
  126. package/dist/scheduling/NatsKvDebounceBackend.js.map +1 -0
  127. package/dist/scheduling/RedisDebounceBackend.d.ts +49 -0
  128. package/dist/scheduling/RedisDebounceBackend.js +356 -0
  129. package/dist/scheduling/RedisDebounceBackend.js.map +1 -0
  130. package/dist/scheduling/createDebounceBackend.d.ts +25 -0
  131. package/dist/scheduling/createDebounceBackend.js +39 -0
  132. package/dist/scheduling/createDebounceBackend.js.map +1 -0
  133. package/dist/scheduling/readSchedulingConfig.d.ts +24 -0
  134. package/dist/scheduling/readSchedulingConfig.js +52 -0
  135. package/dist/scheduling/readSchedulingConfig.js.map +1 -0
  136. package/dist/security/AuditLogger.js +1 -1
  137. package/dist/security/AuditLogger.js.map +1 -1
  138. package/dist/security/AuthMiddleware.d.ts +19 -20
  139. package/dist/security/AuthMiddleware.js +35 -20
  140. package/dist/security/AuthMiddleware.js.map +1 -1
  141. package/dist/security/OAuthProvider.js +2 -2
  142. package/dist/security/OAuthProvider.js.map +1 -1
  143. package/dist/security/SecretManager.js +14 -13
  144. package/dist/security/SecretManager.js.map +1 -1
  145. package/dist/security/index.d.ts +3 -1
  146. package/dist/security/index.js +3 -1
  147. package/dist/security/index.js.map +1 -1
  148. package/dist/testing/TestHarness.d.ts +27 -12
  149. package/dist/testing/TestHarness.js +19 -3
  150. package/dist/testing/TestHarness.js.map +1 -1
  151. package/dist/testing/WorkflowTestRunner.js +0 -7
  152. package/dist/testing/WorkflowTestRunner.js.map +1 -1
  153. package/dist/timeouts/StepTimeoutError.d.ts +22 -0
  154. package/dist/timeouts/StepTimeoutError.js +31 -0
  155. package/dist/timeouts/StepTimeoutError.js.map +1 -0
  156. package/dist/tracing/InMemoryRunStore.d.ts +41 -1
  157. package/dist/tracing/InMemoryRunStore.js +239 -0
  158. package/dist/tracing/InMemoryRunStore.js.map +1 -1
  159. package/dist/tracing/Janitor.d.ts +70 -0
  160. package/dist/tracing/Janitor.js +150 -0
  161. package/dist/tracing/Janitor.js.map +1 -0
  162. package/dist/tracing/PostgresRunStore.d.ts +57 -1
  163. package/dist/tracing/PostgresRunStore.js +711 -6
  164. package/dist/tracing/PostgresRunStore.js.map +1 -1
  165. package/dist/tracing/RoutingDiagnostics.d.ts +55 -0
  166. package/dist/tracing/RoutingDiagnostics.js +50 -0
  167. package/dist/tracing/RoutingDiagnostics.js.map +1 -0
  168. package/dist/tracing/RunStore.d.ts +181 -1
  169. package/dist/tracing/RunTracker.d.ts +244 -9
  170. package/dist/tracing/RunTracker.js +594 -1
  171. package/dist/tracing/RunTracker.js.map +1 -1
  172. package/dist/tracing/SqliteRunStore.d.ts +79 -2
  173. package/dist/tracing/SqliteRunStore.js +775 -16
  174. package/dist/tracing/SqliteRunStore.js.map +1 -1
  175. package/dist/tracing/TraceRouter.d.ts +20 -2
  176. package/dist/tracing/TraceRouter.js +612 -6
  177. package/dist/tracing/TraceRouter.js.map +1 -1
  178. package/dist/tracing/createStore.js +14 -3
  179. package/dist/tracing/createStore.js.map +1 -1
  180. package/dist/tracing/metadataFilter.d.ts +63 -0
  181. package/dist/tracing/metadataFilter.js +224 -0
  182. package/dist/tracing/metadataFilter.js.map +1 -0
  183. package/dist/tracing/sanitize.d.ts +11 -0
  184. package/dist/tracing/sanitize.js +29 -0
  185. package/dist/tracing/sanitize.js.map +1 -1
  186. package/dist/tracing/types.d.ts +672 -2
  187. package/dist/utils/createChildContext.d.ts +32 -0
  188. package/dist/utils/createChildContext.js +113 -0
  189. package/dist/utils/createChildContext.js.map +1 -0
  190. package/dist/utils/envAllowlist.d.ts +35 -0
  191. package/dist/utils/envAllowlist.js +113 -0
  192. package/dist/utils/envAllowlist.js.map +1 -0
  193. package/dist/version/RuntimeVersionValidator.d.ts +38 -0
  194. package/dist/version/RuntimeVersionValidator.js +121 -0
  195. package/dist/version/RuntimeVersionValidator.js.map +1 -0
  196. package/dist/visualization/WorkflowVisualizer.js +4 -4
  197. package/dist/visualization/WorkflowVisualizer.js.map +1 -1
  198. package/dist/workflow/PersistenceHelper.d.ts +18 -10
  199. package/dist/workflow/PersistenceHelper.js +35 -9
  200. package/dist/workflow/PersistenceHelper.js.map +1 -1
  201. package/dist/workflow/WorkflowNormalizer.d.ts +48 -42
  202. package/dist/workflow/WorkflowNormalizer.js +650 -18
  203. package/dist/workflow/WorkflowNormalizer.js.map +1 -1
  204. package/dist/workflow/WorkflowRegistry.d.ts +186 -0
  205. package/dist/workflow/WorkflowRegistry.js +202 -0
  206. package/dist/workflow/WorkflowRegistry.js.map +1 -0
  207. package/dist/workflow/sampleBody.d.ts +54 -0
  208. package/dist/workflow/sampleBody.js +320 -0
  209. package/dist/workflow/sampleBody.js.map +1 -0
  210. package/package.json +3 -8
  211. package/dist/adapters/HttpRuntimeAdapter.d.ts +0 -79
  212. package/dist/adapters/HttpRuntimeAdapter.js +0 -233
  213. package/dist/adapters/HttpRuntimeAdapter.js.map +0 -1
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Tier C #1 · Cross-process debounce backend interface.
3
+ *
4
+ * Mirrors the relationship between `ConcurrencyBackend` and the
5
+ * `RunStore`-backed concurrency gate: an optional async capability that
6
+ * lets `DebounceCoordinator` coordinate windows across processes
7
+ * (NATS KV, Redis) instead of relying on the local in-memory Map.
8
+ *
9
+ * Default behavior is unchanged — when no backend is set, the
10
+ * coordinator uses its existing in-memory state. The backend is opt-in
11
+ * via `BLOK_DEBOUNCE_BACKEND={nats-kv|redis}` and installed by trigger
12
+ * packages during `listen()`.
13
+ *
14
+ * **Semantic trade-off**: this v1 ships *owner-local payload*
15
+ * semantics. The owning process's captured closure (`onFire`) fires
16
+ * when its local timer elapses; payloads from coalesce pings on other
17
+ * processes are dropped (the doc only records `pingCount`,
18
+ * `lastPingAt`, and `scheduledAt`). Cross-process latest-payload-wins
19
+ * is a deferred follow-up — would require persisting each ping's
20
+ * payload to the shared doc.
21
+ */
22
+ export type DebounceBackendMode = "leading" | "trailing";
23
+ export interface DebounceRegisterBackendOpts {
24
+ workflowName: string;
25
+ debounceKey: string;
26
+ mode: DebounceBackendMode;
27
+ delayMs: number;
28
+ maxDelayMs?: number;
29
+ /** Run id allocated by the caller for THIS ping. */
30
+ runId: string;
31
+ /** Process identity used for owner-lease attribution. */
32
+ processId: string;
33
+ /** Owner lease in ms — when expired, another process can take over. */
34
+ ownerLeaseMs: number;
35
+ /** Current wall-clock ms. */
36
+ now: number;
37
+ }
38
+ /**
39
+ * Result of a `registerPing`. Three outcomes, mirroring the
40
+ * single-process `DebounceRegisterResult` shape:
41
+ *
42
+ * - `"owner-new"` — caller is the new owner of a fresh window.
43
+ * For leading mode: fire synchronously. For trailing mode: start a
44
+ * local timer to fire at `scheduledAt`.
45
+ * - `"owner-extend"` — caller is the existing owner; window extended.
46
+ * Cancel + restart local timer to fire at the new `scheduledAt`.
47
+ * - `"coalesce"` — caller is NOT the owner. Mark the run `debounced`
48
+ * with `intoRunId = activeRunId`.
49
+ */
50
+ export interface DebounceRegisterBackendResult {
51
+ outcome: "owner-new" | "owner-extend" | "coalesce";
52
+ activeRunId: string;
53
+ /** The runId of the OWNING process. Equal to opts.runId when outcome ∈ {owner-new, owner-extend}; differs on coalesce. */
54
+ scheduledAt: number;
55
+ pingCount: number;
56
+ }
57
+ /**
58
+ * Result of a `finalize` call from an owning process's local timer.
59
+ *
60
+ * - `"fire"` — caller still owns AND the silence period elapsed.
61
+ * Bucket has been atomically DELETEd; caller dispatches.
62
+ * - `"reschedule"` — caller still owns, but coalesce pings from other
63
+ * processes pushed `scheduledAt` forward. Caller restarts local
64
+ * timer for `scheduledAt - now`.
65
+ * - `"abandoned"` — caller's lease expired and another process took
66
+ * ownership. Caller silently drops the closure.
67
+ */
68
+ export type DebounceFinalizeResult = {
69
+ finalize: "fire";
70
+ } | {
71
+ finalize: "reschedule";
72
+ scheduledAt: number;
73
+ } | {
74
+ finalize: "abandoned";
75
+ };
76
+ export interface DebounceBackend {
77
+ /** Identifying string for logs/metrics. e.g. `"nats-kv"`, `"redis"`. */
78
+ readonly name: string;
79
+ /** Lifecycle — open the underlying connection. Idempotent. */
80
+ connect(): Promise<void>;
81
+ /** Lifecycle — close the underlying connection. Idempotent. */
82
+ disconnect(): Promise<void>;
83
+ /**
84
+ * Atomically record a ping against the `(workflow, debounceKey)`
85
+ * bucket and decide ownership. Returns one of the three outcomes
86
+ * documented on {@link DebounceRegisterBackendResult}.
87
+ */
88
+ registerPing(opts: DebounceRegisterBackendOpts): Promise<DebounceRegisterBackendResult>;
89
+ /**
90
+ * Owner calls this on local timer fire. Atomically:
91
+ * - If `runId` still owns AND `now >= scheduledAt` → DELETE the
92
+ * bucket and return `{finalize: "fire"}`.
93
+ * - If `runId` still owns but `now < scheduledAt` → return
94
+ * `{finalize: "reschedule", scheduledAt}`.
95
+ * - If `runId` no longer owns (lease expired + handoff) → return
96
+ * `{finalize: "abandoned"}`.
97
+ */
98
+ finalize(workflowName: string, debounceKey: string, runId: string, now: number): Promise<DebounceFinalizeResult>;
99
+ /** Cancel an active window without firing. Returns true if cancelled. */
100
+ cancel(workflowName: string, debounceKey: string): Promise<boolean>;
101
+ /**
102
+ * Janitor sweep — purge every bucket whose owner lease expired
103
+ * AND whose `scheduledAt` is in the past. Returns the count of
104
+ * purged buckets. Cheap per-bucket lazy-purge happens inside the
105
+ * registerPing/finalize scripts.
106
+ */
107
+ purgeExpired(now: number): Promise<number>;
108
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tier C #1 · Cross-process debounce backend interface.
3
+ *
4
+ * Mirrors the relationship between `ConcurrencyBackend` and the
5
+ * `RunStore`-backed concurrency gate: an optional async capability that
6
+ * lets `DebounceCoordinator` coordinate windows across processes
7
+ * (NATS KV, Redis) instead of relying on the local in-memory Map.
8
+ *
9
+ * Default behavior is unchanged — when no backend is set, the
10
+ * coordinator uses its existing in-memory state. The backend is opt-in
11
+ * via `BLOK_DEBOUNCE_BACKEND={nats-kv|redis}` and installed by trigger
12
+ * packages during `listen()`.
13
+ *
14
+ * **Semantic trade-off**: this v1 ships *owner-local payload*
15
+ * semantics. The owning process's captured closure (`onFire`) fires
16
+ * when its local timer elapses; payloads from coalesce pings on other
17
+ * processes are dropped (the doc only records `pingCount`,
18
+ * `lastPingAt`, and `scheduledAt`). Cross-process latest-payload-wins
19
+ * is a deferred follow-up — would require persisting each ping's
20
+ * payload to the shared doc.
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=DebounceBackend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DebounceBackend.js","sourceRoot":"","sources":["../../src/scheduling/DebounceBackend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Tier 2 #7 — debounce coordinator. Tier C #1 — cross-process awareness.
3
+ *
4
+ * Coalesces rapid same-key triggers into a single delayed run. Modes:
5
+ *
6
+ * - `"trailing"` (default): each ping resets a timer; the run fires
7
+ * after `delayMs` of silence. Latest payload wins (within a single
8
+ * owning process). `maxDelayMs` bounds tail latency — even with
9
+ * continuous pings, the run fires after `maxDelayMs` from the FIRST
10
+ * ping.
11
+ * - `"leading"`: the first ping fires immediately. Subsequent pings
12
+ * within `delayMs` are dropped. Window resets after `delayMs` of
13
+ * silence.
14
+ *
15
+ * Process-wide singleton. **Default**: in-memory only — fast,
16
+ * synchronous internally. **Cross-process mode (Tier C #1)**: install a
17
+ * `DebounceBackend` via `setBackend()` and the coordinator routes
18
+ * register/cancel through the backend with the same outcome surface,
19
+ * keeping a local timer + closure for the OWNING process. The
20
+ * `register()` API is async — callers must `await` it.
21
+ *
22
+ * Latest-payload semantics in cross-process mode: **owner-local**.
23
+ * Pings from a non-owning process bump pingCount + push scheduledAt in
24
+ * the shared doc but do NOT contribute their payload — only the
25
+ * owning process's captured `onFire` closure fires. Cross-process
26
+ * latest-payload-wins is deferred (would require persisting each
27
+ * ping's payload to the shared doc; tracked in BACKLOG).
28
+ */
29
+ import type { DebounceBackend } from "./DebounceBackend";
30
+ export type DebounceDispatchFn = () => Promise<void>;
31
+ export type DebounceMode = "leading" | "trailing";
32
+ export interface DebounceRegisterOpts {
33
+ workflowName: string;
34
+ debounceKey: string;
35
+ mode: DebounceMode;
36
+ delayMs: number;
37
+ maxDelayMs?: number;
38
+ /**
39
+ * Run id allocated by the caller for THIS ping. On a fresh window
40
+ * this becomes the active run id; on a coalesce it's a "loser" and
41
+ * the caller marks it `debounced` terminal.
42
+ */
43
+ runId: string;
44
+ /**
45
+ * Callback the coordinator invokes when the trailing-mode window
46
+ * closes. Captures ctx + runId via closure. NOT called for leading
47
+ * mode (the caller runs the first ping synchronously) or for
48
+ * coalesce pings (those are dropped by definition).
49
+ */
50
+ onFire: DebounceDispatchFn;
51
+ /**
52
+ * Test-only override of "now" for deterministic tests. Production
53
+ * code does not pass this.
54
+ */
55
+ __now?: number;
56
+ }
57
+ /**
58
+ * The outcome of `register`. Branches the caller's behavior.
59
+ *
60
+ * - `"fire-immediate"` (leading + fresh window) — caller runs the
61
+ * workflow synchronously. The window is now open; subsequent pings
62
+ * in `delayMs` are suppressed.
63
+ * - `"schedule-trailing"` (trailing + fresh window) — caller marks the
64
+ * run `debounced` (transient) and throws `DeferredDispatchSignal`.
65
+ * The coordinator's timer will invoke `onFire` after the window.
66
+ * - `"coalesce"` (leading or trailing + existing window) — caller marks
67
+ * the run `debounced` (terminal, with `intoRunId = activeRunId`) and
68
+ * throws `DeferredDispatchSignal`. The active run absorbs the ping.
69
+ */
70
+ export interface DebounceRegisterResult {
71
+ outcome: "fire-immediate" | "schedule-trailing" | "coalesce";
72
+ /**
73
+ * The runId of the run that owns the active window. Equal to
74
+ * `opts.runId` on `fire-immediate` and `schedule-trailing`; equal to
75
+ * the EXISTING window's runId on `coalesce` (so the caller knows
76
+ * which run absorbed the ping).
77
+ */
78
+ activeRunId: string;
79
+ /** Dispatch time for trailing windows; undefined on `fire-immediate`. */
80
+ scheduledAt?: number;
81
+ /** Pings absorbed by the active window so far (including this one). */
82
+ pingCount: number;
83
+ }
84
+ export declare class DebounceCoordinator {
85
+ private static instance;
86
+ private states;
87
+ private backend;
88
+ /** Process identity for cross-process owner-lease attribution. Stable for the lifetime of the singleton. */
89
+ private readonly processId;
90
+ private ownerLeaseMs;
91
+ static getInstance(): DebounceCoordinator;
92
+ /** Test-only — reset the singleton + clear all state. */
93
+ static resetInstance(): void;
94
+ /**
95
+ * Install a cross-process backend. Set via `HttpTrigger.listen()` /
96
+ * `WorkerTrigger.listen()` when `BLOK_DEBOUNCE_BACKEND` is configured.
97
+ * Pass `null` to revert to the in-memory fast path.
98
+ */
99
+ setBackend(backend: DebounceBackend | null): void;
100
+ getBackend(): DebounceBackend | null;
101
+ /**
102
+ * Override the owner-lease duration. Used by `HttpTrigger.listen()` /
103
+ * `WorkerTrigger.listen()` to apply `BLOK_DEBOUNCE_OWNER_LEASE_MS`.
104
+ */
105
+ setOwnerLeaseMs(ms: number): void;
106
+ private bucket;
107
+ register(opts: DebounceRegisterOpts): Promise<DebounceRegisterResult>;
108
+ /**
109
+ * In-memory fast path — preserved exactly from the v1 single-process
110
+ * implementation. Synchronous internally; the outer signature is
111
+ * async so callers don't need to discriminate.
112
+ */
113
+ private registerLocal;
114
+ private fireTrailingLocal;
115
+ /**
116
+ * Cross-process path — delegates ownership to the backend and uses
117
+ * a local timer + closure for the OWNING process.
118
+ *
119
+ * Three outcomes from the backend:
120
+ * - `owner-new`: this process is the new owner. Start a local timer
121
+ * + closure; on fire, atomically finalize via the backend.
122
+ * - `owner-extend`: this process is already the owner. Cancel +
123
+ * restart the local timer at the new scheduledAt; refresh closure.
124
+ * - `coalesce`: another process owns the window. Just return.
125
+ *
126
+ * Leading mode: `owner-new` translates to `fire-immediate` (caller
127
+ * fires synchronously); `owner-extend`/`coalesce` translate to
128
+ * `coalesce`.
129
+ */
130
+ private registerCrossProcess;
131
+ private installOwnerTimer;
132
+ private fireTrailingCrossProcess;
133
+ /** Cancel an active window without firing. Returns true if cancelled. */
134
+ cancel(workflowName: string, debounceKey: string): Promise<boolean>;
135
+ /** Number of active LOCAL debounce windows. Tests + observability. Excludes cross-process windows owned by other processes. */
136
+ size(): number;
137
+ /** True if THIS process has a local window for `(workflow, key)`. Excludes windows owned by other processes. */
138
+ has(workflowName: string, debounceKey: string): boolean;
139
+ /** Cancel everything without firing. Local state only — cross-process buckets fall back to lease-expiry. */
140
+ clear(): void;
141
+ }
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Tier 2 #7 — debounce coordinator. Tier C #1 — cross-process awareness.
3
+ *
4
+ * Coalesces rapid same-key triggers into a single delayed run. Modes:
5
+ *
6
+ * - `"trailing"` (default): each ping resets a timer; the run fires
7
+ * after `delayMs` of silence. Latest payload wins (within a single
8
+ * owning process). `maxDelayMs` bounds tail latency — even with
9
+ * continuous pings, the run fires after `maxDelayMs` from the FIRST
10
+ * ping.
11
+ * - `"leading"`: the first ping fires immediately. Subsequent pings
12
+ * within `delayMs` are dropped. Window resets after `delayMs` of
13
+ * silence.
14
+ *
15
+ * Process-wide singleton. **Default**: in-memory only — fast,
16
+ * synchronous internally. **Cross-process mode (Tier C #1)**: install a
17
+ * `DebounceBackend` via `setBackend()` and the coordinator routes
18
+ * register/cancel through the backend with the same outcome surface,
19
+ * keeping a local timer + closure for the OWNING process. The
20
+ * `register()` API is async — callers must `await` it.
21
+ *
22
+ * Latest-payload semantics in cross-process mode: **owner-local**.
23
+ * Pings from a non-owning process bump pingCount + push scheduledAt in
24
+ * the shared doc but do NOT contribute their payload — only the
25
+ * owning process's captured `onFire` closure fires. Cross-process
26
+ * latest-payload-wins is deferred (would require persisting each
27
+ * ping's payload to the shared doc; tracked in BACKLOG).
28
+ */
29
+ import { randomUUID } from "node:crypto";
30
+ const DEFAULT_OWNER_LEASE_MS = 60_000;
31
+ export class DebounceCoordinator {
32
+ static instance = null;
33
+ states = new Map();
34
+ backend = null;
35
+ /** Process identity for cross-process owner-lease attribution. Stable for the lifetime of the singleton. */
36
+ processId = randomUUID();
37
+ ownerLeaseMs = DEFAULT_OWNER_LEASE_MS;
38
+ static getInstance() {
39
+ if (!DebounceCoordinator.instance) {
40
+ DebounceCoordinator.instance = new DebounceCoordinator();
41
+ }
42
+ return DebounceCoordinator.instance;
43
+ }
44
+ /** Test-only — reset the singleton + clear all state. */
45
+ static resetInstance() {
46
+ DebounceCoordinator.instance?.clear();
47
+ DebounceCoordinator.instance = null;
48
+ }
49
+ /**
50
+ * Install a cross-process backend. Set via `HttpTrigger.listen()` /
51
+ * `WorkerTrigger.listen()` when `BLOK_DEBOUNCE_BACKEND` is configured.
52
+ * Pass `null` to revert to the in-memory fast path.
53
+ */
54
+ setBackend(backend) {
55
+ this.backend = backend;
56
+ }
57
+ getBackend() {
58
+ return this.backend;
59
+ }
60
+ /**
61
+ * Override the owner-lease duration. Used by `HttpTrigger.listen()` /
62
+ * `WorkerTrigger.listen()` to apply `BLOK_DEBOUNCE_OWNER_LEASE_MS`.
63
+ */
64
+ setOwnerLeaseMs(ms) {
65
+ if (Number.isFinite(ms) && ms > 0) {
66
+ this.ownerLeaseMs = ms;
67
+ }
68
+ }
69
+ bucket(workflowName, debounceKey) {
70
+ return `${workflowName}\x1f${debounceKey}`;
71
+ }
72
+ async register(opts) {
73
+ if (this.backend) {
74
+ return this.registerCrossProcess(opts);
75
+ }
76
+ return this.registerLocal(opts);
77
+ }
78
+ /**
79
+ * In-memory fast path — preserved exactly from the v1 single-process
80
+ * implementation. Synchronous internally; the outer signature is
81
+ * async so callers don't need to discriminate.
82
+ */
83
+ registerLocal(opts) {
84
+ const now = opts.__now ?? Date.now();
85
+ const bucketKey = this.bucket(opts.workflowName, opts.debounceKey);
86
+ const existing = this.states.get(bucketKey);
87
+ // === Leading mode ===
88
+ if (opts.mode === "leading") {
89
+ if (existing) {
90
+ // Window active — coalesce this ping (drop the run).
91
+ existing.pingCount += 1;
92
+ existing.lastPingAt = now;
93
+ return {
94
+ outcome: "coalesce",
95
+ activeRunId: existing.activeRunId,
96
+ pingCount: existing.pingCount,
97
+ };
98
+ }
99
+ // Open a new window. Caller fires synchronously; coordinator just
100
+ // tracks state to suppress follow-ups for `delayMs`.
101
+ const state = {
102
+ bucketKey,
103
+ mode: "leading",
104
+ delayMs: opts.delayMs,
105
+ maxDelayMs: opts.maxDelayMs,
106
+ firstPingAt: now,
107
+ lastPingAt: now,
108
+ pingCount: 1,
109
+ activeRunId: opts.runId,
110
+ };
111
+ // Auto-close the window after delayMs of silence — clear state so
112
+ // subsequent pings start fresh.
113
+ state.timer = setTimeout(() => {
114
+ const cur = this.states.get(bucketKey);
115
+ if (cur && cur.lastPingAt + opts.delayMs <= Date.now()) {
116
+ this.states.delete(bucketKey);
117
+ }
118
+ }, opts.delayMs);
119
+ this.states.set(bucketKey, state);
120
+ return { outcome: "fire-immediate", activeRunId: opts.runId, pingCount: 1 };
121
+ }
122
+ // === Trailing mode ===
123
+ if (existing) {
124
+ // Extend the window: cancel the old timer, set a new one. Latest
125
+ // payload wins via the captured `onFire`.
126
+ if (existing.timer)
127
+ clearTimeout(existing.timer);
128
+ existing.pingCount += 1;
129
+ existing.lastPingAt = now;
130
+ existing.onFire = opts.onFire;
131
+ const naiveDeadline = now + opts.delayMs;
132
+ const dispatchAt = existing.maxDelayDeadline !== undefined ? Math.min(naiveDeadline, existing.maxDelayDeadline) : naiveDeadline;
133
+ const wait = Math.max(0, dispatchAt - now);
134
+ existing.timer = setTimeout(() => this.fireTrailingLocal(bucketKey), wait);
135
+ return {
136
+ outcome: "coalesce",
137
+ activeRunId: existing.activeRunId,
138
+ scheduledAt: dispatchAt,
139
+ pingCount: existing.pingCount,
140
+ };
141
+ }
142
+ // New trailing window.
143
+ const state = {
144
+ bucketKey,
145
+ mode: "trailing",
146
+ delayMs: opts.delayMs,
147
+ maxDelayMs: opts.maxDelayMs,
148
+ firstPingAt: now,
149
+ lastPingAt: now,
150
+ pingCount: 1,
151
+ activeRunId: opts.runId,
152
+ maxDelayDeadline: opts.maxDelayMs !== undefined ? now + opts.maxDelayMs : undefined,
153
+ onFire: opts.onFire,
154
+ };
155
+ const naiveDeadline = now + opts.delayMs;
156
+ const dispatchAt = state.maxDelayDeadline !== undefined ? Math.min(naiveDeadline, state.maxDelayDeadline) : naiveDeadline;
157
+ const wait = Math.max(0, dispatchAt - now);
158
+ state.timer = setTimeout(() => this.fireTrailingLocal(bucketKey), wait);
159
+ this.states.set(bucketKey, state);
160
+ return {
161
+ outcome: "schedule-trailing",
162
+ activeRunId: opts.runId,
163
+ scheduledAt: dispatchAt,
164
+ pingCount: 1,
165
+ };
166
+ }
167
+ fireTrailingLocal(bucketKey) {
168
+ const state = this.states.get(bucketKey);
169
+ if (!state)
170
+ return;
171
+ this.states.delete(bucketKey);
172
+ if (state.onFire) {
173
+ void state.onFire().catch((err) => {
174
+ console.error(`[blok][scheduling] DebounceCoordinator trailing-fire failed for key ${bucketKey}:`, err instanceof Error ? err.stack || err.message : err);
175
+ });
176
+ }
177
+ }
178
+ /**
179
+ * Cross-process path — delegates ownership to the backend and uses
180
+ * a local timer + closure for the OWNING process.
181
+ *
182
+ * Three outcomes from the backend:
183
+ * - `owner-new`: this process is the new owner. Start a local timer
184
+ * + closure; on fire, atomically finalize via the backend.
185
+ * - `owner-extend`: this process is already the owner. Cancel +
186
+ * restart the local timer at the new scheduledAt; refresh closure.
187
+ * - `coalesce`: another process owns the window. Just return.
188
+ *
189
+ * Leading mode: `owner-new` translates to `fire-immediate` (caller
190
+ * fires synchronously); `owner-extend`/`coalesce` translate to
191
+ * `coalesce`.
192
+ */
193
+ async registerCrossProcess(opts) {
194
+ const backend = this.backend;
195
+ if (!backend)
196
+ return this.registerLocal(opts);
197
+ const now = opts.__now ?? Date.now();
198
+ const bucketKey = this.bucket(opts.workflowName, opts.debounceKey);
199
+ let res;
200
+ try {
201
+ res = await backend.registerPing({
202
+ workflowName: opts.workflowName,
203
+ debounceKey: opts.debounceKey,
204
+ mode: opts.mode,
205
+ delayMs: opts.delayMs,
206
+ maxDelayMs: opts.maxDelayMs,
207
+ runId: opts.runId,
208
+ processId: this.processId,
209
+ ownerLeaseMs: this.ownerLeaseMs,
210
+ now,
211
+ });
212
+ }
213
+ catch (err) {
214
+ // Fail-open — fall back to local in-memory window. Same posture
215
+ // as the concurrency-backend fail-fast path (deny-on-error) but
216
+ // debounce isn't a safety gate — better to admit the ping than
217
+ // drop it on a transient broker outage.
218
+ console.warn(`[blok][scheduling] debounce backend registerPing failed for ${bucketKey}: ${err instanceof Error ? err.message : String(err)}; falling back to in-memory window`);
219
+ return this.registerLocal(opts);
220
+ }
221
+ // === Leading mode ===
222
+ if (opts.mode === "leading") {
223
+ if (res.outcome === "owner-new") {
224
+ // Caller fires synchronously; we don't keep a local timer for
225
+ // leading mode (the backend's owner-lease IS the window).
226
+ return { outcome: "fire-immediate", activeRunId: opts.runId, pingCount: res.pingCount };
227
+ }
228
+ return { outcome: "coalesce", activeRunId: res.activeRunId, pingCount: res.pingCount };
229
+ }
230
+ // === Trailing mode ===
231
+ if (res.outcome === "owner-new") {
232
+ // New trailing window owned by this process. Capture the closure +
233
+ // start a local timer to fire at backend-decided scheduledAt.
234
+ this.installOwnerTimer(bucketKey, opts, res.scheduledAt, now);
235
+ return {
236
+ outcome: "schedule-trailing",
237
+ activeRunId: opts.runId,
238
+ scheduledAt: res.scheduledAt,
239
+ pingCount: res.pingCount,
240
+ };
241
+ }
242
+ if (res.outcome === "owner-extend") {
243
+ // We still own. Replace the captured closure (latest payload
244
+ // wins within this process) + reschedule.
245
+ this.installOwnerTimer(bucketKey, opts, res.scheduledAt, now);
246
+ return {
247
+ outcome: "coalesce",
248
+ activeRunId: res.activeRunId,
249
+ scheduledAt: res.scheduledAt,
250
+ pingCount: res.pingCount,
251
+ };
252
+ }
253
+ // outcome === "coalesce" — another process owns. Do not install a
254
+ // local timer; the owning process drives the fire.
255
+ return {
256
+ outcome: "coalesce",
257
+ activeRunId: res.activeRunId,
258
+ scheduledAt: res.scheduledAt,
259
+ pingCount: res.pingCount,
260
+ };
261
+ }
262
+ installOwnerTimer(bucketKey, opts, scheduledAt, now) {
263
+ const existing = this.states.get(bucketKey);
264
+ if (existing?.timer)
265
+ clearTimeout(existing.timer);
266
+ const state = {
267
+ bucketKey,
268
+ mode: opts.mode,
269
+ delayMs: opts.delayMs,
270
+ maxDelayMs: opts.maxDelayMs,
271
+ firstPingAt: existing?.firstPingAt ?? now,
272
+ lastPingAt: now,
273
+ pingCount: (existing?.pingCount ?? 0) + 1,
274
+ activeRunId: opts.runId,
275
+ maxDelayDeadline: existing?.maxDelayDeadline ?? (opts.maxDelayMs !== undefined ? now + opts.maxDelayMs : undefined),
276
+ onFire: opts.onFire,
277
+ };
278
+ const wait = Math.max(0, scheduledAt - now);
279
+ state.timer = setTimeout(() => {
280
+ void this.fireTrailingCrossProcess(bucketKey, opts.workflowName, opts.debounceKey, opts.runId);
281
+ }, wait);
282
+ this.states.set(bucketKey, state);
283
+ }
284
+ async fireTrailingCrossProcess(bucketKey, workflowName, debounceKey, runId) {
285
+ const backend = this.backend;
286
+ const state = this.states.get(bucketKey);
287
+ if (!backend || !state)
288
+ return;
289
+ const now = Date.now();
290
+ let result;
291
+ try {
292
+ result = await backend.finalize(workflowName, debounceKey, runId, now);
293
+ }
294
+ catch (err) {
295
+ // Treat as abandoned — owner-lease will eventually expire and
296
+ // another process can take over. Don't fire to avoid duplicate
297
+ // dispatch.
298
+ console.warn(`[blok][scheduling] debounce backend finalize failed for ${bucketKey}: ${err instanceof Error ? err.message : String(err)}; abandoning local owner state`);
299
+ this.states.delete(bucketKey);
300
+ return;
301
+ }
302
+ if (result.finalize === "fire") {
303
+ this.states.delete(bucketKey);
304
+ if (state.onFire) {
305
+ void state.onFire().catch((err) => {
306
+ console.error(`[blok][scheduling] DebounceCoordinator cross-process fire failed for key ${bucketKey}:`, err instanceof Error ? err.stack || err.message : err);
307
+ });
308
+ }
309
+ return;
310
+ }
311
+ if (result.finalize === "reschedule") {
312
+ // Coalesce pings from other processes pushed scheduledAt forward.
313
+ // Reschedule local timer; closure stays.
314
+ if (state.timer)
315
+ clearTimeout(state.timer);
316
+ const wait = Math.max(0, result.scheduledAt - now);
317
+ state.timer = setTimeout(() => {
318
+ void this.fireTrailingCrossProcess(bucketKey, workflowName, debounceKey, runId);
319
+ }, wait);
320
+ return;
321
+ }
322
+ // finalize === "abandoned" — lease expired; another process took
323
+ // over. Drop the closure silently.
324
+ this.states.delete(bucketKey);
325
+ }
326
+ /** Cancel an active window without firing. Returns true if cancelled. */
327
+ async cancel(workflowName, debounceKey) {
328
+ const bucketKey = this.bucket(workflowName, debounceKey);
329
+ const state = this.states.get(bucketKey);
330
+ if (state?.timer)
331
+ clearTimeout(state.timer);
332
+ const hadLocal = this.states.delete(bucketKey);
333
+ if (this.backend) {
334
+ try {
335
+ const cancelled = await this.backend.cancel(workflowName, debounceKey);
336
+ return cancelled || hadLocal;
337
+ }
338
+ catch (err) {
339
+ console.warn(`[blok][scheduling] debounce backend cancel failed for ${bucketKey}: ${err instanceof Error ? err.message : String(err)}`);
340
+ return hadLocal;
341
+ }
342
+ }
343
+ return hadLocal;
344
+ }
345
+ /** Number of active LOCAL debounce windows. Tests + observability. Excludes cross-process windows owned by other processes. */
346
+ size() {
347
+ return this.states.size;
348
+ }
349
+ /** True if THIS process has a local window for `(workflow, key)`. Excludes windows owned by other processes. */
350
+ has(workflowName, debounceKey) {
351
+ return this.states.has(this.bucket(workflowName, debounceKey));
352
+ }
353
+ /** Cancel everything without firing. Local state only — cross-process buckets fall back to lease-expiry. */
354
+ clear() {
355
+ for (const state of this.states.values()) {
356
+ if (state.timer)
357
+ clearTimeout(state.timer);
358
+ }
359
+ this.states.clear();
360
+ }
361
+ }
362
+ //# sourceMappingURL=DebounceCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DebounceCoordinator.js","sourceRoot":"","sources":["../../src/scheduling/DebounceCoordinator.ts"],"names":[],"mappings":"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"}