@opencorestack/opengridx 3.4.0 → 3.5.0

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 (76) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +6 -0
  3. package/dist/ai.cjs +2 -1
  4. package/dist/ai.cjs.map +1 -1
  5. package/dist/ai.d.ts +203 -3
  6. package/dist/ai.es.js +2 -1
  7. package/dist/ai.es.js.map +1 -1
  8. package/dist/index.d.ts +248 -4
  9. package/dist/opengridx.css +1 -1
  10. package/dist/opengridx.es.js +7104 -5726
  11. package/dist/opengridx.es.js.map +1 -1
  12. package/dist/opengridx.umd.js +16 -16
  13. package/dist/opengridx.umd.js.map +1 -1
  14. package/docs/API_REFERENCE.md +88 -7
  15. package/docs/README.md +3 -1
  16. package/docs/architecture/datagrid-orchestration.md +9 -4
  17. package/docs/components/filter-panel.md +1 -0
  18. package/docs/components/header.md +4 -1
  19. package/docs/components/toolbar.md +7 -0
  20. package/docs/customization/theming.md +3 -1
  21. package/docs/features/ai-toolkit.md +99 -5
  22. package/docs/features/cell-selection.md +8 -2
  23. package/docs/features/fill-handle.md +125 -0
  24. package/docs/features/filtering.md +3 -0
  25. package/docs/features/header-filters.md +138 -0
  26. package/docs/features/keyboard-navigation.md +20 -2
  27. package/docs/roadmap.md +2 -3
  28. package/lib/ai/agentTools.ts +160 -0
  29. package/lib/ai/index.ts +19 -2
  30. package/lib/ai/promptHandler.ts +109 -0
  31. package/lib/ai/types.ts +89 -25
  32. package/lib/ai/validate.ts +10 -0
  33. package/lib/components/AiAssistant/GridAiAssistantArea.tsx +26 -0
  34. package/lib/components/AiAssistant/GridAiAssistantPanel.css +171 -0
  35. package/lib/components/AiAssistant/GridAiAssistantPanel.tsx +145 -0
  36. package/lib/components/AiAssistant/useSpeechInput.ts +74 -0
  37. package/lib/components/Cell/Cell.css +28 -0
  38. package/lib/components/Cell/Cell.tsx +6 -2
  39. package/lib/components/DataGrid/DataGrid.tsx +76 -3
  40. package/lib/components/DataGrid/GridLiveRegion.tsx +5 -2
  41. package/lib/components/DataGrid/GridPinnedRows.tsx +5 -1
  42. package/lib/components/DataGrid/GridVirtualRows.tsx +5 -1
  43. package/lib/components/FilterPanel/FilterPanel.tsx +2 -9
  44. package/lib/components/Header/Header.tsx +44 -23
  45. package/lib/components/Header/headerColumnStyle.ts +28 -0
  46. package/lib/components/HeaderFilter/HeaderFilter.css +160 -0
  47. package/lib/components/HeaderFilter/HeaderFilterCell.tsx +370 -0
  48. package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +124 -0
  49. package/lib/components/HeaderFilter/HeaderFilterRow.tsx +157 -0
  50. package/lib/components/Toolbar/GridToolbar.tsx +51 -0
  51. package/lib/hooks/core/gridApiPlaceholder.ts +8 -0
  52. package/lib/hooks/core/gridToolbarHostContext.ts +5 -0
  53. package/lib/hooks/core/useGridApiMethods.ts +39 -0
  54. package/lib/hooks/core/useGridAriaRows.ts +5 -3
  55. package/lib/hooks/core/useGridCellSelection.ts +77 -11
  56. package/lib/hooks/core/useGridColumnsPanel.ts +24 -1
  57. package/lib/hooks/core/useGridControlledState.ts +27 -0
  58. package/lib/hooks/core/useGridFillHandle.ts +323 -0
  59. package/lib/hooks/core/useGridKeyboardNavigation.ts +29 -10
  60. package/lib/hooks/core/useGridToolbarProps.ts +19 -0
  61. package/lib/hooks/features/useGridAiAssistant.ts +284 -0
  62. package/lib/hooks/features/useGridBatchEdit.ts +2 -2
  63. package/lib/index.ts +3 -0
  64. package/lib/styles/opengridx.css +2 -0
  65. package/lib/theme/DataGridThemeProvider.tsx +2 -0
  66. package/lib/theme/types.ts +2 -0
  67. package/lib/types/index.ts +219 -1
  68. package/lib/utils/aiAssistant.ts +205 -0
  69. package/lib/utils/aiBrand.ts +17 -0
  70. package/lib/utils/cellSelection.ts +25 -5
  71. package/lib/utils/editing/fill.ts +238 -0
  72. package/lib/utils/filtering/index.ts +14 -0
  73. package/lib/utils/focus/index.ts +9 -3
  74. package/lib/utils/headerFilters.ts +189 -0
  75. package/llms.txt +44 -4
  76. package/package.json +1 -1
package/dist/ai.d.ts CHANGED
@@ -1,3 +1,26 @@
1
+ declare type ApiRef = {
2
+ readonly current: GridAgentApi | null | undefined;
3
+ };
4
+
5
+ /**
6
+ * Tools for an AI agent: `get_grid_state`, `set_filter`, `set_sort`, `set_grouping`, `set_aggregation`,
7
+ * `set_column_visibility`, `clear_filters`, and `get_row_summary` only with `allowRowAccess: true` (the
8
+ * one tool that sends row values). Each tool is `{ name, description, inputSchema, execute }`; inputs are
9
+ * checked with `validateGridAiState` and applied through `apiRef` only when valid. `execute` never throws.
10
+ * @since v3.5
11
+ */
12
+ export declare function createGridAgentTools<C extends GridAiColumn>(apiRef: ApiRef, columns: readonly C[], options?: GridAgentToolsOptions): GridAgentTool[];
13
+
14
+ /**
15
+ * Builds `aiAssistant.onPrompt` for your own model. On each prompt it calls
16
+ * `callModel({ prompt, schema, currentState, history }, { signal })`, reads the reply (a state object,
17
+ * a JSON string, or a fenced JSON block inside text), validates it with `validateGridAiState` and
18
+ * returns the branded result. A `callModel` throw or rejection becomes
19
+ * `{ state: {}, errors: [{ path: '', message }] }`; an abort rejects with an `AbortError`.
20
+ * @since v3.5
21
+ */
22
+ export declare function createGridAiPromptHandler<C extends GridAiColumn>(options: GridAiPromptHandlerOptions<C>): (prompt: string, context: GridAiPromptContext) => Promise<GridAiPromptResult>;
23
+
1
24
  /**
2
25
  * A JSON Schema (draft 2020-12) of the grid state a model may return for these columns: filter,
3
26
  * sort, grouping, aggregation, pivot and column visibility, each limited to the fields, operators
@@ -7,10 +30,123 @@
7
30
  */
8
31
  export declare function getGridAiSchema<C extends GridAiColumn>(columns: readonly C[], options?: GridAiSchemaOptions): GridAiSchema;
9
32
 
