@timeax/digital-service-engine 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +1097 -193
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +191 -51
- package/dist/core/index.d.ts +191 -51
- package/dist/core/index.js +1095 -193
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +8563 -2703
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +424 -141
- package/dist/react/index.d.ts +424 -141
- package/dist/react/index.js +8557 -2706
- package/dist/react/index.js.map +1 -1
- package/dist/schema/index.d.cts +123 -54
- package/dist/schema/index.d.ts +123 -54
- package/dist/workspace/index.cjs +541 -255
- package/dist/workspace/index.cjs.map +1 -1
- package/dist/workspace/index.d.cts +79 -51
- package/dist/workspace/index.d.ts +79 -51
- package/dist/workspace/index.js +541 -255
- package/dist/workspace/index.js.map +1 -1
- package/package.json +106 -97
- package/schema/editor-snapshot.schema.json +121 -209
- package/schema/service-props.schema.json +121 -209
package/dist/core/index.js
CHANGED
|
@@ -280,11 +280,6 @@ function hasAnyServiceOption(f) {
|
|
|
280
280
|
var _a;
|
|
281
281
|
return ((_a = f.options) != null ? _a : []).some((o) => isFiniteNumber(o.service_id));
|
|
282
282
|
}
|
|
283
|
-
function isBoundTo(f, tagId) {
|
|
284
|
-
const b = f.bind_id;
|
|
285
|
-
if (!b) return false;
|
|
286
|
-
return Array.isArray(b) ? b.includes(tagId) : b === tagId;
|
|
287
|
-
}
|
|
288
283
|
function getByPath(obj, path) {
|
|
289
284
|
if (!path) return void 0;
|
|
290
285
|
const parts = path.split(".");
|
|
@@ -370,33 +365,214 @@ function withAffected(details, ids) {
|
|
|
370
365
|
return { ...details != null ? details : {}, affectedIds: ids };
|
|
371
366
|
}
|
|
372
367
|
|
|
368
|
+
// src/core/node-map.ts
|
|
369
|
+
function buildNodeMap(props) {
|
|
370
|
+
var _a, _b, _c;
|
|
371
|
+
const map = /* @__PURE__ */ new Map();
|
|
372
|
+
for (const t of (_a = props.filters) != null ? _a : []) {
|
|
373
|
+
if (!map.has(t.id)) map.set(t.id, { kind: "tag", id: t.id, node: t });
|
|
374
|
+
}
|
|
375
|
+
for (const f of (_b = props.fields) != null ? _b : []) {
|
|
376
|
+
if (!map.has(f.id)) map.set(f.id, { kind: "field", id: f.id, node: f });
|
|
377
|
+
for (const o of (_c = f.options) != null ? _c : []) {
|
|
378
|
+
if (!map.has(o.id))
|
|
379
|
+
map.set(o.id, {
|
|
380
|
+
kind: "option",
|
|
381
|
+
id: o.id,
|
|
382
|
+
node: o,
|
|
383
|
+
fieldId: f.id
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return map;
|
|
388
|
+
}
|
|
389
|
+
function resolveTrigger(trigger, nodeMap) {
|
|
390
|
+
const idx = trigger.indexOf("::");
|
|
391
|
+
if (idx !== -1) {
|
|
392
|
+
const fieldId = trigger.slice(0, idx);
|
|
393
|
+
const optionId = trigger.slice(idx + 2);
|
|
394
|
+
return { kind: "composite", triggerKey: trigger, fieldId, optionId };
|
|
395
|
+
}
|
|
396
|
+
const direct = nodeMap.get(trigger);
|
|
397
|
+
if (!direct) return void 0;
|
|
398
|
+
if (direct.kind === "option") {
|
|
399
|
+
return {
|
|
400
|
+
kind: "option",
|
|
401
|
+
triggerKey: trigger,
|
|
402
|
+
id: direct.id,
|
|
403
|
+
fieldId: direct.fieldId
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
return { kind: direct.kind, triggerKey: trigger, id: direct.id };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// src/core/visibility.ts
|
|
410
|
+
function visibleFieldIdsUnder(props, tagId, opts = {}) {
|
|
411
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
412
|
+
const tags = (_a = props.filters) != null ? _a : [];
|
|
413
|
+
const fields = (_b = props.fields) != null ? _b : [];
|
|
414
|
+
const tagById = new Map(tags.map((t) => [t.id, t]));
|
|
415
|
+
const tag = tagById.get(tagId);
|
|
416
|
+
if (!tag) return [];
|
|
417
|
+
const nodeMap = buildNodeMap(props);
|
|
418
|
+
const lineageDepth = /* @__PURE__ */ new Map();
|
|
419
|
+
{
|
|
420
|
+
const guard = /* @__PURE__ */ new Set();
|
|
421
|
+
let cur = tag;
|
|
422
|
+
let d = 0;
|
|
423
|
+
while (cur && !guard.has(cur.id)) {
|
|
424
|
+
lineageDepth.set(cur.id, d++);
|
|
425
|
+
guard.add(cur.id);
|
|
426
|
+
const parentId = cur.bind_id;
|
|
427
|
+
cur = parentId ? tagById.get(parentId) : void 0;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const isTagInLineage = (id) => lineageDepth.has(id);
|
|
431
|
+
const fieldById = new Map(fields.map((f) => [f.id, f]));
|
|
432
|
+
const ownerDepthForField = (f) => {
|
|
433
|
+
const b = f.bind_id;
|
|
434
|
+
if (!b) return void 0;
|
|
435
|
+
if (typeof b === "string") return lineageDepth.get(b);
|
|
436
|
+
let best = void 0;
|
|
437
|
+
for (const id of b) {
|
|
438
|
+
const d = lineageDepth.get(id);
|
|
439
|
+
if (d == null) continue;
|
|
440
|
+
if (best == null || d < best) best = d;
|
|
441
|
+
}
|
|
442
|
+
return best;
|
|
443
|
+
};
|
|
444
|
+
const ownerDepthForTriggerKey = (triggerKey) => {
|
|
445
|
+
const t = resolveTrigger(triggerKey, nodeMap);
|
|
446
|
+
if (!t) return void 0;
|
|
447
|
+
if (t.kind === "composite") {
|
|
448
|
+
const f = fieldById.get(t.fieldId);
|
|
449
|
+
if (!f) return void 0;
|
|
450
|
+
return ownerDepthForField(f);
|
|
451
|
+
}
|
|
452
|
+
if (t.kind === "field") {
|
|
453
|
+
const f = fieldById.get(t.id);
|
|
454
|
+
if (!f || f.button !== true) return void 0;
|
|
455
|
+
return ownerDepthForField(f);
|
|
456
|
+
}
|
|
457
|
+
if (t.kind === "option") {
|
|
458
|
+
const f = t.fieldId ? fieldById.get(t.fieldId) : void 0;
|
|
459
|
+
if (!f) return void 0;
|
|
460
|
+
return ownerDepthForField(f);
|
|
461
|
+
}
|
|
462
|
+
return void 0;
|
|
463
|
+
};
|
|
464
|
+
const tagInclude = new Set((_c = tag.includes) != null ? _c : []);
|
|
465
|
+
const tagExclude = new Set((_d = tag.excludes) != null ? _d : []);
|
|
466
|
+
const selected = (_e = opts.selectedKeys) != null ? _e : /* @__PURE__ */ new Set();
|
|
467
|
+
const incMap = (_f = props.includes_for_buttons) != null ? _f : {};
|
|
468
|
+
const excMap = (_g = props.excludes_for_buttons) != null ? _g : {};
|
|
469
|
+
const relevantTriggersInOrder = [];
|
|
470
|
+
for (const key of selected) {
|
|
471
|
+
const d = ownerDepthForTriggerKey(key);
|
|
472
|
+
if (d == null) continue;
|
|
473
|
+
relevantTriggersInOrder.push(key);
|
|
474
|
+
}
|
|
475
|
+
const visible = /* @__PURE__ */ new Set();
|
|
476
|
+
const isBoundToLineage = (f) => {
|
|
477
|
+
const b = f.bind_id;
|
|
478
|
+
if (!b) return false;
|
|
479
|
+
if (typeof b === "string") return isTagInLineage(b);
|
|
480
|
+
for (const id of b) if (isTagInLineage(id)) return true;
|
|
481
|
+
return false;
|
|
482
|
+
};
|
|
483
|
+
for (const f of fields) {
|
|
484
|
+
if (isBoundToLineage(f)) visible.add(f.id);
|
|
485
|
+
if (tagInclude.has(f.id)) visible.add(f.id);
|
|
486
|
+
}
|
|
487
|
+
for (const id of tagExclude) visible.delete(id);
|
|
488
|
+
const decide = /* @__PURE__ */ new Map();
|
|
489
|
+
const applyDecision = (fieldId, next) => {
|
|
490
|
+
const prev = decide.get(fieldId);
|
|
491
|
+
if (!prev) return void decide.set(fieldId, next);
|
|
492
|
+
if (next.depth < prev.depth) return void decide.set(fieldId, next);
|
|
493
|
+
if (next.depth > prev.depth) return;
|
|
494
|
+
if (prev.kind === "include" && next.kind === "exclude") {
|
|
495
|
+
decide.set(fieldId, next);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
const revealedOrder = [];
|
|
499
|
+
const revealedSeen = /* @__PURE__ */ new Set();
|
|
500
|
+
for (const triggerKey of relevantTriggersInOrder) {
|
|
501
|
+
const depth = ownerDepthForTriggerKey(triggerKey);
|
|
502
|
+
if (depth == null) continue;
|
|
503
|
+
for (const id of (_h = incMap[triggerKey]) != null ? _h : []) {
|
|
504
|
+
applyDecision(id, { depth, kind: "include" });
|
|
505
|
+
if (!revealedSeen.has(id)) {
|
|
506
|
+
revealedSeen.add(id);
|
|
507
|
+
revealedOrder.push(id);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
for (const id of (_i = excMap[triggerKey]) != null ? _i : []) {
|
|
511
|
+
applyDecision(id, { depth, kind: "exclude" });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
for (const [fid, d] of decide) {
|
|
515
|
+
if (d.kind === "include") visible.add(fid);
|
|
516
|
+
else visible.delete(fid);
|
|
517
|
+
}
|
|
518
|
+
const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
|
|
519
|
+
const order = (_j = props.order_for_tags) == null ? void 0 : _j[tagId];
|
|
520
|
+
if (order && order.length) {
|
|
521
|
+
const ordered = order.filter((fid) => visible.has(fid));
|
|
522
|
+
const orderedSet = new Set(ordered);
|
|
523
|
+
const rest = base.filter((fid) => !orderedSet.has(fid));
|
|
524
|
+
return [...ordered, ...rest];
|
|
525
|
+
}
|
|
526
|
+
return base;
|
|
527
|
+
}
|
|
528
|
+
function visibleFieldsUnder(props, tagId, opts = {}) {
|
|
529
|
+
var _a;
|
|
530
|
+
const ids = visibleFieldIdsUnder(props, tagId, opts);
|
|
531
|
+
const fieldById = new Map(((_a = props.fields) != null ? _a : []).map((f) => [f.id, f]));
|
|
532
|
+
return ids.map((id) => fieldById.get(id)).filter(Boolean);
|
|
533
|
+
}
|
|
534
|
+
|
|
373
535
|
// src/core/validate/steps/visibility.ts
|
|
374
536
|
function createFieldsVisibleUnder(v) {
|
|
375
537
|
return (tagId) => {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const excludesTag = new Set((_b = tag == null ? void 0 : tag.excludes) != null ? _b : []);
|
|
380
|
-
const incForOpt = (_c = v.props.includes_for_buttons) != null ? _c : {};
|
|
381
|
-
const excForOpt = (_d = v.props.excludes_for_buttons) != null ? _d : {};
|
|
382
|
-
const includesOpt = /* @__PURE__ */ new Set();
|
|
383
|
-
const excludesOpt = /* @__PURE__ */ new Set();
|
|
384
|
-
for (const key of v.selectedKeys) {
|
|
385
|
-
for (const id of (_e = incForOpt[key]) != null ? _e : []) includesOpt.add(id);
|
|
386
|
-
for (const id of (_f = excForOpt[key]) != null ? _f : []) excludesOpt.add(id);
|
|
387
|
-
}
|
|
388
|
-
const merged = /* @__PURE__ */ new Map();
|
|
389
|
-
for (const f of v.fields) {
|
|
390
|
-
if (isBoundTo(f, tagId)) merged.set(f.id, f);
|
|
391
|
-
if (includesTag.has(f.id)) merged.set(f.id, f);
|
|
392
|
-
if (includesOpt.has(f.id)) merged.set(f.id, f);
|
|
393
|
-
}
|
|
394
|
-
for (const id of excludesTag) merged.delete(id);
|
|
395
|
-
for (const id of excludesOpt) merged.delete(id);
|
|
396
|
-
return Array.from(merged.values());
|
|
538
|
+
return visibleFieldsUnder(v.props, tagId, {
|
|
539
|
+
selectedKeys: v.selectedKeys
|
|
540
|
+
});
|
|
397
541
|
};
|
|
398
542
|
}
|
|
399
|
-
function
|
|
543
|
+
function stableKeyOfSelection(keys) {
|
|
544
|
+
return Array.from(keys).sort().join("|");
|
|
545
|
+
}
|
|
546
|
+
function resolveRootTags(tags) {
|
|
547
|
+
const roots = tags.filter((t) => !t.bind_id);
|
|
548
|
+
return roots.length ? roots : tags.slice(0, 1);
|
|
549
|
+
}
|
|
550
|
+
function isEffectfulTrigger(v, trigger) {
|
|
551
|
+
var _a, _b, _c, _d, _e, _f;
|
|
552
|
+
const inc = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
553
|
+
const exc = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
554
|
+
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;
|
|
555
|
+
}
|
|
556
|
+
function collectSelectableTriggersInContext(v, tagId, selectedKeys, onlyEffectful) {
|
|
557
|
+
var _a;
|
|
558
|
+
const visible = visibleFieldsUnder(v.props, tagId, {
|
|
559
|
+
selectedKeys
|
|
560
|
+
});
|
|
561
|
+
const triggers = [];
|
|
562
|
+
for (const f of visible) {
|
|
563
|
+
if (f.button === true) {
|
|
564
|
+
const t = f.id;
|
|
565
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
566
|
+
}
|
|
567
|
+
for (const o of (_a = f.options) != null ? _a : []) {
|
|
568
|
+
const t = `${f.id}::${o.id}`;
|
|
569
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
triggers.sort();
|
|
573
|
+
return triggers;
|
|
574
|
+
}
|
|
575
|
+
function runVisibilityRulesOnce(v) {
|
|
400
576
|
var _a, _b, _c, _d, _e;
|
|
401
577
|
for (const t of v.tags) {
|
|
402
578
|
const visible = v.fieldsVisibleUnder(t.id);
|
|
@@ -471,12 +647,85 @@ function validateVisibility(v) {
|
|
|
471
647
|
}
|
|
472
648
|
}
|
|
473
649
|
}
|
|
650
|
+
function dedupeErrorsInPlace(v, startIndex) {
|
|
651
|
+
const seen = /* @__PURE__ */ new Set();
|
|
652
|
+
const keyOfErr = (e) => {
|
|
653
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
654
|
+
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 : "";
|
|
655
|
+
const other = (_g = (_f = e == null ? void 0 : e.details) == null ? void 0 : _f.other) != null ? _g : "";
|
|
656
|
+
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 : ""}`;
|
|
657
|
+
};
|
|
658
|
+
const kept = [];
|
|
659
|
+
for (let i = startIndex; i < v.errors.length; i++) {
|
|
660
|
+
const e = v.errors[i];
|
|
661
|
+
const k = keyOfErr(e);
|
|
662
|
+
if (seen.has(k)) continue;
|
|
663
|
+
seen.add(k);
|
|
664
|
+
kept.push(e);
|
|
665
|
+
}
|
|
666
|
+
v.errors.splice(startIndex, v.errors.length - startIndex, ...kept);
|
|
667
|
+
}
|
|
668
|
+
function validateVisibility(v, options = {}) {
|
|
669
|
+
var _a, _b, _c;
|
|
670
|
+
const simulate = options.simulate === true;
|
|
671
|
+
if (!simulate) {
|
|
672
|
+
runVisibilityRulesOnce(v);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
const maxStates = Math.max(1, (_a = options.maxStates) != null ? _a : 500);
|
|
676
|
+
const maxDepth = Math.max(0, (_b = options.maxDepth) != null ? _b : 6);
|
|
677
|
+
const onlyEffectful = options.onlyEffectfulTriggers !== false;
|
|
678
|
+
const roots = resolveRootTags(v.tags);
|
|
679
|
+
const rootTags = options.simulateAllRoots ? roots : roots.slice(0, 1);
|
|
680
|
+
const originalSelected = new Set((_c = v.selectedKeys) != null ? _c : []);
|
|
681
|
+
const errorsStart = v.errors.length;
|
|
682
|
+
const visited = /* @__PURE__ */ new Set();
|
|
683
|
+
const stack = [];
|
|
684
|
+
for (const rt of rootTags) {
|
|
685
|
+
stack.push({
|
|
686
|
+
rootTagId: rt.id,
|
|
687
|
+
selected: new Set(originalSelected),
|
|
688
|
+
depth: 0
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
let validatedStates = 0;
|
|
692
|
+
while (stack.length) {
|
|
693
|
+
if (validatedStates >= maxStates) break;
|
|
694
|
+
const state = stack.pop();
|
|
695
|
+
const sig = stableKeyOfSelection(state.selected);
|
|
696
|
+
if (visited.has(sig)) continue;
|
|
697
|
+
visited.add(sig);
|
|
698
|
+
v.selectedKeys = state.selected;
|
|
699
|
+
validatedStates++;
|
|
700
|
+
runVisibilityRulesOnce(v);
|
|
701
|
+
if (state.depth >= maxDepth) continue;
|
|
702
|
+
const triggers = collectSelectableTriggersInContext(
|
|
703
|
+
v,
|
|
704
|
+
state.rootTagId,
|
|
705
|
+
state.selected,
|
|
706
|
+
onlyEffectful
|
|
707
|
+
);
|
|
708
|
+
for (let i = triggers.length - 1; i >= 0; i--) {
|
|
709
|
+
const trig = triggers[i];
|
|
710
|
+
if (state.selected.has(trig)) continue;
|
|
711
|
+
const next = new Set(state.selected);
|
|
712
|
+
next.add(trig);
|
|
713
|
+
stack.push({
|
|
714
|
+
rootTagId: state.rootTagId,
|
|
715
|
+
selected: next,
|
|
716
|
+
depth: state.depth + 1
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
v.selectedKeys = originalSelected;
|
|
721
|
+
dedupeErrorsInPlace(v, errorsStart);
|
|
722
|
+
}
|
|
474
723
|
|
|
475
724
|
// src/core/validate/steps/structure.ts
|
|
476
725
|
function validateStructure(v) {
|
|
477
726
|
const tags = v.tags;
|
|
478
727
|
const fields = v.fields;
|
|
479
|
-
if (!tags.some((t) => t.id === "root")) {
|
|
728
|
+
if (!tags.some((t) => t.id === "t:root")) {
|
|
480
729
|
v.errors.push({
|
|
481
730
|
code: "root_missing",
|
|
482
731
|
severity: "error",
|
|
@@ -666,58 +915,91 @@ function validateIdentity(v) {
|
|
|
666
915
|
}
|
|
667
916
|
|
|
668
917
|
// src/core/validate/steps/option-maps.ts
|
|
918
|
+
function parseFieldOptionKey(key) {
|
|
919
|
+
const idx = key.indexOf("::");
|
|
920
|
+
if (idx === -1) return null;
|
|
921
|
+
const fieldId = key.slice(0, idx).trim();
|
|
922
|
+
const optionId = key.slice(idx + 2).trim();
|
|
923
|
+
if (!fieldId || !optionId) return null;
|
|
924
|
+
return { fieldId, optionId };
|
|
925
|
+
}
|
|
926
|
+
function hasOption(v, fid, oid) {
|
|
927
|
+
var _a;
|
|
928
|
+
const f = v.fieldById.get(fid);
|
|
929
|
+
if (!f) return false;
|
|
930
|
+
return !!((_a = f.options) != null ? _a : []).find((o) => o.id === oid);
|
|
931
|
+
}
|
|
669
932
|
function validateOptionMaps(v) {
|
|
670
933
|
var _a, _b;
|
|
671
934
|
const incMap = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
672
935
|
const excMap = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
936
|
+
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.`;
|
|
937
|
+
const validateTriggerKey = (key) => {
|
|
938
|
+
const ref = v.nodeMap.get(key);
|
|
939
|
+
if (ref) {
|
|
940
|
+
if (ref.kind === "option") {
|
|
941
|
+
return {
|
|
942
|
+
ok: true,
|
|
943
|
+
nodeId: ref.fieldId,
|
|
944
|
+
affected: [ref.fieldId, ref.id]
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
if (ref.kind === "field") {
|
|
948
|
+
const isButton2 = ref.node.button === true;
|
|
949
|
+
if (!isButton2)
|
|
950
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
951
|
+
return { ok: true, nodeId: ref.id, affected: [ref.id] };
|
|
952
|
+
}
|
|
953
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
954
|
+
}
|
|
955
|
+
const p = parseFieldOptionKey(key);
|
|
956
|
+
if (!p) return { ok: false };
|
|
957
|
+
if (!hasOption(v, p.fieldId, p.optionId))
|
|
958
|
+
return {
|
|
959
|
+
ok: false,
|
|
960
|
+
nodeId: p.fieldId,
|
|
961
|
+
affected: [p.fieldId, p.optionId]
|
|
962
|
+
};
|
|
963
|
+
return {
|
|
964
|
+
ok: true,
|
|
965
|
+
nodeId: p.fieldId,
|
|
966
|
+
affected: [p.fieldId, p.optionId]
|
|
967
|
+
};
|
|
685
968
|
};
|
|
686
|
-
const badKeyMessage = (key) => `Invalid option-map key "${key}". Expected "fieldId::optionId" pointing to an existing option.`;
|
|
687
969
|
for (const k of Object.keys(incMap)) {
|
|
688
|
-
const
|
|
689
|
-
if (!
|
|
970
|
+
const r = validateTriggerKey(k);
|
|
971
|
+
if (!r.ok) {
|
|
690
972
|
v.errors.push({
|
|
691
973
|
code: "bad_option_key",
|
|
692
974
|
severity: "error",
|
|
693
975
|
message: badKeyMessage(k),
|
|
694
|
-
|
|
976
|
+
nodeId: r.nodeId,
|
|
977
|
+
details: withAffected({ key: k }, r.affected)
|
|
695
978
|
});
|
|
696
979
|
}
|
|
697
980
|
}
|
|
698
981
|
for (const k of Object.keys(excMap)) {
|
|
699
|
-
const
|
|
700
|
-
if (!
|
|
982
|
+
const r = validateTriggerKey(k);
|
|
983
|
+
if (!r.ok) {
|
|
701
984
|
v.errors.push({
|
|
702
985
|
code: "bad_option_key",
|
|
703
986
|
severity: "error",
|
|
704
987
|
message: badKeyMessage(k),
|
|
705
|
-
|
|
988
|
+
nodeId: r.nodeId,
|
|
989
|
+
details: withAffected({ key: k }, r.affected)
|
|
706
990
|
});
|
|
707
991
|
}
|
|
708
992
|
}
|
|
709
993
|
for (const k of Object.keys(incMap)) {
|
|
710
|
-
if (k in excMap)
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
});
|
|
720
|
-
}
|
|
994
|
+
if (!(k in excMap)) continue;
|
|
995
|
+
const r = validateTriggerKey(k);
|
|
996
|
+
v.errors.push({
|
|
997
|
+
code: "option_include_exclude_conflict",
|
|
998
|
+
severity: "error",
|
|
999
|
+
message: `Trigger-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
|
|
1000
|
+
nodeId: r.nodeId,
|
|
1001
|
+
details: withAffected({ key: k }, r.affected)
|
|
1002
|
+
});
|
|
721
1003
|
}
|
|
722
1004
|
}
|
|
723
1005
|
|
|
@@ -1622,8 +1904,23 @@ function applyPolicies(errors, props, serviceMap, policies, fieldsVisibleUnder,
|
|
|
1622
1904
|
}
|
|
1623
1905
|
|
|
1624
1906
|
// src/core/validate/index.ts
|
|
1907
|
+
function readVisibilitySimOpts(ctx) {
|
|
1908
|
+
const c = ctx;
|
|
1909
|
+
const simulate = c.simulateVisibility === true || c.visibilitySimulate === true || c.simulate === true;
|
|
1910
|
+
const maxStates = typeof c.maxVisibilityStates === "number" ? c.maxVisibilityStates : typeof c.visibilityMaxStates === "number" ? c.visibilityMaxStates : typeof c.maxStates === "number" ? c.maxStates : void 0;
|
|
1911
|
+
const maxDepth = typeof c.maxVisibilityDepth === "number" ? c.maxVisibilityDepth : typeof c.visibilityMaxDepth === "number" ? c.visibilityMaxDepth : typeof c.maxDepth === "number" ? c.maxDepth : void 0;
|
|
1912
|
+
const simulateAllRoots = c.simulateAllRoots === true || c.visibilitySimulateAllRoots === true;
|
|
1913
|
+
const onlyEffectfulTriggers = c.onlyEffectfulTriggers === false ? false : c.visibilityOnlyEffectfulTriggers !== false;
|
|
1914
|
+
return {
|
|
1915
|
+
simulate,
|
|
1916
|
+
maxStates,
|
|
1917
|
+
maxDepth,
|
|
1918
|
+
simulateAllRoots,
|
|
1919
|
+
onlyEffectfulTriggers
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1625
1922
|
function validate(props, ctx = {}) {
|
|
1626
|
-
var _a, _b;
|
|
1923
|
+
var _a, _b, _c;
|
|
1627
1924
|
const errors = [];
|
|
1628
1925
|
const serviceMap = (_a = ctx.serviceMap) != null ? _a : {};
|
|
1629
1926
|
const selectedKeys = new Set(
|
|
@@ -1637,6 +1934,7 @@ function validate(props, ctx = {}) {
|
|
|
1637
1934
|
for (const f of fields) fieldById.set(f.id, f);
|
|
1638
1935
|
const v = {
|
|
1639
1936
|
props,
|
|
1937
|
+
nodeMap: (_c = ctx.nodeMap) != null ? _c : buildNodeMap(props),
|
|
1640
1938
|
options: ctx,
|
|
1641
1939
|
errors,
|
|
1642
1940
|
serviceMap,
|
|
@@ -1651,7 +1949,8 @@ function validate(props, ctx = {}) {
|
|
|
1651
1949
|
validateIdentity(v);
|
|
1652
1950
|
validateOptionMaps(v);
|
|
1653
1951
|
v.fieldsVisibleUnder = createFieldsVisibleUnder(v);
|
|
1654
|
-
|
|
1952
|
+
const visSim = readVisibilitySimOpts(ctx);
|
|
1953
|
+
validateVisibility(v, visSim);
|
|
1655
1954
|
applyPolicies(
|
|
1656
1955
|
v.errors,
|
|
1657
1956
|
v.props,
|
|
@@ -1670,6 +1969,17 @@ function validate(props, ctx = {}) {
|
|
|
1670
1969
|
validateFallbacks(v);
|
|
1671
1970
|
return v.errors;
|
|
1672
1971
|
}
|
|
1972
|
+
async function validateAsync(props, ctx = {}) {
|
|
1973
|
+
await Promise.resolve();
|
|
1974
|
+
if (typeof requestAnimationFrame === "function") {
|
|
1975
|
+
await new Promise(
|
|
1976
|
+
(resolve) => requestAnimationFrame(() => resolve())
|
|
1977
|
+
);
|
|
1978
|
+
} else {
|
|
1979
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1980
|
+
}
|
|
1981
|
+
return validate(props, ctx);
|
|
1982
|
+
}
|
|
1673
1983
|
|
|
1674
1984
|
// src/core/builder.ts
|
|
1675
1985
|
function createBuilder(opts = {}) {
|
|
@@ -1687,11 +1997,21 @@ var BuilderImpl = class {
|
|
|
1687
1997
|
this.optionOwnerById = /* @__PURE__ */ new Map();
|
|
1688
1998
|
this.history = [];
|
|
1689
1999
|
this.future = [];
|
|
2000
|
+
this._nodemap = null;
|
|
1690
2001
|
var _a;
|
|
1691
2002
|
this.options = { ...opts };
|
|
1692
2003
|
this.historyLimit = (_a = opts.historyLimit) != null ? _a : 50;
|
|
1693
2004
|
}
|
|
1694
2005
|
/* ───── lifecycle ─────────────────────────────────────────────────────── */
|
|
2006
|
+
isTagId(id) {
|
|
2007
|
+
return this.tagById.has(id);
|
|
2008
|
+
}
|
|
2009
|
+
isFieldId(id) {
|
|
2010
|
+
return this.fieldById.has(id);
|
|
2011
|
+
}
|
|
2012
|
+
isOptionId(id) {
|
|
2013
|
+
return this.optionOwnerById.has(id);
|
|
2014
|
+
}
|
|
1695
2015
|
load(raw) {
|
|
1696
2016
|
const next = normalise(raw, {
|
|
1697
2017
|
defaultPricingRole: "base",
|
|
@@ -1911,129 +2231,16 @@ var BuilderImpl = class {
|
|
|
1911
2231
|
return validate(this.props, this.options);
|
|
1912
2232
|
}
|
|
1913
2233
|
visibleFields(tagId, selectedKeys) {
|
|
1914
|
-
var _a
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
let cur = tag;
|
|
1925
|
-
let d = 0;
|
|
1926
|
-
while (cur && !guard.has(cur.id)) {
|
|
1927
|
-
lineageDepth.set(cur.id, d++);
|
|
1928
|
-
guard.add(cur.id);
|
|
1929
|
-
const parentId = cur.bind_id;
|
|
1930
|
-
cur = parentId ? tagById.get(parentId) : void 0;
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
const isTagInLineage = (id) => lineageDepth.has(id);
|
|
1934
|
-
const fieldById = new Map(fields.map((f) => [f.id, f]));
|
|
1935
|
-
const optionOwnerFieldId = /* @__PURE__ */ new Map();
|
|
1936
|
-
for (const f of fields) {
|
|
1937
|
-
for (const o of (_c = f.options) != null ? _c : []) optionOwnerFieldId.set(o.id, f.id);
|
|
1938
|
-
}
|
|
1939
|
-
const ownerDepthForField = (f) => {
|
|
1940
|
-
const b = f.bind_id;
|
|
1941
|
-
if (!b) return void 0;
|
|
1942
|
-
if (typeof b === "string") return lineageDepth.get(b);
|
|
1943
|
-
let best = void 0;
|
|
1944
|
-
for (const id of b) {
|
|
1945
|
-
const d = lineageDepth.get(id);
|
|
1946
|
-
if (d == null) continue;
|
|
1947
|
-
if (best == null || d < best) best = d;
|
|
1948
|
-
}
|
|
1949
|
-
return best;
|
|
1950
|
-
};
|
|
1951
|
-
const ownerDepthForTrigger = (triggerId) => {
|
|
1952
|
-
if (triggerId.startsWith("o:")) {
|
|
1953
|
-
const fid = optionOwnerFieldId.get(triggerId);
|
|
1954
|
-
if (!fid) return void 0;
|
|
1955
|
-
const f2 = fieldById.get(fid);
|
|
1956
|
-
if (!f2) return void 0;
|
|
1957
|
-
return ownerDepthForField(f2);
|
|
1958
|
-
}
|
|
1959
|
-
const f = fieldById.get(triggerId);
|
|
1960
|
-
if (!f || f.button !== true) return void 0;
|
|
1961
|
-
return ownerDepthForField(f);
|
|
1962
|
-
};
|
|
1963
|
-
const tagInclude = new Set((_d = tag.includes) != null ? _d : []);
|
|
1964
|
-
const tagExclude = new Set((_e = tag.excludes) != null ? _e : []);
|
|
1965
|
-
const selected = new Set(
|
|
1966
|
-
(_f = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _f : []
|
|
1967
|
-
);
|
|
1968
|
-
const incMap = (_g = props.includes_for_buttons) != null ? _g : {};
|
|
1969
|
-
const excMap = (_h = props.excludes_for_buttons) != null ? _h : {};
|
|
1970
|
-
const relevantTriggersInOrder = [];
|
|
1971
|
-
for (const key of selected) {
|
|
1972
|
-
const d = ownerDepthForTrigger(key);
|
|
1973
|
-
if (d == null) continue;
|
|
1974
|
-
relevantTriggersInOrder.push(key);
|
|
1975
|
-
}
|
|
1976
|
-
const visible = /* @__PURE__ */ new Set();
|
|
1977
|
-
const isBoundToLineage = (f) => {
|
|
1978
|
-
const b = f.bind_id;
|
|
1979
|
-
if (!b) return false;
|
|
1980
|
-
if (typeof b === "string") return isTagInLineage(b);
|
|
1981
|
-
for (const id of b) if (isTagInLineage(id)) return true;
|
|
1982
|
-
return false;
|
|
1983
|
-
};
|
|
1984
|
-
for (const f of fields) {
|
|
1985
|
-
if (isBoundToLineage(f)) visible.add(f.id);
|
|
1986
|
-
if (tagInclude.has(f.id)) visible.add(f.id);
|
|
1987
|
-
}
|
|
1988
|
-
for (const id of tagExclude) visible.delete(id);
|
|
1989
|
-
const decide = /* @__PURE__ */ new Map();
|
|
1990
|
-
const applyDecision = (fieldId, next) => {
|
|
1991
|
-
const prev = decide.get(fieldId);
|
|
1992
|
-
if (!prev) {
|
|
1993
|
-
decide.set(fieldId, next);
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
if (next.depth < prev.depth) {
|
|
1997
|
-
decide.set(fieldId, next);
|
|
1998
|
-
return;
|
|
1999
|
-
}
|
|
2000
|
-
if (next.depth > prev.depth) return;
|
|
2001
|
-
if (prev.kind === "include" && next.kind === "exclude") {
|
|
2002
|
-
decide.set(fieldId, next);
|
|
2003
|
-
}
|
|
2004
|
-
};
|
|
2005
|
-
const revealedOrder = [];
|
|
2006
|
-
const revealedSeen = /* @__PURE__ */ new Set();
|
|
2007
|
-
for (const triggerId of relevantTriggersInOrder) {
|
|
2008
|
-
const depth = ownerDepthForTrigger(triggerId);
|
|
2009
|
-
if (depth == null) continue;
|
|
2010
|
-
for (const id of (_i = incMap[triggerId]) != null ? _i : []) {
|
|
2011
|
-
applyDecision(id, { depth, kind: "include" });
|
|
2012
|
-
if (!revealedSeen.has(id)) {
|
|
2013
|
-
revealedSeen.add(id);
|
|
2014
|
-
revealedOrder.push(id);
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
for (const id of (_j = excMap[triggerId]) != null ? _j : []) {
|
|
2018
|
-
applyDecision(id, { depth, kind: "exclude" });
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
for (const [fid, d] of decide) {
|
|
2022
|
-
if (d.kind === "include") visible.add(fid);
|
|
2023
|
-
else visible.delete(fid);
|
|
2024
|
-
}
|
|
2025
|
-
const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
|
|
2026
|
-
const order = (_k = props.order_for_tags) == null ? void 0 : _k[tagId];
|
|
2027
|
-
if (order && order.length) {
|
|
2028
|
-
const ordered = order.filter((fid) => visible.has(fid));
|
|
2029
|
-
const orderedSet = new Set(ordered);
|
|
2030
|
-
const rest2 = base.filter((fid) => !orderedSet.has(fid));
|
|
2031
|
-
return [...ordered, ...rest2];
|
|
2032
|
-
}
|
|
2033
|
-
const promoted = revealedOrder.filter((fid) => visible.has(fid));
|
|
2034
|
-
const promotedSet = new Set(promoted);
|
|
2035
|
-
const rest = base.filter((fid) => !promotedSet.has(fid));
|
|
2036
|
-
return [...promoted, ...rest];
|
|
2234
|
+
var _a;
|
|
2235
|
+
return visibleFieldIdsUnder(this.props, tagId, {
|
|
2236
|
+
selectedKeys: new Set(
|
|
2237
|
+
(_a = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _a : []
|
|
2238
|
+
)
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
getNodeMap() {
|
|
2242
|
+
if (!this._nodemap) this._nodemap = buildNodeMap(this.getProps());
|
|
2243
|
+
return this._nodemap;
|
|
2037
2244
|
}
|
|
2038
2245
|
/* ───── history ─────────────────────────────────────────────────────── */
|
|
2039
2246
|
undo() {
|
|
@@ -2057,6 +2264,7 @@ var BuilderImpl = class {
|
|
|
2057
2264
|
this.tagById.clear();
|
|
2058
2265
|
this.fieldById.clear();
|
|
2059
2266
|
this.optionOwnerById.clear();
|
|
2267
|
+
this._nodemap = null;
|
|
2060
2268
|
for (const t of this.props.filters) this.tagById.set(t.id, t);
|
|
2061
2269
|
for (const f of this.props.fields) {
|
|
2062
2270
|
this.fieldById.set(f.id, f);
|
|
@@ -2538,6 +2746,7 @@ var toBindList = (b) => {
|
|
|
2538
2746
|
function createNodeIndex(builder) {
|
|
2539
2747
|
var _a, _b, _c, _d;
|
|
2540
2748
|
const props = builder.getProps();
|
|
2749
|
+
const nodeMap = builder.getNodeMap();
|
|
2541
2750
|
const tags = (_a = props.filters) != null ? _a : [];
|
|
2542
2751
|
const fields = (_b = props.fields) != null ? _b : [];
|
|
2543
2752
|
const tagById = new Map(tags.map((t) => [t.id, t]));
|
|
@@ -2741,7 +2950,9 @@ function createNodeIndex(builder) {
|
|
|
2741
2950
|
return parentById.get(id) === tid;
|
|
2742
2951
|
},
|
|
2743
2952
|
getDescendant(descendantId) {
|
|
2744
|
-
return this.getDescendants().find(
|
|
2953
|
+
return this.getDescendants().find(
|
|
2954
|
+
(item) => item.id == descendantId
|
|
2955
|
+
);
|
|
2745
2956
|
},
|
|
2746
2957
|
getDescendants() {
|
|
2747
2958
|
const results = [];
|
|
@@ -2750,7 +2961,9 @@ function createNodeIndex(builder) {
|
|
|
2750
2961
|
const node2 = getField(fieldId);
|
|
2751
2962
|
if (!node2) continue;
|
|
2752
2963
|
const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, id);
|
|
2753
|
-
results.push(
|
|
2964
|
+
results.push(
|
|
2965
|
+
explicit ? node2 : { ...node2, isInherited: true }
|
|
2966
|
+
);
|
|
2754
2967
|
}
|
|
2755
2968
|
return Object.freeze(results);
|
|
2756
2969
|
}
|
|
@@ -2799,7 +3012,9 @@ function createNodeIndex(builder) {
|
|
|
2799
3012
|
return false;
|
|
2800
3013
|
},
|
|
2801
3014
|
getDescendant(descendantId, context) {
|
|
2802
|
-
return this.getDescendants(context).find(
|
|
3015
|
+
return this.getDescendants(context).find(
|
|
3016
|
+
(item) => item.id == descendantId
|
|
3017
|
+
);
|
|
2803
3018
|
},
|
|
2804
3019
|
getDescendants(tagId) {
|
|
2805
3020
|
return resolveDescendants(id, includes, tagId, !isButton2);
|
|
@@ -2841,7 +3056,9 @@ function createNodeIndex(builder) {
|
|
|
2841
3056
|
return owner.isBoundTo(tagId);
|
|
2842
3057
|
},
|
|
2843
3058
|
getDescendant(descendantId, context) {
|
|
2844
|
-
return this.getDescendants(context).find(
|
|
3059
|
+
return this.getDescendants(context).find(
|
|
3060
|
+
(item) => item.id == descendantId
|
|
3061
|
+
);
|
|
2845
3062
|
},
|
|
2846
3063
|
getDescendants(tagId) {
|
|
2847
3064
|
return resolveDescendants(id, includes, tagId);
|
|
@@ -2852,7 +3069,7 @@ function createNodeIndex(builder) {
|
|
|
2852
3069
|
return node;
|
|
2853
3070
|
};
|
|
2854
3071
|
const getNode = (input) => {
|
|
2855
|
-
var _a2, _b2, _c2, _d2, _e
|
|
3072
|
+
var _a2, _b2, _c2, _d2, _e;
|
|
2856
3073
|
if (typeof input !== "string") {
|
|
2857
3074
|
if ("bind_id" in input && !("type" in input))
|
|
2858
3075
|
return (_a2 = getTag(input.id)) != null ? _a2 : mkUnknown(input.id);
|
|
@@ -2862,11 +3079,7 @@ function createNodeIndex(builder) {
|
|
|
2862
3079
|
}
|
|
2863
3080
|
const cached = nodeCache.get(input);
|
|
2864
3081
|
if (cached) return cached;
|
|
2865
|
-
|
|
2866
|
-
if (id.startsWith("t:")) return (_d2 = getTag(id)) != null ? _d2 : mkUnknown(id);
|
|
2867
|
-
if (id.startsWith("f:")) return (_e = getField(id)) != null ? _e : mkUnknown(id);
|
|
2868
|
-
if (id.startsWith("o:")) return (_f = getOption(id)) != null ? _f : mkUnknown(id);
|
|
2869
|
-
return (_i = (_h = (_g = getTag(id)) != null ? _g : getField(id)) != null ? _h : getOption(id)) != null ? _i : mkUnknown(id);
|
|
3082
|
+
return (_e = (_d2 = nodeMap.get(input)) == null ? void 0 : _d2.node) != null ? _e : mkUnknown(input);
|
|
2870
3083
|
};
|
|
2871
3084
|
const mkUnknown = (id) => {
|
|
2872
3085
|
const u = {
|
|
@@ -2886,7 +3099,695 @@ function createNodeIndex(builder) {
|
|
|
2886
3099
|
getOption
|
|
2887
3100
|
};
|
|
2888
3101
|
}
|
|
3102
|
+
|
|
3103
|
+
// src/utils/prune-fallbacks.ts
|
|
3104
|
+
function pruneInvalidNodeFallbacks(props, services, settings) {
|
|
3105
|
+
var _a, _b;
|
|
3106
|
+
const fb = props.fallbacks;
|
|
3107
|
+
if (!(fb == null ? void 0 : fb.nodes) || Object.keys(fb.nodes).length === 0) {
|
|
3108
|
+
return { props, removed: [] };
|
|
3109
|
+
}
|
|
3110
|
+
const nodeContexts = /* @__PURE__ */ new Map();
|
|
3111
|
+
const nodePrimary = /* @__PURE__ */ new Map();
|
|
3112
|
+
for (const nodeId of Object.keys(fb.nodes)) {
|
|
3113
|
+
const tag = props.filters.find((t) => t.id === nodeId);
|
|
3114
|
+
if (tag) {
|
|
3115
|
+
nodeContexts.set(nodeId, [tag.id]);
|
|
3116
|
+
nodePrimary.set(nodeId, tag.service_id);
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
const field = props.fields.find((f) => Array.isArray(f.options) && f.options.some((o) => o.id === nodeId));
|
|
3120
|
+
if (field) {
|
|
3121
|
+
const contexts = toBindArray(field.bind_id);
|
|
3122
|
+
nodeContexts.set(nodeId, contexts);
|
|
3123
|
+
const opt = field.options.find((o) => o.id === nodeId);
|
|
3124
|
+
nodePrimary.set(nodeId, opt.service_id);
|
|
3125
|
+
continue;
|
|
3126
|
+
}
|
|
3127
|
+
nodeContexts.set(nodeId, []);
|
|
3128
|
+
nodePrimary.set(nodeId, void 0);
|
|
3129
|
+
}
|
|
3130
|
+
const diags = collectFailedFallbacks(props, services, { ...settings, mode: "dev" });
|
|
3131
|
+
const failuresByPair = /* @__PURE__ */ new Map();
|
|
3132
|
+
const totalContextsByNode = /* @__PURE__ */ new Map();
|
|
3133
|
+
for (const [nodeId, ctxs] of nodeContexts.entries()) {
|
|
3134
|
+
totalContextsByNode.set(nodeId, Math.max(1, ctxs.length));
|
|
3135
|
+
}
|
|
3136
|
+
for (const d of diags) {
|
|
3137
|
+
if (d.scope !== "node") continue;
|
|
3138
|
+
const key = `${d.nodeId}::${String(d.candidate)}`;
|
|
3139
|
+
let rec = failuresByPair.get(key);
|
|
3140
|
+
if (!rec) {
|
|
3141
|
+
rec = { reasons: /* @__PURE__ */ new Set(), contexts: /* @__PURE__ */ new Set() };
|
|
3142
|
+
failuresByPair.set(key, rec);
|
|
3143
|
+
}
|
|
3144
|
+
rec.reasons.add(d.reason);
|
|
3145
|
+
if (d.tagContext) rec.contexts.add(d.tagContext);
|
|
3146
|
+
}
|
|
3147
|
+
const prunedNodes = {};
|
|
3148
|
+
const removed = [];
|
|
3149
|
+
for (const [nodeId, list] of Object.entries(fb.nodes)) {
|
|
3150
|
+
const contexts = (_a = nodeContexts.get(nodeId)) != null ? _a : [];
|
|
3151
|
+
const totalContexts = Math.max(1, contexts.length);
|
|
3152
|
+
const keep = [];
|
|
3153
|
+
for (const cand of list) {
|
|
3154
|
+
const key = `${nodeId}::${String(cand)}`;
|
|
3155
|
+
const rec = failuresByPair.get(key);
|
|
3156
|
+
if (!rec) {
|
|
3157
|
+
keep.push(cand);
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3160
|
+
const failedContextsCount = rec.contexts.size > 0 ? rec.contexts.size : totalContexts;
|
|
3161
|
+
const failsAll = failedContextsCount >= totalContexts;
|
|
3162
|
+
if (failsAll) {
|
|
3163
|
+
removed.push({
|
|
3164
|
+
nodeId,
|
|
3165
|
+
candidate: cand,
|
|
3166
|
+
reasons: Array.from(rec.reasons),
|
|
3167
|
+
contexts: contexts.length ? contexts.slice() : void 0
|
|
3168
|
+
});
|
|
3169
|
+
} else {
|
|
3170
|
+
keep.push(cand);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
if (keep.length) prunedNodes[nodeId] = keep;
|
|
3174
|
+
}
|
|
3175
|
+
const outProps = {
|
|
3176
|
+
...props,
|
|
3177
|
+
fallbacks: {
|
|
3178
|
+
...((_b = props.fallbacks) == null ? void 0 : _b.global) ? { global: props.fallbacks.global } : {},
|
|
3179
|
+
...Object.keys(prunedNodes).length ? { nodes: prunedNodes } : {}
|
|
3180
|
+
}
|
|
3181
|
+
};
|
|
3182
|
+
return { props: outProps, removed };
|
|
3183
|
+
}
|
|
3184
|
+
function toBindArray(bind) {
|
|
3185
|
+
if (!bind) return [];
|
|
3186
|
+
return Array.isArray(bind) ? bind.slice() : [bind];
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
// src/utils/util.ts
|
|
3190
|
+
function toFiniteNumber(v) {
|
|
3191
|
+
const n = Number(v);
|
|
3192
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3193
|
+
}
|
|
3194
|
+
function constraintFitOk(svcMap, candidate, constraints) {
|
|
3195
|
+
const cap = svcMap[Number(candidate)];
|
|
3196
|
+
if (!cap) return false;
|
|
3197
|
+
if (constraints.dripfeed === true && !cap.dripfeed) return false;
|
|
3198
|
+
if (constraints.refill === true && !cap.refill) return false;
|
|
3199
|
+
return !(constraints.cancel === true && !cap.cancel);
|
|
3200
|
+
}
|
|
3201
|
+
function rateOk(svcMap, candidate, primary, policy) {
|
|
3202
|
+
var _a, _b, _c;
|
|
3203
|
+
const cand = svcMap[Number(candidate)];
|
|
3204
|
+
const prim = svcMap[Number(primary)];
|
|
3205
|
+
if (!cand || !prim) return false;
|
|
3206
|
+
const cRate = toFiniteNumber(cand.rate);
|
|
3207
|
+
const pRate = toFiniteNumber(prim.rate);
|
|
3208
|
+
if (!Number.isFinite(cRate) || !Number.isFinite(pRate)) return false;
|
|
3209
|
+
const rp = (_a = policy.ratePolicy) != null ? _a : { kind: "lte_primary" };
|
|
3210
|
+
switch (rp.kind) {
|
|
3211
|
+
case "lte_primary":
|
|
3212
|
+
return cRate <= pRate;
|
|
3213
|
+
case "within_pct": {
|
|
3214
|
+
const pct = Math.max(0, (_b = rp.pct) != null ? _b : 0);
|
|
3215
|
+
return cRate <= pRate * (1 + pct / 100);
|
|
3216
|
+
}
|
|
3217
|
+
case "at_least_pct_lower": {
|
|
3218
|
+
const pct = Math.max(0, (_c = rp.pct) != null ? _c : 0);
|
|
3219
|
+
return cRate <= pRate * (1 - pct / 100);
|
|
3220
|
+
}
|
|
3221
|
+
default:
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
// src/utils/build-order-snapshot.ts
|
|
3227
|
+
function buildOrderSnapshot(props, builder, selection, services, settings = {}) {
|
|
3228
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3229
|
+
const mode = (_a = settings.mode) != null ? _a : "prod";
|
|
3230
|
+
const hostDefaultQty = Number.isFinite(
|
|
3231
|
+
(_b = settings.hostDefaultQuantity) != null ? _b : 1
|
|
3232
|
+
) ? settings.hostDefaultQuantity : 1;
|
|
3233
|
+
const fbSettings = {
|
|
3234
|
+
requireConstraintFit: true,
|
|
3235
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3236
|
+
selectionStrategy: "priority",
|
|
3237
|
+
mode: mode === "dev" ? "dev" : "strict",
|
|
3238
|
+
...(_c = settings.fallback) != null ? _c : {}
|
|
3239
|
+
};
|
|
3240
|
+
const builtAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3241
|
+
const tagId = selection.activeTagId;
|
|
3242
|
+
const selectedButtonKeys = (_d = selection.selectedKeys) != null ? _d : toSelectedOptionKeys(selection.optionSelectionsByFieldId);
|
|
3243
|
+
const visibleFieldIds = builder.visibleFields(
|
|
3244
|
+
tagId,
|
|
3245
|
+
selectedButtonKeys
|
|
3246
|
+
);
|
|
3247
|
+
const tagById = new Map(
|
|
3248
|
+
((_e = props.filters) != null ? _e : []).map((t) => [t.id, t])
|
|
3249
|
+
);
|
|
3250
|
+
const fieldById = new Map(
|
|
3251
|
+
((_f = props.fields) != null ? _f : []).map((f) => [f.id, f])
|
|
3252
|
+
);
|
|
3253
|
+
const tagConstraints = (_h = (_g = tagById.get(tagId)) == null ? void 0 : _g.constraints) != null ? _h : void 0;
|
|
3254
|
+
const selectionFields = visibleFieldIds.map((fid) => fieldById.get(fid)).filter((f) => !!f).map((f) => {
|
|
3255
|
+
var _a2;
|
|
3256
|
+
const optIds = isOptionBased(f) ? (_a2 = selection.optionSelectionsByFieldId[f.id]) != null ? _a2 : [] : void 0;
|
|
3257
|
+
return {
|
|
3258
|
+
id: f.id,
|
|
3259
|
+
type: String(f.type),
|
|
3260
|
+
...optIds && optIds.length ? { selectedOptions: optIds } : {}
|
|
3261
|
+
};
|
|
3262
|
+
});
|
|
3263
|
+
const { formValues, selections } = buildInputs(
|
|
3264
|
+
visibleFieldIds,
|
|
3265
|
+
fieldById,
|
|
3266
|
+
selection
|
|
3267
|
+
);
|
|
3268
|
+
const qtyRes = resolveQuantity(
|
|
3269
|
+
visibleFieldIds,
|
|
3270
|
+
fieldById,
|
|
3271
|
+
selection,
|
|
3272
|
+
hostDefaultQty
|
|
3273
|
+
);
|
|
3274
|
+
const quantity = qtyRes.quantity;
|
|
3275
|
+
const quantitySource = qtyRes.source;
|
|
3276
|
+
const { serviceMap, servicesList } = resolveServices(
|
|
3277
|
+
tagId,
|
|
3278
|
+
visibleFieldIds,
|
|
3279
|
+
selection,
|
|
3280
|
+
tagById,
|
|
3281
|
+
fieldById
|
|
3282
|
+
);
|
|
3283
|
+
const { min, max } = resolveMinMax(servicesList, services);
|
|
3284
|
+
const prunedFallbacks = pruneFallbacksConservative(
|
|
3285
|
+
props.fallbacks,
|
|
3286
|
+
{ tagId, constraints: tagConstraints, serviceMap, servicesList },
|
|
3287
|
+
services,
|
|
3288
|
+
fbSettings
|
|
3289
|
+
);
|
|
3290
|
+
const utilities = collectUtilityLineItems(
|
|
3291
|
+
visibleFieldIds,
|
|
3292
|
+
fieldById,
|
|
3293
|
+
selection,
|
|
3294
|
+
quantity
|
|
3295
|
+
);
|
|
3296
|
+
const warnings = mode === "dev" ? buildDevWarnings(
|
|
3297
|
+
props,
|
|
3298
|
+
services,
|
|
3299
|
+
tagId,
|
|
3300
|
+
serviceMap,
|
|
3301
|
+
prunedFallbacks.original,
|
|
3302
|
+
prunedFallbacks.pruned,
|
|
3303
|
+
fieldById,
|
|
3304
|
+
visibleFieldIds,
|
|
3305
|
+
selection
|
|
3306
|
+
) : void 0;
|
|
3307
|
+
const snapshotPolicy = toSnapshotPolicy(fbSettings);
|
|
3308
|
+
const meta = {
|
|
3309
|
+
schema_version: props.schema_version,
|
|
3310
|
+
workspaceId: settings.workspaceId,
|
|
3311
|
+
builder: settings.builderCommit ? { commit: settings.builderCommit } : void 0,
|
|
3312
|
+
context: {
|
|
3313
|
+
tag: tagId,
|
|
3314
|
+
constraints: tagConstraints != null ? tagConstraints : {},
|
|
3315
|
+
nodeContexts: buildNodeContexts(
|
|
3316
|
+
tagId,
|
|
3317
|
+
visibleFieldIds,
|
|
3318
|
+
fieldById,
|
|
3319
|
+
selection
|
|
3320
|
+
),
|
|
3321
|
+
policy: snapshotPolicy
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
const snapshot = {
|
|
3325
|
+
version: "1",
|
|
3326
|
+
mode,
|
|
3327
|
+
builtAt,
|
|
3328
|
+
selection: {
|
|
3329
|
+
tag: tagId,
|
|
3330
|
+
buttons: selectedButtonKeys,
|
|
3331
|
+
fields: selectionFields
|
|
3332
|
+
},
|
|
3333
|
+
inputs: {
|
|
3334
|
+
form: formValues,
|
|
3335
|
+
selections
|
|
3336
|
+
},
|
|
3337
|
+
min,
|
|
3338
|
+
max: max != null ? max : min,
|
|
3339
|
+
quantity,
|
|
3340
|
+
quantitySource,
|
|
3341
|
+
services: servicesList,
|
|
3342
|
+
serviceMap,
|
|
3343
|
+
...prunedFallbacks.pruned ? { fallbacks: prunedFallbacks.pruned } : {},
|
|
3344
|
+
...utilities.length ? { utilities } : {},
|
|
3345
|
+
...warnings ? { warnings } : {},
|
|
3346
|
+
meta
|
|
3347
|
+
};
|
|
3348
|
+
return snapshot;
|
|
3349
|
+
}
|
|
3350
|
+
function isOptionBased(f) {
|
|
3351
|
+
const hasOptions = Array.isArray(f.options) && f.options.length > 0;
|
|
3352
|
+
return hasOptions || isMultiField(f);
|
|
3353
|
+
}
|
|
3354
|
+
function toSelectedOptionKeys(byField) {
|
|
3355
|
+
const keys = [];
|
|
3356
|
+
for (const [fieldId, optionIds] of Object.entries(byField != null ? byField : {})) {
|
|
3357
|
+
for (const optId of optionIds != null ? optionIds : []) {
|
|
3358
|
+
keys.push(`${fieldId}::${optId}`);
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
return keys;
|
|
3362
|
+
}
|
|
3363
|
+
function isServicedBased(field) {
|
|
3364
|
+
if (field.service_id) return true;
|
|
3365
|
+
return !!(field.options && field.options.some((item) => item.service_id));
|
|
3366
|
+
}
|
|
3367
|
+
function buildInputs(visibleFieldIds, fieldById, selection) {
|
|
3368
|
+
const formValues = {};
|
|
3369
|
+
const selections = {};
|
|
3370
|
+
for (const fid of visibleFieldIds) {
|
|
3371
|
+
const f = fieldById.get(fid);
|
|
3372
|
+
if (!f) continue;
|
|
3373
|
+
const selOptIds = selection.optionSelectionsByFieldId[fid];
|
|
3374
|
+
if (selOptIds && selOptIds.length) {
|
|
3375
|
+
selections[fid] = [...selOptIds];
|
|
3376
|
+
}
|
|
3377
|
+
if (!isServicedBased(f)) {
|
|
3378
|
+
const name = f.name;
|
|
3379
|
+
const val = selection.formValuesByFieldId[fid];
|
|
3380
|
+
if (!name || val === void 0) continue;
|
|
3381
|
+
formValues[name] = val;
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return { formValues, selections };
|
|
3385
|
+
}
|
|
3386
|
+
function resolveQuantity(visibleFieldIds, fieldById, selection, hostDefault) {
|
|
3387
|
+
var _a;
|
|
3388
|
+
for (const fid of visibleFieldIds) {
|
|
3389
|
+
const f = fieldById.get(fid);
|
|
3390
|
+
if (!f) continue;
|
|
3391
|
+
const rule = readQuantityRule(
|
|
3392
|
+
(_a = f.meta) == null ? void 0 : _a.quantity
|
|
3393
|
+
);
|
|
3394
|
+
if (!rule) continue;
|
|
3395
|
+
const raw = selection.formValuesByFieldId[fid];
|
|
3396
|
+
const evaluated = evaluateQuantityRule(rule, raw);
|
|
3397
|
+
if (Number.isFinite(evaluated) && evaluated > 0) {
|
|
3398
|
+
return {
|
|
3399
|
+
quantity: evaluated,
|
|
3400
|
+
source: { kind: "field", id: f.id, rule }
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
return {
|
|
3405
|
+
quantity: hostDefault,
|
|
3406
|
+
source: { kind: "default", defaultedFromHost: true }
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
function readQuantityRule(v) {
|
|
3410
|
+
if (!v || typeof v !== "object") return void 0;
|
|
3411
|
+
const src = v;
|
|
3412
|
+
if (src.valueBy !== "value" && src.valueBy !== "length" && src.valueBy !== "eval")
|
|
3413
|
+
return void 0;
|
|
3414
|
+
const out = { valueBy: src.valueBy };
|
|
3415
|
+
if (src.code && typeof src.code === "string") out.code = src.code;
|
|
3416
|
+
return out;
|
|
3417
|
+
}
|
|
3418
|
+
function evaluateQuantityRule(rule, raw) {
|
|
3419
|
+
switch (rule.valueBy) {
|
|
3420
|
+
case "value": {
|
|
3421
|
+
const n = Number(Array.isArray(raw) ? raw[0] : raw);
|
|
3422
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3423
|
+
}
|
|
3424
|
+
case "length": {
|
|
3425
|
+
if (Array.isArray(raw)) return raw.length;
|
|
3426
|
+
if (typeof raw === "string") return raw.length;
|
|
3427
|
+
return NaN;
|
|
3428
|
+
}
|
|
3429
|
+
case "eval": {
|
|
3430
|
+
try {
|
|
3431
|
+
if (!rule.code || typeof rule.code !== "string") return NaN;
|
|
3432
|
+
const fn = new Function(
|
|
3433
|
+
"value",
|
|
3434
|
+
"values",
|
|
3435
|
+
`return (function(){ ${rule.code}
|
|
3436
|
+
})()`
|
|
3437
|
+
);
|
|
3438
|
+
const single = Array.isArray(raw) ? raw[0] : raw;
|
|
3439
|
+
const values = Array.isArray(raw) ? raw : raw !== void 0 ? [raw] : [];
|
|
3440
|
+
const out = fn(single, values);
|
|
3441
|
+
const n = Number(out);
|
|
3442
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3443
|
+
} catch {
|
|
3444
|
+
return NaN;
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
default:
|
|
3448
|
+
return NaN;
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
function resolveServices(tagId, visibleFieldIds, selection, tagById, fieldById) {
|
|
3452
|
+
var _a;
|
|
3453
|
+
const serviceMap = {};
|
|
3454
|
+
const ordered = [];
|
|
3455
|
+
const tag = tagById.get(tagId);
|
|
3456
|
+
let primary;
|
|
3457
|
+
let primaryOrigin;
|
|
3458
|
+
if ((tag == null ? void 0 : tag.service_id) !== void 0) {
|
|
3459
|
+
primary = tag.service_id;
|
|
3460
|
+
primaryOrigin = "tag";
|
|
3461
|
+
}
|
|
3462
|
+
const optionVisit = buildOptionVisitOrder(selection, fieldById);
|
|
3463
|
+
for (const { fieldId, optionId } of optionVisit) {
|
|
3464
|
+
if (!visibleFieldIds.includes(fieldId)) continue;
|
|
3465
|
+
const f = fieldById.get(fieldId);
|
|
3466
|
+
if (!f || !Array.isArray(f.options)) continue;
|
|
3467
|
+
const opt = f.options.find((o) => o.id === optionId);
|
|
3468
|
+
if (!opt) continue;
|
|
3469
|
+
const role = (_a = opt.pricing_role) != null ? _a : "base";
|
|
3470
|
+
const sid = opt.service_id;
|
|
3471
|
+
if (role === "utility") continue;
|
|
3472
|
+
if (sid !== void 0) {
|
|
3473
|
+
if (primary === void 0 || primaryOrigin === "tag") {
|
|
3474
|
+
primary = sid;
|
|
3475
|
+
primaryOrigin = "option";
|
|
3476
|
+
ordered.length = 0;
|
|
3477
|
+
ordered.push(primary);
|
|
3478
|
+
} else {
|
|
3479
|
+
ordered.push(sid);
|
|
3480
|
+
}
|
|
3481
|
+
pushService(serviceMap, optionId, sid);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
if (primaryOrigin !== "option" && primary !== void 0) {
|
|
3485
|
+
ordered.unshift(primary);
|
|
3486
|
+
pushService(serviceMap, tagId, primary);
|
|
3487
|
+
} else {
|
|
3488
|
+
}
|
|
3489
|
+
const servicesList = dedupeByString(ordered);
|
|
3490
|
+
return { serviceMap, servicesList };
|
|
3491
|
+
}
|
|
3492
|
+
function buildOptionVisitOrder(selection, fieldById) {
|
|
3493
|
+
var _a;
|
|
3494
|
+
if (selection.optionTraversalOrder && selection.optionTraversalOrder.length) {
|
|
3495
|
+
return selection.optionTraversalOrder.slice();
|
|
3496
|
+
}
|
|
3497
|
+
const out = [];
|
|
3498
|
+
for (const [fid, optIds] of Object.entries(
|
|
3499
|
+
(_a = selection.optionSelectionsByFieldId) != null ? _a : {}
|
|
3500
|
+
)) {
|
|
3501
|
+
const f = fieldById.get(fid);
|
|
3502
|
+
if (!f) continue;
|
|
3503
|
+
for (const oid of optIds != null ? optIds : [])
|
|
3504
|
+
out.push({ fieldId: fid, optionId: oid });
|
|
3505
|
+
}
|
|
3506
|
+
return out;
|
|
3507
|
+
}
|
|
3508
|
+
function pushService(map, nodeId, sid) {
|
|
3509
|
+
if (!map[nodeId]) map[nodeId] = [];
|
|
3510
|
+
map[nodeId].push(sid);
|
|
3511
|
+
}
|
|
3512
|
+
function dedupeByString(arr) {
|
|
3513
|
+
const s = /* @__PURE__ */ new Set();
|
|
3514
|
+
const out = [];
|
|
3515
|
+
for (const v of arr) {
|
|
3516
|
+
const key = String(v);
|
|
3517
|
+
if (s.has(key)) continue;
|
|
3518
|
+
s.add(key);
|
|
3519
|
+
out.push(v);
|
|
3520
|
+
}
|
|
3521
|
+
return out;
|
|
3522
|
+
}
|
|
3523
|
+
function pruneFallbacksConservative(fallbacks, env, svcMap, policy) {
|
|
3524
|
+
var _a, _b;
|
|
3525
|
+
if (!fallbacks) return { pruned: void 0, original: void 0 };
|
|
3526
|
+
try {
|
|
3527
|
+
const { props: prunedProps } = pruneInvalidNodeFallbacks(
|
|
3528
|
+
{
|
|
3529
|
+
filters: [],
|
|
3530
|
+
fields: [],
|
|
3531
|
+
schema_version: "1.0",
|
|
3532
|
+
fallbacks
|
|
3533
|
+
},
|
|
3534
|
+
svcMap,
|
|
3535
|
+
policy
|
|
3536
|
+
);
|
|
3537
|
+
return {
|
|
3538
|
+
pruned: prunedProps.fallbacks,
|
|
3539
|
+
original: fallbacks
|
|
3540
|
+
};
|
|
3541
|
+
} catch {
|
|
3542
|
+
const out = {};
|
|
3543
|
+
const requireFit = (_a = policy.requireConstraintFit) != null ? _a : true;
|
|
3544
|
+
if (fallbacks.nodes) {
|
|
3545
|
+
const keptNodes = {};
|
|
3546
|
+
for (const [nodeId, candidates] of Object.entries(
|
|
3547
|
+
fallbacks.nodes
|
|
3548
|
+
)) {
|
|
3549
|
+
if (!env.serviceMap[nodeId]) continue;
|
|
3550
|
+
const primary = ((_b = env.serviceMap[nodeId]) != null ? _b : [])[0];
|
|
3551
|
+
const kept = [];
|
|
3552
|
+
for (const cand of candidates != null ? candidates : []) {
|
|
3553
|
+
if (!rateOk(svcMap, cand, primary, policy)) continue;
|
|
3554
|
+
if (requireFit && env.constraints && !constraintFitOk(svcMap, cand, env.constraints))
|
|
3555
|
+
continue;
|
|
3556
|
+
kept.push(cand);
|
|
3557
|
+
}
|
|
3558
|
+
if (kept.length) keptNodes[nodeId] = kept;
|
|
3559
|
+
}
|
|
3560
|
+
if (Object.keys(keptNodes).length) out.nodes = keptNodes;
|
|
3561
|
+
}
|
|
3562
|
+
if (fallbacks.global) {
|
|
3563
|
+
const keptGlobal = {};
|
|
3564
|
+
const present = new Set(env.servicesList.map((sid) => String(sid)));
|
|
3565
|
+
for (const [primary, cands] of Object.entries(
|
|
3566
|
+
fallbacks.global
|
|
3567
|
+
)) {
|
|
3568
|
+
if (!present.has(String(primary))) continue;
|
|
3569
|
+
const primId = isFiniteNumber3(primary) ? Number(primary) : primary;
|
|
3570
|
+
const kept = [];
|
|
3571
|
+
for (const cand of cands != null ? cands : []) {
|
|
3572
|
+
if (!rateOk(svcMap, cand, primId, policy)) continue;
|
|
3573
|
+
if (requireFit && env.constraints && !constraintFitOk(svcMap, cand, env.constraints))
|
|
3574
|
+
continue;
|
|
3575
|
+
kept.push(cand);
|
|
3576
|
+
}
|
|
3577
|
+
if (kept.length) keptGlobal[primId] = kept;
|
|
3578
|
+
}
|
|
3579
|
+
if (Object.keys(keptGlobal).length)
|
|
3580
|
+
out.global = keptGlobal;
|
|
3581
|
+
}
|
|
3582
|
+
return {
|
|
3583
|
+
pruned: Object.keys(out).length ? out : void 0,
|
|
3584
|
+
original: fallbacks
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
function isFiniteNumber3(v) {
|
|
3589
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
3590
|
+
}
|
|
3591
|
+
function collectUtilityLineItems(visibleFieldIds, fieldById, selection, quantity) {
|
|
3592
|
+
var _a, _b, _c, _d, _e;
|
|
3593
|
+
const items = [];
|
|
3594
|
+
for (const fid of visibleFieldIds) {
|
|
3595
|
+
const f = fieldById.get(fid);
|
|
3596
|
+
if (!f) continue;
|
|
3597
|
+
const isUtilityField = ((_a = f.pricing_role) != null ? _a : "base") === "utility";
|
|
3598
|
+
const marker = readUtilityMarker((_b = f.meta) == null ? void 0 : _b.utility);
|
|
3599
|
+
if (isUtilityField && marker) {
|
|
3600
|
+
const val = selection.formValuesByFieldId[f.id];
|
|
3601
|
+
const item = buildUtilityItemFromMarker(
|
|
3602
|
+
f.id,
|
|
3603
|
+
marker,
|
|
3604
|
+
quantity,
|
|
3605
|
+
val
|
|
3606
|
+
);
|
|
3607
|
+
if (item) items.push(item);
|
|
3608
|
+
}
|
|
3609
|
+
if (Array.isArray(f.options) && f.options.length) {
|
|
3610
|
+
const selectedOptIds = (_c = selection.optionSelectionsByFieldId[f.id]) != null ? _c : [];
|
|
3611
|
+
if (selectedOptIds.length) {
|
|
3612
|
+
const optById = new Map(
|
|
3613
|
+
f.options.map((o) => [o.id, o])
|
|
3614
|
+
);
|
|
3615
|
+
for (const oid of selectedOptIds) {
|
|
3616
|
+
const opt = optById.get(oid);
|
|
3617
|
+
if (!opt) continue;
|
|
3618
|
+
if (((_d = opt.pricing_role) != null ? _d : "base") !== "utility") continue;
|
|
3619
|
+
const om = readUtilityMarker((_e = opt.meta) == null ? void 0 : _e.utility);
|
|
3620
|
+
if (!om) continue;
|
|
3621
|
+
const parentVal = selection.formValuesByFieldId[f.id];
|
|
3622
|
+
const item = buildUtilityItemFromMarker(
|
|
3623
|
+
opt.id,
|
|
3624
|
+
om,
|
|
3625
|
+
quantity,
|
|
3626
|
+
parentVal
|
|
3627
|
+
);
|
|
3628
|
+
if (item) items.push(item);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
return items;
|
|
3634
|
+
}
|
|
3635
|
+
function readUtilityMarker(v) {
|
|
3636
|
+
if (!v || typeof v !== "object") return void 0;
|
|
3637
|
+
const src = v;
|
|
3638
|
+
if (!src.mode || typeof src.rate !== "number" || !Number.isFinite(src.rate))
|
|
3639
|
+
return void 0;
|
|
3640
|
+
if (src.mode !== "flat" && src.mode !== "per_quantity" && src.mode !== "per_value" && src.mode !== "percent")
|
|
3641
|
+
return void 0;
|
|
3642
|
+
const out = { mode: src.mode, rate: src.rate };
|
|
3643
|
+
if (src.valueBy === "value" || src.valueBy === "length" || src.valueBy === "eval")
|
|
3644
|
+
out.valueBy = src.valueBy;
|
|
3645
|
+
if (src.code && typeof src.code === "string") out.code = src.code;
|
|
3646
|
+
return out;
|
|
3647
|
+
}
|
|
3648
|
+
function buildUtilityItemFromMarker(nodeId, marker, quantity, value) {
|
|
3649
|
+
var _a, _b;
|
|
3650
|
+
const base = {
|
|
3651
|
+
nodeId,
|
|
3652
|
+
mode: marker.mode,
|
|
3653
|
+
rate: marker.rate,
|
|
3654
|
+
inputs: { quantity }
|
|
3655
|
+
};
|
|
3656
|
+
if (marker.mode === "per_value") {
|
|
3657
|
+
base.inputs.valueBy = (_a = marker.valueBy) != null ? _a : "value";
|
|
3658
|
+
if (marker.valueBy === "length") {
|
|
3659
|
+
base.inputs.value = Array.isArray(value) ? value.length : typeof value === "string" ? value.length : 0;
|
|
3660
|
+
} else if (marker.valueBy === "eval") {
|
|
3661
|
+
base.inputs.evalCodeUsed = true;
|
|
3662
|
+
} else {
|
|
3663
|
+
base.inputs.value = Array.isArray(value) ? (_b = value[0]) != null ? _b : null : value != null ? value : null;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
return base;
|
|
3667
|
+
}
|
|
3668
|
+
function buildNodeContexts(tagId, visibleFieldIds, fieldById, selection) {
|
|
3669
|
+
var _a;
|
|
3670
|
+
const ctx = {};
|
|
3671
|
+
ctx[tagId] = tagId;
|
|
3672
|
+
for (const fid of visibleFieldIds) {
|
|
3673
|
+
const f = fieldById.get(fid);
|
|
3674
|
+
if (!f) continue;
|
|
3675
|
+
const binds = normalizeBindIds(f.bind_id);
|
|
3676
|
+
const applicable = binds.has(tagId);
|
|
3677
|
+
const selectedOptIds = (_a = selection.optionSelectionsByFieldId[fid]) != null ? _a : [];
|
|
3678
|
+
for (const oid of selectedOptIds) {
|
|
3679
|
+
ctx[oid] = applicable ? tagId : null;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
return ctx;
|
|
3683
|
+
}
|
|
3684
|
+
function normalizeBindIds(bind) {
|
|
3685
|
+
const out = /* @__PURE__ */ new Set();
|
|
3686
|
+
if (!bind) return out;
|
|
3687
|
+
if (Array.isArray(bind)) {
|
|
3688
|
+
for (const b of bind) if (b) out.add(String(b));
|
|
3689
|
+
} else {
|
|
3690
|
+
out.add(String(bind));
|
|
3691
|
+
}
|
|
3692
|
+
return out;
|
|
3693
|
+
}
|
|
3694
|
+
function buildDevWarnings(props, svcMap, _tagId, _snapshotServiceMap, originalFallbacks, _prunedFallbacks, fieldById, visibleFieldIds, selection) {
|
|
3695
|
+
const out = {};
|
|
3696
|
+
const maybeCollectFailed = globalThis.collectFailedFallbacks;
|
|
3697
|
+
try {
|
|
3698
|
+
if (maybeCollectFailed && originalFallbacks) {
|
|
3699
|
+
const diags = maybeCollectFailed(
|
|
3700
|
+
{
|
|
3701
|
+
...props,
|
|
3702
|
+
fallbacks: originalFallbacks
|
|
3703
|
+
},
|
|
3704
|
+
svcMap,
|
|
3705
|
+
{ mode: "dev" }
|
|
3706
|
+
);
|
|
3707
|
+
if (diags && diags.length) {
|
|
3708
|
+
out.fallbacks = diags;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
} catch {
|
|
3712
|
+
}
|
|
3713
|
+
const utilityWarnings = [];
|
|
3714
|
+
for (const fid of visibleFieldIds) {
|
|
3715
|
+
const f = fieldById.get(fid);
|
|
3716
|
+
if (!f) continue;
|
|
3717
|
+
const hasVal = selection.formValuesByFieldId[fid] !== void 0;
|
|
3718
|
+
if (hasVal && !f.name && !isOptionBased(f)) {
|
|
3719
|
+
utilityWarnings.push({
|
|
3720
|
+
nodeId: fid,
|
|
3721
|
+
reason: "missing_field_name_for_form_value"
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
if (utilityWarnings.length) {
|
|
3726
|
+
out.utility = utilityWarnings;
|
|
3727
|
+
}
|
|
3728
|
+
if (!out.fallbacks && !out.utility) return void 0;
|
|
3729
|
+
return out;
|
|
3730
|
+
}
|
|
3731
|
+
function toSnapshotPolicy(settings) {
|
|
3732
|
+
var _a, _b, _c;
|
|
3733
|
+
const requireConstraintFit = (_a = settings.requireConstraintFit) != null ? _a : true;
|
|
3734
|
+
const rp = (_b = settings.ratePolicy) != null ? _b : { kind: "lte_primary" };
|
|
3735
|
+
switch (rp.kind) {
|
|
3736
|
+
case "lte_primary":
|
|
3737
|
+
return {
|
|
3738
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3739
|
+
requireConstraintFit
|
|
3740
|
+
};
|
|
3741
|
+
case "within_pct":
|
|
3742
|
+
return {
|
|
3743
|
+
ratePolicy: {
|
|
3744
|
+
kind: "lte_primary",
|
|
3745
|
+
thresholdPct: Math.max(0, (_c = rp.pct) != null ? _c : 0)
|
|
3746
|
+
},
|
|
3747
|
+
requireConstraintFit
|
|
3748
|
+
};
|
|
3749
|
+
case "at_least_pct_lower":
|
|
3750
|
+
return {
|
|
3751
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3752
|
+
requireConstraintFit
|
|
3753
|
+
};
|
|
3754
|
+
default:
|
|
3755
|
+
return {
|
|
3756
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3757
|
+
requireConstraintFit
|
|
3758
|
+
};
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
function getCap2(map, id) {
|
|
3762
|
+
const direct = map[id];
|
|
3763
|
+
if (direct) return direct;
|
|
3764
|
+
const strKey = String(id);
|
|
3765
|
+
const byStr = map[strKey];
|
|
3766
|
+
if (byStr) return byStr;
|
|
3767
|
+
const n = typeof id === "number" ? id : typeof id === "string" ? Number(id) : Number.NaN;
|
|
3768
|
+
if (Number.isFinite(n)) {
|
|
3769
|
+
const byNum = map[n];
|
|
3770
|
+
if (byNum) return byNum;
|
|
3771
|
+
}
|
|
3772
|
+
return void 0;
|
|
3773
|
+
}
|
|
3774
|
+
function resolveMinMax(servicesList, services) {
|
|
3775
|
+
let min = void 0;
|
|
3776
|
+
let max = void 0;
|
|
3777
|
+
for (const sid of servicesList) {
|
|
3778
|
+
const cap = getCap2(services, sid);
|
|
3779
|
+
if (!cap) continue;
|
|
3780
|
+
if (typeof cap.min === "number" && Number.isFinite(cap.min)) {
|
|
3781
|
+
min = min === void 0 ? cap.min : Math.min(min, cap.min);
|
|
3782
|
+
}
|
|
3783
|
+
if (typeof cap.max === "number" && Number.isFinite(cap.max)) {
|
|
3784
|
+
max = max === void 0 ? cap.max : Math.max(max, cap.max);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
return { min: min != null ? min : 1, ...max !== void 0 ? { max } : {} };
|
|
3788
|
+
}
|
|
2889
3789
|
export {
|
|
3790
|
+
buildOrderSnapshot,
|
|
2890
3791
|
collectFailedFallbacks,
|
|
2891
3792
|
createBuilder,
|
|
2892
3793
|
createNodeIndex,
|
|
@@ -2894,6 +3795,7 @@ export {
|
|
|
2894
3795
|
normalise,
|
|
2895
3796
|
resolveServiceFallback,
|
|
2896
3797
|
validate,
|
|
3798
|
+
validateAsync,
|
|
2897
3799
|
validateRateCoherenceDeep
|
|
2898
3800
|
};
|
|
2899
3801
|
//# sourceMappingURL=index.js.map
|