@sanity/workflow-cli 0.8.0 → 0.8.1

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.
@@ -28,7 +28,7 @@ export default class Abort extends Command {
28
28
  try {
29
29
  const result = await workflow.abortInstance({
30
30
  client,
31
- ...buildOperationArgs(config, args.instanceId, flags.reason),
31
+ ...buildOperationArgs({ config, instanceId: args.instanceId, reason: flags.reason }),
32
32
  });
33
33
  const report = abortReport(result);
34
34
  if (!report.fired) {
@@ -21,10 +21,14 @@ export default class DefinitionDelete extends Command {
21
21
  * engine's optionals reject an explicit `undefined` under
22
22
  * `exactOptionalPropertyTypes`.
23
23
  */
24
- export declare function buildDeleteArgs(config: WorkflowConfig, name: string, flags: {
25
- version?: number | undefined;
26
- cascade: boolean;
27
- reason?: string | undefined;
24
+ export declare function buildDeleteArgs({ config, name, flags, }: {
25
+ config: WorkflowConfig;
26
+ name: string;
27
+ flags: {
28
+ version?: number | undefined;
29
+ cascade: boolean;
30
+ reason?: string | undefined;
31
+ };
28
32
  }): Omit<DeleteDefinitionArgs, 'client'>;
29
33
  /** What the spinner names: the workflow, or one pinned version of it. */
30
34
  export declare function deleteTargetLabel(args: Pick<DeleteDefinitionArgs, 'definition' | 'version'>): string;
@@ -36,7 +36,7 @@ export default class DefinitionDelete extends Command {
36
36
  const { args, flags } = await this.parse(DefinitionDelete);
37
37
  const config = loadWorkflowConfig(flags.tag);
38
38
  const client = loadClient();
39
- const deleteArgs = buildDeleteArgs(config, args.name, flags);
39
+ const deleteArgs = buildDeleteArgs({ config, name: args.name, flags });
40
40
  const spinner = ora(`Deleting definition ${deleteTargetLabel(deleteArgs)}…`).start();
41
41
  try {
42
42
  const result = await workflow.deleteDefinition({ client, ...deleteArgs });
@@ -55,7 +55,7 @@ export default class DefinitionDelete extends Command {
55
55
  * engine's optionals reject an explicit `undefined` under
56
56
  * `exactOptionalPropertyTypes`.
57
57
  */
58
- export function buildDeleteArgs(config, name, flags) {
58
+ export function buildDeleteArgs({ config, name, flags, }) {
59
59
  return {
60
60
  ...baseEngineArgs(config),
61
61
  definition: name,
@@ -29,14 +29,19 @@ export default class DefinitionDiff extends Command {
29
29
  const spinner = ora(`Diffing ${args.definition} against deployed…`).start();
30
30
  let deployed;
31
31
  try {
32
- deployed = await fetchDeployedDefinition(client, args.definition, config, flags.version);
32
+ deployed = await fetchDeployedDefinition({
33
+ client,
34
+ name: args.definition,
35
+ config,
36
+ version: flags.version,
37
+ });
33
38
  spinner.succeed(`Diffed ${args.definition}`);
34
39
  }
35
40
  catch (error) {
36
41
  spinner.fail('Diff failed');
37
42
  throw error;
38
43
  }
39
- for (const line of diffReport([diffEntry(def, deployed, config)]))
44
+ for (const line of diffReport([diffEntry({ def, latestRaw: deployed, target: config })]))
40
45
  this.log(line);
41
46
  }
42
47
  }
@@ -47,7 +52,7 @@ export default class DefinitionDiff extends Command {
47
52
  * superseded — latest unless pinned via `version`. An absent latest is a
48
53
  * legitimate "create" diff, but an absent *pinned* version is an error.
49
54
  */
50
- async function fetchDeployedDefinition(client, name, config, version) {
55
+ async function fetchDeployedDefinition({ client, name, config, version, }) {
51
56
  const { groq, params } = buildDefinitionShowQuery({ name, tag: config.tag, version });
52
57
  const deployed = await client.fetch(groq, params);
53
58
  if (deployed === null && version !== undefined) {
@@ -57,11 +57,15 @@ const HEADER_PAD = 11; // "Description" — the longest label in the block
57
57
  * authored definition, so it's passed separately and omitted when unknown. */
58
58
  export function definitionHeader(def, tag) {
59
59
  const rows = [
60
- formatKeyValue('Title', def.title, { padTo: HEADER_PAD }),
61
- formatKeyValue('Description', def.description ?? styleText('dim', '—'), { padTo: HEADER_PAD }),
60
+ formatKeyValue({ key: 'Title', value: def.title, padTo: HEADER_PAD }),
61
+ formatKeyValue({
62
+ key: 'Description',
63
+ value: def.description ?? styleText('dim', '—'),
64
+ padTo: HEADER_PAD,
65
+ }),
62
66
  ];
63
67
  if (tag !== undefined) {
64
- rows.push(formatKeyValue('Tag', tag, { padTo: HEADER_PAD }));
68
+ rows.push(formatKeyValue({ key: 'Tag', value: tag, padTo: HEADER_PAD }));
65
69
  }
66
70
  return [styleText('bold', `${def.name} v${def.version}`), ...rows].join('\n');
67
71
  }
@@ -41,12 +41,12 @@ export default class Deploy extends Command {
41
41
  }
42
42
  const client = loadClient();
43
43
  if (flags['dry-run']) {
44
- await this.runDryRun(client, selected, config);
44
+ await this.runDryRun({ client, defs: selected, config });
45
45
  return;
46
46
  }
47
- await this.executeDeploy(client, selected, config);
47
+ await this.executeDeploy({ client, defs: selected, config });
48
48
  }
49
- async executeDeploy(client, defs, config) {
49
+ async executeDeploy({ client, defs, config, }) {
50
50
  const spinner = ora(`Deploying ${defs.length} definition(s)…`).start();
51
51
  try {
52
52
  const { results } = await workflow.deployDefinitions({
@@ -64,10 +64,10 @@ export default class Deploy extends Command {
64
64
  throw error;
65
65
  }
66
66
  }
67
- async runDryRun(client, defs, config) {
67
+ async runDryRun({ client, defs, config, }) {
68
68
  const spinner = ora(`Diffing ${defs.length} definition(s) against dataset…`).start();
69
69
  try {
70
- const entries = await computeDiffEntries(client, defs, config);
70
+ const entries = await computeDiffEntries({ client, defs, target: config });
71
71
  spinner.succeed(`Diffed ${defs.length} definition(s)`);
72
72
  for (const line of diffReport(entries))
73
73
  this.log(line);
@@ -8,7 +8,11 @@ import { type Diagnosis, type DiagnoseInput, type SuggestedRemediation } from '@
8
8
  * {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
9
9
  * fix block and the `--json` output stay one computation.
10
10
  */
11
- export declare function renderDiagnosis(diagnosis: Diagnosis, input: DiagnoseInput, remediations: SuggestedRemediation[]): string[];
11
+ export declare function renderDiagnosis({ diagnosis, input, remediations, }: {
12
+ diagnosis: Diagnosis;
13
+ input: DiagnoseInput;
14
+ remediations: SuggestedRemediation[];
15
+ }): string[];
12
16
  export default class Diagnose extends Command {
13
17
  static description: string;
14
18
  static examples: string[];
@@ -179,7 +179,7 @@ function statusLine(diagnosis) {
179
179
  * {@link SuggestedRemediation}s rather than re-deriving them, so the rendered
180
180
  * fix block and the `--json` output stay one computation.
181
181
  */
182
- export function renderDiagnosis(diagnosis, input, remediations) {
182
+ export function renderDiagnosis({ diagnosis, input, remediations, }) {
183
183
  const lines = [statusLine(diagnosis)];
184
184
  const inFlight = diagnosis.state === 'progressing' ||
185
185
  diagnosis.state === 'waiting' ||
@@ -255,7 +255,7 @@ export default class Diagnose extends Command {
255
255
  }
256
256
  this.log(instanceHeader(evaluation.instance));
257
257
  this.log('');
258
- for (const line of renderDiagnosis(diagnosis, input, remediations)) {
258
+ for (const line of renderDiagnosis({ diagnosis, input, remediations })) {
259
259
  this.log(line);
260
260
  }
261
261
  }
@@ -14,7 +14,11 @@ export declare function parseParams(pairs: string[]): Record<string, unknown>;
14
14
  * flags to fire one. Pure so the populated/empty permutations assert
15
15
  * without a live evaluation.
16
16
  */
17
- export declare function renderAvailableActions(actions: AvailableAction[], stage: string, instanceId: string): string[];
17
+ export declare function renderAvailableActions({ actions, stage, instanceId, }: {
18
+ actions: AvailableAction[];
19
+ stage: string;
20
+ instanceId: string;
21
+ }): string[];
18
22
  interface FireOutcome {
19
23
  fired: boolean;
20
24
  cascaded: number;
@@ -40,7 +44,11 @@ export interface FireActionReport {
40
44
  * the fired / no-op / cascaded / ops permutations assert without a live
41
45
  * fire.
42
46
  */
43
- export declare function fireActionReport(result: FireOutcome, activity: string, action: string): FireActionReport;
47
+ export declare function fireActionReport({ result, activity, action, }: {
48
+ result: FireOutcome;
49
+ activity: string;
50
+ action: string;
51
+ }): FireActionReport;
44
52
  export default class FireAction extends Command {
45
53
  static description: string;
46
54
  static examples: string[];
@@ -79,7 +79,7 @@ function actionLines(a) {
79
79
  * flags to fire one. Pure so the populated/empty permutations assert
80
80
  * without a live evaluation.
81
81
  */
82
- export function renderAvailableActions(actions, stage, instanceId) {
82
+ export function renderAvailableActions({ actions, stage, instanceId, }) {
83
83
  if (actions.length === 0) {
84
84
  return [`No actions on the activities of the current stage ('${stage}').`];
85
85
  }
@@ -108,7 +108,7 @@ export function fireResultJson(result, ids) {
108
108
  * the fired / no-op / cascaded / ops permutations assert without a live
109
109
  * fire.
110
110
  */
111
- export function fireActionReport(result, activity, action) {
111
+ export function fireActionReport({ result, activity, action, }) {
112
112
  const stage = styleText('bold', result.instance.currentStage);
113
113
  if (!result.fired) {
114
114
  return {
@@ -167,7 +167,7 @@ export default class FireAction extends Command {
167
167
  const client = loadClient();
168
168
  const actor = failOnThrow('fire-action usage error:', () => resolveActor(flags.as, flags['as-role']));
169
169
  if (flags.action === undefined) {
170
- await this.listActions(client, config, args.instanceId, actor, flags.json);
170
+ await this.listActions({ client, config, instanceId: args.instanceId, actor, json: flags.json });
171
171
  return;
172
172
  }
173
173
  if (flags.activity === undefined) {
@@ -184,7 +184,7 @@ export default class FireAction extends Command {
184
184
  json: flags.json,
185
185
  });
186
186
  }
187
- async listActions(client, config, instanceId, actor, json) {
187
+ async listActions({ client, config, instanceId, actor, json, }) {
188
188
  const { evaluation, actions } = await workflow
189
189
  .availableActions({
190
190
  client,
@@ -201,7 +201,7 @@ export default class FireAction extends Command {
201
201
  }
202
202
  this.log(instanceHeader(evaluation.instance));
203
203
  this.log('');
204
- for (const line of renderAvailableActions(actions, stage, instanceId)) {
204
+ for (const line of renderAvailableActions({ actions, stage, instanceId })) {
205
205
  this.log(line);
206
206
  }
207
207
  }
@@ -228,7 +228,11 @@ export default class FireAction extends Command {
228
228
  const spinner = ora(`Firing ${action} on ${activity}…`).start();
229
229
  try {
230
230
  const result = await workflow.fireAction(fireArgs);
231
- emitWriteReport(spinner, fireActionReport(result, activity, action), (line) => this.log(line));
231
+ emitWriteReport({
232
+ spinner,
233
+ report: fireActionReport({ result, activity, action }),
234
+ log: (line) => this.log(line),
235
+ });
232
236
  }
233
237
  catch (error) {
234
238
  spinner.fail('Action rejected');
@@ -17,7 +17,12 @@ export default class MoveStage extends Command {
17
17
  }
18
18
  /** The shared operation args ({@link buildOperationArgs}) plus the
19
19
  * setStage-specific target. */
20
- export declare function buildSetStageArgs(config: WorkflowConfig, instanceId: string, targetStage: string, reason: string | undefined): Omit<SetStageArgs, 'client'>;
20
+ export declare function buildSetStageArgs({ config, instanceId, targetStage, reason, }: {
21
+ config: WorkflowConfig;
22
+ instanceId: string;
23
+ targetStage: string;
24
+ reason: string | undefined;
25
+ }): Omit<SetStageArgs, 'client'>;
21
26
  interface SetStageOutcome {
22
27
  fired: boolean;
23
28
  cascaded: number;
@@ -38,9 +38,18 @@ export default class MoveStage extends Command {
38
38
  try {
39
39
  const result = await workflow.setStage({
40
40
  client,
41
- ...buildSetStageArgs(config, args.instanceId, flags.to, flags.reason),
41
+ ...buildSetStageArgs({
42
+ config,
43
+ instanceId: args.instanceId,
44
+ targetStage: flags.to,
45
+ reason: flags.reason,
46
+ }),
47
+ });
48
+ emitWriteReport({
49
+ spinner,
50
+ report: moveStageReport(result, flags.to),
51
+ log: (line) => this.log(line),
42
52
  });
43
- emitWriteReport(spinner, moveStageReport(result, flags.to), (line) => this.log(line));
44
53
  }
45
54
  catch (error) {
46
55
  spinner.fail('Move rejected');
@@ -51,8 +60,8 @@ export default class MoveStage extends Command {
51
60
  }
52
61
  /** The shared operation args ({@link buildOperationArgs}) plus the
53
62
  * setStage-specific target. */
54
- export function buildSetStageArgs(config, instanceId, targetStage, reason) {
55
- return { ...buildOperationArgs(config, instanceId, reason), targetStage };
63
+ export function buildSetStageArgs({ config, instanceId, targetStage, reason, }) {
64
+ return { ...buildOperationArgs({ config, instanceId, reason }), targetStage };
56
65
  }
57
66
  /**
58
67
  * Turn a `setStage` result into the spinner message + ops block. Pure so
@@ -46,8 +46,8 @@ const HEADER_PAD = 9; // "Completed" — the longest label in the block
46
46
  export function instanceHeader(instance) {
47
47
  const title = `${styleText('bold', `${instance.definition} v${instance.pinnedVersion}`)} ${styleText('cyan', instance._id)}`;
48
48
  const rows = [
49
- formatKeyValue('Stage', instance.currentStage, { padTo: HEADER_PAD }),
50
- formatKeyValue('Started', formatTimestamp(instance.startedAt), { padTo: HEADER_PAD }),
49
+ formatKeyValue({ key: 'Stage', value: instance.currentStage, padTo: HEADER_PAD }),
50
+ formatKeyValue({ key: 'Started', value: formatTimestamp(instance.startedAt), padTo: HEADER_PAD }),
51
51
  ];
52
52
  // Aborted instances also carry a completedAt stamped at the abort, so the
53
53
  // abort line stands in for completion — showing a green "Completed" too would
@@ -56,15 +56,15 @@ export function instanceHeader(instance) {
56
56
  const reason = abortReason(instance);
57
57
  const tail = reason ? ` — ${reason}` : '';
58
58
  const value = styleText('yellow', `${formatTimestamp(instance.abortedAt)}${tail}`);
59
- rows.push(formatKeyValue('Aborted', value, { padTo: HEADER_PAD }));
59
+ rows.push(formatKeyValue({ key: 'Aborted', value, padTo: HEADER_PAD }));
60
60
  }
61
61
  else {
62
62
  const completed = instance.completedAt
63
63
  ? styleText('green', formatTimestamp(instance.completedAt))
64
64
  : styleText('dim', '—');
65
- rows.push(formatKeyValue('Completed', completed, { padTo: HEADER_PAD }));
65
+ rows.push(formatKeyValue({ key: 'Completed', value: completed, padTo: HEADER_PAD }));
66
66
  }
67
- rows.push(formatKeyValue('Tag', instance.tag, { padTo: HEADER_PAD }));
67
+ rows.push(formatKeyValue({ key: 'Tag', value: instance.tag, padTo: HEADER_PAD }));
68
68
  return [title, ...rows].join('\n');
69
69
  }
70
70
  /**
@@ -33,7 +33,7 @@ export default class Tail extends Command {
33
33
  .subscribe({
34
34
  next: async () => {
35
35
  try {
36
- seen = await this.printNewEntries(client, args.instanceId, seen);
36
+ seen = await this.printNewEntries({ client, instanceId: args.instanceId, seen });
37
37
  }
38
38
  catch (err) {
39
39
  const message = err instanceof Error ? err.message : String(err);
@@ -61,7 +61,7 @@ export default class Tail extends Command {
61
61
  * given high-water mark, return the new mark. Pulled out so the
62
62
  * async work in the subscription's `next` handler stays tidy.
63
63
  */
64
- async printNewEntries(client, instanceId, seen) {
64
+ async printNewEntries({ client, instanceId, seen, }) {
65
65
  const next = await client.getDocument(instanceId);
66
66
  if (!next)
67
67
  return seen;
@@ -20,6 +20,10 @@ export declare function resolveActor(as: string | undefined, roles: string[]): A
20
20
  * conditionally because the engine's optional field rejects an explicit
21
21
  * `undefined` under `exactOptionalPropertyTypes`.
22
22
  */
23
- export declare function buildOperationArgs(config: WorkflowConfig, instanceId: string, reason: string | undefined): Omit<OperationArgs, 'client'> & {
23
+ export declare function buildOperationArgs({ config, instanceId, reason, }: {
24
+ config: WorkflowConfig;
25
+ instanceId: string;
26
+ reason: string | undefined;
27
+ }): Omit<OperationArgs, 'client'> & {
24
28
  reason?: string;
25
29
  };
@@ -39,7 +39,7 @@ export function resolveActor(as, roles) {
39
39
  * conditionally because the engine's optional field rejects an explicit
40
40
  * `undefined` under `exactOptionalPropertyTypes`.
41
41
  */
42
- export function buildOperationArgs(config, instanceId, reason) {
42
+ export function buildOperationArgs({ config, instanceId, reason, }) {
43
43
  return {
44
44
  ...baseEngineArgs(config),
45
45
  instanceId,
@@ -27,4 +27,8 @@ export interface WriteReport {
27
27
  * Shared by every write command so the fired / not-fired branches stay
28
28
  * consistent.
29
29
  */
30
- export declare function emitWriteReport(spinner: Ora, report: WriteReport, log: (line: string) => void): void;
30
+ export declare function emitWriteReport({ spinner, report, log, }: {
31
+ spinner: Ora;
32
+ report: WriteReport;
33
+ log: (line: string) => void;
34
+ }): void;
@@ -18,7 +18,7 @@ export function opsAppliedLines(ranOps) {
18
18
  * Shared by every write command so the fired / not-fired branches stay
19
19
  * consistent.
20
20
  */
21
- export function emitWriteReport(spinner, report, log) {
21
+ export function emitWriteReport({ spinner, report, log, }) {
22
22
  if (!report.fired) {
23
23
  spinner.warn(report.message);
24
24
  return;
package/dist/lib/ui.d.ts CHANGED
@@ -9,7 +9,9 @@ export declare function sectionHeader(title: string): string;
9
9
  * the Sanity CLI's `formatKeyValue` so it folds into the shared helper on
10
10
  * merge; pass `padTo` (the longest key's length) to align a block of rows.
11
11
  */
12
- export declare function formatKeyValue(key: string, value: string, options?: {
12
+ export declare function formatKeyValue({ key, value, indent, padTo, }: {
13
+ key: string;
14
+ value: string;
13
15
  indent?: number;
14
16
  padTo?: number;
15
17
  }): string;
package/dist/lib/ui.js CHANGED
@@ -15,9 +15,7 @@ export function sectionHeader(title) {
15
15
  * the Sanity CLI's `formatKeyValue` so it folds into the shared helper on
16
16
  * merge; pass `padTo` (the longest key's length) to align a block of rows.
17
17
  */
18
- export function formatKeyValue(key, value, options) {
19
- const indent = options?.indent ?? 2;
20
- const padTo = options?.padTo ?? 0;
18
+ export function formatKeyValue({ key, value, indent = 2, padTo = 0, }) {
21
19
  const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1);
22
20
  return `${' '.repeat(indent)}${styleText('dim', paddedKey)} ${value}`;
23
21
  }
@@ -681,5 +681,5 @@
681
681
  ]
682
682
  }
683
683
  },
684
- "version": "0.8.0"
684
+ "version": "0.8.1"
685
685
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/workflow-cli",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Command-line tool for deploying, inspecting, and administering Sanity workflow definitions and instances.",
5
5
  "keywords": [
6
6
  "cli",
@@ -53,15 +53,15 @@
53
53
  "diff": "^9.0.0",
54
54
  "log-symbols": "^7.0.1",
55
55
  "ora": "^9.4.0",
56
- "@sanity/workflow-engine": "0.12.0"
56
+ "@sanity/workflow-engine": "0.13.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/diff": "^8.0.0",
60
60
  "@types/node": "^24.12.4",
61
61
  "oclif": "^4.23.16",
62
62
  "vitest": "^4.1.8",
63
- "@sanity/workflow-engine-test": "0.7.0",
64
- "@sanity/workflow-examples": "0.2.0"
63
+ "@sanity/workflow-engine-test": "0.8.0",
64
+ "@sanity/workflow-examples": "0.3.0"
65
65
  },
66
66
  "oclif": {
67
67
  "bin": "sanity-workflows",