@sanity/workflow-engine 0.6.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -136,13 +136,6 @@ export declare type Assignee =
136
136
  role: string;
137
137
  };
138
138
 
139
- /** The assignees recorded for a task in a stage — the runtime half of the
140
- * "who is this waiting on" answer. Empty when the task is unassigned. */
141
- export declare function assigneesOf(
142
- stage: StageEntry | undefined,
143
- taskName: string,
144
- ): Assignee[];
145
-
146
139
  /**
147
140
  * Persisted document and effect-queue `_type` discriminators.
148
141
  */
@@ -771,6 +764,33 @@ declare const AuthoringStageSchema: v.StrictObjectSchema<
771
764
  >,
772
765
  undefined
773
766
  >;
767
+ /**
768
+ * Readiness gates, by name — conditions over the rendered scope that must
769
+ * hold for the task to be *executable*, orthogonal to `filter`
770
+ * (visibility). An unmet requirement keeps the task visible but disables
771
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
772
+ * all must hold (any-of lives inside one condition). Advisory like every
773
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
774
+ * and guards (content-write locks).
775
+ */
776
+ requirements: v.OptionalSchema<
777
+ v.RecordSchema<
778
+ v.SchemaWithPipe<
779
+ readonly [
780
+ v.StringSchema<undefined>,
781
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
782
+ ]
783
+ >,
784
+ v.SchemaWithPipe<
785
+ readonly [
786
+ v.StringSchema<undefined>,
787
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
788
+ ]
789
+ >,
790
+ undefined
791
+ >,
792
+ undefined
793
+ >;
774
794
  /**
775
795
  * Auto-completion condition — evaluated at activation and on every
776
796
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -2591,6 +2611,33 @@ declare const AuthoringTaskSchema: v.StrictObjectSchema<
2591
2611
  >,
2592
2612
  undefined
2593
2613
  >;
2614
+ /**
2615
+ * Readiness gates, by name — conditions over the rendered scope that must
2616
+ * hold for the task to be *executable*, orthogonal to `filter`
2617
+ * (visibility). An unmet requirement keeps the task visible but disables
2618
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
2619
+ * all must hold (any-of lives inside one condition). Advisory like every
2620
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
2621
+ * and guards (content-write locks).
2622
+ */
2623
+ requirements: v.OptionalSchema<
2624
+ v.RecordSchema<
2625
+ v.SchemaWithPipe<
2626
+ readonly [
2627
+ v.StringSchema<undefined>,
2628
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
2629
+ ]
2630
+ >,
2631
+ v.SchemaWithPipe<
2632
+ readonly [
2633
+ v.StringSchema<undefined>,
2634
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
2635
+ ]
2636
+ >,
2637
+ undefined
2638
+ >,
2639
+ undefined
2640
+ >;
2594
2641
  /**
2595
2642
  * Auto-completion condition — evaluated at activation and on every
2596
2643
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -4157,6 +4204,41 @@ declare const AuthoringWorkflowSchema: v.StrictObjectSchema<
4157
4204
  >,
4158
4205
  undefined
4159
4206
  >;
4207
+ /**
4208
+ * Readiness gates, by name — conditions over the rendered scope that must
4209
+ * hold for the task to be *executable*, orthogonal to `filter`
4210
+ * (visibility). An unmet requirement keeps the task visible but disables
4211
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
4212
+ * all must hold (any-of lives inside one condition). Advisory like every
4213
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
4214
+ * and guards (content-write locks).
4215
+ */
4216
+ requirements: v.OptionalSchema<
4217
+ v.RecordSchema<
4218
+ v.SchemaWithPipe<
4219
+ readonly [
4220
+ v.StringSchema<undefined>,
4221
+ v.MinLengthAction<
4222
+ string,
4223
+ 1,
4224
+ "must be a non-empty string"
4225
+ >,
4226
+ ]
4227
+ >,
4228
+ v.SchemaWithPipe<
4229
+ readonly [
4230
+ v.StringSchema<undefined>,
4231
+ v.MinLengthAction<
4232
+ string,
4233
+ 1,
4234
+ "must be a non-empty string"
4235
+ >,
4236
+ ]
4237
+ >,
4238
+ undefined
4239
+ >,
4240
+ undefined
4241
+ >;
4160
4242
  /**
4161
4243
  * Auto-completion condition — evaluated at activation and on every
4162
4244
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -6021,6 +6103,11 @@ declare const AuthoringWorkflowSchema: v.StrictObjectSchema<
6021
6103
  description?: string | undefined;
6022
6104
  activation?: "auto" | "manual" | undefined;
6023
6105
  filter?: string | undefined;
6106
+ requirements?:
6107
+ | {
6108
+ [x: string]: string;
6109
+ }
6110
+ | undefined;
6024
6111
  completeWhen?: string | undefined;
6025
6112
  failWhen?: string | undefined;
6026
6113
  ops?:
@@ -6769,6 +6856,16 @@ export declare function datasetResourceParts(id: string): {
6769
6856
  dataset: string;
6770
6857
  };
6771
6858
 
6859
+ /**
6860
+ * The perspective a content read falls back to when the instance has no
6861
+ * explicit release perspective: `drafts` overlay published (Sanity
6862
+ * draft-precedence), so an unpublished or in-flight subject is what the engine
6863
+ * — and the reactive adapters — evaluate, never a published-only read that
6864
+ * silently misses the draft. The single home for the draft-precedence default;
6865
+ * every content read uses `instance.perspective ?? DEFAULT_CONTENT_PERSPECTIVE`.
6866
+ */
6867
+ export declare const DEFAULT_CONTENT_PERSPECTIVE = "drafts";
6868
+
6772
6869
  /**
6773
6870
  * Default LoggerFactory — writes through the global `console`. Exposed
6774
6871
  * so callers outside `createEngine` (drive scripts, the
@@ -6892,7 +6989,6 @@ export declare interface DiagnoseInput {
6892
6989
  instance: Pick<
6893
6990
  WorkflowInstance,
6894
6991
  | "currentStage"
6895
- | "stages"
6896
6992
  | "completedAt"
6897
6993
  | "abortedAt"
6898
6994
  | "effectHistory"
@@ -6901,6 +6997,7 @@ export declare interface DiagnoseInput {
6901
6997
  >;
6902
6998
  tasks: TaskEvaluation[];
6903
6999
  transitions: TransitionEvaluation[];
7000
+ assignees: Record<string, Assignee[]>;
6904
7001
  }
6905
7002
 
6906
7003
  /** Narrow a full {@link WorkflowEvaluation} to the {@link DiagnoseInput} the
@@ -6912,17 +7009,20 @@ export declare function diagnoseInputFromEvaluation(
6912
7009
  /**
6913
7010
  * Classify an instance. Terminal states win first; then the genuine stuck
6914
7011
  * causes (nothing advances on its own or via a normal action); then `waiting`
6915
- * (an action is available — healthy); else `progressing` (a transition is
6916
- * already satisfied and will cascade).
7012
+ * (an action is available — healthy); then `blocked` (an active task held by
7013
+ * an unmet requirement healthy, not yet actionable); else `progressing` (a
7014
+ * transition is already satisfied and will cascade).
6917
7015
  */
