octaflow 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -203,7 +203,7 @@ next to what your steps actually do.
203
203
  | ↩️ | **Saga compensation** — run rollback handlers in reverse order on failure |
204
204
  | 🚑 | **Crash recovery** — a step whose worker died is re-queued while its attempt budget lasts |
205
205
  | 💓 | **Heartbeats** — a long step proves it is alive, so a dead one is caught in seconds, not minutes |
206
- | 🔭 | **Observability** — lifecycle events (run history) + per-step spans, both pluggable |
206
+ | 🔭 | **Observability** — lifecycle events (run history), per-step spans, and `createMeterObserver` turning the events into OpenTelemetry-style metrics — all pluggable, all structural |
207
207
  | 🤖 | **AI add-on** — instrumented models, token/cost capture, quota, daily rollups |
208
208
  | 🧱 | **Pluggable everything** — `WorkflowStore`, `Dispatcher`, `StepGate`, `FlowObserver`, hooks |
209
209
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { $ as computeReadiness, A as StepConditionHandler, B as StepStatus, C as defineWaitStep, D as StartOptions, E as RetryPolicy, F as StepHandlerNotFoundError, G as WorkflowNotFoundError, H as WorkflowCreatedResult, I as StepHandlerRegistry, J as WorkflowStatus, K as WorkflowNotRetryableError, L as StepId, M as StepError, N as StepExecutionContext, O as StepCompensateHandler, P as StepHandler, Q as ReadinessPlan, R as StepRecord, S as defineSubWorkflowStep, T as InvalidWorkflowDefinitionError, U as WorkflowDefinition, V as WaitTimeoutPolicy, W as WorkflowId, X as JoinRule, Y as WorkflowWithSteps, Z as PlannedSkip, _ as WorkflowOutput, a as STEP_DISPLAY_STATUS, b as defineSleepStep, c as WORKFLOW_STATUS_SCHEMA, d as toPublicWorkflow, et as isTerminalStepStatus, f as StepDeps, g as TypedWorkflow, h as TypedStepContext, i as PublicWorkflowStep, it as ok, j as StepDefinition, k as StepCompensationContext, l as toDisplayStepStatus, m as TypedStep, n as PUBLIC_WORKFLOW_STEP_SCHEMA, nt as Result, o as STEP_DISPLAY_STATUS_SCHEMA, p as StepOutput, q as WorkflowRecord, r as PublicWorkflow, rt as err, s as StepDisplayStatus, t as PUBLIC_WORKFLOW_SCHEMA, tt as FlowErrorShape, u as toPublicStep, v as buildWorkflow, w as FlowError, x as defineStep, y as defineMapStep, z as StepRegistration } from "./index-BURAYZui.js";
2
2
  import { n as Logger, r as noopLogger, t as LogAttributes } from "./logger-BCus3AOa.js";
3
- import { A as WorkflowStore, C as CreateWorkflowStep, D as ListWorkflowsFilters, E as FinishWorkflowParams, O as ReopenWorkflowParams, S as CreateWorkflowParams, T as FailStepParams, _ as StepGateDecision, a as FlowTracer, b as AddChildStep, c as RecordingTracer, d as noopObserver, f as noopTracer, g as StepGate, h as RateRule, i as FlowSpan, k as TransactionalScope, l as createRecordingObserver, m as InMemoryStepGateConfig, n as FlowEventType, o as RecordedSpan, p as ConcurrencyRule, r as FlowObserver, s as RecordingObserver, t as FlowEvent, u as createRecordingTracer, v as StepGateRequest, w as CreatedWorkflow, x as CompleteStepParams, y as createInMemoryStepGate } from "./observability-B5KgSJfF.js";
3
+ import { A as ListWorkflowsFilters, C as AddChildStep, D as CreatedWorkflow, E as CreateWorkflowStep, M as TransactionalScope, N as WorkflowStore, O as FailStepParams, S as createInMemoryStepGate, T as CreateWorkflowParams, _ as InMemoryStepGateConfig, a as FlowSpan, b as StepGateDecision, c as RecordingObserver, d as createMeterObserver, f as createRecordingObserver, g as ConcurrencyRule, h as noopTracer, i as FlowObserver, j as ReopenWorkflowParams, k as FinishWorkflowParams, l as RecordingTracer, m as noopObserver, n as FlowEventType, o as FlowTracer, p as createRecordingTracer, r as FlowMeter, s as RecordedSpan, t as FlowEvent, u as combineObservers, v as RateRule, w as CompleteStepParams, x as StepGateRequest, y as StepGate } from "./observability-Bu-SXLcS.js";
4
4
  import { i as EnqueueOptions, n as DispatchStepPayload, r as Dispatcher, t as DispatchKind } from "./dispatcher-g7Ct0J6w.js";
