@sanity/workflow-engine 0.26.0 → 0.28.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 +65 -0
- package/dist/_chunks-cjs/invariants.cjs +35 -356
- package/dist/_chunks-es/invariants.js +36 -317
- package/dist/define.cjs +1 -6
- package/dist/define.d.cts +45 -15
- package/dist/define.d.ts +45 -15
- package/dist/define.js +2 -7
- package/dist/index.cjs +389 -88
- package/dist/index.d.cts +56 -15
- package/dist/index.d.ts +56 -15
- package/dist/index.js +302 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
# @sanity/workflow-engine
|
|
2
2
|
|
|
3
|
+
## 0.28.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5050b06: **BREAKING:** Authoring a deployment now types through `WorkflowDeploymentInput` / `WorkflowConfigInput`, which require `expectedMinReaderModel` as the current reader-floor literal. Passing a parsed `WorkflowDeployment` / `WorkflowConfig` (where the floor is optional/unverified) into `defineWorkflowConfig` or `defineWorkflows` is a type error — share the authored deployment object instead of `config.deployments[n]`. The same pin shows up on the blueprint resource: `EditorialWorkflowsResource.deployment` and `parseWorkflowDeployment`'s return type are now `AcknowledgedDeployment` (floor asserted to the current literal), so a hand-constructed provider resource must carry that literal too.
|
|
8
|
+
|
|
9
|
+
At runtime, config parse no longer rejects a stale or missing floor on an untargeted deployment. Only deployment-scoped commands assert, and only the selected deployment: definition deploy, `--check` / `--dry-run`, blueprint provision, `start`, `definition delete`, and `definition diff` (the last is a deployment-scoped read). Instance-id commands (`fire-action`, `abort`, `set-stage`, `reset-activity`) deliberately do not assert — they resolve by instance id, not by a declared deployment's acknowledgement — so an unacknowledged floor does not stop those instance commits.
|
|
10
|
+
|
|
11
|
+
Upgrade TypeScript configs and blueprint manifests that fed a parsed deployment into those helpers: keep a shared authored object (`satisfies WorkflowDeploymentInput`) for both `defineWorkflowConfig` and `defineWorkflows`, and give any hand-built `EditorialWorkflowsResource` the current floor literal on `deployment`. Existing configs that already acknowledge the current floor keep working at runtime; a stale selected deployment still fails with `ReaderModelAcknowledgementError` (headline + short steps; the CLI renders it through the clean styled-error path).
|
|
12
|
+
|
|
13
|
+
**Docs impact:** Update the CLI / blueprint authoring examples and the prerelease reader-floor guidance so they name `WorkflowConfigInput` / `WorkflowDeploymentInput` / `AcknowledgedDeployment`, forbid passing `config.deployments[n]` into `defineWorkflows`, state that instance-id writes are outside the acknowledgement gate, and stop implying whole-config parse-time rejection for commands that never select a deployment.
|
|
14
|
+
|
|
15
|
+
- a044ba5: Report a Studio start whose auto-advance lost a write race as the finished run
|
|
16
|
+
it is. Starting a workflow from the Studio while another runtime (a deployed
|
|
17
|
+
Function, a script) drives the same instance could hand the editor a warning
|
|
18
|
+
toast — "'…' started but didn't finish", with a raw `unexpected revision ID`
|
|
19
|
+
mutation error under it — for a run that had in fact completed: the Studio's own
|
|
20
|
+
auto-advance simply lost the write race to whoever committed the equivalent move
|
|
21
|
+
first. That toast was indistinguishable from a genuinely stuck workflow. The
|
|
22
|
+
Studio now confirms the completed start instead of warning about it.
|
|
23
|
+
|
|
24
|
+
Narrow by design: only a lost revision race whose run is already complete is
|
|
25
|
+
reclassified. A conflict that left the run in flight, and any other reason an
|
|
26
|
+
auto-advance failed, still surface as before.
|
|
27
|
+
|
|
28
|
+
`@sanity/workflow-engine` exports `isRevisionConflict`, the predicate that tells
|
|
29
|
+
a lost optimistic-locking race from a real error, so integrations can classify
|
|
30
|
+
one without matching on message text. It answers on the error alone, so narrow
|
|
31
|
+
to a single rev-guarded write before asking — a bare 409 also covers a create-id
|
|
32
|
+
collision.
|
|
33
|
+
|
|
34
|
+
**No upgrade action required.** Engine behaviour is unchanged and the new export
|
|
35
|
+
is additive.
|
|
36
|
+
|
|
37
|
+
**Docs impact:** `isRevisionConflict` is new public engine surface, added to the
|
|
38
|
+
errors section of `docs/reference.md` alongside the concurrency errors it
|
|
39
|
+
classifies; mirror that entry wherever published reference material lists the
|
|
40
|
+
engine's error helpers.
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- 1e4a5da: When two runtimes both try to create the same stage guard after each observing it absent — for example a deployed Function reacting to instance commits while a script advances the same workflow — the losing create no longer aborts guard deploy. It falls through to the body-reconciling patch the existing-document path already uses, and writes its own resolution of the guard's conditions rather than assuming the two are identical. Concurrent deploys of an already-present guard were already a patch and are unchanged.
|
|
45
|
+
|
|
46
|
+
**No upgrade action required.**
|
|
47
|
+
|
|
48
|
+
**Docs impact: None** — `docs/reference.md` already documents `deployStageGuards` as an idempotent upsert.
|
|
49
|
+
|
|
50
|
+
## 0.27.0
|
|
51
|
+
|
|
52
|
+
### Minor Changes
|
|
53
|
+
|
|
54
|
+
- 780cf93: **BREAKING:** `ProjectMember.roles` is now `readonly {name: string; title?: string}[]` instead of `readonly string[]`, and `AssigneeStack`'s `roles` prop takes the same records rather than name strings. This affects integrations that build or read `ProjectMember` rows directly, or render `AssigneeStack` themselves. Read `role.name` wherever a role name was read before, and pass role records rather than name strings into `projectMemberRow` and `AssigneeStack`. Until migrated, TypeScript fails at those sites. Integrations using `useProjectMembers` from `@sanity/workflow-sdk` or the Studio plugin's own equivalent get the new shape without changes, because the adapters own the projection.
|
|
55
|
+
|
|
56
|
+
A role now reads as its project title rather than its machine name — `Administrator`, not `administrator` — wherever one is shown: picker role rows, assignee badges, the instance-snapshot pills, and the hover hint naming a collapsed assignee cluster. A square role avatar takes its letters from the title too, so `Blueprints Deployer` reads as `BD` where `blueprints-deployer` could only ever give one letter; its colour still derives from the machine name, so retitling a project role keeps the role's colour. A role whose membership record carries no title — or a title that is only whitespace, which counts as none — and a role named by an assignee that no current member holds, both still read as the machine name. `roleLabel`, `roleLabelFor`, and `memberRoleFor` are exported for callers rendering their own role labels.
|
|
57
|
+
|
|
58
|
+
Both people-pickers now say who a person is, so choosing one no longer requires knowing the org chart. Search matches role names and titles as well as display name and email, so typing a role narrows the list to its holders. Members are also listed by display name rather than in the order the host supplied them, which was the sequence people joined the project — expect the list order to change, and on a large project to become usable. And hovering a row previews the account behind the name — a larger avatar badged with the person's identity provider, their email, and the roles they hold. Marks exist for Google, GitHub, and `saml-`-prefixed deployments; any other provider badges nothing rather than showing an empty circle. That is what tells two members sharing a display name apart, which no role label could. Rows share one tooltip delay group, so the wait to open is paid once and moving along the list swaps the preview rather than waiting again at each row.
|
|
59
|
+
|
|
60
|
+
`MemberAvatar` takes an optional `loginProvider` to badge the provider mark onto its corner. `ProjectMember` gains optional `loginProvider` and `isCurrentUser`; both are populated by the adapters, so integrations using `useProjectMembers` get them without changes.
|
|
61
|
+
|
|
62
|
+
Neither picker ranks or groups its member rows by the roles those members hold, and neither claims who is eligible for an activity. (The roles-and-members picker still lists selectable roles above the members, as it did before.) An activity has no role gate; only an action does, and a manually fired action's `roles` is folded into its `filter` at desugar, so the accepted set is not readable from a deployed definition. A picker has nothing to derive eligibility from, and a ranked list would assert something no engine or Content Lake check backs.
|
|
63
|
+
|
|
64
|
+
Stored values are unchanged. An assignee still persists the machine role name, and titles are display only.
|
|
65
|
+
|
|
66
|
+
**Docs impact:** Update the `@sanity/workflow-components` member-selection reference for the `ProjectMember.roles` and `AssigneeStack` `roles` shapes and the `roleLabel` / `roleLabelFor` / `memberRoleFor` exports (done in this package's README). Any concept or guide page showing a role in Studio UI should show titles rather than machine names, and the assignment guide should state that a picker never restricts or ranks who may be assigned. Add the `ProjectMember.roles` migration to the release notes.
|
|
67
|
+
|
|
3
68
|
## 0.26.0
|
|
4
69
|
|
|
5
70
|
## 0.25.0
|
|
@@ -20,6 +20,41 @@ function _interopNamespaceCompat(e) {
|
|
|
20
20
|
|
|
21
21
|
var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
|
|
22
22
|
|
|
23
|
+
function isCascadeFired(action) {
|
|
24
|
+
return action.when !== void 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function deriveActivityKind(activity) {
|
|
28
|
+
if (activity.target !== void 0) return "manual";
|
|
29
|
+
const actions = activity.actions ?? [];
|
|
30
|
+
return actions.some(a => !isCascadeFired(a)) ? "user" : actions.some(a => (a.effects ?? []).length > 0 || a.spawn !== void 0) ? "service" : actions.length > 0 ? "receive" : "script";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function deriveExecutorClassification(activity) {
|
|
34
|
+
if (activity.target !== void 0) return "off-system";
|
|
35
|
+
const actions = activity.actions ?? [], cascadeFired = actions.filter(isCascadeFired).length;
|
|
36
|
+
return cascadeFired === actions.length ? "autonomous" : cascadeFired === 0 ? "interactive" : "hybrid";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function driverKind(actor) {
|
|
40
|
+
return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function errorMessage(err) {
|
|
44
|
+
return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function rethrowWithContext(err, context) {
|
|
48
|
+
throw new Error(`${context}: ${errorMessage(err)}`, {
|
|
49
|
+
cause: err
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function andConditions(parts) {
|
|
54
|
+
const present = parts.filter(p => p !== void 0);
|
|
55
|
+
if (present.length !== 0) return present.length === 1 ? present[0] : present.map(p => `(${p})`).join(" && ");
|
|
56
|
+
}
|
|
57
|
+
|
|
23
58
|
class WorkflowError extends Error {
|
|
24
59
|
kind;
|
|
25
60
|
constructor(kind, message, options) {
|
|
@@ -93,322 +128,6 @@ function effectNotFoundMessage(args) {
|
|
|
93
128
|
return args.settled.status === "cancelled" ? `${base} — it was cancelled at ${args.settled.ranAt}${cause}` : `${base} — it already settled "${args.settled.status}" at ${args.settled.ranAt}${cause}`;
|
|
94
129
|
}
|
|
95
130
|
|
|
96
|
-
function errorMessage(err) {
|
|
97
|
-
return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function rethrowWithContext(err, context) {
|
|
101
|
-
throw new Error(`${context}: ${errorMessage(err)}`, {
|
|
102
|
-
cause: err
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
107
|
-
|
|
108
|
-
function terminalState(instance) {
|
|
109
|
-
return instance.abortedAt !== void 0 ? "aborted" : instance.completedAt !== void 0 ? "completed" : "in-flight";
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function isUnprimed(instance) {
|
|
113
|
-
return instance.stages.length === 0 && terminalState(instance) === "in-flight";
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function parseDefinitionSnapshotValue(instance) {
|
|
117
|
-
try {
|
|
118
|
-
return normalizeLegacyActivityRequirements(JSON.parse(instance.definitionSnapshot));
|
|
119
|
-
} catch (err) {
|
|
120
|
-
rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function normalizeLegacyActivityRequirements(value) {
|
|
125
|
-
for (const stage of arrayMember(value, "stages")) for (const activity of arrayMember(stage, "activities")) normalizeLegacyRequirementMap(activity);
|
|
126
|
-
return value;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function arrayMember(value, key) {
|
|
130
|
-
if (typeof value != "object" || value === null) return [];
|
|
131
|
-
const member = value[key];
|
|
132
|
-
return Array.isArray(member) ? member : [];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function normalizeLegacyRequirementMap(value) {
|
|
136
|
-
if (typeof value != "object" || value === null) return;
|
|
137
|
-
const activity = value, requirements = activity.requirements;
|
|
138
|
-
typeof requirements != "object" || requirements === null || Array.isArray(requirements) || (activity.requirements = Object.entries(requirements).map(([name, query]) => ({
|
|
139
|
-
type: "groq",
|
|
140
|
-
name: name,
|
|
141
|
-
query: query
|
|
142
|
-
})));
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function parseDefinitionSnapshot(instance) {
|
|
146
|
-
return parseDefinitionSnapshotValue(instance);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function parentRef(instance) {
|
|
150
|
-
return instance.ancestors.at(-1);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const DATA_MODEL_VERSION = 5, DATA_MODEL_MIN_READER = 4, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/editorial-workflows/prerelease";
|
|
154
|
-
|
|
155
|
-
class ReaderModelAcknowledgementError extends WorkflowError {
|
|
156
|
-
code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
|
|
157
|
-
expectedMinReaderModel;
|
|
158
|
-
engineMinReaderModel=DATA_MODEL_MIN_READER;
|
|
159
|
-
engineModelVersion=DATA_MODEL_VERSION;
|
|
160
|
-
documentationUrl=READER_MODEL_ROLLOUT_URL;
|
|
161
|
-
constructor(expectedMinReaderModel, context = "Deployment") {
|
|
162
|
-
const expected = expectedMinReaderModel === void 0 ? "missing" : String(expectedMinReaderModel);
|
|
163
|
-
super("reader-model-acknowledgement", `${context} expected reader floor ${expected}; the installed engine requires acknowledgement ${DATA_MODEL_MIN_READER}. Do not change the acknowledgement yet: accepting ${DATA_MODEL_MIN_READER} authorizes this engine to write documents that older readers will refuse. Upgrade every Studio, CLI, MCP server, Function, and other runtime that reads engine-owned documents; verify that rollout in every environment sharing the workflow resource; then change the literal in deployment configuration and deploy the writer. Rollout guide: ${READER_MODEL_ROLLOUT_URL}`),
|
|
164
|
-
this.name = "ReaderModelAcknowledgementError", this.expectedMinReaderModel = expectedMinReaderModel;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function assertReaderModelAcknowledgement(expectedMinReaderModel, context) {
|
|
169
|
-
if (typeof expectedMinReaderModel != "number" || !Number.isFinite(expectedMinReaderModel) || !Number.isInteger(expectedMinReaderModel) || expectedMinReaderModel < 0 || expectedMinReaderModel !== DATA_MODEL_MIN_READER) throw new ReaderModelAcknowledgementError(expectedMinReaderModel, context);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
|
|
173
|
-
id: "governed-model-stamps",
|
|
174
|
-
introducedInModel: 1,
|
|
175
|
-
minReaderModel: 0,
|
|
176
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
177
|
-
compatibility: "additive",
|
|
178
|
-
applicability: "unconditional",
|
|
179
|
-
summary: "Definition and instance documents carry model provenance and reader-floor stamps."
|
|
180
|
-
}), Object.freeze({
|
|
181
|
-
id: "subject-field-kind",
|
|
182
|
-
introducedInModel: 2,
|
|
183
|
-
minReaderModel: 0,
|
|
184
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
185
|
-
compatibility: "additive",
|
|
186
|
-
applicability: "detectable",
|
|
187
|
-
summary: "A workflow-level subject field identifies the document a workflow is about."
|
|
188
|
-
}), Object.freeze({
|
|
189
|
-
id: "typed-scalar-choice-lists",
|
|
190
|
-
introducedInModel: 2,
|
|
191
|
-
minReaderModel: 2,
|
|
192
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
193
|
-
compatibility: "reader-floor",
|
|
194
|
-
applicability: "detectable",
|
|
195
|
-
summary: "Scalar fields may constrain writes to a persisted typed choice list."
|
|
196
|
-
}), Object.freeze({
|
|
197
|
-
id: "action-semantics",
|
|
198
|
-
introducedInModel: 2,
|
|
199
|
-
minReaderModel: 0,
|
|
200
|
-
documentTypes: Object.freeze([ "definition" ]),
|
|
201
|
-
compatibility: "additive",
|
|
202
|
-
applicability: "detectable",
|
|
203
|
-
summary: "Ordinary actions may carry a closed bag of advisory workflow semantics."
|
|
204
|
-
}), Object.freeze({
|
|
205
|
-
id: "inclusive-scalar-bounds",
|
|
206
|
-
introducedInModel: 2,
|
|
207
|
-
minReaderModel: 2,
|
|
208
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
209
|
-
compatibility: "reader-floor",
|
|
210
|
-
applicability: "detectable",
|
|
211
|
-
summary: "String, text, and number values may carry persisted inclusive bounds."
|
|
212
|
-
}), Object.freeze({
|
|
213
|
-
id: "progress-field-kind",
|
|
214
|
-
introducedInModel: 3,
|
|
215
|
-
minReaderModel: 0,
|
|
216
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
217
|
-
compatibility: "additive",
|
|
218
|
-
applicability: "detectable",
|
|
219
|
-
summary: "A progress field kind carries application-defined 0–100 completion."
|
|
220
|
-
}), Object.freeze({
|
|
221
|
-
id: "effect-claim-tokens",
|
|
222
|
-
introducedInModel: 3,
|
|
223
|
-
minReaderModel: 0,
|
|
224
|
-
documentTypes: Object.freeze([ "instance" ]),
|
|
225
|
-
compatibility: "additive",
|
|
226
|
-
applicability: "detectable",
|
|
227
|
-
summary: "Pending-effect claims carry an exact-claim token gating mid-dispatch state reports."
|
|
228
|
-
}), Object.freeze({
|
|
229
|
-
id: "classified-principal-ids",
|
|
230
|
-
introducedInModel: 4,
|
|
231
|
-
minReaderModel: 4,
|
|
232
|
-
documentTypes: Object.freeze([ "instance" ]),
|
|
233
|
-
compatibility: "reader-floor",
|
|
234
|
-
applicability: "unconditional",
|
|
235
|
-
summary: "Principal ids are namespace-classified: actor and assignee writes carry the account-global user id only, and readers resolve legacy project-scoped ids through the prefix classifier at the instance read funnel."
|
|
236
|
-
}), Object.freeze({
|
|
237
|
-
id: "readiness-requirements",
|
|
238
|
-
introducedInModel: 4,
|
|
239
|
-
minReaderModel: 4,
|
|
240
|
-
documentTypes: Object.freeze([ "definition" ]),
|
|
241
|
-
compatibility: "reader-floor",
|
|
242
|
-
applicability: "detectable",
|
|
243
|
-
summary: "Start and activity readiness use named polymorphic requirement arrays."
|
|
244
|
-
}), Object.freeze({
|
|
245
|
-
id: "due-date-field-kinds",
|
|
246
|
-
introducedInModel: 5,
|
|
247
|
-
minReaderModel: 0,
|
|
248
|
-
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
249
|
-
compatibility: "additive",
|
|
250
|
-
applicability: "detectable",
|
|
251
|
-
summary: "Due-date field kinds (dueDate, dueDatetime) mark a level deadline, elevated aliases of date/datetime carrying the same stored value."
|
|
252
|
-
}) ]);
|
|
253
|
-
|
|
254
|
-
function recordOf(value) {
|
|
255
|
-
return value !== null && typeof value == "object" && !Array.isArray(value) ? value : void 0;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function recordsAt(record, key) {
|
|
259
|
-
const value = record[key];
|
|
260
|
-
return Array.isArray(value) ? value.map(recordOf).filter(item => item !== void 0) : [];
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function nestedFieldEntries(entries) {
|
|
264
|
-
return entries.flatMap(entry => [ entry, ...nestedFieldEntries(recordsAt(entry, "fields")), ...nestedFieldEntries(recordsAt(entry, "of")) ]);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function parsedDefinitionSnapshot(root) {
|
|
268
|
-
if (typeof root.definitionSnapshot == "string") return recordOf(parseDefinitionSnapshotValue({
|
|
269
|
-
_id: typeof root._id == "string" ? root._id : "<unknown instance>",
|
|
270
|
-
definitionSnapshot: root.definitionSnapshot
|
|
271
|
-
}));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function persistedFieldEntries(document) {
|
|
275
|
-
const root = recordOf(document);
|
|
276
|
-
if (root === void 0) return [];
|
|
277
|
-
const snapshot = parsedDefinitionSnapshot(root), roots = snapshot === void 0 ? [ root ] : [ root, snapshot ], stages = roots.flatMap(candidate => recordsAt(candidate, "stages")), activities = stages.flatMap(stage => recordsAt(stage, "activities")), actions = activities.flatMap(activity => recordsAt(activity, "actions")), effects = actions.flatMap(action => recordsAt(action, "effects"));
|
|
278
|
-
return nestedFieldEntries([ ...roots.flatMap(candidate => recordsAt(candidate, "fields")), ...stages.flatMap(stage => recordsAt(stage, "fields")), ...activities.flatMap(activity => recordsAt(activity, "fields")), ...actions.flatMap(action => recordsAt(action, "params")), ...effects.flatMap(effect => recordsAt(effect, "outputs")) ]);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function hasChoiceList(document) {
|
|
282
|
-
return persistedFieldEntries(document).some(entry => {
|
|
283
|
-
const options = recordOf(entry.options);
|
|
284
|
-
return options !== void 0 && Array.isArray(options.list);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function hasFieldKind(document, kind) {
|
|
289
|
-
return persistedFieldEntries(document).some(entry => entry.type === kind || entry._type === kind);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function hasActionSemantics(document) {
|
|
293
|
-
const root = recordOf(document);
|
|
294
|
-
return root === void 0 ? !1 : recordsAt(root, "stages").flatMap(stage => recordsAt(stage, "activities")).flatMap(activity => recordsAt(activity, "actions")).some(action => Array.isArray(action.semantics));
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function hasScalarValidation(document) {
|
|
298
|
-
return persistedFieldEntries(document).some(entry => {
|
|
299
|
-
const validation = recordOf(entry.validation);
|
|
300
|
-
return typeof validation?.min == "number" || typeof validation?.max == "number";
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function hasClaimTokens(document) {
|
|
305
|
-
const root = recordOf(document);
|
|
306
|
-
return root === void 0 ? !1 : recordsAt(root, "pendingEffects").some(entry => {
|
|
307
|
-
const claim = recordOf(entry.claim);
|
|
308
|
-
return claim !== void 0 && typeof claim.claimToken == "string";
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function hasReadinessRequirements(document) {
|
|
313
|
-
const root = recordOf(document);
|
|
314
|
-
return root === void 0 ? !1 : Array.isArray(recordOf(root.start)?.requirements) ? !0 : recordsAt(root, "stages").some(stage => recordsAt(stage, "activities").some(activity => Array.isArray(activity.requirements)));
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const featureDetectors = {
|
|
318
|
-
"governed-model-stamps": () => !0,
|
|
319
|
-
"subject-field-kind": document => hasFieldKind(document, "subject"),
|
|
320
|
-
"typed-scalar-choice-lists": hasChoiceList,
|
|
321
|
-
"action-semantics": hasActionSemantics,
|
|
322
|
-
"inclusive-scalar-bounds": hasScalarValidation,
|
|
323
|
-
"progress-field-kind": document => hasFieldKind(document, "progress"),
|
|
324
|
-
"effect-claim-tokens": hasClaimTokens,
|
|
325
|
-
"classified-principal-ids": () => !0,
|
|
326
|
-
"readiness-requirements": hasReadinessRequirements,
|
|
327
|
-
"due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime")
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
function requiredModelFeatures(documentType, document) {
|
|
331
|
-
return DATA_MODEL_CHANGES.filter(change => change.documentTypes.some(candidate => candidate === documentType) && featureDetectors[change.id](document));
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function requiredReaderModel(documentType, document) {
|
|
335
|
-
return Math.max(0, ...requiredModelFeatures(documentType, document).map(change => change.minReaderModel));
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function modelStampFor(args) {
|
|
339
|
-
return {
|
|
340
|
-
modelVersion: DATA_MODEL_VERSION,
|
|
341
|
-
minReaderModel: Math.max(DATA_MODEL_MIN_READER, args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function fieldTreeShape(value) {
|
|
346
|
-
if (Array.isArray(value)) return value.map(fieldTreeShape);
|
|
347
|
-
if (value === null) return "null";
|
|
348
|
-
if (typeof value == "object") {
|
|
349
|
-
const record = value;
|
|
350
|
-
return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
|
|
351
|
-
}
|
|
352
|
-
return typeof value;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function modelVersionOf(doc) {
|
|
356
|
-
const stamp = doc.modelVersion;
|
|
357
|
-
return typeof stamp == "number" ? stamp : 0;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function minReaderModelOf(doc) {
|
|
361
|
-
const floor = doc.minReaderModel;
|
|
362
|
-
return typeof floor == "number" ? floor : modelVersionOf(doc);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
class ModelVersionAheadError extends WorkflowError {
|
|
366
|
-
documentId;
|
|
367
|
-
documentModelVersion;
|
|
368
|
-
requiredReaderModel;
|
|
369
|
-
engineModelVersion;
|
|
370
|
-
constructor(args) {
|
|
371
|
-
super("model-version-ahead", `Document "${args.documentId}" was written by engine data model ${args.documentModelVersion} and requires a reader at model ${args.requiredReaderModel} or newer; this engine reads up to model ${DATA_MODEL_VERSION}. Upgrade @sanity/workflow-engine to a version that understands it.`),
|
|
372
|
-
this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
|
|
373
|
-
this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function assertReadableModel(doc) {
|
|
378
|
-
const documentReaderModel = minReaderModelOf(doc);
|
|
379
|
-
if (documentReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
|
|
380
|
-
documentId: doc._id,
|
|
381
|
-
documentModelVersion: modelVersionOf(doc),
|
|
382
|
-
requiredReaderModel: documentReaderModel
|
|
383
|
-
});
|
|
384
|
-
return doc;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function isCascadeFired(action) {
|
|
388
|
-
return action.when !== void 0;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function deriveActivityKind(activity) {
|
|
392
|
-
if (activity.target !== void 0) return "manual";
|
|
393
|
-
const actions = activity.actions ?? [];
|
|
394
|
-
return actions.some(a => !isCascadeFired(a)) ? "user" : actions.some(a => (a.effects ?? []).length > 0 || a.spawn !== void 0) ? "service" : actions.length > 0 ? "receive" : "script";
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function deriveExecutorClassification(activity) {
|
|
398
|
-
if (activity.target !== void 0) return "off-system";
|
|
399
|
-
const actions = activity.actions ?? [], cascadeFired = actions.filter(isCascadeFired).length;
|
|
400
|
-
return cascadeFired === actions.length ? "autonomous" : cascadeFired === 0 ? "interactive" : "hybrid";
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function driverKind(actor) {
|
|
404
|
-
return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function andConditions(parts) {
|
|
408
|
-
const present = parts.filter(p => p !== void 0);
|
|
409
|
-
if (present.length !== 0) return present.length === 1 ? present[0] : present.map(p => `(${p})`).join(" && ");
|
|
410
|
-
}
|
|
411
|
-
|
|
412
131
|
const KNOWN_SCHEMES = /* @__PURE__ */ new Set([ "dataset", "canvas", "media-library", "dashboard" ]), KNOWN_SCHEMES_TEXT = [ ...KNOWN_SCHEMES ].join(", ");
|
|
413
132
|
|
|
414
133
|
class VersionSpecificDatasetGdrError extends Error {
|
|
@@ -4068,12 +3787,6 @@ exports.CONDITION_VARS = CONDITION_VARS;
|
|
|
4068
3787
|
|
|
4069
3788
|
exports.ContractViolationError = ContractViolationError;
|
|
4070
3789
|
|
|
4071
|
-
exports.DATA_MODEL_CHANGES = DATA_MODEL_CHANGES;
|
|
4072
|
-
|
|
4073
|
-
exports.DATA_MODEL_MIN_READER = DATA_MODEL_MIN_READER;
|
|
4074
|
-
|
|
4075
|
-
exports.DATA_MODEL_VERSION = DATA_MODEL_VERSION;
|
|
4076
|
-
|
|
4077
3790
|
exports.DEFAULT_TRANSITION_WHEN = DEFAULT_TRANSITION_WHEN;
|
|
4078
3791
|
|
|
4079
3792
|
exports.DOCUMENT_VALUE_PERMISSIONS = DOCUMENT_VALUE_PERMISSIONS;
|
|
@@ -4116,20 +3829,14 @@ exports.IsoTimestamp = IsoTimestamp;
|
|
|
4116
3829
|
|
|
4117
3830
|
exports.MUTATION_GUARD_ACTIONS = MUTATION_GUARD_ACTIONS;
|
|
4118
3831
|
|
|
4119
|
-
exports.ModelVersionAheadError = ModelVersionAheadError;
|
|
4120
|
-
|
|
4121
3832
|
exports.NonEmptyString = NonEmptyString;
|
|
4122
3833
|
|
|
4123
3834
|
exports.PersistedDocShapeError = PersistedDocShapeError;
|
|
4124
3835
|
|
|
4125
|
-
exports.READER_MODEL_ROLLOUT_URL = READER_MODEL_ROLLOUT_URL;
|
|
4126
|
-
|
|
4127
3836
|
exports.RESERVED_CONDITION_VARS = RESERVED_CONDITION_VARS;
|
|
4128
3837
|
|
|
4129
3838
|
exports.RESOURCE_ALIAS_NAME_SOURCE = RESOURCE_ALIAS_NAME_SOURCE;
|
|
4130
3839
|
|
|
4131
|
-
exports.ReaderModelAcknowledgementError = ReaderModelAcknowledgementError;
|
|
4132
|
-
|
|
4133
3840
|
exports.START_FILTER_VARS = START_FILTER_VARS;
|
|
4134
3841
|
|
|
4135
3842
|
exports.START_REQUIREMENT_VARS = START_REQUIREMENT_VARS;
|
|
@@ -4144,8 +3851,6 @@ exports.VersionSpecificDatasetGdrError = VersionSpecificDatasetGdrError;
|
|
|
4144
3851
|
|
|
4145
3852
|
exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
|
|
4146
3853
|
|
|
4147
|
-
exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
|
|
4148
|
-
|
|
4149
3854
|
exports.WorkflowConfigSchema = WorkflowConfigSchema;
|
|
4150
3855
|
|
|
4151
3856
|
exports.WorkflowError = WorkflowError;
|
|
@@ -4154,10 +3859,6 @@ exports.actorFulfillsRole = actorFulfillsRole;
|
|
|
4154
3859
|
|
|
4155
3860
|
exports.andConditions = andConditions;
|
|
4156
3861
|
|
|
4157
|
-
exports.assertReadableModel = assertReadableModel;
|
|
4158
|
-
|
|
4159
|
-
exports.assertReaderModelAcknowledgement = assertReaderModelAcknowledgement;
|
|
4160
|
-
|
|
4161
3862
|
exports.checkWorkflowInvariants = checkWorkflowInvariants;
|
|
4162
3863
|
|
|
4163
3864
|
exports.choiceValueIssues = choiceValueIssues;
|
|
@@ -4198,8 +3899,6 @@ exports.evaluatePredicates = evaluatePredicates;
|
|
|
4198
3899
|
|
|
4199
3900
|
exports.extractDocumentId = extractDocumentId;
|
|
4200
3901
|
|
|
4201
|
-
exports.fieldTreeShape = fieldTreeShape;
|
|
4202
|
-
|
|
4203
3902
|
exports.fieldValueSchemas = fieldValueSchemas;
|
|
4204
3903
|
|
|
4205
3904
|
exports.firstCarriedGlobalId = firstCarriedGlobalId;
|
|
@@ -4254,24 +3953,10 @@ exports.isTodoListItem = isTodoListItem;
|
|
|
4254
3953
|
|
|
4255
3954
|
exports.isUnevaluable = isUnevaluable;
|
|
4256
3955
|
|
|
4257
|
-
exports.isUnprimed = isUnprimed;
|
|
4258
|
-
|
|
4259
3956
|
exports.labelFor = labelFor;
|
|
4260
3957
|
|
|
4261
3958
|
exports.lakePrincipalId = lakePrincipalId;
|
|
4262
3959
|
|
|
4263
|
-
exports.minReaderModelOf = minReaderModelOf;
|
|
4264
|
-
|
|
4265
|
-
exports.modelStampFor = modelStampFor;
|
|
4266
|
-
|
|
4267
|
-
exports.modelVersionOf = modelVersionOf;
|
|
4268
|
-
|
|
4269
|
-
exports.parentRef = parentRef;
|
|
4270
|
-
|
|
4271
|
-
exports.parseDefinitionSnapshot = parseDefinitionSnapshot;
|
|
4272
|
-
|
|
4273
|
-
exports.parseDefinitionSnapshotValue = parseDefinitionSnapshotValue;
|
|
4274
|
-
|
|
4275
3960
|
exports.parseFieldValue = parseFieldValue;
|
|
4276
3961
|
|
|
4277
3962
|
exports.parseGdr = parseGdr;
|
|
@@ -4304,10 +3989,6 @@ exports.releaseDocId = releaseDocId;
|
|
|
4304
3989
|
|
|
4305
3990
|
exports.releaseRef = releaseRef;
|
|
4306
3991
|
|
|
4307
|
-
exports.requiredModelFeatures = requiredModelFeatures;
|
|
4308
|
-
|
|
4309
|
-
exports.requiredReaderModel = requiredReaderModel;
|
|
4310
|
-
|
|
4311
3992
|
exports.resourceAliasesToMap = resourceAliasesToMap;
|
|
4312
3993
|
|
|
4313
3994
|
exports.resourceFromGdrUri = resourceFromGdrUri;
|
|
@@ -4332,8 +4013,6 @@ exports.startKindOf = startKindOf;
|
|
|
4332
4013
|
|
|
4333
4014
|
exports.tagScopeFilter = tagScopeFilter;
|
|
4334
4015
|
|
|
4335
|
-
exports.terminalState = terminalState;
|
|
4336
|
-
|
|
4337
4016
|
exports.toBareId = toBareId;
|
|
4338
4017
|
|
|
4339
4018
|
exports.toPhysicalGdr = toPhysicalGdr;
|