@timeax/digital-service-engine 0.0.3 → 0.0.5

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.
@@ -2,6 +2,146 @@ import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
+ type TimeRangeEstimate = {
6
+ min_seconds?: number;
7
+ max_seconds?: number;
8
+ label?: string;
9
+ meta?: Record<string, unknown>;
10
+ };
11
+ type SpeedEstimate = {
12
+ amount?: number;
13
+ per?: "minute" | "hour" | "day" | "week" | "month";
14
+ unit?: string;
15
+ label?: string;
16
+ meta?: Record<string, unknown>;
17
+ };
18
+ type ServiceEstimates = {
19
+ start?: TimeRangeEstimate;
20
+ speed?: SpeedEstimate;
21
+ average?: TimeRangeEstimate;
22
+ meta?: Record<string, unknown>;
23
+ };
24
+ type ServiceFlag = {
25
+ enabled: boolean;
26
+ description: string;
27
+ meta?: Record<string, unknown>;
28
+ };
29
+ type IdType = string | number;
30
+ type ServiceFlags = Record<string, ServiceFlag>;
31
+ type DgpServiceCapability = {
32
+ id: IdType;
33
+ name?: string;
34
+ rate: number;
35
+ min?: number;
36
+ max?: number;
37
+ category?: string;
38
+ flags?: ServiceFlags;
39
+ estimates?: ServiceEstimates;
40
+ meta?: Record<string, unknown>;
41
+ [x: string]: any;
42
+ };
43
+ type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
44
+
45
+ type NodeRef$1 = {
46
+ kind: "tag";
47
+ id: string;
48
+ node: Tag;
49
+ } | {
50
+ kind: "field";
51
+ id: string;
52
+ node: Field;
53
+ } | {
54
+ kind: "option";
55
+ id: string;
56
+ node: FieldOption;
57
+ fieldId: string;
58
+ };
59
+ type NodeMap = Map<string, NodeRef$1>;
60
+
61
+ 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";
62
+ type ValidationError = {
63
+ code: ValidationCode;
64
+ message: string;
65
+ severity: "error" | "warning" | "info";
66
+ nodeId?: string;
67
+ details?: Record<string, unknown> & {
68
+ affectedIds?: string[];
69
+ };
70
+ };
71
+ type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
72
+ /**
73
+ * Host-extensible service filter clause.
74
+ * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
75
+ */
76
+ type ServiceWhereClause = {
77
+ path: string;
78
+ op?: ServiceWhereOp;
79
+ value?: unknown;
80
+ };
81
+ type DynamicRule = {
82
+ id: string;
83
+ label?: string;
84
+ scope: "global" | "visible_group";
85
+ subject: "services";
86
+ /**
87
+ * Package-level filter surface:
88
+ * - role/tag/field are core to the builder schema
89
+ * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
90
+ */
91
+ filter?: {
92
+ role?: "base" | "utility" | "both";
93
+ tag_id?: string | string[];
94
+ field_id?: string | string[];
95
+ where?: ServiceWhereClause[];
96
+ };
97
+ /**
98
+ * Projection is intentionally open:
99
+ * hosts may project custom service properties.
100
+ */
101
+ projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
102
+ op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
103
+ value?: number | boolean;
104
+ severity?: "error" | "warning";
105
+ message?: string;
106
+ };
107
+ type ValidatorOptions = {
108
+ serviceMap?: DgpServiceMap;
109
+ nodeMap?: NodeMap;
110
+ allowUnsafe?: boolean;
111
+ selectedOptionKeys?: string[];
112
+ globalUtilityGuard?: boolean;
113
+ policies?: DynamicRule[];
114
+ fallbackSettings?: FallbackSettings;
115
+ };
116
+ type RatePolicy = {
117
+ kind: "lte_primary";
118
+ } | {
119
+ kind: "within_pct";
120
+ pct: number;
121
+ } | {
122
+ kind: "at_least_pct_lower";
123
+ pct: number;
124
+ };
125
+ type FallbackSettings = {
126
+ /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
127
+ requireConstraintFit?: boolean;
128
+ /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
129
+ ratePolicy?: RatePolicy;
130
+ /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
131
+ selectionStrategy?: "priority" | "cheapest";
132
+ /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
133
+ mode?: "strict" | "dev";
134
+ };
135
+
136
+ type ServiceIdRef = number | string;
137
+ type NodeIdRef = string;
138
+ type ServiceFallback = {
139
+ /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
140
+ nodes?: Record<NodeIdRef, ServiceIdRef[]>;
141
+ /** Primary→fallback list used when no node-scoped entry is present */
142
+ global?: Record<ServiceIdRef, ServiceIdRef[]>;
143
+ };
144
+
5
145
  type PricingRole = "base" | "utility";
