@sanity/workflow-engine 0.19.0 → 0.21.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.
@@ -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;
@@ -115,12 +115,33 @@ function isUnprimed(instance) {
115
115
 
116
116
  function parseDefinitionSnapshotValue(instance) {
117
117
  try {
118
- return JSON.parse(instance.definitionSnapshot);
118
+ return normalizeLegacyActivityRequirements(JSON.parse(instance.definitionSnapshot));
119
119
  } catch (err) {
120
120
  rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
121
121
  }
122
122
  }
123
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
+
124
145
  function parseDefinitionSnapshot(instance) {
125
146
  return parseDefinitionSnapshotValue(instance);
126
147
  }
@@ -129,7 +150,7 @@ function parentRef(instance) {
129
150
  return instance.ancestors.at(-1);
130
151
  }
131
152
 
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";
153
+ const DATA_MODEL_VERSION = 4, DATA_MODEL_MIN_READER = 4, READER_MODEL_ROLLOUT_URL = "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
133
154
 
134
155
  class ReaderModelAcknowledgementError extends WorkflowError {
135
156
  code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
@@ -139,7 +160,7 @@ class ReaderModelAcknowledgementError extends WorkflowError {
139
160
  documentationUrl=READER_MODEL_ROLLOUT_URL;
140
161
  constructor(expectedMinReaderModel, context = "Deployment") {
141
162
  const expected = expectedMinReaderModel === void 0 ? "missing" : String(expectedMinReaderModel);
142
- super("reader-model-acknowledgement", `${context} expected reader floor ${expected}; the installed engine requires acknowledgement ${DATA_MODEL_MIN_READER}. Dependency upgrades can change writer compatibility. Upgrade all readers and Functions before accepting a higher floor; after verifying the rollout, change the literal in deployment configuration. ${READER_MODEL_ROLLOUT_URL}`),
163
+ super("reader-model-acknowledgement", `${context} expected reader floor ${expected}; the installed engine requires acknowledgement ${DATA_MODEL_MIN_READER}. Dependency upgrades can change writer compatibility. Upgrade all readers and Functions before accepting a higher floor; after verifying the rollout, change the literal in deployment configuration. Instances written before the new floor may stop matching user-identity queries, and older readers refuse documents this engine touches — a prerelease deployment with no data worth migrating can reset every engine-owned document with \`sanity-workflows nuke\` and start fresh. ${READER_MODEL_ROLLOUT_URL}`),
143
164
  this.name = "ReaderModelAcknowledgementError", this.expectedMinReaderModel = expectedMinReaderModel;
144
165
  }
145
166
  }
@@ -204,6 +225,22 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
204
225
  compatibility: "additive",
205
226
  applicability: "detectable",
206
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."
207
244
  }) ]);
208
245
 
209
246
  function recordOf(value) {
@@ -264,6 +301,11 @@ function hasClaimTokens(document) {
264
301
  });
265
302
  }
266
303
 
