@timeax/digital-service-engine 0.0.2 → 0.0.4

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,381 @@ import { NodeProps } from 'reactflow';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import React, { ReactNode } from 'react';
4
4
 
5
+ interface ButtonValue {
6
+ id: string;
7
+ value: string | number;
8
+ service_id?: number;
9
+ pricing_role?: "base" | "utility";
10
+ meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
11
+ }
12
+ type Scalar = string | number | boolean | ButtonValue | null;
13
+ type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
14
+ type QuantityRule$1 = {
15
+ valueBy: "value" | "length" | "eval";
16
+ code?: string;
17
+ };
18
+ type UtilityLineItem = {
19
+ nodeId: string;
20
+ mode: UtilityMode;
21
+ rate: number;
22
+ inputs: {
23
+ quantity: number;
24
+ value?: Scalar | Scalar[];
25
+ valueBy?: "value" | "length" | "eval";
26
+ evalCodeUsed?: boolean;
27
+ };
28
+ };
29
+ type ServiceFallbacks = ServiceFallback;
30
+ type FallbackDiagnostics = {
31
+ scope: "node" | "global";
32
+ nodeId?: string;
33
+ primary: string | number;
34
+ candidate: string | number;
35
+ reasons: Array<"rate_violation" | "constraint_mismatch" | "unknown_service" | "ambiguous_context">;
36
+ };
37
+ type SnapshotContext = {
38
+ /** The single active tag id for this order */
39
+ tag: string;
40
+ /** Effective (post-propagation) constraints on that tag */
41
+ constraints: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
42
+ /**
43
+ * Per-node evaluation context:
44
+ * - For the active tag node itself: the same tag id.
45
+ * - For an option node: parent's field.bind_id must include this tag to be applicable; otherwise null.
46
+ * - For a field node (optional to include later): same rule as option, derived from field.bind_id.
47
+ */
48
+ nodeContexts: Record<string, string | null>;
49
+ /** Client pruning policy used (so server can mirror/compare). */
50
+ policy: {
51
+ ratePolicy: {
52
+ kind: "lte_primary" | "none";
53
+ thresholdPct?: number;
54
+ };
55
+ requireConstraintFit: boolean;
56
+ };
57
+ };
58
+ type OrderSnapshot = {
59
+ version: "1";
60
+ mode: "prod" | "dev";
61
+ builtAt: string;
62
+ selection: {
63
+ tag: string;
64
+ buttons: string[];
65
+ fields: Array<{
66
+ id: string;
67
+ type: string;
68
+ selectedOptions?: string[];
69
+ }>;
70
+ };
71
+ inputs: {
72
+ form: Record<string, Scalar | Scalar[]>;
73
+ selections: Record<string, string[]>;
74
+ };
75
+ quantity: number;
76
+ quantitySource: {
77
+ kind: "field" | "tag" | "option" | "default";
78
+ id?: string;
79
+ rule?: QuantityRule$1;
80
+ defaultedFromHost?: boolean;
81
+ };
82
+ min: number;
83
+ max: number;
84
+ services: Array<string | number>;
85
+ serviceMap: Record<string, Array<string | number>>;
86
+ fallbacks?: ServiceFallbacks;
87
+ utilities?: UtilityLineItem[];
88
+ warnings?: {
89
+ utility?: Array<{
90
+ nodeId: string;
91
+ reason: string;
92
+ }>;
93
+ fallbacks?: FallbackDiagnostics[];
94
+ };
95
+ meta?: {
96
+ schema_version?: string;
97
+ workspaceId?: string;
98
+ builder?: {
99
+ commit?: string;
100
+ };
101
+ context?: SnapshotContext;
102
+ };
103
+ };
104
+
105
+ type TimeRangeEstimate = {
106
+ min_seconds?: number;
107
+ max_seconds?: number;
108
+ label?: string;
109
+ meta?: Record<string, unknown>;
110
+ };
111
+ type SpeedEstimate = {
112
+ amount?: number;
113
+ per?: "minute" | "hour" | "day" | "week" | "month";
114
+ unit?: string;
115
+ label?: string;
116
+ meta?: Record<string, unknown>;
117
+ };
118
+ type ServiceEstimates = {
119
+ start?: TimeRangeEstimate;
120
+ speed?: SpeedEstimate;
121
+ average?: TimeRangeEstimate;
122
+ meta?: Record<string, unknown>;
123
+ };
124
+ type ServiceFlag = {
125
+ enabled: boolean;
126
+ description: string;
127
+ meta?: Record<string, unknown>;
128
+ };
129
+ type IdType = string | number;
130
+ type ServiceFlags = Record<string, ServiceFlag>;
131
+ type DgpServiceCapability = {
132
+ id: IdType;
133
+ name?: string;
134
+ rate: number;
135
+ min?: number;
136
+ max?: number;
137
+ category?: string;
138
+ flags?: ServiceFlags;
139
+ estimates?: ServiceEstimates;
140
+ meta?: Record<string, unknown>;
141
+ [x: string]: any;
142
+ };
143
+ type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
144
+
145
+ type NodeRef$1 = {
146
+ kind: "tag";
147
+ id: string;
148
+ node: Tag;
149
+ } | {
150
+ kind: "field";
151
+ id: string;
152
+ node: Field;
153
+ } | {
154
+ kind: "option";
155
+ id: string;
156
+ node: FieldOption;
157
+ fieldId: string;
158
+ };
159
+ type NodeMap = Map<string, NodeRef$1>;
160
+
161
+ 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";
162
+ type ValidationError = {
163
+ code: ValidationCode;
164
+ message: string;
165
+ severity: "error" | "warning" | "info";
166
+ nodeId?: string;
167
+ details?: Record<string, unknown> & {
168
+ affectedIds?: string[];
169
+ };
170
+ };
171
+ type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
172
+ /**
173
+ * Host-extensible service filter clause.
174
+ * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
175
+ */
176
+ type ServiceWhereClause = {
177
+ path: string;
178
+ op?: ServiceWhereOp;
179
+ value?: unknown;
180
+ };
181
+ type DynamicRule = {
182
+ id: string;
183
+ label?: string;
184
+ scope: "global" | "visible_group";
185
+ subject: "services";
186
+ /**
187
+ * Package-level filter surface:
188
+ * - role/tag/field are core to the builder schema
189
+ * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
190
+ */
191
+ filter?: {
192
+ role?: "base" | "utility" | "both";
193
+ tag_id?: string | string[];
194
+ field_id?: string | string[];
195
+ where?: ServiceWhereClause[];
196
+ };
197
+ /**
198
+ * Projection is intentionally open:
199
+ * hosts may project custom service properties.
200
+ */
201
+ projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
202
+ op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
203
+ value?: number | boolean;
204
+ severity?: "error" | "warning";
205
+ message?: string;
206
+ };
207
+ type ValidatorOptions = {
208
+ serviceMap?: DgpServiceMap;
209
+ nodeMap?: NodeMap;
210
+ allowUnsafe?: boolean;
211
+ selectedOptionKeys?: string[];
212
+ globalUtilityGuard?: boolean;
213
+ policies?: DynamicRule[];
214
+ fallbackSettings?: FallbackSettings;
215
+ };
216
+ type RatePolicy = {
217
+ kind: "lte_primary";
218
+ } | {
219
+ kind: "within_pct";
220
+ pct: number;
221
+ } | {
222
+ kind: "at_least_pct_lower";
223
+ pct: number;
224
+ };
225
+ type FallbackSettings = {
226
+ /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
227
+ requireConstraintFit?: boolean;
228
+ /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
229
+ ratePolicy?: RatePolicy;
230
+ /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
231
+ selectionStrategy?: "priority" | "cheapest";
232
+ /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
233
+ mode?: "strict" | "dev";
234
+ };
235
+
236
+ type ServiceIdRef = number | string;
237
+ type NodeIdRef = string;
238
+ type ServiceFallback = {
239
+ /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
240
+ nodes?: Record<NodeIdRef, ServiceIdRef[]>;
241
+ /** Primary→fallback list used when no node-scoped entry is present */
242
+ global?: Record<ServiceIdRef, ServiceIdRef[]>;
243
+ };
244
+ /**
245
+ * Optional service-map shape.
246
+ * Keep this loose for now so the editor can be reused by host apps.
247
+ */
248
+ type FallbackEditorServiceRecord = {
249
+ id: ServiceIdRef;
250
+ rate?: number;
251
+ service_id?: ServiceIdRef;
252
+ [key: string]: unknown;
253
+ };
254
+ type FallbackEditorServiceMap = DgpServiceMap;
255
+ type FallbackRegistrationScope = "global" | "node";
256
+ type FallbackScopeRef = {
257
+ scope: "global";
258
+ primary: ServiceIdRef;
259
+ } | {
260
+ scope: "node";
261
+ nodeId: NodeIdRef;
262
+ };
263
+ type FallbackRegistration = {
264
+ scope: FallbackRegistrationScope;
265
+ /**
266
+ * For node scope => node id
267
+ * For global scope => omitted
268
+ */
269
+ scopeId?: NodeIdRef;
270
+ /**
271
+ * The primary DGP service this registration belongs to.
272
+ * For global scope, this is the global key.
273
+ * For node scope, this is resolved from ServiceProps/snapshot context.
274
+ */
275
+ primary: ServiceIdRef;
276
+ /** Registered fallback services */
277
+ services: ServiceIdRef[];
278
+ };
279
+ type FallbackCheckReason = "duplicate" | "self_reference" | "unknown_primary" | "unknown_candidate" | "missing_snapshot" | "node_scope_not_supported" | "node_primary_unresolved" | "ambiguous_context" | "invalid_candidate" | "unknown_service" | "no_primary" | "rate_violation" | "constraint_mismatch" | "cycle" | "no_tag_context" | "missing_service_props" | "node_not_found";
280
+ type FallbackCandidateCheck = {
281
+ candidate: ServiceIdRef;
282
+ ok: boolean;
283
+ reasons: FallbackCheckReason[];
284
+ };
285
+ type FallbackCheckResult = {
286
+ context: FallbackScopeRef;
287
+ /**
288
+ * Resolved primary when known.
289
+ * For global scope this should normally equal context.primary.
290
+ */
291
+ primary?: ServiceIdRef;
292
+ allowed: ServiceIdRef[];
293
+ rejected: FallbackCandidateCheck[];
294
+ warnings: FallbackCheckReason[];
295
+ };
296
+ type FallbackEditorState = {
297
+ original: ServiceFallback;
298
+ current: ServiceFallback;
299
+ changed: boolean;
300
+ };
301
+ type FallbackEditorOptions = {
302
+ /**
303
+ * The editable payload.
304
+ * The editor clones this and never mutates the caller’s object directly.
305
+ */
306
+ fallbacks?: ServiceFallback;
307
+ /**
308
+ * Optional read-only source used to resolve node→service ownership
309
+ * and validate node-scoped registrations.
310
+ */
311
+ props?: ServiceProps;
312
+ /**
313
+ * Optional runtime context enhancer.
314
+ * Useful for ambiguous node contexts / diagnostics.
315
+ */
316
+ snapshot?: OrderSnapshot;
317
+ /**
318
+ * Optional service map used for rate / existence validation.
319
+ */
320
+ services?: FallbackEditorServiceMap;
321
+ /**
322
+ * Optional fallback policy.
323
+ */
324
+ settings?: FallbackSettings;
325
+ };
326
+ type FallbackMutationOptions = {
327
+ /**
328
+ * When true, reject candidates failing validation.
329
+ * When false, keep structurally valid values and return warnings.
330
+ */
331
+ strict?: boolean;
332
+ /**
333
+ * Optional insert position for add/addMany.
334
+ * Omit to append.
335
+ */
336
+ index?: number;
337
+ };
338
+ interface FallbackEditor {
339
+ /** Returns original + current editable state */
340
+ state(): FallbackEditorState;
341
+ /** Returns the current editable fallback payload */
342
+ value(): ServiceFallback;
343
+ /** Restores current back to original */
344
+ reset(): FallbackEditorState;
345
+ /**
346
+ * Returns all registrations belonging to a given primary DGP service.
347
+ *
348
+ * With ServiceProps:
349
+ * - includes global registrations
350
+ * - includes node registrations whose node resolves to this primary
351
+ *
352
+ * Without ServiceProps:
353
+ * - global registrations only
354
+ */
355
+ get(serviceId: ServiceIdRef): FallbackRegistration[];
356
+ /**
357
+ * Direct/raw scope lookup.
358
+ * - global => current.global[primary] ?? []
359
+ * - node => current.nodes[nodeId] ?? []
360
+ */
361
+ getScope(context: FallbackScopeRef): ServiceIdRef[];
362
+ /**
363
+ * Pure validation/preview.
364
+ * If candidates omitted, validates the currently stored scope value.
365
+ */
366
+ check(context: FallbackScopeRef, candidates?: ServiceIdRef[]): FallbackCheckResult;
367
+ /** Adds one candidate to an exact scope */
368
+ add(context: FallbackScopeRef, candidate: ServiceIdRef, options?: FallbackMutationOptions): FallbackEditorState;
369
+ /** Adds many candidates to an exact scope */
370
+ addMany(context: FallbackScopeRef, candidates: ServiceIdRef[], options?: FallbackMutationOptions): FallbackEditorState;
371
+ /** Removes one candidate from an exact scope */
372
+ remove(context: FallbackScopeRef, candidate: ServiceIdRef): FallbackEditorState;
373
+ /** Replaces the exact scope value */
374
+ replace(context: FallbackScopeRef, candidates: ServiceIdRef[], options?: FallbackMutationOptions): FallbackEditorState;
375
+ /** Clears one exact scope value */
376
+ clear(context: FallbackScopeRef): FallbackEditorState;
377
+ }
378
+ declare function createFallbackEditor(options?: FallbackEditorOptions): FallbackEditor;
379
+
5
380
  type PricingRole = "base" | "utility";