5
5
  import { a as WorkflowCompletedArgs, i as BuildStepContextArgs, n as BeforeStartArgs, o as WorkflowHooks, r as BeforeStartResult, t as AfterStepArgs } from "./hooks-BdxIcc92.js";
6
6
  //#region src/core/registry.d.ts
@@ -162,4 +162,4 @@ export declare function createInMemoryWorkflowStore(partitionKey?: string): Work
162
162
  };
163
163
  };
164
164
  //#endregion
165
- export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStore, WorkflowWithSteps, buildWorkflow, computeReadiness, createInMemoryStepGate, createRecordingObserver, createRecordingTracer, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
165
+ export { AddChildStep, AfterStepArgs, BeforeStartArgs, BeforeStartResult, BuildStepContextArgs, CompleteStepParams, ConcurrencyRule, CreateWorkflowParams, CreateWorkflowStep, CreatedWorkflow, DispatchKind, DispatchStepPayload, Dispatcher, EnqueueOptions, FailStepParams, FinishWorkflowParams, FlowError, FlowErrorShape, FlowEvent, FlowEventType, FlowMeter, FlowObserver, FlowSpan, FlowTracer, InMemoryStepGateConfig, InvalidWorkflowDefinitionError, JoinRule, ListWorkflowsFilters, LogAttributes, Logger, PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, PlannedSkip, PublicWorkflow, PublicWorkflowStep, RateRule, ReadinessPlan, RecordedSpan, RecordingObserver, RecordingTracer, ReopenWorkflowParams, Result, RetryPolicy, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, StartOptions, StepCompensateHandler, StepCompensationContext, StepConditionHandler, StepDefinition, StepDeps, StepDisplayStatus, StepError, StepExecutionContext, StepGate, StepGateDecision, StepGateRequest, StepHandler, StepHandlerNotFoundError, StepHandlerRegistry, StepId, StepOutput, StepRecord, StepRegistration, StepStatus, TransactionalScope, TypedStep, TypedStepContext, TypedWorkflow, WORKFLOW_STATUS_SCHEMA, WaitTimeoutPolicy, WorkflowCompletedArgs, WorkflowCreatedResult, WorkflowDefinition, WorkflowHooks, WorkflowId, WorkflowNotFoundError, WorkflowNotRetryableError, WorkflowOutput, WorkflowRecord, WorkflowStatus, WorkflowStore, WorkflowWithSteps, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createMeterObserver, createRecordingObserver, createRecordingTracer, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, isTerminalStepStatus, noopLogger, noopObserver, noopTracer, ok, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
package/dist/index.js CHANGED
@@ -153,6 +153,85 @@ function createRecordingObserver() {
153
153
  }
154
154
  };
155
155
  }
