@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.
@@ -4658,10 +4658,19 @@ function validateOrderKinds(v) {
4658
4658
  }
4659
4659
 
4660
4660
  // src/core/validate/steps/service-vs-input.ts
4661
+ function hasButtonTriggerMap(v, fieldId) {
4662
+ var _a, _b;
4663
+ const includes = (_a = v.props.includes_for_buttons) == null ? void 0 : _a[fieldId];
4664
+ const excludes = (_b = v.props.excludes_for_buttons) == null ? void 0 : _b[fieldId];
4665
+ return Array.isArray(includes) && includes.length > 0 || Array.isArray(excludes) && excludes.length > 0;
4666
+ }
4661
4667
  function validateServiceVsUserInput(v) {
4662
4668
  for (const f of v.fields) {
4663
4669
  const anySvc = hasAnyServiceOption(f);
4664
4670
  const hasName = !!(f.name && f.name.trim());
4671
+ const isButton2 = f.button === true;
4672
+ const hasFieldService = f.service_id !== void 0 && f.service_id !== null;
4673
+ const hasTriggerMap = isButton2 && hasButtonTriggerMap(v, f.id);
4665
4674
  if (f.type === "custom" && anySvc) {
4666
4675
  v.errors.push({
4667
4676
  code: "user_input_field_has_service_option",
@@ -4672,14 +4681,15 @@ function validateServiceVsUserInput(v) {
4672
4681
  });
4673
4682
  }
4674
4683
  if (!hasName) {
4675
- if (!anySvc) {
4676
- v.errors.push({
4677
- code: "service_field_missing_service_id",
4678
- severity: "error",
4679
- message: `Service-backed field "${f.id}" has no "name" and must provide at least one option with a service_id.`,
4680
- nodeId: f.id
4681
- });
4684
+ if (hasFieldService || anySvc || hasTriggerMap) {
4685
+ continue;
4682
4686
  }
4687
+ v.errors.push({
4688
+ code: "service_field_missing_service_id",
4689
+ severity: "error",
4690
+ 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.`,
4691
+ nodeId: f.id
4692
+ });
4683
4693
  } else {
4684
4694
  if (anySvc) {
4685
4695
  v.errors.push({
@@ -6611,7 +6621,7 @@ function createNodeIndex(builder) {
6611
6621
  for (const fieldId of visible) {
6612
6622
  const node = getField(fieldId);
6613
6623
  if (!node) continue;
6614
- const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, tagId);
6624
+ const explicit = includes.has(fieldId);
6615
6625
  results.push(explicit ? node : { ...node, isInherited: true });
6616
6626
  }
6617
6627
  return Object.freeze(results);
@@ -8365,7 +8375,9 @@ function include(ctx, receiverId, idOrIds) {
8365
8375
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
8366
8376
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
8367
8377
  if (receiver.kind === "tag") {
8368
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
8378
+ const t = ((_a = p.filters) != null ? _a : []).find(
8379
+ (x) => x.id === receiverId
8380
+ );
8369
8381
  if (t) {
8370
8382
  const accepted = [];
8371
8383
  const next = new Set((_b = t.includes) != null ? _b : []);
@@ -8407,7 +8419,12 @@ function include(ctx, receiverId, idOrIds) {
8407
8419
  const current = (_f = (_e = p.includes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
8408
8420
  const next = new Set(current);
8409
8421
  for (const id of ids) {
8410
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
8422
+ if (wouldCreateIncludeExcludeCycle(
8423
+ ctx,
8424
+ p,
8425
+ receiverId,
8426
+ id
8427
+ )) {
8411
8428
  ctx.emit("editor:error", {
8412
8429
  message: `Cycle detected: ${receiverId} including ${id} would create a cycle.`,
8413
8430
  code: "cycle_detected",
@@ -8423,7 +8440,8 @@ function include(ctx, receiverId, idOrIds) {
8423
8440
  accepted.push(id);
8424
8441
  }
8425
8442
  if (accepted.length > 0 || current.length > 0) {
8426
- if (!p.includes_for_buttons) p.includes_for_buttons = {};
8443
+ if (!p.includes_for_buttons)
8444
+ p.includes_for_buttons = {};
8427
8445
  p.includes_for_buttons[receiverId] = Array.from(next);
8428
8446
  }
8429
8447
  if ((_g = p.excludes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -8438,7 +8456,9 @@ function include(ctx, receiverId, idOrIds) {
8438
8456
  if (!p.fields) p.fields = [];
8439
8457
  if (!p.filters) p.filters = [];
8440
8458
  } else {
8441
- throw new Error("Receiver must be a tag, button field, or option");
8459
+ throw new Error(
8460
+ "Receiver must be a tag, button field, or option"
8461
+ );
8442
8462
  }
8443
8463
  }),
8444
8464
  undo: () => ctx.undo()
@@ -8453,7 +8473,9 @@ function exclude(ctx, receiverId, idOrIds) {
8453
8473
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
8454
8474
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
8455
8475
  if (receiver.kind === "tag") {
8456
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
8476
+ const t = ((_a = p.filters) != null ? _a : []).find(
8477
+ (x) => x.id === receiverId
8478
+ );
8457
8479
  if (t) {
8458
8480
  const accepted = [];
8459
8481
  const next = new Set((_b = t.excludes) != null ? _b : []);
@@ -8495,7 +8517,12 @@ function exclude(ctx, receiverId, idOrIds) {
8495
8517
  const current = (_f = (_e = p.excludes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
8496
8518
  const next = new Set(current);
8497
8519
  for (const id of ids) {
8498
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
8520
+ if (wouldCreateIncludeExcludeCycle(
8521
+ ctx,
8522
+ p,
8523
+ receiverId,
8524
+ id
8525
+ )) {
8499
8526
  ctx.emit("editor:error", {
8500
8527
  message: `Cycle detected: ${receiverId} excluding ${id} would create a cycle.`,
8501
8528
  code: "cycle_detected",
@@ -8511,7 +8538,8 @@ function exclude(ctx, receiverId, idOrIds) {
8511
8538
  accepted.push(id);
8512
8539
  }
8513
8540
  if (accepted.length > 0 || current.length > 0) {
8514
- if (!p.excludes_for_buttons) p.excludes_for_buttons = {};
8541
+ if (!p.excludes_for_buttons)
8542
+ p.excludes_for_buttons = {};
8515
8543
  p.excludes_for_buttons[receiverId] = Array.from(next);
8516
8544
  }
8517
8545
  if ((_g = p.includes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -8526,7 +8554,9 @@ function exclude(ctx, receiverId, idOrIds) {
8526
8554
  if (!p.fields) p.fields = [];
8527
8555
  if (!p.filters) p.filters = [];
8528
8556
  } else {
8529
- throw new Error("Receiver must be a tag, button field, or option");
8557
+ throw new Error(
8558
+ "Receiver must be a tag, button field, or option"
8559
+ );
8530
8560
  }
8531
8561
  }),
8532
8562
  undo: () => ctx.undo()
@@ -8536,87 +8566,98 @@ function connect(ctx, kind, fromId, toId2) {
8536
8566
  ctx.exec({
8537
8567
  name: `connect:${kind}`,
8538
8568
  do: () => ctx.patchProps((p) => {
8539
- var _a, _b, _c, _d, _e;
8569
+ var _a, _b, _c, _d, _e, _f, _g, _h;
8540
8570
  if (kind === "bind") {
8541
8571
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
8542
8572
  if (wouldCreateTagCycle(ctx, p, fromId, toId2)) {
8543
- throw new Error(`bind would create a cycle: ${fromId} ? ${toId2}`);
8573
+ throw new Error(
8574
+ `bind would create a cycle: ${fromId} ? ${toId2}`
8575
+ );
8544
8576
  }
8545
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
8577
+ const child = ((_a = p.filters) != null ? _a : []).find(
8578
+ (t) => t.id === toId2
8579
+ );
8546
8580
  if (child) child.bind_id = fromId;
8547
8581
  return;
8548
8582
  }
8549
8583
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
8550
8584
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
8551
8585
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
8552
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
8586
+ const f = ((_b = p.fields) != null ? _b : []).find(
8587
+ (x) => x.id === fieldId
8588
+ );
8553
8589
  if (!f) return;
8554
8590
  if (!f.bind_id) {
8555
8591
  f.bind_id = tagId;
8556
8592
  return;
8557
8593
  }
8558
8594
  if (typeof f.bind_id === "string") {
8559
- if (f.bind_id !== tagId) f.bind_id = [f.bind_id, tagId];
8595
+ if (f.bind_id !== tagId) {
8596
+ f.bind_id = [f.bind_id, tagId];
8597
+ }
8560
8598
  return;
8561
8599
  }
8562
- if (!f.bind_id.includes(tagId)) f.bind_id.push(tagId);
8600
+ if (!f.bind_id.includes(tagId)) {
8601
+ f.bind_id.push(tagId);
8602
+ }
8563
8603
  return;
8564
8604
  }
8565
- throw new Error(`bind: unsupported route ${fromId} ? ${toId2}`);
8605
+ throw new Error(
8606
+ `bind: unsupported route ${fromId} ? ${toId2}`
8607
+ );
8566
8608
  }
8567
8609
  if (kind === "include" || kind === "exclude") {
8568
- const key = kind === "include" ? "includes" : "excludes";
8610
+ const tagKey = kind === "include" ? "includes" : "excludes";
8611
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
8569
8612
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
8570
- const t = ((_c = p.filters) != null ? _c : []).find((x) => x.id === fromId);
8613
+ const t = ((_c = p.filters) != null ? _c : []).find(
8614
+ (x) => x.id === fromId
8615
+ );
8571
8616
  if (!t) return;
8572
- const arr = (_d = t[key]) != null ? _d : t[key] = [];
8617
+ const arr = (_d = t[tagKey]) != null ? _d : t[tagKey] = [];
8573
8618
  if (!arr.includes(toId2)) arr.push(toId2);
8574
8619
  return;
8575
8620
  }
8621
+ if (ctx.isFieldId(fromId) && ctx.isFieldId(toId2)) {
8622
+ const source = ((_e = p.fields) != null ? _e : []).find(
8623
+ (x) => x.id === fromId
8624
+ );
8625
+ if (!(source == null ? void 0 : source.button)) {
8626
+ throw new Error(
8627
+ `${kind}: source field must be button=true: ${fromId} ? ${toId2}`
8628
+ );
8629
+ }
8630
+ addMappedField(p, mapKey, fromId, toId2);
8631
+ return;
8632
+ }
8576
8633
  if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
8577
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
8578
- const maps = p[mapKey];
8579
- const next = { ...maps != null ? maps : {} };
8580
- const arr = (_e = next[fromId]) != null ? _e : [];
8581
- if (!arr.includes(toId2)) arr.push(toId2);
8582
- next[fromId] = arr;
8583
- p[mapKey] = next;
8634
+ addMappedField(p, mapKey, fromId, toId2);
8584
8635
  return;
8585
8636
  }
8586
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
8637
+ throw new Error(
8638
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
8639
+ );
8587
8640
  }
8588
8641
  if (kind === "service") {
8589
8642
  ensureServiceExists(ctx.opts, fromId);
8590
8643
  if (toId2.startsWith("t:")) {
8591
- ctx.exec({
8592
- name: "connect:service?tag",
8593
- do: () => ctx.patchProps((next) => {
8594
- var _a2;
8595
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
8596
- if (t) t.service_id = fromId;
8597
- }),
8598
- undo: () => ctx.undo()
8599
- });
8644
+ const t = ((_f = p.filters) != null ? _f : []).find((x) => x.id === toId2);
8645
+ if (t) t.service_id = fromId;
8600
8646
  return;
8601
8647
  }
8602
8648
  if (toId2.startsWith("o:")) {
8603
- ctx.exec({
8604
- name: "connect:service?option",
8605
- do: () => ctx.patchProps((next) => {
8606
- var _a2, _b2;
8607
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
8608
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
8609
- if (o) {
8610
- o.service_id = fromId;
8611
- return;
8612
- }
8613
- }
8614
- }),
8615
- undo: () => ctx.undo()
8616
- });
8649
+ for (const f of (_g = p.fields) != null ? _g : []) {
8650
+ const o = (_h = f.options) == null ? void 0 : _h.find((x) => x.id === toId2);
8651
+ if (o) {
8652
+ o.service_id = fromId;
8653
+ return;
8654
+ }
8655
+ }
8617
8656
  return;
8618
8657
  }
8619
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
8658
+ throw new Error(
8659
+ 'service: to must be a tag ("t:*") or option ("o:*")'
8660
+ );
8620
8661
  }
8621
8662
  throw new Error(`Unknown connect kind: ${kind}`);
8622
8663
  }),
@@ -8627,90 +8668,109 @@ function disconnect(ctx, kind, fromId, toId2) {
8627
8668
  ctx.exec({
8628
8669
  name: `disconnect:${kind}`,
8629
8670
  do: () => ctx.patchProps((p) => {
8630
- var _a, _b, _c, _d, _e, _f, _g, _h;
8671
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
8631
8672
  if (kind === "bind") {
8632
8673
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
8633
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
8634
- if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
8674
+ const child = ((_a = p.filters) != null ? _a : []).find(
8675
+ (t) => t.id === toId2
8676
+ );
8677
+ if ((child == null ? void 0 : child.bind_id) === fromId) {
8678
+ delete child.bind_id;
8679
+ }
8635
8680
  return;
8636
8681
  }
8637
8682
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
8638
8683
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
8639
8684
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
8640
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
8685
+ const f = ((_b = p.fields) != null ? _b : []).find(
8686
+ (x) => x.id === fieldId
8687
+ );
8641
8688
  if (!(f == null ? void 0 : f.bind_id)) return;
8642
8689
  if (typeof f.bind_id === "string") {
8643
- if (f.bind_id === tagId) delete f.bind_id;
8690
+ if (f.bind_id === tagId) {
8691
+ delete f.bind_id;
8692
+ }
8644
8693
  return;
8645
8694
  }
8646
8695
  f.bind_id = f.bind_id.filter((x) => x !== tagId);
8647
- if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) delete f.bind_id;
8696
+ if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) {
8697
+ delete f.bind_id;
8698
+ }
8648
8699
  return;
8649
8700
  }
8650
- throw new Error(`unbind: unsupported route ${fromId} ? ${toId2}`);
8701
+ throw new Error(
8702
+ `unbind: unsupported route ${fromId} ? ${toId2}`
8703
+ );
8651
8704
  }
8652
8705
  if (kind === "include" || kind === "exclude") {
8653
- const key = kind === "include" ? "includes" : "excludes";
8706
+ const tagKey = kind === "include" ? "includes" : "excludes";
8707
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
8654
8708
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
8655
- const t = ((_d = p.filters) != null ? _d : []).find((x) => x.id === fromId);
8709
+ const t = ((_d = p.filters) != null ? _d : []).find(
8710
+ (x) => x.id === fromId
8711
+ );
8656
8712
  if (!t) return;
8657
- t[key] = ((_e = t[key]) != null ? _e : []).filter((x) => x !== toId2);
8658
- if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
8713
+ t[tagKey] = ((_e = t[tagKey]) != null ? _e : []).filter((x) => x !== toId2);
8714
+ if (!((_f = t[tagKey]) == null ? void 0 : _f.length)) {
8715
+ delete t[tagKey];
8716
+ }
8659
8717
  return;
8660
8718
  }
8661
- if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
8662
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
8719
+ if ((ctx.isFieldId(fromId) || ctx.isOptionId(fromId)) && ctx.isFieldId(toId2)) {
8663
8720
  const maps = p[mapKey];
8664
- if (!maps) return;
8665
- if (maps[fromId]) {
8666
- maps[fromId] = ((_g = maps[fromId]) != null ? _g : []).filter(
8667
- (fid) => fid !== toId2
8668
- );
8669
- if (!((_h = maps[fromId]) == null ? void 0 : _h.length)) delete maps[fromId];
8721
+ if (!(maps == null ? void 0 : maps[fromId])) return;
8722
+ maps[fromId] = maps[fromId].filter(
8723
+ (fid) => fid !== toId2
8724
+ );
8725
+ if (!((_g = maps[fromId]) == null ? void 0 : _g.length)) {
8726
+ delete maps[fromId];
8727
+ }
8728
+ if (!Object.keys(maps).length) {
8729
+ delete p[mapKey];
8670
8730
  }
8671
- if (!Object.keys(maps).length) delete p[mapKey];
8672
8731
  return;
8673
8732
  }
8674
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
8733
+ throw new Error(
8734
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
8735
+ );
8675
8736
  }
8676
8737
  if (kind === "service") {
8677
8738
  ensureServiceExists(ctx.opts, fromId);
8678
8739
  if (toId2.startsWith("t:")) {
8679
- ctx.exec({
8680
- name: "disconnect:service?tag",
8681
- do: () => ctx.patchProps((next) => {
8682
- var _a2;
8683
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
8684
- if (t) delete t.service_id;
8685
- }),
8686
- undo: () => ctx.undo()
8687
- });
8740
+ const t = ((_h = p.filters) != null ? _h : []).find((x) => x.id === toId2);
8741
+ if (t) {
8742
+ delete t.service_id;
8743
+ }
8688
8744
  return;
8689
8745
  }
8690
8746
  if (toId2.startsWith("o:")) {
8691
- ctx.exec({
8692
- name: "disconnect:service?option",
8693
- do: () => ctx.patchProps((next) => {
8694
- var _a2, _b2;
8695
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
8696
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
8697
- if (o) {
8698
- delete o.service_id;
8699
- return;
8700
- }
8701
- }
8702
- }),
8703
- undo: () => ctx.undo()
8704
- });
8747
+ for (const f of (_i = p.fields) != null ? _i : []) {
8748
+ const o = (_j = f.options) == null ? void 0 : _j.find((x) => x.id === toId2);
8749
+ if (o) {
8750
+ delete o.service_id;
8751
+ return;
8752
+ }
8753
+ }
8705
8754
  return;
8706
8755
  }
8707
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
8756
+ throw new Error(
8757
+ 'service: to must be a tag ("t:*") or option ("o:*")'
8758
+ );
8708
8759
  }
8709
8760
  throw new Error(`Unknown disconnect kind: ${kind}`);
8710
8761
  }),
8711
8762
  undo: () => ctx.undo()
8712
8763
  });
8713
8764
  }
8765
+ function addMappedField(p, mapKey, fromId, toId2) {
8766
+ var _a, _b;
8767
+ const maps = (_a = p[mapKey]) != null ? _a : {};
8768
+ const arr = (_b = maps[fromId]) != null ? _b : [];
8769
+ if (!arr.includes(toId2)) {
8770
+ maps[fromId] = [...arr, toId2];
8771
+ }
8772
+ p[mapKey] = maps;
8773
+ }
8714
8774
 
8715
8775
  // src/react/canvas/editor/editor-service-filter.ts
8716
8776
  function filterServicesForVisibleGroup2(ctx, candidates, input) {