@nocobase/plugin-flow-engine 2.1.0-alpha.40 → 2.1.0-alpha.45

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.
Files changed (32) hide show
  1. package/dist/externalVersion.js +9 -9
  2. package/dist/node_modules/@ant-design/icons-svg/package.json +1 -1
  3. package/dist/node_modules/acorn/package.json +1 -1
  4. package/dist/node_modules/acorn-jsx/package.json +1 -1
  5. package/dist/node_modules/acorn-walk/package.json +1 -1
  6. package/dist/node_modules/ses/package.json +1 -1
  7. package/dist/node_modules/zod/package.json +1 -1
  8. package/dist/server/flow-surfaces/apply/compiler.js +18 -3
  9. package/dist/server/flow-surfaces/apply/matching.js +2 -0
  10. package/dist/server/flow-surfaces/authoring-validation.js +621 -66
  11. package/dist/server/flow-surfaces/blueprint/compile-blocks.js +21 -3
  12. package/dist/server/flow-surfaces/blueprint/compile-plan.js +9 -9
  13. package/dist/server/flow-surfaces/catalog.js +17 -4
  14. package/dist/server/flow-surfaces/chart-config.js +14 -1
  15. package/dist/server/flow-surfaces/compose-compiler.d.ts +2 -0
  16. package/dist/server/flow-surfaces/compose-compiler.js +2 -0
  17. package/dist/server/flow-surfaces/compose-runtime.js +4 -7
  18. package/dist/server/flow-surfaces/configure-options.js +19 -8
  19. package/dist/server/flow-surfaces/locator.js +16 -2
  20. package/dist/server/flow-surfaces/runjs-authoring/ctx-libs-member-mismatch-stop/index.js +1 -1
  21. package/dist/server/flow-surfaces/runjs-authoring/index.js +6224 -2119
  22. package/dist/server/flow-surfaces/service-utils.d.ts +2 -0
  23. package/dist/server/flow-surfaces/service-utils.js +8 -0
  24. package/dist/server/flow-surfaces/service.d.ts +67 -14
  25. package/dist/server/flow-surfaces/service.js +1316 -122
  26. package/dist/server/flow-surfaces/template-service-utils.d.ts +1 -0
  27. package/dist/swagger/flow-surfaces.d.ts +5 -2
  28. package/dist/swagger/flow-surfaces.examples.d.ts +11 -37
  29. package/dist/swagger/flow-surfaces.examples.js +6 -6
  30. package/dist/swagger/flow-surfaces.js +6 -3
  31. package/dist/swagger/index.d.ts +5 -2
  32. package/package.json +2 -2
@@ -59,6 +59,7 @@ var import_template_reference = require("./template-reference");
59
59
  var import_association_interfaces = require("./association-interfaces");
60
60
  var import_defaults = require("./blueprint/defaults");
61
61
  var import_runjs_authoring = require("./runjs-authoring");