6
146
  type FieldType = "custom" | (string & {});
7
147
  /** ── Marker types (live inside meta; non-breaking) ───────────────────── */
@@ -100,14 +240,6 @@ type ServiceProps = {
100
240
  name?: string;
101
241
  notices?: ServicePropsNotice[];
102
242
  };
103
- type ServiceIdRef = number | string;
104
- type NodeIdRef = string;
105
- type ServiceFallback = {
106
- /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
107
- nodes?: Record<NodeIdRef, ServiceIdRef[]>;
108
- /** Primary→fallback list used when no node-scoped entry is present */
109
- global?: Record<ServiceIdRef, ServiceIdRef[]>;
110
- };
111
243
  type NoticeType = "public" | "private";
112
244
  type NoticeSeverity = "info" | "warning" | "error";
113
245
  /**
@@ -327,137 +459,6 @@ type EditorSnapshot = {
327
459
  meta?: Record<string, unknown>;
328
460
  };
329
461
 
330
- type TimeRangeEstimate = {
331
- min_seconds?: number;
332
- max_seconds?: number;
333
- label?: string;
334
- meta?: Record<string, unknown>;
335
- };
336
- type SpeedEstimate = {
337
- amount?: number;
338
- per?: "minute" | "hour" | "day" | "week" | "month";
339
- unit?: string;
340
- label?: string;
341
- meta?: Record<string, unknown>;
342
- };
343
- type ServiceEstimates = {
344
- start?: TimeRangeEstimate;
345
- speed?: SpeedEstimate;
346
- average?: TimeRangeEstimate;
347
- meta?: Record<string, unknown>;
348
- };
349
- type ServiceFlag = {
350
- enabled: boolean;
351
- description: string;
352
- meta?: Record<string, unknown>;
353
- };
354
- type IdType = string | number;
355
- type ServiceFlags = Record<string, ServiceFlag>;
356
- type DgpServiceCapability = {
357
- id: IdType;
358
- name?: string;
359
- rate: number;
360
- min?: number;
361
- max?: number;
362
- category?: string;
363
- flags?: ServiceFlags;
364
- estimates?: ServiceEstimates;
365
- meta?: Record<string, unknown>;
366
- [x: string]: any;
367
- };
368
- type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
369
-
370
- type NodeRef$1 = {
371
- kind: "tag";
372
- id: string;
373
- node: Tag;
374
- } | {
375
- kind: "field";
376
- id: string;
377
- node: Field;
378
- } | {
379
- kind: "option";
380
- id: string;
381
- node: FieldOption;
382
- fieldId: string;
383
- };
384
- type NodeMap = Map<string, NodeRef$1>;
385
-
386
- 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";
387
- type ValidationError = {
388
- code: ValidationCode;
389
- message: string;
390
- severity: "error" | "warning" | "info";
391
- nodeId?: string;
392
- details?: Record<string, unknown> & {
393
- affectedIds?: string[];
394
- };
395
- };
396
- type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
397
- /**
398
- * Host-extensible service filter clause.
399
- * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
400
- */
401
- type ServiceWhereClause = {
402
- path: string;
403
- op?: ServiceWhereOp;
404
- value?: unknown;
405
- };
406
- type DynamicRule = {
407
- id: string;
408
- label?: string;
409
- scope: "global" | "visible_group";
410
- subject: "services";
411
- /**
412
- * Package-level filter surface:
413
- * - role/tag/field are core to the builder schema
414
- * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
415
- */
416
- filter?: {
417
- role?: "base" | "utility" | "both";
418
- tag_id?: string | string[];
419
- field_id?: string | string[];
420
- where?: ServiceWhereClause[];
421
- };
422
- /**
423
- * Projection is intentionally open:
424
- * hosts may project custom service properties.
425
- */
426
- projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
427
- op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
428
- value?: number | boolean;
429
- severity?: "error" | "warning";
430
- message?: string;
431
- };
432
- type ValidatorOptions = {
433
- serviceMap?: DgpServiceMap;
434
- nodeMap?: NodeMap;
435
- allowUnsafe?: boolean;
436
- selectedOptionKeys?: string[];
437
- globalUtilityGuard?: boolean;
438
- policies?: DynamicRule[];
439
- fallbackSettings?: FallbackSettings;
440
- };
441
- type RatePolicy = {
442
- kind: "lte_primary";
443
- } | {
444
- kind: "within_pct";
445
- pct: number;
446
- } | {
447
- kind: "at_least_pct_lower";
448
- pct: number;
449
- };
450
- type FallbackSettings = {
451
- /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
452
- requireConstraintFit?: boolean;
453
- /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
454
- ratePolicy?: RatePolicy;
455
- /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
456
- selectionStrategy?: "priority" | "cheapest";
457
- /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
458
- mode?: "strict" | "dev";
459
- };
460
-
461
462
  /** Options you can set on the builder (used for validation/visibility) */
