@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.
@@ -3217,11 +3217,6 @@ function hasAnyServiceOption(f) {
3217
3217
  var _a;
3218
3218
  return ((_a = f.options) != null ? _a : []).some((o) => isFiniteNumber(o.service_id));
3219
3219
  }
3220
- function isBoundTo(f, tagId) {
3221
- const b = f.bind_id;
3222
- if (!b) return false;
3223
- return Array.isArray(b) ? b.includes(tagId) : b === tagId;
3224
- }
3225
3220
  function getByPath(obj, path) {
3226
3221
  if (!path) return void 0;
3227
3222
  const parts = path.split(".");
@@ -3307,33 +3302,217 @@ function withAffected(details, ids) {
3307
3302
  return { ...details != null ? details : {}, affectedIds: ids };
3308
3303
  }
3309
3304
 
3305
+ // src/core/node-map.ts
3306
+ function buildNodeMap(props) {
3307
+ var _a, _b, _c;
3308
+ const map = /* @__PURE__ */ new Map();
3309
+ for (const t of (_a = props.filters) != null ? _a : []) {
3310
+ if (!map.has(t.id)) map.set(t.id, { kind: "tag", id: t.id, node: t });
3311
+ }
3312
+ for (const f of (_b = props.fields) != null ? _b : []) {
3313
+ if (!map.has(f.id)) map.set(f.id, { kind: "field", id: f.id, node: f });
3314
+ for (const o of (_c = f.options) != null ? _c : []) {
3315
+ if (!map.has(o.id))
3316
+ map.set(o.id, {
3317
+ kind: "option",
3318
+ id: o.id,
3319
+ node: o,
3320
+ fieldId: f.id
3321
+ });
3322
+ }
3323
+ }
3324
+ return map;
3325
+ }
3326
+ function resolveTrigger(trigger, nodeMap) {
3327
+ const idx = trigger.indexOf("::");
3328
+ if (idx !== -1) {
3329
+ const fieldId = trigger.slice(0, idx);
3330
+ const optionId = trigger.slice(idx + 2);
3331
+ return { kind: "composite", triggerKey: trigger, fieldId, optionId };
3332
+ }
3333
+ const direct = nodeMap.get(trigger);
3334
+ if (!direct) return void 0;
3335
+ if (direct.kind === "option") {
3336
+ return {
3337
+ kind: "option",
3338
+ triggerKey: trigger,
3339
+ id: direct.id,
3340
+ fieldId: direct.fieldId
3341
+ };
3342
+ }
3343
+ return { kind: direct.kind, triggerKey: trigger, id: direct.id };
3344
+ }
3345
+
3346
+ // src/core/visibility.ts
3347
+ function visibleFieldIdsUnder(props, tagId, opts = {}) {
3348
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3349
+ const tags = (_a = props.filters) != null ? _a : [];
3350
+ const fields = (_b = props.fields) != null ? _b : [];
3351
+ const tagById = new Map(tags.map((t) => [t.id, t]));
3352
+ const tag = tagById.get(tagId);
3353
+ if (!tag) return [];
3354
+ const nodeMap = buildNodeMap(props);
3355
+ const lineageDepth = /* @__PURE__ */ new Map();
3356
+ {
3357
+ const guard = /* @__PURE__ */ new Set();
3358
+ let cur = tag;
3359
+ let d = 0;
3360
+ while (cur && !guard.has(cur.id)) {
3361
+ lineageDepth.set(cur.id, d++);
3362
+ guard.add(cur.id);
3363
+ const parentId = cur.bind_id;
3364
+ cur = parentId ? tagById.get(parentId) : void 0;
3365
+ }
3366
+ }
3367
+ const isTagInLineage = (id) => lineageDepth.has(id);
3368
+ const fieldById = new Map(fields.map((f) => [f.id, f]));
3369
+ const ownerDepthForField = (f) => {
3370
+ const b = f.bind_id;
3371
+ if (!b) return void 0;
3372
+ if (typeof b === "string") return lineageDepth.get(b);
3373
+ let best = void 0;
3374
+ for (const id of b) {
3375
+ const d = lineageDepth.get(id);
3376
+ if (d == null) continue;
3377
+ if (best == null || d < best) best = d;
3378
+ }
3379
+ return best;
3380
+ };
3381
+ const ownerDepthForTriggerKey = (triggerKey) => {
3382
+ const t = resolveTrigger(triggerKey, nodeMap);
3383
+ if (!t) return void 0;
3384
+ if (t.kind === "composite") {
3385
+ const f = fieldById.get(t.fieldId);
3386
+ if (!f) return void 0;
3387
+ return ownerDepthForField(f);
3388
+ }
3389
+ if (t.kind === "field") {
3390
+ const f = fieldById.get(t.id);
3391
+ if (!f || f.button !== true) return void 0;
3392
+ return ownerDepthForField(f);
3393
+ }
3394
+ if (t.kind === "option") {
3395
+ const f = t.fieldId ? fieldById.get(t.fieldId) : void 0;
3396
+ if (!f) return void 0;
3397
+ return ownerDepthForField(f);
3398
+ }
3399
+ return void 0;
3400
+ };
3401
+ const tagInclude = new Set((_c = tag.includes) != null ? _c : []);
3402
+ const tagExclude = new Set((_d = tag.excludes) != null ? _d : []);
3403
+ const selected = (_e = opts.selectedKeys) != null ? _e : /* @__PURE__ */ new Set();
3404
+ const incMap = (_f = props.includes_for_buttons) != null ? _f : {};
3405
+ const excMap = (_g = props.excludes_for_buttons) != null ? _g : {};
3406
+ const relevantTriggersInOrder = [];
3407
+ for (const key of selected) {
3408
+ const d = ownerDepthForTriggerKey(key);
3409
+ if (d == null) continue;
3410
+ relevantTriggersInOrder.push(key);
3411
+ }
3412
+ const visible = /* @__PURE__ */ new Set();
3413
+ const isBoundToLineage = (f) => {
3414
+ const b = f.bind_id;
3415
+ if (!b) return false;
3416
+ if (typeof b === "string") return isTagInLineage(b);
3417
+ for (const id of b) if (isTagInLineage(id)) return true;
3418
+ return false;
3419
+ };
3420
+ for (const f of fields) {
3421
+ if (isBoundToLineage(f)) visible.add(f.id);
3422
+ if (tagInclude.has(f.id)) visible.add(f.id);
3423
+ }
3424
+ for (const id of tagExclude) visible.delete(id);
3425
+ const decide = /* @__PURE__ */ new Map();
3426
+ const applyDecision = (fieldId, next) => {
3427
+ const prev = decide.get(fieldId);
3428
+ if (!prev) return void decide.set(fieldId, next);
3429
+ if (next.depth < prev.depth) return void decide.set(fieldId, next);
3430
+ if (next.depth > prev.depth) return;
3431
+ if (prev.kind === "include" && next.kind === "exclude") {
3432
+ decide.set(fieldId, next);
3433
+ }
3434
+ };
3435
+ const revealedOrder = [];
3436
+ const revealedSeen = /* @__PURE__ */ new Set();
3437
+ for (const triggerKey of relevantTriggersInOrder) {
3438
+ const depth = ownerDepthForTriggerKey(triggerKey);
3439
+ if (depth == null) continue;
3440
+ for (const id of (_h = incMap[triggerKey]) != null ? _h : []) {
3441
+ applyDecision(id, { depth, kind: "include" });
3442
+ if (!revealedSeen.has(id)) {
3443
+ revealedSeen.add(id);
3444
+ revealedOrder.push(id);
3445
+ }
3446
+ }
3447
+ for (const id of (_i = excMap[triggerKey]) != null ? _i : []) {
3448
+ applyDecision(id, { depth, kind: "exclude" });
3449
+ }
3450
+ }
3451
+ for (const [fid, d] of decide) {
3452
+ if (d.kind === "include") visible.add(fid);
3453
+ else visible.delete(fid);
3454
+ }
3455
+ const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
3456
+ const order = (_j = props.order_for_tags) == null ? void 0 : _j[tagId];
3457
+ if (order && order.length) {
3458
+ const ordered = order.filter((fid) => visible.has(fid));
3459
+ const orderedSet = new Set(ordered);
3460
+ const rest2 = base.filter((fid) => !orderedSet.has(fid));
3461
+ return [...ordered, ...rest2];
3462
+ }
3463
+ const promoted = revealedOrder.filter((fid) => visible.has(fid));
3464
+ const promotedSet = new Set(promoted);
3465
+ const rest = base.filter((fid) => !promotedSet.has(fid));
3466
+ return [...promoted, ...rest];
3467
+ }
3468
+ function visibleFieldsUnder(props, tagId, opts = {}) {
3469
+ var _a;
3470
+ const ids = visibleFieldIdsUnder(props, tagId, opts);
3471
+ const fieldById = new Map(((_a = props.fields) != null ? _a : []).map((f) => [f.id, f]));
3472
+ return ids.map((id) => fieldById.get(id)).filter(Boolean);
3473
+ }
3474
+
3310
3475
  // src/core/validate/steps/visibility.ts
3311
3476
  function createFieldsVisibleUnder(v) {
3312
3477
  return (tagId) => {
3313
- var _a, _b, _c, _d, _e, _f;
3314
- const tag = v.tagById.get(tagId);
3315
- const includesTag = new Set((_a = tag == null ? void 0 : tag.includes) != null ? _a : []);
3316
- const excludesTag = new Set((_b = tag == null ? void 0 : tag.excludes) != null ? _b : []);
3317
- const incForOpt = (_c = v.props.includes_for_buttons) != null ? _c : {};
3318
- const excForOpt = (_d = v.props.excludes_for_buttons) != null ? _d : {};
3319
- const includesOpt = /* @__PURE__ */ new Set();
3320
- const excludesOpt = /* @__PURE__ */ new Set();
3321
- for (const key of v.selectedKeys) {
3322
- for (const id of (_e = incForOpt[key]) != null ? _e : []) includesOpt.add(id);
3323
- for (const id of (_f = excForOpt[key]) != null ? _f : []) excludesOpt.add(id);
3324
- }
3325
- const merged = /* @__PURE__ */ new Map();
3326
- for (const f of v.fields) {
3327
- if (isBoundTo(f, tagId)) merged.set(f.id, f);
3328
- if (includesTag.has(f.id)) merged.set(f.id, f);
3329
- if (includesOpt.has(f.id)) merged.set(f.id, f);
3330
- }
3331
- for (const id of excludesTag) merged.delete(id);
3332
- for (const id of excludesOpt) merged.delete(id);
3333
- return Array.from(merged.values());
3478
+ return visibleFieldsUnder(v.props, tagId, {
3479
+ selectedKeys: v.selectedKeys
3480
+ });
3334
3481
  };
3335
3482
  }
3336
- function validateVisibility(v) {
3483
+ function stableKeyOfSelection(keys) {
3484
+ return Array.from(keys).sort().join("|");
3485
+ }
3486
+ function resolveRootTags(tags) {
3487
+ const roots = tags.filter((t) => !t.bind_id);
3488
+ return roots.length ? roots : tags.slice(0, 1);
3489
+ }
3490
+ function isEffectfulTrigger(v, trigger) {
3491
+ var _a, _b, _c, _d, _e, _f;
3492
+ const inc = (_a = v.props.includes_for_buttons) != null ? _a : {};
3493
+ const exc = (_b = v.props.excludes_for_buttons) != null ? _b : {};
3494
+ 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;
3495
+ }
3496
+ function collectSelectableTriggersInContext(v, tagId, selectedKeys, onlyEffectful) {
3497
+ var _a;
3498
+ const visible = visibleFieldsUnder(v.props, tagId, {
3499
+ selectedKeys
3500
+ });
3501
+ const triggers = [];
3502
+ for (const f of visible) {
3503
+ if (f.button === true) {
3504
+ const t = f.id;
3505
+ if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
3506
+ }
3507
+ for (const o of (_a = f.options) != null ? _a : []) {
3508
+ const t = `${f.id}::${o.id}`;
3509
+ if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
3510
+ }
3511
+ }
3512
+ triggers.sort();
3513
+ return triggers;
3514
+ }
3515
+ function runVisibilityRulesOnce(v) {
3337
3516
  var _a, _b, _c, _d, _e;
3338
3517
  for (const t of v.tags) {
3339
3518
  const visible = v.fieldsVisibleUnder(t.id);
@@ -3408,12 +3587,85 @@ function validateVisibility(v) {
3408
3587
  }
3409
3588
  }
3410
3589
  }
3590
+ function dedupeErrorsInPlace(v, startIndex) {
3591
+ const seen = /* @__PURE__ */ new Set();
3592
+ const keyOfErr = (e) => {
3593
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3594
+ 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 : "";
3595
+ const other = (_g = (_f = e == null ? void 0 : e.details) == null ? void 0 : _f.other) != null ? _g : "";
3596
+ 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 : ""}`;
3597
+ };
3598
+ const kept = [];
3599
+ for (let i = startIndex; i < v.errors.length; i++) {
3600
+ const e = v.errors[i];
3601
+ const k = keyOfErr(e);
3602
+ if (seen.has(k)) continue;
3603
+ seen.add(k);
3604
+ kept.push(e);
3605
+ }
3606
+ v.errors.splice(startIndex, v.errors.length - startIndex, ...kept);
3607
+ }
3608
+ function validateVisibility(v, options = {}) {
3609
+ var _a, _b, _c;
3610
+ const simulate = options.simulate === true;
3611
+ if (!simulate) {
3612
+ runVisibilityRulesOnce(v);
3613
+ return;
3614
+ }
3615
+ const maxStates = Math.max(1, (_a = options.maxStates) != null ? _a : 500);
3616
+ const maxDepth = Math.max(0, (_b = options.maxDepth) != null ? _b : 6);
3617
+ const onlyEffectful = options.onlyEffectfulTriggers !== false;
3618
+ const roots = resolveRootTags(v.tags);
3619
+ const rootTags = options.simulateAllRoots ? roots : roots.slice(0, 1);
3620
+ const originalSelected = new Set((_c = v.selectedKeys) != null ? _c : []);
3621
+ const errorsStart = v.errors.length;
3622
+ const visited = /* @__PURE__ */ new Set();
3623
+ const stack = [];
3624
+ for (const rt of rootTags) {
3625
+ stack.push({
3626
+ rootTagId: rt.id,
3627
+ selected: new Set(originalSelected),
3628
+ depth: 0
3629
+ });
3630
+ }
3631
+ let validatedStates = 0;
3632
+ while (stack.length) {
3633
+ if (validatedStates >= maxStates) break;
3634
+ const state = stack.pop();
3635
+ const sig = stableKeyOfSelection(state.selected);
3636
+ if (visited.has(sig)) continue;
3637
+ visited.add(sig);
3638
+ v.selectedKeys = state.selected;
3639
+ validatedStates++;
3640
+ runVisibilityRulesOnce(v);
3641
+ if (state.depth >= maxDepth) continue;
3642
+ const triggers = collectSelectableTriggersInContext(
3643
+ v,
3644
+ state.rootTagId,
3645
+ state.selected,
3646
+ onlyEffectful
3647
+ );
3648
+ for (let i = triggers.length - 1; i >= 0; i--) {
3649
+ const trig = triggers[i];
3650
+ if (state.selected.has(trig)) continue;
3651
+ const next = new Set(state.selected);
3652
+ next.add(trig);
3653
+ stack.push({
3654
+ rootTagId: state.rootTagId,
3655
+ selected: next,
3656
+ depth: state.depth + 1
3657
+ });
3658
+ }
3659
+ }
3660
+ v.selectedKeys = originalSelected;
3661
+ dedupeErrorsInPlace(v, errorsStart);
3662
+ }
3411
3663
 
3412
3664
  // src/core/validate/steps/structure.ts
3413
3665
  function validateStructure(v) {
3414
3666
  const tags = v.tags;
3415
3667
  const fields = v.fields;
3416
- if (!tags.some((t) => t.id === "root")) {
3668
+ if (!tags.some((t) => t.id === "t:root")) {
3417
3669
  v.errors.push({
3418
3670
  code: "root_missing",
3419
3671
  severity: "error",
@@ -3603,58 +3855,91 @@ function validateIdentity(v) {
3603
3855
  }
3604
3856
 
3605
3857
  // src/core/validate/steps/option-maps.ts
3858
+ function parseFieldOptionKey(key) {
3859
+ const idx = key.indexOf("::");
3860
+ if (idx === -1) return null;
3861
+ const fieldId = key.slice(0, idx).trim();
3862
+ const optionId = key.slice(idx + 2).trim();
3863
+ if (!fieldId || !optionId) return null;
3864
+ return { fieldId, optionId };
3865
+ }
3866
+ function hasOption(v, fid, oid) {
3867
+ var _a;
3868
+ const f = v.fieldById.get(fid);
3869
+ if (!f) return false;
3870
+ return !!((_a = f.options) != null ? _a : []).find((o) => o.id === oid);
3871
+ }
3606
3872
  function validateOptionMaps(v) {
3607
3873
  var _a, _b;
3608
3874
  const incMap = (_a = v.props.includes_for_buttons) != null ? _a : {};
3609
3875
  const excMap = (_b = v.props.excludes_for_buttons) != null ? _b : {};
3610
- const parseKey = (key) => {
3611
- const parts = key.split("::");
3612
- const fid = parts[0];
3613
- const oid = parts[1];
3614
- if (!fid || !oid) return null;
3615
- return { fieldId: fid, optionId: oid };
3616
- };
3617
- const hasOption = (fid, oid) => {
3618
- var _a2;
3619
- const f = v.fieldById.get(fid);
3620
- if (!f) return false;
3621
- return !!((_a2 = f.options) != null ? _a2 : []).find((o) => o.id === oid);
3876
+ 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.`;
3877
+ const validateTriggerKey = (key) => {
3878
+ const ref = v.nodeMap.get(key);
3879
+ if (ref) {
3880
+ if (ref.kind === "option") {
3881
+ return {
3882
+ ok: true,
3883
+ nodeId: ref.fieldId,
3884
+ affected: [ref.fieldId, ref.id]
3885
+ };
3886
+ }
3887
+ if (ref.kind === "field") {
3888
+ const isButton = ref.node.button === true;
3889
+ if (!isButton)
3890
+ return { ok: false, nodeId: ref.id, affected: [ref.id] };
3891
+ return { ok: true, nodeId: ref.id, affected: [ref.id] };
3892
+ }
3893
+ return { ok: false, nodeId: ref.id, affected: [ref.id] };
3894
+ }
3895
+ const p = parseFieldOptionKey(key);
3896
+ if (!p) return { ok: false };
3897
+ if (!hasOption(v, p.fieldId, p.optionId))
3898
+ return {
3899
+ ok: false,
3900
+ nodeId: p.fieldId,
3901
+ affected: [p.fieldId, p.optionId]
3902
+ };
3903
+ return {
3904
+ ok: true,
3905
+ nodeId: p.fieldId,
3906
+ affected: [p.fieldId, p.optionId]
3907
+ };
3622
3908
  };
3623
- const badKeyMessage = (key) => `Invalid option-map key "${key}". Expected "fieldId::optionId" pointing to an existing option.`;
3624
3909
  for (const k of Object.keys(incMap)) {
3625
- const p = parseKey(k);
3626
- if (!p || !hasOption(p.fieldId, p.optionId)) {
3910
+ const r = validateTriggerKey(k);
3911
+ if (!r.ok) {
3627
3912
  v.errors.push({
3628
3913
  code: "bad_option_key",
3629
3914
  severity: "error",
3630
3915
  message: badKeyMessage(k),
3631
- details: { key: k }
3916
+ nodeId: r.nodeId,
3917
+ details: withAffected({ key: k }, r.affected)
3632
3918
  });
3633
3919
  }
3634
3920
  }
3635
3921
  for (const k of Object.keys(excMap)) {
3636
- const p = parseKey(k);
3637
- if (!p || !hasOption(p.fieldId, p.optionId)) {
3922
+ const r = validateTriggerKey(k);
3923
+ if (!r.ok) {
3638
3924
  v.errors.push({
3639
3925
  code: "bad_option_key",
3640
3926
  severity: "error",
3641
3927
  message: badKeyMessage(k),
3642
- details: { key: k }
3928
+ nodeId: r.nodeId,
3929
+ details: withAffected({ key: k }, r.affected)
3643
3930
  });
3644
3931
  }
3645
3932
  }
3646
3933
  for (const k of Object.keys(incMap)) {
3647
- if (k in excMap) {
3648
- const p = parseKey(k);
3649
- const affected = p ? [p.fieldId, p.optionId] : void 0;
3650
- v.errors.push({
3651
- code: "option_include_exclude_conflict",
3652
- severity: "error",
3653
- message: `Option-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
3654
- nodeId: p == null ? void 0 : p.fieldId,
3655
- details: withAffected({ key: k }, affected)
3656
- });
3657
- }
3934
+ if (!(k in excMap)) continue;
3935
+ const r = validateTriggerKey(k);
3936
+ v.errors.push({
3937
+ code: "option_include_exclude_conflict",
3938
+ severity: "error",
3939
+ message: `Trigger-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
3940
+ nodeId: r.nodeId,
3941
+ details: withAffected({ key: k }, r.affected)
3942
+ });
3658
3943
  }
3659
3944
  }
3660
3945
 
@@ -4559,8 +4844,23 @@ function applyPolicies(errors, props, serviceMap, policies, fieldsVisibleUnder,
4559
4844
  }
4560
4845
 
4561
4846
  // src/core/validate/index.ts
4847
+ function readVisibilitySimOpts(ctx) {
4848
+ const c = ctx;
4849
+ const simulate = c.simulateVisibility === true || c.visibilitySimulate === true || c.simulate === true;
4850
+ const maxStates = typeof c.maxVisibilityStates === "number" ? c.maxVisibilityStates : typeof c.visibilityMaxStates === "number" ? c.visibilityMaxStates : typeof c.maxStates === "number" ? c.maxStates : void 0;
4851
+ const maxDepth = typeof c.maxVisibilityDepth === "number" ? c.maxVisibilityDepth : typeof c.visibilityMaxDepth === "number" ? c.visibilityMaxDepth : typeof c.maxDepth === "number" ? c.maxDepth : void 0;
4852
+ const simulateAllRoots = c.simulateAllRoots === true || c.visibilitySimulateAllRoots === true;
4853
+ const onlyEffectfulTriggers = c.onlyEffectfulTriggers === false ? false : c.visibilityOnlyEffectfulTriggers !== false;
4854
+ return {
4855
+ simulate,
4856
+ maxStates,
4857
+ maxDepth,
4858
+ simulateAllRoots,
4859
+ onlyEffectfulTriggers
4860
+ };
4861
+ }
4562
4862
  function validate(props, ctx = {}) {
4563
- var _a, _b;
4863
+ var _a, _b, _c;
4564
4864
  const errors = [];
4565
4865
  const serviceMap = (_a = ctx.serviceMap) != null ? _a : {};
4566
4866
  const selectedKeys = new Set(
@@ -4574,6 +4874,7 @@ function validate(props, ctx = {}) {
4574
4874
  for (const f of fields) fieldById.set(f.id, f);
4575
4875
  const v = {
4576
4876
  props,
4877
+ nodeMap: (_c = ctx.nodeMap) != null ? _c : buildNodeMap(props),
4577
4878
  options: ctx,
4578
4879
  errors,
4579
4880
  serviceMap,
@@ -4588,7 +4889,8 @@ function validate(props, ctx = {}) {
4588
4889
  validateIdentity(v);
4589
4890
  validateOptionMaps(v);
4590
4891
  v.fieldsVisibleUnder = createFieldsVisibleUnder(v);
4591
- validateVisibility(v);
4892
+ const visSim = readVisibilitySimOpts(ctx);
4893
+ validateVisibility(v, visSim);
4592
4894
  applyPolicies(
4593
4895
  v.errors,
4594
4896
  v.props,
@@ -4624,11 +4926,21 @@ var BuilderImpl = class {
4624
4926
  this.optionOwnerById = /* @__PURE__ */ new Map();
4625
4927
  this.history = [];
4626
4928
  this.future = [];
4929
+ this._nodemap = null;
4627
4930
  var _a;
4628
4931
  this.options = { ...opts };
4629
4932
  this.historyLimit = (_a = opts.historyLimit) != null ? _a : 50;
4630
4933
  }
4631
4934
  /* ───── lifecycle ─────────────────────────────────────────────────────── */
4935
+ isTagId(id) {
4936
+ return this.tagById.has(id);
4937
+ }
4938
+ isFieldId(id) {
4939
+ return this.fieldById.has(id);
4940
+ }
4941
+ isOptionId(id) {
4942
+ return this.optionOwnerById.has(id);
4943
+ }
4632
4944
  load(raw) {
4633
4945
  const next = normalise(raw, {
4634
4946
  defaultPricingRole: "base",
@@ -4848,129 +5160,16 @@ var BuilderImpl = class {
4848
5160
  return validate(this.props, this.options);
4849
5161
  }
4850
5162
  visibleFields(tagId, selectedKeys) {
4851
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
4852
- const props = this.props;
4853
- const tags = (_a = props.filters) != null ? _a : [];
4854
- const fields = (_b = props.fields) != null ? _b : [];
4855
- const tagById = new Map(tags.map((t) => [t.id, t]));
4856
- const tag = tagById.get(tagId);
4857
- if (!tag) return [];
4858
- const lineageDepth = /* @__PURE__ */ new Map();
4859
- {
4860
- const guard = /* @__PURE__ */ new Set();
4861
- let cur = tag;
4862
- let d = 0;
4863
- while (cur && !guard.has(cur.id)) {
4864
- lineageDepth.set(cur.id, d++);
4865
- guard.add(cur.id);
4866
- const parentId = cur.bind_id;
4867
- cur = parentId ? tagById.get(parentId) : void 0;
4868
- }
4869
- }
4870
- const isTagInLineage = (id) => lineageDepth.has(id);
4871
- const fieldById = new Map(fields.map((f) => [f.id, f]));
4872
- const optionOwnerFieldId = /* @__PURE__ */ new Map();
4873
- for (const f of fields) {
4874
- for (const o of (_c = f.options) != null ? _c : []) optionOwnerFieldId.set(o.id, f.id);
4875
- }
4876
- const ownerDepthForField = (f) => {
4877
- const b = f.bind_id;
4878
- if (!b) return void 0;
4879
- if (typeof b === "string") return lineageDepth.get(b);
4880
- let best = void 0;
4881
- for (const id of b) {
4882
- const d = lineageDepth.get(id);
4883
- if (d == null) continue;
4884
- if (best == null || d < best) best = d;
4885
- }
4886
- return best;
4887
- };
4888
- const ownerDepthForTrigger = (triggerId) => {
4889
- if (triggerId.startsWith("o:")) {
4890
- const fid = optionOwnerFieldId.get(triggerId);
4891
- if (!fid) return void 0;
4892
- const f2 = fieldById.get(fid);
4893
- if (!f2) return void 0;
4894
- return ownerDepthForField(f2);
4895
- }
4896
- const f = fieldById.get(triggerId);
4897
- if (!f || f.button !== true) return void 0;
4898
- return ownerDepthForField(f);
4899
- };
4900
- const tagInclude = new Set((_d = tag.includes) != null ? _d : []);
4901
- const tagExclude = new Set((_e = tag.excludes) != null ? _e : []);
4902
- const selected = new Set(
4903
- (_f = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _f : []
4904
- );
4905
- const incMap = (_g = props.includes_for_buttons) != null ? _g : {};
4906
- const excMap = (_h = props.excludes_for_buttons) != null ? _h : {};
4907
- const relevantTriggersInOrder = [];
4908
- for (const key of selected) {
4909
- const d = ownerDepthForTrigger(key);
4910
- if (d == null) continue;
4911
- relevantTriggersInOrder.push(key);
4912
- }
4913
- const visible = /* @__PURE__ */ new Set();
4914
- const isBoundToLineage = (f) => {
4915
- const b = f.bind_id;
4916
- if (!b) return false;
4917
- if (typeof b === "string") return isTagInLineage(b);
4918
- for (const id of b) if (isTagInLineage(id)) return true;
4919
- return false;
4920
- };
4921
- for (const f of fields) {
4922
- if (isBoundToLineage(f)) visible.add(f.id);
4923
- if (tagInclude.has(f.id)) visible.add(f.id);
4924
- }
4925
- for (const id of tagExclude) visible.delete(id);
4926
- const decide = /* @__PURE__ */ new Map();
4927
- const applyDecision = (fieldId, next) => {
4928
- const prev = decide.get(fieldId);
4929
- if (!prev) {
4930
- decide.set(fieldId, next);
4931
- return;
4932
- }
4933
- if (next.depth < prev.depth) {
4934
- decide.set(fieldId, next);
4935
- return;
4936
- }
4937
- if (next.depth > prev.depth) return;
4938
- if (prev.kind === "include" && next.kind === "exclude") {
4939
- decide.set(fieldId, next);
4940
- }
4941
- };
4942
- const revealedOrder = [];
4943
- const revealedSeen = /* @__PURE__ */ new Set();
4944
- for (const triggerId of relevantTriggersInOrder) {
4945
- const depth = ownerDepthForTrigger(triggerId);
4946
- if (depth == null) continue;
4947
- for (const id of (_i = incMap[triggerId]) != null ? _i : []) {
4948
- applyDecision(id, { depth, kind: "include" });
4949
- if (!revealedSeen.has(id)) {
4950
- revealedSeen.add(id);
4951
- revealedOrder.push(id);
4952
- }
4953
- }
4954
- for (const id of (_j = excMap[triggerId]) != null ? _j : []) {
4955
- applyDecision(id, { depth, kind: "exclude" });
4956
- }
4957
- }
4958
- for (const [fid, d] of decide) {
4959
- if (d.kind === "include") visible.add(fid);
4960
- else visible.delete(fid);
4961
- }
4962
- const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
4963
- const order = (_k = props.order_for_tags) == null ? void 0 : _k[tagId];
4964
- if (order && order.length) {
4965
- const ordered = order.filter((fid) => visible.has(fid));
4966
- const orderedSet = new Set(ordered);
4967
- const rest2 = base.filter((fid) => !orderedSet.has(fid));
4968
- return [...ordered, ...rest2];
4969
- }
4970
- const promoted = revealedOrder.filter((fid) => visible.has(fid));
4971
- const promotedSet = new Set(promoted);
4972
- const rest = base.filter((fid) => !promotedSet.has(fid));
4973
- return [...promoted, ...rest];
5163
+ var _a;
5164
+ return visibleFieldIdsUnder(this.props, tagId, {
5165
+ selectedKeys: new Set(
5166
+ (_a = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _a : []
5167
+ )
5168
+ });
5169
+ }
5170
+ getNodeMap() {
5171
+ if (!this._nodemap) this._nodemap = buildNodeMap(this.getProps());
5172
+ return this._nodemap;
4974
5173
  }
4975
5174
  /* ───── history ─────────────────────────────────────────────────────── */
4976
5175
  undo() {
@@ -4994,6 +5193,7 @@ var BuilderImpl = class {
4994
5193
  this.tagById.clear();
4995
5194
  this.fieldById.clear();
4996
5195
  this.optionOwnerById.clear();
5196
+ this._nodemap = null;
4997
5197
  for (const t of this.props.filters) this.tagById.set(t.id, t);
4998
5198
  for (const f of this.props.fields) {
4999
5199
  this.fieldById.set(f.id, f);
@@ -5221,6 +5421,7 @@ var toBindList = (b) => {
5221
5421
  function createNodeIndex(builder) {
5222
5422
  var _a, _b, _c, _d;
5223
5423
  const props = builder.getProps();
5424
+ const nodeMap = builder.getNodeMap();
5224
5425
  const tags = (_a = props.filters) != null ? _a : [];
5225
5426
  const fields = (_b = props.fields) != null ? _b : [];
5226
5427
  const tagById = new Map(tags.map((t) => [t.id, t]));
@@ -5424,7 +5625,9 @@ function createNodeIndex(builder) {
5424
5625
  return parentById.get(id) === tid;
5425
5626
  },
5426
5627
  getDescendant(descendantId) {
5427
- return this.getDescendants().find((item) => item.id == descendantId);
5628
+ return this.getDescendants().find(
5629
+ (item) => item.id == descendantId
5630
+ );
5428
5631
  },
5429
5632
  getDescendants() {
5430
5633
  const results = [];
@@ -5433,7 +5636,9 @@ function createNodeIndex(builder) {
5433
5636
  const node2 = getField(fieldId);
5434
5637
  if (!node2) continue;
5435
5638
  const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, id);
5436
- results.push(explicit ? node2 : { ...node2, isInherited: true });
5639
+ results.push(
5640
+ explicit ? node2 : { ...node2, isInherited: true }
5641
+ );
5437
5642
  }
5438
5643
  return Object.freeze(results);
5439
5644
  }
@@ -5482,7 +5687,9 @@ function createNodeIndex(builder) {
5482
5687
  return false;
5483
5688
  },
5484
5689
  getDescendant(descendantId, context) {
5485
- return this.getDescendants(context).find((item) => item.id == descendantId);
5690
+ return this.getDescendants(context).find(
5691
+ (item) => item.id == descendantId
5692
+ );
5486
5693
  },
5487
5694
  getDescendants(tagId) {
5488
5695
  return resolveDescendants(id, includes, tagId, !isButton);
@@ -5524,7 +5731,9 @@ function createNodeIndex(builder) {
5524
5731
  return owner.isBoundTo(tagId);
5525
5732
  },
5526
5733
  getDescendant(descendantId, context) {
5527
- return this.getDescendants(context).find((item) => item.id == descendantId);
5734
+ return this.getDescendants(context).find(
5735
+ (item) => item.id == descendantId
5736
+ );
5528
5737
  },
5529
5738
  getDescendants(tagId) {
5530
5739
  return resolveDescendants(id, includes, tagId);
@@ -5535,7 +5744,7 @@ function createNodeIndex(builder) {
5535
5744
  return node;
5536
5745
  };
5537
5746
  const getNode = (input) => {
5538
- var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i;
5747
+ var _a2, _b2, _c2, _d2, _e;
5539
5748
  if (typeof input !== "string") {
5540
5749
  if ("bind_id" in input && !("type" in input))
5541
5750
  return (_a2 = getTag(input.id)) != null ? _a2 : mkUnknown(input.id);
@@ -5545,11 +5754,7 @@ function createNodeIndex(builder) {
5545
5754
  }
5546
5755
  const cached = nodeCache.get(input);
5547
5756
  if (cached) return cached;
5548
- const id = input;
5549
- if (id.startsWith("t:")) return (_d2 = getTag(id)) != null ? _d2 : mkUnknown(id);
5550
- if (id.startsWith("f:")) return (_e = getField(id)) != null ? _e : mkUnknown(id);
5551
- if (id.startsWith("o:")) return (_f = getOption(id)) != null ? _f : mkUnknown(id);
5552
- return (_i = (_h = (_g = getTag(id)) != null ? _g : getField(id)) != null ? _h : getOption(id)) != null ? _i : mkUnknown(id);
5757
+ return (_e = (_d2 = nodeMap.get(input)) == null ? void 0 : _d2.node) != null ? _e : mkUnknown(input);
5553
5758
  };
5554
5759
  const mkUnknown = (id) => {
5555
5760
  const u = {
@@ -5609,9 +5814,6 @@ function rateOk(svcMap, candidate, primary, policy) {
5609
5814
 
5610
5815
  // src/react/canvas/editor.ts
5611
5816
  var MAX_LIMIT = 100;
5612
- var isTagId = (id) => id.startsWith("t:");
5613
- var isFieldId = (id) => id.startsWith("f:");
5614
- var isOptionId = (id) => id.startsWith("o:");
5615
5817
  function ownerOfOption(props, optionId) {
5616
5818
  var _a, _b;
5617
5819
  for (const f of (_a = props.fields) != null ? _a : []) {
@@ -5653,6 +5855,15 @@ var Editor = class {
5653
5855
  this.pushHistory(this.makeSnapshot("init"));
5654
5856
  }
5655
5857
  /* ───────────────────────── Public API ───────────────────────── */
5858
+ isTagId(id) {
5859
+ return this.builder.isTagId(id);
5860
+ }
5861
+ isFieldId(id) {
5862
+ return this.builder.isFieldId(id);
5863
+ }
5864
+ isOptionId(id) {
5865
+ return this.builder.isOptionId(id);
5866
+ }
5656
5867
  getProps() {
5657
5868
  return this.builder.getProps();
5658
5869
  }
@@ -5748,7 +5959,7 @@ var Editor = class {
5748
5959
  name: "reLabel",
5749
5960
  do: () => this.patchProps((p) => {
5750
5961
  var _a, _b, _c, _d, _e, _f, _g;
5751
- if (isTagId(id)) {
5962
+ if (this.isTagId(id)) {
5752
5963
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
5753
5964
  if (!t) return;
5754
5965
  if (((_b = t.label) != null ? _b : "") === label) return;
@@ -5756,7 +5967,7 @@ var Editor = class {
5756
5967
  this.api.refreshGraph();
5757
5968
  return;
5758
5969
  }
5759
- if (isOptionId(id)) {
5970
+ if (this.isOptionId(id)) {
5760
5971
  const own = ownerOfOption(p, id);
5761
5972
  if (!own) return;
5762
5973
  const f = ((_c = p.fields) != null ? _c : []).find(
@@ -6057,7 +6268,7 @@ var Editor = class {
6057
6268
  * - Option: use placeOption() instead
6058
6269
  */
6059
6270
  placeNode(id, opts) {
6060
- if (isTagId(id)) {
6271
+ if (this.isTagId(id)) {
6061
6272
  this.exec({
6062
6273
  name: "placeTag",
6063
6274
  do: () => this.patchProps((p) => {
@@ -6104,7 +6315,7 @@ var Editor = class {
6104
6315
  }),
6105
6316
  undo: () => this.api.undo()
6106
6317
  });
6107
- } else if (isFieldId(id)) {
6318
+ } else if (this.isFieldId(id)) {
6108
6319
  if (!opts.scopeTagId)
6109
6320
  throw new Error("placeNode(field): scopeTagId is required");
6110
6321
  const fieldId = id;
@@ -6131,14 +6342,14 @@ var Editor = class {
6131
6342
  }),
6132
6343
  undo: () => this.api.undo()
6133
6344
  });
6134
- } else if (isOptionId(id)) {
6345
+ } else if (this.isOptionId(id)) {
6135
6346
  this.placeOption(id, opts);
6136
6347
  } else {
6137
6348
  throw new Error("placeNode: unknown id prefix");
6138
6349
  }
6139
6350
  }
6140
6351
  placeOption(optionId, opts) {
6141
- if (!isOptionId(optionId))
6352
+ if (!this.isOptionId(optionId))
6142
6353
  throw new Error('placeOption: optionId must start with "o:"');
6143
6354
  this.exec({
6144
6355
  name: "placeOption",
@@ -6195,7 +6406,7 @@ var Editor = class {
6195
6406
  return id;
6196
6407
  }
6197
6408
  updateOption(optionId, patch) {
6198
- if (!isOptionId(optionId))
6409
+ if (!this.isOptionId(optionId))
6199
6410
  throw new Error('updateOption: optionId must start with "o:"');
6200
6411
  this.exec({
6201
6412
  name: "updateOption",
@@ -6214,7 +6425,7 @@ var Editor = class {
6214
6425
  });
6215
6426
  }
6216
6427
  removeOption(optionId) {
6217
- if (!isOptionId(optionId))
6428
+ if (!this.isOptionId(optionId))
6218
6429
  throw new Error('removeOption: optionId must start with "o:"');
6219
6430
  this.exec({
6220
6431
  name: "removeOption",
@@ -6245,17 +6456,17 @@ var Editor = class {
6245
6456
  name: "editLabel",
6246
6457
  do: () => this.patchProps((p) => {
6247
6458
  var _a, _b, _c, _d;
6248
- if (isTagId(id)) {
6459
+ if (this.isTagId(id)) {
6249
6460
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6250
6461
  if (t) t.label = next;
6251
6462
  return;
6252
6463
  }
6253
- if (isFieldId(id)) {
6464
+ if (this.isFieldId(id)) {
6254
6465
  const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === id);
6255
6466
  if (f) f.label = next;
6256
6467
  return;
6257
6468
  }
6258
- if (isOptionId(id)) {
6469
+ if (this.isOptionId(id)) {
6259
6470
  const own = ownerOfOption(p, id);
6260
6471
  if (!own) return;
6261
6472
  const f = ((_c = p.fields) != null ? _c : []).find(
@@ -6294,7 +6505,7 @@ var Editor = class {
6294
6505
  const validId = hasSidKey && typeof input.service_id === "number" && Number.isFinite(input.service_id);
6295
6506
  const sid = validId ? Number(input.service_id) : void 0;
6296
6507
  const nextRole = input.pricing_role;
6297
- if (isTagId(id)) {
6508
+ if (this.isTagId(id)) {
6298
6509
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6299
6510
  if (!t) return;
6300
6511
  if (hasSidKey) {
@@ -6303,7 +6514,7 @@ var Editor = class {
6303
6514
  }
6304
6515
  return;
6305
6516
  }
6306
- if (isOptionId(id)) {
6517
+ if (this.isOptionId(id)) {
6307
6518
  const own = ownerOfOption(p, id);
6308
6519
  if (!own) return;
6309
6520
  const f2 = ((_b = p.fields) != null ? _b : []).find(
@@ -6510,7 +6721,7 @@ var Editor = class {
6510
6721
  });
6511
6722
  }
6512
6723
  remove(id) {
6513
- if (isTagId(id)) {
6724
+ if (this.isTagId(id)) {
6514
6725
  this.exec({
6515
6726
  name: "removeTag",
6516
6727
  do: () => this.patchProps((p) => {
@@ -6550,7 +6761,7 @@ var Editor = class {
6550
6761
  });
6551
6762
  return;
6552
6763
  }
6553
- if (isFieldId(id)) {
6764
+ if (this.isFieldId(id)) {
6554
6765
  this.exec({
6555
6766
  name: "removeField",
6556
6767
  do: () => this.patchProps((p) => {
@@ -6586,7 +6797,7 @@ var Editor = class {
6586
6797
  });
6587
6798
  return;
6588
6799
  }
6589
- if (isOptionId(id)) {
6800
+ if (this.isOptionId(id)) {
6590
6801
  this.removeOption(id);
6591
6802
  return;
6592
6803
  }
@@ -6595,7 +6806,7 @@ var Editor = class {
6595
6806
  getNode(id) {
6596
6807
  var _a, _b, _c, _d;
6597
6808
  const props = this.builder.getProps();
6598
- if (isTagId(id)) {
6809
+ if (this.isTagId(id)) {
6599
6810
  const t = ((_a = props.filters) != null ? _a : []).find((x) => x.id === id);
6600
6811
  return {
6601
6812
  kind: "tag",
@@ -6603,12 +6814,12 @@ var Editor = class {
6603
6814
  owners: { parentTagId: t == null ? void 0 : t.bind_id }
6604
6815
  };
6605
6816
  }
6606
- if (isFieldId(id)) {
6817
+ if (this.isFieldId(id)) {
6607
6818
  const f = ((_b = props.fields) != null ? _b : []).find((x) => x.id === id);
6608
6819
  const bind = Array.isArray(f == null ? void 0 : f.bind_id) ? f.bind_id : (f == null ? void 0 : f.bind_id) ? [f.bind_id] : [];
6609
6820
  return { kind: "field", data: f, owners: { bindTagIds: bind } };
6610
6821
  }
6611
- if (isOptionId(id)) {
6822
+ if (this.isOptionId(id)) {
6612
6823
  const own = ownerOfOption(props, id);
6613
6824
  const f = own ? ((_c = props.fields) != null ? _c : []).find((x) => x.id === own.fieldId) : void 0;
6614
6825
  const o = (_d = f == null ? void 0 : f.options) == null ? void 0 : _d.find((x) => x.id === id);
@@ -6686,7 +6897,7 @@ var Editor = class {
6686
6897
  if (receiverId === targetId) return true;
6687
6898
  const getDirectRelations = (id) => {
6688
6899
  var _a, _b, _c, _d, _e, _f, _g;
6689
- if (isTagId(id)) {
6900
+ if (this.isTagId(id)) {
6690
6901
  const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === id);
6691
6902
  return [...(_b = t == null ? void 0 : t.includes) != null ? _b : [], ...(_c = t == null ? void 0 : t.excludes) != null ? _c : []];
6692
6903
  }
@@ -6906,7 +7117,7 @@ var Editor = class {
6906
7117
  do: () => this.patchProps((p) => {
6907
7118
  var _a, _b, _c, _d, _e;
6908
7119
  if (kind === "bind") {
6909
- if (isTagId(fromId) && isTagId(toId2)) {
7120
+ if (this.isTagId(fromId) && this.isTagId(toId2)) {
6910
7121
  if (this.wouldCreateTagCycle(p, fromId, toId2)) {
6911
7122
  throw new Error(
6912
7123
  `bind would create a cycle: ${fromId} \u2192 ${toId2}`
@@ -6918,9 +7129,9 @@ var Editor = class {
6918
7129
  if (child) child.bind_id = fromId;
6919
7130
  return;
6920
7131
  }
6921
- if (isTagId(fromId) && isFieldId(toId2) || isFieldId(fromId) && isTagId(toId2)) {
6922
- const fieldId = isFieldId(toId2) ? toId2 : fromId;
6923
- const tagId = isTagId(fromId) ? fromId : toId2;
7132
+ if (this.isTagId(fromId) && this.isFieldId(toId2) || this.isFieldId(fromId) && this.isTagId(toId2)) {
7133
+ const fieldId = this.isFieldId(toId2) ? toId2 : fromId;
7134
+ const tagId = this.isTagId(fromId) ? fromId : toId2;
6924
7135
  const f = ((_b = p.fields) != null ? _b : []).find(
6925
7136
  (x) => x.id === fieldId
6926
7137
  );
@@ -6944,7 +7155,7 @@ var Editor = class {
6944
7155
  }
6945
7156
  if (kind === "include" || kind === "exclude") {
6946
7157
  const key = kind === "include" ? "includes" : "excludes";
6947
- if (isTagId(fromId) && isFieldId(toId2)) {
7158
+ if (this.isTagId(fromId) && this.isFieldId(toId2)) {
6948
7159
  const t = ((_c = p.filters) != null ? _c : []).find(
6949
7160
  (x) => x.id === fromId
6950
7161
  );
@@ -6953,7 +7164,7 @@ var Editor = class {
6953
7164
  if (!arr.includes(toId2)) arr.push(toId2);
6954
7165
  return;
6955
7166
  }
6956
- if (isOptionId(fromId) && isFieldId(toId2)) {
7167
+ if (this.isOptionId(fromId) && this.isFieldId(toId2)) {
6957
7168
  const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
6958
7169
  const maps = p[mapKey];
6959
7170
  const next = { ...maps != null ? maps : {} };
@@ -7021,16 +7232,16 @@ var Editor = class {
7021
7232
  do: () => this.patchProps((p) => {
7022
7233
  var _a, _b, _c, _d, _e, _f, _g, _h;
7023
7234
  if (kind === "bind") {
7024
- if (isTagId(fromId) && isTagId(toId2)) {
7235
+ if (this.isTagId(fromId) && this.isTagId(toId2)) {
7025
7236
  const child = ((_a = p.filters) != null ? _a : []).find(
7026
7237
  (t) => t.id === toId2
7027
7238
  );
7028
7239
  if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
7029
7240
  return;
7030
7241
  }
7031
- if (isTagId(fromId) && isFieldId(toId2) || isFieldId(fromId) && isTagId(toId2)) {
7032
- const fieldId = isFieldId(toId2) ? toId2 : fromId;
7033
- const tagId = isTagId(fromId) ? fromId : toId2;
7242
+ if (this.isTagId(fromId) && this.isFieldId(toId2) || this.isFieldId(fromId) && this.isTagId(toId2)) {
7243
+ const fieldId = this.isFieldId(toId2) ? toId2 : fromId;
7244
+ const tagId = this.isTagId(fromId) ? fromId : toId2;
7034
7245
  const f = ((_b = p.fields) != null ? _b : []).find(
7035
7246
  (x) => x.id === fieldId
7036
7247
  );
@@ -7051,7 +7262,7 @@ var Editor = class {
7051
7262
  }
7052
7263
  if (kind === "include" || kind === "exclude") {
7053
7264
  const key = kind === "include" ? "includes" : "excludes";
7054
- if (isTagId(fromId) && isFieldId(toId2)) {
7265
+ if (this.isTagId(fromId) && this.isFieldId(toId2)) {
7055
7266
  const t = ((_d = p.filters) != null ? _d : []).find(
7056
7267
  (x) => x.id === fromId
7057
7268
  );
@@ -7060,7 +7271,7 @@ var Editor = class {
7060
7271
  if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
7061
7272
  return;
7062
7273
  }
7063
- if (isOptionId(fromId) && isFieldId(toId2)) {
7274
+ if (this.isOptionId(fromId) && this.isFieldId(toId2)) {
7064
7275
  const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7065
7276
  const maps = p[mapKey];
7066
7277
  if (!maps) return;
@@ -7527,8 +7738,6 @@ function toStrSet(v) {
7527
7738
  }
7528
7739
 
7529
7740
  // src/react/canvas/selection.ts
7530
- var isTagId2 = (id) => typeof id === "string" && id.startsWith("t:");
7531
- var isOptionId2 = (id) => typeof id === "string" && id.startsWith("o:");
7532
7741
  var Selection = class {
7533
7742
  constructor(builder, opts) {
7534
7743
  this.builder = builder;
@@ -7600,7 +7809,7 @@ var Selection = class {
7600
7809
  var _a;
7601
7810
  const props = this.builder.getProps();
7602
7811
  if (((_a = this.opts.env) != null ? _a : "client") === "workspace") {
7603
- const tagIds = Array.from(this.set).filter(isTagId2);
7812
+ const tagIds = Array.from(this.set).filter(this.builder.isTagId.bind(this.builder));
7604
7813
  if (tagIds.length > 1) {
7605
7814
  return { kind: "multi", groups: Array.from(this.set) };
7606
7815
  }
@@ -7633,7 +7842,7 @@ var Selection = class {
7633
7842
  this.currentTagId = Array.isArray(f.bind_id) ? f.bind_id[0] : f.bind_id;
7634
7843
  return;
7635
7844
  }
7636
- if (isOptionId2(id)) {
7845
+ if (this.builder.isOptionId(id)) {
7637
7846
  const host = fields.find(
7638
7847
  (x) => {
7639
7848
  var _a2;
@@ -7657,7 +7866,7 @@ var Selection = class {
7657
7866
  resolveTagContextId(props) {
7658
7867
  var _a;
7659
7868
  if (this.currentTagId) return this.currentTagId;
7660
- for (const id of this.set) if (isTagId2(id)) return id;
7869
+ for (const id of this.set) if (this.builder.isTagId(id)) return id;
7661
7870
  const fields = (_a = props.fields) != null ? _a : [];
7662
7871
  for (const id of this.set) {
7663
7872
  const f = fields.find((x) => x.id === id);
@@ -7665,7 +7874,7 @@ var Selection = class {
7665
7874
  return Array.isArray(f.bind_id) ? f.bind_id[0] : f.bind_id;
7666
7875
  }
7667
7876
  for (const id of this.set) {
7668
- if (isOptionId2(id)) {
7877
+ if (this.builder.isOptionId(id)) {
7669
7878
  const host = fields.find(
7670
7879
  (x) => {
7671
7880
  var _a2;
@@ -7786,7 +7995,7 @@ var Selection = class {
7786
7995
  }
7787
7996
  findOptionById(fields, selId) {
7788
7997
  var _a, _b;
7789
- if (isOptionId2(selId)) {
7998
+ if (this.builder.isOptionId(selId)) {
7790
7999
  for (const f of fields) {
7791
8000
  const o = (_a = f.options) == null ? void 0 : _a.find((x) => x.id === selId);
7792
8001
  if (o) return o;
@@ -8208,8 +8417,8 @@ function useCanvasOwned(initialProps, canvasOpts, builderOpts) {
8208
8417
  // src/react/workspace/context/hooks/use-canvas.ts
8209
8418
  import * as React16 from "react";
8210
8419
  import { useMemo as useMemo15 } from "react";
8211
- var isTagId3 = (id) => id.startsWith("t:");
8212
- var isOptionId3 = (id) => id.startsWith("o:");
8420
+ var isTagId = (id) => id.startsWith("t:");
8421
+ var isOptionId = (id) => id.startsWith("o:");
8213
8422
  function deriveSelectionInfo(props, ids) {
8214
8423
  var _a, _b, _c, _d;
8215
8424
  const tags = [];
@@ -8218,11 +8427,11 @@ function deriveSelectionInfo(props, ids) {
8218
8427
  const fieldById = /* @__PURE__ */ new Map();
8219
8428
  for (const f of (_a = props.fields) != null ? _a : []) fieldById.set(f.id, f);
8220
8429
  for (const id of ids) {
8221
- if (isTagId3(id)) {
8430
+ if (isTagId(id)) {
8222
8431
  tags.push(id);
8223
8432
  continue;
8224
8433
  }
8225
- if (isOptionId3(id)) {
8434
+ if (isOptionId(id)) {
8226
8435
  options.push(id);
8227
8436
  continue;
8228
8437
  }