@timeax/digital-service-engine 0.0.2 → 0.0.3

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.
@@ -690,6 +690,7 @@ type OrderSnapshot = {
690
690
  builtAt: string;
691
691
  selection: {
692
692
  tag: string;
693
+ buttons: string[];
693
694
  fields: Array<{
694
695
  id: string;
695
696
  type: string;
@@ -782,10 +783,34 @@ declare class Selection {
782
783
  currentTag(): string | undefined;
783
784
  onChange(fn: Listener): () => void;
784
785
  visibleGroup(): VisibleGroupResult;
786
+ /**
787
+ * Build a fieldId -> triggerKeys[] map from the current selection set.
788
+ *
789
+ * What counts as a "button selection" (trigger key):
790
+ * - field key where the field has button === true (e.g. "f:dripfeed")
791
+ * - option key (e.g. "o:fast")
792
+ * - composite key "fieldId::optionId" (e.g. "f:speed::o:fast")
793
+ *
794
+ * Grouping:
795
+ * - button-field trigger groups under its own fieldId
796
+ * - option/composite groups under the option's owning fieldId (from nodeMap)
797
+ *
798
+ * Deterministic:
799
+ * - preserves selection insertion order
800
+ * - de-dupes per field
801
+ */
802
+ buttonSelectionsByFieldId(): Record<string, string[]>;
803
+ /**
804
+ * Returns only selection keys that are valid "trigger buttons":
805
+ * - field keys where field.button === true
806
+ * - option keys
807
+ * - composite keys "fieldId::optionId" (validated by optionId)
808
+ * Excludes tags and non-button fields.
809
+ */
810
+ selectedButtons(): string[];
785
811
  private emit;
786
812
  private updateCurrentTagFrom;
787
813
  private resolveTagContextId;
788
- private selectedButtonTriggerIds;
789
814
  private computeGroupForTag;
790
815
  private addServiceByRole;
791
816
  private findOptionById;
@@ -1590,13 +1615,8 @@ type FormProviderProps = {
1590
1615
  values?: Dict;
1591
1616
  selections?: Record<string, string[]>;
1592
1617
  };
1593
- /**
1594
- * Called on every palette update (values bag).
1595
- * Use to persist anywhere (state/localStorage/etc).
1596
- */
1597
- onUpdate?: (vals: Dict) => void;
1598
1618
  };
1599
- declare function FormProvider({ children, schema, initial, onUpdate, }: FormProviderProps): react_jsx_runtime.JSX.Element;
1619
+ declare function FormProvider({ children, schema, initial }: FormProviderProps): react_jsx_runtime.JSX.Element;
1600
1620
 
1601
1621
  /** Matches your InputWrapper’s expectations */
1602
1622
  type InputKind = string;
@@ -1608,6 +1628,7 @@ type AdapterCtx = {
1608
1628
  type Adapter = {
1609
1629
  valueProp?: string;
1610
1630
  changeProp?: string;
1631
+ errorProp?: string;
1611
1632
  /** normalize what the host emitted into what we store in form-palette */
1612
1633
  getValue?: (next: unknown, current: unknown, ctx: AdapterCtx) => unknown;
1613
1634
  /** REQUIRED if field.options exists */
@@ -1664,8 +1685,9 @@ type InputWrapperProps = {
1664
1685
  extraProps?: Record<string, unknown>;
1665
1686
  templateStrings?: boolean;
1666
1687
  ctxOverrides?: Record<string, unknown>;
1688
+ className?: string;
1667
1689
  };
1668
- declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1690
+ declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, className, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1669
1691
 
1670
1692
  type OrderFlowInit = {
1671
1693
  mode?: "prod" | "dev";
@@ -1685,6 +1707,7 @@ type OrderFlowInit = {
1685
1707
  * Host props provided to further enhance the nodes data
1686
1708
  */
1687
1709
  ctx?: Record<string, unknown>;
1710
+ normalizeRate?(svc: DgpServiceCapability): number;
1688
1711
  };
1689
1712
  type ProviderFlow = {
1690
1713
  builder: Builder;
@@ -1817,7 +1840,7 @@ type UseOrderFlowReturn = {
1817
1840
  clearFirst?: boolean;
1818
1841
  }) => void;
1819
1842
  /** VALIDATES via form.submit() */
1820
- buildSnapshot: () => OrderSnapshot;
1843
+ buildSnapshot: () => OrderSnapshot | undefined;
1821
1844
  fallbackPolicy: FallbackSettings;
1822
1845
  setFallbackPolicy: (next: FallbackSettings) => void;
1823
1846
  };
@@ -690,6 +690,7 @@ type OrderSnapshot = {
690
690
  builtAt: string;
691
691
  selection: {
692
692
  tag: string;
693
+ buttons: string[];
693
694
  fields: Array<{
694
695
  id: string;
695
696
  type: string;
@@ -782,10 +783,34 @@ declare class Selection {
782
783
  currentTag(): string | undefined;
783
784
  onChange(fn: Listener): () => void;
784
785
  visibleGroup(): VisibleGroupResult;
786
+ /**
787
+ * Build a fieldId -> triggerKeys[] map from the current selection set.
788
+ *
789
+ * What counts as a "button selection" (trigger key):
790
+ * - field key where the field has button === true (e.g. "f:dripfeed")
791
+ * - option key (e.g. "o:fast")
792
+ * - composite key "fieldId::optionId" (e.g. "f:speed::o:fast")
793
+ *
794
+ * Grouping:
795
+ * - button-field trigger groups under its own fieldId
796
+ * - option/composite groups under the option's owning fieldId (from nodeMap)
797
+ *
798
+ * Deterministic:
799
+ * - preserves selection insertion order
800
+ * - de-dupes per field
801
+ */
802
+ buttonSelectionsByFieldId(): Record<string, string[]>;
803
+ /**
804
+ * Returns only selection keys that are valid "trigger buttons":
805
+ * - field keys where field.button === true
806
+ * - option keys
807
+ * - composite keys "fieldId::optionId" (validated by optionId)
808
+ * Excludes tags and non-button fields.
809
+ */
810
+ selectedButtons(): string[];
785
811
  private emit;
786
812
  private updateCurrentTagFrom;
787
813
  private resolveTagContextId;
788
- private selectedButtonTriggerIds;
789
814
  private computeGroupForTag;
790
815
  private addServiceByRole;
791
816
  private findOptionById;
@@ -1590,13 +1615,8 @@ type FormProviderProps = {
1590
1615
  values?: Dict;
1591
1616
  selections?: Record<string, string[]>;
1592
1617
  };
1593
- /**
1594
- * Called on every palette update (values bag).
1595
- * Use to persist anywhere (state/localStorage/etc).
1596
- */
1597
- onUpdate?: (vals: Dict) => void;
1598
1618
  };
1599
- declare function FormProvider({ children, schema, initial, onUpdate, }: FormProviderProps): react_jsx_runtime.JSX.Element;
1619
+ declare function FormProvider({ children, schema, initial }: FormProviderProps): react_jsx_runtime.JSX.Element;
1600
1620
 
1601
1621
  /** Matches your InputWrapper’s expectations */
1602
1622
  type InputKind = string;
@@ -1608,6 +1628,7 @@ type AdapterCtx = {
1608
1628
  type Adapter = {
1609
1629
  valueProp?: string;
1610
1630
  changeProp?: string;
1631
+ errorProp?: string;
1611
1632
  /** normalize what the host emitted into what we store in form-palette */
1612
1633
  getValue?: (next: unknown, current: unknown, ctx: AdapterCtx) => unknown;
1613
1634
  /** REQUIRED if field.options exists */
@@ -1664,8 +1685,9 @@ type InputWrapperProps = {
1664
1685
  extraProps?: Record<string, unknown>;
1665
1686
  templateStrings?: boolean;
1666
1687
  ctxOverrides?: Record<string, unknown>;
1688
+ className?: string;
1667
1689
  };
1668
- declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1690
+ declare function Wrapper({ field, disabled, extraProps, templateStrings, ctxOverrides, className, }: InputWrapperProps): react_jsx_runtime.JSX.Element | null;
1669
1691
 
1670
1692
  type OrderFlowInit = {
1671
1693
  mode?: "prod" | "dev";
@@ -1685,6 +1707,7 @@ type OrderFlowInit = {
1685
1707
  * Host props provided to further enhance the nodes data
1686
1708
  */
1687
1709
  ctx?: Record<string, unknown>;
1710
+ normalizeRate?(svc: DgpServiceCapability): number;
1688
1711
  };
1689
1712
  type ProviderFlow = {
1690
1713
  builder: Builder;
@@ -1817,7 +1840,7 @@ type UseOrderFlowReturn = {
1817
1840
  clearFirst?: boolean;
1818
1841
  }) => void;
1819
1842
  /** VALIDATES via form.submit() */
1820
- buildSnapshot: () => OrderSnapshot;
1843
+ buildSnapshot: () => OrderSnapshot | undefined;
1821
1844
  fallbackPolicy: FallbackSettings;
1822
1845
  setFallbackPolicy: (next: FallbackSettings) => void;
1823
1846
  };
@@ -1173,13 +1173,10 @@ function visibleFieldIdsUnder(props, tagId, opts = {}) {
1173
1173
  if (order && order.length) {
1174
1174
  const ordered = order.filter((fid) => visible.has(fid));
1175
1175
  const orderedSet = new Set(ordered);
1176
- const rest2 = base.filter((fid) => !orderedSet.has(fid));
1177
- return [...ordered, ...rest2];
1176
+ const rest = base.filter((fid) => !orderedSet.has(fid));
1177
+ return [...ordered, ...rest];
1178
1178
  }
1179
- const promoted = revealedOrder.filter((fid) => visible.has(fid));
1180
- const promotedSet = new Set(promoted);
1181
- const rest = base.filter((fid) => !promotedSet.has(fid));
1182
- return [...promoted, ...rest];
1179
+ return base;
1183
1180
  }
1184
1181
  function visibleFieldsUnder(props, tagId, opts = {}) {
1185
1182
  var _a;
@@ -3253,7 +3250,7 @@ function rateOk(svcMap, candidate, primary, policy) {
3253
3250
 
3254
3251
  // src/utils/build-order-snapshot.ts
3255
3252
  function buildOrderSnapshot(props, builder, selection, services, settings = {}) {
3256
- var _a, _b, _c, _d, _e, _f, _g;
3253
+ var _a, _b, _c, _d, _e, _f, _g, _h;
3257
3254
  const mode = (_a = settings.mode) != null ? _a : "prod";
3258
3255
  const hostDefaultQty = Number.isFinite(
3259
3256
  (_b = settings.hostDefaultQuantity) != null ? _b : 1
@@ -3267,20 +3264,18 @@ function buildOrderSnapshot(props, builder, selection, services, settings = {})
3267
3264
  };
3268
3265
  const builtAt = (/* @__PURE__ */ new Date()).toISOString();
3269
3266
  const tagId = selection.activeTagId;
3270
- const selectedOptionKeys = toSelectedOptionKeys(
3271
- selection.optionSelectionsByFieldId
3272
- );
3267
+ const selectedButtonKeys = (_d = selection.selectedKeys) != null ? _d : toSelectedOptionKeys(selection.optionSelectionsByFieldId);
3273
3268
  const visibleFieldIds = builder.visibleFields(
3274
3269
  tagId,
3275
- selectedOptionKeys
3270
+ selectedButtonKeys
3276
3271
  );
3277
3272
  const tagById = new Map(
3278
- ((_d = props.filters) != null ? _d : []).map((t) => [t.id, t])
3273
+ ((_e = props.filters) != null ? _e : []).map((t) => [t.id, t])
3279
3274
  );
3280
3275
  const fieldById = new Map(
3281
- ((_e = props.fields) != null ? _e : []).map((f) => [f.id, f])
3276
+ ((_f = props.fields) != null ? _f : []).map((f) => [f.id, f])
3282
3277
  );
3283
- const tagConstraints = (_g = (_f = tagById.get(tagId)) == null ? void 0 : _f.constraints) != null ? _g : void 0;
3278
+ const tagConstraints = (_h = (_g = tagById.get(tagId)) == null ? void 0 : _g.constraints) != null ? _h : void 0;
3284
3279
  const selectionFields = visibleFieldIds.map((fid) => fieldById.get(fid)).filter((f) => !!f).map((f) => {
3285
3280
  var _a2;
3286
3281
  const optIds = isOptionBased(f) ? (_a2 = selection.optionSelectionsByFieldId[f.id]) != null ? _a2 : [] : void 0;
@@ -3357,6 +3352,7 @@ function buildOrderSnapshot(props, builder, selection, services, settings = {})
3357
3352
  builtAt,
3358
3353
  selection: {
3359
3354
  tag: tagId,
3355
+ buttons: selectedButtonKeys,
3360
3356
  fields: selectionFields
3361
3357
  },
3362
3358
  inputs: {
@@ -3389,6 +3385,10 @@ function toSelectedOptionKeys(byField) {
3389
3385
  }
3390
3386
  return keys;
3391
3387
  }
3388
+ function isServicedBased(field) {
3389
+ if (field.service_id) return true;
3390
+ return !!(field.options && field.options.some((item) => item.service_id));
3391
+ }
3392
3392
  function buildInputs(visibleFieldIds, fieldById, selection) {
3393
3393
  const formValues = {};
3394
3394
  const selections = {};
@@ -3399,7 +3399,7 @@ function buildInputs(visibleFieldIds, fieldById, selection) {
3399
3399
  if (selOptIds && selOptIds.length) {
3400
3400
  selections[fid] = [...selOptIds];
3401
3401
  }
3402
- if (!isOptionBased(f)) {
3402
+ if (!isServicedBased(f)) {
3403
3403
  const name = f.name;
3404
3404
  const val = selection.formValuesByFieldId[fid];
3405
3405
  if (!name || val === void 0) continue;
@@ -6008,6 +6008,7 @@ var Selection = class {
6008
6008
  this.emit();
6009
6009
  }
6010
6010
  add(id) {
6011
+ if (this.set.has(id)) this.set.delete(id);
6011
6012
  this.set.add(id);
6012
6013
  this.primaryId = id;
6013
6014
  this.updateCurrentTagFrom(id);
@@ -6062,7 +6063,9 @@ var Selection = class {
6062
6063
  var _a;
6063
6064
  const props = this.builder.getProps();
6064
6065
  if (((_a = this.opts.env) != null ? _a : "client") === "workspace") {
6065
- const tagIds = Array.from(this.set).filter(this.builder.isTagId.bind(this.builder));
6066
+ const tagIds = Array.from(this.set).filter(
6067
+ this.builder.isTagId.bind(this.builder)
6068
+ );
6066
6069
  if (tagIds.length > 1) {
6067
6070
  return { kind: "multi", groups: Array.from(this.set) };
6068
6071
  }
@@ -6073,6 +6076,98 @@ var Selection = class {
6073
6076
  const group = this.computeGroupForTag(props, tagId);
6074
6077
  return { kind: "single", group };
6075
6078
  }
6079
+ /**
6080
+ * Build a fieldId -> triggerKeys[] map from the current selection set.
6081
+ *
6082
+ * What counts as a "button selection" (trigger key):
6083
+ * - field key where the field has button === true (e.g. "f:dripfeed")
6084
+ * - option key (e.g. "o:fast")
6085
+ * - composite key "fieldId::optionId" (e.g. "f:speed::o:fast")
6086
+ *
6087
+ * Grouping:
6088
+ * - button-field trigger groups under its own fieldId
6089
+ * - option/composite groups under the option's owning fieldId (from nodeMap)
6090
+ *
6091
+ * Deterministic:
6092
+ * - preserves selection insertion order
6093
+ * - de-dupes per field
6094
+ */
6095
+ buttonSelectionsByFieldId() {
6096
+ var _a, _b, _c, _d, _e;
6097
+ const nodeMap = this.builder.getNodeMap();
6098
+ const out = {};
6099
+ const push = (fieldId, triggerKey) => {
6100
+ var _a2;
6101
+ const arr = (_a2 = out[fieldId]) != null ? _a2 : out[fieldId] = [];
6102
+ if (!arr.includes(triggerKey)) arr.push(triggerKey);
6103
+ };
6104
+ for (const key of this.set) {
6105
+ if (!key) continue;
6106
+ const idx = key.indexOf("::");
6107
+ if (idx !== -1) {
6108
+ const optionId = key.slice(idx + 2);
6109
+ const optRef = nodeMap.get(optionId);
6110
+ if ((optRef == null ? void 0 : optRef.kind) === "option" && typeof optRef.fieldId === "string") {
6111
+ push(optRef.fieldId, key);
6112
+ }
6113
+ continue;
6114
+ }
6115
+ const ref = nodeMap.get(key);
6116
+ if (!ref) continue;
6117
+ if (ref.kind === "option" && typeof ref.fieldId === "string") {
6118
+ push(ref.fieldId, (_a = ref.id) != null ? _a : key);
6119
+ continue;
6120
+ }
6121
+ if (ref.kind === "field") {
6122
+ const field = (_c = (_b = ref.node) != null ? _b : ref.field) != null ? _c : ref;
6123
+ const fieldId = (_e = (_d = ref.id) != null ? _d : field == null ? void 0 : field.id) != null ? _e : key;
6124
+ if ((field == null ? void 0 : field.button) === true && typeof fieldId === "string") {
6125
+ push(fieldId, fieldId);
6126
+ }
6127
+ }
6128
+ }
6129
+ return out;
6130
+ }
6131
+ /**
6132
+ * Returns only selection keys that are valid "trigger buttons":
6133
+ * - field keys where field.button === true
6134
+ * - option keys
6135
+ * - composite keys "fieldId::optionId" (validated by optionId)
6136
+ * Excludes tags and non-button fields.
6137
+ */
6138
+ selectedButtons() {
6139
+ var _a, _b;
6140
+ const nodeMap = this.builder.getNodeMap();
6141
+ const out = [];
6142
+ const seen = /* @__PURE__ */ new Set();
6143
+ const push = (k) => {
6144
+ if (!seen.has(k)) {
6145
+ seen.add(k);
6146
+ out.push(k);
6147
+ }
6148
+ };
6149
+ for (const key of this.set) {
6150
+ if (!key) continue;
6151
+ const idx = key.indexOf("::");
6152
+ if (idx !== -1) {
6153
+ const optionId = key.slice(idx + 2);
6154
+ const optRef = nodeMap.get(optionId);
6155
+ if ((optRef == null ? void 0 : optRef.kind) === "option") push(key);
6156
+ continue;
6157
+ }
6158
+ const ref = nodeMap.get(key);
6159
+ if (!ref) continue;
6160
+ if (ref.kind === "option") {
6161
+ push(key);
6162
+ continue;
6163
+ }
6164
+ if (ref.kind === "field") {
6165
+ const field = (_b = (_a = ref.node) != null ? _a : ref.field) != null ? _b : ref;
6166
+ if ((field == null ? void 0 : field.button) === true) push(key);
6167
+ }
6168
+ }
6169
+ return out;
6170
+ }
6076
6171
  // ── Internals ────────────────────────────────────────────────────────────
6077
6172
  emit() {
6078
6173
  const payload = {
@@ -6146,28 +6241,13 @@ var Selection = class {
6146
6241
  }
6147
6242
  return this.opts.rootTagId;
6148
6243
  }
6149
- selectedButtonTriggerIds(props) {
6150
- var _a;
6151
- const fields = (_a = props.fields) != null ? _a : [];
6152
- const fieldById = new Map(fields.map((f) => [f.id, f]));
6153
- const out = [];
6154
- for (const selId of this.set) {
6155
- if (selId.startsWith("o:")) {
6156
- out.push(selId);
6157
- continue;
6158
- }
6159
- const f = fieldById.get(selId);
6160
- if ((f == null ? void 0 : f.button) === true) out.push(selId);
6161
- }
6162
- return out;
6163
- }
6164
6244
  computeGroupForTag(props, tagId) {
6165
6245
  var _a, _b, _c, _d, _e, _f, _g;
6166
6246
  const tags = (_a = props.filters) != null ? _a : [];
6167
6247
  const fields = (_b = props.fields) != null ? _b : [];
6168
6248
  const tagById = new Map(tags.map((t) => [t.id, t]));
6169
6249
  const tag = tagById.get(tagId);
6170
- const selectedTriggerIds = this.selectedButtonTriggerIds(props);
6250
+ const selectedTriggerIds = this.selectedButtons();
6171
6251
  const fieldIds = this.builder.visibleFields(tagId, selectedTriggerIds);
6172
6252
  const fieldById = new Map(fields.map((f) => [f.id, f]));
6173
6253
  const visible = fieldIds.map((id) => fieldById.get(id)).filter(Boolean);
@@ -6506,12 +6586,7 @@ function useFormApi() {
6506
6586
  function useOptionalFormApi() {
6507
6587
  return React.useContext(Ctx);
6508
6588
  }
6509
- function FormProvider({
6510
- children,
6511
- schema,
6512
- initial,
6513
- onUpdate
6514
- }) {
6589
+ function FormProvider({ children, schema, initial }) {
6515
6590
  const [bag, setBag] = React.useState(() => {
6516
6591
  var _a;
6517
6592
  return {
@@ -6602,7 +6677,6 @@ function FormProvider({
6602
6677
  },
6603
6678
  submit() {
6604
6679
  const core = coreRef.current;
6605
- console.log(core);
6606
6680
  if (!core) return { values: {}, valid: false };
6607
6681
  return core.submit();
6608
6682
  }
@@ -6615,12 +6689,7 @@ function FormProvider({
6615
6689
  schema,
6616
6690
  valueBag: bag,
6617
6691
  formRef: coreRef,
6618
- onUpdate: (vals) => {
6619
- const next = vals != null ? vals : {};
6620
- setBag((prev) => ({ ...prev, ...next }));
6621
- onUpdate == null ? void 0 : onUpdate({ ...bag, ...next });
6622
- publish();
6623
- },
6692
+ onChange: () => publish(),
6624
6693
  children: [
6625
6694
  /* @__PURE__ */ jsx(Bridge, {}),
6626
6695
  children
@@ -7095,7 +7164,10 @@ function useOrderFlow() {
7095
7164
  const values = (_c2 = (_b2 = (_a2 = ctx.formApi).snapshot) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : {};
7096
7165
  return values;
7097
7166
  }, [ctx.formApi, formTick]);
7098
- const optionSelectionsByFieldId = useMemo4(() => ({}), []);
7167
+ const optionSelectionsByFieldId = useMemo4(
7168
+ () => ({}),
7169
+ []
7170
+ );
7099
7171
  const previewSnapshot = useMemo4(() => {
7100
7172
  var _a2, _b2, _c2;
7101
7173
  if (!ready) {
@@ -7103,7 +7175,7 @@ function useOrderFlow() {
7103
7175
  version: "1",
7104
7176
  mode: "prod",
7105
7177
  builtAt: (/* @__PURE__ */ new Date()).toISOString(),
7106
- selection: { tag: "unknown", fields: [] },
7178
+ selection: { tag: "unknown", fields: [], buttons: [] },
7107
7179
  inputs: { form: {}, selections: {} },
7108
7180
  quantity: 1,
7109
7181
  quantitySource: { kind: "default", defaultedFromHost: true },
@@ -7125,7 +7197,7 @@ function useOrderFlow() {
7125
7197
  }
7126
7198
  };
7127
7199
  }
7128
- const { builder, init } = ctx.ensureReady("previewSnapshot");
7200
+ const { builder, init, selection } = ctx.ensureReady("previewSnapshot");
7129
7201
  const mode = (_b2 = init.mode) != null ? _b2 : "prod";
7130
7202
  const hostDefaultQuantity = Number((_c2 = init.hostDefaultQuantity) != null ? _c2 : 1) || 1;
7131
7203
  return buildOrderSnapshot(
@@ -7134,6 +7206,7 @@ function useOrderFlow() {
7134
7206
  {
7135
7207
  activeTagId: activeTagId != null ? activeTagId : ROOT_TAG_ID2,
7136
7208
  formValuesByFieldId,
7209
+ selectedKeys: selection.selectedButtons(),
7137
7210
  optionSelectionsByFieldId
7138
7211
  // Selection-owned now
7139
7212
  },
@@ -7144,7 +7217,14 @@ function useOrderFlow() {
7144
7217
  fallback: ctx.fallbackPolicy
7145
7218
  }
7146
7219
  );
7147
- }, [ready, ctx, activeTagId, formValuesByFieldId, optionSelectionsByFieldId, selTick]);
7220
+ }, [
7221
+ ready,
7222
+ ctx,
7223
+ activeTagId,
7224
+ formValuesByFieldId,
7225
+ optionSelectionsByFieldId,
7226
+ selTick
7227
+ ]);
7148
7228
  const pricingPreview = useMemo4(() => {
7149
7229
  var _a2, _b2, _c2, _d, _e, _f;
7150
7230
  const empty = {
@@ -7261,17 +7341,21 @@ function useOrderFlow() {
7261
7341
  var _a2, _b2;
7262
7342
  const { builder, selection, init } = ctx.ensureReady("buildSnapshot");
7263
7343
  const tagId = selection.currentTag();
7264
- if (!tagId) throw new Error("OrderFlow: no active tag/context selected");
7344
+ const selectedKeys = selection.selectedButtons();
7345
+ if (!tagId)
7346
+ throw new Error("OrderFlow: no active tag/context selected");
7265
7347
  const mode = (_a2 = init.mode) != null ? _a2 : "prod";
7266
7348
  const hostDefaultQuantity = Number((_b2 = init.hostDefaultQuantity) != null ? _b2 : 1) || 1;
7267
7349
  const submitted = ctx.formApi.submit();
7268
7350
  const values = submitted.values;
7351
+ if (!submitted.valid) return;
7269
7352
  return buildOrderSnapshot(
7270
7353
  builder.getProps(),
7271
7354
  builder,
7272
7355
  {
7273
7356
  activeTagId: tagId,
7274
7357
  formValuesByFieldId: values,
7358
+ selectedKeys,
7275
7359
  optionSelectionsByFieldId
7276
7360
  // Selection-owned
7277
7361
  },
@@ -7391,9 +7475,10 @@ function Wrapper({
7391
7475
  disabled,
7392
7476
  extraProps,
7393
7477
  templateStrings = true,
7394
- ctxOverrides
7478
+ ctxOverrides,
7479
+ className = ""
7395
7480
  }) {
7396
- var _a, _b, _c, _d, _e, _f, _g, _h;
7481
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
7397
7482
  const { registry } = useInputs();
7398
7483
  const flow = useOrderFlow();
7399
7484
  const kind = toKind(field);
@@ -7415,13 +7500,14 @@ function Wrapper({
7415
7500
  }, [baseProps, field.defaults]);
7416
7501
  const valueProp = (_c = adapter.valueProp) != null ? _c : "value";
7417
7502
  const changeProp = (_d = adapter.changeProp) != null ? _d : "onChange";
7503
+ const errorProp = (_e = adapter.errorProp) != null ? _e : "errorText";
7418
7504
  const isOptionBased2 = Array.isArray(field.options) && field.options.length > 0;
7419
7505
  const isActionButton = field.button === true && !isOptionBased2;
7420
7506
  const fp = useField({
7421
7507
  name: field.id,
7422
7508
  required: !!field.required,
7423
7509
  variant: field.type,
7424
- defaultValue: (_e = field.defaults) == null ? void 0 : _e.value,
7510
+ defaultValue: (_f = field.defaults) == null ? void 0 : _f.value,
7425
7511
  disabled: !!disabled
7426
7512
  });
7427
7513
  const optionIds = React4.useMemo(() => {
@@ -7510,11 +7596,12 @@ function Wrapper({
7510
7596
  return extraProps != null ? extraProps : {};
7511
7597
  return templateDeep(extraProps != null ? extraProps : {}, templateCtx);
7512
7598
  }, [extraProps, templateCtx, templateStrings]);
7513
- const fieldProps = (_g = (_f = adapter == null ? void 0 : adapter.getInputPropsFromField) == null ? void 0 : _f.call(adapter, { field, props: flow.raw })) != null ? _g : {};
7599
+ const fieldProps = (_h = (_g = adapter == null ? void 0 : adapter.getInputPropsFromField) == null ? void 0 : _g.call(adapter, { field, props: flow.raw })) != null ? _h : {};
7514
7600
  const hostProps = {
7515
7601
  id: field.id,
7516
7602
  field,
7517
7603
  disabled: !!disabled || !!fp.disabled,
7604
+ required: field.required,
7518
7605
  // DO NOT pass `name` to InputField/entries
7519
7606
  fieldKey: field.id,
7520
7607
  ...fieldProps != null ? fieldProps : {},
@@ -7523,9 +7610,10 @@ function Wrapper({
7523
7610
  ...templatedDefaultProps != null ? templatedDefaultProps : {},
7524
7611
  ...templatedExtraProps != null ? templatedExtraProps : {}
7525
7612
  };
7526
- hostProps[valueProp] = (_h = fp.value) != null ? _h : null;
7613
+ hostProps[valueProp] = (_i = fp.value) != null ? _i : null;
7527
7614
  hostProps[changeProp] = onHostChange;
7528
- return /* @__PURE__ */ jsx4(Component, { ...hostProps });
7615
+ hostProps[errorProp] = fp.error;
7616
+ return /* @__PURE__ */ jsx4("div", { ref: fp.ref, className, children: /* @__PURE__ */ jsx4(Component, { ...hostProps }) });
7529
7617
  }
7530
7618
 
7531
7619
  // src/react/inputs/entries/text.tsx
@@ -9654,6 +9742,7 @@ function withInputFieldUi(desc) {
9654
9742
  return {
9655
9743
  label: field.label,
9656
9744
  tags: fieldNotices.map(toTagPill),
9745
+ required: field.required,
9657
9746
  ...((_b = field.options) == null ? void 0 : _b.length) ? {
9658
9747
  options: field.options.map((item) => {
9659
9748
  const optionNotices = notices.filter(