@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.
@@ -1904,10 +1904,19 @@ function validateOrderKinds(v) {
1904
1904
  }
1905
1905
 
1906
1906
  // src/core/validate/steps/service-vs-input.ts
1907
+ function hasButtonTriggerMap(v, fieldId) {
1908
+ var _a, _b;
1909
+ const includes = (_a = v.props.includes_for_buttons) == null ? void 0 : _a[fieldId];
1910
+ const excludes = (_b = v.props.excludes_for_buttons) == null ? void 0 : _b[fieldId];
1911
+ return Array.isArray(includes) && includes.length > 0 || Array.isArray(excludes) && excludes.length > 0;
1912
+ }
1907
1913
  function validateServiceVsUserInput(v) {
1908
1914
  for (const f of v.fields) {
1909
1915
  const anySvc = hasAnyServiceOption(f);
1910
1916
  const hasName = !!(f.name && f.name.trim());
1917
+ const isButton2 = f.button === true;
1918
+ const hasFieldService = f.service_id !== void 0 && f.service_id !== null;
1919
+ const hasTriggerMap = isButton2 && hasButtonTriggerMap(v, f.id);
1911
1920
  if (f.type === "custom" && anySvc) {
1912
1921
  v.errors.push({
1913
1922
  code: "user_input_field_has_service_option",
@@ -1918,14 +1927,15 @@ function validateServiceVsUserInput(v) {
1918
1927
  });
1919
1928
  }
1920
1929
  if (!hasName) {
1921
- if (!anySvc) {
1922
- v.errors.push({
1923
- code: "service_field_missing_service_id",
1924
- severity: "error",
1925
- message: `Service-backed field "${f.id}" has no "name" and must provide at least one option with a service_id.`,
1926
- nodeId: f.id
1927
- });
1930
+ if (hasFieldService || anySvc || hasTriggerMap) {
1931
+ continue;
1928
1932
  }
1933
+ v.errors.push({
1934
+ code: "service_field_missing_service_id",
1935
+ severity: "error",
1936
+ 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.`,
1937
+ nodeId: f.id
1938
+ });
1929
1939
  } else {
1930
1940
  if (anySvc) {
1931
1941
  v.errors.push({
@@ -4019,7 +4029,7 @@ function createNodeIndex(builder) {
4019
4029
  for (const fieldId of visible) {
4020
4030
  const node = getField(fieldId);
4021
4031
  if (!node) continue;
4022
- const explicit = includes.has(fieldId) || isFieldBoundDirectToTag(fieldId, tagId);
4032
+ const explicit = includes.has(fieldId);
4023
4033
  results.push(explicit ? node : { ...node, isInherited: true });
4024
4034
  }
4025
4035
  return Object.freeze(results);
@@ -7127,7 +7137,9 @@ function include(ctx, receiverId, idOrIds) {
7127
7137
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
7128
7138
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
7129
7139
  if (receiver.kind === "tag") {
7130
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
7140
+ const t = ((_a = p.filters) != null ? _a : []).find(
7141
+ (x) => x.id === receiverId
7142
+ );
7131
7143
  if (t) {
7132
7144
  const accepted = [];
7133
7145
  const next = new Set((_b = t.includes) != null ? _b : []);
@@ -7169,7 +7181,12 @@ function include(ctx, receiverId, idOrIds) {
7169
7181
  const current = (_f = (_e = p.includes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
7170
7182
  const next = new Set(current);
7171
7183
  for (const id of ids) {
7172
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
7184
+ if (wouldCreateIncludeExcludeCycle(
7185
+ ctx,
7186
+ p,
7187
+ receiverId,
7188
+ id
7189
+ )) {
7173
7190
  ctx.emit("editor:error", {
7174
7191
  message: `Cycle detected: ${receiverId} including ${id} would create a cycle.`,
7175
7192
  code: "cycle_detected",
@@ -7185,7 +7202,8 @@ function include(ctx, receiverId, idOrIds) {
7185
7202
  accepted.push(id);
7186
7203
  }
7187
7204
  if (accepted.length > 0 || current.length > 0) {
7188
- if (!p.includes_for_buttons) p.includes_for_buttons = {};
7205
+ if (!p.includes_for_buttons)
7206
+ p.includes_for_buttons = {};
7189
7207
  p.includes_for_buttons[receiverId] = Array.from(next);
7190
7208
  }
7191
7209
  if ((_g = p.excludes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -7200,7 +7218,9 @@ function include(ctx, receiverId, idOrIds) {
7200
7218
  if (!p.fields) p.fields = [];
7201
7219
  if (!p.filters) p.filters = [];
7202
7220
  } else {
7203
- throw new Error("Receiver must be a tag, button field, or option");
7221
+ throw new Error(
7222
+ "Receiver must be a tag, button field, or option"
7223
+ );
7204
7224
  }
7205
7225
  }),
7206
7226
  undo: () => ctx.undo()
@@ -7215,7 +7235,9 @@ function exclude(ctx, receiverId, idOrIds) {
7215
7235
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
7216
7236
  if (receiver.kind === "tag" || receiver.kind === "field" && isActualButtonField(receiver.data) || receiver.kind === "option") {
7217
7237
  if (receiver.kind === "tag") {
7218
- const t = ((_a = p.filters) != null ? _a : []).find((x) => x.id === receiverId);
7238
+ const t = ((_a = p.filters) != null ? _a : []).find(
7239
+ (x) => x.id === receiverId
7240
+ );
7219
7241
  if (t) {
7220
7242
  const accepted = [];
7221
7243
  const next = new Set((_b = t.excludes) != null ? _b : []);
@@ -7257,7 +7279,12 @@ function exclude(ctx, receiverId, idOrIds) {
7257
7279
  const current = (_f = (_e = p.excludes_for_buttons) == null ? void 0 : _e[receiverId]) != null ? _f : [];
7258
7280
  const next = new Set(current);
7259
7281
  for (const id of ids) {
7260
- if (wouldCreateIncludeExcludeCycle(ctx, p, receiverId, id)) {
7282
+ if (wouldCreateIncludeExcludeCycle(
7283
+ ctx,
7284
+ p,
7285
+ receiverId,
7286
+ id
7287
+ )) {
7261
7288
  ctx.emit("editor:error", {
7262
7289
  message: `Cycle detected: ${receiverId} excluding ${id} would create a cycle.`,
7263
7290
  code: "cycle_detected",
@@ -7273,7 +7300,8 @@ function exclude(ctx, receiverId, idOrIds) {
7273
7300
  accepted.push(id);
7274
7301
  }
7275
7302
  if (accepted.length > 0 || current.length > 0) {
7276
- if (!p.excludes_for_buttons) p.excludes_for_buttons = {};
7303
+ if (!p.excludes_for_buttons)
7304
+ p.excludes_for_buttons = {};
7277
7305
  p.excludes_for_buttons[receiverId] = Array.from(next);
7278
7306
  }
7279
7307
  if ((_g = p.includes_for_buttons) == null ? void 0 : _g[receiverId]) {
@@ -7288,7 +7316,9 @@ function exclude(ctx, receiverId, idOrIds) {
7288
7316
  if (!p.fields) p.fields = [];
7289
7317
  if (!p.filters) p.filters = [];
7290
7318
  } else {
7291
- throw new Error("Receiver must be a tag, button field, or option");
7319
+ throw new Error(
7320
+ "Receiver must be a tag, button field, or option"
7321
+ );
7292
7322
  }
7293
7323
  }),
7294
7324
  undo: () => ctx.undo()
@@ -7298,87 +7328,98 @@ function connect(ctx, kind, fromId, toId2) {
7298
7328
  ctx.exec({
7299
7329
  name: `connect:${kind}`,
7300
7330
  do: () => ctx.patchProps((p) => {
7301
- var _a, _b, _c, _d, _e;
7331
+ var _a, _b, _c, _d, _e, _f, _g, _h;
7302
7332
  if (kind === "bind") {
7303
7333
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
7304
7334
  if (wouldCreateTagCycle(ctx, p, fromId, toId2)) {
7305
- throw new Error(`bind would create a cycle: ${fromId} ? ${toId2}`);
7335
+ throw new Error(
7336
+ `bind would create a cycle: ${fromId} ? ${toId2}`
7337
+ );
7306
7338
  }
7307
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
7339
+ const child = ((_a = p.filters) != null ? _a : []).find(
7340
+ (t) => t.id === toId2
7341
+ );
7308
7342
  if (child) child.bind_id = fromId;
7309
7343
  return;
7310
7344
  }
7311
7345
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
7312
7346
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
7313
7347
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
7314
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
7348
+ const f = ((_b = p.fields) != null ? _b : []).find(
7349
+ (x) => x.id === fieldId
7350
+ );
7315
7351
  if (!f) return;
7316
7352
  if (!f.bind_id) {
7317
7353
  f.bind_id = tagId;
7318
7354
  return;
7319
7355
  }
7320
7356
  if (typeof f.bind_id === "string") {
7321
- if (f.bind_id !== tagId) f.bind_id = [f.bind_id, tagId];
7357
+ if (f.bind_id !== tagId) {
7358
+ f.bind_id = [f.bind_id, tagId];
7359
+ }
7322
7360
  return;
7323
7361
  }
7324
- if (!f.bind_id.includes(tagId)) f.bind_id.push(tagId);
7362
+ if (!f.bind_id.includes(tagId)) {
7363
+ f.bind_id.push(tagId);
7364
+ }
7325
7365
  return;
7326
7366
  }
7327
- throw new Error(`bind: unsupported route ${fromId} ? ${toId2}`);
7367
+ throw new Error(
7368
+ `bind: unsupported route ${fromId} ? ${toId2}`
7369
+ );
7328
7370
  }
7329
7371
  if (kind === "include" || kind === "exclude") {
7330
- const key = kind === "include" ? "includes" : "excludes";
7372
+ const tagKey = kind === "include" ? "includes" : "excludes";
7373
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
7331
7374
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
7332
- const t = ((_c = p.filters) != null ? _c : []).find((x) => x.id === fromId);
7375
+ const t = ((_c = p.filters) != null ? _c : []).find(
7376
+ (x) => x.id === fromId
7377
+ );
7333
7378
  if (!t) return;
7334
- const arr = (_d = t[key]) != null ? _d : t[key] = [];
7379
+ const arr = (_d = t[tagKey]) != null ? _d : t[tagKey] = [];
7335
7380
  if (!arr.includes(toId2)) arr.push(toId2);
7336
7381
  return;
7337
7382
  }
7383
+ if (ctx.isFieldId(fromId) && ctx.isFieldId(toId2)) {
7384
+ const source = ((_e = p.fields) != null ? _e : []).find(
7385
+ (x) => x.id === fromId
7386
+ );
7387
+ if (!(source == null ? void 0 : source.button)) {
7388
+ throw new Error(
7389
+ `${kind}: source field must be button=true: ${fromId} ? ${toId2}`
7390
+ );
7391
+ }
7392
+ addMappedField(p, mapKey, fromId, toId2);
7393
+ return;
7394
+ }
7338
7395
  if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
7339
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7340
- const maps = p[mapKey];
7341
- const next = { ...maps != null ? maps : {} };
7342
- const arr = (_e = next[fromId]) != null ? _e : [];
7343
- if (!arr.includes(toId2)) arr.push(toId2);
7344
- next[fromId] = arr;
7345
- p[mapKey] = next;
7396
+ addMappedField(p, mapKey, fromId, toId2);
7346
7397
  return;
7347
7398
  }
7348
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
7399
+ throw new Error(
7400
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
7401
+ );
7349
7402
  }
7350
7403
  if (kind === "service") {
7351
7404
  ensureServiceExists(ctx.opts, fromId);
7352
7405
  if (toId2.startsWith("t:")) {
7353
- ctx.exec({
7354
- name: "connect:service?tag",
7355
- do: () => ctx.patchProps((next) => {
7356
- var _a2;
7357
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
7358
- if (t) t.service_id = fromId;
7359
- }),
7360
- undo: () => ctx.undo()
7361
- });
7406
+ const t = ((_f = p.filters) != null ? _f : []).find((x) => x.id === toId2);
7407
+ if (t) t.service_id = fromId;
7362
7408
  return;
7363
7409
  }
7364
7410
  if (toId2.startsWith("o:")) {
7365
- ctx.exec({
7366
- name: "connect:service?option",
7367
- do: () => ctx.patchProps((next) => {
7368
- var _a2, _b2;
7369
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
7370
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
7371
- if (o) {
7372
- o.service_id = fromId;
7373
- return;
7374
- }
7375
- }
7376
- }),
7377
- undo: () => ctx.undo()
7378
- });
7411
+ for (const f of (_g = p.fields) != null ? _g : []) {
7412
+ const o = (_h = f.options) == null ? void 0 : _h.find((x) => x.id === toId2);
7413
+ if (o) {
7414
+ o.service_id = fromId;
7415
+ return;
7416
+ }
7417
+ }
7379
7418
  return;
7380
7419
  }
7381
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
7420
+ throw new Error(
7421
+ 'service: to must be a tag ("t:*") or option ("o:*")'
7422
+ );
7382
7423
  }
7383
7424
  throw new Error(`Unknown connect kind: ${kind}`);
7384
7425
  }),
@@ -7389,90 +7430,109 @@ function disconnect(ctx, kind, fromId, toId2) {
7389
7430
  ctx.exec({
7390
7431
  name: `disconnect:${kind}`,
7391
7432
  do: () => ctx.patchProps((p) => {
7392
- var _a, _b, _c, _d, _e, _f, _g, _h;
7433
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7393
7434
  if (kind === "bind") {
7394
7435
  if (ctx.isTagId(fromId) && ctx.isTagId(toId2)) {
7395
- const child = ((_a = p.filters) != null ? _a : []).find((t) => t.id === toId2);
7396
- if ((child == null ? void 0 : child.bind_id) === fromId) delete child.bind_id;
7436
+ const child = ((_a = p.filters) != null ? _a : []).find(
7437
+ (t) => t.id === toId2
7438
+ );
7439
+ if ((child == null ? void 0 : child.bind_id) === fromId) {
7440
+ delete child.bind_id;
7441
+ }
7397
7442
  return;
7398
7443
  }
7399
7444
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2) || ctx.isFieldId(fromId) && ctx.isTagId(toId2)) {
7400
7445
  const fieldId = ctx.isFieldId(toId2) ? toId2 : fromId;
7401
7446
  const tagId = ctx.isTagId(fromId) ? fromId : toId2;
7402
- const f = ((_b = p.fields) != null ? _b : []).find((x) => x.id === fieldId);
7447
+ const f = ((_b = p.fields) != null ? _b : []).find(
7448
+ (x) => x.id === fieldId
7449
+ );
7403
7450
  if (!(f == null ? void 0 : f.bind_id)) return;
7404
7451
  if (typeof f.bind_id === "string") {
7405
- if (f.bind_id === tagId) delete f.bind_id;
7452
+ if (f.bind_id === tagId) {
7453
+ delete f.bind_id;
7454
+ }
7406
7455
  return;
7407
7456
  }
7408
7457
  f.bind_id = f.bind_id.filter((x) => x !== tagId);
7409
- if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) delete f.bind_id;
7458
+ if (((_c = f.bind_id) == null ? void 0 : _c.length) === 0) {
7459
+ delete f.bind_id;
7460
+ }
7410
7461
  return;
7411
7462
  }
7412
- throw new Error(`unbind: unsupported route ${fromId} ? ${toId2}`);
7463
+ throw new Error(
7464
+ `unbind: unsupported route ${fromId} ? ${toId2}`
7465
+ );
7413
7466
  }
7414
7467
  if (kind === "include" || kind === "exclude") {
7415
- const key = kind === "include" ? "includes" : "excludes";
7468
+ const tagKey = kind === "include" ? "includes" : "excludes";
7469
+ const mapKey = kind === "include" ? "includes_for_buttons" : "excludes_for_buttons";
7416
7470
  if (ctx.isTagId(fromId) && ctx.isFieldId(toId2)) {
7417
- const t = ((_d = p.filters) != null ? _d : []).find((x) => x.id === fromId);
7471
+ const t = ((_d = p.filters) != null ? _d : []).find(
7472
+ (x) => x.id === fromId
7473
+ );
7418
7474
  if (!t) return;
7419
- t[key] = ((_e = t[key]) != null ? _e : []).filter((x) => x !== toId2);
7420
- if (!((_f = t[key]) == null ? void 0 : _f.length)) delete t[key];
7475
+ t[tagKey] = ((_e = t[tagKey]) != null ? _e : []).filter((x) => x !== toId2);
7476
+ if (!((_f = t[tagKey]) == null ? void 0 : _f.length)) {
7477
+ delete t[tagKey];
7478
+ }
7421
7479
  return;
7422
7480
  }
7423
- if (ctx.isOptionId(fromId) && ctx.isFieldId(toId2)) {
7424
- const mapKey = kind === "include" ? "includes_for_options" : "excludes_for_options";
7481
+ if ((ctx.isFieldId(fromId) || ctx.isOptionId(fromId)) && ctx.isFieldId(toId2)) {
7425
7482
  const maps = p[mapKey];
7426
- if (!maps) return;
7427
- if (maps[fromId]) {
7428
- maps[fromId] = ((_g = maps[fromId]) != null ? _g : []).filter(
7429
- (fid) => fid !== toId2
7430
- );
7431
- if (!((_h = maps[fromId]) == null ? void 0 : _h.length)) delete maps[fromId];
7483
+ if (!(maps == null ? void 0 : maps[fromId])) return;
7484
+ maps[fromId] = maps[fromId].filter(
7485
+ (fid) => fid !== toId2
7486
+ );
7487
+ if (!((_g = maps[fromId]) == null ? void 0 : _g.length)) {
7488
+ delete maps[fromId];
7489
+ }
7490
+ if (!Object.keys(maps).length) {
7491
+ delete p[mapKey];
7432
7492
  }
7433
- if (!Object.keys(maps).length) delete p[mapKey];
7434
7493
  return;
7435
7494
  }
7436
- throw new Error(`${kind}: unsupported route ${fromId} ? ${toId2}`);
7495
+ throw new Error(
7496
+ `${kind}: unsupported route ${fromId} ? ${toId2}`
7497
+ );
7437
7498
  }
7438
7499
  if (kind === "service") {
7439
7500
  ensureServiceExists(ctx.opts, fromId);
7440
7501
  if (toId2.startsWith("t:")) {
7441
- ctx.exec({
7442
- name: "disconnect:service?tag",
7443
- do: () => ctx.patchProps((next) => {
7444
- var _a2;
7445
- const t = ((_a2 = next.filters) != null ? _a2 : []).find((x) => x.id === toId2);
7446
- if (t) delete t.service_id;
7447
- }),
7448
- undo: () => ctx.undo()
7449
- });
7502
+ const t = ((_h = p.filters) != null ? _h : []).find((x) => x.id === toId2);
7503
+ if (t) {
7504
+ delete t.service_id;
7505
+ }
7450
7506
  return;
7451
7507
  }
7452
7508
  if (toId2.startsWith("o:")) {
7453
- ctx.exec({
7454
- name: "disconnect:service?option",
7455
- do: () => ctx.patchProps((next) => {
7456
- var _a2, _b2;
7457
- for (const f of (_a2 = next.fields) != null ? _a2 : []) {
7458
- const o = (_b2 = f.options) == null ? void 0 : _b2.find((x) => x.id === toId2);
7459
- if (o) {
7460
- delete o.service_id;
7461
- return;
7462
- }
7463
- }
7464
- }),
7465
- undo: () => ctx.undo()
7466
- });
7509
+ for (const f of (_i = p.fields) != null ? _i : []) {
7510
+ const o = (_j = f.options) == null ? void 0 : _j.find((x) => x.id === toId2);
7511
+ if (o) {
7512
+ delete o.service_id;
7513
+ return;
7514
+ }
7515
+ }
7467
7516
  return;
7468
7517
  }
7469
- throw new Error('service: to must be a tag ("t:*") or option ("o:*")');
7518
+ throw new Error(
7519
+ 'service: to must be a tag ("t:*") or option ("o:*")'
7520
+ );
7470
7521
  }
7471
7522
  throw new Error(`Unknown disconnect kind: ${kind}`);
7472
7523
  }),
7473
7524
  undo: () => ctx.undo()
7474
7525
  });
7475
7526
  }
7527
+ function addMappedField(p, mapKey, fromId, toId2) {
7528
+ var _a, _b;
7529
+ const maps = (_a = p[mapKey]) != null ? _a : {};
7530
+ const arr = (_b = maps[fromId]) != null ? _b : [];
7531
+ if (!arr.includes(toId2)) {
7532
+ maps[fromId] = [...arr, toId2];
7533
+ }
7534
+ p[mapKey] = maps;
7535
+ }
7476
7536
 
7477
7537
  // src/react/canvas/editor/editor-service-filter.ts
7478
7538
  function filterServicesForVisibleGroup2(ctx, candidates, input) {