@sanity/workflow-engine 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -144,6 +144,9 @@ function resolveFieldSite(site, stage) {
144
144
  ...site.entry.validation !== void 0 ? {
145
145
  validation: site.entry.validation
146
146
  } : {},
147
+ ...site.entry.roles !== void 0 ? {
148
+ roles: site.entry.roles
149
+ } : {},
147
150
  ref: {
148
151
  scope: site.scope,
149
152
  field: site.entry.name
@@ -280,58 +283,58 @@ function resolveTelemetry(telemetry) {
280
283
  }
281
284
 
282
285
  const HIGH_FREQUENCY_SAMPLE_MS = 6e4, WorkflowDefinitionDeployed = defineWorkflowEvent({
283
- name: "Editorial Workflows Definition Deployed",
286
+ name: "Workflows Definition Deployed",
284
287
  version: 1,
285
288
  description: "A definition went through a deploy — status distinguishes a newly minted version from an unchanged redeploy"
286
289
  }), WorkflowInstanceStarted = defineWorkflowEvent({
287
- name: "Editorial Workflows Instance Started",
290
+ name: "Workflows Instance Started",
288
291
  version: 1,
289
292
  description: "A workflow instance was started from a deployed definition"
290
293
  }), WorkflowStageTransitioned = defineWorkflowEvent({
291
- name: "Editorial Workflows Stage Transitioned",
294
+ name: "Workflows Stage Transitioned",
292
295
  version: 1,
293
296
  description: "A committed stage move — one event per hop, cascades included, whichever verb drove it. Unsampled: funnel and dwell reads need complete counts, and volume is bounded by actual movement"
294
297
  }), WorkflowActionFired = defineWorkflowEvent({
295
- name: "Editorial Workflows Action Fired",
298
+ name: "Workflows Action Fired",
296
299
  version: 1,
297
300
  description: "An action was fired against an activity"
298
301
  }), WorkflowFieldEdited = defineWorkflowEvent({
299
- name: "Editorial Workflows Field Edited",
302
+ name: "Workflows Field Edited",
300
303
  version: 1,
301
304
  description: "A declared-editable field was edited through the generic edit seam",
302
305
  maxSampleRate: HIGH_FREQUENCY_SAMPLE_MS
303
306
  }), WorkflowInstanceTicked = defineWorkflowEvent({
304
- name: "Editorial Workflows Instance Ticked",
307
+ name: "Workflows Instance Ticked",
305
308
  version: 1,
306
309
  description: "An instance was re-evaluated for auto-transitions and due waits",
307
310
  maxSampleRate: HIGH_FREQUENCY_SAMPLE_MS
308
311
  }), WorkflowEffectsDrained = defineWorkflowEvent({
309
- name: "Editorial Workflows Effects Drained",
312
+ name: "Workflows Effects Drained",
310
313
  version: 1,
311
314
  description: "A drain pass ran — drainedCount/drainedEffects report the successful dispatches. Unsampled when non-empty (outcome counts must be complete; volume is bounded by real effect work); empty polls are machine-cadence noise, throttled engine-side to at most one per minute"
312
315
  }), WorkflowEffectStateReported = defineWorkflowEvent({
313
- name: "Editorial Workflows Effect State Reported",
316
+ name: "Workflows Effect State Reported",
314
317
  version: 1,
315
318
  description: "A running effect handler committed mid-dispatch field state through commitEffectOps. Sampled (high-frequency by design — a dispatch may report many times): an adoption and usage signal, not an exact per-run report count",
316
319
  maxSampleRate: HIGH_FREQUENCY_SAMPLE_MS
317
320
  }), WorkflowEffectCompleted = defineWorkflowEvent({
318
- name: "Editorial Workflows Effect Completed",
321
+ name: "Workflows Effect Completed",
319
322
  version: 1,
320
- description: "An effect outcome was recorded — reported directly through completeEffect, or cancelled by the abort verb (external runtimes; engine-drained effects surface via Editorial Workflows Effects Drained instead — each outcome counts once)"
323
+ description: "An effect outcome was recorded — reported directly through completeEffect, or cancelled by the abort verb (external runtimes; engine-drained effects surface via Workflows Effects Drained instead — each outcome counts once)"
321
324
  }), WorkflowInstanceAborted = defineWorkflowEvent({
322
- name: "Editorial Workflows Instance Aborted",
325
+ name: "Workflows Instance Aborted",
323
326
  version: 1,
324
327
  description: "The abort admin override was invoked (changed: false = instance already terminal)"
325
328
  }), WorkflowStageSet = defineWorkflowEvent({
326
- name: "Editorial Workflows Stage Set",
329
+ name: "Workflows Stage Set",
327
330
  version: 1,
328
331
  description: "The set-stage admin override was invoked (changed: false = already at the target stage)"
329
332
  }), WorkflowActivityReset = defineWorkflowEvent({
330
- name: "Editorial Workflows Activity Reset",
333
+ name: "Workflows Activity Reset",
331
334
  version: 1,
332
335
  description: "The reset-activity admin override was invoked (changed: false = already at the target status, or the instance was terminal)"
333
336
  }), WorkflowDefinitionDeleted = defineWorkflowEvent({
334
- name: "Editorial Workflows Definition Deleted",
337
+ name: "Workflows Definition Deleted",
335
338
  version: 1,
336
339
  description: "A deployed definition (or one version of it) was removed via the admin verb"
337
340
  });
@@ -449,240 +452,6 @@ function fieldEditedData(args) {
449
452
  };
450
453
  }
451
454
 
452
- const DATA_MODEL_VERSION = 5, DATA_MODEL_MIN_READER = 4, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/editorial-workflows/prerelease";
453
-
454
- class ReaderModelAcknowledgementError extends invariants.WorkflowError {
455
- code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
456
- expectedMinReaderModel;
457
- engineMinReaderModel=DATA_MODEL_MIN_READER;
458
- engineModelVersion=DATA_MODEL_VERSION;
459
- documentationUrl=READER_MODEL_ROLLOUT_URL;
460
- constructor(expectedMinReaderModel, context = "Deployment") {
461
- const expected = expectedMinReaderModel === void 0 ? "missing" : String(expectedMinReaderModel);
462
- super("reader-model-acknowledgement", `${context} expected reader floor ${expected}; the installed engine requires acknowledgement ${DATA_MODEL_MIN_READER}.\nDo not change the acknowledgement yet: accepting ${DATA_MODEL_MIN_READER} authorizes this engine to write documents that older readers will refuse.\nUpgrade every Studio, CLI, MCP server, Function, and other runtime that reads engine-owned documents; verify that rollout in every environment sharing the workflow resource; then change the literal in deployment configuration and deploy the writer.\nRollout guide: ${READER_MODEL_ROLLOUT_URL}`),
463
- this.name = "ReaderModelAcknowledgementError", this.expectedMinReaderModel = expectedMinReaderModel;
464
- }
465
- }
466
-
467
- function assertReaderModelAcknowledgement(expectedMinReaderModel, context) {
468
- if (typeof expectedMinReaderModel != "number" || !Number.isFinite(expectedMinReaderModel) || !Number.isInteger(expectedMinReaderModel) || expectedMinReaderModel < 0 || expectedMinReaderModel !== DATA_MODEL_MIN_READER) throw new ReaderModelAcknowledgementError(expectedMinReaderModel, context);
469
- }
470
-
471
- const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
472
- id: "governed-model-stamps",
473
- introducedInModel: 1,
474
- minReaderModel: 0,
475
- documentTypes: Object.freeze([ "definition", "instance" ]),
476
- compatibility: "additive",
477
- applicability: "unconditional",
478
- summary: "Definition and instance documents carry model provenance and reader-floor stamps."
479
- }), Object.freeze({
480
- id: "subject-field-kind",
481
- introducedInModel: 2,
482
- minReaderModel: 0,
483
- documentTypes: Object.freeze([ "definition", "instance" ]),
484
- compatibility: "additive",
485
- applicability: "detectable",
486
- summary: "A workflow-level subject field identifies the document a workflow is about."
487
- }), Object.freeze({
488
- id: "typed-scalar-choice-lists",
489
- introducedInModel: 2,
490
- minReaderModel: 2,
491
- documentTypes: Object.freeze([ "definition", "instance" ]),
492
- compatibility: "reader-floor",
493
- applicability: "detectable",
494
- summary: "Scalar fields may constrain writes to a persisted typed choice list."
495
- }), Object.freeze({
496
- id: "action-semantics",
497
- introducedInModel: 2,
498
- minReaderModel: 0,
499
- documentTypes: Object.freeze([ "definition" ]),
500
- compatibility: "additive",
501
- applicability: "detectable",
502
- summary: "Ordinary actions may carry a closed bag of advisory workflow semantics."
503
- }), Object.freeze({
504
- id: "inclusive-scalar-bounds",
505
- introducedInModel: 2,
506
- minReaderModel: 2,
507
- documentTypes: Object.freeze([ "definition", "instance" ]),
508
- compatibility: "reader-floor",
509
- applicability: "detectable",
510
- summary: "String, text, and number values may carry persisted inclusive bounds."
511
- }), Object.freeze({
512
- id: "progress-field-kind",
513
- introducedInModel: 3,
514
- minReaderModel: 0,
515
- documentTypes: Object.freeze([ "definition", "instance" ]),
516
- compatibility: "additive",
517
- applicability: "detectable",
518
- summary: "A progress field kind carries application-defined 0–100 completion."
519
- }), Object.freeze({
520
- id: "effect-claim-tokens",
521
- introducedInModel: 3,
522
- minReaderModel: 0,
523
- documentTypes: Object.freeze([ "instance" ]),
524
- compatibility: "additive",
525
- applicability: "detectable",
526
- summary: "Pending-effect claims carry an exact-claim token gating mid-dispatch state reports."
527
- }), Object.freeze({
528
- id: "classified-principal-ids",
529
- introducedInModel: 4,
530
- minReaderModel: 4,
531
- documentTypes: Object.freeze([ "instance" ]),
532
- compatibility: "reader-floor",
533
- applicability: "unconditional",
534
- summary: "Principal ids are namespace-classified: actor and assignee writes carry the account-global user id only, and readers resolve legacy project-scoped ids through the prefix classifier at the instance read funnel."
535
- }), Object.freeze({
536
- id: "readiness-requirements",
537
- introducedInModel: 4,
538
- minReaderModel: 4,
539
- documentTypes: Object.freeze([ "definition" ]),
540
- compatibility: "reader-floor",
541
- applicability: "detectable",
542
- summary: "Start and activity readiness use named polymorphic requirement arrays."
543
- }), Object.freeze({
544
- id: "due-date-field-kinds",
545
- introducedInModel: 5,
546
- minReaderModel: 0,
547
- documentTypes: Object.freeze([ "definition", "instance" ]),
548
- compatibility: "additive",
549
- applicability: "detectable",
550
- summary: "Due-date field kinds (dueDate, dueDatetime) mark a level deadline, elevated aliases of date/datetime carrying the same stored value."
551
- }) ]);
552
-
553
- function recordOf(value) {
554
- return value !== null && typeof value == "object" && !Array.isArray(value) ? value : void 0;
555
- }
556
-
557
- function recordsAt(record, key) {
558
- const value = record[key];
559
- return Array.isArray(value) ? value.map(recordOf).filter(item => item !== void 0) : [];
560
- }
561
-
562
- function nestedFieldEntries(entries) {
563
- return entries.flatMap(entry => [ entry, ...nestedFieldEntries(recordsAt(entry, "fields")), ...nestedFieldEntries(recordsAt(entry, "of")) ]);
564
- }
565
-
566
- function parsedDefinitionSnapshot(root) {
567
- if (typeof root.definitionSnapshot == "string") return recordOf(parseDefinitionSnapshotValue({
568
- _id: typeof root._id == "string" ? root._id : "<unknown instance>",
569
- definitionSnapshot: root.definitionSnapshot
570
- }));
571
- }
572
-
573
- function persistedFieldEntries(document) {
574
- const root = recordOf(document);
575
- if (root === void 0) return [];
576
- const snapshot = parsedDefinitionSnapshot(root), roots = snapshot === void 0 ? [ root ] : [ root, snapshot ], stages = roots.flatMap(candidate => recordsAt(candidate, "stages")), activities = stages.flatMap(stage => recordsAt(stage, "activities")), actions = activities.flatMap(activity => recordsAt(activity, "actions")), effects = actions.flatMap(action => recordsAt(action, "effects"));
577
- return nestedFieldEntries([ ...roots.flatMap(candidate => recordsAt(candidate, "fields")), ...stages.flatMap(stage => recordsAt(stage, "fields")), ...activities.flatMap(activity => recordsAt(activity, "fields")), ...actions.flatMap(action => recordsAt(action, "params")), ...effects.flatMap(effect => recordsAt(effect, "outputs")) ]);
578
- }
579
-
580
- function hasChoiceList(document) {
581
- return persistedFieldEntries(document).some(entry => {
582
- const options = recordOf(entry.options);
583
- return options !== void 0 && Array.isArray(options.list);
584
- });
585
- }
586
-
587
- function hasFieldKind(document, kind) {
588
- return persistedFieldEntries(document).some(entry => entry.type === kind || entry._type === kind);
589
- }
590
-
591
- function hasActionSemantics(document) {
592
- const root = recordOf(document);
593
- return root === void 0 ? !1 : recordsAt(root, "stages").flatMap(stage => recordsAt(stage, "activities")).flatMap(activity => recordsAt(activity, "actions")).some(action => Array.isArray(action.semantics));
594
- }
595
-
596
- function hasScalarValidation(document) {
597
- return persistedFieldEntries(document).some(entry => {
598
- const validation = recordOf(entry.validation);
599
- return typeof validation?.min == "number" || typeof validation?.max == "number";
600
- });
601
- }
602
-
603
- function hasClaimTokens(document) {
604
- const root = recordOf(document);
605
- return root === void 0 ? !1 : recordsAt(root, "pendingEffects").some(entry => {
606
- const claim = recordOf(entry.claim);
607
- return claim !== void 0 && typeof claim.claimToken == "string";
608
- });
609
- }
610
-
611
- function hasReadinessRequirements(document) {
612
- const root = recordOf(document);
613
- return root === void 0 ? !1 : Array.isArray(recordOf(root.start)?.requirements) ? !0 : recordsAt(root, "stages").some(stage => recordsAt(stage, "activities").some(activity => Array.isArray(activity.requirements)));
614
- }
615
-
616
- const featureDetectors = {
617
- "governed-model-stamps": () => !0,
618
- "subject-field-kind": document => hasFieldKind(document, "subject"),
619
- "typed-scalar-choice-lists": hasChoiceList,
620
- "action-semantics": hasActionSemantics,
621
- "inclusive-scalar-bounds": hasScalarValidation,
622
- "progress-field-kind": document => hasFieldKind(document, "progress"),
623
- "effect-claim-tokens": hasClaimTokens,
624
- "classified-principal-ids": () => !0,
625
- "readiness-requirements": hasReadinessRequirements,
626
- "due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime")
627
- };
628
-
629
- function requiredModelFeatures(documentType, document) {
630
- return DATA_MODEL_CHANGES.filter(change => change.documentTypes.some(candidate => candidate === documentType) && featureDetectors[change.id](document));
631
- }
632
-
633
- function requiredReaderModel(documentType, document) {
634
- return Math.max(0, ...requiredModelFeatures(documentType, document).map(change => change.minReaderModel));
635
- }
636
-
637
- function modelStampFor(args) {
638
- return {
639
- modelVersion: DATA_MODEL_VERSION,
640
- minReaderModel: Math.max(DATA_MODEL_MIN_READER, args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
641
- };
642
- }
643
-
644
- function fieldTreeShape(value) {
645
- if (Array.isArray(value)) return value.map(fieldTreeShape);
646
- if (value === null) return "null";
647
- if (typeof value == "object") {
648
- const record = value;
649
- return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
650
- }
651
- return typeof value;
652
- }
653
-
654
- function modelVersionOf(doc) {
655
- const stamp = doc.modelVersion;
656
- return typeof stamp == "number" ? stamp : 0;
657
- }
658
-
659
- function minReaderModelOf(doc) {
660
- const floor = doc.minReaderModel;
661
- return typeof floor == "number" ? floor : modelVersionOf(doc);
662
- }
663
-
664
- class ModelVersionAheadError extends invariants.WorkflowError {
665
- documentId;
666
- documentModelVersion;
667
- requiredReaderModel;
668
- engineModelVersion;
669
- constructor(args) {
670
- super("model-version-ahead", `Document "${args.documentId}" was written by engine data model ${args.documentModelVersion} and requires a reader at model ${args.requiredReaderModel} or newer; this engine reads up to model ${DATA_MODEL_VERSION}. Upgrade @sanity/workflow-engine to a version that understands it.`),
671
- this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
672
- this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
673
- }
674
- }
675
-
676
- function assertReadableModel(doc) {
677
- const documentReaderModel = minReaderModelOf(doc);
678
- if (documentReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
679
- documentId: doc._id,
680
- documentModelVersion: modelVersionOf(doc),
681
- requiredReaderModel: documentReaderModel
682
- });
683
- return doc;
684
- }
685
-
686
455
  function effectSites(def) {
687
456
  const sites = [];
688
457
  for (const stage of def.stages ?? []) for (const activity of stage.activities ?? []) for (const action of activity.actions ?? []) for (const effect of action.effects ?? []) sites.push({
@@ -1157,12 +926,8 @@ function renderWorkflowRead(read, ctx) {
1157
926
  text: groqConditionDescribe.guillemets(title)
1158
927
  });
1159
928
  }
1160
- if (read.variable === "can" && typeof read.path[0] == "string") return groqConditionDescribe.phrase("read.permission", {
1161
- params: {
1162
- permission: read.path[0]
1163
- },
1164
- text: `your ${read.path[0]} permission`
1165
- });
929
+ const keyed = keyedCallerRead(read);
930
+ if (keyed !== void 0) return keyed;
1166
931
  if (read.variable === "actor" && read.path.length === 1 && read.path[0] === "id") return groqConditionDescribe.phrase("read.actor-id", {
1167
932
  params: {},
1168
933
  text: "your id"
@@ -1184,6 +949,24 @@ function renderWorkflowRead(read, ctx) {
1184
949
  });
1185
950
  }
1186
951
 
952
+ function keyedCallerRead(read) {
953
+ const key = read.path[0];
954
+ if (typeof key == "string") {
955
+ if (read.variable === "can") return groqConditionDescribe.phrase("read.permission", {
956
+ params: {
957
+ permission: key
958
+ },
959
+ text: `your ${key} permission`
960
+ });
961
+ if (read.variable === "attributes") return groqConditionDescribe.phrase("read.attribute", {
962
+ params: {
963
+ attribute: key
964
+ },
965
+ text: `your ${groqConditionDescribe.guillemets(key)} attribute`
966
+ });
967
+ }
968
+ }
969
+
1187
970
  function workflowRequirement(requirement, ctx) {
1188
971
  const {target: target} = requirement, clause = clauseVarRequirement(requirement);
1189
972
  if (clause !== void 0) return clause;
@@ -2612,7 +2395,7 @@ class CascadeLimitError extends invariants.WorkflowError {
2612
2395
  }
2613
2396
  }
2614
2397
 
2615
- const FIELD_OP_TYPES = [ "field.set", "field.unset", "field.append", "field.updateWhere", "field.removeWhere" ];
2398
+ const FIELD_OP_TYPES = [ "field.set", "field.setIfMissing", "field.unset", "field.append", "field.inc", "field.dec", "field.updateWhere", "field.removeWhere" ];
2616
2399
 
2617
2400
  function isFieldOp(summary) {
2618
2401
  return FIELD_OP_TYPES.includes(summary.opType);
@@ -2620,38 +2403,7 @@ function isFieldOp(summary) {
2620
2403
 
2621
2404
  function validateActionParams({action: action, activityName: activityName, callerParams: callerParams}) {
2622
2405
  const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
2623
- for (const decl of declared) {
2624
- const value = params[decl.name], present = decl.name in params && value !== void 0 && value !== null;
2625
- if (decl.required === !0 && !present) {
2626
- issues.push({
2627
- param: decl.name,
2628
- reason: "required but missing"
2629
- });
2630
- continue;
2631
- }
2632
- if (!present) continue;
2633
- if (!checkParamType(value, decl)) {
2634
- issues.push({
2635
- param: decl.name,
2636
- reason: `expected type=${decl.type}, got ${typeof value}`
2637
- });
2638
- continue;
2639
- }
2640
- const choiceIssues = invariants.choiceValueIssues(decl.options, value);
2641
- choiceIssues !== void 0 && issues.push({
2642
- param: decl.name,
2643
- reason: choiceIssues.join("; ")
2644
- });
2645
- const validationIssues = invariants.scalarValidationIssues({
2646
- entryType: decl.type,
2647
- validation: decl.validation,
2648
- value: value
2649
- });
2650
- validationIssues !== void 0 && issues.push({
2651
- param: decl.name,
2652
- reason: validationIssues.join("; ")
2653
- });
2654
- }
2406
+ for (const decl of declared) issues.push(...declaredParamIssues(decl, params));
2655
2407
  if (issues.length > 0) throw new ActionParamsInvalidError({
2656
2408
  action: action.name,
2657
2409
  activity: activityName,
@@ -2660,6 +2412,30 @@ function validateActionParams({action: action, activityName: activityName, calle
2660
2412
  return params;
2661
2413
  }
2662
2414
 
2415
+ function declaredParamIssues(decl, params) {
2416
+ const value = params[decl.name];
2417
+ if (!(decl.name in params && value !== void 0 && value !== null)) return decl.required === !0 ? [ {
2418
+ param: decl.name,
2419
+ reason: "required but missing"
2420
+ } ] : [];
2421
+ if (!checkParamType(value, decl)) return [ {
2422
+ param: decl.name,
2423
+ reason: `expected type=${decl.type}, got ${typeof value}`
2424
+ } ];
2425
+ const choiceIssues = invariants.choiceValueIssues(decl.options, value), validationIssues = invariants.scalarValidationIssues({
2426
+ entryType: decl.type,
2427
+ validation: decl.validation,
2428
+ value: value
2429
+ });
2430
+ return [ ...choiceIssues === void 0 ? [] : [ {
2431
+ param: decl.name,
2432
+ reason: choiceIssues.join("; ")
2433
+ } ], ...validationIssues === void 0 ? [] : [ {
2434
+ param: decl.name,
2435
+ reason: validationIssues.join("; ")
2436
+ } ] ];
2437
+ }
2438
+
2663
2439
  function hasStringField(value, field) {
2664
2440
  return typeof value == "object" && value !== null && field in value && typeof value[field] == "string";
2665
2441
  }
@@ -2711,9 +2487,11 @@ async function runOps(args) {
2711
2487
  now: now,
2712
2488
  snapshot: snapshot,
2713
2489
  refSurface: refSurface,
2714
- opsFromDefinition: opsFromDefinition
2490
+ opsFromDefinition: opsFromDefinition,
2491
+ memberRoles: args.memberRoles,
2492
+ roleAliases: args.roleAliases
2715
2493
  });
2716
- summaries.push(summary), mutation.history.push(opAppliedEntry({
2494
+ summaries.push(summary), shouldRecordOpApplied(summary) && mutation.history.push(opAppliedEntry({
2717
2495
  origin: origin,
2718
2496
  summary: summary,
2719
2497
  stage: stage,
@@ -2724,6 +2502,10 @@ async function runOps(args) {
2724
2502
  return summaries;
2725
2503
  }
2726
2504
 
2505
+ function shouldRecordOpApplied(summary) {
2506
+ return summary.opType !== "field.setIfMissing" || summary.resolved !== void 0;
2507
+ }
2508
+
2727
2509
  function opAppliedEntry(args) {
2728
2510
  const {origin: origin, summary: summary, stage: stage, actor: actor, now: now} = args;
2729
2511
  return {
@@ -2795,12 +2577,19 @@ async function applyOp(op, ctx) {
2795
2577
  case "field.set":
2796
2578
  return applyFieldSet(op, ctx);
2797
2579
 
2580
+ case "field.setIfMissing":
2581
+ return applyFieldSetIfMissing(op, ctx);
2582
+
2798
2583
  case "field.unset":
2799
2584
  return applyFieldUnset(op, ctx);
2800
2585
 
2801
2586
  case "field.append":
2802
2587
  return applyFieldAppend(op, ctx);
2803
2588
 
2589
+ case "field.inc":
2590
+ case "field.dec":
2591
+ return applyFieldArithmetic(op, ctx);
2592
+
2804
2593
  case "field.updateWhere":
2805
2594
  return applyFieldUpdateWhere(op, ctx);
2806
2595
 
@@ -2821,7 +2610,12 @@ function applyFieldSet(op, ctx) {
2821
2610
  entryType: entry._type,
2822
2611
  entryName: entry.name,
2823
2612
  value: value,
2824
- ...entryShape(entry)
2613
+ ...entryShape(entry),
2614
+ memberRoles: ctx.memberRoles,
2615
+ roleAliases: ctx.roleAliases,
2616
+ ...entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
2617
+ previousValue: entry.value
2618
+ } : {}
2825
2619
  });
2826
2620
  return assertRuntimeRefsWithinSurface({
2827
2621
  entryType: entry._type,
@@ -2839,6 +2633,78 @@ function applyFieldSet(op, ctx) {
2839
2633
  };
2840
2634
  }
2841
2635
 
2636
+ function applyFieldSetIfMissing(op, ctx) {
2637
+ const entry = locateEntry(ctx, op.target);
2638
+ return invariants.isAlwaysArrayFieldKind(entry._type) && rejectFieldOpTarget({
2639
+ entry: entry,
2640
+ op: op,
2641
+ issue: "is always array-valued — setIfMissing applies to nullable entries only; an empty array entry already holds []"
2642
+ }), entry.value !== null && entry.value !== void 0 ? {
2643
+ opType: op.type,
2644
+ target: op.target
2645
+ } : {
2646
+ ...applyFieldSet({
2647
+ ...op,
2648
+ type: "field.set"
2649
+ }, ctx),
2650
+ opType: op.type
2651
+ };
2652
+ }
2653
+
2654
+ function applyFieldArithmetic(op, ctx) {
2655
+ const entry = locateEntry(ctx, op.target);
2656
+ entry._type !== "number" && rejectFieldOpTarget({
2657
+ entry: entry,
2658
+ op: op,
2659
+ issue: `is a ${entry._type} entry — arithmetic ops target \`number\` entries only`
2660
+ }), typeof entry.value != "number" && rejectFieldOpTarget({
2661
+ entry: entry,
2662
+ op: op,
2663
+ issue: "has no numeric value — initialize it before applying arithmetic"
2664
+ });
2665
+ const resolvedDelta = op.value === void 0 ? 1 : resolveOpValue({
2666
+ src: op.value,
2667
+ ctx: ctx,
2668
+ target: {
2669
+ kind: "number"
2670
+ }
2671
+ });
2672
+ (typeof resolvedDelta != "number" || !Number.isFinite(resolvedDelta)) && rejectFieldOpTarget({
2673
+ entry: entry,
2674
+ op: op,
2675
+ issue: "value must resolve to a finite number"
2676
+ });
2677
+ const nextValue = entry.value + (op.type === "field.inc" ? resolvedDelta : -resolvedDelta);
2678
+ Number.isFinite(nextValue) || rejectFieldOpTarget({
2679
+ entry: entry,
2680
+ op: op,
2681
+ issue: "result must be a finite number"
2682
+ });
2683
+ const value = invariants.validateFieldValue({
2684
+ entryType: entry._type,
2685
+ entryName: entry.name,
2686
+ value: nextValue,
2687
+ ...entryShape(entry)
2688
+ });
2689
+ return setEntryValue(entry, value), {
2690
+ opType: op.type,
2691
+ target: op.target,
2692
+ resolved: {
2693
+ value: value
2694
+ }
2695
+ };
2696
+ }
2697
+
2698
+ function rejectFieldOpTarget(args) {
2699
+ const {entry: entry, op: op, issue: issue} = args;
2700
+ throw new invariants.FieldValueShapeError({
2701
+ entryType: entry._type,
2702
+ entryName: entry.name,
2703
+ mode: "value",
2704
+ issues: [ `${op.type} target ${op.target.scope}:"${op.target.field}" ${issue}` ]
2705
+ });
2706
+ }
2707
+
2842
2708
  function entryShape(entry) {
2843
2709
  return entry._type === "object" ? {
2844
2710
  fields: entry.fields
@@ -2846,7 +2712,17 @@ function entryShape(entry) {
2846
2712
  of: entry.of
2847
2713
  } : invariants.isSingleDocRefEntry(entry) || entry._type === "doc.refs" ? entry.types !== void 0 ? {
2848
2714
  types: entry.types
2849
- } : {} : {
2715
+ } : {} : invariants.isAssignmentFieldEntry(entry) ? assignmentEntryShape(entry) : scalarEntryShape(entry);
2716
+ }
2717
+
2718
+ function assignmentEntryShape(entry) {
2719
+ return entry.roles !== void 0 ? {
2720
+ roles: entry.roles
2721
+ } : {};
2722
+ }
2723
+
2724
+ function scalarEntryShape(entry) {
2725
+ return {
2850
2726
  ...entry.options !== void 0 ? {
2851
2727
  options: entry.options
2852
2728
  } : {},
@@ -2874,15 +2750,10 @@ function appendItemSlot(entry) {
2874
2750
  };
2875
2751
  }
2876
2752
 
2877
- const EMPTY_BY_KIND = {
2878
- "doc.refs": [],
2879
- array: [],
2880
- assignees: []
2881
- };
2882
-
2883
2753
  function applyFieldUnset(op, ctx) {
2884
2754
  const entry = locateEntry(ctx, op.target);
2885
- return setEntryValue(entry, EMPTY_BY_KIND[entry._type] ?? null), {
2755
+ return setEntryValue(entry, invariants.isAlwaysArrayFieldKind(entry._type) ? [] : null),
2756
+ {
2886
2757
  opType: op.type,
2887
2758
  target: op.target
2888
2759
  };
@@ -2900,7 +2771,10 @@ function applyFieldAppend(op, ctx) {
2900
2771
  entryName: entry.name,
2901
2772
  item: item,
2902
2773
  types: entry._type === "doc.refs" ? entry.types : void 0,
2903
- of: entry._type === "array" ? entry.of : void 0
2774
+ of: entry._type === "array" ? entry.of : void 0,
2775
+ roles: entry._type === "assignees" ? entry.roles : void 0,
2776
+ memberRoles: ctx.memberRoles,
2777
+ roleAliases: ctx.roleAliases
2904
2778
  });
2905
2779
  assertRuntimeRefsWithinSurface({
2906
2780
  entryType: entry._type === "doc.refs" ? "doc.ref" : "object",
@@ -2921,7 +2795,7 @@ function applyFieldAppend(op, ctx) {
2921
2795
  }
2922
2796
 
2923
2797
  function withRowKey(item) {
2924
- return typeof item == "object" && item !== null && !Array.isArray(item) && !("_key" in item) ? {
2798
+ return invariants.isRecord(item) && !("_key" in item) ? {
2925
2799
  _key: randomKey(),
2926
2800
  ...item
2927
2801
  } : item;
@@ -2948,6 +2822,13 @@ async function applyFieldUpdateWhere(op, ctx) {
2948
2822
  merge: mergeRecord,
2949
2823
  entry: entry,
2950
2824
  op: op
2825
+ }), invariants.validateFieldValue({
2826
+ entryType: "array",
2827
+ entryName: entry.name,
2828
+ value: [ mergeRecord ],
2829
+ of: entry.of,
2830
+ memberRoles: ctx.memberRoles,
2831
+ roleAliases: ctx.roleAliases
2951
2832
  });
2952
2833
  const matches = await rowMatches({
2953
2834
  where: op.where,
@@ -3064,18 +2945,6 @@ function subSlot(target, field) {
3064
2945
  function resolveOpValue(args) {
3065
2946
  const {src: src, ctx: ctx, target: target} = args;
3066
2947
  switch (src.type) {
3067
- case "literal":
3068
- case "param":
3069
- case "actor":
3070
- case "now":
3071
- return resolveStaticValueExpr(src, ctx);
3072
-
3073
- case "self":
3074
- return ctx.self;
3075
-
3076
- case "stage":
3077
- return ctx.stage;
3078
-
3079
2948
  case "fieldRead":
3080
2949
  {
3081
2950
  const entry = readEntryFromMutation(ctx, src);
@@ -3105,6 +2974,25 @@ function resolveOpValue(args) {
3105
2974
  }
3106
2975
  return out;
3107
2976
  }
2977
+
2978
+ default:
2979
+ return resolveAtomicOpValue(src, ctx);
2980
+ }
2981
+ }
2982
+
2983
+ function resolveAtomicOpValue(src, ctx) {
2984
+ switch (src.type) {
2985
+ case "literal":
2986
+ case "param":
2987
+ case "actor":
2988
+ case "now":
2989
+ return resolveStaticValueExpr(src, ctx);
2990
+
2991
+ case "self":
2992
+ return ctx.self;
2993
+
2994
+ case "stage":
2995
+ return ctx.stage;
3108
2996
  }
3109
2997
  }
3110
2998
 
@@ -3555,75 +3443,424 @@ async function assertInstanceWriteAllowed(args) {
3555
3443
  });
3556
3444
  }
3557
3445
 
3558
- async function renderConditionScope(source, opts) {
3559
- const {instance: instance, definition: definition, snapshot: snapshot, now: now} = source, base = buildParams({
3560
- instance: instance,
3561
- now: now,
3562
- snapshot: snapshot
3563
- }), fields = {
3564
- ...base.fields,
3565
- ...scopedFieldOverlay({
3566
- instance: instance,
3567
- snapshot: snapshot,
3568
- activityName: opts?.activityName
3569
- })
3570
- }, params = {
3571
- ...base,
3572
- fields: fields,
3573
- actor: opts?.actor,
3574
- assigned: opts?.activityName !== void 0 ? assignedFor({
3575
- instance: instance,
3576
- activityName: opts.activityName,
3577
- actor: opts?.actor,
3578
- roleAliases: definition.roleAliases
3579
- }) : !1,
3580
- ...opts?.vars
3581
- };
3582
- return {
3583
- ...await invariants.evaluatePredicates({
3584
- predicates: definition.predicates,
3585
- snapshot: snapshot,
3586
- params: params
3587
- }),
3588
- ...params
3589
- };
3590
- }
3446
+ const DATA_MODEL_VERSION = 8, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 8, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/editorial-workflows/prerelease";
3591
3447
 
3592
- function definitionLookupGroq(explicit) {
3593
- const scoped = `_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${invariants.tagScopeFilter()}`;
3594
- return explicit ? `*[${scoped} && version == $version][0]` : `*[${scoped}] | order(version desc)[0]`;
3448
+ class ReaderModelAcknowledgementError extends invariants.WorkflowError {
3449
+ code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
3450
+ expectedMinReaderModel;
3451
+ requiredMinReaderModel;
3452
+ engineMinReaderModel=DATA_MODEL_MIN_READER;
3453
+ engineMaxReaderModel=DATA_MODEL_MAX_READER;
3454
+ engineModelVersion=DATA_MODEL_VERSION;
3455
+ documentationUrl=READER_MODEL_ROLLOUT_URL;
3456
+ constructor(expectedMinReaderModel, options = {}) {
3457
+ const {context: context = "Deployment", requiredMinReaderModel: requiredMinReaderModel = DATA_MODEL_MIN_READER} = options, expected = expectedMinReaderModel === void 0 ? "missing" : String(expectedMinReaderModel);
3458
+ super("reader-model-acknowledgement", `${context} acknowledges reader model ${expected}; the submitted definitions require at least ${requiredMinReaderModel}, and this writer supports acknowledgements through ${DATA_MODEL_MAX_READER}.\nUse a reviewed numeric literal in that range. Before raising it, ensure every runtime sharing the workflow resource can read the required model.\nRollout guide: ${READER_MODEL_ROLLOUT_URL}`),
3459
+ this.name = "ReaderModelAcknowledgementError", this.expectedMinReaderModel = expectedMinReaderModel,
3460
+ this.requiredMinReaderModel = requiredMinReaderModel;
3461
+ }
3595
3462
  }
3596
3463
 
3597
- function definitionsListGroq(versionOrder) {
3598
- return `*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}] | order(name asc, version ${versionOrder})`;
3464
+ function assertReaderModelAcknowledgement(expectedMinReaderModel, options) {
3465
+ const {context: context, requiredMinReaderModel: requiredMinReaderModel} = options;
3466
+ if (typeof expectedMinReaderModel != "number" || !Number.isFinite(expectedMinReaderModel) || !Number.isInteger(expectedMinReaderModel) || expectedMinReaderModel < 0 || expectedMinReaderModel < requiredMinReaderModel || expectedMinReaderModel > DATA_MODEL_MAX_READER) throw new ReaderModelAcknowledgementError(expectedMinReaderModel, {
3467
+ requiredMinReaderModel: requiredMinReaderModel,
3468
+ ...context !== void 0 ? {
3469
+ context: context
3470
+ } : {}
3471
+ });
3599
3472
  }
3600
3473
 
3601
- function latestDefinitionsGroq() {
3602
- const scoped = `_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}`;
3603
- return `*[${scoped} && version == math::max(*[${scoped} && name == ^.name].version)] | order(name asc)`;
3604
- }
3474
+ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
3475
+ id: "governed-model-stamps",
3476
+ introducedInModel: 1,
3477
+ minReaderModel: 0,
3478
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3479
+ compatibility: "additive",
3480
+ applicability: "unconditional",
3481
+ summary: "Definition and instance documents carry model provenance and reader-floor stamps."
3482
+ }), Object.freeze({
3483
+ id: "subject-field-kind",
3484
+ introducedInModel: 2,
3485
+ minReaderModel: 0,
3486
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3487
+ compatibility: "additive",
3488
+ applicability: "detectable",
3489
+ summary: "A workflow-level subject field identifies the document a workflow is about."
3490
+ }), Object.freeze({
3491
+ id: "typed-scalar-choice-lists",
3492
+ introducedInModel: 2,
3493
+ minReaderModel: 2,
3494
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3495
+ compatibility: "reader-floor",
3496
+ applicability: "detectable",
3497
+ summary: "Scalar fields may constrain writes to a persisted typed choice list."
3498
+ }), Object.freeze({
3499
+ id: "action-semantics",
3500
+ introducedInModel: 2,
3501
+ minReaderModel: 0,
3502
+ documentTypes: Object.freeze([ "definition" ]),
3503
+ compatibility: "additive",
3504
+ applicability: "detectable",
3505
+ summary: "Ordinary actions may carry advisory workflow semantics."
3506
+ }), Object.freeze({
3507
+ id: "inclusive-scalar-bounds",
3508
+ introducedInModel: 2,
3509
+ minReaderModel: 2,
3510
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3511
+ compatibility: "reader-floor",
3512
+ applicability: "detectable",
3513
+ summary: "String, text, and number values may carry persisted inclusive bounds."
3514
+ }), Object.freeze({
3515
+ id: "progress-field-kind",
3516
+ introducedInModel: 3,
3517
+ minReaderModel: 0,
3518
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3519
+ compatibility: "additive",
3520
+ applicability: "detectable",
3521
+ summary: "A progress field kind carries application-defined 0–100 completion."
3522
+ }), Object.freeze({
3523
+ id: "effect-claim-tokens",
3524
+ introducedInModel: 3,
3525
+ minReaderModel: 0,
3526
+ documentTypes: Object.freeze([ "instance" ]),
3527
+ compatibility: "additive",
3528
+ applicability: "detectable",
3529
+ summary: "Pending-effect claims carry an exact-claim token gating mid-dispatch state reports."
3530
+ }), Object.freeze({
3531
+ id: "classified-principal-ids",
3532
+ introducedInModel: 4,
3533
+ minReaderModel: 4,
3534
+ documentTypes: Object.freeze([ "instance" ]),
3535
+ compatibility: "reader-floor",
3536
+ applicability: "unconditional",
3537
+ summary: "Principal ids are namespace-classified: actor and assignee writes carry the account-global user id only, and readers resolve legacy project-scoped ids through the prefix classifier at the instance read funnel."
3538
+ }), Object.freeze({
3539
+ id: "readiness-requirements",
3540
+ introducedInModel: 4,
3541
+ minReaderModel: 4,
3542
+ documentTypes: Object.freeze([ "definition" ]),
3543
+ compatibility: "reader-floor",
3544
+ applicability: "detectable",
3545
+ summary: "Start and activity readiness use named polymorphic requirement arrays."
3546
+ }), Object.freeze({
3547
+ id: "due-date-field-kinds",
3548
+ introducedInModel: 5,
3549
+ minReaderModel: 0,
3550
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3551
+ compatibility: "additive",
3552
+ applicability: "detectable",
3553
+ summary: "Due-date field kinds (dueDate, dueDatetime) mark a level deadline, elevated aliases of date/datetime carrying the same stored value."
3554
+ }), Object.freeze({
3555
+ id: "node-semantics",
3556
+ introducedInModel: 6,
3557
+ minReaderModel: 0,
3558
+ documentTypes: Object.freeze([ "definition" ]),
3559
+ compatibility: "additive",
3560
+ applicability: "detectable",
3561
+ summary: "Workflow, stage, activity, and action nodes may carry signal or custom advisory semantics."
3562
+ }), Object.freeze({
3563
+ id: "field-patch-ops",
3564
+ introducedInModel: 6,
3565
+ minReaderModel: 0,
3566
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3567
+ compatibility: "additive",
3568
+ applicability: "detectable",
3569
+ summary: "Field ops may increment, decrement, or initialize a missing field value."
3570
+ }), Object.freeze({
3571
+ id: "attributes-condition-var",
3572
+ introducedInModel: 7,
3573
+ minReaderModel: 0,
3574
+ documentTypes: Object.freeze([ "definition" ]),
3575
+ compatibility: "additive",
3576
+ applicability: "unconditional",
3577
+ summary: "Caller-bound $attributes condition variable binds the acting token's org-level User Attributes (advisory; absent when unavailable)."
3578
+ }), Object.freeze({
3579
+ id: "role-constrained-assignment-fields",
3580
+ introducedInModel: 8,
3581
+ minReaderModel: 8,
3582
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3583
+ compatibility: "reader-floor",
3584
+ applicability: "detectable",
3585
+ summary: "Assignee fields may restrict newly assigned users and collective roles by role."
3586
+ }) ]);
3605
3587
 
3606
- function deployedTagsGroq() {
3607
- return `array::unique(*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}"].tag) | order(@ asc)`;
3588
+ function recordOf(value) {
3589
+ return value !== null && typeof value == "object" && !Array.isArray(value) ? value : void 0;
3608
3590
  }
3609
3591
 
3610
- function definitionTagsGroq() {
3611
- return `array::unique(*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition].tag) | order(@ asc)`;
3592
+ function recordsAt(record, key) {
3593
+ const value = record[key];
3594
+ return Array.isArray(value) ? value.map(recordOf).filter(item => item !== void 0) : [];
3612
3595
  }
3613
3596
 
3614
- function latestDeployedDefinitions(rows) {
3615
- const byName = /* @__PURE__ */ new Map;
3616
- for (const row of rows) {
3617
- const held = byName.get(row.name);
3618
- (held === void 0 || row.version > held.version) && byName.set(row.name, row);
3619
- }
3620
- return [ ...byName.values() ];
3597
+ function nestedFieldEntries(entries) {
3598
+ return entries.flatMap(entry => [ entry, ...nestedFieldEntries(recordsAt(entry, "fields")), ...nestedFieldEntries(recordsAt(entry, "of")) ]);
3621
3599
  }
3622
3600
 
3623
- function liveChildrenField(instance) {
3624
- const live = liveSubworkflows(instance).length;
3625
- return live > 0 ? {
3626
- liveChildren: live
3601
+ function parsedDefinitionSnapshot(root) {
3602
+ if (typeof root.definitionSnapshot == "string") return recordOf(parseDefinitionSnapshotValue({
3603
+ _id: typeof root._id == "string" ? root._id : "<unknown instance>",
3604
+ definitionSnapshot: root.definitionSnapshot
3605
+ }));
3606
+ }
3607
+
3608
+ function persistedDefinitionTree(document) {
3609
+ const root = recordOf(document);
3610
+ if (root === void 0) return;
3611
+ const snapshot = parsedDefinitionSnapshot(root), roots = snapshot === void 0 ? [ root ] : [ root, snapshot ], {stages: stages, activities: activities, actions: actions} = definitionDescendants(roots);
3612
+ return {
3613
+ roots: roots,
3614
+ stages: stages,
3615
+ activities: activities,
3616
+ actions: actions
3617
+ };
3618
+ }
3619
+
3620
+ function persistedFieldEntries(document) {
3621
+ const tree = persistedDefinitionTree(document);
3622
+ if (tree === void 0) return [];
3623
+ const {roots: roots, stages: stages, activities: activities, actions: actions} = tree, effects = actions.flatMap(action => recordsAt(action, "effects"));
3624
+ return nestedFieldEntries([ ...roots.flatMap(candidate => recordsAt(candidate, "fields")), ...stages.flatMap(stage => recordsAt(stage, "fields")), ...activities.flatMap(activity => recordsAt(activity, "fields")), ...actions.flatMap(action => recordsAt(action, "params")), ...effects.flatMap(effect => recordsAt(effect, "outputs")) ]);
3625
+ }
3626
+
3627
+ function definitionDescendants(roots) {
3628
+ const stages = roots.flatMap(root => recordsAt(root, "stages")), activities = stages.flatMap(stage => recordsAt(stage, "activities")), actions = activities.flatMap(activity => recordsAt(activity, "actions"));
3629
+ return {
3630
+ stages: stages,
3631
+ activities: activities,
3632
+ actions: actions
3633
+ };
3634
+ }
3635
+
3636
+ function hasChoiceList(document) {
3637
+ return persistedFieldEntries(document).some(entry => {
3638
+ const options = recordOf(entry.options);
3639
+ return options !== void 0 && Array.isArray(options.list);
3640
+ });
3641
+ }
3642
+
3643
+ function entryHasKind(entry, kind) {
3644
+ return entryKindMatches(entry, candidate => candidate === kind);
3645
+ }
3646
+
3647
+ function entryKindMatches(entry, accepts) {
3648
+ return [ entry.type, entry._type ].some(kind => typeof kind == "string" && accepts(kind));
3649
+ }
3650
+
3651
+ function hasFieldKind(document, kind) {
3652
+ return persistedFieldEntries(document).some(entry => entryHasKind(entry, kind));
3653
+ }
3654
+
3655
+ function definitionNodes(document) {
3656
+ const root = recordOf(document);
3657
+ if (root !== void 0) return {
3658
+ root: root,
3659
+ ...definitionDescendants([ root ])
3660
+ };
3661
+ }
3662
+
3663
+ function hasActionSemantics(document) {
3664
+ return definitionNodes(document)?.actions.some(hasSemantics) ?? !1;
3665
+ }
3666
+
3667
+ function hasNodeSemantics(document) {
3668
+ const nodes = definitionNodes(document);
3669
+ return nodes === void 0 ? !1 : [ nodes.root, ...nodes.stages, ...nodes.activities ].some(hasSemantics) ? !0 : nodes.actions.some(action => hasSemantics(action) && action.semantics.some(isNodeSemanticValue));
3670
+ }
3671
+
3672
+ function isNodeSemanticValue(semantic) {
3673
+ return typeof semantic == "string" && (semantic.startsWith("signal.") || semantic.startsWith("custom."));
3674
+ }
3675
+
3676
+ function hasSemantics(node) {
3677
+ return Array.isArray(node.semantics);
3678
+ }
3679
+
3680
+ function hasPersistedOpType(document, types) {
3681
+ const tree = persistedDefinitionTree(document);
3682
+ return tree === void 0 ? !1 : tree.actions.flatMap(action => recordsAt(action, "ops")).some(op => typeof op.type == "string" && types.includes(op.type)) ? !0 : tree.roots.some(root => recordsAt(root, "history").some(entry => entry._type === "opApplied" && typeof entry.opType == "string" && types.includes(entry.opType)));
3683
+ }
3684
+
3685
+ function hasScalarValidation(document) {
3686
+ return persistedFieldEntries(document).some(entry => {
3687
+ const validation = recordOf(entry.validation);
3688
+ return typeof validation?.min == "number" || typeof validation?.max == "number";
3689
+ });
3690
+ }
3691
+
3692
+ function hasClaimTokens(document) {
3693
+ const root = recordOf(document);
3694
+ return root === void 0 ? !1 : recordsAt(root, "pendingEffects").some(entry => {
3695
+ const claim = recordOf(entry.claim);
3696
+ return claim !== void 0 && typeof claim.claimToken == "string";
3697
+ });
3698
+ }
3699
+
3700
+ function hasReadinessRequirements(document) {
3701
+ const root = recordOf(document);
3702
+ return root === void 0 ? !1 : Array.isArray(recordOf(root.start)?.requirements) ? !0 : recordsAt(root, "stages").some(stage => recordsAt(stage, "activities").some(activity => Array.isArray(activity.requirements)));
3703
+ }
3704
+
3705
+ function hasRoleConstrainedAssignments(document) {
3706
+ return persistedFieldEntries(document).some(entry => entryKindMatches(entry, invariants.assignmentKindAcceptsRoles) && Array.isArray(entry.roles) && entry.roles.length > 0);
3707
+ }
3708
+
3709
+ const featureDetectors = {
3710
+ "governed-model-stamps": () => !0,
3711
+ "subject-field-kind": document => hasFieldKind(document, "subject"),
3712
+ "typed-scalar-choice-lists": hasChoiceList,
3713
+ "action-semantics": hasActionSemantics,
3714
+ "inclusive-scalar-bounds": hasScalarValidation,
3715
+ "progress-field-kind": document => hasFieldKind(document, "progress"),
3716
+ "effect-claim-tokens": hasClaimTokens,
3717
+ "classified-principal-ids": () => !0,
3718
+ "readiness-requirements": hasReadinessRequirements,
3719
+ "due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime"),
3720
+ "role-constrained-assignment-fields": hasRoleConstrainedAssignments,
3721
+ "node-semantics": hasNodeSemantics,
3722
+ "field-patch-ops": document => hasPersistedOpType(document, [ "field.inc", "field.dec", "field.setIfMissing" ]),
3723
+ "attributes-condition-var": () => !0
3724
+ };
3725
+
3726
+ function requiredModelFeatures(documentType, document) {
3727
+ return DATA_MODEL_CHANGES.filter(change => change.documentTypes.some(candidate => candidate === documentType) && featureDetectors[change.id](document));
3728
+ }
3729
+
3730
+ function requiredReaderModel(documentType, document) {
3731
+ return Math.max(0, ...requiredModelFeatures(documentType, document).map(change => change.minReaderModel));
3732
+ }
3733
+
3734
+ function requiredDefinitionReaderModel(definitions) {
3735
+ return Math.max(DATA_MODEL_MIN_READER, ...definitions.map(definition => requiredReaderModel("definition", definition)));
3736
+ }
3737
+
3738
+ function modelStampFor(args) {
3739
+ return {
3740
+ modelVersion: DATA_MODEL_VERSION,
3741
+ minReaderModel: Math.max(DATA_MODEL_MIN_READER, args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
3742
+ };
3743
+ }
3744
+
3745
+ function fieldTreeShape(value) {
3746
+ if (Array.isArray(value)) return value.map(fieldTreeShape);
3747
+ if (value === null) return "null";
3748
+ if (typeof value == "object") {
3749
+ const record = value;
3750
+ return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
3751
+ }
3752
+ return typeof value;
3753
+ }
3754
+
3755
+ function modelVersionOf(doc) {
3756
+ const stamp = doc.modelVersion;
3757
+ return typeof stamp == "number" ? stamp : 0;
3758
+ }
3759
+
3760
+ function minReaderModelOf(doc) {
3761
+ const floor = doc.minReaderModel;
3762
+ return typeof floor == "number" ? floor : modelVersionOf(doc);
3763
+ }
3764
+
3765
+ class ModelVersionAheadError extends invariants.WorkflowError {
3766
+ documentId;
3767
+ documentModelVersion;
3768
+ requiredReaderModel;
3769
+ engineModelVersion;
3770
+ constructor(args) {
3771
+ super("model-version-ahead", `Document "${args.documentId}" was written by engine data model ${args.documentModelVersion} and requires a reader at model ${args.requiredReaderModel} or newer; this engine reads up to model ${DATA_MODEL_VERSION}. Upgrade @sanity/workflow-engine to a version that understands it.`),
3772
+ this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
3773
+ this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
3774
+ }
3775
+ }
3776
+
3777
+ function assertReadableModel(doc) {
3778
+ const documentReaderModel = minReaderModelOf(doc);
3779
+ if (documentReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
3780
+ documentId: doc._id,
3781
+ documentModelVersion: modelVersionOf(doc),
3782
+ requiredReaderModel: documentReaderModel
3783
+ });
3784
+ return doc;
3785
+ }
3786
+
3787
+ async function renderConditionScope(source, opts) {
3788
+ const {instance: instance, definition: definition, snapshot: snapshot, now: now} = source, base = buildParams({
3789
+ instance: instance,
3790
+ now: now,
3791
+ snapshot: snapshot
3792
+ }), fields = {
3793
+ ...base.fields,
3794
+ ...scopedFieldOverlay({
3795
+ instance: instance,
3796
+ snapshot: snapshot,
3797
+ activityName: opts?.activityName
3798
+ })
3799
+ }, params = {
3800
+ ...base,
3801
+ fields: fields,
3802
+ actor: opts?.actor,
3803
+ assigned: opts?.activityName !== void 0 ? assignedFor({
3804
+ instance: instance,
3805
+ activityName: opts.activityName,
3806
+ actor: opts?.actor,
3807
+ roleAliases: definition.roleAliases
3808
+ }) : !1,
3809
+ ...opts?.vars
3810
+ };
3811
+ return {
3812
+ ...await invariants.evaluatePredicates({
3813
+ predicates: definition.predicates,
3814
+ snapshot: snapshot,
3815
+ params: params
3816
+ }),
3817
+ ...params
3818
+ };
3819
+ }
3820
+
3821
+ function definitionLookupGroq(explicit) {
3822
+ const scoped = `_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${invariants.tagScopeFilter()}`;
3823
+ return explicit ? `*[${scoped} && version == $version][0]` : `*[${scoped}] | order(version desc)[0]`;
3824
+ }
3825
+
3826
+ function definitionsListGroq(versionOrder) {
3827
+ return `*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}] | order(name asc, version ${versionOrder})`;
3828
+ }
3829
+
3830
+ function latestDefinitionsGroq() {
3831
+ const scoped = `_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && ${invariants.tagScopeFilter()}`;
3832
+ return `*[${scoped} && version == math::max(*[${scoped} && name == ^.name].version)] | order(name asc)`;
3833
+ }
3834
+
3835
+ function deployedTagsGroq() {
3836
+ return `array::unique(*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}"].tag) | order(@ asc)`;
3837
+ }
3838
+
3839
+ function definitionTagsGroq() {
3840
+ return `array::unique(*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition].tag) | order(@ asc)`;
3841
+ }
3842
+
3843
+ function latestDeployedDefinitions(rows) {
3844
+ const byName = /* @__PURE__ */ new Map;
3845
+ for (const row of rows) {
3846
+ const held = byName.get(row.name);
3847
+ (held === void 0 || row.version > held.version) && byName.set(row.name, row);
3848
+ }
3849
+ return [ ...byName.values() ];
3850
+ }
3851
+
3852
+ function findStageNode(args) {
3853
+ return args.definition?.stages.find(entry => entry.name === args.stageName);
3854
+ }
3855
+
3856
+ function findActivityNode(args) {
3857
+ return findStageNode(args)?.activities?.find(entry => entry.name === args.activityName);
3858
+ }
3859
+
3860
+ function liveChildrenField(instance) {
3861
+ const live = liveSubworkflows(instance).length;
3862
+ return live > 0 ? {
3863
+ liveChildren: live
3627
3864
  } : {};
3628
3865
  }
3629
3866
 
@@ -3708,6 +3945,27 @@ function noTransitionFiresCause(input) {
3708
3945
  };
3709
3946
  }
3710
3947
 
3948
+ function stuckFromDocumentState(input) {
3949
+ return failedEffectCause(input) ?? failedActivityCause(input) ?? hungEffectCause(input);
3950
+ }
3951
+
3952
+ function documentStuckCause(args) {
3953
+ const stage = findOpenStageEntry(args.instance);
3954
+ if (stage !== void 0) return stuckFromDocumentState({
3955
+ instance: args.instance,
3956
+ activities: stage.activities.map(entry => ({
3957
+ status: entry.status,
3958
+ activity: findActivityNode({
3959
+ activityName: entry.name,
3960
+ definition: args.definition,
3961
+ stageName: stage.name
3962
+ }) ?? {
3963
+ name: entry.name
3964
+ }
3965
+ }))
3966
+ });
3967
+ }
3968
+
3711
3969
  function diagnoseInstance(input) {
3712
3970
  const {instance: instance} = input, terminal = terminalState(instance);
3713
3971
  if (terminal === "aborted" && instance.abortedAt !== void 0) {
@@ -3726,7 +3984,7 @@ function diagnoseInstance(input) {
3726
3984
  at: instance.completedAt,
3727
3985
  ...liveChildrenField(instance)
3728
3986
  };
3729
- const cause = failedEffectCause(input) ?? failedActivityCause(input) ?? hungEffectCause(input) ?? noTransitionFiresCause(input);
3987
+ const cause = stuckFromDocumentState(input) ?? noTransitionFiresCause(input);
3730
3988
  return cause !== void 0 ? {
3731
3989
  state: "stuck",
3732
3990
  cause: cause
@@ -3788,35 +4046,235 @@ function isClaimExpired(claim, now) {
3788
4046
  return claim.leaseExpiresAt === void 0 || hasPassed(claim.leaseExpiresAt, now);
3789
4047
  }
3790
4048
 
3791
- const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], EXECUTION_KINDS = {
3792
- interactive: "interactive",
3793
- server: "server",
3794
- cli: "cli",
3795
- mcp: "mcp",
3796
- drainer: "drainer",
3797
- script: "script",
3798
- test: "test",
3799
- studio: "studio",
3800
- sdkApp: "sdk-app"
3801
- };
4049
+ const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ];
3802
4050
 
3803
- function detectRuntime() {
3804
- const g = globalThis;
3805
- if (g.Deno !== void 0) return "deno";
3806
- if (g.Bun !== void 0) return "bun";
3807
- if (g.EdgeRuntime !== void 0) return "edge";
3808
- if (g.window !== void 0 && g.document !== void 0) return "browser";
3809
- if (g.WorkerGlobalScope !== void 0 && g.self !== void 0) return "worker";
3810
- const proc = g.process;
3811
- return typeof proc == "object" && proc !== null && typeof proc.versions?.node == "string" ? "node" : "unknown";
4051
+ function userLoginProvider(user) {
4052
+ return user?.provider ?? user?.loginProvider;
3812
4053
  }
3813
4054
 
3814
- let inferredRuntime;
4055
+ function optionalString(value) {
4056
+ return value === void 0 || typeof value == "string";
4057
+ }
3815
4058
 
3816
- function resolveExecutionContext(declared) {
3817
- return inferredRuntime ??= detectRuntime(), {
3818
- runtime: inferredRuntime,
3819
- ...declared?.kind !== void 0 ? {
4059
+ function isClientProjectUser(value) {
4060
+ return invariants.isRecord(value) && typeof value.id == "string" && optionalString(value.sanityUserId) && optionalString(value.displayName) && optionalString(value.email) && (value.imageUrl === null || optionalString(value.imageUrl)) && optionalString(value.provider) && optionalString(value.loginProvider);
4061
+ }
4062
+
4063
+ function objectProperty(value, property) {
4064
+ if (!invariants.isRecord(value)) return;
4065
+ const propertyValue = value[property];
4066
+ return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
4067
+ }
4068
+
4069
+ function stringProperty(value, property) {
4070
+ const propertyValue = Reflect.get(value, property);
4071
+ return typeof propertyValue == "string" ? propertyValue : void 0;
4072
+ }
4073
+
4074
+ function apiErrorType(error) {
4075
+ const response = objectProperty(error, "response"), body = objectProperty(response, "body");
4076
+ if (!body) return;
4077
+ const nestedError = objectProperty(body, "error");
4078
+ return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
4079
+ }
4080
+
4081
+ function isProjectUserNotFoundError(error) {
4082
+ return apiErrorType(error) === "projectUserNotFoundError";
4083
+ }
4084
+
4085
+ const memberJoinCache = /* @__PURE__ */ new WeakMap;
4086
+
4087
+ function joinCacheFor(client) {
4088
+ let cache = memberJoinCache.get(client);
4089
+ return cache === void 0 && (cache = /* @__PURE__ */ new Map, memberJoinCache.set(client, cache)),
4090
+ cache;
4091
+ }
4092
+
4093
+ function isClientProjectMember(value) {
4094
+ return invariants.isRecord(value) && typeof value.id == "string";
4095
+ }
4096
+
4097
+ async function requestProjectMembers(request, projectId) {
4098
+ const project = await request({
4099
+ uri: `/projects/${encodeURIComponent(projectId)}`
4100
+ }), members = invariants.isRecord(project) ? project.members : void 0;
4101
+ if (!Array.isArray(members) || !members.every(isClientProjectMember)) throw new Error(`Project member directory response is invalid for project "${projectId}"`);
4102
+ return members;
4103
+ }
4104
+
4105
+ async function requestProjectUsers(args) {
4106
+ const {request: request, projectId: projectId, memberIds: memberIds} = args, response = await request({
4107
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
4108
+ }), rows = Array.isArray(response) ? response : [ response ];
4109
+ if (!rows.every(isClientProjectUser)) throw new Error(`Project user directory response is invalid for project "${projectId}"`);
4110
+ const requested = new Set(memberIds);
4111
+ return rows.filter(row => requested.has(row.id));
4112
+ }
4113
+
4114
+ async function requestProjectMemberDirectory(args) {
4115
+ const {request: request, projectId: projectId} = args, members = await requestProjectMembers(request, projectId), memberIds = members.map(member => member.id).filter(id => invariants.classifyPrincipalId(id).namespace === "project");
4116
+ if (memberIds.length === 0) return {
4117
+ members: members,
4118
+ globalUsers: []
4119
+ };
4120
+ const users = await requestProjectUsers({
4121
+ request: request,
4122
+ projectId: projectId,
4123
+ memberIds: memberIds
4124
+ });
4125
+ return {
4126
+ members: members,
4127
+ globalUsers: users.flatMap(user => {
4128
+ const globalId = invariants.directoryBridgeId(user.sanityUserId);
4129
+ return globalId === void 0 ? [] : [ {
4130
+ memberId: user.id,
4131
+ globalId: globalId,
4132
+ user: user
4133
+ } ];
4134
+ })
4135
+ };
4136
+ }
4137
+
4138
+ async function resolveGlobalProjectUser(args) {
4139
+ const {client: client, request: request, projectId: projectId, id: id} = args, cache = joinCacheFor(client), key = `${projectId}:${id}`, hit = cache.get(key);
4140
+ if (hit !== void 0) return hit === null ? {
4141
+ status: "missing"
4142
+ } : {
4143
+ status: "resolved",
4144
+ user: hit
4145
+ };
4146
+ try {
4147
+ await ingestMemberJoin({
4148
+ request: request,
4149
+ projectId: projectId,
4150
+ cache: cache
4151
+ });
4152
+ const found = cache.get(key);
4153
+ return found != null ? {
4154
+ status: "resolved",
4155
+ user: found
4156
+ } : (cache.set(key, null), {
4157
+ status: "missing"
4158
+ });
4159
+ } catch (cause) {
4160
+ return {
4161
+ status: "inaccessible",
4162
+ cause: cause
4163
+ };
4164
+ }
4165
+ }
4166
+
4167
+ async function ingestMemberJoin(args) {
4168
+ const {request: request, projectId: projectId, cache: cache} = args, snapshot = await requestProjectMemberDirectory({
4169
+ request: request,
4170
+ projectId: projectId
4171
+ });
4172
+ for (const {globalId: globalId, user: user} of snapshot.globalUsers) cache.set(`${projectId}:${globalId}`, user);
4173
+ }
4174
+
4175
+ function clientProjectUserDirectory(client, projectId) {
4176
+ return {
4177
+ findById: async id => {
4178
+ if (!client.request) return {
4179
+ status: "inaccessible",
4180
+ cause: new Error("Project-user resolution requires WorkflowClient.request")
4181
+ };
4182
+ if (invariants.classifyPrincipalId(id).namespace === "global") return resolveGlobalProjectUser({
4183
+ client: client,
4184
+ request: client.request,
4185
+ projectId: projectId,
4186
+ id: id
4187
+ });
4188
+ try {
4189
+ const response = await client.request({
4190
+ uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
4191
+ }), candidate = Array.isArray(response) ? response[0] : response;
4192
+ return candidate == null ? {
4193
+ status: "missing"
4194
+ } : isClientProjectUser(candidate) ? candidate.id !== id ? {
4195
+ status: "inaccessible",
4196
+ cause: new Error(`Project-user response answered id "${candidate.id}", not the requested "${id}"`)
4197
+ } : {
4198
+ status: "resolved",
4199
+ user: candidate
4200
+ } : {
4201
+ status: "inaccessible",
4202
+ cause: new Error("Project-user response had an invalid shape")
4203
+ };
4204
+ } catch (cause) {
4205
+ return isProjectUserNotFoundError(cause) ? {
4206
+ status: "missing"
4207
+ } : {
4208
+ status: "inaccessible",
4209
+ cause: cause
4210
+ };
4211
+ }
4212
+ }
4213
+ };
4214
+ }
4215
+
4216
+ function resolveClientActor(client, args) {
4217
+ return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
4218
+ }
4219
+
4220
+ async function resolveActor(directory, actor) {
4221
+ if (actor.kind !== "person") return {
4222
+ status: "not-person",
4223
+ actor: actor
4224
+ };
4225
+ const personActor = {
4226
+ ...actor,
4227
+ kind: "person"
4228
+ }, lookup = await directory.findById(personActor.id);
4229
+ return lookup.status === "resolved" ? {
4230
+ status: "resolved",
4231
+ actor: personActor,
4232
+ user: lookup.user
4233
+ } : lookup.status === "inaccessible" ? {
4234
+ status: "inaccessible",
4235
+ actor: personActor,
4236
+ ...lookup.cause === void 0 ? {} : {
4237
+ cause: lookup.cause
4238
+ }
4239
+ } : {
4240
+ status: "missing",
4241
+ actor: personActor
4242
+ };
4243
+ }
4244
+
4245
+ function assertRoleConstrainedAssignmentResource(args) {
4246
+ if (hasRoleConstrainedAssignments(args.definition) && args.workflowResource.type !== "dataset") throw new Error(`${args.context}: definition "${args.definition.name}" uses role-constrained assignment fields, which require a dataset workflow resource; received resource type "${args.workflowResource.type}"`);
4247
+ }
4248
+
4249
+ const EXECUTION_KINDS = {
4250
+ interactive: "interactive",
4251
+ server: "server",
4252
+ cli: "cli",
4253
+ mcp: "mcp",
4254
+ drainer: "drainer",
4255
+ script: "script",
4256
+ test: "test",
4257
+ studio: "studio",
4258
+ sdkApp: "sdk-app"
4259
+ };
4260
+
4261
+ function detectRuntime() {
4262
+ const g = globalThis;
4263
+ if (g.Deno !== void 0) return "deno";
4264
+ if (g.Bun !== void 0) return "bun";
4265
+ if (g.EdgeRuntime !== void 0) return "edge";
4266
+ if (g.window !== void 0 && g.document !== void 0) return "browser";
4267
+ if (g.WorkerGlobalScope !== void 0 && g.self !== void 0) return "worker";
4268
+ const proc = g.process;
4269
+ return typeof proc == "object" && proc !== null && typeof proc.versions?.node == "string" ? "node" : "unknown";
4270
+ }
4271
+
4272
+ let inferredRuntime;
4273
+
4274
+ function resolveExecutionContext(declared) {
4275
+ return inferredRuntime ??= detectRuntime(), {
4276
+ runtime: inferredRuntime,
4277
+ ...declared?.kind !== void 0 ? {
3820
4278
  kind: declared.kind
3821
4279
  } : {},
3822
4280
  ...declared?.id !== void 0 ? {
@@ -4105,10 +4563,8 @@ function assertRequiredInputProvided({entryDefs: entryDefs, initialFields: initi
4105
4563
  });
4106
4564
  }
4107
4565
 
4108
- const ARRAY_FIELD_TYPES = /* @__PURE__ */ new Set([ "doc.refs", "array", "assignees" ]);
4109
-
4110
4566
  function defaultEntryValue(entryType) {
4111
- return ARRAY_FIELD_TYPES.has(entryType) ? [] : null;
4567
+ return invariants.isAlwaysArrayFieldKind(entryType) ? [] : null;
4112
4568
  }
4113
4569
 
4114
4570
  function resolveInputValue({entry: entry, initialFields: initialFields, defaultValue: defaultValue}) {
@@ -4213,6 +4669,9 @@ function buildResolvedEntry({entry: entry, value: value, _key: _key, now: now})
4213
4669
  ...entry.validation !== void 0 ? {
4214
4670
  validation: entry.validation
4215
4671
  } : {},
4672
+ ...entry.roles !== void 0 ? {
4673
+ roles: entry.roles
4674
+ } : {},
4216
4675
  value: value,
4217
4676
  ...entry.initialValue?.type === "query" ? {
4218
4677
  resolvedAt: now
@@ -4244,7 +4703,10 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
4244
4703
  fields: entry.fields,
4245
4704
  of: entry.of,
4246
4705
  options: entry.options,
4247
- validation: entry.validation
4706
+ validation: entry.validation,
4707
+ roles: entry.roles,
4708
+ memberRoles: ctx.memberRoles,
4709
+ roleAliases: ctx.roleAliases
4248
4710
  });
4249
4711
  return "issues" in check ? (ctx.recordDiscard?.({
4250
4712
  field: entry.name,
@@ -4269,7 +4731,10 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
4269
4731
  fields: entry.fields,
4270
4732
  of: entry.of,
4271
4733
  options: entry.options,
4272
- validation: entry.validation
4734
+ validation: entry.validation,
4735
+ roles: entry.roles,
4736
+ memberRoles: ctx.memberRoles,
4737
+ roleAliases: ctx.roleAliases
4273
4738
  });
4274
4739
  return entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
4275
4740
  entryType: entry.type,
@@ -4363,7 +4828,7 @@ function coerceToGdr(raw, workflowResource) {
4363
4828
  } : null;
4364
4829
  }
4365
4830
 
4366
- const NonEmpty = invariants.NonEmptyString, UnknownRecord = v__namespace.record(v__namespace.string(), v__namespace.unknown()), PersistedChoiceOptionsSchema = v__namespace.looseObject({
4831
+ const NonEmpty = invariants.NonEmptyString, PersistedAssignmentRolesSchema = v__namespace.pipe(v__namespace.array(invariants.NonEmptyString), v__namespace.minLength(1, "assignment roles must not be empty")), UnknownRecord = v__namespace.record(v__namespace.string(), v__namespace.unknown()), PersistedChoiceOptionsSchema = v__namespace.looseObject({
4367
4832
  list: v__namespace.array(v__namespace.looseObject({
4368
4833
  title: v__namespace.string(),
4369
4834
  value: v__namespace.union([ v__namespace.string(), v__namespace.number() ])
@@ -4378,6 +4843,7 @@ const NonEmpty = invariants.NonEmptyString, UnknownRecord = v__namespace.record(
4378
4843
  description: v__namespace.optional(v__namespace.string()),
4379
4844
  options: v__namespace.optional(PersistedChoiceOptionsSchema),
4380
4845
  validation: v__namespace.optional(PersistedScalarValidationSchema),
4846
+ roles: v__namespace.optional(PersistedAssignmentRolesSchema),
4381
4847
  fields: v__namespace.optional(v__namespace.array(PersistedFieldShapeSchema)),
4382
4848
  of: v__namespace.optional(v__namespace.array(PersistedFieldShapeSchema))
4383
4849
  }))), ExecutionContextSchema = invariants.tolerantObject()({
@@ -4423,7 +4889,13 @@ const OptionalRefTypes = v__namespace.exactOptional(v__namespace.array(v__namesp
4423
4889
  })), v__namespace.looseObject(invariants.tolerantEntries()({
4424
4890
  ...fieldArm("subject", invariants.fieldValueSchemas.subject),
4425
4891
  types: OptionalRefTypes
4426
- })), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("release.ref", invariants.fieldValueSchemas["release.ref"]))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("string", invariants.fieldValueSchemas.string))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("text", invariants.fieldValueSchemas.text))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("number", invariants.fieldValueSchemas.number))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("progress", invariants.fieldValueSchemas.progress))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("boolean", invariants.fieldValueSchemas.boolean))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("date", invariants.fieldValueSchemas.date))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("dueDate", invariants.fieldValueSchemas.dueDate))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("datetime", invariants.fieldValueSchemas.datetime))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("dueDatetime", invariants.fieldValueSchemas.dueDatetime))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("url", invariants.fieldValueSchemas.url))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("actor", invariants.fieldValueSchemas.actor))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("assignee", invariants.fieldValueSchemas.assignee))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("assignees", invariants.fieldValueSchemas.assignees))), v__namespace.looseObject(invariants.tolerantEntries()({
4892
+ })), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("release.ref", invariants.fieldValueSchemas["release.ref"]))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("string", invariants.fieldValueSchemas.string))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("text", invariants.fieldValueSchemas.text))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("number", invariants.fieldValueSchemas.number))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("progress", invariants.fieldValueSchemas.progress))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("boolean", invariants.fieldValueSchemas.boolean))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("date", invariants.fieldValueSchemas.date))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("dueDate", invariants.fieldValueSchemas.dueDate))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("datetime", invariants.fieldValueSchemas.datetime))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("dueDatetime", invariants.fieldValueSchemas.dueDatetime))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("url", invariants.fieldValueSchemas.url))), v__namespace.looseObject(invariants.tolerantEntries()(fieldArm("actor", invariants.fieldValueSchemas.actor))), v__namespace.looseObject(invariants.tolerantEntries()({
4893
+ ...fieldArm("assignee", invariants.fieldValueSchemas.assignee),
4894
+ roles: v__namespace.exactOptional(PersistedAssignmentRolesSchema)
4895
+ })), v__namespace.looseObject(invariants.tolerantEntries()({
4896
+ ...fieldArm("assignees", invariants.fieldValueSchemas.assignees),
4897
+ roles: v__namespace.exactOptional(PersistedAssignmentRolesSchema)
4898
+ })), v__namespace.looseObject(invariants.tolerantEntries()({
4427
4899
  ...fieldArm("object", v__namespace.union([ v__namespace.null(), UnknownRecord ])),
4428
4900
  fields: v__namespace.array(PersistedFieldShapeSchema)
4429
4901
  })), v__namespace.looseObject(invariants.tolerantEntries()({
@@ -4674,6 +5146,37 @@ function parseInstanceDocument(doc) {
4674
5146
  });
4675
5147
  }
4676
5148
 
5149
+ const WorkflowInstancePreviewEffectSchema = invariants.tolerantObject()({
5150
+ name: NonEmpty,
5151
+ title: v__namespace.exactOptional(v__namespace.string())
5152
+ }), WorkflowInstancePreviewSchema = v__namespace.looseObject(invariants.tolerantEntries()({
5153
+ _id: NonEmpty,
5154
+ _type: v__namespace.literal(WORKFLOW_INSTANCE_TYPE),
5155
+ modelVersion: v__namespace.exactOptional(v__namespace.number()),
5156
+ minReaderModel: v__namespace.exactOptional(v__namespace.number()),
5157
+ workflowResource: WorkflowResourceSchema,
5158
+ definition: NonEmpty,
5159
+ pinnedVersion: v__namespace.number(),
5160
+ fields: v__namespace.array(ResolvedFieldEntrySchema),
5161
+ ancestors: v__namespace.array(invariants.GdrShape),
5162
+ perspective: v__namespace.exactOptional(WorkflowPerspectiveSchema),
5163
+ currentStage: NonEmpty,
5164
+ stages: v__namespace.array(StageEntrySchema),
5165
+ claimedEffects: v__namespace.array(WorkflowInstancePreviewEffectSchema),
5166
+ failedEffects: v__namespace.array(WorkflowInstancePreviewEffectSchema),
5167
+ startedAt: invariants.IsoTimestamp,
5168
+ completedAt: v__namespace.exactOptional(invariants.IsoTimestamp),
5169
+ abortedAt: v__namespace.exactOptional(invariants.IsoTimestamp)
5170
+ }));
5171
+
5172
+ function parseInstancePreviewDocument(doc) {
5173
+ return invariants.parsePersistedDoc({
5174
+ schema: WorkflowInstancePreviewSchema,
5175
+ doc: doc,
5176
+ docType: WORKFLOW_INSTANCE_TYPE
5177
+ });
5178
+ }
5179
+
4677
5180
  const SYNC_COMMIT = {
4678
5181
  visibility: "sync"
4679
5182
  }, ENGINE_API_VERSION = "2026-04-29", REQUEST_TAG = {
@@ -4709,7 +5212,9 @@ const SYNC_COMMIT = {
4709
5212
  effect: "workflow.effect",
4710
5213
  verifyDefinitions: "workflow.verify-definitions",
4711
5214
  accessResolveActor: "workflow.access.resolve-actor",
4712
- accessGrants: "workflow.access.grants"
5215
+ accessGrants: "workflow.access.grants",
5216
+ accessResolveOrg: "workflow.access.resolve-org",
5217
+ accessAttributes: "workflow.access.attributes"
4713
5218
  }, RAW_PATCH = /* @__PURE__ */ Symbol("workflow-engine.raw-patch");
4714
5219
 
4715
5220
  function unwrapPatch(patch) {
@@ -5119,6 +5624,40 @@ function readInstanceDoc(doc) {
5119
5624
  return parseInstanceDocument(assertReadableModel(doc));
5120
5625
  }
5121
5626
 
5627
+ function readInstancePreviewDoc(doc) {
5628
+ return hasDocumentId(doc) ? parseInstancePreviewDocument(withoutProjectedNulls(assertReadableModel(doc))) : parseInstancePreviewDocument(doc);
5629
+ }
5630
+
5631
+ function hasDocumentId(doc) {
5632
+ return typeof doc == "object" && doc !== null && typeof doc._id == "string";
5633
+ }
5634
+
5635
+ function withoutProjectedNulls(doc) {
5636
+ const row = withoutNullMembers(doc);
5637
+ if (typeof row != "object" || row === null) return row;
5638
+ const out = {
5639
+ ...row
5640
+ };
5641
+ return Array.isArray(out.stages) && (out.stages = out.stages.map(withoutStageEntryNulls)),
5642
+ Array.isArray(out.claimedEffects) && (out.claimedEffects = out.claimedEffects.map(withoutNullMembers)),
5643
+ Array.isArray(out.failedEffects) && (out.failedEffects = out.failedEffects.map(withoutNullMembers)),
5644
+ out;
5645
+ }
5646
+
5647
+ function withoutStageEntryNulls(entry) {
5648
+ const stage = withoutNullMembers(entry);
5649
+ if (typeof stage != "object" || stage === null) return stage;
5650
+ const out = {
5651
+ ...stage
5652
+ };
5653
+ return Array.isArray(out.activities) && (out.activities = out.activities.map(withoutNullMembers)),
5654
+ out;
5655
+ }
5656
+
5657
+ function withoutNullMembers(value) {
5658
+ return value === null || typeof value != "object" || Array.isArray(value) ? value : Object.fromEntries(Object.entries(value).filter(([, member]) => member !== null));
5659
+ }
5660
+
5122
5661
  async function reload({client: client, instanceId: instanceId, tag: tag}) {
5123
5662
  const doc = await getInstanceDocument(client, instanceId);
5124
5663
  if (!doc) throw new invariants.InstanceNotFoundError({
@@ -5365,7 +5904,8 @@ function loadCallContext({client: client, instanceId: instanceId, options: optio
5365
5904
  } : {},
5366
5905
  ...options.telemetry ? {
5367
5906
  telemetry: options.telemetry
5368
- } : {}
5907
+ } : {},
5908
+ memberRolesLoader: options.memberRolesLoader
5369
5909
  }
5370
5910
  });
5371
5911
  }
@@ -5410,6 +5950,7 @@ async function loadContext({client: client, instanceId: instanceId, options: opt
5410
5950
  ...options.telemetry !== void 0 ? {
5411
5951
  telemetry: options.telemetry
5412
5952
  } : {},
5953
+ memberRolesLoader: options.memberRolesLoader,
5413
5954
  ...options.overlay !== void 0 ? {
5414
5955
  overlay: options.overlay
5415
5956
  } : {},
@@ -5445,7 +5986,10 @@ async function ctxEvaluateCondition({ctx: ctx, condition: condition, opts: opts}
5445
5986
  }
5446
5987
 
5447
5988
  async function buildEngineContext(args) {
5448
- const {client: client, clientForGdr: clientForGdr, refSurface: refSurface, instance: instance, definition: definition} = args, clock = args.clock ?? wallClock;
5989
+ const {client: client, clientForGdr: clientForGdr, refSurface: refSurface, instance: instance, definition: definition} = args, clock = args.clock ?? wallClock, memberRolesLoader = args.memberRolesLoader, memberRoles = await memberRolesLoader({
5990
+ workflowResource: instance.workflowResource,
5991
+ definition: definition
5992
+ });
5449
5993
  return {
5450
5994
  client: client,
5451
5995
  clientForGdr: clientForGdr,
@@ -5462,6 +6006,10 @@ async function buildEngineContext(args) {
5462
6006
  } : {},
5463
6007
  instance: instance,
5464
6008
  definition: definition,
6009
+ memberRolesLoader: memberRolesLoader,
6010
+ ...memberRoles !== void 0 ? {
6011
+ memberRoles: memberRoles
6012
+ } : {},
5465
6013
  snapshot: await hydrateSnapshot({
5466
6014
  client: client,
5467
6015
  clientForGdr: clientForGdr,
@@ -5473,6 +6021,62 @@ async function buildEngineContext(args) {
5473
6021
  };
5474
6022
  }
5475
6023
 
6024
+ function createAssignmentMemberRolesLoader(client) {
6025
+ const byProjectId = /* @__PURE__ */ new Map;
6026
+ return async ({workflowResource: workflowResource, definition: definition}) => {
6027
+ if (!hasRoleConstrainedAssignments(definition)) return;
6028
+ assertRoleConstrainedAssignmentResource({
6029
+ context: "workflow",
6030
+ definition: definition,
6031
+ workflowResource: workflowResource
6032
+ });
6033
+ const projectId = invariants.datasetResourceParts(workflowResource.id).projectId;
6034
+ let pending = byProjectId.get(projectId);
6035
+ return pending === void 0 && (pending = client.request === void 0 ? Promise.reject(new Error(`Definition "${definition.name}" has role-constrained assignments, which require project-directory access for project "${projectId}"; the client cannot issue requests`)) : loadProjectMemberRoles({
6036
+ request: client.request,
6037
+ projectId: projectId
6038
+ }), byProjectId.set(projectId, pending)), pending;
6039
+ };
6040
+ }
6041
+
6042
+ async function loadProjectMemberRoles(args) {
6043
+ const {request: request, projectId: projectId} = args, snapshot = await requestProjectMemberDirectory({
6044
+ request: request,
6045
+ projectId: projectId
6046
+ }), rolesByProjectId = projectMemberRoles({
6047
+ members: snapshot.members,
6048
+ projectId: projectId
6049
+ });
6050
+ return globalizeMemberRoles({
6051
+ snapshot: snapshot,
6052
+ rolesByProjectId: rolesByProjectId
6053
+ });
6054
+ }
6055
+
6056
+ function globalizeMemberRoles(args) {
6057
+ const {snapshot: snapshot, rolesByProjectId: rolesByProjectId} = args, humanIds = [ ...rolesByProjectId.keys() ].filter(id => invariants.classifyPrincipalId(id).namespace === "project");
6058
+ for (const {globalId: globalId, memberId: memberId} of snapshot.globalUsers) {
6059
+ const roles = rolesByProjectId.get(memberId);
6060
+ roles !== void 0 && rolesByProjectId.set(globalId, roles);
6061
+ }
6062
+ for (const id of humanIds) rolesByProjectId.delete(id);
6063
+ return Object.fromEntries(rolesByProjectId);
6064
+ }
6065
+
6066
+ function projectMemberRoles(args) {
6067
+ const result = /* @__PURE__ */ new Map;
6068
+ for (const member of args.members) {
6069
+ const {id: id, roles: roles} = member;
6070
+ if (!Array.isArray(roles) || !roles.every(isProjectMemberRole)) throw new Error(`Project member role directory response is invalid for project "${args.projectId}" and member "${id}"`);
6071
+ result.set(id, roles.map(role => role.name));
6072
+ }
6073
+ return result;
6074
+ }
6075
+
6076
+ function isProjectMemberRole(role) {
6077
+ return invariants.isRecord(role) && typeof role.name == "string";
6078
+ }
6079
+
5476
6080
  function findStage(definition, stageName) {
5477
6081
  const stage = definition.stages.find(s => s.name === stageName);
5478
6082
  if (stage === void 0) throw new Error(`Stage "${stageName}" not found in definition ${definition.name}`);
@@ -5492,6 +6096,8 @@ async function resolveStageFieldEntries(args) {
5492
6096
  tag: instance.tag,
5493
6097
  stageName: stage.name,
5494
6098
  workflowResource: instance.workflowResource,
6099
+ memberRoles: args.memberRoles,
6100
+ roleAliases: args.roleAliases,
5495
6101
  workflowFields: instance.fields ?? [],
5496
6102
  ...instance.perspective !== void 0 ? {
5497
6103
  perspective: instance.perspective
@@ -5518,6 +6124,8 @@ async function resolveActivityFieldEntries(args) {
5518
6124
  stageName: stage.name,
5519
6125
  workflowResource: instance.workflowResource,
5520
6126
  activityName: activity.name,
6127
+ memberRoles: args.memberRoles,
6128
+ roleAliases: args.roleAliases,
5521
6129
  workflowFields: instance.fields ?? [],
5522
6130
  ...instance.perspective !== void 0 ? {
5523
6131
  perspective: instance.perspective
@@ -5673,172 +6281,7 @@ function resolveMetadata(metadata, ctx) {
5673
6281
  return out;
5674
6282
  }
5675
6283
 
5676
- function userLoginProvider(user) {
5677
- return user?.provider ?? user?.loginProvider;
5678
- }
5679
-
5680
- function isRecord(value) {
5681
- return typeof value == "object" && value !== null && !Array.isArray(value);
5682
- }
5683
-
5684
- function optionalString(value) {
5685
- return value === void 0 || typeof value == "string";
5686
- }
5687
-
5688
- function isClientProjectUser(value) {
5689
- return isRecord(value) && typeof value.id == "string" && optionalString(value.sanityUserId) && optionalString(value.displayName) && optionalString(value.email) && (value.imageUrl === null || optionalString(value.imageUrl)) && optionalString(value.provider) && optionalString(value.loginProvider);
5690
- }
5691
-
5692
- function objectProperty(value, property) {
5693
- if (!isRecord(value)) return;
5694
- const propertyValue = value[property];
5695
- return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
5696
- }
5697
-
5698
- function stringProperty(value, property) {
5699
- const propertyValue = Reflect.get(value, property);
5700
- return typeof propertyValue == "string" ? propertyValue : void 0;
5701
- }
5702
-
5703
- function apiErrorType(error) {
5704
- const response = objectProperty(error, "response"), body = objectProperty(response, "body");
5705
- if (!body) return;
5706
- const nestedError = objectProperty(body, "error");
5707
- return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
5708
- }
5709
-
5710
- function isProjectUserNotFoundError(error) {
5711
- return apiErrorType(error) === "projectUserNotFoundError";
5712
- }
5713
-
5714
- const memberJoinCache = /* @__PURE__ */ new WeakMap;
5715
-
5716
- function joinCacheFor(client) {
5717
- let cache = memberJoinCache.get(client);
5718
- return cache === void 0 && (cache = /* @__PURE__ */ new Map, memberJoinCache.set(client, cache)),
5719
- cache;
5720
- }
5721
-
5722
- async function projectMemberIds(request, projectId) {
5723
- const project = await request({
5724
- uri: `/projects/${encodeURIComponent(projectId)}`
5725
- }), members = isRecord(project) ? project.members : void 0;
5726
- return Array.isArray(members) ? members.map(member => isRecord(member) ? member.id : void 0).filter(id => typeof id == "string") : [];
5727
- }
5728
-
5729
- async function resolveGlobalProjectUser(args) {
5730
- const {client: client, request: request, projectId: projectId, id: id} = args, cache = joinCacheFor(client), key = `${projectId}:${id}`, hit = cache.get(key);
5731
- if (hit !== void 0) return hit === null ? {
5732
- status: "missing"
5733
- } : {
5734
- status: "resolved",
5735
- user: hit
5736
- };
5737
- try {
5738
- await ingestMemberJoin({
5739
- request: request,
5740
- projectId: projectId,
5741
- cache: cache
5742
- });
5743
- const found = cache.get(key);
5744
- return found != null ? {
5745
- status: "resolved",
5746
- user: found
5747
- } : (cache.set(key, null), {
5748
- status: "missing"
5749
- });
5750
- } catch (cause) {
5751
- return {
5752
- status: "inaccessible",
5753
- cause: cause
5754
- };
5755
- }
5756
- }
5757
-
5758
- async function ingestMemberJoin(args) {
5759
- const {request: request, projectId: projectId, cache: cache} = args, memberIds = await projectMemberIds(request, projectId);
5760
- if (memberIds.length === 0) return;
5761
- const requested = new Set(memberIds), response = await request({
5762
- uri: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
5763
- });
5764
- for (const row of Array.isArray(response) ? response : [ response ]) {
5765
- if (!isClientProjectUser(row) || !requested.has(row.id)) continue;
5766
- const globalId = invariants.directoryBridgeId(row.sanityUserId);
5767
- globalId !== void 0 && cache.set(`${projectId}:${globalId}`, row);
5768
- }
5769
- }
5770
-
5771
- function clientProjectUserDirectory(client, projectId) {
5772
- return {
5773
- findById: async id => {
5774
- if (!client.request) return {
5775
- status: "inaccessible",
5776
- cause: new Error("Project-user resolution requires WorkflowClient.request")
5777
- };
5778
- if (invariants.classifyPrincipalId(id).namespace === "global") return resolveGlobalProjectUser({
5779
- client: client,
5780
- request: client.request,
5781
- projectId: projectId,
5782
- id: id
5783
- });
5784
- try {
5785
- const response = await client.request({
5786
- uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
5787
- }), candidate = Array.isArray(response) ? response[0] : response;
5788
- return candidate == null ? {
5789
- status: "missing"
5790
- } : isClientProjectUser(candidate) ? candidate.id !== id ? {
5791
- status: "inaccessible",
5792
- cause: new Error(`Project-user response answered id "${candidate.id}", not the requested "${id}"`)
5793
- } : {
5794
- status: "resolved",
5795
- user: candidate
5796
- } : {
5797
- status: "inaccessible",
5798
- cause: new Error("Project-user response had an invalid shape")
5799
- };
5800
- } catch (cause) {
5801
- return isProjectUserNotFoundError(cause) ? {
5802
- status: "missing"
5803
- } : {
5804
- status: "inaccessible",
5805
- cause: cause
5806
- };
5807
- }
5808
- }
5809
- };
5810
- }
5811
-
5812
- function resolveClientActor(client, args) {
5813
- return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
5814
- }
5815
-
5816
- async function resolveActor(directory, actor) {
5817
- if (actor.kind !== "person") return {
5818
- status: "not-person",
5819
- actor: actor
5820
- };
5821
- const personActor = {
5822
- ...actor,
5823
- kind: "person"
5824
- }, lookup = await directory.findById(personActor.id);
5825
- return lookup.status === "resolved" ? {
5826
- status: "resolved",
5827
- actor: personActor,
5828
- user: lookup.user
5829
- } : lookup.status === "inaccessible" ? {
5830
- status: "inaccessible",
5831
- actor: personActor,
5832
- ...lookup.cause === void 0 ? {} : {
5833
- cause: lookup.cause
5834
- }
5835
- } : {
5836
- status: "missing",
5837
- actor: personActor
5838
- };
5839
- }
5840
-
5841
- const IDENTITY_KINDS = /* @__PURE__ */ new Set([ "actor", "assignee", "assignees" ]);
6284
+ const IDENTITY_KINDS = /* @__PURE__ */ new Set([ "actor", "assignee", "assignees" ]);
5842
6285
 
5843
6286
  async function localizeGuardMetadata(args) {
5844
6287
  const {doc: doc, guard: guard, client: client, instance: instance, definition: definition} = args;
@@ -6483,6 +6926,8 @@ async function buildActivityEntry({ctx: ctx, stage: stage, activity: activity, r
6483
6926
  activity: activity,
6484
6927
  now: ctx.now,
6485
6928
  refSurface: ctx.refSurface,
6929
+ memberRoles: ctx.memberRoles,
6930
+ roleAliases: ctx.definition.roleAliases,
6486
6931
  ...recordDiscard !== void 0 ? {
6487
6932
  recordDiscard: recordDiscard
6488
6933
  } : {}
@@ -6685,7 +7130,8 @@ async function spawnRow({ctx: ctx, mutation: mutation, activity: activity, actio
6685
7130
  context: context,
6686
7131
  actor: actor,
6687
7132
  now: now,
6688
- refSurface: ctx.refSurface
7133
+ refSurface: ctx.refSurface,
7134
+ memberRolesLoader: ctx.memberRolesLoader
6689
7135
  });
6690
7136
  mutation.pendingCreates.push({
6691
7137
  body: body,
@@ -6908,13 +7354,16 @@ async function resolveDefinitionRef({client: client, ref: ref, tag: tag}) {
6908
7354
  }
6909
7355
 
6910
7356
  async function prepareChildInstance(args) {
6911
- const {client: client, parent: parent, definition: definition, initialFields: initialFields, context: context, actor: actor, now: now, refSurface: refSurface} = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = instanceDocId(childTag), childRef = {
7357
+ const {client: client, parent: parent, definition: definition, initialFields: initialFields, context: context, actor: actor, now: now, refSurface: refSurface, memberRolesLoader: memberRolesLoader} = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = instanceDocId(childTag), childRef = {
6912
7358
  id: invariants.gdrFromResource(workflowResource, childDocId),
6913
7359
  type: WORKFLOW_INSTANCE_TYPE
6914
7360
  }, ancestors = [ ...parent.ancestors, {
6915
7361
  id: invariants.selfGdr(parent),
6916
7362
  type: WORKFLOW_INSTANCE_TYPE
6917
- } ], inheritedPerspective = parent.perspective, fieldDiscards = [], childFields = await resolveDeclaredFields({
7363
+ } ], inheritedPerspective = parent.perspective, fieldDiscards = [], memberRoles = await memberRolesLoader({
7364
+ workflowResource: workflowResource,
7365
+ definition: definition
7366
+ }), childFields = await resolveDeclaredFields({
6918
7367
  entryDefs: definition.fields,
6919
7368
  initialFields: initialFields,
6920
7369
  ctx: {
@@ -6925,6 +7374,8 @@ async function prepareChildInstance(args) {
6925
7374
  workflowResource: workflowResource,
6926
7375
  definitionName: definition.name,
6927
7376
  refSurface: refSurface,
7377
+ memberRoles: memberRoles,
7378
+ roleAliases: definition.roleAliases,
6928
7379
  inputProvenance: "definition",
6929
7380
  ...inheritedPerspective !== void 0 ? {
6930
7381
  perspective: inheritedPerspective
@@ -6995,6 +7446,8 @@ async function enterStage({ctx: ctx, mutation: mutation, nextStage: nextStage, a
6995
7446
  stage: nextStage,
6996
7447
  now: at,
6997
7448
  refSurface: ctx.refSurface,
7449
+ memberRoles: ctx.memberRoles,
7450
+ roleAliases: ctx.definition.roleAliases,
6998
7451
  recordDiscard: recordFieldDiscards({
6999
7452
  target: mutation.history,
7000
7453
  scope: "stage",
@@ -7180,42 +7633,449 @@ function aclPathForResource(res) {
7180
7633
  const {projectId: projectId, dataset: dataset} = invariants.datasetResourceParts(res.id);
7181
7634
  return `/projects/${projectId}/datasets/${dataset}/acl`;
7182
7635
  }
7183
- if (res.type === "canvas") return `/canvases/${res.id}/acl`;
7636
+ if (res.type === "canvas") return `/canvases/${res.id}/acl`;
7637
+ }
7638
+
7639
+ async function fetchGrants(args) {
7640
+ const {client: client, resourcePath: resourcePath, signal: signal} = args;
7641
+ return client.request({
7642
+ url: resourcePath,
7643
+ tag: REQUEST_TAG.accessGrants,
7644
+ ...signal ? {
7645
+ signal: signal
7646
+ } : {}
7647
+ });
7648
+ }
7649
+
7650
+ async function advisoryCanForCall(args) {
7651
+ const actor = args.options?.actor;
7652
+ if (!(args.options?.grants === void 0 || actor === void 0)) return advisoryCan({
7653
+ instance: args.instance,
7654
+ identity: invariants.lakePrincipalId({
7655
+ actor: actor,
7656
+ localPrincipalId: args.options.localPrincipalId
7657
+ }),
7658
+ grants: args.options.grants
7659
+ });
7660
+ }
7661
+
7662
+ async function advisoryCan({instance: instance, identity: identity, grants: grants}) {
7663
+ if (grants === void 0) return;
7664
+ const can = {};
7665
+ for (const permission of invariants.DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
7666
+ document: instance,
7667
+ grants: grants,
7668
+ permission: permission,
7669
+ userId: identity
7670
+ });
7671
+ return can;
7672
+ }
7673
+
7674
+ const DANGEROUS_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([ "__proto__", "constructor", "prototype" ]);
7675
+
7676
+ function attributeEntry(row) {
7677
+ if (!(!invariants.isRecord(row) || typeof row.key != "string" || row.key.length === 0) && !DANGEROUS_ATTRIBUTE_KEYS.has(row.key) && !(!("activeValue" in row) || row.activeValue === void 0)) return [ row.key, row.activeValue ];
7678
+ }
7679
+
7680
+ function normalizeUserAttributes(response) {
7681
+ if (!invariants.isRecord(response)) return;
7682
+ const rows = response.attributes;
7683
+ if (!Array.isArray(rows)) return;
7684
+ const out = {};
7685
+ for (const row of rows) {
7686
+ const entry = attributeEntry(row);
7687
+ entry !== void 0 && (out[entry[0]] = entry[1]);
7688
+ }
7689
+ return out;
7690
+ }
7691
+
7692
+ const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap, orgIdCache = /* @__PURE__ */ new WeakMap, attributesCache = /* @__PURE__ */ new WeakMap;
7693
+
7694
+ async function resolveAccess(taggedClient, args = {}) {
7695
+ const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
7696
+ if (requestFn === void 0) throw new invariants.ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({ uri: '/users/me' })`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
7697
+ const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
7698
+ client: client,
7699
+ requestFn: requestFn,
7700
+ resourcePath: args.grantsFromPath
7701
+ }) : Promise.resolve(void 0), [identity, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
7702
+ if (identity === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
7703
+ return {
7704
+ actor: identity.actor,
7705
+ ...identity.localPrincipalId !== void 0 ? {
7706
+ localPrincipalId: identity.localPrincipalId
7707
+ } : {},
7708
+ ...grants !== void 0 ? {
7709
+ grants: grants
7710
+ } : {}
7711
+ };
7712
+ }
7713
+
7714
+ async function resolveUserAttributes(taggedClient) {
7715
+ const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
7716
+ if (requestFn !== void 0) return cachedAttributes({
7717
+ client: client,
7718
+ requestFn: requestFn
7719
+ });
7720
+ }
7721
+
7722
+ function cachedActor(client, requestFn) {
7723
+ const cached = actorCache.get(client);
7724
+ if (cached !== void 0) return cached;
7725
+ const pending = fetchActor(client, requestFn).catch(err => {
7726
+ throw actorCache.get(client) === pending && actorCache.delete(client), err;
7727
+ });
7728
+ return actorCache.set(client, pending), pending;
7729
+ }
7730
+
7731
+ function grantsForClientPath(taggedClient, resourcePath) {
7732
+ const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
7733
+ return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
7734
+ client: client,
7735
+ requestFn: requestFn,
7736
+ resourcePath: resourcePath
7737
+ });
7738
+ }
7739
+
7740
+ function lazyRequest(client) {
7741
+ return client.request === void 0 ? void 0 : opts => client.request(opts);
7742
+ }
7743
+
7744
+ function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
7745
+ return cachedByKey({
7746
+ store: grantsCache,
7747
+ client: client,
7748
+ key: resourcePath,
7749
+ load: () => fetchGrantsCached(requestFn, resourcePath)
7750
+ });
7751
+ }
7752
+
7753
+ function cachedByKey(args) {
7754
+ let byKey = args.store.get(args.client);
7755
+ byKey === void 0 && (byKey = /* @__PURE__ */ new Map, args.store.set(args.client, byKey));
7756
+ const cached = byKey.get(args.key);
7757
+ if (cached !== void 0) return cached;
7758
+ const pending = args.evictOnRejection ? args.load().catch(err => {
7759
+ throw byKey.get(args.key) === pending && byKey.delete(args.key), err;
7760
+ }) : args.load();
7761
+ return byKey.set(args.key, pending), pending;
7762
+ }
7763
+
7764
+ async function fetchActor(client, requestFn) {
7765
+ const [resourceUser, globalUser] = await Promise.all([ fetchCurrentUser(requestFn, "the workflow resource host"), fetchGlobalUser(client) ]), resourceId = usableId(resourceUser), globalHostId = usableId(globalUser.user), carriedId = invariants.firstCarriedGlobalId([ globalHostId, resourceId ]), sessionId = carriedId ?? globalHostId ?? resourceId;
7766
+ if (sessionId === void 0) return;
7767
+ const bridge = carriedId === void 0 ? await bridgeProjectPrincipal({
7768
+ client: client,
7769
+ sessionId: sessionId,
7770
+ resourceId: resourceId
7771
+ }) : NO_BRIDGE, id = bridge.status === "resolved" ? bridge.globalId : sessionId;
7772
+ refuseProjectScopedActor({
7773
+ id: id,
7774
+ globalUser: globalUser,
7775
+ globalHostId: globalHostId,
7776
+ bridgeFailure: bridge.status === "unavailable" ? bridge : void 0
7777
+ });
7778
+ const roleNames = roleNamesFor(resourceUser, globalUser);
7779
+ return {
7780
+ actor: {
7781
+ kind: "person",
7782
+ id: id,
7783
+ ...roleNames.length > 0 ? {
7784
+ roles: roleNames
7785
+ } : {}
7786
+ },
7787
+ ...resourceId !== void 0 && resourceId !== id ? {
7788
+ localPrincipalId: resourceId
7789
+ } : {}
7790
+ };
7791
+ }
7792
+
7793
+ function roleNamesFor(resourceUser, globalUser) {
7794
+ return (resourceUser?.roles?.length ? resourceUser : globalUser.user)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
7795
+ }
7796
+
7797
+ const NO_BRIDGE = {
7798
+ status: "not-applicable"
7799
+ };
7800
+
7801
+ function globalRouteReason(globalUser, globalHostId) {
7802
+ return "reason" in globalUser ? globalUser.reason : globalHostId === void 0 ? "no record" : `the global host answered with project-scoped principal "${globalHostId}"`;
7803
+ }
7804
+
7805
+ async function bridgeProjectPrincipal(args) {
7806
+ const {client: client, sessionId: sessionId, resourceId: resourceId} = args;
7807
+ return invariants.classifyPrincipalId(sessionId).namespace !== "project" ? NO_BRIDGE : resourceId === void 0 || invariants.classifyPrincipalId(resourceId).namespace !== "project" ? {
7808
+ status: "unavailable",
7809
+ reason: "no project-scoped principal from the resource host to address the directory with"
7810
+ } : directoryGlobalId({
7811
+ client: client,
7812
+ principalId: resourceId
7813
+ });
7814
+ }
7815
+
7816
+ async function directoryGlobalId(args) {
7817
+ const {client: client, principalId: principalId} = args, projectId = projectIdOf(client);
7818
+ if (projectId === void 0) return {
7819
+ status: "unavailable",
7820
+ reason: "the client reports no projectId, so its user directory has no address"
7821
+ };
7822
+ const lookup = await clientProjectUserDirectory(withRequestTag(client, REQUEST_TAG.accessResolveActor), projectId).findById(principalId);
7823
+ if (lookup.status !== "resolved") return {
7824
+ status: "unavailable",
7825
+ reason: `project "${projectId}"'s user directory reported the principal ${lookup.status}`,
7826
+ ...lookup.status === "inaccessible" && lookup.cause !== void 0 ? {
7827
+ cause: lookup.cause
7828
+ } : {}
7829
+ };
7830
+ const globalId = invariants.directoryBridgeId(lookup.user.sanityUserId);
7831
+ return globalId === void 0 ? {
7832
+ status: "unavailable",
7833
+ reason: `project "${projectId}"'s user directory row for the principal carries no account-global sanityUserId`
7834
+ } : {
7835
+ status: "resolved",
7836
+ globalId: globalId
7837
+ };
7838
+ }
7839
+
7840
+ function refuseProjectScopedActor(args) {
7841
+ const {id: id, globalUser: globalUser, globalHostId: globalHostId, bridgeFailure: bridgeFailure} = args;
7842
+ if (invariants.classifyPrincipalId(id).namespace !== "project") return;
7843
+ const routes = [ `The account-global record: ${globalRouteReason(globalUser, globalHostId)}.` ];
7844
+ bridgeFailure !== void 0 && routes.push(`The project user directory: ${bridgeFailure.reason}.`);
7845
+ const cause = bridgeFailure?.cause ?? ("cause" in globalUser ? globalUser.cause : void 0);
7846
+ throw new Error(`workflow: the caller's identity resolves to the project-scoped principal "${id}" and its account-global id could not be resolved. ${routes.join(" ")} The engine speaks account-global user ids only and never acts as a project-scoped principal.`, cause === void 0 ? void 0 : {
7847
+ cause: cause
7848
+ });
7849
+ }
7850
+
7851
+ function usableId(user) {
7852
+ if (!(!user || typeof user.id != "string" || user.id.length === 0)) return user.id;
7853
+ }
7854
+
7855
+ async function fetchCurrentUser(requestFn, hostDescription) {
7856
+ try {
7857
+ return await requestFn({
7858
+ uri: "/users/me",
7859
+ tag: REQUEST_TAG.accessResolveActor
7860
+ });
7861
+ } catch (err) {
7862
+ throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({ uri: '/users/me' })\`. Check the token/connectivity.`, {
7863
+ cause: err
7864
+ });
7865
+ }
7866
+ }
7867
+
7868
+ async function fetchGlobalUser(client) {
7869
+ const sibling = globalHostRequestOf(client);
7870
+ if ("reason" in sibling) return {
7871
+ user: void 0,
7872
+ reason: sibling.reason,
7873
+ ...sibling.cause !== void 0 ? {
7874
+ cause: sibling.cause
7875
+ } : {}
7876
+ };
7877
+ try {
7878
+ const user = await sibling.request({
7879
+ uri: "/users/me",
7880
+ tag: REQUEST_TAG.accessResolveActor
7881
+ }), id = usableId(user);
7882
+ return sibling.requireGlobalPrincipal && id !== void 0 && invariants.classifyPrincipalId(id).namespace === "project" ? {
7883
+ user: void 0,
7884
+ reason: `global-host /users/me returned project-scoped principal "${id}"; the sibling client is still bound to a project API host`
7885
+ } : {
7886
+ user: user
7887
+ };
7888
+ } catch (err) {
7889
+ return {
7890
+ user: void 0,
7891
+ reason: `global-host /users/me failed: ${invariants.errorMessage(err)}`,
7892
+ cause: err
7893
+ };
7894
+ }
7895
+ }
7896
+
7897
+ function globalHostRequestOf(client) {
7898
+ if (typeof client.withConfig != "function") return {
7899
+ reason: "the client cannot reach the global API host (no withConfig)"
7900
+ };
7901
+ const hostResolution = resolveGlobalApiHost(client);
7902
+ try {
7903
+ const globalClient = client.withConfig({
7904
+ useProjectHostname: !1,
7905
+ apiVersion: ENGINE_API_VERSION,
7906
+ ...hostResolution.apiHost === void 0 ? {} : {
7907
+ apiHost: hostResolution.apiHost
7908
+ }
7909
+ }), request = lazyRequest(globalClient);
7910
+ return request === void 0 ? {
7911
+ reason: "the global-host sibling client cannot issue requests"
7912
+ } : {
7913
+ request: request,
7914
+ requireGlobalPrincipal: hostResolution.requireGlobalPrincipal
7915
+ };
7916
+ } catch (err) {
7917
+ return {
7918
+ reason: `building the global-host sibling client failed: ${invariants.errorMessage(err)}`,
7919
+ cause: err
7920
+ };
7921
+ }
7922
+ }
7923
+
7924
+ function resolveGlobalApiHost(client) {
7925
+ if (typeof client.config != "function") return {
7926
+ requireGlobalPrincipal: !1
7927
+ };
7928
+ const apiHost = client.config().apiHost;
7929
+ if (apiHost === void 0) return {
7930
+ requireGlobalPrincipal: !1
7931
+ };
7932
+ let parsed;
7933
+ try {
7934
+ parsed = new URL(apiHost);
7935
+ } catch {
7936
+ return {
7937
+ requireGlobalPrincipal: !0
7938
+ };
7939
+ }
7940
+ if (/^api\.sanity\.(io|work)$/.test(parsed.hostname)) return {
7941
+ requireGlobalPrincipal: !1
7942
+ };
7943
+ const match = /^[^.]+\.api\.sanity\.(io|work)$/.exec(parsed.hostname);
7944
+ return match === null ? {
7945
+ requireGlobalPrincipal: !0
7946
+ } : (parsed.hostname = `api.sanity.${match[1]}`, {
7947
+ apiHost: parsed.origin,
7948
+ requireGlobalPrincipal: !0
7949
+ });
7950
+ }
7951
+
7952
+ async function fetchGrantsCached(requestFn, resourcePath) {
7953
+ try {
7954
+ return await fetchGrants({
7955
+ client: {
7956
+ request: requestFn
7957
+ },
7958
+ resourcePath: resourcePath
7959
+ });
7960
+ } catch (err) {
7961
+ console.warn(`workflow: failed to fetch grants from "${resourcePath}"; advisory permission reads that depend on them are skipped — a rendered $can stays undefined (conditions referencing it fail closed) and the subject-write forecast omits this resource. The lake still enforces writes. Original error: ${invariants.errorMessage(err)}`);
7962
+ return;
7963
+ }
7964
+ }
7965
+
7966
+ function projectIdOf(client) {
7967
+ if (typeof client.config != "function") return;
7968
+ const projectId = client.config().projectId;
7969
+ return typeof projectId == "string" && projectId.length > 0 ? projectId : void 0;
7970
+ }
7971
+
7972
+ function cachedAttributes(args) {
7973
+ const projectId = projectIdOf(args.client);
7974
+ return projectId === void 0 ? Promise.resolve(void 0) : resolveOrganizationId({
7975
+ client: args.client,
7976
+ requestFn: args.requestFn,
7977
+ projectId: projectId
7978
+ }).then(orgId => {
7979
+ if (orgId !== void 0) return cachedByKey({
7980
+ store: attributesCache,
7981
+ client: args.client,
7982
+ key: orgId,
7983
+ evictOnRejection: !0,
7984
+ load: () => fetchAttributesCached({
7985
+ client: args.client,
7986
+ orgId: orgId
7987
+ })
7988
+ });
7989
+ });
7990
+ }
7991
+
7992
+ async function resolveOrganizationId(args) {
7993
+ const {client: client, requestFn: requestFn, projectId: projectId} = args;
7994
+ return cachedByKey({
7995
+ store: orgIdCache,
7996
+ client: client,
7997
+ key: projectId,
7998
+ evictOnRejection: !0,
7999
+ load: async () => {
8000
+ try {
8001
+ const project = await requestFn({
8002
+ uri: `/projects/${encodeURIComponent(projectId)}`,
8003
+ tag: REQUEST_TAG.accessResolveOrg
8004
+ });
8005
+ if (!invariants.isRecord(project)) return;
8006
+ const orgId = project.organizationId;
8007
+ return typeof orgId == "string" && orgId.length > 0 ? orgId : void 0;
8008
+ } catch (err) {
8009
+ const status = httpStatusOf(err);
8010
+ if (status !== void 0 && EXPECTED_ATTRIBUTES_ABSENCE.has(status)) return;
8011
+ throw new Error(`workflow: failed to resolve organizationId for project "${projectId}"; advisory $attributes cannot be bound. Original error: ${invariants.errorMessage(err)}`, {
8012
+ cause: err
8013
+ });
8014
+ }
8015
+ }
8016
+ });
8017
+ }
8018
+
8019
+ const ATTRIBUTES_FETCH_LIMIT = 100, EXPECTED_ATTRIBUTES_ABSENCE = /* @__PURE__ */ new Set([ 401, 402, 403, 404 ]);
8020
+
8021
+ function httpStatusOf(err) {
8022
+ if (!invariants.isRecord(err)) return;
8023
+ const status = err.statusCode;
8024
+ return typeof status == "number" ? status : void 0;
8025
+ }
8026
+
8027
+ async function fetchAttributesCached(args) {
8028
+ const {client: client, orgId: orgId} = args, sibling = globalHostRequestOf(client);
8029
+ if ("reason" in sibling) {
8030
+ console.warn(`workflow: skipped org attributes fetch (${sibling.reason}); advisory $attributes stays undefined (conditions referencing it fail closed).`);
8031
+ return;
8032
+ }
8033
+ try {
8034
+ const response = await sibling.request({
8035
+ uri: `/organizations/${encodeURIComponent(orgId)}/users/me/attributes?limit=${ATTRIBUTES_FETCH_LIMIT}`,
8036
+ tag: REQUEST_TAG.accessAttributes
8037
+ }), envelope = invariants.isRecord(response) ? response : void 0;
8038
+ if (envelope?.hasMore === !0) {
8039
+ const page = Array.isArray(envelope.attributes) ? envelope.attributes : [];
8040
+ console.warn(`workflow: org attributes for organization "${orgId}" report hasMore=true (bound ${page.length} of the requested limit ${ATTRIBUTES_FETCH_LIMIT}); advisory $attributes may be incomplete — the engine fetches no further pages. Conditions that read missing keys fail closed.`);
8041
+ }
8042
+ const normalized = normalizeUserAttributes(response);
8043
+ return normalized === void 0 && console.warn(`workflow: org attributes response for organization "${orgId}" was unusable; advisory $attributes stays undefined (conditions referencing it fail closed).`),
8044
+ normalized;
8045
+ } catch (err) {
8046
+ const status = httpStatusOf(err);
8047
+ if (status !== void 0 && EXPECTED_ATTRIBUTES_ABSENCE.has(status)) return;
8048
+ throw new Error(`workflow: failed to fetch org attributes for organization "${orgId}". Original error: ${invariants.errorMessage(err)}`, {
8049
+ cause: err
8050
+ });
8051
+ }
7184
8052
  }
7185
8053
 
7186
- async function fetchGrants(args) {
7187
- const {client: client, resourcePath: resourcePath, signal: signal} = args;
7188
- return client.request({
7189
- url: resourcePath,
7190
- tag: REQUEST_TAG.accessGrants,
7191
- ...signal ? {
7192
- signal: signal
8054
+ function callerBoundVars(args) {
8055
+ const vars = {
8056
+ ...args.can !== void 0 ? {
8057
+ can: args.can
8058
+ } : {},
8059
+ ...args.attributes !== void 0 ? {
8060
+ attributes: args.attributes
7193
8061
  } : {}
7194
- });
8062
+ };
8063
+ return Object.keys(vars).length > 0 ? vars : void 0;
7195
8064
  }
7196
8065
 
7197
- async function advisoryCanForCall(args) {
7198
- const actor = args.options?.actor;
7199
- if (!(args.options?.grants === void 0 || actor === void 0)) return advisoryCan({
8066
+ async function callerBoundVarsForCall(args) {
8067
+ const can = await advisoryCanForCall({
7200
8068
  instance: args.instance,
7201
- identity: invariants.lakePrincipalId({
7202
- actor: actor,
7203
- localPrincipalId: args.options.localPrincipalId
7204
- }),
7205
- grants: args.options.grants
7206
- });
7207
- }
7208
-
7209
- async function advisoryCan({instance: instance, identity: identity, grants: grants}) {
7210
- if (grants === void 0) return;
7211
- const can = {};
7212
- for (const permission of invariants.DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
7213
- document: instance,
7214
- grants: grants,
7215
- permission: permission,
7216
- userId: identity
8069
+ options: args.options
8070
+ }), attributes = args.options?.attributes !== void 0 ? args.options.attributes : await resolveUserAttributes(args.client);
8071
+ return callerBoundVars({
8072
+ ...can !== void 0 ? {
8073
+ can: can
8074
+ } : {},
8075
+ ...attributes !== void 0 ? {
8076
+ attributes: attributes
8077
+ } : {}
7217
8078
  });
7218
- return can;
7219
8079
  }
7220
8080
 
7221
8081
  function requirementDescriptor(requirement) {
@@ -7338,7 +8198,8 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
7338
8198
  }
7339
8199
  });
7340
8200
  if (action.filter !== void 0) {
7341
- const can = await advisoryCanForCall({
8201
+ const vars = await callerBoundVarsForCall({
8202
+ client: ctx.client,
7342
8203
  instance: ctx.instance,
7343
8204
  options: options
7344
8205
  });
@@ -7350,10 +8211,8 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
7350
8211
  ...actor !== void 0 ? {
7351
8212
  actor: actor
7352
8213
  } : {},
7353
- ...can !== void 0 ? {
7354
- vars: {
7355
- can: can
7356
- }
8214
+ ...vars !== void 0 ? {
8215
+ vars: vars
7357
8216
  } : {}
7358
8217
  }
7359
8218
  })) throw new ActionDisabledError({
@@ -7426,7 +8285,9 @@ async function applyActionFire({ctx: ctx, mutation: mutation, activity: activity
7426
8285
  self: invariants.selfGdr(ctx.instance),
7427
8286
  now: ctx.now,
7428
8287
  snapshot: ctx.snapshot,
7429
- refSurface: ctx.refSurface
8288
+ refSurface: ctx.refSurface,
8289
+ memberRoles: ctx.memberRoles,
8290
+ roleAliases: ctx.definition.roleAliases
7430
8291
  });
7431
8292
  if (await queueEffects({
7432
8293
  ctx: ctx,
@@ -7609,6 +8470,8 @@ async function primeInitialStage(args) {
7609
8470
  stage: stage,
7610
8471
  now: now,
7611
8472
  refSurface: refSurface,
8473
+ memberRoles: ctx.memberRoles,
8474
+ roleAliases: ctx.definition.roleAliases,
7612
8475
  recordDiscard: recordFieldDiscards({
7613
8476
  target: discards,
7614
8477
  scope: "stage",
@@ -7668,7 +8531,7 @@ async function primeInitialStage(args) {
7668
8531
 
7669
8532
  const CASCADE_LIMIT = 100;
7670
8533
 
7671
- async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts, instance: instance}) {
8534
+ async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts, instance: instance}) {
7672
8535
  const contextOptions = {
7673
8536
  clientForGdr: clientForGdr,
7674
8537
  refSurface: refSurface,
@@ -7689,7 +8552,8 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
7689
8552
  } : {},
7690
8553
  ...settlingCohorts ? {
7691
8554
  settlingCohorts: settlingCohorts
7692
- } : {}
8555
+ } : {},
8556
+ memberRolesLoader: memberRolesLoader
7693
8557
  }, ctx = instance === void 0 ? await loadContext({
7694
8558
  client: client,
7695
8559
  instanceId: instanceId,
@@ -7728,7 +8592,7 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
7728
8592
  });
7729
8593
  }
7730
8594
 
7731
- async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
8595
+ async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
7732
8596
  let count = 0;
7733
8597
  for (;;) {
7734
8598
  const drained = await drainCondemnedChildren({
@@ -7737,6 +8601,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
7737
8601
  actor: actor,
7738
8602
  clientForGdr: clientForGdr,
7739
8603
  refSurface: refSurface,
8604
+ memberRolesLoader: memberRolesLoader,
7740
8605
  clock: clock,
7741
8606
  executionContext: executionContext,
7742
8607
  telemetry: telemetry
@@ -7747,6 +8612,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
7747
8612
  actor: actor,
7748
8613
  clientForGdr: clientForGdr,
7749
8614
  refSurface: refSurface,
8615
+ memberRolesLoader: memberRolesLoader,
7750
8616
  clock: clock,
7751
8617
  executionContext: executionContext,
7752
8618
  telemetry: telemetry,
@@ -7763,7 +8629,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
7763
8629
  }
7764
8630
  }
7765
8631
 
7766
- async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set, instance: preloadedInstance}) {
8632
+ async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set, instance: preloadedInstance}) {
7767
8633
  if (draining.has(instanceId)) return {
7768
8634
  instance: preloadedInstance,
7769
8635
  drained: !1
@@ -7786,6 +8652,7 @@ async function drainCondemnedChildren({client: client, instanceId: instanceId, a
7786
8652
  actor: actor,
7787
8653
  clientForGdr: clientForGdr,
7788
8654
  refSurface: refSurface,
8655
+ memberRolesLoader: memberRolesLoader,
7789
8656
  clock: clock,
7790
8657
  executionContext: executionContext,
7791
8658
  telemetry: telemetry,
@@ -7803,7 +8670,7 @@ async function drainCondemnedChildren({client: client, instanceId: instanceId, a
7803
8670
  };
7804
8671
  }
7805
8672
 
7806
- async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
8673
+ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
7807
8674
  const childId = invariants.toBareId(row.ref.id);
7808
8675
  try {
7809
8676
  const result = await abortInstance({
@@ -7816,6 +8683,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
7816
8683
  } : {},
7817
8684
  clientForGdr: clientForGdr,
7818
8685
  refSurface: refSurface,
8686
+ memberRolesLoader: memberRolesLoader,
7819
8687
  ...clock ? {
7820
8688
  clock: clock
7821
8689
  } : {},
@@ -7833,6 +8701,7 @@ async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, a
7833
8701
  actor: actor,
7834
8702
  clientForGdr: clientForGdr,
7835
8703
  refSurface: refSurface,
8704
+ memberRolesLoader: memberRolesLoader,
7836
8705
  clock: clock,
7837
8706
  executionContext: executionContext,
7838
8707
  telemetry: telemetry,
@@ -7983,7 +8852,8 @@ async function buildCascadeStepContext(args, instance) {
7983
8852
  } : {},
7984
8853
  ...args.settlingCohorts ? {
7985
8854
  settlingCohorts: args.settlingCohorts
7986
- } : {}
8855
+ } : {},
8856
+ memberRolesLoader: args.memberRolesLoader
7987
8857
  });
7988
8858
  }
7989
8859
 
@@ -8211,6 +9081,7 @@ function spawnStepArgs(ctx, args) {
8211
9081
  actor: args.actor,
8212
9082
  clientForGdr: ctx.clientForGdr,
8213
9083
  refSurface: ctx.refSurface,
9084
+ memberRolesLoader: ctx.memberRolesLoader,
8214
9085
  clock: ctx.clock,
8215
9086
  executionContext: ctx.executionContext,
8216
9087
  telemetry: ctx.telemetry,
@@ -8352,7 +9223,11 @@ function validateEffectOutputs(args) {
8352
9223
  issues.push(`"${key}" is not a declared output`);
8353
9224
  continue;
8354
9225
  }
8355
- const check = effectOutputCheck(shape, value);
9226
+ const check = effectOutputCheck({
9227
+ shape: shape,
9228
+ value: value,
9229
+ eligibility: args
9230
+ });
8356
9231
  "issues" in check ? issues.push(...check.issues.map(i => `"${key}": ${i}`)) : normalized[key] = check.output;
8357
9232
  }
8358
9233
  if (issues.length > 0) throw new EffectOutputsInvalidError({
@@ -8362,7 +9237,8 @@ function validateEffectOutputs(args) {
8362
9237
  return normalized;
8363
9238
  }
8364
9239
 
8365
- function effectOutputCheck(shape, value) {
9240
+ function effectOutputCheck(args) {
9241
+ const {shape: shape, value: value, eligibility: eligibility} = args;
8366
9242
  return invariants.parseFieldValue({
8367
9243
  entryType: shape.type,
8368
9244
  value: value,
@@ -8377,7 +9253,12 @@ function effectOutputCheck(shape, value) {
8377
9253
  } : {},
8378
9254
  ...shape.validation !== void 0 ? {
8379
9255
  validation: shape.validation
8380
- } : {}
9256
+ } : {},
9257
+ ...shape.roles !== void 0 ? {
9258
+ roles: shape.roles
9259
+ } : {},
9260
+ memberRoles: eligibility.memberRoles,
9261
+ roleAliases: eligibility.roleAliases
8381
9262
  });
8382
9263
  }
8383
9264
 
@@ -8400,7 +9281,7 @@ function requirePendingEffect(instance, effectKey) {
8400
9281
  });
8401
9282
  }
8402
9283
 
8403
- function validateCompletionInput({pending: pending, definition: definition, status: status, ops: ops, outputs: outputs}) {
9284
+ function validateCompletionInput({pending: pending, definition: definition, status: status, ops: ops, outputs: outputs, memberRoles: memberRoles, roleAliases: roleAliases}) {
8404
9285
  if (status === "failed" && ops !== void 0 && ops.length > 0) throw new EffectOpsInvalidError({
8405
9286
  effect: pending.name,
8406
9287
  issues: [ "ops cannot accompany a failed completion — field.set the outcome on a done completion instead" ]
@@ -8412,7 +9293,9 @@ function validateCompletionInput({pending: pending, definition: definition, stat
8412
9293
  const normalizedOutputs = outputs !== void 0 ? validateEffectOutputs({
8413
9294
  outputs: outputs,
8414
9295
  declared: findEffect(definition, pending.name)?.outputs ?? [],
8415
- effectName: pending.name
9296
+ effectName: pending.name,
9297
+ memberRoles: memberRoles,
9298
+ roleAliases: roleAliases
8416
9299
  }) : void 0;
8417
9300
  return {
8418
9301
  ops: ops !== void 0 ? validateEffectOps(ops, pending.name) : [],
@@ -8431,7 +9314,9 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
8431
9314
  definition: ctx.definition,
8432
9315
  status: status,
8433
9316
  ops: ops,
8434
- outputs: outputs
9317
+ outputs: outputs,
9318
+ memberRoles: ctx.memberRoles,
9319
+ roleAliases: ctx.definition.roleAliases
8435
9320
  }), mutation = startMutation(ctx.instance);
8436
9321
  recordProcessedRequest({
8437
9322
  mutation: mutation,
@@ -8462,7 +9347,9 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
8462
9347
  self: invariants.selfGdr(ctx.instance),
8463
9348
  now: ranAt,
8464
9349
  snapshot: ctx.snapshot,
8465
- refSurface: ctx.refSurface
9350
+ refSurface: ctx.refSurface,
9351
+ memberRoles: ctx.memberRoles,
9352
+ roleAliases: ctx.definition.roleAliases
8466
9353
  }), needsGuardRefresh = wroteEffectOutputs || ranOps.some(isFieldOp);
8467
9354
  return await persistThenMaybeRefresh({
8468
9355
  ctx: ctx,
@@ -8631,7 +9518,9 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
8631
9518
  self: invariants.selfGdr(ctx.instance),
8632
9519
  now: ctx.now,
8633
9520
  snapshot: ctx.snapshot,
8634
- refSurface: ctx.refSurface
9521
+ refSurface: ctx.refSurface,
9522
+ memberRoles: ctx.memberRoles,
9523
+ roleAliases: ctx.definition.roleAliases
8635
9524
  }), await persistThenMaybeRefresh({
8636
9525
  ctx: ctx,
8637
9526
  mutation: mutation,
@@ -8642,306 +9531,63 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
8642
9531
  effect: pending.name
8643
9532
  };
8644
9533
  }
8645
-
8646
- const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
8647
-
8648
- function isResetActivityTarget(value) {
8649
- return RESET_ACTIVITY_TARGETS.includes(value);
8650
- }
8651
-
8652
- async function resetActivity(args) {
8653
- const {client: client, instanceId: instanceId, activity: activity, to: to, requestRecord: requestRecord, options: options} = args, ctx = await loadCallContext({
8654
- client: client,
8655
- instanceId: instanceId,
8656
- options: options
8657
- });
8658
- return commitResetActivity({
8659
- ctx: ctx,
8660
- activity: activity,
8661
- to: to,
8662
- requestRecord: requestRecord,
8663
- actor: options?.actor
8664
- });
8665
- }
8666
-
8667
- async function commitResetActivity({ctx: ctx, activity: activity, to: to, requestRecord: requestRecord, actor: actor}) {
8668
- if (assertRequestUnprocessed({
8669
- instance: ctx.instance,
8670
- record: requestRecord,
8671
- now: ctx.now
8672
- }), isTerminal(ctx)) return {
8673
- fired: !1
8674
- };
8675
- const mutation = startMutation(ctx.instance), openStage2 = findOpenStageEntry(mutation), entry = findCurrentActivityEntry(mutation, activity);
8676
- if (openStage2 === void 0 || entry === void 0) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is not in the current stage of instance "${ctx.instance._id}"`);
8677
- const from = entry.status;
8678
- if (from === to) return {
8679
- fired: !1
8680
- };
8681
- if (!invariants.isTerminalActivityStatus(from)) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is "${from}", not a terminal status — a reset only recovers a resolved (typically failed) activity. Use setStage to force past a live stage.`);
8682
- return recordProcessedRequest({
8683
- mutation: mutation,
8684
- record: requestRecord,
8685
- now: ctx.now
8686
- }), applyActivityStatusChange({
8687
- entry: entry,
8688
- history: mutation.history,
8689
- stage: openStage2.name,
8690
- to: to,
8691
- at: ctx.now,
8692
- ...actor !== void 0 ? {
8693
- actor: actor
8694
- } : {}
8695
- }), await persist(ctx, mutation), {
8696
- fired: !0,
8697
- stage: openStage2.name,
8698
- activity: activity,
8699
- from: from,
8700
- to: to
8701
- };
8702
- }
8703
-
8704
- const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
8705
-
8706
- async function resolveAccess(taggedClient, args = {}) {
8707
- const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
8708
- if (requestFn === void 0) throw new invariants.ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({ uri: '/users/me' })`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
8709
- const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
8710
- client: client,
8711
- requestFn: requestFn,
8712
- resourcePath: args.grantsFromPath
8713
- }) : Promise.resolve(void 0), [identity, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
8714
- if (identity === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
8715
- return {
8716
- actor: identity.actor,
8717
- ...identity.localPrincipalId !== void 0 ? {
8718
- localPrincipalId: identity.localPrincipalId
8719
- } : {},
8720
- ...grants !== void 0 ? {
8721
- grants: grants
8722
- } : {}
8723
- };
8724
- }
8725
-
8726
- function cachedActor(client, requestFn) {
8727
- const cached = actorCache.get(client);
8728
- if (cached !== void 0) return cached;
8729
- const pending = fetchActor(client, requestFn).catch(err => {
8730
- throw actorCache.get(client) === pending && actorCache.delete(client), err;
8731
- });
8732
- return actorCache.set(client, pending), pending;
8733
- }
8734
-
8735
- function grantsForClientPath(taggedClient, resourcePath) {
8736
- const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
8737
- return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
8738
- client: client,
8739
- requestFn: requestFn,
8740
- resourcePath: resourcePath
8741
- });
8742
- }
8743
-
8744
- function lazyRequest(client) {
8745
- return client.request === void 0 ? void 0 : opts => client.request(opts);
8746
- }
8747
-
8748
- function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
8749
- let byPath = grantsCache.get(client);
8750
- byPath === void 0 && (byPath = /* @__PURE__ */ new Map, grantsCache.set(client, byPath));
8751
- let cached = byPath.get(resourcePath);
8752
- return cached === void 0 && (cached = fetchGrantsCached(requestFn, resourcePath),
8753
- byPath.set(resourcePath, cached)), cached;
8754
- }
8755
-
8756
- async function fetchActor(client, requestFn) {
8757
- const [resourceUser, globalUser] = await Promise.all([ fetchCurrentUser(requestFn, "the workflow resource host"), fetchGlobalUser(client) ]), resourceId = usableId(resourceUser), globalHostId = usableId(globalUser.user), carriedId = invariants.firstCarriedGlobalId([ globalHostId, resourceId ]), sessionId = carriedId ?? globalHostId ?? resourceId;
8758
- if (sessionId === void 0) return;
8759
- const bridge = carriedId === void 0 ? await bridgeProjectPrincipal({
8760
- client: client,
8761
- sessionId: sessionId,
8762
- resourceId: resourceId
8763
- }) : NO_BRIDGE, id = bridge.status === "resolved" ? bridge.globalId : sessionId;
8764
- refuseProjectScopedActor({
8765
- id: id,
8766
- globalUser: globalUser,
8767
- globalHostId: globalHostId,
8768
- bridgeFailure: bridge.status === "unavailable" ? bridge : void 0
8769
- });
8770
- const roleNames = roleNamesFor(resourceUser, globalUser);
8771
- return {
8772
- actor: {
8773
- kind: "person",
8774
- id: id,
8775
- ...roleNames.length > 0 ? {
8776
- roles: roleNames
8777
- } : {}
8778
- },
8779
- ...resourceId !== void 0 && resourceId !== id ? {
8780
- localPrincipalId: resourceId
8781
- } : {}
8782
- };
8783
- }
8784
-
8785
- function roleNamesFor(resourceUser, globalUser) {
8786
- return (resourceUser?.roles?.length ? resourceUser : globalUser.user)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
8787
- }
8788
-
8789
- const NO_BRIDGE = {
8790
- status: "not-applicable"
8791
- };
8792
-
8793
- function globalRouteReason(globalUser, globalHostId) {
8794
- return "reason" in globalUser ? globalUser.reason : globalHostId === void 0 ? "no record" : `the global host answered with project-scoped principal "${globalHostId}"`;
8795
- }
8796
-
8797
- async function bridgeProjectPrincipal(args) {
8798
- const {client: client, sessionId: sessionId, resourceId: resourceId} = args;
8799
- return invariants.classifyPrincipalId(sessionId).namespace !== "project" ? NO_BRIDGE : resourceId === void 0 || invariants.classifyPrincipalId(resourceId).namespace !== "project" ? {
8800
- status: "unavailable",
8801
- reason: "no project-scoped principal from the resource host to address the directory with"
8802
- } : directoryGlobalId({
8803
- client: client,
8804
- principalId: resourceId
8805
- });
8806
- }
8807
-
8808
- async function directoryGlobalId(args) {
8809
- const {client: client, principalId: principalId} = args, projectId = typeof client.config == "function" ? client.config().projectId : void 0;
8810
- if (projectId === void 0) return {
8811
- status: "unavailable",
8812
- reason: "the client reports no projectId, so its user directory has no address"
8813
- };
8814
- const lookup = await clientProjectUserDirectory(withRequestTag(client, REQUEST_TAG.accessResolveActor), projectId).findById(principalId);
8815
- if (lookup.status !== "resolved") return {
8816
- status: "unavailable",
8817
- reason: `project "${projectId}"'s user directory reported the principal ${lookup.status}`,
8818
- ...lookup.status === "inaccessible" && lookup.cause !== void 0 ? {
8819
- cause: lookup.cause
8820
- } : {}
8821
- };
8822
- const globalId = invariants.directoryBridgeId(lookup.user.sanityUserId);
8823
- return globalId === void 0 ? {
8824
- status: "unavailable",
8825
- reason: `project "${projectId}"'s user directory row for the principal carries no account-global sanityUserId`
8826
- } : {
8827
- status: "resolved",
8828
- globalId: globalId
8829
- };
8830
- }
8831
-
8832
- function refuseProjectScopedActor(args) {
8833
- const {id: id, globalUser: globalUser, globalHostId: globalHostId, bridgeFailure: bridgeFailure} = args;
8834
- if (invariants.classifyPrincipalId(id).namespace !== "project") return;
8835
- const routes = [ `The account-global record: ${globalRouteReason(globalUser, globalHostId)}.` ];
8836
- bridgeFailure !== void 0 && routes.push(`The project user directory: ${bridgeFailure.reason}.`);
8837
- const cause = bridgeFailure?.cause ?? ("cause" in globalUser ? globalUser.cause : void 0);
8838
- throw new Error(`workflow: the caller's identity resolves to the project-scoped principal "${id}" and its account-global id could not be resolved. ${routes.join(" ")} The engine speaks account-global user ids only and never acts as a project-scoped principal.`, cause === void 0 ? void 0 : {
8839
- cause: cause
8840
- });
8841
- }
8842
-
8843
- function usableId(user) {
8844
- if (!(!user || typeof user.id != "string" || user.id.length === 0)) return user.id;
8845
- }
8846
-
8847
- async function fetchCurrentUser(requestFn, hostDescription) {
8848
- try {
8849
- return await requestFn({
8850
- uri: "/users/me",
8851
- tag: REQUEST_TAG.accessResolveActor
8852
- });
8853
- } catch (err) {
8854
- throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({ uri: '/users/me' })\`. Check the token/connectivity.`, {
8855
- cause: err
8856
- });
8857
- }
9534
+
9535
+ const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
9536
+
9537
+ function isResetActivityTarget(value) {
9538
+ return RESET_ACTIVITY_TARGETS.includes(value);
8858
9539
  }
8859
9540
 
8860
- async function fetchGlobalUser(client) {
8861
- if (typeof client.withConfig != "function") return {
8862
- user: void 0,
8863
- reason: "the client cannot reach the global API host (no withConfig)"
8864
- };
8865
- let globalRequest, globalClient;
8866
- const hostResolution = resolveGlobalApiHost(client);
8867
- try {
8868
- globalClient = client.withConfig({
8869
- useProjectHostname: !1,
8870
- ...hostResolution.apiHost === void 0 ? {} : {
8871
- apiHost: hostResolution.apiHost
8872
- }
8873
- }), globalRequest = lazyRequest(globalClient);
8874
- } catch (err) {
8875
- return {
8876
- user: void 0,
8877
- reason: `building the global-host sibling client failed: ${invariants.errorMessage(err)}`,
8878
- cause: err
8879
- };
8880
- }
8881
- if (globalRequest === void 0) return {
8882
- user: void 0,
8883
- reason: "the global-host sibling client cannot issue requests"
8884
- };
8885
- try {
8886
- const user = await globalRequest({
8887
- uri: "/users/me",
8888
- tag: REQUEST_TAG.accessResolveActor
8889
- }), id = usableId(user);
8890
- return hostResolution.requireGlobalPrincipal && id !== void 0 && invariants.classifyPrincipalId(id).namespace === "project" ? {
8891
- user: void 0,
8892
- reason: `global-host /users/me returned project-scoped principal "${id}"; the sibling client is still bound to a project API host`
8893
- } : {
8894
- user: user
8895
- };
8896
- } catch (err) {
8897
- return {
8898
- user: void 0,
8899
- reason: `global-host /users/me failed: ${invariants.errorMessage(err)}`,
8900
- cause: err
8901
- };
8902
- }
9541
+ async function resetActivity(args) {
9542
+ const {client: client, instanceId: instanceId, activity: activity, to: to, requestRecord: requestRecord, options: options} = args, ctx = await loadCallContext({
9543
+ client: client,
9544
+ instanceId: instanceId,
9545
+ options: options
9546
+ });
9547
+ return commitResetActivity({
9548
+ ctx: ctx,
9549
+ activity: activity,
9550
+ to: to,
9551
+ requestRecord: requestRecord,
9552
+ actor: options?.actor
9553
+ });
8903
9554
  }
8904
9555
 
8905
- function resolveGlobalApiHost(client) {
8906
- if (typeof client.config != "function") return {
8907
- requireGlobalPrincipal: !1
9556
+ async function commitResetActivity({ctx: ctx, activity: activity, to: to, requestRecord: requestRecord, actor: actor}) {
9557
+ if (assertRequestUnprocessed({
9558
+ instance: ctx.instance,
9559
+ record: requestRecord,
9560
+ now: ctx.now
9561
+ }), isTerminal(ctx)) return {
9562
+ fired: !1
8908
9563
  };
8909
- const apiHost = client.config().apiHost;
8910
- if (apiHost === void 0) return {
8911
- requireGlobalPrincipal: !1
9564
+ const mutation = startMutation(ctx.instance), openStage2 = findOpenStageEntry(mutation), entry = findCurrentActivityEntry(mutation, activity);
9565
+ if (openStage2 === void 0 || entry === void 0) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is not in the current stage of instance "${ctx.instance._id}"`);
9566
+ const from = entry.status;
9567
+ if (from === to) return {
9568
+ fired: !1
8912
9569
  };
8913
- let parsed;
8914
- try {
8915
- parsed = new URL(apiHost);
8916
- } catch {
8917
- return {
8918
- requireGlobalPrincipal: !0
8919
- };
8920
- }
8921
- if (/^api\.sanity\.(io|work)$/.test(parsed.hostname)) return {
8922
- requireGlobalPrincipal: !1
9570
+ if (!invariants.isTerminalActivityStatus(from)) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is "${from}", not a terminal status — a reset only recovers a resolved (typically failed) activity. Use setStage to force past a live stage.`);
9571
+ return recordProcessedRequest({
9572
+ mutation: mutation,
9573
+ record: requestRecord,
9574
+ now: ctx.now
9575
+ }), applyActivityStatusChange({
9576
+ entry: entry,
9577
+ history: mutation.history,
9578
+ stage: openStage2.name,
9579
+ to: to,
9580
+ at: ctx.now,
9581
+ ...actor !== void 0 ? {
9582
+ actor: actor
9583
+ } : {}
9584
+ }), await persist(ctx, mutation), {
9585
+ fired: !0,
9586
+ stage: openStage2.name,
9587
+ activity: activity,
9588
+ from: from,
9589
+ to: to
8923
9590
  };
8924
- const match = /^[^.]+\.api\.sanity\.(io|work)$/.exec(parsed.hostname);
8925
- return match === null ? {
8926
- requireGlobalPrincipal: !0
8927
- } : (parsed.hostname = `api.sanity.${match[1]}`, {
8928
- apiHost: parsed.origin,
8929
- requireGlobalPrincipal: !0
8930
- });
8931
- }
8932
-
8933
- async function fetchGrantsCached(requestFn, resourcePath) {
8934
- try {
8935
- return await fetchGrants({
8936
- client: {
8937
- request: requestFn
8938
- },
8939
- resourcePath: resourcePath
8940
- });
8941
- } catch (err) {
8942
- console.warn(`workflow: failed to fetch grants from "${resourcePath}"; advisory permission reads that depend on them are skipped — a rendered $can stays undefined (conditions referencing it fail closed) and the subject-write forecast omits this resource. The lake still enforces writes. Original error: ${invariants.errorMessage(err)}`);
8943
- return;
8944
- }
8945
9591
  }
8946
9592
 
8947
9593
  const REPLAY_SURFACE = {
@@ -9125,7 +9771,7 @@ async function resourceActorId(client) {
9125
9771
  async function evaluateInstance(args) {
9126
9772
  const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
9127
9773
  invariants.validateTag(tag);
9128
- const [access, instance] = await Promise.all([ resolveAccess(client, {
9774
+ const [access, instance, attributes] = await Promise.all([ resolveAccess(client, {
9129
9775
  ...args.grantsFromPath !== void 0 ? {
9130
9776
  grantsFromPath: args.grantsFromPath
9131
9777
  } : {}
@@ -9133,7 +9779,7 @@ async function evaluateInstance(args) {
9133
9779
  client: client,
9134
9780
  instanceId: instanceId,
9135
9781
  tag: tag
9136
- }) ]), {actor: actor, grants: grants, localPrincipalId: localPrincipalId} = access, definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
9782
+ }), resolveUserAttributes(client) ]), {actor: actor, grants: grants, localPrincipalId: localPrincipalId} = access, definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
9137
9783
  client: client,
9138
9784
  workflowResource: workflowResource,
9139
9785
  resourceClients: resourceClients
@@ -9158,6 +9804,9 @@ async function evaluateInstance(args) {
9158
9804
  } : {},
9159
9805
  ...grants !== void 0 ? {
9160
9806
  grants: grants
9807
+ } : {},
9808
+ ...attributes !== void 0 ? {
9809
+ attributes: attributes
9161
9810
  } : {}
9162
9811
  });
9163
9812
  }
@@ -9172,6 +9821,33 @@ function memoizedByName(render) {
9172
9821
  };
9173
9822
  }
9174
9823
 
9824
+ async function callerBoundProjectionScopes(args) {
9825
+ const can = await advisoryCan({
9826
+ instance: args.instance,
9827
+ identity: args.identity,
9828
+ grants: args.grants
9829
+ }), vars = callerBoundVars({
9830
+ ...can !== void 0 ? {
9831
+ can: can
9832
+ } : {},
9833
+ ...args.attributes !== void 0 ? {
9834
+ attributes: args.attributes
9835
+ } : {}
9836
+ }), opts = {
9837
+ actor: args.actor,
9838
+ ...vars !== void 0 ? {
9839
+ vars: vars
9840
+ } : {}
9841
+ };
9842
+ return {
9843
+ scope: await renderConditionScope(args.scopeSource, opts),
9844
+ scopeForActivity: memoizedByName(activityName => renderConditionScope(args.scopeSource, {
9845
+ activityName: activityName,
9846
+ ...opts
9847
+ }))
9848
+ };
9849
+ }
9850
+
9175
9851
  function currentStageOf(instance, definition) {
9176
9852
  try {
9177
9853
  return findStage(definition, instance.currentStage);
@@ -9197,7 +9873,7 @@ async function explainSite(args) {
9197
9873
  }
9198
9874
 
9199
9875
  async function evaluateFromSnapshot(args) {
9200
- const {instance: instance, definition: definition, actor: actor, grants: grants, snapshot: snapshot} = args, now = args.now ?? wallClock(), stage = currentStageOf(instance, definition), autonomy = autonomyOf(definition), stageAutonomy2 = stageAutonomyOf(autonomy, stage.name), fireConsequence = (activity, action) => whatIfFireAction({
9876
+ const {instance: instance, definition: definition, actor: actor, grants: grants, attributes: attributes, snapshot: snapshot} = args, now = args.now ?? wallClock(), stage = currentStageOf(instance, definition), autonomy = autonomyOf(definition), stageAutonomy2 = stageAutonomyOf(autonomy, stage.name), fireConsequence = (activity, action) => whatIfFireAction({
9201
9877
  instance: instance,
9202
9878
  definition: definition,
9203
9879
  snapshot: snapshot,
@@ -9214,22 +9890,14 @@ async function evaluateFromSnapshot(args) {
9214
9890
  }, anchorIdentity = invariants.lakePrincipalId({
9215
9891
  actor: actor,
9216
9892
  localPrincipalId: args.localPrincipalId
9217
- }), can = await advisoryCan({
9218
- instance: instance,
9219
- identity: anchorIdentity,
9220
- grants: grants
9221
- }), scope = await renderConditionScope(scopeSource, {
9222
- actor: actor,
9223
- vars: {
9224
- can: can
9225
- }
9226
- }), scopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
9227
- activityName: activityName,
9893
+ }), {scope: scope, scopeForActivity: scopeForActivity} = await callerBoundProjectionScopes({
9894
+ scopeSource: scopeSource,
9228
9895
  actor: actor,
9229
- vars: {
9230
- can: can
9231
- }
9232
- })), cascadeScopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
9896
+ instance: instance,
9897
+ grants: grants,
9898
+ attributes: attributes,
9899
+ identity: anchorIdentity
9900
+ }), cascadeScopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
9233
9901
  activityName: activityName
9234
9902
  })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
9235
9903
  instance: instance,
@@ -9278,6 +9946,7 @@ async function evaluateFromSnapshot(args) {
9278
9946
  }
9279
9947
  const currentStage = {
9280
9948
  stage: stage,
9949
+ ...semanticsOf(stage),
9281
9950
  activities: activityEvaluations,
9282
9951
  transitions: transitionEvaluations,
9283
9952
  autonomy: stageAutonomy2
@@ -9294,6 +9963,7 @@ async function evaluateFromSnapshot(args) {
9294
9963
  return {
9295
9964
  instance: instance,
9296
9965
  definition: definition,
9966
+ ...semanticsOf(definition),
9297
9967
  actor: actor,
9298
9968
  currentStage: currentStage,
9299
9969
  pendingOnYou: pendingOnYou,
@@ -9345,6 +10015,12 @@ async function evaluateEditableFields(args) {
9345
10015
  ...site.validation !== void 0 ? {
9346
10016
  validation: site.validation
9347
10017
  } : {},
10018
+ ...site.roles !== void 0 ? {
10019
+ roles: site.roles,
10020
+ ...definition.roleAliases !== void 0 ? {
10021
+ roleAliases: definition.roleAliases
10022
+ } : {}
10023
+ } : {},
9348
10024
  value: value,
9349
10025
  editable: reason === void 0,
9350
10026
  ...reason !== void 0 ? {
@@ -9449,6 +10125,7 @@ async function evaluateActivity(args) {
9449
10125
  }));
9450
10126
  return {
9451
10127
  activity: activity,
10128
+ ...semanticsOf(activity),
9452
10129
  status: status,
9453
10130
  kind: invariants.deriveActivityKind(activity),
9454
10131
  classification: invariants.deriveExecutorClassification(activity),
@@ -9619,9 +10296,13 @@ function fireableActionVerdict({args: args, insights: insights}) {
9619
10296
  function actionEvaluationIdentity(action) {
9620
10297
  return {
9621
10298
  action: action,
9622
- ...action.semantics !== void 0 ? {
9623
- semantics: action.semantics
9624
- } : {}
10299
+ ...semanticsOf(action)
10300
+ };
10301
+ }
10302
+
10303
+ function semanticsOf(node) {
10304
+ return node.semantics === void 0 ? {} : {
10305
+ semantics: node.semantics
9625
10306
  };
9626
10307
  }
9627
10308
 
@@ -9690,23 +10371,63 @@ function idsArm(filter, params) {
9690
10371
  }
9691
10372
  }
9692
10373
 
9693
- function instancesQuery(args) {
9694
- const {tag: tag, filter: filter = {}} = args;
9695
- if (invariants.validateTag(tag), filter.limit !== void 0 && (!Number.isInteger(filter.limit) || filter.limit <= 0)) throw new invariants.ContractViolationError(`instancesQuery: limit must be a positive integer; got ${JSON.stringify(filter.limit)}`);
9696
- const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ], params = {
9697
- tag: tag
9698
- };
10374
+ function beforeArm(filter, params) {
10375
+ const before = filter.before;
10376
+ if (before !== void 0) {
10377
+ if (filter.limit === void 0) throw new invariants.ContractViolationError("instancesQuery: `before` requires `limit` — a cursor pages the newest-first sliced read");
10378
+ if (before.id === "" || before.startedAt === "") throw new invariants.ContractViolationError(`instancesQuery: \`before\` needs a non-empty id and startedAt; got ${JSON.stringify(before)}`);
10379
+ return params.beforeStartedAt = before.startedAt, params.beforeId = before.id, "(startedAt < $beforeStartedAt || (startedAt == $beforeStartedAt && _id > $beforeId))";
10380
+ }
10381
+ }
10382
+
10383
+ function compiledConditions(filter, params) {
10384
+ if (filter.limit !== void 0 && (!Number.isInteger(filter.limit) || filter.limit <= 0)) throw new invariants.ContractViolationError(`instancesQuery: limit must be a positive integer; got ${JSON.stringify(filter.limit)}`);
10385
+ const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ];
9699
10386
  filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
9700
10387
  params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
9701
10388
  params.stage = filter.stage);
10389
+ const cursor = beforeArm(filter, params);
10390
+ cursor !== void 0 && conditions.push(cursor);
9702
10391
  const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
9703
- arms.length > 0 && conditions.push(`(${arms.join(" || ")})`);
9704
- const body = `*[${conditions.join(" && ")}]`;
9705
- return filter.limit !== void 0 ? {
9706
- query: `${body} | order(startedAt desc) [0...${filter.limit}]`,
10392
+ return arms.length > 0 && conditions.push(`(${arms.join(" || ")})`), conditions;
10393
+ }
10394
+
10395
+ function orderedSlice(filter) {
10396
+ return filter.limit !== void 0 ? ` | order(startedAt desc, _id asc) [0...${filter.limit}]` : " | order(startedAt asc, _id asc)";
10397
+ }
10398
+
10399
+ function instancesQuery(args) {
10400
+ const {tag: tag, filter: filter = {}} = args;
10401
+ invariants.validateTag(tag);
10402
+ const params = {
10403
+ tag: tag
10404
+ };
10405
+ return {
10406
+ query: `*[${compiledConditions(filter, params).join(" && ")}]${orderedSlice(filter)}`,
9707
10407
  params: params
9708
- } : {
9709
- query: `${body} | order(startedAt asc)`,
10408
+ };
10409
+ }
10410
+
10411
+ function instanceChangesQuery(args) {
10412
+ return invariants.validateTag(args.tag), {
10413
+ query: `*[_type == "${WORKFLOW_INSTANCE_TYPE}" && ${invariants.tagScopeFilter()}]`,
10414
+ params: {
10415
+ tag: args.tag
10416
+ }
10417
+ };
10418
+ }
10419
+
10420
+ const PREVIEW_FIELD_TYPES = [ "actor", "assignee", "assignees", "date", "datetime", "doc.ref", "doc.refs", "dueDate", "dueDatetime", "release.ref", "subject" ], PREVIEW_PROJECTION = `{\n _id, _type, modelVersion, minReaderModel, workflowResource, definition, pinnedVersion,\n currentStage, startedAt, completedAt, abortedAt, perspective, ancestors,\n "fields": fields[_type in $previewFieldTypes],\n "stages": stages[!defined(exitedAt) && name == ^.currentStage]{\n _key, name, enteredAt,\n "fields": fields[_type in $previewFieldTypes],\n "activities": activities[]{\n _key, name, status, completedBy,\n "fields": fields[_type in $previewFieldTypes]\n }\n },\n "claimedEffects": pendingEffects[defined(claim)]{name, title},\n "failedEffects": effectHistory[status == "failed"]{name, title}\n}`.replaceAll(/\s+/g, " ").trim();
10421
+
10422
+ function instancePreviewsQuery(args) {
10423
+ const {tag: tag, filter: filter = {}} = args;
10424
+ invariants.validateTag(tag);
10425
+ const params = {
10426
+ tag: tag,
10427
+ previewFieldTypes: PREVIEW_FIELD_TYPES
10428
+ };
10429
+ return {
10430
+ query: `*[${compiledConditions(filter, params).join(" && ")}]${orderedSlice(filter)} ${PREVIEW_PROJECTION}`,
9710
10431
  params: params
9711
10432
  };
9712
10433
  }
@@ -9891,6 +10612,11 @@ function hashDefinitionContent(def) {
9891
10612
  }
9892
10613
 
9893
10614
  function planDefinitionDeploy({def: def, latest: latest, target: target}) {
10615
+ assertRoleConstrainedAssignmentResource({
10616
+ context: "workflow.deployDefinitions",
10617
+ definition: def,
10618
+ workflowResource: target.workflowResource
10619
+ });
9894
10620
  const expanded = expandResourceAliases(def, target.resourceAliases), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = invariants.definitionDocId({
9895
10621
  tag: target.tag,
9896
10622
  definition: expanded.name,
@@ -10083,7 +10809,9 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
10083
10809
  self: invariants.selfGdr(ctx.instance),
10084
10810
  now: ctx.now,
10085
10811
  snapshot: ctx.snapshot,
10086
- refSurface: ctx.refSurface
10812
+ refSurface: ctx.refSurface,
10813
+ memberRoles: ctx.memberRoles,
10814
+ roleAliases: ctx.definition.roleAliases
10087
10815
  });
10088
10816
  return await persistThenMaybeRefresh({
10089
10817
  ctx: ctx,
@@ -10100,7 +10828,8 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
10100
10828
  }
10101
10829
 
10102
10830
  async function assertFieldEditable({ctx: ctx, site: site, options: options}) {
10103
- const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), can = await advisoryCanForCall({
10831
+ const actor = options?.actor, window = fieldWindowOpen(ctx.instance, site), vars = await callerBoundVarsForCall({
10832
+ client: ctx.client,
10104
10833
  instance: ctx.instance,
10105
10834
  options: options
10106
10835
  }), predicateSatisfied = window.open && site.effective !== !0 && site.effective !== void 0 ? await ctxEvaluateCondition({
@@ -10113,10 +10842,8 @@ async function assertFieldEditable({ctx: ctx, site: site, options: options}) {
10113
10842
  ...actor !== void 0 ? {
10114
10843
  actor: actor
10115
10844
  } : {},
10116
- ...can !== void 0 ? {
10117
- vars: {
10118
- can: can
10119
- }
10845
+ ...vars !== void 0 ? {
10846
+ vars: vars
10120
10847
  } : {}
10121
10848
  }
10122
10849
  }) : site.effective === !0, reason = editDisabledReason({
@@ -10183,6 +10910,7 @@ async function resolveOperationContext(args) {
10183
10910
  return {
10184
10911
  access: access,
10185
10912
  actor: access.actor,
10913
+ memberRolesLoader: createAssignmentMemberRolesLoader(args.client),
10186
10914
  clientForGdr: buildClientForGdr({
10187
10915
  client: args.client,
10188
10916
  workflowResource: args.workflowResource,
@@ -10215,6 +10943,7 @@ async function runCommitVerb(params) {
10215
10943
  actor: context.actor,
10216
10944
  clientForGdr: context.clientForGdr,
10217
10945
  refSurface: context.refSurface,
10946
+ memberRolesLoader: context.memberRolesLoader,
10218
10947
  clock: clock,
10219
10948
  executionContext: args.executionContext,
10220
10949
  telemetry: args.telemetry,
@@ -10239,7 +10968,7 @@ function requestRecordFor(args) {
10239
10968
  }
10240
10969
 
10241
10970
  async function runDeduped(args) {
10242
- const {client: client, tag: tag, instanceId: instanceId, record: record, before: before, now: now, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, run: run} = args, {clock: clock, executionContext: executionContext, telemetry: telemetry} = args, replay = async () => {
10971
+ const {client: client, tag: tag, instanceId: instanceId, record: record, before: before, now: now, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, run: run} = args, {clock: clock, executionContext: executionContext, telemetry: telemetry} = args, replay = async () => {
10243
10972
  const pre = await reload({
10244
10973
  client: client,
10245
10974
  instanceId: instanceId,
@@ -10251,6 +10980,7 @@ async function runDeduped(args) {
10251
10980
  actor: actor,
10252
10981
  clientForGdr: clientForGdr,
10253
10982
  refSurface: refSurface,
10983
+ memberRolesLoader: memberRolesLoader,
10254
10984
  clock: clock,
10255
10985
  executionContext: executionContext,
10256
10986
  telemetry: telemetry
@@ -10274,13 +11004,14 @@ async function runDeduped(args) {
10274
11004
  }
10275
11005
  }
10276
11006
 
10277
- async function cascade({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
11007
+ async function cascade({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
10278
11008
  const count = await cascadeAutoTransitions({
10279
11009
  client: client,
10280
11010
  instanceId: instanceId,
10281
11011
  actor: actor,
10282
11012
  clientForGdr: clientForGdr,
10283
11013
  refSurface: refSurface,
11014
+ memberRolesLoader: memberRolesLoader,
10284
11015
  ...clock !== void 0 ? {
10285
11016
  clock: clock
10286
11017
  } : {},
@@ -10300,6 +11031,7 @@ async function cascade({client: client, instanceId: instanceId, actor: actor, cl
10300
11031
  actor: actor,
10301
11032
  clientForGdr: clientForGdr,
10302
11033
  refSurface: refSurface,
11034
+ memberRolesLoader: memberRolesLoader,
10303
11035
  ...clock !== void 0 ? {
10304
11036
  clock: clock
10305
11037
  } : {},
@@ -10313,7 +11045,7 @@ async function cascade({client: client, instanceId: instanceId, actor: actor, cl
10313
11045
  }
10314
11046
 
10315
11047
  async function cascadeAndReload(args) {
10316
- const {client: client, tag: tag, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock} = args;
11048
+ const {client: client, tag: tag, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock} = args;
10317
11049
  return {
10318
11050
  cascaded: await cascade({
10319
11051
  client: client,
@@ -10321,6 +11053,7 @@ async function cascadeAndReload(args) {
10321
11053
  actor: actor,
10322
11054
  clientForGdr: clientForGdr,
10323
11055
  refSurface: refSurface,
11056
+ memberRolesLoader: memberRolesLoader,
10324
11057
  clock: clock,
10325
11058
  ...args.executionContext !== void 0 ? {
10326
11059
  executionContext: args.executionContext
@@ -10344,6 +11077,7 @@ async function settleStart(args) {
10344
11077
  actor: args.actor,
10345
11078
  clientForGdr: args.clientForGdr,
10346
11079
  refSurface: args.refSurface,
11080
+ memberRolesLoader: args.memberRolesLoader,
10347
11081
  clock: args.clock,
10348
11082
  ...args.executionContext !== void 0 ? {
10349
11083
  executionContext: args.executionContext
@@ -10386,31 +11120,20 @@ function resumeMismatch(args) {
10386
11120
  }
10387
11121
 
10388
11122
  async function resumeStart(args) {
10389
- const {existing: existing} = args, mismatch = resumeMismatch({
11123
+ const {existing: existing, definition: definition, version: version, initialFieldCount: initialFieldCount, ...settlementArgs} = args, mismatch = resumeMismatch({
10390
11124
  existing: existing,
10391
- definition: args.definition,
10392
- version: args.version
11125
+ definition: definition,
11126
+ version: version
10393
11127
  });
10394
11128
  if (mismatch !== void 0) throw new invariants.ContractViolationError(`startInstance: instanceId "${existing._id}" already exists and ${mismatch}. A supplied instanceId is start's idempotency key — reuse one only to retry the same start.`);
10395
11129
  if (existing.stages.length === 0 && terminalState(existing) !== "in-flight") throw new invariants.ContractViolationError(`startInstance: instanceId "${existing._id}" belongs to a start that was discarded (aborted before it finished starting) — mint a new id to start again.`);
10396
11130
  const completesStart = isUnprimed(existing), emitStarted = () => resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedDataFor({
10397
11131
  instance: existing,
10398
- initialFieldCount: args.initialFieldCount,
11132
+ initialFieldCount: initialFieldCount,
10399
11133
  viaSpawn: !1
10400
11134
  })), cascaded = await settleStart({
10401
- client: args.client,
10402
- tag: args.tag,
11135
+ ...settlementArgs,
10403
11136
  instanceId: existing._id,
10404
- actor: args.actor,
10405
- clientForGdr: args.clientForGdr,
10406
- refSurface: args.refSurface,
10407
- clock: args.clock,
10408
- ...args.executionContext !== void 0 ? {
10409
- executionContext: args.executionContext
10410
- } : {},
10411
- ...args.telemetry !== void 0 ? {
10412
- telemetry: args.telemetry
10413
- } : {},
10414
11137
  ...completesStart ? {
10415
11138
  emitStarted: emitStarted
10416
11139
  } : {}
@@ -10426,7 +11149,7 @@ async function resumeStart(args) {
10426
11149
  };
10427
11150
  }
10428
11151
 
10429
- function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: clientForGdr, refSurface: refSurface, executionContext: executionContext, telemetry: telemetry}) {
11152
+ function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, executionContext: executionContext, telemetry: telemetry}) {
10430
11153
  return {
10431
11154
  ...actor !== void 0 ? {
10432
11155
  actor: actor
@@ -10434,6 +11157,7 @@ function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: client
10434
11157
  clock: clock,
10435
11158
  clientForGdr: clientForGdr,
10436
11159
  refSurface: refSurface,
11160
+ memberRolesLoader: memberRolesLoader,
10437
11161
  ...executionContext !== void 0 ? {
10438
11162
  executionContext: executionContext
10439
11163
  } : {},
@@ -10444,7 +11168,7 @@ function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: client
10444
11168
  }
10445
11169
 
10446
11170
  async function abortAndPropagate(args) {
10447
- const {client: client, instanceId: instanceId, reason: reason, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, requestRecord: requestRecord} = args, result = await abortInstance({
11171
+ const {client: client, instanceId: instanceId, reason: reason, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, requestRecord: requestRecord} = args, result = await abortInstance({
10448
11172
  client: client,
10449
11173
  instanceId: instanceId,
10450
11174
  ...reason !== void 0 ? {
@@ -10458,6 +11182,7 @@ async function abortAndPropagate(args) {
10458
11182
  clock: clock,
10459
11183
  clientForGdr: clientForGdr,
10460
11184
  refSurface: refSurface,
11185
+ memberRolesLoader: memberRolesLoader,
10461
11186
  executionContext: executionContext,
10462
11187
  telemetry: telemetry
10463
11188
  })
@@ -10467,6 +11192,7 @@ async function abortAndPropagate(args) {
10467
11192
  actor: actor,
10468
11193
  clientForGdr: clientForGdr,
10469
11194
  refSurface: refSurface,
11195
+ memberRolesLoader: memberRolesLoader,
10470
11196
  clock: clock,
10471
11197
  ...executionContext !== void 0 ? {
10472
11198
  executionContext: executionContext
@@ -10484,6 +11210,7 @@ async function abortAndPropagate(args) {
10484
11210
  actor: actor,
10485
11211
  clientForGdr: clientForGdr,
10486
11212
  refSurface: refSurface,
11213
+ memberRolesLoader: memberRolesLoader,
10487
11214
  clock: clock,
10488
11215
  ...executionContext !== void 0 ? {
10489
11216
  executionContext: executionContext
@@ -10525,6 +11252,7 @@ async function dispatchGatedWrite(args) {
10525
11252
  actor: args.actor,
10526
11253
  clientForGdr: args.clientForGdr,
10527
11254
  refSurface: args.refSurface,
11255
+ memberRolesLoader: args.memberRolesLoader,
10528
11256
  clock: args.clock,
10529
11257
  executionContext: args.executionContext,
10530
11258
  telemetry: args.telemetry
@@ -10600,12 +11328,16 @@ function buildEngineCallOptions(args) {
10600
11328
  actor: args.actor,
10601
11329
  clientForGdr: args.clientForGdr,
10602
11330
  refSurface: args.refSurface,
11331
+ memberRolesLoader: args.memberRolesLoader,
10603
11332
  ...args.localPrincipalId !== void 0 ? {
10604
11333
  localPrincipalId: args.localPrincipalId
10605
11334
  } : {},
10606
11335
  ...args.grants !== void 0 ? {
10607
11336
  grants: args.grants
10608
11337
  } : {},
11338
+ ...args.attributes !== void 0 ? {
11339
+ attributes: args.attributes
11340
+ } : {},
10609
11341
  ...args.clock !== void 0 ? {
10610
11342
  clock: args.clock
10611
11343
  } : {},
@@ -10636,7 +11368,7 @@ async function applyAction(args) {
10636
11368
  }
10637
11369
 
10638
11370
  async function deleteDefinition(args) {
10639
- const {client: client, tag: tag, definition: definition, version: version, cascade: cascade2, reason: reason, executionContext: executionContext, telemetry: telemetry} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, versions = await loadDefinitionVersionsOrThrow({
11371
+ const {client: client, tag: tag, definition: definition, version: version, cascade: cascade2, reason: reason, executionContext: executionContext, telemetry: telemetry} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader} = await resolveOperationContext(args), clock = args.clock ?? wallClock, versions = await loadDefinitionVersionsOrThrow({
10640
11372
  client: client,
10641
11373
  definition: definition,
10642
11374
  tag: tag
@@ -10675,6 +11407,7 @@ async function deleteDefinition(args) {
10675
11407
  actor: actor,
10676
11408
  clientForGdr: clientForGdr,
10677
11409
  refSurface: refSurface,
11410
+ memberRolesLoader: memberRolesLoader,
10678
11411
  clock: clock,
10679
11412
  executionContext: executionContext,
10680
11413
  telemetry: telemetry
@@ -10733,6 +11466,7 @@ async function abortInstances(args) {
10733
11466
  actor: actor,
10734
11467
  clientForGdr: clientForGdr,
10735
11468
  refSurface: args.refSurface,
11469
+ memberRolesLoader: args.memberRolesLoader,
10736
11470
  clock: clock,
10737
11471
  ...executionContext !== void 0 ? {
10738
11472
  executionContext: executionContext
@@ -10765,11 +11499,12 @@ async function fetchStartSlice(args) {
10765
11499
 
10766
11500
  const workflow = {
10767
11501
  deployDefinitions: async rawArgs => {
10768
- assertReaderModelAcknowledgement(rawArgs.expectedMinReaderModel);
10769
- const args = taggedScope(rawArgs, REQUEST_TAG.deploy), {client: client, tag: tag, resourceAliases: resourceAliases} = args;
11502
+ const args = taggedScope(rawArgs, REQUEST_TAG.deploy), {client: client, tag: tag, workflowResource: workflowResource, resourceAliases: resourceAliases} = args;
10770
11503
  invariants.validateTag(tag);
10771
11504
  const definitions = args.definitions.map(def => parseDefinitionInput(def, "workflow.deployDefinitions"));
10772
- definitions.forEach(validateDefinition);
11505
+ definitions.forEach(validateDefinition), assertReaderModelAcknowledgement(args.expectedMinReaderModel, {
11506
+ requiredMinReaderModel: requiredDefinitionReaderModel(definitions)
11507
+ });
10773
11508
  const ordered = await sortByDependencies({
10774
11509
  client: client,
10775
11510
  definitions: definitions,
@@ -10788,6 +11523,7 @@ const workflow = {
10788
11523
  latest: latest,
10789
11524
  target: {
10790
11525
  tag: tag,
11526
+ workflowResource: workflowResource,
10791
11527
  ...resourceAliases !== void 0 ? {
10792
11528
  resourceAliases: resourceAliases
10793
11529
  } : {}
@@ -10838,7 +11574,7 @@ const workflow = {
10838
11574
  }), result;
10839
11575
  },
10840
11576
  startInstance: async rawArgs => {
10841
- const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, definition: definitionName, version: version, initialFields: initialFields, instanceId: instanceId, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, clock = args.clock ?? wallClock, seedFields = initialFields ?? [], existing = instanceId !== void 0 ? await getInstanceDocument(client, instanceId) : void 0;
11577
+ const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, definition: definitionName, version: version, initialFields: initialFields, instanceId: instanceId, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader} = operationContext, clock = args.clock ?? wallClock, seedFields = initialFields ?? [], existing = instanceId !== void 0 ? await getInstanceDocument(client, instanceId) : void 0;
10842
11578
  return existing !== void 0 && existing.tag === tag ? resumeStart({
10843
11579
  client: client,
10844
11580
  tag: tag,
@@ -10849,6 +11585,7 @@ const workflow = {
10849
11585
  actor: actor,
10850
11586
  clientForGdr: clientForGdr,
10851
11587
  refSurface: refSurface,
11588
+ memberRolesLoader: memberRolesLoader,
10852
11589
  clock: clock,
10853
11590
  ...executionContext !== void 0 ? {
10854
11591
  executionContext: executionContext
@@ -10868,7 +11605,7 @@ const workflow = {
10868
11605
  return runCommitVerb({
10869
11606
  args: args,
10870
11607
  op: "fireAction",
10871
- run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
11608
+ run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
10872
11609
  if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return {
10873
11610
  instance: before,
10874
11611
  cascaded: 0,
@@ -10882,6 +11619,7 @@ const workflow = {
10882
11619
  actor: actor,
10883
11620
  clientForGdr: clientForGdr,
10884
11621
  refSurface: refSurface,
11622
+ memberRolesLoader: memberRolesLoader,
10885
11623
  clock: clock,
10886
11624
  before: before,
10887
11625
  ...grantsFromPath !== void 0 ? {
@@ -10915,6 +11653,7 @@ const workflow = {
10915
11653
  clock: clock,
10916
11654
  clientForGdr: clientForGdr,
10917
11655
  refSurface: refSurface,
11656
+ memberRolesLoader: memberRolesLoader,
10918
11657
  ...executionContext !== void 0 ? {
10919
11658
  executionContext: executionContext
10920
11659
  } : {},
@@ -10943,7 +11682,7 @@ const workflow = {
10943
11682
  return runCommitVerb({
10944
11683
  args: args,
10945
11684
  op: "editField",
10946
- run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
11685
+ run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
10947
11686
  const result = await dispatchGatedWrite({
10948
11687
  client: client,
10949
11688
  tag: tag,
@@ -10952,6 +11691,7 @@ const workflow = {
10952
11691
  actor: actor,
10953
11692
  clientForGdr: clientForGdr,
10954
11693
  refSurface: refSurface,
11694
+ memberRolesLoader: memberRolesLoader,
10955
11695
  clock: clock,
10956
11696
  before: before,
10957
11697
  ...grantsFromPath !== void 0 ? {
@@ -10986,6 +11726,7 @@ const workflow = {
10986
11726
  clock: clock,
10987
11727
  clientForGdr: clientForGdr,
10988
11728
  refSurface: refSurface,
11729
+ memberRolesLoader: memberRolesLoader,
10989
11730
  ...executionContext !== void 0 ? {
10990
11731
  executionContext: executionContext
10991
11732
  } : {},
@@ -11006,7 +11747,7 @@ const workflow = {
11006
11747
  });
11007
11748
  },
11008
11749
  completeEffect: async rawArgs => {
11009
- const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
11750
+ const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
11010
11751
  idempotencyKey: args.idempotencyKey,
11011
11752
  op: "completeEffect",
11012
11753
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11017,9 +11758,7 @@ const workflow = {
11017
11758
  instanceId: instanceId,
11018
11759
  record: record,
11019
11760
  now: clock(),
11020
- actor: actor,
11021
- clientForGdr: clientForGdr,
11022
- refSurface: refSurface,
11761
+ ...operationContext,
11023
11762
  clock: clock,
11024
11763
  executionContext: executionContext,
11025
11764
  telemetry: args.telemetry,
@@ -11048,10 +11787,8 @@ const workflow = {
11048
11787
  requestRecord: record
11049
11788
  } : {},
11050
11789
  options: engineOptionsForActor({
11051
- actor: actor,
11790
+ ...operationContext,
11052
11791
  clock: clock,
11053
- clientForGdr: clientForGdr,
11054
- refSurface: refSurface,
11055
11792
  executionContext: executionContext,
11056
11793
  telemetry: args.telemetry
11057
11794
  })
@@ -11059,9 +11796,7 @@ const workflow = {
11059
11796
  client: client,
11060
11797
  tag: tag,
11061
11798
  instanceId: instanceId,
11062
- actor: actor,
11063
- clientForGdr: clientForGdr,
11064
- refSurface: refSurface,
11799
+ ...operationContext,
11065
11800
  clock: clock,
11066
11801
  executionContext: executionContext,
11067
11802
  telemetry: args.telemetry
@@ -11082,7 +11817,7 @@ const workflow = {
11082
11817
  });
11083
11818
  },
11084
11819
  commitEffectOps: async rawArgs => {
11085
- const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
11820
+ const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
11086
11821
  idempotencyKey: args.idempotencyKey,
11087
11822
  op: "commitEffectOps",
11088
11823
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11094,9 +11829,7 @@ const workflow = {
11094
11829
  instanceId: instanceId,
11095
11830
  record: record,
11096
11831
  now: clock(),
11097
- actor: actor,
11098
- clientForGdr: clientForGdr,
11099
- refSurface: refSurface,
11832
+ ...operationContext,
11100
11833
  clock: clock,
11101
11834
  executionContext: executionContext,
11102
11835
  telemetry: args.telemetry,
@@ -11110,10 +11843,8 @@ const workflow = {
11110
11843
  requestRecord: record,
11111
11844
  leaseMs: args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS,
11112
11845
  options: engineOptionsForActor({
11113
- actor: actor,
11846
+ ...operationContext,
11114
11847
  clock: clock,
11115
- clientForGdr: clientForGdr,
11116
- refSurface: refSurface,
11117
11848
  executionContext: executionContext,
11118
11849
  telemetry: args.telemetry
11119
11850
  })
@@ -11121,9 +11852,7 @@ const workflow = {
11121
11852
  client: client,
11122
11853
  tag: tag,
11123
11854
  instanceId: instanceId,
11124
- actor: actor,
11125
- clientForGdr: clientForGdr,
11126
- refSurface: refSurface,
11855
+ ...operationContext,
11127
11856
  clock: clock,
11128
11857
  executionContext: executionContext,
11129
11858
  telemetry: args.telemetry
@@ -11142,7 +11871,7 @@ const workflow = {
11142
11871
  });
11143
11872
  },
11144
11873
  tick: async rawArgs => {
11145
- const args = taggedScope(rawArgs, REQUEST_TAG.tick), {client: client, tag: tag, instanceId: instanceId, executionContext: executionContext} = args, {access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload({
11874
+ const args = taggedScope(rawArgs, REQUEST_TAG.tick), {client: client, tag: tag, instanceId: instanceId, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), {access: access} = operationContext, clock = args.clock ?? wallClock, current = await reload({
11146
11875
  client: client,
11147
11876
  instanceId: instanceId,
11148
11877
  tag: tag
@@ -11156,9 +11885,7 @@ const workflow = {
11156
11885
  client: client,
11157
11886
  tag: tag,
11158
11887
  instanceId: instanceId,
11159
- actor: actor,
11160
- clientForGdr: clientForGdr,
11161
- refSurface: refSurface,
11888
+ ...operationContext,
11162
11889
  clock: clock,
11163
11890
  executionContext: executionContext,
11164
11891
  telemetry: args.telemetry
@@ -11178,7 +11905,7 @@ const workflow = {
11178
11905
  return runCommitVerb({
11179
11906
  args: args,
11180
11907
  op: "setStage",
11181
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
11908
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
11182
11909
  const result = await setStage({
11183
11910
  client: client,
11184
11911
  instanceId: instanceId,
@@ -11194,6 +11921,7 @@ const workflow = {
11194
11921
  clock: clock,
11195
11922
  clientForGdr: clientForGdr,
11196
11923
  refSurface: refSurface,
11924
+ memberRolesLoader: memberRolesLoader,
11197
11925
  executionContext: executionContext,
11198
11926
  telemetry: args.telemetry
11199
11927
  })
@@ -11203,6 +11931,7 @@ const workflow = {
11203
11931
  actor: actor,
11204
11932
  clientForGdr: clientForGdr,
11205
11933
  refSurface: refSurface,
11934
+ memberRolesLoader: memberRolesLoader,
11206
11935
  clock: clock,
11207
11936
  ...executionContext !== void 0 ? {
11208
11937
  executionContext: executionContext
@@ -11232,7 +11961,7 @@ const workflow = {
11232
11961
  return runCommitVerb({
11233
11962
  args: args,
11234
11963
  op: "abortInstance",
11235
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
11964
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
11236
11965
  const changed = await abortAndPropagate({
11237
11966
  client: client,
11238
11967
  instanceId: instanceId,
@@ -11240,6 +11969,7 @@ const workflow = {
11240
11969
  actor: actor,
11241
11970
  clientForGdr: clientForGdr,
11242
11971
  refSurface: refSurface,
11972
+ memberRolesLoader: memberRolesLoader,
11243
11973
  clock: clock,
11244
11974
  ...executionContext !== void 0 ? {
11245
11975
  executionContext: executionContext
@@ -11273,7 +12003,7 @@ const workflow = {
11273
12003
  return runCommitVerb({
11274
12004
  args: args,
11275
12005
  op: "resetActivity",
11276
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, record: record, before: before}) => {
12006
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
11277
12007
  const result = await resetActivity({
11278
12008
  client: client,
11279
12009
  instanceId: instanceId,
@@ -11287,6 +12017,7 @@ const workflow = {
11287
12017
  clock: clock,
11288
12018
  clientForGdr: clientForGdr,
11289
12019
  refSurface: refSurface,
12020
+ memberRolesLoader: memberRolesLoader,
11290
12021
  executionContext: executionContext,
11291
12022
  telemetry: args.telemetry
11292
12023
  })
@@ -11296,6 +12027,7 @@ const workflow = {
11296
12027
  actor: actor,
11297
12028
  clientForGdr: clientForGdr,
11298
12029
  refSurface: refSurface,
12030
+ memberRolesLoader: memberRolesLoader,
11299
12031
  clock: clock,
11300
12032
  ...executionContext !== void 0 ? {
11301
12033
  executionContext: executionContext
@@ -11601,7 +12333,7 @@ async function startRequirementVerdict(args) {
11601
12333
  }
11602
12334
 
11603
12335
  async function startFreshInstance(args) {
11604
- const {args: startArgs, operationContext: operationContext, clock: clock, seedFields: seedFields} = args, {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = startArgs, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, definition = await loadDefinition({
12336
+ const {args: startArgs, operationContext: operationContext, clock: clock, seedFields: seedFields} = args, {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = startArgs, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader} = operationContext, definition = await loadDefinition({
11605
12337
  client: client,
11606
12338
  definition: definitionName,
11607
12339
  version: version,
@@ -11625,6 +12357,7 @@ async function startFreshInstance(args) {
11625
12357
  id: id,
11626
12358
  now: now,
11627
12359
  refSurface: refSurface,
12360
+ memberRolesLoader: memberRolesLoader,
11628
12361
  ...perspective !== void 0 ? {
11629
12362
  perspective: perspective
11630
12363
  } : {}
@@ -11656,6 +12389,7 @@ async function startFreshInstance(args) {
11656
12389
  actor: actor,
11657
12390
  clientForGdr: clientForGdr,
11658
12391
  refSurface: refSurface,
12392
+ memberRolesLoader: memberRolesLoader,
11659
12393
  clock: clock,
11660
12394
  ...executionContext !== void 0 ? {
11661
12395
  executionContext: executionContext
@@ -11711,7 +12445,10 @@ async function assertStartInputs(args) {
11711
12445
  }
11712
12446
 
11713
12447
  async function resolveStartFields(args) {
11714
- const {client: client, tag: tag, workflowResource: workflowResource, definition: definition, initialFields: initialFields, id: id, now: now, refSurface: refSurface, perspective: perspective} = args, fieldDiscards = [];
12448
+ const {client: client, tag: tag, workflowResource: workflowResource, definition: definition, initialFields: initialFields, id: id, now: now, refSurface: refSurface, memberRolesLoader: memberRolesLoader, perspective: perspective} = args, fieldDiscards = [], memberRoles = await memberRolesLoader({
12449
+ workflowResource: workflowResource,
12450
+ definition: definition
12451
+ });
11715
12452
  return {
11716
12453
  resolvedFields: await resolveDeclaredFields({
11717
12454
  entryDefs: definition.fields ?? [],
@@ -11724,6 +12461,8 @@ async function resolveStartFields(args) {
11724
12461
  workflowResource: workflowResource,
11725
12462
  definitionName: definition.name,
11726
12463
  refSurface: refSurface,
12464
+ memberRoles: memberRoles,
12465
+ roleAliases: definition.roleAliases,
11727
12466
  ...perspective !== void 0 ? {
11728
12467
  perspective: perspective
11729
12468
  } : {},
@@ -12364,7 +13103,7 @@ function createInstanceSession(args) {
12364
13103
  refSurface: evalScope.refSurface
12365
13104
  });
12366
13105
  }, evaluateWith = async ({held: held, guards: guards, self: self}) => {
12367
- const {actor: actor, localPrincipalId: localPrincipalId, grants: grants} = await access(), resourceGrants = await subjectResourceGrants({
13106
+ const [{actor: actor, localPrincipalId: localPrincipalId, grants: grants}, attributes] = await Promise.all([ access(), resolveUserAttributes(client) ]), resourceGrants = await subjectResourceGrants({
12368
13107
  clientForGdr: evalScope.clientForGdr,
12369
13108
  instance: instance
12370
13109
  }), normalizedSelf = await normalizeInstanceIdentities({
@@ -12386,13 +13125,17 @@ function createInstanceSession(args) {
12386
13125
  } : {},
12387
13126
  ...grants !== void 0 ? {
12388
13127
  grants: grants
13128
+ } : {},
13129
+ ...attributes !== void 0 ? {
13130
+ attributes: attributes
12389
13131
  } : {}
12390
13132
  });
12391
- }, settleAfterApply = async ({actor: actor, held: held, ranOps: ranOps, scope: scope}) => {
13133
+ }, settleAfterApply = async ({actor: actor, held: held, ranOps: ranOps, scope: scope, memberRolesLoader: memberRolesLoader}) => {
12392
13134
  const cascaded = await cascadeHeld({
12393
13135
  scope: scope,
12394
13136
  actor: actor,
12395
- held: held
13137
+ held: held,
13138
+ memberRolesLoader: memberRolesLoader
12396
13139
  });
12397
13140
  return instance = await reload({
12398
13141
  client: scope.client,
@@ -12406,12 +13149,13 @@ function createInstanceSession(args) {
12406
13149
  ranOps: ranOps
12407
13150
  } : {}
12408
13151
  };
12409
- }, cascadeHeld = ({scope: scope, actor: actor, held: held}) => cascade({
13152
+ }, cascadeHeld = ({scope: scope, actor: actor, held: held, memberRolesLoader: memberRolesLoader}) => cascade({
12410
13153
  client: scope.client,
12411
13154
  instanceId: instance._id,
12412
13155
  actor: actor,
12413
13156
  clientForGdr: scope.clientForGdr,
12414
13157
  refSurface: scope.refSurface,
13158
+ memberRolesLoader: memberRolesLoader,
12415
13159
  ...clock !== void 0 ? {
12416
13160
  clock: clock
12417
13161
  } : {},
@@ -12508,6 +13252,7 @@ function createInstanceSession(args) {
12508
13252
  },
12509
13253
  tick() {
12510
13254
  return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
13255
+ const memberRolesLoader = createAssignmentMemberRolesLoader(tickScope.client);
12511
13256
  await assertInstanceWriteAllowed({
12512
13257
  instance: instance,
12513
13258
  guards: heldGuards,
@@ -12523,7 +13268,8 @@ function createInstanceSession(args) {
12523
13268
  }), cascaded = await cascadeHeld({
12524
13269
  scope: tickScope,
12525
13270
  actor: actor,
12526
- held: held
13271
+ held: held,
13272
+ memberRolesLoader: memberRolesLoader
12527
13273
  });
12528
13274
  return instance = await reload({
12529
13275
  client: tickScope.client,
@@ -12542,6 +13288,7 @@ function createInstanceSession(args) {
12542
13288
  },
12543
13289
  fireAction({activity: activity, action: action, params: params}) {
12544
13290
  return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
13291
+ const memberRolesLoader = createAssignmentMemberRolesLoader(fireScope.client);
12545
13292
  assertActionAllowed({
12546
13293
  evaluation: await evaluateWith({
12547
13294
  held: held,
@@ -12562,6 +13309,7 @@ function createInstanceSession(args) {
12562
13309
  } : {},
12563
13310
  clientForGdr: fireScope.clientForGdr,
12564
13311
  refSurface: fireScope.refSurface,
13312
+ memberRolesLoader: memberRolesLoader,
12565
13313
  ...grants !== void 0 ? {
12566
13314
  grants: grants
12567
13315
  } : {},
@@ -12581,7 +13329,8 @@ function createInstanceSession(args) {
12581
13329
  actor: actor,
12582
13330
  held: held,
12583
13331
  ranOps: ranOps,
12584
- scope: fireScope
13332
+ scope: fireScope,
13333
+ memberRolesLoader: memberRolesLoader
12585
13334
  });
12586
13335
  return telemetry.log(WorkflowActionFired, actionFiredData({
12587
13336
  instance: preOp,
@@ -12603,6 +13352,7 @@ function createInstanceSession(args) {
12603
13352
  return Promise.reject(err);
12604
13353
  }
12605
13354
  return commit(async ({actor: actor, localPrincipalId: localPrincipalId}, held) => {
13355
+ const memberRolesLoader = createAssignmentMemberRolesLoader(editScope.client);
12606
13356
  assertEditAllowed(await evaluateWith({
12607
13357
  held: held,
12608
13358
  guards: heldGuards,
@@ -12624,6 +13374,7 @@ function createInstanceSession(args) {
12624
13374
  } : {},
12625
13375
  clientForGdr: editScope.clientForGdr,
12626
13376
  refSurface: editScope.refSurface,
13377
+ memberRolesLoader: memberRolesLoader,
12627
13378
  ...grants !== void 0 ? {
12628
13379
  grants: grants
12629
13380
  } : {},
@@ -12640,7 +13391,8 @@ function createInstanceSession(args) {
12640
13391
  actor: actor,
12641
13392
  held: held,
12642
13393
  ranOps: ranOps,
12643
- scope: editScope
13394
+ scope: editScope,
13395
+ memberRolesLoader: memberRolesLoader
12644
13396
  });
12645
13397
  return telemetry.log(WorkflowFieldEdited, fieldEditedData({
12646
13398
  instance: preOp,
@@ -12959,8 +13711,11 @@ function attributeMember({member: member, group: group, chain: chain}) {
12959
13711
  }
12960
13712
 
12961
13713
  function diffEntry({def: rawDef, latestRaw: latestRaw, target: target}) {
12962
- assertReaderModelAcknowledgement(target.expectedMinReaderModel);
12963
- const def = parseDefinitionInput(rawDef, "diffEntry"), plan = planDefinitionDeploy({
13714
+ const def = parseDefinitionInput(rawDef, "diffEntry");
13715
+ assertReaderModelAcknowledgement(target.expectedMinReaderModel, {
13716
+ requiredMinReaderModel: requiredDefinitionReaderModel([ def ])
13717
+ });
13718
+ const plan = planDefinitionDeploy({
12964
13719
  def: def,
12965
13720
  latest: asLatest(latestRaw),
12966
13721
  target: target
@@ -12991,10 +13746,13 @@ function asLatest(raw) {
12991
13746
  }
12992
13747
 
12993
13748
  async function computeDiffEntries({client: client, defs: defs, target: target}) {
12994
- assertReaderModelAcknowledgement(target.expectedMinReaderModel);
13749
+ const definitions = defs.map(def => parseDefinitionInput(def, "computeDiffEntries"));
13750
+ assertReaderModelAcknowledgement(target.expectedMinReaderModel, {
13751
+ requiredMinReaderModel: requiredDefinitionReaderModel(definitions)
13752
+ });
12995
13753
  const entries = [];
12996
- for (const rawDef of defs) {
12997
- const def = parseDefinitionInput(rawDef, "computeDiffEntries"), latest = await loadLatestDeployed({
13754
+ for (const def of definitions) {
13755
+ const latest = await loadLatestDeployed({
12998
13756
  client: client,
12999
13757
  definition: def.name,
13000
13758
  tag: target.tag
@@ -13081,7 +13839,7 @@ const HISTORY_DISPLAY = {
13081
13839
  },
13082
13840
  opApplied: {
13083
13841
  title: "Op applied",
13084
- description: "An inline field-mutation op (field.set / field.append / status.set / etc.) ran during an action commit."
13842
+ description: "An action, direct edit, or effect completion executed an operation and recorded its audit payload."
13085
13843
  },
13086
13844
  fieldQueryDiscarded: {
13087
13845
  title: "Query result discarded",
@@ -13186,6 +13944,10 @@ const HISTORY_DISPLAY = {
13186
13944
  title: "Set field entry",
13187
13945
  description: "Overwrite a field entry's value with a resolved value expression."
13188
13946
  },
13947
+ "field.setIfMissing": {
13948
+ title: "Set field entry if missing",
13949
+ description: "Set a nullable field entry's value only when it is currently null or undefined; array, assignees, and doc.refs entries are always arrays and are not supported."
13950
+ },
13189
13951
  "field.unset": {
13190
13952
  title: "Unset field entry",
13191
13953
  description: "Reset a field entry to its default (null for scalars, [] for array kinds)."
@@ -13194,6 +13956,14 @@ const HISTORY_DISPLAY = {
13194
13956
  title: "Append to field entry",
13195
13957
  description: "Push a resolved item onto an array-kind entry (array, assignees, doc.refs)."
13196
13958
  },
13959
+ "field.inc": {
13960
+ title: "Increment field entry",
13961
+ description: "Add a resolved numeric delta, defaulting to 1, to a number entry that already holds a finite number."
13962
+ },
13963
+ "field.dec": {
13964
+ title: "Decrement field entry",
13965
+ description: "Subtract a resolved numeric delta, defaulting to 1, from a number entry that already holds a finite number."
13966
+ },
13197
13967
  "field.updateWhere": {
13198
13968
  title: "Update matching rows",
13199
13969
  description: "Merge declared sub-fields into rows of an `array` entry that match the `where` condition (`array` only — `doc.refs`/`assignees` rows are engine-shaped)."
@@ -13319,6 +14089,8 @@ exports.CONDITION_VARS = invariants.CONDITION_VARS;
13319
14089
 
13320
14090
  exports.ContractViolationError = invariants.ContractViolationError;
13321
14091
 
14092
+ exports.DECISION_SEMANTICS = invariants.DECISION_SEMANTICS;
14093
+
13322
14094
  exports.DEFAULT_TRANSITION_WHEN = invariants.DEFAULT_TRANSITION_WHEN;
13323
14095
 
13324
14096
  exports.DRIVER_KINDS = invariants.DRIVER_KINDS;
@@ -13345,6 +14117,8 @@ exports.PersistedDocShapeError = invariants.PersistedDocShapeError;
13345
14117
 
13346
14118
  exports.RESERVED_CONDITION_VARS = invariants.RESERVED_CONDITION_VARS;
13347
14119
 
14120
+ exports.SIGNAL_SEMANTICS = invariants.SIGNAL_SEMANTICS;
14121
+
13348
14122
  exports.START_FILTER_VARS = invariants.START_FILTER_VARS;
13349
14123
 
13350
14124
  exports.START_REQUIREMENT_VARS = invariants.START_REQUIREMENT_VARS;
@@ -13357,6 +14131,8 @@ exports.WORKFLOW_DEFINITION_TYPE = invariants.WORKFLOW_DEFINITION_TYPE;
13357
14131
 
13358
14132
  exports.WorkflowError = invariants.WorkflowError;
13359
14133
 
14134
+ exports.actorFulfillsRole = invariants.actorFulfillsRole;
14135
+
13360
14136
  exports.classifyPrincipalId = invariants.classifyPrincipalId;
13361
14137
 
13362
14138
  exports.clientConfigFromResource = invariants.clientConfigFromResource;
@@ -13571,6 +14347,8 @@ exports.ConcurrentFireActionError = ConcurrentFireActionError;
13571
14347
 
13572
14348
  exports.DATA_MODEL_CHANGES = DATA_MODEL_CHANGES;
13573
14349
 
14350
+ exports.DATA_MODEL_MAX_READER = DATA_MODEL_MAX_READER;
14351
+
13574
14352
  exports.DATA_MODEL_MIN_READER = DATA_MODEL_MIN_READER;
13575
14353
 
13576
14354
  exports.DATA_MODEL_VERSION = DATA_MODEL_VERSION;
@@ -13663,6 +14441,8 @@ exports.WorkflowFieldEdited = WorkflowFieldEdited;
13663
14441
 
13664
14442
  exports.WorkflowInstanceAborted = WorkflowInstanceAborted;
13665
14443
 
14444
+ exports.WorkflowInstancePreviewSchema = WorkflowInstancePreviewSchema;
14445
+
13666
14446
  exports.WorkflowInstanceSchema = WorkflowInstanceSchema;
13667
14447
 
13668
14448
  exports.WorkflowInstanceStarted = WorkflowInstanceStarted;
@@ -13777,6 +14557,8 @@ exports.documentActionDenials = documentActionDenials;
13777
14557
 
13778
14558
  exports.documentPrefilter = documentPrefilter;
13779
14559
 
14560
+ exports.documentStuckCause = documentStuckCause;
14561
+
13780
14562
  exports.effectOutputsMap = effectOutputsMap;
13781
14563
 
13782
14564
  exports.entryDocRefs = entryDocRefs;
@@ -13793,10 +14575,14 @@ exports.explainStartRequirement = explainStartRequirement;
13793
14575
 
13794
14576
  exports.fieldTreeShape = fieldTreeShape;
13795
14577
 
14578
+ exports.findActivityNode = findActivityNode;
14579
+
13796
14580
  exports.findCurrentActivityEntry = findCurrentActivityEntry;
13797
14581
 
13798
14582
  exports.findOpenStageEntry = findOpenStageEntry;
13799
14583
 
14584
+ exports.findStageNode = findStageNode;
14585
+
13800
14586
  exports.groupSitesOf = groupSitesOf;
13801
14587
 
13802
14588
  exports.guardMatches = guardMatches;
@@ -13815,10 +14601,14 @@ exports.inFlightFilter = inFlightFilter;
13815
14601
 
13816
14602
  exports.initialFieldIssues = initialFieldIssues;
13817
14603
 
14604
+ exports.instanceChangesQuery = instanceChangesQuery;
14605
+
13818
14606
  exports.instanceDocId = instanceDocId;
13819
14607
 
13820
14608
  exports.instanceGuardQuery = instanceGuardQuery;
13821
14609
 
14610
+ exports.instancePreviewsQuery = instancePreviewsQuery;
14611
+
13822
14612
  exports.instanceWatchesDocument = instanceWatchesDocument;
13823
14613
 
13824
14614
  exports.instancesGuardQuery = instancesGuardQuery;
@@ -13873,6 +14663,8 @@ exports.parseGuardDocument = parseGuardDocument;
13873
14663
 
13874
14664
  exports.parseInstanceDocument = parseInstanceDocument;
13875
14665
 
14666
+ exports.parseInstancePreviewDocument = parseInstancePreviewDocument;
14667
+
13876
14668
  exports.processShellUserProperties = processShellUserProperties;
13877
14669
 
13878
14670
  exports.projectStartSliceRow = projectStartSliceRow;
@@ -13881,12 +14673,16 @@ exports.projectToWatchRef = projectToWatchRef;
13881
14673
 
13882
14674
  exports.readInstanceDoc = readInstanceDoc;
13883
14675
 
14676
+ exports.readInstancePreviewDoc = readInstancePreviewDoc;
14677
+
13884
14678
  exports.readsRaw = readsRaw;
13885
14679
 
13886
14680
  exports.refsOf = refsOf;
13887
14681
 
13888
14682
  exports.remediationsFor = remediationsFor;
13889
14683
 
14684
+ exports.requiredDefinitionReaderModel = requiredDefinitionReaderModel;
14685
+
13890
14686
  exports.requiredModelFeatures = requiredModelFeatures;
13891
14687
 
13892
14688
  exports.requiredReaderModel = requiredReaderModel;
@@ -13899,6 +14695,8 @@ exports.resolveClientActor = resolveClientActor;
13899
14695
 
13900
14696
  exports.resolveFieldEntry = resolveFieldEntry$1;
13901
14697
 
14698
+ exports.resolveUserAttributes = resolveUserAttributes;
14699
+
13902
14700
  exports.retractStageGuards = retractStageGuards;
13903
14701
 
13904
14702
  exports.silentLogger = silentLogger;