156
+ /** Seconds-scale duration buckets, the OpenTelemetry semantic-convention set. */
157
+ const DURATION_SECONDS_BOUNDARIES = [
158
+ .005,
159
+ .01,
160
+ .025,
161
+ .05,
162
+ .075,
163
+ .1,
164
+ .25,
165
+ .5,
166
+ .75,
167
+ 1,
168
+ 2.5,
169
+ 5,
170
+ 7.5,
171
+ 10,
172
+ 30,
173
+ 60,
174
+ 300
175
+ ];
176
+ /**
177
+ * A `FlowObserver` that turns the event stream into metrics:
178
+ *
179
+ * - `octaflow.workflow.events` (counter) — every workflow transition, by
180
+ * `workflow_type` and `event`.
181
+ * - `octaflow.step.events` (counter) — every step transition, by
182
+ * `workflow_type`, `step_key`, `step_type` and `event`.
183
+ * - `octaflow.step.duration` (histogram, seconds) — one point per
184
+ * `step.completed` / `step.failed`, by `workflow_type`, `step_key` and
185
+ * `outcome`.
186
+ *
187
+ * Attributes are the definitions' names, never ids — a workflow id in a
188
+ * metric attribute is a cardinality leak. Pass it as `observer` to the engine;
189
+ * combine with a history sink through `combineObservers`.
190
+ */
191
+ function createMeterObserver(meter) {
192
+ const workflowEvents = meter.counter("octaflow.workflow.events", {
193
+ description: "Workflow lifecycle transitions.",
194
+ unit: "{event}"
195
+ });
196
+ const stepEvents = meter.counter("octaflow.step.events", {
197
+ description: "Step lifecycle transitions.",
198
+ unit: "{event}"
199
+ });
200
+ const stepDuration = meter.histogram("octaflow.step.duration", {
201
+ description: "Wall-clock duration of one step run.",
202
+ unit: "s",
203
+ boundaries: DURATION_SECONDS_BOUNDARIES
204
+ });
205
+ return { record(event) {
206
+ const workflowType = event.workflowType ?? "";
207
+ if (event.type.startsWith("workflow.")) {
208
+ workflowEvents.add(1, {
209
+ workflow_type: workflowType,
210
+ event: event.type
211
+ });
212
+ return;
213
+ }
214
+ stepEvents.add(1, {
215
+ workflow_type: workflowType,
216
+ step_key: event.stepKey ?? "",
217
+ step_type: event.stepType ?? "",
218
+ event: event.type
219
+ });
220
+ if ((event.type === "step.completed" || event.type === "step.failed") && event.durationMs !== void 0) stepDuration.record(event.durationMs / 1e3, {
221
+ workflow_type: workflowType,
222
+ step_key: event.stepKey ?? "",
223
+ outcome: event.type === "step.completed" ? "completed" : "failed"
224
+ });
225
+ } };
226
+ }
227
+ /** Fan one event out to several observers (history sink + metrics, say). Each is guarded. */
228
+ function combineObservers(...observers) {
229
+ return { record(event) {
230
+ for (const observer of observers) try {
231
+ observer.record(event);
232
+ } catch {}
233
+ } };
234
+ }
156
235
  const noopSpan = {
157
236
  setAttribute() {},
158
237
  recordError() {},
@@ -2154,4 +2233,4 @@ function createInMemoryWorkflowStore(partitionKey = "default") {
2154
2233
  };
2155
2234
  }
2156
2235
  //#endregion