33
+ /**
34
+ * The part of `GridApi` the agent tools use. A grid's `apiRef.current` fits it.
35
+ * @since v3.5
36
+ */
37
+ export declare interface GridAgentApi {
38
+ getGridAiState: () => GridAiState;
39
+ setFilterModel: (model: NonNullable<GridAiState['filterModel']>) => void;
40
+ setSortModel: (model: NonNullable<GridAiState['sortModel']>) => void;
41
+ setRowGroupingModel: (model: NonNullable<GridAiState['rowGroupingModel']>) => void;
42
+ setAggregationModel: (model: NonNullable<GridAiState['aggregationModel']>) => void;
43
+ setColumnVisibilityModel: (model: NonNullable<GridAiState['columnVisibilityModel']>) => void;
44
+ getAllFilteredRows: () => object[];
45
+ }
46
+
47
+ /**
48
+ * A tool in the shape most tool-calling APIs take: `name`, `description`, a JSON Schema for the input
49
+ * and `execute`. No SDK is involved. @since v3.5
50
+ */
51
+ export declare interface GridAgentTool {
52
+ name: string;
53
+ description: string;
54
+ inputSchema: GridAiJsonSchema;
55
+ execute: (input: unknown) => Promise<GridAgentToolResult>;
56
+ }
57
+
58
+ /** What every tool's `execute` resolves with. It never throws or rejects. @since v3.5 */
59
+ export declare interface GridAgentToolResult {
60
+ ok: boolean;
61
+ /** The state the tool applied. */
62
+ applied?: GridAiState;
63
+ /** Why nothing was applied. */
64
+ errors?: GridAiValidationError[];
65
+ /** `get_grid_state`: the current models. */
66
+ state?: GridAiState;
67
+ /** `get_grid_state`: the columns the tools know. */
68
+ columns?: {
69
+ field: string;
70
+ headerName: string;
71
+ type: string;
72
+ }[];
73
+ /** `get_row_summary`: the number of rows that pass the filter. */
74
+ rowCount?: number;
75
+ /** `get_row_summary`: the first rows, limited to the allowed fields. */
76
+ rows?: Record<string, unknown>[];
77
+ }
78
+
79
+ /** Options of `createGridAgentTools`. @since v3.5 */
80
+ export declare interface GridAgentToolsOptions {
81
+ /** Adds `get_row_summary`, the one tool that sends row values to the model. Default: false. */
82
+ allowRowAccess?: boolean;
83
+ /** Fields `get_row_summary` returns. Default: every column the tools were given. */
84
+ fields?: readonly string[];
85
+ /** Most rows `get_row_summary` returns. Default: 20. */
86
+ maxRows?: number;
87
+ }
88
+
10
89
  declare interface GridAggregationModel {
11
90
  [field: string]: string;
12
91
  }
13
92
 
93
+ /** The `aiAssistant` prop: the "Ask AI" prompt panel. @since v3.5 */
94
+ export declare interface GridAiAssistantOptions {
95
+ /**
96
+ * Answers a prompt. Call your own model and return the validated result: `createGridAiPromptHandler`
97
+ * from `@opencorestack/opengridx/ai` builds this function, or call `validateGridAiState` yourself.
98
+ */
99
+ onPrompt: (prompt: string, context: GridAiPromptContext) => Promise<GridAiPromptResult>;
100
+ /** Placeholder of the prompt input. Default: `"Ask about this table…"` */
101
+ placeholder?: string;
102
+ /** Example prompts shown as clickable chips. */
103
+ suggestions?: string[];
104
+ /** Shows a microphone button where the browser has speech recognition; the microphone is asked for only on click. Default: true. */
105
+ voice?: boolean;
106
+ /** The parts of the state the assistant may change. Default: every part. */
107
+ parts?: GridAiPart[];
108
+ }
109
+
110
+ /** Props of `slots.aiAssistantPanel` and the built-in panel, rendered while the panel is open. @since v3.5 */
111
+ export declare interface GridAiAssistantPanelProps {
112
+ status: GridAiAssistantStatus;
113
+ /** The status line: "Thinking…", "Applied 3 changes", an error. */
114
+ statusText: string;
115
+ /** The assistant's reply text (`result.message`). */
116
+ message: string | null;
117
+ /** What the last reply asked for that was ignored. */
118
+ errors: GridAiValidationError[];
119
+ /** The changes the last reply applied. */
120
+ chips: GridAiChip[];
121
+ /** Earlier prompts of this session, oldest first. */
122
+ history: GridAiHistoryEntry[];
123
+ suggestions: string[];
124
+ placeholder: string;
125
+ /** `voice` is on and this browser has speech recognition. */
126
+ voiceAvailable: boolean;
127
+ /** Undo can restore the state from before the last reply. */
128
+ canUndo: boolean;
129
+ submit: (prompt: string) => void;
130
+ /** Aborts the running request. */
131
+ stop: () => void;
132
+ /** Restores the state from before the last reply. */
133
+ undo: () => void;
134
+ /** Re-applies the last reply without this change. */
135
+ removeChip: (id: string) => void;
136
+ /** Closes the panel; focus returns to where it was before it opened. */
137
+ close: () => void;
138
+ }
139
+
140
+ /** The assistant panel's state. @since v3.5 */
141
+ export declare type GridAiAssistantStatus = 'idle' | 'running' | 'applied' | 'stopped' | 'error';
142
+
143
+ /** One change the assistant applied, shown as a removable chip. @since v3.5 */
144
+ export declare interface GridAiChip {
145
+ id: string;
146
+ part: GridAiPart;
147
+ label: string;
148
+ }
149
+
14
150
  /**
15
151
  * The column properties the AI toolkit reads. A `GridColDef` (typed or untyped) is accepted as is;
16
152
  * renderers, getters and formatters are never called, and no row data is read.
@@ -37,12 +173,30 @@ export declare interface GridAiColumn {
37
173
  isSpacer?: boolean;
38
174
  }
39
175
 
176
+ /** One earlier prompt of this session and the state the assistant applied for it. @since v3.5 */
177
+ export declare interface GridAiHistoryEntry {
178
+ prompt: string;
179
+ applied: GridAiState;
180
+ }
181
+
40
182
  /** A JSON Schema (draft 2020-12) node. */
41
183
  export declare type GridAiJsonSchema = {
42
184
  [keyword: string]: unknown;
43
185
  };
44
186
 
45
- /** A part of the grid state the model may change. */
187
+ /** What `callModel` receives: send it to your model. @since v3.5 */
188
+ export declare interface GridAiModelRequest {
189
+ prompt: string;
190
+ /** `getGridAiSchema(columns, …)`: use it as the structured-output format. */
191
+ schema: GridAiSchema;
192
+ currentState: GridAiState;
193
+ history: {
194
+ prompt: string;
195
+ applied: GridAiState;
196
+ }[];
197
+ }
198
+
199
+ /** A part of the grid state a model may change. @since v3.4 */
46
200
  export declare type GridAiPart = 'filter' | 'sort' | 'grouping' | 'aggregation' | 'pivot' | 'columnVisibility';
47
201
 
48
202
  /** Chooses the parts of the state; `exclude` wins over `include`. Default: every part. */
