@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.
@@ -1222,13 +1222,10 @@ function visibleFieldIdsUnder(props, tagId, opts = {}) {
1222
1222
  if (order && order.length) {
1223
1223
  const ordered = order.filter((fid) => visible.has(fid));
1224
1224
  const orderedSet = new Set(ordered);
1225
- const rest2 = base.filter((fid) => !orderedSet.has(fid));
1226
- return [...ordered, ...rest2];
1225
+ const rest = base.filter((fid) => !orderedSet.has(fid));
1226
+ return [...ordered, ...rest];
1227
1227
  }
1228
- const promoted = revealedOrder.filter((fid) => visible.has(fid));
1229
- const promotedSet = new Set(promoted);
1230
- const rest = base.filter((fid) => !promotedSet.has(fid));
1231
- return [...promoted, ...rest];
1228
+ return base;
1232
1229
  }
1233
1230
  function visibleFieldsUnder(props, tagId, opts = {}) {
1234
1231
  var _a;
@@ -3302,7 +3299,7 @@ function rateOk(svcMap, candidate, primary, policy) {
3302
3299
 
3303
3300
  // src/utils/build-order-snapshot.ts
3304
3301
  function buildOrderSnapshot(props, builder, selection, services, settings = {}) {
3305
- var _a, _b, _c, _d, _e, _f, _g;
3302
+ var _a, _b, _c, _d, _e, _f, _g, _h;
3306
3303
  const mode = (_a = settings.mode) != null ? _a : "prod";
3307
3304
  const hostDefaultQty = Number.isFinite(
3308
3305
  (_b = settings.hostDefaultQuantity) != null ? _b : 1
@@ -3316,20 +3313,18 @@ function buildOrderSnapshot(props, builder, selection, services, settings = {})
3316
3313
  };
3317
3314
  const builtAt = (/* @__PURE__ */ new Date()).toISOString();
3318
3315
  const tagId = selection.activeTagId;
3319
- const selectedOptionKeys = toSelectedOptionKeys(
3320
- selection.optionSelectionsByFieldId
3321
- );
3316
+ const selectedButtonKeys = (_d = selection.selectedKeys) != null ? _d : toSelectedOptionKeys(selection.optionSelectionsByFieldId);
3322
3317
  const visibleFieldIds = builder.visibleFields(
3323
3318
  tagId,
3324
- selectedOptionKeys
3319
+ selectedButtonKeys
3325
3320
  );
3326
3321
  const tagById = new Map(
3327
- ((_d = props.filters) != null ? _d : []).map((t) => [t.id, t])
3322
+ ((_e = props.filters) != null ? _e : []).map((t) => [t.id, t])
3328
3323
  );
3329
3324
  const fieldById = new Map(
3330
- ((_e = props.fields) != null ? _e : []).map((f) => [f.id, f])
3325
+ ((_f = props.fields) != null ? _f : []).map((f) => [f.id, f])
3331
3326
  );
3332
- const tagConstraints = (_g = (_f = tagById.get(tagId)) == null ? void 0 : _f.constraints) != null ? _g : void 0;
3327
+ const tagConstraints = (_h = (_g = tagById.get(tagId)) == null ? void 0 : _g.constraints) != null ? _h : void 0;
3333
3328
  const selectionFields = visibleFieldIds.map((fid) => fieldById.get(fid)).filter((f) => !!f).map((f) => {
3334
3329
  var _a2;
3335
3330
  const optIds = isOptionBased(f) ? (_a2 = selection.optionSelectionsByFieldId[f.id]) != null ? _a2 : [] : void 0;
@@ -3406,6 +3401,7 @@ function buildOrderSnapshot(props, builder, selection, services, settings = {})
3406
3401
  builtAt,
3407
3402
  selection: {
3408
3403
  tag: tagId,
3404
+ buttons: selectedButtonKeys,
3409
3405
  fields: selectionFields
3410
3406
  },
3411
3407
  inputs: {
@@ -3438,6 +3434,10 @@ function toSelectedOptionKeys(byField) {
3438
3434
  }
3439
3435
  return keys;
3440
3436
  }
3437
+ function isServicedBased(field) {
3438
+ if (field.service_id) return true;
3439
+ return !!(field.options && field.options.some((item) => item.service_id));
3440
+ }
3441
3441
  function buildInputs(visibleFieldIds, fieldById, selection) {
3442
3442
  const formValues = {};
3443
3443
  const selections = {};
@@ -3448,7 +3448,7 @@ function buildInputs(visibleFieldIds, fieldById, selection) {
3448
3448
  if (selOptIds && selOptIds.length) {
3449
3449
  selections[fid] = [...selOptIds];
3450
3450
  }
3451
- if (!isOptionBased(f)) {
3451
+ if (!isServicedBased(f)) {
3452
3452
  const name = f.name;
3453
3453
  const val = selection.formValuesByFieldId[fid];
3454
3454
  if (!name || val === void 0) continue;
@@ -6057,6 +6057,7 @@ var Selection = class {
6057
6057
  this.emit();
6058
6058
  }
6059
6059
  add(id) {
6060
+ if (this.set.has(id)) this.set.delete(id);
6060
6061
  this.set.add(id);
6061
6062
  this.primaryId = id;
6062
6063
  this.updateCurrentTagFrom(id);
@@ -6111,7 +6112,9 @@ var Selection = class {
6111
6112
  var _a;
6112
6113
  const props = this.builder.getProps();
6113
6114
  if (((_a = this.opts.env) != null ? _a : "client") === "workspace") {
6114
- const tagIds = Array.from(this.set).filter(this.builder.isTagId.bind(this.builder));
6115
+ const tagIds = Array.from(this.set).filter(
6116
+ this.builder.isTagId.bind(this.builder)
6117
+ );
6115
6118
  if (tagIds.length > 1) {
6116
6119
  return { kind: "multi", groups: Array.from(this.set) };
6117
6120
  }
@@ -6122,6 +6125,98 @@ var Selection = class {
6122
6125
  const group = this.computeGroupForTag(props, tagId);
6123
6126
  return { kind: "single", group };
6124
6127
  }
6128
+ /**
6129
+ * Build a fieldId -> triggerKeys[] map from the current selection set.
6130
+ *
6131
+ * What counts as a "button selection" (trigger key):
6132
+ * - field key where the field has button === true (e.g. "f:dripfeed")
6133
+ * - option key (e.g. "o:fast")
6134
+ * - composite key "fieldId::optionId" (e.g. "f:speed::o:fast")
6135
+ *
6136
+ * Grouping:
6137
+ * - button-field trigger groups under its own fieldId
6138
+ * - option/composite groups under the option's owning fieldId (from nodeMap)
6139
+ *
6140
+ * Deterministic:
6141
+ * - preserves selection insertion order
6142
+ * - de-dupes per field
6143
+ */
6144
+ buttonSelectionsByFieldId() {
6145
+ var _a, _b, _c, _d, _e;
6146
+ const nodeMap = this.builder.getNodeMap();
6147
+ const out = {};
6148
+ const push = (fieldId, triggerKey) => {
6149
+ var _a2;
6150
+ const arr = (_a2 = out[fieldId]) != null ? _a2 : out[fieldId] = [];
6151
+ if (!arr.includes(triggerKey)) arr.push(triggerKey);
6152
+ };
6153
+ for (const key of this.set) {
6154
+ if (!key) continue;
6155
+ const idx = key.indexOf("::");
6156
+ if (idx !== -1) {
6157
+ const optionId = key.slice(idx + 2);
6158
+ const optRef = nodeMap.get(optionId);
6159
+ if ((optRef == null ? void 0 : optRef.kind) === "option" && typeof optRef.fieldId === "string") {
6160
+ push(optRef.fieldId, key);
6161
+ }
6162
+ continue;
6163
+ }
6164
+ const ref = nodeMap.get(key);
6165
+ if (!ref) continue;
6166
+ if (ref.kind === "option" && typeof ref.fieldId === "string") {
6167
+ push(ref.fieldId, (_a = ref.id) != null ? _a : key);
6168
+ continue;
6169
+ }
6170
+ if (ref.kind === "field") {
6171
+ const field = (_c = (_b = ref.node) != null ? _b : ref.field) != null ? _c : ref;
6172
+ const fieldId = (_e = (_d = ref.id) != null ? _d : field == null ? void 0 : field.id) != null ? _e : key;
6173
+ if ((field == null ? void 0 : field.button) === true && typeof fieldId === "string") {
6174
+ push(fieldId, fieldId);
6175
+ }
6176
+ }
6177
+ }
6178
+ return out;
6179
+ }
6180
+ /**
6181
+ * Returns only selection keys that are valid "trigger buttons":
6182
+ * - field keys where field.button === true
6183
+ * - option keys
6184
+ * - composite keys "fieldId::optionId" (validated by optionId)
6185
+ * Excludes tags and non-button fields.
6186
+ */
6187
+ selectedButtons() {
6188
+ var _a, _b;
6189
+ const nodeMap = this.builder.getNodeMap();
6190
+ const out = [];
6191
+ const seen = /* @__PURE__ */ new Set();
6192
+ const push = (k) => {
6193
+ if (!seen.has(k)) {
6194
+ seen.add(k);
6195
+ out.push(k);
6196
+ }
6197
+ };
6198
+ for (const key of this.set) {
6199
+ if (!key) continue;
6200
+ const idx = key.indexOf("::");
6201
+ if (idx !== -1) {
6202
+ const optionId = key.slice(idx + 2);
6203
+ const optRef = nodeMap.get(optionId);
6204
+ if ((optRef == null ? void 0 : optRef.kind) === "option") push(key);
6205
+ continue;
6206
+ }
6207
+ const ref = nodeMap.get(key);
6208
+ if (!ref) continue;
6209
+ if (ref.kind === "option") {
6210
+ push(key);
6211
+ continue;
6212
+ }
6213
+ if (ref.kind === "field") {
6214
+ const field = (_b = (_a = ref.node) != null ? _a : ref.field) != null ? _b : ref;
6215
+ if ((field == null ? void 0 : field.button) === true) push(key);
6216
+ }
6217
+ }
6218
+ return out;
6219
+ }
6125
6220
  // ── Internals ────────────────────────────────────────────────────────────
6126
6221
  emit() {
6127
6222
  const payload = {
@@ -6195,28 +6290,13 @@ var Selection = class {
6195
6290
  }
6196
6291
  return this.opts.rootTagId;
6197
6292
  }
6198
- selectedButtonTriggerIds(props) {
6199
- var _a;
6200
- const fields = (_a = props.fields) != null ? _a : [];
6201
- const fieldById = new Map(fields.map((f) => [f.id, f]));
6202
- const out = [];
6203
- for (const selId of this.set) {
6204
- if (selId.startsWith("o:")) {
6205
- out.push(selId);
6206
- continue;
6207
- }
6208
- const f = fieldById.get(selId);
6209
- if ((f == null ? void 0 : f.button) === true) out.push(selId);
6210
- }
6211
- return out;
6212
- }
6213
6293
  computeGroupForTag(props, tagId) {
6214
6294
  var _a, _b, _c, _d, _e, _f, _g;
6215
6295
  const tags = (_a = props.filters) != null ? _a : [];
6216
6296
  const fields = (_b = props.fields) != null ? _b : [];
6217
6297
  const tagById = new Map(tags.map((t) => [t.id, t]));
6218
6298
  const tag = tagById.get(tagId);
6219
- const selectedTriggerIds = this.selectedButtonTriggerIds(props);
6299
+ const selectedTriggerIds = this.selectedButtons();
6220
6300
  const fieldIds = this.builder.visibleFields(tagId, selectedTriggerIds);
6221
6301
  const fieldById = new Map(fields.map((f) => [f.id, f]));
6222
6302
  const visible = fieldIds.map((id) => fieldById.get(id)).filter(Boolean);
@@ -6555,12 +6635,7 @@ function useFormApi() {
6555
6635
  function useOptionalFormApi() {
6556
6636
  return React.useContext(Ctx);
6557
6637
  }
6558
- function FormProvider({
6559
- children,
6560
- schema,
6561
- initial,
6562
- onUpdate
6563
- }) {
6638
+ function FormProvider({ children, schema, initial }) {
6564
6639
  const [bag, setBag] = React.useState(() => {
6565
6640
  var _a;
6566
6641
  return {
@@ -6651,7 +6726,6 @@ function FormProvider({
6651
6726
  },
6652
6727
  submit() {
6653
6728
  const core = coreRef.current;
6654
- console.log(core);
6655
6729
  if (!core) return { values: {}, valid: false };
6656
6730
  return core.submit();
6657
6731
  }
@@ -6664,12 +6738,7 @@ function FormProvider({
6664
6738
  schema,
6665
6739
  valueBag: bag,
6666
6740
  formRef: coreRef,
6667
- onUpdate: (vals) => {
6668
- const next = vals != null ? vals : {};
6669
- setBag((prev) => ({ ...prev, ...next }));
6670
- onUpdate == null ? void 0 : onUpdate({ ...bag, ...next });
6671
- publish();
6672
- },
6741
+ onChange: () => publish(),
6673
6742
  children: [
6674
6743
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bridge, {}),
6675
6744
  children
@@ -7134,7 +7203,10 @@ function useOrderFlow() {
7134
7203
  const values = (_c2 = (_b2 = (_a2 = ctx.formApi).snapshot) == null ? void 0 : _b2.call(_a2)) != null ? _c2 : {};
7135
7204
  return values;
7136
7205
  }, [ctx.formApi, formTick]);
7137
- const optionSelectionsByFieldId = (0, import_react6.useMemo)(() => ({}), []);
7206
+ const optionSelectionsByFieldId = (0, import_react6.useMemo)(
7207
+ () => ({}),
7208
+ []
7209
+ );
7138
7210
  const previewSnapshot = (0, import_react6.useMemo)(() => {
7139
7211
  var _a2, _b2, _c2;
7140
7212
  if (!ready) {
@@ -7142,7 +7214,7 @@ function useOrderFlow() {
7142
7214
  version: "1",
7143
7215
  mode: "prod",
7144
7216
  builtAt: (/* @__PURE__ */ new Date()).toISOString(),
7145
- selection: { tag: "unknown", fields: [] },
7217
+ selection: { tag: "unknown", fields: [], buttons: [] },
7146
7218
  inputs: { form: {}, selections: {} },
7147
7219
  quantity: 1,
7148
7220
  quantitySource: { kind: "default", defaultedFromHost: true },
@@ -7164,7 +7236,7 @@ function useOrderFlow() {
7164
7236
  }
7165
7237
  };
7166
7238
  }
7167
- const { builder, init } = ctx.ensureReady("previewSnapshot");
7239
+ const { builder, init, selection } = ctx.ensureReady("previewSnapshot");
7168
7240
  const mode = (_b2 = init.mode) != null ? _b2 : "prod";
7169
7241
  const hostDefaultQuantity = Number((_c2 = init.hostDefaultQuantity) != null ? _c2 : 1) || 1;
7170
7242
  return buildOrderSnapshot(
@@ -7173,6 +7245,7 @@ function useOrderFlow() {
7173
7245
  {
7174
7246
  activeTagId: activeTagId != null ? activeTagId : ROOT_TAG_ID2,
7175
7247
  formValuesByFieldId,
7248
+ selectedKeys: selection.selectedButtons(),
7176
7249
  optionSelectionsByFieldId
7177
7250
  // Selection-owned now
7178
7251
  },
@@ -7183,7 +7256,14 @@ function useOrderFlow() {
7183
7256
  fallback: ctx.fallbackPolicy
7184
7257
  }
7185
7258
  );
7186
- }, [ready, ctx, activeTagId, formValuesByFieldId, optionSelectionsByFieldId, selTick]);
7259
+ }, [
7260
+ ready,
7261
+ ctx,
7262
+ activeTagId,
7263
+ formValuesByFieldId,
7264
+ optionSelectionsByFieldId,
7265
+ selTick
7266
+ ]);
7187
7267
  const pricingPreview = (0, import_react6.useMemo)(() => {
7188
7268
  var _a2, _b2, _c2, _d, _e, _f;
7189
7269
  const empty = {
@@ -7300,17 +7380,21 @@ function useOrderFlow() {
7300
7380
  var _a2, _b2;
7301
7381
  const { builder, selection, init } = ctx.ensureReady("buildSnapshot");
7302
7382
  const tagId = selection.currentTag();
7303
- if (!tagId) throw new Error("OrderFlow: no active tag/context selected");
7383
+ const selectedKeys = selection.selectedButtons();
7384
+ if (!tagId)
7385
+ throw new Error("OrderFlow: no active tag/context selected");
7304
7386
  const mode = (_a2 = init.mode) != null ? _a2 : "prod";
7305
7387
  const hostDefaultQuantity = Number((_b2 = init.hostDefaultQuantity) != null ? _b2 : 1) || 1;
7306
7388
  const submitted = ctx.formApi.submit();
7307
7389
  const values = submitted.values;
7390
+ if (!submitted.valid) return;
7308
7391
  return buildOrderSnapshot(
7309
7392
  builder.getProps(),
7310
7393
  builder,
7311
7394
  {
7312
7395
  activeTagId: tagId,
7313
7396
  formValuesByFieldId: values,
7397
+ selectedKeys,
7314
7398
  optionSelectionsByFieldId
7315
7399
  // Selection-owned
7316
7400
  },
@@ -7430,9 +7514,10 @@ function Wrapper({
7430
7514
  disabled,
7431
7515
  extraProps,
7432
7516
  templateStrings = true,
7433
- ctxOverrides
7517
+ ctxOverrides,
7518
+ className = ""
7434
7519
  }) {
7435
- var _a, _b, _c, _d, _e, _f, _g, _h;
7520
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
7436
7521
  const { registry } = useInputs();
7437
7522
  const flow = useOrderFlow();
7438
7523
  const kind = toKind(field);
@@ -7454,13 +7539,14 @@ function Wrapper({
7454
7539
  }, [baseProps, field.defaults]);
7455
7540
  const valueProp = (_c = adapter.valueProp) != null ? _c : "value";
7456
7541
  const changeProp = (_d = adapter.changeProp) != null ? _d : "onChange";
7542
+ const errorProp = (_e = adapter.errorProp) != null ? _e : "errorText";
7457
7543
  const isOptionBased2 = Array.isArray(field.options) && field.options.length > 0;
7458
7544
  const isActionButton = field.button === true && !isOptionBased2;
7459
7545
  const fp = (0, import_form_palette2.useField)({
7460
7546
  name: field.id,
7461
7547
  required: !!field.required,
7462
7548
  variant: field.type,
7463
- defaultValue: (_e = field.defaults) == null ? void 0 : _e.value,
7549
+ defaultValue: (_f = field.defaults) == null ? void 0 : _f.value,
7464
7550
  disabled: !!disabled
7465
7551
  });
7466
7552
  const optionIds = React4.useMemo(() => {
@@ -7549,11 +7635,12 @@ function Wrapper({
7549
7635
  return extraProps != null ? extraProps : {};
7550
7636
  return templateDeep(extraProps != null ? extraProps : {}, templateCtx);
7551
7637
  }, [extraProps, templateCtx, templateStrings]);
7552
- const fieldProps = (_g = (_f = adapter == null ? void 0 : adapter.getInputPropsFromField) == null ? void 0 : _f.call(adapter, { field, props: flow.raw })) != null ? _g : {};
7638
+ const fieldProps = (_h = (_g = adapter == null ? void 0 : adapter.getInputPropsFromField) == null ? void 0 : _g.call(adapter, { field, props: flow.raw })) != null ? _h : {};
7553
7639
  const hostProps = {
7554
7640
  id: field.id,
7555
7641
  field,
7556
7642
  disabled: !!disabled || !!fp.disabled,
7643
+ required: field.required,
7557
7644
  // DO NOT pass `name` to InputField/entries
7558
7645
  fieldKey: field.id,
7559
7646
  ...fieldProps != null ? fieldProps : {},
@@ -7562,9 +7649,10 @@ function Wrapper({
7562
7649
  ...templatedDefaultProps != null ? templatedDefaultProps : {},
7563
7650
  ...templatedExtraProps != null ? templatedExtraProps : {}
7564
7651
  };
7565
- hostProps[valueProp] = (_h = fp.value) != null ? _h : null;
7652
+ hostProps[valueProp] = (_i = fp.value) != null ? _i : null;
7566
7653
  hostProps[changeProp] = onHostChange;
7567
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Component, { ...hostProps });
7654
+ hostProps[errorProp] = fp.error;
7655
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: fp.ref, className, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Component, { ...hostProps }) });
7568
7656
  }
7569
7657
 
7570
7658
  // src/react/inputs/entries/text.tsx
@@ -9693,6 +9781,7 @@ function withInputFieldUi(desc) {
9693
9781
  return {
9694
9782
  label: field.label,
9695
9783
  tags: fieldNotices.map(toTagPill),
9784
+ required: field.required,
9696
9785
  ...((_b = field.options) == null ? void 0 : _b.length) ? {
9697
9786
  options: field.options.map((item) => {
9698
9787
  const optionNotices = notices.filter(