62
+ var import_configure_options = require("./configure-options");
62
63
  const MAIN_BLOCK_UNSUPPORTED_SECTIONS = {
63
64
  calendar: ["fields", "fieldGroups", "recordActions", "fieldsLayout"],
64
65
  kanban: ["fieldGroups", "recordActions", "fieldsLayout"],
@@ -71,6 +72,55 @@ const SORTABLE_BLOCK_TYPES = /* @__PURE__ */ new Set(["table", "details", "list"
71
72
  const CHART_BLOCK_TYPES = /* @__PURE__ */ new Set(["chart"]);
72
73
  const COMMENTS_PAGE_SIZE_VALUES = /* @__PURE__ */ new Set([5, 10, 20, 50, 100, 200]);
73
74
  const RECORD_HISTORY_INTERNAL_COLLECTIONS = /* @__PURE__ */ new Set(["recordHistories", "recordFieldHistories"]);
75
+ const VISIBLE_FIELD_REQUIRED_DATA_BLOCK_TYPES = /* @__PURE__ */ new Set([
76
+ "table",
77
+ "list",
78
+ "gridCard",
79
+ "details",
80
+ "createForm",
81
+ "editForm",
82
+ "filterForm",
83
+ "kanban"
84
+ ]);
85
+ const VISIBLE_FIELD_MINIMUM_DATA_BLOCK_TYPES = /* @__PURE__ */ new Set(["table", "list", "gridCard", "details"]);
86
+ const RICH_COLLECTION_VISIBLE_FIELD_THRESHOLD = import_public_data_surface_default_filter.FLOW_SURFACE_DEFAULT_FILTER_REQUIRED_FIELD_COUNT * 2;
87
+ const RICH_DATA_BLOCK_VISIBLE_FIELD_MINIMUM = 3;
88
+ const NON_BUSINESS_VISIBLE_FIELD_NAMES = /* @__PURE__ */ new Set([
89
+ "id",
90
+ "uid",
91
+ "createdAt",
92
+ "updatedAt",
93
+ "deletedAt",
94
+ "createdBy",
95
+ "updatedBy",
96
+ "deletedBy",
97
+ "createdById",
98
+ "updatedById",
99
+ "deletedById",
100
+ "created_at",
101
+ "updated_at",
102
+ "deleted_at",
103
+ "created_by",
104
+ "updated_by",
105
+ "deleted_by"
106
+ ]);
107
+ const NON_BUSINESS_VISIBLE_FIELD_INTERFACES = /* @__PURE__ */ new Set([
108
+ "id",
109
+ "createdAt",
110
+ "updatedAt",
111
+ "createdBy",
112
+ "updatedBy",
113
+ "sort"
114
+ ]);
115
+ const NON_BUSINESS_VISIBLE_FIELD_TYPES = /* @__PURE__ */ new Set([
116
+ "action",
117
+ "actions",
118
+ "button",
119
+ "divider",
120
+ "operation",
121
+ "operations",
122
+ "sort"
123
+ ]);
74
124
  const ANT_DESIGN_ICON_NAMES = new Set(Object.keys(antDesignIconAsn || {}));
75
125
  const PUBLIC_BLOCK_TYPE_BY_MODEL_USE = {
76
126
  TableBlockModel: "table",
@@ -106,9 +156,14 @@ const GRID_CARD_ALLOWED_SETTINGS_KEYS = /* @__PURE__ */ new Set([
106
156
  "sorting",
107
157
  "layout"
108
158
  ]);
159
+ const TABLE_ALLOWED_SETTINGS_KEYS = /* @__PURE__ */ new Set([...(0, import_configure_options.getConfigureOptionKeysForUse)("TableBlockModel"), "sort"]);
160
+ const TABLE_INTERNAL_AUTHORING_KEYS = ["tableSettings", "defaultSorting", "stepParams"];
161
+ const TABLE_SETTINGS_REPAIR_HINT = "Use public table settings keys such as settings.pageSize, settings.sorting, settings.dataScope, settings.density, settings.showRowNumbers, settings.treeTable, settings.dragSort, and settings.dragSortBy. Do not nest persisted tableSettings/defaultSorting/stepParams payloads.";
109
162
  const JS_BLOCK_ALLOWED_SETTINGS_KEYS = /* @__PURE__ */ new Set(["title", "description", "className", "code", "version"]);
110
163
  const JS_BLOCK_TOP_LEVEL_JS_KEYS = ["code", "version"];
111
164
  const JS_BLOCK_INTERNAL_AUTHORING_KEYS = ["props", "decoratorProps", "flowRegistry", "stepParams"];
165
+ const JS_BLOCK_REPAIR_HINT = "This is a jsBlock payload shape problem. Repair this jsBlock using inline settings.code/settings.version, or applyBlueprint assets.scripts.<key>.code plus block.script. Do not change this block type to table, chart, actionPanel, gridCard, or another block type.";
166
+ const CHART_REPAIR_HINT = "This is a chart payload shape problem. Repair this chart using assets.charts.<key>.query/visual plus block.chart, or localized settings.query/settings.visual. Do not change this block type to table, jsBlock, actionPanel, gridCard, or another block type, and do not drop or defer the chart. KPI / summary numbers should use jsBlock; charts are for trends, distributions, rankings, and visual analysis.";
112
167
  const CHART_QUERY_MODE_SET = new Set(import_chart_config.CHART_QUERY_MODES);
113
168
  const CHART_VISUAL_MODE_SET = new Set(import_chart_config.CHART_VISUAL_MODES);
114
169
  const CHART_BASIC_VISUAL_TYPE_SET = new Set(import_chart_config.CHART_BASIC_VISUAL_TYPES);
@@ -264,6 +319,67 @@ const DEFAULT_POPUPS_ALLOWED_KEYS = ["view", "addNew", "edit", "associations"];
264
319
  const DEFAULT_POPUP_ACTION_ALLOWED_KEYS = ["name", "description"];
265
320
  const DEFAULT_POPUP_ASSOCIATION_ALLOWED_KEYS = ["view", "addNew", "edit"];
266
321
  const DEFAULT_POPUP_ACTIONS = ["view", "addNew", "edit"];
322
+ const AUTHORING_AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS = ["type", "uid", "target"];
323
+ const AUTHORING_AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS = [
324
+ "title",
325
+ "message",
326
+ "autoSend",
327
+ "skillSettings",
328
+ "model",
329
+ "webSearch"
330
+ ];
331
+ const AUTHORING_AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS = ["system", "user", "workContext"];
332
+ const AUTHORING_AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS = ["llmService", "model"];
333
+ const AUTHORING_AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS = ["skills", "tools", "skillsVersion", "toolsVersion"];
334
+ const AUTHORING_AI_EMPLOYEE_STYLE_PUBLIC_KEYS = ["size", "mask"];
335
+ const AUTHORING_AI_EMPLOYEE_DEFAULT_STYLE = {
336
+ size: 40,
337
+ mask: false
338
+ };
339
+ function buildCalendarMainBlockRepairDetails(section) {
340
+ const popupSectionHint = section === "fieldGroups" || section === "recordActions" || section === "fieldsLayout" ? " Put event form/details content under settings.quickCreatePopup or settings.eventPopup instead of on the calendar main block." : "";
341
+ return {
342
+ repairHint: "Calendar main block payload shape issue. Put event bindings under settings.titleField, settings.startField, settings.endField, and optional settings.colorField; do not put collection fields in block fields. Keep block type calendar and repair this payload." + popupSectionHint,
343
+ example: {
344
+ type: "calendar",
345
+ collection: "tasks",
346
+ settings: {
347
+ titleField: "title",
348
+ startField: "startAt",
349
+ endField: "endAt"
350
+ }
351
+ }
352
+ };
353
+ }
354
+ function buildKanbanMainBlockRepairDetails(section) {
355
+ return {
356
+ repairHint: "Kanban main block payload shape issue. Put card display fields in block fields, grouping in settings.groupField, and quick-create/card details under settings.quickCreatePopup or settings.cardPopup. Keep block type kanban and repair this payload.",
357
+ section,
358
+ example: {
359
+ type: "kanban",
360
+ collection: "tasks",
361
+ fields: ["title", "priority"],
362
+ settings: {
363
+ groupField: "status"
364
+ }
365
+ }
366
+ };
367
+ }
368
+ function buildTwoColumnLayoutRepairDetails() {
369
+ return {
370
+ repairHint: "When multiple non-filter blocks should share a row, put them in the same layout row. Do not place every block on a separate row.",
371
+ example: {
372
+ layout: {
373
+ rows: [
374
+ [
375
+ { key: "calendar", span: 12 },
376
+ { key: "kanban", span: 12 }
377
+ ]
378
+ ]
379
+ }
380
+ }
381
+ };
382
+ }
267
383
  async function assertFlowSurfaceAuthoringPayload(actionName, values, context = {}) {
268
384
  const errors = await collectFlowSurfaceAuthoringErrors(actionName, values, context);
269
385
  if (errors.length) {
@@ -329,38 +445,43 @@ async function collectNavigationGroupErrors(actionName, values, context, errors)
329
445
  return;
330
446
  }
331
447
  const matchedRoutes = await context.findMenuGroupRoutesByTitle(groupTitle, context.transaction);
332
- if (matchedRoutes.length <= 1) {
448
+ const rootMatchedRoutes = filterRootMenuGroupRoutes(matchedRoutes);
449
+ if (rootMatchedRoutes.length <= 1) {
333
450
  return;
334
451
  }
335
452
  pushAuthoringError(errors, {
336
453
  path: "$.navigation.group.title",
337
454
  ruleId: "navigation-group-title-ambiguous",
338
- message: `flowSurfaces authoring $.navigation.group.title '${groupTitle}' matches ${matchedRoutes.length} existing menu groups; pass navigation.group.routeId explicitly`,
455
+ message: `flowSurfaces authoring $.navigation.group.title '${groupTitle}' matches ${rootMatchedRoutes.length} existing root menu groups; pass navigation.group.routeId explicitly`,
339
456
  details: {
340
457
  title: groupTitle,
341
- matches: matchedRoutes.length
458
+ parentMenuRouteId: null,
459
+ matches: rootMatchedRoutes.length
342
460
  }
343
461
  });
344
462
  }
345
463
  async function collectNavigationIconErrors(actionName, values, context, errors) {
346
- var _a, _b;
464
+ var _a, _b, _c;
347
465
  if (actionName !== "applyBlueprint" || (values == null ? void 0 : values.mode) !== "create") {
348
466
  return;
349
467
  }
350
468
  const group = _.isPlainObject((_a = values == null ? void 0 : values.navigation) == null ? void 0 : _a.group) ? values.navigation.group : null;
351
469
  const groupRouteId = String((group == null ? void 0 : group.routeId) || "").trim();
352
- if (group && !groupRouteId) {
470
+ if (group && !groupRouteId && group.hideInMenu !== true) {
353
471
  const groupIcon = String(group.icon || "").trim();
354
472
  if (!groupIcon && await shouldRequireNewNavigationGroupIcon(group, context)) {
355
473
  pushAuthoringError(errors, {
356
474
  path: "$.navigation.group.icon",
357
- ruleId: "missing-menu-group-icon",
358
- message: "flowSurfaces authoring $.navigation.group.icon is required when creating a new navigation group"
475
+ ruleId: "navigation-icon-required",
476
+ message: "flowSurfaces authoring $.navigation.group.icon is required when creating a visible navigation group",
477
+ details: {
478
+ repairHint: "Pass a valid Ant Design icon name such as AppstoreOutlined, DashboardOutlined, or FolderOpenOutlined."
479
+ }
359
480
  });
360
481
  } else if (groupIcon && !isValidAntDesignIconName(groupIcon)) {
361
482
  pushAuthoringError(errors, {
362
483
  path: "$.navigation.group.icon",
363
- ruleId: "invalid-menu-group-icon",
484
+ ruleId: "navigation-icon-unknown",
364
485
  message: "flowSurfaces authoring $.navigation.group.icon must be a valid Ant Design icon name",
365
486
  details: {
366
487
  icon: groupIcon
@@ -370,7 +491,7 @@ async function collectNavigationIconErrors(actionName, values, context, errors)
370
491
  } else if (group && String(group.icon || "").trim() && !isValidAntDesignIconName(group.icon)) {
371
492
  pushAuthoringError(errors, {
372
493
  path: "$.navigation.group.icon",
373
- ruleId: "invalid-menu-group-icon",
494
+ ruleId: "navigation-icon-unknown",
374
495
  message: "flowSurfaces authoring $.navigation.group.icon must be a valid Ant Design icon name",
375
496
  details: {
376
497
  icon: String(group.icon || "").trim()
@@ -382,13 +503,24 @@ async function collectNavigationIconErrors(actionName, values, context, errors)
382
503
  if (itemIcon && !isValidAntDesignIconName(itemIcon)) {
383
504
  pushAuthoringError(errors, {
384
505
  path: "$.navigation.item.icon",
385
- ruleId: "invalid-menu-item-icon",
506
+ ruleId: "navigation-icon-unknown",
386
507
  message: "flowSurfaces authoring $.navigation.item.icon must be a valid Ant Design icon name",
387
508
  details: {
388
509
  icon: itemIcon
389
510
  }
390
511
  });
391
512
  }
513
+ const pageIcon = String(((_c = values == null ? void 0 : values.page) == null ? void 0 : _c.icon) || "").trim();
514
+ if (pageIcon && !isValidAntDesignIconName(pageIcon)) {
515
+ pushAuthoringError(errors, {
516
+ path: "$.page.icon",
517
+ ruleId: "navigation-icon-unknown",
518
+ message: "flowSurfaces authoring $.page.icon must be a valid Ant Design icon name when used as the create-mode page route icon",
519
+ details: {
520
+ icon: pageIcon
521
+ }
522
+ });
523
+ }
392
524
  }
393
525
  async function shouldRequireNewNavigationGroupIcon(group, context) {
394
526
  const groupTitle = String((group == null ? void 0 : group.title) || "").trim();
@@ -396,7 +528,22 @@ async function shouldRequireNewNavigationGroupIcon(group, context) {
396
528
  return true;
397
529
  }
398
530
  const matchedRoutes = await context.findMenuGroupRoutesByTitle(groupTitle, context.transaction);
399
- return matchedRoutes.length === 0;
531
+ return filterRootMenuGroupRoutes(matchedRoutes).length === 0;
532
+ }
533
+ function filterRootMenuGroupRoutes(routes) {
534
+ return _.castArray(routes || []).filter(
535
+ (route) => routeParentIdMatches(readAuthoringRouteField(route, "parentId"), null)
536
+ );
537
+ }
538
+ function readAuthoringRouteField(route, key) {
539
+ var _a;
540
+ return ((_a = route == null ? void 0 : route.get) == null ? void 0 : _a.call(route, key)) ?? (route == null ? void 0 : route[key]);
541
+ }
542
+ function routeParentIdMatches(routeParentId, parentId) {
543
+ if (_.isNil(routeParentId) && _.isNil(parentId)) {
544
+ return true;
545
+ }
546
+ return String(routeParentId ?? "") === String(parentId ?? "");
400
547
  }
401
548
  function isValidAntDesignIconName(value) {
402
549
  const normalized = String(value || "").trim();
@@ -494,6 +641,39 @@ function collectChartAssetBlockTreeErrors(block, path, chartAssets, errors) {
494
641
  );
495
642
  });
496
643
  }
644
+ function withJsBlockRepairHint(details = {}) {
645
+ return {
646
+ ...details,
647
+ repairHint: JS_BLOCK_REPAIR_HINT
648
+ };
649
+ }
650
+ function withChartRepairHint(details = {}) {
651
+ return {
652
+ ...details,
653
+ repairHint: CHART_REPAIR_HINT,
654
+ repairSteps: [
655
+ "Keep the block type as chart.",
656
+ "Define assets.charts.<key>.query and assets.charts.<key>.visual.",
657
+ "Reference that asset from the chart block with block.chart = <key>.",
658
+ "Retry the chart payload instead of replacing the chart with another block type or omitting it."
659
+ ],
660
+ expectedShape: {
661
+ assets: {
662
+ charts: {
663
+ chartKey: {
664
+ query: "builder/sql query configuration",
665
+ visual: "basic/custom visual configuration"
666
+ }
667
+ }
668
+ },
669
+ block: {
670
+ type: "chart",
671
+ chart: "chartKey"
672
+ }
673
+ },
674
+ forbiddenFallbacks: ["table", "jsBlock", "actionPanel", "gridCard", "drop chart", "defer chart"]
675
+ };
676
+ }
497
677
  function collectChartBlockAssetReferenceErrors(block, path, chartAssets, errors) {
498
678
  if (!CHART_BLOCK_TYPES.has(String((block == null ? void 0 : block.type) || "").trim())) {
499
679
  return;
@@ -502,7 +682,8 @@ function collectChartBlockAssetReferenceErrors(block, path, chartAssets, errors)
502
682
  pushAuthoringError(errors, {
503
683
  path: `${path}.stepParams`,
504
684
  ruleId: "chart-block-step-params-unsupported",
505
- message: `flowSurfaces authoring ${path}.stepParams is not accepted on public chart blocks; put chart configuration under assets.charts and reference it with block.chart`
685
+ message: `flowSurfaces authoring ${path}.stepParams is not accepted on public chart blocks; put chart configuration under assets.charts and reference it with block.chart`,
686
+ details: withChartRepairHint()
506
687
  });
507
688
  }
508
689
  const chartKey = String(block.chart || "").trim();
@@ -510,7 +691,8 @@ function collectChartBlockAssetReferenceErrors(block, path, chartAssets, errors)
510
691
  pushAuthoringError(errors, {
511
692
  path: `${path}.chart`,
512
693
  ruleId: "chart-block-asset-reference-required",
513
- message: `flowSurfaces authoring ${path}.chart must reference one key from assets.charts`
694
+ message: `flowSurfaces authoring ${path}.chart must reference one key from assets.charts`,
695
+ details: withChartRepairHint()
514
696
  });
515
697
  return;
516
698
  }
@@ -519,9 +701,9 @@ function collectChartBlockAssetReferenceErrors(block, path, chartAssets, errors)
519
701
  path: `${path}.chart`,
520
702
  ruleId: "chart-block-asset-reference-missing",
521
703
  message: `flowSurfaces authoring ${path}.chart references missing chart asset '${chartKey}'`,
522
- details: {
704
+ details: withChartRepairHint({
523
705
  chartKey
524
- }
706
+ })
525
707
  });
526
708
  }
527
709
  }
@@ -535,7 +717,8 @@ function collectChartAssetRegistryErrors(charts, path, context, errors) {
535
717
  pushAuthoringError(errors, {
536
718
  path: assetPath,
537
719
  ruleId: "chart-asset-invalid",
538
- message: `flowSurfaces authoring ${assetPath} must be an object`
720
+ message: `flowSurfaces authoring ${assetPath} must be an object`,
721
+ details: withChartRepairHint()
539
722
  });
540
723
  return;
541
724
  }
@@ -549,7 +732,8 @@ function collectChartAssetQueryErrors(asset, path, context, errors) {
549
732
  pushAuthoringError(errors, {
550
733
  path: `${path}.query`,
551
734
  ruleId: "chart-query-missing",
552
- message: `flowSurfaces authoring ${path}.query is required`
735
+ message: `flowSurfaces authoring ${path}.query is required`,
736
+ details: withChartRepairHint()
553
737
  });
554
738
  return;
555
739
  }
@@ -559,9 +743,9 @@ function collectChartAssetQueryErrors(asset, path, context, errors) {
559
743
  path: `${path}.query.mode`,
560
744
  ruleId: "chart-query-mode-unsupported",
561
745
  message: `flowSurfaces authoring ${path}.query.mode '${mode}' is not supported`,
562
- details: {
746
+ details: withChartRepairHint({
563
747
  mode
564
- }
748
+ })
565
749
  });
566
750
  return;
567
751
  }
@@ -577,14 +761,16 @@ function collectBuilderChartAssetQueryErrors(query, path, context, errors) {
577
761
  pushAuthoringError(errors, {
578
762
  path: `${path}.query.resource`,
579
763
  ruleId: "chart-builder-query-resource-missing",
580
- message: `flowSurfaces authoring ${path}.query.resource.collectionName is required for builder chart assets`
764
+ message: `flowSurfaces authoring ${path}.query.resource.collectionName is required for builder chart assets`,
765
+ details: withChartRepairHint()
581
766
  });
582
767
  }
583
768
  if (!Array.isArray(query.measures) || !query.measures.length) {
584
769
  pushAuthoringError(errors, {
585
770
  path: `${path}.query.measures`,
586
771
  ruleId: "chart-builder-query-measures-missing",
587
- message: `flowSurfaces authoring ${path}.query.measures must include at least one measure`
772
+ message: `flowSurfaces authoring ${path}.query.measures must include at least one measure`,
773
+ details: withChartRepairHint()
588
774
  });
589
775
  }
590
776
  collectForbiddenObjectKeyErrors(
@@ -592,7 +778,8 @@ function collectBuilderChartAssetQueryErrors(query, path, context, errors) {
592
778
  `${path}.query`,
593
779
  CHART_BUILDER_QUERY_FORBIDDEN_KEYS,
594
780
  "chart-builder-query-forbidden-keys",
595
- errors
781
+ errors,
782
+ withChartRepairHint()
596
783
  );
597
784
  collectBuilderChartAssetFieldErrors(query, path, context, errors);
598
785
  }
@@ -640,11 +827,11 @@ function collectBuilderChartAssetFieldErrors(query, path, context, errors) {
640
827
  path: item.fieldPath,
641
828
  ruleId: "chart-builder-query-field-unknown",
642
829
  message: `flowSurfaces authoring ${item.fieldPath} references unknown field '${fieldPath}' on collection '${dataSourceKey}.${collectionName}'`,
643
- details: {
830
+ details: withChartRepairHint({
644
831
  fieldPath,
645
832
  dataSourceKey,
646
833
  collectionName
647
- }
834
+ })
648
835
  });
649
836
  continue;
650
837
  }
@@ -659,12 +846,12 @@ function collectBuilderChartAssetFieldErrors(query, path, context, errors) {
659
846
  path: item.fieldPath,
660
847
  ruleId: "chart-builder-query-association-field-requires-subfield",
661
848
  message: `flowSurfaces authoring ${item.fieldPath} references association field '${fieldPath}' directly; use scalar subfield '${suggestion.suggestedFieldPath}' for builder charts`,
662
- details: {
849
+ details: withChartRepairHint({
663
850
  fieldPath,
664
851
  dataSourceKey,
665
852
  collectionName,
666
853
  ...suggestion
667
- }
854
+ })
668
855
  });
669
856
  }
670
857
  }
@@ -691,7 +878,8 @@ function collectSqlChartAssetQueryErrors(query, path, errors) {
691
878
  pushAuthoringError(errors, {
692
879
  path: `${path}.query.sql`,
693
880
  ruleId: "chart-sql-query-text-missing",
694
- message: `flowSurfaces authoring ${path}.query.sql must be a non-empty string`
881
+ message: `flowSurfaces authoring ${path}.query.sql must be a non-empty string`,
882
+ details: withChartRepairHint()
695
883
  });
696
884
  }
697
885
  collectForbiddenObjectKeyErrors(
@@ -699,7 +887,8 @@ function collectSqlChartAssetQueryErrors(query, path, errors) {
699
887
  `${path}.query`,
700
888
  CHART_SQL_QUERY_FORBIDDEN_KEYS,
701
889
  "chart-sql-query-forbidden-builder-keys",
702
- errors
890
+ errors,
891
+ withChartRepairHint()
703
892
  );
704
893
  }
705
894
  function collectChartAssetVisualErrors(asset, path, errors) {
@@ -708,7 +897,8 @@ function collectChartAssetVisualErrors(asset, path, errors) {
708
897
  pushAuthoringError(errors, {
709
898
  path: `${path}.visual`,
710
899
  ruleId: "chart-visual-missing",
711
- message: `flowSurfaces authoring ${path}.visual is required`
900
+ message: `flowSurfaces authoring ${path}.visual is required`,
901
+ details: withChartRepairHint()
712
902
  });
713
903
  return;
714
904
  }
@@ -717,7 +907,8 @@ function collectChartAssetVisualErrors(asset, path, errors) {
717
907
  `${path}.visual`,
718
908
  CHART_VISUAL_LEGACY_BUILDER_KEYS,
719
909
  "chart-visual-legacy-builder-keys-unsupported",
720
- errors
910
+ errors,
911
+ withChartRepairHint()
721
912
  );
722
913
  const mode = String(visual.mode || "basic").trim();
723
914
  if (!CHART_VISUAL_MODE_SET.has(mode)) {
@@ -725,9 +916,9 @@ function collectChartAssetVisualErrors(asset, path, errors) {
725
916
  path: `${path}.visual.mode`,
726
917
  ruleId: "chart-visual-mode-unsupported",
727
918
  message: `flowSurfaces authoring ${path}.visual.mode '${mode}' is not supported`,
728
- details: {
919
+ details: withChartRepairHint({
729
920
  mode
730
- }
921
+ })
731
922
  });
732
923
  return;
733
924
  }
@@ -736,7 +927,8 @@ function collectChartAssetVisualErrors(asset, path, errors) {
736
927
  pushAuthoringError(errors, {
737
928
  path: `${path}.visual.raw`,
738
929
  ruleId: "chart-custom-visual-raw-missing",
739
- message: `flowSurfaces authoring ${path}.visual.raw is required for custom chart assets`
930
+ message: `flowSurfaces authoring ${path}.visual.raw is required for custom chart assets`,
931
+ details: withChartRepairHint()
740
932
  });
741
933
  }
742
934
  collectForbiddenObjectKeyErrors(
@@ -744,7 +936,8 @@ function collectChartAssetVisualErrors(asset, path, errors) {
744
936
  `${path}.visual`,
745
937
  CHART_CUSTOM_VISUAL_FORBIDDEN_KEYS,
746
938
  "chart-custom-visual-public-keys-unsupported",
747
- errors
939
+ errors,
940
+ withChartRepairHint()
748
941
  );
749
942
  return;
750
943
  }
@@ -753,23 +946,25 @@ function collectChartAssetVisualErrors(asset, path, errors) {
753
946
  pushAuthoringError(errors, {
754
947
  path: `${path}.visual.type`,
755
948
  ruleId: "chart-visual-type-missing",
756
- message: `flowSurfaces authoring ${path}.visual.type is required for basic chart assets`
949
+ message: `flowSurfaces authoring ${path}.visual.type is required for basic chart assets`,
950
+ details: withChartRepairHint()
757
951
  });
758
952
  } else if (!CHART_BASIC_VISUAL_TYPE_SET.has(type)) {
759
953
  pushAuthoringError(errors, {
760
954
  path: `${path}.visual.type`,
761
955
  ruleId: "chart-visual-type-unsupported",
762
956
  message: `flowSurfaces authoring ${path}.visual.type '${type}' is not supported`,
763
- details: {
957
+ details: withChartRepairHint({
764
958
  type
765
- }
959
+ })
766
960
  });
767
961
  }
768
962
  if (!_.isPlainObject(visual.mappings)) {
769
963
  pushAuthoringError(errors, {
770
964
  path: `${path}.visual.mappings`,
771
965
  ruleId: "chart-visual-mappings-missing",
772
- message: `flowSurfaces authoring ${path}.visual.mappings is required for basic chart assets`
966
+ message: `flowSurfaces authoring ${path}.visual.mappings is required for basic chart assets`,
967
+ details: withChartRepairHint()
773
968
  });
774
969
  return;
775
970
  }
@@ -781,14 +976,14 @@ function collectChartAssetVisualErrors(asset, path, errors) {
781
976
  path: `${path}.visual.mappings`,
782
977
  ruleId: "chart-visual-required-mappings-missing",
783
978
  message: `flowSurfaces authoring ${path}.visual.mappings is missing required keys: ${missingMappings.join(", ")}`,
784
- details: {
979
+ details: withChartRepairHint({
785
980
  type,
786
981
  missingMappings
787
- }
982
+ })
788
983
  });
