You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
chore!: release prep — package checks, changesets, and reconciliation for the v8 review remediation
- Adopt publint --strict + attw --pack as a per-package check:package
turbo task (contract/testing: esm-only profile; worker: node16 —
subpath-only exports can't resolve under node10; client: full).
- Fix a real release blocker check:package caught: the testing
package's workspace:^ sibling peers broke pnpm pack/publish
(ERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL, the peers are
deliberately not installed to avoid a turbo package-graph cycle) —
they now use concrete ^8.0.0-beta.3 ranges that changesets keeps
bumped.
- One major changeset for all four packages summarizing the v8 review
remediation breaks.
- Docs reconciliation: testing API guide sections (createContractTest,
runActivity, createTimeSkippingTest, createGlobalSetup), example
package descriptions, testing peer requirements in install docs and
README, stale zod-internal claims, dependencies.md testing peers +
no-devDep cycle exception, getHandle TSDoc example narrows with
isOk() (unthrown 5).
- Register second.workflows.ts as a knip entry (loaded by path string).
- Mark both v8 specs implemented; D4 scope cuts recorded as still
deferred.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`pino` appears in the catalog and is used by `examples/` only — it's not imported from any published package's `src/`.
18
18
@@ -38,15 +38,17 @@ All dependency versions are centralized in `pnpm-workspace.yaml` under the `cata
38
38
39
39
Anything that appears in a published package's **public type signatures** must be a peer dep, not a regular dep — otherwise downstream consumers can end up with two disjoint nominal types in their typechecker (theirs and ours), even though the runtime classes are compatible.
| contract |`unthrown ^5` (optional — only needed when using `result-async`) |
46
-
| testing |`vitest ^4` (the `globalSetup` hook integrates with vitest's test runner), `@temporalio/client ^1`, `@temporalio/testing ^1`, `@temporalio/worker ^1` (all exposed by the fixtures' public types — e.g. `TestWorkflowEnvironment` in `time-skipping`) |
| contract |`unthrown ^5` (optional — only needed when using `result-async`) |
46
+
| testing |`@temporal-contract/client`, `@temporal-contract/contract`, `@temporal-contract/worker` (concrete `^8.x` ranges — see below), `unthrown ^5` (the contract-aware fixtures expose `TypedClient`/`ContractClient`/`ActivitiesHandler`/`AsyncResult` in their public types), `vitest ^4` (the `globalSetup` hook integrates with vitest's test runner), `@temporalio/client ^1`, `@temporalio/testing ^1`, `@temporalio/worker ^1` (all exposed by the fixtures' public types — e.g. `TestWorkflowEnvironment` in `time-skipping`) |
47
47
48
48
When you add a peer dep, also add it to `devDependencies` (with the same `"catalog:"` reference) so the local workspace build still resolves it. The workspace has `autoInstallPeers: false`, so peers must be present somewhere on the install side.
49
49
50
+
**Deliberate exception:** the testing package's `@temporal-contract/*` sibling peers have NO matching devDeps. client and worker devDepend on testing for their integration fixtures, so adding the siblings to testing's devDeps would put a cycle in the package graph, which turbo 2 rejects. Local resolution goes through tsconfig `paths` mapped to the siblings' sources plus vitest aliases instead — see the comments in `packages/testing/tsconfig.json` and `packages/testing/vitest.config.ts`. For the same reason these peers use **concrete `^8.x` semver ranges, not `workspace:^`**: pnpm can only rewrite the `workspace:` protocol at pack/publish time for deps that are actually installed, so `workspace:^` here breaks `pnpm pack`/`pnpm publish` (`ERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL`). Changesets keeps the ranges bumped (`updateInternalDependencies: "patch"`, and the four packages are a fixed group).
51
+
50
52
## Security `overrides` (`pnpm-workspace.yaml`)
51
53
52
54
`pnpm-workspace.yaml` pins minimum versions for transitive dependencies via its `overrides:` block to close known CVEs (currently `fast-uri`, `protobufjs`, and `testcontainers>undici`). When a security audit flags a new vulnerability, add the pin there (with a comment citing the GHSA and the reachability reasoning) rather than waiting for upstream to update. Advisories that are unreachable in this repo are suppressed via `auditConfig.ignoreGhsas`, each with a documented justification.
v8 review remediation — the full-surface overhaul from the six-track 8.0 review. Headline breaks, per package:
9
+
10
+
**All boundaries (client + worker):** payloads are now parsed exactly once, on the receiving side. The sender still validates (surfacing a typed `Err`/`ValidationError` early) but transmits the caller's original value, so transforming schemas (`z.coerce.*`, `.transform(...)`) apply once end-to-end instead of twice.
11
+
12
+
**`@temporal-contract/contract`:**
13
+
14
+
-`defineContract`'s structural validation is hand-rolled and strict (unknown keys rejected); the zod runtime dependency is gone.
15
+
-`input` is optional on signal/query/update definitions — `defineSignal()` / `defineQuery({ output })` with no input means the handler receives `undefined`, no `z.void()` ceremony.
16
+
- ESM-only build (the CJS artifacts and `main`/`module`/`require` conditions are removed).
17
+
-`InferContractWorkflows` is deleted (trivial alias).
18
+
19
+
**`@temporal-contract/client`:**
20
+
21
+
-`TypedClient` is split from the contract: `TypedClient.create({ client })` is connection-scoped, and `typedClient.for(contract)` returns the contract-bound `ContractClient` with the workflow/schedule methods. A `readonly raw` escape hatch exposes the underlying `Client`.
22
+
-`getHandle` is synchronous, returns `Result<TypedWorkflowHandle, WorkflowNotInContractError>`, and accepts `runId`/`firstExecutionRunId` options; handles carry `runId`/`firstExecutionRunId`; typed `startUpdate` joins `executeUpdate`.
23
+
-`WorkflowNotFoundError` is renamed `WorkflowNotInContractError`.
24
+
- Schedule surface parity: typed `ScheduleAlreadyExistsError`/`ScheduleNotFoundError` on the error channel (instead of defects), plus `update`, `backfill`, and `list`.
25
+
- The six unused `ClientInfer*` type exports are deleted.
26
+
27
+
**`@temporal-contract/worker`:**
28
+
29
+
- Invalid signal payloads are dropped and logged (`log.warn`), never thrown — `SignalInputValidationError` is deleted; a stale client can no longer terminally kill a workflow execution.
30
+
- Contract misuse inside workflow code (unknown signal/query/update or workflow name, async schema, uncovered activity options) now fails fast as a non-retryable `ContractMisuseError``ApplicationFailure` instead of hanging executions in Workflow Task retries.
31
+
- Exported `qualify` is renamed `qualifyFailure` (no alias).
32
+
-`activities` is optional on `createWorker`, and activity-less workflows no longer need `{}` entries in the implementations map.
33
+
-`TypedChildWorkflowHandle` gains a typed `signals` map and `firstExecutionRunId`.
34
+
35
+
**`@temporal-contract/testing`:**
36
+
37
+
- New contract-aware fixtures: `createContractTest(contract, { workflowsPath, activities?, workerOptions? })` yields `{ client, typedClient, worker }` against the testcontainers server, and `runActivity(definition, implementation, input, { env? })` runs one implementation inside `MockActivityEnvironment` (vitest-free).
|[`order-processing-contract`](https://github.com/btravstack/temporal-contract/tree/main/examples/order-processing-contract)|Schemas and the contract. Depended on by the other two |
|[`order-processing-client`](https://github.com/btravstack/temporal-contract/tree/main/examples/order-processing-client)|Starts workflows and handles results|
|[`order-processing-contract`](https://github.com/btravstack/temporal-contract/tree/main/examples/order-processing-contract)|The shared contract, built composition-first with the `define*` helpers: signals (payload-carrying and payload-less), an argument-less query, a `PaymentDeclined` typed error shared by activity and workflow, and a schedule-ready, activity-less cleanup workflow. Depended on by the other two |
17
+
|[`order-processing-worker`](https://github.com/btravstack/temporal-contract/tree/main/examples/order-processing-worker)|Clean-architecture worker: `AsyncResult` activities with `qualifyFailure` and typed error constructors, a `condition`-based approval gate with signal/query handlers, an activity-less schedule-driven workflow, and integration tests|
18
+
|[`order-processing-client`](https://github.com/btravstack/temporal-contract/tree/main/examples/order-processing-client)|`TypedClient.create({ client }).for(contract)` in action: typed signals/queries through handles, the synchronous `getHandle`, exhaustive `match` + `P.tag` including the rehydrated `PaymentDeclined` contract error, and `schedule.create` with the create-if-absent idiom |
0 commit comments