@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.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/core/index.ts
|
|
21
21
|
var core_exports = {};
|
|
22
22
|
__export(core_exports, {
|
|
23
|
+
buildOrderSnapshot: () => buildOrderSnapshot,
|
|
23
24
|
collectFailedFallbacks: () => collectFailedFallbacks,
|
|
24
25
|
createBuilder: () => createBuilder,
|
|
25
26
|
createNodeIndex: () => createNodeIndex,
|
|
@@ -27,6 +28,7 @@ __export(core_exports, {
|
|
|
27
28
|
normalise: () => normalise,
|
|
28
29
|
resolveServiceFallback: () => resolveServiceFallback,
|
|
29
30
|
validate: () => validate,
|
|
31
|
+
validateAsync: () => validateAsync,
|
|
30
32
|
validateRateCoherenceDeep: () => validateRateCoherenceDeep
|
|
31
33
|
});
|
|
32
34
|
module.exports = __toCommonJS(core_exports);
|
|
@@ -313,11 +315,6 @@ function hasAnyServiceOption(f) {
|
|
|
313
315
|
var _a;
|
|
314
316
|
return ((_a = f.options) != null ? _a : []).some((o) => isFiniteNumber(o.service_id));
|
|
315
317
|
}
|
|
316
|
-
function isBoundTo(f, tagId) {
|
|
317
|
-
const b = f.bind_id;
|
|
318
|
-
if (!b) return false;
|
|
319
|
-
return Array.isArray(b) ? b.includes(tagId) : b === tagId;
|
|
320
|
-
}
|
|
321
318
|
function getByPath(obj, path) {
|
|
322
319
|
if (!path) return void 0;
|
|
323
320
|
const parts = path.split(".");
|
|
@@ -403,33 +400,214 @@ function withAffected(details, ids) {
|
|
|
403
400
|
return { ...details != null ? details : {}, affectedIds: ids };
|
|
404
401
|
}
|
|
405
402
|
|
|
403
|
+
// src/core/node-map.ts
|
|
404
|
+
function buildNodeMap(props) {
|
|
405
|
+
var _a, _b, _c;
|
|
406
|
+
const map = /* @__PURE__ */ new Map();
|
|
407
|
+
for (const t of (_a = props.filters) != null ? _a : []) {
|
|
408
|
+
if (!map.has(t.id)) map.set(t.id, { kind: "tag", id: t.id, node: t });
|
|
409
|
+
}
|
|
410
|
+
for (const f of (_b = props.fields) != null ? _b : []) {
|
|
411
|
+
if (!map.has(f.id)) map.set(f.id, { kind: "field", id: f.id, node: f });
|
|
412
|
+
for (const o of (_c = f.options) != null ? _c : []) {
|
|
413
|
+
if (!map.has(o.id))
|
|
414
|
+
map.set(o.id, {
|
|
415
|
+
kind: "option",
|
|
416
|
+
id: o.id,
|
|
417
|
+
node: o,
|
|
418
|
+
fieldId: f.id
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return map;
|
|
423
|
+
}
|
|
424
|
+
function resolveTrigger(trigger, nodeMap) {
|
|
425
|
+
const idx = trigger.indexOf("::");
|
|
426
|
+
if (idx !== -1) {
|
|
427
|
+
const fieldId = trigger.slice(0, idx);
|
|
428
|
+
const optionId = trigger.slice(idx + 2);
|
|
429
|
+
return { kind: "composite", triggerKey: trigger, fieldId, optionId };
|
|
430
|
+
}
|
|
431
|
+
const direct = nodeMap.get(trigger);
|
|
432
|
+
if (!direct) return void 0;
|
|
433
|
+
if (direct.kind === "option") {
|
|
434
|
+
return {
|
|
435
|
+
kind: "option",
|
|
436
|
+
triggerKey: trigger,
|
|
437
|
+
id: direct.id,
|
|
438
|
+
fieldId: direct.fieldId
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
return { kind: direct.kind, triggerKey: trigger, id: direct.id };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/core/visibility.ts
|
|
445
|
+
function visibleFieldIdsUnder(props, tagId, opts = {}) {
|
|
446
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
447
|
+
const tags = (_a = props.filters) != null ? _a : [];
|
|
448
|
+
const fields = (_b = props.fields) != null ? _b : [];
|
|
449
|
+
const tagById = new Map(tags.map((t) => [t.id, t]));
|
|
450
|
+
const tag = tagById.get(tagId);
|
|
451
|
+
if (!tag) return [];
|
|
452
|
+
const nodeMap = buildNodeMap(props);
|
|
453
|
+
const lineageDepth = /* @__PURE__ */ new Map();
|
|
454
|
+
{
|
|
455
|
+
const guard = /* @__PURE__ */ new Set();
|
|
456
|
+
let cur = tag;
|
|
457
|
+
let d = 0;
|
|
458
|
+
while (cur && !guard.has(cur.id)) {
|
|
459
|
+
lineageDepth.set(cur.id, d++);
|
|
460
|
+
guard.add(cur.id);
|
|
461
|
+
const parentId = cur.bind_id;
|
|
462
|
+
cur = parentId ? tagById.get(parentId) : void 0;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const isTagInLineage = (id) => lineageDepth.has(id);
|
|
466
|
+
const fieldById = new Map(fields.map((f) => [f.id, f]));
|
|
467
|
+
const ownerDepthForField = (f) => {
|
|
468
|
+
const b = f.bind_id;
|
|
469
|
+
if (!b) return void 0;
|
|
470
|
+
if (typeof b === "string") return lineageDepth.get(b);
|
|
471
|
+
let best = void 0;
|
|
472
|
+
for (const id of b) {
|
|
473
|
+
const d = lineageDepth.get(id);
|
|
474
|
+
if (d == null) continue;
|
|
475
|
+
if (best == null || d < best) best = d;
|
|
476
|
+
}
|
|
477
|
+
return best;
|
|
478
|
+
};
|
|
479
|
+
const ownerDepthForTriggerKey = (triggerKey) => {
|
|
480
|
+
const t = resolveTrigger(triggerKey, nodeMap);
|
|
481
|
+
if (!t) return void 0;
|
|
482
|
+
if (t.kind === "composite") {
|
|
483
|
+
const f = fieldById.get(t.fieldId);
|
|
484
|
+
if (!f) return void 0;
|
|
485
|
+
return ownerDepthForField(f);
|
|
486
|
+
}
|
|
487
|
+
if (t.kind === "field") {
|
|
488
|
+
const f = fieldById.get(t.id);
|
|
489
|
+
if (!f || f.button !== true) return void 0;
|
|
490
|
+
return ownerDepthForField(f);
|
|
491
|
+
}
|
|
492
|
+
if (t.kind === "option") {
|
|
493
|
+
const f = t.fieldId ? fieldById.get(t.fieldId) : void 0;
|
|
494
|
+
if (!f) return void 0;
|
|
495
|
+
return ownerDepthForField(f);
|
|
496
|
+
}
|
|
497
|
+
return void 0;
|
|
498
|
+
};
|
|
499
|
+
const tagInclude = new Set((_c = tag.includes) != null ? _c : []);
|
|
500
|
+
const tagExclude = new Set((_d = tag.excludes) != null ? _d : []);
|
|
501
|
+
const selected = (_e = opts.selectedKeys) != null ? _e : /* @__PURE__ */ new Set();
|
|
502
|
+
const incMap = (_f = props.includes_for_buttons) != null ? _f : {};
|
|
503
|
+
const excMap = (_g = props.excludes_for_buttons) != null ? _g : {};
|
|
504
|
+
const relevantTriggersInOrder = [];
|
|
505
|
+
for (const key of selected) {
|
|
506
|
+
const d = ownerDepthForTriggerKey(key);
|
|
507
|
+
if (d == null) continue;
|
|
508
|
+
relevantTriggersInOrder.push(key);
|
|
509
|
+
}
|
|
510
|
+
const visible = /* @__PURE__ */ new Set();
|
|
511
|
+
const isBoundToLineage = (f) => {
|
|
512
|
+
const b = f.bind_id;
|
|
513
|
+
if (!b) return false;
|
|
514
|
+
if (typeof b === "string") return isTagInLineage(b);
|
|
515
|
+
for (const id of b) if (isTagInLineage(id)) return true;
|
|
516
|
+
return false;
|
|
517
|
+
};
|
|
518
|
+
for (const f of fields) {
|
|
519
|
+
if (isBoundToLineage(f)) visible.add(f.id);
|
|
520
|
+
if (tagInclude.has(f.id)) visible.add(f.id);
|
|
521
|
+
}
|
|
522
|
+
for (const id of tagExclude) visible.delete(id);
|
|
523
|
+
const decide = /* @__PURE__ */ new Map();
|
|
524
|
+
const applyDecision = (fieldId, next) => {
|
|
525
|
+
const prev = decide.get(fieldId);
|
|
526
|
+
if (!prev) return void decide.set(fieldId, next);
|
|
527
|
+
if (next.depth < prev.depth) return void decide.set(fieldId, next);
|
|
528
|
+
if (next.depth > prev.depth) return;
|
|
529
|
+
if (prev.kind === "include" && next.kind === "exclude") {
|
|
530
|
+
decide.set(fieldId, next);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
const revealedOrder = [];
|
|
534
|
+
const revealedSeen = /* @__PURE__ */ new Set();
|
|
535
|
+
for (const triggerKey of relevantTriggersInOrder) {
|
|
536
|
+
const depth = ownerDepthForTriggerKey(triggerKey);
|
|
537
|
+
if (depth == null) continue;
|
|
538
|
+
for (const id of (_h = incMap[triggerKey]) != null ? _h : []) {
|
|
539
|
+
applyDecision(id, { depth, kind: "include" });
|
|
540
|
+
if (!revealedSeen.has(id)) {
|
|
541
|
+
revealedSeen.add(id);
|
|
542
|
+
revealedOrder.push(id);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
for (const id of (_i = excMap[triggerKey]) != null ? _i : []) {
|
|
546
|
+
applyDecision(id, { depth, kind: "exclude" });
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
for (const [fid, d] of decide) {
|
|
550
|
+
if (d.kind === "include") visible.add(fid);
|
|
551
|
+
else visible.delete(fid);
|
|
552
|
+
}
|
|
553
|
+
const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
|
|
554
|
+
const order = (_j = props.order_for_tags) == null ? void 0 : _j[tagId];
|
|
555
|
+
if (order && order.length) {
|
|
556
|
+
const ordered = order.filter((fid) => visible.has(fid));
|
|
557
|
+
const orderedSet = new Set(ordered);
|
|
558
|
+
const rest = base.filter((fid) => !orderedSet.has(fid));
|
|
559
|
+
return [...ordered, ...rest];
|
|
560
|
+
}
|
|
561
|
+
return base;
|
|
562
|
+
}
|
|
563
|
+
function visibleFieldsUnder(props, tagId, opts = {}) {
|
|
564
|
+
var _a;
|
|
565
|
+
const ids = visibleFieldIdsUnder(props, tagId, opts);
|
|
566
|
+
const fieldById = new Map(((_a = props.fields) != null ? _a : []).map((f) => [f.id, f]));
|
|
567
|
+
return ids.map((id) => fieldById.get(id)).filter(Boolean);
|
|
568
|
+
}
|
|
569
|
+
|
|
406
570
|
// src/core/validate/steps/visibility.ts
|
|
407
571
|
function createFieldsVisibleUnder(v) {
|
|
408
572
|
return (tagId) => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const excludesTag = new Set((_b = tag == null ? void 0 : tag.excludes) != null ? _b : []);
|
|
413
|
-
const incForOpt = (_c = v.props.includes_for_buttons) != null ? _c : {};
|
|
414
|
-
const excForOpt = (_d = v.props.excludes_for_buttons) != null ? _d : {};
|
|
415
|
-
const includesOpt = /* @__PURE__ */ new Set();
|
|
416
|
-
const excludesOpt = /* @__PURE__ */ new Set();
|
|
417
|
-
for (const key of v.selectedKeys) {
|
|
418
|
-
for (const id of (_e = incForOpt[key]) != null ? _e : []) includesOpt.add(id);
|
|
419
|
-
for (const id of (_f = excForOpt[key]) != null ? _f : []) excludesOpt.add(id);
|
|
420
|
-
}
|
|
421
|
-
const merged = /* @__PURE__ */ new Map();
|
|
422
|
-
for (const f of v.fields) {
|
|
423
|
-
if (isBoundTo(f, tagId)) merged.set(f.id, f);
|
|
424
|
-
if (includesTag.has(f.id)) merged.set(f.id, f);
|
|
425
|
-
if (includesOpt.has(f.id)) merged.set(f.id, f);
|
|
426
|
-
}
|
|
427
|
-
for (const id of excludesTag) merged.delete(id);
|
|
428
|
-
for (const id of excludesOpt) merged.delete(id);
|
|
429
|
-
return Array.from(merged.values());
|
|
573
|
+
return visibleFieldsUnder(v.props, tagId, {
|
|
574
|
+
selectedKeys: v.selectedKeys
|
|
575
|
+
});
|
|
430
576
|
};
|
|
431
577
|
}
|
|
432
|
-
function
|
|
578
|
+
function stableKeyOfSelection(keys) {
|
|
579
|
+
return Array.from(keys).sort().join("|");
|
|
580
|
+
}
|
|
581
|
+
function resolveRootTags(tags) {
|
|
582
|
+
const roots = tags.filter((t) => !t.bind_id);
|
|
583
|
+
return roots.length ? roots : tags.slice(0, 1);
|
|
584
|
+
}
|
|
585
|
+
function isEffectfulTrigger(v, trigger) {
|
|
586
|
+
var _a, _b, _c, _d, _e, _f;
|
|
587
|
+
const inc = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
588
|
+
const exc = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
589
|
+
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;
|
|
590
|
+
}
|
|
591
|
+
function collectSelectableTriggersInContext(v, tagId, selectedKeys, onlyEffectful) {
|
|
592
|
+
var _a;
|
|
593
|
+
const visible = visibleFieldsUnder(v.props, tagId, {
|
|
594
|
+
selectedKeys
|
|
595
|
+
});
|
|
596
|
+
const triggers = [];
|
|
597
|
+
for (const f of visible) {
|
|
598
|
+
if (f.button === true) {
|
|
599
|
+
const t = f.id;
|
|
600
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
601
|
+
}
|
|
602
|
+
for (const o of (_a = f.options) != null ? _a : []) {
|
|
603
|
+
const t = `${f.id}::${o.id}`;
|
|
604
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
triggers.sort();
|
|
608
|
+
return triggers;
|
|
609
|
+
}
|
|
610
|
+
function runVisibilityRulesOnce(v) {
|
|
433
611
|
var _a, _b, _c, _d, _e;
|
|
434
612
|
for (const t of v.tags) {
|
|
435
613
|
const visible = v.fieldsVisibleUnder(t.id);
|
|
@@ -504,12 +682,85 @@ function validateVisibility(v) {
|
|
|
504
682
|
}
|
|
505
683
|
}
|
|
506
684
|
}
|
|
685
|
+
function dedupeErrorsInPlace(v, startIndex) {
|
|
686
|
+
const seen = /* @__PURE__ */ new Set();
|
|
687
|
+
const keyOfErr = (e) => {
|
|
688
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
689
|
+
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 : "";
|
|
690
|
+
const other = (_g = (_f = e == null ? void 0 : e.details) == null ? void 0 : _f.other) != null ? _g : "";
|
|
691
|
+
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 : ""}`;
|
|
692
|
+
};
|
|
693
|
+
const kept = [];
|
|
694
|
+
for (let i = startIndex; i < v.errors.length; i++) {
|
|
695
|
+
const e = v.errors[i];
|
|
696
|
+
const k = keyOfErr(e);
|
|
697
|
+
if (seen.has(k)) continue;
|
|
698
|
+
seen.add(k);
|
|
699
|
+
kept.push(e);
|
|
700
|
+
}
|
|
701
|
+
v.errors.splice(startIndex, v.errors.length - startIndex, ...kept);
|
|
702
|
+
}
|
|
703
|
+
function validateVisibility(v, options = {}) {
|
|
704
|
+
var _a, _b, _c;
|
|
705
|
+
const simulate = options.simulate === true;
|
|
706
|
+
if (!simulate) {
|
|
707
|
+
runVisibilityRulesOnce(v);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const maxStates = Math.max(1, (_a = options.maxStates) != null ? _a : 500);
|
|
711
|
+
const maxDepth = Math.max(0, (_b = options.maxDepth) != null ? _b : 6);
|
|
712
|
+
const onlyEffectful = options.onlyEffectfulTriggers !== false;
|
|
713
|
+
const roots = resolveRootTags(v.tags);
|
|
714
|
+
const rootTags = options.simulateAllRoots ? roots : roots.slice(0, 1);
|
|
715
|
+
const originalSelected = new Set((_c = v.selectedKeys) != null ? _c : []);
|
|
716
|
+
const errorsStart = v.errors.length;
|
|
717
|
+
const visited = /* @__PURE__ */ new Set();
|
|
718
|
+
const stack = [];
|
|
719
|
+
for (const rt of rootTags) {
|
|
720
|
+
stack.push({
|
|
721
|
+
rootTagId: rt.id,
|
|
722
|
+
selected: new Set(originalSelected),
|
|
723
|
+
depth: 0
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
let validatedStates = 0;
|
|
727
|
+
while (stack.length) {
|
|
728
|
+
if (validatedStates >= maxStates) break;
|
|
729
|
+
const state = stack.pop();
|
|
730
|
+
const sig = stableKeyOfSelection(state.selected);
|
|
731
|
+
if (visited.has(sig)) continue;
|
|
732
|
+
visited.add(sig);
|
|
733
|
+
v.selectedKeys = state.selected;
|
|
734
|
+
validatedStates++;
|
|
735
|
+
runVisibilityRulesOnce(v);
|
|
736
|
+
if (state.depth >= maxDepth) continue;
|
|
737
|
+
const triggers = collectSelectableTriggersInContext(
|
|
738
|
+
v,
|
|
739
|
+
state.rootTagId,
|
|
740
|
+
state.selected,
|
|
741
|
+
onlyEffectful
|
|
742
|
+
);
|
|
743
|
+
for (let i = triggers.length - 1; i >= 0; i--) {
|
|
744
|
+
const trig = triggers[i];
|
|
745
|
+
if (state.selected.has(trig)) continue;
|
|
746
|
+
const next = new Set(state.selected);
|
|
747
|
+
next.add(trig);
|
|
748
|
+
stack.push({
|
|
749
|
+
rootTagId: state.rootTagId,
|
|
750
|
+
selected: next,
|
|
751
|
+
depth: state.depth + 1
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
v.selectedKeys = originalSelected;
|
|
756
|
+
dedupeErrorsInPlace(v, errorsStart);
|
|
757
|
+
}
|
|
507
758
|
|
|
508
759
|
// src/core/validate/steps/structure.ts
|
|
509
760
|
function validateStructure(v) {
|
|
510
761
|
const tags = v.tags;
|
|
511
762
|
const fields = v.fields;
|
|
512
|
-
if (!tags.some((t) => t.id === "root")) {
|
|
763
|
+
if (!tags.some((t) => t.id === "t:root")) {
|
|
513
764
|
v.errors.push({
|
|
514
765
|
code: "root_missing",
|
|
515
766
|
severity: "error",
|
|
@@ -699,58 +950,91 @@ function validateIdentity(v) {
|
|
|
699
950
|
}
|
|
700
951
|
|
|
701
952
|
// src/core/validate/steps/option-maps.ts
|
|
953
|
+
function parseFieldOptionKey(key) {
|
|
954
|
+
const idx = key.indexOf("::");
|
|
955
|
+
if (idx === -1) return null;
|
|
956
|
+
const fieldId = key.slice(0, idx).trim();
|
|
957
|
+
const optionId = key.slice(idx + 2).trim();
|
|
958
|
+
if (!fieldId || !optionId) return null;
|
|
959
|
+
return { fieldId, optionId };
|
|
960
|
+
}
|
|
961
|
+
function hasOption(v, fid, oid) {
|
|
962
|
+
var _a;
|
|
963
|
+
const f = v.fieldById.get(fid);
|
|
964
|
+
if (!f) return false;
|
|
965
|
+
return !!((_a = f.options) != null ? _a : []).find((o) => o.id === oid);
|
|
966
|
+
}
|
|
702
967
|
function validateOptionMaps(v) {
|
|
703
968
|
var _a, _b;
|
|
704
969
|
const incMap = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
705
970
|
const excMap = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
971
|
+
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.`;
|
|
972
|
+
const validateTriggerKey = (key) => {
|
|
973
|
+
const ref = v.nodeMap.get(key);
|
|
974
|
+
if (ref) {
|
|
975
|
+
if (ref.kind === "option") {
|
|
976
|
+
return {
|
|
977
|
+
ok: true,
|
|
978
|
+
nodeId: ref.fieldId,
|
|
979
|
+
affected: [ref.fieldId, ref.id]
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
if (ref.kind === "field") {
|
|
983
|
+
const isButton2 = ref.node.button === true;
|
|
984
|
+
if (!isButton2)
|
|
985
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
986
|
+
return { ok: true, nodeId: ref.id, affected: [ref.id] };
|
|
987
|
+
}
|
|
988
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
989
|
+
}
|
|
990
|
+
const p = parseFieldOptionKey(key);
|
|
991
|
+
if (!p) return { ok: false };
|
|
992
|
+
if (!hasOption(v, p.fieldId, p.optionId))
|
|
993
|
+
return {
|
|
994
|
+
ok: false,
|
|
995
|
+
nodeId: p.fieldId,
|
|
996
|
+
affected: [p.fieldId, p.optionId]
|
|
997
|
+
};
|
|
998
|
+
return {
|
|
999
|
+
ok: true,
|
|
1000
|
+
nodeId: p.fieldId,
|
|
1001
|
+
affected: [p.fieldId, p.optionId]
|
|
1002
|
+
};
|
|
718
1003
|
};
|
|
719
|
-
const badKeyMessage = (key) => `Invalid option-map key "${key}". Expected "fieldId::optionId" pointing to an existing option.`;
|
|
720
1004
|
for (const k of Object.keys(incMap)) {
|
|
721
|
-
const
|
|
722
|
-
if (!
|
|
1005
|
+
const r = validateTriggerKey(k);
|
|
1006
|
+
if (!r.ok) {
|
|
723
1007
|
v.errors.push({
|
|
724
1008
|
code: "bad_option_key",
|
|
725
1009
|
severity: "error",
|
|
726
1010
|
message: badKeyMessage(k),
|
|
727
|
-
|
|
1011
|
+
nodeId: r.nodeId,
|
|
1012
|
+
details: withAffected({ key: k }, r.affected)
|
|
728
1013
|
});
|
|
729
1014
|
}
|
|
730
1015
|
}
|
|
731
1016
|
for (const k of Object.keys(excMap)) {
|
|
732
|
-
const
|
|
733
|
-
if (!
|
|
1017
|
+
const r = validateTriggerKey(k);
|
|
1018
|
+
if (!r.ok) {
|
|
734
1019
|
v.errors.push({
|
|
735
1020
|
code: "bad_option_key",
|
|
736
1021
|
severity: "error",
|
|
737
1022
|
message: badKeyMessage(k),
|
|
738
|
-
|
|
1023
|
+
nodeId: r.nodeId,
|
|
1024
|
+
details: withAffected({ key: k }, r.affected)
|
|
739
1025
|
});
|
|
740
1026
|
}
|
|
741
1027
|
}
|
|
742
1028
|
for (const k of Object.keys(incMap)) {
|
|
743
|
-
if (k in excMap)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
});
|
|
753
|
-
}
|
|
1029
|
+
if (!(k in excMap)) continue;
|
|
1030
|
+
const r = validateTriggerKey(k);
|
|
1031
|
+
v.errors.push({
|
|
1032
|
+
code: "option_include_exclude_conflict",
|
|
1033
|
+
severity: "error",
|
|
1034
|
+
message: `Trigger-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
|
|
1035
|
+
nodeId: r.nodeId,
|
|
1036
|
+
details: withAffected({ key: k }, r.affected)
|
|
1037
|
+
});
|
|
754
1038
|
}
|
|
755
1039
|
}
|
|
756
1040
|
|
|
@@ -1655,8 +1939,23 @@ function applyPolicies(errors, props, serviceMap, policies, fieldsVisibleUnder,
|
|
|
1655
1939
|
}
|
|
1656
1940
|
|
|
1657
1941
|
// src/core/validate/index.ts
|
|
1942
|
+
function readVisibilitySimOpts(ctx) {
|
|
1943
|
+
const c = ctx;
|
|
1944
|
+
const simulate = c.simulateVisibility === true || c.visibilitySimulate === true || c.simulate === true;
|
|
1945
|
+
const maxStates = typeof c.maxVisibilityStates === "number" ? c.maxVisibilityStates : typeof c.visibilityMaxStates === "number" ? c.visibilityMaxStates : typeof c.maxStates === "number" ? c.maxStates : void 0;
|
|
1946
|
+
const maxDepth = typeof c.maxVisibilityDepth === "number" ? c.maxVisibilityDepth : typeof c.visibilityMaxDepth === "number" ? c.visibilityMaxDepth : typeof c.maxDepth === "number" ? c.maxDepth : void 0;
|
|
1947
|
+
const simulateAllRoots = c.simulateAllRoots === true || c.visibilitySimulateAllRoots === true;
|
|
1948
|
+
const onlyEffectfulTriggers = c.onlyEffectfulTriggers === false ? false : c.visibilityOnlyEffectfulTriggers !== false;
|
|
1949
|
+
return {
|
|
1950
|
+
simulate,
|
|
1951
|
+
maxStates,
|
|
1952
|
+
maxDepth,
|
|
1953
|
+
simulateAllRoots,
|
|
1954
|
+
onlyEffectfulTriggers
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1658
1957
|
function validate(props, ctx = {}) {
|
|
1659
|
-
var _a, _b;
|
|
1958
|
+
var _a, _b, _c;
|
|
1660
1959
|
const errors = [];
|
|
1661
1960
|
const serviceMap = (_a = ctx.serviceMap) != null ? _a : {};
|
|
1662
1961
|
const selectedKeys = new Set(
|
|
@@ -1670,6 +1969,7 @@ function validate(props, ctx = {}) {
|
|
|
1670
1969
|
for (const f of fields) fieldById.set(f.id, f);
|
|
1671
1970
|
const v = {
|
|
1672
1971
|
props,
|
|
1972
|
+
nodeMap: (_c = ctx.nodeMap) != null ? _c : buildNodeMap(props),
|
|
1673
1973
|
options: ctx,
|
|
1674
1974
|
errors,
|
|
1675
1975
|
serviceMap,
|
|
@@ -1684,7 +1984,8 @@ function validate(props, ctx = {}) {
|
|
|
1684
1984
|
validateIdentity(v);
|
|
1685
1985
|
validateOptionMaps(v);
|
|
1686
1986
|
v.fieldsVisibleUnder = createFieldsVisibleUnder(v);
|
|
1687
|
-
|
|
1987
|
+
const visSim = readVisibilitySimOpts(ctx);
|
|
1988
|
+
validateVisibility(v, visSim);
|
|
1688
1989
|
applyPolicies(
|
|
1689
1990
|
v.errors,
|
|
1690
1991
|
v.props,
|
|
@@ -1703,6 +2004,17 @@ function validate(props, ctx = {}) {
|
|
|
1703
2004
|
validateFallbacks(v);
|
|
1704
2005
|
return v.errors;
|
|
1705
2006
|
}
|
|
2007
|
+
async function validateAsync(props, ctx = {}) {
|
|
2008
|
+
await Promise.resolve();
|
|
2009
|
+
if (typeof requestAnimationFrame === "function") {
|
|
2010
|
+
await new Promise(
|
|
2011
|
+
(resolve) => requestAnimationFrame(() => resolve())
|
|
2012
|
+
);
|
|
2013
|
+
} else {
|
|
2014
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2015
|
+
}
|
|
2016
|
+
return validate(props, ctx);
|
|
2017
|
+
}
|
|
1706
2018
|
|
|
1707
2019
|
// src/core/builder.ts
|
|
1708
2020
|
function createBuilder(opts = {}) {
|
|
@@ -1720,11 +2032,21 @@ var BuilderImpl = class {
|
|
|
1720
2032
|
this.optionOwnerById = /* @__PURE__ */ new Map();
|
|
1721
2033
|
this.history = [];
|
|
1722
2034
|
this.future = [];
|
|
2035
|
+
this._nodemap = null;
|
|
1723
2036
|
var _a;
|
|
1724
2037
|
this.options = { ...opts };
|
|
1725
2038
|
this.historyLimit = (_a = opts.historyLimit) != null ? _a : 50;
|
|
1726
2039
|
}
|
|
1727
2040
|
/* ───── lifecycle ─────────────────────────────────────────────────────── */
|
|
2041
|
+
isTagId(id) {
|
|
2042
|
+
return this.tagById.has(id);
|
|
2043
|
+
}
|
|
2044
|
+
isFieldId(id) {
|
|
2045
|
+
return this.fieldById.has(id);
|
|
2046
|
+
}
|
|
2047
|
+
isOptionId(id) {
|
|
2048
|
+
return this.optionOwnerById.has(id);
|
|
2049
|
+
}
|
|
1728
2050
|
load(raw) {
|
|
1729
2051
|
const next = normalise(raw, {
|
|
1730
2052
|
defaultPricingRole: "base",
|
|
@@ -1944,129 +2266,16 @@ var BuilderImpl = class {
|
|
|
1944
2266
|
return validate(this.props, this.options);
|
|
1945
2267
|
}
|
|
1946
2268
|
visibleFields(tagId, selectedKeys) {
|
|
1947
|
-
var _a
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
let cur = tag;
|
|
1958
|
-
let d = 0;
|
|
1959
|
-
while (cur && !guard.has(cur.id)) {
|
|
1960
|
-
lineageDepth.set(cur.id, d++);
|
|
1961
|
-
guard.add(cur.id);
|
|
1962
|
-
const parentId = cur.bind_id;
|
|
1963
|
-
cur = parentId ? tagById.get(parentId) : void 0;
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
const isTagInLineage = (id) => lineageDepth.has(id);
|
|
1967
|
-
const fieldById = new Map(fields.map((f) => [f.id, f]));
|
|
1968
|
-
const optionOwnerFieldId = /* @__PURE__ */ new Map();
|
|
1969
|
-
for (const f of fields) {
|
|
1970
|
-
for (const o of (_c = f.options) != null ? _c : []) optionOwnerFieldId.set(o.id, f.id);
|
|
1971
|
-
}
|
|
1972
|
-
const ownerDepthForField = (f) => {
|
|
1973
|
-
const b = f.bind_id;
|
|
1974
|
-
if (!b) return void 0;
|
|
1975
|
-
if (typeof b === "string") return lineageDepth.get(b);
|
|
1976
|
-
let best = void 0;
|
|
1977
|
-
for (const id of b) {
|
|
1978
|
-
const d = lineageDepth.get(id);
|
|
1979
|
-
if (d == null) continue;
|
|
1980
|
-
if (best == null || d < best) best = d;
|
|
1981
|
-
}
|
|
1982
|
-
return best;
|
|
1983
|
-
};
|
|
1984
|
-
const ownerDepthForTrigger = (triggerId) => {
|
|
1985
|
-
if (triggerId.startsWith("o:")) {
|
|
1986
|
-
const fid = optionOwnerFieldId.get(triggerId);
|
|
1987
|
-
if (!fid) return void 0;
|
|
1988
|
-
const f2 = fieldById.get(fid);
|
|
1989
|
-
if (!f2) return void 0;
|
|
1990
|
-
return ownerDepthForField(f2);
|
|
1991
|
-
}
|
|
1992
|
-
const f = fieldById.get(triggerId);
|
|
1993
|
-
if (!f || f.button !== true) return void 0;
|
|
1994
|
-
return ownerDepthForField(f);
|
|
1995
|
-
};
|
|
1996
|
-
const tagInclude = new Set((_d = tag.includes) != null ? _d : []);
|
|
1997
|
-
const tagExclude = new Set((_e = tag.excludes) != null ? _e : []);
|
|
1998
|
-
const selected = new Set(
|
|
1999
|
-
(_f = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _f : []
|
|
2000
|
-
);
|
|
2001
|
-
const incMap = (_g = props.includes_for_buttons) != null ? _g : {};
|
|
2002
|
-
const excMap = (_h = props.excludes_for_buttons) != null ? _h : {};
|
|
2003
|
-
const relevantTriggersInOrder = [];
|
|
2004
|
-
for (const key of selected) {
|
|
2005
|
-
const d = ownerDepthForTrigger(key);
|
|
2006
|
-
if (d == null) continue;
|
|
2007
|
-
relevantTriggersInOrder.push(key);
|
|
2008
|
-
}
|
|
2009
|
-
const visible = /* @__PURE__ */ new Set();
|
|
2010
|
-
const isBoundToLineage = (f) => {
|
|
2011
|
-
const b = f.bind_id;
|
|
2012
|
-
if (!b) return false;
|
|
2013
|
-
if (typeof b === "string") return isTagInLineage(b);
|
|
2014
|
-
for (const id of b) if (isTagInLineage(id)) return true;
|
|
2015
|
-
return false;
|
|
2016
|
-
};
|
|
2017
|
-
for (const f of fields) {
|
|
2018
|
-
if (isBoundToLineage(f)) visible.add(f.id);
|
|
2019
|
-
if (tagInclude.has(f.id)) visible.add(f.id);
|
|
2020
|
-
}
|
|
2021
|
-
for (const id of tagExclude) visible.delete(id);
|
|
2022
|
-
const decide = /* @__PURE__ */ new Map();
|
|
2023
|
-
const applyDecision = (fieldId, next) => {
|
|
2024
|
-
const prev = decide.get(fieldId);
|
|
2025
|
-
if (!prev) {
|
|
2026
|
-
decide.set(fieldId, next);
|
|
2027
|
-
return;
|
|
2028
|
-
}
|
|
2029
|
-
if (next.depth < prev.depth) {
|
|
2030
|
-
decide.set(fieldId, next);
|
|
2031
|
-
return;
|
|
2032
|
-
}
|
|
2033
|
-
if (next.depth > prev.depth) return;
|
|
2034
|
-
if (prev.kind === "include" && next.kind === "exclude") {
|
|
2035
|
-
decide.set(fieldId, next);
|
|
2036
|
-
}
|
|
2037
|
-
};
|
|
2038
|
-
const revealedOrder = [];
|
|
2039
|
-
const revealedSeen = /* @__PURE__ */ new Set();
|
|
2040
|
-
for (const triggerId of relevantTriggersInOrder) {
|
|
2041
|
-
const depth = ownerDepthForTrigger(triggerId);
|
|
2042
|
-
if (depth == null) continue;
|
|
2043
|
-
for (const id of (_i = incMap[triggerId]) != null ? _i : []) {
|
|
2044
|
-
applyDecision(id, { depth, kind: "include" });
|
|
2045
|
-
if (!revealedSeen.has(id)) {
|
|
2046
|
-
revealedSeen.add(id);
|
|
2047
|
-
revealedOrder.push(id);
|
|
2048
|
-
}
|
|
2049
|
-
}
|
|
2050
|
-
for (const id of (_j = excMap[triggerId]) != null ? _j : []) {
|
|
2051
|
-
applyDecision(id, { depth, kind: "exclude" });
|
|
2052
|
-
}
|
|
2053
|
-
}
|
|
2054
|
-
for (const [fid, d] of decide) {
|
|
2055
|
-
if (d.kind === "include") visible.add(fid);
|
|
2056
|
-
else visible.delete(fid);
|
|
2057
|
-
}
|
|
2058
|
-
const base = fields.filter((f) => visible.has(f.id)).map((f) => f.id);
|
|
2059
|
-
const order = (_k = props.order_for_tags) == null ? void 0 : _k[tagId];
|
|
2060
|
-
if (order && order.length) {
|
|
2061
|
-
const ordered = order.filter((fid) => visible.has(fid));
|
|
2062
|
-
const orderedSet = new Set(ordered);
|
|
2063
|
-
const rest2 = base.filter((fid) => !orderedSet.has(fid));
|
|
2064
|
-
return [...ordered, ...rest2];
|
|
2065
|
-
}
|
|
2066
|
-
const promoted = revealedOrder.filter((fid) => visible.has(fid));
|
|
2067
|
-
const promotedSet = new Set(promoted);
|
|
2068
|
-
const rest = base.filter((fid) => !promotedSet.has(fid));
|
|
2069
|
-
return [...promoted, ...rest];
|
|
2269
|
+
var _a;
|
|
2270
|
+
return visibleFieldIdsUnder(this.props, tagId, {
|
|
2271
|
+
selectedKeys: new Set(
|
|
2272
|
+
(_a = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _a : []
|
|
2273
|
+
)
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
getNodeMap() {
|
|
2277
|
+
if (!this._nodemap) this._nodemap = buildNodeMap(this.getProps());
|
|
2278
|
+
return this._nodemap;
|
|
2070
2279
|
}
|
|
2071
2280
|
/* ───── history ─────────────────────────────────────────────────────── */
|
|
2072
2281
|
undo() {
|
|
@@ -2090,6 +2299,7 @@ var BuilderImpl = class {
|
|
|
2090
2299
|
this.tagById.clear();
|
|
2091
2300
|
this.fieldById.clear();
|
|
2092
2301
|
this.optionOwnerById.clear();
|
|
2302
|
+
this._nodemap = null;
|
|
2093
2303
|
for (const t of this.props.filters) this.tagById.set(t.id, t);
|
|
2094
2304
|
for (const f of this.props.fields) {
|
|
2095
2305
|
this.fieldById.set(f.id, f);
|
|
@@ -2571,6 +2781,7 @@ var toBindList = (b) => {
|
|
|
2571
2781
|
function createNodeIndex(builder) {
|
|
2572
2782
|
var _a, _b, _c, _d;
|
|
2573
2783
|
const props = builder.getProps();
|
|
2784
|
+
const nodeMap = builder.getNodeMap();
|
|
2574
2785
|
const tags = (_a = props.filters) != null ? _a : [];
|
|
2575
2786
|
const fields = (_b = props.fields) != null ? _b : [];
|
|
2576
2787
|
const tagById = new Map(tags.map((t) => [t.id, t]));
|
|
@@ -2774,7 +2985,9 @@ function createNodeIndex(builder) {
|
|
|
2774
2985
|
return parentById.get(id) === tid;
|
|
2775
2986
|
},
|
|
2776
2987
|
getDescendant(descendantId) {
|
|
2777
|
-
return this.getDescendants().find(
|
|
2988
|
+
return this.getDescendants().find(
|
|
2989
|
+
(item) => item.id == descendantId
|
|
2990
|
+
);
|
|
2778
2991
|
},
|
|
2779
2992
|
getDescendants() {
|
|
2780
2993
|
const results = [];
|
|
@@ -2783,7 +2996,9 @@ function createNodeIndex(builder) {
|
|
|
2783
2996
|
const node2 = getField(fieldId);
|
|
2784
2997
|
if (!node2) continue;
|
|
2785
2998
|
const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, id);
|
|
2786
|
-
results.push(
|
|
2999
|
+
results.push(
|
|
3000
|
+
explicit ? node2 : { ...node2, isInherited: true }
|
|
3001
|
+
);
|
|
2787
3002
|
}
|
|
2788
3003
|
return Object.freeze(results);
|
|
2789
3004
|
}
|
|
@@ -2832,7 +3047,9 @@ function createNodeIndex(builder) {
|
|
|
2832
3047
|
return false;
|
|
2833
3048
|
},
|
|
2834
3049
|
getDescendant(descendantId, context) {
|
|
2835
|
-
return this.getDescendants(context).find(
|
|
3050
|
+
return this.getDescendants(context).find(
|
|
3051
|
+
(item) => item.id == descendantId
|
|
3052
|
+
);
|
|
2836
3053
|
},
|
|
2837
3054
|
getDescendants(tagId) {
|
|
2838
3055
|
return resolveDescendants(id, includes, tagId, !isButton2);
|
|
@@ -2874,7 +3091,9 @@ function createNodeIndex(builder) {
|
|
|
2874
3091
|
return owner.isBoundTo(tagId);
|
|
2875
3092
|
},
|
|
2876
3093
|
getDescendant(descendantId, context) {
|
|
2877
|
-
return this.getDescendants(context).find(
|
|
3094
|
+
return this.getDescendants(context).find(
|
|
3095
|
+
(item) => item.id == descendantId
|
|
3096
|
+
);
|
|
2878
3097
|
},
|
|
2879
3098
|
getDescendants(tagId) {
|
|
2880
3099
|
return resolveDescendants(id, includes, tagId);
|
|
@@ -2885,7 +3104,7 @@ function createNodeIndex(builder) {
|
|
|
2885
3104
|
return node;
|
|
2886
3105
|
};
|
|
2887
3106
|
const getNode = (input) => {
|
|
2888
|
-
var _a2, _b2, _c2, _d2, _e
|
|
3107
|
+
var _a2, _b2, _c2, _d2, _e;
|
|
2889
3108
|
if (typeof input !== "string") {
|
|
2890
3109
|
if ("bind_id" in input && !("type" in input))
|
|
2891
3110
|
return (_a2 = getTag(input.id)) != null ? _a2 : mkUnknown(input.id);
|
|
@@ -2895,11 +3114,7 @@ function createNodeIndex(builder) {
|
|
|
2895
3114
|
}
|
|
2896
3115
|
const cached = nodeCache.get(input);
|
|
2897
3116
|
if (cached) return cached;
|
|
2898
|
-
|
|
2899
|
-
if (id.startsWith("t:")) return (_d2 = getTag(id)) != null ? _d2 : mkUnknown(id);
|
|
2900
|
-
if (id.startsWith("f:")) return (_e = getField(id)) != null ? _e : mkUnknown(id);
|
|
2901
|
-
if (id.startsWith("o:")) return (_f = getOption(id)) != null ? _f : mkUnknown(id);
|
|
2902
|
-
return (_i = (_h = (_g = getTag(id)) != null ? _g : getField(id)) != null ? _h : getOption(id)) != null ? _i : mkUnknown(id);
|
|
3117
|
+
return (_e = (_d2 = nodeMap.get(input)) == null ? void 0 : _d2.node) != null ? _e : mkUnknown(input);
|
|
2903
3118
|
};
|
|
2904
3119
|
const mkUnknown = (id) => {
|
|
2905
3120
|
const u = {
|
|
@@ -2919,8 +3134,696 @@ function createNodeIndex(builder) {
|
|
|
2919
3134
|
getOption
|
|
2920
3135
|
};
|
|
2921
3136
|
}
|
|
3137
|
+
|
|
3138
|
+
// src/utils/prune-fallbacks.ts
|
|
3139
|
+
function pruneInvalidNodeFallbacks(props, services, settings) {
|
|
3140
|
+
var _a, _b;
|
|
3141
|
+
const fb = props.fallbacks;
|
|
3142
|
+
if (!(fb == null ? void 0 : fb.nodes) || Object.keys(fb.nodes).length === 0) {
|
|
3143
|
+
return { props, removed: [] };
|
|
3144
|
+
}
|
|
3145
|
+
const nodeContexts = /* @__PURE__ */ new Map();
|
|
3146
|
+
const nodePrimary = /* @__PURE__ */ new Map();
|
|
3147
|
+
for (const nodeId of Object.keys(fb.nodes)) {
|
|
3148
|
+
const tag = props.filters.find((t) => t.id === nodeId);
|
|
3149
|
+
if (tag) {
|
|
3150
|
+
nodeContexts.set(nodeId, [tag.id]);
|
|
3151
|
+
nodePrimary.set(nodeId, tag.service_id);
|
|
3152
|
+
continue;
|
|
3153
|
+
}
|
|
3154
|
+
const field = props.fields.find((f) => Array.isArray(f.options) && f.options.some((o) => o.id === nodeId));
|
|
3155
|
+
if (field) {
|
|
3156
|
+
const contexts = toBindArray(field.bind_id);
|
|
3157
|
+
nodeContexts.set(nodeId, contexts);
|
|
3158
|
+
const opt = field.options.find((o) => o.id === nodeId);
|
|
3159
|
+
nodePrimary.set(nodeId, opt.service_id);
|
|
3160
|
+
continue;
|
|
3161
|
+
}
|
|
3162
|
+
nodeContexts.set(nodeId, []);
|
|
3163
|
+
nodePrimary.set(nodeId, void 0);
|
|
3164
|
+
}
|
|
3165
|
+
const diags = collectFailedFallbacks(props, services, { ...settings, mode: "dev" });
|
|
3166
|
+
const failuresByPair = /* @__PURE__ */ new Map();
|
|
3167
|
+
const totalContextsByNode = /* @__PURE__ */ new Map();
|
|
3168
|
+
for (const [nodeId, ctxs] of nodeContexts.entries()) {
|
|
3169
|
+
totalContextsByNode.set(nodeId, Math.max(1, ctxs.length));
|
|
3170
|
+
}
|
|
3171
|
+
for (const d of diags) {
|
|
3172
|
+
if (d.scope !== "node") continue;
|
|
3173
|
+
const key = `${d.nodeId}::${String(d.candidate)}`;
|
|
3174
|
+
let rec = failuresByPair.get(key);
|
|
3175
|
+
if (!rec) {
|
|
3176
|
+
rec = { reasons: /* @__PURE__ */ new Set(), contexts: /* @__PURE__ */ new Set() };
|
|
3177
|
+
failuresByPair.set(key, rec);
|
|
3178
|
+
}
|
|
3179
|
+
rec.reasons.add(d.reason);
|
|
3180
|
+
if (d.tagContext) rec.contexts.add(d.tagContext);
|
|
3181
|
+
}
|
|
3182
|
+
const prunedNodes = {};
|
|
3183
|
+
const removed = [];
|
|
3184
|
+
for (const [nodeId, list] of Object.entries(fb.nodes)) {
|
|
3185
|
+
const contexts = (_a = nodeContexts.get(nodeId)) != null ? _a : [];
|
|
3186
|
+
const totalContexts = Math.max(1, contexts.length);
|
|
3187
|
+
const keep = [];
|
|
3188
|
+
for (const cand of list) {
|
|
3189
|
+
const key = `${nodeId}::${String(cand)}`;
|
|
3190
|
+
const rec = failuresByPair.get(key);
|
|
3191
|
+
if (!rec) {
|
|
3192
|
+
keep.push(cand);
|
|
3193
|
+
continue;
|
|
3194
|
+
}
|
|
3195
|
+
const failedContextsCount = rec.contexts.size > 0 ? rec.contexts.size : totalContexts;
|
|
3196
|
+
const failsAll = failedContextsCount >= totalContexts;
|
|
3197
|
+
if (failsAll) {
|
|
3198
|
+
removed.push({
|
|
3199
|
+
nodeId,
|
|
3200
|
+
candidate: cand,
|
|
3201
|
+
reasons: Array.from(rec.reasons),
|
|
3202
|
+
contexts: contexts.length ? contexts.slice() : void 0
|
|
3203
|
+
});
|
|
3204
|
+
} else {
|
|
3205
|
+
keep.push(cand);
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
if (keep.length) prunedNodes[nodeId] = keep;
|
|
3209
|
+
}
|
|
3210
|
+
const outProps = {
|
|
3211
|
+
...props,
|
|
3212
|
+
fallbacks: {
|
|
3213
|
+
...((_b = props.fallbacks) == null ? void 0 : _b.global) ? { global: props.fallbacks.global } : {},
|
|
3214
|
+
...Object.keys(prunedNodes).length ? { nodes: prunedNodes } : {}
|
|
3215
|
+
}
|
|
3216
|
+
};
|
|
3217
|
+
return { props: outProps, removed };
|
|
3218
|
+
}
|
|
3219
|
+
function toBindArray(bind) {
|
|
3220
|
+
if (!bind) return [];
|
|
3221
|
+
return Array.isArray(bind) ? bind.slice() : [bind];
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
// src/utils/util.ts
|
|
3225
|
+
function toFiniteNumber(v) {
|
|
3226
|
+
const n = Number(v);
|
|
3227
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3228
|
+
}
|
|
3229
|
+
function constraintFitOk(svcMap, candidate, constraints) {
|
|
3230
|
+
const cap = svcMap[Number(candidate)];
|
|
3231
|
+
if (!cap) return false;
|
|
3232
|
+
if (constraints.dripfeed === true && !cap.dripfeed) return false;
|
|
3233
|
+
if (constraints.refill === true && !cap.refill) return false;
|
|
3234
|
+
return !(constraints.cancel === true && !cap.cancel);
|
|
3235
|
+
}
|
|
3236
|
+
function rateOk(svcMap, candidate, primary, policy) {
|
|
3237
|
+
var _a, _b, _c;
|
|
3238
|
+
const cand = svcMap[Number(candidate)];
|
|
3239
|
+
const prim = svcMap[Number(primary)];
|
|
3240
|
+
if (!cand || !prim) return false;
|
|
3241
|
+
const cRate = toFiniteNumber(cand.rate);
|
|
3242
|
+
const pRate = toFiniteNumber(prim.rate);
|
|
3243
|
+
if (!Number.isFinite(cRate) || !Number.isFinite(pRate)) return false;
|
|
3244
|
+
const rp = (_a = policy.ratePolicy) != null ? _a : { kind: "lte_primary" };
|
|
3245
|
+
switch (rp.kind) {
|
|
3246
|
+
case "lte_primary":
|
|
3247
|
+
return cRate <= pRate;
|
|
3248
|
+
case "within_pct": {
|
|
3249
|
+
const pct = Math.max(0, (_b = rp.pct) != null ? _b : 0);
|
|
3250
|
+
return cRate <= pRate * (1 + pct / 100);
|
|
3251
|
+
}
|
|
3252
|
+
case "at_least_pct_lower": {
|
|
3253
|
+
const pct = Math.max(0, (_c = rp.pct) != null ? _c : 0);
|
|
3254
|
+
return cRate <= pRate * (1 - pct / 100);
|
|
3255
|
+
}
|
|
3256
|
+
default:
|
|
3257
|
+
return false;
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
// src/utils/build-order-snapshot.ts
|
|
3262
|
+
function buildOrderSnapshot(props, builder, selection, services, settings = {}) {
|
|
3263
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3264
|
+
const mode = (_a = settings.mode) != null ? _a : "prod";
|
|
3265
|
+
const hostDefaultQty = Number.isFinite(
|
|
3266
|
+
(_b = settings.hostDefaultQuantity) != null ? _b : 1
|
|
3267
|
+
) ? settings.hostDefaultQuantity : 1;
|
|
3268
|
+
const fbSettings = {
|
|
3269
|
+
requireConstraintFit: true,
|
|
3270
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3271
|
+
selectionStrategy: "priority",
|
|
3272
|
+
mode: mode === "dev" ? "dev" : "strict",
|
|
3273
|
+
...(_c = settings.fallback) != null ? _c : {}
|
|
3274
|
+
};
|
|
3275
|
+
const builtAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3276
|
+
const tagId = selection.activeTagId;
|
|
3277
|
+
const selectedButtonKeys = (_d = selection.selectedKeys) != null ? _d : toSelectedOptionKeys(selection.optionSelectionsByFieldId);
|
|
3278
|
+
const visibleFieldIds = builder.visibleFields(
|
|
3279
|
+
tagId,
|
|
3280
|
+
selectedButtonKeys
|
|
3281
|
+
);
|
|
3282
|
+
const tagById = new Map(
|
|
3283
|
+
((_e = props.filters) != null ? _e : []).map((t) => [t.id, t])
|
|
3284
|
+
);
|
|
3285
|
+
const fieldById = new Map(
|
|
3286
|
+
((_f = props.fields) != null ? _f : []).map((f) => [f.id, f])
|
|
3287
|
+
);
|
|
3288
|
+
const tagConstraints = (_h = (_g = tagById.get(tagId)) == null ? void 0 : _g.constraints) != null ? _h : void 0;
|
|
3289
|
+
const selectionFields = visibleFieldIds.map((fid) => fieldById.get(fid)).filter((f) => !!f).map((f) => {
|
|
3290
|
+
var _a2;
|
|
3291
|
+
const optIds = isOptionBased(f) ? (_a2 = selection.optionSelectionsByFieldId[f.id]) != null ? _a2 : [] : void 0;
|
|
3292
|
+
return {
|
|
3293
|
+
id: f.id,
|
|
3294
|
+
type: String(f.type),
|
|
3295
|
+
...optIds && optIds.length ? { selectedOptions: optIds } : {}
|
|
3296
|
+
};
|
|
3297
|
+
});
|
|
3298
|
+
const { formValues, selections } = buildInputs(
|
|
3299
|
+
visibleFieldIds,
|
|
3300
|
+
fieldById,
|
|
3301
|
+
selection
|
|
3302
|
+
);
|
|
3303
|
+
const qtyRes = resolveQuantity(
|
|
3304
|
+
visibleFieldIds,
|
|
3305
|
+
fieldById,
|
|
3306
|
+
selection,
|
|
3307
|
+
hostDefaultQty
|
|
3308
|
+
);
|
|
3309
|
+
const quantity = qtyRes.quantity;
|
|
3310
|
+
const quantitySource = qtyRes.source;
|
|
3311
|
+
const { serviceMap, servicesList } = resolveServices(
|
|
3312
|
+
tagId,
|
|
3313
|
+
visibleFieldIds,
|
|
3314
|
+
selection,
|
|
3315
|
+
tagById,
|
|
3316
|
+
fieldById
|
|
3317
|
+
);
|
|
3318
|
+
const { min, max } = resolveMinMax(servicesList, services);
|
|
3319
|
+
const prunedFallbacks = pruneFallbacksConservative(
|
|
3320
|
+
props.fallbacks,
|
|
3321
|
+
{ tagId, constraints: tagConstraints, serviceMap, servicesList },
|
|
3322
|
+
services,
|
|
3323
|
+
fbSettings
|
|
3324
|
+
);
|
|
3325
|
+
const utilities = collectUtilityLineItems(
|
|
3326
|
+
visibleFieldIds,
|
|
3327
|
+
fieldById,
|
|
3328
|
+
selection,
|
|
3329
|
+
quantity
|
|
3330
|
+
);
|
|
3331
|
+
const warnings = mode === "dev" ? buildDevWarnings(
|
|
3332
|
+
props,
|
|
3333
|
+
services,
|
|
3334
|
+
tagId,
|
|
3335
|
+
serviceMap,
|
|
3336
|
+
prunedFallbacks.original,
|
|
3337
|
+
prunedFallbacks.pruned,
|
|
3338
|
+
fieldById,
|
|
3339
|
+
visibleFieldIds,
|
|
3340
|
+
selection
|
|
3341
|
+
) : void 0;
|
|
3342
|
+
const snapshotPolicy = toSnapshotPolicy(fbSettings);
|
|
3343
|
+
const meta = {
|
|
3344
|
+
schema_version: props.schema_version,
|
|
3345
|
+
workspaceId: settings.workspaceId,
|
|
3346
|
+
builder: settings.builderCommit ? { commit: settings.builderCommit } : void 0,
|
|
3347
|
+
context: {
|
|
3348
|
+
tag: tagId,
|
|
3349
|
+
constraints: tagConstraints != null ? tagConstraints : {},
|
|
3350
|
+
nodeContexts: buildNodeContexts(
|
|
3351
|
+
tagId,
|
|
3352
|
+
visibleFieldIds,
|
|
3353
|
+
fieldById,
|
|
3354
|
+
selection
|
|
3355
|
+
),
|
|
3356
|
+
policy: snapshotPolicy
|
|
3357
|
+
}
|
|
3358
|
+
};
|
|
3359
|
+
const snapshot = {
|
|
3360
|
+
version: "1",
|
|
3361
|
+
mode,
|
|
3362
|
+
builtAt,
|
|
3363
|
+
selection: {
|
|
3364
|
+
tag: tagId,
|
|
3365
|
+
buttons: selectedButtonKeys,
|
|
3366
|
+
fields: selectionFields
|
|
3367
|
+
},
|
|
3368
|
+
inputs: {
|
|
3369
|
+
form: formValues,
|
|
3370
|
+
selections
|
|
3371
|
+
},
|
|
3372
|
+
min,
|
|
3373
|
+
max: max != null ? max : min,
|
|
3374
|
+
quantity,
|
|
3375
|
+
quantitySource,
|
|
3376
|
+
services: servicesList,
|
|
3377
|
+
serviceMap,
|
|
3378
|
+
...prunedFallbacks.pruned ? { fallbacks: prunedFallbacks.pruned } : {},
|
|
3379
|
+
...utilities.length ? { utilities } : {},
|
|
3380
|
+
...warnings ? { warnings } : {},
|
|
3381
|
+
meta
|
|
3382
|
+
};
|
|
3383
|
+
return snapshot;
|
|
3384
|
+
}
|
|
3385
|
+
function isOptionBased(f) {
|
|
3386
|
+
const hasOptions = Array.isArray(f.options) && f.options.length > 0;
|
|
3387
|
+
return hasOptions || isMultiField(f);
|
|
3388
|
+
}
|
|
3389
|
+
function toSelectedOptionKeys(byField) {
|
|
3390
|
+
const keys = [];
|
|
3391
|
+
for (const [fieldId, optionIds] of Object.entries(byField != null ? byField : {})) {
|
|
3392
|
+
for (const optId of optionIds != null ? optionIds : []) {
|
|
3393
|
+
keys.push(`${fieldId}::${optId}`);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
return keys;
|
|
3397
|
+
}
|
|
3398
|
+
function isServicedBased(field) {
|
|
3399
|
+
if (field.service_id) return true;
|
|
3400
|
+
return !!(field.options && field.options.some((item) => item.service_id));
|
|
3401
|
+
}
|
|
3402
|
+
function buildInputs(visibleFieldIds, fieldById, selection) {
|
|
3403
|
+
const formValues = {};
|
|
3404
|
+
const selections = {};
|
|
3405
|
+
for (const fid of visibleFieldIds) {
|
|
3406
|
+
const f = fieldById.get(fid);
|
|
3407
|
+
if (!f) continue;
|
|
3408
|
+
const selOptIds = selection.optionSelectionsByFieldId[fid];
|
|
3409
|
+
if (selOptIds && selOptIds.length) {
|
|
3410
|
+
selections[fid] = [...selOptIds];
|
|
3411
|
+
}
|
|
3412
|
+
if (!isServicedBased(f)) {
|
|
3413
|
+
const name = f.name;
|
|
3414
|
+
const val = selection.formValuesByFieldId[fid];
|
|
3415
|
+
if (!name || val === void 0) continue;
|
|
3416
|
+
formValues[name] = val;
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
return { formValues, selections };
|
|
3420
|
+
}
|
|
3421
|
+
function resolveQuantity(visibleFieldIds, fieldById, selection, hostDefault) {
|
|
3422
|
+
var _a;
|
|
3423
|
+
for (const fid of visibleFieldIds) {
|
|
3424
|
+
const f = fieldById.get(fid);
|
|
3425
|
+
if (!f) continue;
|
|
3426
|
+
const rule = readQuantityRule(
|
|
3427
|
+
(_a = f.meta) == null ? void 0 : _a.quantity
|
|
3428
|
+
);
|
|
3429
|
+
if (!rule) continue;
|
|
3430
|
+
const raw = selection.formValuesByFieldId[fid];
|
|
3431
|
+
const evaluated = evaluateQuantityRule(rule, raw);
|
|
3432
|
+
if (Number.isFinite(evaluated) && evaluated > 0) {
|
|
3433
|
+
return {
|
|
3434
|
+
quantity: evaluated,
|
|
3435
|
+
source: { kind: "field", id: f.id, rule }
|
|
3436
|
+
};
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
quantity: hostDefault,
|
|
3441
|
+
source: { kind: "default", defaultedFromHost: true }
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
function readQuantityRule(v) {
|
|
3445
|
+
if (!v || typeof v !== "object") return void 0;
|
|
3446
|
+
const src = v;
|
|
3447
|
+
if (src.valueBy !== "value" && src.valueBy !== "length" && src.valueBy !== "eval")
|
|
3448
|
+
return void 0;
|
|
3449
|
+
const out = { valueBy: src.valueBy };
|
|
3450
|
+
if (src.code && typeof src.code === "string") out.code = src.code;
|
|
3451
|
+
return out;
|
|
3452
|
+
}
|
|
3453
|
+
function evaluateQuantityRule(rule, raw) {
|
|
3454
|
+
switch (rule.valueBy) {
|
|
3455
|
+
case "value": {
|
|
3456
|
+
const n = Number(Array.isArray(raw) ? raw[0] : raw);
|
|
3457
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3458
|
+
}
|
|
3459
|
+
case "length": {
|
|
3460
|
+
if (Array.isArray(raw)) return raw.length;
|
|
3461
|
+
if (typeof raw === "string") return raw.length;
|
|
3462
|
+
return NaN;
|
|
3463
|
+
}
|
|
3464
|
+
case "eval": {
|
|
3465
|
+
try {
|
|
3466
|
+
if (!rule.code || typeof rule.code !== "string") return NaN;
|
|
3467
|
+
const fn = new Function(
|
|
3468
|
+
"value",
|
|
3469
|
+
"values",
|
|
3470
|
+
`return (function(){ ${rule.code}
|
|
3471
|
+
})()`
|
|
3472
|
+
);
|
|
3473
|
+
const single = Array.isArray(raw) ? raw[0] : raw;
|
|
3474
|
+
const values = Array.isArray(raw) ? raw : raw !== void 0 ? [raw] : [];
|
|
3475
|
+
const out = fn(single, values);
|
|
3476
|
+
const n = Number(out);
|
|
3477
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3478
|
+
} catch {
|
|
3479
|
+
return NaN;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
default:
|
|
3483
|
+
return NaN;
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
function resolveServices(tagId, visibleFieldIds, selection, tagById, fieldById) {
|
|
3487
|
+
var _a;
|
|
3488
|
+
const serviceMap = {};
|
|
3489
|
+
const ordered = [];
|
|
3490
|
+
const tag = tagById.get(tagId);
|
|
3491
|
+
let primary;
|
|
3492
|
+
let primaryOrigin;
|
|
3493
|
+
if ((tag == null ? void 0 : tag.service_id) !== void 0) {
|
|
3494
|
+
primary = tag.service_id;
|
|
3495
|
+
primaryOrigin = "tag";
|
|
3496
|
+
}
|
|
3497
|
+
const optionVisit = buildOptionVisitOrder(selection, fieldById);
|
|
3498
|
+
for (const { fieldId, optionId } of optionVisit) {
|
|
3499
|
+
if (!visibleFieldIds.includes(fieldId)) continue;
|
|
3500
|
+
const f = fieldById.get(fieldId);
|
|
3501
|
+
if (!f || !Array.isArray(f.options)) continue;
|
|
3502
|
+
const opt = f.options.find((o) => o.id === optionId);
|
|
3503
|
+
if (!opt) continue;
|
|
3504
|
+
const role = (_a = opt.pricing_role) != null ? _a : "base";
|
|
3505
|
+
const sid = opt.service_id;
|
|
3506
|
+
if (role === "utility") continue;
|
|
3507
|
+
if (sid !== void 0) {
|
|
3508
|
+
if (primary === void 0 || primaryOrigin === "tag") {
|
|
3509
|
+
primary = sid;
|
|
3510
|
+
primaryOrigin = "option";
|
|
3511
|
+
ordered.length = 0;
|
|
3512
|
+
ordered.push(primary);
|
|
3513
|
+
} else {
|
|
3514
|
+
ordered.push(sid);
|
|
3515
|
+
}
|
|
3516
|
+
pushService(serviceMap, optionId, sid);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
if (primaryOrigin !== "option" && primary !== void 0) {
|
|
3520
|
+
ordered.unshift(primary);
|
|
3521
|
+
pushService(serviceMap, tagId, primary);
|
|
3522
|
+
} else {
|
|
3523
|
+
}
|
|
3524
|
+
const servicesList = dedupeByString(ordered);
|
|
3525
|
+
return { serviceMap, servicesList };
|
|
3526
|
+
}
|
|
3527
|
+
function buildOptionVisitOrder(selection, fieldById) {
|
|
3528
|
+
var _a;
|
|
3529
|
+
if (selection.optionTraversalOrder && selection.optionTraversalOrder.length) {
|
|
3530
|
+
return selection.optionTraversalOrder.slice();
|
|
3531
|
+
}
|
|
3532
|
+
const out = [];
|
|
3533
|
+
for (const [fid, optIds] of Object.entries(
|
|
3534
|
+
(_a = selection.optionSelectionsByFieldId) != null ? _a : {}
|
|
3535
|
+
)) {
|
|
3536
|
+
const f = fieldById.get(fid);
|
|
3537
|
+
if (!f) continue;
|
|
3538
|
+
for (const oid of optIds != null ? optIds : [])
|
|
3539
|
+
out.push({ fieldId: fid, optionId: oid });
|
|
3540
|
+
}
|
|
3541
|
+
return out;
|
|
3542
|
+
}
|
|
3543
|
+
function pushService(map, nodeId, sid) {
|
|
3544
|
+
if (!map[nodeId]) map[nodeId] = [];
|
|
3545
|
+
map[nodeId].push(sid);
|
|
3546
|
+
}
|
|
3547
|
+
function dedupeByString(arr) {
|
|
3548
|
+
const s = /* @__PURE__ */ new Set();
|
|
3549
|
+
const out = [];
|
|
3550
|
+
for (const v of arr) {
|
|
3551
|
+
const key = String(v);
|
|
3552
|
+
if (s.has(key)) continue;
|
|
3553
|
+
s.add(key);
|
|
3554
|
+
out.push(v);
|
|
3555
|
+
}
|
|
3556
|
+
return out;
|
|
3557
|
+
}
|
|
3558
|
+
function pruneFallbacksConservative(fallbacks, env, svcMap, policy) {
|
|
3559
|
+
var _a, _b;
|
|
3560
|
+
if (!fallbacks) return { pruned: void 0, original: void 0 };
|
|
3561
|
+
try {
|
|
3562
|
+
const { props: prunedProps } = pruneInvalidNodeFallbacks(
|
|
3563
|
+
{
|
|
3564
|
+
filters: [],
|
|
3565
|
+
fields: [],
|
|
3566
|
+
schema_version: "1.0",
|
|
3567
|
+
fallbacks
|
|
3568
|
+
},
|
|
3569
|
+
svcMap,
|
|
3570
|
+
policy
|
|
3571
|
+
);
|
|
3572
|
+
return {
|
|
3573
|
+
pruned: prunedProps.fallbacks,
|
|
3574
|
+
original: fallbacks
|
|
3575
|
+
};
|
|
3576
|
+
} catch {
|
|
3577
|
+
const out = {};
|
|
3578
|
+
const requireFit = (_a = policy.requireConstraintFit) != null ? _a : true;
|
|
3579
|
+
if (fallbacks.nodes) {
|
|
3580
|
+
const keptNodes = {};
|
|
3581
|
+
for (const [nodeId, candidates] of Object.entries(
|
|
3582
|
+
fallbacks.nodes
|
|
3583
|
+
)) {
|
|
3584
|
+
if (!env.serviceMap[nodeId]) continue;
|
|
3585
|
+
const primary = ((_b = env.serviceMap[nodeId]) != null ? _b : [])[0];
|
|
3586
|
+
const kept = [];
|
|
3587
|
+
for (const cand of candidates != null ? candidates : []) {
|
|
3588
|
+
if (!rateOk(svcMap, cand, primary, policy)) continue;
|
|
3589
|
+
if (requireFit && env.constraints && !constraintFitOk(svcMap, cand, env.constraints))
|
|
3590
|
+
continue;
|
|
3591
|
+
kept.push(cand);
|
|
3592
|
+
}
|
|
3593
|
+
if (kept.length) keptNodes[nodeId] = kept;
|
|
3594
|
+
}
|
|
3595
|
+
if (Object.keys(keptNodes).length) out.nodes = keptNodes;
|
|
3596
|
+
}
|
|
3597
|
+
if (fallbacks.global) {
|
|
3598
|
+
const keptGlobal = {};
|
|
3599
|
+
const present = new Set(env.servicesList.map((sid) => String(sid)));
|
|
3600
|
+
for (const [primary, cands] of Object.entries(
|
|
3601
|
+
fallbacks.global
|
|
3602
|
+
)) {
|
|
3603
|
+
if (!present.has(String(primary))) continue;
|
|
3604
|
+
const primId = isFiniteNumber3(primary) ? Number(primary) : primary;
|
|
3605
|
+
const kept = [];
|
|
3606
|
+
for (const cand of cands != null ? cands : []) {
|
|
3607
|
+
if (!rateOk(svcMap, cand, primId, policy)) continue;
|
|
3608
|
+
if (requireFit && env.constraints && !constraintFitOk(svcMap, cand, env.constraints))
|
|
3609
|
+
continue;
|
|
3610
|
+
kept.push(cand);
|
|
3611
|
+
}
|
|
3612
|
+
if (kept.length) keptGlobal[primId] = kept;
|
|
3613
|
+
}
|
|
3614
|
+
if (Object.keys(keptGlobal).length)
|
|
3615
|
+
out.global = keptGlobal;
|
|
3616
|
+
}
|
|
3617
|
+
return {
|
|
3618
|
+
pruned: Object.keys(out).length ? out : void 0,
|
|
3619
|
+
original: fallbacks
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
function isFiniteNumber3(v) {
|
|
3624
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
3625
|
+
}
|
|
3626
|
+
function collectUtilityLineItems(visibleFieldIds, fieldById, selection, quantity) {
|
|
3627
|
+
var _a, _b, _c, _d, _e;
|
|
3628
|
+
const items = [];
|
|
3629
|
+
for (const fid of visibleFieldIds) {
|
|
3630
|
+
const f = fieldById.get(fid);
|
|
3631
|
+
if (!f) continue;
|
|
3632
|
+
const isUtilityField = ((_a = f.pricing_role) != null ? _a : "base") === "utility";
|
|
3633
|
+
const marker = readUtilityMarker((_b = f.meta) == null ? void 0 : _b.utility);
|
|
3634
|
+
if (isUtilityField && marker) {
|
|
3635
|
+
const val = selection.formValuesByFieldId[f.id];
|
|
3636
|
+
const item = buildUtilityItemFromMarker(
|
|
3637
|
+
f.id,
|
|
3638
|
+
marker,
|
|
3639
|
+
quantity,
|
|
3640
|
+
val
|
|
3641
|
+
);
|
|
3642
|
+
if (item) items.push(item);
|
|
3643
|
+
}
|
|
3644
|
+
if (Array.isArray(f.options) && f.options.length) {
|
|
3645
|
+
const selectedOptIds = (_c = selection.optionSelectionsByFieldId[f.id]) != null ? _c : [];
|
|
3646
|
+
if (selectedOptIds.length) {
|
|
3647
|
+
const optById = new Map(
|
|
3648
|
+
f.options.map((o) => [o.id, o])
|
|
3649
|
+
);
|
|
3650
|
+
for (const oid of selectedOptIds) {
|
|
3651
|
+
const opt = optById.get(oid);
|
|
3652
|
+
if (!opt) continue;
|
|
3653
|
+
if (((_d = opt.pricing_role) != null ? _d : "base") !== "utility") continue;
|
|
3654
|
+
const om = readUtilityMarker((_e = opt.meta) == null ? void 0 : _e.utility);
|
|
3655
|
+
if (!om) continue;
|
|
3656
|
+
const parentVal = selection.formValuesByFieldId[f.id];
|
|
3657
|
+
const item = buildUtilityItemFromMarker(
|
|
3658
|
+
opt.id,
|
|
3659
|
+
om,
|
|
3660
|
+
quantity,
|
|
3661
|
+
parentVal
|
|
3662
|
+
);
|
|
3663
|
+
if (item) items.push(item);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
return items;
|
|
3669
|
+
}
|
|
3670
|
+
function readUtilityMarker(v) {
|
|
3671
|
+
if (!v || typeof v !== "object") return void 0;
|
|
3672
|
+
const src = v;
|
|
3673
|
+
if (!src.mode || typeof src.rate !== "number" || !Number.isFinite(src.rate))
|
|
3674
|
+
return void 0;
|
|
3675
|
+
if (src.mode !== "flat" && src.mode !== "per_quantity" && src.mode !== "per_value" && src.mode !== "percent")
|
|
3676
|
+
return void 0;
|
|
3677
|
+
const out = { mode: src.mode, rate: src.rate };
|
|
3678
|
+
if (src.valueBy === "value" || src.valueBy === "length" || src.valueBy === "eval")
|
|
3679
|
+
out.valueBy = src.valueBy;
|
|
3680
|
+
if (src.code && typeof src.code === "string") out.code = src.code;
|
|
3681
|
+
return out;
|
|
3682
|
+
}
|
|
3683
|
+
function buildUtilityItemFromMarker(nodeId, marker, quantity, value) {
|
|
3684
|
+
var _a, _b;
|
|
3685
|
+
const base = {
|
|
3686
|
+
nodeId,
|
|
3687
|
+
mode: marker.mode,
|
|
3688
|
+
rate: marker.rate,
|
|
3689
|
+
inputs: { quantity }
|
|
3690
|
+
};
|
|
3691
|
+
if (marker.mode === "per_value") {
|
|
3692
|
+
base.inputs.valueBy = (_a = marker.valueBy) != null ? _a : "value";
|
|
3693
|
+
if (marker.valueBy === "length") {
|
|
3694
|
+
base.inputs.value = Array.isArray(value) ? value.length : typeof value === "string" ? value.length : 0;
|
|
3695
|
+
} else if (marker.valueBy === "eval") {
|
|
3696
|
+
base.inputs.evalCodeUsed = true;
|
|
3697
|
+
} else {
|
|
3698
|
+
base.inputs.value = Array.isArray(value) ? (_b = value[0]) != null ? _b : null : value != null ? value : null;
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
return base;
|
|
3702
|
+
}
|
|
3703
|
+
function buildNodeContexts(tagId, visibleFieldIds, fieldById, selection) {
|
|
3704
|
+
var _a;
|
|
3705
|
+
const ctx = {};
|
|
3706
|
+
ctx[tagId] = tagId;
|
|
3707
|
+
for (const fid of visibleFieldIds) {
|
|
3708
|
+
const f = fieldById.get(fid);
|
|
3709
|
+
if (!f) continue;
|
|
3710
|
+
const binds = normalizeBindIds(f.bind_id);
|
|
3711
|
+
const applicable = binds.has(tagId);
|
|
3712
|
+
const selectedOptIds = (_a = selection.optionSelectionsByFieldId[fid]) != null ? _a : [];
|
|
3713
|
+
for (const oid of selectedOptIds) {
|
|
3714
|
+
ctx[oid] = applicable ? tagId : null;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
return ctx;
|
|
3718
|
+
}
|
|
3719
|
+
function normalizeBindIds(bind) {
|
|
3720
|
+
const out = /* @__PURE__ */ new Set();
|
|
3721
|
+
if (!bind) return out;
|
|
3722
|
+
if (Array.isArray(bind)) {
|
|
3723
|
+
for (const b of bind) if (b) out.add(String(b));
|
|
3724
|
+
} else {
|
|
3725
|
+
out.add(String(bind));
|
|
3726
|
+
}
|
|
3727
|
+
return out;
|
|
3728
|
+
}
|
|
3729
|
+
function buildDevWarnings(props, svcMap, _tagId, _snapshotServiceMap, originalFallbacks, _prunedFallbacks, fieldById, visibleFieldIds, selection) {
|
|
3730
|
+
const out = {};
|
|
3731
|
+
const maybeCollectFailed = globalThis.collectFailedFallbacks;
|
|
3732
|
+
try {
|
|
3733
|
+
if (maybeCollectFailed && originalFallbacks) {
|
|
3734
|
+
const diags = maybeCollectFailed(
|
|
3735
|
+
{
|
|
3736
|
+
...props,
|
|
3737
|
+
fallbacks: originalFallbacks
|
|
3738
|
+
},
|
|
3739
|
+
svcMap,
|
|
3740
|
+
{ mode: "dev" }
|
|
3741
|
+
);
|
|
3742
|
+
if (diags && diags.length) {
|
|
3743
|
+
out.fallbacks = diags;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
} catch {
|
|
3747
|
+
}
|
|
3748
|
+
const utilityWarnings = [];
|
|
3749
|
+
for (const fid of visibleFieldIds) {
|
|
3750
|
+
const f = fieldById.get(fid);
|
|
3751
|
+
if (!f) continue;
|
|
3752
|
+
const hasVal = selection.formValuesByFieldId[fid] !== void 0;
|
|
3753
|
+
if (hasVal && !f.name && !isOptionBased(f)) {
|
|
3754
|
+
utilityWarnings.push({
|
|
3755
|
+
nodeId: fid,
|
|
3756
|
+
reason: "missing_field_name_for_form_value"
|
|
3757
|
+
});
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
if (utilityWarnings.length) {
|
|
3761
|
+
out.utility = utilityWarnings;
|
|
3762
|
+
}
|
|
3763
|
+
if (!out.fallbacks && !out.utility) return void 0;
|
|
3764
|
+
return out;
|
|
3765
|
+
}
|
|
3766
|
+
function toSnapshotPolicy(settings) {
|
|
3767
|
+
var _a, _b, _c;
|
|
3768
|
+
const requireConstraintFit = (_a = settings.requireConstraintFit) != null ? _a : true;
|
|
3769
|
+
const rp = (_b = settings.ratePolicy) != null ? _b : { kind: "lte_primary" };
|
|
3770
|
+
switch (rp.kind) {
|
|
3771
|
+
case "lte_primary":
|
|
3772
|
+
return {
|
|
3773
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3774
|
+
requireConstraintFit
|
|
3775
|
+
};
|
|
3776
|
+
case "within_pct":
|
|
3777
|
+
return {
|
|
3778
|
+
ratePolicy: {
|
|
3779
|
+
kind: "lte_primary",
|
|
3780
|
+
thresholdPct: Math.max(0, (_c = rp.pct) != null ? _c : 0)
|
|
3781
|
+
},
|
|
3782
|
+
requireConstraintFit
|
|
3783
|
+
};
|
|
3784
|
+
case "at_least_pct_lower":
|
|
3785
|
+
return {
|
|
3786
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3787
|
+
requireConstraintFit
|
|
3788
|
+
};
|
|
3789
|
+
default:
|
|
3790
|
+
return {
|
|
3791
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3792
|
+
requireConstraintFit
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
function getCap2(map, id) {
|
|
3797
|
+
const direct = map[id];
|
|
3798
|
+
if (direct) return direct;
|
|
3799
|
+
const strKey = String(id);
|
|
3800
|
+
const byStr = map[strKey];
|
|
3801
|
+
if (byStr) return byStr;
|
|
3802
|
+
const n = typeof id === "number" ? id : typeof id === "string" ? Number(id) : Number.NaN;
|
|
3803
|
+
if (Number.isFinite(n)) {
|
|
3804
|
+
const byNum = map[n];
|
|
3805
|
+
if (byNum) return byNum;
|
|
3806
|
+
}
|
|
3807
|
+
return void 0;
|
|
3808
|
+
}
|
|
3809
|
+
function resolveMinMax(servicesList, services) {
|
|
3810
|
+
let min = void 0;
|
|
3811
|
+
let max = void 0;
|
|
3812
|
+
for (const sid of servicesList) {
|
|
3813
|
+
const cap = getCap2(services, sid);
|
|
3814
|
+
if (!cap) continue;
|
|
3815
|
+
if (typeof cap.min === "number" && Number.isFinite(cap.min)) {
|
|
3816
|
+
min = min === void 0 ? cap.min : Math.min(min, cap.min);
|
|
3817
|
+
}
|
|
3818
|
+
if (typeof cap.max === "number" && Number.isFinite(cap.max)) {
|
|
3819
|
+
max = max === void 0 ? cap.max : Math.max(max, cap.max);
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
return { min: min != null ? min : 1, ...max !== void 0 ? { max } : {} };
|
|
3823
|
+
}
|
|
2922
3824
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2923
3825
|
0 && (module.exports = {
|
|
3826
|
+
buildOrderSnapshot,
|
|
2924
3827
|
collectFailedFallbacks,
|
|
2925
3828
|
createBuilder,
|
|
2926
3829
|
createNodeIndex,
|
|
@@ -2928,6 +3831,7 @@ function createNodeIndex(builder) {
|
|
|
2928
3831
|
normalise,
|
|
2929
3832
|
resolveServiceFallback,
|
|
2930
3833
|
validate,
|
|
3834
|
+
validateAsync,
|
|
2931
3835
|
validateRateCoherenceDeep
|
|
2932
3836
|
});
|
|
2933
3837
|
//# sourceMappingURL=index.cjs.map
|