789
984
  }
790
985
  }
791
- function collectForbiddenObjectKeyErrors(value, path, forbiddenKeys, ruleId, errors) {
986
+ function collectForbiddenObjectKeyErrors(value, path, forbiddenKeys, ruleId, errors, details = {}) {
792
987
  if (!_.isPlainObject(value)) {
793
988
  return;
794
989
  }
@@ -801,6 +996,7 @@ function collectForbiddenObjectKeyErrors(value, path, forbiddenKeys, ruleId, err
801
996
  ruleId,
802
997
  message: `flowSurfaces authoring ${path} does not accept keys: ${keys.join(", ")}`,
803
998
  details: {
999
+ ...details,
804
1000
  keys
805
1001
  }
806
1002
  });
@@ -3186,11 +3382,13 @@ function collectBlockErrors(block, path, errors, localKeys, context) {
3186
3382
  context
3187
3383
  );
3188
3384
  collectSemanticBindingErrors(block, blockType, path, errors, context);
3385
+ collectVisibleDataBlockFieldErrors(block, blockType, path, errors, context);
3189
3386
  collectCommentsBlockErrors(block, blockType, path, errors, context);
3190
3387
  collectRecordHistoryBlockErrors(block, blockType, path, errors, context);
3191
3388
  collectChartDisplayTitleErrors(block, blockType, path, errors);
3192
3389
  collectTreeTableExplicitFieldsErrors(block, blockType, path, errors, context);
3193
3390
  collectTreeConnectFieldsErrors((_a = block.settings) == null ? void 0 : _a.connectFields, `${path}.settings.connectFields`, errors);
3391
+ collectTableSettingsErrors(block, blockType, path, errors);
3194
3392
  collectGridCardSettingsErrors(block, blockType, path, errors);
3195
3393
  const descendantContext = getBlockDescendantValidationContext(block, context);
3196
3394
  collectActionListErrors(block.actions, `${path}.actions`, errors, block, descendantContext, "actions");
@@ -3214,7 +3412,8 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3214
3412
  pushAuthoringError(errors, {
3215
3413
  path: `${path}.type`,
3216
3414
  ruleId: "jsBlock-type-alias-unsupported",
3217
- message: `flowSurfaces authoring ${path}.type must be "jsBlock"; "js" is only an action type and is not a public JSBlock block alias. Use ${path}.settings.code for inline JSBlock code`
3415
+ message: `flowSurfaces authoring ${path}.type must be "jsBlock"; "js" is only an action type and is not a public JSBlock block alias. Use ${path}.settings.code for inline JSBlock code`,
3416
+ details: withJsBlockRepairHint()
3218
3417
  });
3219
3418
  return;
3220
3419
  }