304
+ function hasReadinessRequirements(document) {
305
+ const root = recordOf(document);
306
+ 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)));
307
+ }
308
+
267
309
  const featureDetectors = {
268
310
  "governed-model-stamps": () => !0,
269
311
  "subject-field-kind": document => hasFieldKind(document, "subject"),
@@ -271,7 +313,9 @@ const featureDetectors = {
271
313
  "action-semantics": hasActionSemantics,
272
314
  "inclusive-scalar-bounds": hasScalarValidation,
273
315
  "progress-field-kind": document => hasFieldKind(document, "progress"),
274
- "effect-claim-tokens": hasClaimTokens
316
+ "effect-claim-tokens": hasClaimTokens,
317
+ "classified-principal-ids": () => !0,
318
+ "readiness-requirements": hasReadinessRequirements
275
319
  };
276
320
 
277
321
  function requiredModelFeatures(documentType, document) {
@@ -285,7 +329,7 @@ function requiredReaderModel(documentType, document) {
285
329
  function modelStampFor(args) {
286
330
  return {
287
331
  modelVersion: DATA_MODEL_VERSION,
288
- minReaderModel: Math.max(args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
332
+ minReaderModel: Math.max(DATA_MODEL_MIN_READER, args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
289
333
  };
290
334
  }
291
335
 
@@ -358,6 +402,21 @@ function andConditions(parts) {
358
402
 
359
403
  const KNOWN_SCHEMES = /* @__PURE__ */ new Set([ "dataset", "canvas", "media-library", "dashboard" ]), KNOWN_SCHEMES_TEXT = [ ...KNOWN_SCHEMES ].join(", ");
360
404
 
405
+ class VersionSpecificDatasetGdrError extends Error {
406
+ documentId;
407
+ stableDocumentId;
408
+ constructor(documentId, uri) {
409
+ const stableDocumentId = idUtils.getPublishedId(documentId);
410
+ 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.`),
411
+ this.name = "VersionSpecificDatasetGdrError", this.documentId = documentId, this.stableDocumentId = stableDocumentId;
412
+ }
413
+ }
414
+
415
+ function assertStableDatasetDocumentId(documentId, uri) {
416
+ const sanityId = documentId;
417
+ if (!(!idUtils.isDraftId(sanityId) && !idUtils.isVersionId(sanityId))) throw new VersionSpecificDatasetGdrError(documentId, uri);
418
+ }
419
+
361
420
  function parseGdr(uri) {
362
421
  const colon = uri.indexOf(":");
363
422
  if (colon < 0) throw new Error(`Invalid GDR "${uri}": must be a URI of form "<scheme>:<...id-parts>". Known schemes: ${KNOWN_SCHEMES_TEXT}.`);
@@ -367,11 +426,12 @@ function parseGdr(uri) {
367
426
  if (parts.some(part => part.length === 0)) throw new Error(`Invalid GDR "${uri}": id parts must be non-empty (no leading, trailing, or doubled ":").`);
368
427
  if (scheme === "dataset") {
369
428
  if (parts.length !== 3) throw new Error(`Invalid GDR "${uri}": dataset scheme requires <projectId>:<dataset>:<documentId> (3 parts after scheme); got ${parts.length}.`);
370
- return {
429
+ const documentId = parts[2];
430
+ return assertStableDatasetDocumentId(documentId, uri), {
371
431
  scheme: "dataset",
372
432
  projectId: parts[0],
373
433
  dataset: parts[1],
374
- documentId: parts[2]
434
+ documentId: documentId
375
435
  };
376
436
  }
377
437
  if (parts.length !== 2) throw new Error(`Invalid GDR "${uri}": ${scheme} scheme requires <resourceId>:<documentId> (2 parts after scheme); got ${parts.length}.`);
@@ -391,7 +451,11 @@ function tryParseGdr(uri) {
391
451
  }
392
452
 
393
453
  function gdrUri(parts) {
394
- return parts.scheme === "dataset" ? `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}` : `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
454
+ if (parts.scheme === "dataset") {
455
+ const uri = `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}`;
456
+ return assertStableDatasetDocumentId(parts.documentId, uri), uri;
457
+ }
458
+ return `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
395
459
  }
396
460
 
397
461
  function extractDocumentId(gdrUriString) {
@@ -794,8 +858,8 @@ function desugarStart(start) {
794
858
  ...start.filter !== void 0 ? {
795
859
  filter: start.filter
796
860
  } : {},
797
- ...start.allowed !== void 0 ? {
798
- allowed: start.allowed
861
+ ...start.requirements !== void 0 ? {
862
+ requirements: start.requirements
799
863
  } : {}
800
864
  };
801
865
  }
@@ -1557,7 +1621,7 @@ const ACTION_SEMANTICS = [ "decision.accept", "decision.decline" ], FIELD_SCOPES
1557
1621
  binding: "always",
1558
1622
  label: "the spawned subworkflows",
1559
1623
  description: "Every row of the instance's subworkflow registry, faceted by `activity`/`action`/`definition`/`rowKey`/`status` (`'active'|'done'|'aborted'`) with `current` marking the open stage entry's cohort and `stage` the child's current stage. Usable anywhere — transition `when`s, requirements, any stage's gates; the settled gate is `count($subworkflows[activity == <name> && current && status == 'active']) == 0`."
1560
- } ], SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR = "subjectHasInFlightInstance", RESERVED_CONDITION_VARS = [ ...CONDITION_VARS.map(v2 => v2.name), SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR ], FILTER_SCOPE_VARS = CONDITION_VARS.filter(v2 => v2.binding === "always").map(v2 => v2.name), CALLER_BOUND_VARS = CONDITION_VARS.filter(v2 => v2.binding === "caller").map(v2 => v2.name), START_FILTER_VARS = [ {
1624
+ } ], RESERVED_CONDITION_VARS = CONDITION_VARS.map(v2 => v2.name), FILTER_SCOPE_VARS = CONDITION_VARS.filter(v2 => v2.binding === "always").map(v2 => v2.name), CALLER_BOUND_VARS = CONDITION_VARS.filter(v2 => v2.binding === "caller").map(v2 => v2.name), START_FILTER_VARS = [ {
1561
1625
  name: "tag",
1562
1626
  label: "this engine tag",
1563
1627
  description: "The engine's tag partition — scope `*[...]` instance scans with `tag == $tag`."
@@ -1569,11 +1633,7 @@ const ACTION_SEMANTICS = [ "decision.accept", "decision.decline" ], FIELD_SCOPES
1569
1633
  name: "now",
1570
1634
  label: "the current time",
1571
1635
  description: "The ISO clock reading of the evaluating engine."
1572
- }, {
1573
- name: SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR,
1574
- label: "the subject already has an in-flight workflow",
1575
- description: "Whether any instance in this engine tag, across all definitions, has the same resource-qualified subject and no `completedAt`. Advisory under concurrent starts."
1576
- } ], START_ALLOWED_VARS = [ ...START_FILTER_VARS, {
1636
+ } ], START_REQUIREMENT_VARS = [ ...START_FILTER_VARS, {
1577
1637
  name: "fields",
1578
1638
  label: "the start's input fields",
1579
1639
  description: "The caller's input entries by name (`initialFields` — at `startInstance`, the values the start would seed; at a pre-flight, the values gathered so far, so a read of a not-yet-supplied entry is GROQ null). Document references bind as GDR envelopes — `$fields.<entry>.id` is the GDR URI, never a string authors assemble — a singular `doc.ref` or `subject` included (nothing hydrates at the gate or the pre-flight). Pathed reads are deploy-checked against these envelope shapes."
@@ -1758,6 +1818,37 @@ function isNotesEntry(entry) {
1758
1818
  return columns !== void 0 && columns.has("body") && columns.has("actor") && columns.has("at");
1759
1819
  }
1760
1820
 
1821
+ const ANONYMOUS_IDENTITY = "<anonymous>", SYSTEM_IDENTITY = "<system>", E_PREFIXED_PROJECT_ID = /^e-(.+)$/;
1822
+
1823
+ function classifyPrincipalId(id) {
1824
+ if (id === ANONYMOUS_IDENTITY || id === SYSTEM_IDENTITY) return {
1825
+ namespace: "sentinel"
1826
+ };
1827
+ if (id.startsWith("g")) return {
1828
+ namespace: "global",
1829
+ globalId: id
1830
+ };
1831
+ if (id.startsWith("p-")) return {
1832
+ namespace: "robot",
1833
+ globalId: id
1834
+ };
1835
+ const embeddedGlobal = E_PREFIXED_PROJECT_ID.exec(id)?.[1];
1836
+ return embeddedGlobal !== void 0 ? embeddedGlobal.startsWith("g") ? {
1837
+ namespace: "project",
1838
+ globalId: embeddedGlobal
1839
+ } : {
1840
+ namespace: "unknown"
1841
+ } : id.startsWith("p") ? {
1842
+ namespace: "project"
1843
+ } : {
1844
+ namespace: "unknown"
1845
+ };
1846
+ }
1847
+
1848
+ function lakePrincipalId(args) {
1849
+ return args.localPrincipalId ?? args.actor.id;
1850
+ }
1851
+
1761
1852
  class FieldValueShapeError extends WorkflowError {
1762
1853
  entryType;
1763
1854
  entryName;
@@ -1798,11 +1889,11 @@ function checkChoiceList(args) {
1798
1889
  const {entryType: entryType, options: options, validation: validation} = args;
1799
1890
  if (options === void 0) return;
1800
1891
  if (!CHOICE_KINDS.has(entryType)) return [ `\`options\` is not valid on "${entryType}" values` ];
1801
- const kind = normalizedChoiceKind(entryType), issues = options.list.flatMap((option, index) => checkValueAgainst({
1892
+ const kind = normalizedChoiceKind(entryType), issues = options.list.flatMap((option, index) => issuesOf(checkValueAgainst({
1802
1893
  entryType: kind,
1803
1894
  value: option.value,
1804
1895
  validation: validation
1805
- }, valueSchemas)?.map(issue => `at options.list.${index}.value: ${issue}`) ?? []), seen = /* @__PURE__ */ new Set;
1896
+ }, valueSchemas))?.map(issue => `at options.list.${index}.value: ${issue}`) ?? []), seen = /* @__PURE__ */ new Set;
1806
1897
  for (const option of options.list) seen.has(option.value) && issues.push(`duplicate option value ${JSON.stringify(option.value)}`),
1807
1898
  seen.add(option.value);
1808
1899
  return issues.length === 0 ? void 0 : issues;
@@ -1828,8 +1919,27 @@ const fieldValueSchemas = {
1828
1919
  actor: v__namespace.union([ v__namespace.null(), ActorShape ]),
1829
1920
  assignee: v__namespace.union([ v__namespace.null(), AssigneeShape ]),
1830
1921
  assignees: v__namespace.array(AssigneeShape)
1831
- }, valueSchemas = {
1922
+ }, WritePrincipalId = v__namespace.pipe(NonEmptyString, v__namespace.rawTransform(({dataset: dataset, addIssue: addIssue}) => {
1923
+ const classified = classifyPrincipalId(dataset.value);
1924
+ return classified.namespace === "global" || classified.namespace === "robot" ? dataset.value : classified.namespace === "project" && classified.globalId !== void 0 ? classified.globalId : (addIssue({
1925
+ message: `principal id "${dataset.value}" is not an account-global user id. Workflow user ids are the global \`sanityUserId\` (or a robot token's id). Resolve project members through your surface's member hook, or /projects/<projectId>/users/<id> → sanityUserId.`
1926
+ }), dataset.value);
1927
+ })), ActorWriteShape = tolerantObject()({
1928
+ kind: v__namespace.picklist(ACTOR_KINDS),
1929
+ id: WritePrincipalId,
1930
+ roles: v__namespace.exactOptional(v__namespace.array(v__namespace.string())),
1931
+ onBehalfOf: v__namespace.exactOptional(v__namespace.string())
1932
+ }), AssigneeWriteShape = v__namespace.union([ tolerantObject()({
1933
+ type: v__namespace.literal("user"),
1934
+ id: WritePrincipalId
1935
+ }), tolerantObject()({
1936
+ type: v__namespace.literal("role"),
1937
+ role: NonEmptyString
1938
+ }) ]), valueSchemas = {
1832
1939
  ...fieldValueSchemas,
1940
+ actor: v__namespace.nullable(ActorWriteShape),
1941
+ assignee: v__namespace.nullable(AssigneeWriteShape),
1942
+ assignees: v__namespace.array(AssigneeWriteShape),
1833
1943
  query: v__namespace.any()
1834
1944
  };
1835
1945
 
@@ -1900,7 +2010,7 @@ function wholeValueSchema(args) {
1900
2010
  function appendItemSchema(entryType, shape) {
1901
2011
  if (entryType === "array") return objectSchema(shape.of ?? [], valueSchemas);
1902
2012
  if (entryType === "doc.refs") return GdrShape;
1903
- if (entryType === "assignees") return AssigneeShape;
2013
+ if (entryType === "assignees") return AssigneeWriteShape;
1904
2014
  }
1905
2015
 
1906
2016
  function rejectedRefTypes(args) {
@@ -1925,7 +2035,7 @@ function gdrTypeOf(item) {
1925
2035
  return typeof t == "string" ? t : void 0;
1926
2036
  }
1927
2037
 
1928
- function checkFieldValue(args) {
2038
+ function parseFieldValue(args) {
1929
2039
  return checkValueAgainst(args, valueSchemas);
1930
2040
  }
1931
2041
 
@@ -1935,9 +2045,14 @@ function checkValueAgainst(args, leaf) {
1935
2045
  shape: args,
1936
2046
  leaf: leaf
1937
2047
  });
1938
- if (schema === void 0) return [ `unknown field entry type ${args.entryType}` ];
2048
+ if (schema === void 0) return {
2049
+ issues: [ `unknown field entry type ${args.entryType}` ]
2050
+ };
1939
2051
  const result = v__namespace.safeParse(schema, args.value);
1940
- return result.success ? refTypeIssues({
2052
+ if (!result.success) return {
2053
+ issues: formatIssues(result.issues)
2054
+ };
2055
+ const postIssues = refTypeIssues({
1941
2056
  entryType: args.entryType,
1942
2057
  types: args.types,
1943
2058
  value: args.value
@@ -1945,17 +2060,27 @@ function checkValueAgainst(args, leaf) {
1945
2060
  entryType: args.entryType,
1946
2061
  validation: args.validation,
1947
2062
  value: args.value
1948
- }) : formatIssues(result.issues);
2063
+ });
2064
+ return postIssues !== void 0 ? {
2065
+ issues: postIssues
2066
+ } : {
2067
+ output: result.output
2068
+ };
2069
+ }
2070
+
2071
+ function issuesOf(check) {
2072
+ return "issues" in check ? check.issues : void 0;
1949
2073
  }
1950
2074
 
1951
2075
  function validateFieldValue(args) {
1952
- const issues = checkFieldValue(args);
1953
- if (issues !== void 0) throw new FieldValueShapeError({
2076
+ const check = checkValueAgainst(args, valueSchemas);
2077
+ if ("issues" in check) throw new FieldValueShapeError({
1954
2078
  entryType: args.entryType,
1955
2079
  entryName: args.entryName,
1956
- issues: issues,
2080
+ issues: check.issues,
1957
2081
  mode: "value"
1958
2082
  });
2083
+ return check.output;
1959
2084
  }
1960
2085
 
1961
2086
  const BARE_ID_SOURCE = "[A-Za-z0-9_][A-Za-z0-9._-]*", BARE_ID_RE = new RegExp(`^${BARE_ID_SOURCE}$`), ALIAS_REF_RE = new RegExp(`^@${RESOURCE_ALIAS_NAME_SOURCE}:${BARE_ID_SOURCE}$`);
@@ -1984,7 +2109,7 @@ const AuthoringRefId = v__namespace.pipe(v__namespace.string(), v__namespace.che
1984
2109
  };
1985
2110
 
1986
2111
  function checkLiteralSeed(args) {
1987
- return args.value === null ? [ "a literal seed cannot be null — omit `initialValue` to start the field empty" ] : checkValueAgainst(args, seedValueSchemas);
2112
+ return args.value === null ? [ "a literal seed cannot be null — omit `initialValue` to start the field empty" ] : issuesOf(checkValueAgainst(args, seedValueSchemas));
1988
2113
  }
1989
2114
 
1990
2115
  function validateFieldAppendItem(args) {
@@ -2013,13 +2138,15 @@ function validateFieldAppendItem(args) {
2013
2138
  issues: typeIssues,
2014
2139
  mode: "item"
2015
2140
  });
2141
+ return result.output;
2016
2142
  }
2017
2143
 
2018
2144
  function formatIssues(issues, formatMessage = issue => issue.message) {
2019
- return issues.map(i => {
2020
- const keys = i.path?.map(p => p.key) ?? [];
2021
- return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${formatMessage(i)}`;
2022
- });
2145
+ const formatOne = (issue, prefix) => {
2146
+ const keys = [ ...prefix, ...issue.path?.map(p => p.key) ?? [] ], sub = issue.issues?.filter(candidate => candidate.expected !== "null");
2147
+ return sub !== void 0 && sub.length > 0 ? sub.flatMap(candidate => formatOne(candidate, keys)) : [ `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${formatMessage(issue)}` ];
2148
+ };
2149
+ return issues.flatMap(issue => formatOne(issue, []));
2023
2150
  }
2024
2151
 
2025
2152
  const NonEmpty = NonEmptyString, PositiveInt = v__namespace.pipe(v__namespace.number(), v__namespace.integer(), v__namespace.minValue(1)), GROQ_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -2029,7 +2156,15 @@ function groqIdentifier(referencedAs) {
2029
2156
  }
2030
2157
 
2031
2158
  function picklist(options) {
2032
- return v__namespace.picklist(options, `Invalid option: expected one of ${options.map(o => `"${o}"`).join("|")}`);
2159
+ return v__namespace.picklist(options, invalidOptionMessage(options));
2160
+ }
2161
+
2162
+ function invalidOptionMessage(options) {
2163
+ return `Invalid option: expected one of ${options.map(option => `"${option}"`).join("|")}`;
2164
+ }
2165
+
2166
+ function exhaustiveOptions() {
2167
+ return options => options;
2033
2168
  }
2034
2169
 
2035
2170
  function pinned() {
@@ -2044,12 +2179,12 @@ const LiteralSchema = v__namespace.strictObject({
2044
2179
  scope: v__namespace.optional(v__namespace.union([ v__namespace.literal("workflow"), v__namespace.literal("stage") ])),
2045
2180
  field: NonEmpty,
2046
2181
  path: v__namespace.optional(v__namespace.string())
2047
- }), FieldSourceSchema = v__namespace.union([ v__namespace.strictObject({
2182
+ }), FieldSourceSchema = v__namespace.variant("type", [ v__namespace.strictObject({
2048
2183
  type: v__namespace.literal("input")
2049
2184
  }), v__namespace.strictObject({
2050
2185
  type: v__namespace.literal("query"),
2051
2186
  query: NonEmpty
2052
- }), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.union([ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
2187
+ }), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.variant("type", [ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
2053
2188
  type: v__namespace.literal("param"),
2054
2189
  param: NonEmpty
2055
2190
  }), v__namespace.strictObject({
@@ -2144,7 +2279,7 @@ function groupMembershipNames(group) {
2144
2279
  return group === void 0 ? [] : typeof group == "string" ? [ group ] : [ ...group ];
2145
2280
  }
2146
2281
 
2147
- 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), FieldEntryName = groqIdentifier("`$fields.<name>`"), FiniteNumber = v__namespace.pipe(v__namespace.number(), v__namespace.finite("must be finite")), ScalarValidationSchema = v__namespace.pipe(v__namespace.strictObject({
2282
+ 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({
2148
2283
  min: v__namespace.optional(FiniteNumber),
2149
2284
  max: v__namespace.optional(FiniteNumber)
2150
2285
  }), 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({
@@ -2209,9 +2344,9 @@ function fieldBase(editable, group) {
2209
2344
  };
2210
2345
  }
2211
2346
 
2212
- function fieldEntryFields(editable, group) {
2347
+ function fieldEntryFields({editable: editable, group: group, kind: kind = FieldKindSchema}) {
2213
2348
  return {
2214
- type: FieldKindSchema,
2349
+ type: kind,
2215
2350
  ...fieldBase(editable, group),
2216
2351
  options: v__namespace.optional(ChoiceOptionsSchema),
2217
2352
  validation: v__namespace.optional(ScalarValidationSchema),
@@ -2272,7 +2407,14 @@ function scalarValidationDeclarationIssues(entry) {
2272
2407
  return issues.length === 0 ? void 0 : issues;
2273
2408
  }
2274
2409
 
2275
- const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields(StoredEditableSchema, StoredGroupMembershipSchema)), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields(AuthoringEditableSchema, AuthoringGroupMembershipSchema)), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), ClaimFieldSchema = pinned()(v__namespace.strictObject({
2410
+ const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
2411
+ editable: StoredEditableSchema,
2412
+ group: StoredGroupMembershipSchema
2413
+ })), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
2414
+ editable: AuthoringEditableSchema,
2415
+ group: AuthoringGroupMembershipSchema,
2416
+ kind: AuthoringRawFieldKindSchema
2417
+ })), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), ClaimFieldSchema = pinned()(v__namespace.strictObject({
2276
2418
  type: v__namespace.literal("claim"),
2277
2419
  name: FieldEntryName,
2278
2420
  title: v__namespace.optional(v__namespace.string()),
@@ -2287,7 +2429,10 @@ function listSugarFields(type) {
2287
2429
  };
2288
2430
  }
2289
2431
 
2290
- const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.union([ RawAuthoringFieldEntrySchema, ClaimFieldSchema, TodoListFieldSchema, NotesFieldSchema ])), EffectSchema = v__namespace.strictObject({
2432
+ const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.lazy(input => {
2433
+ const type = asShape(input).type;
2434
+ return type === "claim" ? ClaimFieldSchema : type === "todoList" ? TodoListFieldSchema : type === "notes" ? NotesFieldSchema : RawAuthoringFieldEntrySchema;
2435
+ })), EffectSchema = v__namespace.strictObject({
2291
2436
  name: NonEmpty,
2292
2437
  title: v__namespace.optional(v__namespace.string()),
2293
2438
  description: v__namespace.optional(v__namespace.string()),
@@ -2352,7 +2497,18 @@ const StoredActionSchema = pinned()(v__namespace.strictObject({
2352
2497
  filter: v__namespace.optional(ConditionSchema),
2353
2498
  params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
2354
2499
  effects: v__namespace.optional(v__namespace.array(EffectSchema))
2355
- })), AuthoringActionSchema = pinned()(v__namespace.lazy(input => typeof input == "object" && input !== null && "type" in input ? ClaimActionSchema : RawAuthoringActionSchema));
2500
+ })), AuthoringActionSchema = pinned()(v__namespace.lazy(input => typeof input == "object" && input !== null && "type" in input ? ClaimActionSchema : RawAuthoringActionSchema)), requirementBase = {
2501
+ name: NonEmpty,
2502
+ title: v__namespace.optional(v__namespace.string()),
2503
+ description: v__namespace.optional(v__namespace.string())
2504
+ }, GroqRequirementSchemaRaw = v__namespace.strictObject({
2505
+ ...requirementBase,
2506
+ type: v__namespace.literal("groq"),
2507
+ query: ConditionSchema
2508
+ }), SingleSubjectRequirementSchemaRaw = v__namespace.strictObject({
2509
+ ...requirementBase,
2510
+ type: v__namespace.literal("singleSubject")
2511
+ }), GroqRequirementSchema = pinned()(GroqRequirementSchemaRaw), StartRequirementSchema = pinned()(v__namespace.variant("type", [ GroqRequirementSchemaRaw, SingleSubjectRequirementSchemaRaw ]));
2356
2512
 
2357
2513
  function activityFields({field: field, action: action, target: target, group: group}) {
2358
2514
  return {
@@ -2363,7 +2519,7 @@ function activityFields({field: field, action: action, target: target, group: gr
2363
2519
  group: v__namespace.optional(group),
2364
2520
  target: v__namespace.optional(target),
2365
2521
  filter: v__namespace.optional(ConditionSchema),
2366
- requirements: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
2522
+ requirements: v__namespace.optional(v__namespace.array(GroqRequirementSchema)),
2367
2523
  actions: v__namespace.optional(v__namespace.array(action)),
2368
2524
  fields: v__namespace.optional(v__namespace.array(field))
2369
2525
  };
@@ -2459,7 +2615,7 @@ function startFields(kind) {
2459
2615
  return {
2460
2616
  kind: kind,
2461
2617
  filter: v__namespace.optional(ConditionSchema),
2462
- allowed: v__namespace.optional(ConditionSchema)
2618
+ requirements: v__namespace.optional(v__namespace.array(StartRequirementSchema))
2463
2619
  };
2464
2620
  }
2465
2621
 
@@ -2598,6 +2754,13 @@ function checkActivities({def: def, i: i, activityNames: activityNames, issues:
2598
2754
  })),
2599
2755
  what: `action name in activity "${activity.name}"`,
2600
2756
  issues: issues
2757
+ }), checkDuplicates({
2758
+ names: (activity.requirements ?? []).map((requirement, r) => ({
2759
+ name: requirement.name,
2760
+ path: [ ...path, "requirements", r, "name" ]
2761
+ })),
2762
+ what: `requirement name in activity "${activity.name}"`,
2763
+ issues: issues
2601
2764
  }), checkStatusSetTargets({
2602
2765
  activity: activity,
2603
2766
  activityNames: activityNames,
@@ -2906,10 +3069,10 @@ function collectActivityConditionSites({activity: activity, path: path, stageFie
2906
3069
  policy: "cascade",
2907
3070
  fields: fields
2908
3071
  });
2909
- for (const [name, groq2] of Object.entries(activity.requirements ?? {})) sites.push({
2910
- groq: groq2,
2911
- path: [ ...path, "requirements", name ],
2912
- label: `activity "${activity.name}" requirement "${name}"`,
3072
+ for (const [index, requirement] of (activity.requirements ?? []).entries()) sites.push({
3073
+ groq: requirement.query,
3074
+ path: [ ...path, "requirements", index, "query" ],
3075
+ label: `activity "${activity.name}" requirement "${requirement.name}"`,
2913
3076
  policy: "caller-bound",
2914
3077
  fields: fields
2915
3078
  });
@@ -3105,73 +3268,83 @@ function checkTriggeredActionParams(def, issues) {
3105
3268
  }
3106
3269
 
3107
3270
  function checkStart(def, issues) {
3108
- if (def.start !== void 0 && (def.lifecycle === "child" && issues.push({
3271
+ if (def.start !== void 0 && (checkDuplicates({
3272
+ names: (def.start.requirements ?? []).map((requirement, index) => ({
3273
+ name: requirement.name,
3274
+ path: [ "start", "requirements", index, "name" ]
3275
+ })),
3276
+ what: "start requirement name",
3277
+ issues: issues
3278
+ }), def.lifecycle === "child" && issues.push({
3109
3279
  path: [ "start" ],
3110
3280
  message: "a spawn-only (lifecycle 'child') definition declares `start` — children are instantiated by a parent's `spawn`, never started standalone, so the block would never apply. Remove `start`, or drop `lifecycle: 'child'`"
3111
- }), checkStartFilterReads(def, issues), checkStartAllowedReads(def, issues), checkStartSubjectVariable(def, issues),
3112
- def.start.kind === "autonomous")) for (const [n, entry] of (def.fields ?? []).entries()) entry.required !== !0 || isSubjectEntry(entry) || issues.push({
3281
+ }), checkStartFilterReads(def, issues), checkStartRequirementReads(def, issues),
3282
+ checkSingleSubjectRequirements(def, issues), def.start.kind === "autonomous")) for (const [n, entry] of (def.fields ?? []).entries()) entry.required !== !0 || isSubjectEntry(entry) || issues.push({
3113
3283
  path: [ "fields", n, "required" ],
3114
3284
  message: `start.kind 'autonomous' means runs are initiated by a system reacting to a document, so every required input must be derivable from that triggering document — required entry "${entry.name}" (kind "${entry.type}") is not the workflow's subject. Make it optional, seed it another way (query/literal), or declare it the \`subject\` entry (the document the run is about)`
3115
3285
  });
3116
3286
  }
3117
3287
 
3118
- function checkStartSubjectVariable(def, issues) {
3288
+ function checkSingleSubjectRequirements(def, issues) {
3289
+ const indexes = (def.start?.requirements ?? []).map((requirement, index) => ({
3290
+ requirement: requirement,
3291
+ index: index
3292
+ })).filter(({requirement: requirement}) => requirement.type === "singleSubject"), [first] = indexes;
3293
+ if (first === void 0) return;
3119
3294
  const subject = (def.fields ?? []).find(isSubjectEntry);
3120
- for (const [key, condition] of [ [ "filter", def.start?.filter ], [ "allowed", def.start?.allowed ] ]) if (condition !== void 0 && conditionParameterNames(condition).has(SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR)) {
3121
- if (subject === void 0) {
3122
- issues.push({
3123
- path: [ "start", key ],
3124
- message: `start.${key} reads $${SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the definition declares no \`subject\` entry — the engine has no prospective subject identity to match. Declare a \`subject\` entry (the document the workflow is about), or remove the variable`
3125
- });
3126
- continue;
3127
- }
3128
- key === "allowed" && !isInputSourced(subject) && issues.push({
3129
- path: [ "start", key ],
3130
- message: `start.allowed reads $${SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the definition's subject entry "${subject.name}" is not \`input\`-sourced — the start gate cannot bind its prospective subject. Make the subject input-sourced, or remove the variable`
3295
+ if (subject === void 0) {
3296
+ issues.push({
3297
+ path: [ "start", "requirements", first.index ],
3298
+ message: "a singleSubject start requirement caps the definition at one in-flight run per subject, but the definition declares no `subject` entry — there is no subject identity to dedupe on, so the rule could never apply. Declare a `subject` entry (the document the workflow is about), or remove the requirement"
3131
3299
  });
3300
+ return;
3132
3301
  }
3302
+ isInputSourced(subject) || issues.push({
3303
+ path: [ "start", "requirements", first.index ],
3304
+ message: `a singleSubject start requirement matches the prospective subject against in-flight runs, but the definition's subject entry "${subject.name}" is not \`input\`-sourced — the start gate cannot bind a prospective subject. Make the subject input-sourced, or remove the requirement`
3305
+ });
3133
3306
  }
3134
3307
 
3135
3308
  function checkStartFilterReads(def, issues) {
3136
3309
  const filter = def.start?.filter;
3137
3310
  filter !== void 0 && (conditionParameterNames(filter).has("fields") && issues.push({
3138
3311
  path: [ "start", "filter" ],
3139
- message: "start.filter reads $fields, but the filter is browse-time-pure — a start surface evaluates it per document, before any inputs exist, so $fields cannot be bound. Move the input-dependent rule to start.allowed, the start-time permission predicate that binds $fields and is enforced by startInstance"
3312
+ message: "start.filter reads $fields, but the filter is browse-time-pure — a start surface evaluates it per document, before any inputs exist, so $fields cannot be bound. Move the input-dependent rule to a start requirement, the start-time readiness predicate that binds $fields and is enforced by startInstance"
3140
3313
  }), readsRootDocument(filter) && !(def.fields ?? []).some(isSubjectEntry) && issues.push({
3141
3314
  path: [ "start", "filter" ],
3142
3315
  message: "start.filter reads the candidate document (its root), but the definition declares no `subject` entry — a read surface would never have a document to bind as root, so every root read is GROQ null and the filter silently misevaluates. Declare a `subject` entry (the document the workflow is about), or gate on the dataset instead"
3143
3316
  }));
3144
3317
  }
3145
3318
 
3146
- function checkStartAllowedReads(def, issues) {
3147
- const allowed = def.start?.allowed;
3148
- if (allowed === void 0) return;
3319
+ function checkStartRequirementReads(def, issues) {
3149
3320
  const bindable = new Map((def.fields ?? []).filter(isInputSourced).map(entry => [ entry.name, entry ])), declared = new Set((def.fields ?? []).map(entry => entry.name)), nullVerdict = `so the read is GROQ null and null semantics decide the verdict (refuse-all or vacuously-allow, by the expression's shape), never the caller's real input. Bindable (input) fields: ${knownList(bindable.keys())}`;
3150
- for (const read of conditionFieldReads(allowed)) {
3151
- const entry = bindable.get(read.name);
3152
- if (entry === void 0) {
3153
- issues.push({
3154
- path: [ "start", "allowed" ],
3155
- message: declared.has(read.name) ? `start.allowed reads $fields.${read.name}, but "${read.name}" is not an \`input\`-sourced entry — $fields binds only the caller's input entries (query/literal/fieldRead entries resolve at materialisation, after the start gate), ${nullVerdict}` : `start.allowed reads $fields.${read.name}, but no workflow-scope field entry named "${read.name}" is declared — $fields binds only the caller's input entries, ${nullVerdict}`
3321
+ for (const [index, requirement] of (def.start?.requirements ?? []).entries()) if (requirement.type === "groq") {
3322
+ for (const read of conditionFieldReads(requirement.query)) {
3323
+ const entry = bindable.get(read.name);
3324
+ if (entry === void 0) {
3325
+ issues.push({
3326
+ path: [ "start", "requirements", index, "query" ],
3327
+ message: declared.has(read.name) ? `start requirement reads $fields.${read.name}, but "${read.name}" is not an \`input\`-sourced entry — $fields binds only the caller's input entries (query/literal/fieldRead entries resolve at materialisation, after the start gate), ${nullVerdict}` : `start requirement reads $fields.${read.name}, but no workflow-scope field entry named "${read.name}" is declared — $fields binds only the caller's input entries, ${nullVerdict}`
3328
+ });
3329
+ continue;
3330
+ }
3331
+ pushFieldReadPathIssue({
3332
+ where: "start requirement",
3333
+ read: {
3334
+ field: read.name,
3335
+ path: read.path
3336
+ },
3337
+ target: entry,
3338
+ path: [ "start", "requirements", index, "query" ],
3339
+ issues: issues,
3340
+ nodes: START_REQUIREMENT_VALUE_NODES
3156
3341
  });
3157
- continue;
3158
3342
  }
3159
- pushFieldReadPathIssue({
3160
- where: "start.allowed",
3161
- read: {
3162
- field: read.name,
3163
- path: read.path
3164
- },
3165
- target: entry,
3166
- path: [ "start", "allowed" ],
3167
- issues: issues,
3168
- nodes: START_ALLOWED_VALUE_NODES
3343
+ readsRootDocument(requirement.query) && issues.push({
3344
+ path: [ "start", "requirements", index, "query" ],
3345
+ message: "start requirement reads the candidate document as its root, but no root ever binds in the start-requirement context — startInstance holds inputs, not a loaded document, so the read is GROQ null and null semantics decide the verdict, never the workflow's real state. Read the subject as $fields.<entry> (its GDR URI is $fields.<entry>.id); a per-document visibility rule belongs in start.filter"
3169
3346
  });
3170
3347
  }
3171
- readsRootDocument(allowed) && issues.push({
3172
- path: [ "start", "allowed" ],
3173
- message: "start.allowed reads the candidate document as its root, but no root ever binds in the start-allowed context — startInstance holds inputs, not a loaded document, so the read is GROQ null and null semantics decide the verdict, never the workflow's real state. Read the subject as $fields.<entry> (its GDR URI is $fields.<entry>.id); a per-document visibility rule belongs in start.filter"
3174
- });
3175
3348
  }
3176
3349
 
3177
3350
  function checkGroups(def, issues) {
@@ -3696,7 +3869,7 @@ const SCALAR = {
3696
3869
  kind: "rows",
3697
3870
  of: shape.of ?? []
3698
3871
  })
3699
- }, START_ALLOWED_VALUE_NODES = {
3872
+ }, START_REQUIREMENT_VALUE_NODES = {
3700
3873
  ...VALUE_NODES,
3701
3874
  "doc.ref": () => GDR_VALUE,
3702
3875
  subject: () => GDR_VALUE
@@ -3785,6 +3958,8 @@ exports.ACTIVITY_STATUSES = ACTIVITY_STATUSES;
3785
3958
 
3786
3959
  exports.ACTOR_KINDS = ACTOR_KINDS;
3787
3960
 
3961
+ exports.ANONYMOUS_IDENTITY = ANONYMOUS_IDENTITY;
3962
+
3788
3963
  exports.ActorShape = ActorShape;
3789
3964
 
3790
3965
  exports.AuthoringActionSchema = AuthoringActionSchema;
@@ -3871,16 +4046,18 @@ exports.RESOURCE_ALIAS_NAME_SOURCE = RESOURCE_ALIAS_NAME_SOURCE;
3871
4046
 
3872
4047
  exports.ReaderModelAcknowledgementError = ReaderModelAcknowledgementError;
3873
4048
 
3874
- exports.START_ALLOWED_VARS = START_ALLOWED_VARS;
3875
-
3876
4049
  exports.START_FILTER_VARS = START_FILTER_VARS;
3877
4050
 
3878
- exports.SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR = SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR;
4051
+ exports.START_REQUIREMENT_VARS = START_REQUIREMENT_VARS;
4052
+
4053
+ exports.SYSTEM_IDENTITY = SYSTEM_IDENTITY;
3879
4054
 
3880
4055
  exports.SpawnContractsInvalidError = SpawnContractsInvalidError;
3881
4056
 
3882
4057
  exports.StoredFieldOpSchema = StoredFieldOpSchema;
3883
4058
 
4059
+ exports.VersionSpecificDatasetGdrError = VersionSpecificDatasetGdrError;
4060
+
3884
4061
  exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
3885
4062
 
3886
4063
  exports.WORKFLOW_INSTANCE_TYPE = WORKFLOW_INSTANCE_TYPE;
@@ -3897,12 +4074,12 @@ exports.assertReadableModel = assertReadableModel;
3897
4074
 
3898
4075
  exports.assertReaderModelAcknowledgement = assertReaderModelAcknowledgement;
3899
4076
 
3900
- exports.checkFieldValue = checkFieldValue;
3901
-
3902
4077
  exports.checkWorkflowInvariants = checkWorkflowInvariants;
3903
4078
 
3904
4079
  exports.choiceValueIssues = choiceValueIssues;
3905
4080
 
4081
+ exports.classifyPrincipalId = classifyPrincipalId;
4082
+
3906
4083
  exports.clientConfigFromResource = clientConfigFromResource;
3907
4084
 
3908
4085
  exports.conditionEffectReads = conditionEffectReads;
@@ -3993,6 +4170,8 @@ exports.isUnprimed = isUnprimed;
3993
4170
 
3994
4171
  exports.labelFor = labelFor;
3995
4172
 
4173
+ exports.lakePrincipalId = lakePrincipalId;
4174
+
3996
4175
  exports.minReaderModelOf = minReaderModelOf;
3997
4176
 
3998
4177
  exports.modelStampFor = modelStampFor;
@@ -4003,6 +4182,8 @@ exports.parentRef = parentRef;
4003
4182
 
4004
4183
  exports.parseDefinitionSnapshot = parseDefinitionSnapshot;
4005
4184
 
4185
+ exports.parseFieldValue = parseFieldValue;
4186
+
4006
4187
  exports.parseGdr = parseGdr;
4007
4188
 
4008
4189
  exports.parseOrThrow = parseOrThrow;