Skip to content

Releases: btravers/temporal-contract

@temporal-contract/[email protected]

04 May 00:15

Choose a tag to compare

Minor Changes

  • 58fb9cd: Close part of the API gap with @swan-io/boxed, document the rest.

    Closes #186.

    New Result methods

    • result.tap(fn) — run a side effect with the Ok value, return the Result unchanged. No-op on Err.
    • result.tapError(fn) — run a side effect with the Err value, return the Result unchanged. No-op on Ok.
    • result.flatMapError(fn) — Err-path equivalent of flatMap. Useful for recovery and error-type transformations.
    • Result.allFromDict({...}) — combine a record of Results into a Result of a record. First Err wins.

    All four match the corresponding @swan-io/boxed semantics.

    New docs page

    docs/guide/boxed-vs-swan.md enumerates the full Result and Future surface for both libraries side-by-side, calls out each gap with its reason (determinism, soundness regression, not-yet-ported), establishes match / isOk / isError as the canonical discriminants (with tag documented as the power-user escape hatch), and includes a migration cheat sheet. The package README links it; the existing result-pattern.md "Both packages provide the same API" claim has been corrected.

    Still intentionally absent

    • Result#getWithDefault — duplicate of getOr; removed in 0.x.
    • Result#toOption, okToOption, errorToOption, Option type — Option was removed when nothing in the codebase consumed it. Use result.match({ Ok: (v) => v, Error: () => undefined }).
    • Result.fromExecution<T, E>(fn) typed-error overload — was unsound (error as E cast without runtime guard). The un-narrowed Result<T, unknown> form is preserved; narrow at the call site via .mapError.
    • Future.concurrent and Future.mapOkToResult — useful but not blocking; ports welcome.
  • d70f25e: declareWorkflow accepts a new optional activityOptionsByName field for
    per-activity ActivityOptions overrides.

    Closes #122.

    Today, activityOptions applies to every activity reachable from the
    workflow. activityOptionsByName lets you override timeouts, retry policy,
    or any other Temporal ActivityOptions field for individual activities:

    declareWorkflow({
      workflowName: "processOrder",
      contract,
      activityOptions: {
        startToCloseTimeout: "1 minute", // default for all activities
      },
      activityOptionsByName: {
        // Payment gateway is slow — give it room and retry aggressively.
        chargePayment: {
          startToCloseTimeout: "5 minutes",
          retry: { maximumAttempts: 5 },
        },
        // Cheap CPU-bound check — fail fast if it stalls.
        validateOrder: { startToCloseTimeout: "5 seconds" },
      },
      implementation: ...,
    });

    Each entry shallow-merges over the workflow default. The override wins on
    every property it specifies, including the entire nested retry block —
    this matches Temporal's "one ActivityOptions per proxyActivities call"
    semantics, where each scheduled activity carries one full options bag.

    Activity names are typed against the contract (workflow-local + global), so
    typos surface at compile time rather than running silently with the default
    options.

    Non-breaking: existing workflows that only use activityOptions are
    unchanged.

  • ad1e1da: Round-2 review-driven cleanup. Several small breaking removals, a typed-error overload on Future.fromPromise, and a deduplication of the client's typed-handle proxies.

    Breaking changes (@temporal-contract/boxed)

    • Removed getWithDefault from Result. It was a literal duplicate of getOr. Migrate by using getOr(...) everywhere.
    • Removed the half-implemented Option type and the Result#toOption() method. They had no constructors, no methods, and no consumer in the codebase. If you need optionality, use T | undefined.
    • Result.fromExecution and Result.fromAsyncExecution now return Result<T, unknown> (the second E generic is gone). The previous signature accepted an E generic but cast error as E without any runtime guard, which was unsound. Narrow at the call site: Result.fromExecution(...).mapError((e) => mapToYourError(e)).

    Breaking changes (@temporal-contract/worker)

    • Removed the getWorkflowActivities, getWorkflowActivityNames, isWorkflowActivity, and getWorkflowNames helpers from @temporal-contract/worker/activity. They had no internal usage, no example usage, and isWorkflowActivity was misnamed (returned true for global activities). If you depended on them, derive equivalents directly from the contract — but remember the merge with global activities:

      // Before:
      const activities = getWorkflowActivities(contract, "processOrder");
      const names = getWorkflowActivityNames(contract, "processOrder");
      const isAvailable = isWorkflowActivity(contract, "processOrder", "send");
      const workflows = getWorkflowNames(contract);
      
      // After:
      const activities = {
        ...(contract.activities ?? {}),
        ...(contract.workflows.processOrder.activities ?? {}),
      };
      const names = Object.keys(activities);
      const isAvailable = "send" in activities;
      const workflows = Object.keys(contract.workflows);

      contract.workflows[name].activities alone only contains workflow-local activities; you must merge contract.activities to match the old helper's behavior.

    Breaking changes (@temporal-contract/client)

    • The internal proxy generation was deduplicated. The shape and types of TypedWorkflowHandle.queries/signals/updates are unchanged.
    • RuntimeClientError is now exported. Match against it with instanceof RuntimeClientError or ts-pattern's P.instanceOf(RuntimeClientError).

    Additions

    • Future.fromPromise (@temporal-contract/boxed) accepts an optional mapError argument that lifts the error type at the boundary instead of stripping to unknown. Existing call sites without the second argument are unchanged.
    • defineQuery's JSDoc now calls out the synchronous-validator constraint (Temporal queries must complete synchronously, so async refinements aren't supported).
    • New tests: typed-error Future.fromPromise overload coverage, swan-boxed round-trip preservation, deterministic-replay assertions for Future chains, negative type-level assertion for the worker/client InferInput/InferOutput duality.

    Internal

    • Hoisted the args.length === 1 ? args[0] : args heuristic into a single extractHandlerInput helper used across activity, workflow, signal, query, and update handlers.
    • Dropped runtime defensive checks in defineSignal/defineQuery/defineUpdate that the type system already prevents.
    • Activity and workflow entry points now carry a top-of-file comment explaining the swan-vs-local Result/Future split.
    • Future.then / catch / finally JSDoc clarifies they return raw Promises and break Future chainability.
  • 5948e4e: Add TypedClient#signalWithStart for the actor-style "send a signal, start the workflow if it doesn't exist" pattern.

    Closes #178.

    Both halves of the call are typed against the contract: workflow input validates against contract.workflows[name].input, signal input validates against contract.workflows[name].signals[signalName].input. Returns a TypedWorkflowHandleWithSignaledRunId — the standard typed handle plus a signaledRunId field for correlating the signal with the (possibly pre-existing) workflow execution chain.

    const result = await client.signalWithStart("processOrder", {
      workflowId: "order-123",
      args: { orderId: "ORD-123", customerId: "CUST-1" },     // typed against workflow input
      signalName: "cancel",                                     // restricted to declared signals
      signalArgs: { reason: "duplicate" },                      // typed against signal input
    });
    
    result.match({
      Ok: (handle) => console.log("signaled run", handle.signaledRunId),
      Error: (error) => /* WorkflowNotFoundError | WorkflowValidationError | SignalValidationError | RuntimeClientError */,
    });
  • 80c822b: Add typed context.continueAsNew(...) to the workflow context.

    Closes #179.

    Two overloads:

    // Same workflow — args validated against this workflow's input schema
    return context.continueAsNew({ ...args, retryCount: args.retryCount + 1 });
    
    // Cross-contract — workflowType and taskQueue come from the destination
    // contract automatically; args validated against the destination's input
    return context.continueAsNew(otherContract, "otherWorkflow", { ...newArgs });

    Both validate args via the same Standard Schema check declareWorkflow runs on incoming inputs. On validation failure, throws WorkflowInputValidationError, which surfaces back to Temporal as a controlled workflow failure rather than silently proceeding with an invalid run.

    Both forms also accept a third optional argument matching Temporal's ContinueAsNewOptions minus workflowType / taskQueue (those come from the contract). The user options are spread last so power users can override fields like workflowRunTimeout, memo, or retry.

    Returns Promise<never> — Temporal's continueAsNew throws an internal exception that the runtime intercepts to terminate the current execution and start a new one.

  • 26ab350: Add typed Schedules to TypedClient (Temporal 1.16+).

    Closes #181.

    const result = await client.schedule.create("processOrder", {
      scheduleId: "daily-sweep",
      spec: { cronExpressions: ["0 2 * * *"] },
      args: { orderId: "sweep" },        // typed against the workflow's input
      policies: { overlap: "SKIP" },
      workflowExecutionTimeout: "1 hour",...
Read more

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

Patch Changes

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

Patch Changes

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

Patch Changes

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

Patch Changes

@temporal-contract/[email protected]

17 Feb 16:49
65ad558

Choose a tag to compare

Minor Changes

  • Align project structure with amqp-contract and address code quality issues across packages.

@temporal-contract/[email protected]

22 Jan 01:19
5120f89

Choose a tag to compare

Minor Changes

  • Breaking Changes

    Improvements

    Documentation

    • Enhanced documentation site with comprehensive SEO (meta tags, JSON-LD structured data, sitemap, canonical URLs)
    • Added "Why temporal-contract?" guide explaining the value proposition
    • Added "Troubleshooting" guide with common issues and solutions
    • Simplified homepage with cleaner feature presentation and quick example
    • Reorganized sidebar navigation to match industry patterns

    Package Fixes

    • @temporal-contract/worker-nestjs: Updated peer dependencies from NestJS ^10 to ^11 for consistency with client-nestjs
    • @temporal-contract/worker-nestjs: Changed hardcoded dependency versions to use pnpm catalog references

Patch Changes