@sanity/workflow-engine 0.31.0 → 0.33.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 +323 -0
- package/DATAMODEL.md +223 -9
- package/README.md +2 -2
- package/dist/_chunks-cjs/invariants.cjs +2113 -1940
- package/dist/_chunks-es/invariants.js +2041 -1918
- package/dist/define.cjs +6 -3
- package/dist/define.d.cts +668 -1195
- package/dist/define.d.ts +668 -1195
- package/dist/define.js +7 -4
- package/dist/index.cjs +2513 -968
- package/dist/index.d.cts +1467 -1923
- package/dist/index.d.ts +1467 -1923
- package/dist/index.js +2432 -927
- package/package.json +6 -4
|
@@ -40,6 +40,37 @@ function driverKind(actor) {
|
|
|
40
40
|
return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function isFilterScopedOut(entry) {
|
|
44
|
+
return entry.status === "skipped" && entry.startedAt === void 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function liveSubworkflows(host) {
|
|
48
|
+
return (host.subworkflows ?? []).filter(row => row.resolved === void 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolvedChildStatus(child) {
|
|
52
|
+
if (!(child.completedAt === void 0 || child.completedAt === null)) return child.abortedAt !== void 0 && child.abortedAt !== null ? "aborted" : "done";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function condemnedSubworkflows(host) {
|
|
56
|
+
return liveSubworkflows(host).filter(row => row.abortPending !== void 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function condemnSubworkflow(row, owed) {
|
|
60
|
+
row.abortPending !== void 0 || row.resolved !== void 0 || (row.abortPending = {
|
|
61
|
+
at: owed.at,
|
|
62
|
+
reason: owed.reason
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function findOpenStageEntry(host) {
|
|
67
|
+
return host.stages.find(s => s.name === host.currentStage && s.exitedAt === void 0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function findCurrentActivityEntry(host, activityName) {
|
|
71
|
+
return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
|
|
72
|
+
}
|
|
73
|
+
|
|
43
74
|
function errorMessage(err) {
|
|
44
75
|
return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
45
76
|
}
|
|
@@ -353,107 +384,191 @@ function tagScopeFilter() {
|
|
|
353
384
|
return "tag == $tag";
|
|
354
385
|
}
|
|
355
386
|
|
|
356
|
-
const NonEmptyString$1 = v__namespace.pipe(v__namespace.string(), v__namespace.
|
|
387
|
+
const NonEmptyString$1 = v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1, "must be a non-empty string"));
|
|
357
388
|
|
|
358
|
-
function
|
|
359
|
-
return value
|
|
360
|
-
try {
|
|
361
|
-
return validate(value), !0;
|
|
362
|
-
} catch {
|
|
363
|
-
return !1;
|
|
364
|
-
}
|
|
365
|
-
};
|
|
389
|
+
function isParseableInstant(value) {
|
|
390
|
+
return typeof value == "string" && !Number.isNaN(Date.parse(value));
|
|
366
391
|
}
|
|
367
392
|
|
|
368
|
-
const
|
|
393
|
+
const IsoTimestamp = v__namespace.pipe(v__namespace.string(), v__namespace.check(s => isParseableInstant(s), "must be an ISO-8601 datetime string"));
|
|
369
394
|
|
|
370
|
-
function
|
|
371
|
-
return
|
|
395
|
+
function tolerantObject() {
|
|
396
|
+
return (entries, ..._exact) => v__namespace.looseObject(entries);
|
|
372
397
|
}
|
|
373
398
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}), v__namespace.object({
|
|
378
|
-
type: v__namespace.literal("canvas"),
|
|
379
|
-
id: NonEmptyString$1
|
|
380
|
-
}), v__namespace.object({
|
|
381
|
-
type: v__namespace.literal("media-library"),
|
|
382
|
-
id: NonEmptyString$1
|
|
383
|
-
}), v__namespace.object({
|
|
384
|
-
type: v__namespace.literal("dashboard"),
|
|
385
|
-
id: NonEmptyString$1
|
|
386
|
-
}) ]), ResourceBindingSchema = v__namespace.object({
|
|
387
|
-
name: v__namespace.pipe(NonEmptyString$1, v__namespace.check(isValidAliasName, "invalid resource handle name — lowercase letters, digits and dashes only, no leading dash")),
|
|
388
|
-
resource: WorkflowResourceSchema
|
|
389
|
-
}), 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({
|
|
390
|
-
name: lakeSegment("name"),
|
|
391
|
-
expectedMinReaderModel: v__namespace.optional(v__namespace.custom(() => !0), void 0),
|
|
392
|
-
tag: lakeSegment("tag"),
|
|
393
|
-
workflowResource: WorkflowResourceSchema,
|
|
394
|
-
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"))),
|
|
395
|
-
definitions: v__namespace.pipe(v__namespace.array(DefinitionSchema), v__namespace.minLength(1, "a deployment needs at least one definition"))
|
|
396
|
-
});
|
|
399
|
+
function tolerantEntries() {
|
|
400
|
+
return (entries, ..._exact) => entries;
|
|
401
|
+
}
|
|
397
402
|
|
|
398
|
-
function
|
|
399
|
-
|
|
400
|
-
for (const item of items) {
|
|
401
|
-
const key = keyOf(item), earlier = seen.get(key);
|
|
402
|
-
if (earlier !== void 0) return [ earlier, item ];
|
|
403
|
-
seen.set(key, item);
|
|
404
|
-
}
|
|
403
|
+
function schemaTreeShape(schema) {
|
|
404
|
+
return walkSchemaShape(schema, /* @__PURE__ */ new Set);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
if (
|
|
407
|
+
function walkSchemaShape(node, path) {
|
|
408
|
+
if (typeof node != "object" || node === null) return "unknown";
|
|
409
|
+
if (path.has(node)) return "(circular)";
|
|
410
|
+
path.add(node);
|
|
411
|
+
try {
|
|
412
|
+
const schema = node;
|
|
413
|
+
return containerShape(schema, path) ?? leafShape(schema);
|
|
414
|
+
} finally {
|
|
415
|
+
path.delete(node);
|
|
416
|
+
}
|
|
410
417
|
}
|
|
411
418
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
419
|
+
const objectWalker = (schema, path) => objectEntriesShape(schema.entries, path), unionWalker = (schema, path) => ({
|
|
420
|
+
union: schema.options.map(option => walkSchemaShape(option, path))
|
|
421
|
+
}), unwrapWalker = (schema, path) => walkSchemaShape(schema.wrapped, path), CONTAINER_WALKERS = {
|
|
422
|
+
strict_object: objectWalker,
|
|
423
|
+
loose_object: objectWalker,
|
|
424
|
+
object: objectWalker,
|
|
425
|
+
array: (schema, path) => ({
|
|
426
|
+
array: walkSchemaShape(schema.item, path)
|
|
427
|
+
}),
|
|
428
|
+
record: (schema, path) => ({
|
|
429
|
+
record: walkSchemaShape(schema.value, path)
|
|
430
|
+
}),
|
|
431
|
+
union: unionWalker,
|
|
432
|
+
variant: unionWalker,
|
|
433
|
+
lazy: (schema, path) => walkSchemaShape(schema.getter(void 0), path),
|
|
434
|
+
exact_optional: unwrapWalker,
|
|
435
|
+
optional: unwrapWalker,
|
|
436
|
+
nullable: unwrapWalker
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
function containerShape(schema, path) {
|
|
440
|
+
return CONTAINER_WALKERS[String(schema.type)]?.(schema, path);
|
|
415
441
|
}
|
|
416
442
|
|
|
417
|
-
function
|
|
418
|
-
|
|
443
|
+
function objectEntriesShape(entries, path) {
|
|
444
|
+
const out = {};
|
|
445
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
446
|
+
const optional = isOptionalEntry(entry), wrapped = optional ? entry.wrapped : entry;
|
|
447
|
+
out[optional ? `${key}?` : key] = walkSchemaShape(wrapped, path);
|
|
448
|
+
}
|
|
449
|
+
return Object.fromEntries(Object.entries(out).sort(([a], [b]) => a < b ? -1 : 1));
|
|
419
450
|
}
|
|
420
451
|
|
|
421
|
-
function
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
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`;
|
|
452
|
+
function isOptionalEntry(entry) {
|
|
453
|
+
if (typeof entry != "object" || entry === null) return !1;
|
|
454
|
+
const kind = entry.type;
|
|
455
|
+
return kind === "exact_optional" || kind === "optional";
|
|
426
456
|
}
|
|
427
457
|
|
|
428
|
-
const
|
|
429
|
-
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")),
|
|
430
|
-
telemetry: v__namespace.optional(TelemetryLoggerSchema)
|
|
431
|
-
});
|
|
458
|
+
const LEAF_KINDS = /* @__PURE__ */ new Set([ "string", "number", "boolean", "null", "undefined", "unknown", "any" ]);
|
|
432
459
|
|
|
433
|
-
function
|
|
434
|
-
|
|
460
|
+
function leafShape(schema) {
|
|
461
|
+
if (schema.type === "picklist") return schema.options.join(" | ");
|
|
462
|
+
if (schema.type === "literal") return `literal ${String(schema.literal)}`;
|
|
463
|
+
if (schema.type === "custom") return typeof schema.message == "string" ? `custom(${schema.message})` : "custom";
|
|
464
|
+
if (!LEAF_KINDS.has(String(schema.type))) throw new Error(`schemaTreeShape: unhandled schema kind "${String(schema.type)}" — extend the walker before regenerating the model ledger`);
|
|
465
|
+
return String(schema.type);
|
|
435
466
|
}
|
|
436
467
|
|
|
437
|
-
|
|
468
|
+
function formatValidationError(label, issues) {
|
|
469
|
+
const lines = issues.map(issue => ` - ${issue.path.length === 0 ? "(root)" : formatIssuePath(issue.path)}: ${issue.message}`);
|
|
470
|
+
return `${label} failed validation (${issues.length} issue${issues.length === 1 ? "" : "s"}):\n${lines.join(`\n`)}`;
|
|
471
|
+
}
|
|
438
472
|
|
|
439
|
-
function
|
|
440
|
-
return
|
|
473
|
+
function issuesFromValibot(issues) {
|
|
474
|
+
return issues.map(issue => ({
|
|
475
|
+
path: issue.path ? issue.path.map(item => item.key) : [],
|
|
476
|
+
message: issue.message
|
|
477
|
+
}));
|
|
441
478
|
}
|
|
442
479
|
|
|
443
|
-
function
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
480
|
+
function formatIssuePath(path) {
|
|
481
|
+
let out = "";
|
|
482
|
+
for (const seg of path) typeof seg == "number" ? out += `[${seg}]` : out += out.length === 0 ? String(seg) : `.${String(seg)}`;
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
447
485
|
|
|
448
|
-
|
|
449
|
-
|
|
486
|
+
class PersistedDocShapeError extends WorkflowError {
|
|
487
|
+
documentId;
|
|
488
|
+
documentType;
|
|
489
|
+
issues;
|
|
490
|
+
constructor(args) {
|
|
491
|
+
super("persisted-doc-shape", formatValidationError(`Persisted ${args.documentType} document "${args.documentId}"`, args.issues)),
|
|
492
|
+
this.name = "PersistedDocShapeError", this.documentId = args.documentId, this.documentType = args.documentType,
|
|
493
|
+
this.issues = args.issues;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
450
496
|
|
|
451
|
-
|
|
452
|
-
|
|
497
|
+
function parsePersistedDoc(args) {
|
|
498
|
+
const result = v__namespace.safeParse(args.schema, args.doc);
|
|
499
|
+
if (!result.success) throw new PersistedDocShapeError({
|
|
500
|
+
documentId: documentIdOf(args.doc),
|
|
501
|
+
documentType: args.docType,
|
|
502
|
+
issues: issuesFromValibot(result.issues)
|
|
503
|
+
});
|
|
504
|
+
return result.output;
|
|
505
|
+
}
|
|
453
506
|
|
|
454
|
-
|
|
455
|
-
|
|
507
|
+
function documentIdOf(doc) {
|
|
508
|
+
if (typeof doc == "object" && doc !== null) {
|
|
509
|
+
const id = doc._id;
|
|
510
|
+
if (typeof id == "string") return id;
|
|
456
511
|
}
|
|
512
|
+
return "(unknown id)";
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const ACTOR_KINDS = [ "person", "agent", "system" ];
|
|
516
|
+
|
|
517
|
+
function releaseDocId(releaseName) {
|
|
518
|
+
return `_.releases.${releaseName}`;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function releaseRef({res: res, releaseName: releaseName}) {
|
|
522
|
+
if (releaseName.length === 0) throw new ContractViolationError("releaseRef: releaseName must be a non-empty release name");
|
|
523
|
+
return {
|
|
524
|
+
id: gdrFromResource(res, releaseDocId(releaseName)),
|
|
525
|
+
type: "system.release",
|
|
526
|
+
releaseName: releaseName
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function isAlwaysArrayFieldKind(kind) {
|
|
531
|
+
return kind === "doc.refs" || kind === "assignee" || kind === "assignees" || kind === "array";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function isSingleDocRefKind(kind) {
|
|
535
|
+
return kind === "doc.ref" || kind === "subject";
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function refKindAcceptsTypes(kind) {
|
|
539
|
+
return isSingleDocRefKind(kind) || kind === "doc.refs";
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function assignmentKindAcceptsRoles(kind) {
|
|
543
|
+
return kind === "assignee" || kind === "assignees";
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function isSingleDocRefEntry(entry) {
|
|
547
|
+
return isSingleDocRefKind(entry._type);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function isAssignmentFieldEntry(entry) {
|
|
551
|
+
return assignmentKindAcceptsRoles(entry._type);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function isTodoListItem(row) {
|
|
555
|
+
if (typeof row != "object" || row === null) return !1;
|
|
556
|
+
const candidate = row, status = candidate.status;
|
|
557
|
+
return typeof candidate._key == "string" && typeof candidate.label == "string" && (status == null || typeof status == "string");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function declaredRowColumns(entry) {
|
|
561
|
+
if (("_type" in entry ? entry._type : entry.type) === "array") return new Set((("of" in entry ? entry.of : void 0) ?? []).map(shape => shape.name));
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function isTodoListEntry(entry) {
|
|
565
|
+
const columns = declaredRowColumns(entry);
|
|
566
|
+
return columns !== void 0 && columns.has("label") && columns.has("status");
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
function isNotesEntry(entry) {
|
|
570
|
+
const columns = declaredRowColumns(entry);
|
|
571
|
+
return columns !== void 0 && columns.has("body") && columns.has("actor") && columns.has("at");
|
|
457
572
|
}
|
|
458
573
|
|
|
459
574
|
const UNIVERSAL_ROLE_ALIAS_KEY = "$all";
|
|
@@ -484,2183 +599,2157 @@ function actorFulfillsRole({actorRoles: actorRoles, required: required, aliases:
|
|
|
484
599
|
return actorRoles.some(role => accepted.has(role));
|
|
485
600
|
}
|
|
486
601
|
|
|
487
|
-
function
|
|
488
|
-
return
|
|
602
|
+
function normalizeAssignmentMembers(value) {
|
|
603
|
+
return value === null ? [] : Array.isArray(value) ? value : [ value ];
|
|
489
604
|
}
|
|
490
605
|
|
|
491
|
-
function
|
|
492
|
-
|
|
493
|
-
if (serialized === void 0) throw new Error(`groq tag cannot serialize ${typeof value} — interpolate JSON-representable values only`);
|
|
494
|
-
return serialized;
|
|
606
|
+
function assignmentMembers(entries) {
|
|
607
|
+
return entries.flatMap(entry => isAssignmentFieldEntry(entry) ? normalizeAssignmentMembers(entry.value) : []);
|
|
495
608
|
}
|
|
496
609
|
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
checkReservedRoleAliasKeys(authoring.roleAliases, issues);
|
|
500
|
-
const roleAliases = normalizeRoleAliases(authoring.roleAliases), ctx = {
|
|
501
|
-
issues: issues,
|
|
502
|
-
claimFields: /* @__PURE__ */ new Map,
|
|
503
|
-
claimedFields: /* @__PURE__ */ new Set,
|
|
504
|
-
roleAliases: roleAliases
|
|
505
|
-
}, workflowFields2 = desugarFieldEntries({
|
|
506
|
-
entries: authoring.fields,
|
|
507
|
-
path: [ "fields" ],
|
|
508
|
-
ctx: ctx
|
|
509
|
-
}), workflowLayer = layerOf(workflowFields2), stages = authoring.stages.map((stage, i) => {
|
|
510
|
-
const path = [ "stages", i ], stageFields2 = desugarFieldEntries({
|
|
511
|
-
entries: stage.fields,
|
|
512
|
-
path: [ ...path, "fields" ],
|
|
513
|
-
ctx: ctx
|
|
514
|
-
}), stageEnv = {
|
|
515
|
-
layers: [ {
|
|
516
|
-
scope: "stage",
|
|
517
|
-
entries: layerOf(stageFields2)
|
|
518
|
-
}, {
|
|
519
|
-
scope: "workflow",
|
|
520
|
-
entries: workflowLayer
|
|
521
|
-
} ]
|
|
522
|
-
}, activities = (stage.activities ?? []).map((activity, j) => desugarActivity({
|
|
523
|
-
activity: activity,
|
|
524
|
-
path: [ ...path, "activities", j ],
|
|
525
|
-
stageEnv: stageEnv,
|
|
526
|
-
ctx: ctx
|
|
527
|
-
})), transitions = (stage.transitions ?? []).map(transition => desugarTransition({
|
|
528
|
-
transition: transition
|
|
529
|
-
})), editable = desugarStageEditable({
|
|
530
|
-
overrides: stage.editable,
|
|
531
|
-
inScope: editableFieldNames({
|
|
532
|
-
workflowFields: workflowFields2,
|
|
533
|
-
stageFields: stageFields2,
|
|
534
|
-
activities: activities
|
|
535
|
-
}),
|
|
536
|
-
path: [ ...path, "editable" ],
|
|
537
|
-
ctx: ctx
|
|
538
|
-
});
|
|
539
|
-
return {
|
|
540
|
-
...stripUndefined({
|
|
541
|
-
name: stage.name,
|
|
542
|
-
semantics: stage.semantics,
|
|
543
|
-
title: stage.title,
|
|
544
|
-
description: stage.description,
|
|
545
|
-
groups: stage.groups,
|
|
546
|
-
guards: stage.guards?.map(desugarGuard)
|
|
547
|
-
}),
|
|
548
|
-
...stageFields2 ? {
|
|
549
|
-
fields: stageFields2
|
|
550
|
-
} : {},
|
|
551
|
-
...activities.length > 0 ? {
|
|
552
|
-
activities: activities
|
|
553
|
-
} : {},
|
|
554
|
-
...transitions.length > 0 ? {
|
|
555
|
-
transitions: transitions
|
|
556
|
-
} : {},
|
|
557
|
-
...editable ? {
|
|
558
|
-
editable: editable
|
|
559
|
-
} : {}
|
|
560
|
-
};
|
|
561
|
-
});
|
|
562
|
-
return checkUnclaimedClaimFields(ctx), {
|
|
563
|
-
definition: {
|
|
564
|
-
...stripUndefined({
|
|
565
|
-
name: authoring.name,
|
|
566
|
-
semantics: authoring.semantics,
|
|
567
|
-
title: authoring.title,
|
|
568
|
-
description: authoring.description,
|
|
569
|
-
groups: authoring.groups,
|
|
570
|
-
lifecycle: authoring.lifecycle,
|
|
571
|
-
start: desugarStart(authoring.start),
|
|
572
|
-
initialStage: authoring.initialStage,
|
|
573
|
-
predicates: authoring.predicates,
|
|
574
|
-
roleAliases: roleAliases
|
|
575
|
-
}),
|
|
576
|
-
...workflowFields2 ? {
|
|
577
|
-
fields: workflowFields2
|
|
578
|
-
} : {},
|
|
579
|
-
stages: stages
|
|
580
|
-
},
|
|
581
|
-
issues: ctx.issues
|
|
582
|
-
};
|
|
610
|
+
function assignmentState(members) {
|
|
611
|
+
return members.length === 0 ? "unrouted" : members.some(member => member.type === "user") ? "held" : "routed";
|
|
583
612
|
}
|
|
584
613
|
|
|
585
|
-
function
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
...start.filter !== void 0 ? {
|
|
589
|
-
filter: start.filter
|
|
590
|
-
} : {},
|
|
591
|
-
...start.requirements !== void 0 ? {
|
|
592
|
-
requirements: start.requirements
|
|
593
|
-
} : {}
|
|
594
|
-
};
|
|
614
|
+
function activeAssignmentMembers(members) {
|
|
615
|
+
const users = members.filter(member => member.type === "user");
|
|
616
|
+
return users.length > 0 ? users : members;
|
|
595
617
|
}
|
|
596
618
|
|
|
597
|
-
function
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
message: `role alias key "${key}" uses the reserved "$" prefix — that namespace is the engine's stored spelling for the universal fulfiller. Use "*" to mean "fulfills any gate", or rename the role.`
|
|
601
|
-
});
|
|
619
|
+
function assignmentMatch(members, identity) {
|
|
620
|
+
const active = activeAssignmentMembers(members);
|
|
621
|
+
return active.some(member => member.type === "user" && member.id === identity.userId) ? "user" : active.some(member => member.type === "role" && identity.roles.includes(member.role)) ? "role" : void 0;
|
|
602
622
|
}
|
|
603
623
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
name: "label",
|
|
607
|
-
title: "Label"
|
|
608
|
-
}, {
|
|
609
|
-
type: "string",
|
|
610
|
-
name: "status",
|
|
611
|
-
title: "Status"
|
|
612
|
-
}, {
|
|
613
|
-
type: "assignee",
|
|
614
|
-
name: "assignee",
|
|
615
|
-
title: "Assignee"
|
|
616
|
-
}, {
|
|
617
|
-
type: "date",
|
|
618
|
-
name: "dueDate",
|
|
619
|
-
title: "Due date"
|
|
620
|
-
} ], NOTES_OF = [ {
|
|
621
|
-
type: "text",
|
|
622
|
-
name: "body",
|
|
623
|
-
title: "Body"
|
|
624
|
-
}, {
|
|
625
|
-
type: "actor",
|
|
626
|
-
name: "actor",
|
|
627
|
-
title: "Actor"
|
|
628
|
-
}, {
|
|
629
|
-
type: "datetime",
|
|
630
|
-
name: "at",
|
|
631
|
-
title: "At"
|
|
632
|
-
} ];
|
|
633
|
-
|
|
634
|
-
function desugarFieldEntries({entries: entries, path: path, ctx: ctx}) {
|
|
635
|
-
return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({
|
|
636
|
-
entry: entry,
|
|
637
|
-
path: [ ...path, i ],
|
|
638
|
-
ctx: ctx
|
|
639
|
-
}));
|
|
624
|
+
function identityMatchesAssignment(members, identity) {
|
|
625
|
+
return assignmentMatch(members, identity) !== void 0;
|
|
640
626
|
}
|
|
641
627
|
|
|
642
|
-
function
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
});
|
|
648
|
-
if (entry.type === "todoList" || entry.type === "notes") return desugarListField({
|
|
649
|
-
entry: entry,
|
|
650
|
-
path: path,
|
|
651
|
-
ctx: ctx
|
|
652
|
-
});
|
|
653
|
-
const editable = normalizeEditable({
|
|
654
|
-
editable: entry.editable,
|
|
655
|
-
path: [ ...path, "editable" ],
|
|
656
|
-
ctx: ctx
|
|
657
|
-
});
|
|
658
|
-
return {
|
|
659
|
-
...stripUndefined({
|
|
660
|
-
type: entry.type,
|
|
661
|
-
name: entry.name,
|
|
662
|
-
title: entry.title,
|
|
663
|
-
description: entry.description,
|
|
664
|
-
group: normalizeGroup(entry.group),
|
|
665
|
-
required: entry.required,
|
|
666
|
-
initialValue: entry.initialValue,
|
|
667
|
-
editable: editable,
|
|
668
|
-
options: entry.options,
|
|
669
|
-
validation: entry.validation,
|
|
670
|
-
types: entry.types,
|
|
671
|
-
roles: entry.roles,
|
|
672
|
-
fields: entry.fields,
|
|
673
|
-
of: entry.of
|
|
674
|
-
})
|
|
628
|
+
function assignmentStateCounts(assignments, identity) {
|
|
629
|
+
const counts = {
|
|
630
|
+
unrouted: 0,
|
|
631
|
+
routed: 0,
|
|
632
|
+
held: 0
|
|
675
633
|
};
|
|
634
|
+
for (const members of assignments) {
|
|
635
|
+
const state = assignmentState(members);
|
|
636
|
+
state === "unrouted" ? counts.unrouted += 1 : identityMatchesAssignment(members, identity) && (counts[state] += 1);
|
|
637
|
+
}
|
|
638
|
+
return counts;
|
|
676
639
|
}
|
|
677
640
|
|
|
678
|
-
function
|
|
679
|
-
|
|
680
|
-
...stripUndefined({
|
|
681
|
-
name: entry.name,
|
|
682
|
-
title: entry.title,
|
|
683
|
-
description: entry.description,
|
|
684
|
-
group: normalizeGroup(entry.group)
|
|
685
|
-
}),
|
|
686
|
-
type: "actor"
|
|
687
|
-
};
|
|
688
|
-
return ctx.claimFields.set(desugared, {
|
|
689
|
-
name: entry.name,
|
|
690
|
-
path: path
|
|
691
|
-
}), desugared;
|
|
641
|
+
function openActivityAssignments(instance) {
|
|
642
|
+
return (findOpenStageEntry(instance)?.activities ?? []).filter(activity => activity.status === "active").map(activity => assignmentMembers(activity.fields ?? []));
|
|
692
643
|
}
|
|
693
644
|
|
|
694
|
-
function
|
|
695
|
-
|
|
696
|
-
editable: entry.editable,
|
|
697
|
-
path: [ ...path, "editable" ],
|
|
698
|
-
ctx: ctx
|
|
699
|
-
});
|
|
700
|
-
return {
|
|
701
|
-
...stripUndefined({
|
|
702
|
-
name: entry.name,
|
|
703
|
-
title: entry.title,
|
|
704
|
-
description: entry.description,
|
|
705
|
-
group: normalizeGroup(entry.group),
|
|
706
|
-
required: entry.required,
|
|
707
|
-
initialValue: entry.initialValue,
|
|
708
|
-
editable: editable
|
|
709
|
-
}),
|
|
710
|
-
type: "array",
|
|
711
|
-
of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
|
|
712
|
-
};
|
|
645
|
+
function instanceAssignmentStateCounts(instance, identity) {
|
|
646
|
+
return assignmentStateCounts(openActivityAssignments(instance), identity);
|
|
713
647
|
}
|
|
714
648
|
|
|
715
|
-
function
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
message: "editable: [] names no roles — use `true` to open the field to anyone in its scope, or list at least one role"
|
|
723
|
-
});
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
return condition;
|
|
727
|
-
}
|
|
728
|
-
return editable;
|
|
649
|
+
function actorMatchesAssignment(args) {
|
|
650
|
+
const actor = args.actor;
|
|
651
|
+
return actor === void 0 ? !1 : activeAssignmentMembers(args.members).some(member => member.type === "user" ? member.id === actor.id : actorFulfillsRole({
|
|
652
|
+
actorRoles: actor.roles,
|
|
653
|
+
required: member.role,
|
|
654
|
+
aliases: args.roleAliases
|
|
655
|
+
}));
|
|
729
656
|
}
|
|
730
657
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
658
|
+
const ANONYMOUS_IDENTITY = "<anonymous>", SYSTEM_IDENTITY = "<system>", E_PREFIXED_PROJECT_ID = /^e-(.+)$/;
|
|
659
|
+
|
|
660
|
+
function classifyPrincipalId(id) {
|
|
661
|
+
if (id === ANONYMOUS_IDENTITY || id === SYSTEM_IDENTITY) return {
|
|
662
|
+
namespace: "sentinel"
|
|
663
|
+
};
|
|
664
|
+
if (id.startsWith("g")) return {
|
|
665
|
+
namespace: "global",
|
|
666
|
+
globalId: id
|
|
667
|
+
};
|
|
668
|
+
if (id.startsWith("p-")) return {
|
|
669
|
+
namespace: "robot",
|
|
670
|
+
globalId: id
|
|
671
|
+
};
|
|
672
|
+
const embeddedGlobal = E_PREFIXED_PROJECT_ID.exec(id)?.[1];
|
|
673
|
+
return embeddedGlobal !== void 0 ? embeddedGlobal.startsWith("g") ? {
|
|
674
|
+
namespace: "project",
|
|
675
|
+
globalId: embeddedGlobal
|
|
676
|
+
} : {
|
|
677
|
+
namespace: "unknown"
|
|
678
|
+
} : id.startsWith("p") ? {
|
|
679
|
+
namespace: "project"
|
|
680
|
+
} : {
|
|
681
|
+
namespace: "unknown"
|
|
734
682
|
};
|
|
735
|
-
collect(workflowFields2), collect(stageFields2);
|
|
736
|
-
for (const activity of activities) collect(activity.fields);
|
|
737
|
-
return names;
|
|
738
683
|
}
|
|
739
684
|
|
|
740
|
-
function
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
const normalized = normalizeEditable({
|
|
752
|
-
editable: value,
|
|
753
|
-
path: [ ...path, name ],
|
|
754
|
-
ctx: ctx
|
|
755
|
-
});
|
|
756
|
-
normalized !== void 0 && (out[name] = normalized);
|
|
685
|
+
function directoryBridgeId(sanityUserId) {
|
|
686
|
+
if (typeof sanityUserId == "string") return classifyPrincipalId(sanityUserId).namespace === "global" ? sanityUserId : void 0;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function firstCarriedGlobalId(candidates) {
|
|
690
|
+
for (const candidate of candidates) {
|
|
691
|
+
if (candidate === void 0) continue;
|
|
692
|
+
const {globalId: globalId} = classifyPrincipalId(candidate);
|
|
693
|
+
if (globalId !== void 0) return globalId;
|
|
757
694
|
}
|
|
758
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
759
695
|
}
|
|
760
696
|
|
|
761
|
-
function
|
|
762
|
-
return
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function normalizeGroup(group) {
|
|
766
|
-
return group === void 0 || Array.isArray(group) ? group : [ group ];
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
function desugarActivity({activity: activity, path: path, stageEnv: stageEnv, ctx: ctx}) {
|
|
770
|
-
const activityFields2 = desugarFieldEntries({
|
|
771
|
-
entries: activity.fields,
|
|
772
|
-
path: [ ...path, "fields" ],
|
|
773
|
-
ctx: ctx
|
|
774
|
-
}), env = {
|
|
775
|
-
layers: [ {
|
|
776
|
-
scope: "activity",
|
|
777
|
-
entries: layerOf(activityFields2)
|
|
778
|
-
}, ...stageEnv.layers ]
|
|
779
|
-
}, actions = (activity.actions ?? []).map((action, a) => desugarAction({
|
|
780
|
-
action: action,
|
|
781
|
-
path: [ ...path, "actions", a ],
|
|
782
|
-
env: env,
|
|
783
|
-
activityName: activity.name,
|
|
784
|
-
ctx: ctx
|
|
785
|
-
})), target = desugarTarget({
|
|
786
|
-
target: activity.target,
|
|
787
|
-
env: env,
|
|
788
|
-
path: [ ...path, "target" ],
|
|
789
|
-
ctx: ctx
|
|
790
|
-
});
|
|
791
|
-
return {
|
|
792
|
-
...stripUndefined({
|
|
793
|
-
name: activity.name,
|
|
794
|
-
semantics: activity.semantics,
|
|
795
|
-
title: activity.title,
|
|
796
|
-
description: activity.description,
|
|
797
|
-
groups: activity.groups,
|
|
798
|
-
group: normalizeGroup(activity.group),
|
|
799
|
-
filter: activity.filter,
|
|
800
|
-
requirements: activity.requirements
|
|
801
|
-
}),
|
|
802
|
-
...target ? {
|
|
803
|
-
target: target
|
|
804
|
-
} : {},
|
|
805
|
-
...activityFields2 ? {
|
|
806
|
-
fields: activityFields2
|
|
807
|
-
} : {},
|
|
808
|
-
...actions.length > 0 ? {
|
|
809
|
-
actions: actions
|
|
810
|
-
} : {}
|
|
811
|
-
};
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref" ];
|
|
815
|
-
|
|
816
|
-
function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
|
|
817
|
-
if (target === void 0 || target.type === "url") return target;
|
|
818
|
-
const ref = typeof target.field == "string" ? {
|
|
819
|
-
field: target.field
|
|
820
|
-
} : target.field, field = resolveRef({
|
|
821
|
-
ref: ref,
|
|
822
|
-
env: env,
|
|
823
|
-
path: [ ...path, "field" ],
|
|
824
|
-
ctx: ctx
|
|
825
|
-
}) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
826
|
-
return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
|
|
827
|
-
path: [ ...path, "field" ],
|
|
828
|
-
message: `manual activity target references "${field.field}" of kind "${entry.type}" — a deep-link target needs a document-valued entry (${TARGET_DOC_KINDS.join(", ")})`
|
|
829
|
-
}), {
|
|
830
|
-
type: "field",
|
|
831
|
-
field: field
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
function reportEmptyActionRoles({action: action, path: path, ctx: ctx}) {
|
|
836
|
-
action.roles === void 0 || action.roles.length > 0 || ctx.issues.push({
|
|
837
|
-
path: [ ...path, "roles" ],
|
|
838
|
-
message: "roles: [] names no roles — omit it to allow any identity, or list at least one role"
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
function desugarActionOps(args) {
|
|
843
|
-
const {action: action, path: path, env: env, activityName: activityName, ctx: ctx} = args, ops = desugarOps({
|
|
844
|
-
ops: action.ops,
|
|
845
|
-
path: [ ...path, "ops" ],
|
|
846
|
-
env: env,
|
|
847
|
-
firingActivity: activityName,
|
|
848
|
-
ctx: ctx
|
|
849
|
-
}) ?? [];
|
|
850
|
-
return action.status !== void 0 && ops.push({
|
|
851
|
-
type: "status.set",
|
|
852
|
-
activity: activityName,
|
|
853
|
-
status: action.status
|
|
854
|
-
}), ops;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
function desugarAction({action: action, path: path, env: env, activityName: activityName, ctx: ctx}) {
|
|
858
|
-
if ("type" in action) return desugarClaimAction({
|
|
859
|
-
action: action,
|
|
860
|
-
path: path,
|
|
861
|
-
env: env,
|
|
862
|
-
ctx: ctx
|
|
863
|
-
});
|
|
864
|
-
reportEmptyActionRoles({
|
|
865
|
-
action: action,
|
|
866
|
-
path: path,
|
|
867
|
-
ctx: ctx
|
|
868
|
-
});
|
|
869
|
-
const cascadeFired = isCascadeFired(action), filter = cascadeFired ? action.filter : andConditions([ rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = desugarActionOps({
|
|
870
|
-
action: action,
|
|
871
|
-
path: path,
|
|
872
|
-
env: env,
|
|
873
|
-
activityName: activityName,
|
|
874
|
-
ctx: ctx
|
|
875
|
-
});
|
|
876
|
-
return {
|
|
877
|
-
...stripUndefined({
|
|
878
|
-
name: action.name,
|
|
879
|
-
semantics: action.semantics,
|
|
880
|
-
title: action.title,
|
|
881
|
-
description: action.description,
|
|
882
|
-
group: normalizeGroup(action.group),
|
|
883
|
-
when: action.when,
|
|
884
|
-
params: action.params,
|
|
885
|
-
effects: action.effects,
|
|
886
|
-
spawn: action.spawn
|
|
887
|
-
}),
|
|
888
|
-
...cascadeFired && action.roles !== void 0 && action.roles.length > 0 ? {
|
|
889
|
-
roles: action.roles
|
|
890
|
-
} : {},
|
|
891
|
-
...filter ? {
|
|
892
|
-
filter: filter
|
|
893
|
-
} : {},
|
|
894
|
-
...ops.length > 0 ? {
|
|
895
|
-
ops: ops
|
|
896
|
-
} : {}
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
function desugarClaimAction({action: action, path: path, env: env, ctx: ctx}) {
|
|
901
|
-
const ref = typeof action.field == "string" ? {
|
|
902
|
-
field: action.field
|
|
903
|
-
} : action.field, resolved = resolveRef({
|
|
904
|
-
ref: ref,
|
|
905
|
-
env: env,
|
|
906
|
-
path: [ ...path, "field" ],
|
|
907
|
-
ctx: ctx
|
|
908
|
-
});
|
|
909
|
-
if (resolved) {
|
|
910
|
-
const entry = entryAt(env, resolved);
|
|
911
|
-
entry && entry.type !== "actor" && ctx.issues.push({
|
|
912
|
-
path: [ ...path, "field" ],
|
|
913
|
-
message: `claim action "${action.name}" references "${resolved.field}" of kind "${entry.type}" — a claim pair needs an actor-valued entry (a "claim" or "actor" field declaration)`
|
|
914
|
-
}), checkShadowedClaimTarget({
|
|
915
|
-
actionName: action.name,
|
|
916
|
-
field: ref.field,
|
|
917
|
-
resolved: resolved,
|
|
918
|
-
env: env,
|
|
919
|
-
path: [ ...path, "field" ],
|
|
920
|
-
ctx: ctx
|
|
921
|
-
}), entry && ctx.claimedFields.add(entry);
|
|
922
|
-
}
|
|
923
|
-
const noSteal = `!defined($fields.${ref.field})`, filter = andConditions([ noSteal, rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = resolved ? [ {
|
|
924
|
-
type: "field.set",
|
|
925
|
-
target: resolved,
|
|
926
|
-
value: {
|
|
927
|
-
type: "actor"
|
|
928
|
-
}
|
|
929
|
-
} ] : [];
|
|
930
|
-
return {
|
|
931
|
-
...stripUndefined({
|
|
932
|
-
name: action.name,
|
|
933
|
-
title: action.title,
|
|
934
|
-
description: action.description,
|
|
935
|
-
group: normalizeGroup(action.group),
|
|
936
|
-
params: action.params,
|
|
937
|
-
effects: action.effects
|
|
938
|
-
}),
|
|
939
|
-
filter: filter,
|
|
940
|
-
...ops.length > 0 ? {
|
|
941
|
-
ops: ops
|
|
942
|
-
} : {}
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
function checkShadowedClaimTarget({actionName: actionName, field: field, resolved: resolved, env: env, path: path, ctx: ctx}) {
|
|
947
|
-
const nearest = env.layers.find(l => l.entries.has(field));
|
|
948
|
-
nearest === void 0 || nearest.scope === resolved.scope || ctx.issues.push({
|
|
949
|
-
path: path,
|
|
950
|
-
message: `claim action "${actionName}" targets "${field}" at scope "${resolved.scope}", but a nearer ${nearest.scope}-scope entry of the same name shadows it in $fields — the no-steal filter would read the shadowing entry while the claim writes the "${resolved.scope}" one. Rename one of the entries or claim the nearer one`
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
function checkUnclaimedClaimFields(ctx) {
|
|
955
|
-
for (const [entry, {name: name, path: path}] of ctx.claimFields) ctx.claimedFields.has(entry) || ctx.issues.push({
|
|
956
|
-
path: path,
|
|
957
|
-
message: `claim field "${name}" is never referenced by a claim action — you announced the pattern and wrote half of it. Declare a defineAction({ type: "claim", field: "${name}" }) or use a raw actor entry`
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
const DEFAULT_TRANSITION_WHEN = "$allActivitiesDone";
|
|
962
|
-
|
|
963
|
-
function desugarTransition({transition: transition}) {
|
|
964
|
-
return {
|
|
965
|
-
...stripUndefined({
|
|
966
|
-
name: transition.name,
|
|
967
|
-
title: transition.title,
|
|
968
|
-
description: transition.description,
|
|
969
|
-
to: transition.to
|
|
970
|
-
}),
|
|
971
|
-
when: transition.when ?? DEFAULT_TRANSITION_WHEN
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function desugarGuard(guard) {
|
|
976
|
-
const {match: match, metadata: metadata, ...rest} = guard, {idRefs: idRefs, ...matchRest} = match;
|
|
977
|
-
return {
|
|
978
|
-
...rest,
|
|
979
|
-
match: {
|
|
980
|
-
...matchRest,
|
|
981
|
-
...idRefs !== void 0 ? {
|
|
982
|
-
idRefs: idRefs.map(printGuardRead)
|
|
983
|
-
} : {}
|
|
984
|
-
},
|
|
985
|
-
...metadata !== void 0 ? {
|
|
986
|
-
metadata: Object.fromEntries(Object.entries(metadata).map(([key, read]) => [ key, printGuardRead(read) ]))
|
|
987
|
-
} : {}
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
function desugarOps({ops: ops, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
|
|
992
|
-
if (ops) return ops.map((op, i) => desugarOp({
|
|
993
|
-
op: op,
|
|
994
|
-
path: [ ...path, i ],
|
|
995
|
-
env: env,
|
|
996
|
-
firingActivity: firingActivity,
|
|
997
|
-
ctx: ctx
|
|
998
|
-
}));
|
|
697
|
+
function lakePrincipalId(args) {
|
|
698
|
+
return args.localPrincipalId ?? args.actor.id;
|
|
999
699
|
}
|
|
1000
700
|
|
|
1001
|
-
function
|
|
1002
|
-
|
|
1003
|
-
type: "status.set",
|
|
1004
|
-
activity: op.activity ?? firingActivity,
|
|
1005
|
-
status: op.status
|
|
1006
|
-
};
|
|
1007
|
-
if (op.type === "audit") return desugarAuditOp({
|
|
1008
|
-
op: op,
|
|
1009
|
-
path: path,
|
|
1010
|
-
env: env,
|
|
1011
|
-
ctx: ctx
|
|
1012
|
-
});
|
|
1013
|
-
const target = resolveRef({
|
|
1014
|
-
ref: op.target,
|
|
1015
|
-
env: env,
|
|
1016
|
-
path: [ ...path, "target" ],
|
|
1017
|
-
ctx: ctx
|
|
1018
|
-
}) ?? fallbackRef(op.target);
|
|
1019
|
-
return {
|
|
1020
|
-
...op,
|
|
1021
|
-
target: target
|
|
1022
|
-
};
|
|
701
|
+
function isRecord(value) {
|
|
702
|
+
return typeof value == "object" && value !== null && !Array.isArray(value);
|
|
1023
703
|
}
|
|
1024
704
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
function desugarAuditOp({op: op, path: path, env: env, ctx: ctx}) {
|
|
1035
|
-
const target = resolveRef({
|
|
1036
|
-
ref: op.target,
|
|
1037
|
-
env: env,
|
|
1038
|
-
path: [ ...path, "target" ],
|
|
1039
|
-
ctx: ctx
|
|
1040
|
-
}) ?? fallbackRef(op.target);
|
|
1041
|
-
if (op.value.type !== "object") return ctx.issues.push({
|
|
1042
|
-
path: [ ...path, "value" ],
|
|
1043
|
-
message: `audit value must be an object source carrying the domain fields (got "${op.value.type}")`
|
|
1044
|
-
}), {
|
|
1045
|
-
type: "field.append",
|
|
1046
|
-
target: target,
|
|
1047
|
-
value: op.value
|
|
1048
|
-
};
|
|
1049
|
-
const actorField = op.stampFields?.actor ?? "actor", atField = op.stampFields?.at ?? "at", fields = {
|
|
1050
|
-
...op.value.fields
|
|
1051
|
-
};
|
|
1052
|
-
for (const [stamp, source] of [ [ actorField, AUDIT_STAMPS.actor ], [ atField, AUDIT_STAMPS.at ] ]) {
|
|
1053
|
-
if (stamp in fields) {
|
|
1054
|
-
ctx.issues.push({
|
|
1055
|
-
path: [ ...path, "value", "fields", stamp ],
|
|
1056
|
-
message: `audit stamp field "${stamp}" collides with an authored domain field — rename yours or remap the stamp via stampFields`
|
|
1057
|
-
});
|
|
1058
|
-
continue;
|
|
1059
|
-
}
|
|
1060
|
-
fields[stamp] = source;
|
|
705
|
+
class FieldValueShapeError extends WorkflowError {
|
|
706
|
+
entryType;
|
|
707
|
+
entryName;
|
|
708
|
+
issues;
|
|
709
|
+
constructor(args) {
|
|
710
|
+
const issueText = args.issues.join("; ");
|
|
711
|
+
super("field-value-shape", `Field entry ${args.mode} shape invalid for "${args.entryName}" (${args.entryType}): ${issueText}`),
|
|
712
|
+
this.name = "FieldValueShapeError", this.entryType = args.entryType, this.entryName = args.entryName,
|
|
713
|
+
this.issues = args.issues;
|
|
1061
714
|
}
|
|
1062
|
-
return {
|
|
1063
|
-
type: "field.append",
|
|
1064
|
-
target: target,
|
|
1065
|
-
value: {
|
|
1066
|
-
type: "object",
|
|
1067
|
-
fields: fields
|
|
1068
|
-
}
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
function resolveRef({ref: ref, env: env, path: path, ctx: ctx}) {
|
|
1073
|
-
const layers = ref.scope === void 0 ? env.layers : env.layers.filter(l => l.scope === ref.scope);
|
|
1074
|
-
for (const layer of layers) if (layer.entries.has(ref.field)) return {
|
|
1075
|
-
scope: layer.scope,
|
|
1076
|
-
field: ref.field
|
|
1077
|
-
};
|
|
1078
|
-
const reachable = env.layers.flatMap(l => [ ...l.entries.keys() ].map(n => `${l.scope}:${n}`));
|
|
1079
|
-
ctx.issues.push({
|
|
1080
|
-
path: path,
|
|
1081
|
-
message: `field reference "${ref.field}"${ref.scope ? ` (scope "${ref.scope}")` : ""} does not resolve to a declared entry. Reachable: ${reachable.join(", ") || "(none)"}`
|
|
1082
|
-
});
|
|
1083
715
|
}
|
|
1084
716
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
717
|
+
const GdrUriSchema = v__namespace.custom(s => typeof s == "string" && isGdrUri(s), "must be a GDR URI"), GdrShape = tolerantObject()({
|
|
718
|
+
id: GdrUriSchema,
|
|
719
|
+
type: NonEmptyString$1
|
|
720
|
+
}), ReleaseRefShape = v__namespace.pipe(tolerantObject()({
|
|
721
|
+
id: GdrUriSchema,
|
|
722
|
+
type: v__namespace.literal("system.release"),
|
|
723
|
+
releaseName: NonEmptyString$1
|
|
724
|
+
}), v__namespace.check(ref => !isGdrUri(ref.id) || extractDocumentId(ref.id) === releaseDocId(ref.releaseName), "id must point at the `_.releases.<releaseName>` doc named by releaseName")), ActorShape = tolerantObject()({
|
|
725
|
+
kind: v__namespace.picklist(ACTOR_KINDS),
|
|
726
|
+
id: NonEmptyString$1,
|
|
727
|
+
roles: v__namespace.exactOptional(v__namespace.array(v__namespace.string())),
|
|
728
|
+
onBehalfOf: v__namespace.exactOptional(v__namespace.string())
|
|
729
|
+
}), AssigneeShape = v__namespace.union([ tolerantObject()({
|
|
730
|
+
type: v__namespace.literal("user"),
|
|
731
|
+
id: NonEmptyString$1
|
|
732
|
+
}), tolerantObject()({
|
|
733
|
+
type: v__namespace.literal("role"),
|
|
734
|
+
role: NonEmptyString$1
|
|
735
|
+
}) ]), AssigneeListShape = v__namespace.pipe(v__namespace.union([ v__namespace.null(), AssigneeShape, v__namespace.array(AssigneeShape) ]), v__namespace.transform(normalizeAssignmentMembers)), 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", "dueDate", "datetime", "dueDatetime", "dateTime" ]);
|
|
1088
736
|
|
|
1089
|
-
function
|
|
1090
|
-
return
|
|
1091
|
-
scope: ref.scope ?? "workflow",
|
|
1092
|
-
field: ref.field
|
|
1093
|
-
};
|
|
737
|
+
function normalizedChoiceKind(kind) {
|
|
738
|
+
return kind === "dateTime" ? "datetime" : kind;
|
|
1094
739
|
}
|
|
1095
740
|
|
|
1096
|
-
function
|
|
1097
|
-
|
|
741
|
+
function checkChoiceList(args) {
|
|
742
|
+
const {entryType: entryType, options: options, validation: validation} = args;
|
|
743
|
+
if (options === void 0) return;
|
|
744
|
+
if (!CHOICE_KINDS.has(entryType)) return [ `\`options\` is not valid on "${entryType}" values` ];
|
|
745
|
+
const kind = normalizedChoiceKind(entryType), issues = options.list.flatMap((option, index) => issuesOf(checkValueAgainst({
|
|
746
|
+
entryType: kind,
|
|
747
|
+
value: option.value,
|
|
748
|
+
validation: validation
|
|
749
|
+
}, valueSchemas))?.map(issue => `at options.list.${index}.value: ${issue}`) ?? []), seen = /* @__PURE__ */ new Set;
|
|
750
|
+
for (const option of options.list) seen.has(option.value) && issues.push(`duplicate option value ${JSON.stringify(option.value)}`),
|
|
751
|
+
seen.add(option.value);
|
|
752
|
+
return issues.length === 0 ? void 0 : issues;
|
|
1098
753
|
}
|
|
1099
754
|
|
|
1100
|
-
function
|
|
1101
|
-
|
|
1102
|
-
for (const [k, value] of Object.entries(obj)) value !== void 0 && (out[k] = value);
|
|
1103
|
-
return out;
|
|
755
|
+
function choiceValueIssues(options, value) {
|
|
756
|
+
if (!(options === void 0 || value === null || value === void 0)) return options.list.some(option => Object.is(option.value, value)) ? void 0 : [ `value ${JSON.stringify(value)} is not declared in \`options.list\`; expected one of ${options.list.map(option => JSON.stringify(option.value)).join(", ")}` ];
|
|
1104
757
|
}
|
|
1105
758
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
759
|
+
const fieldValueSchemas = {
|
|
760
|
+
"doc.ref": v__namespace.union([ v__namespace.null(), GdrShape ]),
|
|
761
|
+
"doc.refs": v__namespace.array(GdrShape),
|
|
762
|
+
subject: v__namespace.union([ v__namespace.null(), GdrShape ]),
|
|
763
|
+
"release.ref": v__namespace.union([ v__namespace.null(), ReleaseRefShape ]),
|
|
764
|
+
string: NullableString,
|
|
765
|
+
text: NullableString,
|
|
766
|
+
number: NullableNumber,
|
|
767
|
+
progress: NullableProgress,
|
|
768
|
+
boolean: NullableBoolean,
|
|
769
|
+
date: NullableDate,
|
|
770
|
+
dueDate: NullableDate,
|
|
771
|
+
datetime: NullableDateTime,
|
|
772
|
+
dueDatetime: NullableDateTime,
|
|
773
|
+
url: NullableUrl,
|
|
774
|
+
actor: v__namespace.union([ v__namespace.null(), ActorShape ]),
|
|
775
|
+
assignee: AssigneeListShape,
|
|
776
|
+
assignees: v__namespace.array(AssigneeShape)
|
|
777
|
+
}, WritePrincipalId = v__namespace.pipe(NonEmptyString$1, v__namespace.rawTransform(({dataset: dataset, addIssue: addIssue}) => {
|
|
778
|
+
const classified = classifyPrincipalId(dataset.value);
|
|
779
|
+
return classified.namespace === "global" || classified.namespace === "robot" ? dataset.value : classified.namespace === "project" && classified.globalId !== void 0 ? classified.globalId : (addIssue({
|
|
780
|
+
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.`
|
|
781
|
+
}), dataset.value);
|
|
782
|
+
})), ActorWriteShape = tolerantObject()({
|
|
783
|
+
kind: v__namespace.picklist(ACTOR_KINDS),
|
|
784
|
+
id: WritePrincipalId,
|
|
785
|
+
roles: v__namespace.exactOptional(v__namespace.array(v__namespace.string())),
|
|
786
|
+
onBehalfOf: v__namespace.exactOptional(v__namespace.string())
|
|
787
|
+
}), AssigneePrincipalId = v__namespace.pipe(WritePrincipalId, v__namespace.check(id => classifyPrincipalId(id).namespace !== "robot", "robot principals cannot be assignment members")), AssigneeWriteShape = v__namespace.union([ tolerantObject()({
|
|
788
|
+
type: v__namespace.literal("user"),
|
|
789
|
+
id: AssigneePrincipalId
|
|
790
|
+
}), tolerantObject()({
|
|
791
|
+
type: v__namespace.literal("role"),
|
|
792
|
+
role: NonEmptyString$1
|
|
793
|
+
}) ]), SingularAssigneeWriteShape = v__namespace.pipe(v__namespace.union([ AssigneeWriteShape, v__namespace.array(AssigneeWriteShape) ]), v__namespace.transform(normalizeAssignmentMembers), v__namespace.check(members => members.filter(member => member.type === "user").length <= 1, "must contain at most one user member")), valueSchemas = {
|
|
794
|
+
...fieldValueSchemas,
|
|
795
|
+
actor: v__namespace.nullable(ActorWriteShape),
|
|
796
|
+
assignee: SingularAssigneeWriteShape,
|
|
797
|
+
assignees: v__namespace.array(AssigneeWriteShape),
|
|
798
|
+
query: v__namespace.any()
|
|
799
|
+
};
|
|
1109
800
|
|
|
1110
|
-
|
|
1111
|
-
const {
|
|
1112
|
-
return
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
dataset: snapshot.docs
|
|
801
|
+
function shapeValueSchema(args) {
|
|
802
|
+
const {shape: shape, leaf: leaf} = args;
|
|
803
|
+
if (shape.type === "object") return objectSchema({
|
|
804
|
+
fields: shape.fields ?? [],
|
|
805
|
+
leaf: leaf
|
|
1116
806
|
});
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const result = await runGroq({
|
|
1127
|
-
groq: groq2,
|
|
1128
|
-
params: args.params,
|
|
1129
|
-
snapshot: args.snapshot
|
|
1130
|
-
}), outcome = groqConditionDescribe.conditionOutcome(result);
|
|
1131
|
-
out[name] = outcome === "unevaluable" ? null : outcome === "satisfied";
|
|
1132
|
-
}
|
|
1133
|
-
return out;
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
async function runGroq({groq: groq2, params: params, snapshot: snapshot}) {
|
|
1137
|
-
return groqConditionDescribe.runGroq({
|
|
1138
|
-
groq: groq2,
|
|
1139
|
-
params: params,
|
|
1140
|
-
dataset: snapshot.docs
|
|
807
|
+
if (shape.type === "array") return v__namespace.array(objectSchema({
|
|
808
|
+
fields: shape.of ?? [],
|
|
809
|
+
leaf: leaf
|
|
810
|
+
}));
|
|
811
|
+
const schema = leaf[shape.type] ?? v__namespace.any();
|
|
812
|
+
return constrainedScalarSchema({
|
|
813
|
+
schema: schema,
|
|
814
|
+
entryType: shape.type,
|
|
815
|
+
...shape
|
|
1141
816
|
});
|
|
1142
817
|
}
|
|
1143
818
|
|
|
1144
|
-
function
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
try {
|
|
1148
|
-
tree = groqJs.parse(groq2);
|
|
1149
|
-
} catch (err) {
|
|
1150
|
-
issues.push(errorMessage(err));
|
|
1151
|
-
}
|
|
1152
|
-
if (/\*\s*\[\s*_type\b/.test(groq2) && issues.push("condition scans by `_type` — that's a discovery query, not a predicate. Conditions evaluate against the in-memory snapshot (instance + ancestors + field-declared docs). To bring extra docs in scope, declare a `doc.ref` (or `doc.refs`) field entry on the workflow or this stage. For lake scans like \"all articles in this release\", use a spawn action's `forEach` instead."),
|
|
1153
|
-
boundVars !== void 0 && tree !== void 0) for (const name of conditionParameterNames(groq2)) boundVars.includes(name) || issues.push(`reads $${name}, which this scope does not bind — an unbound variable evaluates to GROQ null, so the condition silently never matches. Bound here: ` + boundVars.map(n => `$${n}`).join(", "));
|
|
1154
|
-
return issues;
|
|
819
|
+
function scalarMeasurement(entryType, value) {
|
|
820
|
+
if ((entryType === "number" || entryType === "progress") && typeof value == "number") return value;
|
|
821
|
+
if ((entryType === "string" || entryType === "text") && typeof value == "string") return value.length;
|
|
1155
822
|
}
|
|
1156
823
|
|
|
1157
|
-
function
|
|
1158
|
-
const
|
|
1159
|
-
return
|
|
1160
|
-
node.type === "Parameter" && typeof node.name == "string" && read.add(node.name);
|
|
1161
|
-
}), read;
|
|
824
|
+
function scalarBoundIssue(args) {
|
|
825
|
+
const {entryType: entryType, measured: measured, bound: bound, limit: limit} = args;
|
|
826
|
+
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}`;
|
|
1162
827
|
}
|
|
1163
828
|
|
|
1164
|
-
function
|
|
1165
|
-
|
|
829
|
+
function scalarValidationIssues(args) {
|
|
830
|
+
const {entryType: entryType, validation: validation, value: value} = args;
|
|
831
|
+
if (validation === void 0 || value === null || value === void 0) return;
|
|
832
|
+
const measured = scalarMeasurement(entryType, value);
|
|
833
|
+
if (measured === void 0) return;
|
|
834
|
+
const issues = [ scalarBoundIssue({
|
|
835
|
+
entryType: entryType,
|
|
836
|
+
measured: measured,
|
|
837
|
+
bound: validation.min,
|
|
838
|
+
limit: "min"
|
|
839
|
+
}), scalarBoundIssue({
|
|
840
|
+
entryType: entryType,
|
|
841
|
+
measured: measured,
|
|
842
|
+
bound: validation.max,
|
|
843
|
+
limit: "max"
|
|
844
|
+
}) ].filter(issue => issue !== void 0);
|
|
845
|
+
return issues.length === 0 ? void 0 : issues;
|
|
1166
846
|
}
|
|
1167
847
|
|
|
1168
|
-
function
|
|
1169
|
-
const
|
|
1170
|
-
return
|
|
848
|
+
function constrainedScalarSchema(args) {
|
|
849
|
+
const {schema: schema, entryType: entryType, options: options, validation: validation} = args;
|
|
850
|
+
return options === void 0 && validation === void 0 ? schema : v__namespace.pipe(schema, v__namespace.check(value => choiceValueIssues(options, value) === void 0 && scalarValidationIssues({
|
|
851
|
+
entryType: entryType,
|
|
852
|
+
validation: validation,
|
|
853
|
+
value: value
|
|
854
|
+
}) === void 0, issue => [ ...choiceValueIssues(options, issue.input) ?? [], ...scalarValidationIssues({
|
|
855
|
+
entryType: entryType,
|
|
856
|
+
validation: validation,
|
|
857
|
+
value: issue.input
|
|
858
|
+
}) ?? [] ].join("; ")));
|
|
1171
859
|
}
|
|
1172
860
|
|
|
1173
|
-
function
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
if (chain !== void 0) {
|
|
1181
|
-
const [name, ...tail] = chain, path = tail.length > 0 ? tail.join(".") : void 0;
|
|
1182
|
-
reads.set(`${name}.${path ?? ""}`, {
|
|
1183
|
-
name: name,
|
|
1184
|
-
path: path
|
|
1185
|
-
});
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
for (const value of Object.values(node)) collectFieldReads(value, reads);
|
|
861
|
+
function objectSchema(args) {
|
|
862
|
+
const {fields: fields, leaf: leaf} = args, entries = /* @__PURE__ */ Object.create(null);
|
|
863
|
+
for (const f of fields) entries[f.name] = v__namespace.optional(shapeValueSchema({
|
|
864
|
+
shape: f,
|
|
865
|
+
leaf: leaf
|
|
866
|
+
}));
|
|
867
|
+
return v__namespace.looseObject(entries);
|
|
1189
868
|
}
|
|
1190
869
|
|
|
1191
|
-
function
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
current = base;
|
|
1201
|
-
}
|
|
870
|
+
function wholeValueSchema(args) {
|
|
871
|
+
const {entryType: entryType, shape: shape, leaf: leaf} = args;
|
|
872
|
+
return entryType === "object" ? v__namespace.union([ v__namespace.null(), objectSchema({
|
|
873
|
+
fields: shape.fields ?? [],
|
|
874
|
+
leaf: leaf
|
|
875
|
+
}) ]) : entryType === "array" ? v__namespace.array(objectSchema({
|
|
876
|
+
fields: shape.of ?? [],
|
|
877
|
+
leaf: leaf
|
|
878
|
+
})) : leaf[entryType];
|
|
1202
879
|
}
|
|
1203
880
|
|
|
1204
|
-
function
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
key: node.name
|
|
1212
|
-
});
|
|
1213
|
-
}), reads;
|
|
881
|
+
function appendItemSchema(entryType, shape) {
|
|
882
|
+
if (entryType === "array") return objectSchema({
|
|
883
|
+
fields: shape.of ?? [],
|
|
884
|
+
leaf: valueSchemas
|
|
885
|
+
});
|
|
886
|
+
if (entryType === "doc.refs") return GdrShape;
|
|
887
|
+
if (entryType === "assignee" || entryType === "assignees") return AssigneeWriteShape;
|
|
1214
888
|
}
|
|
1215
889
|
|
|
1216
|
-
function
|
|
1217
|
-
|
|
890
|
+
function rejectedRefTypes(args) {
|
|
891
|
+
const {entryType: entryType, types: types, value: value} = args;
|
|
892
|
+
if (types === void 0 || value === null || value === void 0) return [];
|
|
893
|
+
if (!refKindAcceptsTypes(entryType)) return [];
|
|
894
|
+
let items = [ value ];
|
|
895
|
+
return entryType === "doc.refs" && (items = Array.isArray(value) ? value : []),
|
|
896
|
+
[ ...new Set(items.map(gdrTypeOf).filter(t => t !== void 0 && !types.includes(t))) ];
|
|
1218
897
|
}
|
|
1219
898
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
PipeFuncCall: "args"
|
|
1226
|
-
};
|
|
1227
|
-
|
|
1228
|
-
function nodeReadsRoot(node, depth) {
|
|
1229
|
-
if (Array.isArray(node)) return node.some(item => nodeReadsRoot(item, depth));
|
|
1230
|
-
if (typeof node != "object" || node === null) return !1;
|
|
1231
|
-
const typed = node;
|
|
1232
|
-
if (depth === 0 && typed.type === "This" || depth === 0 && typed.type === "AccessAttribute" && typed.base === void 0) return !0;
|
|
1233
|
-
if (typed.type === "Parent") {
|
|
1234
|
-
const climb = typeof typed.n == "number" ? typed.n : 1;
|
|
1235
|
-
return depth - climb <= 0;
|
|
1236
|
-
}
|
|
1237
|
-
const scopedChild = typeof typed.type == "string" ? SCOPED_CHILD[typed.type] : void 0;
|
|
1238
|
-
return scopedChild !== void 0 ? Object.entries(node).some(([key, value]) => nodeReadsRoot(value, key === scopedChild ? depth + 1 : depth)) : Object.values(node).some(value => nodeReadsRoot(value, depth));
|
|
899
|
+
function refTypeIssues(args) {
|
|
900
|
+
const rejected = rejectedRefTypes(args);
|
|
901
|
+
if (rejected.length === 0) return;
|
|
902
|
+
const accepts = (args.types ?? []).map(t => `"${t}"`).join(", ");
|
|
903
|
+
return rejected.map(t => `document type "${t}" is not accepted — this entry accepts ${accepts} (a GDR's \`type\` names the target document's schema type)`);
|
|
1239
904
|
}
|
|
1240
905
|
|
|
1241
|
-
function
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
906
|
+
function gdrTypeOf(item) {
|
|
907
|
+
if (typeof item != "object" || item === null) return;
|
|
908
|
+
const t = item.type;
|
|
909
|
+
return typeof t == "string" ? t : void 0;
|
|
1247
910
|
}
|
|
1248
911
|
|
|
1249
|
-
function
|
|
1250
|
-
|
|
1251
|
-
for (const item of node) walkAstNodes(item, visit);
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
if (!(typeof node != "object" || node === null)) {
|
|
1255
|
-
visit(node);
|
|
1256
|
-
for (const value of Object.values(node)) walkAstNodes(value, visit);
|
|
1257
|
-
}
|
|
912
|
+
function parseFieldValue(args) {
|
|
913
|
+
return checkValueAgainst(args, valueSchemas);
|
|
1258
914
|
}
|
|
1259
915
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
916
|
+
function checkValueAgainst(args, leaf) {
|
|
917
|
+
const schema = wholeValueSchema({
|
|
918
|
+
entryType: args.entryType,
|
|
919
|
+
shape: args,
|
|
920
|
+
leaf: leaf
|
|
921
|
+
});
|
|
922
|
+
if (schema === void 0) return {
|
|
923
|
+
issues: [ `unknown field entry type ${args.entryType}` ]
|
|
924
|
+
};
|
|
925
|
+
const result = v__namespace.safeParse(schema, args.value);
|
|
926
|
+
if (!result.success) return {
|
|
927
|
+
issues: formatIssues(result.issues)
|
|
928
|
+
};
|
|
929
|
+
const postIssues = refTypeIssues({
|
|
930
|
+
entryType: args.entryType,
|
|
931
|
+
types: args.types,
|
|
932
|
+
value: args.value
|
|
933
|
+
}) ?? choiceValueIssues(args.options, args.value) ?? scalarValidationIssues({
|
|
934
|
+
entryType: args.entryType,
|
|
935
|
+
validation: args.validation,
|
|
936
|
+
value: args.value
|
|
937
|
+
}) ?? assignmentValueIssues(args, result.output);
|
|
938
|
+
return postIssues !== void 0 ? {
|
|
939
|
+
issues: postIssues
|
|
940
|
+
} : {
|
|
941
|
+
output: result.output
|
|
942
|
+
};
|
|
1264
943
|
}
|
|
1265
944
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
function releaseDocId(releaseName) {
|
|
1269
|
-
return `_.releases.${releaseName}`;
|
|
945
|
+
function assignmentIdentity(assignee) {
|
|
946
|
+
return assignee.type === "user" ? `user:${assignee.id}` : `role:${assignee.role}`;
|
|
1270
947
|
}
|
|
1271
|
-
|
|
1272
|
-
function
|
|
1273
|
-
|
|
1274
|
-
return
|
|
1275
|
-
id: gdrFromResource(res, releaseDocId(releaseName)),
|
|
1276
|
-
type: "system.release",
|
|
1277
|
-
releaseName: releaseName
|
|
1278
|
-
};
|
|
948
|
+
|
|
949
|
+
function assignmentCandidate(value) {
|
|
950
|
+
const parsed = v__namespace.safeParse(AssigneeShape, value);
|
|
951
|
+
return parsed.success ? parsed.output : void 0;
|
|
1279
952
|
}
|
|
1280
953
|
|
|
1281
|
-
function
|
|
1282
|
-
|
|
954
|
+
function assignmentValueIssues(args, value) {
|
|
955
|
+
const retained = previousAssignmentIdentities(args), issues = [];
|
|
956
|
+
return visitAssignmentEntries({
|
|
957
|
+
entryType: args.entryType,
|
|
958
|
+
value: value,
|
|
959
|
+
roles: args.roles,
|
|
960
|
+
fields: args.fields,
|
|
961
|
+
of: args.of,
|
|
962
|
+
path: "$"
|
|
963
|
+
}, ({assignee: assignee, roles: roles, path: path}) => {
|
|
964
|
+
consumeRetainedAssignment({
|
|
965
|
+
retained: retained,
|
|
966
|
+
path: path,
|
|
967
|
+
identity: assignmentIdentity(assignee)
|
|
968
|
+
}) || issues.push(...assignmentCandidateIssues({
|
|
969
|
+
roles: roles,
|
|
970
|
+
memberRoles: args.memberRoles,
|
|
971
|
+
roleAliases: args.roleAliases
|
|
972
|
+
}, assignee));
|
|
973
|
+
}), issues.length === 0 ? void 0 : issues;
|
|
1283
974
|
}
|
|
1284
975
|
|
|
1285
|
-
function
|
|
1286
|
-
|
|
976
|
+
function consumeRetainedAssignment(args) {
|
|
977
|
+
const identities = args.retained.get(args.path), remaining = identities?.get(args.identity) ?? 0;
|
|
978
|
+
return remaining === 0 || identities === void 0 ? !1 : (remaining === 1 ? identities.delete(args.identity) : identities.set(args.identity, remaining - 1),
|
|
979
|
+
!0);
|
|
1287
980
|
}
|
|
1288
981
|
|
|
1289
|
-
function
|
|
1290
|
-
|
|
982
|
+
function previousAssignmentIdentities(args) {
|
|
983
|
+
const retained = /* @__PURE__ */ new Map;
|
|
984
|
+
if (args.previousValue === void 0) return retained;
|
|
985
|
+
const schema = wholeValueSchema({
|
|
986
|
+
entryType: args.entryType,
|
|
987
|
+
shape: args,
|
|
988
|
+
leaf: valueSchemas
|
|
989
|
+
});
|
|
990
|
+
if (schema === void 0) return retained;
|
|
991
|
+
const previous = v__namespace.safeParse(schema, args.previousValue);
|
|
992
|
+
return previous.success && visitAssignmentEntries({
|
|
993
|
+
entryType: args.entryType,
|
|
994
|
+
value: previous.output,
|
|
995
|
+
roles: args.roles,
|
|
996
|
+
fields: args.fields,
|
|
997
|
+
of: args.of,
|
|
998
|
+
path: "$"
|
|
999
|
+
}, ({assignee: assignee, path: path}) => {
|
|
1000
|
+
const identities = retained.get(path) ?? /* @__PURE__ */ new Map, identity = assignmentIdentity(assignee);
|
|
1001
|
+
identities.set(identity, (identities.get(identity) ?? 0) + 1), retained.set(path, identities);
|
|
1002
|
+
}), retained;
|
|
1291
1003
|
}
|
|
1292
1004
|
|
|
1293
|
-
function
|
|
1294
|
-
|
|
1005
|
+
function visitAssignmentEntries(args, visit) {
|
|
1006
|
+
if (assignmentKindAcceptsRoles(args.entryType)) {
|
|
1007
|
+
visitAssignmentCandidates(args, visit);
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
if (args.entryType === "object") {
|
|
1011
|
+
visitAssignmentFields({
|
|
1012
|
+
fields: args.fields,
|
|
1013
|
+
value: args.value,
|
|
1014
|
+
path: args.path
|
|
1015
|
+
}, visit);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (!(args.entryType !== "array" || !Array.isArray(args.value))) for (const row of args.value) visitAssignmentFields({
|
|
1019
|
+
fields: args.of,
|
|
1020
|
+
value: row,
|
|
1021
|
+
path: `${args.path}[]`
|
|
1022
|
+
}, visit);
|
|
1295
1023
|
}
|
|
1296
1024
|
|
|
1297
|
-
function
|
|
1298
|
-
|
|
1025
|
+
function visitAssignmentCandidates(args, visit) {
|
|
1026
|
+
if (args.roles === void 0) return;
|
|
1027
|
+
const indexedValues = Array.isArray(args.value) ? args.value.map(value => ({
|
|
1028
|
+
value: value,
|
|
1029
|
+
path: args.path
|
|
1030
|
+
})) : [ {
|
|
1031
|
+
value: args.value,
|
|
1032
|
+
path: args.path
|
|
1033
|
+
} ];
|
|
1034
|
+
for (const {value: value, path: path} of indexedValues) {
|
|
1035
|
+
const assignee = assignmentCandidate(value);
|
|
1036
|
+
assignee !== void 0 && visit({
|
|
1037
|
+
assignee: assignee,
|
|
1038
|
+
roles: args.roles,
|
|
1039
|
+
path: path
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1299
1042
|
}
|
|
1300
1043
|
|
|
1301
|
-
function
|
|
1302
|
-
|
|
1044
|
+
function visitAssignmentFields(args, visit) {
|
|
1045
|
+
if (!(args.fields === void 0 || !isRecord(args.value))) for (const field of args.fields) visitAssignmentShape({
|
|
1046
|
+
shape: field,
|
|
1047
|
+
value: args.value[field.name],
|
|
1048
|
+
path: `${args.path}.${field.name}`
|
|
1049
|
+
}, visit);
|
|
1303
1050
|
}
|
|
1304
1051
|
|
|
1305
|
-
function
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1052
|
+
function visitAssignmentShape(args, visit) {
|
|
1053
|
+
visitAssignmentEntries({
|
|
1054
|
+
entryType: args.shape.type,
|
|
1055
|
+
value: args.value,
|
|
1056
|
+
roles: args.shape.roles,
|
|
1057
|
+
fields: args.shape.fields,
|
|
1058
|
+
of: args.shape.of,
|
|
1059
|
+
path: args.path
|
|
1060
|
+
}, visit);
|
|
1309
1061
|
}
|
|
1310
1062
|
|
|
1311
|
-
function
|
|
1312
|
-
if (
|
|
1063
|
+
function assignmentCandidateIssues(args, assignee) {
|
|
1064
|
+
if (assignee.type === "role") return collectiveRoleIssues(args, assignee.role);
|
|
1065
|
+
const held = args.memberRoles?.[assignee.id];
|
|
1066
|
+
return held === void 0 ? args.memberRoles === void 0 ? [] : [ `user "${assignee.id}" has no available project membership for role eligibility` ] : args.roles.some(required => actorFulfillsRole({
|
|
1067
|
+
actorRoles: held,
|
|
1068
|
+
required: required,
|
|
1069
|
+
aliases: args.roleAliases
|
|
1070
|
+
})) ? [] : [ `user "${assignee.id}" does not fulfill any eligible role: ${formatEligibleRoles(args.roles)}` ];
|
|
1313
1071
|
}
|
|
1314
1072
|
|
|
1315
|
-
function
|
|
1316
|
-
|
|
1317
|
-
return columns !== void 0 && columns.has("label") && columns.has("status");
|
|
1073
|
+
function collectiveRoleIssues(args, role) {
|
|
1074
|
+
return args.roles.includes(role) ? [] : [ `collective role "${role}" is not eligible — this entry accepts ${formatEligibleRoles(args.roles)}` ];
|
|
1318
1075
|
}
|
|
1319
1076
|
|
|
1320
|
-
function
|
|
1321
|
-
|
|
1322
|
-
return columns !== void 0 && columns.has("body") && columns.has("actor") && columns.has("at");
|
|
1077
|
+
function formatEligibleRoles(roles) {
|
|
1078
|
+
return roles.map(role => `"${role}"`).join(", ");
|
|
1323
1079
|
}
|
|
1324
1080
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
description: "The parent instance's GDR URI, `null` on a root instance."
|
|
1340
|
-
}, {
|
|
1341
|
-
name: "ancestors",
|
|
1342
|
-
binding: "always",
|
|
1343
|
-
label: "the workflow's ancestors",
|
|
1344
|
-
description: "GDR URIs of the ancestor chain, root first."
|
|
1345
|
-
}, {
|
|
1346
|
-
name: "stage",
|
|
1347
|
-
binding: "always",
|
|
1348
|
-
label: "the current stage",
|
|
1349
|
-
description: "The current stage's name."
|
|
1350
|
-
}, {
|
|
1351
|
-
name: "now",
|
|
1352
|
-
binding: "always",
|
|
1353
|
-
label: "the current time",
|
|
1354
|
-
description: "The ISO clock reading shared by every condition in one evaluation pass, so they agree on the time."
|
|
1355
|
-
}, {
|
|
1356
|
-
name: "context",
|
|
1357
|
-
binding: "always",
|
|
1358
|
-
label: "start-time context",
|
|
1359
|
-
description: "The instance's `context` bag: values seeded at `startInstance` plus a parent's `spawn.context` handoff, by entry name. Written only at start — never mutated afterwards."
|
|
1360
|
-
}, {
|
|
1361
|
-
name: "effects",
|
|
1362
|
-
binding: "always",
|
|
1363
|
-
label: "automation outputs",
|
|
1364
|
-
description: "Completed effects' outputs, namespaced by effect name (`$effects['<effect>'].<output>`) — each effect's latest completed run wins. Handler fuel — transition triggers should read instance/lake state (or $effectStatus) instead."
|
|
1365
|
-
}, {
|
|
1366
|
-
name: "effectStatus",
|
|
1367
|
-
binding: "always",
|
|
1368
|
-
label: "automation status",
|
|
1369
|
-
description: "Effect name → `'done'` | `'failed'` of the latest completed run queued during the current stage entry; absent until the effect drains for this entry. Re-entry-safe: runs queued under a prior entry never count, so `$effectStatus['<effect>'] == 'done'` (or `defined($effectStatus['<effect>'])` for settled-either-way) waits for the fresh run."
|
|
1370
|
-
}, {
|
|
1371
|
-
name: "activities",
|
|
1372
|
-
binding: "always",
|
|
1373
|
-
label: "this stage's activities",
|
|
1374
|
-
description: "The current stage's activity rows, statuses included."
|
|
1375
|
-
}, {
|
|
1376
|
-
name: "allActivitiesDone",
|
|
1377
|
-
binding: "always",
|
|
1378
|
-
label: "all activities in this stage are finished",
|
|
1379
|
-
description: "Every current-stage activity is `done` or `skipped` — the default transition gate."
|
|
1380
|
-
}, {
|
|
1381
|
-
name: "anyActivityFailed",
|
|
1382
|
-
binding: "always",
|
|
1383
|
-
label: "an activity in this stage has failed",
|
|
1384
|
-
description: "Some current-stage activity is `failed`."
|
|
1385
|
-
}, {
|
|
1386
|
-
name: "actor",
|
|
1387
|
-
binding: "caller",
|
|
1388
|
-
label: "you",
|
|
1389
|
-
description: "The acting identity (id + roles); `undefined` when no caller rides the evaluation. Never holds a value in the cascade gates (deploy-rejected there)."
|
|
1390
|
-
}, {
|
|
1391
|
-
name: "assigned",
|
|
1392
|
-
binding: "caller",
|
|
1393
|
-
label: "you are assigned to this activity",
|
|
1394
|
-
description: "Whether the caller matches the activity's assignees-kind field entry (by user id, or by a role under the definition's `roleAliases`); `false` outside an activity context. Constant `false` in the cascade gates (deploy-rejected there)."
|
|
1395
|
-
}, {
|
|
1396
|
-
name: "can",
|
|
1397
|
-
binding: "caller",
|
|
1398
|
-
label: "your permissions",
|
|
1399
|
-
description: "Advisory per-permission booleans computed from the caller's grants; `undefined` without grants. Bound wherever grants ride the evaluation: the projection's rendered scope (fireAction-action filters, activity requirements, editability predicates) and the fireAction/editField commit gates. Deploy rejects it at every site that evaluates without grants: transition `when`s, activity filters, cascade-fired actions' `when`/`filter`, effect bindings, where-op `where`s, and the spawn `forEach`/`with`/`context` sites."
|
|
1400
|
-
}, {
|
|
1401
|
-
name: "attributes",
|
|
1402
|
-
binding: "caller",
|
|
1403
|
-
label: "your attributes",
|
|
1404
|
-
description: "Advisory org-level User Attributes for the caller (Enterprise — same values as lake `user::attributes()`), keyed by attribute name with each active scalar or array value. `undefined` on expected HTTP absence; unexpected fetch failures throw; empty page binds `{}`. Bound wherever grants ride the evaluation (same sites as `$can`). Soft-gate paths fetch at most 100 attributes (no further pages) and warn when the envelope reports `hasMore: true` (partial bag still binds). Not a security boundary — the Content Lake remains the only enforcement point."
|
|
1405
|
-
}, {
|
|
1406
|
-
name: "row",
|
|
1407
|
-
binding: "spawn",
|
|
1408
|
-
label: "the spawned row",
|
|
1409
|
-
description: "One `spawn.forEach` result row, bound while its `with` map evaluates — and the row under test while a where-op `where` evaluates (per row). Deploy rejects a read at every other site (including `spawn.forEach` itself and `spawn.context`), where it is GROQ null."
|
|
1410
|
-
}, {
|
|
1411
|
-
name: "params",
|
|
1412
|
-
binding: "caller",
|
|
1413
|
-
label: "the action's arguments",
|
|
1414
|
-
description: "The firing action's args — they hold values only while a fireAction-fired action's effect bindings and where-op `where`s evaluate (spawn sites don't bind it at all, and a cascade-fired action has no caller to supply args). Deploy rejects a read in the cascade gates, in a cascade-fired action's payload, and in the caller-bound projection (action filters, requirements, editable predicates): args exist only once the caller fires the action, after those sites have evaluated."
|
|
1415
|
-
}, {
|
|
1416
|
-
name: "subworkflows",
|
|
1417
|
-
binding: "always",
|
|
1418
|
-
label: "the spawned subworkflows",
|
|
1419
|
-
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`."
|
|
1420
|
-
} ], 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 = [ {
|
|
1421
|
-
name: "tag",
|
|
1422
|
-
label: "this engine tag",
|
|
1423
|
-
description: "The engine's tag partition — scope `*[...]` instance scans with `tag == $tag`."
|
|
1424
|
-
}, {
|
|
1425
|
-
name: "definition",
|
|
1426
|
-
label: "this workflow definition",
|
|
1427
|
-
description: "The `name` of the definition under evaluation (its own start block binds it)."
|
|
1428
|
-
}, {
|
|
1429
|
-
name: "now",
|
|
1430
|
-
label: "the current time",
|
|
1431
|
-
description: "The ISO clock reading of the evaluating engine."
|
|
1432
|
-
} ], START_REQUIREMENT_VARS = [ ...START_FILTER_VARS, {
|
|
1433
|
-
name: "fields",
|
|
1434
|
-
label: "the start's input fields",
|
|
1435
|
-
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."
|
|
1436
|
-
} ], GUARD_PREDICATE_VARS = [ {
|
|
1437
|
-
name: "guard",
|
|
1438
|
-
description: "The guard document itself (its `metadata` carries deploy-time resolved values)."
|
|
1439
|
-
}, {
|
|
1440
|
-
name: "mutation",
|
|
1441
|
-
description: "The attempted mutation — `mutation.action` is the write kind being gated."
|
|
1442
|
-
} ], NonEmptyString = v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1, "must be a non-empty string"));
|
|
1081
|
+
function issuesOf(check) {
|
|
1082
|
+
return "issues" in check ? check.issues : void 0;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
function validateFieldValue(args) {
|
|
1086
|
+
const check = checkValueAgainst(args, valueSchemas);
|
|
1087
|
+
if ("issues" in check) throw new FieldValueShapeError({
|
|
1088
|
+
entryType: args.entryType,
|
|
1089
|
+
entryName: args.entryName,
|
|
1090
|
+
issues: check.issues,
|
|
1091
|
+
mode: "value"
|
|
1092
|
+
});
|
|
1093
|
+
return check.output;
|
|
1094
|
+
}
|
|
1443
1095
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1096
|
+
const BARE_ID_SOURCE = "[A-Za-z0-9_][A-Za-z0-9._-]{0,127}", BARE_ID_RE = new RegExp(`^${BARE_ID_SOURCE}$`), ALIAS_REF_RE = new RegExp(`^@${RESOURCE_ALIAS_NAME_SOURCE}:${BARE_ID_SOURCE}$`);
|
|
1097
|
+
|
|
1098
|
+
function isBareDocumentId(id) {
|
|
1099
|
+
return BARE_ID_RE.test(id) && !id.includes("..");
|
|
1446
1100
|
}
|
|
1447
1101
|
|
|
1448
|
-
|
|
1102
|
+
function isAuthoringRefId(id) {
|
|
1103
|
+
if (id.startsWith("@")) {
|
|
1104
|
+
const separator = id.indexOf(":");
|
|
1105
|
+
return ALIAS_REF_RE.test(id) && isBareDocumentId(id.slice(separator + 1));
|
|
1106
|
+
}
|
|
1107
|
+
return id.includes(":") ? isGdrUri(id) : isBareDocumentId(id);
|
|
1108
|
+
}
|
|
1449
1109
|
|
|
1450
|
-
function
|
|
1451
|
-
return (
|
|
1110
|
+
function isBareSeedId(id) {
|
|
1111
|
+
return isBareDocumentId(id);
|
|
1452
1112
|
}
|
|
1453
1113
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1114
|
+
const AuthoringRefId = v__namespace.pipe(v__namespace.string(), v__namespace.check(isAuthoringRefId, "must be a bare document id, a GDR URI, or a portable `@<alias>:<id>` reference")), AuthoringGdrShape = v__namespace.looseObject({
|
|
1115
|
+
id: AuthoringRefId,
|
|
1116
|
+
type: NonEmptyString$1
|
|
1117
|
+
}), seedValueSchemas = {
|
|
1118
|
+
...valueSchemas,
|
|
1119
|
+
"doc.ref": v__namespace.union([ v__namespace.null(), AuthoringGdrShape ]),
|
|
1120
|
+
"doc.refs": v__namespace.array(AuthoringGdrShape),
|
|
1121
|
+
subject: v__namespace.union([ v__namespace.null(), AuthoringGdrShape ]),
|
|
1122
|
+
"release.ref": v__namespace.union([ v__namespace.null(), v__namespace.looseObject({
|
|
1123
|
+
id: AuthoringRefId,
|
|
1124
|
+
type: v__namespace.literal("system.release"),
|
|
1125
|
+
releaseName: NonEmptyString$1
|
|
1126
|
+
}) ])
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
function checkLiteralSeed(args) {
|
|
1130
|
+
return args.value === null ? [ "a literal seed cannot be null — omit `initialValue` to start the field empty" ] : issuesOf(checkValueAgainst(args, seedValueSchemas));
|
|
1456
1131
|
}
|
|
1457
1132
|
|
|
1458
|
-
function
|
|
1459
|
-
|
|
1133
|
+
function validateFieldAppendItem(args) {
|
|
1134
|
+
const schema = appendItemSchema(args.entryType, args);
|
|
1135
|
+
if (schema === void 0) throw new FieldValueShapeError({
|
|
1136
|
+
entryType: args.entryType,
|
|
1137
|
+
entryName: args.entryName,
|
|
1138
|
+
issues: [ `field entry type ${args.entryType} does not support append` ],
|
|
1139
|
+
mode: "item"
|
|
1140
|
+
});
|
|
1141
|
+
const result = v__namespace.safeParse(schema, args.item);
|
|
1142
|
+
if (!result.success) throw new FieldValueShapeError({
|
|
1143
|
+
entryType: args.entryType,
|
|
1144
|
+
entryName: args.entryName,
|
|
1145
|
+
issues: formatIssues(result.issues),
|
|
1146
|
+
mode: "item"
|
|
1147
|
+
});
|
|
1148
|
+
const assignmentArgs = {
|
|
1149
|
+
entryType: appendAssignmentEntryType(args.entryType),
|
|
1150
|
+
value: result.output,
|
|
1151
|
+
roles: args.roles,
|
|
1152
|
+
fields: args.entryType === "array" ? args.of : void 0,
|
|
1153
|
+
memberRoles: args.memberRoles,
|
|
1154
|
+
roleAliases: args.roleAliases
|
|
1155
|
+
}, assignmentIssues = assignmentValueIssues(assignmentArgs, result.output);
|
|
1156
|
+
if (assignmentIssues !== void 0) throw new FieldValueShapeError({
|
|
1157
|
+
entryType: args.entryType,
|
|
1158
|
+
entryName: args.entryName,
|
|
1159
|
+
issues: assignmentIssues,
|
|
1160
|
+
mode: "item"
|
|
1161
|
+
});
|
|
1162
|
+
const typeIssues = refTypeIssues({
|
|
1163
|
+
entryType: args.entryType,
|
|
1164
|
+
types: args.types,
|
|
1165
|
+
value: [ args.item ]
|
|
1166
|
+
});
|
|
1167
|
+
if (typeIssues !== void 0) throw new FieldValueShapeError({
|
|
1168
|
+
entryType: args.entryType,
|
|
1169
|
+
entryName: args.entryName,
|
|
1170
|
+
issues: typeIssues,
|
|
1171
|
+
mode: "item"
|
|
1172
|
+
});
|
|
1173
|
+
return result.output;
|
|
1460
1174
|
}
|
|
1461
1175
|
|
|
1462
|
-
function
|
|
1463
|
-
|
|
1464
|
-
if (path.has(node)) return "(circular)";
|
|
1465
|
-
path.add(node);
|
|
1466
|
-
try {
|
|
1467
|
-
const schema = node;
|
|
1468
|
-
return containerShape(schema, path) ?? leafShape(schema);
|
|
1469
|
-
} finally {
|
|
1470
|
-
path.delete(node);
|
|
1471
|
-
}
|
|
1176
|
+
function appendAssignmentEntryType(entryType) {
|
|
1177
|
+
return entryType === "assignee" || entryType === "assignees" ? "assignee" : entryType === "array" ? "object" : entryType;
|
|
1472
1178
|
}
|
|
1473
1179
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
array: walkSchemaShape(schema.item, path)
|
|
1482
|
-
}),
|
|
1483
|
-
record: (schema, path) => ({
|
|
1484
|
-
record: walkSchemaShape(schema.value, path)
|
|
1485
|
-
}),
|
|
1486
|
-
union: unionWalker,
|
|
1487
|
-
variant: unionWalker,
|
|
1488
|
-
lazy: (schema, path) => walkSchemaShape(schema.getter(void 0), path),
|
|
1489
|
-
exact_optional: unwrapWalker,
|
|
1490
|
-
optional: unwrapWalker,
|
|
1491
|
-
nullable: unwrapWalker
|
|
1492
|
-
};
|
|
1180
|
+
function formatIssues(issues, formatMessage = issue => issue.message) {
|
|
1181
|
+
const formatOne = (issue, prefix) => {
|
|
1182
|
+
const keys = [ ...prefix, ...issue.path?.map(p => p.key) ?? [] ], sub = issue.issues?.filter(candidate => candidate.expected !== "null");
|
|
1183
|
+
return sub !== void 0 && sub.length > 0 ? sub.flatMap(candidate => formatOne(candidate, keys)) : [ `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${formatMessage(issue)}` ];
|
|
1184
|
+
};
|
|
1185
|
+
return issues.flatMap(issue => formatOne(issue, []));
|
|
1186
|
+
}
|
|
1493
1187
|
|
|
1494
|
-
|
|
1495
|
-
|
|
1188
|
+
const ACTIVITY_STATUSES = groqConditionDescribe._exhaustiveOptions()([ "active", "done", "skipped", "failed" ]), TERMINAL_ACTIVITY_STATUSES = groqConditionDescribe._exhaustiveOptions()([ "done", "skipped", "failed" ]);
|
|
1189
|
+
|
|
1190
|
+
function isTerminalActivityStatus(status) {
|
|
1191
|
+
return TERMINAL_ACTIVITY_STATUSES.includes(status);
|
|
1496
1192
|
}
|
|
1497
1193
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1194
|
+
const SIGNAL_SEMANTICS = [ "signal.positive", "signal.caution", "signal.critical" ], DECISION_SEMANTICS = [ "decision.accept", "decision.decline" ], ACTION_SEMANTICS = [ ...DECISION_SEMANTICS, ...SIGNAL_SEMANTICS ], FIELD_SCOPES = groqConditionDescribe._exhaustiveOptions()([ "workflow", "stage", "activity" ]), DOCUMENT_VALUE_PERMISSIONS = [ "create", "manage", "read", "update" ], LAKE_MUTATION_GUARD_ACTIONS = groqConditionDescribe._exhaustiveOptions()([ "create", "update", "delete" ]), MUTATION_GUARD_ACTIONS = groqConditionDescribe._exhaustiveOptions()([ ...LAKE_MUTATION_GUARD_ACTIONS, "publish", "unpublish" ]), GUARD_ACTIONS_REQUIRED_MESSAGE = "a guard must match at least one action", MUTATION_GUARD_ID_SPACES = [ "authored", "edit", "published" ];
|
|
1195
|
+
|
|
1196
|
+
function mutationGuardActionIdSpace(action) {
|
|
1197
|
+
return action === "update" ? "edit" : action === "publish" || action === "unpublish" ? "published" : "authored";
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
function mutationGuardRequiresSplitEmission(actions) {
|
|
1201
|
+
return new Set(actions.map(mutationGuardActionIdSpace)).size > 1;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const ACTIVITY_KINDS = [ "user", "service", "script", "manual", "receive" ], EXECUTOR_CLASSIFICATIONS = [ "autonomous", "interactive", "off-system", "hybrid" ], GROUP_KINDS = [ "core", "details" ], DRIVER_KINDS = [ "person", "agent", "service", "engine" ], NonEmpty = NonEmptyString$1, PositiveInt = v__namespace.pipe(v__namespace.number(), v__namespace.integer(), v__namespace.minValue(1)), GROQ_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1205
|
+
|
|
1206
|
+
function groqIdentifier(referencedAs) {
|
|
1207
|
+
return v__namespace.pipe(v__namespace.string(), v__namespace.regex(GROQ_IDENTIFIER, `must be a GROQ-safe identifier (letters, digits, underscore; not starting with a digit) because it is referenced as ${referencedAs} in GROQ conditions`));
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function picklist(options) {
|
|
1211
|
+
return v__namespace.picklist(options, invalidOptionMessage(options));
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function invalidOptionMessage(options) {
|
|
1215
|
+
return `Invalid option: expected one of ${options.map(option => `"${option}"`).join("|")}`;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
function pinned() {
|
|
1219
|
+
return (schema, ..._exact) => schema;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
const LiteralSchema = v__namespace.strictObject({
|
|
1223
|
+
type: v__namespace.literal("literal"),
|
|
1224
|
+
value: v__namespace.unknown()
|
|
1225
|
+
}), FieldReadSchema = v__namespace.strictObject({
|
|
1226
|
+
type: v__namespace.literal("fieldRead"),
|
|
1227
|
+
scope: v__namespace.optional(v__namespace.union([ v__namespace.literal("workflow"), v__namespace.literal("stage") ])),
|
|
1228
|
+
field: NonEmpty,
|
|
1229
|
+
path: v__namespace.optional(v__namespace.string())
|
|
1230
|
+
}), FieldSourceSchema = v__namespace.variant("type", [ v__namespace.strictObject({
|
|
1231
|
+
type: v__namespace.literal("input")
|
|
1232
|
+
}), v__namespace.strictObject({
|
|
1233
|
+
type: v__namespace.literal("query"),
|
|
1234
|
+
query: NonEmpty
|
|
1235
|
+
}), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.variant("type", [ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
|
|
1236
|
+
type: v__namespace.literal("param"),
|
|
1237
|
+
param: NonEmpty
|
|
1238
|
+
}), v__namespace.strictObject({
|
|
1239
|
+
type: v__namespace.literal("actor")
|
|
1240
|
+
}), v__namespace.strictObject({
|
|
1241
|
+
type: v__namespace.literal("now")
|
|
1242
|
+
}), v__namespace.strictObject({
|
|
1243
|
+
type: v__namespace.literal("self")
|
|
1244
|
+
}), v__namespace.strictObject({
|
|
1245
|
+
type: v__namespace.literal("stage")
|
|
1246
|
+
}), v__namespace.strictObject({
|
|
1247
|
+
type: v__namespace.literal("object"),
|
|
1248
|
+
fields: v__namespace.record(NonEmpty, ValueExprSchema)
|
|
1249
|
+
}) ])), StoredFieldRefSchema = v__namespace.strictObject({
|
|
1250
|
+
scope: picklist(FIELD_SCOPES),
|
|
1251
|
+
field: NonEmpty
|
|
1252
|
+
}), AuthoringFieldRefSchema = v__namespace.strictObject({
|
|
1253
|
+
scope: v__namespace.optional(picklist(FIELD_SCOPES)),
|
|
1254
|
+
field: NonEmpty
|
|
1255
|
+
}), HREF_SCHEMES = [ "http:", "https:" ];
|
|
1256
|
+
|
|
1257
|
+
function isHttpUrl(value) {
|
|
1258
|
+
try {
|
|
1259
|
+
return HREF_SCHEMES.includes(new URL(value).protocol);
|
|
1260
|
+
} catch {
|
|
1261
|
+
return !1;
|
|
1503
1262
|
}
|
|
1504
|
-
return Object.fromEntries(Object.entries(out).sort(([a], [b]) => a < b ? -1 : 1));
|
|
1505
1263
|
}
|
|
1506
1264
|
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
return
|
|
1265
|
+
const UrlString = v__namespace.pipe(v__namespace.string(), v__namespace.url("must be a valid URL"), v__namespace.check(isHttpUrl, "must be an http(s) URL"));
|
|
1266
|
+
|
|
1267
|
+
function manualTargetSchema(ref) {
|
|
1268
|
+
return v__namespace.variant("type", [ v__namespace.strictObject({
|
|
1269
|
+
type: v__namespace.literal("url"),
|
|
1270
|
+
url: UrlString
|
|
1271
|
+
}), v__namespace.strictObject({
|
|
1272
|
+
type: v__namespace.literal("field"),
|
|
1273
|
+
field: ref
|
|
1274
|
+
}) ]);
|
|
1511
1275
|
}
|
|
1512
1276
|
|
|
1513
|
-
const
|
|
1277
|
+
const StoredManualTargetSchema = pinned()(manualTargetSchema(StoredFieldRefSchema)), AuthoringManualTargetSchema = pinned()(manualTargetSchema(v__namespace.union([ NonEmpty, AuthoringFieldRefSchema ]))), ConditionSchema = NonEmpty;
|
|
1514
1278
|
|
|
1515
|
-
function
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1279
|
+
function opSchemas(targetSchema) {
|
|
1280
|
+
return [ v__namespace.strictObject({
|
|
1281
|
+
type: v__namespace.literal("field.set"),
|
|
1282
|
+
target: targetSchema,
|
|
1283
|
+
value: ValueExprSchema
|
|
1284
|
+
}), v__namespace.strictObject({
|
|
1285
|
+
type: v__namespace.literal("field.setIfMissing"),
|
|
1286
|
+
target: targetSchema,
|
|
1287
|
+
value: ValueExprSchema
|
|
1288
|
+
}), v__namespace.strictObject({
|
|
1289
|
+
type: v__namespace.literal("field.unset"),
|
|
1290
|
+
target: targetSchema
|
|
1291
|
+
}), v__namespace.strictObject({
|
|
1292
|
+
type: v__namespace.literal("field.append"),
|
|
1293
|
+
target: targetSchema,
|
|
1294
|
+
value: ValueExprSchema
|
|
1295
|
+
}), v__namespace.strictObject({
|
|
1296
|
+
type: v__namespace.literal("field.inc"),
|
|
1297
|
+
target: targetSchema,
|
|
1298
|
+
value: v__namespace.optional(ValueExprSchema)
|
|
1299
|
+
}), v__namespace.strictObject({
|
|
1300
|
+
type: v__namespace.literal("field.dec"),
|
|
1301
|
+
target: targetSchema,
|
|
1302
|
+
value: v__namespace.optional(ValueExprSchema)
|
|
1303
|
+
}), v__namespace.strictObject({
|
|
1304
|
+
type: v__namespace.literal("field.updateWhere"),
|
|
1305
|
+
target: targetSchema,
|
|
1306
|
+
where: ConditionSchema,
|
|
1307
|
+
value: ValueExprSchema
|
|
1308
|
+
}), v__namespace.strictObject({
|
|
1309
|
+
type: v__namespace.literal("field.removeWhere"),
|
|
1310
|
+
target: targetSchema,
|
|
1311
|
+
where: ConditionSchema
|
|
1312
|
+
}) ];
|
|
1521
1313
|
}
|
|
1522
1314
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1315
|
+
const StoredFieldOpSchema = pinned()(v__namespace.variant("type", [ ...opSchemas(StoredFieldRefSchema) ])), StoredOpSchema = pinned()(v__namespace.variant("type", [ ...opSchemas(StoredFieldRefSchema), v__namespace.strictObject({
|
|
1316
|
+
type: v__namespace.literal("status.set"),
|
|
1317
|
+
activity: NonEmpty,
|
|
1318
|
+
status: picklist(ACTIVITY_STATUSES)
|
|
1319
|
+
}) ])), AuditOpSchema = v__namespace.strictObject({
|
|
1320
|
+
type: v__namespace.literal("audit"),
|
|
1321
|
+
target: AuthoringFieldRefSchema,
|
|
1322
|
+
value: ValueExprSchema,
|
|
1323
|
+
stampFields: v__namespace.optional(v__namespace.strictObject({
|
|
1324
|
+
actor: v__namespace.optional(NonEmpty),
|
|
1325
|
+
at: v__namespace.optional(NonEmpty)
|
|
1326
|
+
}))
|
|
1327
|
+
}), AuthoringOpSchema = pinned()(v__namespace.variant("type", [ ...opSchemas(AuthoringFieldRefSchema), v__namespace.strictObject({
|
|
1328
|
+
type: v__namespace.literal("status.set"),
|
|
1329
|
+
activity: v__namespace.optional(NonEmpty),
|
|
1330
|
+
status: picklist(ACTIVITY_STATUSES)
|
|
1331
|
+
}), AuditOpSchema ])), GroupName = v__namespace.pipe(v__namespace.string(), v__namespace.regex(GROQ_IDENTIFIER, "must be an identifier (letters, digits, underscore; not starting with a digit)")), GroupSchema = pinned()(v__namespace.strictObject({
|
|
1332
|
+
name: GroupName,
|
|
1333
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1334
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1335
|
+
kind: v__namespace.optional(picklist(GROUP_KINDS))
|
|
1336
|
+
})), GroupNameList = v__namespace.pipe(v__namespace.array(GroupName), v__namespace.minLength(1, "name at least one group, or omit `group`"), v__namespace.check(names => new Set(names).size === names.length, "a group is listed more than once — list each group once")), StoredGroupMembershipSchema = GroupNameList, AuthoringGroupMembershipSchema = v__namespace.union([ GroupName, GroupNameList ]);
|
|
1337
|
+
|
|
1338
|
+
function groupMembershipNames(group) {
|
|
1339
|
+
return group === void 0 ? [] : typeof group == "string" ? [ group ] : [ ...group ];
|
|
1526
1340
|
}
|
|
1527
1341
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1342
|
+
const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref", "string", "text", "number", "progress", "boolean", "date", "dueDate", "datetime", "dueDatetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), AUTHORING_FIELD_SUGAR_KINDS = groqConditionDescribe._exhaustiveOptions()([ "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({
|
|
1343
|
+
min: v__namespace.optional(FiniteNumber),
|
|
1344
|
+
max: v__namespace.optional(FiniteNumber)
|
|
1345
|
+
}), 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({
|
|
1346
|
+
list: v__namespace.pipe(v__namespace.array(v__namespace.strictObject({
|
|
1347
|
+
title: NonEmpty,
|
|
1348
|
+
value: v__namespace.union([ v__namespace.string(), v__namespace.number() ])
|
|
1349
|
+
})), v__namespace.minLength(1, "declare at least one choice, or omit `options`"))
|
|
1350
|
+
});
|
|
1534
1351
|
|
|
1535
|
-
function
|
|
1536
|
-
|
|
1537
|
-
for (const seg of path) typeof seg == "number" ? out += `[${seg}]` : out += out.length === 0 ? String(seg) : `.${String(seg)}`;
|
|
1538
|
-
return out;
|
|
1352
|
+
function asShape(input) {
|
|
1353
|
+
return typeof input == "object" && input !== null ? input : {};
|
|
1539
1354
|
}
|
|
1540
1355
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
issues;
|
|
1545
|
-
constructor(args) {
|
|
1546
|
-
super("persisted-doc-shape", formatValidationError(`Persisted ${args.documentType} document "${args.documentId}"`, args.issues)),
|
|
1547
|
-
this.name = "PersistedDocShapeError", this.documentId = args.documentId, this.documentType = args.documentType,
|
|
1548
|
-
this.issues = args.issues;
|
|
1549
|
-
}
|
|
1356
|
+
function compositeShapeOk(input) {
|
|
1357
|
+
const shape = asShape(input);
|
|
1358
|
+
return shape.type === "object" ? Array.isArray(shape.fields) && shape.fields.length > 0 && shape.of === void 0 : shape.type === "array" ? Array.isArray(shape.of) && shape.of.length > 0 && shape.fields === void 0 : shape.fields === void 0 && shape.of === void 0;
|
|
1550
1359
|
}
|
|
1551
1360
|
|
|
1552
|
-
function
|
|
1553
|
-
const
|
|
1554
|
-
|
|
1555
|
-
documentId: documentIdOf(args.doc),
|
|
1556
|
-
documentType: args.docType,
|
|
1557
|
-
issues: issuesFromValibot(result.issues)
|
|
1558
|
-
});
|
|
1559
|
-
return result.output;
|
|
1361
|
+
function compositeShapeMessage(input) {
|
|
1362
|
+
const shape = asShape(input);
|
|
1363
|
+
return shape.type === "object" ? shape.of !== void 0 ? "an `object` kind declares its sub-fields with `fields`, not `of`" : "an `object` kind needs a non-empty `fields` list of sub-field shapes" : shape.type === "array" ? shape.fields !== void 0 ? "an `array` kind declares its item shape with `of`, not `fields`" : "an `array` kind needs a non-empty `of` list of sub-field shapes" : `\`fields\` / \`of\` are only valid on the \`object\` / \`array\` kinds, not "${String(shape.type)}"`;
|
|
1560
1364
|
}
|
|
1561
1365
|
|
|
1562
|
-
function
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1366
|
+
function duplicateSubfieldName(input) {
|
|
1367
|
+
const shape = asShape(input);
|
|
1368
|
+
let list = [];
|
|
1369
|
+
Array.isArray(shape.fields) ? list = shape.fields : Array.isArray(shape.of) && (list = shape.of);
|
|
1370
|
+
const seen = /* @__PURE__ */ new Set;
|
|
1371
|
+
for (const item of list) {
|
|
1372
|
+
const name = asShape(item).name;
|
|
1373
|
+
if (typeof name == "string") {
|
|
1374
|
+
if (seen.has(name)) return name;
|
|
1375
|
+
seen.add(name);
|
|
1376
|
+
}
|
|
1566
1377
|
}
|
|
1567
|
-
return "(unknown id)";
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
const ACTOR_KINDS = [ "person", "agent", "system" ], ANONYMOUS_IDENTITY = "<anonymous>", SYSTEM_IDENTITY = "<system>", E_PREFIXED_PROJECT_ID = /^e-(.+)$/;
|
|
1571
|
-
|
|
1572
|
-
function classifyPrincipalId(id) {
|
|
1573
|
-
if (id === ANONYMOUS_IDENTITY || id === SYSTEM_IDENTITY) return {
|
|
1574
|
-
namespace: "sentinel"
|
|
1575
|
-
};
|
|
1576
|
-
if (id.startsWith("g")) return {
|
|
1577
|
-
namespace: "global",
|
|
1578
|
-
globalId: id
|
|
1579
|
-
};
|
|
1580
|
-
if (id.startsWith("p-")) return {
|
|
1581
|
-
namespace: "robot",
|
|
1582
|
-
globalId: id
|
|
1583
|
-
};
|
|
1584
|
-
const embeddedGlobal = E_PREFIXED_PROJECT_ID.exec(id)?.[1];
|
|
1585
|
-
return embeddedGlobal !== void 0 ? embeddedGlobal.startsWith("g") ? {
|
|
1586
|
-
namespace: "project",
|
|
1587
|
-
globalId: embeddedGlobal
|
|
1588
|
-
} : {
|
|
1589
|
-
namespace: "unknown"
|
|
1590
|
-
} : id.startsWith("p") ? {
|
|
1591
|
-
namespace: "project"
|
|
1592
|
-
} : {
|
|
1593
|
-
namespace: "unknown"
|
|
1594
|
-
};
|
|
1595
1378
|
}
|
|
1596
1379
|
|
|
1597
|
-
function
|
|
1598
|
-
|
|
1380
|
+
function compositeChecked(entries) {
|
|
1381
|
+
return v__namespace.pipe(v__namespace.strictObject(entries), v__namespace.check(input => compositeShapeOk(input), issue => compositeShapeMessage(issue.input)), v__namespace.check(input => duplicateSubfieldName(input) === void 0, issue => `duplicate sub-field name "${duplicateSubfieldName(issue.input)}" — sub-field names must be unique within \`fields\` / \`of\``));
|
|
1599
1382
|
}
|
|
1600
1383
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1384
|
+
const AssignmentRolesSchema = v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "declare at least one eligible role, or omit `roles`")), FieldShapeSchema = v__namespace.lazy(() => v__namespace.pipe(compositeChecked({
|
|
1385
|
+
type: FieldValueKindSchema,
|
|
1386
|
+
name: FieldEntryName,
|
|
1387
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1388
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1389
|
+
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
1390
|
+
validation: v__namespace.optional(ScalarValidationSchema),
|
|
1391
|
+
roles: v__namespace.optional(AssignmentRolesSchema),
|
|
1392
|
+
fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
|
|
1393
|
+
of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
|
|
1394
|
+
}), choiceOptionsCheck(), scalarValidationCheck(), assignmentRolesCheck())), StoredEditableSchema = pinned()(v__namespace.union([ v__namespace.literal(!0), NonEmpty ])), AuthoringEditableSchema = pinned()(v__namespace.union([ v__namespace.literal(!0), v__namespace.array(NonEmpty), NonEmpty ]));
|
|
1608
1395
|
|
|
1609
|
-
function
|
|
1610
|
-
return
|
|
1396
|
+
function fieldBase(editable, group) {
|
|
1397
|
+
return {
|
|
1398
|
+
name: FieldEntryName,
|
|
1399
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1400
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1401
|
+
group: v__namespace.optional(group),
|
|
1402
|
+
required: v__namespace.optional(v__namespace.boolean()),
|
|
1403
|
+
initialValue: v__namespace.optional(FieldSourceSchema),
|
|
1404
|
+
editable: v__namespace.optional(editable)
|
|
1405
|
+
};
|
|
1611
1406
|
}
|
|
1612
1407
|
|
|
1613
|
-
function
|
|
1614
|
-
return
|
|
1408
|
+
function fieldEntryFields({editable: editable, group: group, kind: kind = FieldKindSchema}) {
|
|
1409
|
+
return {
|
|
1410
|
+
type: kind,
|
|
1411
|
+
...fieldBase(editable, group),
|
|
1412
|
+
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
1413
|
+
validation: v__namespace.optional(ScalarValidationSchema),
|
|
1414
|
+
types: v__namespace.optional(v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "declare at least one accepted type, or omit `types` to accept any"))),
|
|
1415
|
+
roles: v__namespace.optional(AssignmentRolesSchema),
|
|
1416
|
+
fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
|
|
1417
|
+
of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
|
|
1418
|
+
};
|
|
1615
1419
|
}
|
|
1616
1420
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1421
|
+
function literalSeedIssues(entry) {
|
|
1422
|
+
if (entry.initialValue?.type === "literal") return checkLiteralSeed({
|
|
1423
|
+
entryType: entry.type,
|
|
1424
|
+
value: entry.initialValue.value,
|
|
1425
|
+
types: entry.types,
|
|
1426
|
+
fields: entry.fields,
|
|
1427
|
+
of: entry.of,
|
|
1428
|
+
options: entry.options,
|
|
1429
|
+
validation: entry.validation,
|
|
1430
|
+
roles: entry.roles
|
|
1431
|
+
});
|
|
1627
1432
|
}
|
|
1628
1433
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
type: NonEmptyString
|
|
1632
|
-
}), ReleaseRefShape = v__namespace.pipe(tolerantObject()({
|
|
1633
|
-
id: GdrUriSchema,
|
|
1634
|
-
type: v__namespace.literal("system.release"),
|
|
1635
|
-
releaseName: NonEmptyString
|
|
1636
|
-
}), v__namespace.check(ref => !isGdrUri(ref.id) || extractDocumentId(ref.id) === releaseDocId(ref.releaseName), "id must point at the `_.releases.<releaseName>` doc named by releaseName")), ActorShape = tolerantObject()({
|
|
1637
|
-
kind: v__namespace.picklist(ACTOR_KINDS),
|
|
1638
|
-
id: NonEmptyString,
|
|
1639
|
-
roles: v__namespace.exactOptional(v__namespace.array(v__namespace.string())),
|
|
1640
|
-
onBehalfOf: v__namespace.exactOptional(v__namespace.string())
|
|
1641
|
-
}), AssigneeShape = v__namespace.union([ tolerantObject()({
|
|
1642
|
-
type: v__namespace.literal("user"),
|
|
1643
|
-
id: NonEmptyString
|
|
1644
|
-
}), tolerantObject()({
|
|
1645
|
-
type: v__namespace.literal("role"),
|
|
1646
|
-
role: NonEmptyString
|
|
1647
|
-
}) ]), 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", "dueDate", "datetime", "dueDatetime", "dateTime" ]);
|
|
1648
|
-
|
|
1649
|
-
function normalizedChoiceKind(kind) {
|
|
1650
|
-
return kind === "dateTime" ? "datetime" : kind;
|
|
1434
|
+
function literalSeedCheck() {
|
|
1435
|
+
return v__namespace.check(entry => literalSeedIssues(entry) === void 0, issue => `initialValue literal does not fit the declared kind: ${(literalSeedIssues(issue.input) ?? []).join("; ")}`);
|
|
1651
1436
|
}
|
|
1652
1437
|
|
|
1653
|
-
function
|
|
1654
|
-
|
|
1655
|
-
if (options === void 0) return;
|
|
1656
|
-
if (!CHOICE_KINDS.has(entryType)) return [ `\`options\` is not valid on "${entryType}" values` ];
|
|
1657
|
-
const kind = normalizedChoiceKind(entryType), issues = options.list.flatMap((option, index) => issuesOf(checkValueAgainst({
|
|
1658
|
-
entryType: kind,
|
|
1659
|
-
value: option.value,
|
|
1660
|
-
validation: validation
|
|
1661
|
-
}, valueSchemas))?.map(issue => `at options.list.${index}.value: ${issue}`) ?? []), seen = /* @__PURE__ */ new Set;
|
|
1662
|
-
for (const option of options.list) seen.has(option.value) && issues.push(`duplicate option value ${JSON.stringify(option.value)}`),
|
|
1663
|
-
seen.add(option.value);
|
|
1664
|
-
return issues.length === 0 ? void 0 : issues;
|
|
1438
|
+
function refTypesCheck() {
|
|
1439
|
+
return v__namespace.check(entry => entry.types === void 0 || refKindAcceptsTypes(entry.type), issue => `\`types\` is only valid on \`doc.ref\` / \`doc.refs\` / \`subject\` entries, not "${issue.input.type}"`);
|
|
1665
1440
|
}
|
|
1666
1441
|
|
|
1667
|
-
function
|
|
1668
|
-
|
|
1442
|
+
function assignmentRolesCheck() {
|
|
1443
|
+
return v__namespace.check(entry => entry.roles === void 0 || assignmentKindAcceptsRoles(entry.type), issue => `\`roles\` is only valid on \`assignee\` / \`assignees\` entries, not "${issue.input.type}"`);
|
|
1669
1444
|
}
|
|
1670
1445
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
date: NullableDate,
|
|
1682
|
-
dueDate: NullableDate,
|
|
1683
|
-
datetime: NullableDateTime,
|
|
1684
|
-
dueDatetime: NullableDateTime,
|
|
1685
|
-
url: NullableUrl,
|
|
1686
|
-
actor: v__namespace.union([ v__namespace.null(), ActorShape ]),
|
|
1687
|
-
assignee: v__namespace.union([ v__namespace.null(), AssigneeShape ]),
|
|
1688
|
-
assignees: v__namespace.array(AssigneeShape)
|
|
1689
|
-
}, WritePrincipalId = v__namespace.pipe(NonEmptyString, v__namespace.rawTransform(({dataset: dataset, addIssue: addIssue}) => {
|
|
1690
|
-
const classified = classifyPrincipalId(dataset.value);
|
|
1691
|
-
return classified.namespace === "global" || classified.namespace === "robot" ? dataset.value : classified.namespace === "project" && classified.globalId !== void 0 ? classified.globalId : (addIssue({
|
|
1692
|
-
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.`
|
|
1693
|
-
}), dataset.value);
|
|
1694
|
-
})), ActorWriteShape = tolerantObject()({
|
|
1695
|
-
kind: v__namespace.picklist(ACTOR_KINDS),
|
|
1696
|
-
id: WritePrincipalId,
|
|
1697
|
-
roles: v__namespace.exactOptional(v__namespace.array(v__namespace.string())),
|
|
1698
|
-
onBehalfOf: v__namespace.exactOptional(v__namespace.string())
|
|
1699
|
-
}), AssigneeWriteShape = v__namespace.union([ tolerantObject()({
|
|
1700
|
-
type: v__namespace.literal("user"),
|
|
1701
|
-
id: WritePrincipalId
|
|
1702
|
-
}), tolerantObject()({
|
|
1703
|
-
type: v__namespace.literal("role"),
|
|
1704
|
-
role: NonEmptyString
|
|
1705
|
-
}) ]), valueSchemas = {
|
|
1706
|
-
...fieldValueSchemas,
|
|
1707
|
-
actor: v__namespace.nullable(ActorWriteShape),
|
|
1708
|
-
assignee: v__namespace.nullable(AssigneeWriteShape),
|
|
1709
|
-
assignees: v__namespace.array(AssigneeWriteShape),
|
|
1710
|
-
query: v__namespace.any()
|
|
1711
|
-
};
|
|
1712
|
-
|
|
1713
|
-
function shapeValueSchema(args) {
|
|
1714
|
-
const {shape: shape, leaf: leaf} = args;
|
|
1715
|
-
if (shape.type === "object") return objectSchema({
|
|
1716
|
-
fields: shape.fields ?? [],
|
|
1717
|
-
leaf: leaf
|
|
1718
|
-
});
|
|
1719
|
-
if (shape.type === "array") return v__namespace.array(objectSchema({
|
|
1720
|
-
fields: shape.of ?? [],
|
|
1721
|
-
leaf: leaf
|
|
1722
|
-
}));
|
|
1723
|
-
const schema = leaf[shape.type] ?? v__namespace.any();
|
|
1724
|
-
return constrainedScalarSchema({
|
|
1725
|
-
schema: schema,
|
|
1726
|
-
entryType: shape.type,
|
|
1727
|
-
...shape
|
|
1728
|
-
});
|
|
1446
|
+
function choiceOptionsCheck() {
|
|
1447
|
+
return v__namespace.check(entry => checkChoiceList({
|
|
1448
|
+
entryType: entry.type,
|
|
1449
|
+
options: entry.options,
|
|
1450
|
+
validation: entry.validation
|
|
1451
|
+
}) === void 0, issue => (checkChoiceList({
|
|
1452
|
+
entryType: issue.input.type,
|
|
1453
|
+
options: issue.input.options,
|
|
1454
|
+
validation: issue.input.validation
|
|
1455
|
+
}) ?? []).join("; "));
|
|
1729
1456
|
}
|
|
1730
1457
|
|
|
1731
|
-
|
|
1732
|
-
if ((entryType === "number" || entryType === "progress") && typeof value == "number") return value;
|
|
1733
|
-
if ((entryType === "string" || entryType === "text") && typeof value == "string") return value.length;
|
|
1734
|
-
}
|
|
1458
|
+
const SCALAR_VALIDATION_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number", "progress" ]);
|
|
1735
1459
|
|
|
1736
|
-
function
|
|
1737
|
-
|
|
1738
|
-
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}`;
|
|
1460
|
+
function scalarValidationCheck() {
|
|
1461
|
+
return v__namespace.check(entry => scalarValidationDeclarationIssues(entry) === void 0, issue => (scalarValidationDeclarationIssues(issue.input) ?? []).join("; "));
|
|
1739
1462
|
}
|
|
1740
1463
|
|
|
1741
|
-
function
|
|
1742
|
-
const {
|
|
1743
|
-
if (validation === void 0
|
|
1744
|
-
|
|
1745
|
-
if (
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
}), scalarBoundIssue({
|
|
1752
|
-
entryType: entryType,
|
|
1753
|
-
measured: measured,
|
|
1754
|
-
bound: validation.max,
|
|
1755
|
-
limit: "max"
|
|
1756
|
-
}) ].filter(issue => issue !== void 0);
|
|
1464
|
+
function scalarValidationDeclarationIssues(entry) {
|
|
1465
|
+
const {type: type, validation: validation} = entry;
|
|
1466
|
+
if (validation === void 0) return;
|
|
1467
|
+
if (!SCALAR_VALIDATION_KINDS.has(type)) return [ `\`validation\` is only valid on \`string\` / \`text\` / \`number\` / \`progress\` values, not "${type}"` ];
|
|
1468
|
+
if (type === "progress") {
|
|
1469
|
+
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` ]);
|
|
1470
|
+
return issues2.length === 0 ? void 0 : issues2;
|
|
1471
|
+
}
|
|
1472
|
+
if (type === "number") return;
|
|
1473
|
+
const issues = Object.entries(validation).flatMap(([bound, value]) => Number.isInteger(value) && value >= 0 ? [] : [ `\`validation.${bound}\` must be a non-negative integer for ${type} length` ]);
|
|
1757
1474
|
return issues.length === 0 ? void 0 : issues;
|
|
1758
1475
|
}
|
|
1759
1476
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1477
|
+
const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
|
|
1478
|
+
editable: StoredEditableSchema,
|
|
1479
|
+
group: StoredGroupMembershipSchema
|
|
1480
|
+
})), refTypesCheck(), assignmentRolesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields({
|
|
1481
|
+
editable: AuthoringEditableSchema,
|
|
1482
|
+
group: AuthoringGroupMembershipSchema,
|
|
1483
|
+
kind: AuthoringRawFieldKindSchema
|
|
1484
|
+
})), refTypesCheck(), assignmentRolesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck()));
|
|
1485
|
+
|
|
1486
|
+
function listSugarFields(type) {
|
|
1487
|
+
return {
|
|
1488
|
+
type: v__namespace.literal(type),
|
|
1489
|
+
...fieldBase(AuthoringEditableSchema, AuthoringGroupMembershipSchema)
|
|
1490
|
+
};
|
|
1771
1491
|
}
|
|
1772
1492
|
|
|
1773
|
-
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1493
|
+
const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.lazy(input => {
|
|
1494
|
+
const type = asShape(input).type;
|
|
1495
|
+
return type === "todoList" ? TodoListFieldSchema : type === "notes" ? NotesFieldSchema : RawAuthoringFieldEntrySchema;
|
|
1496
|
+
})), RUNTIME_KINDS = groqConditionDescribe._exhaustiveOptions()([ "function", "durableFunction", "selfHosted" ]), RuntimeBlockSchema = pinned()(v__namespace.strictObject({
|
|
1497
|
+
kind: picklist(RUNTIME_KINDS)
|
|
1498
|
+
})), EffectRuntimeBlockSchema = pinned()(v__namespace.variant("kind", [ v__namespace.strictObject({
|
|
1499
|
+
kind: v__namespace.literal("function"),
|
|
1500
|
+
timeout: v__namespace.optional(PositiveInt),
|
|
1501
|
+
memory: v__namespace.optional(PositiveInt)
|
|
1502
|
+
}), v__namespace.strictObject({
|
|
1503
|
+
kind: v__namespace.literal("durableFunction")
|
|
1504
|
+
}), v__namespace.strictObject({
|
|
1505
|
+
kind: v__namespace.literal("selfHosted")
|
|
1506
|
+
}) ], invalidOptionMessage(RUNTIME_KINDS))), EFFECT_RETRY_KINDS = groqConditionDescribe._exhaustiveOptions()([ "engine" ]), EffectRetryBackoffSchema = pinned()(v__namespace.strictObject({
|
|
1507
|
+
kind: picklist([ "fixed", "exponential" ]),
|
|
1508
|
+
delayMs: PositiveInt
|
|
1509
|
+
}));
|
|
1510
|
+
|
|
1511
|
+
function effectRetryFields(kind) {
|
|
1512
|
+
return {
|
|
1513
|
+
kind: kind,
|
|
1514
|
+
attempts: PositiveInt,
|
|
1515
|
+
backoff: v__namespace.optional(EffectRetryBackoffSchema),
|
|
1516
|
+
expiryMs: v__namespace.optional(PositiveInt)
|
|
1517
|
+
};
|
|
1780
1518
|
}
|
|
1781
1519
|
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1520
|
+
const StoredEffectRetrySchema = pinned()(v__namespace.strictObject(effectRetryFields(picklist(EFFECT_RETRY_KINDS)))), AuthoringEffectRetrySchema = pinned()(v__namespace.strictObject(effectRetryFields(v__namespace.optional(picklist(EFFECT_RETRY_KINDS)))));
|
|
1521
|
+
|
|
1522
|
+
function effectFields(retry) {
|
|
1523
|
+
return {
|
|
1524
|
+
name: NonEmpty,
|
|
1525
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1526
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1527
|
+
bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), ConditionSchema)),
|
|
1528
|
+
input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown())),
|
|
1529
|
+
outputs: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
|
|
1530
|
+
retry: v__namespace.optional(retry)
|
|
1531
|
+
};
|
|
1791
1532
|
}
|
|
1792
1533
|
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1534
|
+
const EffectSchema = pinned()(v__namespace.strictObject(effectFields(StoredEffectRetrySchema))), AuthoringEffectSchema = pinned()(v__namespace.strictObject({
|
|
1535
|
+
...effectFields(AuthoringEffectRetrySchema),
|
|
1536
|
+
runtime: v__namespace.optional(EffectRuntimeBlockSchema)
|
|
1537
|
+
})), DefinitionRefSchema = v__namespace.strictObject({
|
|
1538
|
+
name: NonEmpty,
|
|
1539
|
+
version: v__namespace.optional(v__namespace.union([ PositiveInt, v__namespace.literal("latest") ]))
|
|
1540
|
+
}), SubworkflowsSchema = v__namespace.strictObject({
|
|
1541
|
+
forEach: NonEmpty,
|
|
1542
|
+
definition: DefinitionRefSchema,
|
|
1543
|
+
with: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
|
|
1544
|
+
context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
|
|
1545
|
+
onExit: v__namespace.optional(picklist([ "detach", "abort" ]))
|
|
1546
|
+
}), ActionParamSchema = v__namespace.pipe(v__namespace.strictObject({
|
|
1547
|
+
type: picklist([ "string", "number", "boolean", "url", "dateTime", "actor", "doc.ref", "doc.refs", "json" ]),
|
|
1548
|
+
name: NonEmpty,
|
|
1549
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1550
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1551
|
+
required: v__namespace.optional(v__namespace.boolean()),
|
|
1552
|
+
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
1553
|
+
validation: v__namespace.optional(ScalarValidationSchema)
|
|
1554
|
+
}), choiceOptionsCheck(), scalarValidationCheck()), CUSTOM_SEMANTIC_HINT = "`custom.<camelCaseMeaning>`", CustomSemanticSchema = v__namespace.custom(input => typeof input == "string" && /^custom\.[a-z][a-zA-Z0-9]*$/.test(input)), SemanticSchema = v__namespace.union([ picklist(SIGNAL_SEMANTICS), CustomSemanticSchema ], `expected ${SIGNAL_SEMANTICS.join(", ")}, or ${CUSTOM_SEMANTIC_HINT}`), ActionSemanticSchema = v__namespace.union([ picklist(ACTION_SEMANTICS), CustomSemanticSchema ], `expected ${ACTION_SEMANTICS.join(", ")}, or ${CUSTOM_SEMANTIC_HINT}`);
|
|
1555
|
+
|
|
1556
|
+
function semanticNamespace(semantic) {
|
|
1557
|
+
return semantic.startsWith("custom.") ? semantic : semantic.split(".", 1)[0] ?? semantic;
|
|
1800
1558
|
}
|
|
1801
1559
|
|
|
1802
|
-
function
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
if (!refKindAcceptsTypes(entryType)) return [];
|
|
1806
|
-
let items = [ value ];
|
|
1807
|
-
return entryType === "doc.refs" && (items = Array.isArray(value) ? value : []),
|
|
1808
|
-
[ ...new Set(items.map(gdrTypeOf).filter(t => t !== void 0 && !types.includes(t))) ];
|
|
1560
|
+
function hasUniqueSemanticNamespaces(semantics) {
|
|
1561
|
+
const namespaces = semantics.map(semanticNamespace);
|
|
1562
|
+
return new Set(namespaces).size === namespaces.length;
|
|
1809
1563
|
}
|
|
1810
1564
|
|
|
1811
|
-
function
|
|
1812
|
-
|
|
1813
|
-
if (rejected.length === 0) return;
|
|
1814
|
-
const accepts = (args.types ?? []).map(t => `"${t}"`).join(", ");
|
|
1815
|
-
return rejected.map(t => `document type "${t}" is not accepted — this entry accepts ${accepts} (a GDR's \`type\` names the target document's schema type)`);
|
|
1565
|
+
function semanticsFieldSchema(semantic) {
|
|
1566
|
+
return v__namespace.optional(v__namespace.pipe(v__namespace.array(semantic), v__namespace.minLength(1, "declare at least one semantic, or omit `semantics`"), v__namespace.check(hasUniqueSemanticNamespaces, "declare at most one semantic from each namespace")));
|
|
1816
1567
|
}
|
|
1817
1568
|
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1569
|
+
const SemanticsFieldSchema = semanticsFieldSchema(SemanticSchema), ActionSemanticsFieldSchema = semanticsFieldSchema(ActionSemanticSchema);
|
|
1570
|
+
|
|
1571
|
+
function actionFields(schemas) {
|
|
1572
|
+
const {op: op, group: group, effect: effect} = schemas;
|
|
1573
|
+
return {
|
|
1574
|
+
name: NonEmpty,
|
|
1575
|
+
semantics: ActionSemanticsFieldSchema,
|
|
1576
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1577
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1578
|
+
group: v__namespace.optional(group),
|
|
1579
|
+
when: v__namespace.optional(ConditionSchema),
|
|
1580
|
+
filter: v__namespace.optional(ConditionSchema),
|
|
1581
|
+
params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
|
|
1582
|
+
ops: v__namespace.optional(v__namespace.array(op)),
|
|
1583
|
+
effects: v__namespace.optional(v__namespace.array(effect)),
|
|
1584
|
+
spawn: v__namespace.optional(SubworkflowsSchema)
|
|
1585
|
+
};
|
|
1822
1586
|
}
|
|
1823
1587
|
|
|
1824
|
-
|
|
1825
|
-
|
|
1588
|
+
const StoredActionSchema = pinned()(v__namespace.strictObject({
|
|
1589
|
+
...actionFields({
|
|
1590
|
+
op: StoredOpSchema,
|
|
1591
|
+
group: StoredGroupMembershipSchema,
|
|
1592
|
+
effect: EffectSchema
|
|
1593
|
+
}),
|
|
1594
|
+
roles: v__namespace.optional(v__namespace.array(NonEmpty))
|
|
1595
|
+
})), TerminalActivityStatus = picklist(TERMINAL_ACTIVITY_STATUSES), RawAuthoringActionSchema = pinned()(v__namespace.strictObject({
|
|
1596
|
+
...actionFields({
|
|
1597
|
+
op: AuthoringOpSchema,
|
|
1598
|
+
group: AuthoringGroupMembershipSchema,
|
|
1599
|
+
effect: AuthoringEffectSchema
|
|
1600
|
+
}),
|
|
1601
|
+
roles: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
1602
|
+
status: v__namespace.optional(TerminalActivityStatus)
|
|
1603
|
+
})), AuthoringActionSchema = pinned()(RawAuthoringActionSchema), requirementBase = {
|
|
1604
|
+
name: NonEmpty,
|
|
1605
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1606
|
+
description: v__namespace.optional(v__namespace.string())
|
|
1607
|
+
}, GroqRequirementSchemaRaw = v__namespace.strictObject({
|
|
1608
|
+
...requirementBase,
|
|
1609
|
+
type: v__namespace.literal("groq"),
|
|
1610
|
+
query: ConditionSchema
|
|
1611
|
+
}), SingleSubjectRequirementSchemaRaw = v__namespace.strictObject({
|
|
1612
|
+
...requirementBase,
|
|
1613
|
+
type: v__namespace.literal("singleSubject")
|
|
1614
|
+
}), GroqRequirementSchema = pinned()(GroqRequirementSchemaRaw), StartRequirementSchema = pinned()(v__namespace.variant("type", [ GroqRequirementSchemaRaw, SingleSubjectRequirementSchemaRaw ]));
|
|
1615
|
+
|
|
1616
|
+
function activityFields({field: field, action: action, target: target, group: group}) {
|
|
1617
|
+
return {
|
|
1618
|
+
name: NonEmpty,
|
|
1619
|
+
semantics: SemanticsFieldSchema,
|
|
1620
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1621
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1622
|
+
groups: v__namespace.optional(v__namespace.array(GroupSchema)),
|
|
1623
|
+
group: v__namespace.optional(group),
|
|
1624
|
+
target: v__namespace.optional(target),
|
|
1625
|
+
filter: v__namespace.optional(ConditionSchema),
|
|
1626
|
+
requirements: v__namespace.optional(v__namespace.array(GroqRequirementSchema)),
|
|
1627
|
+
actions: v__namespace.optional(v__namespace.array(action)),
|
|
1628
|
+
fields: v__namespace.optional(v__namespace.array(field))
|
|
1629
|
+
};
|
|
1826
1630
|
}
|
|
1827
1631
|
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1632
|
+
const StoredActivitySchema = pinned()(v__namespace.strictObject(activityFields({
|
|
1633
|
+
field: FieldEntrySchema,
|
|
1634
|
+
action: StoredActionSchema,
|
|
1635
|
+
target: StoredManualTargetSchema,
|
|
1636
|
+
group: StoredGroupMembershipSchema
|
|
1637
|
+
}))), AuthoringActivitySchema = pinned()(v__namespace.strictObject(activityFields({
|
|
1638
|
+
field: AuthoringFieldEntrySchema,
|
|
1639
|
+
action: AuthoringActionSchema,
|
|
1640
|
+
target: AuthoringManualTargetSchema,
|
|
1641
|
+
group: AuthoringGroupMembershipSchema
|
|
1642
|
+
})));
|
|
1643
|
+
|
|
1644
|
+
function transitionFields(when) {
|
|
1645
|
+
return {
|
|
1646
|
+
name: NonEmpty,
|
|
1647
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1648
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1649
|
+
to: NonEmpty,
|
|
1650
|
+
when: when
|
|
1836
1651
|
};
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
const StoredTransitionSchema = pinned()(v__namespace.strictObject(transitionFields(ConditionSchema))), AuthoringTransitionSchema = pinned()(v__namespace.strictObject(transitionFields(v__namespace.optional(ConditionSchema)))), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardActionsSchema = v__namespace.array(GuardActionSchema), NonEmptyGuardActionsSchema = v__namespace.pipe(GuardActionsSchema, v__namespace.minLength(1, GUARD_ACTIONS_REQUIRED_MESSAGE)), GuardReadPath = v__namespace.pipe(NonEmpty, v__namespace.check(path => !/[\r\n\u2028\u2029]/.test(path), "a guard read path cannot contain a line break")), GuardReadSchema = pinned()(v__namespace.variant("type", [ v__namespace.strictObject({
|
|
1655
|
+
type: v__namespace.literal("self")
|
|
1656
|
+
}), v__namespace.strictObject({
|
|
1657
|
+
type: v__namespace.literal("now")
|
|
1658
|
+
}), v__namespace.strictObject({
|
|
1659
|
+
type: v__namespace.literal("fieldRead"),
|
|
1660
|
+
field: FieldEntryName,
|
|
1661
|
+
path: v__namespace.optional(GuardReadPath)
|
|
1662
|
+
}), v__namespace.strictObject({
|
|
1663
|
+
type: v__namespace.literal("effectsRead"),
|
|
1664
|
+
effect: v__namespace.pipe(NonEmpty, v__namespace.check(name => !name.includes("'"), "an effect name cannot contain `'`")),
|
|
1665
|
+
path: v__namespace.optional(GuardReadPath)
|
|
1666
|
+
}) ]));
|
|
1667
|
+
|
|
1668
|
+
function guardMatchFields(read, actions) {
|
|
1669
|
+
return {
|
|
1670
|
+
types: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
1671
|
+
idRefs: v__namespace.optional(v__namespace.array(read)),
|
|
1672
|
+
idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
1673
|
+
actions: actions
|
|
1840
1674
|
};
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
issues: postIssues
|
|
1852
|
-
} : {
|
|
1853
|
-
output: result.output
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
function guardFields(read, actions) {
|
|
1678
|
+
return {
|
|
1679
|
+
name: NonEmpty,
|
|
1680
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1681
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1682
|
+
match: v__namespace.strictObject(guardMatchFields(read, actions)),
|
|
1683
|
+
predicate: v__namespace.optional(v__namespace.string()),
|
|
1684
|
+
metadata: v__namespace.optional(v__namespace.record(NonEmpty, read))
|
|
1854
1685
|
};
|
|
1855
1686
|
}
|
|
1856
1687
|
|
|
1857
|
-
|
|
1858
|
-
return assignee.type === "user" ? `user:${assignee.id}` : `role:${assignee.role}`;
|
|
1859
|
-
}
|
|
1688
|
+
const GuardSchema = v__namespace.strictObject(guardFields(NonEmpty, GuardActionsSchema)), AuthoringGuardSchema = v__namespace.strictObject(guardFields(GuardReadSchema, NonEmptyGuardActionsSchema));
|
|
1860
1689
|
|
|
1861
|
-
function
|
|
1862
|
-
|
|
1863
|
-
|
|
1690
|
+
function stageFields({field: field, activity: activity, transition: transition, guard: guard, editable: editable}) {
|
|
1691
|
+
return {
|
|
1692
|
+
name: NonEmpty,
|
|
1693
|
+
semantics: SemanticsFieldSchema,
|
|
1694
|
+
title: v__namespace.optional(v__namespace.string()),
|
|
1695
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1696
|
+
groups: v__namespace.optional(v__namespace.array(GroupSchema)),
|
|
1697
|
+
activities: v__namespace.optional(v__namespace.array(activity)),
|
|
1698
|
+
transitions: v__namespace.optional(v__namespace.array(transition)),
|
|
1699
|
+
guards: v__namespace.optional(v__namespace.array(guard)),
|
|
1700
|
+
fields: v__namespace.optional(v__namespace.array(field)),
|
|
1701
|
+
editable: v__namespace.optional(v__namespace.record(FieldEntryName, editable))
|
|
1702
|
+
};
|
|
1864
1703
|
}
|
|
1865
1704
|
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
}
|
|
1705
|
+
const StoredStageSchema = pinned()(v__namespace.strictObject(stageFields({
|
|
1706
|
+
field: FieldEntrySchema,
|
|
1707
|
+
activity: StoredActivitySchema,
|
|
1708
|
+
transition: StoredTransitionSchema,
|
|
1709
|
+
guard: GuardSchema,
|
|
1710
|
+
editable: StoredEditableSchema
|
|
1711
|
+
}))), AuthoringStageSchema = pinned()(v__namespace.strictObject(stageFields({
|
|
1712
|
+
field: AuthoringFieldEntrySchema,
|
|
1713
|
+
activity: AuthoringActivitySchema,
|
|
1714
|
+
transition: AuthoringTransitionSchema,
|
|
1715
|
+
guard: AuthoringGuardSchema,
|
|
1716
|
+
editable: AuthoringEditableSchema
|
|
1717
|
+
}))), RoleAliasesSchema = pinned()(v__namespace.record(NonEmpty, v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "a role alias must list at least one fulfilling role")))), WORKFLOW_LIFECYCLES = groqConditionDescribe._exhaustiveOptions()([ "standalone", "child" ]), START_KINDS = groqConditionDescribe._exhaustiveOptions()([ "interactive", "autonomous" ]);
|
|
1718
|
+
|
|
1719
|
+
function startFields(kind) {
|
|
1720
|
+
return {
|
|
1721
|
+
kind: kind,
|
|
1722
|
+
filter: v__namespace.optional(ConditionSchema),
|
|
1723
|
+
requirements: v__namespace.optional(v__namespace.array(StartRequirementSchema))
|
|
1724
|
+
};
|
|
1886
1725
|
}
|
|
1887
1726
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1727
|
+
const StoredStartSchema = pinned()(v__namespace.strictObject(startFields(picklist(START_KINDS)))), AuthoringStartSchema = pinned()(v__namespace.strictObject(startFields(v__namespace.optional(picklist(START_KINDS)))));
|
|
1728
|
+
|
|
1729
|
+
function workflowFields({field: field, stage: stage, start: start}) {
|
|
1730
|
+
return {
|
|
1731
|
+
name: NonEmpty,
|
|
1732
|
+
semantics: SemanticsFieldSchema,
|
|
1733
|
+
title: NonEmpty,
|
|
1734
|
+
description: v__namespace.optional(v__namespace.string()),
|
|
1735
|
+
groups: v__namespace.optional(v__namespace.array(GroupSchema)),
|
|
1736
|
+
lifecycle: v__namespace.optional(picklist(WORKFLOW_LIFECYCLES)),
|
|
1737
|
+
start: v__namespace.optional(start),
|
|
1738
|
+
initialStage: NonEmpty,
|
|
1739
|
+
fields: v__namespace.optional(v__namespace.array(field)),
|
|
1740
|
+
stages: v__namespace.pipe(v__namespace.array(stage), v__namespace.minLength(1, "must declare at least one stage")),
|
|
1741
|
+
predicates: v__namespace.optional(v__namespace.record(groqIdentifier("`$<name>`"), ConditionSchema)),
|
|
1742
|
+
roleAliases: v__namespace.optional(RoleAliasesSchema)
|
|
1743
|
+
};
|
|
1892
1744
|
}
|
|
1893
1745
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1746
|
+
const WorkflowDefinitionSchema = pinned()(v__namespace.strictObject(workflowFields({
|
|
1747
|
+
field: FieldEntrySchema,
|
|
1748
|
+
stage: StoredStageSchema,
|
|
1749
|
+
start: StoredStartSchema
|
|
1750
|
+
})));
|
|
1751
|
+
|
|
1752
|
+
function parseStoredDefinition(input, label) {
|
|
1753
|
+
return parseOrThrow({
|
|
1754
|
+
schema: WorkflowDefinitionSchema,
|
|
1755
|
+
input: input,
|
|
1756
|
+
label: label
|
|
1901
1757
|
});
|
|
1902
|
-
if (schema === void 0) return retained;
|
|
1903
|
-
const previous = v__namespace.safeParse(schema, args.previousValue);
|
|
1904
|
-
return previous.success && visitAssignmentEntries({
|
|
1905
|
-
entryType: args.entryType,
|
|
1906
|
-
value: previous.output,
|
|
1907
|
-
roles: args.roles,
|
|
1908
|
-
fields: args.fields,
|
|
1909
|
-
of: args.of,
|
|
1910
|
-
path: "$"
|
|
1911
|
-
}, ({assignee: assignee, path: path}) => {
|
|
1912
|
-
const identities = retained.get(path) ?? /* @__PURE__ */ new Map, identity = assignmentIdentity(assignee);
|
|
1913
|
-
identities.set(identity, (identities.get(identity) ?? 0) + 1), retained.set(path, identities);
|
|
1914
|
-
}), retained;
|
|
1915
1758
|
}
|
|
1916
1759
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
value: args.value,
|
|
1926
|
-
path: args.path
|
|
1927
|
-
}, visit);
|
|
1928
|
-
return;
|
|
1929
|
-
}
|
|
1930
|
-
if (!(args.entryType !== "array" || !Array.isArray(args.value))) for (const row of args.value) visitAssignmentFields({
|
|
1931
|
-
fields: args.of,
|
|
1932
|
-
value: row,
|
|
1933
|
-
path: `${args.path}[]`
|
|
1934
|
-
}, visit);
|
|
1935
|
-
}
|
|
1760
|
+
const AuthoringWorkflowSchema = pinned()(v__namespace.strictObject({
|
|
1761
|
+
...workflowFields({
|
|
1762
|
+
field: AuthoringFieldEntrySchema,
|
|
1763
|
+
stage: AuthoringStageSchema,
|
|
1764
|
+
start: AuthoringStartSchema
|
|
1765
|
+
}),
|
|
1766
|
+
runtime: v__namespace.optional(RuntimeBlockSchema)
|
|
1767
|
+
})), WORKFLOW_DEFINITION_TYPE = "sanity.workflow.definition";
|
|
1936
1768
|
|
|
1937
|
-
function
|
|
1938
|
-
|
|
1939
|
-
const indexedValues = args.entryType === "assignees" && Array.isArray(args.value) ? args.value.map(value => ({
|
|
1940
|
-
value: value,
|
|
1941
|
-
path: args.path
|
|
1942
|
-
})) : [ {
|
|
1943
|
-
value: args.value,
|
|
1944
|
-
path: args.path
|
|
1945
|
-
} ];
|
|
1946
|
-
for (const {value: value, path: path} of indexedValues) {
|
|
1947
|
-
const assignee = assignmentCandidate(value);
|
|
1948
|
-
assignee !== void 0 && visit({
|
|
1949
|
-
assignee: assignee,
|
|
1950
|
-
roles: args.roles,
|
|
1951
|
-
path: path
|
|
1952
|
-
});
|
|
1953
|
-
}
|
|
1769
|
+
function isStartableDefinition(definition) {
|
|
1770
|
+
return definition.lifecycle !== "child";
|
|
1954
1771
|
}
|
|
1955
1772
|
|
|
1956
|
-
function
|
|
1957
|
-
|
|
1958
|
-
shape: field,
|
|
1959
|
-
value: args.value[field.name],
|
|
1960
|
-
path: `${args.path}.${field.name}`
|
|
1961
|
-
}, visit);
|
|
1773
|
+
function startKindOf(definition) {
|
|
1774
|
+
return definition.start?.kind ?? "interactive";
|
|
1962
1775
|
}
|
|
1963
1776
|
|
|
1964
|
-
function
|
|
1965
|
-
|
|
1966
|
-
entryType: args.shape.type,
|
|
1967
|
-
value: args.value,
|
|
1968
|
-
roles: args.shape.roles,
|
|
1969
|
-
fields: args.shape.fields,
|
|
1970
|
-
of: args.shape.of,
|
|
1971
|
-
path: args.path
|
|
1972
|
-
}, visit);
|
|
1777
|
+
function isSubjectEntry(entry) {
|
|
1778
|
+
return entry.type === "subject";
|
|
1973
1779
|
}
|
|
1974
1780
|
|
|
1975
|
-
function
|
|
1976
|
-
|
|
1977
|
-
const held = args.memberRoles?.[assignee.id];
|
|
1978
|
-
return held === void 0 ? args.memberRoles === void 0 ? [] : [ `user "${assignee.id}" has no available project membership for role eligibility` ] : args.roles.some(required => actorFulfillsRole({
|
|
1979
|
-
actorRoles: held,
|
|
1980
|
-
required: required,
|
|
1981
|
-
aliases: args.roleAliases
|
|
1982
|
-
})) ? [] : [ `user "${assignee.id}" does not fulfill any eligible role: ${formatEligibleRoles(args.roles)}` ];
|
|
1781
|
+
function isDueDateEntry(entry) {
|
|
1782
|
+
return entry.type === "dueDate" || entry.type === "dueDatetime";
|
|
1983
1783
|
}
|
|
1984
1784
|
|
|
1985
|
-
function
|
|
1986
|
-
return
|
|
1785
|
+
function isInputSourced(entry) {
|
|
1786
|
+
return entry.initialValue?.type === "input";
|
|
1987
1787
|
}
|
|
1988
1788
|
|
|
1989
|
-
function
|
|
1990
|
-
|
|
1789
|
+
function parseOrThrow({schema: schema, input: input, label: label}) {
|
|
1790
|
+
const result = v__namespace.safeParse(schema, input);
|
|
1791
|
+
if (!result.success) throw new Error(formatValidationError(label, issuesFromValibot(result.issues)));
|
|
1792
|
+
return result.output;
|
|
1991
1793
|
}
|
|
1992
1794
|
|
|
1993
|
-
function
|
|
1994
|
-
|
|
1795
|
+
function labelFor(fn, value) {
|
|
1796
|
+
if (value && typeof value == "object" && "name" in value) {
|
|
1797
|
+
const raw = value.name;
|
|
1798
|
+
if (typeof raw == "string") return `${fn}("${raw}")`;
|
|
1799
|
+
}
|
|
1800
|
+
return fn;
|
|
1995
1801
|
}
|
|
1996
1802
|
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
1803
|
+
const NonEmptyString = v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty("must not be empty"));
|
|
1804
|
+
|
|
1805
|
+
function asPredicate(validate) {
|
|
1806
|
+
return value => {
|
|
1807
|
+
try {
|
|
1808
|
+
return validate(value), !0;
|
|
1809
|
+
} catch {
|
|
1810
|
+
return !1;
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
2006
1813
|
}
|
|
2007
1814
|
|
|
2008
|
-
const
|
|
1815
|
+
const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(validateResourceAliasName), isValidDatasetId = asPredicate(datasetResourceParts);
|
|
2009
1816
|
|
|
2010
|
-
function
|
|
2011
|
-
return
|
|
1817
|
+
function lakeSegment(label) {
|
|
1818
|
+
return v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty(), v__namespace.check(isValidTag, `invalid ${label} — ${LAKE_ID_SEGMENT_GLOSS}`));
|
|
2012
1819
|
}
|
|
2013
1820
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
1821
|
+
const WorkflowResourceSchema = v__namespace.variant("type", [ v__namespace.object({
|
|
1822
|
+
type: v__namespace.literal("dataset"),
|
|
1823
|
+
id: v__namespace.pipe(NonEmptyString, v__namespace.check(isValidDatasetId, 'invalid dataset resource id — expected "<projectId>.<dataset>"'))
|
|
1824
|
+
}), v__namespace.object({
|
|
1825
|
+
type: v__namespace.literal("canvas"),
|
|
1826
|
+
id: NonEmptyString
|
|
1827
|
+
}), v__namespace.object({
|
|
1828
|
+
type: v__namespace.literal("media-library"),
|
|
1829
|
+
id: NonEmptyString
|
|
1830
|
+
}), v__namespace.object({
|
|
1831
|
+
type: v__namespace.literal("dashboard"),
|
|
1832
|
+
id: NonEmptyString
|
|
1833
|
+
}) ]), ResourceBindingSchema = v__namespace.object({
|
|
1834
|
+
name: v__namespace.pipe(NonEmptyString, v__namespace.check(isValidAliasName, "invalid resource handle name — lowercase letters, digits and dashes only, no leading dash")),
|
|
1835
|
+
resource: WorkflowResourceSchema
|
|
1836
|
+
}), 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({
|
|
1837
|
+
name: lakeSegment("name"),
|
|
1838
|
+
expectedMinReaderModel: v__namespace.optional(v__namespace.custom(() => !0), void 0),
|
|
1839
|
+
tag: lakeSegment("tag"),
|
|
1840
|
+
workflowResource: WorkflowResourceSchema,
|
|
1841
|
+
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"))),
|
|
1842
|
+
definitions: v__namespace.pipe(v__namespace.array(DefinitionSchema), v__namespace.minLength(1, "a deployment needs at least one definition")),
|
|
1843
|
+
runtime: v__namespace.optional(RuntimeBlockSchema)
|
|
1844
|
+
});
|
|
2017
1845
|
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
"release.ref": v__namespace.union([ v__namespace.null(), v__namespace.looseObject({
|
|
2027
|
-
id: AuthoringRefId,
|
|
2028
|
-
type: v__namespace.literal("system.release"),
|
|
2029
|
-
releaseName: NonEmptyString
|
|
2030
|
-
}) ])
|
|
2031
|
-
};
|
|
1846
|
+
function firstDuplicatePair(items, keyOf) {
|
|
1847
|
+
const seen = /* @__PURE__ */ new Map;
|
|
1848
|
+
for (const item of items) {
|
|
1849
|
+
const key = keyOf(item), earlier = seen.get(key);
|
|
1850
|
+
if (earlier !== void 0) return [ earlier, item ];
|
|
1851
|
+
seen.set(key, item);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
2032
1854
|
|
|
2033
|
-
function
|
|
2034
|
-
|
|
1855
|
+
function duplicateHandleMessage(bindings) {
|
|
1856
|
+
const pair = firstDuplicatePair(bindings, binding => binding.name);
|
|
1857
|
+
if (pair !== void 0) return `duplicate resource handle name "${pair[1].name}" — each binding name must be unique within a deployment`;
|
|
2035
1858
|
}
|
|
2036
1859
|
|
|
2037
|
-
function
|
|
2038
|
-
const
|
|
2039
|
-
if (
|
|
2040
|
-
entryType: args.entryType,
|
|
2041
|
-
entryName: args.entryName,
|
|
2042
|
-
issues: [ `field entry type ${args.entryType} does not support append` ],
|
|
2043
|
-
mode: "item"
|
|
2044
|
-
});
|
|
2045
|
-
const result = v__namespace.safeParse(schema, args.item);
|
|
2046
|
-
if (!result.success) throw new FieldValueShapeError({
|
|
2047
|
-
entryType: args.entryType,
|
|
2048
|
-
entryName: args.entryName,
|
|
2049
|
-
issues: formatIssues(result.issues),
|
|
2050
|
-
mode: "item"
|
|
2051
|
-
});
|
|
2052
|
-
const assignmentArgs = {
|
|
2053
|
-
entryType: appendAssignmentEntryType(args.entryType),
|
|
2054
|
-
value: result.output,
|
|
2055
|
-
roles: args.roles,
|
|
2056
|
-
fields: args.entryType === "array" ? args.of : void 0,
|
|
2057
|
-
memberRoles: args.memberRoles,
|
|
2058
|
-
roleAliases: args.roleAliases
|
|
2059
|
-
}, assignmentIssues = assignmentValueIssues(assignmentArgs, result.output);
|
|
2060
|
-
if (assignmentIssues !== void 0) throw new FieldValueShapeError({
|
|
2061
|
-
entryType: args.entryType,
|
|
2062
|
-
entryName: args.entryName,
|
|
2063
|
-
issues: assignmentIssues,
|
|
2064
|
-
mode: "item"
|
|
2065
|
-
});
|
|
2066
|
-
const typeIssues = refTypeIssues({
|
|
2067
|
-
entryType: args.entryType,
|
|
2068
|
-
types: args.types,
|
|
2069
|
-
value: [ args.item ]
|
|
2070
|
-
});
|
|
2071
|
-
if (typeIssues !== void 0) throw new FieldValueShapeError({
|
|
2072
|
-
entryType: args.entryType,
|
|
2073
|
-
entryName: args.entryName,
|
|
2074
|
-
issues: typeIssues,
|
|
2075
|
-
mode: "item"
|
|
2076
|
-
});
|
|
2077
|
-
return result.output;
|
|
1860
|
+
function duplicateNameMessage(deployments) {
|
|
1861
|
+
const pair = firstDuplicatePair(deployments, deployment => deployment.name);
|
|
1862
|
+
if (pair !== void 0) return `duplicate deployment name "${pair[1].name}" — each deployment must use a unique name`;
|
|
2078
1863
|
}
|
|
2079
1864
|
|
|
2080
|
-
function
|
|
2081
|
-
return
|
|
1865
|
+
function partitionKey(deployment) {
|
|
1866
|
+
return `${resourceGdr(deployment.workflowResource)} ${deployment.tag}`;
|
|
2082
1867
|
}
|
|
2083
1868
|
|
|
2084
|
-
function
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
};
|
|
2089
|
-
return issues.flatMap(issue => formatOne(issue, []));
|
|
1869
|
+
function partitionCollisionMessage(deployments) {
|
|
1870
|
+
const pair = firstDuplicatePair(deployments, partitionKey);
|
|
1871
|
+
if (pair === void 0) return;
|
|
1872
|
+
const [first, second] = pair;
|
|
1873
|
+
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`;
|
|
2090
1874
|
}
|
|
2091
1875
|
|
|
2092
|
-
const
|
|
1876
|
+
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({
|
|
1877
|
+
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")),
|
|
1878
|
+
telemetry: v__namespace.optional(TelemetryLoggerSchema)
|
|
1879
|
+
});
|
|
2093
1880
|
|
|
2094
|
-
function
|
|
2095
|
-
return
|
|
1881
|
+
function resourceAliasesToMap(resourceAliases) {
|
|
1882
|
+
return Object.fromEntries((resourceAliases ?? []).map(binding => [ binding.name, binding.resource ]));
|
|
2096
1883
|
}
|
|
2097
1884
|
|
|
2098
|
-
|
|
2099
|
-
return v__namespace.picklist(options, invalidOptionMessage(options));
|
|
2100
|
-
}
|
|
1885
|
+
const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
|
|
2101
1886
|
|
|
2102
|
-
function
|
|
2103
|
-
return
|
|
1887
|
+
function isGuardReadExpr(expr) {
|
|
1888
|
+
return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
|
|
2104
1889
|
}
|
|
2105
1890
|
|
|
2106
|
-
function
|
|
2107
|
-
|
|
1891
|
+
function printGuardRead(read) {
|
|
1892
|
+
switch (read.type) {
|
|
1893
|
+
case "self":
|
|
1894
|
+
return "$self";
|
|
1895
|
+
|
|
1896
|
+
case "now":
|
|
1897
|
+
return "$now";
|
|
1898
|
+
|
|
1899
|
+
case "fieldRead":
|
|
1900
|
+
return read.path === void 0 ? `$fields.${read.field}` : `$fields.${read.field}.${read.path}`;
|
|
1901
|
+
|
|
1902
|
+
case "effectsRead":
|
|
1903
|
+
return read.path === void 0 ? `$effects['${read.effect}']` : `$effects['${read.effect}'].${read.path}`;
|
|
1904
|
+
}
|
|
2108
1905
|
}
|
|
2109
1906
|
|
|
2110
|
-
function
|
|
2111
|
-
return (
|
|
1907
|
+
function groq(strings, ...values) {
|
|
1908
|
+
return strings.reduce((out, part, i) => i === 0 ? part : `${out}${serializeGroqValue(values[i - 1])}${part}`, "");
|
|
2112
1909
|
}
|
|
2113
1910
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
scope: v__namespace.optional(v__namespace.union([ v__namespace.literal("workflow"), v__namespace.literal("stage") ])),
|
|
2120
|
-
field: NonEmpty,
|
|
2121
|
-
path: v__namespace.optional(v__namespace.string())
|
|
2122
|
-
}), FieldSourceSchema = v__namespace.variant("type", [ v__namespace.strictObject({
|
|
2123
|
-
type: v__namespace.literal("input")
|
|
2124
|
-
}), v__namespace.strictObject({
|
|
2125
|
-
type: v__namespace.literal("query"),
|
|
2126
|
-
query: NonEmpty
|
|
2127
|
-
}), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.variant("type", [ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
|
|
2128
|
-
type: v__namespace.literal("param"),
|
|
2129
|
-
param: NonEmpty
|
|
2130
|
-
}), v__namespace.strictObject({
|
|
2131
|
-
type: v__namespace.literal("actor")
|
|
2132
|
-
}), v__namespace.strictObject({
|
|
2133
|
-
type: v__namespace.literal("now")
|
|
2134
|
-
}), v__namespace.strictObject({
|
|
2135
|
-
type: v__namespace.literal("self")
|
|
2136
|
-
}), v__namespace.strictObject({
|
|
2137
|
-
type: v__namespace.literal("stage")
|
|
2138
|
-
}), v__namespace.strictObject({
|
|
2139
|
-
type: v__namespace.literal("object"),
|
|
2140
|
-
fields: v__namespace.record(NonEmpty, ValueExprSchema)
|
|
2141
|
-
}) ])), StoredFieldRefSchema = v__namespace.strictObject({
|
|
2142
|
-
scope: picklist(FIELD_SCOPES),
|
|
2143
|
-
field: NonEmpty
|
|
2144
|
-
}), AuthoringFieldRefSchema = v__namespace.strictObject({
|
|
2145
|
-
scope: v__namespace.optional(picklist(FIELD_SCOPES)),
|
|
2146
|
-
field: NonEmpty
|
|
2147
|
-
}), HREF_SCHEMES = [ "http:", "https:" ];
|
|
1911
|
+
function serializeGroqValue(value) {
|
|
1912
|
+
const serialized = JSON.stringify(value);
|
|
1913
|
+
if (serialized === void 0) throw new Error(`groq tag cannot serialize ${typeof value} — interpolate JSON-representable values only`);
|
|
1914
|
+
return serialized;
|
|
1915
|
+
}
|
|
2148
1916
|
|
|
2149
|
-
function
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
1917
|
+
function desugarWorkflow(authoring) {
|
|
1918
|
+
const issues = [];
|
|
1919
|
+
checkReservedRoleAliasKeys(authoring.roleAliases, issues);
|
|
1920
|
+
const roleAliases = normalizeRoleAliases(authoring.roleAliases), ctx = {
|
|
1921
|
+
issues: issues,
|
|
1922
|
+
roleAliases: roleAliases,
|
|
1923
|
+
effectRuntime: /* @__PURE__ */ new Map
|
|
1924
|
+
}, workflowFields2 = desugarFieldEntries({
|
|
1925
|
+
entries: authoring.fields,
|
|
1926
|
+
path: [ "fields" ],
|
|
1927
|
+
ctx: ctx
|
|
1928
|
+
}), workflowLayer = layerOf(workflowFields2), stages = authoring.stages.map((stage, i) => {
|
|
1929
|
+
const path = [ "stages", i ], stageFields2 = desugarFieldEntries({
|
|
1930
|
+
entries: stage.fields,
|
|
1931
|
+
path: [ ...path, "fields" ],
|
|
1932
|
+
ctx: ctx
|
|
1933
|
+
}), stageEnv = {
|
|
1934
|
+
layers: [ {
|
|
1935
|
+
scope: "stage",
|
|
1936
|
+
entries: layerOf(stageFields2)
|
|
1937
|
+
}, {
|
|
1938
|
+
scope: "workflow",
|
|
1939
|
+
entries: workflowLayer
|
|
1940
|
+
} ]
|
|
1941
|
+
}, activities = (stage.activities ?? []).map((activity, j) => desugarActivity({
|
|
1942
|
+
activity: activity,
|
|
1943
|
+
path: [ ...path, "activities", j ],
|
|
1944
|
+
stageEnv: stageEnv,
|
|
1945
|
+
ctx: ctx
|
|
1946
|
+
})), transitions = (stage.transitions ?? []).map(transition => desugarTransition({
|
|
1947
|
+
transition: transition
|
|
1948
|
+
})), editable = desugarStageEditable({
|
|
1949
|
+
overrides: stage.editable,
|
|
1950
|
+
inScope: editableFieldNames({
|
|
1951
|
+
workflowFields: workflowFields2,
|
|
1952
|
+
stageFields: stageFields2,
|
|
1953
|
+
activities: activities
|
|
1954
|
+
}),
|
|
1955
|
+
path: [ ...path, "editable" ],
|
|
1956
|
+
ctx: ctx
|
|
1957
|
+
});
|
|
1958
|
+
return {
|
|
1959
|
+
...stripUndefined({
|
|
1960
|
+
name: stage.name,
|
|
1961
|
+
semantics: stage.semantics,
|
|
1962
|
+
title: stage.title,
|
|
1963
|
+
description: stage.description,
|
|
1964
|
+
groups: stage.groups,
|
|
1965
|
+
guards: stage.guards?.map(desugarGuard)
|
|
1966
|
+
}),
|
|
1967
|
+
...stageFields2 ? {
|
|
1968
|
+
fields: stageFields2
|
|
1969
|
+
} : {},
|
|
1970
|
+
...activities.length > 0 ? {
|
|
1971
|
+
activities: activities
|
|
1972
|
+
} : {},
|
|
1973
|
+
...transitions.length > 0 ? {
|
|
1974
|
+
transitions: transitions
|
|
1975
|
+
} : {},
|
|
1976
|
+
...editable ? {
|
|
1977
|
+
editable: editable
|
|
1978
|
+
} : {}
|
|
1979
|
+
};
|
|
1980
|
+
});
|
|
1981
|
+
return {
|
|
1982
|
+
definition: {
|
|
1983
|
+
...stripUndefined({
|
|
1984
|
+
name: authoring.name,
|
|
1985
|
+
semantics: authoring.semantics,
|
|
1986
|
+
title: authoring.title,
|
|
1987
|
+
description: authoring.description,
|
|
1988
|
+
groups: authoring.groups,
|
|
1989
|
+
lifecycle: authoring.lifecycle,
|
|
1990
|
+
start: desugarStart(authoring.start),
|
|
1991
|
+
initialStage: authoring.initialStage,
|
|
1992
|
+
predicates: authoring.predicates,
|
|
1993
|
+
roleAliases: roleAliases
|
|
1994
|
+
}),
|
|
1995
|
+
...workflowFields2 ? {
|
|
1996
|
+
fields: workflowFields2
|
|
1997
|
+
} : {},
|
|
1998
|
+
stages: stages
|
|
1999
|
+
},
|
|
2000
|
+
runtime: collectRuntime({
|
|
2001
|
+
authoring: authoring,
|
|
2002
|
+
ctx: ctx
|
|
2003
|
+
}),
|
|
2004
|
+
issues: ctx.issues
|
|
2005
|
+
};
|
|
2155
2006
|
}
|
|
2156
2007
|
|
|
2157
|
-
|
|
2008
|
+
function collectRuntime(args) {
|
|
2009
|
+
const {kind: kind} = args.authoring.runtime ?? {}, effects = Object.fromEntries(args.ctx.effectRuntime), declared = {
|
|
2010
|
+
...kind === void 0 ? {} : {
|
|
2011
|
+
kind: kind
|
|
2012
|
+
},
|
|
2013
|
+
...Object.keys(effects).length === 0 ? {} : {
|
|
2014
|
+
effects: effects
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
return Object.keys(declared).length === 0 ? void 0 : declared;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
function desugarEffect(args) {
|
|
2021
|
+
const {runtime: runtime, ...effect} = args.effect;
|
|
2022
|
+
runtime !== void 0 && args.ctx.effectRuntime.set(effect.name, runtime);
|
|
2023
|
+
const {retry: retry} = effect;
|
|
2024
|
+
return retry === void 0 ? effect : {
|
|
2025
|
+
...effect,
|
|
2026
|
+
retry: {
|
|
2027
|
+
...retry,
|
|
2028
|
+
kind: retry.kind ?? "engine"
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2158
2032
|
|
|
2159
|
-
function
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2033
|
+
function desugarStart(start) {
|
|
2034
|
+
if (start !== void 0) return {
|
|
2035
|
+
kind: start.kind ?? "interactive",
|
|
2036
|
+
...start.filter !== void 0 ? {
|
|
2037
|
+
filter: start.filter
|
|
2038
|
+
} : {},
|
|
2039
|
+
...start.requirements !== void 0 ? {
|
|
2040
|
+
requirements: start.requirements
|
|
2041
|
+
} : {}
|
|
2042
|
+
};
|
|
2167
2043
|
}
|
|
2168
2044
|
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
target: targetSchema,
|
|
2175
|
-
value: ValueExprSchema
|
|
2176
|
-
}), v__namespace.strictObject({
|
|
2177
|
-
type: v__namespace.literal("field.setIfMissing"),
|
|
2178
|
-
target: targetSchema,
|
|
2179
|
-
value: ValueExprSchema
|
|
2180
|
-
}), v__namespace.strictObject({
|
|
2181
|
-
type: v__namespace.literal("field.unset"),
|
|
2182
|
-
target: targetSchema
|
|
2183
|
-
}), v__namespace.strictObject({
|
|
2184
|
-
type: v__namespace.literal("field.append"),
|
|
2185
|
-
target: targetSchema,
|
|
2186
|
-
value: ValueExprSchema
|
|
2187
|
-
}), v__namespace.strictObject({
|
|
2188
|
-
type: v__namespace.literal("field.inc"),
|
|
2189
|
-
target: targetSchema,
|
|
2190
|
-
value: v__namespace.optional(ValueExprSchema)
|
|
2191
|
-
}), v__namespace.strictObject({
|
|
2192
|
-
type: v__namespace.literal("field.dec"),
|
|
2193
|
-
target: targetSchema,
|
|
2194
|
-
value: v__namespace.optional(ValueExprSchema)
|
|
2195
|
-
}), v__namespace.strictObject({
|
|
2196
|
-
type: v__namespace.literal("field.updateWhere"),
|
|
2197
|
-
target: targetSchema,
|
|
2198
|
-
where: ConditionSchema,
|
|
2199
|
-
value: ValueExprSchema
|
|
2200
|
-
}), v__namespace.strictObject({
|
|
2201
|
-
type: v__namespace.literal("field.removeWhere"),
|
|
2202
|
-
target: targetSchema,
|
|
2203
|
-
where: ConditionSchema
|
|
2204
|
-
}) ];
|
|
2045
|
+
function checkReservedRoleAliasKeys(aliases, issues) {
|
|
2046
|
+
for (const key of Object.keys(aliases ?? {})) key.startsWith("$") && issues.push({
|
|
2047
|
+
path: [ "roleAliases", key ],
|
|
2048
|
+
message: `role alias key "${key}" uses the reserved "$" prefix — that namespace is the engine's stored spelling for the universal fulfiller. Use "*" to mean "fulfills any gate", or rename the role.`
|
|
2049
|
+
});
|
|
2205
2050
|
}
|
|
2206
2051
|
|
|
2207
|
-
const
|
|
2208
|
-
type:
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
}
|
|
2212
|
-
type:
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
}
|
|
2220
|
-
type:
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2052
|
+
const TODOLIST_OF = [ {
|
|
2053
|
+
type: "string",
|
|
2054
|
+
name: "label",
|
|
2055
|
+
title: "Label"
|
|
2056
|
+
}, {
|
|
2057
|
+
type: "string",
|
|
2058
|
+
name: "status",
|
|
2059
|
+
title: "Status"
|
|
2060
|
+
}, {
|
|
2061
|
+
type: "assignee",
|
|
2062
|
+
name: "assignee",
|
|
2063
|
+
title: "Assignee"
|
|
2064
|
+
}, {
|
|
2065
|
+
type: "date",
|
|
2066
|
+
name: "dueDate",
|
|
2067
|
+
title: "Due date"
|
|
2068
|
+
} ], NOTES_OF = [ {
|
|
2069
|
+
type: "text",
|
|
2070
|
+
name: "body",
|
|
2071
|
+
title: "Body"
|
|
2072
|
+
}, {
|
|
2073
|
+
type: "actor",
|
|
2074
|
+
name: "actor",
|
|
2075
|
+
title: "Actor"
|
|
2076
|
+
}, {
|
|
2077
|
+
type: "datetime",
|
|
2078
|
+
name: "at",
|
|
2079
|
+
title: "At"
|
|
2080
|
+
} ];
|
|
2229
2081
|
|
|
2230
|
-
function
|
|
2231
|
-
return
|
|
2082
|
+
function desugarFieldEntries({entries: entries, path: path, ctx: ctx}) {
|
|
2083
|
+
return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({
|
|
2084
|
+
entry: entry,
|
|
2085
|
+
path: [ ...path, i ],
|
|
2086
|
+
ctx: ctx
|
|
2087
|
+
}));
|
|
2232
2088
|
}
|
|
2233
2089
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2090
|
+
function desugarFieldEntry({entry: entry, path: path, ctx: ctx}) {
|
|
2091
|
+
if (entry.type === "todoList" || entry.type === "notes") return desugarListField({
|
|
2092
|
+
entry: entry,
|
|
2093
|
+
path: path,
|
|
2094
|
+
ctx: ctx
|
|
2095
|
+
});
|
|
2096
|
+
const editable = normalizeEditable({
|
|
2097
|
+
editable: entry.editable,
|
|
2098
|
+
path: [ ...path, "editable" ],
|
|
2099
|
+
ctx: ctx
|
|
2100
|
+
});
|
|
2101
|
+
return {
|
|
2102
|
+
...stripUndefined({
|
|
2103
|
+
type: entry.type,
|
|
2104
|
+
name: entry.name,
|
|
2105
|
+
title: entry.title,
|
|
2106
|
+
description: entry.description,
|
|
2107
|
+
group: normalizeGroup(entry.group),
|
|
2108
|
+
required: entry.required,
|
|
2109
|
+
initialValue: entry.initialValue,
|
|
2110
|
+
editable: editable,
|
|
2111
|
+
options: entry.options,
|
|
2112
|
+
validation: entry.validation,
|
|
2113
|
+
types: entry.types,
|
|
2114
|
+
roles: entry.roles,
|
|
2115
|
+
fields: entry.fields,
|
|
2116
|
+
of: entry.of
|
|
2117
|
+
})
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2243
2120
|
|
|
2244
|
-
function
|
|
2245
|
-
|
|
2121
|
+
function desugarListField({entry: entry, path: path, ctx: ctx}) {
|
|
2122
|
+
const editable = normalizeEditable({
|
|
2123
|
+
editable: entry.editable,
|
|
2124
|
+
path: [ ...path, "editable" ],
|
|
2125
|
+
ctx: ctx
|
|
2126
|
+
});
|
|
2127
|
+
return {
|
|
2128
|
+
...stripUndefined({
|
|
2129
|
+
name: entry.name,
|
|
2130
|
+
title: entry.title,
|
|
2131
|
+
description: entry.description,
|
|
2132
|
+
group: normalizeGroup(entry.group),
|
|
2133
|
+
required: entry.required,
|
|
2134
|
+
initialValue: entry.initialValue,
|
|
2135
|
+
editable: editable
|
|
2136
|
+
}),
|
|
2137
|
+
type: "array",
|
|
2138
|
+
of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
|
|
2139
|
+
};
|
|
2246
2140
|
}
|
|
2247
2141
|
|
|
2248
|
-
function
|
|
2249
|
-
|
|
2250
|
-
|
|
2142
|
+
function normalizeEditable({editable: editable, path: path, ctx: ctx}) {
|
|
2143
|
+
if (editable === void 0 || editable === !0) return editable;
|
|
2144
|
+
if (Array.isArray(editable)) {
|
|
2145
|
+
const condition = rolesCondition(editable, ctx.roleAliases);
|
|
2146
|
+
if (condition === void 0) {
|
|
2147
|
+
ctx.issues.push({
|
|
2148
|
+
path: path,
|
|
2149
|
+
message: "editable: [] names no roles — use `true` to open the field to anyone in its scope, or list at least one role"
|
|
2150
|
+
});
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
return condition;
|
|
2154
|
+
}
|
|
2155
|
+
return editable;
|
|
2251
2156
|
}
|
|
2252
2157
|
|
|
2253
|
-
function
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2158
|
+
function editableFieldNames({workflowFields: workflowFields2, stageFields: stageFields2, activities: activities}) {
|
|
2159
|
+
const names = /* @__PURE__ */ new Set, collect = entries => {
|
|
2160
|
+
for (const entry of entries ?? []) entry.editable !== void 0 && names.add(entry.name);
|
|
2161
|
+
};
|
|
2162
|
+
collect(workflowFields2), collect(stageFields2);
|
|
2163
|
+
for (const activity of activities) collect(activity.fields);
|
|
2164
|
+
return names;
|
|
2256
2165
|
}
|
|
2257
2166
|
|
|
2258
|
-
function
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2167
|
+
function desugarStageEditable({overrides: overrides, inScope: inScope, path: path, ctx: ctx}) {
|
|
2168
|
+
if (overrides === void 0) return;
|
|
2169
|
+
const out = {};
|
|
2170
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
2171
|
+
if (!inScope.has(name)) {
|
|
2172
|
+
ctx.issues.push({
|
|
2173
|
+
path: [ ...path, name ],
|
|
2174
|
+
message: `stage editable override "${name}" does not narrow an editable field in scope — name a workflow/stage/activity field of this stage that declares \`editable\``
|
|
2175
|
+
});
|
|
2176
|
+
continue;
|
|
2268
2177
|
}
|
|
2178
|
+
const normalized = normalizeEditable({
|
|
2179
|
+
editable: value,
|
|
2180
|
+
path: [ ...path, name ],
|
|
2181
|
+
ctx: ctx
|
|
2182
|
+
});
|
|
2183
|
+
normalized !== void 0 && (out[name] = normalized);
|
|
2269
2184
|
}
|
|
2185
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
2270
2186
|
}
|
|
2271
2187
|
|
|
2272
|
-
function
|
|
2273
|
-
return
|
|
2188
|
+
function layerOf(entries) {
|
|
2189
|
+
return new Map((entries ?? []).map(entry => [ entry.name, entry ]));
|
|
2274
2190
|
}
|
|
2275
2191
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2280
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2281
|
-
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
2282
|
-
validation: v__namespace.optional(ScalarValidationSchema),
|
|
2283
|
-
roles: v__namespace.optional(AssignmentRolesSchema),
|
|
2284
|
-
fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
|
|
2285
|
-
of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
|
|
2286
|
-
}), choiceOptionsCheck(), scalarValidationCheck(), assignmentRolesCheck())), StoredEditableSchema = v__namespace.union([ v__namespace.literal(!0), NonEmpty ]), AuthoringEditableSchema = v__namespace.union([ v__namespace.literal(!0), v__namespace.array(NonEmpty), NonEmpty ]);
|
|
2192
|
+
function normalizeGroup(group) {
|
|
2193
|
+
return group === void 0 || Array.isArray(group) ? group : [ group ];
|
|
2194
|
+
}
|
|
2287
2195
|
|
|
2288
|
-
function
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2196
|
+
function desugarActivity({activity: activity, path: path, stageEnv: stageEnv, ctx: ctx}) {
|
|
2197
|
+
const activityFields2 = desugarFieldEntries({
|
|
2198
|
+
entries: activity.fields,
|
|
2199
|
+
path: [ ...path, "fields" ],
|
|
2200
|
+
ctx: ctx
|
|
2201
|
+
}), env = {
|
|
2202
|
+
layers: [ {
|
|
2203
|
+
scope: "activity",
|
|
2204
|
+
entries: layerOf(activityFields2)
|
|
2205
|
+
}, ...stageEnv.layers ]
|
|
2206
|
+
}, actions = (activity.actions ?? []).map((action, a) => desugarAction({
|
|
2207
|
+
action: action,
|
|
2208
|
+
path: [ ...path, "actions", a ],
|
|
2209
|
+
env: env,
|
|
2210
|
+
activityName: activity.name,
|
|
2211
|
+
ctx: ctx
|
|
2212
|
+
})), target = desugarTarget({
|
|
2213
|
+
target: activity.target,
|
|
2214
|
+
env: env,
|
|
2215
|
+
path: [ ...path, "target" ],
|
|
2216
|
+
ctx: ctx
|
|
2217
|
+
});
|
|
2218
|
+
return {
|
|
2219
|
+
...stripUndefined({
|
|
2220
|
+
name: activity.name,
|
|
2221
|
+
semantics: activity.semantics,
|
|
2222
|
+
title: activity.title,
|
|
2223
|
+
description: activity.description,
|
|
2224
|
+
groups: activity.groups,
|
|
2225
|
+
group: normalizeGroup(activity.group),
|
|
2226
|
+
filter: activity.filter,
|
|
2227
|
+
requirements: activity.requirements
|
|
2228
|
+
}),
|
|
2229
|
+
...target ? {
|
|
2230
|
+
target: target
|
|
2231
|
+
} : {},
|
|
2232
|
+
...activityFields2 ? {
|
|
2233
|
+
fields: activityFields2
|
|
2234
|
+
} : {},
|
|
2235
|
+
...actions.length > 0 ? {
|
|
2236
|
+
actions: actions
|
|
2237
|
+
} : {}
|
|
2297
2238
|
};
|
|
2298
2239
|
}
|
|
2299
2240
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2241
|
+
const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref" ];
|
|
2242
|
+
|
|
2243
|
+
function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
|
|
2244
|
+
if (target === void 0 || target.type === "url") return target;
|
|
2245
|
+
const ref = typeof target.field == "string" ? {
|
|
2246
|
+
field: target.field
|
|
2247
|
+
} : target.field, field = resolveRef({
|
|
2248
|
+
ref: ref,
|
|
2249
|
+
env: env,
|
|
2250
|
+
path: [ ...path, "field" ],
|
|
2251
|
+
ctx: ctx
|
|
2252
|
+
}) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
2253
|
+
return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
|
|
2254
|
+
path: [ ...path, "field" ],
|
|
2255
|
+
message: `manual activity target references "${field.field}" of kind "${entry.type}" — a deep-link target needs a document-valued entry (${TARGET_DOC_KINDS.join(", ")})`
|
|
2256
|
+
}), {
|
|
2257
|
+
type: "field",
|
|
2258
|
+
field: field
|
|
2310
2259
|
};
|
|
2311
2260
|
}
|
|
2312
2261
|
|
|
2313
|
-
function
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
types: entry.types,
|
|
2318
|
-
fields: entry.fields,
|
|
2319
|
-
of: entry.of,
|
|
2320
|
-
options: entry.options,
|
|
2321
|
-
validation: entry.validation,
|
|
2322
|
-
roles: entry.roles
|
|
2262
|
+
function reportEmptyActionRoles({action: action, path: path, ctx: ctx}) {
|
|
2263
|
+
action.roles === void 0 || action.roles.length > 0 || ctx.issues.push({
|
|
2264
|
+
path: [ ...path, "roles" ],
|
|
2265
|
+
message: "roles: [] names no roles — omit it to allow any identity, or list at least one role"
|
|
2323
2266
|
});
|
|
2324
2267
|
}
|
|
2325
2268
|
|
|
2326
|
-
function
|
|
2327
|
-
|
|
2269
|
+
function desugarActionOps(args) {
|
|
2270
|
+
const {action: action, path: path, env: env, activityName: activityName, ctx: ctx} = args, ops = desugarOps({
|
|
2271
|
+
ops: action.ops,
|
|
2272
|
+
path: [ ...path, "ops" ],
|
|
2273
|
+
env: env,
|
|
2274
|
+
firingActivity: activityName,
|
|
2275
|
+
ctx: ctx
|
|
2276
|
+
}) ?? [];
|
|
2277
|
+
return action.status !== void 0 && ops.push({
|
|
2278
|
+
type: "status.set",
|
|
2279
|
+
activity: activityName,
|
|
2280
|
+
status: action.status
|
|
2281
|
+
}), ops;
|
|
2328
2282
|
}
|
|
2329
2283
|
|
|
2330
|
-
function
|
|
2331
|
-
|
|
2284
|
+
function desugarAction({action: action, path: path, env: env, activityName: activityName, ctx: ctx}) {
|
|
2285
|
+
reportEmptyActionRoles({
|
|
2286
|
+
action: action,
|
|
2287
|
+
path: path,
|
|
2288
|
+
ctx: ctx
|
|
2289
|
+
});
|
|
2290
|
+
const cascadeFired = isCascadeFired(action), filter = cascadeFired ? action.filter : andConditions([ rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = desugarActionOps({
|
|
2291
|
+
action: action,
|
|
2292
|
+
path: path,
|
|
2293
|
+
env: env,
|
|
2294
|
+
activityName: activityName,
|
|
2295
|
+
ctx: ctx
|
|
2296
|
+
});
|
|
2297
|
+
return {
|
|
2298
|
+
...stripUndefined({
|
|
2299
|
+
name: action.name,
|
|
2300
|
+
semantics: action.semantics,
|
|
2301
|
+
title: action.title,
|
|
2302
|
+
description: action.description,
|
|
2303
|
+
group: normalizeGroup(action.group),
|
|
2304
|
+
when: action.when,
|
|
2305
|
+
params: action.params,
|
|
2306
|
+
effects: action.effects?.map(effect => desugarEffect({
|
|
2307
|
+
effect: effect,
|
|
2308
|
+
ctx: ctx
|
|
2309
|
+
})),
|
|
2310
|
+
spawn: action.spawn
|
|
2311
|
+
}),
|
|
2312
|
+
...cascadeFired && action.roles !== void 0 && action.roles.length > 0 ? {
|
|
2313
|
+
roles: action.roles
|
|
2314
|
+
} : {},
|
|
2315
|
+
...filter ? {
|
|
2316
|
+
filter: filter
|
|
2317
|
+
} : {},
|
|
2318
|
+
...ops.length > 0 ? {
|
|
2319
|
+
ops: ops
|
|
2320
|
+
} : {}
|
|
2321
|
+
};
|
|
2332
2322
|
}
|
|
2333
2323
|
|
|
2334
|
-
|
|
2335
|
-
return v__namespace.check(entry => entry.roles === void 0 || assignmentKindAcceptsRoles(entry.type), issue => `\`roles\` is only valid on \`assignee\` / \`assignees\` entries, not "${issue.input.type}"`);
|
|
2336
|
-
}
|
|
2324
|
+
const DEFAULT_TRANSITION_WHEN = "$allActivitiesDone";
|
|
2337
2325
|
|
|
2338
|
-
function
|
|
2339
|
-
return
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
}
|
|
2326
|
+
function desugarTransition({transition: transition}) {
|
|
2327
|
+
return {
|
|
2328
|
+
...stripUndefined({
|
|
2329
|
+
name: transition.name,
|
|
2330
|
+
title: transition.title,
|
|
2331
|
+
description: transition.description,
|
|
2332
|
+
to: transition.to
|
|
2333
|
+
}),
|
|
2334
|
+
when: transition.when ?? DEFAULT_TRANSITION_WHEN
|
|
2335
|
+
};
|
|
2348
2336
|
}
|
|
2349
2337
|
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2338
|
+
function desugarGuard(guard) {
|
|
2339
|
+
const {match: match, metadata: metadata, ...rest} = guard, {idRefs: idRefs, ...matchRest} = match;
|
|
2340
|
+
return {
|
|
2341
|
+
...rest,
|
|
2342
|
+
match: {
|
|
2343
|
+
...matchRest,
|
|
2344
|
+
...idRefs !== void 0 ? {
|
|
2345
|
+
idRefs: idRefs.map(printGuardRead)
|
|
2346
|
+
} : {}
|
|
2347
|
+
},
|
|
2348
|
+
...metadata !== void 0 ? {
|
|
2349
|
+
metadata: Object.fromEntries(Object.entries(metadata).map(([key, read]) => [ key, printGuardRead(read) ]))
|
|
2350
|
+
} : {}
|
|
2351
|
+
};
|
|
2354
2352
|
}
|
|
2355
2353
|
|
|
2356
|
-
function
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
}
|
|
2364
|
-
if (type === "number") return;
|
|
2365
|
-
const issues = Object.entries(validation).flatMap(([bound, value]) => Number.isInteger(value) && value >= 0 ? [] : [ `\`validation.${bound}\` must be a non-negative integer for ${type} length` ]);
|
|
2366
|
-
return issues.length === 0 ? void 0 : issues;
|
|
2354
|
+
function desugarOps({ops: ops, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
|
|
2355
|
+
if (ops) return ops.map((op, i) => desugarOp({
|
|
2356
|
+
op: op,
|
|
2357
|
+
path: [ ...path, i ],
|
|
2358
|
+
env: env,
|
|
2359
|
+
firingActivity: firingActivity,
|
|
2360
|
+
ctx: ctx
|
|
2361
|
+
}));
|
|
2367
2362
|
}
|
|
2368
2363
|
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2364
|
+
function desugarOp({op: op, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
|
|
2365
|
+
if (op.type === "status.set") return {
|
|
2366
|
+
type: "status.set",
|
|
2367
|
+
activity: op.activity ?? firingActivity,
|
|
2368
|
+
status: op.status
|
|
2369
|
+
};
|
|
2370
|
+
if (op.type === "audit") return desugarAuditOp({
|
|
2371
|
+
op: op,
|
|
2372
|
+
path: path,
|
|
2373
|
+
env: env,
|
|
2374
|
+
ctx: ctx
|
|
2375
|
+
});
|
|
2376
|
+
const target = resolveRef({
|
|
2377
|
+
ref: op.target,
|
|
2378
|
+
env: env,
|
|
2379
|
+
path: [ ...path, "target" ],
|
|
2380
|
+
ctx: ctx
|
|
2381
|
+
}) ?? fallbackRef(op.target);
|
|
2385
2382
|
return {
|
|
2386
|
-
|
|
2387
|
-
|
|
2383
|
+
...op,
|
|
2384
|
+
target: target
|
|
2388
2385
|
};
|
|
2389
2386
|
}
|
|
2390
2387
|
|
|
2391
|
-
const
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown())),
|
|
2400
|
-
outputs: v__namespace.optional(v__namespace.array(FieldShapeSchema))
|
|
2401
|
-
}), DefinitionRefSchema = v__namespace.strictObject({
|
|
2402
|
-
name: NonEmpty,
|
|
2403
|
-
version: v__namespace.optional(v__namespace.union([ PositiveInt, v__namespace.literal("latest") ]))
|
|
2404
|
-
}), SubworkflowsSchema = v__namespace.strictObject({
|
|
2405
|
-
forEach: NonEmpty,
|
|
2406
|
-
definition: DefinitionRefSchema,
|
|
2407
|
-
with: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
|
|
2408
|
-
context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
|
|
2409
|
-
onExit: v__namespace.optional(picklist([ "detach", "abort" ]))
|
|
2410
|
-
}), ActionParamSchema = v__namespace.pipe(v__namespace.strictObject({
|
|
2411
|
-
type: picklist([ "string", "number", "boolean", "url", "dateTime", "actor", "doc.ref", "doc.refs", "json" ]),
|
|
2412
|
-
name: NonEmpty,
|
|
2413
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2414
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2415
|
-
required: v__namespace.optional(v__namespace.boolean()),
|
|
2416
|
-
options: v__namespace.optional(ChoiceOptionsSchema),
|
|
2417
|
-
validation: v__namespace.optional(ScalarValidationSchema)
|
|
2418
|
-
}), choiceOptionsCheck(), scalarValidationCheck()), CUSTOM_SEMANTIC_HINT = "`custom.<camelCaseMeaning>`", CustomSemanticSchema = v__namespace.custom(input => typeof input == "string" && /^custom\.[a-z][a-zA-Z0-9]*$/.test(input)), SemanticSchema = v__namespace.union([ picklist(SIGNAL_SEMANTICS), CustomSemanticSchema ], `expected ${SIGNAL_SEMANTICS.join(", ")}, or ${CUSTOM_SEMANTIC_HINT}`), ActionSemanticSchema = v__namespace.union([ picklist(ACTION_SEMANTICS), CustomSemanticSchema ], `expected ${ACTION_SEMANTICS.join(", ")}, or ${CUSTOM_SEMANTIC_HINT}`);
|
|
2388
|
+
const AUDIT_STAMPS = {
|
|
2389
|
+
actor: {
|
|
2390
|
+
type: "actor"
|
|
2391
|
+
},
|
|
2392
|
+
at: {
|
|
2393
|
+
type: "now"
|
|
2394
|
+
}
|
|
2395
|
+
};
|
|
2419
2396
|
|
|
2420
|
-
function
|
|
2421
|
-
|
|
2397
|
+
function desugarAuditOp({op: op, path: path, env: env, ctx: ctx}) {
|
|
2398
|
+
const target = resolveRef({
|
|
2399
|
+
ref: op.target,
|
|
2400
|
+
env: env,
|
|
2401
|
+
path: [ ...path, "target" ],
|
|
2402
|
+
ctx: ctx
|
|
2403
|
+
}) ?? fallbackRef(op.target);
|
|
2404
|
+
if (op.value.type !== "object") return ctx.issues.push({
|
|
2405
|
+
path: [ ...path, "value" ],
|
|
2406
|
+
message: `audit value must be an object source carrying the domain fields (got "${op.value.type}")`
|
|
2407
|
+
}), {
|
|
2408
|
+
type: "field.append",
|
|
2409
|
+
target: target,
|
|
2410
|
+
value: op.value
|
|
2411
|
+
};
|
|
2412
|
+
const actorField = op.stampFields?.actor ?? "actor", atField = op.stampFields?.at ?? "at", fields = {
|
|
2413
|
+
...op.value.fields
|
|
2414
|
+
};
|
|
2415
|
+
for (const [stamp, source] of [ [ actorField, AUDIT_STAMPS.actor ], [ atField, AUDIT_STAMPS.at ] ]) {
|
|
2416
|
+
if (stamp in fields) {
|
|
2417
|
+
ctx.issues.push({
|
|
2418
|
+
path: [ ...path, "value", "fields", stamp ],
|
|
2419
|
+
message: `audit stamp field "${stamp}" collides with an authored domain field — rename yours or remap the stamp via stampFields`
|
|
2420
|
+
});
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
fields[stamp] = source;
|
|
2424
|
+
}
|
|
2425
|
+
return {
|
|
2426
|
+
type: "field.append",
|
|
2427
|
+
target: target,
|
|
2428
|
+
value: {
|
|
2429
|
+
type: "object",
|
|
2430
|
+
fields: fields
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2422
2433
|
}
|
|
2423
2434
|
|
|
2424
|
-
function
|
|
2425
|
-
const
|
|
2426
|
-
|
|
2435
|
+
function resolveRef({ref: ref, env: env, path: path, ctx: ctx}) {
|
|
2436
|
+
const layers = ref.scope === void 0 ? env.layers : env.layers.filter(l => l.scope === ref.scope);
|
|
2437
|
+
for (const layer of layers) if (layer.entries.has(ref.field)) return {
|
|
2438
|
+
scope: layer.scope,
|
|
2439
|
+
field: ref.field
|
|
2440
|
+
};
|
|
2441
|
+
const reachable = env.layers.flatMap(l => [ ...l.entries.keys() ].map(n => `${l.scope}:${n}`));
|
|
2442
|
+
ctx.issues.push({
|
|
2443
|
+
path: path,
|
|
2444
|
+
message: `field reference "${ref.field}"${ref.scope ? ` (scope "${ref.scope}")` : ""} does not resolve to a declared entry. Reachable: ${reachable.join(", ") || "(none)"}`
|
|
2445
|
+
});
|
|
2427
2446
|
}
|
|
2428
2447
|
|
|
2429
|
-
function
|
|
2430
|
-
return
|
|
2448
|
+
function entryAt(env, ref) {
|
|
2449
|
+
return env.layers.find(l => l.scope === ref.scope)?.entries.get(ref.field);
|
|
2431
2450
|
}
|
|
2432
2451
|
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
function actionFields(op, group) {
|
|
2452
|
+
function fallbackRef(ref) {
|
|
2436
2453
|
return {
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2440
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2441
|
-
group: v__namespace.optional(group),
|
|
2442
|
-
when: v__namespace.optional(ConditionSchema),
|
|
2443
|
-
filter: v__namespace.optional(ConditionSchema),
|
|
2444
|
-
params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
|
|
2445
|
-
ops: v__namespace.optional(v__namespace.array(op)),
|
|
2446
|
-
effects: v__namespace.optional(v__namespace.array(EffectSchema)),
|
|
2447
|
-
spawn: v__namespace.optional(SubworkflowsSchema)
|
|
2454
|
+
scope: ref.scope ?? "workflow",
|
|
2455
|
+
field: ref.field
|
|
2448
2456
|
};
|
|
2449
2457
|
}
|
|
2450
2458
|
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
roles: v__namespace.optional(v__namespace.array(NonEmpty))
|
|
2454
|
-
})), TerminalActivityStatus = picklist(TERMINAL_ACTIVITY_STATUSES), RawAuthoringActionSchema = pinned()(v__namespace.strictObject({
|
|
2455
|
-
...actionFields(AuthoringOpSchema, AuthoringGroupMembershipSchema),
|
|
2456
|
-
roles: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
2457
|
-
status: v__namespace.optional(TerminalActivityStatus)
|
|
2458
|
-
})), ClaimActionSchema = pinned()(v__namespace.strictObject({
|
|
2459
|
-
type: v__namespace.literal("claim"),
|
|
2460
|
-
name: NonEmpty,
|
|
2461
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2462
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2463
|
-
group: v__namespace.optional(AuthoringGroupMembershipSchema),
|
|
2464
|
-
field: v__namespace.union([ NonEmpty, AuthoringFieldRefSchema ]),
|
|
2465
|
-
roles: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
2466
|
-
filter: v__namespace.optional(ConditionSchema),
|
|
2467
|
-
params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
|
|
2468
|
-
effects: v__namespace.optional(v__namespace.array(EffectSchema))
|
|
2469
|
-
})), AuthoringActionSchema = pinned()(v__namespace.lazy(input => typeof input == "object" && input !== null && "type" in input ? ClaimActionSchema : RawAuthoringActionSchema)), requirementBase = {
|
|
2470
|
-
name: NonEmpty,
|
|
2471
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2472
|
-
description: v__namespace.optional(v__namespace.string())
|
|
2473
|
-
}, GroqRequirementSchemaRaw = v__namespace.strictObject({
|
|
2474
|
-
...requirementBase,
|
|
2475
|
-
type: v__namespace.literal("groq"),
|
|
2476
|
-
query: ConditionSchema
|
|
2477
|
-
}), SingleSubjectRequirementSchemaRaw = v__namespace.strictObject({
|
|
2478
|
-
...requirementBase,
|
|
2479
|
-
type: v__namespace.literal("singleSubject")
|
|
2480
|
-
}), GroqRequirementSchema = pinned()(GroqRequirementSchemaRaw), StartRequirementSchema = pinned()(v__namespace.variant("type", [ GroqRequirementSchemaRaw, SingleSubjectRequirementSchemaRaw ]));
|
|
2481
|
-
|
|
2482
|
-
function activityFields({field: field, action: action, target: target, group: group}) {
|
|
2483
|
-
return {
|
|
2484
|
-
name: NonEmpty,
|
|
2485
|
-
semantics: SemanticsFieldSchema,
|
|
2486
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2487
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2488
|
-
groups: v__namespace.optional(v__namespace.array(GroupSchema)),
|
|
2489
|
-
group: v__namespace.optional(group),
|
|
2490
|
-
target: v__namespace.optional(target),
|
|
2491
|
-
filter: v__namespace.optional(ConditionSchema),
|
|
2492
|
-
requirements: v__namespace.optional(v__namespace.array(GroqRequirementSchema)),
|
|
2493
|
-
actions: v__namespace.optional(v__namespace.array(action)),
|
|
2494
|
-
fields: v__namespace.optional(v__namespace.array(field))
|
|
2495
|
-
};
|
|
2459
|
+
function rolesCondition(roles, aliases) {
|
|
2460
|
+
if (!(!roles || roles.length === 0)) return groq`count($actor.roles[@ in ${expandRequiredRoles(roles, aliases)}]) > 0`;
|
|
2496
2461
|
}
|
|
2497
2462
|
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
}))), AuthoringActivitySchema = pinned()(v__namespace.strictObject(activityFields({
|
|
2504
|
-
field: AuthoringFieldEntrySchema,
|
|
2505
|
-
action: AuthoringActionSchema,
|
|
2506
|
-
target: AuthoringManualTargetSchema,
|
|
2507
|
-
group: AuthoringGroupMembershipSchema
|
|
2508
|
-
})));
|
|
2463
|
+
function stripUndefined(obj) {
|
|
2464
|
+
const out = {};
|
|
2465
|
+
for (const [k, value] of Object.entries(obj)) value !== void 0 && (out[k] = value);
|
|
2466
|
+
return out;
|
|
2467
|
+
}
|
|
2509
2468
|
|
|
2510
|
-
function
|
|
2511
|
-
return
|
|
2512
|
-
name: NonEmpty,
|
|
2513
|
-
title: v__namespace.optional(v__namespace.string()),
|
|
2514
|
-
description: v__namespace.optional(v__namespace.string()),
|
|
2515
|
-
to: NonEmpty,
|
|
2516
|
-
when: when
|
|
2517
|
-
};
|
|
2469
|
+
function isUnevaluable(result) {
|
|
2470
|
+
return result == null;
|
|
2518
2471
|
}
|
|
2519
2472
|
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
}), v__namespace.strictObject({
|
|
2529
|
-
type: v__namespace.literal("effectsRead"),
|
|
2530
|
-
effect: v__namespace.pipe(NonEmpty, v__namespace.check(name => !name.includes("'"), "an effect name cannot contain `'`")),
|
|
2531
|
-
path: v__namespace.optional(GuardReadPath)
|
|
2532
|
-
}) ]);
|
|
2473
|
+
async function evaluateConditionOutcome(args) {
|
|
2474
|
+
const {condition: condition, snapshot: snapshot, params: params} = args;
|
|
2475
|
+
return groqConditionDescribe.evaluateConditionOutcome({
|
|
2476
|
+
condition: condition,
|
|
2477
|
+
params: params,
|
|
2478
|
+
dataset: snapshot.docs
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
2533
2481
|
|
|
2534
|
-
function
|
|
2535
|
-
return
|
|
2536
|
-
types: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
2537
|
-
idRefs: v__namespace.optional(v__namespace.array(read)),
|
|
2538
|
-
idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
|
|
2539
|
-
actions: v__namespace.pipe(v__namespace.array(GuardActionSchema), v__namespace.minLength(1, "a guard must match at least one action"))
|
|
2540
|
-
};
|
|
2482
|
+
async function evaluateCondition(args) {
|
|
2483
|
+
return await evaluateConditionOutcome(args) === "satisfied";
|
|
2541
2484
|
}
|
|
2542
2485
|
|
|
2543
|
-
function
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2486
|
+
async function evaluatePredicates(args) {
|
|
2487
|
+
const out = {};
|
|
2488
|
+
for (const [name, groq2] of Object.entries(args.predicates ?? {})) {
|
|
2489
|
+
const result = await runGroq({
|
|
2490
|
+
groq: groq2,
|
|
2491
|
+
params: args.params,
|
|
2492
|
+
snapshot: args.snapshot
|
|
2493
|
+
}), outcome = groqConditionDescribe.conditionOutcome(result);
|
|
2494
|
+
out[name] = outcome === "unevaluable" ? null : outcome === "satisfied";
|
|
2495
|
+
}
|
|
2496
|
+
return out;
|
|
2552
2497
|
}
|
|
2553
2498
|
|
|
2554
|
-
|
|
2499
|
+
async function runGroq({groq: groq2, params: params, snapshot: snapshot}) {
|
|
2500
|
+
return groqConditionDescribe.runGroq({
|
|
2501
|
+
groq: groq2,
|
|
2502
|
+
params: params,
|
|
2503
|
+
dataset: snapshot.docs
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2555
2506
|
|
|
2556
|
-
function
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
editable: v__namespace.optional(v__namespace.record(FieldEntryName, editable))
|
|
2568
|
-
};
|
|
2507
|
+
function conditionSyntaxIssues(groq2, boundVars) {
|
|
2508
|
+
const issues = [];
|
|
2509
|
+
let tree;
|
|
2510
|
+
try {
|
|
2511
|
+
tree = groqJs.parse(groq2);
|
|
2512
|
+
} catch (err) {
|
|
2513
|
+
issues.push(errorMessage(err));
|
|
2514
|
+
}
|
|
2515
|
+
if (/\*\s*\[\s*_type\b/.test(groq2) && issues.push("condition scans by `_type` — that's a discovery query, not a predicate. Conditions evaluate against the in-memory snapshot (instance + ancestors + field-declared docs). To bring extra docs in scope, declare a `doc.ref` (or `doc.refs`) field entry on the workflow or this stage. For lake scans like \"all articles in this release\", use a spawn action's `forEach` instead."),
|
|
2516
|
+
boundVars !== void 0 && tree !== void 0) for (const name of conditionParameterNames(groq2)) boundVars.includes(name) || issues.push(`reads $${name}, which this scope does not bind — an unbound variable evaluates to GROQ null, so the condition silently never matches. Bound here: ` + boundVars.map(n => `$${n}`).join(", "));
|
|
2517
|
+
return issues;
|
|
2569
2518
|
}
|
|
2570
2519
|
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
}))), AuthoringStageSchema = pinned()(v__namespace.strictObject(stageFields({
|
|
2578
|
-
field: AuthoringFieldEntrySchema,
|
|
2579
|
-
activity: AuthoringActivitySchema,
|
|
2580
|
-
transition: AuthoringTransitionSchema,
|
|
2581
|
-
guard: AuthoringGuardSchema,
|
|
2582
|
-
editable: AuthoringEditableSchema
|
|
2583
|
-
}))), RoleAliasesSchema = v__namespace.record(NonEmpty, v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "a role alias must list at least one fulfilling role"))), WORKFLOW_LIFECYCLES = [ "standalone", "child" ], START_KINDS = [ "interactive", "autonomous" ];
|
|
2520
|
+
function conditionParameterNames(groq2) {
|
|
2521
|
+
const read = /* @__PURE__ */ new Set;
|
|
2522
|
+
return walkAstNodes(tryParseGroq(groq2), node => {
|
|
2523
|
+
node.type === "Parameter" && typeof node.name == "string" && read.add(node.name);
|
|
2524
|
+
}), read;
|
|
2525
|
+
}
|
|
2584
2526
|
|
|
2585
|
-
function
|
|
2586
|
-
return
|
|
2587
|
-
kind: kind,
|
|
2588
|
-
filter: v__namespace.optional(ConditionSchema),
|
|
2589
|
-
requirements: v__namespace.optional(v__namespace.array(StartRequirementSchema))
|
|
2590
|
-
};
|
|
2527
|
+
function conditionFieldReadNames(groq2) {
|
|
2528
|
+
return new Set(conditionFieldReads(groq2).map(read => read.name));
|
|
2591
2529
|
}
|
|
2592
2530
|
|
|
2593
|
-
|
|
2531
|
+
function conditionFieldReads(groq2) {
|
|
2532
|
+
const reads = /* @__PURE__ */ new Map;
|
|
2533
|
+
return collectFieldReads(tryParseGroq(groq2), reads), [ ...reads.values() ];
|
|
2534
|
+
}
|
|
2594
2535
|
|
|
2595
|
-
function
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
}
|
|
2536
|
+
function collectFieldReads(node, reads) {
|
|
2537
|
+
if (Array.isArray(node)) {
|
|
2538
|
+
for (const item of node) collectFieldReads(item, reads);
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
2541
|
+
if (typeof node != "object" || node === null) return;
|
|
2542
|
+
const chain = fieldsAttributeChain(node);
|
|
2543
|
+
if (chain !== void 0) {
|
|
2544
|
+
const [name, ...tail] = chain, path = tail.length > 0 ? tail.join(".") : void 0;
|
|
2545
|
+
reads.set(`${name}.${path ?? ""}`, {
|
|
2546
|
+
name: name,
|
|
2547
|
+
path: path
|
|
2548
|
+
});
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
for (const value of Object.values(node)) collectFieldReads(value, reads);
|
|
2610
2552
|
}
|
|
2611
2553
|
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
});
|
|
2554
|
+
function fieldsAttributeChain(node) {
|
|
2555
|
+
const names = [];
|
|
2556
|
+
let current = node;
|
|
2557
|
+
for (;typeof current == "object" && current !== null; ) {
|
|
2558
|
+
const typed = current;
|
|
2559
|
+
if (typed.type !== "AccessAttribute" || typeof typed.name != "string") return;
|
|
2560
|
+
names.unshift(typed.name);
|
|
2561
|
+
const base = typed.base;
|
|
2562
|
+
if (base?.type === "Parameter" && base.name === "fields") return names;
|
|
2563
|
+
current = base;
|
|
2564
|
+
}
|
|
2624
2565
|
}
|
|
2625
2566
|
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2567
|
+
function conditionEffectReads(groq2) {
|
|
2568
|
+
const reads = [];
|
|
2569
|
+
return walkAstNodes(tryParseGroq(groq2), node => {
|
|
2570
|
+
if (node.type !== "AccessAttribute" || typeof node.name != "string") return;
|
|
2571
|
+
const base = node.base;
|
|
2572
|
+
base?.type !== "AccessAttribute" || typeof base.name != "string" || base.base?.type === "Parameter" && base.base.name === "effects" && reads.push({
|
|
2573
|
+
effect: base.name,
|
|
2574
|
+
key: node.name
|
|
2575
|
+
});
|
|
2576
|
+
}), reads;
|
|
2634
2577
|
}
|
|
2635
2578
|
|
|
2636
|
-
function
|
|
2637
|
-
return
|
|
2579
|
+
function readsRootDocument(groq2) {
|
|
2580
|
+
return nodeReadsRoot(tryParseGroq(groq2), 0);
|
|
2638
2581
|
}
|
|
2639
2582
|
|
|
2640
|
-
|
|
2641
|
-
|
|
2583
|
+
const SCOPED_CHILD = {
|
|
2584
|
+
Filter: "expr",
|
|
2585
|
+
Projection: "expr",
|
|
2586
|
+
Map: "expr",
|
|
2587
|
+
FlatMap: "expr",
|
|
2588
|
+
PipeFuncCall: "args"
|
|
2589
|
+
};
|
|
2590
|
+
|
|
2591
|
+
function nodeReadsRoot(node, depth) {
|
|
2592
|
+
if (Array.isArray(node)) return node.some(item => nodeReadsRoot(item, depth));
|
|
2593
|
+
if (typeof node != "object" || node === null) return !1;
|
|
2594
|
+
const typed = node;
|
|
2595
|
+
if (depth === 0 && typed.type === "This" || depth === 0 && typed.type === "AccessAttribute" && typed.base === void 0) return !0;
|
|
2596
|
+
if (typed.type === "Parent") {
|
|
2597
|
+
const climb = typeof typed.n == "number" ? typed.n : 1;
|
|
2598
|
+
return depth - climb <= 0;
|
|
2599
|
+
}
|
|
2600
|
+
const scopedChild = typeof typed.type == "string" ? SCOPED_CHILD[typed.type] : void 0;
|
|
2601
|
+
return scopedChild !== void 0 ? Object.entries(node).some(([key, value]) => nodeReadsRoot(value, key === scopedChild ? depth + 1 : depth)) : Object.values(node).some(value => nodeReadsRoot(value, depth));
|
|
2642
2602
|
}
|
|
2643
2603
|
|
|
2644
|
-
function
|
|
2645
|
-
|
|
2604
|
+
function tryParseGroq(groq2) {
|
|
2605
|
+
try {
|
|
2606
|
+
return groqJs.parse(groq2);
|
|
2607
|
+
} catch {
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2646
2610
|
}
|
|
2647
2611
|
|
|
2648
|
-
function
|
|
2649
|
-
|
|
2612
|
+
function walkAstNodes(node, visit) {
|
|
2613
|
+
if (Array.isArray(node)) {
|
|
2614
|
+
for (const item of node) walkAstNodes(item, visit);
|
|
2615
|
+
return;
|
|
2616
|
+
}
|
|
2617
|
+
if (!(typeof node != "object" || node === null)) {
|
|
2618
|
+
visit(node);
|
|
2619
|
+
for (const value of Object.values(node)) walkAstNodes(value, visit);
|
|
2620
|
+
}
|
|
2650
2621
|
}
|
|
2651
2622
|
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2623
|
+
const CONDITION_VARS = [ {
|
|
2624
|
+
name: "self",
|
|
2625
|
+
binding: "always",
|
|
2626
|
+
label: "this workflow instance",
|
|
2627
|
+
description: "GDR URI of the instance document itself — `*[_id == $self][0]` reads the instance in the snapshot."
|
|
2628
|
+
}, {
|
|
2629
|
+
name: "fields",
|
|
2630
|
+
binding: "always",
|
|
2631
|
+
label: "the workflow's fields",
|
|
2632
|
+
description: "Declared field entries rendered by name (`$fields.<name>` is the value, no wrapper). Stage/activity scopes overlay lexically. What a read puts in your hand follows the declared kind: a singular `doc.ref` (or `subject`) DEREFERENCES into the hydrated document (lake `_id`/`_type` plus content fields — which may themselves be named `id`/`type`), while `doc.refs` elements and `release.ref` stay REFERENCES — `{id, type[, releaseName]}` with `id` a GDR URI — because identity reads (membership, counting, joins) must stay total without hydrating every target, and targets may live in resources the evaluation cannot fetch from. Conditions evaluate against the in-memory snapshot only, so a dereferencing plural kind would silently hole wherever hydration lagged."
|
|
2633
|
+
}, {
|
|
2634
|
+
name: "parent",
|
|
2635
|
+
binding: "always",
|
|
2636
|
+
label: "the parent workflow",
|
|
2637
|
+
description: "The parent instance's GDR URI, `null` on a root instance."
|
|
2638
|
+
}, {
|
|
2639
|
+
name: "ancestors",
|
|
2640
|
+
binding: "always",
|
|
2641
|
+
label: "the workflow's ancestors",
|
|
2642
|
+
description: "GDR URIs of the ancestor chain, root first."
|
|
2643
|
+
}, {
|
|
2644
|
+
name: "stage",
|
|
2645
|
+
binding: "always",
|
|
2646
|
+
label: "the current stage",
|
|
2647
|
+
description: "The current stage's name."
|
|
2648
|
+
}, {
|
|
2649
|
+
name: "now",
|
|
2650
|
+
binding: "always",
|
|
2651
|
+
label: "the current time",
|
|
2652
|
+
description: "The ISO clock reading shared by every condition in one evaluation pass, so they agree on the time."
|
|
2653
|
+
}, {
|
|
2654
|
+
name: "context",
|
|
2655
|
+
binding: "always",
|
|
2656
|
+
label: "start-time context",
|
|
2657
|
+
description: "The instance's `context` bag: values seeded at `startInstance` plus a parent's `spawn.context` handoff, by entry name. Written only at start — never mutated afterwards."
|
|
2658
|
+
}, {
|
|
2659
|
+
name: "effects",
|
|
2660
|
+
binding: "always",
|
|
2661
|
+
label: "automation outputs",
|
|
2662
|
+
description: "Completed effects' outputs, namespaced by effect name (`$effects['<effect>'].<output>`) — each effect's latest completed run wins. Handler fuel — transition triggers should read instance/lake state (or $effectStatus) instead."
|
|
2663
|
+
}, {
|
|
2664
|
+
name: "effectStatus",
|
|
2665
|
+
binding: "always",
|
|
2666
|
+
label: "automation status",
|
|
2667
|
+
description: "Effect name → `'done'` | `'failed'` of the latest completed run queued during the current stage entry; absent until the effect drains for this entry. Re-entry-safe: runs queued under a prior entry never count, so `$effectStatus['<effect>'] == 'done'` (or `defined($effectStatus['<effect>'])` for settled-either-way) waits for the fresh run."
|
|
2668
|
+
}, {
|
|
2669
|
+
name: "activities",
|
|
2670
|
+
binding: "always",
|
|
2671
|
+
label: "this stage's activities",
|
|
2672
|
+
description: "The current stage's activity rows, statuses included."
|
|
2673
|
+
}, {
|
|
2674
|
+
name: "allActivitiesDone",
|
|
2675
|
+
binding: "always",
|
|
2676
|
+
label: "all activities in this stage are finished",
|
|
2677
|
+
description: "Every current-stage activity is `done` or `skipped` — the default transition gate."
|
|
2678
|
+
}, {
|
|
2679
|
+
name: "anyActivityFailed",
|
|
2680
|
+
binding: "always",
|
|
2681
|
+
label: "an activity in this stage has failed",
|
|
2682
|
+
description: "Some current-stage activity is `failed`."
|
|
2683
|
+
}, {
|
|
2684
|
+
name: "actor",
|
|
2685
|
+
binding: "caller",
|
|
2686
|
+
label: "you",
|
|
2687
|
+
description: "The acting identity (id + roles); `undefined` when no caller rides the evaluation. Never holds a value in the cascade gates (deploy-rejected there)."
|
|
2688
|
+
}, {
|
|
2689
|
+
name: "assigned",
|
|
2690
|
+
binding: "caller",
|
|
2691
|
+
label: "you are assigned to this activity",
|
|
2692
|
+
description: "Whether the caller matches the activity's assignees-kind field entry (by user id, or by a role under the definition's `roleAliases`); `false` outside an activity context. Constant `false` in the cascade gates (deploy-rejected there)."
|
|
2693
|
+
}, {
|
|
2694
|
+
name: "can",
|
|
2695
|
+
binding: "caller",
|
|
2696
|
+
label: "your permissions",
|
|
2697
|
+
description: "Advisory per-permission booleans computed from the caller's grants; `undefined` without grants. Bound wherever grants ride the evaluation: the projection's rendered scope (fireAction-action filters, activity requirements, editability predicates) and the fireAction/editField commit gates. Deploy rejects it at every site that evaluates without grants: transition `when`s, activity filters, cascade-fired actions' `when`/`filter`, effect bindings, where-op `where`s, and the spawn `forEach`/`with`/`context` sites."
|
|
2698
|
+
}, {
|
|
2699
|
+
name: "attributes",
|
|
2700
|
+
binding: "caller",
|
|
2701
|
+
label: "your attributes",
|
|
2702
|
+
description: "Advisory org-level User Attributes for the caller (Enterprise — same values as lake `user::attributes()`), keyed by attribute name with each active scalar or array value. `undefined` on expected HTTP absence; unexpected fetch failures throw; empty page binds `{}`. Bound wherever grants ride the evaluation (same sites as `$can`). Soft-gate paths fetch at most 100 attributes (no further pages) and warn when the envelope reports `hasMore: true` (partial bag still binds). Not a security boundary — the Content Lake remains the only enforcement point."
|
|
2703
|
+
}, {
|
|
2704
|
+
name: "row",
|
|
2705
|
+
binding: "spawn",
|
|
2706
|
+
label: "the spawned row",
|
|
2707
|
+
description: "One `spawn.forEach` result row, bound while its `with` map evaluates — and the row under test while a where-op `where` evaluates (per row). Deploy rejects a read at every other site (including `spawn.forEach` itself and `spawn.context`), where it is GROQ null."
|
|
2708
|
+
}, {
|
|
2709
|
+
name: "params",
|
|
2710
|
+
binding: "caller",
|
|
2711
|
+
label: "the action's arguments",
|
|
2712
|
+
description: "The firing action's args — they hold values only while a fireAction-fired action's effect bindings and where-op `where`s evaluate (spawn sites don't bind it at all, and a cascade-fired action has no caller to supply args). Deploy rejects a read in the cascade gates, in a cascade-fired action's payload, and in the caller-bound projection (action filters, requirements, editable predicates): args exist only once the caller fires the action, after those sites have evaluated."
|
|
2713
|
+
}, {
|
|
2714
|
+
name: "subworkflows",
|
|
2715
|
+
binding: "always",
|
|
2716
|
+
label: "the spawned subworkflows",
|
|
2717
|
+
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`."
|
|
2718
|
+
} ], 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 = [ {
|
|
2719
|
+
name: "tag",
|
|
2720
|
+
label: "this engine tag",
|
|
2721
|
+
description: "The engine's tag partition — scope `*[...]` instance scans with `tag == $tag`."
|
|
2722
|
+
}, {
|
|
2723
|
+
name: "definition",
|
|
2724
|
+
label: "this workflow definition",
|
|
2725
|
+
description: "The `name` of the definition under evaluation (its own start block binds it)."
|
|
2726
|
+
}, {
|
|
2727
|
+
name: "now",
|
|
2728
|
+
label: "the current time",
|
|
2729
|
+
description: "The ISO clock reading of the evaluating engine."
|
|
2730
|
+
} ], START_REQUIREMENT_VARS = [ ...START_FILTER_VARS, {
|
|
2731
|
+
name: "fields",
|
|
2732
|
+
label: "the start's input fields",
|
|
2733
|
+
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."
|
|
2734
|
+
} ], GUARD_PREDICATE_VARS = [ {
|
|
2735
|
+
name: "document",
|
|
2736
|
+
description: "The attempted mutation images, exposed as `document.before` and `document.after`."
|
|
2737
|
+
}, {
|
|
2738
|
+
name: "guard",
|
|
2739
|
+
description: "The guard document itself (its `metadata` carries deploy-time resolved values)."
|
|
2740
|
+
}, {
|
|
2741
|
+
name: "mutation",
|
|
2742
|
+
description: "The attempted mutation — `mutation.action` is the write kind being gated."
|
|
2743
|
+
} ];
|
|
2744
|
+
|
|
2745
|
+
function effectBackoffMs(retry, endedAttempts) {
|
|
2746
|
+
const {backoff: backoff} = retry;
|
|
2747
|
+
return backoff === void 0 || endedAttempts < 1 ? 0 : backoff.kind === "fixed" ? backoff.delayMs : backoff.delayMs * 2 ** (endedAttempts - 1);
|
|
2656
2748
|
}
|
|
2657
2749
|
|
|
2658
|
-
function
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
if (typeof raw == "string") return `${fn}("${raw}")`;
|
|
2662
|
-
}
|
|
2663
|
-
return fn;
|
|
2750
|
+
function totalEffectBackoffMs(retry) {
|
|
2751
|
+
const waits = retry.attempts - 1;
|
|
2752
|
+
return retry.backoff === void 0 || waits < 1 ? 0 : retry.backoff.kind === "fixed" ? retry.backoff.delayMs * waits : retry.backoff.delayMs * (2 ** waits - 1);
|
|
2664
2753
|
}
|
|
2665
2754
|
|
|
2666
2755
|
function knownList(ids) {
|
|
@@ -3251,6 +3340,40 @@ function pushOutputIssues(args) {
|
|
|
3251
3340
|
}
|
|
3252
3341
|
}
|
|
3253
3342
|
|
|
3343
|
+
function checkEffectRetry(def, issues) {
|
|
3344
|
+
for (const {action: action, path: path} of actionSites(def)) for (const [e, effect] of (action.effects ?? []).entries()) effect.retry !== void 0 && pushRetryIssue({
|
|
3345
|
+
retry: effect.retry,
|
|
3346
|
+
name: effect.name,
|
|
3347
|
+
path: [ ...path, "effects", e, "retry" ],
|
|
3348
|
+
issues: issues
|
|
3349
|
+
});
|
|
3350
|
+
}
|
|
3351
|
+
|
|
3352
|
+
function pushRetryIssue(args) {
|
|
3353
|
+
const {retry: retry, name: name, path: path, issues: issues} = args;
|
|
3354
|
+
if (retry.expiryMs !== void 0 && retry.expiryMs > MAX_RETRY_SPAN_MS) {
|
|
3355
|
+
issues.push({
|
|
3356
|
+
path: [ ...path, "expiryMs" ],
|
|
3357
|
+
message: `effect "${name}" declares a ${retry.expiryMs}ms \`expiryMs\`, past the ${MAX_RETRY_SPAN_MS}ms ceiling — a policy runs inside one \`drainEffects\` invocation, and no host offers a single execution longer than a year. Lower \`expiryMs\``
|
|
3358
|
+
});
|
|
3359
|
+
return;
|
|
3360
|
+
}
|
|
3361
|
+
const wait = totalEffectBackoffMs(retry);
|
|
3362
|
+
if (wait > MAX_RETRY_SPAN_MS) {
|
|
3363
|
+
issues.push({
|
|
3364
|
+
path: [ ...path, "attempts" ],
|
|
3365
|
+
message: `effect "${name}" accumulates ${spanned(wait)} of backoff before attempt ${retry.attempts}, past the ${MAX_RETRY_SPAN_MS}ms ceiling — a policy runs inside one \`drainEffects\` invocation, and no host offers a single execution longer than a year. Lower \`attempts\` or \`backoff.delayMs\``
|
|
3366
|
+
});
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3369
|
+
retry.expiryMs === void 0 || wait < retry.expiryMs || issues.push({
|
|
3370
|
+
path: [ ...path, "expiryMs" ],
|
|
3371
|
+
message: `effect "${name}" waits ${wait}ms of backoff before attempt ${retry.attempts}, which its ${retry.expiryMs}ms \`expiryMs\` closes first — the effect fails before the declared attempts run. Raise \`expiryMs\`, or lower \`attempts\`/\`backoff.delayMs\``
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
const spanned = ms => Number.isFinite(ms) ? `${ms}ms` : "more milliseconds than a number can count", MAX_RETRY_SPAN_MS = 316224e5;
|
|
3376
|
+
|
|
3254
3377
|
function checkSubjectEntries(def, issues) {
|
|
3255
3378
|
for (const {entries: entries, scope: scope, path: path, label: label} of fieldScopes(def)) {
|
|
3256
3379
|
const subjects = (entries ?? []).flatMap((entry, n) => isSubjectEntry(entry) ? [ {
|
|
@@ -4026,7 +4149,7 @@ function checkWorkflowInvariants(def) {
|
|
|
4026
4149
|
checkUnboundConditionVars(def, issues), checkConditionFieldReads(def, issues), checkFieldReadSeeds(def, issues),
|
|
4027
4150
|
checkFieldReadOpValues(def, issues), checkFieldTargetOps(def, issues), checkGuardFieldReads(def, issues),
|
|
4028
4151
|
checkAssigneesEntries(def, issues), checkDueDateEntries(def, issues), checkSubjectEntries(def, issues),
|
|
4029
|
-
checkLevelKindEffectOutputs(def, issues), checkActivityTerminalPaths(def, issues),
|
|
4152
|
+
checkLevelKindEffectOutputs(def, issues), checkEffectRetry(def, issues), checkActivityTerminalPaths(def, issues),
|
|
4030
4153
|
checkTerminalStageActivities(def, issues), checkTriggeredActionParams(def, issues),
|
|
4031
4154
|
checkStoredRolesPlacement(def, issues), checkGroups(def, issues), issues;
|
|
4032
4155
|
}
|
|
@@ -4047,6 +4170,8 @@ exports.AuthoringActionSchema = AuthoringActionSchema;
|
|
|
4047
4170
|
|
|
4048
4171
|
exports.AuthoringActivitySchema = AuthoringActivitySchema;
|
|
4049
4172
|
|
|
4173
|
+
exports.AuthoringEffectSchema = AuthoringEffectSchema;
|
|
4174
|
+
|
|
4050
4175
|
exports.AuthoringFieldEntrySchema = AuthoringFieldEntrySchema;
|
|
4051
4176
|
|
|
4052
4177
|
exports.AuthoringGuardSchema = AuthoringGuardSchema;
|
|
@@ -4083,8 +4208,6 @@ exports.EXECUTOR_CLASSIFICATIONS = EXECUTOR_CLASSIFICATIONS;
|
|
|
4083
4208
|
|
|
4084
4209
|
exports.EffectNotFoundError = EffectNotFoundError;
|
|
4085
4210
|
|
|
4086
|
-
exports.EffectSchema = EffectSchema;
|
|
4087
|
-
|
|
4088
4211
|
exports.FIELD_READ = FIELD_READ;
|
|
4089
4212
|
|
|
4090
4213
|
exports.FIELD_SCOPES = FIELD_SCOPES;
|
|
@@ -4097,6 +4220,8 @@ exports.FieldValueShapeError = FieldValueShapeError;
|
|
|
4097
4220
|
|
|
4098
4221
|
exports.GROUP_KINDS = GROUP_KINDS;
|
|
4099
4222
|
|
|
4223
|
+
exports.GUARD_ACTIONS_REQUIRED_MESSAGE = GUARD_ACTIONS_REQUIRED_MESSAGE;
|
|
4224
|
+
|
|
4100
4225
|
exports.GUARD_PREDICATE_VARS = GUARD_PREDICATE_VARS;
|
|
4101
4226
|
|
|
4102
4227
|
exports.GdrShape = GdrShape;
|
|
@@ -4107,9 +4232,13 @@ exports.InstanceNotFoundError = InstanceNotFoundError;
|
|
|
4107
4232
|
|
|
4108
4233
|
exports.IsoTimestamp = IsoTimestamp;
|
|
4109
4234
|
|
|
4235
|
+
exports.LAKE_MUTATION_GUARD_ACTIONS = LAKE_MUTATION_GUARD_ACTIONS;
|
|
4236
|
+
|
|
4110
4237
|
exports.MUTATION_GUARD_ACTIONS = MUTATION_GUARD_ACTIONS;
|
|
4111
4238
|
|
|
4112
|
-
exports.
|
|
4239
|
+
exports.MUTATION_GUARD_ID_SPACES = MUTATION_GUARD_ID_SPACES;
|
|
4240
|
+
|
|
4241
|
+
exports.NonEmptyString = NonEmptyString$1;
|
|
4113
4242
|
|
|
4114
4243
|
exports.PersistedDocShapeError = PersistedDocShapeError;
|
|
4115
4244
|
|
|
@@ -4129,6 +4258,8 @@ exports.SpawnContractsInvalidError = SpawnContractsInvalidError;
|
|
|
4129
4258
|
|
|
4130
4259
|
exports.StoredFieldOpSchema = StoredFieldOpSchema;
|
|
4131
4260
|
|
|
4261
|
+
exports.UNIVERSAL_ROLE_ALIAS_KEY = UNIVERSAL_ROLE_ALIAS_KEY;
|
|
4262
|
+
|
|
4132
4263
|
exports.VersionSpecificDatasetGdrError = VersionSpecificDatasetGdrError;
|
|
4133
4264
|
|
|
4134
4265
|
exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
|
|
@@ -4137,12 +4268,24 @@ exports.WorkflowConfigSchema = WorkflowConfigSchema;
|
|
|
4137
4268
|
|
|
4138
4269
|
exports.WorkflowError = WorkflowError;
|
|
4139
4270
|
|
|
4271
|
+
exports.activeAssignmentMembers = activeAssignmentMembers;
|
|
4272
|
+
|
|
4140
4273
|
exports.actorFulfillsRole = actorFulfillsRole;
|
|
4141
4274
|
|
|
4275
|
+
exports.actorMatchesAssignment = actorMatchesAssignment;
|
|
4276
|
+
|
|
4142
4277
|
exports.andConditions = andConditions;
|
|
4143
4278
|
|
|
4144
4279
|
exports.assignmentKindAcceptsRoles = assignmentKindAcceptsRoles;
|
|
4145
4280
|
|
|
4281
|
+
exports.assignmentMatch = assignmentMatch;
|
|
4282
|
+
|
|
4283
|
+
exports.assignmentMembers = assignmentMembers;
|
|
4284
|
+
|
|
4285
|
+
exports.assignmentState = assignmentState;
|
|
4286
|
+
|
|
4287
|
+
exports.assignmentStateCounts = assignmentStateCounts;
|
|
4288
|
+
|
|
4146
4289
|
exports.checkWorkflowInvariants = checkWorkflowInvariants;
|
|
4147
4290
|
|
|
4148
4291
|
exports.choiceValueIssues = choiceValueIssues;
|
|
@@ -4151,6 +4294,10 @@ exports.classifyPrincipalId = classifyPrincipalId;
|
|
|
4151
4294
|
|
|
4152
4295
|
exports.clientConfigFromResource = clientConfigFromResource;
|
|
4153
4296
|
|
|
4297
|
+
exports.condemnSubworkflow = condemnSubworkflow;
|
|
4298
|
+
|
|
4299
|
+
exports.condemnedSubworkflows = condemnedSubworkflows;
|
|
4300
|
+
|
|
4154
4301
|
exports.conditionEffectReads = conditionEffectReads;
|
|
4155
4302
|
|
|
4156
4303
|
exports.conditionFieldReadNames = conditionFieldReadNames;
|
|
@@ -4173,6 +4320,8 @@ exports.directoryBridgeId = directoryBridgeId;
|
|
|
4173
4320
|
|
|
4174
4321
|
exports.driverKind = driverKind;
|
|
4175
4322
|
|
|
4323
|
+
exports.effectBackoffMs = effectBackoffMs;
|
|
4324
|
+
|
|
4176
4325
|
exports.errorMessage = errorMessage;
|
|
4177
4326
|
|
|
4178
4327
|
exports.evaluateCondition = evaluateCondition;
|
|
@@ -4185,6 +4334,10 @@ exports.extractDocumentId = extractDocumentId;
|
|
|
4185
4334
|
|
|
4186
4335
|
exports.fieldValueSchemas = fieldValueSchemas;
|
|
4187
4336
|
|
|
4337
|
+
exports.findCurrentActivityEntry = findCurrentActivityEntry;
|
|
4338
|
+
|
|
4339
|
+
exports.findOpenStageEntry = findOpenStageEntry;
|
|
4340
|
+
|
|
4188
4341
|
exports.firstCarriedGlobalId = firstCarriedGlobalId;
|
|
4189
4342
|
|
|
4190
4343
|
exports.formatIssuePath = formatIssuePath;
|
|
@@ -4205,14 +4358,22 @@ exports.groq = groq;
|
|
|
4205
4358
|
|
|
4206
4359
|
exports.groupMembershipNames = groupMembershipNames;
|
|
4207
4360
|
|
|
4361
|
+
exports.identityMatchesAssignment = identityMatchesAssignment;
|
|
4362
|
+
|
|
4363
|
+
exports.instanceAssignmentStateCounts = instanceAssignmentStateCounts;
|
|
4364
|
+
|
|
4208
4365
|
exports.isAlwaysArrayFieldKind = isAlwaysArrayFieldKind;
|
|
4209
4366
|
|
|
4210
4367
|
exports.isAssignmentFieldEntry = isAssignmentFieldEntry;
|
|
4211
4368
|
|
|
4369
|
+
exports.isBareDocumentId = isBareDocumentId;
|
|
4370
|
+
|
|
4212
4371
|
exports.isBareSeedId = isBareSeedId;
|
|
4213
4372
|
|
|
4214
4373
|
exports.isCascadeFired = isCascadeFired;
|
|
4215
4374
|
|
|
4375
|
+
exports.isFilterScopedOut = isFilterScopedOut;
|
|
4376
|
+
|
|
4216
4377
|
exports.isGdr = isGdr;
|
|
4217
4378
|
|
|
4218
4379
|
exports.isGdrUri = isGdrUri;
|
|
@@ -4247,6 +4408,16 @@ exports.labelFor = labelFor;
|
|
|
4247
4408
|
|
|
4248
4409
|
exports.lakePrincipalId = lakePrincipalId;
|
|
4249
4410
|
|
|
4411
|
+
exports.liveSubworkflows = liveSubworkflows;
|
|
4412
|
+
|
|
4413
|
+
exports.mutationGuardActionIdSpace = mutationGuardActionIdSpace;
|
|
4414
|
+
|
|
4415
|
+
exports.mutationGuardRequiresSplitEmission = mutationGuardRequiresSplitEmission;
|
|
4416
|
+
|
|
4417
|
+
exports.normalizeAssignmentMembers = normalizeAssignmentMembers;
|
|
4418
|
+
|
|
4419
|
+
exports.openActivityAssignments = openActivityAssignments;
|
|
4420
|
+
|
|
4250
4421
|
exports.parseFieldValue = parseFieldValue;
|
|
4251
4422
|
|
|
4252
4423
|
exports.parseGdr = parseGdr;
|
|
@@ -4279,6 +4450,8 @@ exports.releaseDocId = releaseDocId;
|
|
|
4279
4450
|
|
|
4280
4451
|
exports.releaseRef = releaseRef;
|
|
4281
4452
|
|
|
4453
|
+
exports.resolvedChildStatus = resolvedChildStatus;
|
|
4454
|
+
|
|
4282
4455
|
exports.resourceAliasesToMap = resourceAliasesToMap;
|
|
4283
4456
|
|
|
4284
4457
|
exports.resourceFromGdrUri = resourceFromGdrUri;
|