Releases: btravers/temporal-contract
@temporal-contract/[email protected]
Minor Changes
-
58fb9cd: Close part of the API gap with
@swan-io/boxed, document the rest.Closes #186.
New
Resultmethodsresult.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 offlatMap. 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/boxedsemantics.New docs page
docs/guide/boxed-vs-swan.mdenumerates the fullResultandFuturesurface for both libraries side-by-side, calls out each gap with its reason (determinism, soundness regression, not-yet-ported), establishesmatch/isOk/isErroras the canonical discriminants (withtagdocumented as the power-user escape hatch), and includes a migration cheat sheet. The package README links it; the existingresult-pattern.md"Both packages provide the same API" claim has been corrected.Still intentionally absent
Result#getWithDefault— duplicate ofgetOr; removed in 0.x.Result#toOption,okToOption,errorToOption,Optiontype —Optionwas removed when nothing in the codebase consumed it. Useresult.match({ Ok: (v) => v, Error: () => undefined }).Result.fromExecution<T, E>(fn)typed-error overload — was unsound (error as Ecast without runtime guard). The un-narrowedResult<T, unknown>form is preserved; narrow at the call site via.mapError.Future.concurrentandFuture.mapOkToResult— useful but not blocking; ports welcome.
-
d70f25e:
declareWorkflowaccepts a new optionalactivityOptionsByNamefield for
per-activityActivityOptionsoverrides.Closes #122.
Today,
activityOptionsapplies to every activity reachable from the
workflow.activityOptionsByNamelets you override timeouts, retry policy,
or any other TemporalActivityOptionsfield 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 nestedretryblock —
this matches Temporal's "oneActivityOptionsperproxyActivitiescall"
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
activityOptionsare
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
getWithDefaultfromResult. It was a literal duplicate ofgetOr. Migrate by usinggetOr(...)everywhere. - Removed the half-implemented
Optiontype and theResult#toOption()method. They had no constructors, no methods, and no consumer in the codebase. If you need optionality, useT | undefined. Result.fromExecutionandResult.fromAsyncExecutionnow returnResult<T, unknown>(the secondEgeneric is gone). The previous signature accepted anEgeneric but casterror as Ewithout 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, andgetWorkflowNameshelpers from@temporal-contract/worker/activity. They had no internal usage, no example usage, andisWorkflowActivitywas 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].activitiesalone only contains workflow-local activities; you must mergecontract.activitiesto 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/updatesare unchanged. RuntimeClientErroris now exported. Match against it withinstanceof RuntimeClientErroror ts-pattern'sP.instanceOf(RuntimeClientError).
Additions
Future.fromPromise(@temporal-contract/boxed) accepts an optionalmapErrorargument that lifts the error type at the boundary instead of stripping tounknown. 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.fromPromiseoverload coverage, swan-boxed round-trip preservation, deterministic-replay assertions forFuturechains, negative type-level assertion for the worker/clientInferInput/InferOutputduality.
Internal
- Hoisted the
args.length === 1 ? args[0] : argsheuristic into a singleextractHandlerInputhelper used across activity, workflow, signal, query, and update handlers. - Dropped runtime defensive checks in
defineSignal/defineQuery/defineUpdatethat 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/finallyJSDoc clarifies they return rawPromises and break Future chainability.
- Removed
-
5948e4e: Add
TypedClient#signalWithStartfor 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 againstcontract.workflows[name].signals[signalName].input. Returns aTypedWorkflowHandleWithSignaledRunId— the standard typed handle plus asignaledRunIdfield 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
declareWorkflowruns on incoming inputs. On validation failure, throwsWorkflowInputValidationError, 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
ContinueAsNewOptionsminusworkflowType/taskQueue(those come from the contract). The user options are spread last so power users can override fields likeworkflowRunTimeout,memo, orretry.Returns
Promise<never>— Temporal'scontinueAsNewthrows 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",...
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
Patch Changes
- Updated dependencies
- @temporal-contract/[email protected]
- @temporal-contract/[email protected]
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
Patch Changes
- Updated dependencies
- @temporal-contract/[email protected]
- @temporal-contract/[email protected]
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
Patch Changes
- Updated dependencies
- @temporal-contract/[email protected]
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
Patch Changes
- Updated dependencies
- @temporal-contract/[email protected]
- @temporal-contract/[email protected]
@temporal-contract/[email protected]
Minor Changes
- Align project structure with amqp-contract and address code quality issues across packages.
@temporal-contract/[email protected]
Minor Changes
-
Breaking Changes
- Removed unimplemented Nexus types from public API (
defineNexusOperation,defineNexusService, and related types). These were proof-of-concept exports that were not yet functional. The planned Nexus API design is documented at https://btravers.github.io/temporal-contract/guide/nexus-integration
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
- Removed unimplemented Nexus types from public API (
Patch Changes
- Updated dependencies
- @temporal-contract/[email protected]
- @temporal-contract/[email protected]