@@ -3228,7 +3427,8 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3228
3427
  pushAuthoringError(errors, {
3229
3428
  path: `${path}.${key}`,
3230
3429
  ruleId: `jsBlock-top-level-${key}-unsupported`,
3231
- message: `flowSurfaces authoring ${path}.${key} is not accepted on public jsBlock blocks; use ${path}.settings.code and ${path}.settings.version for inline JS code`
3430
+ message: `flowSurfaces authoring ${path}.${key} is not accepted on public jsBlock blocks; use ${path}.settings.code and ${path}.settings.version for inline JS code`,
3431
+ details: withJsBlockRepairHint({ key })
3232
3432
  });
3233
3433
  });
3234
3434
  JS_BLOCK_INTERNAL_AUTHORING_KEYS.forEach((key) => {
@@ -3239,16 +3439,17 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3239
3439
  path: `${path}.${key}`,
3240
3440
  ruleId: key === "stepParams" ? "jsBlock-stepParams-unsupported" : "jsBlock-internal-field-unsupported",
3241
3441
  message: `flowSurfaces authoring ${path}.${key} is not accepted on public jsBlock blocks; use ${path}.settings.code and ${path}.settings.version instead of internal persisted fields`,
3242
- details: {
3442
+ details: withJsBlockRepairHint({
3243
3443
  key
3244
- }
3444
+ })
3245
3445
  });
