@timeax/digital-service-engine 0.0.1 → 0.0.2

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.
@@ -3260,11 +3260,6 @@ function hasAnyServiceOption(f) {
3260
3260
  var _a;
3261
3261
  return ((_a = f.options) != null ? _a : []).some((o) => isFiniteNumber(o.service_id));
3262
3262
  }
3263
- function isBoundTo(f, tagId) {
3264
- const b = f.bind_id;
3265
- if (!b) return false;
3266
- return Array.isArray(b) ? b.includes(tagId) : b === tagId;
3267
- }
3268
3263
  function getByPath(obj, path) {
3269
3264
  if (!path) return void 0;
3270
3265
  const parts = path.split(".");
@@ -3350,33 +3345,217 @@ function withAffected(details, ids) {
3350
3345
  return { ...details != null ? details : {}, affectedIds: ids };
3351
3346
  }
3352
3347
 
3348
+ // src/core/node-map.ts
3349
+ function buildNodeMap(props) {
3350
+ var _a, _b, _c;
3351
+ const map = /* @__PURE__ */ new Map();
3352
+ for (const t of (_a = props.filters) != null ? _a : []) {
3353
+ if (!map.has(t.id)) map.set(t.id, { kind: "tag", id: t.id, node: t });
3354
+ }
3355
+ for (const f of (_b = props.fields) != null ? _b : []) {
3356
+ if (!map.has(f.id)) map.set(f.id, { kind: "field", id: f.id, node: f });
3357
+ for (const o of (_c = f.options) != null ? _c : []) {
3358
+ if (!map.has(o.id))
3359
+ map.set(o.id, {
3360
+ kind: "option",
3361
+ id: o.id,
3362
+ node: o,
3363
+ fieldId: f.id
3364
+ });
3365
+ }
3366
+ }
3367
+ return map;
3368
+ }
3369
+ function resolveTrigger(trigger, nodeMap) {
3370
+ const idx = trigger.indexOf("::");
3371
+ if (idx !== -1) {
3372
+ const fieldId = trigger.slice(0, idx);
3373
+ const optionId = trigger.slice(idx + 2);
3374
+ return { kind: "composite", triggerKey: trigger, fieldId, optionId };
3375
+ }
3376
+ const direct = nodeMap.get(trigger);
3377
+ if (!direct) return void 0;
3378
+ if (direct.kind === "option") {
3379
+ return {
3380
+ kind: "option",
3381
+ triggerKey: trigger,
3382
+ id: direct.id,
3383
+ fieldId: direct.fieldId
3384
+ };
3385
+ }
3386
+ return { kind: direct.kind, triggerKey: trigger, id: direct.id };
3387
+ }
3388
+
3389
+ // src/core/visibility.ts
3390
+ function visibleFieldIdsUnder(props, tagId, opts = {}) {
3391
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3392
+ const tags = (_a = props.filters) != null ? _a : [];
3393
+ const fields = (_b = props.fields) != null ? _b : [];
3394
+ const tagById = new Map(tags.map((t) => [t.id, t]));
3395
+ const tag = tagById.get(tagId);
3396
+ if (!tag) return [];
3397
+ const nodeMap = buildNodeMap(props);
3398
+ const lineageDepth = /* @__PURE__ */ new Map();
3399
+ {
3400
+ const guard = /* @__PURE__ */ new Set();
3401
+ let cur = tag;
3402
+ let d = 0;
3403
+ while (cur && !guard.has(cur.id)) {
3404
+ lineageDepth.set(cur.id, d++);
3405
+ guard.add(cur.id);
3406
+ const parentId = cur.bind_id;
3407
+ cur = parentId ? tagById.get(parentId) : void 0;
3408
+ }
3409
+ }
3410
+ const isTagInLineage = (id) => lineageDepth.has(id);
3411
+ const fieldById = new Map(fields.map((f) => [f.id, f]));
3412
+ const ownerDepthForField = (f) => {
3413
+ const b = f.bind_id;
3414
+ if (!b) return void 0;
3415
+ if (typeof b === "string") return lineageDepth.get(b);
3416
+ let best = void 0;
3417
+ for (const id of b) {
3418
+ const d = lineageDepth.get(id);
3419
+ if (d == null) continue;
3420
+ if (best == null || d < best) best = d;
3421
+ }
3422
+ return best;
3423
+ };
3424
+ const ownerDepthForTriggerKey = (triggerKey) => {
3425
+ const t = resolveTrigger(triggerKey, nodeMap);
3426
+ if (!t) return void 0;
3427
+ if (t.kind === "composite") {
3428
+ const f = fieldById.get(t.fieldId);
3429
+ if (!f) return void 0;
3430
+ return ownerDepthForField(f);
3431
+ }
3432
+ if (t.kind === "field") {
3433
+ const f = fieldById.get(t.id);
3434
+ if (!f || f.button !== true) return void 0;
3435
+ return ownerDepthForField(f);
3436
+ }
3437
+ if (t.kind === "option") {
3438
+ const f = t.fieldId ? fieldById.get(t.fieldId) : void 0;
3439
+ if (!f) return void 0;
3440
+ return ownerDepthForField(f);
3441
+ }
3442
+ return void 0;
3443
+ };
3444
+ const tagInclude = new Set((_c = tag.includes) != null ? _c : []);
3445
+ const tagExclude = new Set((_d = tag.excludes) != null ? _d : []);
3446
+ const selected = (_e = opts.selectedKeys) != null ? _e : /* @__PURE__ */ new Set();
3447
+ const incMap = (_f = props.includes_for_buttons) != null ? _f : {};
3448
+ const excMap = (_g = props.excludes_for_buttons) != null ? _g : {};
3449
+ const relevantTriggersInOrder = [];
3450
+ for (const key of selected) {
3451
+ const d = ownerDepthForTriggerKey(key);
3452
+ if (d == null) continue;
3453
+ relevantTriggersInOrder.push(key);
3454
+ }
3455
+ const visible = /* @__PURE__ */ new Set();
3456
+ const isBoundToLineage = (f) => {
3457
+ const b = f.bind_id;
3458
+ if (!b) return false;
3459
+ if (typeof b === "string") return isTagInLineage(b);
3460
+ for (const id of b) if (isTagInLineage(id)) return true;
3461
+ return false;
3462
+ };
3463
+ for (const f of fields) {
3464
+ if (isBoundToLineage(f)) visible.add(f.id);
3465
+ if (tagInclude.has(f.id)) visible.add(f.id);
3466
+ }
3467
+ for (const id of tagExclude) visible.delete(id);
3468
+ const decide = /* @__PURE__ */ new Map();
3469
+ const applyDecision = (fieldId, next) => {
3470
+ const prev = decide.get(fieldId);
3471
+ if (!prev) return void decide.set(fieldId, next);
3472
+ if (next.depth < prev.depth) return void decide.set(fieldId, next);
3473
+ if (next.depth > prev.depth) return;
3474
+ if (prev.kind === "include" && next.kind === "exclude") {
3475
+ decide.set(fieldId, next);
3476
+ }
3477
+ };
3478
+ const revealedOrder = [];
3479
+ const revealedSeen = /* @__PURE__ */ new Set();
3480
+ for (const triggerKey of relevantTriggersInOrder) {
3481
+ const depth = ownerDepthForTriggerKey(triggerKey);
3482
+ if (depth == null) continue;
3483
+ for (const id of (_h = incMap[triggerKey]) != null ? _h : []) {
3484
+ applyDecision(id, { depth, kind: "include" });
3485
+ if (!revealedSeen.has(id)) {
3486
+ revealedSeen.add(id);
3487
+ revealedOrder.push(id);
3488
+ }
3489
+ }
3490
+ for (const id of (_i = excMap[triggerKey]) != null ? _i : []) {
3491
+ applyDecision(id, { depth, kind: "exclude" });
3492
+ }
3493
+ }
3494
+ for (const [fid, d] of decide) {
3495
+ if (d.kind === "include") visible.add(fid);
3496
+ else visible.delete(fid);
3497
+ }
3498
+ const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
3499
+ const order = (_j = props.order_for_tags) == null ? void 0 : _j[tagId];
3500
+ if (order && order.length) {
3501
+ const ordered = order.filter((fid) => visible.has(fid));
3502
+ const orderedSet = new Set(ordered);
3503
+ const rest2 = base.filter((fid) => !orderedSet.has(fid));
3504
+ return [...ordered, ...rest2];
3505
+ }
3506
+ const promoted = revealedOrder.filter((fid) => visible.has(fid));
3507
+ const promotedSet = new Set(promoted);
3508
+ const rest = base.filter((fid) => !promotedSet.has(fid));
3509
+ return [...promoted, ...rest];
3510
+ }
3511
+ function visibleFieldsUnder(props, tagId, opts = {}) {
3512
+ var _a;
3513
+ const ids = visibleFieldIdsUnder(props, tagId, opts);
3514
+ const fieldById = new Map(((_a = props.fields) != null ? _a : []).map((f) => [f.id, f]));
3515
+ return ids.map((id) => fieldById.get(id)).filter(Boolean);
3516
+ }
3517
+
3353
3518
  // src/core/validate/steps/visibility.ts
3354
3519
  function createFieldsVisibleUnder(v) {
3355
3520
  return (tagId) => {
3356
- var _a, _b, _c, _d, _e, _f;
3357
- const tag = v.tagById.get(tagId);
3358
- const includesTag = new Set((_a = tag == null ? void 0 : tag.includes) != null ? _a : []);
3359
- const excludesTag = new Set((_b = tag == null ? void 0 : tag.excludes) != null ? _b : []);
3360
- const incForOpt = (_c = v.props.includes_for_buttons) != null ? _c : {};
3361
- const excForOpt = (_d = v.props.excludes_for_buttons) != null ? _d : {};
3362
- const includesOpt = /* @__PURE__ */ new Set();
3363
- const excludesOpt = /* @__PURE__ */ new Set();
3364
- for (const key of v.selectedKeys) {
3365
- for (const id of (_e = incForOpt[key]) != null ? _e : []) includesOpt.add(id);
3366
- for (const id of (_f = excForOpt[key]) != null ? _f : []) excludesOpt.add(id);
3367
- }
3368
- const merged = /* @__PURE__ */ new Map();
3369
- for (const f of v.fields) {
3370
- if (isBoundTo(f, tagId)) merged.set(f.id, f);
3371
- if (includesTag.has(f.id)) merged.set(f.id, f);
3372
- if (includesOpt.has(f.id)) merged.set(f.id, f);
3373
- }
3374
- for (const id of excludesTag) merged.delete(id);
3375
- for (const id of excludesOpt) merged.delete(id);
3376
- return Array.from(merged.values());
3521
+ return visibleFieldsUnder(v.props, tagId, {
3522
+ selectedKeys: v.selectedKeys
3523
+ });
3377
3524
  };
3378
3525
  }
3379
- function validateVisibility(v) {
3526
+ function stableKeyOfSelection(keys) {
3527
+ return Array.from(keys).sort().join("|");
3528
+ }
3529
+ function resolveRootTags(tags) {
3530
+ const roots = tags.filter((t) => !t.bind_id);
3531
+ return roots.length ? roots : tags.slice(0, 1);
3532
+ }
3533
+ function isEffectfulTrigger(v, trigger) {
3534
+ var _a, _b, _c, _d, _e, _f;
3535
+ const inc = (_a = v.props.includes_for_buttons) != null ? _a : {};
3536
+ const exc = (_b = v.props.excludes_for_buttons) != null ? _b : {};
3537
+ return ((_d = (_c = inc[trigger]) == null ? void 0 : _c.length) != null ? _d : 0) > 0 || ((_f = (_e = exc[trigger]) == null ? void 0 : _e.length) != null ? _f : 0) > 0;
3538
+ }
3539
+ function collectSelectableTriggersInContext(v, tagId, selectedKeys, onlyEffectful) {
3540
+ var _a;
3541
+ const visible = visibleFieldsUnder(v.props, tagId, {
3542
+ selectedKeys
3543
+ });
3544
+ const triggers = [];
3545
+ for (const f of visible) {
3546
+ if (f.button === true) {
3547
+ const t = f.id;
3548
+ if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
3549
+ }
3550
+ for (const o of (_a = f.options) != null ? _a : []) {
3551
+ const t = `${f.id}::${o.id}`;
3552
+ if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
3553
+ }
3554
+ }
3555
+ triggers.sort();
3556
+ return triggers;
3557
+ }
3558
+ function runVisibilityRulesOnce(v) {
3380
3559
  var _a, _b, _c, _d, _e;
3381
3560
  for (const t of v.tags) {
3382
3561
  const visible = v.fieldsVisibleUnder(t.id);
@@ -3451,12 +3630,85 @@ function validateVisibility(v) {
3451
3630
  }
3452
3631
  }
3453
3632
  }
3633
+ function dedupeErrorsInPlace(v, startIndex) {
3634
+ const seen = /* @__PURE__ */ new Set();
3635
+ const keyOfErr = (e) => {
3636
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3637
+ const tagId = (_e = (_d = (_a = e == null ? void 0 : e.details) == null ? void 0 : _a.tagId) != null ? _d : (_c = (_b = e == null ? void 0 : e.details) == null ? void 0 : _b.affected) == null ? void 0 : _c.tagId) != null ? _e : "";
3638
+ const other = (_g = (_f = e == null ? void 0 : e.details) == null ? void 0 : _f.other) != null ? _g : "";
3639
+ return `${(_h = e == null ? void 0 : e.code) != null ? _h : ""}::${(_i = e == null ? void 0 : e.nodeId) != null ? _i : ""}::${tagId}::${other}::${(_j = e == null ? void 0 : e.message) != null ? _j : ""}`;
3640
+ };
3641
+ const kept = [];
3642
+ for (let i = startIndex; i < v.errors.length; i++) {
3643
+ const e = v.errors[i];
3644
+ const k = keyOfErr(e);
3645
+ if (seen.has(k)) continue;
3646
+ seen.add(k);
3647
+ kept.push(e);
3648
+ }
3649
+ v.errors.splice(startIndex, v.errors.length - startIndex, ...kept);
3650
+ }
3651
+ function validateVisibility(v, options = {}) {
3652
+ var _a, _b, _c;
3653
+ const simulate = options.simulate === true;
3654
+ if (!simulate) {
3655
+ runVisibilityRulesOnce(v);
3656
+ return;
3657
+ }
3658
+ const maxStates = Math.max(1, (_a = options.maxStates) != null ? _a : 500);
3659
+ const maxDepth = Math.max(0, (_b = options.maxDepth) != null ? _b : 6);
3660
+ const onlyEffectful = options.onlyEffectfulTriggers !== false;
3661
+ const roots = resolveRootTags(v.tags);
3662
+ const rootTags = options.simulateAllRoots ? roots : roots.slice(0, 1);
3663
+ const originalSelected = new Set((_c = v.selectedKeys) != null ? _c : []);
3664
+ const errorsStart = v.errors.length;
3665
+ const visited = /* @__PURE__ */ new Set();
3666
+ const stack = [];
3667
+ for (const rt of rootTags) {
3668
+ stack.push({
3669
+ rootTagId: rt.id,
3670
+ selected: new Set(originalSelected),
3671
+ depth: 0
3672
+ });
3673
+ }
3674
+ let validatedStates = 0;
3675
+ while (stack.length) {
3676
+ if (validatedStates >= maxStates) break;
3677
+ const state = stack.pop();
3678
+ const sig = stableKeyOfSelection(state.selected);
3679
+ if (visited.has(sig)) continue;
3680
+ visited.add(sig);
3681
+ v.selectedKeys = state.selected;
3682
+ validatedStates++;
3683
+ runVisibilityRulesOnce(v);
3684
+ if (state.depth >= maxDepth) continue;
3685
+ const triggers = collectSelectableTriggersInContext(
3686
+ v,
3687
+ state.rootTagId,
3688
+ state.selected,
3689
+ onlyEffectful
3690
+ );
3691
+ for (let i = triggers.length - 1; i >= 0; i--) {
3692
+ const trig = triggers[i];
3693
+ if (state.selected.has(trig)) continue;
3694
+ const next = new Set(state.selected);
3695
+ next.add(trig);
3696
+ stack.push({
3697
+ rootTagId: state.rootTagId,
3698
+ selected: next,
3699
+ depth: state.depth + 1
3700
+ });
3701
+ }
3702
+ }
3703
+ v.selectedKeys = originalSelected;
3704
+ dedupeErrorsInPlace(v, errorsStart);
3705
+ }
3454
3706
 
3455
3707
  // src/core/validate/steps/structure.ts
3456
3708
  function validateStructure(v) {
3457
3709
  const tags = v.tags;
3458
3710
  const fields = v.fields;
3459
- if (!tags.some((t) => t.id === "root")) {
3711
+ if (!tags.some((t) => t.id === "t:root")) {
3460
3712
  v.errors.push({
3461
3713
  code: "root_missing",
3462
3714
  severity: "error",
@@ -3646,58 +3898,91 @@ function validateIdentity(v) {
3646
3898
  }
3647
3899
 
3648
3900
  // src/core/validate/steps/option-maps.ts
3901
+ function parseFieldOptionKey(key) {
3902
+ const idx = key.indexOf("::");
3903
+ if (idx === -1) return null;
3904
+ const fieldId = key.slice(0, idx).trim();
3905
+ const optionId = key.slice(idx + 2).trim();
3906
+ if (!fieldId || !optionId) return null;
3907
+ return { fieldId, optionId };
3908
+ }
3909
+ function hasOption(v, fid, oid) {
3910
+ var _a;
3911
+ const f = v.fieldById.get(fid);
3912
+ if (!f) return false;
3913
+ return !!((_a = f.options) != null ? _a : []).find((o) => o.id === oid);
3914
+ }
3649
3915
  function validateOptionMaps(v) {
3650
3916
  var _a, _b;
3651
3917
  const incMap = (_a = v.props.includes_for_buttons) != null ? _a : {};
3652
3918
  const excMap = (_b = v.props.excludes_for_buttons) != null ? _b : {};
3653
- const parseKey = (key) => {
3654
- const parts = key.split("::");
3655
- const fid = parts[0];
3656
- const oid = parts[1];
3657
- if (!fid || !oid) return null;
3658
- return { fieldId: fid, optionId: oid };
3659
- };
3660
- const hasOption = (fid, oid) => {
3661
- var _a2;
3662
- const f = v.fieldById.get(fid);
3663
- if (!f) return false;
3664
- return !!((_a2 = f.options) != null ? _a2 : []).find((o) => o.id === oid);
3919
+ const badKeyMessage = (key) => `Invalid trigger-map key "${key}". Expected a known node id (option or button-field), or "fieldId::optionId" pointing to an existing option.`;
3920
+ const validateTriggerKey = (key) => {
3921
+ const ref = v.nodeMap.get(key);
3922
+ if (ref) {
3923
+ if (ref.kind === "option") {
3924
+ return {
3925
+ ok: true,
3926
+ nodeId: ref.fieldId,
3927
+ affected: [ref.fieldId, ref.id]
3928
+ };
3929
+ }
3930
+ if (ref.kind === "field") {
3931
+ const isButton = ref.node.button === true;
3932
+ if (!isButton)
3933
+ return { ok: false, nodeId: ref.id, affected: [ref.id] };
3934
+ return { ok: true, nodeId: ref.id, affected: [ref.id] };
3935
+ }
3936
+ return { ok: false, nodeId: ref.id, affected: [ref.id] };
3937
+ }
3938
+ const p = parseFieldOptionKey(key);
3939
+ if (!p) return { ok: false };
3940
+ if (!hasOption(v, p.fieldId, p.optionId))
3941
+ return {
3942
+ ok: false,
3943
+ nodeId: p.fieldId,
3944
+ affected: [p.fieldId, p.optionId]
3945
+ };
3946
+ return {
3947
+ ok: true,
3948
+ nodeId: p.fieldId,
3949
+ affected: [p.fieldId, p.optionId]
3950
+ };
3665
3951
  };
3666
- const badKeyMessage = (key) => `Invalid option-map key "${key}". Expected "fieldId::optionId" pointing to an existing option.`;
3667
3952
  for (const k of Object.keys(incMap)) {
3668
- const p = parseKey(k);
3669
- if (!p || !hasOption(p.fieldId, p.optionId)) {
3953
+ const r = validateTriggerKey(k);
3954
+ if (!r.ok) {
3670
3955
  v.errors.push({
3671
3956
  code: "bad_option_key",
3672
3957
  severity: "error",
3673
3958
  message: badKeyMessage(k),
3674
- details: { key: k }
3959
+ nodeId: r.nodeId,
3960
+ details: withAffected({ key: k }, r.affected)
3675
3961
  });
3676
3962
  }
3677
3963
  }
3678
3964
  for (const k of Object.keys(excMap)) {
3679
- const p = parseKey(k);
3680
- if (!p || !hasOption(p.fieldId, p.optionId)) {
3965
+ const r = validateTriggerKey(k);
3966
+ if (!r.ok) {
3681
3967
  v.errors.push({
3682
3968
  code: "bad_option_key",
3683
3969
  severity: "error",
3684
3970
  message: badKeyMessage(k),
3685
- details: { key: k }
3971
+ nodeId: r.nodeId,
3972
+ details: withAffected({ key: k }, r.affected)
3686
3973
  });
3687
3974
  }
3688
3975
  }
3689
3976
  for (const k of Object.keys(incMap)) {
3690
- if (k in excMap) {
3691
- const p = parseKey(k);
3692
- const affected = p ? [p.fieldId, p.optionId] : void 0;
3693
- v.errors.push({
3694
- code: "option_include_exclude_conflict",
3695
- severity: "error",
3696
- message: `Option-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
3697
- nodeId: p == null ? void 0 : p.fieldId,
3698
- details: withAffected({ key: k }, affected)
3699
- });
3700
- }
3977
+ if (!(k in excMap)) continue;
3978
+ const r = validateTriggerKey(k);
3979
+ v.errors.push({
3980
+ code: "option_include_exclude_conflict",
3981
+ severity: "error",
3982
+ message: `Trigger-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
3983
+ nodeId: r.nodeId,
3984
+ details: withAffected({ key: k }, r.affected)
3985
+ });
3701
3986
  }
3702
3987
  }
3703
3988
 
@@ -4602,8 +4887,23 @@ function applyPolicies(errors, props, serviceMap, policies, fieldsVisibleUnder,
4602
4887
  }
4603
4888
 
4604
4889
  // src/core/validate/index.ts
4890
+ function readVisibilitySimOpts(ctx) {
4891
+ const c = ctx;
4892
+ const simulate = c.simulateVisibility === true || c.visibilitySimulate === true || c.simulate === true;
4893
+ const maxStates = typeof c.maxVisibilityStates === "number" ? c.maxVisibilityStates : typeof c.visibilityMaxStates === "number" ? c.visibilityMaxStates : typeof c.maxStates === "number" ? c.maxStates : void 0;
4894
+ const maxDepth = typeof c.maxVisibilityDepth === "number" ? c.maxVisibilityDepth : typeof c.visibilityMaxDepth === "number" ? c.visibilityMaxDepth : typeof c.maxDepth === "number" ? c.maxDepth : void 0;
4895
+ const simulateAllRoots = c.simulateAllRoots === true || c.visibilitySimulateAllRoots === true;
4896
+ const onlyEffectfulTriggers = c.onlyEffectfulTriggers === false ? false : c.visibilityOnlyEffectfulTriggers !== false;
4897
+ return {
4898
+ simulate,
4899
+ maxStates,
4900
+ maxDepth,
4901
+ simulateAllRoots,
4902
+ onlyEffectfulTriggers
4903
+ };
4904
+ }
4605
4905
  function validate(props, ctx = {}) {
4606
- var _a, _b;
4906
+ var _a, _b, _c;
4607
4907
  const errors = [];
4608
4908
  const serviceMap = (_a = ctx.serviceMap) != null ? _a : {};
4609
4909
  const selectedKeys = new Set(
@@ -4617,6 +4917,7 @@ function validate(props, ctx = {}) {
4617
4917
  for (const f of fields) fieldById.set(f.id, f);
4618
4918
  const v = {
4619
4919
  props,
4920
+ nodeMap: (_c = ctx.nodeMap) != null ? _c : buildNodeMap(props),
4620
4921
  options: ctx,
4621
4922
  errors,
4622
4923
  serviceMap,
@@ -4631,7 +4932,8 @@ function validate(props, ctx = {}) {
4631
4932
  validateIdentity(v);
4632
4933
  validateOptionMaps(v);
4633
4934
  v.fieldsVisibleUnder = createFieldsVisibleUnder(v);
4634
- validateVisibility(v);
4935
+ const visSim = readVisibilitySimOpts(ctx);
4936
+ validateVisibility(v, visSim);
4635
4937
  applyPolicies(
4636
4938
  v.errors,
4637
4939
  v.props,
@@ -4667,11 +4969,21 @@ var BuilderImpl = class {
4667
4969
  this.optionOwnerById = /* @__PURE__ */ new Map();
4668
4970
  this.history = [];
4669
4971
  this.future = [];
4972
+ this._nodemap = null;
4670
4973
  var _a;
4671
4974
  this.options = { ...opts };
4672
4975
  this.historyLimit = (_a = opts.historyLimit) != null ? _a : 50;
4673
4976
  }
4674
4977
  /* ───── lifecycle ─────────────────────────────────────────────────────── */
4978
+ isTagId(id) {
4979
+ return this.tagById.has(id);
4980
+ }
4981
+ isFieldId(id) {
4982
+ return this.fieldById.has(id);
4983
+ }
4984
+ isOptionId(id) {
4985
+ return this.optionOwnerById.has(id);
4986
+ }
4675
4987
  load(raw) {
4676
4988
  const next = normalise(raw, {
4677
4989
  defaultPricingRole: "base",
@@ -4891,129 +5203,16 @@ var BuilderImpl = class {
4891
5203
  return validate(this.props, this.options);
4892
5204
  }
4893
5205
  visibleFields(tagId, selectedKeys) {
4894
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
4895
- const props = this.props;
4896
- const tags = (_a = props.filters) != null ? _a : [];
4897
- const fields = (_b = props.fields) != null ? _b : [];
4898
- const tagById = new Map(tags.map((t) => [t.id, t]));
4899
- const tag = tagById.get(tagId);
4900
- if (!tag) return [];
4901
- const lineageDepth = /* @__PURE__ */ new Map();
4902
- {
4903
- const guard = /* @__PURE__ */ new Set();
4904
- let cur = tag;
4905
- let d = 0;
4906
- while (cur && !guard.has(cur.id)) {
4907
- lineageDepth.set(cur.id, d++);
4908
- guard.add(cur.id);
4909
- const parentId = cur.bind_id;
4910
- cur = parentId ? tagById.get(parentId) : void 0;
4911
- }
4912
- }
4913
- const isTagInLineage = (id) => lineageDepth.has(id);
4914
- const fieldById = new Map(fields.map((f) => [f.id, f]));
4915
- const optionOwnerFieldId = /* @__PURE__ */ new Map();
4916
- for (const f of fields) {
4917
- for (const o of (_c = f.options) != null ? _c : []) optionOwnerFieldId.set(o.id, f.id);
4918
- }
4919
- const ownerDepthForField = (f) => {
4920
- const b = f.bind_id;
4921
- if (!b) return void 0;
4922
- if (typeof b === "string") return lineageDepth.get(b);
4923
- let best = void 0;
4924
- for (const id of b) {
4925
- const d = lineageDepth.get(id);
4926
- if (d == null) continue;
4927
- if (best == null || d < best) best = d;
4928
- }
4929
- return best;
4930
- };
4931
- const ownerDepthForTrigger = (triggerId) => {
4932
- if (triggerId.startsWith("o:")) {
4933
- const fid = optionOwnerFieldId.get(triggerId);
4934
- if (!fid) return void 0;
4935
- const f2 = fieldById.get(fid);
4936
- if (!f2) return void 0;
4937
- return ownerDepthForField(f2);
4938
- }
4939
- const f = fieldById.get(triggerId);
4940
- if (!f || f.button !== true) return void 0;
4941
- return ownerDepthForField(f);
4942
- };
4943
- const tagInclude = new Set((_d = tag.includes) != null ? _d : []);
4944
- const tagExclude = new Set((_e = tag.excludes) != null ? _e : []);
4945
- const selected = new Set(
4946
- (_f = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _f : []
4947
- );
4948
- const incMap = (_g = props.includes_for_buttons) != null ? _g : {};
4949
- const excMap = (_h = props.excludes_for_buttons) != null ? _h : {};
4950
- const relevantTriggersInOrder = [];
4951
- for (const key of selected) {
4952
- const d = ownerDepthForTrigger(key);
4953
- if (d == null) continue;
4954
- relevantTriggersInOrder.push(key);
4955
- }
4956
- const visible = /* @__PURE__ */ new Set();
4957
- const isBoundToLineage = (f) => {
4958
- const b = f.bind_id;
4959
- if (!b) return false;
4960
- if (typeof b === "string") return isTagInLineage(b);
4961
- for (const id of b) if (isTagInLineage(id)) return true;
4962
- return false;
4963
- };
4964
- for (const f of fields) {
4965
- if (isBoundToLineage(f)) visible.add(f.id);
4966
- if (tagInclude.has(f.id)) visible.add(f.id);
4967
- }
4968
- for (const id of tagExclude) visible.delete(id);
4969
- const decide = /* @__PURE__ */ new Map();
4970
- const applyDecision = (fieldId, next) => {
4971
- const prev = decide.get(fieldId);
4972
- if (!prev) {
4973
- decide.set(fieldId, next);
4974
- return;
4975
- }
4976
- if (next.depth < prev.depth) {
4977
- decide.set(fieldId, next);
4978
- return;
4979
- }
4980
- if (next.depth > prev.depth) return;
4981
- if (prev.kind === "include" && next.kind === "exclude") {
4982
- decide.set(fieldId, next);
4983
- }
4984
- };
4985
- const revealedOrder = [];
4986
- const revealedSeen = /* @__PURE__ */ new Set();
4987
- for (const triggerId of relevantTriggersInOrder) {
4988
- const depth = ownerDepthForTrigger(triggerId);
4989
- if (depth == null) continue;
4990
- for (const id of (_i = incMap[triggerId]) != null ? _i : []) {
4991
- applyDecision(id, { depth, kind: "include" });
4992
- if (!revealedSeen.has(id)) {
4993
- revealedSeen.add(id);
4994
- revealedOrder.push(id);
4995
- }
4996
- }
4997
- for (const id of (_j = excMap[triggerId]) != null ? _j : []) {
4998
- applyDecision(id, { depth, kind: "exclude" });
4999
- }
5000
- }
5001
- for (const [fid, d] of decide) {
5002
- if (d.kind === "include") visible.add(fid);
5003
- else visible.delete(fid);
5004
- }
5005
- const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
5006
- const order = (_k = props.order_for_tags) == null ? void 0 : _k[tagId];
5007
- if (order && order.length) {
5008
- const ordered = order.filter((fid) => visible.has(fid));
5009
- const orderedSet = new Set(ordered);
5010
- const rest2 = base.filter((fid) => !orderedSet.has(fid));
5011
- return [...ordered, ...rest2];
5012
- }
5013
- const promoted = revealedOrder.filter((fid) => visible.has(fid));
5014
- const promotedSet = new Set(promoted);
5015
- const rest = base.filter((fid) => !promotedSet.has(fid));
5016
- return [...promoted, ...rest];
5206
+ var _a;
5207
+ return visibleFieldIdsUnder(this.props, tagId, {
5208
+ selectedKeys: new Set(
5209
+ (_a = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _a : []
5210
+ )
5211
+ });
5212
+ }
5213
+ getNodeMap() {
5214
+ if (!this._nodemap) this._nodemap = buildNodeMap(this.getProps());
5215
+ return this._nodemap;
5017
5216
  }
5018
5217
  /* ───── history ─────────────────────────────────────────────────────── */
5019
5218
  undo() {
@@ -5037,6 +5236,7 @@ var BuilderImpl = class {
5037
5236
  this.tagById.clear();
5038
5237
  this.fieldById.clear();
5039
5238
  this.optionOwnerById.clear();
5239
+ this._nodemap = null;
5040
5240
  for (const t of this.props.filters) this.tagById.set(t.id, t);
5041
5241
  for (const f of this.props.fields) {
5042
5242
  this.fieldById.set(f.id, f);
@@ -5264,6 +5464,7 @@ var toBindList = (b) => {
5264
5464
  function createNodeIndex(builder) {
5265
5465
  var _a, _b, _c, _d;
5266
5466
  const props = builder.getProps();
5467
+ const nodeMap = builder.getNodeMap();
5267
5468
  const tags = (_a = props.filters) != null ? _a : [];
5268
5469
  const fields = (_b = props.fields) != null ? _b : [];
5269
5470
  const tagById = new Map(tags.map((t) => [t.id, t]));
@@ -5467,7 +5668,9 @@ function createNodeIndex(builder) {
5467
5668
  return parentById.get(id) === tid;
5468
5669
  },
5469
5670
  getDescendant(descendantId) {
5470
- return this.getDescendants().find((item) => item.id == descendantId);
5671
+ return this.getDescendants().find(
5672
+ (item) => item.id == descendantId
5673
+ );
5471
5674
  },
5472
5675
  getDescendants() {
5473
5676
  const results = [];
@@ -5476,7 +5679,9 @@ function createNodeIndex(builder) {
5476
5679
  const node2 = getField(fieldId);
5477
5680
  if (!node2) continue;
5478
5681
  const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, id);
5479
- results.push(explicit ? node2 : { ...node2, isInherited: true });
5682
+ results.push(
5683
+ explicit ? node2 : { ...node2, isInherited: true }
5684
+ );
5480
5685
  }
5481
5686
  return Object.freeze(results);
5482
5687
  }
@@ -5525,7 +5730,9 @@ function createNodeIndex(builder) {
5525
5730
  return false;
5526
5731
  },
5527
5732
  getDescendant(descendantId, context) {
5528
- return this.getDescendants(context).find((item) => item.id == descendantId);
5733
+ return this.getDescendants(context).find(
5734
+ (item) => item.id == descendantId
5735
+ );
5529
5736
  },
5530
5737
  getDescendants(tagId) {
5531
5738
  return resolveDescendants(id, includes, tagId, !isButton);
@@ -5567,7 +5774,9 @@ function createNodeIndex(builder) {
5567
5774
  return owner.isBoundTo(tagId);
5568
5775
  },
5569
5776
  getDescendant(descendantId, context) {
5570
- return this.getDescendants(context).find((item) => item.id == descendantId);
5777
+ return this.getDescendants(context).find(
5778
+ (item) => item.id == descendantId
5779
+ );
5571
5780
  },
5572
5781
  getDescendants(tagId) {
5573
5782
  return resolveDescendants(id, includes, tagId);
@@ -5578,7 +5787,7 @@ function createNodeIndex(builder) {
5578
5787
  return node;
5579
5788
  };
5580
5789
  const getNode = (input) => {
5581
- var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i;
5790
+ var _a2, _b2, _c2, _d2, _e;
5582
5791
  if (typeof input !== "string") {
5583
5792
  if ("bind_id" in input && !("type" in input))
5584
5793
  return (_a2 = getTag(input.id)) != null ? _a2 : mkUnknown(input.id);
@@ -5588,11 +5797,7 @@ function createNodeIndex(builder) {
5588
5797
  }
5589
5798
  const cached = nodeCache.get(input);
5590
5799
  if (cached) return cached;
5591
- const id = input;
5592
- if (id.startsWith("t:")) return (_d2 = getTag(id)) != null ? _d2 : mkUnknown(id);
5593
- if (id.startsWith("f:")) return (_e = getField(id)) != null ? _e : mkUnknown(id);
5594
- if (id.startsWith("o:")) return (_f = getOption(id)) != null ? _f : mkUnknown(id);
5595
- return (_i = (_h = (_g = getTag(id)) != null ? _g : getField(id)) != null ? _h : getOption(id)) != null ? _i : mkUnknown(id);
5800
+ return (_e = (_d2 = nodeMap.get(input)) == null ? void 0 : _d2.node) != null ? _e : mkUnknown(input);
5596
5801
  };
5597
5802
  const mkUnknown = (id) => {
5598
5803
  const u = {
@@ -5652,9 +5857,6 @@ function rateOk(svcMap, candidate, primary, policy) {
5652
5857
 
5653
5858
  // src/react/canvas/editor.ts
5654
5859
  var MAX_LIMIT = 100;
5655
- var isTagId = (id) => id.startsWith("t:");
5656
- var isFieldId = (id) => id.startsWith("f:");
5657
- var isOptionId = (id) => id.startsWith("o:");
5658
5860
  function ownerOfOption(props, optionId) {
5659
5861
  var _a, _b;
5660
5862
  for (const f of (_a = props.fields) != null ? _a : []) {
@@ -5696,6 +5898,15 @@ var Editor = class {
5696
5898
  this.pushHistory(this.makeSnapshot("init"));
5697
5899
  }
5698
5900
  /* ───────────────────────── Public API ───────────────────────── */
5901
+ isTagId(id) {
5902
+ return this.builder.isTagId(id);
5903
+ }
5904
+ isFieldId(id) {
5905
+ return this.builder.isFieldId(id);
5906
+ }
5907
+ isOptionId(id) {
5908
+ return this.builder.isOptionId(id);
5909
+ }
5699
5910
  getProps() {
5700
5911
  return this.builder.getProps();
5701
5912
  }
@@ -5791,7 +6002,7 @@ var Editor = class {
5791
6002
  name: "reLabel",
5792
6003
  do: () => this.patchProps((p) => {
5793
6004
  var _a, _b, _c, _d, _e, _f, _g;
5794
- if (isTagId(id)) {
6005
+ if (this.isTagId(id)) {
5795
6006
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
5796
6007
  if (!t) return;
5797
6008
  if (((_b = t.label) != null ? _b : "") === label) return;
@@ -5799,7 +6010,7 @@ var Editor = class {
5799
6010
  this.api.refreshGraph();
5800
6011
  return;
5801
6012
  }
5802
- if (isOptionId(id)) {
6013
+ if (this.isOptionId(id)) {
5803
6014
  const own = ownerOfOption(p, id);
5804
6015
  if (!own) return;
5805
6016
  const f = ((_c = p.fields) != null ? _c : []).find(
@@ -6100,7 +6311,7 @@ var Editor = class {
6100
6311
  * - Option: use placeOption() instead
6101
6312
  */
6102
6313
  placeNode(id, opts) {
6103
- if (isTagId(id)) {
6314
+ if (this.isTagId(id)) {
6104
6315
  this.exec({
6105
6316
  name: "placeTag",
6106
6317
  do: () => this.patchProps((p) => {
@@ -6147,7 +6358,7 @@ var Editor = class {
6147
6358
  }),
6148
6359
  undo: () => this.api.undo()
6149
6360
  });
6150
- } else if (isFieldId(id)) {
6361
+ } else if (this.isFieldId(id)) {
6151
6362
  if (!opts.scopeTagId)
6152
6363
  throw new Error("placeNode(field): scopeTagId is required");
6153
6364
  const fieldId = id;
@@ -6174,14 +6385,14 @@ var Editor = class {
6174
6385
  }),
6175
6386
  undo: () => this.api.undo()
6176
6387
  });
6177
- } else if (isOptionId(id)) {
6388
+ } else if (this.isOptionId(id)) {
6178
6389
  this.placeOption(id, opts);
6179
6390
  } else {
6180
6391
  throw new Error("placeNode: unknown id prefix");
6181
6392
  }
6182
6393
  }
6183
6394
  placeOption(optionId, opts) {
6184
- if (!isOptionId(optionId))
6395
+ if (!this.isOptionId(optionId))
6185
6396
  throw new Error('placeOption: optionId must start with "o:"');
6186
6397
  this.exec({
6187
6398
  name: "placeOption",
@@ -6238,7 +6449,7 @@ var Editor = class {
6238
6449
  return id;
6239
6450
  }
6240
6451
  updateOption(optionId, patch) {
6241
- if (!isOptionId(optionId))
6452
+ if (!this.isOptionId(optionId))
6242
6453
  throw new Error('updateOption: optionId must start with "o:"');
6243
6454
  this.exec({
6244
6455
  name: "updateOption",
@@ -6257,7 +6468,7 @@ var Editor = class {
6257
6468
  });
6258
6469
  }
6259
6470
  removeOption(optionId) {
6260
- if (!isOptionId(optionId))
6471
+ if (!this.isOptionId(optionId))
6261
6472
  throw new Error('removeOption: optionId must start with "o:"');
6262
6473
  this.exec({
6263
6474
  name: "removeOption",
@@ -6288,17 +6499,17 @@ var Editor = class {
6288
6499
  name: "editLabel",
6289
6500
  do: () => this.patchProps((p) => {
6290
6501
  var _a, _b, _c, _d;
6291
- if (isTagId(id)) {
6502
+ if (this.isTagId(id)) {
6292
6503
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6293
6504
  if (t) t.label = next;
6294
6505
  return;
6295
6506
  }
6296
- if (isFieldId(id)) {
6507
+ if (this.isFieldId(id)) {
6297
6508
  const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === id);
6298
6509
  if (f) f.label = next;
6299
6510
  return;
6300
6511
  }
6301
- if (isOptionId(id)) {
6512
+ if (this.isOptionId(id)) {
6302
6513
  const own = ownerOfOption(p, id);
6303
6514
  if (!own) return;
6304
6515
  const f = ((_c = p.fields) != null ? _c : []).find(
@@ -6337,7 +6548,7 @@ var Editor = class {
6337
6548
  const validId = hasSidKey && typeof input.service_id === "number" && Number.isFinite(input.service_id);
6338
6549
  const sid = validId ? Number(input.service_id) : void 0;
6339
6550
  const nextRole = input.pricing_role;
6340
- if (isTagId(id)) {
6551
+ if (this.isTagId(id)) {
6341
6552
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6342
6553
  if (!t) return;
6343
6554
  if (hasSidKey) {
@@ -6346,7 +6557,7 @@ var Editor = class {
6346
6557
  }
6347
6558
  return;
6348
6559
  }
6349
- if (isOptionId(id)) {
6560
+ if (this.isOptionId(id)) {
6350
6561
  const own = ownerOfOption(p, id);
6351
6562
  if (!own) return;
6352
6563
  const f2 = ((_b = p.fields) != null ? _b : []).find(
@@ -6553,7 +6764,7 @@ var Editor = class {
6553
6764
  });
6554
6765
  }
6555
6766
  remove(id) {
6556
- if (isTagId(id)) {
6767
+ if (this.isTagId(id)) {
6557
6768
  this.exec({
6558
6769
  name: "removeTag",
6559
6770
  do: () => this.patchProps((p) => {
@@ -6593,7 +6804,7 @@ var Editor = class {
6593
6804
  });
6594
6805
  return;
6595
6806
  }
6596
- if (isFieldId(id)) {
6807
+ if (this.isFieldId(id)) {
6597
6808
  this.exec({
6598
6809
  name: "removeField",
6599
6810
  do: () => this.patchProps((p) => {
@@ -6629,7 +6840,7 @@ var Editor = class {
6629
6840
  });
6630
6841
  return;
6631
6842
  }
6632
- if (isOptionId(id)) {
6843
+ if (this.isOptionId(id)) {
6633
6844
  this.removeOption(id);
6634
6845
  return;
6635
6846
  }
@@ -6638,7 +6849,7 @@ var Editor = class {
6638
6849
  getNode(id) {
6639
6850
  var _a, _b, _c, _d;
6640
6851
  const props = this.builder.getProps();
6641
- if (isTagId(id)) {
6852
+ if (this.isTagId(id)) {
6642
6853
  const t = ((_a = props.filters) != null ? _a : []).find((x) => x.id === id);
6643
6854
  return {
6644
6855
  kind: "tag",
@@ -6646,12 +6857,12 @@ var Editor = class {
6646
6857
  owners: { parentTagId: t == null ? void 0 : t.bind_id }
6647
6858
  };
6648
6859
  }
6649
- if (isFieldId(id)) {
6860
+ if (this.isFieldId(id)) {
6650
6861
  const f = ((_b = props.fields) != null ? _b : []).find((x) => x.id === id);
6651
6862
  const bind = Array.isArray(f == null ? void 0 : f.bind_id) ? f.bind_id : (f == null ? void 0 : f.bind_id) ? [f.bind_id] : [];
6652
6863
  return { kind: "field", data: f, owners: { bindTagIds: bind } };
6653
6864
  }
6654
- if (isOptionId(id)) {
6865
+ if (this.isOptionId(id)) {
6655
6866
  const own = ownerOfOption(props, id);
6656
6867
  const f = own ? ((_c = props.fields) != null ? _c : []).find((x) => x.id === own.fieldId) : void 0;
6657
6868
  const o = (_d = f == null ? void 0 : f.options) == null ? void 0 : _d.find((x) => x.id === id);
@@ -6729,7 +6940,7 @@ var Editor = class {
6729
6940
  if (receiverId === targetId) return true;
6730
6941
  const getDirectRelations = (id) => {
6731
6942
  var _a, _b, _c, _d, _e, _f, _g;
6732
- if (isTagId(id)) {
6943
+ if (this.isTagId(id)) {
6733
6944
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6734
6945
  return [...(_b = t == null ? void 0 : t.includes) != null ? _b : [], ...(_c = t == null ? void 0 : t.excludes) != null ? _c : []];
6735
6946
  }
@@ -6949,7 +7160,7 @@ var Editor = class {
6949
7160
  do: () => this.patchProps((p) => {
6950
7161
  var _a, _b, _c, _d, _e;
6951
7162
  if (kind === "bind") {
6952
- if (isTagId(fromId) && isTagId(toId2)) {
7163
+ if (this.isTagId(fromId) && this.isTagId(toId2)) {
6953
7164
  if (this.wouldCreateTagCycle(p, fromId, toId2)) {
6954
7165
  throw new Error(
6955
7166
  `bind would create a cycle: ${fromId} \u2192 ${toId2}`
@@ -6961,9 +7172,9 @@ var Editor = class {
6961
7172
  if (child) child.bind_id = fromId;
6962
7173
  return;
6963
7174
  }
6964
- if (isTagId(fromId) && isFieldId(toId2) || isFieldId(fromId) && isTagId(toId2)) {
6965
- const fieldId = isFieldId(toId2) ? toId2 : fromId;
6966
- const tagId = isTagId(fromId) ? fromId : toId2;
7175
+ if (this.isTagId(fromId) && this.isFieldId(toId2) || this.isFieldId(fromId) && this.isTagId(toId2)) {
7176
+ const fieldId = this.isFieldId(toId2) ? toId2 : fromId;
7177
+ const tagId = this.isTagId(fromId) ? fromId : toId2;
6967
7178
  const f = ((_b = p.fields) != null ? _b : []).find(
6968
7179
  (x) => x.id === fieldId
6969
7180
  );
@@ -6987,7 +7198,7 @@ var Editor = class {
6987
7198
  }
6988
7199
  if (kind === "include" || kind === "exclude") {
6989
7200
  const key = kind === "include" ? "includes" : "excludes";
6990
- if (isTagId(fromId) && isFieldId(toId2)) {
7201
+ if (this.isTagId(fromId) && this.isFieldId(toId2)) {
6991
7202
  const t = ((_c = p.filters) != null ? _c : []).find(
6992
7203
  (x) => x.id === fromId
6993
7204
  );
@@ -6996,7 +7207,7 @@ var Editor = class {
6996
7207
  if (!arr.includes(toId2)) arr.push(toId2);
6997
7208
  return;
6998
7209
  }
6999
- if (isOptionId(fromId) && isFieldId(toId2)) {
7210
+ if (this.isOptionId(fromId) && this.isFieldId(toId2)) {
7000
7211
  const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7001
7212
  const maps = p[mapKey];
7002
7213
  const next = { ...maps != null ? maps : {} };
@@ -7064,16 +7275,16 @@ var Editor = class {
7064
7275
  do: () => this.patchProps((p) => {
7065
7276
  var _a, _b, _c, _d, _e, _f, _g, _h;
7066
7277
  if (kind === "bind") {
7067
- if (isTagId(fromId) && isTagId(toId2)) {
7278
+ if (this.isTagId(fromId) && this.isTagId(toId2)) {
7068
7279
  const child = ((_a = p.filters) != null ? _a : []).find(
7069
7280
  (t) => t.id === toId2
7070
7281
  );
7071
7282
  if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
7072
7283
  return;
7073
7284
  }
7074
- if (isTagId(fromId) && isFieldId(toId2) || isFieldId(fromId) && isTagId(toId2)) {
7075
- const fieldId = isFieldId(toId2) ? toId2 : fromId;
7076
- const tagId = isTagId(fromId) ? fromId : toId2;
7285
+ if (this.isTagId(fromId) && this.isFieldId(toId2) || this.isFieldId(fromId) && this.isTagId(toId2)) {
7286
+ const fieldId = this.isFieldId(toId2) ? toId2 : fromId;
7287
+ const tagId = this.isTagId(fromId) ? fromId : toId2;
7077
7288
  const f = ((_b = p.fields) != null ? _b : []).find(
7078
7289
  (x) => x.id === fieldId
7079
7290
  );
@@ -7094,7 +7305,7 @@ var Editor = class {
7094
7305
  }
7095
7306
  if (kind === "include" || kind === "exclude") {
7096
7307
  const key = kind === "include" ? "includes" : "excludes";
7097
- if (isTagId(fromId) && isFieldId(toId2)) {
7308
+ if (this.isTagId(fromId) && this.isFieldId(toId2)) {
7098
7309
  const t = ((_d = p.filters) != null ? _d : []).find(
7099
7310
  (x) => x.id === fromId
7100
7311
  );
@@ -7103,7 +7314,7 @@ var Editor = class {
7103
7314
  if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
7104
7315
  return;
7105
7316
  }
7106
- if (isOptionId(fromId) && isFieldId(toId2)) {
7317
+ if (this.isOptionId(fromId) && this.isFieldId(toId2)) {
7107
7318
  const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7108
7319
  const maps = p[mapKey];
7109
7320
  if (!maps) return;
@@ -7570,8 +7781,6 @@ function toStrSet(v) {
7570
7781
  }
7571
7782
 
7572
7783
  // src/react/canvas/selection.ts
7573
- var isTagId2 = (id) => typeof id === "string" && id.startsWith("t:");
7574
- var isOptionId2 = (id) => typeof id === "string" && id.startsWith("o:");
7575
7784
  var Selection = class {
7576
7785
  constructor(builder, opts) {
7577
7786
  this.builder = builder;
@@ -7643,7 +7852,7 @@ var Selection = class {
7643
7852
  var _a;
7644
7853
  const props = this.builder.getProps();
7645
7854
  if (((_a = this.opts.env) != null ? _a : "client") === "workspace") {
7646
- const tagIds = Array.from(this.set).filter(isTagId2);
7855
+ const tagIds = Array.from(this.set).filter(this.builder.isTagId.bind(this.builder));
7647
7856
  if (tagIds.length > 1) {
7648
7857
  return { kind: "multi", groups: Array.from(this.set) };
7649
7858
  }
@@ -7676,7 +7885,7 @@ var Selection = class {
7676
7885
  this.currentTagId = Array.isArray(f.bind_id) ? f.bind_id[0] : f.bind_id;
7677
7886
  return;
7678
7887
  }
7679
- if (isOptionId2(id)) {
7888
+ if (this.builder.isOptionId(id)) {
7680
7889
  const host = fields.find(
7681
7890
  (x) => {
7682
7891
  var _a2;
@@ -7700,7 +7909,7 @@ var Selection = class {
7700
7909
  resolveTagContextId(props) {
7701
7910
  var _a;
7702
7911
  if (this.currentTagId) return this.currentTagId;
7703
- for (const id of this.set) if (isTagId2(id)) return id;
7912
+ for (const id of this.set) if (this.builder.isTagId(id)) return id;
7704
7913
  const fields = (_a = props.fields) != null ? _a : [];
7705
7914
  for (const id of this.set) {
7706
7915
  const f = fields.find((x) => x.id === id);
@@ -7708,7 +7917,7 @@ var Selection = class {
7708
7917
  return Array.isArray(f.bind_id) ? f.bind_id[0] : f.bind_id;
7709
7918
  }
7710
7919
  for (const id of this.set) {
7711
- if (isOptionId2(id)) {
7920
+ if (this.builder.isOptionId(id)) {
7712
7921
  const host = fields.find(
7713
7922
  (x) => {
7714
7923
  var _a2;
@@ -7829,7 +8038,7 @@ var Selection = class {
7829
8038
  }
7830
8039
  findOptionById(fields, selId) {
7831
8040
  var _a, _b;
7832
- if (isOptionId2(selId)) {
8041
+ if (this.builder.isOptionId(selId)) {
7833
8042
  for (const f of fields) {
7834
8043
  const o = (_a = f.options) == null ? void 0 : _a.find((x) => x.id === selId);
7835
8044
  if (o) return o;
@@ -8251,8 +8460,8 @@ function useCanvasOwned(initialProps, canvasOpts, builderOpts) {
8251
8460
  // src/react/workspace/context/hooks/use-canvas.ts
8252
8461
  var React16 = __toESM(require("react"), 1);
8253
8462
  var import_react4 = require("react");
8254
- var isTagId3 = (id) => id.startsWith("t:");
8255
- var isOptionId3 = (id) => id.startsWith("o:");
8463
+ var isTagId = (id) => id.startsWith("t:");
8464
+ var isOptionId = (id) => id.startsWith("o:");
8256
8465
  function deriveSelectionInfo(props, ids) {
8257
8466
  var _a, _b, _c, _d;
8258
8467
  const tags = [];
@@ -8261,11 +8470,11 @@ function deriveSelectionInfo(props, ids) {
8261
8470
  const fieldById = /* @__PURE__ */ new Map();
8262
8471
  for (const f of (_a = props.fields) != null ? _a : []) fieldById.set(f.id, f);
8263
8472
  for (const id of ids) {
8264
- if (isTagId3(id)) {
8473
+ if (isTagId(id)) {
8265
8474
  tags.push(id);
8266
8475
  continue;
8267
8476
  }
8268
- if (isOptionId3(id)) {
8477
+ if (isOptionId(id)) {
8269
8478
  options.push(id);
8270
8479
  continue;
8271
8480
  }