@timeax/digital-service-engine 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +1097 -193
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +189 -51
- package/dist/core/index.d.ts +189 -51
- package/dist/core/index.js +1095 -193
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +8313 -2542
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +401 -141
- package/dist/react/index.d.ts +401 -141
- package/dist/react/index.js +8471 -2709
- package/dist/react/index.js.map +1 -1
- package/dist/schema/index.d.cts +122 -54
- package/dist/schema/index.d.ts +122 -54
- package/dist/workspace/index.cjs +448 -239
- package/dist/workspace/index.cjs.map +1 -1
- package/dist/workspace/index.d.cts +54 -50
- package/dist/workspace/index.d.ts +54 -50
- package/dist/workspace/index.js +448 -239
- package/dist/workspace/index.js.map +1 -1
- package/package.json +15 -6
- 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,217 @@ 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 rest2 = base.filter((fid) => !orderedSet.has(fid));
|
|
559
|
+
return [...ordered, ...rest2];
|
|
560
|
+
}
|
|
561
|
+
const promoted = revealedOrder.filter((fid) => visible.has(fid));
|
|
562
|
+
const promotedSet = new Set(promoted);
|
|
563
|
+
const rest = base.filter((fid) => !promotedSet.has(fid));
|
|
564
|
+
return [...promoted, ...rest];
|
|
565
|
+
}
|
|
566
|
+
function visibleFieldsUnder(props, tagId, opts = {}) {
|
|
567
|
+
var _a;
|
|
568
|
+
const ids = visibleFieldIdsUnder(props, tagId, opts);
|
|
569
|
+
const fieldById = new Map(((_a = props.fields) != null ? _a : []).map((f) => [f.id, f]));
|
|
570
|
+
return ids.map((id) => fieldById.get(id)).filter(Boolean);
|
|
571
|
+
}
|
|
572
|
+
|
|
406
573
|
// src/core/validate/steps/visibility.ts
|
|
407
574
|
function createFieldsVisibleUnder(v) {
|
|
408
575
|
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());
|
|
576
|
+
return visibleFieldsUnder(v.props, tagId, {
|
|
577
|
+
selectedKeys: v.selectedKeys
|
|
578
|
+
});
|
|
430
579
|
};
|
|
431
580
|
}
|
|
432
|
-
function
|
|
581
|
+
function stableKeyOfSelection(keys) {
|
|
582
|
+
return Array.from(keys).sort().join("|");
|
|
583
|
+
}
|
|
584
|
+
function resolveRootTags(tags) {
|
|
585
|
+
const roots = tags.filter((t) => !t.bind_id);
|
|
586
|
+
return roots.length ? roots : tags.slice(0, 1);
|
|
587
|
+
}
|
|
588
|
+
function isEffectfulTrigger(v, trigger) {
|
|
589
|
+
var _a, _b, _c, _d, _e, _f;
|
|
590
|
+
const inc = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
591
|
+
const exc = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
592
|
+
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;
|
|
593
|
+
}
|
|
594
|
+
function collectSelectableTriggersInContext(v, tagId, selectedKeys, onlyEffectful) {
|
|
595
|
+
var _a;
|
|
596
|
+
const visible = visibleFieldsUnder(v.props, tagId, {
|
|
597
|
+
selectedKeys
|
|
598
|
+
});
|
|
599
|
+
const triggers = [];
|
|
600
|
+
for (const f of visible) {
|
|
601
|
+
if (f.button === true) {
|
|
602
|
+
const t = f.id;
|
|
603
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
604
|
+
}
|
|
605
|
+
for (const o of (_a = f.options) != null ? _a : []) {
|
|
606
|
+
const t = `${f.id}::${o.id}`;
|
|
607
|
+
if (!onlyEffectful || isEffectfulTrigger(v, t)) triggers.push(t);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
triggers.sort();
|
|
611
|
+
return triggers;
|
|
612
|
+
}
|
|
613
|
+
function runVisibilityRulesOnce(v) {
|
|
433
614
|
var _a, _b, _c, _d, _e;
|
|
434
615
|
for (const t of v.tags) {
|
|
435
616
|
const visible = v.fieldsVisibleUnder(t.id);
|
|
@@ -504,12 +685,85 @@ function validateVisibility(v) {
|
|
|
504
685
|
}
|
|
505
686
|
}
|
|
506
687
|
}
|
|
688
|
+
function dedupeErrorsInPlace(v, startIndex) {
|
|
689
|
+
const seen = /* @__PURE__ */ new Set();
|
|
690
|
+
const keyOfErr = (e) => {
|
|
691
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
692
|
+
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 : "";
|
|
693
|
+
const other = (_g = (_f = e == null ? void 0 : e.details) == null ? void 0 : _f.other) != null ? _g : "";
|
|
694
|
+
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 : ""}`;
|
|
695
|
+
};
|
|
696
|
+
const kept = [];
|
|
697
|
+
for (let i = startIndex; i < v.errors.length; i++) {
|
|
698
|
+
const e = v.errors[i];
|
|
699
|
+
const k = keyOfErr(e);
|
|
700
|
+
if (seen.has(k)) continue;
|
|
701
|
+
seen.add(k);
|
|
702
|
+
kept.push(e);
|
|
703
|
+
}
|
|
704
|
+
v.errors.splice(startIndex, v.errors.length - startIndex, ...kept);
|
|
705
|
+
}
|
|
706
|
+
function validateVisibility(v, options = {}) {
|
|
707
|
+
var _a, _b, _c;
|
|
708
|
+
const simulate = options.simulate === true;
|
|
709
|
+
if (!simulate) {
|
|
710
|
+
runVisibilityRulesOnce(v);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const maxStates = Math.max(1, (_a = options.maxStates) != null ? _a : 500);
|
|
714
|
+
const maxDepth = Math.max(0, (_b = options.maxDepth) != null ? _b : 6);
|
|
715
|
+
const onlyEffectful = options.onlyEffectfulTriggers !== false;
|
|
716
|
+
const roots = resolveRootTags(v.tags);
|
|
717
|
+
const rootTags = options.simulateAllRoots ? roots : roots.slice(0, 1);
|
|
718
|
+
const originalSelected = new Set((_c = v.selectedKeys) != null ? _c : []);
|
|
719
|
+
const errorsStart = v.errors.length;
|
|
720
|
+
const visited = /* @__PURE__ */ new Set();
|
|
721
|
+
const stack = [];
|
|
722
|
+
for (const rt of rootTags) {
|
|
723
|
+
stack.push({
|
|
724
|
+
rootTagId: rt.id,
|
|
725
|
+
selected: new Set(originalSelected),
|
|
726
|
+
depth: 0
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
let validatedStates = 0;
|
|
730
|
+
while (stack.length) {
|
|
731
|
+
if (validatedStates >= maxStates) break;
|
|
732
|
+
const state = stack.pop();
|
|
733
|
+
const sig = stableKeyOfSelection(state.selected);
|
|
734
|
+
if (visited.has(sig)) continue;
|
|
735
|
+
visited.add(sig);
|
|
736
|
+
v.selectedKeys = state.selected;
|
|
737
|
+
validatedStates++;
|
|
738
|
+
runVisibilityRulesOnce(v);
|
|
739
|
+
if (state.depth >= maxDepth) continue;
|
|
740
|
+
const triggers = collectSelectableTriggersInContext(
|
|
741
|
+
v,
|
|
742
|
+
state.rootTagId,
|
|
743
|
+
state.selected,
|
|
744
|
+
onlyEffectful
|
|
745
|
+
);
|
|
746
|
+
for (let i = triggers.length - 1; i >= 0; i--) {
|
|
747
|
+
const trig = triggers[i];
|
|
748
|
+
if (state.selected.has(trig)) continue;
|
|
749
|
+
const next = new Set(state.selected);
|
|
750
|
+
next.add(trig);
|
|
751
|
+
stack.push({
|
|
752
|
+
rootTagId: state.rootTagId,
|
|
753
|
+
selected: next,
|
|
754
|
+
depth: state.depth + 1
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
v.selectedKeys = originalSelected;
|
|
759
|
+
dedupeErrorsInPlace(v, errorsStart);
|
|
760
|
+
}
|
|
507
761
|
|
|
508
762
|
// src/core/validate/steps/structure.ts
|
|
509
763
|
function validateStructure(v) {
|
|
510
764
|
const tags = v.tags;
|
|
511
765
|
const fields = v.fields;
|
|
512
|
-
if (!tags.some((t) => t.id === "root")) {
|
|
766
|
+
if (!tags.some((t) => t.id === "t:root")) {
|
|
513
767
|
v.errors.push({
|
|
514
768
|
code: "root_missing",
|
|
515
769
|
severity: "error",
|
|
@@ -699,58 +953,91 @@ function validateIdentity(v) {
|
|
|
699
953
|
}
|
|
700
954
|
|
|
701
955
|
// src/core/validate/steps/option-maps.ts
|
|
956
|
+
function parseFieldOptionKey(key) {
|
|
957
|
+
const idx = key.indexOf("::");
|
|
958
|
+
if (idx === -1) return null;
|
|
959
|
+
const fieldId = key.slice(0, idx).trim();
|
|
960
|
+
const optionId = key.slice(idx + 2).trim();
|
|
961
|
+
if (!fieldId || !optionId) return null;
|
|
962
|
+
return { fieldId, optionId };
|
|
963
|
+
}
|
|
964
|
+
function hasOption(v, fid, oid) {
|
|
965
|
+
var _a;
|
|
966
|
+
const f = v.fieldById.get(fid);
|
|
967
|
+
if (!f) return false;
|
|
968
|
+
return !!((_a = f.options) != null ? _a : []).find((o) => o.id === oid);
|
|
969
|
+
}
|
|
702
970
|
function validateOptionMaps(v) {
|
|
703
971
|
var _a, _b;
|
|
704
972
|
const incMap = (_a = v.props.includes_for_buttons) != null ? _a : {};
|
|
705
973
|
const excMap = (_b = v.props.excludes_for_buttons) != null ? _b : {};
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
974
|
+
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.`;
|
|
975
|
+
const validateTriggerKey = (key) => {
|
|
976
|
+
const ref = v.nodeMap.get(key);
|
|
977
|
+
if (ref) {
|
|
978
|
+
if (ref.kind === "option") {
|
|
979
|
+
return {
|
|
980
|
+
ok: true,
|
|
981
|
+
nodeId: ref.fieldId,
|
|
982
|
+
affected: [ref.fieldId, ref.id]
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
if (ref.kind === "field") {
|
|
986
|
+
const isButton2 = ref.node.button === true;
|
|
987
|
+
if (!isButton2)
|
|
988
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
989
|
+
return { ok: true, nodeId: ref.id, affected: [ref.id] };
|
|
990
|
+
}
|
|
991
|
+
return { ok: false, nodeId: ref.id, affected: [ref.id] };
|
|
992
|
+
}
|
|
993
|
+
const p = parseFieldOptionKey(key);
|
|
994
|
+
if (!p) return { ok: false };
|
|
995
|
+
if (!hasOption(v, p.fieldId, p.optionId))
|
|
996
|
+
return {
|
|
997
|
+
ok: false,
|
|
998
|
+
nodeId: p.fieldId,
|
|
999
|
+
affected: [p.fieldId, p.optionId]
|
|
1000
|
+
};
|
|
1001
|
+
return {
|
|
1002
|
+
ok: true,
|
|
1003
|
+
nodeId: p.fieldId,
|
|
1004
|
+
affected: [p.fieldId, p.optionId]
|
|
1005
|
+
};
|
|
718
1006
|
};
|
|
719
|
-
const badKeyMessage = (key) => `Invalid option-map key "${key}". Expected "fieldId::optionId" pointing to an existing option.`;
|
|
720
1007
|
for (const k of Object.keys(incMap)) {
|
|
721
|
-
const
|
|
722
|
-
if (!
|
|
1008
|
+
const r = validateTriggerKey(k);
|
|
1009
|
+
if (!r.ok) {
|
|
723
1010
|
v.errors.push({
|
|
724
1011
|
code: "bad_option_key",
|
|
725
1012
|
severity: "error",
|
|
726
1013
|
message: badKeyMessage(k),
|
|
727
|
-
|
|
1014
|
+
nodeId: r.nodeId,
|
|
1015
|
+
details: withAffected({ key: k }, r.affected)
|
|
728
1016
|
});
|
|
729
1017
|
}
|
|
730
1018
|
}
|
|
731
1019
|
for (const k of Object.keys(excMap)) {
|
|
732
|
-
const
|
|
733
|
-
if (!
|
|
1020
|
+
const r = validateTriggerKey(k);
|
|
1021
|
+
if (!r.ok) {
|
|
734
1022
|
v.errors.push({
|
|
735
1023
|
code: "bad_option_key",
|
|
736
1024
|
severity: "error",
|
|
737
1025
|
message: badKeyMessage(k),
|
|
738
|
-
|
|
1026
|
+
nodeId: r.nodeId,
|
|
1027
|
+
details: withAffected({ key: k }, r.affected)
|
|
739
1028
|
});
|
|
740
1029
|
}
|
|
741
1030
|
}
|
|
742
1031
|
for (const k of Object.keys(incMap)) {
|
|
743
|
-
if (k in excMap)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
});
|
|
753
|
-
}
|
|
1032
|
+
if (!(k in excMap)) continue;
|
|
1033
|
+
const r = validateTriggerKey(k);
|
|
1034
|
+
v.errors.push({
|
|
1035
|
+
code: "option_include_exclude_conflict",
|
|
1036
|
+
severity: "error",
|
|
1037
|
+
message: `Trigger-map key "${k}" appears in both includes_for_buttons and excludes_for_buttons.`,
|
|
1038
|
+
nodeId: r.nodeId,
|
|
1039
|
+
details: withAffected({ key: k }, r.affected)
|
|
1040
|
+
});
|
|
754
1041
|
}
|
|
755
1042
|
}
|
|
756
1043
|
|
|
@@ -1655,8 +1942,23 @@ function applyPolicies(errors, props, serviceMap, policies, fieldsVisibleUnder,
|
|
|
1655
1942
|
}
|
|
1656
1943
|
|
|
1657
1944
|
// src/core/validate/index.ts
|
|
1945
|
+
function readVisibilitySimOpts(ctx) {
|
|
1946
|
+
const c = ctx;
|
|
1947
|
+
const simulate = c.simulateVisibility === true || c.visibilitySimulate === true || c.simulate === true;
|
|
1948
|
+
const maxStates = typeof c.maxVisibilityStates === "number" ? c.maxVisibilityStates : typeof c.visibilityMaxStates === "number" ? c.visibilityMaxStates : typeof c.maxStates === "number" ? c.maxStates : void 0;
|
|
1949
|
+
const maxDepth = typeof c.maxVisibilityDepth === "number" ? c.maxVisibilityDepth : typeof c.visibilityMaxDepth === "number" ? c.visibilityMaxDepth : typeof c.maxDepth === "number" ? c.maxDepth : void 0;
|
|
1950
|
+
const simulateAllRoots = c.simulateAllRoots === true || c.visibilitySimulateAllRoots === true;
|
|
1951
|
+
const onlyEffectfulTriggers = c.onlyEffectfulTriggers === false ? false : c.visibilityOnlyEffectfulTriggers !== false;
|
|
1952
|
+
return {
|
|
1953
|
+
simulate,
|
|
1954
|
+
maxStates,
|
|
1955
|
+
maxDepth,
|
|
1956
|
+
simulateAllRoots,
|
|
1957
|
+
onlyEffectfulTriggers
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1658
1960
|
function validate(props, ctx = {}) {
|
|
1659
|
-
var _a, _b;
|
|
1961
|
+
var _a, _b, _c;
|
|
1660
1962
|
const errors = [];
|
|
1661
1963
|
const serviceMap = (_a = ctx.serviceMap) != null ? _a : {};
|
|
1662
1964
|
const selectedKeys = new Set(
|
|
@@ -1670,6 +1972,7 @@ function validate(props, ctx = {}) {
|
|
|
1670
1972
|
for (const f of fields) fieldById.set(f.id, f);
|
|
1671
1973
|
const v = {
|
|
1672
1974
|
props,
|
|
1975
|
+
nodeMap: (_c = ctx.nodeMap) != null ? _c : buildNodeMap(props),
|
|
1673
1976
|
options: ctx,
|
|
1674
1977
|
errors,
|
|
1675
1978
|
serviceMap,
|
|
@@ -1684,7 +1987,8 @@ function validate(props, ctx = {}) {
|
|
|
1684
1987
|
validateIdentity(v);
|
|
1685
1988
|
validateOptionMaps(v);
|
|
1686
1989
|
v.fieldsVisibleUnder = createFieldsVisibleUnder(v);
|
|
1687
|
-
|
|
1990
|
+
const visSim = readVisibilitySimOpts(ctx);
|
|
1991
|
+
validateVisibility(v, visSim);
|
|
1688
1992
|
applyPolicies(
|
|
1689
1993
|
v.errors,
|
|
1690
1994
|
v.props,
|
|
@@ -1703,6 +2007,17 @@ function validate(props, ctx = {}) {
|
|
|
1703
2007
|
validateFallbacks(v);
|
|
1704
2008
|
return v.errors;
|
|
1705
2009
|
}
|
|
2010
|
+
async function validateAsync(props, ctx = {}) {
|
|
2011
|
+
await Promise.resolve();
|
|
2012
|
+
if (typeof requestAnimationFrame === "function") {
|
|
2013
|
+
await new Promise(
|
|
2014
|
+
(resolve) => requestAnimationFrame(() => resolve())
|
|
2015
|
+
);
|
|
2016
|
+
} else {
|
|
2017
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2018
|
+
}
|
|
2019
|
+
return validate(props, ctx);
|
|
2020
|
+
}
|
|
1706
2021
|
|
|
1707
2022
|
// src/core/builder.ts
|
|
1708
2023
|
function createBuilder(opts = {}) {
|
|
@@ -1720,11 +2035,21 @@ var BuilderImpl = class {
|
|
|
1720
2035
|
this.optionOwnerById = /* @__PURE__ */ new Map();
|
|
1721
2036
|
this.history = [];
|
|
1722
2037
|
this.future = [];
|
|
2038
|
+
this._nodemap = null;
|
|
1723
2039
|
var _a;
|
|
1724
2040
|
this.options = { ...opts };
|
|
1725
2041
|
this.historyLimit = (_a = opts.historyLimit) != null ? _a : 50;
|
|
1726
2042
|
}
|
|
1727
2043
|
/* ───── lifecycle ─────────────────────────────────────────────────────── */
|
|
2044
|
+
isTagId(id) {
|
|
2045
|
+
return this.tagById.has(id);
|
|
2046
|
+
}
|
|
2047
|
+
isFieldId(id) {
|
|
2048
|
+
return this.fieldById.has(id);
|
|
2049
|
+
}
|
|
2050
|
+
isOptionId(id) {
|
|
2051
|
+
return this.optionOwnerById.has(id);
|
|
2052
|
+
}
|
|
1728
2053
|
load(raw) {
|
|
1729
2054
|
const next = normalise(raw, {
|
|
1730
2055
|
defaultPricingRole: "base",
|
|
@@ -1944,129 +2269,16 @@ var BuilderImpl = class {
|
|
|
1944
2269
|
return validate(this.props, this.options);
|
|
1945
2270
|
}
|
|
1946
2271
|
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];
|
|
2272
|
+
var _a;
|
|
2273
|
+
return visibleFieldIdsUnder(this.props, tagId, {
|
|
2274
|
+
selectedKeys: new Set(
|
|
2275
|
+
(_a = selectedKeys != null ? selectedKeys : this.options.selectedOptionKeys) != null ? _a : []
|
|
2276
|
+
)
|
|
2277
|
+
});
|
|
2278
|
+
}
|
|
2279
|
+
getNodeMap() {
|
|
2280
|
+
if (!this._nodemap) this._nodemap = buildNodeMap(this.getProps());
|
|
2281
|
+
return this._nodemap;
|
|
2070
2282
|
}
|
|
2071
2283
|
/* ───── history ─────────────────────────────────────────────────────── */
|
|
2072
2284
|
undo() {
|
|
@@ -2090,6 +2302,7 @@ var BuilderImpl = class {
|
|
|
2090
2302
|
this.tagById.clear();
|
|
2091
2303
|
this.fieldById.clear();
|
|
2092
2304
|
this.optionOwnerById.clear();
|
|
2305
|
+
this._nodemap = null;
|
|
2093
2306
|
for (const t of this.props.filters) this.tagById.set(t.id, t);
|
|
2094
2307
|
for (const f of this.props.fields) {
|
|
2095
2308
|
this.fieldById.set(f.id, f);
|
|
@@ -2571,6 +2784,7 @@ var toBindList = (b) => {
|
|
|
2571
2784
|
function createNodeIndex(builder) {
|
|
2572
2785
|
var _a, _b, _c, _d;
|
|
2573
2786
|
const props = builder.getProps();
|
|
2787
|
+
const nodeMap = builder.getNodeMap();
|
|
2574
2788
|
const tags = (_a = props.filters) != null ? _a : [];
|
|
2575
2789
|
const fields = (_b = props.fields) != null ? _b : [];
|
|
2576
2790
|
const tagById = new Map(tags.map((t) => [t.id, t]));
|
|
@@ -2774,7 +2988,9 @@ function createNodeIndex(builder) {
|
|
|
2774
2988
|
return parentById.get(id) === tid;
|
|
2775
2989
|
},
|
|
2776
2990
|
getDescendant(descendantId) {
|
|
2777
|
-
return this.getDescendants().find(
|
|
2991
|
+
return this.getDescendants().find(
|
|
2992
|
+
(item) => item.id == descendantId
|
|
2993
|
+
);
|
|
2778
2994
|
},
|
|
2779
2995
|
getDescendants() {
|
|
2780
2996
|
const results = [];
|
|
@@ -2783,7 +2999,9 @@ function createNodeIndex(builder) {
|
|
|
2783
2999
|
const node2 = getField(fieldId);
|
|
2784
3000
|
if (!node2) continue;
|
|
2785
3001
|
const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, id);
|
|
2786
|
-
results.push(
|
|
3002
|
+
results.push(
|
|
3003
|
+
explicit ? node2 : { ...node2, isInherited: true }
|
|
3004
|
+
);
|
|
2787
3005
|
}
|
|
2788
3006
|
return Object.freeze(results);
|
|
2789
3007
|
}
|
|
@@ -2832,7 +3050,9 @@ function createNodeIndex(builder) {
|
|
|
2832
3050
|
return false;
|
|
2833
3051
|
},
|
|
2834
3052
|
getDescendant(descendantId, context) {
|
|
2835
|
-
return this.getDescendants(context).find(
|
|
3053
|
+
return this.getDescendants(context).find(
|
|
3054
|
+
(item) => item.id == descendantId
|
|
3055
|
+
);
|
|
2836
3056
|
},
|
|
2837
3057
|
getDescendants(tagId) {
|
|
2838
3058
|
return resolveDescendants(id, includes, tagId, !isButton2);
|
|
@@ -2874,7 +3094,9 @@ function createNodeIndex(builder) {
|
|
|
2874
3094
|
return owner.isBoundTo(tagId);
|
|
2875
3095
|
},
|
|
2876
3096
|
getDescendant(descendantId, context) {
|
|
2877
|
-
return this.getDescendants(context).find(
|
|
3097
|
+
return this.getDescendants(context).find(
|
|
3098
|
+
(item) => item.id == descendantId
|
|
3099
|
+
);
|
|
2878
3100
|
},
|
|
2879
3101
|
getDescendants(tagId) {
|
|
2880
3102
|
return resolveDescendants(id, includes, tagId);
|
|
@@ -2885,7 +3107,7 @@ function createNodeIndex(builder) {
|
|
|
2885
3107
|
return node;
|
|
2886
3108
|
};
|
|
2887
3109
|
const getNode = (input) => {
|
|
2888
|
-
var _a2, _b2, _c2, _d2, _e
|
|
3110
|
+
var _a2, _b2, _c2, _d2, _e;
|
|
2889
3111
|
if (typeof input !== "string") {
|
|
2890
3112
|
if ("bind_id" in input && !("type" in input))
|
|
2891
3113
|
return (_a2 = getTag(input.id)) != null ? _a2 : mkUnknown(input.id);
|
|
@@ -2895,11 +3117,7 @@ function createNodeIndex(builder) {
|
|
|
2895
3117
|
}
|
|
2896
3118
|
const cached = nodeCache.get(input);
|
|
2897
3119
|
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);
|
|
3120
|
+
return (_e = (_d2 = nodeMap.get(input)) == null ? void 0 : _d2.node) != null ? _e : mkUnknown(input);
|
|
2903
3121
|
};
|
|
2904
3122
|
const mkUnknown = (id) => {
|
|
2905
3123
|
const u = {
|
|
@@ -2919,8 +3137,693 @@ function createNodeIndex(builder) {
|
|
|
2919
3137
|
getOption
|
|
2920
3138
|
};
|
|
2921
3139
|
}
|
|
3140
|
+
|
|
3141
|
+
// src/utils/prune-fallbacks.ts
|
|
3142
|
+
function pruneInvalidNodeFallbacks(props, services, settings) {
|
|
3143
|
+
var _a, _b;
|
|
3144
|
+
const fb = props.fallbacks;
|
|
3145
|
+
if (!(fb == null ? void 0 : fb.nodes) || Object.keys(fb.nodes).length === 0) {
|
|
3146
|
+
return { props, removed: [] };
|
|
3147
|
+
}
|
|
3148
|
+
const nodeContexts = /* @__PURE__ */ new Map();
|
|
3149
|
+
const nodePrimary = /* @__PURE__ */ new Map();
|
|
3150
|
+
for (const nodeId of Object.keys(fb.nodes)) {
|
|
3151
|
+
const tag = props.filters.find((t) => t.id === nodeId);
|
|
3152
|
+
if (tag) {
|
|
3153
|
+
nodeContexts.set(nodeId, [tag.id]);
|
|
3154
|
+
nodePrimary.set(nodeId, tag.service_id);
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
const field = props.fields.find((f) => Array.isArray(f.options) && f.options.some((o) => o.id === nodeId));
|
|
3158
|
+
if (field) {
|
|
3159
|
+
const contexts = toBindArray(field.bind_id);
|
|
3160
|
+
nodeContexts.set(nodeId, contexts);
|
|
3161
|
+
const opt = field.options.find((o) => o.id === nodeId);
|
|
3162
|
+
nodePrimary.set(nodeId, opt.service_id);
|
|
3163
|
+
continue;
|
|
3164
|
+
}
|
|
3165
|
+
nodeContexts.set(nodeId, []);
|
|
3166
|
+
nodePrimary.set(nodeId, void 0);
|
|
3167
|
+
}
|
|
3168
|
+
const diags = collectFailedFallbacks(props, services, { ...settings, mode: "dev" });
|
|
3169
|
+
const failuresByPair = /* @__PURE__ */ new Map();
|
|
3170
|
+
const totalContextsByNode = /* @__PURE__ */ new Map();
|
|
3171
|
+
for (const [nodeId, ctxs] of nodeContexts.entries()) {
|
|
3172
|
+
totalContextsByNode.set(nodeId, Math.max(1, ctxs.length));
|
|
3173
|
+
}
|
|
3174
|
+
for (const d of diags) {
|
|
3175
|
+
if (d.scope !== "node") continue;
|
|
3176
|
+
const key = `${d.nodeId}::${String(d.candidate)}`;
|
|
3177
|
+
let rec = failuresByPair.get(key);
|
|
3178
|
+
if (!rec) {
|
|
3179
|
+
rec = { reasons: /* @__PURE__ */ new Set(), contexts: /* @__PURE__ */ new Set() };
|
|
3180
|
+
failuresByPair.set(key, rec);
|
|
3181
|
+
}
|
|
3182
|
+
rec.reasons.add(d.reason);
|
|
3183
|
+
if (d.tagContext) rec.contexts.add(d.tagContext);
|
|
3184
|
+
}
|
|
3185
|
+
const prunedNodes = {};
|
|
3186
|
+
const removed = [];
|
|
3187
|
+
for (const [nodeId, list] of Object.entries(fb.nodes)) {
|
|
3188
|
+
const contexts = (_a = nodeContexts.get(nodeId)) != null ? _a : [];
|
|
3189
|
+
const totalContexts = Math.max(1, contexts.length);
|
|
3190
|
+
const keep = [];
|
|
3191
|
+
for (const cand of list) {
|
|
3192
|
+
const key = `${nodeId}::${String(cand)}`;
|
|
3193
|
+
const rec = failuresByPair.get(key);
|
|
3194
|
+
if (!rec) {
|
|
3195
|
+
keep.push(cand);
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
const failedContextsCount = rec.contexts.size > 0 ? rec.contexts.size : totalContexts;
|
|
3199
|
+
const failsAll = failedContextsCount >= totalContexts;
|
|
3200
|
+
if (failsAll) {
|
|
3201
|
+
removed.push({
|
|
3202
|
+
nodeId,
|
|
3203
|
+
candidate: cand,
|
|
3204
|
+
reasons: Array.from(rec.reasons),
|
|
3205
|
+
contexts: contexts.length ? contexts.slice() : void 0
|
|
3206
|
+
});
|
|
3207
|
+
} else {
|
|
3208
|
+
keep.push(cand);
|
|
3209
|
+
}
|
|
3210
|
+
}
|
|
3211
|
+
if (keep.length) prunedNodes[nodeId] = keep;
|
|
3212
|
+
}
|
|
3213
|
+
const outProps = {
|
|
3214
|
+
...props,
|
|
3215
|
+
fallbacks: {
|
|
3216
|
+
...((_b = props.fallbacks) == null ? void 0 : _b.global) ? { global: props.fallbacks.global } : {},
|
|
3217
|
+
...Object.keys(prunedNodes).length ? { nodes: prunedNodes } : {}
|
|
3218
|
+
}
|
|
3219
|
+
};
|
|
3220
|
+
return { props: outProps, removed };
|
|
3221
|
+
}
|
|
3222
|
+
function toBindArray(bind) {
|
|
3223
|
+
if (!bind) return [];
|
|
3224
|
+
return Array.isArray(bind) ? bind.slice() : [bind];
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// src/utils/util.ts
|
|
3228
|
+
function toFiniteNumber(v) {
|
|
3229
|
+
const n = Number(v);
|
|
3230
|
+
return Number.isFinite(n) ? n : NaN;
|
|
3231
|
+
}
|
|
3232
|
+
function constraintFitOk(svcMap, candidate, constraints) {
|
|
3233
|
+
const cap = svcMap[Number(candidate)];
|
|
3234
|
+
if (!cap) return false;
|
|
3235
|
+
if (constraints.dripfeed === true && !cap.dripfeed) return false;
|
|
3236
|
+
if (constraints.refill === true && !cap.refill) return false;
|
|
3237
|
+
return !(constraints.cancel === true && !cap.cancel);
|
|
3238
|
+
}
|
|
3239
|
+
function rateOk(svcMap, candidate, primary, policy) {
|
|
3240
|
+
var _a, _b, _c;
|
|
3241
|
+
const cand = svcMap[Number(candidate)];
|
|
3242
|
+
const prim = svcMap[Number(primary)];
|
|
3243
|
+
if (!cand || !prim) return false;
|
|
3244
|
+
const cRate = toFiniteNumber(cand.rate);
|
|
3245
|
+
const pRate = toFiniteNumber(prim.rate);
|
|
3246
|
+
if (!Number.isFinite(cRate) || !Number.isFinite(pRate)) return false;
|
|
3247
|
+
const rp = (_a = policy.ratePolicy) != null ? _a : { kind: "lte_primary" };
|
|
3248
|
+
switch (rp.kind) {
|
|
3249
|
+
case "lte_primary":
|
|
3250
|
+
return cRate <= pRate;
|
|
3251
|
+
case "within_pct": {
|
|
3252
|
+
const pct = Math.max(0, (_b = rp.pct) != null ? _b : 0);
|
|
3253
|
+
return cRate <= pRate * (1 + pct / 100);
|
|
3254
|
+
}
|
|
3255
|
+
case "at_least_pct_lower": {
|
|
3256
|
+
const pct = Math.max(0, (_c = rp.pct) != null ? _c : 0);
|
|
3257
|
+
return cRate <= pRate * (1 - pct / 100);
|
|
3258
|
+
}
|
|
3259
|
+
default:
|
|
3260
|
+
return false;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
// src/utils/build-order-snapshot.ts
|
|
3265
|
+
function buildOrderSnapshot(props, builder, selection, services, settings = {}) {
|
|
3266
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3267
|
+
const mode = (_a = settings.mode) != null ? _a : "prod";
|
|
3268
|
+
const hostDefaultQty = Number.isFinite(
|
|
3269
|
+
(_b = settings.hostDefaultQuantity) != null ? _b : 1
|
|
3270
|
+
) ? settings.hostDefaultQuantity : 1;
|
|
3271
|
+
const fbSettings = {
|
|
3272
|
+
requireConstraintFit: true,
|
|
3273
|
+
ratePolicy: { kind: "lte_primary" },
|
|
3274
|
+
selectionStrategy: "priority",
|
|
3275
|
+
mode: mode === "dev" ? "dev" : "strict",
|
|
3276
|
+
...(_c = settings.fallback) != null ? _c : {}
|
|
3277
|
+
};
|
|
3278
|
+
const builtAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3279
|
+
const tagId = selection.activeTagId;
|
|
3280
|
+
const selectedOptionKeys = toSelectedOptionKeys(
|
|
3281
|
+
selection.optionSelectionsByFieldId
|
|
3282
|
+
);
|
|
3283
|
+
const visibleFieldIds = builder.visibleFields(
|
|
3284
|
+
tagId,
|
|
3285
|
+
selectedOptionKeys
|
|
3286
|
+
);
|
|
3287
|
+
const tagById = new Map(
|
|
3288
|
+
((_d = props.filters) != null ? _d : []).map((t) => [t.id, t])
|
|
3289
|
+
);
|
|
3290
|
+
const fieldById = new Map(
|
|
3291
|
+
((_e = props.fields) != null ? _e : []).map((f) => [f.id, f])
|
|
3292
|
+
);
|
|
3293
|
+
const tagConstraints = (_g = (_f = tagById.get(tagId)) == null ? void 0 : _f.constraints) != null ? _g : void 0;
|
|
3294
|
+
const selectionFields = visibleFieldIds.map((fid) => fieldById.get(fid)).filter((f) => !!f).map((f) => {
|
|
3295
|
+
var _a2;
|
|
3296
|
+
const optIds = isOptionBased(f) ? (_a2 = selection.optionSelectionsByFieldId[f.id]) != null ? _a2 : [] : void 0;
|
|
3297
|
+
return {
|
|
3298
|
+
id: f.id,
|
|
3299
|
+
type: String(f.type),
|
|
3300
|
+
...optIds && optIds.length ? { selectedOptions: optIds } : {}
|
|
3301
|
+
};
|
|
3302
|
+
});
|
|
3303
|
+
const { formValues, selections } = buildInputs(
|
|
3304
|
+
visibleFieldIds,
|
|
3305
|
+
fieldById,
|
|
3306
|
+
selection
|
|
3307
|
+
);
|
|
3308
|
+
const qtyRes = resolveQuantity(
|
|
3309
|
+
visibleFieldIds,
|
|
3310
|
+
fieldById,
|
|
3311
|
+
selection,
|
|
3312
|
+
hostDefaultQty
|
|
3313
|
+
);
|
|
3314
|
+
const quantity = qtyRes.quantity;
|
|
3315
|
+
const quantitySource = qtyRes.source;
|
|
3316
|
+
const { serviceMap, servicesList } = resolveServices(
|
|
3317
|
+
tagId,
|
|
3318
|
+
visibleFieldIds,
|
|
3319
|
+
selection,
|
|
3320
|
+
tagById,
|
|
3321
|
+
fieldById
|
|
3322
|
+
);
|
|
3323
|
+
const { min, max } = resolveMinMax(servicesList, services);
|
|
3324
|
+
const prunedFallbacks = pruneFallbacksConservative(
|
|
3325
|
+
props.fallbacks,
|
|
3326
|
+
{ tagId, constraints: tagConstraints, serviceMap, servicesList },
|
|
3327
|
+
services,
|
|
3328
|
+
fbSettings
|
|
3329
|
+
);
|
|
3330
|
+
const utilities = collectUtilityLineItems(
|
|
3331
|
+
visibleFieldIds,
|
|
3332
|
+
fieldById,
|
|
3333
|
+
selection,
|
|
3334
|
+
quantity
|
|
3335
|
+
);
|
|
3336
|
+
const warnings = mode === "dev" ? buildDevWarnings(
|
|
3337
|
+
props,
|
|
3338
|
+
services,
|
|
3339
|
+
tagId,
|
|
3340
|
+
serviceMap,
|
|
3341
|
+
prunedFallbacks.original,
|
|
3342
|
+
prunedFallbacks.pruned,
|
|
3343
|
+
fieldById,
|
|
3344
|
+
visibleFieldIds,
|
|
3345
|
+
selection
|
|
3346
|
+
) : void 0;
|
|
3347
|
+
const snapshotPolicy = toSnapshotPolicy(fbSettings);
|
|
3348
|
+
const meta = {
|
|
3349
|
+
schema_version: props.schema_version,
|
|
3350
|
+
workspaceId: settings.workspaceId,
|
|
3351
|
+
builder: settings.builderCommit ? { commit: settings.builderCommit } : void 0,
|
|
3352
|
+
context: {
|
|
3353
|
+
tag: tagId,
|
|
3354
|
+
constraints: tagConstraints != null ? tagConstraints : {},
|
|
3355
|
+
nodeContexts: buildNodeContexts(
|
|
3356
|
+
tagId,
|
|
3357
|
+
visibleFieldIds,
|
|
3358
|
+
fieldById,
|
|
3359
|
+
selection
|
|
3360
|
+
),
|
|
3361
|
+
policy: snapshotPolicy
|
|
3362
|
+
}
|
|
3363
|
+
};
|
|
3364
|
+
const snapshot = {
|
|
3365
|
+
version: "1",
|
|
3366
|
+
mode,
|
|
3367
|
+
builtAt,
|
|
3368
|
+
selection: {
|
|
3369
|
+
tag: tagId,
|
|
3370
|
+
fields: selectionFields
|
|
3371
|
+
},
|
|
3372
|
+
inputs: {
|
|
3373
|
+
form: formValues,
|
|
3374
|
+
selections
|
|
3375
|
+
},
|
|
3376
|
+
min,
|
|
3377
|
+
max: max != null ? max : min,
|
|
3378
|
+
quantity,
|
|
3379
|
+
quantitySource,
|
|
3380
|
+
services: servicesList,
|
|
3381
|
+
serviceMap,
|
|
3382
|
+
...prunedFallbacks.pruned ? { fallbacks: prunedFallbacks.pruned } : {},
|
|
3383
|
+
...utilities.length ? { utilities } : {},
|
|
3384
|
+
...warnings ? { warnings } : {},
|
|
3385
|
+
meta
|
|
3386
|
+
};
|
|
3387
|
+
return snapshot;
|
|
3388
|
+
}
|
|
3389
|
+
function isOptionBased(f) {
|
|
3390
|
+
const hasOptions = Array.isArray(f.options) && f.options.length > 0;
|
|
3391
|
+
return hasOptions || isMultiField(f);
|
|
3392
|
+
}
|
|
3393
|
+
function toSelectedOptionKeys(byField) {
|
|
3394
|
+
const keys = [];
|
|
3395
|
+
for (const [fieldId, optionIds] of Object.entries(byField != null ? byField : {})) {
|
|
3396
|
+
for (const optId of optionIds != null ? optionIds : []) {
|
|
3397
|
+
keys.push(`${fieldId}::${optId}`);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
return keys;
|
|
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 (!isOptionBased(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
|