@opencorestack/opengridx 3.5.0 → 3.6.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 (79) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +6 -3
  3. package/dist/ai.cjs +4 -2
  4. package/dist/ai.cjs.map +1 -1
  5. package/dist/ai.d.ts +2 -2
  6. package/dist/ai.es.js +4 -2
  7. package/dist/ai.es.js.map +1 -1
  8. package/dist/index.d.ts +32 -0
  9. package/dist/opengridx.css +1 -1
  10. package/dist/opengridx.es.js +6924 -6691
  11. package/dist/opengridx.es.js.map +1 -1
  12. package/dist/opengridx.umd.js +17 -17
  13. package/dist/opengridx.umd.js.map +1 -1
  14. package/docs/API_REFERENCE.md +14 -1
  15. package/docs/README.md +2 -0
  16. package/docs/architecture/datagrid-orchestration.md +2 -1
  17. package/docs/features/ai-toolkit.md +2 -2
  18. package/docs/features/export-guide.md +2 -0
  19. package/docs/features/header-filters.md +34 -0
  20. package/docs/features/keyboard-navigation.md +2 -0
  21. package/docs/features/nextjs.md +241 -0
  22. package/docs/features/pdf-export.md +2 -0
  23. package/docs/features/pinning.md +2 -0
  24. package/docs/features/rtl.md +58 -0
  25. package/docs/features/virtualization.md +5 -5
  26. package/docs/performance.md +180 -1
  27. package/docs/roadmap.md +5 -3
  28. package/lib/ai/promptHandler.ts +12 -8
  29. package/lib/ai/validate.ts +5 -2
  30. package/lib/components/AiAssistant/GridAiAssistantPanel.css +1 -1
  31. package/lib/components/Cell/Cell.css +29 -9
  32. package/lib/components/Cell/Cell.tsx +2 -2
  33. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +2 -2
  34. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +3 -3
  35. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +18 -2
  36. package/lib/components/DataGrid/DataGrid.css +12 -6
  37. package/lib/components/DataGrid/DataGrid.tsx +9 -1
  38. package/lib/components/DataGrid/GridAggregationFooter.tsx +3 -3
  39. package/lib/components/DataGrid/GridListView.tsx +3 -1
  40. package/lib/components/Header/ColumnMenu.css +1 -1
  41. package/lib/components/Header/ColumnMenu.tsx +14 -6
  42. package/lib/components/Header/Header.css +18 -10
  43. package/lib/components/Header/Header.tsx +16 -9
  44. package/lib/components/Header/headerColumnStyle.ts +3 -2
  45. package/lib/components/HeaderFilter/HeaderFilter.css +8 -2
  46. package/lib/components/HeaderFilter/HeaderFilterCell.tsx +21 -18
  47. package/lib/components/HeaderFilter/HeaderFilterOperatorMenu.tsx +5 -4
  48. package/lib/components/HeaderFilter/HeaderFilterRow.tsx +9 -3
  49. package/lib/components/ListView/ListViewRow.tsx +1 -1
  50. package/lib/components/Pagination/Pagination.css +4 -0
  51. package/lib/components/Pagination/Pagination.tsx +4 -4
  52. package/lib/components/Row/Row.tsx +3 -3
  53. package/lib/components/SkeletonRow/SkeletonRow.tsx +2 -2
  54. package/lib/components/Toolbar/GridToolbar.tsx +8 -28
  55. package/lib/components/Toolbar/PivotPanel.tsx +4 -7
  56. package/lib/components/ui/ExpandIcon.css +13 -0
  57. package/lib/components/ui/ExpandIcon.tsx +2 -4
  58. package/lib/hooks/core/useGridCellSelection.ts +20 -5
  59. package/lib/hooks/core/useGridColumns.tsx +2 -2
  60. package/lib/hooks/core/useGridDevWarnings.ts +21 -6
  61. package/lib/hooks/core/useGridKeyboardNavigation.ts +7 -4
  62. package/lib/hooks/core/useGridMaxContentHeight.ts +16 -0
  63. package/lib/hooks/core/useGridScrollSync.ts +28 -5
  64. package/lib/hooks/core/useGridVirtualization.ts +19 -0
  65. package/lib/hooks/core/useLayout.ts +13 -6
  66. package/lib/hooks/useRowGrouping.ts +9 -9
  67. package/lib/types/index.ts +30 -0
  68. package/lib/utils/aggregation/index.ts +56 -1
  69. package/lib/utils/direction.ts +52 -0
  70. package/lib/utils/export/exportToExcelAdvanced.ts +9 -2
  71. package/lib/utils/export/exportToPdf.ts +2 -1
  72. package/lib/utils/filtering/index.ts +58 -6
  73. package/lib/utils/headerFilterLocale.ts +37 -0
  74. package/lib/utils/maxElementHeight.ts +53 -0
  75. package/lib/utils/scroll/index.ts +5 -3
  76. package/lib/utils/sorting/index.ts +141 -26
  77. package/lib/utils/values.ts +10 -6
  78. package/llms.txt +4 -2
  79. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,54 @@
5
5
 
6
6
  ---
7
7
 