3246
3446
  });
3247
3447
  if (hasOwn(block, "script") && context.authoringActionName !== "applyBlueprint") {
3248
3448
  pushAuthoringError(errors, {
3249
3449
  path: `${path}.script`,
3250
3450
  ruleId: "jsBlock-script-unsupported",
3251
- message: `flowSurfaces authoring ${path}.script is only supported by applyBlueprint assets.scripts; use ${path}.settings.code for localized jsBlock inline JS code`
3451
+ message: `flowSurfaces authoring ${path}.script is only supported by applyBlueprint assets.scripts; use ${path}.settings.code for localized jsBlock inline JS code`,
3452
+ details: withJsBlockRepairHint()
3252
3453
  });
3253
3454
  }
3254
3455
  const settings = _.isPlainObject(block.settings) ? block.settings : void 0;
@@ -3261,10 +3462,10 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3261
3462
  path: `${path}.settings.${key}`,
3262
3463
  ruleId: "jsBlock-settings-unsupported-key",
3263
3464
  message: `flowSurfaces authoring ${path}.settings.${key} is not part of the public jsBlock contract; use ${path}.settings.code and ${path}.settings.version for inline JS code`,
3264
- details: {
3465
+ details: withJsBlockRepairHint({
3265
3466
  key,
3266
3467
  allowedKeys: Array.from(JS_BLOCK_ALLOWED_SETTINGS_KEYS)
3267
- }
3468
+ })
3268
3469
  });
3269
3470
  });
3270
3471
  }
@@ -3274,9 +3475,9 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3274
3475
  path: `${path}.script`,
3275
3476
  ruleId: "jsBlock-mixed-inline-and-script",
3276
3477
  message: `flowSurfaces authoring ${path} cannot combine script asset references with ${inlineKeys.map((key) => `settings.${key}`).join(", ")}; use either applyBlueprint assets.scripts + block.script or inline ${path}.settings.code`,
3277
- details: {
3478
+ details: withJsBlockRepairHint({
3278
3479
  inlineKeys
3279
- }
3480
+ })
3280
3481
  });
3281
3482
  }
3282
3483
  const hasInlineCode = typeof (settings == null ? void 0 : settings.code) === "string" && !!settings.code.trim();
@@ -3286,7 +3487,8 @@ function collectJsBlockPublicContractErrors(block, path, errors, context) {
3286
3487
  pushAuthoringError(errors, {
3287
3488
  path,
3288
3489
  ruleId: "jsBlock-source-required",
3289
- message: `flowSurfaces authoring ${path} jsBlock must include inline ${path}.settings.code or, for applyBlueprint only, a block.script asset reference`
3490
+ message: `flowSurfaces authoring ${path} jsBlock must include inline ${path}.settings.code or, for applyBlueprint only, a block.script asset reference`,
3491
+ details: withJsBlockRepairHint()
3290
3492
  });
3291
3493
  }
3292
3494
  }
@@ -3302,16 +3504,17 @@ function collectJsBlockConfigurePublicContractErrors(changes, path, errors) {
3302
3504
  path: `${path}.${key}`,
3303
3505
  ruleId: key === "stepParams" ? "jsBlock-stepParams-unsupported" : "jsBlock-internal-field-unsupported",
3304
3506
  message: `flowSurfaces authoring ${path}.${key} is not accepted on public jsBlock configure changes; use ${path}.code and ${path}.version instead of internal persisted fields`,
3305
- details: {
3507
+ details: withJsBlockRepairHint({
3306
3508
  key
3307
- }
3509
+ })
3308
3510
  });
3309
3511
  });
3310
3512
  if (hasOwn(changes, "script")) {
3311
3513
  pushAuthoringError(errors, {
3312
3514
  path: `${path}.script`,
3313
3515
  ruleId: "jsBlock-script-unsupported",
3314
- message: `flowSurfaces authoring ${path}.script is only supported by applyBlueprint assets.scripts; use ${path}.code for localized jsBlock configure JS code`
3516
+ message: `flowSurfaces authoring ${path}.script is only supported by applyBlueprint assets.scripts; use ${path}.code for localized jsBlock configure JS code`,
3517
+ details: withJsBlockRepairHint()
3315
3518
  });
3316
3519
  }
3317
3520
  const inlineKeys = JS_BLOCK_TOP_LEVEL_JS_KEYS.filter((key) => hasOwn(changes, key));
@@ -3320,9 +3523,9 @@ function collectJsBlockConfigurePublicContractErrors(changes, path, errors) {
3320
3523
  path: `${path}.script`,
3321
3524
  ruleId: "jsBlock-mixed-inline-and-script",
3322
3525
  message: `flowSurfaces authoring ${path} cannot combine script asset references with ${inlineKeys.map((key) => `${path}.${key}`).join(", ")}; use either applyBlueprint assets.scripts + block.script or localized ${path}.code`,
3323
- details: {
3526
+ details: withJsBlockRepairHint({
3324
3527
  inlineKeys
3325
- }
3528
+ })
3326
3529
  });
3327
3530
  }
3328
3531
  if (!hasOwn(changes, "settings")) {
@@ -3333,7 +3536,8 @@ function collectJsBlockConfigurePublicContractErrors(changes, path, errors) {
3333
3536
  pushAuthoringError(errors, {
3334
3537
  path: `${path}.settings`,
3335
3538
  ruleId: "jsBlock-settings-unsupported-key",
3336
- message: `flowSurfaces authoring ${path}.settings is not part of the public jsBlock configure contract; use ${path}.code and ${path}.version`
3539
+ message: `flowSurfaces authoring ${path}.settings is not part of the public jsBlock configure contract; use ${path}.code and ${path}.version`,
3540
+ details: withJsBlockRepairHint()
3337
3541
  });
3338
3542
  return;
3339
3543
  }
@@ -3342,9 +3546,9 @@ function collectJsBlockConfigurePublicContractErrors(changes, path, errors) {
3342
3546
  path: `${path}.settings.${key}`,
3343
3547
  ruleId: "jsBlock-settings-unsupported-key",
3344
3548
  message: `flowSurfaces authoring ${path}.settings.${key} is not part of the public jsBlock configure contract; use ${path}.${key}`,
3345
- details: {
3549
+ details: withJsBlockRepairHint({
3346
3550
  key
3347
- }
3551
+ })
3348
3552
  });
3349
3553
  });
3350
3554
  }
@@ -3376,7 +3580,8 @@ function collectUnsupportedMainBlockSectionErrors(block, blockType, path, errors
3376
3580
  pushAuthoringError(errors, {
3377
3581
  path: `${path}.${section}`,
3378
3582
  ruleId: `${blockType}-main-block-unsupported-${section}`,
3379
- message: `flowSurfaces authoring ${path} ${blockType} main blocks do not support ${section}`
3583
+ message: `flowSurfaces authoring ${path} ${blockType} main blocks do not support ${section}`,
3584
+ details: blockType === "calendar" ? buildCalendarMainBlockRepairDetails(section) : blockType === "kanban" ? buildKanbanMainBlockRepairDetails(section) : void 0
3380
3585
  });
3381
3586
  });
3382
3587
  }
@@ -3393,7 +3598,8 @@ function collectApplyBlueprintKanbanFieldLimitErrors(block, blockType, path, err
3393
3598
  message: `flowSurfaces authoring ${path}.fields supports at most 2 fields for applyBlueprint kanban main blocks`,
3394
3599
  details: {
3395
3600
  max: 2,
3396
- count: block.fields.length
3601
+ count: block.fields.length,
3602
+ repairHint: "Kanban main block fields controls compact card display only. Keep at most 2 fields in applyBlueprint kanban fields, move richer card details to settings.cardPopup, and keep block type kanban."
3397
3603
  }
3398
3604
  });
