@timeax/digital-service-engine 0.2.1 → 0.2.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.
@@ -1845,6 +1845,9 @@ function toFiniteNumber(v) {
1845
1845
  const n = Number(v);
1846
1846
  return Number.isFinite(n) ? n : NaN;
1847
1847
  }
1848
+ function isValidServiceIdRef(value) {
1849
+ return typeof value === "number" && Number.isFinite(value) || typeof value === "string" && value.trim().length > 0;
1850
+ }
1848
1851
  function constraintFitOk(svcMap, candidate, constraints) {
1849
1852
  const cap = getServiceCapability(svcMap, candidate);
1850
1853
  if (!cap) return false;
@@ -1853,18 +1856,32 @@ function constraintFitOk(svcMap, candidate, constraints) {
1853
1856
  return !(constraints.cancel === true && !cap.cancel);
1854
1857
  }
1855
1858
  function getServiceCapability(svcMap, candidate) {
1856
- if (candidate === void 0 || candidate === null) return void 0;
1857
- const direct = svcMap[candidate];
1858
- if (direct) return direct;
1859
- const byString = svcMap[String(candidate)];
1860
- if (byString) return byString;
1861
- if (typeof candidate === "string") {
1862
- const maybeNumber = Number(candidate);
1863
- if (Number.isFinite(maybeNumber)) {
1864
- return svcMap[maybeNumber];
1865
- }
1859
+ var _a;
1860
+ return (_a = getServiceCapabilityEntry(svcMap, candidate)) == null ? void 0 : _a.capability;
1861
+ }
1862
+ function getServiceCapabilityCanonicalRef(svcMap, candidate) {
1863
+ const entry = getServiceCapabilityEntry(svcMap, candidate);
1864
+ if (!entry) return void 0;
1865
+ return getCanonicalServiceRef(entry.key, entry.capability);
1866
+ }
1867
+ function getServiceCapabilityAliases(svcMap, candidate) {
1868
+ const entry = getServiceCapabilityEntry(svcMap, candidate);
1869
+ if (!entry) return [];
1870
+ return collectServiceRefAliases(entry.key, entry.capability);
1871
+ }
1872
+ function isSameServiceCapabilityRef(svcMap, left, right) {
1873
+ if (!isValidServiceIdRef(left) || !isValidServiceIdRef(right)) return false;
1874
+ const leftAliases = new Set(
1875
+ getServiceCapabilityAliases(svcMap, left).map((value) => String(value))
1876
+ );
1877
+ if (!leftAliases.size) {
1878
+ leftAliases.add(String(left));
1866
1879
  }
1867
- return void 0;
1880
+ const rightAliases = getServiceCapabilityAliases(svcMap, right);
1881
+ if (!rightAliases.length) {
1882
+ return leftAliases.has(String(right));
1883
+ }
1884
+ return rightAliases.some((value) => leftAliases.has(String(value)));
1868
1885
  }
1869
1886
  function normalizeRatePolicy(policy) {
1870
1887
  var _a;
@@ -1900,6 +1917,74 @@ function rateOk(svcMap, candidate, primary, policy) {
1900
1917
  if (!Number.isFinite(cRate) || !Number.isFinite(pRate)) return false;
1901
1918
  return passesRatePolicy(policy.ratePolicy, pRate, cRate);
1902
1919
  }
1920
+ function getServiceCapabilityEntry(svcMap, candidate) {
1921
+ if (candidate === void 0 || candidate === null) return void 0;
1922
+ const direct = svcMap[candidate];
1923
+ if (direct) {
1924
+ return { key: String(candidate), capability: direct };
1925
+ }
1926
+ const byString = svcMap[String(candidate)];
1927
+ if (byString) {
1928
+ return { key: String(candidate), capability: byString };
1929
+ }
1930
+ if (typeof candidate === "string") {
1931
+ const maybeNumber = Number(candidate);
1932
+ if (Number.isFinite(maybeNumber)) {
1933
+ const byNumber = svcMap[maybeNumber];
1934
+ if (byNumber) {
1935
+ return { key: String(maybeNumber), capability: byNumber };
1936
+ }
1937
+ }
1938
+ }
1939
+ const target = String(candidate);
1940
+ for (const [key, capability] of Object.entries(svcMap != null ? svcMap : {})) {
1941
+ if (collectServiceRefAliases(key, capability).some(
1942
+ (alias) => String(alias) === target
1943
+ )) {
1944
+ return { key, capability };
1945
+ }
1946
+ }
1947
+ return void 0;
1948
+ }
1949
+ function collectServiceRefAliases(key, capability) {
1950
+ const out = [];
1951
+ const seen = /* @__PURE__ */ new Set();
1952
+ const push = (value) => {
1953
+ if (!isValidServiceIdRef(value)) return;
1954
+ const normalized = normalizeServiceRef(value);
1955
+ if (!normalized) return;
1956
+ const aliasKey = String(normalized);
1957
+ if (seen.has(aliasKey)) return;
1958
+ seen.add(aliasKey);
1959
+ out.push(normalized);
1960
+ };
1961
+ push(getCanonicalServiceRef(key, capability));
1962
+ push(capability.service);
1963
+ push(capability.key);
1964
+ push(capability.id);
1965
+ return out;
1966
+ }
1967
+ function getCanonicalServiceRef(key, capability) {
1968
+ const explicitRefs = [capability.service, capability.key, capability.id];
1969
+ for (const ref of explicitRefs) {
1970
+ if (!isValidServiceIdRef(ref)) continue;
1971
+ if (String(ref) === key) {
1972
+ return ref;
1973
+ }
1974
+ }
1975
+ return normalizeServiceRef(key);
1976
+ }
1977
+ function normalizeServiceRef(value) {
1978
+ if (!isValidServiceIdRef(value)) return void 0;
1979
+ if (typeof value === "number") return value;
1980
+ const trimmed = value.trim();
1981
+ if (!trimmed) return void 0;
1982
+ const asNumber = Number(trimmed);
1983
+ if (Number.isFinite(asNumber) && String(asNumber) === trimmed) {
1984
+ return asNumber;
1985
+ }
1986
+ return trimmed;
1987
+ }
1903
1988
 
1904
1989
  // src/core/validate/steps/rates.ts
1905
1990
  function validateRates(v) {
@@ -3345,7 +3430,7 @@ function collectFailedFallbacks(props, services, settings) {
3345
3430
  const s = { ...DEFAULT_SETTINGS, ...settings != null ? settings : {} };
3346
3431
  const out = [];
3347
3432
  const fb = (_a = props.fallbacks) != null ? _a : {};
3348
- const primaryRate = (p) => rateOf(services, p);
3433
+ const primaryRate = (primary) => rateOf(services, primary);
3349
3434
  for (const [nodeId, list] of Object.entries((_b = fb.nodes) != null ? _b : {})) {
3350
3435
  const { primary, tagContexts } = primaryForNode(props, nodeId);
3351
3436
  if (!primary) {
@@ -3358,34 +3443,34 @@ function collectFailedFallbacks(props, services, settings) {
3358
3443
  });
3359
3444
  continue;
3360
3445
  }
3361
- for (const cand of list) {
3362
- const cap = getCap(services, cand);
3363
- if (!cap) {
3446
+ for (const candidate of list) {
3447
+ const capability = getCap(services, candidate);
3448
+ if (!capability) {
3364
3449
  out.push({
3365
3450
  scope: "node",
3366
3451
  nodeId,
3367
3452
  primary,
3368
- candidate: cand,
3453
+ candidate,
3369
3454
  reason: "unknown_service"
3370
3455
  });
3371
3456
  continue;
3372
3457
  }
3373
- if (String(cand) === String(primary)) {
3458
+ if (isSameServiceCapabilityRef(services, candidate, primary)) {
3374
3459
  out.push({
3375
3460
  scope: "node",
3376
3461
  nodeId,
3377
3462
  primary,
3378
- candidate: cand,
3463
+ candidate,
3379
3464
  reason: "cycle"
3380
3465
  });
3381
3466
  continue;
3382
3467
  }
3383
- if (!passesRate(s.ratePolicy, primaryRate(primary), cap.rate)) {
3468
+ if (!passesRate(s.ratePolicy, primaryRate(primary), capability.rate)) {
3384
3469
  out.push({
3385
3470
  scope: "node",
3386
3471
  nodeId,
3387
3472
  primary,
3388
- candidate: cand,
3473
+ candidate,
3389
3474
  reason: "rate_violation"
3390
3475
  });
3391
3476
  continue;
@@ -3395,58 +3480,55 @@ function collectFailedFallbacks(props, services, settings) {
3395
3480
  scope: "node",
3396
3481
  nodeId,
3397
3482
  primary,
3398
- candidate: cand,
3483
+ candidate,
3399
3484
  reason: "no_tag_context"
3400
3485
  });
3401
3486
  continue;
3402
3487
  }
3403
- let anyPass = false;
3404
- let anyFail = false;
3405
3488
  for (const tagId of tagContexts) {
3406
- const ok = s.requireConstraintFit ? satisfiesTagConstraints(tagId, { services, props }, cap) : true;
3407
- if (ok) anyPass = true;
3408
- else {
3409
- anyFail = true;
3410
- out.push({
3411
- scope: "node",
3412
- nodeId,
3413
- primary,
3414
- candidate: cand,
3415
- tagContext: tagId,
3416
- reason: "constraint_mismatch"
3417
- });
3418
- }
3489
+ const fitsConstraints = s.requireConstraintFit ? satisfiesTagConstraints(
3490
+ tagId,
3491
+ { services, props },
3492
+ capability
3493
+ ) : true;
3494
+ if (fitsConstraints) continue;
3495
+ out.push({
3496
+ scope: "node",
3497
+ nodeId,
3498
+ primary,
3499
+ candidate,
3500
+ tagContext: tagId,
3501
+ reason: "constraint_mismatch"
3502
+ });
3419
3503
  }
3420
- void anyPass;
3421
- void anyFail;
3422
3504
  }
3423
3505
  }
3424
3506
  for (const [primary, list] of Object.entries((_c = fb.global) != null ? _c : {})) {
3425
- for (const cand of list) {
3426
- const cap = getCap(services, cand);
3427
- if (!cap) {
3507
+ for (const candidate of list) {
3508
+ const capability = getCap(services, candidate);
3509
+ if (!capability) {
3428
3510
  out.push({
3429
3511
  scope: "global",
3430
3512
  primary,
3431
- candidate: cand,
3513
+ candidate,
3432
3514
  reason: "unknown_service"
3433
3515
  });
3434
3516
  continue;
3435
3517
  }
3436
- if (String(cand) === String(primary)) {
3518
+ if (isSameServiceCapabilityRef(services, candidate, primary)) {
3437
3519
  out.push({
3438
3520
  scope: "global",
3439
3521
  primary,
3440
- candidate: cand,
3522
+ candidate,
3441
3523
  reason: "cycle"
3442
3524
  });
3443
3525
  continue;
3444
3526
  }
3445
- if (!passesRate(s.ratePolicy, primaryRate(primary), cap.rate)) {
3527
+ if (!passesRate(s.ratePolicy, primaryRate(primary), capability.rate)) {
3446
3528
  out.push({
3447
3529
  scope: "global",
3448
3530
  primary,
3449
- candidate: cand,
3531
+ candidate,
3450
3532
  reason: "rate_violation"
3451
3533
  });
3452
3534
  }
@@ -3455,52 +3537,61 @@ function collectFailedFallbacks(props, services, settings) {
3455
3537
  return out;
3456
3538
  }
3457
3539
  function rateOf(map, id) {
3458
- var _a;
3540
+ var _a, _b;
3459
3541
  if (id === void 0 || id === null) return void 0;
3460
- const c = getCap(map, id);
3461
- return (_a = c == null ? void 0 : c.rate) != null ? _a : void 0;
3542
+ return (_b = (_a = getCap(map, id)) == null ? void 0 : _a.rate) != null ? _b : void 0;
3462
3543
  }
3463
- function passesRate(policy, primaryRate, candRate) {
3464
- if (typeof candRate !== "number" || !Number.isFinite(candRate))
3544
+ function passesRate(policy, primaryRate, candidateRate) {
3545
+ if (typeof candidateRate !== "number" || !Number.isFinite(candidateRate)) {
3465
3546
  return false;
3466
- if (typeof primaryRate !== "number" || !Number.isFinite(primaryRate))
3547
+ }
3548
+ if (typeof primaryRate !== "number" || !Number.isFinite(primaryRate)) {
3467
3549
  return false;
3468
- return passesRatePolicy(normalizeRatePolicy(policy), primaryRate, candRate);
3550
+ }
3551
+ return passesRatePolicy(
3552
+ normalizeRatePolicy(policy),
3553
+ primaryRate,
3554
+ candidateRate
3555
+ );
3469
3556
  }
3470
3557
  function getCap(map, id) {
3471
3558
  return getServiceCapability(map, id);
3472
3559
  }
3473
- function isCapFlagEnabled(cap, flagId) {
3560
+ function isCapFlagEnabled(capability, flagId) {
3474
3561
  var _a, _b;
3475
- const fromFlags = (_b = (_a = cap.flags) == null ? void 0 : _a[flagId]) == null ? void 0 : _b.enabled;
3562
+ const fromFlags = (_b = (_a = capability.flags) == null ? void 0 : _a[flagId]) == null ? void 0 : _b.enabled;
3476
3563
  if (fromFlags === true) return true;
3477
3564
  if (fromFlags === false) return false;
3478
- const legacy = cap[flagId];
3565
+ const legacy = capability[flagId];
3479
3566
  return legacy === true;
3480
3567
  }
3481
- function satisfiesTagConstraints(tagId, ctx, cap) {
3482
- const tag = ctx.props.filters.find((t) => t.id === tagId);
3483
- const eff = tag == null ? void 0 : tag.constraints;
3484
- if (!eff) return true;
3485
- for (const [key, value] of Object.entries(eff)) {
3486
- if (value === true && !isCapFlagEnabled(cap, key)) {
3568
+ function satisfiesTagConstraints(tagId, ctx, capability) {
3569
+ const tag = ctx.props.filters.find((item) => item.id === tagId);
3570
+ const effectiveConstraints2 = tag == null ? void 0 : tag.constraints;
3571
+ if (!effectiveConstraints2) return true;
3572
+ for (const [key, value] of Object.entries(effectiveConstraints2)) {
3573
+ if (value === true && !isCapFlagEnabled(capability, key)) {
3487
3574
  return false;
3488
3575
  }
3489
3576
  }
3490
3577
  return true;
3491
3578
  }
3492
3579
  function primaryForNode(props, nodeId) {
3493
- const tag = props.filters.find((t) => t.id === nodeId);
3580
+ const tag = props.filters.find((item) => item.id === nodeId);
3494
3581
  if (tag) {
3495
3582
  return { primary: tag.service_id, tagContexts: [tag.id] };
3496
3583
  }
3497
3584
  const field = props.fields.find(
3498
- (f) => Array.isArray(f.options) && f.options.some((o) => o.id === nodeId)
3585
+ (item) => Array.isArray(item.options) && item.options.some((option2) => option2.id === nodeId)
3499
3586
  );
3500
- if (!field) return { tagContexts: [], reasonNoPrimary: "no_parent_field" };
3501
- const opt = field.options.find((o) => o.id === nodeId);
3502
- const contexts = bindIdsToArray(field.bind_id);
3503
- return { primary: opt.service_id, tagContexts: contexts };
3587
+ if (!field) {
3588
+ return { tagContexts: [], reasonNoPrimary: "no_parent_field" };
3589
+ }
3590
+ const option = field.options.find((item) => item.id === nodeId);
3591
+ return {
3592
+ primary: option.service_id,
3593
+ tagContexts: bindIdsToArray(field.bind_id)
3594
+ };
3504
3595
  }
3505
3596
  function bindIdsToArray(bind) {
3506
3597
  if (!bind) return [];
@@ -3510,43 +3601,54 @@ function getEligibleFallbacks(params) {
3510
3601
  var _a, _b, _c, _d, _e, _f;
3511
3602
  const s = { ...DEFAULT_SETTINGS, ...(_a = params.settings) != null ? _a : {} };
3512
3603
  const { primary, nodeId, tagId, services } = params;
3513
- const fb = (_b = params.fallbacks) != null ? _b : {};
3514
- const excludes = new Set(((_c = params.exclude) != null ? _c : []).map(String));
3515
- excludes.add(String(primary));
3516
- const unique = (_d = params.unique) != null ? _d : true;
3517
- const lists = [];
3518
- if (nodeId && ((_e = fb.nodes) == null ? void 0 : _e[nodeId])) lists.push(fb.nodes[nodeId]);
3519
- if ((_f = fb.global) == null ? void 0 : _f[primary]) lists.push(fb.global[primary]);
3520
- if (!lists.length) return [];
3604
+ const excludes = /* @__PURE__ */ new Set();
3605
+ for (const ref of (_b = params.exclude) != null ? _b : []) {
3606
+ addComparableServiceRef(excludes, services, ref);
3607
+ }
3608
+ addComparableServiceRef(excludes, services, primary);
3609
+ const source = (_c = params.source) != null ? _c : "registered";
3610
+ const candidateLists = source === "all_services" ? [listServicePoolCandidates(services)] : listRegisteredFallbackCandidates(
3611
+ (_d = params.fallbacks) != null ? _d : {},
3612
+ primary,
3613
+ nodeId,
3614
+ services
3615
+ );
3616
+ if (!candidateLists.length) return [];
3521
3617
  const primaryRate = rateOf(services, primary);
3522
3618
  const seen = /* @__PURE__ */ new Set();
3523
3619
  const eligible = [];
3524
- for (const list of lists) {
3525
- for (const cand of list) {
3526
- const key = String(cand);
3527
- if (excludes.has(key)) continue;
3528
- if (unique && seen.has(key)) continue;
3529
- seen.add(key);
3530
- const cap = getCap(services, cand);
3531
- if (!cap) continue;
3532
- if (!passesRate(s.ratePolicy, primaryRate, cap.rate)) continue;
3620
+ for (const list of candidateLists) {
3621
+ for (const candidate of list) {
3622
+ if (hasComparableServiceRef(excludes, services, candidate)) continue;
3623
+ const capability = getCap(services, candidate);
3624
+ if (!capability) continue;
3625
+ const candidateId = (_e = getServiceCapabilityCanonicalRef(services, candidate)) != null ? _e : candidate;
3626
+ const candidateIdentity = getComparableServiceRefKey(
3627
+ services,
3628
+ candidateId
3629
+ );
3630
+ if (((_f = params.unique) != null ? _f : true) && seen.has(candidateIdentity)) continue;
3631
+ seen.add(candidateIdentity);
3632
+ if (!passesRate(s.ratePolicy, primaryRate, capability.rate)) {
3633
+ continue;
3634
+ }
3533
3635
  if (s.requireConstraintFit && tagId) {
3534
- const ok = satisfiesTagConstraints(
3636
+ const fitsConstraints = satisfiesTagConstraints(
3535
3637
  tagId,
3536
3638
  { props: params.props, services },
3537
- cap
3639
+ capability
3538
3640
  );
3539
- if (!ok) continue;
3641
+ if (!fitsConstraints) continue;
3540
3642
  }
3541
- eligible.push(cand);
3643
+ eligible.push(candidateId);
3542
3644
  }
3543
3645
  }
3544
3646
  if (s.selectionStrategy === "cheapest") {
3545
- eligible.sort((a, b) => {
3647
+ eligible.sort((left, right) => {
3546
3648
  var _a2, _b2;
3547
- const ra = (_a2 = rateOf(services, a)) != null ? _a2 : Infinity;
3548
- const rb = (_b2 = rateOf(services, b)) != null ? _b2 : Infinity;
3549
- return ra - rb;
3649
+ const leftRate = (_a2 = rateOf(services, left)) != null ? _a2 : Infinity;
3650
+ const rightRate = (_b2 = rateOf(services, right)) != null ? _b2 : Infinity;
3651
+ return leftRate - rightRate;
3550
3652
  });
3551
3653
  }
3552
3654
  if (typeof params.limit === "number" && params.limit >= 0) {
@@ -3554,10 +3656,104 @@ function getEligibleFallbacks(params) {
3554
3656
  }
3555
3657
  return eligible;
3556
3658
  }
3659
+ function getAssignedServiceIds(params) {
3660
+ var _a, _b, _c, _d, _e;
3661
+ const seen = /* @__PURE__ */ new Set();
3662
+ const out = [];
3663
+ const push = (value) => {
3664
+ if (!isValidServiceIdRef(value)) return;
3665
+ const key = String(value);
3666
+ if (seen.has(key)) return;
3667
+ seen.add(key);
3668
+ out.push(value);
3669
+ };
3670
+ const props = params.props;
3671
+ if (props) {
3672
+ for (const tag of (_a = props.filters) != null ? _a : []) {
3673
+ push(tag.service_id);
3674
+ }
3675
+ for (const field of (_b = props.fields) != null ? _b : []) {
3676
+ const fieldService = field.service_id;
3677
+ if (field.button === true) {
3678
+ push(fieldService);
3679
+ }
3680
+ for (const option of (_c = field.options) != null ? _c : []) {
3681
+ if (option.pricing_role === "utility") continue;
3682
+ push(option.service_id);
3683
+ }
3684
+ }
3685
+ }
3686
+ const snapshot = params.snapshot;
3687
+ if (snapshot) {
3688
+ for (const serviceId of (_d = snapshot.services) != null ? _d : []) {
3689
+ push(serviceId);
3690
+ }
3691
+ for (const list of Object.values((_e = snapshot.serviceMap) != null ? _e : {})) {
3692
+ for (const serviceId of list != null ? list : []) {
3693
+ push(serviceId);
3694
+ }
3695
+ }
3696
+ }
3697
+ return out;
3698
+ }
3557
3699
  function getFallbackRegistrationInfo(props, nodeId) {
3558
3700
  const { primary, tagContexts } = primaryForNode(props, nodeId);
3559
3701
  return { primary, tagContexts };
3560
3702
  }
3703
+ function listRegisteredFallbackCandidates(fallbacks, primary, nodeId, services) {
3704
+ var _a, _b;
3705
+ const lists = [];
3706
+ if (nodeId && ((_a = fallbacks.nodes) == null ? void 0 : _a[nodeId])) {
3707
+ lists.push(fallbacks.nodes[nodeId]);
3708
+ }
3709
+ for (const [registeredPrimary, list] of Object.entries((_b = fallbacks.global) != null ? _b : {})) {
3710
+ if (!isMatchingServiceRef(services, registeredPrimary, primary)) continue;
3711
+ lists.push(list);
3712
+ }
3713
+ return lists;
3714
+ }
3715
+ function listServicePoolCandidates(services) {
3716
+ const seen = /* @__PURE__ */ new Set();
3717
+ const out = [];
3718
+ for (const [key, capability] of Object.entries(services != null ? services : {})) {
3719
+ const candidate = getServicePoolCandidateId(key, capability);
3720
+ if (!isValidServiceIdRef(candidate)) continue;
3721
+ const identity = getComparableServiceRefKey(services, candidate);
3722
+ if (seen.has(identity)) continue;
3723
+ seen.add(identity);
3724
+ out.push(candidate);
3725
+ }
3726
+ return out;
3727
+ }
3728
+ function getServicePoolCandidateId(key, capability) {
3729
+ var _a;
3730
+ return (_a = getServiceCapabilityCanonicalRef({ [key]: capability }, key)) != null ? _a : key;
3731
+ }
3732
+ function addComparableServiceRef(target, services, value) {
3733
+ for (const ref of getComparableServiceRefs(services, value)) {
3734
+ target.add(ref);
3735
+ }
3736
+ }
3737
+ function hasComparableServiceRef(target, services, value) {
3738
+ return getComparableServiceRefs(services, value).some((ref) => target.has(ref));
3739
+ }
3740
+ function getComparableServiceRefKey(services, value) {
3741
+ if (!isValidServiceIdRef(value)) return "";
3742
+ const canonical = getServiceCapabilityCanonicalRef(services, value);
3743
+ return String(canonical != null ? canonical : value);
3744
+ }
3745
+ function getComparableServiceRefs(services, value) {
3746
+ if (!isValidServiceIdRef(value)) return [];
3747
+ const aliases = getServiceCapabilityAliases(services, value);
3748
+ if (!aliases.length) {
3749
+ return [String(value)];
3750
+ }
3751
+ return aliases.map((ref) => String(ref));
3752
+ }
3753
+ function isMatchingServiceRef(services, left, right) {
3754
+ if (!services) return String(left) === String(right);
3755
+ return isSameServiceCapabilityRef(services, left, right);
3756
+ }
3561
3757
 
3562
3758
  // src/core/tag-relations.ts
3563
3759
  var toId = (x) => typeof x === "string" ? x : x.id;
@@ -5096,6 +5292,7 @@ function createFallbackEditor(options = {}) {
5096
5292
  const original = cloneFallbacks(options.fallbacks);
5097
5293
  let current = cloneFallbacks(options.fallbacks);
5098
5294
  const props = options.props;
5295
+ const snapshot = options.snapshot;
5099
5296
  const services = (_a = options.services) != null ? _a : {};
5100
5297
  const settings = (_b = options.settings) != null ? _b : {};
5101
5298
  function state() {
@@ -5190,7 +5387,11 @@ function createFallbackEditor(options = {}) {
5190
5387
  const allowed2 = [];
5191
5388
  for (const candidate of normalized) {
5192
5389
  const reasons = [];
5193
- if (String(candidate) === String(context.primary)) {
5390
+ if (isSameServiceCapabilityRef(
5391
+ services,
5392
+ candidate,
5393
+ context.primary
5394
+ )) {
5194
5395
  reasons.push("self_reference");
5195
5396
  }
5196
5397
  if (reasons.length) {
@@ -5278,7 +5479,19 @@ function createFallbackEditor(options = {}) {
5278
5479
  return writeScope(context, []);
5279
5480
  }
5280
5481
  function eligible(context, opt) {
5482
+ var _a2, _b2;
5281
5483
  if (!props) return [];
5484
+ const source = (_a2 = opt == null ? void 0 : opt.source) != null ? _a2 : "all_services";
5485
+ const exclude2 = normalizeCandidateList(
5486
+ [
5487
+ ...(_b2 = opt == null ? void 0 : opt.exclude) != null ? _b2 : [],
5488
+ ...source === "all_services" ? [
5489
+ ...getAssignedServiceIds({ props, snapshot }),
5490
+ ...getScope(context)
5491
+ ] : []
5492
+ ],
5493
+ true
5494
+ );
5282
5495
  if (context.scope === "global") {
5283
5496
  return getEligibleFallbacks({
5284
5497
  primary: context.primary,
@@ -5286,9 +5499,10 @@ function createFallbackEditor(options = {}) {
5286
5499
  fallbacks: current,
5287
5500
  settings,
5288
5501
  props,
5289
- exclude: opt == null ? void 0 : opt.exclude,
5502
+ exclude: exclude2,
5290
5503
  unique: opt == null ? void 0 : opt.unique,
5291
- limit: opt == null ? void 0 : opt.limit
5504
+ limit: opt == null ? void 0 : opt.limit,
5505
+ source
5292
5506
  });
5293
5507
  }
5294
5508
  const info = getFallbackRegistrationInfo(props, context.nodeId);
@@ -5296,14 +5510,19 @@ function createFallbackEditor(options = {}) {
5296
5510
  return getEligibleFallbacks({
5297
5511
  primary: info.primary,
5298
5512
  nodeId: context.nodeId,
5299
- tagId: info.tagContexts[0],
5513
+ tagId: resolveNodeTagContext({
5514
+ nodeId: context.nodeId,
5515
+ snapshot,
5516
+ fallbackTagContexts: info.tagContexts
5517
+ }),
5300
5518
  services,
5301
5519
  fallbacks: current,
5302
5520
  settings,
5303
5521
  props,
5304
- exclude: opt == null ? void 0 : opt.exclude,
5522
+ exclude: exclude2,
5305
5523
  unique: opt == null ? void 0 : opt.unique,
5306
- limit: opt == null ? void 0 : opt.limit
5524
+ limit: opt == null ? void 0 : opt.limit,
5525
+ source
5307
5526
  });
5308
5527
  }
5309
5528
  function writeScope(context, nextList) {
@@ -5361,14 +5580,14 @@ function sameFallbacks(a, b) {
5361
5580
  function normalizeCandidateList(input, preserveOrder) {
5362
5581
  const out = [];
5363
5582
  for (const item of input != null ? input : []) {
5364
- if (!isValidServiceIdRef(item)) continue;
5583
+ if (!isValidServiceIdRef2(item)) continue;
5365
5584
  const exists = out.some((x) => String(x) === String(item));
5366
5585
  if (exists) continue;
5367
5586
  out.push(item);
5368
5587
  }
5369
5588
  return preserveOrder ? out : out;
5370
5589
  }
5371
- function isValidServiceIdRef(value) {
5590
+ function isValidServiceIdRef2(value) {
5372
5591
  return typeof value === "number" && Number.isFinite(value) || typeof value === "string" && value.trim().length > 0;
5373
5592
  }
5374
5593
  function clamp(n, min, max) {
@@ -5377,7 +5596,7 @@ function clamp(n, min, max) {
5377
5596
  function getNodeRegistrationInfo(props, nodeId) {
5378
5597
  const tag = props.filters.find((t) => t.id === nodeId);
5379
5598
  if (tag) {
5380
- if (!isValidServiceIdRef(tag.service_id)) {
5599
+ if (!isValidServiceIdRef2(tag.service_id)) {
5381
5600
  return { ok: false, reasons: ["no_primary"] };
5382
5601
  }
5383
5602
  return {
@@ -5390,7 +5609,7 @@ function getNodeRegistrationInfo(props, nodeId) {
5390
5609
  if (!hit) {
5391
5610
  return { ok: false, reasons: ["node_not_found"] };
5392
5611
  }
5393
- if (!isValidServiceIdRef(hit.option.service_id)) {
5612
+ if (!isValidServiceIdRef2(hit.option.service_id)) {
5394
5613
  return { ok: false, reasons: ["no_primary"] };
5395
5614
  }
5396
5615
  return {
@@ -5412,6 +5631,15 @@ function bindIdsToArray2(v) {
5412
5631
  if (Array.isArray(v)) return v.filter(Boolean);
5413
5632
  return v ? [v] : [];
5414
5633
  }
5634
+ function resolveNodeTagContext(params) {
5635
+ var _a, _b, _c;
5636
+ const nodeContexts = (_c = (_b = (_a = params.snapshot) == null ? void 0 : _a.meta) == null ? void 0 : _b.context) == null ? void 0 : _c.nodeContexts;
5637
+ if (nodeContexts && Object.prototype.hasOwnProperty.call(nodeContexts, params.nodeId)) {
5638
+ const tagId = nodeContexts[params.nodeId];
5639
+ return typeof tagId === "string" && tagId.trim().length > 0 ? tagId : void 0;
5640
+ }
5641
+ return params.fallbackTagContexts[0];
5642
+ }
5415
5643
  function mapDiagReason(reason) {
5416
5644
  switch (String(reason)) {
5417
5645
  case "unknown_service":
@@ -12113,10 +12341,10 @@ function FallbackEditorInner({ className }) {
12113
12341
  "div",
12114
12342
  {
12115
12343
  className: [
12116
- "min-h-screen bg-zinc-100 p-4 text-zinc-900 dark:bg-zinc-950 dark:text-zinc-100",
12344
+ "h-full min-h-0 overflow-hidden bg-zinc-100 p-4 text-zinc-900 dark:bg-zinc-950 dark:text-zinc-100",
12117
12345
  className
12118
12346
  ].filter(Boolean).join(" "),
12119
- children: /* @__PURE__ */ jsxs2("div", { className: "mx-auto flex max-w-7xl flex-col gap-4", children: [
12347
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex h-full min-h-0 flex-col gap-4", children: [
12120
12348
  /* @__PURE__ */ jsx6(
12121
12349
  FallbackEditorHeader,
12122
12350
  {
@@ -12128,9 +12356,9 @@ function FallbackEditorInner({ className }) {
12128
12356
  saving: headerSaving
12129
12357
  }
12130
12358
  ),
12131
- /* @__PURE__ */ jsxs2("div", { className: "grid gap-4 xl:grid-cols-[300px_minmax(0,1fr)_360px]", children: [
12359
+ /* @__PURE__ */ jsxs2("div", { className: "grid min-h-0 flex-1 gap-4 overflow-hidden xl:grid-cols-[300px_minmax(0,1fr)_360px]", children: [
12132
12360
  /* @__PURE__ */ jsx6(FallbackServiceSidebar, {}),
12133
- /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-col gap-4", children: [
12361
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-h-0 flex-col gap-4 overflow-y-auto", children: [
12134
12362
  /* @__PURE__ */ jsx6("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-start justify-between gap-4", children: [
12135
12363
  /* @__PURE__ */ jsxs2("div", { children: [
12136
12364
  /* @__PURE__ */ jsx6("h2", { className: "text-lg font-semibold text-zinc-900 dark:text-zinc-100", children: activeServiceId !== void 0 ? `Service #${String(activeServiceId)}` : "No service selected" }),
@@ -12274,7 +12502,7 @@ function FallbackDetailsPanel() {
12274
12502
  () => services.find((s) => String(s.id) === String(activeServiceId)),
12275
12503
  [services, activeServiceId]
12276
12504
  );
12277
- return /* @__PURE__ */ jsxs4("aside", { className: "flex min-h-0 flex-col gap-4", children: [
12505
+ return /* @__PURE__ */ jsxs4("aside", { className: "flex min-h-0 flex-col gap-4 overflow-y-auto", children: [
12278
12506
  /* @__PURE__ */ jsxs4("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: [
12279
12507
  /* @__PURE__ */ jsx8("h3", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Primary service info" }),
12280
12508
  !service ? /* @__PURE__ */ jsx8("p", { className: "mt-3 text-sm text-zinc-500 dark:text-zinc-400", children: "No service selected." }) : /* @__PURE__ */ jsxs4("div", { className: "mt-3 space-y-2 text-sm", children: [
@@ -12436,7 +12664,7 @@ function FallbackSettingsPanel() {
12436
12664
  kind: "lte_primary",
12437
12665
  pct: 5
12438
12666
  };
12439
- return /* @__PURE__ */ jsxs6("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: [
12667
+ return /* @__PURE__ */ jsxs6("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900 overflow-y-auto", children: [
12440
12668
  /* @__PURE__ */ jsxs6("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
12441
12669
  /* @__PURE__ */ jsxs6("div", { children: [
12442
12670
  /* @__PURE__ */ jsx10("h3", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Fallback settings" }),
@@ -12635,7 +12863,7 @@ function FallbackServiceSidebar() {
12635
12863
  }
12636
12864
  );
12637
12865
  }, [query, services]);
12638
- return /* @__PURE__ */ jsxs7("aside", { className: "flex min-h-0 flex-col rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: [
12866
+ return /* @__PURE__ */ jsxs7("aside", { className: "flex h-full min-h-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: [
12639
12867
  /* @__PURE__ */ jsxs7("div", { className: "border-b border-zinc-200 p-4 dark:border-zinc-800", children: [
12640
12868
  /* @__PURE__ */ jsx11("h2", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Primary services" }),
12641
12869
  /* @__PURE__ */ jsx11("p", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: "Services currently active in the builder/runtime context." })
@@ -12650,7 +12878,7 @@ function FallbackServiceSidebar() {
12650
12878
  className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
12651
12879
  }
12652
12880
  ),
12653
- /* @__PURE__ */ jsx11("div", { className: "mt-3 flex-1 space-y-2 overflow-auto", children: filtered.map((service) => {
12881
+ /* @__PURE__ */ jsx11("div", { className: "mt-3 flex-1 space-y-2 overflow-y-auto", children: filtered.map((service) => {
12654
12882
  var _a, _b;
12655
12883
  const active = String(service.id) === String(activeServiceId);
12656
12884
  const count = get(service.id).length;
@@ -12691,11 +12919,172 @@ function FallbackServiceSidebar() {
12691
12919
  }
12692
12920
 
12693
12921
  // src/react/fallback-editor/FallbackRegistrationsPanel.tsx
12694
- import React13 from "react";
12922
+ import React11 from "react";
12923
+ import { Fragment, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
12924
+ function FallbackRegistrationsPanel() {
12925
+ const { activeServiceId, remove: remove2, clear, check } = useFallbackEditor();
12926
+ const registrations = useActiveFallbackRegistrations();
12927
+ const eligibleServices = useEligibleServiceList();
12928
+ const [candidatePickerOpen, setCandidatePickerOpen] = React11.useState(false);
12929
+ const [candidateContext, setCandidateContext] = React11.useState(null);
12930
+ const [candidatePrimaryId, setCandidatePrimaryId] = React11.useState(void 0);
12931
+ const [registrationDialogOpen, setRegistrationDialogOpen] = React11.useState(false);
12932
+ const makeContext = React11.useCallback(
12933
+ (registration) => {
12934
+ if (registration.scope === "global") {
12935
+ return {
12936
+ scope: "global",
12937
+ primary: registration.primary
12938
+ };
12939
+ }
12940
+ return {
12941
+ scope: "node",
12942
+ nodeId: registration.scopeId
12943
+ };
12944
+ },
12945
+ []
12946
+ );
12947
+ const openCandidatePicker = React11.useCallback(
12948
+ (context, primaryId) => {
12949
+ setCandidateContext(context);
12950
+ setCandidatePrimaryId(primaryId);
12951
+ setCandidatePickerOpen(true);
12952
+ },
12953
+ []
12954
+ );
12955
+ if (activeServiceId === void 0 || activeServiceId === null) {
12956
+ return /* @__PURE__ */ jsx12("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900 ", children: /* @__PURE__ */ jsx12("div", { className: "rounded-2xl border border-dashed border-zinc-300 p-6 text-sm text-zinc-500 dark:border-zinc-700 dark:text-zinc-400", children: "Select a primary service to start editing." }) });
12957
+ }
12958
+ return /* @__PURE__ */ jsxs8(Fragment, { children: [
12959
+ /* @__PURE__ */ jsxs8("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900 overflow-y-auto", children: [
12960
+ /* @__PURE__ */ jsxs8("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
12961
+ /* @__PURE__ */ jsxs8("div", { children: [
12962
+ /* @__PURE__ */ jsx12("h3", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Registered fallbacks" }),
12963
+ /* @__PURE__ */ jsx12("p", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: "Use eligible services as fallback candidates for the selected primary." })
12964
+ ] }),
12965
+ /* @__PURE__ */ jsx12(
12966
+ "button",
12967
+ {
12968
+ type: "button",
12969
+ onClick: () => setRegistrationDialogOpen(true),
12970
+ className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm font-medium text-zinc-700 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200 dark:hover:bg-zinc-800",
12971
+ children: "Add registration"
12972
+ }
12973
+ )
12974
+ ] }),
12975
+ /* @__PURE__ */ jsx12("div", { className: "space-y-4", children: registrations.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "rounded-2xl border border-dashed border-zinc-300 p-6 text-sm text-zinc-500 dark:border-zinc-700 dark:text-zinc-400", children: "No registrations yet for this primary service." }) : registrations.map((reg, index) => {
12976
+ var _a;
12977
+ const context = makeContext(reg);
12978
+ const candidates = reg.services;
12979
+ return /* @__PURE__ */ jsxs8(
12980
+ "div",
12981
+ {
12982
+ className: "rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950",
12983
+ children: [
12984
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
12985
+ /* @__PURE__ */ jsxs8("div", { children: [
12986
+ /* @__PURE__ */ jsx12("div", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: reg.scope === "global" ? "Global registration" : `Node \xB7 ${reg.scopeId}` }),
12987
+ /* @__PURE__ */ jsxs8("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [
12988
+ "Primary #",
12989
+ String(reg.primary)
12990
+ ] })
12991
+ ] }),
12992
+ /* @__PURE__ */ jsxs8("span", { className: "rounded-full border border-zinc-200 bg-white px-2 py-1 text-[11px] text-zinc-600 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300", children: [
12993
+ reg.scope,
12994
+ reg.scopeId ? ` \xB7 ${reg.scopeId}` : ""
12995
+ ] })
12996
+ ] }),
12997
+ /* @__PURE__ */ jsx12("div", { className: "mt-4 flex flex-wrap gap-2", children: candidates.length === 0 ? /* @__PURE__ */ jsx12("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No fallback services yet." }) : candidates.map((candidate) => {
12998
+ var _a2;
12999
+ const preview = check(context, [
13000
+ candidate
13001
+ ]);
13002
+ const rejected = preview.rejected[0];
13003
+ const tone = rejected ? "border-red-200 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300" : "border-zinc-200 bg-white text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200";
13004
+ const service = eligibleServices.find(
13005
+ (s) => String(s.id) === String(candidate)
13006
+ );
13007
+ return /* @__PURE__ */ jsxs8(
13008
+ "div",
13009
+ {
13010
+ className: `inline-flex items-center gap-2 rounded-xl border px-3 py-2 text-xs ${tone}`,
13011
+ children: [
13012
+ /* @__PURE__ */ jsx12("span", { children: service ? `#${String(service.id)} \xB7 ${(_a2 = service.name) != null ? _a2 : "Unnamed"}` : `#${String(candidate)}` }),
13013
+ rejected ? /* @__PURE__ */ jsx12("span", { className: "rounded-full border border-current/20 px-2 py-0.5 text-[10px]", children: rejected.reasons.join(
13014
+ ", "
13015
+ ) }) : /* @__PURE__ */ jsx12("span", { className: "rounded-full border border-current/20 px-2 py-0.5 text-[10px]", children: "valid" }),
13016
+ /* @__PURE__ */ jsx12(
13017
+ "button",
13018
+ {
13019
+ type: "button",
13020
+ onClick: () => remove2(
13021
+ context,
13022
+ candidate
13023
+ ),
13024
+ className: "text-current/70 hover:text-current",
13025
+ children: "\xD7"
13026
+ }
13027
+ )
13028
+ ]
13029
+ },
13030
+ String(candidate)
13031
+ );
13032
+ }) }),
13033
+ /* @__PURE__ */ jsxs8("div", { className: "mt-4 flex gap-2", children: [
13034
+ /* @__PURE__ */ jsx12(
13035
+ "button",
13036
+ {
13037
+ type: "button",
13038
+ onClick: () => openCandidatePicker(
13039
+ context,
13040
+ reg.primary
13041
+ ),
13042
+ className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm font-medium text-zinc-700 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200 dark:hover:bg-zinc-800",
13043
+ children: "Add fallback"
13044
+ }
13045
+ ),
13046
+ /* @__PURE__ */ jsx12(
13047
+ "button",
13048
+ {
13049
+ type: "button",
13050
+ onClick: () => clear(context),
13051
+ className: "rounded-xl border border-red-300 bg-white px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50 dark:border-red-900/50 dark:bg-zinc-900 dark:text-red-300 dark:hover:bg-red-950/20",
13052
+ children: "Clear"
13053
+ }
13054
+ )
13055
+ ] })
13056
+ ]
13057
+ },
13058
+ `${reg.scope}:${String((_a = reg.scopeId) != null ? _a : "global")}:${index}`
13059
+ );
13060
+ }) })
13061
+ ] }),
13062
+ /* @__PURE__ */ jsx12(
13063
+ FallbackAddRegistrationDialog,
13064
+ {
13065
+ open: registrationDialogOpen,
13066
+ onClose: () => setRegistrationDialogOpen(false),
13067
+ onSelect: (context, primaryId) => {
13068
+ setRegistrationDialogOpen(false);
13069
+ openCandidatePicker(context, primaryId);
13070
+ }
13071
+ }
13072
+ ),
13073
+ /* @__PURE__ */ jsx12(
13074
+ FallbackAddCandidatesDialog,
13075
+ {
13076
+ open: candidatePickerOpen,
13077
+ onClose: () => setCandidatePickerOpen(false),
13078
+ context: candidateContext,
13079
+ primaryId: candidatePrimaryId
13080
+ }
13081
+ )
13082
+ ] });
13083
+ }
12695
13084
 
12696
13085
  // src/react/fallback-editor/FallbackAddCandidatesDialog.tsx
12697
- import React11 from "react";
12698
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
13086
+ import React12 from "react";
13087
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
12699
13088
  function FallbackAddCandidatesDialog({
12700
13089
  open,
12701
13090
  onClose,
@@ -12704,23 +13093,23 @@ function FallbackAddCandidatesDialog({
12704
13093
  }) {
12705
13094
  const { eligible, addMany } = useFallbackEditor();
12706
13095
  const eligibleServices = useEligibleServiceList();
12707
- const [query, setQuery] = React11.useState("");
12708
- const [filterEligibleOnly, setFilterEligibleOnly] = React11.useState(true);
12709
- const [selected, setSelected] = React11.useState(/* @__PURE__ */ new Set());
12710
- const [submitting, setSubmitting] = React11.useState(false);
12711
- React11.useEffect(() => {
13096
+ const [query, setQuery] = React12.useState("");
13097
+ const [filterEligibleOnly, setFilterEligibleOnly] = React12.useState(true);
13098
+ const [selected, setSelected] = React12.useState(/* @__PURE__ */ new Set());
13099
+ const [submitting, setSubmitting] = React12.useState(false);
13100
+ React12.useEffect(() => {
12712
13101
  if (!open) {
12713
13102
  setQuery("");
12714
13103
  setFilterEligibleOnly(true);
12715
13104
  setSelected(/* @__PURE__ */ new Set());
12716
13105
  }
12717
13106
  }, [open]);
12718
- const allowedIds = React11.useMemo(() => {
13107
+ const allowedIds = React12.useMemo(() => {
12719
13108
  if (!context) return null;
12720
13109
  if (!filterEligibleOnly) return null;
12721
13110
  return new Set(eligible(context).map((id) => String(id)));
12722
13111
  }, [context, filterEligibleOnly, eligible]);
12723
- const items = React11.useMemo(() => {
13112
+ const items = React12.useMemo(() => {
12724
13113
  const q = query.trim().toLowerCase();
12725
13114
  return eligibleServices.filter((service) => {
12726
13115
  var _a, _b;
@@ -12755,13 +13144,13 @@ function FallbackAddCandidatesDialog({
12755
13144
  }
12756
13145
  }
12757
13146
  if (!open || !context) return null;
12758
- return /* @__PURE__ */ jsx12("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxs8("div", { className: "flex max-h-[85vh] w-full max-w-3xl flex-col rounded-2xl border border-zinc-200 bg-white shadow-2xl dark:border-zinc-800 dark:bg-zinc-900", children: [
12759
- /* @__PURE__ */ jsxs8("div", { className: "border-b border-zinc-200 p-4 dark:border-zinc-800", children: [
12760
- /* @__PURE__ */ jsx12("h3", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Add fallback services" }),
12761
- /* @__PURE__ */ jsx12("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-400", children: "Search and select one or more eligible fallback candidates." })
13147
+ return /* @__PURE__ */ jsx13("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxs9("div", { className: "flex max-h-[85vh] w-full max-w-3xl flex-col rounded-2xl border border-zinc-200 bg-white shadow-2xl dark:border-zinc-800 dark:bg-zinc-900", children: [
13148
+ /* @__PURE__ */ jsxs9("div", { className: "border-b border-zinc-200 p-4 dark:border-zinc-800", children: [
13149
+ /* @__PURE__ */ jsx13("h3", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Add fallback services" }),
13150
+ /* @__PURE__ */ jsx13("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-400", children: "Search and select one or more eligible fallback candidates." })
12762
13151
  ] }),
12763
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-3 p-4", children: [
12764
- /* @__PURE__ */ jsx12(
13152
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 p-4", children: [
13153
+ /* @__PURE__ */ jsx13(
12765
13154
  "input",
12766
13155
  {
12767
13156
  value: query,
@@ -12770,8 +13159,8 @@ function FallbackAddCandidatesDialog({
12770
13159
  className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
12771
13160
  }
12772
13161
  ),
12773
- /* @__PURE__ */ jsxs8("label", { className: "inline-flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [
12774
- /* @__PURE__ */ jsx12(
13162
+ /* @__PURE__ */ jsxs9("label", { className: "inline-flex items-center gap-2 text-sm text-zinc-700 dark:text-zinc-300", children: [
13163
+ /* @__PURE__ */ jsx13(
12775
13164
  "input",
12776
13165
  {
12777
13166
  type: "checkbox",
@@ -12782,7 +13171,7 @@ function FallbackAddCandidatesDialog({
12782
13171
  ),
12783
13172
  "Filter eligible only"
12784
13173
  ] }),
12785
- /* @__PURE__ */ jsx12(
13174
+ /* @__PURE__ */ jsx13(
12786
13175
  VirtualServiceList,
12787
13176
  {
12788
13177
  items,
@@ -12792,13 +13181,13 @@ function FallbackAddCandidatesDialog({
12792
13181
  }
12793
13182
  )
12794
13183
  ] }),
12795
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between border-t border-zinc-200 p-4 dark:border-zinc-800", children: [
12796
- /* @__PURE__ */ jsxs8("div", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: [
13184
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between border-t border-zinc-200 p-4 dark:border-zinc-800", children: [
13185
+ /* @__PURE__ */ jsxs9("div", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: [
12797
13186
  selected.size,
12798
13187
  " selected"
12799
13188
  ] }),
12800
- /* @__PURE__ */ jsxs8("div", { className: "flex gap-2", children: [
12801
- /* @__PURE__ */ jsx12(
13189
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
13190
+ /* @__PURE__ */ jsx13(
12802
13191
  "button",
12803
13192
  {
12804
13193
  type: "button",
@@ -12807,7 +13196,7 @@ function FallbackAddCandidatesDialog({
12807
13196
  children: "Cancel"
12808
13197
  }
12809
13198
  ),
12810
- /* @__PURE__ */ jsx12(
13199
+ /* @__PURE__ */ jsx13(
12811
13200
  "button",
12812
13201
  {
12813
13202
  type: "button",
@@ -12823,8 +13212,8 @@ function FallbackAddCandidatesDialog({
12823
13212
  }
12824
13213
 
12825
13214
  // src/react/fallback-editor/FallbackAddRegistrationDialog.tsx
12826
- import React12 from "react";
12827
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
13215
+ import React13 from "react";
13216
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
12828
13217
  function FallbackAddRegistrationDialog({
12829
13218
  open,
12830
13219
  onClose,
@@ -12832,23 +13221,23 @@ function FallbackAddRegistrationDialog({
12832
13221
  }) {
12833
13222
  const { activeServiceId, serviceProps, snapshot } = useFallbackEditor();
12834
13223
  const registrations = useActiveFallbackRegistrations();
12835
- const [scope, setScope] = React12.useState("global");
12836
- const [nodeId, setNodeId] = React12.useState("");
12837
- const mode = React12.useMemo(() => {
13224
+ const [scope, setScope] = React13.useState("global");
13225
+ const [nodeId, setNodeId] = React13.useState("");
13226
+ const mode = React13.useMemo(() => {
12838
13227
  if (snapshot) return "snapshot";
12839
13228
  if (serviceProps) return "props";
12840
13229
  return "none";
12841
13230
  }, [snapshot, serviceProps]);
12842
- React12.useEffect(() => {
13231
+ React13.useEffect(() => {
12843
13232
  if (open) {
12844
13233
  setScope("global");
12845
13234
  setNodeId("");
12846
13235
  }
12847
13236
  }, [open]);
12848
- const hasGlobal = React12.useMemo(() => {
13237
+ const hasGlobal = React13.useMemo(() => {
12849
13238
  return registrations.some((r) => r.scope === "global");
12850
13239
  }, [registrations]);
12851
- const nodeTargets = React12.useMemo(() => {
13240
+ const nodeTargets = React13.useMemo(() => {
12852
13241
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12853
13242
  if (activeServiceId === void 0 || activeServiceId === null) {
12854
13243
  return [];
@@ -12938,12 +13327,12 @@ function FallbackAddRegistrationDialog({
12938
13327
  }
12939
13328
  return [];
12940
13329
  }, [mode, snapshot, serviceProps, activeServiceId]);
12941
- React12.useEffect(() => {
13330
+ React13.useEffect(() => {
12942
13331
  if (hasGlobal && scope === "global") {
12943
13332
  setScope("node");
12944
13333
  }
12945
13334
  }, [hasGlobal, scope]);
12946
- React12.useEffect(() => {
13335
+ React13.useEffect(() => {
12947
13336
  if (scope === "node" && nodeId) {
12948
13337
  const exists = nodeTargets.some((node) => node.id === nodeId);
12949
13338
  if (!exists) setNodeId("");
@@ -12974,17 +13363,17 @@ function FallbackAddRegistrationDialog({
12974
13363
  }
12975
13364
  if (!open) return null;
12976
13365
  const nodeScopeDisabled = nodeTargets.length === 0;
12977
- return /* @__PURE__ */ jsx13("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxs9("div", { className: "w-full max-w-lg rounded-2xl border border-zinc-200 bg-white shadow-2xl dark:border-zinc-800 dark:bg-zinc-900", children: [
12978
- /* @__PURE__ */ jsxs9("div", { className: "border-b border-zinc-200 p-4 dark:border-zinc-800", children: [
12979
- /* @__PURE__ */ jsx13("h3", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Add registration" }),
12980
- /* @__PURE__ */ jsx13("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-400", children: "Choose the registration scope before selecting fallback candidates." })
13366
+ return /* @__PURE__ */ jsx14("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxs10("div", { className: "w-full max-w-lg rounded-2xl border border-zinc-200 bg-white shadow-2xl dark:border-zinc-800 dark:bg-zinc-900", children: [
13367
+ /* @__PURE__ */ jsxs10("div", { className: "border-b border-zinc-200 p-4 dark:border-zinc-800", children: [
13368
+ /* @__PURE__ */ jsx14("h3", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Add registration" }),
13369
+ /* @__PURE__ */ jsx14("p", { className: "mt-1 text-sm text-zinc-500 dark:text-zinc-400", children: "Choose the registration scope before selecting fallback candidates." })
12981
13370
  ] }),
12982
- /* @__PURE__ */ jsxs9("div", { className: "space-y-4 p-4", children: [
12983
- /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
12984
- /* @__PURE__ */ jsx13("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Scope" }),
12985
- /* @__PURE__ */ jsxs9("div", { className: "grid gap-2", children: [
12986
- !hasGlobal && /* @__PURE__ */ jsxs9("label", { className: "flex cursor-pointer items-start gap-3 rounded-xl border border-zinc-200 p-3 dark:border-zinc-800", children: [
12987
- /* @__PURE__ */ jsx13(
13371
+ /* @__PURE__ */ jsxs10("div", { className: "space-y-4 p-4", children: [
13372
+ /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
13373
+ /* @__PURE__ */ jsx14("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Scope" }),
13374
+ /* @__PURE__ */ jsxs10("div", { className: "grid gap-2", children: [
13375
+ !hasGlobal && /* @__PURE__ */ jsxs10("label", { className: "flex cursor-pointer items-start gap-3 rounded-xl border border-zinc-200 p-3 dark:border-zinc-800", children: [
13376
+ /* @__PURE__ */ jsx14(
12988
13377
  "input",
12989
13378
  {
12990
13379
  type: "radio",
@@ -12994,12 +13383,12 @@ function FallbackAddRegistrationDialog({
12994
13383
  className: "mt-1 h-4 w-4"
12995
13384
  }
12996
13385
  ),
12997
- /* @__PURE__ */ jsxs9("div", { children: [
12998
- /* @__PURE__ */ jsx13("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Global" }),
12999
- /* @__PURE__ */ jsx13("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: "Use one global registration for this primary service." })
13386
+ /* @__PURE__ */ jsxs10("div", { children: [
13387
+ /* @__PURE__ */ jsx14("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Global" }),
13388
+ /* @__PURE__ */ jsx14("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: "Use one global registration for this primary service." })
13000
13389
  ] })
13001
13390
  ] }),
13002
- /* @__PURE__ */ jsxs9(
13391
+ /* @__PURE__ */ jsxs10(
13003
13392
  "label",
13004
13393
  {
13005
13394
  className: [
@@ -13007,7 +13396,7 @@ function FallbackAddRegistrationDialog({
13007
13396
  nodeScopeDisabled ? "cursor-not-allowed border-zinc-200 opacity-60 dark:border-zinc-800" : "cursor-pointer border-zinc-200 dark:border-zinc-800"
13008
13397
  ].join(" "),
13009
13398
  children: [
13010
- /* @__PURE__ */ jsx13(
13399
+ /* @__PURE__ */ jsx14(
13011
13400
  "input",
13012
13401
  {
13013
13402
  type: "radio",
@@ -13018,26 +13407,26 @@ function FallbackAddRegistrationDialog({
13018
13407
  className: "mt-1 h-4 w-4"
13019
13408
  }
13020
13409
  ),
13021
- /* @__PURE__ */ jsxs9("div", { children: [
13022
- /* @__PURE__ */ jsx13("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Node" }),
13023
- /* @__PURE__ */ jsx13("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: mode === "snapshot" ? "Pick a node currently active in the order snapshot for this primary service." : mode === "props" ? "Pick a tag, field, or option from ServiceProps that maps to this primary service." : "Node-scoped registration is unavailable without OrderSnapshot or ServiceProps." })
13410
+ /* @__PURE__ */ jsxs10("div", { children: [
13411
+ /* @__PURE__ */ jsx14("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Node" }),
13412
+ /* @__PURE__ */ jsx14("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: mode === "snapshot" ? "Pick a node currently active in the order snapshot for this primary service." : mode === "props" ? "Pick a tag, field, or option from ServiceProps that maps to this primary service." : "Node-scoped registration is unavailable without OrderSnapshot or ServiceProps." })
13024
13413
  ] })
13025
13414
  ]
13026
13415
  }
13027
13416
  )
13028
13417
  ] })
13029
13418
  ] }),
13030
- scope === "node" && /* @__PURE__ */ jsxs9("div", { className: "space-y-2", children: [
13031
- /* @__PURE__ */ jsx13("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Node id" }),
13032
- /* @__PURE__ */ jsxs9(
13419
+ scope === "node" && /* @__PURE__ */ jsxs10("div", { className: "space-y-2", children: [
13420
+ /* @__PURE__ */ jsx14("div", { className: "text-sm font-medium text-zinc-900 dark:text-zinc-100", children: "Node id" }),
13421
+ /* @__PURE__ */ jsxs10(
13033
13422
  "select",
13034
13423
  {
13035
13424
  value: nodeId,
13036
13425
  onChange: (e) => setNodeId(e.target.value),
13037
13426
  className: "w-full rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm outline-none focus:border-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100",
13038
13427
  children: [
13039
- /* @__PURE__ */ jsx13("option", { value: "", children: "Select node\u2026" }),
13040
- nodeTargets.map((node) => /* @__PURE__ */ jsxs9("option", { value: node.id, children: [
13428
+ /* @__PURE__ */ jsx14("option", { value: "", children: "Select node\u2026" }),
13429
+ nodeTargets.map((node) => /* @__PURE__ */ jsxs10("option", { value: node.id, children: [
13041
13430
  "[",
13042
13431
  node.kind,
13043
13432
  "] ",
@@ -13048,11 +13437,11 @@ function FallbackAddRegistrationDialog({
13048
13437
  ]
13049
13438
  }
13050
13439
  ),
13051
- nodeScopeDisabled ? /* @__PURE__ */ jsx13("div", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: mode === "snapshot" ? "No active snapshot nodes were found for this primary service." : mode === "props" ? "No ServiceProps nodes were found for this primary service." : "Node-scoped registration requires either OrderSnapshot or ServiceProps." }) : null
13440
+ nodeScopeDisabled ? /* @__PURE__ */ jsx14("div", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: mode === "snapshot" ? "No active snapshot nodes were found for this primary service." : mode === "props" ? "No ServiceProps nodes were found for this primary service." : "Node-scoped registration requires either OrderSnapshot or ServiceProps." }) : null
13052
13441
  ] })
13053
13442
  ] }),
13054
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-end gap-2 border-t border-zinc-200 p-4 dark:border-zinc-800", children: [
13055
- /* @__PURE__ */ jsx13(
13443
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-end gap-2 border-t border-zinc-200 p-4 dark:border-zinc-800", children: [
13444
+ /* @__PURE__ */ jsx14(
13056
13445
  "button",
13057
13446
  {
13058
13447
  type: "button",
@@ -13061,7 +13450,7 @@ function FallbackAddRegistrationDialog({
13061
13450
  children: "Cancel"
13062
13451
  }
13063
13452
  ),
13064
- /* @__PURE__ */ jsx13(
13453
+ /* @__PURE__ */ jsx14(
13065
13454
  "button",
13066
13455
  {
13067
13456
  type: "button",
@@ -13104,169 +13493,6 @@ function resolveNodeMeta(props, nodeId) {
13104
13493
  }
13105
13494
  return { kind: "node", label: nodeId };
13106
13495
  }
13107
-
13108
- // src/react/fallback-editor/FallbackRegistrationsPanel.tsx
13109
- import { Fragment, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
13110
- function FallbackRegistrationsPanel() {
13111
- const { activeServiceId, remove: remove2, clear, check } = useFallbackEditor();
13112
- const registrations = useActiveFallbackRegistrations();
13113
- const eligibleServices = useEligibleServiceList();
13114
- const [candidatePickerOpen, setCandidatePickerOpen] = React13.useState(false);
13115
- const [candidateContext, setCandidateContext] = React13.useState(null);
13116
- const [candidatePrimaryId, setCandidatePrimaryId] = React13.useState(void 0);
13117
- const [registrationDialogOpen, setRegistrationDialogOpen] = React13.useState(false);
13118
- const makeContext = React13.useCallback(
13119
- (registration) => {
13120
- if (registration.scope === "global") {
13121
- return {
13122
- scope: "global",
13123
- primary: registration.primary
13124
- };
13125
- }
13126
- return {
13127
- scope: "node",
13128
- nodeId: registration.scopeId
13129
- };
13130
- },
13131
- []
13132
- );
13133
- const openCandidatePicker = React13.useCallback(
13134
- (context, primaryId) => {
13135
- setCandidateContext(context);
13136
- setCandidatePrimaryId(primaryId);
13137
- setCandidatePickerOpen(true);
13138
- },
13139
- []
13140
- );
13141
- if (activeServiceId === void 0 || activeServiceId === null) {
13142
- return /* @__PURE__ */ jsx14("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: /* @__PURE__ */ jsx14("div", { className: "rounded-2xl border border-dashed border-zinc-300 p-6 text-sm text-zinc-500 dark:border-zinc-700 dark:text-zinc-400", children: "Select a primary service to start editing." }) });
13143
- }
13144
- return /* @__PURE__ */ jsxs10(Fragment, { children: [
13145
- /* @__PURE__ */ jsxs10("section", { className: "rounded-2xl border border-zinc-200 bg-white p-4 shadow-sm dark:border-zinc-800 dark:bg-zinc-900", children: [
13146
- /* @__PURE__ */ jsxs10("div", { className: "mb-4 flex items-start justify-between gap-3", children: [
13147
- /* @__PURE__ */ jsxs10("div", { children: [
13148
- /* @__PURE__ */ jsx14("h3", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: "Registered fallbacks" }),
13149
- /* @__PURE__ */ jsx14("p", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: "Use eligible services as fallback candidates for the selected primary." })
13150
- ] }),
13151
- /* @__PURE__ */ jsx14(
13152
- "button",
13153
- {
13154
- type: "button",
13155
- onClick: () => setRegistrationDialogOpen(true),
13156
- className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm font-medium text-zinc-700 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-200 dark:hover:bg-zinc-800",
13157
- children: "Add registration"
13158
- }
13159
- )
13160
- ] }),
13161
- /* @__PURE__ */ jsx14("div", { className: "space-y-4", children: registrations.length === 0 ? /* @__PURE__ */ jsx14("div", { className: "rounded-2xl border border-dashed border-zinc-300 p-6 text-sm text-zinc-500 dark:border-zinc-700 dark:text-zinc-400", children: "No registrations yet for this primary service." }) : registrations.map((reg, index) => {
13162
- var _a;
13163
- const context = makeContext(reg);
13164
- const candidates = reg.services;
13165
- return /* @__PURE__ */ jsxs10(
13166
- "div",
13167
- {
13168
- className: "rounded-2xl border border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-950",
13169
- children: [
13170
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
13171
- /* @__PURE__ */ jsxs10("div", { children: [
13172
- /* @__PURE__ */ jsx14("div", { className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100", children: reg.scope === "global" ? "Global registration" : `Node \xB7 ${reg.scopeId}` }),
13173
- /* @__PURE__ */ jsxs10("div", { className: "mt-1 text-xs text-zinc-500 dark:text-zinc-400", children: [
13174
- "Primary #",
13175
- String(reg.primary)
13176
- ] })
13177
- ] }),
13178
- /* @__PURE__ */ jsxs10("span", { className: "rounded-full border border-zinc-200 bg-white px-2 py-1 text-[11px] text-zinc-600 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300", children: [
13179
- reg.scope,
13180
- reg.scopeId ? ` \xB7 ${reg.scopeId}` : ""
13181
- ] })
13182
- ] }),
13183
- /* @__PURE__ */ jsx14("div", { className: "mt-4 flex flex-wrap gap-2", children: candidates.length === 0 ? /* @__PURE__ */ jsx14("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "No fallback services yet." }) : candidates.map((candidate) => {
13184
- var _a2;
13185
- const preview = check(context, [
13186
- candidate
13187
- ]);
13188
- const rejected = preview.rejected[0];
13189
- const tone = rejected ? "border-red-200 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/30 dark:text-red-300" : "border-zinc-200 bg-white text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200";
13190
- const service = eligibleServices.find(
13191
- (s) => String(s.id) === String(candidate)
13192
- );
13193
- return /* @__PURE__ */ jsxs10(
13194
- "div",
13195
- {
13196
- className: `inline-flex items-center gap-2 rounded-xl border px-3 py-2 text-xs ${tone}`,
13197
- children: [
13198
- /* @__PURE__ */ jsx14("span", { children: service ? `#${String(service.id)} \xB7 ${(_a2 = service.name) != null ? _a2 : "Unnamed"}` : `#${String(candidate)}` }),
13199
- rejected ? /* @__PURE__ */ jsx14("span", { className: "rounded-full border border-current/20 px-2 py-0.5 text-[10px]", children: rejected.reasons.join(
13200
- ", "
13201
- ) }) : /* @__PURE__ */ jsx14("span", { className: "rounded-full border border-current/20 px-2 py-0.5 text-[10px]", children: "valid" }),
13202
- /* @__PURE__ */ jsx14(
13203
- "button",
13204
- {
13205
- type: "button",
13206
- onClick: () => remove2(
13207
- context,
13208
- candidate
13209
- ),
13210
- className: "text-current/70 hover:text-current",
13211
- children: "\xD7"
13212
- }
13213
- )
13214
- ]
13215
- },
13216
- String(candidate)
13217
- );
13218
- }) }),
13219
- /* @__PURE__ */ jsxs10("div", { className: "mt-4 flex gap-2", children: [
13220
- /* @__PURE__ */ jsx14(
13221
- "button",
13222
- {
13223
- type: "button",
13224
- onClick: () => openCandidatePicker(
13225
- context,
13226
- reg.primary
13227
- ),
13228
- className: "rounded-xl border border-zinc-300 bg-white px-3 py-2 text-sm font-medium text-zinc-700 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200 dark:hover:bg-zinc-800",
13229
- children: "Add fallback"
13230
- }
13231
- ),
13232
- /* @__PURE__ */ jsx14(
13233
- "button",
13234
- {
13235
- type: "button",
13236
- onClick: () => clear(context),
13237
- className: "rounded-xl border border-red-300 bg-white px-3 py-2 text-sm font-medium text-red-600 hover:bg-red-50 dark:border-red-900/50 dark:bg-zinc-900 dark:text-red-300 dark:hover:bg-red-950/20",
13238
- children: "Clear"
13239
- }
13240
- )
13241
- ] })
13242
- ]
13243
- },
13244
- `${reg.scope}:${String((_a = reg.scopeId) != null ? _a : "global")}:${index}`
13245
- );
13246
- }) })
13247
- ] }),
13248
- /* @__PURE__ */ jsx14(
13249
- FallbackAddRegistrationDialog,
13250
- {
13251
- open: registrationDialogOpen,
13252
- onClose: () => setRegistrationDialogOpen(false),
13253
- onSelect: (context, primaryId) => {
13254
- setRegistrationDialogOpen(false);
13255
- openCandidatePicker(context, primaryId);
13256
- }
13257
- }
13258
- ),
13259
- /* @__PURE__ */ jsx14(
13260
- FallbackAddCandidatesDialog,
13261
- {
13262
- open: candidatePickerOpen,
13263
- onClose: () => setCandidatePickerOpen(false),
13264
- context: candidateContext,
13265
- primaryId: candidatePrimaryId
13266
- }
13267
- )
13268
- ] });
13269
- }
13270
13496
  export {
13271
13497
  CanvasAPI,
13272
13498
  EventBus,