BUILD / FRAMEWORK INTEGRATIONS

Your framework, unchanged.

Use a small application-level adapter to bring engine snapshots into your existing reactive model.

A headless integration

React, Vue, and Angular dependencies belong to your application, never to the core package. The examples below adapt the same public IEngine interface. They are patterns to copy and extend—not exports from @newvexa/workify.

Start runs on explicit actions, not during rendering, on module import, or automatically inside a lifecycle effect. Keep an engine stable for its view or consumer.

Framework adapters

useWorkflow.tsTypeScript
import { useCallback, useEffect, useSyncExternalStore } from "react";
import type { IEngine } from "@newvexa/workify";

export function useWorkflow(engine: IEngine) {
  const subscribe = useCallback(
    (listener: () => void) => engine.subscribe(listener), [engine],
  );
  const getSnapshot = useCallback(() => engine.getSnapshot(), [engine]);

  useEffect(() => {
    const stop = engine.onEvent((event) => {
      if (event.type === "run.finished") console.log(event.status);
    });
    return stop;
  }, [engine]);

  return useSyncExternalStore(subscribe, getSnapshot);
}

Use the engine as an external store. Subscribe to snapshots with a custom hook and clean up execution event listeners with useEffect. Integration guide

In Vue, call the composable inside setup() or an active effect scope. Read state.value.status in script; templates unwrap the ref. onScopeDispose removes the state observer.

React lifecycle & events

The React hook uses useSyncExternalStore for immutable snapshots and useEffect to subscribe to detailed execution events. Both APIs are cleaned up when the consumer changes or unmounts. Keep the instance stable:

WorkflowView.tsxTypeScript
import { useState } from "react";
import { createWorkflowEngine } from "./workflow";
import { useWorkflow } from "./useWorkflow";

// Inside your component:
const [engine] = useState(() => createWorkflowEngine());
const snapshot = useWorkflow(engine);
// Start explicitly from a button handler:
// onClick={() => void engine.run(21)}

Angular dependency injection

Provide both the service and engine at the component/view level. Import the quick-start factory and the token/service from your application files, then use the provider array in the component’s providers metadata.

workflow.providers.tsTypeScript
const workflowProviders = [
  WorkflowService,
  { provide: WORKFLOW_ENGINE, useFactory: createWorkflowEngine },
];
// Use workflowProviders in your component's providers.

The service bridges subscriptions to a signal. DestroyRef removes the observer on view destruction; it does not automatically stop an owned run.

Lifecycle & cancellation

Unsubscribing and cancelling are different operations. On cleanup, remove listeners and abort any run the view owns. Cooperative cancellation still waits for started handlers to settle.

cancel.tsTypeScript
const controller = new AbortController();
const pending = engine.run(21, { signal: controller.signal });
controller.abort();
const result = await pending;
console.log(result.status); // "cancelled"

For complete run/cancel/error handling, read the shipped framework examples:

Documentation for @newvexa/workify 0.1.3Read the full source guide