@@ -51,6 +205,45 @@ export declare interface GridAiPartOptions {
51
205
  exclude?: readonly GridAiPart[];
52
206
  }
53
207
 
208
+ /** What `aiAssistant.onPrompt` receives next to the prompt. @since v3.5 */
209
+ export declare interface GridAiPromptContext {
210
+ /** The grid's current models (`apiRef.getGridAiState()`). */
211
+ currentState: GridAiState;
212
+ /** Earlier prompts of this session, oldest first. */
213
+ history: GridAiHistoryEntry[];
214
+ /** Aborted when the user presses Stop, sends another prompt or closes the panel. */
215
+ signal: AbortSignal;
216
+ }
217
+
218
+ /** Options of `createGridAiPromptHandler`. @since v3.5 */
219
+ export declare interface GridAiPromptHandlerOptions<C extends GridAiColumn = GridAiColumn> {
220
+ columns: readonly C[];
221
+ /**
222
+ * Calls your model. Resolve with its reply: a state object, a JSON string, or text with a fenced JSON
223
+ * block. A string `message` key next to the state, or the text around the JSON, is shown as the reply.
224
+ */
225
+ callModel: (request: GridAiModelRequest, options: {
226
+ signal: AbortSignal;
227
+ }) => Promise<unknown>;
228
+ /** The parts the model may change (schema and validation). Default: every part. */
229
+ parts?: readonly GridAiPart[];
230
+ /** Options for the schema; its `include` / `exclude` also limit validation. */
231
+ schemaOptions?: GridAiSchemaOptions;
232
+ }
233
+
234
+ /**
235
+ * What `aiAssistant.onPrompt` resolves with. The grid applies it only when its `state` comes from
236
+ * `validateGridAiState` (or `createGridAiPromptHandler`), which brands the result and its `state`.
237
+ * @since v3.5
238
+ */
239
+ export declare interface GridAiPromptResult {
240
+ state: GridAiState;
241
+ /** What the validator dropped; shown as "Ignored: …". */
242
+ errors: GridAiValidationError[];
243
+ /** The assistant's reply text, shown in the panel. */
244
+ message?: string;
245
+ }
246
+
54
247
  /** The schema returned by `getGridAiSchema`. `schemaVersion` changes when its shape changes. */