6918
7016
  export declare function diagnoseInstance(input: DiagnoseInput): Diagnosis;
6919
7017
 
6920
- /** The `workflow.diagnose` result — the verdict plus the evaluation it was
7018
+ /** The `workflow.diagnose` result — the verdict, the structured remediations
7019
+ * that would unstick it (empty unless `stuck`), and the evaluation it was
6921
7020
  * derived from, so a consumer can render the supporting evidence (current
6922
7021
  * stage tasks + transitions) without a second projection. */
6923
7022
  export declare interface DiagnoseResult {
6924
7023
  evaluation: WorkflowEvaluation;
6925
7024
  diagnosis: Diagnosis;
7025
+ remediations: SuggestedRemediation[];
6926
7026
  }
6927
7027
 
6928
7028
  export declare type Diagnosis =
@@ -6935,6 +7035,12 @@ export declare type Diagnosis =
6935
7035
  assignees: Assignee[];
6936
7036
  actions: string[];
6937
7037
  }
7038
+ | {
7039
+ state: "blocked";
7040
+ task: string;
7041
+ requirements: string[];
7042
+ assignees: Assignee[];
7043
+ }
6938
7044
  | {
6939
7045
  state: "completed";
6940
7046
  at: string;
@@ -7004,6 +7110,18 @@ export declare type DisabledReason =
7004
7110
  | {
7005
7111
  kind: "instance-completed";
7006
7112
  completedAt: string;
7113
+ }
7114
+ | {
7115
+ /**
7116
+ * The task's declared {@link Task.requirements} aren't all satisfied —
7117
+ * the readiness axis. The task is visible and the actor authorized, but
7118
+ * its own preconditions don't hold yet. `unmetRequirements` names the
7119
+ * failing keys so a consumer can disable the affirmative control and say
7120
+ * which precondition is outstanding. Distinct from `filter-failed`
7121
+ * (visibility/authorization) and `mutation-guard-denied` (content-write).
7122
+ */
7123
+ kind: "requirements-unmet";
7124
+ unmetRequirements: string[];
7007
7125
  };
7008
7126
 
7009
7127
  export declare interface DispatchResult {
@@ -8372,12 +8490,6 @@ export declare interface OpAppliedSummary {
8372
8490
  resolved?: Record<string, unknown>;
8373
8491
  }
8374
8492
 
8375
- /** The instance's current, not-yet-exited {@link StageEntry}, if it has one —
8376
- * the engine's canonical {@link findOpenStageEntry}, over the diagnosis input. */
8377
- export declare function openStage(
8378
- instance: Pick<WorkflowInstance, "currentStage" | "stages">,
8379
- ): StageEntry | undefined;
8380
-
8381
8493
  export declare interface OperationArgs {
8382
8494
  client: WorkflowClient;
8383
8495
  /** Engine-scope environment partition — required. */
@@ -8534,6 +8646,30 @@ export declare interface ReleaseRef extends GlobalDocumentReference {
8534
8646
  releaseName: string;
8535
8647
  }
8536
8648
 
8649
+ /**
8650
+ * The remediations that would unstick a diagnosis — the *what to do about it*
8651
+ * half. Empty unless the instance is `stuck`: a `waiting` instance advances on
8652
+ * its own next action (see `availableActions`), and terminal or `progressing`
8653
+ * ones need nothing. Each verb is flagged
8654
+ * {@link SuggestedRemediation.available} from {@link RUNNABLE_VERBS}.
8655
+ */
8656
+ export declare function remediationsFor(
8657
+ diagnosis: Diagnosis,
8658
+ ): SuggestedRemediation[];
8659
+
8660
+ /**
8661
+ * A verb that would unstick a {@link StuckCause} — the *what to do about it*
8662
+ * half of a diagnosis. Surface-neutral identifiers; a consumer maps each to
8663
+ * its own command or button. `retry-effect`, `drain-effects` and `reset-task`
8664
+ * are not yet callable engine operations — see {@link SuggestedRemediation.available}.
8665
+ */
8666
+ export declare type RemediationVerb =
8667
+ | "retry-effect"
8668
+ | "drain-effects"
8669
+ | "reset-task"
8670
+ | "set-stage"
8671
+ | "abort";
8672
+
8537
8673
  /**
8538
8674
  * Resolve the engine's `WorkflowAccess` for a client. Override wins
8539
8675
  * outright; otherwise both halves are fetched in parallel and
@@ -9365,6 +9501,33 @@ declare const StoredStageSchema: v.StrictObjectSchema<
9365
9501
  >,
9366
9502
  undefined
9367
9503
  >;
9504
+ /**
9505
+ * Readiness gates, by name — conditions over the rendered scope that must
9506
+ * hold for the task to be *executable*, orthogonal to `filter`
9507
+ * (visibility). An unmet requirement keeps the task visible but disables
9508
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
9509
+ * all must hold (any-of lives inside one condition). Advisory like every
9510
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
9511
+ * and guards (content-write locks).
9512
+ */
9513
+ requirements: v.OptionalSchema<
9514
+ v.RecordSchema<
9515
+ v.SchemaWithPipe<
9516
+ readonly [
9517
+ v.StringSchema<undefined>,
9518
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
9519
+ ]
9520
+ >,
9521
+ v.SchemaWithPipe<
9522
+ readonly [
9523
+ v.StringSchema<undefined>,
9524
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
9525
+ ]
9526
+ >,
9527
+ undefined
9528
+ >,
9529
+ undefined
9530
+ >;
9368
9531
  /**
9369
9532
  * Auto-completion condition — evaluated at activation and on every
9370
9533
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -10609,6 +10772,33 @@ declare const StoredTaskSchema: v.StrictObjectSchema<
10609
10772
  >,
10610
10773
  undefined
10611
10774
  >;
10775
+ /**
10776
+ * Readiness gates, by name — conditions over the rendered scope that must
10777
+ * hold for the task to be *executable*, orthogonal to `filter`
10778
+ * (visibility). An unmet requirement keeps the task visible but disables
10779
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
10780
+ * all must hold (any-of lives inside one condition). Advisory like every
10781
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
10782
+ * and guards (content-write locks).
10783
+ */
10784
+ requirements: v.OptionalSchema<
10785
+ v.RecordSchema<
10786
+ v.SchemaWithPipe<
10787
+ readonly [
10788
+ v.StringSchema<undefined>,
10789
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
10790
+ ]
10791
+ >,
10792
+ v.SchemaWithPipe<
10793
+ readonly [
10794
+ v.StringSchema<undefined>,
10795
+ v.MinLengthAction<string, 1, "must be a non-empty string">,
10796
+ ]
10797
+ >,
10798
+ undefined
10799
+ >,
10800
+ undefined
10801
+ >;
10612
10802
  /**
10613
10803
  * Auto-completion condition — evaluated at activation and on every
10614
10804
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -11658,6 +11848,17 @@ export declare type StuckCause =
11658
11848
  }
11659
11849
  | {
11660
11850
  kind: "no-transition-fires";
11851
+ }
11852
+ /**
11853
+ * Every task resolved, but an exit transition's filter is *unevaluable*
11854
+ * (GROQ `null` — a referenced operand is missing/unreadable), so selection
11855
+ * halts. Unlike {@link StuckCause} `no-transition-fires` this is recoverable:
11856
+ * the cascade re-fires once the operand resolves (e.g. the subject is
11857
+ * published or the field is filled). Carries the undecidable transitions.
11858
+ */
11859
+ | {
11860
+ kind: "transition-unevaluable";
11861
+ transitions: string[];
11661
11862
  };
11662
11863
 
11663
11864
  /**
@@ -11773,6 +11974,17 @@ declare const SubworkflowsSchema: v.StrictObjectSchema<
11773
11974
  undefined
11774
11975
  >;
11775
11976
 
11977
+ export declare interface SuggestedRemediation {
11978
+ verb: RemediationVerb;
11979
+ /** True iff a consumer can run this as an engine operation today. `false`
11980
+ * marks a remediation we can name but not yet execute — a planned verb, or
11981
+ * an operational step (re-running the effect drainer). */
11982
+ available: boolean;
11983
+ /** Surface-neutral one-line rationale — no color or terminal symbols; a
11984
+ * consumer decorates it for its own surface. */
11985
+ rationale: string;
11986
+ }
11987
+
11776
11988
  /**
11777
11989
  * The engine's read-partition invariant as a GROQ predicate: a document is
11778
11990
  * visible when its `tag` equals the caller's `$tag` param. The single
@@ -11811,6 +12023,13 @@ export declare interface TaskEntry {
11811
12023
  filterEvaluation?: {
11812
12024
  at: string;
11813
12025
  truthy: boolean;
12026
+ /**
12027
+ * The filter evaluated to GROQ `null` — a referenced operand was missing
12028
+ * or incomparable, so it couldn't be decided. The engine fails closed
12029
+ * (keeps the task in scope) rather than reading this as a deliberate
12030
+ * `false`; `truthy` is `false` here but the task is NOT skipped.
12031
+ */
12032
+ unevaluable?: boolean;
11814
12033
  detail?: string;
11815
12034
  };
11816
12035
  /**
@@ -11829,6 +12048,13 @@ export declare interface TaskEvaluation {
11829
12048
  status: TaskStatus;
11830
12049
  /** Whether this task is the current actor's responsibility right now. */
11831
12050
  pendingOnActor: boolean;
12051
+ /**
12052
+ * The task's unmet {@link Task.requirements}, by name — present iff at least
12053
+ * one is unmet. The task's own readiness summary: a consumer can explain why
12054
+ * the whole task is gated without inspecting each action (every action also
12055
+ * carries the matching `requirements-unmet` `disabledReason`).
12056
+ */
12057
+ unmetRequirements?: string[];
11832
12058
  actions: ActionEvaluation[];
11833
12059
  }