3399
3605
  }
@@ -3489,6 +3695,7 @@ async function collectConfigureErrors(values, errors, context) {
3489
3695
  collectCommentsBlockErrors(changesBlock, hostBlockType, "$.changes", errors, context);
3490
3696
  collectRecordHistoryBlockErrors(changesBlock, hostBlockType, "$.changes", errors, context);
3491
3697
  collectChartDisplayTitleErrors(changes, hostBlockType, "$.changes", errors);
3698
+ collectTableSettingsErrors(changes, hostBlockType, "$.changes", errors, { directSettings: true });
3492
3699
  collectGridCardSettingsErrors(changes, hostBlockType, "$.changes", errors, { directSettings: true });
3493
3700
  collectAssignValuesErrors(changes.assignValues, "$.changes.assignValues", errors, changesBlock, context);
3494
3701
  collectTriggerWorkflowsErrors(changes.triggerWorkflows, "$.changes.triggerWorkflows", errors);
@@ -3531,9 +3738,18 @@ function pushAuthoringError(errors, error) {
3531
3738
  type: "bad_request",
3532
3739
  code: "FLOW_SURFACE_AUTHORING_VALIDATION_ERROR",
3533
3740
  status: 400,
3534
- ...error
3741
+ ...error,
3742
+ message: appendRepairHintToAuthoringMessage(error)
3535
3743
  });
3536
3744
  }
3745
+ function appendRepairHintToAuthoringMessage(error) {
3746
+ var _a;
3747
+ const repairHint = typeof ((_a = error.details) == null ? void 0 : _a.repairHint) === "string" ? error.details.repairHint.trim() : "";
3748
+ if (!repairHint || error.message.includes(repairHint)) {
3749
+ return error.message;
3750
+ }
3751
+ return `${error.message}. ${repairHint}`;
3752
+ }
3537
3753
  function hasOwnDefined(value, key) {
3538
3754
  return _.isPlainObject(value) && Object.prototype.hasOwnProperty.call(value, key) && !_.isUndefined(value[key]);
3539
3755
  }
@@ -3696,7 +3912,8 @@ function collectPublicLayoutErrors(layout, layoutPath, knownEntries, kind, error
3696
3912
  pushAuthoringError(errors, {
3697
3913
  path: `${layoutPath}.rows`,
3698
3914
  ruleId: "block-layout-single-column",
3699
- message: `flowSurfaces authoring ${layoutPath}.rows must not place every non-filter block on its own row`
3915
+ message: `flowSurfaces authoring ${layoutPath}.rows must not place every non-filter block on its own row`,
3916
+ details: buildTwoColumnLayoutRepairDetails()
3700
3917
  });
3701
3918
  }
3702
3919
  }
@@ -4140,6 +4357,195 @@ function doesBlockConsumeDefaultFilterAction(block) {
4140
4357
  const blockType = String((block == null ? void 0 : block.type) || "").trim();
4141
4358
  return (0, import_public_data_surface_default_filter.isFlowSurfacePublicDataSurfaceBlockType)(blockType) && !(0, import_template_reference.hasFlowSurfaceTemplateDocument)(block == null ? void 0 : block.template);
4142
4359
  }
