@sanity/workflow-engine 0.18.0 → 0.20.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 +112 -0
- package/DATAMODEL.md +53 -0
- package/dist/_chunks-cjs/invariants.cjs +138 -28
- package/dist/_chunks-es/invariants.js +138 -28
- package/dist/define.d.cts +232 -30
- package/dist/define.d.ts +232 -30
- package/dist/index.cjs +2586 -1930
- package/dist/index.d.cts +545 -75
- package/dist/index.d.ts +545 -75
- package/dist/index.js +2607 -1967
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,117 @@
|
|
|
1
1
|
# @sanity/workflow-engine
|
|
2
2
|
|
|
3
|
+
## 0.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ab5e454: Export project-user response validation for host adapters that consume Sanity project-user APIs.
|
|
8
|
+
- e3122cc: New `instancesGuardQuery(instanceIds)` — the set-shaped guard filter (`sourceInstanceId in $instanceIds`, ordered by `_id`) reactive adapters subscribe as one shared live query per resource for every co-mounted session. `instanceGuardQuery(id)` delegates to it, so the per-instance and per-set reads share one filter definition; its compiled query string changes accordingly (same rows selected).
|
|
9
|
+
- 7c4dd86: Reduce guard lifecycle request volume and chunk orphan cleanup transactions.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- bce09fc: Restore automatic effect request tags while preserving concrete handler client APIs, backed by native request-prefix support in the test client.
|
|
14
|
+
- efb4cd9: Discriminator validation now reports an unknown field kind, field source, or op value expression at its exact `type` path instead of collapsing to "Invalid type: Expected Object but received Object". The schemas route on `type` rather than trying indistinguishable object arms in a plain union, so newer syntax used with an older engine identifies the unsupported value and the installed engine's valid options.
|
|
15
|
+
- 46b0285: **BREAKING:** Replace manually declared document applicability with automatic
|
|
16
|
+
discovery from deployed first-class subject fields.
|
|
17
|
+
|
|
18
|
+
Update Studio configuration as follows:
|
|
19
|
+
- `mappings` is optional. Omit it when every applicable definition declares a
|
|
20
|
+
first-class subject. A mapping may customize an automatically discovered
|
|
21
|
+
`(docType, definition)` binding or explicitly register a definition modeled
|
|
22
|
+
with a plain `doc.ref` instead of a first-class subject.
|
|
23
|
+
- Multiple workflows may target one document type. Use one mapping row for each
|
|
24
|
+
distinct `(docType, definition)` pair. An exact duplicate pair is a
|
|
25
|
+
configuration error rather than a last-row-wins override.
|
|
26
|
+
- Remove the top-level `autoStart` map or function. Put `autoStart: true` on
|
|
27
|
+
each mapping row that should start automatically. Configure workspace-specific
|
|
28
|
+
behavior in that workspace's mapping rows. This also works for explicitly
|
|
29
|
+
registered definitions using the `doc.ref` field named `subject` convention.
|
|
30
|
+
- Replace `workflowDefaultDocumentNode({mappings})` with
|
|
31
|
+
`workflowDefaultDocumentNode()`.
|
|
32
|
+
|
|
33
|
+
Before:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
structureTool({defaultDocumentNode: workflowDefaultDocumentNode({mappings})})
|
|
37
|
+
workflowStudioPlugin({
|
|
38
|
+
tag: 'production',
|
|
39
|
+
mappings,
|
|
40
|
+
autoStart: {article: ['article-review', 'legal-review']},
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
structureTool({defaultDocumentNode: workflowDefaultDocumentNode()})
|
|
48
|
+
workflowStudioPlugin({
|
|
49
|
+
tag: 'production',
|
|
50
|
+
mappings: [
|
|
51
|
+
{
|
|
52
|
+
docType: 'article',
|
|
53
|
+
definition: 'article-review',
|
|
54
|
+
label: 'Article review',
|
|
55
|
+
autoStart: true,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
docType: 'article',
|
|
59
|
+
definition: 'legal-review',
|
|
60
|
+
label: 'Legal review',
|
|
61
|
+
autoStart: true,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Also remove all per-schema Editorial Workflows preview wiring:
|
|
68
|
+
- Remove `components: {preview: WorkflowStagePreview}`.
|
|
69
|
+
- Remove `_id` added only for Editorial Workflows from `preview.select` and
|
|
70
|
+
stop passing it through `preview.prepare`.
|
|
71
|
+
- Remove imports of `WorkflowStagePreview`; that component is no longer a
|
|
72
|
+
public package export.
|
|
73
|
+
- Remove imports of `mappingForDocType` and `workflowDocTypes`; effective
|
|
74
|
+
mappings are resolved inside the plugin and those host-side helpers are no
|
|
75
|
+
longer exported.
|
|
76
|
+
- Keep each schema's native inferred or custom preview unchanged. The plugin
|
|
77
|
+
now installs preview middleware itself, consumes the document identity Studio
|
|
78
|
+
already supplies, and delegates title, subtitle, media, and custom preview
|
|
79
|
+
composition through `renderDefault`.
|
|
80
|
+
|
|
81
|
+
Workflow stage pills appear on Studio surfaces that invoke preview middleware,
|
|
82
|
+
including reference and array-item previews. Custom preview components that
|
|
83
|
+
replace Studio's layout must render the `status` prop they receive. Studio's
|
|
84
|
+
Structure document-list rows bypass both plugin and schema preview middleware,
|
|
85
|
+
so they do not show workflow stage pills. Workflow status remains available in
|
|
86
|
+
the document form, footer badge, Workflows view, and Workflows tool.
|
|
87
|
+
|
|
88
|
+
Document-reference GDRs for dataset content now reject stored draft IDs
|
|
89
|
+
(`drafts.<id>`) and Content Release version IDs
|
|
90
|
+
(`versions.<release>.<id>`). Use the stable document ID in the GDR and select
|
|
91
|
+
the draft or release through workflow perspective instead. The validation error
|
|
92
|
+
includes the corresponding stable ID so CLI and API callers can correct the
|
|
93
|
+
input before an unresolvable workflow instance is created.
|
|
94
|
+
|
|
95
|
+
Previously persisted draft/version GDRs remain invalid workflow identities.
|
|
96
|
+
Read-side Studio displays now tolerate them by showing the raw URI instead of
|
|
97
|
+
crashing, and query-sourced occurrences are discarded through the existing
|
|
98
|
+
fail-soft field-resolution path. Correct existing data by starting a new
|
|
99
|
+
instance with the stable document ID; perspective selects the desired draft or
|
|
100
|
+
release content.
|
|
101
|
+
|
|
102
|
+
- 8a76c67: Add incremental reactive-session document feeds, coalesce watched-document emissions, and reuse discovery derivations and Studio layout output. Deleted watched documents now leave the held snapshot instead of remaining as stale evaluation input. The component test runner also has aggregate-suite timeout headroom.
|
|
103
|
+
- 7e8f459: Batch snapshot hydration to reduce watched-document read latency.
|
|
104
|
+
- 98e488e: Stabilize Editorial Workflows discovery subscriptions and limit full reactive sessions to surfaces that need live evaluation.
|
|
105
|
+
|
|
106
|
+
## 0.19.0
|
|
107
|
+
|
|
108
|
+
### Minor Changes
|
|
109
|
+
|
|
110
|
+
- 24b11dd: Effect handlers can report intermediate field state mid-dispatch. The handler context gains `ctx.commitOps({ops, idempotencyKey})` — field ops only, validated like completion ops, executed through the new `workflow.commitEffectOps` verb as a standard engine commit (gate, history, idempotency ledger, compare-and-swap persist, guard refresh, cascade) — and `ctx.setProgress(field, value)`, sugar that coalesces pending absolute sets per field. Calls enqueue synchronously into a bounded per-dispatch queue (depth 32, 200 commits per dispatch) that serializes commits in call order; overflow throws synchronously at the call site, and settlement drains the queue before completion so an un-awaited failed report still fails the effect. Pending-effect claims now carry a `claimToken` minted on every claim and takeover: a report from an expired, released, or superseded dispatch is rejected before writing (`StaleEffectClaimError`), and a successful report renews the claim's lease. Completion semantics are unchanged and remain claim-blind first-writer-wins. Each report emits the sampled `Editorial Workflows Effect State Reported` telemetry event (adoption signal, max one per minute). Persisted-model note: the claim token rides persisted model 3 additively (see `DATAMODEL.md`).
|
|
111
|
+
- 2005ab7: Action evaluations carry a `firing` consequence (`FiringConsequence` `{exitsStage, transition?}`): what firing the action would do to the flow right now, computed by replaying the fire in memory through the same machinery a real commit runs — ops, the triggered fixpoint, then transition selection — against the projection's state. The answer is total for the current state and conditional on the commit landing; it is omitted where the replay can't be faithful (caller params, a spawn's dataset-driven fan-out in the hop, a terminal instance, a stage visit whose entries have diverged from the pinned definition) and on cascade-fired actions. Advisory, like every derived verdict. Wrong-shape `field.updateWhere` aborts (a merge resolving to a non-object; a resolved merge carrying reserved row keys) now throw the typed `FieldValueShapeError` on the commit path, joining the other wrong-shape write aborts.
|
|
112
|
+
- 24b11dd: Add the `progress` field kind: an elevated `number` carrying application-defined 0–100 completion, so Studio and SDK surfaces can identify and elevate it without guessing which number field means "how far along". Every non-null value must be finite and within 0–100 inclusive (fractions allowed), enforced by one value schema at every write boundary — declaration seeds, start values, edits, ops, nested sub-fields, and effect outputs. Choice lists are rejected on the kind at authoring time. Mints persisted model 3 additively (see `DATAMODEL.md`).
|
|
113
|
+
- 59b40fd: **BREAKING:** a deployment's `name` is now its identity in `defineWorkflowConfig`: enforced unique across the config and constrained to the same grammar as `tag` (lowercase letters, digits, dashes — free text like "My cool workflow" is rejected). Tags may now repeat across deployments; the enforced storage invariant is instead the `(workflowResource, tag)` pair — two deployments sharing both are rejected with each entry named, since they would write into the same partition and fight over definition versions.
|
|
114
|
+
|
|
3
115
|
## 0.18.0
|
|
4
116
|
|
|
5
117
|
### Minor Changes
|
package/DATAMODEL.md
CHANGED
|
@@ -445,6 +445,59 @@ Spread-based mutation copies preserve `validation` across every full persist;
|
|
|
445
445
|
the derived floor prevents older writers from reaching constrained model-2
|
|
446
446
|
instances.
|
|
447
447
|
|
|
448
|
+
### Model 3 — the `progress` field kind + effect claim tokens (reader floor: 2)
|
|
449
|
+
|
|
450
|
+
Two additive growths ship together as model 3 — model 2 was released (npm
|
|
451
|
+
`@sanity/workflow-engine@0.18.0`) before either landed, so they mint the next
|
|
452
|
+
version rather than amending a shipped one. Neither raises the reader floor;
|
|
453
|
+
the writer maximum stays 2.
|
|
454
|
+
|
|
455
|
+
**The `progress` field kind.** An elevated `number` (same stored numeric
|
|
456
|
+
value) carrying application-defined 0–100 completion, so surfaces can
|
|
457
|
+
identify and elevate it without guessing which number field means "how far
|
|
458
|
+
along". Every non-null value is finite and within 0–100 inclusive (fractions
|
|
459
|
+
allowed), enforced at every write boundary by the kind's one value schema.
|
|
460
|
+
Model 2's `validation` tree composes with it as NARROWING only: each declared
|
|
461
|
+
bound must itself sit within 0–100, gated at authoring. Both stamped trees
|
|
462
|
+
grow the value:
|
|
463
|
+
|
|
464
|
+
- **Definition tree** — `fields[].type` (and nested `FieldShape.type`)
|
|
465
|
+
admits `'progress'`.
|
|
466
|
+
- **Instance tree** — resolved `fields[]` entries admit `_type: 'progress'`
|
|
467
|
+
(the same arm shape as `number`).
|
|
468
|
+
|
|
469
|
+
Manifest feature: `progress-field-kind` (definition + instance, additive,
|
|
470
|
+
detectable, floor 0).
|
|
471
|
+
|
|
472
|
+
Would an old reader misread the kind (rule 5)? No — the same loud refusal as
|
|
473
|
+
the `subject` kind: an older engine (model 2 and below) that loaded a
|
|
474
|
+
progress-carrying instance fails the strict `fields[]` variant with
|
|
475
|
+
`PersistedDocShapeError` before interpreting anything, and cannot deploy or
|
|
476
|
+
evaluate a progress-declaring definition. No older code path can write a
|
|
477
|
+
progress field while skipping its bound, so there is no silent bypass to
|
|
478
|
+
fence — the kind leaves the floor at 0.
|
|
479
|
+
|
|
480
|
+
**Effect claim tokens.** `pendingEffects[].claim` grows an optional
|
|
481
|
+
`claimToken` — an opaque identifier minted fresh on every claim and
|
|
482
|
+
takeover. It is the exact-claim identity a mid-dispatch state report
|
|
483
|
+
(`commitEffectOps`) must present: `effectKey` cannot serve because a lease
|
|
484
|
+
takeover keeps the same key. A successful report also renews the claim's
|
|
485
|
+
`leaseExpiresAt` in the same commit.
|
|
486
|
+
|
|
487
|
+
Manifest feature: `effect-claim-tokens` (instance, additive, detectable,
|
|
488
|
+
floor 0).
|
|
489
|
+
|
|
490
|
+
Tolerant read: an older engine's claim parse admits unknown keys, so the
|
|
491
|
+
token rides through untouched and means nothing to it — older claim
|
|
492
|
+
semantics (presence + lease) are unchanged. Round-trip survival: full
|
|
493
|
+
persists derive from the complete loaded post-state, so an older writer
|
|
494
|
+
carries the token along; the claim-REPLACING writes (takeover, sweep
|
|
495
|
+
release, completion drain) discard it deliberately, which is the token's
|
|
496
|
+
own lifecycle — a report presenting a discarded token is rejected before
|
|
497
|
+
writing, failing closed. A claim without a token (older writer's takeover)
|
|
498
|
+
simply cannot authorise mid-dispatch reports; dispatch and completion are
|
|
499
|
+
unaffected.
|
|
500
|
+
|
|
448
501
|
## Pending governed changes
|
|
449
502
|
|
|
450
503
|
- **`temp.system.guard` → `system.guard`** — the guard doc type's `temp.`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var v = require("valibot"), groqConditionDescribe = require("@sanity/groq-condition-describe"), groqJs = require("groq-js");
|
|
3
|
+
var v = require("valibot"), groqConditionDescribe = require("@sanity/groq-condition-describe"), groqJs = require("groq-js"), idUtils = require("@sanity/id-utils");
|
|
4
4
|
|
|
5
5
|
function _interopNamespaceCompat(e) {
|
|
6
6
|
if (e && typeof e == "object" && "default" in e) return e;
|
|
@@ -129,7 +129,7 @@ function parentRef(instance) {
|
|
|
129
129
|
return instance.ancestors.at(-1);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
const DATA_MODEL_VERSION =
|
|
132
|
+
const DATA_MODEL_VERSION = 3, DATA_MODEL_MIN_READER = 2, READER_MODEL_ROLLOUT_URL = "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
|
|
133
133
|
|
|
134
134
|
class ReaderModelAcknowledgementError extends WorkflowError {
|
|
135
135
|
code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
|
|
@@ -188,6 +188,22 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
|
|
|
188
188
|
compatibility: "reader-floor",
|
|
189
189
|
applicability: "detectable",
|
|
190
190
|
summary: "String, text, and number values may carry persisted inclusive bounds."
|
|
191
|
+
}), Object.freeze({
|
|
192
|
+
id: "progress-field-kind",
|
|
193
|
+
introducedInModel: 3,
|
|
194
|
+
minReaderModel: 0,
|
|
195
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
196
|
+
compatibility: "additive",
|
|
197
|
+
applicability: "detectable",
|
|
198
|
+
summary: "A progress field kind carries application-defined 0–100 completion."
|
|
199
|
+
}), Object.freeze({
|
|
200
|
+
id: "effect-claim-tokens",
|
|
201
|
+
introducedInModel: 3,
|
|
202
|
+
minReaderModel: 0,
|
|
203
|
+
documentTypes: Object.freeze([ "instance" ]),
|
|
204
|
+
compatibility: "additive",
|
|
205
|
+
applicability: "detectable",
|
|
206
|
+
summary: "Pending-effect claims carry an exact-claim token gating mid-dispatch state reports."
|
|
191
207
|
}) ]);
|
|
192
208
|
|
|
193
209
|
function recordOf(value) {
|
|
@@ -224,8 +240,8 @@ function hasChoiceList(document) {
|
|
|
224
240
|
});
|
|
225
241
|
}
|
|
226
242
|
|
|
227
|
-
function
|
|
228
|
-
return persistedFieldEntries(document).some(entry => entry.type ===
|
|
243
|
+
function hasFieldKind(document, kind) {
|
|
244
|
+
return persistedFieldEntries(document).some(entry => entry.type === kind || entry._type === kind);
|
|
229
245
|
}
|
|
230
246
|
|
|
231
247
|
function hasActionSemantics(document) {
|
|
@@ -240,12 +256,22 @@ function hasScalarValidation(document) {
|
|
|
240
256
|
});
|
|
241
257
|
}
|
|
242
258
|
|
|
259
|
+
function hasClaimTokens(document) {
|
|
260
|
+
const root = recordOf(document);
|
|
261
|
+
return root === void 0 ? !1 : recordsAt(root, "pendingEffects").some(entry => {
|
|
262
|
+
const claim = recordOf(entry.claim);
|
|
263
|
+
return claim !== void 0 && typeof claim.claimToken == "string";
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
243
267
|
const featureDetectors = {
|
|
244
268
|
"governed-model-stamps": () => !0,
|
|
245
|
-
"subject-field-kind":
|
|
269
|
+
"subject-field-kind": document => hasFieldKind(document, "subject"),
|
|
246
270
|
"typed-scalar-choice-lists": hasChoiceList,
|
|
247
271
|
"action-semantics": hasActionSemantics,
|
|
248
|
-
"inclusive-scalar-bounds": hasScalarValidation
|
|
272
|
+
"inclusive-scalar-bounds": hasScalarValidation,
|
|
273
|
+
"progress-field-kind": document => hasFieldKind(document, "progress"),
|
|
274
|
+
"effect-claim-tokens": hasClaimTokens
|
|
249
275
|
};
|
|
250
276
|
|
|
251
277
|
function requiredModelFeatures(documentType, document) {
|
|
@@ -332,6 +358,21 @@ function andConditions(parts) {
|
|
|
332
358
|
|
|
333
359
|
const KNOWN_SCHEMES = /* @__PURE__ */ new Set([ "dataset", "canvas", "media-library", "dashboard" ]), KNOWN_SCHEMES_TEXT = [ ...KNOWN_SCHEMES ].join(", ");
|
|
334
360
|
|
|
361
|
+
class VersionSpecificDatasetGdrError extends Error {
|
|
362
|
+
documentId;
|
|
363
|
+
stableDocumentId;
|
|
364
|
+
constructor(documentId, uri) {
|
|
365
|
+
const stableDocumentId = idUtils.getPublishedId(documentId);
|
|
366
|
+
super(`Invalid GDR "${uri}": dataset document ID "${documentId}" identifies a stored draft or release version. Use the stable document ID "${stableDocumentId}" and select drafts/releases through the workflow perspective.`),
|
|
367
|
+
this.name = "VersionSpecificDatasetGdrError", this.documentId = documentId, this.stableDocumentId = stableDocumentId;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function assertStableDatasetDocumentId(documentId, uri) {
|
|
372
|
+
const sanityId = documentId;
|
|
373
|
+
if (!(!idUtils.isDraftId(sanityId) && !idUtils.isVersionId(sanityId))) throw new VersionSpecificDatasetGdrError(documentId, uri);
|
|
374
|
+
}
|
|
375
|
+
|
|
335
376
|
function parseGdr(uri) {
|
|
336
377
|
const colon = uri.indexOf(":");
|
|
337
378
|
if (colon < 0) throw new Error(`Invalid GDR "${uri}": must be a URI of form "<scheme>:<...id-parts>". Known schemes: ${KNOWN_SCHEMES_TEXT}.`);
|
|
@@ -341,11 +382,12 @@ function parseGdr(uri) {
|
|
|
341
382
|
if (parts.some(part => part.length === 0)) throw new Error(`Invalid GDR "${uri}": id parts must be non-empty (no leading, trailing, or doubled ":").`);
|
|
342
383
|
if (scheme === "dataset") {
|
|
343
384
|
if (parts.length !== 3) throw new Error(`Invalid GDR "${uri}": dataset scheme requires <projectId>:<dataset>:<documentId> (3 parts after scheme); got ${parts.length}.`);
|
|
344
|
-
|
|
385
|
+
const documentId = parts[2];
|
|
386
|
+
return assertStableDatasetDocumentId(documentId, uri), {
|
|
345
387
|
scheme: "dataset",
|
|
346
388
|
projectId: parts[0],
|
|
347
389
|
dataset: parts[1],
|
|
348
|
-
documentId:
|
|
390
|
+
documentId: documentId
|
|
349
391
|
};
|
|
350
392
|
}
|
|
351
393
|
if (parts.length !== 2) throw new Error(`Invalid GDR "${uri}": ${scheme} scheme requires <resourceId>:<documentId> (2 parts after scheme); got ${parts.length}.`);
|
|
@@ -365,7 +407,11 @@ function tryParseGdr(uri) {
|
|
|
365
407
|
}
|
|
366
408
|
|
|
367
409
|
function gdrUri(parts) {
|
|
368
|
-
|
|
410
|
+
if (parts.scheme === "dataset") {
|
|
411
|
+
const uri = `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}`;
|
|
412
|
+
return assertStableDatasetDocumentId(parts.documentId, uri), uri;
|
|
413
|
+
}
|
|
414
|
+
return `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
|
|
369
415
|
}
|
|
370
416
|
|
|
371
417
|
function extractDocumentId(gdrUriString) {
|
|
@@ -547,7 +593,13 @@ function asPredicate(validate) {
|
|
|
547
593
|
};
|
|
548
594
|
}
|
|
549
595
|
|
|
550
|
-
const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(validateResourceAliasName), isValidDatasetId = asPredicate(datasetResourceParts)
|
|
596
|
+
const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(validateResourceAliasName), isValidDatasetId = asPredicate(datasetResourceParts);
|
|
597
|
+
|
|
598
|
+
function lakeSegment(label) {
|
|
599
|
+
return v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty(), v__namespace.check(isValidTag, `invalid ${label} — ${LAKE_ID_SEGMENT_GLOSS}`));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const WorkflowResourceSchema = v__namespace.variant("type", [ v__namespace.object({
|
|
551
603
|
type: v__namespace.literal("dataset"),
|
|
552
604
|
id: v__namespace.pipe(NonEmptyString$1, v__namespace.check(isValidDatasetId, 'invalid dataset resource id — expected "<projectId>.<dataset>"'))
|
|
553
605
|
}), v__namespace.object({
|
|
@@ -563,14 +615,46 @@ const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(vali
|
|
|
563
615
|
name: v__namespace.pipe(NonEmptyString$1, v__namespace.check(isValidAliasName, "invalid resource handle name — lowercase letters, digits and dashes only, no leading dash")),
|
|
564
616
|
resource: WorkflowResourceSchema
|
|
565
617
|
}), DefinitionSchema = v__namespace.custom(input => typeof input == "object" && input !== null && typeof input.name == "string", "expected a workflow definition (an object with a string `name`)"), DeploymentSchema = v__namespace.object({
|
|
566
|
-
name:
|
|
618
|
+
name: lakeSegment("name"),
|
|
567
619
|
expectedMinReaderModel: v__namespace.optional(v__namespace.custom(() => !0), void 0),
|
|
568
|
-
tag:
|
|
620
|
+
tag: lakeSegment("tag"),
|
|
569
621
|
workflowResource: WorkflowResourceSchema,
|
|
570
|
-
resourceAliases: v__namespace.optional(v__namespace.pipe(v__namespace.array(ResourceBindingSchema), v__namespace.check(bindings =>
|
|
622
|
+
resourceAliases: v__namespace.optional(v__namespace.pipe(v__namespace.array(ResourceBindingSchema), v__namespace.check(bindings => duplicateHandleMessage(bindings) === void 0, issue => duplicateHandleMessage(issue.input) ?? "duplicate resource handle name"))),
|
|
571
623
|
definitions: v__namespace.pipe(v__namespace.array(DefinitionSchema), v__namespace.minLength(1, "a deployment needs at least one definition"))
|
|
572
|
-
})
|
|
573
|
-
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
function firstDuplicatePair(items, keyOf) {
|
|
627
|
+
const seen = /* @__PURE__ */ new Map;
|
|
628
|
+
for (const item of items) {
|
|
629
|
+
const key = keyOf(item), earlier = seen.get(key);
|
|
630
|
+
if (earlier !== void 0) return [ earlier, item ];
|
|
631
|
+
seen.set(key, item);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function duplicateHandleMessage(bindings) {
|
|
636
|
+
const pair = firstDuplicatePair(bindings, binding => binding.name);
|
|
637
|
+
if (pair !== void 0) return `duplicate resource handle name "${pair[1].name}" — each binding name must be unique within a deployment`;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function duplicateNameMessage(deployments) {
|
|
641
|
+
const pair = firstDuplicatePair(deployments, deployment => deployment.name);
|
|
642
|
+
if (pair !== void 0) return `duplicate deployment name "${pair[1].name}" — each deployment must use a unique name`;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function partitionKey(deployment) {
|
|
646
|
+
return `${resourceGdr(deployment.workflowResource)} ${deployment.tag}`;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function partitionCollisionMessage(deployments) {
|
|
650
|
+
const pair = firstDuplicatePair(deployments, partitionKey);
|
|
651
|
+
if (pair === void 0) return;
|
|
652
|
+
const [first, second] = pair;
|
|
653
|
+
return `deployments "${first.name}" and "${second.name}" share workflow resource + tag "${second.tag}" — both would write into the same partition; change one deployment’s tag or resource`;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const TelemetryLoggerSchema = v__namespace.custom(input => typeof input == "object" && input !== null && typeof input.log == "function", "expected a telemetry logger (an object with a `log` function)"), WorkflowConfigSchema = v__namespace.object({
|
|
657
|
+
deployments: v__namespace.pipe(v__namespace.array(DeploymentSchema), v__namespace.minLength(1, "a config needs at least one deployment"), v__namespace.check(deployments => duplicateNameMessage(deployments) === void 0, issue => duplicateNameMessage(issue.input) ?? "duplicate deployment name"), v__namespace.check(deployments => partitionCollisionMessage(deployments) === void 0, issue => partitionCollisionMessage(issue.input) ?? "duplicate deployment partition")),
|
|
574
658
|
telemetry: v__namespace.optional(TelemetryLoggerSchema)
|
|
575
659
|
});
|
|
576
660
|
|
|
@@ -1724,7 +1808,7 @@ const GdrUriSchema = v__namespace.custom(s => typeof s == "string" && isGdrUri(s
|
|
|
1724
1808
|
}), tolerantObject()({
|
|
1725
1809
|
type: v__namespace.literal("role"),
|
|
1726
1810
|
role: NonEmptyString
|
|
1727
|
-
}) ]), NullableString = v__namespace.union([ v__namespace.null(), v__namespace.string() ]), NullableNumber = v__namespace.union([ v__namespace.null(), v__namespace.number() ]), NullableBoolean = v__namespace.union([ v__namespace.null(), v__namespace.boolean() ]), NullableDateTime = v__namespace.union([ v__namespace.null(), IsoTimestamp ]), NullableDate = v__namespace.union([ v__namespace.null(), v__namespace.pipe(v__namespace.string(), v__namespace.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date")) ]), NullableUrl = NullableString, CHOICE_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number", "url", "date", "datetime", "dateTime" ]);
|
|
1811
|
+
}) ]), NullableString = v__namespace.union([ v__namespace.null(), v__namespace.string() ]), NullableNumber = v__namespace.union([ v__namespace.null(), v__namespace.number() ]), NullableBoolean = v__namespace.union([ v__namespace.null(), v__namespace.boolean() ]), NullableProgress = v__namespace.union([ v__namespace.null(), v__namespace.pipe(v__namespace.number(), v__namespace.finite("progress must be a finite number"), v__namespace.minValue(0, "progress must be at least 0"), v__namespace.maxValue(100, "progress must be at most 100")) ]), NullableDateTime = v__namespace.union([ v__namespace.null(), IsoTimestamp ]), NullableDate = v__namespace.union([ v__namespace.null(), v__namespace.pipe(v__namespace.string(), v__namespace.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date")) ]), NullableUrl = NullableString, CHOICE_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number", "url", "date", "datetime", "dateTime" ]);
|
|
1728
1812
|
|
|
1729
1813
|
function normalizedChoiceKind(kind) {
|
|
1730
1814
|
return kind === "dateTime" ? "datetime" : kind;
|
|
@@ -1756,6 +1840,7 @@ const fieldValueSchemas = {
|
|
|
1756
1840
|
string: NullableString,
|
|
1757
1841
|
text: NullableString,
|
|
1758
1842
|
number: NullableNumber,
|
|
1843
|
+
progress: NullableProgress,
|
|
1759
1844
|
boolean: NullableBoolean,
|
|
1760
1845
|
date: NullableDate,
|
|
1761
1846
|
datetime: NullableDateTime,
|
|
@@ -1780,13 +1865,13 @@ function shapeValueSchema(shape, leaf) {
|
|
|
1780
1865
|
}
|
|
1781
1866
|
|
|
1782
1867
|
function scalarMeasurement(entryType, value) {
|
|
1783
|
-
if (entryType === "number" && typeof value == "number") return value;
|
|
1868
|
+
if ((entryType === "number" || entryType === "progress") && typeof value == "number") return value;
|
|
1784
1869
|
if ((entryType === "string" || entryType === "text") && typeof value == "string") return value.length;
|
|
1785
1870
|
}
|
|
1786
1871
|
|
|
1787
1872
|
function scalarBoundIssue(args) {
|
|
1788
1873
|
const {entryType: entryType, measured: measured, bound: bound, limit: limit} = args;
|
|
1789
|
-
return bound === void 0 || limit === "min" && measured >= bound || limit === "max" && measured <= bound ? void 0 : `${entryType === "number" ? "" : "length "}must be ${limit === "min" ? "greater than or equal to" : "less than or equal to"} ${bound}`;
|
|
1874
|
+
return bound === void 0 || limit === "min" && measured >= bound || limit === "max" && measured <= bound ? void 0 : `${entryType === "number" || entryType === "progress" ? "" : "length "}must be ${limit === "min" ? "greater than or equal to" : "less than or equal to"} ${bound}`;
|
|
1790
1875
|
}
|
|
1791
1876
|
|
|
1792
1877
|
function scalarValidationIssues(args) {
|
|
@@ -1964,7 +2049,15 @@ function groqIdentifier(referencedAs) {
|
|
|
1964
2049
|
}
|
|
1965
2050
|
|
|
1966
2051
|
function picklist(options) {
|
|
1967
|
-
return v__namespace.picklist(options,
|
|
2052
|
+
return v__namespace.picklist(options, invalidOptionMessage(options));
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
function invalidOptionMessage(options) {
|
|
2056
|
+
return `Invalid option: expected one of ${options.map(option => `"${option}"`).join("|")}`;
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
function exhaustiveOptions() {
|
|
2060
|
+
return options => options;
|
|
1968
2061
|
}
|
|
1969
2062
|
|
|
1970
2063
|
function pinned() {
|
|
@@ -1979,12 +2072,12 @@ const LiteralSchema = v__namespace.strictObject({
|
|
|
1979
2072
|
scope: v__namespace.optional(v__namespace.union([ v__namespace.literal("workflow"), v__namespace.literal("stage") ])),
|
|
1980
2073
|
field: NonEmpty,
|
|
1981
2074
|
path: v__namespace.optional(v__namespace.string())
|
|
1982
|
-
}), FieldSourceSchema = v__namespace.
|
|
2075
|
+
}), FieldSourceSchema = v__namespace.variant("type", [ v__namespace.strictObject({
|
|
1983
2076
|
type: v__namespace.literal("input")
|
|
1984
2077
|
}), v__namespace.strictObject({
|
|
1985
2078
|
type: v__namespace.literal("query"),
|
|
1986
2079
|
query: NonEmpty
|
|
1987
|
-
}), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.
|
|
2080
|
+
}), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.variant("type", [ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
|
|
1988
2081
|
type: v__namespace.literal("param"),
|
|
1989
2082
|
param: NonEmpty
|
|
1990
2083
|
}), v__namespace.strictObject({
|
|
@@ -2079,7 +2172,7 @@ function groupMembershipNames(group) {
|
|
|
2079
2172
|
return group === void 0 ? [] : typeof group == "string" ? [ group ] : [ ...group ];
|
|
2080
2173
|
}
|
|
2081
2174
|
|
|
2082
|
-
const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref", "string", "text", "number", "boolean", "date", "datetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), FieldEntryName = groqIdentifier("`$fields.<name>`"), FiniteNumber = v__namespace.pipe(v__namespace.number(), v__namespace.finite("must be finite")), ScalarValidationSchema = v__namespace.pipe(v__namespace.strictObject({
|
|
2175
|
+
const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref", "string", "text", "number", "progress", "boolean", "date", "datetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), AUTHORING_FIELD_SUGAR_KINDS = exhaustiveOptions()([ "claim", "todoList", "notes" ]), AUTHORING_FIELD_KINDS = [ ...FIELD_VALUE_KINDS, ...AUTHORING_FIELD_SUGAR_KINDS ], AuthoringRawFieldKindSchema = v__namespace.picklist(FIELD_VALUE_KINDS, issue => `${invalidOptionMessage(AUTHORING_FIELD_KINDS)} but received ${JSON.stringify(issue.input)}`), FieldEntryName = groqIdentifier("`$fields.<name>`"), FiniteNumber = v__namespace.pipe(v__namespace.number(), v__namespace.finite("must be finite")), ScalarValidationSchema = v__namespace.pipe(v__namespace.strictObject({
|
|
2083
2176
|
min: v__namespace.optional(FiniteNumber),
|
|
2084
2177
|
max: v__namespace.optional(FiniteNumber)
|
|
2085
2178
|
}), v__namespace.check(validation => validation.min !== void 0 || validation.max !== void 0, "declare at least one bound, or omit `validation`"), v__namespace.check(validation => validation.min === void 0 || validation.max === void 0 || validation.min <= validation.max, "`min` must be less than or equal to `max`")), ChoiceOptionsSchema = v__namespace.strictObject({
|
|
@@ -2144,9 +2237,9 @@ function fieldBase(editable, group) {
|
|
|
2144
2237
|
};
|
|
2145
2238
|
}
|
|
2146
2239
|
|
|
2147
|
-
function fieldEntryFields(editable, group) {
|
|
2240
|
+
function fieldEntryFields({editable: editable, group: group, kind: kind = FieldKindSchema}) {
|
|
2148
2241
|
return {
|
|
2149
|
-
type:
|
|
2242
|
+
type: kind,
|
|
2150
2243
|
...fieldBase(editable, group),
|
|
2151
2244
|
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
2152
2245
|
validation: v__namespace.optional(ScalarValidationSchema),
|
|
@@ -2188,7 +2281,7 @@ function choiceOptionsCheck() {
|
|
|
2188
2281
|
}) ?? []).join("; "));
|
|
2189
2282
|
}
|
|
2190
2283
|
|
|
2191
|
-
const SCALAR_VALIDATION_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number" ]);
|
|
2284
|
+
const SCALAR_VALIDATION_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number", "progress" ]);
|
|
2192
2285
|
|
|
2193
2286
|
function scalarValidationCheck() {
|
|
2194
2287
|
return v__namespace.check(entry => scalarValidationDeclarationIssues(entry) === void 0, issue => (scalarValidationDeclarationIssues(issue.input) ?? []).join("; "));
|
|
@@ -2197,13 +2290,24 @@ function scalarValidationCheck() {
|
|
|
2197
2290
|
function scalarValidationDeclarationIssues(entry) {
|
|
2198
2291
|
const {type: type, validation: validation} = entry;
|
|
2199
2292
|
if (validation === void 0) return;
|
|
2200
|
-
if (!SCALAR_VALIDATION_KINDS.has(type)) return [ `\`validation\` is only valid on \`string\` / \`text\` / \`number\` values, not "${type}"` ];
|
|
2293
|
+
if (!SCALAR_VALIDATION_KINDS.has(type)) return [ `\`validation\` is only valid on \`string\` / \`text\` / \`number\` / \`progress\` values, not "${type}"` ];
|
|
2294
|
+
if (type === "progress") {
|
|
2295
|
+
const issues2 = Object.entries(validation).flatMap(([bound, value]) => typeof value == "number" && value >= 0 && value <= 100 ? [] : [ `\`validation.${bound}\` must stay within the progress kind's 0–100 contract` ]);
|
|
2296
|
+
return issues2.length === 0 ? void 0 : issues2;
|
|
2297
|
+
}
|
|
2201
2298
|
if (type === "number") return;
|
|
2202
2299
|
const issues = Object.entries(validation).flatMap(([bound, value]) => Number.isInteger(value) && value >= 0 ? [] : [ `\`validation.${bound}\` must be a non-negative integer for ${type} length` ]);
|
|
2203
2300
|
return issues.length === 0 ? void 0 : issues;
|
|
2204
2301
|
}
|
|
2205
2302
|
|
|
2206
|
-
const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields(
|
|
2303
|
+
const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
|
|
2304
|
+
editable: StoredEditableSchema,
|
|
2305
|
+
group: StoredGroupMembershipSchema
|
|
2306
|
+
})), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
|
|
2307
|
+
editable: AuthoringEditableSchema,
|
|
2308
|
+
group: AuthoringGroupMembershipSchema,
|
|
2309
|
+
kind: AuthoringRawFieldKindSchema
|
|
2310
|
+
})), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), ClaimFieldSchema = pinned()(v__namespace.strictObject({
|
|
2207
2311
|
type: v__namespace.literal("claim"),
|
|
2208
2312
|
name: FieldEntryName,
|
|
2209
2313
|
title: v__namespace.optional(v__namespace.string()),
|
|
@@ -2218,7 +2322,10 @@ function listSugarFields(type) {
|
|
|
2218
2322
|
};
|
|
2219
2323
|
}
|
|
2220
2324
|
|
|
2221
|
-
const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.
|
|
2325
|
+
const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.lazy(input => {
|
|
2326
|
+
const type = asShape(input).type;
|
|
2327
|
+
return type === "claim" ? ClaimFieldSchema : type === "todoList" ? TodoListFieldSchema : type === "notes" ? NotesFieldSchema : RawAuthoringFieldEntrySchema;
|
|
2328
|
+
})), EffectSchema = v__namespace.strictObject({
|
|
2222
2329
|
name: NonEmpty,
|
|
2223
2330
|
title: v__namespace.optional(v__namespace.string()),
|
|
2224
2331
|
description: v__namespace.optional(v__namespace.string()),
|
|
@@ -3608,6 +3715,7 @@ const SCALAR = {
|
|
|
3608
3715
|
string: () => SCALAR,
|
|
3609
3716
|
text: () => SCALAR,
|
|
3610
3717
|
number: () => SCALAR,
|
|
3718
|
+
progress: () => SCALAR,
|
|
3611
3719
|
boolean: () => SCALAR,
|
|
3612
3720
|
date: () => SCALAR,
|
|
3613
3721
|
datetime: () => SCALAR,
|
|
@@ -3811,6 +3919,8 @@ exports.SpawnContractsInvalidError = SpawnContractsInvalidError;
|
|
|
3811
3919
|
|
|
3812
3920
|
exports.StoredFieldOpSchema = StoredFieldOpSchema;
|
|
3813
3921
|
|
|
3922
|
+
exports.VersionSpecificDatasetGdrError = VersionSpecificDatasetGdrError;
|
|
3923
|
+
|
|
3814
3924
|
exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
|
|
3815
3925
|
|
|
3816
3926
|
exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
|