@timeax/digital-service-engine 0.2.5 → 0.2.7

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.
@@ -4701,10 +4701,19 @@ function validateOrderKinds(v) {
4701
4701
  }
4702
4702
 
4703
4703
  // src/core/validate/steps/service-vs-input.ts
4704
+ function hasButtonTriggerMap(v, fieldId) {
4705
+ var _a, _b;
4706
+ const includes = (_a = v.props.includes_for_buttons) == null ? void 0 : _a[fieldId];
4707
+ const excludes = (_b = v.props.excludes_for_buttons) == null ? void 0 : _b[fieldId];
4708
+ return Array.isArray(includes) && includes.length > 0 || Array.isArray(excludes) && excludes.length > 0;
4709
+ }
4704
4710
  function validateServiceVsUserInput(v) {
4705
4711
  for (const f of v.fields) {
4706
4712
  const anySvc = hasAnyServiceOption(f);
4707
4713
  const hasName = !!(f.name && f.name.trim());
4714
+ const isButton2 = f.button === true;
4715
+ const hasFieldService = f.service_id !== void 0 && f.service_id !== null;
4716
+ const hasTriggerMap = isButton2 && hasButtonTriggerMap(v, f.id);
4708
4717
  if (f.type === "custom" && anySvc) {
4709
4718
  v.errors.push({
4710
4719
  code: "user_input_field_has_service_option",
@@ -4715,14 +4724,15 @@ function validateServiceVsUserInput(v) {
4715
4724
  });
4716
4725
  }
4717
4726
  if (!hasName) {
4718
- if (!anySvc) {
4719
- v.errors.push({
4720
- code: "service_field_missing_service_id",
4721
- severity: "error",
4722
- message: `Service-backed field "${f.id}" has no "name" and must provide at least one option with a service_id.`,
4723
- nodeId: f.id
4724
- });
4727
+ if (hasFieldService || anySvc || hasTriggerMap) {
4728
+ continue;
4725
4729
  }
4730
+ v.errors.push({
4731
+ code: "service_field_missing_service_id",
4732
+ severity: "error",
4733
+ message: isButton2 ? `Button field "${f.id}" has no "name", no "service_id", and no includes/excludes trigger map. Add a name, attach a service_id, or configure includes_for_buttons/excludes_for_buttons.` : `Service-backed field "${f.id}" has no "name" and must provide at least one option with a service_id.`,
4734
+ nodeId: f.id
4735
+ });
4726
4736
  } else {
4727
4737
  if (anySvc) {
4728
4738
  v.errors.push({
@@ -6654,7 +6664,7 @@ function createNodeIndex(builder) {
6654
6664
  for (const fieldId of visible) {
6655
6665
  const node = getField(fieldId);
6656
6666
  if (!node) continue;
6657
- const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, tagId);
6667
+ const explicit = includes.has(fieldId);
6658
6668
  results.push(explicit ? node : { ...node, isInherited: true });
6659
6669
  }
6660
6670
  return Object.freeze(results);
@@ -8408,7 +8418,9 @@ function include(ctx, receiverId, idOrIds) {
8408
8418
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
8409
8419
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
8410
8420
  if (receiver.kind === "tag") {
8411
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
8421
+ const t = ((_a = p.filters) != null ? _a : []).find(
8422
+ (x) => x.id === receiverId
8423
+ );
8412
8424
  if (t) {
8413
8425
  const accepted = [];
8414
8426
  const next = new Set((_b = t.includes) != null ? _b : []);
@@ -8450,7 +8462,12 @@ function include(ctx, receiverId, idOrIds) {
8450
8462
  const current = (_f = (_e = p.includes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
8451
8463
  const next = new Set(current);
8452
8464
  for (const id of ids) {
8453
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
8465
+ if (wouldCreateIncludeExcludeCycle(
8466
+ ctx,
8467
+ p,
8468
+ receiverId,
8469
+ id
8470
+ )) {
8454
8471
  ctx.emit("editor:error", {
8455
8472
  message: `Cycle detected: ${receiverId} including ${id} would create a cycle.`,
8456
8473
  code: "cycle_detected",
@@ -8466,7 +8483,8 @@ function include(ctx, receiverId, idOrIds) {
8466
8483
  accepted.push(id);
8467
8484
  }
8468
8485
  if (accepted.length > 0 || current.length > 0) {
8469
- if (!p.includes_for_buttons) p.includes_for_buttons = {};
8486
+ if (!p.includes_for_buttons)
8487
+ p.includes_for_buttons = {};
8470
8488
  p.includes_for_buttons[receiverId] = Array.from(next);
8471
8489
  }
8472
8490
  if ((_g = p.excludes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -8481,7 +8499,9 @@ function include(ctx, receiverId, idOrIds) {
8481
8499
  if (!p.fields) p.fields = [];
8482
8500
  if (!p.filters) p.filters = [];
8483
8501
  } else {
8484
- throw new Error("Receiver must be a tag, button field, or option");
8502
+ throw new Error(
8503
+ "Receiver must be a tag, button field, or option"
8504
+ );
8485
8505
  }
8486
8506
  }),
8487
8507
  undo: () => ctx.undo()
@@ -8496,7 +8516,9 @@ function exclude(ctx, receiverId, idOrIds) {
8496
8516
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
8497
8517
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
8498
8518
  if (receiver.kind === "tag") {
8499
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
8519
+ const t = ((_a = p.filters) != null ? _a : []).find(
8520
+ (x) => x.id === receiverId
8521
+ );
8500
8522
  if (t) {
8501
8523
  const accepted = [];
8502
8524
  const next = new Set((_b = t.excludes) != null ? _b : []);
@@ -8538,7 +8560,12 @@ function exclude(ctx, receiverId, idOrIds) {
8538
8560
  const current = (_f = (_e = p.excludes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
8539
8561
  const next = new Set(current);
8540
8562
  for (const id of ids) {
8541
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
8563
+ if (wouldCreateIncludeExcludeCycle(
8564
+ ctx,
8565
+ p,
8566
+ receiverId,
8567
+ id
8568
+ )) {
8542
8569
  ctx.emit("editor:error", {
8543
8570
  message: `Cycle detected: ${receiverId} excluding ${id} would create a cycle.`,
8544
8571
  code: "cycle_detected",
@@ -8554,7 +8581,8 @@ function exclude(ctx, receiverId, idOrIds) {
8554
8581
  accepted.push(id);
8555
8582
  }
8556
8583
  if (accepted.length > 0 || current.length > 0) {
8557
- if (!p.excludes_for_buttons) p.excludes_for_buttons = {};
8584
+ if (!p.excludes_for_buttons)
8585
+ p.excludes_for_buttons = {};
8558
8586
  p.excludes_for_buttons[receiverId] = Array.from(next);
8559
8587
  }
8560
8588
  if ((_g = p.includes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -8569,7 +8597,9 @@ function exclude(ctx, receiverId, idOrIds) {
8569
8597
  if (!p.fields) p.fields = [];
8570
8598
  if (!p.filters) p.filters = [];
8571
8599
  } else {
8572
- throw new Error("Receiver must be a tag, button field, or option");
8600
+ throw new Error(
8601
+ "Receiver must be a tag, button field, or option"
8602
+ );
8573
8603
  }
8574
8604
  }),
8575
8605
  undo: () => ctx.undo()
@@ -8579,87 +8609,98 @@ function connect(ctx, kind, fromId, toId2) {
8579
8609
  ctx.exec({
8580
8610
  name: `connect:${kind}`,
8581
8611
  do: () => ctx.patchProps((p) => {
8582
- var _a, _b, _c, _d, _e;
8612
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8583
8613
  if (kind === "bind") {
8584
8614
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
8585
8615
  if (wouldCreateTagCycle(ctx, p, fromId, toId2)) {
8586
- throw new Error(`bind would create a cycle: ${fromId} ? ${toId2}`);
8616
+ throw new Error(
8617
+ `bind would create a cycle: ${fromId} ? ${toId2}`
8618
+ );
8587
8619
  }
8588
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
8620
+ const child = ((_a = p.filters) != null ? _a : []).find(
8621
+ (t) => t.id === toId2
8622
+ );
8589
8623
  if (child) child.bind_id = fromId;
8590
8624
  return;
8591
8625
  }
8592
8626
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
8593
8627
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
8594
8628
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
8595
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
8629
+ const f = ((_b = p.fields) != null ? _b : []).find(
8630
+ (x) => x.id === fieldId
8631
+ );
8596
8632
  if (!f) return;
8597
8633
  if (!f.bind_id) {
8598
8634
  f.bind_id = tagId;
8599
8635
  return;
8600
8636
  }
8601
8637
  if (typeof f.bind_id === "string") {
8602
- if (f.bind_id !== tagId) f.bind_id = [f.bind_id, tagId];
8638
+ if (f.bind_id !== tagId) {
8639
+ f.bind_id = [f.bind_id, tagId];
8640
+ }
8603
8641
  return;
8604
8642
  }
8605
- if (!f.bind_id.includes(tagId)) f.bind_id.push(tagId);
8643
+ if (!f.bind_id.includes(tagId)) {
8644
+ f.bind_id.push(tagId);
8645
+ }
8606
8646
  return;
8607
8647
  }
8608
- throw new Error(`bind: unsupported route ${fromId} ? ${toId2}`);
8648
+ throw new Error(
8649
+ `bind: unsupported route ${fromId} ? ${toId2}`
8650
+ );
8609
8651
  }
8610
8652
  if (kind === "include" || kind === "exclude") {
8611
- const key = kind === "include" ? "includes" : "excludes";
8653
+ const tagKey = kind === "include" ? "includes" : "excludes";
8654
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
8612
8655
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
8613
- const t = ((_c = p.filters) != null ? _c : []).find((x) => x.id === fromId);
8656
+ const t = ((_c = p.filters) != null ? _c : []).find(
8657
+ (x) => x.id === fromId
8658
+ );
8614
8659
  if (!t) return;
8615
- const arr = (_d = t[key]) != null ? _d : t[key] = [];
8660
+ const arr = (_d = t[tagKey]) != null ? _d : t[tagKey] = [];
8616
8661
  if (!arr.includes(toId2)) arr.push(toId2);
8617
8662
  return;
8618
8663
  }
8664
+ if (ctx.isFieldId(fromId) && ctx.isFieldId(toId2)) {
8665
+ const source = ((_e = p.fields) != null ? _e : []).find(
8666
+ (x) => x.id === fromId
8667
+ );
8668
+ if (!(source == null ? void 0 : source.button)) {
8669
+ throw new Error(
8670
+ `${kind}: source field must be button=true: ${fromId} ? ${toId2}`
8671
+ );
8672
+ }
8673
+ addMappedField(p, mapKey, fromId, toId2);
8674
+ return;
8675
+ }
8619
8676
  if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
8620
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
8621
- const maps = p[mapKey];
8622
- const next = { ...maps != null ? maps : {} };
8623
- const arr = (_e = next[fromId]) != null ? _e : [];
8624
- if (!arr.includes(toId2)) arr.push(toId2);
8625
- next[fromId] = arr;
8626
- p[mapKey] = next;
8677
+ addMappedField(p, mapKey, fromId, toId2);
8627
8678
  return;
8628
8679
  }
8629
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
8680
+ throw new Error(
8681
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
8682
+ );
8630
8683
  }
8631
8684
  if (kind === "service") {
8632
8685
  ensureServiceExists(ctx.opts, fromId);
8633
8686
  if (toId2.startsWith("t:")) {
8634
- ctx.exec({
8635
- name: "connect:service?tag",
8636
- do: () => ctx.patchProps((next) => {
8637
- var _a2;
8638
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
8639
- if (t) t.service_id = fromId;
8640
- }),
8641
- undo: () => ctx.undo()
8642
- });
8687
+ const t = ((_f = p.filters) != null ? _f : []).find((x) => x.id === toId2);
8688
+ if (t) t.service_id = fromId;
8643
8689
  return;
8644
8690
  }
8645
8691
  if (toId2.startsWith("o:")) {
8646
- ctx.exec({
8647
- name: "connect:service?option",
8648
- do: () => ctx.patchProps((next) => {
8649
- var _a2, _b2;
8650
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
8651
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
8652
- if (o) {
8653
- o.service_id = fromId;
8654
- return;
8655
- }
8656
- }
8657
- }),
8658
- undo: () => ctx.undo()
8659
- });
8692
+ for (const f of (_g = p.fields) != null ? _g : []) {
8693
+ const o = (_h = f.options) == null ? void 0 : _h.find((x) => x.id === toId2);
8694
+ if (o) {
8695
+ o.service_id = fromId;
8696
+ return;
8697
+ }
8698
+ }
8660
8699
  return;
8661
8700
  }
8662
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
8701
+ throw new Error(
8702
+ 'service: to must be a tag ("t:*") or option ("o:*")'
8703
+ );
8663
8704
  }
8664
8705
  throw new Error(`Unknown connect kind: ${kind}`);
8665
8706
  }),
@@ -8670,90 +8711,109 @@ function disconnect(ctx, kind, fromId, toId2) {
8670
8711
  ctx.exec({
8671
8712
  name: `disconnect:${kind}`,
8672
8713
  do: () => ctx.patchProps((p) => {
8673
- var _a, _b, _c, _d, _e, _f, _g, _h;
8714
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
8674
8715
  if (kind === "bind") {
8675
8716
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
8676
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
8677
- if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
8717
+ const child = ((_a = p.filters) != null ? _a : []).find(
8718
+ (t) => t.id === toId2
8719
+ );
8720
+ if ((child == null ? void 0 : child.bind_id) === fromId) {
8721
+ delete child.bind_id;
8722
+ }
8678
8723
  return;
8679
8724
  }
8680
8725
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
8681
8726
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
8682
8727
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
8683
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
8728
+ const f = ((_b = p.fields) != null ? _b : []).find(
8729
+ (x) => x.id === fieldId
8730
+ );
8684
8731
  if (!(f == null ? void 0 : f.bind_id)) return;
8685
8732
  if (typeof f.bind_id === "string") {
8686
- if (f.bind_id === tagId) delete f.bind_id;
8733
+ if (f.bind_id === tagId) {
8734
+ delete f.bind_id;
8735
+ }
8687
8736
  return;
8688
8737
  }
8689
8738
  f.bind_id = f.bind_id.filter((x) => x !== tagId);
8690
- if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) delete f.bind_id;
8739
+ if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) {
8740
+ delete f.bind_id;
8741
+ }
8691
8742
  return;
8692
8743
  }
8693
- throw new Error(`unbind: unsupported route ${fromId} ? ${toId2}`);
8744
+ throw new Error(
8745
+ `unbind: unsupported route ${fromId} ? ${toId2}`
8746
+ );
8694
8747
  }
8695
8748
  if (kind === "include" || kind === "exclude") {
8696
- const key = kind === "include" ? "includes" : "excludes";
8749
+ const tagKey = kind === "include" ? "includes" : "excludes";
8750
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
8697
8751
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
8698
- const t = ((_d = p.filters) != null ? _d : []).find((x) => x.id === fromId);
8752
+ const t = ((_d = p.filters) != null ? _d : []).find(
8753
+ (x) => x.id === fromId
8754
+ );
8699
8755
  if (!t) return;
8700
- t[key] = ((_e = t[key]) != null ? _e : []).filter((x) => x !== toId2);
8701
- if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
8756
+ t[tagKey] = ((_e = t[tagKey]) != null ? _e : []).filter((x) => x !== toId2);
8757
+ if (!((_f = t[tagKey]) == null ? void 0 : _f.length)) {
8758
+ delete t[tagKey];
8759
+ }
8702
8760
  return;
8703
8761
  }
8704
- if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
8705
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
8762
+ if ((ctx.isFieldId(fromId) || ctx.isOptionId(fromId)) && ctx.isFieldId(toId2)) {
8706
8763
  const maps = p[mapKey];
8707
- if (!maps) return;
8708
- if (maps[fromId]) {
8709
- maps[fromId] = ((_g = maps[fromId]) != null ? _g : []).filter(
8710
- (fid) => fid !== toId2
8711
- );
8712
- if (!((_h = maps[fromId]) == null ? void 0 : _h.length)) delete maps[fromId];
8764
+ if (!(maps == null ? void 0 : maps[fromId])) return;
8765
+ maps[fromId] = maps[fromId].filter(
8766
+ (fid) => fid !== toId2
8767
+ );
8768
+ if (!((_g = maps[fromId]) == null ? void 0 : _g.length)) {
8769
+ delete maps[fromId];
8770
+ }
8771
+ if (!Object.keys(maps).length) {
8772
+ delete p[mapKey];
8713
8773
  }
8714
- if (!Object.keys(maps).length) delete p[mapKey];
8715
8774
  return;
8716
8775
  }
8717
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
8776
+ throw new Error(
8777
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
8778
+ );
8718
8779
  }
8719
8780
  if (kind === "service") {
8720
8781
  ensureServiceExists(ctx.opts, fromId);
8721
8782
  if (toId2.startsWith("t:")) {
8722
- ctx.exec({
8723
- name: "disconnect:service?tag",
8724
- do: () => ctx.patchProps((next) => {
8725
- var _a2;
8726
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
8727
- if (t) delete t.service_id;
8728
- }),
8729
- undo: () => ctx.undo()
8730
- });
8783
+ const t = ((_h = p.filters) != null ? _h : []).find((x) => x.id === toId2);
8784
+ if (t) {
8785
+ delete t.service_id;
8786
+ }
8731
8787
  return;
8732
8788
  }
8733
8789
  if (toId2.startsWith("o:")) {
8734
- ctx.exec({
8735
- name: "disconnect:service?option",
8736
- do: () => ctx.patchProps((next) => {
8737
- var _a2, _b2;
8738
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
8739
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
8740
- if (o) {
8741
- delete o.service_id;
8742
- return;
8743
- }
8744
- }
8745
- }),
8746
- undo: () => ctx.undo()
8747
- });
8790
+ for (const f of (_i = p.fields) != null ? _i : []) {
8791
+ const o = (_j = f.options) == null ? void 0 : _j.find((x) => x.id === toId2);
8792
+ if (o) {
8793
+ delete o.service_id;
8794
+ return;
8795
+ }
8796
+ }
8748
8797
  return;
8749
8798
  }
8750
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
8799
+ throw new Error(
8800
+ 'service: to must be a tag ("t:*") or option ("o:*")'
8801
+ );
8751
8802
  }
8752
8803
  throw new Error(`Unknown disconnect kind: ${kind}`);
8753
8804
  }),
8754
8805
  undo: () => ctx.undo()
8755
8806
  });
8756
8807
  }
8808
+ function addMappedField(p, mapKey, fromId, toId2) {
8809
+ var _a, _b;
8810
+ const maps = (_a = p[mapKey]) != null ? _a : {};
8811
+ const arr = (_b = maps[fromId]) != null ? _b : [];
8812
+ if (!arr.includes(toId2)) {
8813
+ maps[fromId] = [...arr, toId2];
8814
+ }
8815
+ p[mapKey] = maps;
8816
+ }
8757
8817
 
8758
8818
  // src/react/canvas/editor/editor-service-filter.ts
8759
8819
  function filterServicesForVisibleGroup2(ctx, candidates, input) {