4360
+ function collectVisibleDataBlockFieldErrors(block, blockType, path, errors, context) {
4361
+ if (!VISIBLE_FIELD_REQUIRED_DATA_BLOCK_TYPES.has(blockType) || (0, import_template_reference.hasFlowSurfaceTemplateReference)(block == null ? void 0 : block.template)) {
4362
+ return;
4363
+ }
4364
+ const fieldEntries = collectVisibleDataBlockFieldEntries(block, path);
4365
+ const validBusinessFieldNames = collectVisibleDataBlockValidBusinessFieldNames(block, context, fieldEntries);
4366
+ if (!fieldEntries.length || !validBusinessFieldNames.length) {
4367
+ const hasBlockFields2 = Array.isArray(block == null ? void 0 : block.fields);
4368
+ const hasBlockFieldGroups2 = Array.isArray(block == null ? void 0 : block.fieldGroups);
4369
+ const fieldConfigPath2 = !hasBlockFields2 && hasBlockFieldGroups2 ? `${path}.fieldGroups` : `${path}.fields`;
4370
+ const fieldConfigLabel2 = !hasBlockFields2 && hasBlockFieldGroups2 ? `${path}.fieldGroups[].fields` : `${path}.fields`;
4371
+ const suggestedFields = pickVisibleDataBlockFieldSuggestions(block, context);
4372
+ pushAuthoringError(errors, {
4373
+ path: fieldConfigPath2,
4374
+ ruleId: "data-block-visible-fields-required",
4375
+ message: `flowSurfaces authoring ${path} ${blockType} block has no valid business fields. Add collection field names to ${fieldConfigLabel2}; defaults.collections.*.fieldGroups only configures generated popups/forms and is not a substitute for visible block columns.`,
4376
+ details: {
4377
+ blockType,
4378
+ collection: getBlockCollectionName(block, context),
4379
+ fieldCount: fieldEntries.length,
4380
+ ...suggestedFields.length ? { suggestion: { fields: suggestedFields } } : {}
4381
+ }
4382
+ });
4383
+ return;
4384
+ }
4385
+ if (!VISIBLE_FIELD_MINIMUM_DATA_BLOCK_TYPES.has(blockType)) {
4386
+ return;
4387
+ }
4388
+ if (hasUnknownVisibleDataBlockFieldPath(block, context, fieldEntries)) {
4389
+ return;
4390
+ }
4391
+ const eligibleBusinessFields = collectVisibleDataBlockEligibleBusinessFieldNames(block, context);
4392
+ if (eligibleBusinessFields.length < RICH_COLLECTION_VISIBLE_FIELD_THRESHOLD) {
4393
+ return;
4394
+ }
4395
+ const requiredFieldCount = Math.min(RICH_DATA_BLOCK_VISIBLE_FIELD_MINIMUM, eligibleBusinessFields.length);
4396
+ if (validBusinessFieldNames.length >= requiredFieldCount) {
4397
+ return;
4398
+ }
4399
+ const hasBlockFields = Array.isArray(block == null ? void 0 : block.fields);
4400
+ const hasBlockFieldGroups = Array.isArray(block == null ? void 0 : block.fieldGroups);
4401
+ const fieldConfigPath = !hasBlockFields && hasBlockFieldGroups ? `${path}.fieldGroups` : `${path}.fields`;
4402
+ const fieldConfigLabel = !hasBlockFields && hasBlockFieldGroups ? `${path}.fieldGroups[].fields` : `${path}.fields`;
4403
+ pushAuthoringError(errors, {
4404
+ path: fieldConfigPath,
4405
+ ruleId: "data-block-visible-fields-minimum",
4406
+ message: `flowSurfaces authoring ${path} ${blockType} block only has ${validBusinessFieldNames.length} valid business field(s). Add at least ${requiredFieldCount} collection field names to ${fieldConfigLabel}; defaults.collections.*.fieldGroups only configures generated popups/forms and is not a substitute for visible block columns.`,
4407
+ details: {
4408
+ blockType,
4409
+ collection: getBlockCollectionName(block, context),
4410
+ fieldCount: validBusinessFieldNames.length,
4411
+ requiredFieldCount,
4412
+ eligibleBusinessFieldCount: eligibleBusinessFields.length,
4413
+ suggestion: {
4414
+ fields: eligibleBusinessFields.slice(0, requiredFieldCount)
4415
+ }
4416
+ }
4417
+ });
4418
+ }
4419
+ function collectVisibleDataBlockFieldEntries(block, path) {
4420
+ const entries = [];
4421
+ if (Array.isArray(block == null ? void 0 : block.fields)) {
4422
+ block.fields.forEach((field, index) => {
4423
+ entries.push({ field, path: `${path}.fields[${index}]` });
4424
+ });
4425
+ }
4426
+ if (Array.isArray(block == null ? void 0 : block.fieldGroups)) {
4427
+ block.fieldGroups.forEach((group, groupIndex) => {
4428
+ if (!Array.isArray(group == null ? void 0 : group.fields)) {
4429
+ return;
4430
+ }
4431
+ group.fields.forEach((field, fieldIndex) => {
4432
+ entries.push({
4433
+ field,
4434
+ path: `${path}.fieldGroups[${groupIndex}].fields[${fieldIndex}]`
4435
+ });
4436
+ });
4437
+ });
4438
+ }
4439
+ return entries;
4440
+ }
4441
+ function collectVisibleDataBlockValidBusinessFieldNames(block, context, fieldEntries) {
4442
+ const names = /* @__PURE__ */ new Set();
4443
+ fieldEntries.forEach((entry) => {
4444
+ const fieldPath = getAssociationAwareFieldPathInput(entry.field);
4445
+ if (!fieldPath || fieldPath.startsWith("$") || fieldPath.startsWith("{{")) {
4446
+ return;
4447
+ }
4448
+ if (!isVisibleDataBlockBusinessField(entry.field, block, context)) {
4449
+ return;
4450
+ }
4451
+ names.add((0, import_service_helpers.normalizeFieldPath)(fieldPath));
4452
+ });
4453
+ return Array.from(names);
4454
+ }
4455
+ function hasUnknownVisibleDataBlockFieldPath(block, context, fieldEntries) {
4456
+ const collection = getBlockCollection(block, context);
4457
+ if (!collection) {
4458
+ return false;
4459
+ }
4460
+ return fieldEntries.some((entry) => {
4461
+ const fieldPath = getAssociationAwareFieldPathInput(entry.field);
4462
+ if (!fieldPath || fieldPath.startsWith("$") || fieldPath.startsWith("{{")) {
4463
+ return false;
4464
+ }
4465
+ return !(0, import_service_helpers.resolveFieldFromCollection)(collection, (0, import_service_helpers.normalizeFieldPath)(fieldPath));
4466
+ });
4467
+ }
4468
+ function collectVisibleDataBlockEligibleBusinessFieldNames(block, context) {
4469
+ const collection = getBlockCollection(block, context);
4470
+ if (!collection) {
4471
+ return [];
4472
+ }
4473
+ return (0, import_service_helpers.getCollectionFields)(collection).map((field) => {
4474
+ const name = String((0, import_service_helpers.getFieldName)(field) || "").trim();
4475
+ if (!name || !isVisibleDataBlockMinimumCandidateResolvedField(field, name)) {
4476
+ return "";
4477
+ }
4478
+ return name;
4479
+ }).filter(Boolean);
4480
+ }
4481
+ function isVisibleDataBlockMinimumCandidateResolvedField(field, fieldName) {
4482
+ if (!isVisibleDataBlockBusinessResolvedField(field, fieldName)) {
4483
+ return false;
4484
+ }
4485
+ if ((0, import_service_helpers.isAssociationField)(field)) {
4486
+ return false;
4487
+ }
4488
+ if (/Id$/.test(fieldName)) {
4489
+ return false;
4490
+ }
4491
+ return true;
4492
+ }
4493
+ function isVisibleDataBlockBusinessField(field, block, context) {
4494
+ const fieldPath = getAssociationAwareFieldPathInput(field);
4495
+ if (!fieldPath || fieldPath.startsWith("$") || fieldPath.startsWith("{{")) {
4496
+ return false;
4497
+ }
4498
+ if (_.isPlainObject(field)) {
4499
+ if (field.hidden === true || field.internal === true || field.synthetic === true || field.actionOnly === true) {
4500
+ return false;
4501
+ }
4502
+ const semanticType = String(field.type || field.fieldType || field.interface || "").trim();
4503
+ if (semanticType && NON_BUSINESS_VISIBLE_FIELD_TYPES.has(semanticType)) {
4504
+ return false;
4505
+ }
4506
+ }
4507
+ const collection = getBlockCollection(block, context);
4508
+ if (!collection) {
4509
+ return true;
4510
+ }
4511
+ const resolvedField = (0, import_service_helpers.resolveFieldFromCollection)(collection, (0, import_service_helpers.normalizeFieldPath)(fieldPath));
4512
+ if (!resolvedField) {
4513
+ return true;
4514
+ }
4515
+ const fieldName = (0, import_service_helpers.getFieldName)(resolvedField) || fieldPath;
4516
+ return isVisibleDataBlockBusinessResolvedField(resolvedField, fieldName);
4517
+ }
4518
+ function pickVisibleDataBlockFieldSuggestions(block, context) {
4519
+ const collection = getBlockCollection(block, context);
4520
+ if (!collection) {
4521
+ return [];
4522
+ }
4523
+ return (0, import_service_helpers.getCollectionFields)(collection).map((field) => {
4524
+ const name = String((0, import_service_helpers.getFieldName)(field) || "").trim();
4525
+ if (!name || !isVisibleDataBlockBusinessResolvedField(field, name)) {
4526
+ return "";
4527
+ }
4528
+ return name;
4529
+ }).filter(Boolean).slice(0, 3);
4530
+ }
4531
+ function isVisibleDataBlockBusinessResolvedField(field, fieldName) {
4532
+ var _a;
4533
+ if (NON_BUSINESS_VISIBLE_FIELD_NAMES.has(fieldName)) {
4534
+ return false;
4535
+ }
4536
+ const fieldInterface = String((0, import_service_helpers.getFieldInterface)(field) || "").trim();
4537
+ if (NON_BUSINESS_VISIBLE_FIELD_INTERFACES.has(fieldInterface)) {
4538
+ return false;
4539
+ }
4540
+ const fieldType = String((0, import_service_helpers.getFieldType)(field) || "").trim();
4541
+ if (NON_BUSINESS_VISIBLE_FIELD_TYPES.has(fieldType)) {
4542
+ return false;
4543
+ }
4544
+ if ((field == null ? void 0 : field.hidden) === true || ((_a = field == null ? void 0 : field.options) == null ? void 0 : _a.hidden) === true) {
4545
+ return false;
4546
+ }
4547
+ return true;
4548
+ }
4143
4549
  function hasConcreteFilterItem(value) {
4144
4550
  if (!_.isPlainObject(value)) {
4145
4551
  return false;
@@ -4768,6 +5174,43 @@ function getCollectionFilterTargetKey(collection) {
4768
5174
  function normalizeDataSourceKey(value) {
4769
5175
  return String(value || "main").trim() || "main";
4770
5176
  }
5177
+ function collectTableSettingsErrors(block, blockType, blockPath, errors, options = {}) {
5178
+ if (blockType !== "table" || !_.isPlainObject(block)) {
5179
+ return;
5180
+ }
5181
+ TABLE_INTERNAL_AUTHORING_KEYS.forEach((key) => {
5182
+ if (Object.prototype.hasOwnProperty.call(block, key)) {
5183
+ pushTableSettingsUnsupportedError(errors, `${blockPath}.${key}`, key);
5184
+ }
5185
+ });
5186
+ const hasSettings = _.isPlainObject(block == null ? void 0 : block.settings);
5187
+ if (!hasSettings) {
5188
+ return;
5189
+ }
5190
+ const settings = block.settings;
5191
+ const settingsPath = `${blockPath}.settings`;
5192
+ Object.keys(settings).forEach((key) => {
5193
+ if (TABLE_ALLOWED_SETTINGS_KEYS.has(key) && options.directSettings !== true) {
5194
+ return;
5195
+ }
5196
+ if (options.directSettings === true && !TABLE_INTERNAL_AUTHORING_KEYS.includes(key)) {
5197
+ return;
5198
+ }
5199
+ pushTableSettingsUnsupportedError(errors, `${settingsPath}.${key}`, key);
5200
+ });
5201
+ }
5202
+ function pushTableSettingsUnsupportedError(errors, path, key) {
5203
+ pushAuthoringError(errors, {
5204
+ path,
5205
+ ruleId: "table-settings-unsupported-key",
5206
+ message: `flowSurfaces authoring ${path} is not part of the public table settings contract`,
5207
+ details: {
5208
+ key,
5209
+ allowedKeys: Array.from(TABLE_ALLOWED_SETTINGS_KEYS),
5210
+ repairHint: TABLE_SETTINGS_REPAIR_HINT
5211
+ }
5212
+ });
5213
+ }
4771
5214
  function collectGridCardSettingsErrors(block, blockType, blockPath, errors, options = {}) {
4772
5215
  const hasSettings = _.isPlainObject(block == null ? void 0 : block.settings);
4773
5216
  if (!hasSettings && options.directSettings !== true) {
@@ -4853,8 +5296,115 @@ function collectActionListErrors(actions, path, errors, block, context = {}, slo
4853
5296
  if (!Array.isArray(actions)) {
4854
5297
  return;
4855
5298
  }
5299
+ collectDuplicateAIEmployeeActionErrors(actions, path, errors, block);
4856
5300
  actions.forEach((action, index) => collectActionErrors(action, `${path}[${index}]`, errors, block, context, slot));
4857
5301
  }
5302
+ function collectDuplicateAIEmployeeActionErrors(actions, path, errors, block) {
5303
+ const seen = /* @__PURE__ */ new Map();
5304
+ actions.forEach((action, index) => {
5305
+ const actionType = resolveAuthoringActionType(action, block);
5306
+ if (actionType !== "aiEmployee" || !_.isPlainObject(action)) {
5307
+ return;
5308
+ }
5309
+ const identity = buildAuthoringAIEmployeeActionIdentity(action.settings);
5310
+ if (!identity) {
5311
+ return;
5312
+ }
5313
+ const currentPath = `${path}[${index}]`;
5314
+ const previousPath = seen.get(identity);
5315
+ if (previousPath) {
5316
+ pushAuthoringError(errors, {
5317
+ path: currentPath,
5318
+ ruleId: "duplicate-ai-employee-action",
5319
+ message: `flowSurfaces authoring ${currentPath} duplicates an existing AI employee action in the same action container; reuse or update the existing button instead of appending another identical AI employee action.`,
5320
+ details: {
5321
+ duplicateOf: previousPath,
5322
+ repairHint: "Remove the duplicate aiEmployee action, or change username, task title/key, or workContext if this is a genuinely different AI employee action."
5323
+ }
5324
+ });
5325
+ return;
5326
+ }
5327
+ seen.set(identity, currentPath);
5328
+ });
5329
+ }
5330
+ function buildAuthoringAIEmployeeActionIdentity(settings) {
5331
+ if (!_.isPlainObject(settings)) {
5332
+ return "";
5333
+ }
5334
+ const username = String(settings.username || "").trim();
5335
+ if (!username) {
5336
+ return "";
5337
+ }
5338
+ return stableSerializeAuthoringValue(normalizeAuthoringAIEmployeePublicSettingsForIdentity(settings));
5339
+ }
5340
+ function normalizeAuthoringAIEmployeePublicSettingsForIdentity(settings) {
5341
+ return {
5342
+ username: String(settings.username || "").trim(),
5343
+ auto: typeof settings.auto === "boolean" ? settings.auto : false,
5344
+ workContext: Object.prototype.hasOwnProperty.call(settings, "workContext") ? normalizeAuthoringAIEmployeeWorkContextForIdentity(settings.workContext) : [{ type: "flow-model", target: "self" }],
5345
+ tasks: normalizeAuthoringAIEmployeeTasksForIdentity(settings.tasks),
5346
+ style: {
5347
+ ...AUTHORING_AI_EMPLOYEE_DEFAULT_STYLE,
5348
+ ..._.isPlainObject(settings.style) ? _.pick(settings.style, AUTHORING_AI_EMPLOYEE_STYLE_PUBLIC_KEYS) : {}
5349
+ }
5350
+ };
5351
+ }
5352
+ function normalizeAuthoringAIEmployeeWorkContextForIdentity(value) {
5353
+ return _.castArray(value || []).map((item) => {
5354
+ if (!_.isPlainObject(item)) {
5355
+ return item;
5356
+ }
5357
+ const output = _.pick(item, AUTHORING_AI_EMPLOYEE_WORK_CONTEXT_PUBLIC_KEYS);
5358
+ if (!Object.prototype.hasOwnProperty.call(output, "type") || _.isUndefined(output.type) || output.type === null) {
5359
+ output.type = "flow-model";
5360
+ }
5361
+ return output;
5362
+ });
5363
+ }
5364
+ function normalizeAuthoringAIEmployeeTasksForIdentity(value) {
5365
+ return _.castArray(value || []).map((task) => {
5366
+ if (!_.isPlainObject(task)) {
5367
+ return task;
5368
+ }
5369
+ const output = _.pick(task, AUTHORING_AI_EMPLOYEE_TASK_PUBLIC_SETTING_KEYS);
5370
+ if (_.isPlainObject(output.message)) {
5371
+ output.message = _.pick(output.message, AUTHORING_AI_EMPLOYEE_TASK_MESSAGE_PUBLIC_KEYS);
5372
+ if (_.isPlainObject(output.message.workContext)) {
5373
+ output.message.workContext = normalizeAuthoringAIEmployeeWorkContextForIdentity(output.message.workContext);
5374
+ } else if (Array.isArray(output.message.workContext)) {
5375
+ output.message.workContext = normalizeAuthoringAIEmployeeWorkContextForIdentity(output.message.workContext);
5376
+ }
5377
+ }
5378
+ if (Object.prototype.hasOwnProperty.call(task, "prompt") && !Object.prototype.hasOwnProperty.call(output.message || {}, "user")) {
5379
+ output.message = {
5380
+ ..._.isPlainObject(output.message) ? output.message : {},
5381
+ user: task.prompt
5382
+ };
5383
+ }
5384
+ if (_.isPlainObject(output.model)) {
5385
+ output.model = _.pick(output.model, AUTHORING_AI_EMPLOYEE_TASK_MODEL_PUBLIC_KEYS);
5386
+ }
5387
+ if (_.isPlainObject(output.skillSettings)) {
5388
+ output.skillSettings = _.pick(output.skillSettings, AUTHORING_AI_EMPLOYEE_SKILL_SETTINGS_PUBLIC_KEYS);
5389
+ if (Array.isArray(output.skillSettings.skills) && !Object.prototype.hasOwnProperty.call(output.skillSettings, "skillsVersion")) {
5390
+ output.skillSettings.skillsVersion = 2;
5391
+ }
5392
+ if (Array.isArray(output.skillSettings.tools) && !Object.prototype.hasOwnProperty.call(output.skillSettings, "toolsVersion")) {
5393
+ output.skillSettings.toolsVersion = 2;
5394
+ }
5395
+ }
5396
+ return output;
5397
+ });
5398
+ }
5399
+ function stableSerializeAuthoringValue(value) {
5400
+ if (Array.isArray(value)) {
5401
+ return `[${value.map((item) => stableSerializeAuthoringValue(item)).join(",")}]`;
5402
+ }
5403
+ if (_.isPlainObject(value)) {
5404
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableSerializeAuthoringValue(value[key])}`).join(",")}}`;
5405
+ }
5406
+ return JSON.stringify(value);
5407
+ }
4858
5408
  function resolveAuthoringActionType(action, block) {
4859
5409
  const rawType = typeof action === "string" ? action : _.isPlainObject(action) ? action.type : "";
4860
5410
  const normalized = String(rawType || "").trim().toLowerCase();
@@ -5388,7 +5938,8 @@ function collectApplyBlueprintScriptAssetReferenceErrors(spec, path, errors, con
5388
5938
  pushAuthoringError(errors, {
5389
5939
  path: `${path}.script`,
5390
5940
  ruleId: "apply-blueprint-script-asset-key-invalid",
5391
- message: `flowSurfaces applyBlueprint ${publicPath}.script must be a non-empty string asset key; use settings.code for inline JS code`
5941
+ message: `flowSurfaces applyBlueprint ${publicPath}.script must be a non-empty string asset key; use settings.code for inline JS code`,
5942
+ details: withJsBlockRepairHint()
5392
5943
  });
5393
5944
  return;
5394
5945
  }
@@ -5399,14 +5950,18 @@ function collectApplyBlueprintScriptAssetReferenceErrors(spec, path, errors, con
5399
5950
  pushAuthoringError(errors, {
5400
5951
  path: `${path}.script`,
5401
5952
  ruleId: "apply-blueprint-script-asset-code-required",
5402
- message: `flowSurfaces applyBlueprint ${publicPath}.script references script asset '${scriptKey}' without non-empty code; use settings.code for inline JS code`
5953
+ message: `flowSurfaces applyBlueprint ${publicPath}.script references script asset '${scriptKey}' without non-empty code; use settings.code for inline JS code`,
5954
+ details: withJsBlockRepairHint({
5955
+ scriptKey
5956
+ })
5403
5957
  });
5404
5958
  return;
5405
5959
  }
5406
5960
  pushAuthoringError(errors, {
5407
5961
  path: `${path}.script`,
5408
5962
  ruleId: "apply-blueprint-script-asset-key-invalid",
5409
- message: `flowSurfaces applyBlueprint ${publicPath}.script must be a string asset key; use settings.code for inline JS code`
5963
+ message: `flowSurfaces applyBlueprint ${publicPath}.script must be a string asset key; use settings.code for inline JS code`,
5964
+ details: withJsBlockRepairHint()
5410
5965
  });
5411
5966
  }
5412
5967
  function hasApplyBlueprintRunnableScriptAssetReference(script, context) {