462
463
  type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
463
464
  serviceMap?: DgpServiceMap;
@@ -2,6 +2,146 @@ import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
 
5
+ type TimeRangeEstimate = {
6
+ min_seconds?: number;
7
+ max_seconds?: number;
8
+ label?: string;
9
+ meta?: Record<string, unknown>;
10
+ };
11
+ type SpeedEstimate = {
12
+ amount?: number;
13
+ per?: "minute" | "hour" | "day" | "week" | "month";
14
+ unit?: string;
15
+ label?: string;
16
+ meta?: Record<string, unknown>;
17
+ };
18
+ type ServiceEstimates = {
19
+ start?: TimeRangeEstimate;
20
+ speed?: SpeedEstimate;
21
+ average?: TimeRangeEstimate;
22
+ meta?: Record<string, unknown>;
23
+ };
24
+ type ServiceFlag = {
25
+ enabled: boolean;
26
+ description: string;
27
+ meta?: Record<string, unknown>;
28
+ };
29
+ type IdType = string | number;
30
+ type ServiceFlags = Record<string, ServiceFlag>;
31
+ type DgpServiceCapability = {
32
+ id: IdType;
33
+ name?: string;
34
+ rate: number;
35
+ min?: number;
36
+ max?: number;
37
+ category?: string;
38
+ flags?: ServiceFlags;
39
+ estimates?: ServiceEstimates;
40
+ meta?: Record<string, unknown>;
41
+ [x: string]: any;
42
+ };
43
+ type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
44
+
45
+ type NodeRef$1 = {
46
+ kind: "tag";
47
+ id: string;
48
+ node: Tag;
49
+ } | {
50
+ kind: "field";
51
+ id: string;
52
+ node: Field;
53
+ } | {
54
+ kind: "option";
55
+ id: string;
56
+ node: FieldOption;
57
+ fieldId: string;
58
+ };
59
+ type NodeMap = Map<string, NodeRef$1>;
60
+
61
+ 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";
62
+ type ValidationError = {
63
+ code: ValidationCode;
64
+ message: string;
65
+ severity: "error" | "warning" | "info";
66
+ nodeId?: string;
67
+ details?: Record<string, unknown> & {
68
+ affectedIds?: string[];
69
+ };
70
+ };
71
+ type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
72
+ /**
73
+ * Host-extensible service filter clause.
74
+ * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
75
+ */
76
+ type ServiceWhereClause = {
77
+ path: string;
78
+ op?: ServiceWhereOp;
79
+ value?: unknown;
80
+ };
81
+ type DynamicRule = {
82
+ id: string;
83
+ label?: string;
84
+ scope: "global" | "visible_group";
85
+ subject: "services";
86
+ /**
87
+ * Package-level filter surface:
88
+ * - role/tag/field are core to the builder schema
89
+ * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
90
+ */
91
+ filter?: {
92
+ role?: "base" | "utility" | "both";
93
+ tag_id?: string | string[];
94
+ field_id?: string | string[];
95
+ where?: ServiceWhereClause[];
96
+ };
97
+ /**
98
+ * Projection is intentionally open:
99
+ * hosts may project custom service properties.
100
+ */
101
+ projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
102
+ op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
103
+ value?: number | boolean;
104
+ severity?: "error" | "warning";
105
+ message?: string;
106
+ };
107
+ type ValidatorOptions = {
108
+ serviceMap?: DgpServiceMap;
109
+ nodeMap?: NodeMap;
110
+ allowUnsafe?: boolean;
111
+ selectedOptionKeys?: string[];
112
+ globalUtilityGuard?: boolean;
113
+ policies?: DynamicRule[];
114
+ fallbackSettings?: FallbackSettings;
115
+ };
116
+ type RatePolicy = {
117
+ kind: "lte_primary";
118
+ } | {
119
+ kind: "within_pct";
120
+ pct: number;
121
+ } | {
122
+ kind: "at_least_pct_lower";
123
+ pct: number;
124
+ };
125
+ type FallbackSettings = {
126
+ /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
127
+ requireConstraintFit?: boolean;
128
+ /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
129
+ ratePolicy?: RatePolicy;
130
+ /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
131
+ selectionStrategy?: "priority" | "cheapest";
132
+ /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
133
+ mode?: "strict" | "dev";
134
+ };
135
+
136
+ type ServiceIdRef = number | string;
137
+ type NodeIdRef = string;
138
+ type ServiceFallback = {
139
+ /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
140
+ nodes?: Record<NodeIdRef, ServiceIdRef[]>;
141
+ /** Primary→fallback list used when no node-scoped entry is present */
142
+ global?: Record<ServiceIdRef, ServiceIdRef[]>;
143
+ };
144
+
5
145
  type PricingRole = "base" | "utility";