55
248
  export declare interface GridAiSchema extends GridAiJsonSchema {
56
249
  $schema: 'https://json-schema.org/draft/2020-12/schema';
@@ -73,7 +266,7 @@ export declare interface GridAiSchemaOptions extends GridAiPartOptions {
73
266
  examples?: boolean;
74
267
  }
75
268
 
76
- /** The validated state: only parts, fields, operators and values the columns allow. */
269
+ /** Grid state as the AI toolkit reads and writes it. Only the parts present are applied. @since v3.4 */
77
270
  export declare interface GridAiState {
78
271
  filterModel?: GridFilterModel;
79
272
  sortModel?: GridSortItem[];
@@ -85,12 +278,16 @@ export declare interface GridAiState {
85
278
 
86
279
  export declare type GridAiValidateOptions = GridAiPartOptions;
87
280
 
88
- /** One thing the validator dropped or could not use. `path` points into the model output. */
281
+ /** One thing the validator dropped or could not use. `path` points into the model output. @since v3.4 */
89
282
  export declare interface GridAiValidationError {
90
283
  path: string;
91
284
  message: string;
92
285
  }
93
286
 
287
+ /**
288
+ * The validated state and what was dropped. Since v3.5 the result and its `state` carry the brand
289
+ * `Symbol.for('opengridx.ai.validated')` (not enumerable), which the grid's `aiAssistant` checks.
290
+ */
94
291
  export declare interface GridAiValidationResult {
95
292
  state: GridAiState;
96
293
  errors: GridAiValidationError[];
@@ -159,6 +356,9 @@ declare interface GridSortItem {
159
356
  * parts, fields, operators and enum values are dropped, each with an error; values are coerced to
160
357
  * the column type (`"1,200"` → 1200, a date → `"YYYY-MM-DD"`); filter groups left empty are
161
358
  * removed. Never throws, and never returns a field the columns do not allow.
359
+ *
360
+ * The result and its `state` carry the brand `Symbol.for('opengridx.ai.validated')` (not enumerable):
361
+ * the grid's `aiAssistant` applies only a branded state. Spreading the result keeps `state` branded.
162
362
  * @since v3.4
163
363
  */
164
364
  export declare function validateGridAiState<C extends GridAiColumn>(json: unknown, columns: readonly C[], options?: GridAiValidateOptions): GridAiValidationResult;
package/dist/ai.es.js CHANGED
@@ -1 +1,2 @@
1
- const Q=/^(\d{4})-(\d{2})-(\d{2})$/;function D(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const n=e.trim();if(n==="")return null;const i=Q.exec(n);if(i){const r=Number(i[1]),o=Number(i[2])-1,l=Number(i[3]);if(t=new Date(r,o,l),t.getMonth()!==o||t.getDate()!==l)return null}else t=new Date(n)}else return null;return Number.isNaN(t.getTime())?null:t}function R(e){const t=D(e);if(!t)return"";const n=String(t.getFullYear()).padStart(4,"0"),i=String(t.getMonth()+1).padStart(2,"0"),r=String(t.getDate()).padStart(2,"0");return`${n}-${i}-${r}`}function Z(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const K=["sum","avg","count","min","max","unique"],E=["sum","avg","count","min","max"],G=["isEmpty","isNotEmpty"],A=3,x=["filter","sort","grouping","aggregation","pivot","columnVisibility"],W={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function F(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function I(e){const t=Array.isArray(e?.include)?e.include:x,n=Array.isArray(e?.exclude)?e.exclude:[];return x.filter(i=>t.includes(i)&&!n.includes(i))}function C(e){if(!Array.isArray(e))return[];const t=new Set,n=[];for(const i of e){if(!$(i))continue;const r=i.field;typeof r!="string"||r===""||r.startsWith("__")||i.isSpacer===!0||t.has(r)||(t.add(r),n.push(i))}return n}function _(e){return Z(e.type)}const L=e=>e.filterable!==!1,J=e=>e.sortable!==!1,O=e=>e.groupable!==!1,U=e=>e.hideable!==!1;function b(e,t=K){if(e.aggregable===!1)return[];const n=e.type==="number"||e.aggregable===!0?t:t.filter(r=>r==="count"),i=e.availableAggregationFunctions;return Array.isArray(i)?n.filter(r=>i.includes(r)):[...n]}function ee(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const n of e.valueOptions){const i=n!==null&&typeof n=="object"?n.value:n;(typeof i=="string"||typeof i=="number"&&Number.isFinite(i))&&!t.includes(i)&&t.push(i)}return t}const te="OpenGridX grid state",ne=10;function w(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,n=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${n}`}function y(e,t,n){return n.descriptions?{...e,description:t}:e}function re(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(n=>typeof n=="string"||typeof n=="boolean"||typeof n=="number"&&Number.isFinite(n)).slice(0,ne)}function ie(e,t){let n;switch(e.type){case"number":n={type:"number"};break;case"date":n={type:"string",format:"date"};break;case"boolean":n={type:"boolean"};break;case"singleSelect":{const r=ee(e),o=r.length>0?{enum:r}:{type:"string"};n={anyOf:[o,{type:"array",items:o}]};break}default:n={type:"string"}}const i=re(e,t);return i.length>0?{...n,examples:i}:n}function oe(e,t){const n=_(e),i=n.filter(l=>G.includes(l)),r=e.type==="singleSelect"?"; isAnyOf takes an array":"",o=i.length>0?`${i.join(" and ")} take no value${r}`:r.slice(2);return y({type:"object",properties:{field:{const:e.field},operator:{enum:n},value:ie(e,t)},required:["field","operator"],additionalProperties:!1},o?`${w(e)}: ${o}`:w(e),t)}function se(e,t,n){const i=e.filter(L),r={},o={enum:["and","or"]};if(i.length>0){n.filterItem={oneOf:i.map(l=>oe(l,t))};for(let l=A;l>=1;l--){const s=l===A?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${l+1}`}]};n[`filterGroup${l}`]={type:"object",properties:{logicOperator:o,items:{type:"array",items:s}},required:["logicOperator","items"],additionalProperties:!1}}r.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},r.logicOperator=o}return r.quickFilterValues=y({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:r,additionalProperties:!1}}function M(e){return{enum:e.map(t=>t.field)}}function le(e,t,n,i){switch(e){case"filter":return se(t,n,i);case"sort":{const r=t.filter(J);return r.length===0?null:y({type:"array",items:{type:"object",properties:{field:M(r),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",n)}case"grouping":{const r=t.filter(O);return r.length===0?null:y({type:"array",items:M(r),uniqueItems:!0},"Outermost first",n)}case"aggregation":{const r={};for(const o of t){const l=b(o);l.length>0&&(r[o.field]=y({enum:l},w(o),n))}return Object.keys(r).length===0?null:{type:"object",properties:r,additionalProperties:!1}}case"pivot":{const r=t.filter(O),o=t.map(s=>({col:s,fns:b(s,E)})).filter(s=>s.fns.length>0).map(({col:s,fns:u})=>y({type:"object",properties:{field:{const:s.field},aggFn:{enum:u}},required:["field","aggFn"],additionalProperties:!1},w(s),n));if(r.length===0||o.length===0)return null;const l={type:"array",items:M(r),uniqueItems:!0};return{type:"object",properties:{rowFields:l,columnFields:l,valueFields:{type:"array",items:{oneOf:o}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const r={};for(const o of t.filter(U))r[o.field]=y({type:"boolean"},w(o),n);return Object.keys(r).length===0?null:y({type:"object",properties:r,additionalProperties:!1},"false hides a column",n)}}}function ue(e,t={}){const n={descriptions:t.descriptions!==!1,examples:t.examples!==!1},i=C(e),r={},o={};for(const s of I(t)){const u=le(s,i,n,r);u&&(o[W[s]]=u)}const l={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:te,type:"object",properties:o,additionalProperties:!1};return n.descriptions&&(l.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(r).length>0&&(l.$defs=r),l}const m={ok:!1},p=e=>({ok:!0,value:e}),ae=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,ce=/^\.\d+(?:e[+-]?\d+)?$/i,fe=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,pe=/[\p{Sc}\s]/gu;function de(e){let t=e.replace(pe,"");if(t==="")return e.trim()===""?p(null):m;let n=!1;t.startsWith("(")&&t.endsWith(")")&&(n=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(n=!n),t=t.slice(1));let i=!1;if(t.endsWith("%")&&(i=!0,t=t.slice(0,-1)),fe.test(t))t=t.split(",").join("");else if(!ae.test(t)&&!ce.test(t))return m;let r=Number(t);return Number.isFinite(r)?(i&&(r/=100),n&&(r=-r),p(r)):m}function ge(e,t){const n=e.trim();if(n==="")return p(null);const i=D(n);return i?p(i):m}const ye=new Set(["true","yes","1"]),me=new Set(["false","no","0"]);function $e(e,t){const n=e.trim().toLowerCase();return n===""?p(null):ye.has(n)?p(!0):me.has(n)?p(!1):m}function be(e,t){const n=e.trim();if(n==="")return p(null);if(!t||t.length===0)return m;const i=n.toLowerCase();for(const r of t){const o=typeof r=="object"?r.value:r;if(String(o).toLowerCase()===i)return p(o)}for(const r of t)if(typeof r=="object"&&r.label.toLowerCase()===i)return p(r.value);return m}const c=e=>({ok:!0,value:e}),d={ok:!1,reason:"invalidValue"},N=e=>e.ok?c(e.value):d;function j(e){return e==null||typeof e=="string"&&e.trim()===""}function he(e){return e==null?c(""):typeof e=="string"?c(e):typeof e=="number"||typeof e=="boolean"?c(String(e)):d}function ve(e){return j(e)?c(null):typeof e=="number"?Number.isFinite(e)?c(e):d:typeof e!="string"?d:N(de(e))}function Fe(e){return j(e)?c(null):e instanceof Date?Number.isNaN(e.getTime())?d:c(e):typeof e=="number"?Number.isFinite(e)?c(new Date(e)):d:typeof e!="string"?d:N(ge(e))}function T(e){return j(e)?c(null):typeof e=="boolean"?c(e):typeof e!="string"&&typeof e!="number"?d:N($e(String(e)))}function Oe(e,t){if(j(e))return c(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return d;const n=t.valueOptions;return!n||n.length===0?c(e):N(be(String(e),n))}function we(e,t){switch(t.type){case"number":return ve(e);case"date":return Fe(e);case"boolean":return T(e);case"singleSelect":return Oe(e,t);default:return he(e)}}const V=200,ke=100;function a(e,t,n){return e.errors.length<ke&&e.errors.push({path:t,message:n}),null}function f(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function B(e){return typeof e=="string"?e.trim().toLowerCase():null}function q(e){return Object.keys(e).filter(t=>t!=="__proto__")}function h(e,t,n,i,r){const o=typeof t=="string"?e.byField.get(t):void 0;return o?i(o)?o:a(e,n,`Field ${f(t)} cannot be ${r}`):a(e,n,`Unknown field ${f(t)}`)}function v(e,t,n){if(t==null)return[];const i=Array.isArray(t)?t:[t];return i.length>V&&a(e,n,`Only the first ${V} entries are read`),i.slice(0,V)}function k(e,t,n,i){const r=B(t);return i.find(o=>o.toLowerCase()===r)??a(e,n,`${f(t)} is not one of ${i.join(", ")}`)}function X(e,t,n,i){const r=we(t,n);if(!r.ok)return a(e,i,`${f(t)} is not a valid ${n.type??"string"} value`);const o=r.value instanceof Date?R(r.value):r.value;return o==null||o===""?a(e,i,"Missing value"):{v:o}}function Ae(e,t,n){const i=h(e,t.field,`${n}.field`,L,"filtered");if(!i)return null;const r=k(e,t.operator,`${n}.operator`,_(i));if(r===null)return null;const o={field:i.field,operator:r};if(G.includes(o.operator))return o;const l=F(t,"value")?t.value:void 0;if(o.operator==="isAnyOf"){const u=[];return v(e,l,`${n}.value`).forEach((g,H)=>{const S=X(e,g,i,`${n}.value[${H}]`);S&&!u.includes(S.v)&&u.push(S.v)}),u.length===0?a(e,`${n}.value`,"No valid values"):(o.value=u,o)}if(Array.isArray(l))return a(e,`${n}.value`,"Expected one value, not a list");const s=X(e,l,i,`${n}.value`);return s?(o.value=s.v,o):null}function Y(e,t,n){return t===void 0?null:k(e,t,n,["and","or"])}function z(e,t,n,i){const r=[];return v(e,t,n).forEach((o,l)=>{const s=`${n}[${l}]`;if(!$(o))a(e,s,`Expected a filter, got ${f(o)}`);else if(F(o,"items")||F(o,"logicOperator")){if(i>=A){a(e,s,`Groups nest at most ${A} levels`);return}const u=z(e,o.items,`${s}.items`,i+1);u.length===0?a(e,s,"Empty group removed"):r.push({logicOperator:Y(e,o.logicOperator,`${s}.logicOperator`)??"and",items:u})}else{const u=Ae(e,o,s);u&&r.push(u)}}),r}function Ne(e,t,n){const i={};F(t,"items")&&(i.items=z(e,t.items,`${n}.items`,0));const r=Y(e,t.logicOperator,`${n}.logicOperator`);return r&&(i.logicOperator=r),F(t,"quickFilterValues")&&(i.quickFilterValues=[],v(e,t.quickFilterValues,`${n}.quickFilterValues`).forEach((o,l)=>{const s=typeof o=="string"||typeof o=="number"?String(o).trim():null;s===null?a(e,`${n}.quickFilterValues[${l}]`,`Expected text, got ${f(o)}`):s!==""&&i.quickFilterValues?.push(s)})),i}function je(e,t,n){const i=[];return v(e,t,n).forEach((r,o)=>{const l=`${n}[${o}]`;if(!$(r))return a(e,l,`Expected { field, sort }, got ${f(r)}`);const s=h(e,r.field,`${l}.field`,J,"sorted"),u=s&&k(e,r.sort,`${l}.sort`,["asc","desc"]);!s||!u||(i.some(g=>g.field===s.field)?a(e,l,`${f(s.field)} is sorted twice`):i.push({field:s.field,sort:u}))}),i}function P(e,t,n,i,r){const o=[];return v(e,t,n).forEach((l,s)=>{const u=h(e,l,`${n}[${s}]`,i,r);u&&!o.includes(u.field)&&o.push(u.field)}),o}const Se=e=>b(e).length>0,Ee=e=>b(e,E).length>0;function xe(e,t,n){const i={};for(const r of q(t)){if(B(t[r])==="none")continue;const o=h(e,r,`${n}.${r}`,Se,"aggregated"),l=o&&k(e,t[r],`${n}.${r}`,b(o));o&&l&&(i[o.field]=l)}return i}function Me(e,t,n){const i=[];return v(e,t.valueFields,`${n}.valueFields`).forEach((r,o)=>{const l=`${n}.valueFields[${o}]`;if(!$(r))return a(e,l,`Expected { field, aggFn }, got ${f(r)}`);const s=h(e,r.field,`${l}.field`,Ee,"a pivot value"),u=s&&k(e,r.aggFn,`${l}.aggFn`,b(s,E));s&&u&&!i.some(g=>g.field===s.field&&g.aggFn===u)&&i.push({field:s.field,aggFn:u})}),{rowFields:P(e,t.rowFields,`${n}.rowFields`,O,"a pivot label"),columnFields:P(e,t.columnFields,`${n}.columnFields`,O,"a pivot label"),valueFields:i}}function Ve(e,t,n){const i={};for(const r of q(t)){const o=h(e,r,`${n}.${r}`,U,"hidden"),l=T(t[r]);o&&(l.ok&&l.value!==null?i[o.field]=l.value:a(e,`${n}.${r}`,`Expected true or false, got ${f(t[r])}`))}return i}function qe(e,t,n,i,r){t==="sort"?r.sortModel=je(e,n,i):t==="grouping"?r.rowGroupingModel=P(e,n,i,O,"grouped"):$(n)?t==="filter"?r.filterModel=Ne(e,n,i):t==="aggregation"?r.aggregationModel=xe(e,n,i):t==="pivot"?r.pivotModel=Me(e,n,i):r.columnVisibilityModel=Ve(e,n,i):a(e,i,`Expected an object, got ${f(n)}`)}function Pe(e,t,n={}){const i={errors:[],byField:new Map},r={};try{for(const s of C(t))i.byField.set(s.field,s);let o=e;if(typeof e=="string")try{o=JSON.parse(e)}catch{return a(i,"","Not valid JSON"),{state:r,errors:i.errors}}if(!$(o))return a(i,"",`Expected an object, got ${f(o)}`),{state:r,errors:i.errors};const l=I(n);for(const s of q(o)){const u=x.find(g=>W[g]===s);s==="schemaVersion"?o[s]!==1&&a(i,s,`Unsupported schemaVersion ${f(o[s])}`):u?l.includes(u)?qe(i,u,o[s],s,r):a(i,s,"Part not allowed"):a(i,s,"Unknown part")}}catch{return a(i,"","Could not read the input"),{state:{},errors:i.errors}}return{state:r,errors:i.errors}}export{ue as getGridAiSchema,Pe as validateGridAiState};
1
+ const K=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||K.has(e)||(K.add(e),console.warn(`[OpenGridX] ${t}.`,r))}function pe(e,t,r){const o=e[t];if(!r?.valueGetter)return{value:o};try{return{value:r.valueGetter({row:e,field:t,value:o})}}catch(n){return{value:void 0,error:n??new Error("valueGetter failed")}}}function ge(e,t,r){const{value:o,error:n}=pe(e,t,r);return n!==void 0&&de(`valueGetter:${t}`,`valueGetter for column "${t}" threw; the value is read as undefined`,n),o}const me=/^(\d{4})-(\d{2})-(\d{2})$/;function Q(e){if(e==null)return null;let t;if(e instanceof Date)t=e;else if(typeof e=="number")t=new Date(e);else if(typeof e=="string"){const r=e.trim();if(r==="")return null;const o=me.exec(r);if(o){const n=Number(o[1]),i=Number(o[2])-1,s=Number(o[3]);if(t=new Date(n,i,s),t.getMonth()!==i||t.getDate()!==s)return null}else t=new Date(r)}else return null;return Number.isNaN(t.getTime())?null:t}function ye(e){const t=Q(e);if(!t)return"";const r=String(t.getFullYear()).padStart(4,"0"),o=String(t.getMonth()+1).padStart(2,"0"),n=String(t.getDate()).padStart(2,"0");return`${r}-${o}-${n}`}function he(e){switch(e){case"number":return["=","!=",">",">=","<","<=","isEmpty","isNotEmpty"];case"date":return["is","not","after","onOrAfter","before","onOrBefore","isEmpty","isNotEmpty"];case"boolean":return["is"];case"singleSelect":return["isAnyOf","is","not"];default:return["contains","equals","startsWith","endsWith","isEmpty","isNotEmpty"]}}const be=["sum","avg","count","min","max","unique"],W=["sum","avg","count","min","max"],Z=["isEmpty","isNotEmpty"],q=3,I=["filter","sort","grouping","aggregation","pivot","columnVisibility"],L={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function v(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function b(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function ee(e){const t=Array.isArray(e?.include)?e.include:I,r=Array.isArray(e?.exclude)?e.exclude:[];return I.filter(o=>t.includes(o)&&!r.includes(o))}function U(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const o of e){if(!b(o))continue;const n=o.field;typeof n!="string"||n===""||n.startsWith("__")||o.isSpacer===!0||t.has(n)||(t.add(n),r.push(o))}return r}function te(e){return he(e.type)}const re=e=>e.filterable!==!1,ne=e=>e.sortable!==!1,k=e=>e.groupable!==!1,oe=e=>e.hideable!==!1;function S(e,t=be){if(e.aggregable===!1)return[];const r=e.type==="number"||e.aggregable===!0?t:t.filter(n=>n==="count"),o=e.availableAggregationFunctions;return Array.isArray(o)?r.filter(n=>o.includes(n)):[...r]}function $e(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const r of e.valueOptions){const o=r!==null&&typeof r=="object"?r.value:r;(typeof o=="string"||typeof o=="number"&&Number.isFinite(o))&&!t.includes(o)&&t.push(o)}return t}const ve="OpenGridX grid state",we=10;function N(e){const t=typeof e.headerName=="string"&&e.headerName!==""?e.headerName:e.field,r=typeof e.description=="string"&&e.description!==""?` (${e.description})`:"";return`${t}${r}`}function M(e,t,r){return r.descriptions?{...e,description:t}:e}function Me(e,t){return!t.examples||!Array.isArray(e.aiExamples)?[]:e.aiExamples.filter(r=>typeof r=="string"||typeof r=="boolean"||typeof r=="number"&&Number.isFinite(r)).slice(0,we)}function Ae(e,t){let r;switch(e.type){case"number":r={type:"number"};break;case"date":r={type:"string",format:"date"};break;case"boolean":r={type:"boolean"};break;case"singleSelect":{const n=$e(e),i=n.length>0?{enum:n}:{type:"string"};r={anyOf:[i,{type:"array",items:i}]};break}default:r={type:"string"}}const o=Me(e,t);return o.length>0?{...r,examples:o}:r}function Oe(e,t){const r=te(e),o=r.filter(s=>Z.includes(s)),n=e.type==="singleSelect"?"; isAnyOf takes an array":"",i=o.length>0?`${o.join(" and ")} take no value${n}`:n.slice(2);return M({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:Ae(e,t)},required:["field","operator"],additionalProperties:!1},i?`${N(e)}: ${i}`:N(e),t)}function Fe(e,t,r){const o=e.filter(re),n={},i={enum:["and","or"]};if(o.length>0){r.filterItem={oneOf:o.map(s=>Oe(s,t))};for(let s=q;s>=1;s--){const l=s===q?{$ref:"#/$defs/filterItem"}:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:`#/$defs/filterGroup${s+1}`}]};r[`filterGroup${s}`]={type:"object",properties:{logicOperator:i,items:{type:"array",items:l}},required:["logicOperator","items"],additionalProperties:!1}}n.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},n.logicOperator=i}return n.quickFilterValues=M({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:n,additionalProperties:!1}}function J(e){return{enum:e.map(t=>t.field)}}function Se(e,t,r,o){switch(e){case"filter":return Fe(t,r,o);case"sort":{const n=t.filter(ne);return n.length===0?null:M({type:"array",items:{type:"object",properties:{field:J(n),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const n=t.filter(k);return n.length===0?null:M({type:"array",items:J(n),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const n={};for(const i of t){const s=S(i);s.length>0&&(n[i.field]=M({enum:s},N(i),r))}return Object.keys(n).length===0?null:{type:"object",properties:n,additionalProperties:!1}}case"pivot":{const n=t.filter(k),i=t.map(l=>({col:l,fns:S(l,W)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>M({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},N(l),r));if(n.length===0||i.length===0)return null;const s={type:"array",items:J(n),uniqueItems:!0};return{type:"object",properties:{rowFields:s,columnFields:s,valueFields:{type:"array",items:{oneOf:i}}},required:["rowFields","columnFields","valueFields"],additionalProperties:!1}}case"columnVisibility":{const n={};for(const i of t.filter(oe))n[i.field]=M({type:"boolean"},N(i),r);return Object.keys(n).length===0?null:M({type:"object",properties:n,additionalProperties:!1},"false hides a column",r)}}}function X(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},o=U(e),n={},i={};for(const l of ee(t)){const a=Se(l,o,r,n);a&&(i[L[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:ve,type:"object",properties:i,additionalProperties:!1};return r.descriptions&&(s.description="Grid state to apply. Include only the parts the user asked to change."),Object.keys(n).length>0&&(s.$defs=n),s}const je=Symbol.for("opengridx.ai.validated");function _(e){return Object.defineProperty(e,je,{value:!0,enumerable:!1}),e}const O={ok:!1},$=e=>({ok:!0,value:e}),xe=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,Ee=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,Ne=/[\p{Sc}\s]/gu;function Ge(e){let t=e.replace(Ne,"");if(t==="")return e.trim()===""?$(null):O;let r=!1;t.startsWith("(")&&t.endsWith(")")&&(r=!0,t=t.slice(1,-1)),(t.startsWith("-")||t.startsWith("+")||t.startsWith("\u2212"))&&(t[0]!=="+"&&(r=!r),t=t.slice(1));let o=!1;if(t.endsWith("%")&&(o=!0,t=t.slice(0,-1)),ke.test(t))t=t.split(",").join("");else if(!xe.test(t)&&!Ee.test(t))return O;let n=Number(t);return Number.isFinite(n)?(o&&(n/=100),r&&(n=-n),$(n)):O}function Ve(e,t){const r=e.trim();if(r==="")return $(null);const o=Q(r);return o?$(o):O}const qe=new Set(["true","yes","1"]),_e=new Set(["false","no","0"]);function Pe(e,t){const r=e.trim().toLowerCase();return r===""?$(null):qe.has(r)?$(!0):_e.has(r)?$(!1):O}function Ce(e,t){const r=e.trim();if(r==="")return $(null);if(!t||t.length===0)return O;const o=r.toLowerCase();for(const n of t){const i=typeof n=="object"?n.value:n;if(String(i).toLowerCase()===o)return $(i)}for(const n of t)if(typeof n=="object"&&n.label.toLowerCase()===o)return $(n.value);return O}const d=e=>({ok:!0,value:e}),w={ok:!1,reason:"invalidValue"},P=e=>e.ok?d(e.value):w;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function De(e){return e==null?d(""):typeof e=="string"?d(e):typeof e=="number"||typeof e=="boolean"?d(String(e)):w}function Te(e){return C(e)?d(null):typeof e=="number"?Number.isFinite(e)?d(e):w:typeof e!="string"?w:P(Ge(e))}function Re(e){return C(e)?d(null):e instanceof Date?Number.isNaN(e.getTime())?w:d(e):typeof e=="number"?Number.isFinite(e)?d(new Date(e)):w:typeof e!="string"?w:P(Ve(e))}function ie(e){return C(e)?d(null):typeof e=="boolean"?d(e):typeof e!="string"&&typeof e!="number"?w:P(Pe(String(e)))}function We(e,t){if(C(e))return d(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return w;const r=t.valueOptions;return!r||r.length===0?d(e):P(Ce(String(e),r))}function Ie(e,t){switch(t.type){case"number":return Te(e);case"date":return Re(e);case"boolean":return ie(e);case"singleSelect":return We(e,t);default:return De(e)}}const B=200,Le=100;function u(e,t,r){return e.errors.length<Le&&e.errors.push({path:t,message:r}),null}function p(e){return typeof e=="string"?JSON.stringify(e.slice(0,60)):e===null||typeof e!="object"?String(e):Array.isArray(e)?"a list":"an object"}function se(e){return typeof e=="string"?e.trim().toLowerCase():null}function H(e){return Object.keys(e).filter(t=>t!=="__proto__")}function j(e,t,r,o,n){const i=typeof t=="string"?e.byField.get(t):void 0;return i?o(i)?i:u(e,r,`Field ${p(t)} cannot be ${n}`):u(e,r,`Unknown field ${p(t)}`)}function x(e,t,r){if(t==null)return[];const o=Array.isArray(t)?t:[t];return o.length>B&&u(e,r,`Only the first ${B} entries are read`),o.slice(0,B)}function G(e,t,r,o){const n=se(t);return o.find(i=>i.toLowerCase()===n)??u(e,r,`${p(t)} is not one of ${o.join(", ")}`)}function le(e,t,r,o){const n=Ie(t,r);if(!n.ok)return u(e,o,`${p(t)} is not a valid ${r.type??"string"} value`);const i=n.value instanceof Date?ye(n.value):n.value;return i==null||i===""?u(e,o,"Missing value"):{v:i}}function Ue(e,t,r){const o=j(e,t.field,`${r}.field`,re,"filtered");if(!o)return null;const n=G(e,t.operator,`${r}.operator`,te(o));if(n===null)return null;const i={field:o.field,operator:n};if(Z.includes(i.operator))return i;const s=v(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return x(e,s,`${r}.value`).forEach((c,A)=>{const g=le(e,c,o,`${r}.value[${A}]`);g&&!a.includes(g.v)&&a.push(g.v)}),a.length===0?u(e,`${r}.value`,"No valid values"):(i.value=a,i)}if(Array.isArray(s))return u(e,`${r}.value`,"Expected one value, not a list");const l=le(e,s,o,`${r}.value`);return l?(i.value=l.v,i):null}function ae(e,t,r){return t===void 0?null:G(e,t,r,["and","or"])}function ue(e,t,r,o){const n=[];return x(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!b(i))u(e,l,`Expected a filter, got ${p(i)}`);else if(v(i,"items")||v(i,"logicOperator")){if(o>=q){u(e,l,`Groups nest at most ${q} levels`);return}const a=ue(e,i.items,`${l}.items`,o+1);a.length===0?u(e,l,"Empty group removed"):n.push({logicOperator:ae(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Ue(e,i,l);a&&n.push(a)}}),n}function Je(e,t,r){const o={};v(t,"items")&&(o.items=ue(e,t.items,`${r}.items`,0));const n=ae(e,t.logicOperator,`${r}.logicOperator`);return n&&(o.logicOperator=n),v(t,"quickFilterValues")&&(o.quickFilterValues=[],x(e,t.quickFilterValues,`${r}.quickFilterValues`).forEach((i,s)=>{const l=typeof i=="string"||typeof i=="number"?String(i).trim():null;l===null?u(e,`${r}.quickFilterValues[${s}]`,`Expected text, got ${p(i)}`):l!==""&&o.quickFilterValues?.push(l)})),o}function Xe(e,t,r){const o=[];return x(e,t,r).forEach((n,i)=>{const s=`${r}[${i}]`;if(!b(n))return u(e,s,`Expected { field, sort }, got ${p(n)}`);const l=j(e,n.field,`${s}.field`,ne,"sorted"),a=l&&G(e,n.sort,`${s}.sort`,["asc","desc"]);!l||!a||(o.some(c=>c.field===l.field)?u(e,s,`${p(l.field)} is sorted twice`):o.push({field:l.field,sort:a}))}),o}function Y(e,t,r,o,n){const i=[];return x(e,t,r).forEach((s,l)=>{const a=j(e,s,`${r}[${l}]`,o,n);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Be=e=>S(e).length>0,He=e=>S(e,W).length>0;function Ye(e,t,r){const o={};for(const n of H(t)){if(se(t[n])==="none")continue;const i=j(e,n,`${r}.${n}`,Be,"aggregated"),s=i&&G(e,t[n],`${r}.${n}`,S(i));i&&s&&(o[i.field]=s)}return o}function ze(e,t,r){const o=[];return x(e,t.valueFields,`${r}.valueFields`).forEach((n,i)=>{const s=`${r}.valueFields[${i}]`;if(!b(n))return u(e,s,`Expected { field, aggFn }, got ${p(n)}`);const l=j(e,n.field,`${s}.field`,He,"a pivot value"),a=l&&G(e,n.aggFn,`${s}.aggFn`,S(l,W));l&&a&&!o.some(c=>c.field===l.field&&c.aggFn===a)&&o.push({field:l.field,aggFn:a})}),{rowFields:Y(e,t.rowFields,`${r}.rowFields`,k,"a pivot label"),columnFields:Y(e,t.columnFields,`${r}.columnFields`,k,"a pivot label"),valueFields:o}}function Ke(e,t,r){const o={};for(const n of H(t)){const i=j(e,n,`${r}.${n}`,oe,"hidden"),s=ie(t[n]);i&&(s.ok&&s.value!==null?o[i.field]=s.value:u(e,`${r}.${n}`,`Expected true or false, got ${p(t[n])}`))}return o}function Qe(e,t,r,o,n){t==="sort"?n.sortModel=Xe(e,r,o):t==="grouping"?n.rowGroupingModel=Y(e,r,o,k,"grouped"):b(r)?t==="filter"?n.filterModel=Je(e,r,o):t==="aggregation"?n.aggregationModel=Ye(e,r,o):t==="pivot"?n.pivotModel=ze(e,r,o):n.columnVisibilityModel=Ke(e,r,o):u(e,o,`Expected an object, got ${p(r)}`)}function z(e,t,r={}){const o=Ze(e,t,r);return _(o.state),_(o)}function Ze(e,t,r){const o={errors:[],byField:new Map},n={};try{for(const l of U(t))o.byField.set(l.field,l);let i=e;if(typeof e=="string")try{i=JSON.parse(e)}catch{return u(o,"","Not valid JSON"),{state:n,errors:o.errors}}if(!b(i))return u(o,"",`Expected an object, got ${p(i)}`),{state:n,errors:o.errors};const s=ee(r);for(const l of H(i)){const a=I.find(c=>L[c]===l);l==="schemaVersion"?i[l]!==1&&u(o,l,`Unsupported schemaVersion ${p(i[l])}`):a?s.includes(a)?Qe(o,a,i[l],l,n):u(o,l,"Part not allowed"):u(o,l,"Unknown part")}}catch{return u(o,"","Could not read the input"),{state:{},errors:o.errors}}return{state:n,errors:o.errors}}const V="```";function D(){return typeof DOMException=="function"?new DOMException("The operation was aborted.","AbortError"):Object.assign(new Error("The operation was aborted."),{name:"AbortError"})}function et(e){const t=e.trim();if(t.startsWith("{"))return{json:t,message:""};const r=e.indexOf(V),o=r<0?-1:e.indexOf(`
2
+ `,r+V.length),n=o<0?-1:e.indexOf(V,o+1),i=n<0?"":e.slice(r+V.length,o).trim().toLowerCase();return n<0||i!==""&&i!=="json"?{json:null,message:t}:{json:e.slice(o+1,n),message:`${e.slice(0,r)}${e.slice(n+V.length)}`.trim()}}function tt(e){return e instanceof Error&&e.message?e.message:typeof e=="string"&&e?e:"Unknown error"}function ce(e,t){const r={state:_({}),errors:e};return t&&(r.message=t),_(r)}function rt(e,t,r){let o=e,n="";if(typeof e=="string"){const s=et(e);if(s.json===null)return ce([],s.message||void 0);o=s.json,n=s.message}else if(b(e)&&v(e,"message")&&typeof e.message=="string"){const{message:s,...l}=e;o=l,n=s}const i=z(o,t,r);return n&&(i.message=n),i}function nt(e){const{columns:t,callModel:r,parts:o,schemaOptions:n={}}=e,i={include:o??n.include,exclude:n.exclude},s=X(t,{...n,...i});return async(l,a)=>{const c=a?.signal??new AbortController().signal;if(c.aborted)throw D();const A=new Promise((m,y)=>{c.addEventListener("abort",()=>y(D()),{once:!0})});A.catch(()=>{});let g;try{const m={prompt:String(l),schema:s,currentState:a?.currentState??{},history:Array.isArray(a?.history)?a.history:[]};g=await Promise.race([r(m,{signal:c}),A])}catch(m){if(c.aborted)throw D();return ce([{path:"",message:`The model call failed: ${tt(m)}`}])}if(c.aborted)throw D();return rt(g,t,i)}}const ot=20,it=1e3,st=[{name:"set_filter",part:"filter",description:"Replace the filter model of the grid (filters and quick-filter words)."},{name:"set_sort",part:"sort",description:"Replace the sort model of the grid, most significant field first. An empty list clears sorting."},{name:"set_grouping",part:"grouping",description:"Group the rows by these fields, outermost first. An empty list removes grouping."},{name:"set_aggregation",part:"aggregation",description:"Replace the summaries: field to aggregation function. An empty object removes them."},{name:"set_column_visibility",part:"columnVisibility",description:"Show (true) or hide (false) columns. Columns not listed keep their visibility."}];function E(e,t=""){return{ok:!1,errors:[{path:t,message:e}]}}async function T(e){try{return e()}catch(t){return E(t instanceof Error&&t.message?t.message:"The tool failed")}}function R(e){const t=e?.current;return t&&typeof t.getGridAiState=="function"?t:null}function lt(e){return e instanceof Date?Number.isNaN(e.getTime())?null:e.toISOString():typeof e=="number"?Number.isFinite(e)?e:null:typeof e=="string"||typeof e=="boolean"?e:null}function at(e,t,r,o,n){const i=L[o],s=X(t,{include:[o]});if(!v(s.properties,i))return null;const{$schema:l,schemaVersion:a,title:c,description:A,...g}=s;return{name:r,description:n,inputSchema:{...g,required:[i]},execute:m=>T(()=>{const y=R(e);if(!y)return E("The grid is not ready");if(!b(m)||!v(m,i))return E(`Expected an object with "${i}"`,i);const{state:f,errors:F}=z({[i]:m[i]},t,{include:[o]});if(F.length>0)return{ok:!1,errors:F};const h={};return f.filterModel&&y.setFilterModel(h.filterModel=f.filterModel),f.sortModel&&y.setSortModel(h.sortModel=f.sortModel),f.rowGroupingModel&&y.setRowGroupingModel(h.rowGroupingModel=f.rowGroupingModel),f.aggregationModel&&y.setAggregationModel(h.aggregationModel=f.aggregationModel),f.columnVisibilityModel&&(h.columnVisibilityModel={...y.getGridAiState().columnVisibilityModel,...f.columnVisibilityModel},y.setColumnVisibilityModel(h.columnVisibilityModel)),{ok:!0,applied:h}})}}const fe={type:"object",properties:{},additionalProperties:!1};function ut(e,t,r={}){const o=U(t),n=[{name:"get_grid_state",description:"Read the current filter, sort, grouping, aggregation, pivot and column visibility of the grid, and its columns.",inputSchema:fe,execute:()=>T(()=>{const i=R(e);return i?{ok:!0,state:i.getGridAiState(),columns:o.map(s=>({field:s.field,headerName:typeof s.headerName=="string"?s.headerName:s.field,type:typeof s.type=="string"?s.type:"string"}))}:E("The grid is not ready")})}];for(const{name:i,part:s,description:l}of st){const a=at(e,o,i,s,l);a&&n.push(a)}if(n.push({name:"clear_filters",description:"Remove every filter and the quick-filter words.",inputSchema:fe,execute:()=>T(()=>{const i=R(e);if(!i)return E("The grid is not ready");const s={items:[]};return i.setFilterModel(s),{ok:!0,applied:{filterModel:s}}})}),r.allowRowAccess===!0){const i=typeof r.maxRows=="number"&&r.maxRows>=1?Math.min(Math.floor(r.maxRows),it):ot,s=Array.isArray(r.fields)?o.filter(l=>r.fields?.includes(l.field)):o;n.push({name:"get_row_summary",description:`Count the rows that pass the current filter and read the first ones (at most ${i}, fields: ${s.map(l=>l.field).join(", ")}).`,inputSchema:{type:"object",properties:{limit:{type:"integer",minimum:1,maximum:i}},additionalProperties:!1},execute:l=>T(()=>{const a=R(e);if(!a)return E("The grid is not ready");const c=b(l)&&v(l,"limit")?l.limit:i,A=typeof c=="number"&&c>=1?Math.min(Math.floor(c),i):i,g=a.getAllFilteredRows(),m=Array.isArray(g)?g:[],y=m.slice(0,A).map(f=>{const F={};if(!b(f))return F;for(const h of s)F[h.field]=lt(ge(f,h.field,h));return F});return{ok:!0,rowCount:m.length,rows:y}})})}return n}export{ut as createGridAgentTools,nt as createGridAiPromptHandler,X as getGridAiSchema,z as validateGridAiState};