@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.
@@ -1840,10 +1840,19 @@ function validateOrderKinds(v) {
1840
1840
  }
1841
1841
 
1842
1842
  // src/core/validate/steps/service-vs-input.ts
1843
+ function hasButtonTriggerMap(v, fieldId) {
1844
+ var _a, _b;
1845
+ const includes = (_a = v.props.includes_for_buttons) == null ? void 0 : _a[fieldId];
1846
+ const excludes = (_b = v.props.excludes_for_buttons) == null ? void 0 : _b[fieldId];
1847
+ return Array.isArray(includes) && includes.length > 0 || Array.isArray(excludes) && excludes.length > 0;
1848
+ }
1843
1849
  function validateServiceVsUserInput(v) {
1844
1850
  for (const f of v.fields) {
1845
1851
  const anySvc = hasAnyServiceOption(f);
1846
1852
  const hasName = !!(f.name && f.name.trim());
1853
+ const isButton2 = f.button === true;
1854
+ const hasFieldService = f.service_id !== void 0 && f.service_id !== null;
1855
+ const hasTriggerMap = isButton2 && hasButtonTriggerMap(v, f.id);
1847
1856
  if (f.type === "custom" && anySvc) {
1848
1857
  v.errors.push({
1849
1858
  code: "user_input_field_has_service_option",
@@ -1854,14 +1863,15 @@ function validateServiceVsUserInput(v) {
1854
1863
  });
1855
1864
  }
1856
1865
  if (!hasName) {
1857
- if (!anySvc) {
1858
- v.errors.push({
1859
- code: "service_field_missing_service_id",
1860
- severity: "error",
1861
- message: `Service-backed field "${f.id}" has no "name" and must provide at least one option with a service_id.`,
1862
- nodeId: f.id
1863
- });
1866
+ if (hasFieldService || anySvc || hasTriggerMap) {
1867
+ continue;
1864
1868
  }
1869
+ v.errors.push({
1870
+ code: "service_field_missing_service_id",
1871
+ severity: "error",
1872
+ 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.`,
1873
+ nodeId: f.id
1874
+ });
1865
1875
  } else {
1866
1876
  if (anySvc) {
1867
1877
  v.errors.push({
@@ -3955,7 +3965,7 @@ function createNodeIndex(builder) {
3955
3965
  for (const fieldId of visible) {
3956
3966
  const node = getField(fieldId);
3957
3967
  if (!node) continue;
3958
- const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, tagId);
3968
+ const explicit = includes.has(fieldId);
3959
3969
  results.push(explicit ? node : { ...node, isInherited: true });
3960
3970
  }
3961
3971
  return Object.freeze(results);
@@ -7063,7 +7073,9 @@ function include(ctx, receiverId, idOrIds) {
7063
7073
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
7064
7074
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
7065
7075
  if (receiver.kind === "tag") {
7066
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
7076
+ const t = ((_a = p.filters) != null ? _a : []).find(
7077
+ (x) => x.id === receiverId
7078
+ );
7067
7079
  if (t) {
7068
7080
  const accepted = [];
7069
7081
  const next = new Set((_b = t.includes) != null ? _b : []);
@@ -7105,7 +7117,12 @@ function include(ctx, receiverId, idOrIds) {
7105
7117
  const current = (_f = (_e = p.includes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
7106
7118
  const next = new Set(current);
7107
7119
  for (const id of ids) {
7108
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
7120
+ if (wouldCreateIncludeExcludeCycle(
7121
+ ctx,
7122
+ p,
7123
+ receiverId,
7124
+ id
7125
+ )) {
7109
7126
  ctx.emit("editor:error", {
7110
7127
  message: `Cycle detected: ${receiverId} including ${id} would create a cycle.`,
7111
7128
  code: "cycle_detected",
@@ -7121,7 +7138,8 @@ function include(ctx, receiverId, idOrIds) {
7121
7138
  accepted.push(id);
7122
7139
  }
7123
7140
  if (accepted.length > 0 || current.length > 0) {
7124
- if (!p.includes_for_buttons) p.includes_for_buttons = {};
7141
+ if (!p.includes_for_buttons)
7142
+ p.includes_for_buttons = {};
7125
7143
  p.includes_for_buttons[receiverId] = Array.from(next);
7126
7144
  }
7127
7145
  if ((_g = p.excludes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -7136,7 +7154,9 @@ function include(ctx, receiverId, idOrIds) {
7136
7154
  if (!p.fields) p.fields = [];
7137
7155
  if (!p.filters) p.filters = [];
7138
7156
  } else {
7139
- throw new Error("Receiver must be a tag, button field, or option");
7157
+ throw new Error(
7158
+ "Receiver must be a tag, button field, or option"
7159
+ );
7140
7160
  }
7141
7161
  }),
7142
7162
  undo: () => ctx.undo()
@@ -7151,7 +7171,9 @@ function exclude(ctx, receiverId, idOrIds) {
7151
7171
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
7152
7172
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
7153
7173
  if (receiver.kind === "tag") {
7154
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
7174
+ const t = ((_a = p.filters) != null ? _a : []).find(
7175
+ (x) => x.id === receiverId
7176
+ );
7155
7177
  if (t) {
7156
7178
  const accepted = [];
7157
7179
  const next = new Set((_b = t.excludes) != null ? _b : []);
@@ -7193,7 +7215,12 @@ function exclude(ctx, receiverId, idOrIds) {
7193
7215
  const current = (_f = (_e = p.excludes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
7194
7216
  const next = new Set(current);
7195
7217
  for (const id of ids) {
7196
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
7218
+ if (wouldCreateIncludeExcludeCycle(
7219
+ ctx,
7220
+ p,
7221
+ receiverId,
7222
+ id
7223
+ )) {
7197
7224
  ctx.emit("editor:error", {
7198
7225
  message: `Cycle detected: ${receiverId} excluding ${id} would create a cycle.`,
7199
7226
  code: "cycle_detected",
@@ -7209,7 +7236,8 @@ function exclude(ctx, receiverId, idOrIds) {
7209
7236
  accepted.push(id);
7210
7237
  }
7211
7238
  if (accepted.length > 0 || current.length > 0) {
7212
- if (!p.excludes_for_buttons) p.excludes_for_buttons = {};
7239
+ if (!p.excludes_for_buttons)
7240
+ p.excludes_for_buttons = {};
7213
7241
  p.excludes_for_buttons[receiverId] = Array.from(next);
7214
7242
  }
7215
7243
  if ((_g = p.includes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -7224,7 +7252,9 @@ function exclude(ctx, receiverId, idOrIds) {
7224
7252
  if (!p.fields) p.fields = [];
7225
7253
  if (!p.filters) p.filters = [];
7226
7254
  } else {
7227
- throw new Error("Receiver must be a tag, button field, or option");
7255
+ throw new Error(
7256
+ "Receiver must be a tag, button field, or option"
7257
+ );
7228
7258
  }
7229
7259
  }),
7230
7260
  undo: () => ctx.undo()
@@ -7234,87 +7264,98 @@ function connect(ctx, kind, fromId, toId2) {
7234
7264
  ctx.exec({
7235
7265
  name: `connect:${kind}`,
7236
7266
  do: () => ctx.patchProps((p) => {
7237
- var _a, _b, _c, _d, _e;
7267
+ var _a, _b, _c, _d, _e, _f, _g, _h;
7238
7268
  if (kind === "bind") {
7239
7269
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
7240
7270
  if (wouldCreateTagCycle(ctx, p, fromId, toId2)) {
7241
- throw new Error(`bind would create a cycle: ${fromId} ? ${toId2}`);
7271
+ throw new Error(
7272
+ `bind would create a cycle: ${fromId} ? ${toId2}`
7273
+ );
7242
7274
  }
7243
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
7275
+ const child = ((_a = p.filters) != null ? _a : []).find(
7276
+ (t) => t.id === toId2
7277
+ );
7244
7278
  if (child) child.bind_id = fromId;
7245
7279
  return;
7246
7280
  }
7247
7281
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
7248
7282
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
7249
7283
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
7250
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
7284
+ const f = ((_b = p.fields) != null ? _b : []).find(
7285
+ (x) => x.id === fieldId
7286
+ );
7251
7287
  if (!f) return;
7252
7288
  if (!f.bind_id) {
7253
7289
  f.bind_id = tagId;
7254
7290
  return;
7255
7291
  }
7256
7292
  if (typeof f.bind_id === "string") {
7257
- if (f.bind_id !== tagId) f.bind_id = [f.bind_id, tagId];
7293
+ if (f.bind_id !== tagId) {
7294
+ f.bind_id = [f.bind_id, tagId];
7295
+ }
7258
7296
  return;
7259
7297
  }
7260
- if (!f.bind_id.includes(tagId)) f.bind_id.push(tagId);
7298
+ if (!f.bind_id.includes(tagId)) {
7299
+ f.bind_id.push(tagId);
7300
+ }
7261
7301
  return;
7262
7302
  }
7263
- throw new Error(`bind: unsupported route ${fromId} ? ${toId2}`);
7303
+ throw new Error(
7304
+ `bind: unsupported route ${fromId} ? ${toId2}`
7305
+ );
7264
7306
  }
7265
7307
  if (kind === "include" || kind === "exclude") {
7266
- const key = kind === "include" ? "includes" : "excludes";
7308
+ const tagKey = kind === "include" ? "includes" : "excludes";
7309
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
7267
7310
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
7268
- const t = ((_c = p.filters) != null ? _c : []).find((x) => x.id === fromId);
7311
+ const t = ((_c = p.filters) != null ? _c : []).find(
7312
+ (x) => x.id === fromId
7313
+ );
7269
7314
  if (!t) return;
7270
- const arr = (_d = t[key]) != null ? _d : t[key] = [];
7315
+ const arr = (_d = t[tagKey]) != null ? _d : t[tagKey] = [];
7271
7316
  if (!arr.includes(toId2)) arr.push(toId2);
7272
7317
  return;
7273
7318
  }
7319
+ if (ctx.isFieldId(fromId) && ctx.isFieldId(toId2)) {
7320
+ const source = ((_e = p.fields) != null ? _e : []).find(
7321
+ (x) => x.id === fromId
7322
+ );
7323
+ if (!(source == null ? void 0 : source.button)) {
7324
+ throw new Error(
7325
+ `${kind}: source field must be button=true: ${fromId} ? ${toId2}`
7326
+ );
7327
+ }
7328
+ addMappedField(p, mapKey, fromId, toId2);
7329
+ return;
7330
+ }
7274
7331
  if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
7275
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7276
- const maps = p[mapKey];
7277
- const next = { ...maps != null ? maps : {} };
7278
- const arr = (_e = next[fromId]) != null ? _e : [];
7279
- if (!arr.includes(toId2)) arr.push(toId2);
7280
- next[fromId] = arr;
7281
- p[mapKey] = next;
7332
+ addMappedField(p, mapKey, fromId, toId2);
7282
7333
  return;
7283
7334
  }
7284
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
7335
+ throw new Error(
7336
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
7337
+ );
7285
7338
  }
7286
7339
  if (kind === "service") {
7287
7340
  ensureServiceExists(ctx.opts, fromId);
7288
7341
  if (toId2.startsWith("t:")) {
7289
- ctx.exec({
7290
- name: "connect:service?tag",
7291
- do: () => ctx.patchProps((next) => {
7292
- var _a2;
7293
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
7294
- if (t) t.service_id = fromId;
7295
- }),
7296
- undo: () => ctx.undo()
7297
- });
7342
+ const t = ((_f = p.filters) != null ? _f : []).find((x) => x.id === toId2);
7343
+ if (t) t.service_id = fromId;
7298
7344
  return;
7299
7345
  }
7300
7346
  if (toId2.startsWith("o:")) {
7301
- ctx.exec({
7302
- name: "connect:service?option",
7303
- do: () => ctx.patchProps((next) => {
7304
- var _a2, _b2;
7305
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
7306
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
7307
- if (o) {
7308
- o.service_id = fromId;
7309
- return;
7310
- }
7311
- }
7312
- }),
7313
- undo: () => ctx.undo()
7314
- });
7347
+ for (const f of (_g = p.fields) != null ? _g : []) {
7348
+ const o = (_h = f.options) == null ? void 0 : _h.find((x) => x.id === toId2);
7349
+ if (o) {
7350
+ o.service_id = fromId;
7351
+ return;
7352
+ }
7353
+ }
7315
7354
  return;
7316
7355
  }
7317
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
7356
+ throw new Error(
7357
+ 'service: to must be a tag ("t:*") or option ("o:*")'
7358
+ );
7318
7359
  }
7319
7360
  throw new Error(`Unknown connect kind: ${kind}`);
7320
7361
  }),
@@ -7325,90 +7366,109 @@ function disconnect(ctx, kind, fromId, toId2) {
7325
7366
  ctx.exec({
7326
7367
  name: `disconnect:${kind}`,
7327
7368
  do: () => ctx.patchProps((p) => {
7328
- var _a, _b, _c, _d, _e, _f, _g, _h;
7369
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7329
7370
  if (kind === "bind") {
7330
7371
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
7331
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
7332
- if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
7372
+ const child = ((_a = p.filters) != null ? _a : []).find(
7373
+ (t) => t.id === toId2
7374
+ );
7375
+ if ((child == null ? void 0 : child.bind_id) === fromId) {
7376
+ delete child.bind_id;
7377
+ }
7333
7378
  return;
7334
7379
  }
7335
7380
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
7336
7381
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
7337
7382
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
7338
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
7383
+ const f = ((_b = p.fields) != null ? _b : []).find(
7384
+ (x) => x.id === fieldId
7385
+ );
7339
7386
  if (!(f == null ? void 0 : f.bind_id)) return;
7340
7387
  if (typeof f.bind_id === "string") {
7341
- if (f.bind_id === tagId) delete f.bind_id;
7388
+ if (f.bind_id === tagId) {
7389
+ delete f.bind_id;
7390
+ }
7342
7391
  return;
7343
7392
  }
7344
7393
  f.bind_id = f.bind_id.filter((x) => x !== tagId);
7345
- if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) delete f.bind_id;
7394
+ if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) {
7395
+ delete f.bind_id;
7396
+ }
7346
7397
  return;
7347
7398
  }
7348
- throw new Error(`unbind: unsupported route ${fromId} ? ${toId2}`);
7399
+ throw new Error(
7400
+ `unbind: unsupported route ${fromId} ? ${toId2}`
7401
+ );
7349
7402
  }
7350
7403
  if (kind === "include" || kind === "exclude") {
7351
- const key = kind === "include" ? "includes" : "excludes";
7404
+ const tagKey = kind === "include" ? "includes" : "excludes";
7405
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
7352
7406
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
7353
- const t = ((_d = p.filters) != null ? _d : []).find((x) => x.id === fromId);
7407
+ const t = ((_d = p.filters) != null ? _d : []).find(
7408
+ (x) => x.id === fromId
7409
+ );
7354
7410
  if (!t) return;
7355
- t[key] = ((_e = t[key]) != null ? _e : []).filter((x) => x !== toId2);
7356
- if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
7411
+ t[tagKey] = ((_e = t[tagKey]) != null ? _e : []).filter((x) => x !== toId2);
7412
+ if (!((_f = t[tagKey]) == null ? void 0 : _f.length)) {
7413
+ delete t[tagKey];
7414
+ }
7357
7415
  return;
7358
7416
  }
7359
- if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
7360
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7417
+ if ((ctx.isFieldId(fromId) || ctx.isOptionId(fromId)) && ctx.isFieldId(toId2)) {
7361
7418
  const maps = p[mapKey];
7362
- if (!maps) return;
7363
- if (maps[fromId]) {
7364
- maps[fromId] = ((_g = maps[fromId]) != null ? _g : []).filter(
7365
- (fid) => fid !== toId2
7366
- );
7367
- if (!((_h = maps[fromId]) == null ? void 0 : _h.length)) delete maps[fromId];
7419
+ if (!(maps == null ? void 0 : maps[fromId])) return;
7420
+ maps[fromId] = maps[fromId].filter(
7421
+ (fid) => fid !== toId2
7422
+ );
7423
+ if (!((_g = maps[fromId]) == null ? void 0 : _g.length)) {
7424
+ delete maps[fromId];
7425
+ }
7426
+ if (!Object.keys(maps).length) {
7427
+ delete p[mapKey];
7368
7428
  }
7369
- if (!Object.keys(maps).length) delete p[mapKey];
7370
7429
  return;
7371
7430
  }
7372
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
7431
+ throw new Error(
7432
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
7433
+ );
7373
7434
  }
7374
7435
  if (kind === "service") {
7375
7436
  ensureServiceExists(ctx.opts, fromId);
7376
7437
  if (toId2.startsWith("t:")) {
7377
- ctx.exec({
7378
- name: "disconnect:service?tag",
7379
- do: () => ctx.patchProps((next) => {
7380
- var _a2;
7381
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
7382
- if (t) delete t.service_id;
7383
- }),
7384
- undo: () => ctx.undo()
7385
- });
7438
+ const t = ((_h = p.filters) != null ? _h : []).find((x) => x.id === toId2);
7439
+ if (t) {
7440
+ delete t.service_id;
7441
+ }
7386
7442
  return;
7387
7443
  }
7388
7444
  if (toId2.startsWith("o:")) {
7389
- ctx.exec({
7390
- name: "disconnect:service?option",
7391
- do: () => ctx.patchProps((next) => {
7392
- var _a2, _b2;
7393
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
7394
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
7395
- if (o) {
7396
- delete o.service_id;
7397
- return;
7398
- }
7399
- }
7400
- }),
7401
- undo: () => ctx.undo()
7402
- });
7445
+ for (const f of (_i = p.fields) != null ? _i : []) {
7446
+ const o = (_j = f.options) == null ? void 0 : _j.find((x) => x.id === toId2);
7447
+ if (o) {
7448
+ delete o.service_id;
7449
+ return;
7450
+ }
7451
+ }
7403
7452
  return;
7404
7453
  }
7405
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
7454
+ throw new Error(
7455
+ 'service: to must be a tag ("t:*") or option ("o:*")'
7456
+ );
7406
7457
  }
7407
7458
  throw new Error(`Unknown disconnect kind: ${kind}`);
7408
7459
  }),
7409
7460
  undo: () => ctx.undo()
7410
7461
  });
7411
7462
  }
7463
+ function addMappedField(p, mapKey, fromId, toId2) {
7464
+ var _a, _b;
7465
+ const maps = (_a = p[mapKey]) != null ? _a : {};
7466
+ const arr = (_b = maps[fromId]) != null ? _b : [];
7467
+ if (!arr.includes(toId2)) {
7468
+ maps[fromId] = [...arr, toId2];
7469
+ }
7470
+ p[mapKey] = maps;
7471
+ }
7412
7472
 
7413
7473
  // src/react/canvas/editor/editor-service-filter.ts
7414
7474
  function filterServicesForVisibleGroup2(ctx, candidates, input) {