6
146
  type FieldType = "custom" | (string & {});
7
147
  /** ── Marker types (live inside meta; non-breaking) ───────────────────── */
@@ -100,14 +240,6 @@ type ServiceProps = {
100
240
  name?: string;
101
241
  notices?: ServicePropsNotice[];
102
242
  };
103
- type ServiceIdRef = number | string;
104
- type NodeIdRef = string;
105
- type ServiceFallback = {
106
- /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
107
- nodes?: Record<NodeIdRef, ServiceIdRef[]>;
108
- /** Primary→fallback list used when no node-scoped entry is present */
109
- global?: Record<ServiceIdRef, ServiceIdRef[]>;
110
- };
111
243
  type NoticeType = "public" | "private";
112
244
  type NoticeSeverity = "info" | "warning" | "error";
113
245
  /**
@@ -327,137 +459,6 @@ type EditorSnapshot = {
327
459
  meta?: Record<string, unknown>;
328
460
  };
329
461
 
330
- type TimeRangeEstimate = {
331
- min_seconds?: number;
332
- max_seconds?: number;
333
- label?: string;
334
- meta?: Record<string, unknown>;
335
- };
336
- type SpeedEstimate = {
337
- amount?: number;
338
- per?: "minute" | "hour" | "day" | "week" | "month";
339
- unit?: string;
340
- label?: string;
341
- meta?: Record<string, unknown>;
342
- };
343
- type ServiceEstimates = {
344
- start?: TimeRangeEstimate;
345
- speed?: SpeedEstimate;
346
- average?: TimeRangeEstimate;
347
- meta?: Record<string, unknown>;
348
- };
349
- type ServiceFlag = {
350
- enabled: boolean;
351
- description: string;
352
- meta?: Record<string, unknown>;
353
- };
354
- type IdType = string | number;
355
- type ServiceFlags = Record<string, ServiceFlag>;
356
- type DgpServiceCapability = {
357
- id: IdType;
358
- name?: string;
359
- rate: number;
360
- min?: number;
361
- max?: number;
362
- category?: string;
363
- flags?: ServiceFlags;
364
- estimates?: ServiceEstimates;
365
- meta?: Record<string, unknown>;
366
- [x: string]: any;
367
- };
368
- type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
369
-
370
- type NodeRef$1 = {
371
- kind: "tag";
372
- id: string;
373
- node: Tag;
374
- } | {
375
- kind: "field";
376
- id: string;
377
- node: Field;
378
- } | {
379
- kind: "option";
380
- id: string;
381
- node: FieldOption;
382
- fieldId: string;
383
- };
384
- type NodeMap = Map<string, NodeRef$1>;
385
-
386
- 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";
387
- type ValidationError = {
388
- code: ValidationCode;
389
- message: string;
390
- severity: "error" | "warning" | "info";
391
- nodeId?: string;
392
- details?: Record<string, unknown> & {
393
- affectedIds?: string[];
394
- };
395
- };
396
- type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
397
- /**
398
- * Host-extensible service filter clause.
399
- * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
400
- */
401
- type ServiceWhereClause = {
402
- path: string;
403
- op?: ServiceWhereOp;
404
- value?: unknown;
405
- };
406
- type DynamicRule = {
407
- id: string;
408
- label?: string;
409
- scope: "global" | "visible_group";
410
- subject: "services";
411
- /**
412
- * Package-level filter surface:
413
- * - role/tag/field are core to the builder schema
414
- * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
415
- */
416
- filter?: {
417
- role?: "base" | "utility" | "both";
418
- tag_id?: string | string[];
419
- field_id?: string | string[];
420
- where?: ServiceWhereClause[];
421
- };
422
- /**
423
- * Projection is intentionally open:
424
- * hosts may project custom service properties.
425
- */
426
- projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
427
- op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
428
- value?: number | boolean;
429
- severity?: "error" | "warning";
430
- message?: string;
431
- };
432
- type ValidatorOptions = {
433
- serviceMap?: DgpServiceMap;
434
- nodeMap?: NodeMap;
435
- allowUnsafe?: boolean;
436
- selectedOptionKeys?: string[];
437
- globalUtilityGuard?: boolean;
438
- policies?: DynamicRule[];
439
- fallbackSettings?: FallbackSettings;
440
- };
441
- type RatePolicy = {
442
- kind: "lte_primary";
443
- } | {
444
- kind: "within_pct";
445
- pct: number;
446
- } | {
447
- kind: "at_least_pct_lower";
448
- pct: number;
449
- };
450
- type FallbackSettings = {
451
- /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
452
- requireConstraintFit?: boolean;
453
- /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
454
- ratePolicy?: RatePolicy;
455
- /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
456
- selectionStrategy?: "priority" | "cheapest";
457
- /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
458
- mode?: "strict" | "dev";
459
- };
460
-
461
462
  /** Options you can set on the builder (used for validation/visibility) */
462
463
  type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
463
464
  serviceMap?: DgpServiceMap;