2157
- export { PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, WORKFLOW_STATUS_SCHEMA, buildWorkflow, computeReadiness, createInMemoryStepGate, createInMemoryWorkflowStore, createRecordingObserver, createRecordingTracer, createStepHandlerRegistry, createWorkflowEngine, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, explicitRetryability, isRetryableError, isTerminalStepStatus, markRetryable, nonRetryableError, noopLogger, noopObserver, noopTracer, ok, retryableError, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
2236
+ export { PUBLIC_WORKFLOW_SCHEMA, PUBLIC_WORKFLOW_STEP_SCHEMA, STEP_DISPLAY_STATUS, STEP_DISPLAY_STATUS_SCHEMA, WORKFLOW_STATUS_SCHEMA, buildWorkflow, combineObservers, computeReadiness, createInMemoryStepGate, createInMemoryWorkflowStore, createMeterObserver, createRecordingObserver, createRecordingTracer, createStepHandlerRegistry, createWorkflowEngine, defineMapStep, defineSleepStep, defineStep, defineSubWorkflowStep, defineWaitStep, err, explicitRetryability, isRetryableError, isTerminalStepStatus, markRetryable, nonRetryableError, noopLogger, noopObserver, noopTracer, ok, retryableError, toDisplayStepStatus, toPublicStep, toPublicWorkflow };
@@ -316,6 +316,45 @@ interface RecordingObserver extends FlowObserver {
316
316
  clear(): void;
317
317
  }
318
318
  declare function createRecordingObserver(): RecordingObserver;
319
+ /**
320
+ * The slice of a metrics API the engine's meter observer needs. Structural:
321
+ * `@octabits-io/framework/telemetry`'s meter and an OpenTelemetry `Meter`
322
+ * both fit (the SDK's `createCounter`/`createHistogram` need a two-line
323
+ * adapter, the framework's meter none).
324
+ */
325
+ interface FlowMeter {
326
+ counter(name: string, options?: {
327
+ description?: string;
328
+ unit?: string;
329
+ }): {
330
+ add(value: number, attributes?: Record<string, string | number | boolean>): void;
331
+ };
332
+ histogram(name: string, options?: {
333
+ description?: string;
334
+ unit?: string;
335
+ boundaries?: readonly number[];
336
+ }): {
337
+ record(value: number, attributes?: Record<string, string | number | boolean>): void;
338
+ };
339
+ }
340
+ /**
341
+ * A `FlowObserver` that turns the event stream into metrics:
342
+ *
343
+ * - `octaflow.workflow.events` (counter) — every workflow transition, by
344
+ * `workflow_type` and `event`.
345
+ * - `octaflow.step.events` (counter) — every step transition, by
346
+ * `workflow_type`, `step_key`, `step_type` and `event`.
347
+ * - `octaflow.step.duration` (histogram, seconds) — one point per
348
+ * `step.completed` / `step.failed`, by `workflow_type`, `step_key` and
349
+ * `outcome`.
350
+ *
351
+ * Attributes are the definitions' names, never ids — a workflow id in a
352
+ * metric attribute is a cardinality leak. Pass it as `observer` to the engine;
353
+ * combine with a history sink through `combineObservers`.
354
+ */
355
+ declare function createMeterObserver(meter: FlowMeter): FlowObserver;
356
+ /** Fan one event out to several observers (history sink + metrics, say). Each is guarded. */
357
+ declare function combineObservers(...observers: FlowObserver[]): FlowObserver;
319
358
  /** A minimal span handle. Maps 1:1 onto an OpenTelemetry span in an adapter. */
320
359
  interface FlowSpan {
321
360
  setAttribute(key: string, value: string | number | boolean): void;
@@ -342,4 +381,4 @@ interface RecordingTracer extends FlowTracer {
342
381
  }
343
382
  declare function createRecordingTracer(): RecordingTracer;
344
383
  //#endregion
345
- export { WorkflowStore as A, CreateWorkflowStep as C, ListWorkflowsFilters as D, FinishWorkflowParams as E, ReopenWorkflowParams as O, CreateWorkflowParams as S, FailStepParams as T, StepGateDecision as _, FlowTracer as a, AddChildStep as b, RecordingTracer as c, noopObserver as d, noopTracer as f, StepGate as g, RateRule as h, FlowSpan as i, TransactionalScope as k, createRecordingObserver as l, InMemoryStepGateConfig as m, FlowEventType as n, RecordedSpan as o, ConcurrencyRule as p, FlowObserver as r, RecordingObserver as s, FlowEvent as t, createRecordingTracer as u, StepGateRequest as v, CreatedWorkflow as w, CompleteStepParams as x, createInMemoryStepGate as y };
384
+ export { ListWorkflowsFilters as A, AddChildStep as C, CreatedWorkflow as D, CreateWorkflowStep as E, TransactionalScope as M, WorkflowStore as N, FailStepParams as O, createInMemoryStepGate as S, CreateWorkflowParams as T, InMemoryStepGateConfig as _, FlowSpan as a, StepGateDecision as b, RecordingObserver as c, createMeterObserver as d, createRecordingObserver as f, ConcurrencyRule as g, noopTracer as h, FlowObserver as i, ReopenWorkflowParams as j, FinishWorkflowParams as k, RecordingTracer as l, noopObserver as m, FlowEventType as n, FlowTracer as o, createRecordingTracer as p, FlowMeter as r, RecordedSpan as s, FlowEvent as t, combineObservers as u, RateRule as v, CompleteStepParams as w, StepGateRequest as x, StepGate as y };
@@ -1,5 +1,5 @@
1
1
  import { W as WorkflowId } from "../index-BURAYZui.js";
2
- import { A as WorkflowStore, g as StepGate, h as RateRule, p as ConcurrencyRule, r as FlowObserver, t as FlowEvent } from "../observability-B5KgSJfF.js";
2
+ import { N as WorkflowStore, g as ConcurrencyRule, i as FlowObserver, t as FlowEvent, v as RateRule, y as StepGate } from "../observability-Bu-SXLcS.js";
3
3
  import { Pool } from "pg";
4
4
  //#region src/store-pg/ddl.d.ts
5
5
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "octaflow",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Durable DAG workflow engine: Zod-typed steps over Postgres + pg-boss, with an optional AI add-on (token/cost/quota). Layered single package.",
5
5
  "license": "MIT",
6
6
  "type": "module",