11834
12060
 
@@ -11845,8 +12071,17 @@ export declare type Transition = v.InferOutput<typeof StoredTransitionSchema>;
11845
12071
 
11846
12072
  export declare interface TransitionEvaluation {
11847
12073
  transition: Transition;
11848
- /** Result of evaluating the transition's filter at read time. */
12074
+ /** Whether the transition's filter is definitively satisfied at read time. */
11849
12075
  filterSatisfied: boolean;
12076
+ /**
12077
+ * The filter evaluated to GROQ `null` — a referenced operand was missing or
12078
+ * incomparable, so it couldn't be decided. `filterSatisfied` is `false`, but
12079
+ * this is a *recoverable hold*, not a deliberate `false`: the engine halts
12080
+ * selection (it won't fall through to a later transition) and the cascade
12081
+ * re-fires once the operand resolves. Lets a diagnosis tell an undecidable
12082
+ * hold apart from a genuine routing dead-end.
12083
+ */
12084
+ unevaluable: boolean;
11850
12085
  }
11851
12086
 
11852
12087
  export declare type TransitionOp = v.InferOutput<
@@ -12269,6 +12504,7 @@ export declare interface WorkflowClient {
12269
12504
  },
12270
12505
  >(
12271
12506
  doc: T,
12507
+ options?: WorkflowCommitOptions,
12272
12508
  ) => Promise<T>;
