@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.d.cts
CHANGED
|
@@ -30,59 +30,9 @@ interface BaseFieldUI {
|
|
|
30
30
|
name?: string;
|
|
31
31
|
label: string;
|
|
32
32
|
required?: boolean;
|
|
33
|
-
/** Host-defined prop names → typed UI nodes */
|
|
34
|
-
ui?: Record<string, Ui>;
|
|
35
33
|
/** Host-defined prop names → runtime default values (untyped base) */
|
|
36
34
|
defaults?: Record<string, unknown>;
|
|
37
35
|
}
|
|
38
|
-
type Ui = UiString | UiNumber | UiBoolean | UiAnyOf | UiArray | UiObject;
|
|
39
|
-
/** string */
|
|
40
|
-
interface UiString {
|
|
41
|
-
type: "string";
|
|
42
|
-
enum?: string[];
|
|
43
|
-
minLength?: number;
|
|
44
|
-
maxLength?: number;
|
|
45
|
-
pattern?: string;
|
|
46
|
-
format?: string;
|
|
47
|
-
}
|
|
48
|
-
/** number */
|
|
49
|
-
interface UiNumber {
|
|
50
|
-
type: "number";
|
|
51
|
-
minimum?: number;
|
|
52
|
-
maximum?: number;
|
|
53
|
-
multipleOf?: number;
|
|
54
|
-
}
|
|
55
|
-
/** boolean */
|
|
56
|
-
interface UiBoolean {
|
|
57
|
-
type: "boolean";
|
|
58
|
-
}
|
|
59
|
-
/** enumerated choices */
|
|
60
|
-
interface UiAnyOf {
|
|
61
|
-
type: "anyOf";
|
|
62
|
-
multiple?: boolean;
|
|
63
|
-
items: Array<{
|
|
64
|
-
type: "string" | "number" | "boolean";
|
|
65
|
-
title?: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
value: string | number | boolean;
|
|
68
|
-
}>;
|
|
69
|
-
}
|
|
70
|
-
/** arrays: homogeneous (item) or tuple (items) */
|
|
71
|
-
interface UiArray {
|
|
72
|
-
type: "array";
|
|
73
|
-
item?: Ui;
|
|
74
|
-
items?: Ui[];
|
|
75
|
-
minItems?: number;
|
|
76
|
-
maxItems?: number;
|
|
77
|
-
uniqueItems?: boolean;
|
|
78
|
-
}
|
|
79
|
-
/** objects: nested props */
|
|
80
|
-
interface UiObject {
|
|
81
|
-
type: "object";
|
|
82
|
-
fields: Record<string, Ui>;
|
|
83
|
-
required?: string[];
|
|
84
|
-
order?: string[];
|
|
85
|
-
}
|
|
86
36
|
type FieldOption = {
|
|
87
37
|
id: string;
|
|
88
38
|
label: string;
|
|
@@ -143,6 +93,8 @@ type ServiceProps = {
|
|
|
143
93
|
excludes_for_buttons?: Record<string, string[]>;
|
|
144
94
|
schema_version?: string;
|
|
145
95
|
fallbacks?: ServiceFallback;
|
|
96
|
+
name?: string;
|
|
97
|
+
notices?: ServicePropsNotice[];
|
|
146
98
|
};
|
|
147
99
|
type ServiceIdRef = number | string;
|
|
148
100
|
type NodeIdRef = string;
|
|
@@ -152,6 +104,34 @@ type ServiceFallback = {
|
|
|
152
104
|
/** Primary→fallback list used when no node-scoped entry is present */
|
|
153
105
|
global?: Record<ServiceIdRef, ServiceIdRef[]>;
|
|
154
106
|
};
|
|
107
|
+
type NoticeType = "public" | "private";
|
|
108
|
+
type NoticeSeverity = "info" | "warning" | "error";
|
|
109
|
+
/**
|
|
110
|
+
* “label” is lightweight + UI-friendly (best, sale, hot, etc).
|
|
111
|
+
* Others remain semantic / governance oriented.
|
|
112
|
+
*/
|
|
113
|
+
type NoticeKind = "label" | "warning" | "deprecation" | "compat" | "migration" | "policy";
|
|
114
|
+
type NoticeTarget = {
|
|
115
|
+
scope: "global";
|
|
116
|
+
} | {
|
|
117
|
+
scope: "node";
|
|
118
|
+
node_kind: "tag" | "field" | "option";
|
|
119
|
+
node_id: string;
|
|
120
|
+
};
|
|
121
|
+
interface ServicePropsNotice {
|
|
122
|
+
id: string;
|
|
123
|
+
type: NoticeType;
|
|
124
|
+
kind: NoticeKind;
|
|
125
|
+
severity: NoticeSeverity;
|
|
126
|
+
target: NoticeTarget;
|
|
127
|
+
title: string;
|
|
128
|
+
description?: string;
|
|
129
|
+
reason?: string;
|
|
130
|
+
marked_at?: string;
|
|
131
|
+
icon?: string;
|
|
132
|
+
color?: string;
|
|
133
|
+
meta?: Record<string, unknown>;
|
|
134
|
+
}
|
|
155
135
|
|
|
156
136
|
type NodeKind$1 = "tag" | "field" | "comment" | "option";
|
|
157
137
|
type EdgeKind = "child" | "bind" | "include" | "exclude" | "error" | "anchor";
|
|
@@ -213,6 +193,123 @@ type DgpServiceCapability = {
|
|
|
213
193
|
};
|
|
214
194
|
type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
|
|
215
195
|
|
|
196
|
+
interface ButtonValue {
|
|
197
|
+
id: string;
|
|
198
|
+
value: string | number;
|
|
199
|
+
service_id?: number;
|
|
200
|
+
pricing_role?: "base" | "utility";
|
|
201
|
+
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
202
|
+
}
|
|
203
|
+
type Scalar = string | number | boolean | ButtonValue | null;
|
|
204
|
+
type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
|
|
205
|
+
type QuantityRule = {
|
|
206
|
+
valueBy: "value" | "length" | "eval";
|
|
207
|
+
code?: string;
|
|
208
|
+
};
|
|
209
|
+
type UtilityLineItem = {
|
|
210
|
+
nodeId: string;
|
|
211
|
+
mode: UtilityMode;
|
|
212
|
+
rate: number;
|
|
213
|
+
inputs: {
|
|
214
|
+
quantity: number;
|
|
215
|
+
value?: Scalar | Scalar[];
|
|
216
|
+
valueBy?: "value" | "length" | "eval";
|
|
217
|
+
evalCodeUsed?: boolean;
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
type FallbackDiagnostics = {
|
|
221
|
+
scope: "node" | "global";
|
|
222
|
+
nodeId?: string;
|
|
223
|
+
primary: string | number;
|
|
224
|
+
candidate: string | number;
|
|
225
|
+
reasons: Array<"rate_violation" | "constraint_mismatch" | "unknown_service" | "ambiguous_context">;
|
|
226
|
+
};
|
|
227
|
+
type SnapshotContext = {
|
|
228
|
+
/** The single active tag id for this order */
|
|
229
|
+
tag: string;
|
|
230
|
+
/** Effective (post-propagation) constraints on that tag */
|
|
231
|
+
constraints: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
|
|
232
|
+
/**
|
|
233
|
+
* Per-node evaluation context:
|
|
234
|
+
* - For the active tag node itself: the same tag id.
|
|
235
|
+
* - For an option node: parent's field.bind_id must include this tag to be applicable; otherwise null.
|
|
236
|
+
* - For a field node (optional to include later): same rule as option, derived from field.bind_id.
|
|
237
|
+
*/
|
|
238
|
+
nodeContexts: Record<string, string | null>;
|
|
239
|
+
/** Client pruning policy used (so server can mirror/compare). */
|
|
240
|
+
policy: {
|
|
241
|
+
ratePolicy: {
|
|
242
|
+
kind: "lte_primary" | "none";
|
|
243
|
+
thresholdPct?: number;
|
|
244
|
+
};
|
|
245
|
+
requireConstraintFit: boolean;
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
type OrderSnapshot = {
|
|
249
|
+
version: "1";
|
|
250
|
+
mode: "prod" | "dev";
|
|
251
|
+
builtAt: string;
|
|
252
|
+
selection: {
|
|
253
|
+
tag: string;
|
|
254
|
+
fields: Array<{
|
|
255
|
+
id: string;
|
|
256
|
+
type: string;
|
|
257
|
+
selectedOptions?: string[];
|
|
258
|
+
}>;
|
|
259
|
+
};
|
|
260
|
+
inputs: {
|
|
261
|
+
form: Record<string, Scalar | Scalar[]>;
|
|
262
|
+
selections: Record<string, string[]>;
|
|
263
|
+
};
|
|
264
|
+
quantity: number;
|
|
265
|
+
quantitySource: {
|
|
266
|
+
kind: "field" | "tag" | "option" | "default";
|
|
267
|
+
id?: string;
|
|
268
|
+
rule?: QuantityRule;
|
|
269
|
+
defaultedFromHost?: boolean;
|
|
270
|
+
};
|
|
271
|
+
min: number;
|
|
272
|
+
max: number;
|
|
273
|
+
services: Array<string | number>;
|
|
274
|
+
serviceMap: Record<string, Array<string | number>>;
|
|
275
|
+
fallbacks?: {
|
|
276
|
+
nodes?: Record<string, Array<string | number>>;
|
|
277
|
+
global?: Record<string | number, Array<string | number>>;
|
|
278
|
+
};
|
|
279
|
+
utilities?: UtilityLineItem[];
|
|
280
|
+
warnings?: {
|
|
281
|
+
utility?: Array<{
|
|
282
|
+
nodeId: string;
|
|
283
|
+
reason: string;
|
|
284
|
+
}>;
|
|
285
|
+
fallbacks?: FallbackDiagnostics[];
|
|
286
|
+
};
|
|
287
|
+
meta?: {
|
|
288
|
+
schema_version?: string;
|
|
289
|
+
workspaceId?: string;
|
|
290
|
+
builder?: {
|
|
291
|
+
commit?: string;
|
|
292
|
+
};
|
|
293
|
+
context?: SnapshotContext;
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
type NodeRef = {
|
|
298
|
+
kind: "tag";
|
|
299
|
+
id: string;
|
|
300
|
+
node: Tag;
|
|
301
|
+
} | {
|
|
302
|
+
kind: "field";
|
|
303
|
+
id: string;
|
|
304
|
+
node: Field;
|
|
305
|
+
} | {
|
|
306
|
+
kind: "option";
|
|
307
|
+
id: string;
|
|
308
|
+
node: FieldOption;
|
|
309
|
+
fieldId: string;
|
|
310
|
+
};
|
|
311
|
+
type NodeMap = Map<string, NodeRef>;
|
|
312
|
+
|
|
216
313
|
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";
|
|
217
314
|
type ValidationError = {
|
|
218
315
|
code: ValidationCode;
|
|
@@ -261,6 +358,7 @@ type DynamicRule = {
|
|
|
261
358
|
};
|
|
262
359
|
type ValidatorOptions = {
|
|
263
360
|
serviceMap?: DgpServiceMap;
|
|
361
|
+
nodeMap?: NodeMap;
|
|
264
362
|
allowUnsafe?: boolean;
|
|
265
363
|
selectedOptionKeys?: string[];
|
|
266
364
|
globalUtilityGuard?: boolean;
|
|
@@ -294,7 +392,17 @@ type NormaliseOptions = {
|
|
|
294
392
|
};
|
|
295
393
|
declare function normalise(input: unknown, opts?: NormaliseOptions): ServiceProps;
|
|
296
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Core synchronous validate.
|
|
397
|
+
*/
|
|
297
398
|
declare function validate(props: ServiceProps, ctx?: ValidatorOptions): ValidationError[];
|
|
399
|
+
/**
|
|
400
|
+
* UI-friendly async wrapper:
|
|
401
|
+
* - yields to microtask queue
|
|
402
|
+
* - then yields to the next animation frame (so paint can happen)
|
|
403
|
+
* - then runs validate()
|
|
404
|
+
*/
|
|
405
|
+
declare function validateAsync(props: ServiceProps, ctx?: ValidatorOptions): Promise<ValidationError[]>;
|
|
298
406
|
|
|
299
407
|
/** Options you can set on the builder (used for validation/visibility) */
|
|
300
408
|
type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
|
|
@@ -338,6 +446,10 @@ interface Builder {
|
|
|
338
446
|
description: string;
|
|
339
447
|
value: string;
|
|
340
448
|
}[];
|
|
449
|
+
isTagId(id: string): boolean;
|
|
450
|
+
isFieldId(id: string): boolean;
|
|
451
|
+
isOptionId(id: string): boolean;
|
|
452
|
+
getNodeMap(): NodeMap;
|
|
341
453
|
}
|
|
342
454
|
declare function createBuilder(opts?: BuilderOptions): Builder;
|
|
343
455
|
|
|
@@ -504,4 +616,30 @@ interface NodeIndex {
|
|
|
504
616
|
}
|
|
505
617
|
declare function createNodeIndex(builder: Builder): NodeIndex;
|
|
506
618
|
|
|
507
|
-
|
|
619
|
+
type BuildOrderSnapshotSettings = {
|
|
620
|
+
mode?: "prod" | "dev";
|
|
621
|
+
hostDefaultQuantity?: number;
|
|
622
|
+
/** Full fallback policy */
|
|
623
|
+
fallback?: FallbackSettings;
|
|
624
|
+
workspaceId?: string;
|
|
625
|
+
builderCommit?: string;
|
|
626
|
+
};
|
|
627
|
+
type BuildOrderSelection = {
|
|
628
|
+
/** Single active context (one tag) coming from Selection */
|
|
629
|
+
activeTagId: string;
|
|
630
|
+
/** Non-option inputs, keyed by fieldId (will be remapped to field.name in the payload) */
|
|
631
|
+
formValuesByFieldId: Record<string, Scalar | Scalar[]>;
|
|
632
|
+
/** Option selections, keyed by fieldId → optionId[] */
|
|
633
|
+
optionSelectionsByFieldId: Record<string, string[]>;
|
|
634
|
+
/**
|
|
635
|
+
* Selection visit order for options (optional, improves "first option wins primary" determinism).
|
|
636
|
+
* If omitted, iteration order falls back to Object.entries(optionSelectionsByFieldId).
|
|
637
|
+
*/
|
|
638
|
+
optionTraversalOrder?: Array<{
|
|
639
|
+
fieldId: string;
|
|
640
|
+
optionId: string;
|
|
641
|
+
}>;
|
|
642
|
+
};
|
|
643
|
+
declare function buildOrderSnapshot(props: ServiceProps, builder: Builder, selection: BuildOrderSelection, services: DgpServiceMap, settings?: BuildOrderSnapshotSettings): OrderSnapshot;
|
|
644
|
+
|
|
645
|
+
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FieldNode, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createNodeIndex, getEligibleFallbacks, normalise, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -30,59 +30,9 @@ interface BaseFieldUI {
|
|
|
30
30
|
name?: string;
|
|
31
31
|
label: string;
|
|
32
32
|
required?: boolean;
|
|
33
|
-
/** Host-defined prop names → typed UI nodes */
|
|
34
|
-
ui?: Record<string, Ui>;
|
|
35
33
|
/** Host-defined prop names → runtime default values (untyped base) */
|
|
36
34
|
defaults?: Record<string, unknown>;
|
|
37
35
|
}
|
|
38
|
-
type Ui = UiString | UiNumber | UiBoolean | UiAnyOf | UiArray | UiObject;
|
|
39
|
-
/** string */
|
|
40
|
-
interface UiString {
|
|
41
|
-
type: "string";
|
|
42
|
-
enum?: string[];
|
|
43
|
-
minLength?: number;
|
|
44
|
-
maxLength?: number;
|
|
45
|
-
pattern?: string;
|
|
46
|
-
format?: string;
|
|
47
|
-
}
|
|
48
|
-
/** number */
|
|
49
|
-
interface UiNumber {
|
|
50
|
-
type: "number";
|
|
51
|
-
minimum?: number;
|
|
52
|
-
maximum?: number;
|
|
53
|
-
multipleOf?: number;
|
|
54
|
-
}
|
|
55
|
-
/** boolean */
|
|
56
|
-
interface UiBoolean {
|
|
57
|
-
type: "boolean";
|
|
58
|
-
}
|
|
59
|
-
/** enumerated choices */
|
|
60
|
-
interface UiAnyOf {
|
|
61
|
-
type: "anyOf";
|
|
62
|
-
multiple?: boolean;
|
|
63
|
-
items: Array<{
|
|
64
|
-
type: "string" | "number" | "boolean";
|
|
65
|
-
title?: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
value: string | number | boolean;
|
|
68
|
-
}>;
|
|
69
|
-
}
|
|
70
|
-
/** arrays: homogeneous (item) or tuple (items) */
|
|
71
|
-
interface UiArray {
|
|
72
|
-
type: "array";
|
|
73
|
-
item?: Ui;
|
|
74
|
-
items?: Ui[];
|
|
75
|
-
minItems?: number;
|
|
76
|
-
maxItems?: number;
|
|
77
|
-
uniqueItems?: boolean;
|
|
78
|
-
}
|
|
79
|
-
/** objects: nested props */
|
|
80
|
-
interface UiObject {
|
|
81
|
-
type: "object";
|
|
82
|
-
fields: Record<string, Ui>;
|
|
83
|
-
required?: string[];
|
|
84
|
-
order?: string[];
|
|
85
|
-
}
|
|
86
36
|
type FieldOption = {
|
|
87
37
|
id: string;
|
|
88
38
|
label: string;
|
|
@@ -143,6 +93,8 @@ type ServiceProps = {
|
|
|
143
93
|
excludes_for_buttons?: Record<string, string[]>;
|
|
144
94
|
schema_version?: string;
|
|
145
95
|
fallbacks?: ServiceFallback;
|
|
96
|
+
name?: string;
|
|
97
|
+
notices?: ServicePropsNotice[];
|
|
146
98
|
};
|
|
147
99
|
type ServiceIdRef = number | string;
|
|
148
100
|
type NodeIdRef = string;
|
|
@@ -152,6 +104,34 @@ type ServiceFallback = {
|
|
|
152
104
|
/** Primary→fallback list used when no node-scoped entry is present */
|
|
153
105
|
global?: Record<ServiceIdRef, ServiceIdRef[]>;
|
|
154
106
|
};
|
|
107
|
+
type NoticeType = "public" | "private";
|
|
108
|
+
type NoticeSeverity = "info" | "warning" | "error";
|
|
109
|
+
/**
|
|
110
|
+
* “label” is lightweight + UI-friendly (best, sale, hot, etc).
|
|
111
|
+
* Others remain semantic / governance oriented.
|
|
112
|
+
*/
|
|
113
|
+
type NoticeKind = "label" | "warning" | "deprecation" | "compat" | "migration" | "policy";
|
|
114
|
+
type NoticeTarget = {
|
|
115
|
+
scope: "global";
|
|
116
|
+
} | {
|
|
117
|
+
scope: "node";
|
|
118
|
+
node_kind: "tag" | "field" | "option";
|
|
119
|
+
node_id: string;
|
|
120
|
+
};
|
|
121
|
+
interface ServicePropsNotice {
|
|
122
|
+
id: string;
|
|
123
|
+
type: NoticeType;
|
|
124
|
+
kind: NoticeKind;
|
|
125
|
+
severity: NoticeSeverity;
|
|
126
|
+
target: NoticeTarget;
|
|
127
|
+
title: string;
|
|
128
|
+
description?: string;
|
|
129
|
+
reason?: string;
|
|
130
|
+
marked_at?: string;
|
|
131
|
+
icon?: string;
|
|
132
|
+
color?: string;
|
|
133
|
+
meta?: Record<string, unknown>;
|
|
134
|
+
}
|
|
155
135
|
|
|
156
136
|
type NodeKind$1 = "tag" | "field" | "comment" | "option";
|
|
157
137
|
type EdgeKind = "child" | "bind" | "include" | "exclude" | "error" | "anchor";
|
|
@@ -213,6 +193,123 @@ type DgpServiceCapability = {
|
|
|
213
193
|
};
|
|
214
194
|
type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
|
|
215
195
|
|
|
196
|
+
interface ButtonValue {
|
|
197
|
+
id: string;
|
|
198
|
+
value: string | number;
|
|
199
|
+
service_id?: number;
|
|
200
|
+
pricing_role?: "base" | "utility";
|
|
201
|
+
meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
|
|
202
|
+
}
|
|
203
|
+
type Scalar = string | number | boolean | ButtonValue | null;
|
|
204
|
+
type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
|
|
205
|
+
type QuantityRule = {
|
|
206
|
+
valueBy: "value" | "length" | "eval";
|
|
207
|
+
code?: string;
|
|
208
|
+
};
|
|
209
|
+
type UtilityLineItem = {
|
|
210
|
+
nodeId: string;
|
|
211
|
+
mode: UtilityMode;
|
|
212
|
+
rate: number;
|
|
213
|
+
inputs: {
|
|
214
|
+
quantity: number;
|
|
215
|
+
value?: Scalar | Scalar[];
|
|
216
|
+
valueBy?: "value" | "length" | "eval";
|
|
217
|
+
evalCodeUsed?: boolean;
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
type FallbackDiagnostics = {
|
|
221
|
+
scope: "node" | "global";
|
|
222
|
+
nodeId?: string;
|
|
223
|
+
primary: string | number;
|
|
224
|
+
candidate: string | number;
|
|
225
|
+
reasons: Array<"rate_violation" | "constraint_mismatch" | "unknown_service" | "ambiguous_context">;
|
|
226
|
+
};
|
|
227
|
+
type SnapshotContext = {
|
|
228
|
+
/** The single active tag id for this order */
|
|
229
|
+
tag: string;
|
|
230
|
+
/** Effective (post-propagation) constraints on that tag */
|
|
231
|
+
constraints: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
|
|
232
|
+
/**
|
|
233
|
+
* Per-node evaluation context:
|
|
234
|
+
* - For the active tag node itself: the same tag id.
|
|
235
|
+
* - For an option node: parent's field.bind_id must include this tag to be applicable; otherwise null.
|
|
236
|
+
* - For a field node (optional to include later): same rule as option, derived from field.bind_id.
|
|
237
|
+
*/
|
|
238
|
+
nodeContexts: Record<string, string | null>;
|
|
239
|
+
/** Client pruning policy used (so server can mirror/compare). */
|
|
240
|
+
policy: {
|
|
241
|
+
ratePolicy: {
|
|
242
|
+
kind: "lte_primary" | "none";
|
|
243
|
+
thresholdPct?: number;
|
|
244
|
+
};
|
|
245
|
+
requireConstraintFit: boolean;
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
type OrderSnapshot = {
|
|
249
|
+
version: "1";
|
|
250
|
+
mode: "prod" | "dev";
|
|
251
|
+
builtAt: string;
|
|
252
|
+
selection: {
|
|
253
|
+
tag: string;
|
|
254
|
+
fields: Array<{
|
|
255
|
+
id: string;
|
|
256
|
+
type: string;
|
|
257
|
+
selectedOptions?: string[];
|
|
258
|
+
}>;
|
|
259
|
+
};
|
|
260
|
+
inputs: {
|
|
261
|
+
form: Record<string, Scalar | Scalar[]>;
|
|
262
|
+
selections: Record<string, string[]>;
|
|
263
|
+
};
|
|
264
|
+
quantity: number;
|
|
265
|
+
quantitySource: {
|
|
266
|
+
kind: "field" | "tag" | "option" | "default";
|
|
267
|
+
id?: string;
|
|
268
|
+
rule?: QuantityRule;
|
|
269
|
+
defaultedFromHost?: boolean;
|
|
270
|
+
};
|
|
271
|
+
min: number;
|
|
272
|
+
max: number;
|
|
273
|
+
services: Array<string | number>;
|
|
274
|
+
serviceMap: Record<string, Array<string | number>>;
|
|
275
|
+
fallbacks?: {
|
|
276
|
+
nodes?: Record<string, Array<string | number>>;
|
|
277
|
+
global?: Record<string | number, Array<string | number>>;
|
|
278
|
+
};
|
|
279
|
+
utilities?: UtilityLineItem[];
|
|
280
|
+
warnings?: {
|
|
281
|
+
utility?: Array<{
|
|
282
|
+
nodeId: string;
|
|
283
|
+
reason: string;
|
|
284
|
+
}>;
|
|
285
|
+
fallbacks?: FallbackDiagnostics[];
|
|
286
|
+
};
|
|
287
|
+
meta?: {
|
|
288
|
+
schema_version?: string;
|
|
289
|
+
workspaceId?: string;
|
|
290
|
+
builder?: {
|
|
291
|
+
commit?: string;
|
|
292
|
+
};
|
|
293
|
+
context?: SnapshotContext;
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
type NodeRef = {
|
|
298
|
+
kind: "tag";
|
|
299
|
+
id: string;
|
|
300
|
+
node: Tag;
|
|
301
|
+
} | {
|
|
302
|
+
kind: "field";
|
|
303
|
+
id: string;
|
|
304
|
+
node: Field;
|
|
305
|
+
} | {
|
|
306
|
+
kind: "option";
|
|
307
|
+
id: string;
|
|
308
|
+
node: FieldOption;
|
|
309
|
+
fieldId: string;
|
|
310
|
+
};
|
|
311
|
+
type NodeMap = Map<string, NodeRef>;
|
|
312
|
+
|
|
216
313
|
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";
|
|
217
314
|
type ValidationError = {
|
|
218
315
|
code: ValidationCode;
|
|
@@ -261,6 +358,7 @@ type DynamicRule = {
|
|
|
261
358
|
};
|
|
262
359
|
type ValidatorOptions = {
|
|
263
360
|
serviceMap?: DgpServiceMap;
|
|
361
|
+
nodeMap?: NodeMap;
|
|
264
362
|
allowUnsafe?: boolean;
|
|
265
363
|
selectedOptionKeys?: string[];
|
|
266
364
|
globalUtilityGuard?: boolean;
|
|
@@ -294,7 +392,17 @@ type NormaliseOptions = {
|
|
|
294
392
|
};
|
|
295
393
|
declare function normalise(input: unknown, opts?: NormaliseOptions): ServiceProps;
|
|
296
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Core synchronous validate.
|
|
397
|
+
*/
|
|
297
398
|
declare function validate(props: ServiceProps, ctx?: ValidatorOptions): ValidationError[];
|
|
399
|
+
/**
|
|
400
|
+
* UI-friendly async wrapper:
|
|
401
|
+
* - yields to microtask queue
|
|
402
|
+
* - then yields to the next animation frame (so paint can happen)
|
|
403
|
+
* - then runs validate()
|
|
404
|
+
*/
|
|
405
|
+
declare function validateAsync(props: ServiceProps, ctx?: ValidatorOptions): Promise<ValidationError[]>;
|
|
298
406
|
|
|
299
407
|
/** Options you can set on the builder (used for validation/visibility) */
|
|
300
408
|
type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
|
|
@@ -338,6 +446,10 @@ interface Builder {
|
|
|
338
446
|
description: string;
|
|
339
447
|
value: string;
|
|
340
448
|
}[];
|
|
449
|
+
isTagId(id: string): boolean;
|
|
450
|
+
isFieldId(id: string): boolean;
|
|
451
|
+
isOptionId(id: string): boolean;
|
|
452
|
+
getNodeMap(): NodeMap;
|
|
341
453
|
}
|
|
342
454
|
declare function createBuilder(opts?: BuilderOptions): Builder;
|
|
343
455
|
|
|
@@ -504,4 +616,30 @@ interface NodeIndex {
|
|
|
504
616
|
}
|
|
505
617
|
declare function createNodeIndex(builder: Builder): NodeIndex;
|
|
506
618
|
|
|
507
|
-
|
|
619
|
+
type BuildOrderSnapshotSettings = {
|
|
620
|
+
mode?: "prod" | "dev";
|
|
621
|
+
hostDefaultQuantity?: number;
|
|
622
|
+
/** Full fallback policy */
|
|
623
|
+
fallback?: FallbackSettings;
|
|
624
|
+
workspaceId?: string;
|
|
625
|
+
builderCommit?: string;
|
|
626
|
+
};
|
|
627
|
+
type BuildOrderSelection = {
|
|
628
|
+
/** Single active context (one tag) coming from Selection */
|
|
629
|
+
activeTagId: string;
|
|
630
|
+
/** Non-option inputs, keyed by fieldId (will be remapped to field.name in the payload) */
|
|
631
|
+
formValuesByFieldId: Record<string, Scalar | Scalar[]>;
|
|
632
|
+
/** Option selections, keyed by fieldId → optionId[] */
|
|
633
|
+
optionSelectionsByFieldId: Record<string, string[]>;
|
|
634
|
+
/**
|
|
635
|
+
* Selection visit order for options (optional, improves "first option wins primary" determinism).
|
|
636
|
+
* If omitted, iteration order falls back to Object.entries(optionSelectionsByFieldId).
|
|
637
|
+
*/
|
|
638
|
+
optionTraversalOrder?: Array<{
|
|
639
|
+
fieldId: string;
|
|
640
|
+
optionId: string;
|
|
641
|
+
}>;
|
|
642
|
+
};
|
|
643
|
+
declare function buildOrderSnapshot(props: ServiceProps, builder: Builder, selection: BuildOrderSelection, services: DgpServiceMap, settings?: BuildOrderSnapshotSettings): OrderSnapshot;
|
|
644
|
+
|
|
645
|
+
export { type AncestryHit, type AnyNode, type Builder, type BuilderOptions, type FailedFallbackContext, type FieldNode, type NodeIndex, type NodeKind, type NormaliseOptions, type OptionNode, type RateCoherenceDiagnostic, type RelKind, type TagNode, type UnknownNode, type WithAncestry, buildOrderSnapshot, collectFailedFallbacks, createBuilder, createNodeIndex, getEligibleFallbacks, normalise, resolveServiceFallback, validate, validateAsync, validateRateCoherenceDeep };
|