@timeax/digital-service-engine 0.0.6 → 0.0.8
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 +1149 -504
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +114 -42
- package/dist/core/index.d.ts +114 -42
- package/dist/core/index.js +1147 -504
- package/dist/core/index.js.map +1 -1
- package/dist/react/index.cjs +4763 -3239
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +400 -244
- package/dist/react/index.d.ts +400 -244
- package/dist/react/index.js +4763 -3239
- package/dist/react/index.js.map +1 -1
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +163 -66
- package/dist/schema/index.d.ts +163 -66
- package/dist/workspace/index.cjs +5622 -2576
- package/dist/workspace/index.cjs.map +1 -1
- package/dist/workspace/index.d.cts +494 -263
- package/dist/workspace/index.d.ts +494 -263
- package/dist/workspace/index.js +5642 -2598
- package/dist/workspace/index.js.map +1 -1
- package/package.json +10 -10
- package/schema/editor-snapshot.schema.json +373 -9
- package/schema/service-props.schema.json +86 -9
package/dist/core/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
interface ButtonValue {
|
|
2
2
|
id: string;
|
|
3
3
|
value: string | number;
|
|
4
|
-
service_id?:
|
|
4
|
+
service_id?: ServiceIdRef;
|
|
5
5
|
pricing_role?: "base" | "utility";
|
|
6
6
|
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
7
7
|
}
|
|
@@ -10,16 +10,23 @@ type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
|
|
|
10
10
|
type QuantityRule = {
|
|
11
11
|
valueBy: "value" | "length" | "eval";
|
|
12
12
|
code?: string;
|
|
13
|
+
multiply?: number;
|
|
14
|
+
clamp?: {
|
|
15
|
+
min?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
};
|
|
18
|
+
fallback?: number;
|
|
13
19
|
};
|
|
14
20
|
type UtilityLineItem = {
|
|
15
21
|
nodeId: string;
|
|
16
22
|
mode: UtilityMode;
|
|
17
23
|
rate: number;
|
|
24
|
+
percentBase?: "service_total" | "base_service" | "all";
|
|
25
|
+
label?: string;
|
|
18
26
|
inputs: {
|
|
19
27
|
quantity: number;
|
|
20
28
|
value?: Scalar | Scalar[];
|
|
21
|
-
valueBy?: "value" | "length"
|
|
22
|
-
evalCodeUsed?: boolean;
|
|
29
|
+
valueBy?: "value" | "length";
|
|
23
30
|
};
|
|
24
31
|
};
|
|
25
32
|
type ServiceFallbacks = ServiceFallback;
|
|
@@ -45,8 +52,16 @@ type SnapshotContext = {
|
|
|
45
52
|
/** Client pruning policy used (so server can mirror/compare). */
|
|
46
53
|
policy: {
|
|
47
54
|
ratePolicy: {
|
|
48
|
-
kind: "
|
|
49
|
-
|
|
55
|
+
kind: "eq_primary";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "lte_primary";
|
|
58
|
+
pct: number;
|
|
59
|
+
} | {
|
|
60
|
+
kind: "within_pct";
|
|
61
|
+
pct: number;
|
|
62
|
+
} | {
|
|
63
|
+
kind: "at_least_pct_lower";
|
|
64
|
+
pct: number;
|
|
50
65
|
};
|
|
51
66
|
requireConstraintFit: boolean;
|
|
52
67
|
};
|
|
@@ -154,7 +169,7 @@ type NodeRef = {
|
|
|
154
169
|
};
|
|
155
170
|
type NodeMap = Map<string, NodeRef>;
|
|
156
171
|
|
|
157
|
-
type ValidationCode = "root_missing" | "cycle_in_tags" | "bad_bind_reference" | "duplicate_id" | "duplicate_tag_label" | "duplicate_field_name" | "label_missing" | "duplicate_visible_label" | "bad_option_key" | "option_include_exclude_conflict" | "service_field_missing_service_id" | "user_input_field_has_service_option" | "rate_mismatch_across_base" | "utility_without_base" | "unsupported_constraint" | "constraint_contradiction" | "custom_component_missing" | "policy_violation" | "field_unbound" | "constraint_overridden" | "unsupported_constraint_option" | "custom_component_unresolvable" | "quantity_multiple_markers" | "utility_with_service_id" | "utility_missing_rate" | "utility_invalid_mode" | "fallback_bad_node" | "fallback_unknown_service" | "fallback_cycle" | "fallback_no_primary" | "fallback_rate_violation" | "fallback_constraint_mismatch" | "fallback_no_tag_context";
|
|
172
|
+
type ValidationCode = "root_missing" | "cycle_in_tags" | "bad_bind_reference" | "duplicate_id" | "duplicate_tag_label" | "duplicate_field_name" | "label_missing" | "duplicate_visible_label" | "bad_option_key" | "option_include_exclude_conflict" | "service_field_missing_service_id" | "user_input_field_has_service_option" | "rate_mismatch_across_base" | "rate_coherence_violation" | "utility_without_base" | "unsupported_constraint" | "constraint_contradiction" | "custom_component_missing" | "policy_violation" | "field_unbound" | "constraint_overridden" | "unsupported_constraint_option" | "custom_component_unresolvable" | "quantity_multiple_markers" | "utility_with_service_id" | "utility_missing_rate" | "utility_invalid_mode" | "fallback_bad_node" | "fallback_unknown_service" | "fallback_cycle" | "fallback_no_primary" | "fallback_rate_violation" | "fallback_constraint_mismatch" | "fallback_no_tag_context" | "field_validation_invalid_rule" | "field_validation_invalid_op" | "field_validation_eval_missing_code" | "field_validation_between_missing_bounds" | "field_validation_match_missing_pattern";
|
|
158
173
|
type ValidationError = {
|
|
159
174
|
code: ValidationCode;
|
|
160
175
|
message: string;
|
|
@@ -210,7 +225,10 @@ type ValidatorOptions = {
|
|
|
210
225
|
fallbackSettings?: FallbackSettings;
|
|
211
226
|
};
|
|
212
227
|
type RatePolicy = {
|
|
228
|
+
kind: "eq_primary";
|
|
229
|
+
} | {
|
|
213
230
|
kind: "lte_primary";
|
|
231
|
+
pct: number;
|
|
214
232
|
} | {
|
|
215
233
|
kind: "within_pct";
|
|
216
234
|
pct: number;
|
|
@@ -221,7 +239,7 @@ type RatePolicy = {
|
|
|
221
239
|
type FallbackSettings = {
|
|
222
240
|
/** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
|
|
223
241
|
requireConstraintFit?: boolean;
|
|
224
|
-
/** Rate rule policy. Default: { kind: 'lte_primary'
|
|
242
|
+
/** Rate rule policy. Default: { kind: 'lte_primary', pct: 5 }. */
|
|
225
243
|
ratePolicy?: RatePolicy;
|
|
226
244
|
/** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
|
|
227
245
|
selectionStrategy?: "priority" | "cheapest";
|
|
@@ -229,7 +247,6 @@ type FallbackSettings = {
|
|
|
229
247
|
mode?: "strict" | "dev";
|
|
230
248
|
};
|
|
231
249
|
|
|
232
|
-
type ServiceIdRef = number | string;
|
|
233
250
|
type NodeIdRef = string;
|
|
234
251
|
type ServiceFallback = {
|
|
235
252
|
/** Node-scoped fallbacks: prefer these when that node’s primary service fails */
|
|
@@ -322,6 +339,7 @@ type FallbackMutationOptions = {
|
|
|
322
339
|
index?: number;
|
|
323
340
|
};
|
|
324
341
|
|
|
342
|
+
type ServiceIdRef = IdType;
|
|
325
343
|
type PricingRole = "base" | "utility";
|
|
326
344
|
type FieldType = "custom" | (string & {});
|
|
327
345
|
/** ── Marker types (live inside meta; non-breaking) ───────────────────── */
|
|
@@ -357,11 +375,25 @@ interface BaseFieldUI {
|
|
|
357
375
|
/** Host-defined prop names → runtime default values (untyped base) */
|
|
358
376
|
defaults?: Record<string, unknown>;
|
|
359
377
|
}
|
|
378
|
+
type FieldValidationValueBy = "value" | "length" | "eval";
|
|
379
|
+
type FieldValidationOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "between" | "in" | "nin" | "truthy" | "falsy" | "match";
|
|
380
|
+
type FieldValidationRule = {
|
|
381
|
+
valueBy?: FieldValidationValueBy;
|
|
382
|
+
op: FieldValidationOp;
|
|
383
|
+
value?: unknown;
|
|
384
|
+
min?: number;
|
|
385
|
+
max?: number;
|
|
386
|
+
values?: unknown[];
|
|
387
|
+
pattern?: string;
|
|
388
|
+
flags?: string;
|
|
389
|
+
code?: string;
|
|
390
|
+
message?: string;
|
|
391
|
+
};
|
|
360
392
|
type FieldOption = {
|
|
361
393
|
id: string;
|
|
362
394
|
label: string;
|
|
363
395
|
value?: string | number;
|
|
364
|
-
service_id?:
|
|
396
|
+
service_id?: ServiceIdRef;
|
|
365
397
|
pricing_role?: PricingRole;
|
|
366
398
|
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
367
399
|
};
|
|
@@ -374,6 +406,7 @@ type Field = BaseFieldUI & {
|
|
|
374
406
|
description?: string;
|
|
375
407
|
component?: string;
|
|
376
408
|
pricing_role?: PricingRole;
|
|
409
|
+
validation?: FieldValidationRule[];
|
|
377
410
|
meta?: Record<string, unknown> & QuantityMark & UtilityMark & {
|
|
378
411
|
multi?: boolean;
|
|
379
412
|
};
|
|
@@ -382,14 +415,14 @@ type Field = BaseFieldUI & {
|
|
|
382
415
|
service_id?: undefined;
|
|
383
416
|
} | ({
|
|
384
417
|
button: true;
|
|
385
|
-
service_id?:
|
|
418
|
+
service_id?: ServiceIdRef;
|
|
386
419
|
} & WithQuantityDefault));
|
|
387
420
|
type ConstraintKey = string;
|
|
388
421
|
type Tag = {
|
|
389
422
|
id: string;
|
|
390
423
|
label: string;
|
|
391
424
|
bind_id?: string;
|
|
392
|
-
service_id?:
|
|
425
|
+
service_id?: ServiceIdRef;
|
|
393
426
|
includes?: string[];
|
|
394
427
|
excludes?: string[];
|
|
395
428
|
meta?: Record<string, unknown> & WithQuantityDefault;
|
|
@@ -475,6 +508,7 @@ type NormaliseOptions = {
|
|
|
475
508
|
constraints?: string[];
|
|
476
509
|
};
|
|
477
510
|
declare function normalise(input: unknown, opts?: NormaliseOptions): ServiceProps;
|
|
511
|
+
declare function normalizeFieldValidation(input: unknown): FieldValidationRule[] | undefined;
|
|
478
512
|
|
|
479
513
|
/**
|
|
480
514
|
* Core synchronous validate.
|
|
@@ -491,8 +525,6 @@ declare function validateAsync(props: ServiceProps, ctx?: ValidatorOptions): Pro
|
|
|
491
525
|
/** Options you can set on the builder (used for validation/visibility) */
|
|
492
526
|
type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
|
|
493
527
|
serviceMap?: DgpServiceMap;
|
|
494
|
-
/** max history entries for undo/redo */
|
|
495
|
-
historyLimit?: number;
|
|
496
528
|
/**
|
|
497
529
|
* Field ids whose options should be shown as nodes in the graph.
|
|
498
530
|
* If a field id is NOT in this set, its options are not materialized as nodes:
|
|
@@ -517,9 +549,6 @@ interface Builder {
|
|
|
517
549
|
visibleFields(tagId: string, selectedOptionKeys?: string[]): string[];
|
|
518
550
|
/** Update builder options (validator context etc.) */
|
|
519
551
|
setOptions(patch: Partial<BuilderOptions>): void;
|
|
520
|
-
/** History */
|
|
521
|
-
undo(): boolean;
|
|
522
|
-
redo(): boolean;
|
|
523
552
|
/** Access the current props (already normalised) */
|
|
524
553
|
getProps(): ServiceProps;
|
|
525
554
|
/** Service map for validation/rules */
|
|
@@ -583,48 +612,50 @@ declare function getFallbackRegistrationInfo(props: ServiceProps, nodeId: NodeId
|
|
|
583
612
|
tagContexts: string[];
|
|
584
613
|
};
|
|
585
614
|
|
|
586
|
-
type
|
|
615
|
+
type SimulationAnchor = {
|
|
587
616
|
kind: "field" | "option";
|
|
588
617
|
id: string;
|
|
618
|
+
fieldId: string;
|
|
589
619
|
label?: string;
|
|
590
|
-
service_id: number;
|
|
591
|
-
rate: number;
|
|
592
620
|
};
|
|
593
|
-
|
|
594
|
-
type RateCoherenceDiagnostic = {
|
|
621
|
+
type SharedDiagnostic = {
|
|
595
622
|
scope: "visible_group";
|
|
596
623
|
tagId: string;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
624
|
+
nodeId: string;
|
|
625
|
+
message: string;
|
|
626
|
+
simulationAnchor?: SimulationAnchor;
|
|
627
|
+
invalidFieldIds: string[];
|
|
628
|
+
};
|
|
629
|
+
type RateCoherenceDiagnostic = (SharedDiagnostic & {
|
|
630
|
+
kind: "contextual";
|
|
631
|
+
primary: {
|
|
632
|
+
nodeId: string;
|
|
633
|
+
fieldId: string;
|
|
606
634
|
label?: string;
|
|
607
|
-
|
|
635
|
+
refKind: "single" | "multi";
|
|
636
|
+
service_id?: ServiceIdRef;
|
|
608
637
|
rate: number;
|
|
609
638
|
};
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
message: string;
|
|
613
|
-
/** Which button triggered this simulation */
|
|
614
|
-
simulationAnchor: {
|
|
615
|
-
kind: "field" | "option";
|
|
616
|
-
id: string;
|
|
639
|
+
offender: {
|
|
640
|
+
nodeId: string;
|
|
617
641
|
fieldId: string;
|
|
618
642
|
label?: string;
|
|
643
|
+
refKind: "single" | "multi";
|
|
644
|
+
service_id?: ServiceIdRef;
|
|
645
|
+
rate: number;
|
|
619
646
|
};
|
|
620
|
-
|
|
621
|
-
|
|
647
|
+
policy: RatePolicy["kind"];
|
|
648
|
+
policyPct?: number;
|
|
649
|
+
}) | (SharedDiagnostic & {
|
|
650
|
+
kind: "internal_field";
|
|
651
|
+
fieldId: string;
|
|
652
|
+
});
|
|
622
653
|
declare function validateRateCoherenceDeep(params: {
|
|
623
654
|
builder: Builder;
|
|
624
655
|
services: DgpServiceMap;
|
|
625
656
|
tagId: string;
|
|
626
|
-
/** Optional rate policy (defaults to { kind: 'lte_primary' }) */
|
|
627
657
|
ratePolicy?: RatePolicy;
|
|
658
|
+
invalidFieldIds?: Iterable<string>;
|
|
628
659
|
}): RateCoherenceDiagnostic[];
|
|
629
660
|
|
|
630
661
|
type NodeKind = "tag" | "field" | "option" | "unknown";
|
|
@@ -704,6 +735,47 @@ interface NodeIndex {
|
|
|
704
735
|
}
|
|
705
736
|
declare function createNodeIndex(builder: Builder): NodeIndex;
|
|
706
737
|
|
|
738
|
+
type PolicyDiagnostic = {
|
|
739
|
+
ruleIndex: number;
|
|
740
|
+
ruleId?: string;
|
|
741
|
+
severity: "error" | "warning";
|
|
742
|
+
message: string;
|
|
743
|
+
path?: string;
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
type ServiceCheck = {
|
|
747
|
+
id: ServiceIdRef;
|
|
748
|
+
ok: boolean;
|
|
749
|
+
fitsConstraints: boolean;
|
|
750
|
+
passesRate: boolean;
|
|
751
|
+
passesPolicies: boolean;
|
|
752
|
+
policyErrors?: string[];
|
|
753
|
+
policyWarnings?: string[];
|
|
754
|
+
reasons: Array<"constraint_mismatch" | "rate_policy" | "policy_error" | "missing_capability">;
|
|
755
|
+
cap?: DgpServiceCapability;
|
|
756
|
+
rate?: number;
|
|
757
|
+
};
|
|
758
|
+
type FilterServicesForVisibleGroupInput = {
|
|
759
|
+
candidates: ServiceIdRef[];
|
|
760
|
+
context: {
|
|
761
|
+
tagId: string;
|
|
762
|
+
selectedButtons?: string[];
|
|
763
|
+
usedServiceIds: ServiceIdRef[];
|
|
764
|
+
effectiveConstraints?: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
|
|
765
|
+
policies?: unknown;
|
|
766
|
+
fallback?: FallbackSettings;
|
|
767
|
+
strictSafety?: boolean;
|
|
768
|
+
enforcePolicies?: boolean;
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
type FilterServicesForVisibleGroupResult = {
|
|
772
|
+
checks: ServiceCheck[];
|
|
773
|
+
diagnostics?: PolicyDiagnostic[];
|
|
774
|
+
};
|
|
775
|
+
declare function filterServicesForVisibleGroup(input: FilterServicesForVisibleGroupInput, deps: {
|
|
776
|
+
builder: Builder;
|
|
777
|
+
}): FilterServicesForVisibleGroupResult;
|
|
778
|
+
|
|
707
779
|
type BuildOrderSnapshotSettings = {
|
|
708
780
|
mode?: "prod" | "dev";
|
|
709
781
|
hostDefaultQuantity?: number;
|
|
@@ -765,4 +837,4 @@ interface FallbackEditor {
|
|
|
765
837
|
}
|
|
766
838
|
declare function createFallbackEditor(options?: FallbackEditorOptions): FallbackEditor;
|
|
767
839
|
|
|
768
|
-
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FallbackEditor, type FieldNode, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createFallbackEditor, createNodeIndex, getEligibleFallbacks, getFallbackRegistrationInfo, normalise, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|
|
840
|
+
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FallbackEditor, type FieldNode, type FilterServicesForVisibleGroupInput, type FilterServicesForVisibleGroupResult, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type ServiceCheck, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createFallbackEditor, createNodeIndex, filterServicesForVisibleGroup, getEligibleFallbacks, getFallbackRegistrationInfo, normalise, normalizeFieldValidation, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
interface ButtonValue {
|
|
2
2
|
id: string;
|
|
3
3
|
value: string | number;
|
|
4
|
-
service_id?:
|
|
4
|
+
service_id?: ServiceIdRef;
|
|
5
5
|
pricing_role?: "base" | "utility";
|
|
6
6
|
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
7
7
|
}
|
|
@@ -10,16 +10,23 @@ type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
|
|
|
10
10
|
type QuantityRule = {
|
|
11
11
|
valueBy: "value" | "length" | "eval";
|
|
12
12
|
code?: string;
|
|
13
|
+
multiply?: number;
|
|
14
|
+
clamp?: {
|
|
15
|
+
min?: number;
|
|
16
|
+
max?: number;
|
|
17
|
+
};
|
|
18
|
+
fallback?: number;
|
|
13
19
|
};
|
|
14
20
|
type UtilityLineItem = {
|
|
15
21
|
nodeId: string;
|
|
16
22
|
mode: UtilityMode;
|
|
17
23
|
rate: number;
|
|
24
|
+
percentBase?: "service_total" | "base_service" | "all";
|
|
25
|
+
label?: string;
|
|
18
26
|
inputs: {
|
|
19
27
|
quantity: number;
|
|
20
28
|
value?: Scalar | Scalar[];
|
|
21
|
-
valueBy?: "value" | "length"
|
|
22
|
-
evalCodeUsed?: boolean;
|
|
29
|
+
valueBy?: "value" | "length";
|
|
23
30
|
};
|
|
24
31
|
};
|
|
25
32
|
type ServiceFallbacks = ServiceFallback;
|
|
@@ -45,8 +52,16 @@ type SnapshotContext = {
|
|
|
45
52
|
/** Client pruning policy used (so server can mirror/compare). */
|
|
46
53
|
policy: {
|
|
47
54
|
ratePolicy: {
|
|
48
|
-
kind: "
|
|
49
|
-
|
|
55
|
+
kind: "eq_primary";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "lte_primary";
|
|
58
|
+
pct: number;
|
|
59
|
+
} | {
|
|
60
|
+
kind: "within_pct";
|
|
61
|
+
pct: number;
|
|
62
|
+
} | {
|
|
63
|
+
kind: "at_least_pct_lower";
|
|
64
|
+
pct: number;
|
|
50
65
|
};
|
|
51
66
|
requireConstraintFit: boolean;
|
|
52
67
|
};
|
|
@@ -154,7 +169,7 @@ type NodeRef = {
|
|
|
154
169
|
};
|
|
155
170
|
type NodeMap = Map<string, NodeRef>;
|
|
156
171
|
|
|
157
|
-
type ValidationCode = "root_missing" | "cycle_in_tags" | "bad_bind_reference" | "duplicate_id" | "duplicate_tag_label" | "duplicate_field_name" | "label_missing" | "duplicate_visible_label" | "bad_option_key" | "option_include_exclude_conflict" | "service_field_missing_service_id" | "user_input_field_has_service_option" | "rate_mismatch_across_base" | "utility_without_base" | "unsupported_constraint" | "constraint_contradiction" | "custom_component_missing" | "policy_violation" | "field_unbound" | "constraint_overridden" | "unsupported_constraint_option" | "custom_component_unresolvable" | "quantity_multiple_markers" | "utility_with_service_id" | "utility_missing_rate" | "utility_invalid_mode" | "fallback_bad_node" | "fallback_unknown_service" | "fallback_cycle" | "fallback_no_primary" | "fallback_rate_violation" | "fallback_constraint_mismatch" | "fallback_no_tag_context";
|
|
172
|
+
type ValidationCode = "root_missing" | "cycle_in_tags" | "bad_bind_reference" | "duplicate_id" | "duplicate_tag_label" | "duplicate_field_name" | "label_missing" | "duplicate_visible_label" | "bad_option_key" | "option_include_exclude_conflict" | "service_field_missing_service_id" | "user_input_field_has_service_option" | "rate_mismatch_across_base" | "rate_coherence_violation" | "utility_without_base" | "unsupported_constraint" | "constraint_contradiction" | "custom_component_missing" | "policy_violation" | "field_unbound" | "constraint_overridden" | "unsupported_constraint_option" | "custom_component_unresolvable" | "quantity_multiple_markers" | "utility_with_service_id" | "utility_missing_rate" | "utility_invalid_mode" | "fallback_bad_node" | "fallback_unknown_service" | "fallback_cycle" | "fallback_no_primary" | "fallback_rate_violation" | "fallback_constraint_mismatch" | "fallback_no_tag_context" | "field_validation_invalid_rule" | "field_validation_invalid_op" | "field_validation_eval_missing_code" | "field_validation_between_missing_bounds" | "field_validation_match_missing_pattern";
|
|
158
173
|
type ValidationError = {
|
|
159
174
|
code: ValidationCode;
|
|
160
175
|
message: string;
|
|
@@ -210,7 +225,10 @@ type ValidatorOptions = {
|
|
|
210
225
|
fallbackSettings?: FallbackSettings;
|
|
211
226
|
};
|
|
212
227
|
type RatePolicy = {
|
|
228
|
+
kind: "eq_primary";
|
|
229
|
+
} | {
|
|
213
230
|
kind: "lte_primary";
|
|
231
|
+
pct: number;
|
|
214
232
|
} | {
|
|
215
233
|
kind: "within_pct";
|
|
216
234
|
pct: number;
|
|
@@ -221,7 +239,7 @@ type RatePolicy = {
|
|
|
221
239
|
type FallbackSettings = {
|
|
222
240
|
/** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
|
|
223
241
|
requireConstraintFit?: boolean;
|
|
224
|
-
/** Rate rule policy. Default: { kind: 'lte_primary'
|
|
242
|
+
/** Rate rule policy. Default: { kind: 'lte_primary', pct: 5 }. */
|
|
225
243
|
ratePolicy?: RatePolicy;
|
|
226
244
|
/** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
|
|
227
245
|
selectionStrategy?: "priority" | "cheapest";
|
|
@@ -229,7 +247,6 @@ type FallbackSettings = {
|
|
|
229
247
|
mode?: "strict" | "dev";
|
|
230
248
|
};
|
|
231
249
|
|
|
232
|
-
type ServiceIdRef = number | string;
|
|
233
250
|
type NodeIdRef = string;
|
|
234
251
|
type ServiceFallback = {
|
|
235
252
|
/** Node-scoped fallbacks: prefer these when that node’s primary service fails */
|
|
@@ -322,6 +339,7 @@ type FallbackMutationOptions = {
|
|
|
322
339
|
index?: number;
|
|
323
340
|
};
|
|
324
341
|
|
|
342
|
+
type ServiceIdRef = IdType;
|
|
325
343
|
type PricingRole = "base" | "utility";
|
|
326
344
|
type FieldType = "custom" | (string & {});
|
|
327
345
|
/** ── Marker types (live inside meta; non-breaking) ───────────────────── */
|
|
@@ -357,11 +375,25 @@ interface BaseFieldUI {
|
|
|
357
375
|
/** Host-defined prop names → runtime default values (untyped base) */
|
|
358
376
|
defaults?: Record<string, unknown>;
|
|
359
377
|
}
|
|
378
|
+
type FieldValidationValueBy = "value" | "length" | "eval";
|
|
379
|
+
type FieldValidationOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "between" | "in" | "nin" | "truthy" | "falsy" | "match";
|
|
380
|
+
type FieldValidationRule = {
|
|
381
|
+
valueBy?: FieldValidationValueBy;
|
|
382
|
+
op: FieldValidationOp;
|
|
383
|
+
value?: unknown;
|
|
384
|
+
min?: number;
|
|
385
|
+
max?: number;
|
|
386
|
+
values?: unknown[];
|
|
387
|
+
pattern?: string;
|
|
388
|
+
flags?: string;
|
|
389
|
+
code?: string;
|
|
390
|
+
message?: string;
|
|
391
|
+
};
|
|
360
392
|
type FieldOption = {
|
|
361
393
|
id: string;
|
|
362
394
|
label: string;
|
|
363
395
|
value?: string | number;
|
|
364
|
-
service_id?:
|
|
396
|
+
service_id?: ServiceIdRef;
|
|
365
397
|
pricing_role?: PricingRole;
|
|
366
398
|
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
367
399
|
};
|
|
@@ -374,6 +406,7 @@ type Field = BaseFieldUI & {
|
|
|
374
406
|
description?: string;
|
|
375
407
|
component?: string;
|
|
376
408
|
pricing_role?: PricingRole;
|
|
409
|
+
validation?: FieldValidationRule[];
|
|
377
410
|
meta?: Record<string, unknown> & QuantityMark & UtilityMark & {
|
|
378
411
|
multi?: boolean;
|
|
379
412
|
};
|
|
@@ -382,14 +415,14 @@ type Field = BaseFieldUI & {
|
|
|
382
415
|
service_id?: undefined;
|
|
383
416
|
} | ({
|
|
384
417
|
button: true;
|
|
385
|
-
service_id?:
|
|
418
|
+
service_id?: ServiceIdRef;
|
|
386
419
|
} & WithQuantityDefault));
|
|
387
420
|
type ConstraintKey = string;
|
|
388
421
|
type Tag = {
|
|
389
422
|
id: string;
|
|
390
423
|
label: string;
|
|
391
424
|
bind_id?: string;
|
|
392
|
-
service_id?:
|
|
425
|
+
service_id?: ServiceIdRef;
|
|
393
426
|
includes?: string[];
|
|
394
427
|
excludes?: string[];
|
|
395
428
|
meta?: Record<string, unknown> & WithQuantityDefault;
|
|
@@ -475,6 +508,7 @@ type NormaliseOptions = {
|
|
|
475
508
|
constraints?: string[];
|
|
476
509
|
};
|
|
477
510
|
declare function normalise(input: unknown, opts?: NormaliseOptions): ServiceProps;
|
|
511
|
+
declare function normalizeFieldValidation(input: unknown): FieldValidationRule[] | undefined;
|
|
478
512
|
|
|
479
513
|
/**
|
|
480
514
|
* Core synchronous validate.
|
|
@@ -491,8 +525,6 @@ declare function validateAsync(props: ServiceProps, ctx?: ValidatorOptions): Pro
|
|
|
491
525
|
/** Options you can set on the builder (used for validation/visibility) */
|
|
492
526
|
type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
|
|
493
527
|
serviceMap?: DgpServiceMap;
|
|
494
|
-
/** max history entries for undo/redo */
|
|
495
|
-
historyLimit?: number;
|
|
496
528
|
/**
|
|
497
529
|
* Field ids whose options should be shown as nodes in the graph.
|
|
498
530
|
* If a field id is NOT in this set, its options are not materialized as nodes:
|
|
@@ -517,9 +549,6 @@ interface Builder {
|
|
|
517
549
|
visibleFields(tagId: string, selectedOptionKeys?: string[]): string[];
|
|
518
550
|
/** Update builder options (validator context etc.) */
|
|
519
551
|
setOptions(patch: Partial<BuilderOptions>): void;
|
|
520
|
-
/** History */
|
|
521
|
-
undo(): boolean;
|
|
522
|
-
redo(): boolean;
|
|
523
552
|
/** Access the current props (already normalised) */
|
|
524
553
|
getProps(): ServiceProps;
|
|
525
554
|
/** Service map for validation/rules */
|
|
@@ -583,48 +612,50 @@ declare function getFallbackRegistrationInfo(props: ServiceProps, nodeId: NodeId
|
|
|
583
612
|
tagContexts: string[];
|
|
584
613
|
};
|
|
585
614
|
|
|
586
|
-
type
|
|
615
|
+
type SimulationAnchor = {
|
|
587
616
|
kind: "field" | "option";
|
|
588
617
|
id: string;
|
|
618
|
+
fieldId: string;
|
|
589
619
|
label?: string;
|
|
590
|
-
service_id: number;
|
|
591
|
-
rate: number;
|
|
592
620
|
};
|
|
593
|
-
|
|
594
|
-
type RateCoherenceDiagnostic = {
|
|
621
|
+
type SharedDiagnostic = {
|
|
595
622
|
scope: "visible_group";
|
|
596
623
|
tagId: string;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
624
|
+
nodeId: string;
|
|
625
|
+
message: string;
|
|
626
|
+
simulationAnchor?: SimulationAnchor;
|
|
627
|
+
invalidFieldIds: string[];
|
|
628
|
+
};
|
|
629
|
+
type RateCoherenceDiagnostic = (SharedDiagnostic & {
|
|
630
|
+
kind: "contextual";
|
|
631
|
+
primary: {
|
|
632
|
+
nodeId: string;
|
|
633
|
+
fieldId: string;
|
|
606
634
|
label?: string;
|
|
607
|
-
|
|
635
|
+
refKind: "single" | "multi";
|
|
636
|
+
service_id?: ServiceIdRef;
|
|
608
637
|
rate: number;
|
|
609
638
|
};
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
message: string;
|
|
613
|
-
/** Which button triggered this simulation */
|
|
614
|
-
simulationAnchor: {
|
|
615
|
-
kind: "field" | "option";
|
|
616
|
-
id: string;
|
|
639
|
+
offender: {
|
|
640
|
+
nodeId: string;
|
|
617
641
|
fieldId: string;
|
|
618
642
|
label?: string;
|
|
643
|
+
refKind: "single" | "multi";
|
|
644
|
+
service_id?: ServiceIdRef;
|
|
645
|
+
rate: number;
|
|
619
646
|
};
|
|
620
|
-
|
|
621
|
-
|
|
647
|
+
policy: RatePolicy["kind"];
|
|
648
|
+
policyPct?: number;
|
|
649
|
+
}) | (SharedDiagnostic & {
|
|
650
|
+
kind: "internal_field";
|
|
651
|
+
fieldId: string;
|
|
652
|
+
});
|
|
622
653
|
declare function validateRateCoherenceDeep(params: {
|
|
623
654
|
builder: Builder;
|
|
624
655
|
services: DgpServiceMap;
|
|
625
656
|
tagId: string;
|
|
626
|
-
/** Optional rate policy (defaults to { kind: 'lte_primary' }) */
|
|
627
657
|
ratePolicy?: RatePolicy;
|
|
658
|
+
invalidFieldIds?: Iterable<string>;
|
|
628
659
|
}): RateCoherenceDiagnostic[];
|
|
629
660
|
|
|
630
661
|
type NodeKind = "tag" | "field" | "option" | "unknown";
|
|
@@ -704,6 +735,47 @@ interface NodeIndex {
|
|
|
704
735
|
}
|
|
705
736
|
declare function createNodeIndex(builder: Builder): NodeIndex;
|
|
706
737
|
|
|
738
|
+
type PolicyDiagnostic = {
|
|
739
|
+
ruleIndex: number;
|
|
740
|
+
ruleId?: string;
|
|
741
|
+
severity: "error" | "warning";
|
|
742
|
+
message: string;
|
|
743
|
+
path?: string;
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
type ServiceCheck = {
|
|
747
|
+
id: ServiceIdRef;
|
|
748
|
+
ok: boolean;
|
|
749
|
+
fitsConstraints: boolean;
|
|
750
|
+
passesRate: boolean;
|
|
751
|
+
passesPolicies: boolean;
|
|
752
|
+
policyErrors?: string[];
|
|
753
|
+
policyWarnings?: string[];
|
|
754
|
+
reasons: Array<"constraint_mismatch" | "rate_policy" | "policy_error" | "missing_capability">;
|
|
755
|
+
cap?: DgpServiceCapability;
|
|
756
|
+
rate?: number;
|
|
757
|
+
};
|
|
758
|
+
type FilterServicesForVisibleGroupInput = {
|
|
759
|
+
candidates: ServiceIdRef[];
|
|
760
|
+
context: {
|
|
761
|
+
tagId: string;
|
|
762
|
+
selectedButtons?: string[];
|
|
763
|
+
usedServiceIds: ServiceIdRef[];
|
|
764
|
+
effectiveConstraints?: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
|
|
765
|
+
policies?: unknown;
|
|
766
|
+
fallback?: FallbackSettings;
|
|
767
|
+
strictSafety?: boolean;
|
|
768
|
+
enforcePolicies?: boolean;
|
|
769
|
+
};
|
|
770
|
+
};
|
|
771
|
+
type FilterServicesForVisibleGroupResult = {
|
|
772
|
+
checks: ServiceCheck[];
|
|
773
|
+
diagnostics?: PolicyDiagnostic[];
|
|
774
|
+
};
|
|
775
|
+
declare function filterServicesForVisibleGroup(input: FilterServicesForVisibleGroupInput, deps: {
|
|
776
|
+
builder: Builder;
|
|
777
|
+
}): FilterServicesForVisibleGroupResult;
|
|
778
|
+
|
|
707
779
|
type BuildOrderSnapshotSettings = {
|
|
708
780
|
mode?: "prod" | "dev";
|
|
709
781
|
hostDefaultQuantity?: number;
|
|
@@ -765,4 +837,4 @@ interface FallbackEditor {
|
|
|
765
837
|
}
|
|
766
838
|
declare function createFallbackEditor(options?: FallbackEditorOptions): FallbackEditor;
|
|
767
839
|
|
|
768
|
-
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FallbackEditor, type FieldNode, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createFallbackEditor, createNodeIndex, getEligibleFallbacks, getFallbackRegistrationInfo, normalise, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|
|
840
|
+
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FallbackEditor, type FieldNode, type FilterServicesForVisibleGroupInput, type FilterServicesForVisibleGroupResult, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type ServiceCheck, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createFallbackEditor, createNodeIndex, filterServicesForVisibleGroup, getEligibleFallbacks, getFallbackRegistrationInfo, normalise, normalizeFieldValidation, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|