8
+ ## [3.6.0] — 2026-10-12
9
+
10
+ ### Added
11
+
12
+ - **Translatable header filter row.** New `GridLocaleText` keys for every string the header filter row shows or announces: `headerFilterCustom` / `headerFilterCustomTooltip` ("Custom filter"), `headerFilterCellLabel`, `headerFilterValueLabel`, `headerFilterOperatorButtonLabel`, `headerFilterOperatorMenuLabel`, `headerFilterClearLabel` (aria-labels, functions of the column header), `headerFilterClearTooltip`, `headerFilterAny` / `headerFilterTrue` / `headerFilterFalse` (the selects) and `headerFilterOperators` (operator names). Each defaults to the English text it replaces. The *Locale Text* demo shows them in French.
13
+
14
+ ### Fixed
15
+
16
+ - The column resize handle now releases its pointer capture when a resize ends. A capture left behind (for example by a press that never got a real `pointerup`) could keep the pointer's later events from reaching the rest of the page.
17
+ - **Apps without the optional export peers failed to build in webpack and Next.js** (`Module not found: Can't resolve 'exceljs'` from `next build` on Next.js 15 and 16, and from plain webpack 5). The `exceljs` import in `exportToExcelAdvanced` now sits inside a `try` block, as the `jspdf` imports already did, which webpack and Turbopack treat as an optional dependency: apps without `exceljs`, `jspdf` and `jspdf-autotable` build (webpack shows a warning) and `exportToExcelAdvanced` rejects with `exportToExcelAdvanced requires 'exceljs'. Run: npm install exceljs`. Apps that install the peers are unchanged. The `next.config.ts` aliases in the Next.js guide are no longer needed. The package smoke suite has a third fixture, `e2e/fixtures/nopeers` (webpack 5, no peers), that must build, render the grid and get the install hints in Chromium, Firefox and WebKit.
18
+ - **`validateGridAiState` reported a top-level `message` as "Unknown part"** when given a JSON string, and also for an object passed directly. A string `message` (the model's reply text) is now skipped in both forms; a non-string `message` is reported as `Expected text, got …`. `createGridAiPromptHandler` now also returns a `message` key found inside a JSON string reply or a fenced JSON block as `result.message` (joined with the text around the block when both exist), as it did for object replies. The Next.js guide no longer strips `message` before validating.
19
+ - **Importing the grid from a React Server Component crashed the build** (`TypeError: createContext is not a function` in Next.js App Router "collecting page data"). `dist/opengridx.es.js` now starts with `'use client'`, so a Server Component gets a client reference: `<DataGrid>` with serialisable props can be rendered from a server file, and the grid hydrates in the browser. Wrapping it in your own `'use client'` file (needed for column functions) works as before. `scripts/check-bundle.mjs` checks the directive. The UMD build is minified and does not carry it; Next.js and other RSC bundlers load the ES build.
20
+ - **Right-to-left grids** (`dir="rtl"` on an ancestor or `<html>`). Unpinned RTL grids now work: horizontal scrolling no longer leaves the body blank (column virtualization read the negative RTL `scrollLeft` as 0; offsets are now measured from the start edge, also for keyboard scroll-into-view and `scrollToIndexes`); ArrowLeft / ArrowRight (and Shift+Arrow ranges, Alt+Arrow expand / collapse and column resize) follow the screen direction; cell text uses `text-align: start` / `end` (`align: 'left'` / `'right'` mean start / end); the resize handle sits on the column's left edge and dragging left widens it; the fill handle sits on the range's bottom-left corner; the range outline is drawn on its outer edges; dragging a range past the left edge auto-scrolls; toolbar panels open rightwards from their button. Left-to-right grids are unchanged (logical CSS properties, or code that runs only when the computed direction is `rtl`; a browser test compared 415 element positions in Chromium, Firefox and WebKit before and after).
21
+ - **Pinned columns in right-to-left grids.** `pinnedColumns.left` / `.right` now mean the inline start / end: in RTL left-pinned columns (and the checkbox, expand and reorder columns pinned with `pinCheckboxColumn` / `pinExpandColumn`) stick to the right edge and right-pinned ones to the left edge, in the header, column group rows, header filter row, body, skeleton rows and aggregation footer. They used physical `left` / `right` sticky offsets and scrolled away. Pinned shadows and section borders face the scrolling columns, range drag auto-scroll stops at the pinned sections on either side, and the column menu's *Pin to Left* / *Pin to Right* pin by screen side (in RTL *Pin to Left* adds to `pinnedColumns.right`). Left-to-right is pixel-identical: a browser test compared 2,211 element boxes, shadows, borders and pseudo-element offsets (pinned columns, system columns, column groups, header filters, footer; before and after a scroll and with a range) in Chromium, Firefox and WebKit before and after.
22
+ - **Mirrored icons in RTL.** Pagination first / previous / next / last arrows and the tree / group expand chevron mirror in RTL; tree indentation is on the start side. Sort arrows and other non-directional icons are unchanged.
23
+ - **Switching `dir` on a mounted, scrolled grid** left the body blank in Firefox (it resets `scrollLeft` without a scroll event); the grid now re-reads the scroll position when a `dir` attribute changes.
24
+ - **RTL browser tests failed on Linux CI by 10 px.** Test-only: Playwright's headless Chromium hides scrollbars, but with classic scrollbars `scrollbar-gutter: stable` still reserves the gutter and in RTL Chromium then cannot scroll to the start (`scrollLeft` stops at minus the gutter; a plain scroller does the same). The tests now measure against the browser's scroll origin and scrollport.
25
+
26
+ ### Docs
27
+
28
+ - **Right-to-left guide** — `docs/features/rtl.md`: what works in RTL, what was fixed, and the known limitations with workarounds (do not pin columns in RTL grids). New demo page *Right-to-Left* (`/rtl`) with Arabic and Hebrew data and an RTL toggle.
29
+ - **Next.js (App Router) guide** — `docs/features/nextjs.md`: the `'use client'` grid file, the stylesheet in `app/layout.tsx`, server-fetched rows as props (columns with functions stay in the client file), `dataSource` with a Route Handler, `@opencorestack/opengridx/ai` on the server and in the browser (the validation brand does not survive JSON, so the client validates), the Pages Router, and the common errors. Verified with `next build` + `next start` on Next.js 16.4 (Turbopack) and 15.5 (webpack), React 19.3, in Chromium, Firefox and WebKit. It also covers the optional export peers (not needed to build; see Fixed).
30
+
31
+ ### Changed
32
+
33
+ - **Faster sort, quick filter and grouping on large data; results unchanged.** Row order, filtered rows and group contents are the same as before (randomized tests compare them with the previous implementation). Medians at 1M rows (`npm run bench`, Apple M1, before → after, `docs/performance.md` → Benchmarks):
34
+ - Sort by a number column: Chromium 1,716 → 467 ms, Firefox 1,935 → 468 ms, WebKit 1,126 → 340 ms.
35
+ - Sort by a string column: Chromium 1,510 → 467 ms, Firefox 2,338 → 560 ms, WebKit 2,939 → 562 ms. Strings are ranked by sorting only their distinct values with the collator instead of comparing every row pair with it.
36
+ - Quick filter, first keystroke: Chromium 2,127 → 1,813 ms, Firefox 3,283 → 2,789 ms, WebKit 3,646 → 3,166 ms.
37
+ - Group by a column + `sum`: Chromium 924 → 693 ms, Firefox 1,879 → 1,776 ms, WebKit 818 → 675 ms.
38
+ - Also faster: number filters (144 → 97 ms in Chromium) and mount (804 → 621 ms), from reading plain cells without allocating and computing row heights in one pass.
39
+
40
+ ### Internal
41
+
42
+ - `docs/superpowers/specs/2026-10-12-nonblocking-pipeline-proposal.md`: a proposal for time-sliced sorting, filtering and grouping (not implemented).
43
+
44
+ ## [3.5.1] — 2026-10-12
45
+
46
+ ### Fixed
47
+
48
+ - **Large grids did not scroll at all in Firefox.** Firefox lays out elements up to about 17.9M px and collapses a taller one instead of clamping it, so a grid with more content (about 344,000 rows at the default 52 px row height, 559,000 at 32 px) showed only its first screen of rows and had nothing to scroll. The grid now measures the browser's height limit once per page (an invisible probe, `lib/utils/maxElementHeight.ts`) and caps its scroll content to it: Firefox scrolls normally and reaches every row above the limit, as Chromium and WebKit already did up to theirs (about 33.5M px). Rows below the limit are still not reachable in a single scrolling grid; use pagination or server-side loading. Nothing changes for grids under the limit.
49
+ - The dev warning about very tall content now appears only when rows are actually cut off in the current browser, and says how many rows can be scrolled to ("Only the first 344,000 of 500,000 rows can be scrolled to in this browser").
50
+
51
+ ### Internal
52
+
53
+ - **Large-dataset benchmark** — `npm run bench` (`bench/run.mjs`) packs the library, installs it into `bench/app` (a Vite + React 19 production app, like the smoke fixtures; port 4320) and measures it with Playwright in Chromium, Firefox and WebKit at 10k, 100k, 500k and 1M rows (1M also at 32 px): mount, sort, filter, quick filter, group + `sum`, a scripted 5 s scroll (frame-time percentiles and missed frames), DOM size, the furthest reachable row, and long tasks and JS heap in Chromium. Times run from the call to the first painted frame showing the result; medians of 5 runs after a warm-up. `bench/report.mjs` writes the Markdown tables; results and method are in `docs/performance.md` → Benchmarks. Flags `--rows`, `--engine`, `--repeat`, `--quick`. The weekly workflow runs a Chromium `--quick` pass and uploads the JSON; it never fails on numbers. Packing and tarball install are shared with the smoke suite (`scripts/lib/package-fixture.mjs`).
54
+ - **Docs: the browser height limit, as measured** — `docs/performance.md`, `docs/features/virtualization.md` and the README now give the benchmark's numbers instead of unmeasured claims ("60fps for 100k+ rows"), and correct the Firefox limit: past about 17.9M px of content (about 344,000 rows at 52 px) Firefox does not stop at the cap like Chromium and WebKit; the content collapses and the grid does not scroll at all.
55
+
8
56
  ## [3.5.0] — 2026-10-09
9
57
 
10
58
  ### Added
package/README.md CHANGED
@@ -68,7 +68,7 @@ Without a `height` prop the grid fills its container (`height: 100%`, or the fre
68
68
 
69
69
  ## 🚀 Key Features
70
70
 
71
- - **High-Performance Virtualization**: Row and column virtualization for 100,000+ rows. (Browsers cap an element's height, so a single scrolling grid reaches about 645,000 rows at the default 52 px row height; page or stream larger datasets.)
71
+ - **High-Performance Virtualization**: Row and column virtualization, [benchmarked](docs/performance.md#benchmarks) from 10k to 1M rows. (Browsers cap an element's height, so a single scrolling grid reaches 645,277 rows at the default 52 px row height in Chromium and Safari and about 344,000 in Firefox; page or stream larger datasets.)
72
72
  - **Advanced Layouts**: Native support for **Row & Column Spanning**, Grouping, and Tree Data.
73
73
  - **Data Orchestration**: 19 filter operators across string, number, date, boolean and select columns, AND/OR filter groups, multi-column sorting, and client or server pagination.
74
74
  - **Zero UI Dependencies**: 100% vanilla CSS (BEM) and pure React/TypeScript logic.
@@ -500,7 +500,8 @@ Built-in themes: `darkTheme`, `roseTheme`, `emeraldTheme`, `amberTheme`, `compac
500
500
  - **Tree-shaking Ready**: ES Module build — bundlers (Vite, Webpack) only include what you use.
501
501
  - **Zero UI Dependencies**: No MUI, Ant Design, or Radix. Pure React + vanilla CSS.
502
502
  - **Lazy Advanced Export**: ExcelJS and jsPDF are optional peer deps — not bundled, loaded only when you call `exportToExcelAdvanced` / `exportToPdf`.
503
- - **Efficient Rendering**: Row and column virtualization keeps the DOM small for 100k+ rows (needs a bounded grid height).
503
+ - **Efficient Rendering**: Row and column virtualization keeps the DOM small (needs a bounded grid height): about 19 rows at rest and under 90 while scrolling fast, at 10k or 1M rows.
504
+ - **[Benchmarks](docs/performance.md#benchmarks)** (`npm run bench`, Apple M1, Chromium, production build): at 100k rows the grid mounts in 89 ms, sorts in 102–160 ms, filters in 28–33 ms and groups with a sum in 87 ms; at 1M rows mount takes 0.7 s and a sort 1.1–1.3 s. Scrolling missed no frames at any size in Chromium. Past the browser's height cap (645,277 rows at 52 px in Chromium and Safari, about 344,000 in Firefox) rows cannot be scrolled to; page or stream larger datasets.
504
505
 
505
506
  ---
506
507
 
@@ -515,7 +516,7 @@ Full documentation at 👉 **[opencorestack.github.io/OpenGridX](https://opencor
515
516
  - **[Column Visibility](docs/components/column-visibility.md)** — Column show/hide panel
516
517
 
517
518
  ### 🚀 Features
518
- - **[Virtualization](docs/features/virtualization.md)** — 60fps rendering for 100k+ rows
519
+ - **[Virtualization](docs/features/virtualization.md)** — row and column virtualization; [measured](docs/performance.md#benchmarks) from 10k to 1M rows
519
520
  - **[Filtering & Search](docs/features/filtering.md)** — Quick filters and advanced operators
520
521
  - **[Sorting & Pagination](docs/features/sorting-pagination.md)** — Multi-column sorting
521
522
  - **[Editing & Reordering](docs/features/editing-reordering.md)** — Inline edits and DnD
@@ -531,7 +532,9 @@ Full documentation at 👉 **[opencorestack.github.io/OpenGridX](https://opencor
531
532
  - **[Fill Handle](docs/features/fill-handle.md)** — Drag the range corner to fill a series or copy; Ctrl+D / Ctrl+R
532
533
  - **[AI Toolkit](docs/features/ai-toolkit.md)** — Schema and validator for driving the grid from your own model
533
534
  - **[Keyboard & Accessibility](docs/features/keyboard-navigation.md)** — Keys, focus and ARIA
535
+ - **[Right-to-Left (RTL)](docs/features/rtl.md)** — `dir="rtl"` support and its limits
534
536
  - **[Data Source](docs/features/data-source.md)** — Server-side integration
537
+ - **[Next.js (App Router)](docs/features/nextjs.md)** — Client components, server-fetched rows and Route Handlers
535
538
  - **[Loading States](docs/features/loading-states.md)** — Skeleton and shimmer overlays
536
539
 
537
540
  ### 📊 Advanced Data
package/dist/ai.cjs CHANGED
@@ -1,2 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||Q.has(e)||(Q.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 ee(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=ee(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 be(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 he=["sum","avg","count","min","max","unique"],B=["sum","avg","count","min","max"],te=["isEmpty","isNotEmpty"],D=3,U=["filter","sort","grouping","aggregation","pivot","columnVisibility"],H={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function re(e){const t=Array.isArray(e?.include)?e.include:U,r=Array.isArray(e?.exclude)?e.exclude:[];return U.filter(o=>t.includes(o)&&!r.includes(o))}function J(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const o of e){if(!$(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 ne(e){return be(e.type)}const ie=e=>e.filterable!==!1,oe=e=>e.sortable!==!1,T=e=>e.groupable!==!1,se=e=>e.hideable!==!1;function F(e,t=he){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 Ae="OpenGridX grid state",Se=10;function j(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 O(e,t,r){return r.descriptions?{...e,description:t}:e}function Oe(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,Se)}function ve(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=Oe(e,t);return o.length>0?{...r,examples:o}:r}function Me(e,t){const r=ne(e),o=r.filter(s=>te.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 O({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:ve(e,t)},required:["field","operator"],additionalProperties:!1},i?`${j(e)}: ${i}`:j(e),t)}function Ee(e,t,r){const o=e.filter(ie),n={},i={enum:["and","or"]};if(o.length>0){r.filterItem={oneOf:o.map(s=>Me(s,t))};for(let s=D;s>=1;s--){const l=s===D?{$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=O({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:n,additionalProperties:!1}}function q(e){return{enum:e.map(t=>t.field)}}function we(e,t,r,o){switch(e){case"filter":return Ee(t,r,o);case"sort":{const n=t.filter(oe);return n.length===0?null:O({type:"array",items:{type:"object",properties:{field:q(n),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const n=t.filter(T);return n.length===0?null:O({type:"array",items:q(n),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const n={};for(const i of t){const s=F(i);s.length>0&&(n[i.field]=O({enum:s},j(i),r))}return Object.keys(n).length===0?null:{type:"object",properties:n,additionalProperties:!1}}case"pivot":{const n=t.filter(T),i=t.map(l=>({col:l,fns:F(l,B)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>O({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},j(l),r));if(n.length===0||i.length===0)return null;const s={type:"array",items:q(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(se))n[i.field]=O({type:"boolean"},j(i),r);return Object.keys(n).length===0?null:O({type:"object",properties:n,additionalProperties:!1},"false hides a column",r)}}}function Y(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},o=J(e),n={},i={};for(const l of re(t)){const a=we(l,o,r,n);a&&(i[H[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:Ae,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 Fe=Symbol.for("opengridx.ai.validated");function I(e){return Object.defineProperty(e,Fe,{value:!0,enumerable:!1}),e}const M={ok:!1},h=e=>({ok:!0,value:e}),Ne=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,_e=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,je=/[\p{Sc}\s]/gu;function Te(e){let t=e.replace(je,"");if(t==="")return e.trim()===""?h(null):M;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(!Ne.test(t)&&!_e.test(t))return M;let n=Number(t);return Number.isFinite(n)?(o&&(n/=100),r&&(n=-n),h(n)):M}function Ge(e,t){const r=e.trim();if(r==="")return h(null);const o=ee(r);return o?h(o):M}const Ve=new Set(["true","yes","1"]),Pe=new Set(["false","no","0"]);function Re(e,t){const r=e.trim().toLowerCase();return r===""?h(null):Ve.has(r)?h(!0):Pe.has(r)?h(!1):M}function De(e,t){const r=e.trim();if(r==="")return h(null);if(!t||t.length===0)return M;const o=r.toLowerCase();for(const n of t){const i=typeof n=="object"?n.value:n;if(String(i).toLowerCase()===o)return h(i)}for(const n of t)if(typeof n=="object"&&n.label.toLowerCase()===o)return h(n.value);return M}const d=e=>({ok:!0,value:e}),A={ok:!1,reason:"invalidValue"},L=e=>e.ok?d(e.value):A;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function Ie(e){return e==null?d(""):typeof e=="string"?d(e):typeof e=="number"||typeof e=="boolean"?d(String(e)):A}function Le(e){return C(e)?d(null):typeof e=="number"?Number.isFinite(e)?d(e):A:typeof e!="string"?A:L(Te(e))}function Ce(e){return C(e)?d(null):e instanceof Date?Number.isNaN(e.getTime())?A:d(e):typeof e=="number"?Number.isFinite(e)?d(new Date(e)):A:typeof e!="string"?A:L(Ge(e))}function le(e){return C(e)?d(null):typeof e=="boolean"?d(e):typeof e!="string"&&typeof e!="number"?A:L(Re(String(e)))}function qe(e,t){if(C(e))return d(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return A;const r=t.valueOptions;return!r||r.length===0?d(e):L(De(String(e),r))}function We(e,t){switch(t.type){case"number":return Le(e);case"date":return Ce(e);case"boolean":return le(e);case"singleSelect":return qe(e,t);default:return Ie(e)}}const W=200,Ue=100;function u(e,t,r){return e.errors.length<Ue&&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 ae(e){return typeof e=="string"?e.trim().toLowerCase():null}function K(e){return Object.keys(e).filter(t=>t!=="__proto__")}function N(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 _(e,t,r){if(t==null)return[];const o=Array.isArray(t)?t:[t];return o.length>W&&u(e,r,`Only the first ${W} entries are read`),o.slice(0,W)}function G(e,t,r,o){const n=ae(t);return o.find(i=>i.toLowerCase()===n)??u(e,r,`${p(t)} is not one of ${o.join(", ")}`)}function Z(e,t,r,o){const n=We(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 Xe(e,t,r){const o=N(e,t.field,`${r}.field`,ie,"filtered");if(!o)return null;const n=G(e,t.operator,`${r}.operator`,ne(o));if(n===null)return null;const i={field:o.field,operator:n};if(te.includes(i.operator))return i;const s=S(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return _(e,s,`${r}.value`).forEach((c,v)=>{const g=Z(e,c,o,`${r}.value[${v}]`);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=Z(e,s,o,`${r}.value`);return l?(i.value=l.v,i):null}function ue(e,t,r){return t===void 0?null:G(e,t,r,["and","or"])}function fe(e,t,r,o){const n=[];return _(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!$(i))u(e,l,`Expected a filter, got ${p(i)}`);else if(S(i,"items")||S(i,"logicOperator")){if(o>=D){u(e,l,`Groups nest at most ${D} levels`);return}const a=fe(e,i.items,`${l}.items`,o+1);a.length===0?u(e,l,"Empty group removed"):n.push({logicOperator:ue(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Xe(e,i,l);a&&n.push(a)}}),n}function Be(e,t,r){const o={};S(t,"items")&&(o.items=fe(e,t.items,`${r}.items`,0));const n=ue(e,t.logicOperator,`${r}.logicOperator`);return n&&(o.logicOperator=n),S(t,"quickFilterValues")&&(o.quickFilterValues=[],_(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 He(e,t,r){const o=[];return _(e,t,r).forEach((n,i)=>{const s=`${r}[${i}]`;if(!$(n))return u(e,s,`Expected { field, sort }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,oe,"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 X(e,t,r,o,n){const i=[];return _(e,t,r).forEach((s,l)=>{const a=N(e,s,`${r}[${l}]`,o,n);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Je=e=>F(e).length>0,Ye=e=>F(e,B).length>0;function Ke(e,t,r){const o={};for(const n of K(t)){if(ae(t[n])==="none")continue;const i=N(e,n,`${r}.${n}`,Je,"aggregated"),s=i&&G(e,t[n],`${r}.${n}`,F(i));i&&s&&(o[i.field]=s)}return o}function ze(e,t,r){const o=[];return _(e,t.valueFields,`${r}.valueFields`).forEach((n,i)=>{const s=`${r}.valueFields[${i}]`;if(!$(n))return u(e,s,`Expected { field, aggFn }, got ${p(n)}`);const l=N(e,n.field,`${s}.field`,Ye,"a pivot value"),a=l&&G(e,n.aggFn,`${s}.aggFn`,F(l,B));l&&a&&!o.some(c=>c.field===l.field&&c.aggFn===a)&&o.push({field:l.field,aggFn:a})}),{rowFields:X(e,t.rowFields,`${r}.rowFields`,T,"a pivot label"),columnFields:X(e,t.columnFields,`${r}.columnFields`,T,"a pivot label"),valueFields:o}}function Qe(e,t,r){const o={};for(const n of K(t)){const i=N(e,n,`${r}.${n}`,se,"hidden"),s=le(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 Ze(e,t,r,o,n){t==="sort"?n.sortModel=He(e,r,o):t==="grouping"?n.rowGroupingModel=X(e,r,o,T,"grouped"):$(r)?t==="filter"?n.filterModel=Be(e,r,o):t==="aggregation"?n.aggregationModel=Ke(e,r,o):t==="pivot"?n.pivotModel=ze(e,r,o):n.columnVisibilityModel=Qe(e,r,o):u(e,o,`Expected an object, got ${p(r)}`)}function z(e,t,r={}){const o=xe(e,t,r);return I(o.state),I(o)}function xe(e,t,r){const o={errors:[],byField:new Map},n={};try{for(const l of J(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(!$(i))return u(o,"",`Expected an object, got ${p(i)}`),{state:n,errors:o.errors};const s=re(r);for(const l of K(i)){const a=U.find(c=>H[c]===l);l==="schemaVersion"?i[l]!==1&&u(o,l,`Unsupported schemaVersion ${p(i[l])}`):a?s.includes(a)?Ze(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 k="```";function V(){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(k),o=r<0?-1:e.indexOf(`
2
- `,r+k.length),n=o<0?-1:e.indexOf(k,o+1),i=n<0?"":e.slice(r+k.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+k.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:I({}),errors:e};return t&&(r.message=t),I(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($(e)&&S(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=Y(t,{...n,...i});return async(l,a)=>{const c=a?.signal??new AbortController().signal;if(c.aborted)throw V();const v=new Promise((m,y)=>{c.addEventListener("abort",()=>y(V()),{once:!0})});v.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}),v])}catch(m){if(c.aborted)throw V();return ce([{path:"",message:`The model call failed: ${tt(m)}`}])}if(c.aborted)throw V();return rt(g,t,i)}}const it=20,ot=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 w(e,t=""){return{ok:!1,errors:[{path:t,message:e}]}}async function P(e){try{return e()}catch(t){return w(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=H[o],s=Y(t,{include:[o]});if(!S(s.properties,i))return null;const{$schema:l,schemaVersion:a,title:c,description:v,...g}=s;return{name:r,description:n,inputSchema:{...g,required:[i]},execute:m=>P(()=>{const y=R(e);if(!y)return w("The grid is not ready");if(!$(m)||!S(m,i))return w(`Expected an object with "${i}"`,i);const{state:f,errors:E}=z({[i]:m[i]},t,{include:[o]});if(E.length>0)return{ok:!1,errors:E};const b={};return f.filterModel&&y.setFilterModel(b.filterModel=f.filterModel),f.sortModel&&y.setSortModel(b.sortModel=f.sortModel),f.rowGroupingModel&&y.setRowGroupingModel(b.rowGroupingModel=f.rowGroupingModel),f.aggregationModel&&y.setAggregationModel(b.aggregationModel=f.aggregationModel),f.columnVisibilityModel&&(b.columnVisibilityModel={...y.getGridAiState().columnVisibilityModel,...f.columnVisibilityModel},y.setColumnVisibilityModel(b.columnVisibilityModel)),{ok:!0,applied:b}})}}const x={type:"object",properties:{},additionalProperties:!1};function ut(e,t,r={}){const o=J(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:x,execute:()=>P(()=>{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"}))}:w("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:x,execute:()=>P(()=>{const i=R(e);if(!i)return w("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),ot):it,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=>P(()=>{const a=R(e);if(!a)return w("The grid is not ready");const c=$(l)&&S(l,"limit")?l.limit:i,v=typeof c=="number"&&c>=1?Math.min(Math.floor(c),i):i,g=a.getAllFilteredRows(),m=Array.isArray(g)?g:[],y=m.slice(0,v).map(f=>{const E={};if(!$(f))return E;for(const b of s)E[b.field]=lt(ge(f,b.field,b));return E});return{ok:!0,rowCount:m.length,rows:y}})})}return n}exports.createGridAgentTools=ut,exports.createGridAiPromptHandler=nt,exports.getGridAiSchema=Y,exports.validateGridAiState=z;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Q=new Set;function de(e,t,r){process.env.NODE_ENV==="production"||Q.has(e)||(Q.add(e),console.warn(`[OpenGridX] ${t}.`,r))}function pe(e,t,r){const n=e[t];if(!r?.valueGetter)return{value:n};try{return{value:r.valueGetter({row:e,field:t,value:n})}}catch(o){return{value:void 0,error:o??new Error("valueGetter failed")}}}function ge(e,t,r){if(!r?.valueGetter)return e[t];const{value:n,error:o}=pe(e,t,r);return o!==void 0&&de(`valueGetter:${t}`,`valueGetter for column "${t}" threw; the value is read as undefined`,o),n}const me=/^(\d{4})-(\d{2})-(\d{2})$/;function ee(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 n=me.exec(r);if(n){const o=Number(n[1]),i=Number(n[2])-1,s=Number(n[3]);if(t=new Date(o,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=ee(e);if(!t)return"";const r=String(t.getFullYear()).padStart(4,"0"),n=String(t.getMonth()+1).padStart(2,"0"),o=String(t.getDate()).padStart(2,"0");return`${r}-${n}-${o}`}function be(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 he=["sum","avg","count","min","max","unique"],X=["sum","avg","count","min","max"],te=["isEmpty","isNotEmpty"],I=3,U=["filter","sort","grouping","aggregation","pivot","columnVisibility"],H={filter:"filterModel",sort:"sortModel",grouping:"rowGroupingModel",aggregation:"aggregationModel",pivot:"pivotModel",columnVisibility:"columnVisibilityModel"};function S(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function $(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function re(e){const t=Array.isArray(e?.include)?e.include:U,r=Array.isArray(e?.exclude)?e.exclude:[];return U.filter(n=>t.includes(n)&&!r.includes(n))}function J(e){if(!Array.isArray(e))return[];const t=new Set,r=[];for(const n of e){if(!$(n))continue;const o=n.field;typeof o!="string"||o===""||o.startsWith("__")||n.isSpacer===!0||t.has(o)||(t.add(o),r.push(n))}return r}function ne(e){return be(e.type)}const ie=e=>e.filterable!==!1,oe=e=>e.sortable!==!1,G=e=>e.groupable!==!1,se=e=>e.hideable!==!1;function F(e,t=he){if(e.aggregable===!1)return[];const r=e.type==="number"||e.aggregable===!0?t:t.filter(o=>o==="count"),n=e.availableAggregationFunctions;return Array.isArray(n)?r.filter(o=>n.includes(o)):[...r]}function $e(e){const t=[];if(!Array.isArray(e.valueOptions))return t;for(const r of e.valueOptions){const n=r!==null&&typeof r=="object"?r.value:r;(typeof n=="string"||typeof n=="number"&&Number.isFinite(n))&&!t.includes(n)&&t.push(n)}return t}const Ae="OpenGridX grid state",Se=10;function j(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 O(e,t,r){return r.descriptions?{...e,description:t}:e}function Oe(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,Se)}function ve(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 o=$e(e),i=o.length>0?{enum:o}:{type:"string"};r={anyOf:[i,{type:"array",items:i}]};break}default:r={type:"string"}}const n=Oe(e,t);return n.length>0?{...r,examples:n}:r}function Me(e,t){const r=ne(e),n=r.filter(s=>te.includes(s)),o=e.type==="singleSelect"?"; isAnyOf takes an array":"",i=n.length>0?`${n.join(" and ")} take no value${o}`:o.slice(2);return O({type:"object",properties:{field:{const:e.field},operator:{enum:r},value:ve(e,t)},required:["field","operator"],additionalProperties:!1},i?`${j(e)}: ${i}`:j(e),t)}function Ee(e,t,r){const n=e.filter(ie),o={},i={enum:["and","or"]};if(n.length>0){r.filterItem={oneOf:n.map(s=>Me(s,t))};for(let s=I;s>=1;s--){const l=s===I?{$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}}o.items={type:"array",items:{anyOf:[{$ref:"#/$defs/filterItem"},{$ref:"#/$defs/filterGroup1"}]}},o.logicOperator=i}return o.quickFilterValues=O({type:"array",items:{type:"string"}},"Words a row must contain, in any column",t),{type:"object",properties:o,additionalProperties:!1}}function q(e){return{enum:e.map(t=>t.field)}}function we(e,t,r,n){switch(e){case"filter":return Ee(t,r,n);case"sort":{const o=t.filter(oe);return o.length===0?null:O({type:"array",items:{type:"object",properties:{field:q(o),sort:{enum:["asc","desc"]}},required:["field","sort"],additionalProperties:!1}},"Most significant first",r)}case"grouping":{const o=t.filter(G);return o.length===0?null:O({type:"array",items:q(o),uniqueItems:!0},"Outermost first",r)}case"aggregation":{const o={};for(const i of t){const s=F(i);s.length>0&&(o[i.field]=O({enum:s},j(i),r))}return Object.keys(o).length===0?null:{type:"object",properties:o,additionalProperties:!1}}case"pivot":{const o=t.filter(G),i=t.map(l=>({col:l,fns:F(l,X)})).filter(l=>l.fns.length>0).map(({col:l,fns:a})=>O({type:"object",properties:{field:{const:l.field},aggFn:{enum:a}},required:["field","aggFn"],additionalProperties:!1},j(l),r));if(o.length===0||i.length===0)return null;const s={type:"array",items:q(o),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 o={};for(const i of t.filter(se))o[i.field]=O({type:"boolean"},j(i),r);return Object.keys(o).length===0?null:O({type:"object",properties:o,additionalProperties:!1},"false hides a column",r)}}}function Y(e,t={}){const r={descriptions:t.descriptions!==!1,examples:t.examples!==!1},n=J(e),o={},i={};for(const l of re(t)){const a=we(l,n,r,o);a&&(i[H[l]]=a)}const s={$schema:"https://json-schema.org/draft/2020-12/schema",schemaVersion:1,title:Ae,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(o).length>0&&(s.$defs=o),s}const Fe=Symbol.for("opengridx.ai.validated");function D(e){return Object.defineProperty(e,Fe,{value:!0,enumerable:!1}),e}const M={ok:!1},h=e=>({ok:!0,value:e}),Ne=/^\d+(?:\.\d*)?(?:e[+-]?\d+)?$/i,_e=/^\.\d+(?:e[+-]?\d+)?$/i,ke=/^\d{1,3}(?:,\d{3})+(?:\.\d*)?$/,je=/[\p{Sc}\s]/gu;function Ge(e){let t=e.replace(je,"");if(t==="")return e.trim()===""?h(null):M;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 n=!1;if(t.endsWith("%")&&(n=!0,t=t.slice(0,-1)),ke.test(t))t=t.split(",").join("");else if(!Ne.test(t)&&!_e.test(t))return M;let o=Number(t);return Number.isFinite(o)?(n&&(o/=100),r&&(o=-o),h(o)):M}function Te(e,t){const r=e.trim();if(r==="")return h(null);const n=ee(r);return n?h(n):M}const Ve=new Set(["true","yes","1"]),Pe=new Set(["false","no","0"]);function Re(e,t){const r=e.trim().toLowerCase();return r===""?h(null):Ve.has(r)?h(!0):Pe.has(r)?h(!1):M}function Ie(e,t){const r=e.trim();if(r==="")return h(null);if(!t||t.length===0)return M;const n=r.toLowerCase();for(const o of t){const i=typeof o=="object"?o.value:o;if(String(i).toLowerCase()===n)return h(i)}for(const o of t)if(typeof o=="object"&&o.label.toLowerCase()===n)return h(o.value);return M}const p=e=>({ok:!0,value:e}),A={ok:!1,reason:"invalidValue"},L=e=>e.ok?p(e.value):A;function C(e){return e==null||typeof e=="string"&&e.trim()===""}function De(e){return e==null?p(""):typeof e=="string"?p(e):typeof e=="number"||typeof e=="boolean"?p(String(e)):A}function Le(e){return C(e)?p(null):typeof e=="number"?Number.isFinite(e)?p(e):A:typeof e!="string"?A:L(Ge(e))}function Ce(e){return C(e)?p(null):e instanceof Date?Number.isNaN(e.getTime())?A:p(e):typeof e=="number"?Number.isFinite(e)?p(new Date(e)):A:typeof e!="string"?A:L(Te(e))}function le(e){return C(e)?p(null):typeof e=="boolean"?p(e):typeof e!="string"&&typeof e!="number"?A:L(Re(String(e)))}function qe(e,t){if(C(e))return p(null);if(typeof e!="string"&&typeof e!="number"&&typeof e!="boolean")return A;const r=t.valueOptions;return!r||r.length===0?p(e):L(Ie(String(e),r))}function We(e,t){switch(t.type){case"number":return Le(e);case"date":return Ce(e);case"boolean":return le(e);case"singleSelect":return qe(e,t);default:return De(e)}}const W=200,Ue=100;function u(e,t,r){return e.errors.length<Ue&&e.errors.push({path:t,message:r}),null}function c(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 ae(e){return typeof e=="string"?e.trim().toLowerCase():null}function K(e){return Object.keys(e).filter(t=>t!=="__proto__")}function N(e,t,r,n,o){const i=typeof t=="string"?e.byField.get(t):void 0;return i?n(i)?i:u(e,r,`Field ${c(t)} cannot be ${o}`):u(e,r,`Unknown field ${c(t)}`)}function _(e,t,r){if(t==null)return[];const n=Array.isArray(t)?t:[t];return n.length>W&&u(e,r,`Only the first ${W} entries are read`),n.slice(0,W)}function T(e,t,r,n){const o=ae(t);return n.find(i=>i.toLowerCase()===o)??u(e,r,`${c(t)} is not one of ${n.join(", ")}`)}function Z(e,t,r,n){const o=We(t,r);if(!o.ok)return u(e,n,`${c(t)} is not a valid ${r.type??"string"} value`);const i=o.value instanceof Date?ye(o.value):o.value;return i==null||i===""?u(e,n,"Missing value"):{v:i}}function Be(e,t,r){const n=N(e,t.field,`${r}.field`,ie,"filtered");if(!n)return null;const o=T(e,t.operator,`${r}.operator`,ne(n));if(o===null)return null;const i={field:n.field,operator:o};if(te.includes(i.operator))return i;const s=S(t,"value")?t.value:void 0;if(i.operator==="isAnyOf"){const a=[];return _(e,s,`${r}.value`).forEach((f,v)=>{const g=Z(e,f,n,`${r}.value[${v}]`);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=Z(e,s,n,`${r}.value`);return l?(i.value=l.v,i):null}function ue(e,t,r){return t===void 0?null:T(e,t,r,["and","or"])}function fe(e,t,r,n){const o=[];return _(e,t,r).forEach((i,s)=>{const l=`${r}[${s}]`;if(!$(i))u(e,l,`Expected a filter, got ${c(i)}`);else if(S(i,"items")||S(i,"logicOperator")){if(n>=I){u(e,l,`Groups nest at most ${I} levels`);return}const a=fe(e,i.items,`${l}.items`,n+1);a.length===0?u(e,l,"Empty group removed"):o.push({logicOperator:ue(e,i.logicOperator,`${l}.logicOperator`)??"and",items:a})}else{const a=Be(e,i,l);a&&o.push(a)}}),o}function Xe(e,t,r){const n={};S(t,"items")&&(n.items=fe(e,t.items,`${r}.items`,0));const o=ue(e,t.logicOperator,`${r}.logicOperator`);return o&&(n.logicOperator=o),S(t,"quickFilterValues")&&(n.quickFilterValues=[],_(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 ${c(i)}`):l!==""&&n.quickFilterValues?.push(l)})),n}function He(e,t,r){const n=[];return _(e,t,r).forEach((o,i)=>{const s=`${r}[${i}]`;if(!$(o))return u(e,s,`Expected { field, sort }, got ${c(o)}`);const l=N(e,o.field,`${s}.field`,oe,"sorted"),a=l&&T(e,o.sort,`${s}.sort`,["asc","desc"]);!l||!a||(n.some(f=>f.field===l.field)?u(e,s,`${c(l.field)} is sorted twice`):n.push({field:l.field,sort:a}))}),n}function B(e,t,r,n,o){const i=[];return _(e,t,r).forEach((s,l)=>{const a=N(e,s,`${r}[${l}]`,n,o);a&&!i.includes(a.field)&&i.push(a.field)}),i}const Je=e=>F(e).length>0,Ye=e=>F(e,X).length>0;function Ke(e,t,r){const n={};for(const o of K(t)){if(ae(t[o])==="none")continue;const i=N(e,o,`${r}.${o}`,Je,"aggregated"),s=i&&T(e,t[o],`${r}.${o}`,F(i));i&&s&&(n[i.field]=s)}return n}function ze(e,t,r){const n=[];return _(e,t.valueFields,`${r}.valueFields`).forEach((o,i)=>{const s=`${r}.valueFields[${i}]`;if(!$(o))return u(e,s,`Expected { field, aggFn }, got ${c(o)}`);const l=N(e,o.field,`${s}.field`,Ye,"a pivot value"),a=l&&T(e,o.aggFn,`${s}.aggFn`,F(l,X));l&&a&&!n.some(f=>f.field===l.field&&f.aggFn===a)&&n.push({field:l.field,aggFn:a})}),{rowFields:B(e,t.rowFields,`${r}.rowFields`,G,"a pivot label"),columnFields:B(e,t.columnFields,`${r}.columnFields`,G,"a pivot label"),valueFields:n}}function Qe(e,t,r){const n={};for(const o of K(t)){const i=N(e,o,`${r}.${o}`,se,"hidden"),s=le(t[o]);i&&(s.ok&&s.value!==null?n[i.field]=s.value:u(e,`${r}.${o}`,`Expected true or false, got ${c(t[o])}`))}return n}function Ze(e,t,r,n,o){t==="sort"?o.sortModel=He(e,r,n):t==="grouping"?o.rowGroupingModel=B(e,r,n,G,"grouped"):$(r)?t==="filter"?o.filterModel=Xe(e,r,n):t==="aggregation"?o.aggregationModel=Ke(e,r,n):t==="pivot"?o.pivotModel=ze(e,r,n):o.columnVisibilityModel=Qe(e,r,n):u(e,n,`Expected an object, got ${c(r)}`)}function z(e,t,r={}){const n=xe(e,t,r);return D(n.state),D(n)}function xe(e,t,r){const n={errors:[],byField:new Map},o={};try{for(const l of J(t))n.byField.set(l.field,l);let i=e;if(typeof e=="string")try{i=JSON.parse(e)}catch{return u(n,"","Not valid JSON"),{state:o,errors:n.errors}}if(!$(i))return u(n,"",`Expected an object, got ${c(i)}`),{state:o,errors:n.errors};const s=re(r);for(const l of K(i)){const a=U.find(f=>H[f]===l);l==="schemaVersion"?i[l]!==1&&u(n,l,`Unsupported schemaVersion ${c(i[l])}`):l==="message"?typeof i[l]!="string"&&u(n,l,`Expected text, got ${c(i[l])}`):a?s.includes(a)?Ze(n,a,i[l],l,o):u(n,l,"Part not allowed"):u(n,l,"Unknown part")}}catch{return u(n,"","Could not read the input"),{state:{},errors:n.errors}}return{state:o,errors:n.errors}}const k="```";function V(){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(k),n=r<0?-1:e.indexOf(`
2
+ `,r+k.length),o=n<0?-1:e.indexOf(k,n+1),i=o<0?"":e.slice(r+k.length,n).trim().toLowerCase();return o<0||i!==""&&i!=="json"?{json:null,message:t}:{json:e.slice(n+1,o),message:`${e.slice(0,r)}${e.slice(o+k.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:D({}),errors:e};return t&&(r.message=t),D(r)}function rt(e,t,r){let n=e,o="";if(typeof e=="string"){const a=et(e);if(a.json===null)return ce([],a.message||void 0);o=a.message;try{n=JSON.parse(a.json)}catch{n=a.json}}const i=$(n)&&S(n,"message")&&typeof n.message=="string"?n.message.trim():"",s=[o,i].filter(Boolean).join(`
3
+
4
+ `),l=z(n,t,r);return s&&(l.message=s),l}function nt(e){const{columns:t,callModel:r,parts:n,schemaOptions:o={}}=e,i={include:n??o.include,exclude:o.exclude},s=Y(t,{...o,...i});return async(l,a)=>{const f=a?.signal??new AbortController().signal;if(f.aborted)throw V();const v=new Promise((m,y)=>{f.addEventListener("abort",()=>y(V()),{once:!0})});v.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:f}),v])}catch(m){if(f.aborted)throw V();return ce([{path:"",message:`The model call failed: ${tt(m)}`}])}if(f.aborted)throw V();return rt(g,t,i)}}const it=20,ot=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 w(e,t=""){return{ok:!1,errors:[{path:t,message:e}]}}async function P(e){try{return e()}catch(t){return w(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,n,o){const i=H[n],s=Y(t,{include:[n]});if(!S(s.properties,i))return null;const{$schema:l,schemaVersion:a,title:f,description:v,...g}=s;return{name:r,description:o,inputSchema:{...g,required:[i]},execute:m=>P(()=>{const y=R(e);if(!y)return w("The grid is not ready");if(!$(m)||!S(m,i))return w(`Expected an object with "${i}"`,i);const{state:d,errors:E}=z({[i]:m[i]},t,{include:[n]});if(E.length>0)return{ok:!1,errors:E};const b={};return d.filterModel&&y.setFilterModel(b.filterModel=d.filterModel),d.sortModel&&y.setSortModel(b.sortModel=d.sortModel),d.rowGroupingModel&&y.setRowGroupingModel(b.rowGroupingModel=d.rowGroupingModel),d.aggregationModel&&y.setAggregationModel(b.aggregationModel=d.aggregationModel),d.columnVisibilityModel&&(b.columnVisibilityModel={...y.getGridAiState().columnVisibilityModel,...d.columnVisibilityModel},y.setColumnVisibilityModel(b.columnVisibilityModel)),{ok:!0,applied:b}})}}const x={type:"object",properties:{},additionalProperties:!1};function ut(e,t,r={}){const n=J(t),o=[{name:"get_grid_state",description:"Read the current filter, sort, grouping, aggregation, pivot and column visibility of the grid, and its columns.",inputSchema:x,execute:()=>P(()=>{const i=R(e);return i?{ok:!0,state:i.getGridAiState(),columns:n.map(s=>({field:s.field,headerName:typeof s.headerName=="string"?s.headerName:s.field,type:typeof s.type=="string"?s.type:"string"}))}:w("The grid is not ready")})}];for(const{name:i,part:s,description:l}of st){const a=at(e,n,i,s,l);a&&o.push(a)}if(o.push({name:"clear_filters",description:"Remove every filter and the quick-filter words.",inputSchema:x,execute:()=>P(()=>{const i=R(e);if(!i)return w("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),ot):it,s=Array.isArray(r.fields)?n.filter(l=>r.fields?.includes(l.field)):n;o.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=>P(()=>{const a=R(e);if(!a)return w("The grid is not ready");const f=$(l)&&S(l,"limit")?l.limit:i,v=typeof f=="number"&&f>=1?Math.min(Math.floor(f),i):i,g=a.getAllFilteredRows(),m=Array.isArray(g)?g:[],y=m.slice(0,v).map(d=>{const E={};if(!$(d))return E;for(const b of s)E[b.field]=lt(ge(d,b.field,b));return E});return{ok:!0,rowCount:m.length,rows:y}})})}return o}exports.createGridAgentTools=ut,exports.createGridAiPromptHandler=nt,exports.getGridAiSchema=Y,exports.validateGridAiState=z;