/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var chart_config_exports = {};
__export(chart_config_exports, {
  CHART_BASIC_VISUAL_TYPES: () => CHART_BASIC_VISUAL_TYPES,
  CHART_DEFAULT_DATA_SOURCE_KEY: () => CHART_DEFAULT_DATA_SOURCE_KEY,
  CHART_FUNNEL_SORTS: () => CHART_FUNNEL_SORTS,
  CHART_LABEL_TYPES: () => CHART_LABEL_TYPES,
  CHART_QUERY_AGGREGATIONS: () => CHART_QUERY_AGGREGATIONS,
  CHART_QUERY_MODES: () => CHART_QUERY_MODES,
  CHART_SORT_DIRECTIONS: () => CHART_SORT_DIRECTIONS,
  CHART_SORT_NULLS: () => CHART_SORT_NULLS,
  CHART_VISUAL_MODES: () => CHART_VISUAL_MODES,
  buildChartConfigureFromSemanticChanges: () => buildChartConfigureFromSemanticChanges,
  canonicalizeChartConfigure: () => canonicalizeChartConfigure,
  deriveChartSemanticState: () => deriveChartSemanticState,
  getChartBuilderQueryAliases: () => getChartBuilderQueryAliases,
  getChartBuilderQueryOutputs: () => getChartBuilderQueryOutputs,
  getChartBuilderResourceInit: () => getChartBuilderResourceInit,
  getChartRiskyPatternHints: () => getChartRiskyPatternHints,
  getChartSafeDefaultHints: () => getChartSafeDefaultHints,
  getChartSupportedMappingsByType: () => getChartSupportedMappingsByType,
  getChartSupportedStylesByType: () => getChartSupportedStylesByType,
  getChartSupportedVisualTypes: () => getChartSupportedVisualTypes,
  getChartUnsupportedPatternHints: () => getChartUnsupportedPatternHints,
  getChartVisualCommonStyleKeys: () => getChartVisualCommonStyleKeys,
  getChartVisualMappingAliases: () => getChartVisualMappingAliases,
  isChartBuilderQuery: () => isChartBuilderQuery
});
module.exports = __toCommonJS(chart_config_exports);
var import_lodash = __toESM(require("lodash"));
var import_errors = require("./errors");
var import_filter_group = require("./filter-group");
const CHART_REPAIR_HINT = "This is a chart payload shape problem. Repair the current chart query/visual mappings and keep the chart block type. Do not change this block type to table, jsBlock, actionPanel, gridCard, or another block type. KPI / summary numbers should use jsBlock; charts are for trends, distributions, rankings, and visual analysis.";
const CHART_FORBIDDEN_FALLBACKS = [
  "table",
  "list",
  "jsBlock",
  "actionPanel",
  "gridCard",
  "markdown",
  "drop chart",
  "defer chart"
];
const CHART_REPAIR_STEPS = [
  "Keep the block type as chart.",
  "Repair the chart query and visual mappings on the current chart payload.",
  "Retry the chart payload instead of replacing the chart with another block type, omitting it, or deferring it."
];
const CHART_EXPECTED_SHAPE = {
  settings: {
    query: {
      mode: "builder",
      resource: {
        dataSourceKey: "main",
        collectionName: "employees"
      },
      measures: [
        {
          field: "id",
          aggregation: "count",
          alias: "employeeCount"
        }
      ]
    },
    visual: {
      mode: "basic",
      type: "bar",
      mappings: {
        x: "status",
        y: "employeeCount"
      }
    }
  }
};
function withChartRepairDetails(details = {}) {
  return {
    ...details,
    requiredBlockType: "chart",
    fixStrategy: "repair_same_block_type",
    repairHint: CHART_REPAIR_HINT,
    repairSteps: CHART_REPAIR_STEPS,
    expectedShape: CHART_EXPECTED_SHAPE,
    repairExample: {
      settings: {
        query: {
          mode: "builder",
          resource: {
            dataSourceKey: "main",
            collectionName: "<collectionName>"
          },
          measures: [
            {
              field: "id",
              aggregation: "count",
              alias: "recordCount"
            }
          ],
          dimensions: [
            {
              field: "<dimensionField>"
            }
          ]
        },
        visual: {
          mode: "basic",
          type: "bar",
          mappings: {
            x: "<dimensionField>",
            y: "recordCount"
          }
        }
      }
    },
    forbiddenFallbacks: CHART_FORBIDDEN_FALLBACKS
  };
}
function withChartRepairMessage(message) {
  return `${message}. ${CHART_REPAIR_HINT}`;
}
const CHART_DEFAULT_DATA_SOURCE_KEY = "main";
const CHART_QUERY_MODES = ["builder", "sql"];
const CHART_VISUAL_MODES = ["basic", "custom"];
const CHART_BASIC_VISUAL_TYPES = [
  "line",
  "area",
  "bar",
  "barHorizontal",
  "pie",
  "doughnut",
  "funnel",
  "scatter"
];
const CHART_QUERY_AGGREGATIONS = ["sum", "count", "avg", "max", "min"];
const CHART_SORT_DIRECTIONS = ["asc", "desc"];
const CHART_SORT_NULLS = ["default", "first", "last"];
const CHART_LABEL_TYPES = ["value", "percent"];
const CHART_FUNNEL_SORTS = ["descending", "ascending"];
const CHART_SAFE_DEFAULT_HINTS = [
  {
    key: "builder_basic_minimal",
    title: "Use builder + basic first",
    description: "Prefer query.mode='builder' with visual.mode='basic', a single measure, explicit mappings, and no sorting on the first attempt."
  },
  {
    key: "block_outer_props_only",
    title: "Keep block chrome minimal",
    description: "Expose only the minimal block chrome settings first, then add richer chart query or visual settings only when the first render is stable."
  }
];
const CHART_RISKY_PATTERN_HINTS = [
  {
    key: "basic_multi_measure",
    title: "Basic visual with multiple measures",
    description: "Basic visual mappings with multiple measures often need manual tuning and browser verification."
  },
  {
    key: "custom_visual_raw",
    title: "Custom visual raw option",
    description: "visual.mode='custom' relies on raw ECharts option code and is not schema-validated by FlowSurfaces."
  },
  {
    key: "events_raw",
    title: "Raw chart events",
    description: "events.raw executes custom JS against the chart instance and should always be browser-verified."
  }
];
const CHART_UNSUPPORTED_PATTERN_HINTS = [
  {
    key: "builder_measure_sorting",
    title: "Builder sorting on derived measure outputs",
    description: "Builder query.sorting cannot target aggregated measure outputs or custom measure aliases because the current runtime rejects that shape."
  }
];
const CHART_QUERY_MODE_SET = new Set(CHART_QUERY_MODES);
const CHART_VISUAL_MODE_SET = new Set(CHART_VISUAL_MODES);
const CHART_BASIC_VISUAL_TYPE_SET = new Set(CHART_BASIC_VISUAL_TYPES);
const CHART_QUERY_AGGREGATION_SET = new Set(CHART_QUERY_AGGREGATIONS);
const CHART_SORT_DIRECTION_SET = new Set(CHART_SORT_DIRECTIONS);
const CHART_SORT_NULLS_SET = new Set(CHART_SORT_NULLS);
const CHART_LABEL_TYPE_SET = new Set(CHART_LABEL_TYPES);
const CHART_FUNNEL_SORT_SET = new Set(CHART_FUNNEL_SORTS);
const CHART_MAPPING_KEYS = /* @__PURE__ */ new Set(["x", "y", "category", "value", "series", "size"]);
const CHART_STYLE_KEYS = /* @__PURE__ */ new Set([
  "legend",
  "tooltip",
  "label",
  "boundaryGap",
  "xAxisLabelRotate",
  "yAxisSplitLine",
  "smooth",
  "stack",
  "radiusInner",
  "radiusOuter",
  "labelType",
  "sort",
  "minSize",
  "maxSize"
]);
const CHART_COMMON_STYLE_KEYS = /* @__PURE__ */ new Set(["legend", "tooltip", "label"]);
const CHART_STYLE_KEYS_BY_TYPE = {
  line: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "boundaryGap", "xAxisLabelRotate", "yAxisSplitLine", "smooth"]),
  area: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "boundaryGap", "xAxisLabelRotate", "yAxisSplitLine", "smooth", "stack"]),
  bar: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "boundaryGap", "xAxisLabelRotate", "yAxisSplitLine", "stack"]),
  barHorizontal: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "boundaryGap", "xAxisLabelRotate", "yAxisSplitLine", "stack"]),
  scatter: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "xAxisLabelRotate", "yAxisSplitLine"]),
  pie: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "radiusInner", "radiusOuter", "labelType"]),
  doughnut: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "radiusInner", "radiusOuter", "labelType"]),
  funnel: /* @__PURE__ */ new Set(["legend", "tooltip", "label", "sort", "minSize", "maxSize"])
};
const CHART_MAPPING_KEYS_BY_TYPE = {
  line: /* @__PURE__ */ new Set(["x", "y", "series"]),
  area: /* @__PURE__ */ new Set(["x", "y", "series"]),
  bar: /* @__PURE__ */ new Set(["x", "y", "series"]),
  barHorizontal: /* @__PURE__ */ new Set(["x", "y", "series"]),
  scatter: /* @__PURE__ */ new Set(["x", "y", "series", "size"]),
  pie: /* @__PURE__ */ new Set(["category", "value"]),
  doughnut: /* @__PURE__ */ new Set(["category", "value"]),
  funnel: /* @__PURE__ */ new Set(["category", "value"])
};
const CHART_REQUIRED_MAPPING_KEYS_BY_TYPE = {
  line: /* @__PURE__ */ new Set(["x", "y"]),
  area: /* @__PURE__ */ new Set(["x", "y"]),
  bar: /* @__PURE__ */ new Set(["x", "y"]),
  barHorizontal: /* @__PURE__ */ new Set(["x", "y"]),
  scatter: /* @__PURE__ */ new Set(["x", "y"]),
  pie: /* @__PURE__ */ new Set(["category", "value"]),
  doughnut: /* @__PURE__ */ new Set(["category", "value"]),
  funnel: /* @__PURE__ */ new Set(["category", "value"])
};
const CHART_STYLE_SUPPORT_BY_KEY = {
  legend: {
    type: "boolean",
    description: "Show or hide legend"
  },
  tooltip: {
    type: "boolean",
    description: "Show or hide tooltip"
  },
  label: {
    type: "boolean",
    description: "Show or hide series labels"
  },
  boundaryGap: {
    type: "boolean",
    description: "Whether the category axis keeps boundary gap"
  },
  xAxisLabelRotate: {
    type: "number",
    min: 0,
    max: 90,
    description: "Rotate x-axis labels in degrees"
  },
  yAxisSplitLine: {
    type: "boolean",
    description: "Show or hide y-axis split lines"
  },
  smooth: {
    type: "boolean",
    description: "Render smooth curves for line and area charts"
  },
  stack: {
    type: "boolean",
    description: "Stack compatible series together"
  },
  radiusInner: {
    type: "number",
    min: 0,
    max: 100,
    description: "Inner radius percentage; when radiusOuter is also set it must be less than or equal to radiusOuter"
  },
  radiusOuter: {
    type: "number",
    min: 0,
    max: 100,
    description: "Outer radius percentage; when radiusInner is also set it must be greater than or equal to radiusInner"
  },
  labelType: {
    type: "string",
    enumValues: [...CHART_LABEL_TYPES],
    description: "Pie or doughnut label content mode"
  },
  sort: {
    type: "string",
    enumValues: [...CHART_FUNNEL_SORTS],
    description: "Funnel ordering direction"
  },
  minSize: {
    type: "number",
    min: 0,
    max: 100,
    description: "Funnel minimum percentage size; when maxSize is also set it must be less than or equal to maxSize"
  },
  maxSize: {
    type: "number",
    min: 0,
    max: 100,
    description: "Funnel maximum percentage size; when minSize is also set it must be greater than or equal to minSize"
  }
};
function hasOwn(input, key) {
  return Object.prototype.hasOwnProperty.call(input || {}, key);
}
function hasAnyOwn(input, keys) {
  return keys.some((key) => hasOwn(input, key));
}
function hasMeaningfulSemanticPatch(input, key) {
  if (!hasOwn(input, key)) {
    return false;
  }
  const value = input == null ? void 0 : input[key];
  if (import_lodash.default.isNull(value)) {
    return true;
  }
  if (import_lodash.default.isPlainObject(value)) {
    return Object.keys(value).length > 0;
  }
  return !import_lodash.default.isUndefined(value);
}
function buildDefinedObject(input) {
  const result = {};
  for (const [key, value] of Object.entries(input)) {
    if (!import_lodash.default.isUndefined(value)) {
      result[key] = value;
    }
  }
  return result;
}
function ensurePlainObject(input, label) {
  if (!import_lodash.default.isPlainObject(input)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be an object`);
  }
  return input;
}
function normalizeTrimmedString(input, label, options = {}) {
  if (typeof input !== "string") {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be a string`);
  }
  const normalized = input.trim();
  if (!options.allowEmpty && !normalized) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} cannot be empty`);
  }
  return normalized;
}
function normalizeOptionalTrimmedString(input, label) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    return void 0;
  }
  return normalizeTrimmedString(input, label);
}
function normalizeBoolean(input, label) {
  if (!import_lodash.default.isBoolean(input)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be a boolean`);
  }
  return input;
}
function normalizeOptionalBoolean(input, label) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    return void 0;
  }
  return normalizeBoolean(input, label);
}
function normalizeInteger(input, label, options = {}) {
  if (!import_lodash.default.isFinite(input) || !Number.isInteger(input)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be an integer`);
  }
  const normalized = Number(input);
  if (!import_lodash.default.isUndefined(options.min) && normalized < options.min) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be greater than or equal to ${options.min}`);
  }
  if (!import_lodash.default.isUndefined(options.max) && normalized > options.max) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be less than or equal to ${options.max}`);
  }
  return normalized;
}
function normalizeOptionalInteger(input, label, options = {}) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    return void 0;
  }
  return normalizeInteger(input, label, options);
}
function normalizeEnumValue(input, allowed, label, options = {}) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input) || String(input).trim() === "") {
    if (!import_lodash.default.isUndefined(options.fallback)) {
      return options.fallback;
    }
    throw new import_errors.FlowSurfaceBadRequestError(`${label} is required`);
  }
  const normalized = String(input).trim();
  if (!allowed.has(normalized)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} is invalid: ${normalized}`);
  }
  return normalized;
}
function normalizeOptionalEnumValue(input, allowed, label) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input) || String(input).trim() === "") {
    return void 0;
  }
  return normalizeEnumValue(input, allowed, label);
}
function normalizeFieldPathValue(input, label, options = {}) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    if (options.required) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label} is required`);
    }
    return void 0;
  }
  if (typeof input === "string") {
    const normalized = input.trim();
    if (!normalized) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label} cannot be empty`);
    }
    return normalized;
  }
  if (Array.isArray(input)) {
    const normalized = input.map((segment) => normalizeTrimmedString(segment, `${label}[]`));
    if (!normalized.length) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label} cannot be empty`);
    }
    return normalized;
  }
  throw new import_errors.FlowSurfaceBadRequestError(`${label} must be a string or string[]`);
}
function normalizeChartQueryFieldPathValue(input, label, options = {}) {
  const normalized = normalizeFieldPathValue(input, label, options);
  if (typeof normalized !== "string" || !normalized.includes(".")) {
    return normalized;
  }
  const segments = normalized.split(".").map((segment) => segment.trim());
  if (segments.some((segment) => !segment)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} cannot contain empty path segments`);
  }
  return segments;
}
function aliasOfFieldValue(input) {
  if (Array.isArray(input)) {
    return input.filter(Boolean).map((item) => String(item)).join(".");
  }
  return String(input || "").trim();
}
function aliasOfSelection(input) {
  const explicitAlias = typeof (input == null ? void 0 : input.alias) === "string" ? input.alias.trim() : "";
  if (explicitAlias) {
    return explicitAlias;
  }
  return aliasOfFieldValue(input == null ? void 0 : input.field);
}
function normalizeDirectionLike(input, label) {
  const raw = normalizeTrimmedString(input, label).toLowerCase();
  if (!CHART_SORT_DIRECTION_SET.has(raw)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} is invalid: ${input}`);
  }
  return raw;
}
function toPersistedOrder(direction) {
  return direction.toUpperCase();
}
function fromPersistedOrder(input) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input) || String(input).trim() === "") {
    return void 0;
  }
  const normalized = String(input).trim().toLowerCase();
  return CHART_SORT_DIRECTION_SET.has(normalized) ? normalized : void 0;
}
function createEmptyFilterGroup() {
  return import_lodash.default.cloneDeep(import_filter_group.FLOW_SURFACE_EMPTY_FILTER_GROUP);
}
function assertAllowedKeys(input, allowed, label) {
  const unknownKeys = Object.keys(input).filter((key) => !allowed.has(key));
  if (unknownKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} does not support: ${unknownKeys.join(", ")}`);
  }
}
function mergeReplaceArrays(base, patch) {
  return import_lodash.default.mergeWith({}, import_lodash.default.cloneDeep(base || {}), import_lodash.default.cloneDeep(patch || {}), (_objValue, srcValue) => {
    if (Array.isArray(srcValue)) {
      return srcValue;
    }
    return void 0;
  });
}
function normalizeChartResourceFromCollectionPath(collectionPath, label, options = {}) {
  if (import_lodash.default.isUndefined(collectionPath) || import_lodash.default.isNull(collectionPath)) {
    if (options.required) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label} is required`);
    }
    return void 0;
  }
  if (!Array.isArray(collectionPath)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} must be a string[]`);
  }
  const collectionName = normalizeTrimmedString(collectionPath[1], `${label}[1]`);
  const dataSourceKey = import_lodash.default.isUndefined(collectionPath[0]) || import_lodash.default.isNull(collectionPath[0]) || String(collectionPath[0]).trim() === "" ? CHART_DEFAULT_DATA_SOURCE_KEY : normalizeTrimmedString(collectionPath[0], `${label}[0]`);
  return {
    dataSourceKey,
    collectionName
  };
}
function normalizeChartResource(input, label, options = {}) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    if (options.required) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label} is required`);
    }
    return void 0;
  }
  const normalized = ensurePlainObject(input, label);
  const collectionName = normalizeTrimmedString(normalized.collectionName, `${label}.collectionName`);
  const dataSourceKey = import_lodash.default.isUndefined(normalized.dataSourceKey) || import_lodash.default.isNull(normalized.dataSourceKey) ? CHART_DEFAULT_DATA_SOURCE_KEY : normalizeTrimmedString(normalized.dataSourceKey, `${label}.dataSourceKey`);
  return {
    dataSourceKey,
    collectionName
  };
}
function normalizeMergedChartResource(query, options = {}) {
  if (hasOwn(query, "resource") && hasOwn(query, "collectionPath")) {
    const resource = normalizeChartResource(query.resource, "chart query.resource");
    const collectionPathResource = normalizeChartResourceFromCollectionPath(
      query.collectionPath,
      "chart query.collectionPath"
    );
    if (!import_lodash.default.isEqual(resource, collectionPathResource)) {
      throw new import_errors.FlowSurfaceBadRequestError(
        "chart query.resource and chart query.collectionPath must reference the same collection"
      );
    }
    return resource;
  }
  if (hasOwn(query, "resource")) {
    return normalizeChartResource(query.resource, "chart query.resource", options);
  }
  if (hasOwn(query, "collectionPath")) {
    return normalizeChartResourceFromCollectionPath(query.collectionPath, "chart query.collectionPath", options);
  }
  if (options.required) {
    throw new import_errors.FlowSurfaceBadRequestError("chart query.resource is required");
  }
  return void 0;
}
function normalizeChartMeasure(input, index) {
  const label = `chart query.measures[${index}]`;
  const normalized = ensurePlainObject(input, label);
  const field = normalizeChartQueryFieldPathValue(normalized.field, `${label}.field`, { required: true });
  const alias = normalizeOptionalTrimmedString(normalized.alias, `${label}.alias`);
  const aggregation = normalizeOptionalEnumValue(
    normalized.aggregation,
    CHART_QUERY_AGGREGATION_SET,
    `${label}.aggregation`
  );
  return buildDefinedObject({
    field,
    aggregation,
    alias: alias || (Array.isArray(field) && field.length > 1 ? aliasOfFieldValue(field) : void 0),
    distinct: normalizeOptionalBoolean(normalized.distinct, `${label}.distinct`)
  });
}
function normalizeChartDimension(input, index) {
  const label = `chart query.dimensions[${index}]`;
  const normalized = ensurePlainObject(input, label);
  const field = normalizeChartQueryFieldPathValue(normalized.field, `${label}.field`, { required: true });
  const alias = normalizeOptionalTrimmedString(normalized.alias, `${label}.alias`);
  return buildDefinedObject({
    field,
    format: normalizeOptionalTrimmedString(normalized.format, `${label}.format`),
    alias: alias || (Array.isArray(field) && field.length > 1 ? aliasOfFieldValue(field) : void 0)
  });
}
function normalizeBuilderCountMeasureFieldForRuntime(measure, dimensions) {
  if (measure.aggregation !== "count" || measure.distinct || aliasOfFieldValue(measure.field) !== "id") {
    return measure;
  }
  const fallbackDimension = import_lodash.default.castArray(dimensions || []).find((dimension) => aliasOfFieldValue(dimension == null ? void 0 : dimension.field));
  if (!fallbackDimension) {
    return measure;
  }
  if (Array.isArray(fallbackDimension.field) && fallbackDimension.field.length > 1) {
    return measure;
  }
  if (typeof fallbackDimension.field === "string" && fallbackDimension.field.includes(".")) {
    return measure;
  }
  return {
    ...measure,
    field: import_lodash.default.cloneDeep(fallbackDimension.field)
  };
}
function normalizeChartSortingItem(input, index) {
  const label = `chart query.sorting[${index}]`;
  const normalized = ensurePlainObject(input, label);
  const directionSource = hasOwn(normalized, "direction") ? normalized.direction : normalized.order;
  const direction = normalizeDirectionLike(
    directionSource,
    `${label}.${hasOwn(normalized, "direction") ? "direction" : "order"}`
  );
  return buildDefinedObject({
    field: normalizeChartQueryFieldPathValue(normalized.field, `${label}.field`, { required: true }),
    order: toPersistedOrder(direction),
    nulls: normalizeOptionalEnumValue(normalized.nulls, CHART_SORT_NULLS_SET, `${label}.nulls`)
  });
}
function isFilterGroupLike(input) {
  return import_lodash.default.isPlainObject(input) && isBackendQueryLogicKey(input.logic) && Array.isArray(input.items);
}
function assertFilterGroupKeys(input, label) {
  if (!isFilterGroupLike(input)) {
    return;
  }
  const unsupportedKeys = Object.keys(input).filter((key) => key !== "logic" && key !== "items");
  if (unsupportedKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label} does not support: ${unsupportedKeys.join(", ")}`);
  }
  if (Array.isArray(input.items)) {
    input.items.forEach((item, index) => {
      assertFilterGroupKeys(item, `${label}.items[${index}]`);
    });
  }
}
function isBackendQueryLogicKey(key) {
  return key === "$and" || key === "$or";
}
function getBackendQueryLogicKey(input, label) {
  const keys = Object.keys(input);
  const logicKeys = keys.filter(isBackendQueryLogicKey);
  if (!logicKeys.length) {
    return void 0;
  }
  if (logicKeys.length > 1 || keys.length > 1) {
    throw new import_errors.FlowSurfaceBadRequestError(
      `${label}: cannot convert backend query filter with mixed logical and field conditions`
    );
  }
  return logicKeys[0];
}
function convertBackendFieldConditionToFilterItems(field, condition, label) {
  if (!field.trim() || field.startsWith("$")) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label}: cannot convert backend query filter field "${field}"`);
  }
  if (!import_lodash.default.isPlainObject(condition)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label}.${field}: backend query filter condition must be an object`);
  }
  const operators = Object.keys(condition);
  if (!operators.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label}.${field}: backend query filter condition cannot be empty`);
  }
  return operators.map((operator) => {
    if (!operator.startsWith("$")) {
      (0, import_filter_group.assertFlowSurfaceFilterOperator)(operator, `${label}.${field}.${operator}`);
    }
    if (!operator.startsWith("$") || isBackendQueryLogicKey(operator)) {
      throw new import_errors.FlowSurfaceBadRequestError(
        `${label}.${field}: cannot convert backend query filter operator "${operator}"`
      );
    }
    return {
      path: field,
      operator,
      value: import_lodash.default.cloneDeep(condition[operator])
    };
  });
}
function convertBackendQueryFilterToFilterGroup(input, label) {
  const logicKey = getBackendQueryLogicKey(input, label);
  if (logicKey) {
    const operands = input[logicKey];
    if (!Array.isArray(operands)) {
      throw new import_errors.FlowSurfaceBadRequestError(`${label}.${logicKey}: backend query filter operands must be an array`);
    }
    return {
      logic: logicKey,
      items: operands.map(
        (operand, index) => convertBackendQueryOperandToFilterItem(operand, `${label}.${logicKey}[${index}]`)
      )
    };
  }
  return {
    logic: "$and",
    items: Object.entries(input).flatMap(
      ([field, condition]) => convertBackendFieldConditionToFilterItems(field, condition, label)
    )
  };
}
function convertBackendQueryOperandToFilterItem(input, label) {
  if (isFilterGroupLike(input)) {
    throw new import_errors.FlowSurfaceBadRequestError(`${label}: cannot mix filter groups with backend query filters`);
  }
  const group = convertBackendQueryFilterToFilterGroup(ensurePlainObject(input, label), label);
  if (group.logic === "$and" && group.items.length === 1) {
    return group.items[0];
  }
  return group;
}
function normalizeFilterGroupValue(input, label) {
  if (import_lodash.default.isUndefined(input) || import_lodash.default.isNull(input)) {
    return void 0;
  }
  const normalized = ensurePlainObject(input, label);
  assertFilterGroupKeys(normalized, label);
  const filterGroup = !Object.keys(normalized).length || isFilterGroupLike(normalized) ? (0, import_filter_group.normalizeFlowSurfaceFilterGroupValue)(normalized, label, { strictDateValues: true }) : (0, import_filter_group.normalizeFlowSurfaceFilterGroupValue)(convertBackendQueryFilterToFilterGroup(normalized, label), label, {
    strictDateValues: true
  });
  validateFilterGroupPaths(filterGroup, label);
  return filterGroup;
}
function validateFilterGroupPaths(input, label) {
  const items = Array.isArray(input == null ? void 0 : input.items) ? input.items : [];
  for (let index = 0; index < items.length; index += 1) {
    const item = items[index];
    if (!item || typeof item !== "object") {
      continue;
    }
    if (Array.isArray(item.items)) {
      validateFilterGroupPaths(item, `${label}.items[${index}]`);
      continue;
    }
    if (hasOwn(item, "path")) {
      const path = item.path;
      if (typeof path !== "string" || !path.trim()) {
        throw new import_errors.FlowSurfaceBadRequestError(`${label}.items[${index}].path cannot be empty`);
      }
    }
  }
}
function inferQueryMode(query) {
  if (hasOwn(query, "mode") && !import_lodash.default.isUndefined(query.mode) && !import_lodash.default.isNull(query.mode)) {
    return normalizeEnumValue(query.mode, CHART_QUERY_MODE_SET, "chart query.mode");
  }
  if (hasAnyOwn(query, ["sql", "sqlDatasource"])) {
    return "sql";
  }
  return "builder";
}
function validateLooseChartQuery(query) {
  inferQueryMode(query);
  validateChartQueryFilterOperators(query);
  if (!hasOwn(query, "resource") || !hasOwn(query, "collectionPath")) {
    return;
  }
  const resource = normalizeChartResource(query.resource, "chart query.resource");
  const collectionPathResource = normalizeChartResourceFromCollectionPath(
    query.collectionPath,
    "chart query.collectionPath"
  );
  if (!import_lodash.default.isEqual(resource, collectionPathResource)) {
    throw new import_errors.FlowSurfaceBadRequestError(
      "chart query.resource and chart query.collectionPath must reference the same collection"
    );
  }
}
function validateChartQueryFilterOperators(query) {
  if (hasOwn(query, "filter")) {
    normalizeFilterGroupValue(query.filter, "chart query.filter");
  }
}
function canStrictNormalizeChartQuery(query) {
  validateLooseChartQuery(query);
  const mode = inferQueryMode(query);
  if (mode === "sql") {
    return typeof (query == null ? void 0 : query.sql) === "string" && !!query.sql.trim();
  }
  if (!Array.isArray(query == null ? void 0 : query.measures)) {
    return false;
  }
  return !!normalizeMergedChartResource(query);
}
function mergeChartQuerySection(current, patch) {
  if (import_lodash.default.isUndefined(patch)) {
    return import_lodash.default.isUndefined(current) ? void 0 : import_lodash.default.cloneDeep(current);
  }
  if (import_lodash.default.isNull(patch)) {
    return void 0;
  }
  const normalizedPatch = ensurePlainObject(patch, "chart query");
  const currentMode = import_lodash.default.isPlainObject(current) ? inferQueryMode(current) : void 0;
  const nextMode = inferQueryMode(normalizedPatch);
  const modeChanged = currentMode && currentMode !== nextMode;
  const base = modeChanged ? {} : current || {};
  const merged = mergeReplaceArrays(base, import_lodash.default.omit(normalizedPatch, ["filter"]));
  if (hasOwn(normalizedPatch, "filter")) {
    merged.filter = import_lodash.default.cloneDeep(normalizedPatch.filter);
  }
  if (nextMode !== "builder") {
    return merged;
  }
  const currentResource = currentMode === "builder" ? normalizeMergedChartResource(current || {}) : void 0;
  const nextResource = normalizeMergedChartResource(merged, { required: true });
  const resourceChanged = currentMode === "builder" && !import_lodash.default.isEqual(currentResource, nextResource);
  if (!resourceChanged) {
    return merged;
  }
  return {
    ...merged,
    ...hasOwn(normalizedPatch, "measures") ? {} : { measures: [] },
    ...hasOwn(normalizedPatch, "dimensions") ? {} : { dimensions: [] },
    ...hasOwn(normalizedPatch, "sorting") || hasOwn(normalizedPatch, "orders") ? {} : { sorting: [] },
    ...hasOwn(normalizedPatch, "filter") ? {} : { filter: createEmptyFilterGroup() }
  };
}
function inferVisualMode(visual) {
  if (hasOwn(visual, "mode") && !import_lodash.default.isUndefined(visual.mode) && !import_lodash.default.isNull(visual.mode)) {
    return normalizeEnumValue(visual.mode, CHART_VISUAL_MODE_SET, "chart visual.mode");
  }
  if (hasOwn(visual, "raw")) {
    return "custom";
  }
  return "basic";
}
function inferBasicVisualType(visual) {
  return normalizeEnumValue(visual.type, CHART_BASIC_VISUAL_TYPE_SET, "chart visual.type", { fallback: "line" });
}
function canDeriveSemanticVisualFromOption(option) {
  var _a;
  const mode = inferVisualMode(option);
  if (mode === "custom") {
    const raw = option.raw ?? ((_a = option.builder) == null ? void 0 : _a.raw);
    return typeof raw === "string" && !!raw.trim();
  }
  const builder = import_lodash.default.isPlainObject(option.builder) ? option.builder : option;
  if (!import_lodash.default.isPlainObject(builder)) {
    return false;
  }
  if (typeof builder.type === "string" && builder.type.trim()) {
    return true;
  }
  const mappingKeys = [
    "xField",
    "yField",
    "seriesField",
    "sizeField",
    "pieCategory",
    "pieValue",
    "doughnutCategory",
    "doughnutValue",
    "funnelCategory",
    "funnelValue"
  ];
  if (mappingKeys.some((key) => hasOwn(builder, key))) {
    return true;
  }
  const scalarCompatibilityChecks = [
    ["legend", import_lodash.default.isBoolean],
    ["tooltip", import_lodash.default.isBoolean],
    ["label", import_lodash.default.isBoolean],
    ["boundaryGap", import_lodash.default.isBoolean],
    ["yAxisSplitLine", import_lodash.default.isBoolean],
    ["smooth", import_lodash.default.isBoolean],
    ["stack", import_lodash.default.isBoolean],
    ["xAxisLabelRotate", import_lodash.default.isFinite],
    ["pieRadiusInner", import_lodash.default.isFinite],
    ["pieRadiusOuter", import_lodash.default.isFinite],
    ["doughnutRadiusInner", import_lodash.default.isFinite],
    ["doughnutRadiusOuter", import_lodash.default.isFinite],
    ["funnelMinSize", import_lodash.default.isFinite],
    ["funnelMaxSize", import_lodash.default.isFinite],
    ["pieLabelType", (value) => typeof value === "string"],
    ["doughnutLabelType", (value) => typeof value === "string"],
    ["funnelSort", (value) => typeof value === "string"]
  ];
  return scalarCompatibilityChecks.some(([key, predicate]) => hasOwn(builder, key) && predicate(builder[key]));
}
function filterVisualStateForType(current, type) {
  const filteredMappings = import_lodash.default.pick(
    ensurePlainObject(current.mappings || {}, "chart visual.mappings"),
    Array.from(CHART_MAPPING_KEYS_BY_TYPE[type] || [])
  );
  const filteredStyle = import_lodash.default.pick(
    ensurePlainObject(current.style || {}, "chart visual.style"),
    Array.from(CHART_STYLE_KEYS_BY_TYPE[type] || [])
  );
  return buildDefinedObject({
    mode: "basic",
    type,
    ...Object.keys(filteredMappings).length ? { mappings: filteredMappings } : {},
    ...Object.keys(filteredStyle).length ? { style: filteredStyle } : {}
  });
}
function sanitizeBasicVisualForBuilderQuery(visual, query) {
  if (!import_lodash.default.isPlainObject(visual) || inferVisualMode(visual) !== "basic" || (query == null ? void 0 : query.mode) !== "builder") {
    return visual;
  }
  const type = inferBasicVisualType(visual);
  const allowedOutputs = collectBuilderQueryOutputFields(query);
  if (!allowedOutputs.size) {
    return buildDefinedObject({
      mode: "basic",
      type,
      ...Object.keys(ensurePlainObject(visual.style || {}, "chart visual.style")).length ? { style: import_lodash.default.cloneDeep(visual.style) } : {}
    });
  }
  const filteredMappings = import_lodash.default.pickBy(
    import_lodash.default.pick(
      ensurePlainObject(visual.mappings || {}, "chart visual.mappings"),
      Array.from(CHART_MAPPING_KEYS_BY_TYPE[type] || [])
    ),
    (value) => {
      const alias = aliasOfFieldValue(value);
      return !!alias && allowedOutputs.has(alias);
    }
  );
  const requiredKeys = Array.from(CHART_REQUIRED_MAPPING_KEYS_BY_TYPE[type] || []);
  const hasAllRequiredMappings = !requiredKeys.length || requiredKeys.every((key) => Object.prototype.hasOwnProperty.call(filteredMappings, key));
  return buildDefinedObject({
    mode: "basic",
    type,
    ...hasAllRequiredMappings && Object.keys(filteredMappings).length ? { mappings: filteredMappings } : {},
    ...Object.keys(ensurePlainObject(visual.style || {}, "chart visual.style")).length ? { style: import_lodash.default.cloneDeep(visual.style) } : {}
  });
}
function sanitizeBasicVisualForSqlQuery(visual) {
  if (!import_lodash.default.isPlainObject(visual) || inferVisualMode(visual) !== "basic") {
    return visual;
  }
  return buildDefinedObject({
    mode: "basic",
    type: inferBasicVisualType(visual),
    ...Object.keys(ensurePlainObject(visual.style || {}, "chart visual.style")).length ? { style: import_lodash.default.cloneDeep(visual.style) } : {}
  });
}
function mergeChartVisualSection(current, patch) {
  if (import_lodash.default.isUndefined(patch)) {
    return import_lodash.default.isUndefined(current) ? void 0 : import_lodash.default.cloneDeep(current);
  }
  if (import_lodash.default.isNull(patch)) {
    return void 0;
  }
  const normalizedPatch = ensurePlainObject(patch, "chart visual");
  const currentMode = import_lodash.default.isPlainObject(current) ? inferVisualMode(current) : void 0;
  const nextMode = inferVisualMode(normalizedPatch);
  const modeChanged = currentMode && currentMode !== nextMode;
  if (modeChanged) {
    return mergeReplaceArrays({}, normalizedPatch);
  }
  if (nextMode !== "basic") {
    return mergeReplaceArrays(current || {}, normalizedPatch);
  }
  const currentType = currentMode === "basic" && import_lodash.default.isPlainObject(current) ? inferBasicVisualType(current) : void 0;
  const nextType = inferBasicVisualType(mergeReplaceArrays(current || {}, normalizedPatch));
  if (currentType && currentType !== nextType) {
    return mergeReplaceArrays(filterVisualStateForType(current || {}, nextType), normalizedPatch);
  }
  return mergeReplaceArrays(current || {}, normalizedPatch);
}
function mergeChartSection(current, patch, label) {
  if (import_lodash.default.isUndefined(patch)) {
    return import_lodash.default.isUndefined(current) ? void 0 : import_lodash.default.cloneDeep(current);
  }
  if (import_lodash.default.isNull(patch)) {
    return void 0;
  }
  const normalizedPatch = ensurePlainObject(patch, label);
  return mergeReplaceArrays(current || {}, normalizedPatch);
}
function collectBuilderQueryOutputFields(query) {
  const outputs = /* @__PURE__ */ new Set();
  for (const dimension of import_lodash.default.castArray((query == null ? void 0 : query.dimensions) || [])) {
    const alias = aliasOfSelection(dimension);
    if (alias) {
      outputs.add(alias);
    }
  }
  for (const measure of import_lodash.default.castArray((query == null ? void 0 : query.measures) || [])) {
    const alias = aliasOfSelection(measure);
    if (alias) {
      outputs.add(alias);
    }
  }
  return outputs;
}
function buildBuilderQueryOutputs(query) {
  const outputs = [];
  for (const dimension of import_lodash.default.castArray((query == null ? void 0 : query.dimensions) || [])) {
    const alias = aliasOfSelection(dimension);
    if (!alias) {
      continue;
    }
    outputs.push({
      alias,
      source: "builder",
      kind: "dimension",
      field: import_lodash.default.cloneDeep(dimension.field),
      format: dimension.format
    });
  }
  for (const measure of import_lodash.default.castArray((query == null ? void 0 : query.measures) || [])) {
    const alias = aliasOfSelection(measure);
    if (!alias) {
      continue;
    }
    outputs.push({
      alias,
      source: "builder",
      kind: "measure",
      field: import_lodash.default.cloneDeep(measure.field),
      aggregation: measure.aggregation,
      distinct: measure.distinct
    });
  }
  return outputs;
}
function assertBuilderSortingFields(query, sorting) {
  const hasAggregation = import_lodash.default.castArray((query == null ? void 0 : query.measures) || []).some((item) => !!(item == null ? void 0 : item.aggregation));
  if (!hasAggregation || !(sorting == null ? void 0 : sorting.length)) {
    return;
  }
  const allowedFields = collectBuilderQueryOutputFields(query);
  for (const item of sorting) {
    const alias = aliasOfFieldValue(item.field);
    if (!alias || !allowedFields.has(alias)) {
      throw new import_errors.FlowSurfaceBadRequestError("chart query.sorting only supports selected dimension/measure fields");
    }
  }
}
function assertBuilderRuntimeCompatibleSorting(query, sorting) {
  if (!(sorting == null ? void 0 : sorting.length)) {
    return;
  }
  const unsupportedMeasureOutputs = /* @__PURE__ */ new Set();
  for (const measure of import_lodash.default.castArray((query == null ? void 0 : query.measures) || [])) {
    const outputAlias = aliasOfSelection(measure);
    const rawFieldAlias = aliasOfFieldValue(measure == null ? void 0 : measure.field);
    if (!outputAlias) {
      continue;
    }
    if ((measure == null ? void 0 : measure.aggregation) || rawFieldAlias && outputAlias !== rawFieldAlias) {
      unsupportedMeasureOutputs.add(outputAlias);
    }
  }
  for (const item of sorting) {
    const sortingField = aliasOfFieldValue(item == null ? void 0 : item.field);
    if (sortingField && unsupportedMeasureOutputs.has(sortingField)) {
      throw new import_errors.FlowSurfaceBadRequestError(
        "chart query.sorting does not support aggregated measure outputs or custom measure aliases in builder mode"
      );
    }
  }
}
function normalizeBuilderQuery(query) {
  const resource = normalizeMergedChartResource(query, { required: true });
  const dimensions = import_lodash.default.isUndefined(query.dimensions) ? void 0 : Array.isArray(query.dimensions) ? query.dimensions.map((item, index) => normalizeChartDimension(item, index)) : (() => {
    throw new import_errors.FlowSurfaceBadRequestError("chart query.dimensions must be an array");
  })();
  const measures = Array.isArray(query.measures) ? query.measures.map((item, index) => normalizeChartMeasure(item, index)).map((measure) => normalizeBuilderCountMeasureFieldForRuntime(measure, dimensions)) : (() => {
    throw new import_errors.FlowSurfaceBadRequestError("chart query.measures must be an array");
  })();
  if (!measures.length) {
    throw new import_errors.FlowSurfaceBadRequestError("chart query.measures cannot be empty");
  }
  const rawSorting = hasOwn(query, "sorting") ? query.sorting : query.orders;
  const sorting = import_lodash.default.isUndefined(rawSorting) ? void 0 : Array.isArray(rawSorting) ? rawSorting.map((item, index) => normalizeChartSortingItem(item, index)) : (() => {
    throw new import_errors.FlowSurfaceBadRequestError("chart query.sorting must be an array");
  })();
  const filter = normalizeFilterGroupValue(query.filter, "chart query.filter");
  assertBuilderSortingFields({ measures, dimensions }, sorting || []);
  assertBuilderRuntimeCompatibleSorting({ measures, dimensions }, sorting || []);
  return buildDefinedObject({
    mode: "builder",
    collectionPath: [resource.dataSourceKey, resource.collectionName],
    measures,
    dimensions,
    filter,
    orders: sorting,
    limit: normalizeOptionalInteger(query.limit, "chart query.limit", { min: 0 }),
    offset: normalizeOptionalInteger(query.offset, "chart query.offset", { min: 0 })
  });
}
function normalizeSqlQuery(query) {
  const unsupportedKeys = [
    "resource",
    "collectionPath",
    "measures",
    "dimensions",
    "filter",
    "sorting",
    "orders",
    "limit",
    "offset"
  ].filter((key) => hasOwn(query, key));
  if (unsupportedKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`chart query.mode='sql' does not support: ${unsupportedKeys.join(", ")}`);
  }
  return buildDefinedObject({
    mode: "sql",
    sql: normalizeTrimmedString(query.sql, "chart query.sql"),
    sqlDatasource: import_lodash.default.isUndefined(query.sqlDatasource) || import_lodash.default.isNull(query.sqlDatasource) ? void 0 : normalizeTrimmedString(query.sqlDatasource, "chart query.sqlDatasource")
  });
}
function normalizeChartQuery(query) {
  const normalized = ensurePlainObject(query, "chart query");
  const mode = inferQueryMode(normalized);
  return mode === "sql" ? normalizeSqlQuery(normalized) : normalizeBuilderQuery(normalized);
}
function normalizeCommonChartStyle(style) {
  return buildDefinedObject({
    legend: normalizeOptionalBoolean(style.legend, "chart visual.style.legend"),
    tooltip: normalizeOptionalBoolean(style.tooltip, "chart visual.style.tooltip"),
    label: normalizeOptionalBoolean(style.label, "chart visual.style.label")
  });
}
function normalizeBasicVisual(visual) {
  const type = inferBasicVisualType(visual);
  if (hasOwn(visual, "raw")) {
    throw new import_errors.FlowSurfaceBadRequestError("chart visual.mode='basic' does not support raw");
  }
  const mappings = import_lodash.default.isUndefined(visual.mappings) ? {} : ensurePlainObject(visual.mappings, "chart visual.mappings");
  const style = import_lodash.default.isUndefined(visual.style) ? {} : ensurePlainObject(visual.style, "chart visual.style");
  const requireRequiredMappings = Object.keys(mappings).length > 0;
  assertAllowedKeys(mappings, CHART_MAPPING_KEYS, "chart visual.mappings");
  assertAllowedKeys(style, CHART_STYLE_KEYS, "chart visual.style");
  const allowedStyleKeys = CHART_STYLE_KEYS_BY_TYPE[type] || /* @__PURE__ */ new Set();
  const invalidStyleKeys = Object.keys(style).filter((key) => !allowedStyleKeys.has(key));
  if (invalidStyleKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(
      `chart visual.type='${type}' does not support style keys: ${invalidStyleKeys.join(", ")}`
    );
  }
  const builder = {
    type,
    ...normalizeCommonChartStyle(style)
  };
  switch (type) {
    case "line":
    case "area":
    case "bar":
    case "barHorizontal":
      builder.xField = normalizeFieldPathValue(mappings.x, "chart visual.mappings.x", {
        required: requireRequiredMappings
      });
      builder.yField = normalizeFieldPathValue(mappings.y, "chart visual.mappings.y", {
        required: requireRequiredMappings
      });
      builder.seriesField = normalizeFieldPathValue(mappings.series, "chart visual.mappings.series");
      builder.boundaryGap = normalizeOptionalBoolean(style.boundaryGap, "chart visual.style.boundaryGap");
      builder.xAxisLabelRotate = normalizeOptionalInteger(
        style.xAxisLabelRotate,
        "chart visual.style.xAxisLabelRotate",
        {
          min: 0,
          max: 90
        }
      );
      builder.yAxisSplitLine = normalizeOptionalBoolean(style.yAxisSplitLine, "chart visual.style.yAxisSplitLine");
      if (type === "line" || type === "area") {
        builder.smooth = normalizeOptionalBoolean(style.smooth, "chart visual.style.smooth");
      }
      if (type === "area" || type === "bar" || type === "barHorizontal") {
        builder.stack = normalizeOptionalBoolean(style.stack, "chart visual.style.stack");
      }
      break;
    case "scatter":
      builder.xField = normalizeFieldPathValue(mappings.x, "chart visual.mappings.x", {
        required: requireRequiredMappings
      });
      builder.yField = normalizeFieldPathValue(mappings.y, "chart visual.mappings.y", {
        required: requireRequiredMappings
      });
      builder.seriesField = normalizeFieldPathValue(mappings.series, "chart visual.mappings.series");
      builder.sizeField = normalizeFieldPathValue(mappings.size, "chart visual.mappings.size");
      builder.xAxisLabelRotate = normalizeOptionalInteger(
        style.xAxisLabelRotate,
        "chart visual.style.xAxisLabelRotate",
        {
          min: 0,
          max: 90
        }
      );
      builder.yAxisSplitLine = normalizeOptionalBoolean(style.yAxisSplitLine, "chart visual.style.yAxisSplitLine");
      break;
    case "pie":
    case "doughnut":
      builder[type === "pie" ? "pieCategory" : "doughnutCategory"] = normalizeFieldPathValue(
        mappings.category,
        "chart visual.mappings.category",
        { required: requireRequiredMappings }
      );
      builder[type === "pie" ? "pieValue" : "doughnutValue"] = normalizeFieldPathValue(
        mappings.value,
        "chart visual.mappings.value",
        { required: requireRequiredMappings }
      );
      builder[type === "pie" ? "pieRadiusInner" : "doughnutRadiusInner"] = normalizeOptionalInteger(
        style.radiusInner,
        "chart visual.style.radiusInner",
        { min: 0, max: 100 }
      );
      builder[type === "pie" ? "pieRadiusOuter" : "doughnutRadiusOuter"] = normalizeOptionalInteger(
        style.radiusOuter,
        "chart visual.style.radiusOuter",
        { min: 0, max: 100 }
      );
      builder[type === "pie" ? "pieLabelType" : "doughnutLabelType"] = normalizeOptionalEnumValue(
        style.labelType,
        CHART_LABEL_TYPE_SET,
        "chart visual.style.labelType"
      );
      {
        const inner = builder[type === "pie" ? "pieRadiusInner" : "doughnutRadiusInner"];
        const outer = builder[type === "pie" ? "pieRadiusOuter" : "doughnutRadiusOuter"];
        if (!import_lodash.default.isUndefined(inner) && !import_lodash.default.isUndefined(outer) && inner > outer) {
          throw new import_errors.FlowSurfaceBadRequestError(
            "chart visual.style.radiusOuter must be greater than or equal to radiusInner"
          );
        }
      }
      break;
    case "funnel":
      builder.funnelCategory = normalizeFieldPathValue(mappings.category, "chart visual.mappings.category", {
        required: requireRequiredMappings
      });
      builder.funnelValue = normalizeFieldPathValue(mappings.value, "chart visual.mappings.value", {
        required: requireRequiredMappings
      });
      builder.funnelSort = normalizeOptionalEnumValue(style.sort, CHART_FUNNEL_SORT_SET, "chart visual.style.sort");
      builder.funnelMinSize = normalizeOptionalInteger(style.minSize, "chart visual.style.minSize", {
        min: 0,
        max: 100
      });
      builder.funnelMaxSize = normalizeOptionalInteger(style.maxSize, "chart visual.style.maxSize", {
        min: 0,
        max: 100
      });
      if (!import_lodash.default.isUndefined(builder.funnelMinSize) && !import_lodash.default.isUndefined(builder.funnelMaxSize) && builder.funnelMinSize > builder.funnelMaxSize) {
        throw new import_errors.FlowSurfaceBadRequestError("chart visual.style.maxSize must be greater than or equal to minSize");
      }
      break;
    default:
      break;
  }
  return {
    mode: "basic",
    builder: buildDefinedObject(builder)
  };
}
function normalizeCustomVisual(visual) {
  const unsupportedKeys = ["type", "mappings", "style"].filter((key) => hasOwn(visual, key));
  if (unsupportedKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`chart visual.mode='custom' does not support: ${unsupportedKeys.join(", ")}`);
  }
  return {
    mode: "custom",
    raw: normalizeTrimmedString(visual.raw, "chart visual.raw")
  };
}
function assertBasicVisualMappingsAgainstBuilderQuery(builderVisual, query) {
  const allowedOutputs = collectBuilderQueryOutputFields(query);
  if (!allowedOutputs.size) {
    return;
  }
  const mappingValues = [
    builderVisual.xField,
    builderVisual.yField,
    builderVisual.seriesField,
    builderVisual.sizeField,
    builderVisual.pieCategory,
    builderVisual.pieValue,
    builderVisual.doughnutCategory,
    builderVisual.doughnutValue,
    builderVisual.funnelCategory,
    builderVisual.funnelValue
  ].filter((value) => !import_lodash.default.isUndefined(value) && !import_lodash.default.isNull(value)).map((value) => aliasOfFieldValue(value));
  for (const value of mappingValues) {
    if (!value || !allowedOutputs.has(value)) {
      throw new import_errors.FlowSurfaceBadRequestError(
        withChartRepairMessage(
          `chart visual mappings only support query output fields: ${Array.from(allowedOutputs).join(", ")}`
        ),
        void 0,
        {
          details: withChartRepairDetails({
            allowedOutputs: Array.from(allowedOutputs)
          })
        }
      );
    }
  }
}
function normalizeChartVisual(visual, query) {
  const normalized = ensurePlainObject(visual, "chart visual");
  const mode = inferVisualMode(normalized);
  if (mode === "custom") {
    return normalizeCustomVisual(normalized);
  }
  const visualConfig = normalizeBasicVisual(normalized);
  if ((query == null ? void 0 : query.mode) === "builder") {
    assertBasicVisualMappingsAgainstBuilderQuery(visualConfig.builder || {}, query);
  }
  return visualConfig;
}
function normalizeChartEvents(events) {
  const normalized = ensurePlainObject(events, "chart events");
  const unsupportedKeys = Object.keys(normalized).filter((key) => key !== "raw");
  if (unsupportedKeys.length) {
    throw new import_errors.FlowSurfaceBadRequestError(`chart events does not support: ${unsupportedKeys.join(", ")}`);
  }
  if (import_lodash.default.isUndefined(normalized.raw) || import_lodash.default.isNull(normalized.raw)) {
    return void 0;
  }
  return {
    mode: "custom",
    raw: normalizeTrimmedString(normalized.raw, "chart events.raw")
  };
}
function deriveChartSortingItem(item) {
  if (!import_lodash.default.isPlainObject(item)) {
    return void 0;
  }
  const direction = fromPersistedOrder(item.order) || fromPersistedOrder(item.direction);
  return buildDefinedObject({
    field: import_lodash.default.cloneDeep(item.field),
    direction,
    nulls: item.nulls
  });
}
function deriveChartQuery(configure) {
  const query = configure == null ? void 0 : configure.query;
  if (!import_lodash.default.isPlainObject(query)) {
    return void 0;
  }
  if (!canStrictNormalizeChartQuery(query)) {
    return void 0;
  }
  if (query.mode === "sql") {
    return buildDefinedObject({
      mode: "sql",
      sql: query.sql,
      sqlDatasource: query.sqlDatasource
    });
  }
  return buildDefinedObject({
    mode: "builder",
    resource: getChartBuilderResourceInit(configure),
    measures: import_lodash.default.cloneDeep(query.measures),
    dimensions: import_lodash.default.cloneDeep(query.dimensions),
    filter: import_lodash.default.cloneDeep(query.filter),
    sorting: import_lodash.default.castArray(hasOwn(query, "sorting") ? query.sorting : query.orders).map((item) => deriveChartSortingItem(item)).filter(Boolean),
    limit: query.limit,
    offset: query.offset
  });
}
function deriveChartVisual(configure) {
  var _a;
  const option = (_a = configure == null ? void 0 : configure.chart) == null ? void 0 : _a.option;
  if (!import_lodash.default.isPlainObject(option)) {
    return void 0;
  }
  if (!canDeriveSemanticVisualFromOption(option)) {
    return void 0;
  }
  if (option.mode === "custom") {
    return buildDefinedObject({
      mode: "custom",
      raw: option.raw
    });
  }
  const builder = import_lodash.default.isPlainObject(option.builder) ? option.builder : import_lodash.default.isPlainObject(option) ? option : {};
  const type = String(builder.type || "line");
  const style = buildDefinedObject({
    legend: builder.legend,
    tooltip: builder.tooltip,
    label: builder.label,
    boundaryGap: builder.boundaryGap,
    xAxisLabelRotate: builder.xAxisLabelRotate,
    yAxisSplitLine: builder.yAxisSplitLine,
    smooth: builder.smooth,
    stack: builder.stack,
    radiusInner: builder.pieRadiusInner ?? builder.doughnutRadiusInner,
    radiusOuter: builder.pieRadiusOuter ?? builder.doughnutRadiusOuter,
    labelType: builder.pieLabelType ?? builder.doughnutLabelType,
    sort: builder.funnelSort,
    minSize: builder.funnelMinSize,
    maxSize: builder.funnelMaxSize
  });
  const mappings = buildDefinedObject({
    x: builder.xField,
    y: builder.yField,
    category: builder.pieCategory ?? builder.doughnutCategory ?? builder.funnelCategory,
    value: builder.pieValue ?? builder.doughnutValue ?? builder.funnelValue,
    series: builder.seriesField,
    size: builder.sizeField
  });
  return buildDefinedObject({
    mode: "basic",
    type,
    ...Object.keys(mappings).length ? { mappings } : {},
    ...Object.keys(style).length ? { style } : {}
  });
}
function deriveChartEvents(configure) {
  var _a;
  const events = (_a = configure == null ? void 0 : configure.chart) == null ? void 0 : _a.events;
  if (!import_lodash.default.isPlainObject(events)) {
    return void 0;
  }
  return buildDefinedObject({
    raw: events.raw
  });
}
function rebuildChartConfigureFromSemanticState(nextState) {
  const nextConfigure = {};
  if (nextState.query) {
    nextConfigure.query = normalizeChartQuery(nextState.query);
  }
  if (nextState.visual || nextState.events) {
    nextConfigure.chart = buildDefinedObject({
      option: nextState.visual ? normalizeChartVisual(nextState.visual, nextConfigure.query) : void 0,
      events: nextState.events ? normalizeChartEvents(nextState.events) : void 0
    });
  }
  return nextConfigure;
}
function deriveChartSemanticState(configure) {
  return buildDefinedObject({
    query: deriveChartQuery(configure),
    visual: deriveChartVisual(configure),
    events: deriveChartEvents(configure)
  });
}
function getChartBuilderResourceInit(configure) {
  const query = configure == null ? void 0 : configure.query;
  if (!import_lodash.default.isPlainObject(query) || query.mode === "sql") {
    return null;
  }
  return normalizeMergedChartResource(query) || null;
}
function getChartBuilderQueryOutputs(configure) {
  var _a;
  const query = (_a = deriveChartSemanticState(configure)) == null ? void 0 : _a.query;
  if ((query == null ? void 0 : query.mode) !== "builder") {
    return [];
  }
  return buildBuilderQueryOutputs(query);
}
function getChartBuilderQueryAliases(configure) {
  return Array.from(
    new Set(
      getChartBuilderQueryOutputs(configure).map((output) => typeof (output == null ? void 0 : output.alias) === "string" ? output.alias.trim() : "").filter((alias) => !!alias)
    )
  );
}
function getChartSupportedVisualTypes() {
  return [...CHART_BASIC_VISUAL_TYPES];
}
function getChartSupportedMappingsByType() {
  return Object.fromEntries(
    Object.entries(CHART_MAPPING_KEYS_BY_TYPE).map(([type, mappings]) => [
      type,
      {
        allowed: Array.from(mappings),
        required: Array.from(CHART_REQUIRED_MAPPING_KEYS_BY_TYPE[type] || [])
      }
    ])
  );
}
function getChartSupportedStylesByType() {
  return import_lodash.default.cloneDeep(
    Object.fromEntries(
      Object.entries(CHART_STYLE_KEYS_BY_TYPE).map(([type, styleKeys]) => [
        type,
        Object.fromEntries(
          Array.from(styleKeys).map((styleKey) => [styleKey, import_lodash.default.cloneDeep(CHART_STYLE_SUPPORT_BY_KEY[styleKey])])
        )
      ])
    )
  );
}
function getChartSafeDefaultHints() {
  return import_lodash.default.cloneDeep(CHART_SAFE_DEFAULT_HINTS);
}
function getChartRiskyPatternHints() {
  return import_lodash.default.cloneDeep(CHART_RISKY_PATTERN_HINTS);
}
function getChartUnsupportedPatternHints() {
  return import_lodash.default.cloneDeep(CHART_UNSUPPORTED_PATTERN_HINTS);
}
function getChartVisualMappingAliases(input) {
  const visual = (input == null ? void 0 : input.visual) ? input.visual : input;
  if (!import_lodash.default.isPlainObject(visual)) {
    return [];
  }
  const semanticVisual = (visual == null ? void 0 : visual.mode) || (visual == null ? void 0 : visual.mappings) || (visual == null ? void 0 : visual.type) ? visual : deriveChartVisual({ chart: { option: visual } });
  if (!semanticVisual || semanticVisual.mode !== "basic" || !import_lodash.default.isPlainObject(semanticVisual.mappings)) {
    return [];
  }
  return import_lodash.default.uniq(
    Object.values(semanticVisual.mappings).map((value) => aliasOfFieldValue(value)).filter(Boolean)
  );
}
function canonicalizeChartConfigure(configure) {
  if (import_lodash.default.isUndefined(configure) || import_lodash.default.isNull(configure)) {
    return configure;
  }
  ensurePlainObject(configure, "chart configure");
  const cloned = import_lodash.default.cloneDeep(configure);
  const nextConfigure = import_lodash.default.omit(cloned, ["query", "chart"]);
  const rawQuery = import_lodash.default.isUndefined(cloned.query) ? void 0 : ensurePlainObject(cloned.query, "chart query");
  const rawChart = import_lodash.default.isUndefined(cloned.chart) ? void 0 : ensurePlainObject(cloned.chart, "chart chart");
  const rawOption = import_lodash.default.isUndefined(rawChart == null ? void 0 : rawChart.option) ? void 0 : ensurePlainObject(rawChart.option, "chart option");
  const rawEvents = import_lodash.default.isUndefined(rawChart == null ? void 0 : rawChart.events) ? void 0 : ensurePlainObject(rawChart.events, "chart events");
  if (!import_lodash.default.isUndefined(rawQuery)) {
    nextConfigure.query = canStrictNormalizeChartQuery(rawQuery) ? normalizeChartQuery(rawQuery) : import_lodash.default.cloneDeep(rawQuery);
  }
  if (!import_lodash.default.isUndefined(rawChart)) {
    const nextChart = import_lodash.default.omit(import_lodash.default.cloneDeep(rawChart), ["option", "events"]);
    const strictQuery = nextConfigure.query;
    if (!import_lodash.default.isUndefined(rawOption)) {
      if (canDeriveSemanticVisualFromOption(rawOption)) {
        const semanticVisual = deriveChartVisual({ chart: { option: rawOption } });
        nextChart.option = semanticVisual ? normalizeChartVisual(semanticVisual, (strictQuery == null ? void 0 : strictQuery.mode) === "builder" ? strictQuery : void 0) : import_lodash.default.cloneDeep(rawOption);
      } else {
        if (hasOwn(rawOption, "mode")) {
          inferVisualMode(rawOption);
        }
        nextChart.option = import_lodash.default.cloneDeep(rawOption);
      }
    }
    if (!import_lodash.default.isUndefined(rawEvents)) {
      if (hasOwn(rawEvents, "raw") || hasOwn(rawEvents, "mode")) {
        const semanticEvents = deriveChartEvents({ chart: { events: rawEvents } });
        nextChart.events = semanticEvents ? normalizeChartEvents(semanticEvents) : void 0;
      } else {
        nextChart.events = import_lodash.default.cloneDeep(rawEvents);
      }
    }
    if (Object.keys(nextChart).length) {
      nextConfigure.chart = nextChart;
    }
  }
  return nextConfigure;
}
function buildChartConfigureFromSemanticChanges(currentConfigure, changes) {
  var _a, _b, _c, _d;
  if (hasOwn(changes, "configure") && ["query", "visual", "events"].some((key) => hasOwn(changes, key))) {
    throw new import_errors.FlowSurfaceBadRequestError(
      "chart configure cannot mix legacy configure with semantic query/visual/events changes"
    );
  }
  if (hasOwn(changes, "configure")) {
    return canonicalizeChartConfigure(changes.configure);
  }
  const hasSemanticChanges = ["query", "visual", "events"].some((key) => hasOwn(changes, key));
  if (!hasSemanticChanges) {
    return import_lodash.default.cloneDeep(currentConfigure) || {};
  }
  const nextQueryPatch = hasMeaningfulSemanticPatch(changes, "query") ? changes.query : void 0;
  const nextVisualPatch = hasMeaningfulSemanticPatch(changes, "visual") ? changes.visual : void 0;
  const nextEventsPatch = hasMeaningfulSemanticPatch(changes, "events") ? changes.events : void 0;
  const currentState = deriveChartSemanticState(currentConfigure);
  const nextState = {
    query: mergeChartQuerySection(currentState.query, nextQueryPatch),
    visual: mergeChartVisualSection(currentState.visual, nextVisualPatch),
    events: mergeChartSection(currentState.events, nextEventsPatch, "chart events")
  };
  if (hasMeaningfulSemanticPatch(changes, "query") && !hasMeaningfulSemanticPatch(changes, "visual") && ((_a = nextState.query) == null ? void 0 : _a.mode) === "builder" && ((_b = nextState.visual) == null ? void 0 : _b.mode) === "basic") {
    nextState.visual = sanitizeBasicVisualForBuilderQuery(nextState.visual, nextState.query);
  }
  if (hasMeaningfulSemanticPatch(changes, "query") && !hasMeaningfulSemanticPatch(changes, "visual") && ((_c = nextState.query) == null ? void 0 : _c.mode) === "sql" && ((_d = nextState.visual) == null ? void 0 : _d.mode) === "basic") {
    nextState.visual = sanitizeBasicVisualForSqlQuery(nextState.visual);
  }
  return rebuildChartConfigureFromSemanticState(nextState);
}
function isChartBuilderQuery(configure) {
  return !!getChartBuilderResourceInit(configure);
}
function getChartVisualCommonStyleKeys() {
  return Array.from(CHART_COMMON_STYLE_KEYS);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  CHART_BASIC_VISUAL_TYPES,
  CHART_DEFAULT_DATA_SOURCE_KEY,
  CHART_FUNNEL_SORTS,
  CHART_LABEL_TYPES,
  CHART_QUERY_AGGREGATIONS,
  CHART_QUERY_MODES,
  CHART_SORT_DIRECTIONS,
  CHART_SORT_NULLS,
  CHART_VISUAL_MODES,
  buildChartConfigureFromSemanticChanges,
  canonicalizeChartConfigure,
  deriveChartSemanticState,
  getChartBuilderQueryAliases,
  getChartBuilderQueryOutputs,
  getChartBuilderResourceInit,
  getChartRiskyPatternHints,
  getChartSafeDefaultHints,
  getChartSupportedMappingsByType,
  getChartSupportedStylesByType,
  getChartSupportedVisualTypes,
  getChartUnsupportedPatternHints,
  getChartVisualCommonStyleKeys,
  getChartVisualMappingAliases,
  isChartBuilderQuery
});