6
381
  type FieldType = "custom" | (string & {});
7
382
  /** ── Marker types (live inside meta; non-breaking) ───────────────────── */
@@ -214,14 +589,6 @@ type ServiceProps = {
214
589
  name?: string;
215
590
  notices?: ServicePropsNotice[];
216
591
  };
217
- type ServiceIdRef = number | string;
218
- type NodeIdRef = string;
219
- type ServiceFallback = {
220
- /** Node-scoped fallbacks: prefer these when that node’s primary service fails */
221
- nodes?: Record<NodeIdRef, ServiceIdRef[]>;
222
- /** Primary→fallback list used when no node-scoped entry is present */
223
- global?: Record<ServiceIdRef, ServiceIdRef[]>;
224
- };
225
592
  type NoticeType = "public" | "private";
226
593
  type NoticeSeverity = "info" | "warning" | "error";
227
594
  /**
@@ -449,137 +816,6 @@ type EditorSnapshot = {
449
816
  meta?: Record<string, unknown>;
450
817
  };
451
818
 
452
- type TimeRangeEstimate = {
453
- min_seconds?: number;
454
- max_seconds?: number;
455
- label?: string;
456
- meta?: Record<string, unknown>;
457
- };
458
- type SpeedEstimate = {
459
- amount?: number;
460
- per?: "minute" | "hour" | "day" | "week" | "month";
461
- unit?: string;
462
- label?: string;
463
- meta?: Record<string, unknown>;
464
- };
465
- type ServiceEstimates = {
466
- start?: TimeRangeEstimate;
467
- speed?: SpeedEstimate;
468
- average?: TimeRangeEstimate;
469
- meta?: Record<string, unknown>;
470
- };
471
- type ServiceFlag = {
472
- enabled: boolean;
473
- description: string;
474
- meta?: Record<string, unknown>;
475
- };
476
- type IdType = string | number;
477
- type ServiceFlags = Record<string, ServiceFlag>;
478
- type DgpServiceCapability = {
479
- id: IdType;
480
- name?: string;
481
- rate: number;
482
- min?: number;
483
- max?: number;
484
- category?: string;
485
- flags?: ServiceFlags;
486
- estimates?: ServiceEstimates;
487
- meta?: Record<string, unknown>;
488
- [x: string]: any;
489
- };
490
- type DgpServiceMap = Record<string, DgpServiceCapability> & Record<number, DgpServiceCapability>;
491
-
492
- type NodeRef$1 = {
493
- kind: "tag";
494
- id: string;
495
- node: Tag;
496
- } | {
497
- kind: "field";
498
- id: string;
499
- node: Field;
500
- } | {
501
- kind: "option";
502
- id: string;
503
- node: FieldOption;
504
- fieldId: string;
505
- };
506
- type NodeMap = Map<string, NodeRef$1>;
507
-
508
- 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";
509
- type ValidationError = {
510
- code: ValidationCode;
511
- message: string;
512
- severity: "error" | "warning" | "info";
513
- nodeId?: string;
514
- details?: Record<string, unknown> & {
515
- affectedIds?: string[];
516
- };
517
- };
518
- type ServiceWhereOp = "eq" | "neq" | "in" | "nin" | "exists" | "truthy" | "falsy" | "sw";
519
- /**
520
- * Host-extensible service filter clause.
521
- * `path` should usually be "service.<prop>" or "service.meta.<prop>" etc.
522
- */
523
- type ServiceWhereClause = {
524
- path: string;
525
- op?: ServiceWhereOp;
526
- value?: unknown;
527
- };
528
- type DynamicRule = {
529
- id: string;
530
- label?: string;
531
- scope: "global" | "visible_group";
532
- subject: "services";
533
- /**
534
- * Package-level filter surface:
535
- * - role/tag/field are core to the builder schema
536
- * - where[] allows hosts to filter against extra service properties (handler_id/platform_id/type/key/category_id/etc.)
537
- */
538
- filter?: {
539
- role?: "base" | "utility" | "both";
540
- tag_id?: string | string[];
541
- field_id?: string | string[];
542
- where?: ServiceWhereClause[];
543
- };
544
- /**
545
- * Projection is intentionally open:
546
- * hosts may project custom service properties.
547
- */
548
- projection?: "service.id" | "service.name" | "service.rate" | "service.min" | "service.max" | "service.category" | string;
549
- op: "all_equal" | "unique" | "no_mix" | "all_true" | "any_true" | "max_count" | "min_count";
550
- value?: number | boolean;
551
- severity?: "error" | "warning";
552
- message?: string;
553
- };
554
- type ValidatorOptions = {
555
- serviceMap?: DgpServiceMap;
556
- nodeMap?: NodeMap;
557
- allowUnsafe?: boolean;
558
- selectedOptionKeys?: string[];
559
- globalUtilityGuard?: boolean;
560
- policies?: DynamicRule[];
561
- fallbackSettings?: FallbackSettings;
562
- };
563
- type RatePolicy = {
564
- kind: "lte_primary";
565
- } | {
566
- kind: "within_pct";
567
- pct: number;
568
- } | {
569
- kind: "at_least_pct_lower";
570
- pct: number;
571
- };
572
- type FallbackSettings = {
573
- /** Require fallbacks to satisfy tag constraints (dripfeed/refill/cancel) when a tag context is known. Default: true */
574
- requireConstraintFit?: boolean;
575
- /** Rate rule policy. Default: { kind: 'lte_primary' } i.e. candidate.rate <= primary.rate */
576
- ratePolicy?: RatePolicy;
577
- /** When multiple candidates remain, choose first (priority) or cheapest. Default: 'priority' */
578
- selectionStrategy?: "priority" | "cheapest";
579
- /** Validation mode: 'strict' → node-scoped violations reported as ValidationError; 'dev' → only collect diagnostics. Default: 'strict' */
580
- mode?: "strict" | "dev";
581
- };
582
-
583
819
  /** Options you can set on the builder (used for validation/visibility) */
