@sanity/workflow-cli 0.32.0 → 0.33.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/CHANGELOG.md +145 -0
- package/README.md +243 -41
- package/dist/commands/workflows/blueprint/generate.d.ts +9 -0
- package/dist/commands/workflows/blueprint/generate.js +28 -0
- package/dist/commands/workflows/diagnose.d.ts +4 -2
- package/dist/commands/workflows/diagnose.js +11 -82
- package/dist/commands/workflows/show.d.ts +3 -5
- package/dist/commands/workflows/show.js +28 -1
- package/dist/hooks/prerun/telemetry.d.ts +6 -0
- package/dist/hooks/prerun/telemetry.js +14 -7
- package/dist/lib/blueprint-emit.d.ts +30 -0
- package/dist/lib/blueprint-emit.js +147 -0
- package/dist/lib/blueprint-needs.d.ts +2 -0
- package/dist/lib/blueprint-needs.js +109 -0
- package/dist/lib/cause-detail.d.ts +9 -0
- package/dist/lib/cause-detail.js +111 -0
- package/dist/lib/load-config.d.ts +20 -0
- package/dist/lib/load-config.js +16 -13
- package/dist/lib/share-definitions.d.ts +2 -2
- package/dist/lib/share-definitions.js +2 -2
- package/dist/lib/telemetry-setup.d.ts +2 -0
- package/dist/lib/telemetry-setup.js +2 -2
- package/dist/lib/telemetry.d.ts +5 -2
- package/dist/lib/telemetry.js +13 -4
- package/dist/standalone-argv.d.ts +2 -2
- package/dist/standalone-argv.js +1 -0
- package/oclif.manifest.json +33 -1
- package/package.json +15 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,150 @@
|
|
|
1
1
|
# @sanity/workflow-cli
|
|
2
2
|
|
|
3
|
+
## 0.33.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 225e0fb: **BREAKING:** Workflows App SDK and Studio integrations now require `@sanity/sdk` 3.1 or later in the 3.x line, and `@sanity/workflow-sdk` requires the matching `@sanity/sdk-react` 3.1 line when its React entry is used. The previous SDK 2 peer contract is no longer supported. The exported `WorkflowClient`, `TelemetryIntakeClient`, `ProjectUserProfileClient`, and `StudioUserClient` request contracts now pass their target in `url`; the previous `uri` request target is no longer used. The engine's effective client config also accepts the broader `{type: string, id: string}` resource descriptors returned by Sanity client 8.
|
|
8
|
+
|
|
9
|
+
Before upgrading Workflows, upgrade `@sanity/sdk` and `@sanity/sdk-react` together to 3.1 or later. Applications that stay on SDK 2 must stay on an earlier Workflows release. If you implement any of the request contracts named above, update it to read the request target from `url` instead of `uri`; otherwise its request-backed reads will fail. Existing `WorkflowClient.config()` implementations need no change when their resource descriptor already has string `type` and `id` fields. No separate CLI upgrade action is required; its definition-sharing and telemetry requests adopt `url` internally.
|
|
10
|
+
|
|
11
|
+
Before installing, override SDK 3's `@sanity/mutate` dependency to `0.18.2` in your application's root package-manager configuration, reinstall, and commit the updated lockfile. SDK 3.1.0 allows Mutate 0.18.1, which can leave document reads pending with client 8. For npm, set `overrides["@sanity/sdk"]["@sanity/mutate"]` to `"0.18.2"` in `package.json`. For pnpm, set `overrides['@sanity/sdk@3>@sanity/mutate']` to `0.18.2` in `pnpm-workspace.yaml`. Keep the override until your SDK release requires Mutate 0.18.2 or later. Complete examples and verification steps are in the Installation section of the published `@sanity/workflow-sdk` README.
|
|
12
|
+
|
|
13
|
+
Malformed project-member responses now report an inaccessible directory and can recover on a later lookup, instead of being cached as a missing user. No additional upgrade action is required for this correction.
|
|
14
|
+
|
|
15
|
+
**Docs impact:** Update the App SDK and Studio integration installation guidance, package compatibility references, and examples to require Sanity App SDK 3.1 and the consumer Mutate override described in the published `@sanity/workflow-sdk` README; update the client API references for `request({url})`, effective resource descriptors, and directory-response failures.
|
|
16
|
+
|
|
17
|
+
- ba031fe: **Experimental, and not ready for production use in this release.** Both commands are available so you can try the generated runtime and give feedback; their flags and their output may still change. What you would hit today:
|
|
18
|
+
- The start watcher runs on every create and update of a subject type, not only on writes that can actually start a workflow, so it wakes for documents that already have an instance or match no definition. The heartbeat is scheduled every minute and evaluates every in-flight instance in the tag when it runs; how often the platform runs it is a plan limit.
|
|
19
|
+
- `sanity-workflows deploy` does not check the generated tree, so drift between your definitions and the tree on disk is caught only by `blueprint generate --check` in CI.
|
|
20
|
+
- Definitions and functions deploy in two steps, definitions first, until the Blueprints service registers the `sanity.workflow` resource.
|
|
21
|
+
- A generated file cannot be taken over: editing one is drift, and the next generation rewrites it.
|
|
22
|
+
- A retry policy is not checked against its function's timeout at generation, so an effect can be configured to retry for longer than its function may run.
|
|
23
|
+
|
|
24
|
+
All of these are tracked, and the next release is the bar for using this in production.
|
|
25
|
+
|
|
26
|
+
`sanity-workflows blueprint generate` generates the Sanity Blueprints runtime your definitions require and writes it next to `sanity.workflow.ts`. It prints the needs analysis first — which effects force a drain function, which `$now` condition forces a scheduled heartbeat, which autonomous definition forces a start watcher, and the explicit "no functions needed" answer for a purely interactive workflow — then writes `workflows.blueprint.ts`, one function module and manifest per derived need, the effect-handler registry, and one handler stub per declared effect name. Handler stubs are yours: they are scaffolded once and never overwritten. Your own `sanity.blueprint.ts` receives the `workflowResources` import and spread once. The command deletes nothing; a handler for an effect you removed, a scaffold blocked by a path differing only in case, and a blueprint it could not wire are reported as warnings for you to resolve. The generated tree is one artifact for the whole config, so the command always covers every deployment.
|
|
27
|
+
|
|
28
|
+
`sanity-workflows blueprint generate --check` is the CI drift gate. It renders the same runtime in memory, compares it with the files on disk, writes nothing, and exits zero when they match. Otherwise it exits 1 and lists every file with its reason — `missing`, `differs`, an orphaned handler, or a blueprint that is not wired — so a CI log says what to fix without anyone reading source. A declared effect with no handler file on disk reports as `missing`, which is the handler-coverage check.
|
|
29
|
+
|
|
30
|
+
**No upgrade action required.** Both commands are new and nothing else changes. To use them, your `sanity.workflow.ts` must export each deployment under its own `name`, and that export must be the deployment object itself — `export const production = {name: 'production', …} satisfies WorkflowDeploymentInput`, then `export default defineWorkflowConfig({deployments: [production]})` — because every generated module imports its deployment from `./sanity.workflow` by that name; a config that only default-exports fails the command with the export to add rather than writing a tree that cannot compile. The command checks the name only, so make sure each export is the deployment the config lists: a typecheck of the generated modules catches a wrong export only where a generated function uses it, and a deployment that needs no functions emits none. Your `sanity.workflow.ts` must also be importable inside a deployed function, because every generated module imports it and each function bundles it: reading an absent variable there yields `undefined`, but a config that throws when a variable is missing, or requires one to build the deployment, fails every deployed function on init. Keep the deployment's resource coordinates literal in that file and read secrets inside your handlers. Deploy your definitions with `sanity-workflows deploy` before `npx sanity blueprints deploy`, because the generated blueprint carries the definitions entry commented out until the Blueprints service accepts the `sanity.workflow` resource type, and a function that starts or advances an instance resolves its definition from the Content Lake. A generated heartbeat is a scheduled function, which the platform only accepts on an organization-scoped stack, and how often it may run is a plan limit. Also install `@sanity/blueprints`, `@sanity/functions`, and `@sanity/workflow-blueprint` in your project: each generated function's manifest copies the specifications your project declares for `@sanity/functions`, `@sanity/workflow-blueprint`, and `@sanity/workflow-engine`, and a package you declare nowhere fails the command instead of being stamped with a guessed range. `typescript` is now a dependency of the CLI, because parsing `sanity.blueprint.ts` to insert the wiring uses the TypeScript compiler.
|
|
31
|
+
|
|
32
|
+
**Docs impact:** Rewrite the cookbook chapter on running workflows on a server so it drives the runtime through these two commands rather than describing hand-written functions, and cover the CI drift check there. Update the CLI command reference with both commands, and the Blueprints concept page where it explains who writes the generated tree.
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- 393ac71: CLI and MCP telemetry now includes `context.surface` (`cli` or `mcp`) and
|
|
37
|
+
execution mode in each event's `context.environment`. Dashboards can include
|
|
38
|
+
shell and engine activity while distinguishing it from SDK events marked
|
|
39
|
+
`sdk`. Existing command trace context is preserved.
|
|
40
|
+
|
|
41
|
+
All three surfaces report `production` for `NODE_ENV=production`, and
|
|
42
|
+
`development` for `development` or `test`. Unset, empty, and unrecognized values
|
|
43
|
+
default to `production` for CLI and MCP execution mode, and `development` for
|
|
44
|
+
SDK build mode. SDK environment classification is unchanged. When comparing
|
|
45
|
+
activity across surfaces, group or filter by surface alongside environment.
|
|
46
|
+
Environment does not identify a production dataset or deployment; API host,
|
|
47
|
+
dataset name, and workflow tag do not set it.
|
|
48
|
+
|
|
49
|
+
**No upgrade action required.** Existing telemetry consent and opt-out
|
|
50
|
+
settings still apply. Historical events are unchanged.
|
|
51
|
+
|
|
52
|
+
**Docs impact:** Update CLI, MCP, and SDK telemetry guidance to explain surface
|
|
53
|
+
context, build versus execution mode, the explicit defaults, and how to
|
|
54
|
+
interpret environment when comparing activity across surfaces.
|
|
55
|
+
|
|
56
|
+
- 8874c50: **BREAKING:** A `subject`, `doc.ref`, or `doc.refs` field marked `required: true`
|
|
57
|
+
now requires its selected documents to remain readable after initialization.
|
|
58
|
+
Previously, the flag checked only that an initial value was supplied. Missing
|
|
59
|
+
required targets now produce a fault and prevent normal actions, triggered
|
|
60
|
+
actions, and transitions from advancing, even without an activity requirement
|
|
61
|
+
reading those targets. Abort and direct edits to editable fields remain
|
|
62
|
+
available. Optional references fault only when an unmet runtime condition needs
|
|
63
|
+
their content; completed workflows remain completed.
|
|
64
|
+
|
|
65
|
+
`StuckCause` adds `document-missing`. Update exhaustive handlers before upgrading,
|
|
66
|
+
or their typechecks fail and renderers have no matching branch. Evaluation
|
|
67
|
+
identifies the affected field and reference in `missingDocuments`, with its title
|
|
68
|
+
and completed availability evidence; `blockingMissingDocuments` selects the
|
|
69
|
+
references that prevent progress. The engine checks draft, published, and release
|
|
70
|
+
representations before distinguishing deletion from inaccessible content or
|
|
71
|
+
content outside the workflow perspective. Studio, CLI, and MCP use that same
|
|
72
|
+
result. Failed availability checks report unreadable content and log the cause;
|
|
73
|
+
optional references still permit unrelated actions. Incomplete reads do not
|
|
74
|
+
claim deletion, and an existing task fault keeps
|
|
75
|
+
its recovery controls. The fault clears when the required content becomes
|
|
76
|
+
readable; this release adds no undo-delete operation and does not automatically
|
|
77
|
+
abort workflows.
|
|
78
|
+
|
|
79
|
+
Upgrade every Workflows runtime sharing affected data, including Studio, CLI,
|
|
80
|
+
MCP servers, and deployed Functions, before relying on continued required-target
|
|
81
|
+
availability. Then set the deployment's reviewed `expectedMinReaderModel` to
|
|
82
|
+
`10` before deploying definitions with required content references. Writers stamp
|
|
83
|
+
model 10; this feature requires reader model 10, while documents without it keep
|
|
84
|
+
the floor required by their other features, normally 4, 8, or 9. Existing
|
|
85
|
+
instances remain readable without a backfill and adopt the rule under upgraded
|
|
86
|
+
engines. Their stored floor rises on the next full write, so an older runtime
|
|
87
|
+
can still advance an unstamped existing instance until the fleet is upgraded.
|
|
88
|
+
See `packages/workflow-engine/DATAMODEL.md` for the complete rollout contract.
|
|
89
|
+
|
|
90
|
+
The rendered CLI `show` command evaluates running instances. If evaluation fails,
|
|
91
|
+
it warns and displays stored state; terminal instances and `show --json` retain
|
|
92
|
+
their stored-state behavior.
|
|
93
|
+
|
|
94
|
+
**Docs impact:** Update field requiredness and initialization guidance, required
|
|
95
|
+
reference repair examples, the model-10 readers-first rollout, the diagnostics
|
|
96
|
+
reference for `StuckCause` and `MissingDocument`, CLI `show` and `diagnose`, MCP
|
|
97
|
+
workflow-state guidance, and the Workflows tool and document-view guides for
|
|
98
|
+
loading, deletion, permissions, and perspectives.
|
|
99
|
+
|
|
100
|
+
- 3b42bdc: The Workflows CLI command-trace now sets the oclif command id as trace context (`groupOrCommand`) when the built-in telemetry shell starts, so telemetry reporting can break invocations down by command the same way it can for other Sanity CLI commands. The id remains in the log payload as `command` for config-provided telemetry loggers.
|
|
101
|
+
|
|
102
|
+
**No upgrade action required.** Existing CLI installs begin emitting the queryable context after upgrading; historical rows are unchanged.
|
|
103
|
+
|
|
104
|
+
**Docs impact:** None — internal telemetry envelope shape for an existing event; no public API or user-facing CLI behavior change.
|
|
105
|
+
|
|
106
|
+
- 7eb9eca: Correct the API references for field initialization and edits, start requirements,
|
|
107
|
+
transitions, reference IDs, effect handling, reactive state, member controls,
|
|
108
|
+
Studio mappings, test helpers, and GROQ condition outcomes. The references state
|
|
109
|
+
caller constraints and defaults that were missing or incorrect. Package setup
|
|
110
|
+
guidance identifies the public npm packages and supported deployment command;
|
|
111
|
+
the MCP validation description distinguishes validation from deployment checks.
|
|
112
|
+
Runtime behavior and API signatures are unchanged.
|
|
113
|
+
|
|
114
|
+
**No upgrade action required.**
|
|
115
|
+
|
|
116
|
+
**Docs impact:** After release and reference sync, reconcile the modeling,
|
|
117
|
+
runtime, reactive UI, Studio, testing, deployment, MCP, and evaluation-insight
|
|
118
|
+
guides and references with the corrected contracts. Fix affected examples and
|
|
119
|
+
replace redundant API inventories with verified symbol links while preserving
|
|
120
|
+
useful teaching and the CLI/MCP reference material not exposed by TypeDoc.
|
|
121
|
+
|
|
122
|
+
- 2cef086: Internal maintenance consolidates engine action/edit handling and Studio document
|
|
123
|
+
and workflow title sorting. Action and field-edit calls retain their inputs and
|
|
124
|
+
results, and document and workflow titles retain their ordering. Action
|
|
125
|
+
availability, telemetry, and persisted document formats are unchanged.
|
|
126
|
+
|
|
127
|
+
**No upgrade action required.**
|
|
128
|
+
|
|
129
|
+
**Docs impact: None** because public APIs, configuration, and workflow behavior
|
|
130
|
+
are unchanged.
|
|
131
|
+
|
|
132
|
+
- Updated dependencies [8874c50]
|
|
133
|
+
- Updated dependencies [b3b2797]
|
|
134
|
+
- Updated dependencies [393ac71]
|
|
135
|
+
- Updated dependencies [8874c50]
|
|
136
|
+
- Updated dependencies [ba031fe]
|
|
137
|
+
- Updated dependencies [0555271]
|
|
138
|
+
- Updated dependencies [b3b2797]
|
|
139
|
+
- Updated dependencies [225e0fb]
|
|
140
|
+
- Updated dependencies [b7510cc]
|
|
141
|
+
- Updated dependencies [7eb9eca]
|
|
142
|
+
- Updated dependencies [2cef086]
|
|
143
|
+
- Updated dependencies [232f811]
|
|
144
|
+
- Updated dependencies [ba031fe]
|
|
145
|
+
- @sanity/workflow-blueprint@0.33.0
|
|
146
|
+
- @sanity/workflow-engine@0.33.0
|
|
147
|
+
|
|
3
148
|
## 0.32.0
|
|
4
149
|
|
|
5
150
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,23 +3,31 @@
|
|
|
3
3
|
Command-line tool for deploying, inspecting, and administering Sanity workflow
|
|
4
4
|
definitions and instances.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
> `sanity login`.
|
|
6
|
+
The package is in early access and is publicly available on npm. Commands that
|
|
7
|
+
access Sanity use the resources in `sanity.workflow.ts` and authenticate with a
|
|
8
|
+
login session or an API token. `deploy --check` validates definitions offline.
|
|
10
9
|
|
|
11
10
|
## Run
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
Use Node.js 20.12 or later. Install the CLI and its engine peer from the same
|
|
13
|
+
Workflows release in your project:
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
pnpm --filter @sanity/workflow-cli dev list --include-completed
|
|
20
|
-
pnpm --filter @sanity/workflow-cli dev show wf-instance.abc123
|
|
15
|
+
```sh
|
|
16
|
+
npm install --save-dev @sanity/workflow-cli @sanity/workflow-engine
|
|
17
|
+
npx @sanity/workflow-cli --help
|
|
21
18
|
```
|
|
22
19
|
|
|
20
|
+
After authentication and [configuration](#configuration), run commands through
|
|
21
|
+
the project-local package:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx @sanity/workflow-cli deploy --dry-run
|
|
25
|
+
npx @sanity/workflow-cli list --include-completed
|
|
26
|
+
npx @sanity/workflow-cli show INSTANCE_ID
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Replace `INSTANCE_ID` with the workflow instance ID returned by `list`.
|
|
30
|
+
|
|
23
31
|
The package is an [oclif](https://oclif.io) plugin: every command's canonical
|
|
24
32
|
id nests under the `workflows` topic
|
|
25
33
|
(`sanity-workflows workflows deploy`), so mounting the package into
|
|
@@ -32,9 +40,9 @@ binary's stable surface — its production and development entrypoints
|
|
|
32
40
|
resolves the command. Those short forms are **not** registered as oclif aliases,
|
|
33
41
|
so a host mount does not pollute the host root.
|
|
34
42
|
|
|
35
|
-
Authenticate
|
|
36
|
-
CI, set `SANITY_AUTH_TOKEN` instead.
|
|
37
|
-
directory you run from
|
|
43
|
+
Authenticate with `npx sanity@latest login`; the CLI reads that session token.
|
|
44
|
+
For CI, set `SANITY_AUTH_TOKEN` instead. Create a `sanity.workflow.ts` in the
|
|
45
|
+
directory you run from, as described in [Configuration](#configuration).
|
|
38
46
|
|
|
39
47
|
The token must span **every resource the workflow references**, not just the
|
|
40
48
|
workflow resource. Runtime commands build one client from the deployment
|
|
@@ -52,28 +60,29 @@ the directory you run from. It exports a config built with
|
|
|
52
60
|
environment):
|
|
53
61
|
|
|
54
62
|
```ts
|
|
63
|
+
import type {WorkflowDeploymentInput} from '@sanity/workflow-engine'
|
|
55
64
|
import {defineWorkflowConfig} from '@sanity/workflow-engine/define'
|
|
56
65
|
|
|
57
66
|
import {articleReview, urlDraft} from './src/workflows.ts'
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
],
|
|
73
|
-
definitions: [articleReview, urlDraft], // the batch this deployment ships
|
|
74
|
-
},
|
|
68
|
+
// Export each deployment under its own `name`, then list the exports in the
|
|
69
|
+
// config. `blueprint generate` requires these named exports; nothing else does.
|
|
70
|
+
export const production = {
|
|
71
|
+
expectedMinReaderModel: 4,
|
|
72
|
+
name: 'production', // the deployment's unique identity (lowercase letters, digits, dashes)
|
|
73
|
+
tag: 'prod', // the environment partition the engine's docs are scoped to
|
|
74
|
+
workflowResource: {type: 'dataset', id: 'acme.workflows'}, // where those docs live
|
|
75
|
+
resourceAliases: [
|
|
76
|
+
// binds each `@<handle>:` a definition references to a physical resource
|
|
77
|
+
// in a DIFFERENT dataset from `workflowResource` (same-resource content
|
|
78
|
+
// needs no alias — see below)
|
|
79
|
+
{name: 'content', resource: {type: 'dataset', id: 'acme.content'}},
|
|
80
|
+
{name: 'assets', resource: {type: 'dataset', id: 'acme.assets'}},
|
|
75
81
|
],
|
|
76
|
-
|
|
82
|
+
definitions: [articleReview, urlDraft], // the batch this deployment ships
|
|
83
|
+
} satisfies WorkflowDeploymentInput
|
|
84
|
+
|
|
85
|
+
export default defineWorkflowConfig({deployments: [production]})
|
|
77
86
|
```
|
|
78
87
|
|
|
79
88
|
`name` is the deployment's identity: unique across the config and constrained
|
|
@@ -82,16 +91,20 @@ repeat, as long as no two deployments share both a `workflowResource` and a
|
|
|
82
91
|
`tag` — that pair is the storage partition, and the config rejects the
|
|
83
92
|
collision naming both entries.
|
|
84
93
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
tag
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
For `deploy`, `start`, `definition diff`, and `definition delete`, select a
|
|
95
|
+
deployment with `--deployment <name>`. With one deployment configured, you can
|
|
96
|
+
omit the selector. `--tag <tag>` selects one deployment when the tag is unique;
|
|
97
|
+
on `deploy`, it targets every deployment carrying the tag. With several
|
|
98
|
+
deployments and no selector, these commands prompt for a deployment in an
|
|
99
|
+
interactive terminal. In a non-interactive shell, they fail asking for
|
|
100
|
+
`--deployment` or `--tag`; `deploy` also suggests `--all-tags`.
|
|
101
|
+
|
|
102
|
+
The instance-targeted commands `abort`, `set-stage`, `reset-activity`,
|
|
103
|
+
`fire-action`, and `diagnose` locate the instance by ID across configured
|
|
104
|
+
resources. They do not prompt for a deployment. Their optional `--deployment`
|
|
105
|
+
and `--tag` flags narrow the resources searched; the operation's tag comes from
|
|
106
|
+
the loaded instance.
|
|
107
|
+
|
|
95
108
|
`--all-tags` deploys every deployment in the config in one run: a failure in one doesn't
|
|
96
109
|
stop the rest — the run continues, prints a summary of what failed, and exits
|
|
97
110
|
non-zero. The client's project + dataset are derived from the deployment's
|
|
@@ -173,6 +186,8 @@ Invoke as `sanity-workflows <command>` (or `sanity workflows <command>` once the
|
|
|
173
186
|
| `set-stage <instance-id> --to <stage>` | Move an instance to a stage, skipping declared transitions. The target stage's enter lifecycle still runs. |
|
|
174
187
|
| `reset-activity <instance-id> <activity>` | Re-run a failed activity, or `--skip` it so a gated transition can fire. |
|
|
175
188
|
| `fire-action <instance-id>` | Fire an action on a waiting activity. Omit `--action` to list what can be fired. |
|
|
189
|
+
| `blueprint generate` | Write the Sanity Blueprints runtime the definitions require next to `sanity.workflow.ts`. |
|
|
190
|
+
| `blueprint generate --check` | Verify that runtime still matches the definitions. Writes nothing; exits non-zero on any difference. |
|
|
176
191
|
| `definition list` | List deployed definitions. |
|
|
177
192
|
| `definition show <name>` | Show a deployed definition (latest version, or `--version`). |
|
|
178
193
|
| `definition diff <name>` | Diff the in-code definition against what is deployed. |
|
|
@@ -180,6 +195,182 @@ Invoke as `sanity-workflows <command>` (or `sanity workflows <command>` once the
|
|
|
180
195
|
| `nuke --deployment <name>` / `nuke --tag <tag>` | Dev reset: delete every engine-owned document for that tag (instances, definitions, guards). Prints a plan, then asks for confirmation. Content documents are never touched. |
|
|
181
196
|
| `nuke --instance <id>` | Dev reset: delete one finished instance and its guards. Refuses in-flight instances (abort first). |
|
|
182
197
|
|
|
198
|
+
## Generating the Blueprints runtime
|
|
199
|
+
|
|
200
|
+
**Experimental, and not ready for production use in this release.** Both
|
|
201
|
+
commands are available so you can try the generated runtime and give feedback;
|
|
202
|
+
their flags and their output may still change. What you would hit today:
|
|
203
|
+
|
|
204
|
+
- The start watcher runs on every create and update of a subject type, not only
|
|
205
|
+
on writes that can actually start a workflow, so it wakes for documents that
|
|
206
|
+
already have an instance or match no definition. The heartbeat is scheduled
|
|
207
|
+
every minute and evaluates every in-flight instance in the tag when it runs;
|
|
208
|
+
how often the platform runs it is the plan limit described below.
|
|
209
|
+
- `sanity-workflows deploy` does not check the generated tree, so drift between
|
|
210
|
+
your definitions and the tree on disk is caught only by
|
|
211
|
+
`blueprint generate --check` in CI.
|
|
212
|
+
- Definitions and functions deploy in two steps, definitions first, until the
|
|
213
|
+
Blueprints service registers the `sanity.workflow` resource.
|
|
214
|
+
- A generated file cannot be taken over: editing one is drift, and the next
|
|
215
|
+
generation rewrites it.
|
|
216
|
+
- A retry policy is not checked against its function's timeout at generation, so
|
|
217
|
+
an effect can be configured to retry for longer than its function may run.
|
|
218
|
+
|
|
219
|
+
All of these are tracked, and the next release is the bar for using this in
|
|
220
|
+
production.
|
|
221
|
+
|
|
222
|
+
A definition that declares an effect, reads `$now`, or starts autonomously needs
|
|
223
|
+
server-side code: something has to run the effect handlers, sample deadlines, and
|
|
224
|
+
start instances from document writes. `blueprint generate` derives that runtime
|
|
225
|
+
from the definitions and writes it next to `sanity.workflow.ts`, so
|
|
226
|
+
`npx sanity blueprints deploy` has something to deploy. A purely interactive
|
|
227
|
+
workflow needs none of it, and the command says so.
|
|
228
|
+
|
|
229
|
+
Five prerequisites:
|
|
230
|
+
|
|
231
|
+
- **Each deployment is exported under its own `name`, and the export is the
|
|
232
|
+
deployment object itself**, as the [Configuration](#configuration) example
|
|
233
|
+
does: every generated module imports its deployment from `./sanity.workflow`
|
|
234
|
+
by that name. The command refuses to write anything until each name is
|
|
235
|
+
exported, and names the export to add. It checks the name only, so make sure
|
|
236
|
+
each export is the deployment your config lists. A typecheck of the generated
|
|
237
|
+
modules catches a wrong export only where a generated function uses it, and a
|
|
238
|
+
deployment that needs no functions emits none.
|
|
239
|
+
- **`sanity.workflow.ts` is importable inside a deployed function.** Every
|
|
240
|
+
generated module imports it, and each function bundles it, so the file runs
|
|
241
|
+
where your shell and your `.env` do not exist. Reading an absent variable
|
|
242
|
+
there yields `undefined`; what fails is a config that **throws** when a
|
|
243
|
+
variable is missing, or requires one to build the deployment, because that
|
|
244
|
+
runs on init before your handler is reached. Keep the deployment's resource
|
|
245
|
+
coordinates literal in the file and read secrets inside your handlers.
|
|
246
|
+
- **The Blueprints packages the generated modules import are installed**,
|
|
247
|
+
alongside the CLI and its peers.
|
|
248
|
+
- **Your definitions are deployed before the functions.** The generated
|
|
249
|
+
`workflows.blueprint.ts` carries the definitions entry commented out, because
|
|
250
|
+
the Blueprints service does not accept the `sanity.workflow` resource type
|
|
251
|
+
yet, so `npx sanity blueprints deploy` does not deploy them. Run
|
|
252
|
+
`npx @sanity/workflow-cli deploy` first: a function that starts or advances an
|
|
253
|
+
instance resolves its definition from the Content Lake, and finds nothing
|
|
254
|
+
until that deploy has run.
|
|
255
|
+
- **A generated heartbeat needs an organization-scoped stack.** The heartbeat is
|
|
256
|
+
a scheduled function, and the platform only accepts one on a stack in
|
|
257
|
+
organization scope. A project-scoped stack refuses it, and with it the whole
|
|
258
|
+
deploy. See
|
|
259
|
+
[Promote a stack to organization scope](https://www.sanity.io/docs/blueprints/promote-stack-to-organization-scope).
|
|
260
|
+
How often it may run is a plan limit rather than something the generator
|
|
261
|
+
chooses — see [the schedule](#the-generated-heartbeats-schedule) below.
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
npm install --save-dev @sanity/blueprints @sanity/functions @sanity/workflow-blueprint
|
|
265
|
+
npx @sanity/workflow-cli blueprint generate
|
|
266
|
+
npx @sanity/workflow-cli deploy
|
|
267
|
+
npx sanity blueprints deploy
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Each generated function's `package.json` copies the specifications your project
|
|
271
|
+
declares for `@sanity/functions`, `@sanity/workflow-blueprint`, and
|
|
272
|
+
`@sanity/workflow-engine`. A package you declare nowhere fails the command
|
|
273
|
+
rather than being stamped with a guessed range.
|
|
274
|
+
|
|
275
|
+
The command prints what the definitions need and why, then every file it wrote:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
$ npx @sanity/workflow-cli blueprint generate
|
|
279
|
+
Runtime needs:
|
|
280
|
+
|
|
281
|
+
▸ prod (prod)
|
|
282
|
+
hosting: this deployment declares function, and each level below inherits it
|
|
283
|
+
→ function workflow(s): article-review
|
|
284
|
+
→ function effect(s): notify-reviewer
|
|
285
|
+
effects: 1 declared — notify-reviewer
|
|
286
|
+
→ drain function wf-prod-drain-effects runs notify-reviewer
|
|
287
|
+
triggered by instance writes matching _type == "sanity.workflow.instance" && tag == "prod" && count(pendingEffects[!defined(claim)]) > 0
|
|
288
|
+
clock: samples $now deadlines; sweeps stale effect claims left by a dead drain
|
|
289
|
+
article-review · working · action-when · $fields.subject.embargoAt <= $now
|
|
290
|
+
→ scheduled function wf-prod-heartbeat on * * * * *
|
|
291
|
+
a scheduled function runs at most as often as your organization's plan allows — every minute on Enterprise, hourly on Growth, daily on Free (https://www.sanity.io/docs/functions/functions-introduction) — and a schedule below that threshold is deployed and never invoked
|
|
292
|
+
autonomous starts: 1 definition(s) — article-review
|
|
293
|
+
→ start watcher wf-prod-start-workflows on acme.workflows matching _type in ["article"]
|
|
294
|
+
|
|
295
|
+
Generated tree:
|
|
296
|
+
created workflows.blueprint.ts
|
|
297
|
+
created functions/wf-prod-drain-effects/index.ts
|
|
298
|
+
created functions/wf-prod-drain-effects/package.json
|
|
299
|
+
created functions/wf-prod-heartbeat/index.ts
|
|
300
|
+
created functions/wf-prod-heartbeat/package.json
|
|
301
|
+
created functions/wf-prod-start-workflows/index.ts
|
|
302
|
+
created functions/wf-prod-start-workflows/package.json
|
|
303
|
+
created effect-handlers/all.ts
|
|
304
|
+
created effect-handlers/notify-reviewer.ts yours now — implement the effect
|
|
305
|
+
created sanity.blueprint.ts the workflow resources spread
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Fill in each `effect-handlers/<effect name>.ts`: they are yours, scaffolded once
|
|
309
|
+
and never overwritten. Everything marked `created`, `updated`, or `unchanged`
|
|
310
|
+
under `workflows.blueprint.ts`, `functions/`, and `effect-handlers/all.ts` is
|
|
311
|
+
regenerated on every run, so edits there do not survive. Your own
|
|
312
|
+
`sanity.blueprint.ts` receives the `workflowResources` import and spread once;
|
|
313
|
+
after that the command leaves it alone. The command never deletes a file. A
|
|
314
|
+
handler for an effect you removed, a scaffold blocked by a path that differs
|
|
315
|
+
only in case, and a blueprint it could not wire are reported for you to resolve.
|
|
316
|
+
|
|
317
|
+
The command covers every deployment in the config, because the emitted
|
|
318
|
+
`workflows.blueprint.ts` declares them all.
|
|
319
|
+
|
|
320
|
+
### Where each workflow runs
|
|
321
|
+
|
|
322
|
+
The report opens with the hosting each `runtime` block resolved to, after every
|
|
323
|
+
level has inherited from the one above it. It matters because a name listed
|
|
324
|
+
under `durableFunction` or `selfHosted` has no emitted function to point at:
|
|
325
|
+
|
|
326
|
+
- `function` — the drain, heartbeat, and start watcher above.
|
|
327
|
+
- `durableFunction` — nothing is emitted yet. The rest of the deployment emits
|
|
328
|
+
normally around it.
|
|
329
|
+
- `selfHosted` — nothing is emitted, and the report ends with what your own
|
|
330
|
+
process must do instead: which deadlines to tick, which effects to drain,
|
|
331
|
+
which parents to re-evaluate when a child settles, and which autonomous
|
|
332
|
+
definitions to start. A self-hosted deadline is never a reason to emit a
|
|
333
|
+
heartbeat, so it is listed there and not under `clock`; an emitted heartbeat
|
|
334
|
+
ticks every in-flight instance in the tag, whatever hosts each workflow.
|
|
335
|
+
|
|
336
|
+
### The generated heartbeat's schedule
|
|
337
|
+
|
|
338
|
+
The heartbeat is emitted on `* * * * *`, one run a minute, and how often the
|
|
339
|
+
platform will actually run it is a plan limit rather than something the
|
|
340
|
+
generator can choose: Sanity documents the threshold as
|
|
341
|
+
[Free daily, Growth hourly, Enterprise minutely](https://www.sanity.io/docs/functions/functions-introduction).
|
|
342
|
+
Below Enterprise the platform accepts the scheduled function and does not run
|
|
343
|
+
it, so a workflow whose deadlines depend on the heartbeat does not advance and
|
|
344
|
+
nothing reports an error. Check your plan's cadence before relying on a `$now`
|
|
345
|
+
deadline in a deployed workflow.
|
|
346
|
+
|
|
347
|
+
### Checking for drift in CI
|
|
348
|
+
|
|
349
|
+
`blueprint generate --check` renders the same runtime in memory and compares it
|
|
350
|
+
with the files on disk. It writes nothing and exits zero when they match:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
$ npx @sanity/workflow-cli blueprint generate --check
|
|
354
|
+
✔ the generated tree matches the definitions
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Otherwise it exits `1` and names every file, so the log alone says what to fix:
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
$ npx @sanity/workflow-cli blueprint generate --check
|
|
361
|
+
✖ The generated tree does not match the definitions (1):
|
|
362
|
+
differs effect-handlers/all.ts
|
|
363
|
+
Run `sanity-workflows blueprint generate` to bring it back in line.
|
|
364
|
+
$ echo $?
|
|
365
|
+
1
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
A file reports `missing` when nothing is at its path, `differs` when the file
|
|
369
|
+
there has other contents, and a handler for a declared effect reports `missing`
|
|
370
|
+
when you have not written it yet. Add the command to the job that builds your
|
|
371
|
+
blueprint and a definition change can no longer ship without the runtime it
|
|
372
|
+
needs.
|
|
373
|
+
|
|
183
374
|
## Telemetry
|
|
184
375
|
|
|
185
376
|
The CLI collects usage telemetry through Sanity's standard pipeline: a
|
|
@@ -193,6 +384,17 @@ is passed — see [Definition sharing](#definition-sharing)),
|
|
|
193
384
|
and a session that isn't logged in sends nothing. A one-time notice on stderr
|
|
194
385
|
discloses collection on first use.
|
|
195
386
|
|
|
387
|
+
Each event includes `context.surface: 'cli'` and its process execution mode
|
|
388
|
+
in `context.environment`. `NODE_ENV=production` reports `production`;
|
|
389
|
+
`development` and `test` report `development`. Unset, empty, and unrecognized
|
|
390
|
+
values default to `production` for installed CLI and MCP runs. The SDK reports
|
|
391
|
+
build mode and defaults to `development` instead.
|
|
392
|
+
|
|
393
|
+
When comparing activity across surfaces, group or filter by `context.surface`
|
|
394
|
+
alongside `context.environment`. Environment does not identify a production
|
|
395
|
+
dataset or deployment; the API host, dataset name, and deployment tag do not
|
|
396
|
+
set it. Existing telemetry consent and opt-out settings still apply.
|
|
397
|
+
|
|
196
398
|
One extension point: supply your own logger as `telemetry` in
|
|
197
399
|
`sanity.workflow.ts` and the built-in pipeline is not constructed at all —
|
|
198
400
|
every event flows to your implementation unconditionally (CI included), and
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WorkflowCommand } from '../../../lib/base-command.ts';
|
|
2
|
+
export default class BlueprintGenerate extends WorkflowCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
check: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { WorkflowCommand } from "../../../lib/base-command.js";
|
|
3
|
+
import { checkBlueprint, generateBlueprint } from "../../../lib/blueprint-emit.js";
|
|
4
|
+
import { loadWorkflowConfigModule } from "../../../lib/load-config.js";
|
|
5
|
+
export default class BlueprintGenerate extends WorkflowCommand {
|
|
6
|
+
static description = 'Experimental: generate the Sanity Blueprints runtime the definitions require, next to sanity.workflow.ts. Writes the workflow resources, one function per derived need, the effect-handler registry, and a handler stub per declared effect. Covers every deployment in the config, because the emitted resources module declares them all. These flags and this output may change before the Blueprints backend accepts the sanity.workflow resource.';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> workflows blueprint generate',
|
|
9
|
+
'<%= config.bin %> workflows blueprint generate --check',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
check: Flags.boolean({
|
|
13
|
+
description: 'Experimental: verify the tree on disk still matches the definitions; write nothing and exit non-zero on any difference. The CI drift gate.',
|
|
14
|
+
default: false,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(BlueprintGenerate);
|
|
19
|
+
const root = process.cwd();
|
|
20
|
+
const { config, configFile, exportedNames } = await loadWorkflowConfigModule(root);
|
|
21
|
+
const log = (line) => this.log(line);
|
|
22
|
+
if (flags.check) {
|
|
23
|
+
checkBlueprint({ root, config, configFile, exportedNames, log });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
generateBlueprint({ root, config, configFile, exportedNames, log });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
|
|
1
|
+
import { type Diagnosis, type DiagnoseInput, type MissingDocument, type SuggestedRemediation, type WorkflowEvaluation } from '@sanity/workflow-engine';
|
|
2
2
|
import { WorkflowCommand } from '../../lib/base-command.ts';
|
|
3
3
|
/** The `--json` transitions payload: raw derived state (atom GROQ, negation,
|
|
4
4
|
* pivotality, solvable requirement) — structure for scripts, never baked
|
|
@@ -23,10 +23,12 @@ export declare function rawTransitionProjection(evaluation: WorkflowEvaluation):
|
|
|
23
23
|
* {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
|
|
24
24
|
* fix block and the `--json` output stay one computation.
|
|
25
25
|
*/
|
|
26
|
-
export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, }: {
|
|
26
|
+
export declare function renderDiagnosis({ diagnosis, input, remediations, explanations, allMissingDocuments, }: {
|
|
27
27
|
diagnosis: Diagnosis;
|
|
28
28
|
input: DiagnoseInput;
|
|
29
29
|
remediations: SuggestedRemediation[];
|
|
30
|
+
/** Full evaluation evidence, including references outside the blocking subset. */
|
|
31
|
+
allMissingDocuments?: MissingDocument[] | undefined;
|
|
30
32
|
/** Insight summaries per unsatisfied transition name — rendered under the
|
|
31
33
|
* transition line so "stuck" reads as "stuck until X". */
|
|
32
34
|
explanations?: ReadonlyMap<string, string> | undefined;
|