12273
12509
  /**
12274
12510
  * Build a multi-document transaction. All operations succeed or fail
@@ -12321,6 +12557,21 @@ export declare interface WorkflowClient {
12321
12557
  }) => Promise<T>;
12322
12558
  }
12323
12559
 
12560
+ export declare interface WorkflowCommitOptions {
12561
+ /**
12562
+ * When the mutation becomes visible to subsequent queries. The engine
12563
+ * reads its own writes back through GROQ (`fetch`) — a freshly-spawned
12564
+ * child listed by id, a just-deployed definition — so single-document
12565
+ * writes (`create`, `patch().commit()`) commit `'sync'`: the change is
12566
+ * query-visible before the call resolves and the next read can't miss it.
12567
+ * `@sanity/client` already defaults to `'sync'`, but the engine states it
12568
+ * rather than depend on a transport default a consumer's client config
12569
+ * could change underneath it. (Transactions ride that same default — see
12570
+ * {@link WorkflowTransaction.commit}.)
12571
+ */
12572
+ visibility?: WorkflowVisibility;
12573
+ }
12574
+
12324
12575
  export declare type WorkflowDefinition = v.InferOutput<
12325
12576
  typeof WorkflowDefinitionSchema
12326
12577
  >;
@@ -12463,6 +12714,41 @@ declare const WorkflowDefinitionSchema: v.StrictObjectSchema<
12463
12714
  >,