584
820
  type BuilderOptions = Omit<ValidatorOptions, "serviceMap"> & {
585
821
  serviceMap?: DgpServiceMap;
@@ -628,111 +864,6 @@ interface Builder {
628
864
  getNodeMap(): NodeMap;
629
865
  }
630
866
 
631
- interface ButtonValue {
632
- id: string;
633
- value: string | number;
634
- service_id?: number;
635
- pricing_role?: "base" | "utility";
636
- meta?: Record<string, unknown> & UtilityMark & WithQuantityDefault;
637
- }
638
- type Scalar = string | number | boolean | ButtonValue | null;
639
- type UtilityMode = "flat" | "per_quantity" | "per_value" | "percent";
640
- type QuantityRule$1 = {
641
- valueBy: "value" | "length" | "eval";
642
- code?: string;
643
- };
644
- type UtilityLineItem = {
645
- nodeId: string;
646
- mode: UtilityMode;
647
- rate: number;
648
- inputs: {
649
- quantity: number;
650
- value?: Scalar | Scalar[];
651
- valueBy?: "value" | "length" | "eval";
652
- evalCodeUsed?: boolean;
653
- };
654
- };
655
- type ServiceFallbacks = {
656
- nodes?: Record<string, Array<string | number>>;
657
- global?: Record<string | number, Array<string | number>>;
658
- };
659
- type FallbackDiagnostics = {
660
- scope: "node" | "global";
661
- nodeId?: string;
662
- primary: string | number;
663
- candidate: string | number;
664
- reasons: Array<"rate_violation" | "constraint_mismatch" | "unknown_service" | "ambiguous_context">;
665
- };
666
- type SnapshotContext = {
667
- /** The single active tag id for this order */
668
- tag: string;
669
- /** Effective (post-propagation) constraints on that tag */
670
- constraints: Partial<Record<"refill" | "cancel" | "dripfeed", boolean>>;
671
- /**
672
- * Per-node evaluation context:
673
- * - For the active tag node itself: the same tag id.
674
- * - For an option node: parent's field.bind_id must include this tag to be applicable; otherwise null.
675
- * - For a field node (optional to include later): same rule as option, derived from field.bind_id.
676
- */
677
- nodeContexts: Record<string, string | null>;
678
- /** Client pruning policy used (so server can mirror/compare). */
679
- policy: {
680
- ratePolicy: {
681
- kind: "lte_primary" | "none";
682
- thresholdPct?: number;
683
- };
684
- requireConstraintFit: boolean;
685
- };
686
- };
687
- type OrderSnapshot = {
688
- version: "1";
689
- mode: "prod" | "dev";
690
- builtAt: string;
691
- selection: {
692
- tag: string;
693
- fields: Array<{
694
- id: string;
695
- type: string;
696
- selectedOptions?: string[];
697
- }>;
698
- };
699
- inputs: {
700
- form: Record<string, Scalar | Scalar[]>;
701
- selections: Record<string, string[]>;
702
- };
703
- quantity: number;
704
- quantitySource: {
705
- kind: "field" | "tag" | "option" | "default";
706
- id?: string;
707
- rule?: QuantityRule$1;
708
- defaultedFromHost?: boolean;
709
- };
710
- min: number;
711
- max: number;
712
- services: Array<string | number>;
713
- serviceMap: Record<string, Array<string | number>>;
714
- fallbacks?: {
715
- nodes?: Record<string, Array<string | number>>;
716
- global?: Record<string | number, Array<string | number>>;
717
- };
718
- utilities?: UtilityLineItem[];
719
- warnings?: {
720
- utility?: Array<{
721
- nodeId: string;
722
- reason: string;
723
- }>;
724
- fallbacks?: FallbackDiagnostics[];
725
- };
726
- meta?: {
727
- schema_version?: string;
728
- workspaceId?: string;
729
- builder?: {
730
- commit?: string;
731
- };
732
- context?: SnapshotContext;
733
- };
734
- };
735
-
736
867
  type Env = "client" | "workspace";
737
868
  type VisibleGroup = {
738
869
  tagId?: string;
@@ -782,10 +913,34 @@ declare class Selection {
782
913
  currentTag(): string | undefined;
783
914
  onChange(fn: Listener): () => void;
784
915
  visibleGroup(): VisibleGroupResult;
916
+ /**
917
+ * Build a fieldId -> triggerKeys[] map from the current selection set.
918
+ *
919
+ * What counts as a "button selection" (trigger key):
920
+ * - field key where the field has button === true (e.g. "f:dripfeed")
921
+ * - option key (e.g. "o:fast")
922
+ * - composite key "fieldId::optionId" (e.g. "f:speed::o:fast")
923
+ *
924
+ * Grouping:
925
+ * - button-field trigger groups under its own fieldId
926
+ * - option/composite groups under the option's owning fieldId (from nodeMap)
927
+ *
928
+ * Deterministic:
929
+ * - preserves selection insertion order
930
+ * - de-dupes per field
931
+ */
932
+ buttonSelectionsByFieldId(): Record<string, string[]>;
933
+ /**
934
+ * Returns only selection keys that are valid "trigger buttons":
935
+ * - field keys where field.button === true
936
+ * - option keys
937
+ * - composite keys "fieldId::optionId" (validated by optionId)
938
+ * Excludes tags and non-button fields.
939
+ */
940
+ selectedButtons(): string[];
785
941
  private emit;
786
942
  private updateCurrentTagFrom;
787
943
  private resolveTagContextId;
788
- private selectedButtonTriggerIds;
789
944
  private computeGroupForTag;
790
945
  private addServiceByRole;
791
946
  private findOptionById;
@@ -1590,13 +1745,8 @@ type FormProviderProps = {
1590
1745
  values?: Dict;
1591
1746
  selections?: Record<string, string[]>;
1592
1747
  };
1593
- /**
1594
- * Called on every palette update (values bag).
1595
- * Use to persist anywhere (state/localStorage/etc).
1596
- */
1597
- onUpdate?: (vals: Dict) => void;
1598
1748
  };
1599
- declare function FormProvider({ children, schema, initial, onUpdate, }: FormProviderProps): react_jsx_runtime.JSX.Element;
1749
+ declare function FormProvider({ children, schema, initial }: FormProviderProps): react_jsx_runtime.JSX.Element;
1600
1750
 
1601
1751
  /** Matches your InputWrapper’s expectations */
1602
1752
  type InputKind = string;
@@ -1608,6 +1758,7 @@ type AdapterCtx = {
1608
1758
  type Adapter = {
1609
1759
  valueProp?: string;
1610
1760
  changeProp?: string;
1761
+ errorProp?: string;
1611
1762
  /** normalize what the host emitted into what we store in form-palette */
1612
1763
  getValue?: (next: unknown, current: unknown, ctx: AdapterCtx) => unknown;
1613
1764
  /** REQUIRED if field.options exists */
@@ -1664,8 +1815,9 @@ type InputWrapperProps = {
1664
1815
  extraProps?: Record<string, unknown>;
1665
1816
  templateStrings?: boolean;
1666
1817
  ctxOverrides?: Record<string, unknown>;
1818
+ className?: string;
1667
1819
  };
1668
- declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1820
+ declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, className, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1669
1821
 
1670
1822
  type OrderFlowInit = {
1671
1823
  mode?: "prod" | "dev";
@@ -1685,6 +1837,7 @@ type OrderFlowInit = {
1685
1837
  * Host props provided to further enhance the nodes data
1686
1838
  */
1687
1839
  ctx?: Record<string, unknown>;
1840
+ normalizeRate?(svc: DgpServiceCapability): number;
1688
1841
  };
1689
1842
  type ProviderFlow = {
1690
1843
  builder: Builder;
@@ -1817,7 +1970,7 @@ type UseOrderFlowReturn = {
1817
1970
  clearFirst?: boolean;
1818
1971
  }) => void;
1819
1972
  /** VALIDATES via form.submit() */
1820
- buildSnapshot: () => OrderSnapshot;
1973
+ buildSnapshot: () => OrderSnapshot | undefined;
1821
1974
  fallbackPolicy: FallbackSettings;
1822
1975
  setFallbackPolicy: (next: FallbackSettings) => void;
1823
1976
  };
@@ -1829,4 +1982,4 @@ declare function useOrderFlow(): UseOrderFlowReturn;
1829
1982
  */
1830
1983
  declare function registerEntries(registry: Registry): void;
1831
1984
 
1832
- export { type Adapter, type AdapterCtx, type AdminPolicies, type BaseFieldUI, type ButtonValue, CanvasAPI, type CanvasEvents, type CanvasOptions, type CanvasState, type Command, type CommentAnchor, type CommentId, type CommentMessage, type CommentNode, type CommentThread, type ConnectKind, type ConstraintKey, type DgpServiceCapability, type DgpServiceMap, type DraftWire, type DynamicRule, type EdgeKind, type EdgeRoute, type EdgeView, type EditorEvents, type EditorOptions, type EditorSnapshot, EventBus, type EventMap, type FallbackDiagnostics, type FallbackSettings, type Field, type FieldOption, type FieldType, type FieldWithTypedDefaults, type FlagKey, type FlowNode, type FormApi, FormProvider, type FormProviderProps, type FormSnapshot, type GraphEdge, type GraphNode, type GraphSnapshot, type IdType, type InputDescriptor, type InputKind, type InputVariant, type InputWrapperProps, type LayoutState, type NodeIdRef, type NodeKind, type NodePos, type NodePositions, type NodeView, type NoticeKind, type NoticeSeverity, type NoticeTarget, type NoticeType, OrderFlowProvider, type OrderSnapshot, type PricingRole, Provider, type QuantityMark, type QuantityRule$1 as QuantityRule, type RatePolicy, type Registry, type Scalar, type ServiceEstimates, type ServiceFallback, type ServiceFallbacks, type ServiceFlag, type ServiceFlags, type ServiceIdRef, type ServiceProps, type ServicePropsNotice, type ServiceWhereClause, type ServiceWhereOp, type SnapshotContext, type SpeedEstimate, type Tag, type ThreadId, type TimeRangeEstimate, type Ui, type UiAnyOf, type UiArray, type UiBoolean, type UiNumber, type UiObject, type UiString, type UiValue, type UtilityLineItem, type UtilityMark, type UtilityMode, type ValidationCode, type ValidationError, type ValidatorOptions, type Viewport, type WithQuantityDefault, Wrapper, createInputRegistry, registerEntries, resolveInputDescriptor, useFormApi, useInputs, useOptionalFormApi, useOrderFlow, useOrderFlowContext };
1985
+ export { type Adapter, type AdapterCtx, type AdminPolicies, type BaseFieldUI, type ButtonValue, CanvasAPI, type CanvasEvents, type CanvasOptions, type CanvasState, type Command, type CommentAnchor, type CommentId, type CommentMessage, type CommentNode, type CommentThread, type ConnectKind, type ConstraintKey, type DgpServiceCapability, type DgpServiceMap, type DraftWire, type DynamicRule, type EdgeKind, type EdgeRoute, type EdgeView, type EditorEvents, type EditorOptions, type EditorSnapshot, EventBus, type EventMap, type FallbackCandidateCheck, type FallbackCheckReason, type FallbackCheckResult, type FallbackDiagnostics, type FallbackEditor, type FallbackEditorOptions, type FallbackEditorServiceMap, type FallbackEditorServiceRecord, type FallbackEditorState, type FallbackMutationOptions, type FallbackRegistration, type FallbackRegistrationScope, type FallbackScopeRef, type FallbackSettings, type Field, type FieldOption, type FieldType, type FieldWithTypedDefaults, type FlagKey, type FlowNode, type FormApi, FormProvider, type FormProviderProps, type FormSnapshot, type GraphEdge, type GraphNode, type GraphSnapshot, type IdType, type InputDescriptor, type InputKind, type InputVariant, type InputWrapperProps, type LayoutState, type NodeIdRef, type NodeKind, type NodePos, type NodePositions, type NodeView, type NoticeKind, type NoticeSeverity, type NoticeTarget, type NoticeType, OrderFlowProvider, type OrderSnapshot, type PricingRole, Provider, type QuantityMark, type QuantityRule$1 as QuantityRule, type RatePolicy, type Registry, type Scalar, type ServiceEstimates, type ServiceFallback, type ServiceFallbacks, type ServiceFlag, type ServiceFlags, type ServiceIdRef, type ServiceProps, type ServicePropsNotice, type ServiceWhereClause, type ServiceWhereOp, type SnapshotContext, type SpeedEstimate, type Tag, type ThreadId, type TimeRangeEstimate, type Ui, type UiAnyOf, type UiArray, type UiBoolean, type UiNumber, type UiObject, type UiString, type UiValue, type UtilityLineItem, type UtilityMark, type UtilityMode, type ValidationCode, type ValidationError, type ValidatorOptions, type Viewport, type WithQuantityDefault, Wrapper, createFallbackEditor, createInputRegistry, registerEntries, resolveInputDescriptor, useFormApi, useInputs, useOptionalFormApi, useOrderFlow, useOrderFlowContext };