12464
12715
  undefined
12465
12716
  >;
12717
+ /**
12718
+ * Readiness gates, by name — conditions over the rendered scope that must
12719
+ * hold for the task to be *executable*, orthogonal to `filter`
12720
+ * (visibility). An unmet requirement keeps the task visible but disables
12721
+ * its actions with a `requirements-unmet` verdict naming the unmet keys;
12722
+ * all must hold (any-of lives inside one condition). Advisory like every
12723
+ * engine gate — the lake still enforces. Distinct from ACL (authorization)
12724
+ * and guards (content-write locks).
12725
+ */
12726
+ requirements: v.OptionalSchema<
12727
+ v.RecordSchema<
12728
+ v.SchemaWithPipe<
12729
+ readonly [
12730
+ v.StringSchema<undefined>,
12731
+ v.MinLengthAction<
12732
+ string,
12733
+ 1,
12734
+ "must be a non-empty string"
12735
+ >,
12736
+ ]
12737
+ >,
12738
+ v.SchemaWithPipe<
12739
+ readonly [
12740
+ v.StringSchema<undefined>,
12741
+ v.MinLengthAction<
12742
+ string,
12743
+ 1,
12744
+ "must be a non-empty string"
12745
+ >,
12746
+ ]
12747
+ >,
12748
+ undefined
12749
+ >,
12750
+ undefined
12751
+ >;
12466
12752
  /**
12467
12753
  * Auto-completion condition — evaluated at activation and on every
12468
12754
  * cascade; truthy flips the task to `done` with a system actor. On a
@@ -13747,6 +14033,11 @@ declare const WorkflowDefinitionSchema: v.StrictObjectSchema<
13747
14033
  description?: string | undefined;
13748
14034
  activation?: "auto" | "manual" | undefined;
13749
14035
  filter?: string | undefined;
14036
+ requirements?:
14037
+ | {
14038
+ [x: string]: string;
14039
+ }
14040
+ | undefined;
13750
14041
  completeWhen?: string | undefined;
13751
14042
  failWhen?: string | undefined;
13752
14043
  ops?:
@@ -14225,7 +14516,7 @@ export declare interface WorkflowPatch {
14225
14516
  setIfMissing: (props: Record<string, unknown>) => WorkflowPatch;
14226
14517
  unset: (paths: string[]) => WorkflowPatch;
14227
14518
  ifRevisionId: (rev: string) => WorkflowPatch;
14228
- commit: () => Promise<SanityDocument>;
14519
+ commit: (options?: WorkflowCommitOptions) => Promise<SanityDocument>;
14229
14520
  }
14230
14521
 
14231
14522
  /**
@@ -14326,7 +14617,20 @@ export declare interface WorkflowTransaction {
14326
14617
  * carry this shape.
14327
14618
  */
14328
14619
  delete: (id: string) => WorkflowTransaction;
14620
+ /**
14621
+ * Commit the batch. No options — a transaction rides the client's *default*
14622
+ * `visibility`: `'sync'` in `@sanity/client` (so a post-spawn `listInstances`
14623
+ * GROQ sees freshly-created children), immediate in the in-memory test fake.
14624
+ * This is a standing engine assumption — a consumer that configured an
14625
+ * `async` default would make spawn read-backs racy. Single-document writes
14626
+ * state `'sync'` explicitly via {@link WorkflowCommitOptions}; transactions
14627
+ * can't (yet) — the test fake's commit options don't carry `visibility`, and
14628
+ * per repo convention that gap is closed upstream in the fake, not worked
14629
+ * around here.
14630
+ */
14329
14631
  commit: () => Promise<unknown>;
14330
14632
  }
14331
14633
 
14634
+ export declare type WorkflowVisibility = "sync" | "async" | "deferred";
14635
+
14332
14636
  export {};