@graphenedata/cli 0.0.15 → 0.0.17

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 (117) hide show
  1. package/README.md +174 -0
  2. package/dist/cli/bigQuery-OQUNH3VT.js +75 -0
  3. package/dist/cli/bigQuery-OQUNH3VT.js.map +7 -0
  4. package/dist/cli/chunk-56K2FF57.js +53 -0
  5. package/dist/cli/chunk-56K2FF57.js.map +7 -0
  6. package/dist/cli/chunk-TZTTALAV.js +12868 -0
  7. package/dist/cli/chunk-TZTTALAV.js.map +7 -0
  8. package/dist/cli/cli.js +260 -11196
  9. package/dist/cli/clickhouse-S3BJSKND.js +65 -0
  10. package/dist/cli/clickhouse-S3BJSKND.js.map +7 -0
  11. package/dist/cli/duckdb-TKVMONRK.js +87 -0
  12. package/dist/cli/duckdb-TKVMONRK.js.map +7 -0
  13. package/dist/cli/serve2-S2LL4D4D.js +448 -0
  14. package/dist/cli/serve2-S2LL4D4D.js.map +7 -0
  15. package/dist/cli/snowflake-3VPDEYYP.js +128 -0
  16. package/dist/cli/snowflake-3VPDEYYP.js.map +7 -0
  17. package/dist/index.d.ts +63 -0
  18. package/dist/lang/index.d.ts +63 -0
  19. package/dist/skills/graphene/SKILL.md +156 -95
  20. package/dist/skills/graphene/references/big-value.md +6 -41
  21. package/dist/skills/graphene/references/date-range.md +64 -0
  22. package/dist/skills/graphene/references/dropdown.md +3 -4
  23. package/dist/skills/graphene/references/echarts.md +162 -0
  24. package/dist/skills/graphene/references/gsql.md +55 -25
  25. package/dist/skills/graphene/references/model-gsql.md +70 -0
  26. package/dist/skills/graphene/references/table.md +13 -14
  27. package/dist/skills/graphene/references/text-input.md +2 -1
  28. package/dist/ui/app.css +239 -340
  29. package/dist/ui/component-utilities/dataShaping.ts +484 -0
  30. package/dist/ui/component-utilities/dataSummary.ts +57 -0
  31. package/dist/ui/component-utilities/enrich.ts +793 -0
  32. package/dist/ui/component-utilities/format.ts +177 -0
  33. package/dist/ui/component-utilities/inputUtils.ts +44 -8
  34. package/dist/ui/component-utilities/theme.ts +200 -0
  35. package/dist/ui/component-utilities/themeStores.ts +21 -8
  36. package/dist/ui/component-utilities/types.ts +70 -0
  37. package/dist/ui/components/AreaChart.svelte +57 -105
  38. package/dist/ui/components/BarChart.svelte +71 -129
  39. package/dist/ui/components/BigValue.svelte +24 -40
  40. package/dist/ui/components/Column.svelte +10 -18
  41. package/dist/ui/components/DateRange.svelte +54 -21
  42. package/dist/ui/components/Dropdown.svelte +47 -26
  43. package/dist/ui/components/DropdownOption.svelte +1 -2
  44. package/dist/ui/components/ECharts.svelte +181 -67
  45. package/dist/ui/components/InlineDelta.svelte +50 -31
  46. package/dist/ui/components/LineChart.svelte +54 -125
  47. package/dist/ui/components/PieChart.svelte +27 -37
  48. package/dist/ui/components/QueryLoad.svelte +77 -45
  49. package/dist/ui/components/Row.svelte +2 -1
  50. package/dist/ui/components/ScatterPlot.svelte +52 -0
  51. package/dist/ui/components/Skeleton.svelte +32 -0
  52. package/dist/ui/components/Table.svelte +3 -2
  53. package/dist/ui/components/TableGroupRow.svelte +28 -36
  54. package/dist/ui/components/TableHarness.svelte +32 -0
  55. package/dist/ui/components/TableHeader.svelte +34 -59
  56. package/dist/ui/components/TableRow.svelte +14 -38
  57. package/dist/ui/components/TableSubtotalRow.svelte +18 -21
  58. package/dist/ui/components/TableTotalRow.svelte +27 -37
  59. package/dist/ui/components/TextInput.svelte +13 -12
  60. package/dist/ui/components/Value.svelte +25 -0
  61. package/dist/ui/components/_Table.svelte +72 -70
  62. package/dist/ui/internal/ChartGallery.svelte +527 -0
  63. package/dist/ui/internal/ErrorDisplay.svelte +22 -97
  64. package/dist/ui/internal/LocalApp.svelte +84 -19
  65. package/dist/ui/internal/PageNavGroup.svelte +269 -0
  66. package/dist/ui/internal/Sidebar.svelte +178 -0
  67. package/dist/ui/internal/SidebarToggle.svelte +47 -0
  68. package/dist/ui/internal/StyleGallery.svelte +244 -0
  69. package/dist/ui/internal/clientCache.ts +2 -2
  70. package/dist/ui/internal/pageInputs.svelte.js +292 -0
  71. package/dist/ui/internal/queryEngine.ts +112 -129
  72. package/dist/ui/internal/runSocket.ts +31 -14
  73. package/dist/ui/internal/sidebar.svelte.js +18 -0
  74. package/dist/ui/internal/telemetry.ts +51 -16
  75. package/dist/ui/internal/types.d.ts +7 -0
  76. package/dist/ui/web.js +30 -11
  77. package/package.json +40 -38
  78. package/dist/skills/graphene/references/area-chart.md +0 -95
  79. package/dist/skills/graphene/references/bar-chart.md +0 -112
  80. package/dist/skills/graphene/references/line-chart.md +0 -108
  81. package/dist/skills/graphene/references/pie-chart.md +0 -29
  82. package/dist/skills/graphene/references/value-formats.md +0 -104
  83. package/dist/ui/component-utilities/autoFormatting.js +0 -280
  84. package/dist/ui/component-utilities/builtInFormats.js +0 -481
  85. package/dist/ui/component-utilities/chartContext.js +0 -12
  86. package/dist/ui/component-utilities/chartWindowDebug.js +0 -21
  87. package/dist/ui/component-utilities/checkInputs.js +0 -84
  88. package/dist/ui/component-utilities/convert.js +0 -15
  89. package/dist/ui/component-utilities/dateParsing.js +0 -56
  90. package/dist/ui/component-utilities/dropdownContext.ts +0 -1
  91. package/dist/ui/component-utilities/echarts.js +0 -252
  92. package/dist/ui/component-utilities/echartsThemes.js +0 -443
  93. package/dist/ui/component-utilities/formatTitle.js +0 -24
  94. package/dist/ui/component-utilities/formatting.js +0 -241
  95. package/dist/ui/component-utilities/getColumnExtents.js +0 -79
  96. package/dist/ui/component-utilities/getColumnSummary.js +0 -62
  97. package/dist/ui/component-utilities/getCompletedData.js +0 -122
  98. package/dist/ui/component-utilities/getDistinctCount.js +0 -7
  99. package/dist/ui/component-utilities/getDistinctValues.js +0 -15
  100. package/dist/ui/component-utilities/getSeriesConfig.js +0 -231
  101. package/dist/ui/component-utilities/getSortedData.js +0 -9
  102. package/dist/ui/component-utilities/getStackPercentages.js +0 -45
  103. package/dist/ui/component-utilities/getStackedData.js +0 -19
  104. package/dist/ui/component-utilities/getYAxisIndex.js +0 -15
  105. package/dist/ui/component-utilities/globalContexts.js +0 -1
  106. package/dist/ui/component-utilities/helpers/getCompletedData.helpers.js +0 -119
  107. package/dist/ui/component-utilities/replaceNulls.js +0 -16
  108. package/dist/ui/component-utilities/tableUtils.ts +0 -107
  109. package/dist/ui/component-utilities/tidyWithTypes.js +0 -9
  110. package/dist/ui/components/Area.svelte +0 -214
  111. package/dist/ui/components/Bar.svelte +0 -347
  112. package/dist/ui/components/Chart.svelte +0 -995
  113. package/dist/ui/components/Line.svelte +0 -227
  114. package/dist/ui/internal/NavSidebar.svelte +0 -396
  115. package/dist/ui/internal/theme.ts +0 -60
  116. package/dist/ui/public/inter-latin-ext.woff2 +0 -0
  117. package/dist/ui/public/inter-latin.woff2 +0 -0
@@ -0,0 +1,244 @@
1
+ <script lang="ts">
2
+ import {onMount} from 'svelte'
3
+
4
+ let styleVars = [
5
+ '--font-prose',
6
+ '--font-sans',
7
+ '--font-ui',
8
+ '--font-mono',
9
+ '--color-bg',
10
+ '--color-primary-strong',
11
+ '--color-body',
12
+ '--color-muted',
13
+ '--color-tertiary',
14
+ '--color-border',
15
+ '--color-border-strong',
16
+ '--color-code-bg',
17
+ ]
18
+
19
+ let styleRows = $state([] as Array<{name: string, value: string}>)
20
+
21
+ let sampleNodes = {} as Record<string, HTMLElement>
22
+
23
+ let readCssVar = (name: string) => getComputedStyle(document.documentElement).getPropertyValue(name).trim() || '(empty)'
24
+
25
+ let refreshRows = () => {
26
+ styleRows = styleVars.map(name => ({name, value: readCssVar(name)}))
27
+ }
28
+
29
+ let registerSample = (node: HTMLElement) => {
30
+ let key = node.dataset.styleDemo
31
+ if (!key) return
32
+
33
+ sampleNodes[key] = node
34
+ refreshRows()
35
+
36
+ return {
37
+ destroy: () => {
38
+ delete sampleNodes[key]
39
+ },
40
+ }
41
+ }
42
+
43
+ onMount(() => {
44
+ refreshRows()
45
+
46
+ let cssObserver = new MutationObserver(() => refreshRows())
47
+ cssObserver.observe(document.head, {childList: true, subtree: true, attributes: true})
48
+
49
+ let frame = requestAnimationFrame(() => refreshRows())
50
+ window.addEventListener('resize', refreshRows)
51
+
52
+ return () => {
53
+ cssObserver.disconnect()
54
+ cancelAnimationFrame(frame)
55
+ window.removeEventListener('resize', refreshRows)
56
+ }
57
+ })
58
+ </script>
59
+
60
+ <div class="style-demo">
61
+ <h1 class="style-demo-title">Style Demo</h1>
62
+
63
+ <section class="sample-panel">
64
+ <p>This is a regular paragraph at the top of the post. It has a couple sentences to give it some body. The quick brown fox jumps over the lazy dog.</p>
65
+
66
+ <h1 data-style-demo="h1" use:registerSample>Heading 1</h1>
67
+ <p>This is text after an h1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
68
+
69
+ <h2 data-style-demo="h2" use:registerSample>Heading 2</h2>
70
+ <p>This is text after an h2. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
71
+
72
+ <h3 data-style-demo="h3" use:registerSample>Heading 3</h3>
73
+ <p>This is text after an h3. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
74
+
75
+ <h2>Paragraph spacing</h2>
76
+ <p>Here is the first paragraph. It should have visible space below it before the next paragraph starts.</p>
77
+ <p>Here is the second paragraph. There should be a clear gap between this and the paragraph above. If the spacing is working correctly, you'll see breathing room between each block.</p>
78
+ <p>Here is a third paragraph to make it obvious.</p>
79
+
80
+ <h2>Unordered list</h2>
81
+ <ul>
82
+ <li>First item in the list</li>
83
+ <li>Second item with <strong>bold text</strong> inside it</li>
84
+ <li>Third item with <em>italic text</em> inside it</li>
85
+ <li>Fourth item that is a bit longer to see how it wraps across multiple lines on smaller viewports</li>
86
+ <li>Fifth and final item</li>
87
+ </ul>
88
+
89
+ <h2>Ordered list</h2>
90
+ <ol>
91
+ <li>Step one: install dependencies</li>
92
+ <li>Step two: configure your environment</li>
93
+ <li>Step three: run the dev server</li>
94
+ <li>Step four: open your browser at localhost</li>
95
+ <li>Step five: profit</li>
96
+ </ol>
97
+
98
+ <h2>Nested lists</h2>
99
+ <ul>
100
+ <li>Top level item A
101
+ <ul>
102
+ <li>Nested item A1</li>
103
+ <li>Nested item A2
104
+ <ul>
105
+ <li>Doubly nested A2a</li>
106
+ </ul>
107
+ </li>
108
+ </ul>
109
+ </li>
110
+ <li>Top level item B
111
+ <ul>
112
+ <li>Nested item B1</li>
113
+ </ul>
114
+ </li>
115
+ <li>Top level item C</li>
116
+ </ul>
117
+
118
+ <h2>Inline formatting</h2>
119
+ <p data-style-demo="body" use:registerSample>
120
+ This paragraph has <strong>bold text</strong>, <em>italic text</em>, <del>strikethrough text</del>, and <code data-style-demo="code" use:registerSample>inline code</code>. It also has a <a data-style-demo="link" use:registerSample href="/index">link to the about page</a> to test link styling.
121
+ </p>
122
+ <p>You can also combine: <strong><em>bold and italic</em></strong> together.</p>
123
+
124
+ <h2>Blockquote</h2>
125
+ <blockquote data-style-demo="blockquote" use:registerSample>
126
+ This is a blockquote. It should have a left border and be visually distinct from regular paragraph text. It might span multiple lines to show how longer quotes are handled.
127
+ </blockquote>
128
+ <blockquote>Another blockquote with <strong>bold</strong> and <em>italic</em> inside it.</blockquote>
129
+
130
+ <h2>Horizontal rule</h2>
131
+ <p>Above the rule.</p>
132
+ <hr />
133
+ <p>Below the rule.</p>
134
+
135
+ <h2>Table</h2>
136
+ <table>
137
+ <thead>
138
+ <tr>
139
+ <th>Name</th>
140
+ <th>Role</th>
141
+ <th>Location</th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ <tr><td>Kevin Marr</td><td>Co-Founder &amp; CEO</td><td>San Francisco</td></tr>
146
+ <tr><td>Grant Marvin</td><td>Co-Founder &amp; CTO</td><td>San Francisco</td></tr>
147
+ <tr><td>Dylan Scott</td><td>Founding Engineer</td><td>San Francisco</td></tr>
148
+ </tbody>
149
+ </table>
150
+ <p>A table with alignment:</p>
151
+ <table>
152
+ <thead>
153
+ <tr>
154
+ <th style="text-align:left">Metric</th>
155
+ <th style="text-align:right">Q1</th>
156
+ <th style="text-align:right">Q2</th>
157
+ <th style="text-align:right">Q3</th>
158
+ <th style="text-align:right">Change</th>
159
+ </tr>
160
+ </thead>
161
+ <tbody>
162
+ <tr>
163
+ <td style="text-align:left">Revenue</td>
164
+ <td style="text-align:right">$1.2M</td>
165
+ <td style="text-align:right">$1.8M</td>
166
+ <td style="text-align:right">$2.4M</td>
167
+ <td style="text-align:right">+33%</td>
168
+ </tr>
169
+ <tr>
170
+ <td style="text-align:left">Active users</td>
171
+ <td style="text-align:right">4,200</td>
172
+ <td style="text-align:right">6,100</td>
173
+ <td style="text-align:right">8,800</td>
174
+ <td style="text-align:right">+44%</td>
175
+ </tr>
176
+ <tr>
177
+ <td style="text-align:left">Queries / day</td>
178
+ <td style="text-align:right">12,000</td>
179
+ <td style="text-align:right">19,500</td>
180
+ <td style="text-align:right">31,000</td>
181
+ <td style="text-align:right">+59%</td>
182
+ </tr>
183
+ </tbody>
184
+ </table>
185
+
186
+ <h2>Mixed content</h2>
187
+ <p>Here's a paragraph followed by a list:</p>
188
+ <p>There are three reasons analytics should live in your repo:</p>
189
+ <ol>
190
+ <li><strong>Drift prevention.</strong> When your semantic model is versioned alongside your dbt code, column renames and schema changes surface as type errors immediately.</li>
191
+ <li><strong>Code review.</strong> Changes to metrics and dashboards go through PRs just like application code — with diffs, comments, and history.</li>
192
+ <li><strong>Agent compatibility.</strong> AI coding agents work in your repo. If your analytics layer is a GUI, agents are locked out.</li>
193
+ </ol>
194
+ <p>And then back to regular prose after the list.</p>
195
+
196
+ <h2>Final paragraph</h2>
197
+ <p>This is the last paragraph of the post. Everything above should be styled consistently and match the overall site aesthetic — using the same font, color tokens, and spacing as the rest of graphenedata.com.</p>
198
+ </section>
199
+
200
+ <h2>Style variables</h2>
201
+ <table class="token-table">
202
+ <thead>
203
+ <tr>
204
+ <th>Variable</th>
205
+ <th>Value</th>
206
+ </tr>
207
+ </thead>
208
+ <tbody>
209
+ {#each styleRows as row (row.name)}
210
+ <tr>
211
+ <td><code>{row.name}</code></td>
212
+ <td><code>{row.value}</code></td>
213
+ </tr>
214
+ {/each}
215
+ </tbody>
216
+ </table>
217
+ </div>
218
+
219
+ <style>
220
+ .style-demo {
221
+ max-width: 1200px;
222
+ margin: 0 auto;
223
+ }
224
+
225
+ .sample-panel {
226
+ margin-bottom: 1.25rem;
227
+ }
228
+
229
+ .token-table {
230
+ width: 100%;
231
+ border-collapse: collapse;
232
+ margin-bottom: 1.25rem;
233
+ table-layout: fixed;
234
+ }
235
+
236
+ .token-table th,
237
+ .token-table td {
238
+ border: 1px solid var(--color-border);
239
+ text-align: left;
240
+ padding: 8px 10px;
241
+ vertical-align: top;
242
+ overflow-wrap: anywhere;
243
+ }
244
+ </style>
@@ -10,7 +10,7 @@ async function getCache() {
10
10
  return cache
11
11
  }
12
12
 
13
- export async function getHashes() {
13
+ export async function getHashes(): Promise<string[]> {
14
14
  let store = await getCache()
15
15
  let keys = await store.keys()
16
16
  return keys
@@ -23,7 +23,7 @@ export async function getHashes() {
23
23
  }
24
24
  return url.pathname.replace(/^\//, '')
25
25
  })
26
- .filter(h => !!h)
26
+ .filter(Boolean) as string[]
27
27
  }
28
28
 
29
29
  export async function cacheRead(hash: string): Promise<any | null> {
@@ -0,0 +1,292 @@
1
+ import { createContext } from "svelte";
2
+ class BoundField {
3
+ constructor(controller, name, codec) {
4
+ this.controller = controller;
5
+ this.name = name;
6
+ this.codec = codec;
7
+ this.value = $state(codec.empty());
8
+ this.hasExternalValue = $state(false);
9
+ this.controller.registerField(this);
10
+ this.sync(this.controller.getParams(), "init");
11
+ }
12
+ value;
13
+ hasExternalValue;
14
+ keys() {
15
+ return this.codec.keys(this.name);
16
+ }
17
+ sync(params, source) {
18
+ let next = this.codec.read(params, this.name);
19
+ this.value = next.value;
20
+ if (source === "init") this.hasExternalValue = next.present;
21
+ else if (source === "external") this.hasExternalValue = true;
22
+ }
23
+ normalize(params) {
24
+ this.codec.normalize?.(params, this.name);
25
+ }
26
+ set(next) {
27
+ let nextParams = this.codec.write(this.name, next);
28
+ if (typeof window !== "undefined" && window.$GRAPHENE?.updateParams) window.$GRAPHENE.updateParams(nextParams);
29
+ else this.controller.updateParams(nextParams);
30
+ }
31
+ destroy() {
32
+ this.controller.unregisterField(this);
33
+ }
34
+ }
35
+ const textCodec = {
36
+ keys: (name) => [name],
37
+ empty: () => "",
38
+ read(params, name) {
39
+ let raw = params[name];
40
+ if (raw === void 0 || raw === null) return { present: false, value: "" };
41
+ if (Array.isArray(raw)) return { present: raw.length > 0, value: raw.length ? String(raw[0] ?? "") : "" };
42
+ return { present: true, value: String(raw) };
43
+ },
44
+ write(name, value) {
45
+ return { [name]: value === "" ? null : value };
46
+ }
47
+ };
48
+ const dropdownSingleCodec = {
49
+ keys: (name) => [name],
50
+ empty: () => [],
51
+ read(params, name) {
52
+ let raw = params[name];
53
+ if (raw === void 0 || raw === null) return { present: false, value: [] };
54
+ if (Array.isArray(raw)) return { present: raw.length > 0, value: raw.length ? [raw[0]] : [] };
55
+ return { present: true, value: [raw] };
56
+ },
57
+ write(name, value) {
58
+ return { [name]: value.length ? value[0] : null };
59
+ }
60
+ };
61
+ const dropdownMultiCodec = {
62
+ keys: (name) => [name],
63
+ empty: () => [],
64
+ read(params, name) {
65
+ let raw = params[name];
66
+ if (raw === void 0 || raw === null) return { present: false, value: [] };
67
+ if (Array.isArray(raw)) return { present: raw.length > 0, value: [...raw] };
68
+ return { present: true, value: [raw] };
69
+ },
70
+ write(name, value) {
71
+ return { [name]: value.length ? [...value] : null };
72
+ },
73
+ normalize(params, name) {
74
+ let raw = params[name];
75
+ if (raw === void 0 || raw === null || Array.isArray(raw)) return;
76
+ params[name] = [raw];
77
+ }
78
+ };
79
+ const dateRangeCodec = {
80
+ keys: (name) => [`${name}_start`, `${name}_end`],
81
+ empty: () => ({ start: null, end: null }),
82
+ read(params, name) {
83
+ let startRaw = params[`${name}_start`];
84
+ let endRaw = params[`${name}_end`];
85
+ let start = readScalar(startRaw);
86
+ let end = readScalar(endRaw);
87
+ return { present: start !== null || end !== null, value: { start, end } };
88
+ },
89
+ write(name, value) {
90
+ return {
91
+ [`${name}_start`]: value.start,
92
+ [`${name}_end`]: value.end
93
+ };
94
+ }
95
+ };
96
+ function readScalar(value) {
97
+ if (value === void 0 || value === null) return null;
98
+ if (Array.isArray(value)) return value.length ? String(value[0] ?? "") : null;
99
+ return String(value);
100
+ }
101
+ function cloneParamValue(value) {
102
+ if (Array.isArray(value)) return [...value];
103
+ return value;
104
+ }
105
+ function cloneParams(params) {
106
+ return Object.fromEntries(Object.entries(params).map(([name, value]) => [name, cloneParamValue(value)]));
107
+ }
108
+ function paramValueEqual(left, right) {
109
+ if (Array.isArray(left) || Array.isArray(right)) {
110
+ if (!Array.isArray(left) || !Array.isArray(right)) return false;
111
+ if (left.length !== right.length) return false;
112
+ return left.every((value, index) => value === right[index]);
113
+ }
114
+ return left === right;
115
+ }
116
+ function getChangedKeys(left, right) {
117
+ let changed = /* @__PURE__ */ new Set();
118
+ let keys = /* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)]);
119
+ keys.forEach((key) => {
120
+ if (!paramValueEqual(left[key], right[key])) changed.add(key);
121
+ });
122
+ return changed;
123
+ }
124
+ class PageInputs {
125
+ params;
126
+ subscribers;
127
+ fieldsByKey;
128
+ onPopState;
129
+ constructor() {
130
+ this.params = $state.raw({});
131
+ this.subscribers = /* @__PURE__ */ new Set();
132
+ this.fieldsByKey = /* @__PURE__ */ new Map();
133
+ this.params = cloneParams(this.readFromUrl());
134
+ if (typeof window !== "undefined") {
135
+ this.onPopState = () => this.syncFromUrl();
136
+ window.addEventListener("popstate", this.onPopState);
137
+ }
138
+ }
139
+ destroy() {
140
+ if (this.onPopState) window.removeEventListener("popstate", this.onPopState);
141
+ this.subscribers.clear();
142
+ this.fieldsByKey.clear();
143
+ }
144
+ registerField(field) {
145
+ field.keys().forEach((key) => {
146
+ let fields = this.fieldsByKey.get(key);
147
+ if (!fields) {
148
+ fields = /* @__PURE__ */ new Set();
149
+ this.fieldsByKey.set(key, fields);
150
+ }
151
+ fields.add(field);
152
+ });
153
+ }
154
+ unregisterField(field) {
155
+ field.keys().forEach((key) => {
156
+ let fields = this.fieldsByKey.get(key);
157
+ if (!fields) return;
158
+ fields.delete(field);
159
+ if (fields.size === 0) this.fieldsByKey.delete(key);
160
+ });
161
+ }
162
+ text(name) {
163
+ return new BoundField(this, name, textCodec);
164
+ }
165
+ dropdown(name, multiple) {
166
+ return new BoundField(this, name, multiple ? dropdownMultiCodec : dropdownSingleCodec);
167
+ }
168
+ dateRange(name) {
169
+ return new BoundField(this, name, dateRangeCodec);
170
+ }
171
+ getParam(name) {
172
+ return this.params[name];
173
+ }
174
+ getParams() {
175
+ return cloneParams(this.params);
176
+ }
177
+ subscribeParams(subscriber) {
178
+ this.subscribers.add(subscriber);
179
+ return () => this.subscribers.delete(subscriber);
180
+ }
181
+ updateParam(name, value) {
182
+ this.updateParams({ [name]: value });
183
+ }
184
+ updateParams(nextParams) {
185
+ let merged = { ...this.params };
186
+ Object.entries(nextParams).forEach(([name, value]) => {
187
+ merged[name] = Array.isArray(value) ? [...value] : value;
188
+ });
189
+ this.applySnapshot(merged, "local");
190
+ }
191
+ reset() {
192
+ let changed = new Set(Object.keys(this.params));
193
+ if (changed.size === 0) return;
194
+ this.params = {};
195
+ this.syncUrl();
196
+ this.syncFields(changed, "local");
197
+ let snapshot = this.getParams();
198
+ this.subscribers.forEach((subscriber) => subscriber(snapshot, { changed }));
199
+ }
200
+ syncFromUrl() {
201
+ this.applySnapshot(this.readFromUrl(), "external");
202
+ }
203
+ applySnapshot(nextParams, source) {
204
+ let cloned = cloneParams(nextParams);
205
+ this.normalizeSnapshot(cloned);
206
+ let changed = getChangedKeys(this.params, cloned);
207
+ if (changed.size === 0) return;
208
+ this.params = cloned;
209
+ if (source !== "external") this.syncUrl();
210
+ this.syncFields(changed, source);
211
+ let snapshot = this.getParams();
212
+ this.subscribers.forEach((subscriber) => subscriber(snapshot, { changed }));
213
+ window.$GRAPHENE?.rerunQueries?.();
214
+ }
215
+ syncFields(changed, source) {
216
+ let fields = /* @__PURE__ */ new Set();
217
+ changed.forEach((key) => {
218
+ this.fieldsByKey.get(key)?.forEach((field) => fields.add(field));
219
+ });
220
+ fields.forEach((field) => field.sync(this.params, source));
221
+ }
222
+ normalizeSnapshot(params) {
223
+ let fields = /* @__PURE__ */ new Set();
224
+ this.fieldsByKey.forEach((keyFields) => {
225
+ keyFields.forEach((field) => fields.add(field));
226
+ });
227
+ fields.forEach((field) => field.normalize(params));
228
+ }
229
+ syncUrl() {
230
+ if (typeof window === "undefined") return;
231
+ let search = new URLSearchParams();
232
+ Object.entries(this.params).forEach(([name, value]) => {
233
+ if (Array.isArray(value)) {
234
+ value.forEach((item) => search.append(name, String(item)));
235
+ return;
236
+ }
237
+ if (value === null || value === void 0 || value === "") return;
238
+ search.append(name, String(value));
239
+ });
240
+ let nextSearch = search.toString();
241
+ let currentSearch = window.location.search.replace(/^\?/, "");
242
+ if (nextSearch === currentSearch) return;
243
+ let nextUrl = window.location.pathname + (nextSearch ? `?${nextSearch}` : "") + window.location.hash;
244
+ window.history.replaceState(window.history.state, "", nextUrl);
245
+ }
246
+ readFromUrl() {
247
+ if (typeof window === "undefined") return {};
248
+ let next = {};
249
+ for (let [name, value] of new URLSearchParams(window.location.search).entries()) {
250
+ let existing = next[name];
251
+ if (existing === void 0) next[name] = value;
252
+ else if (Array.isArray(existing)) existing.push(value);
253
+ else next[name] = [String(existing), value];
254
+ }
255
+ return next;
256
+ }
257
+ }
258
+ const [getPageInputsContext, setPageInputsContext] = createContext();
259
+ let activePageInputs = null;
260
+ function activatePageInputs(pageInputs) {
261
+ if (activePageInputs && activePageInputs !== pageInputs) activePageInputs.destroy();
262
+ activePageInputs = pageInputs;
263
+ return pageInputs;
264
+ }
265
+ function getActivePageInputs() {
266
+ if (!activePageInputs) activePageInputs = new PageInputs();
267
+ return activePageInputs;
268
+ }
269
+ function releasePageInputs(pageInputs) {
270
+ if (activePageInputs !== pageInputs) return;
271
+ pageInputs.destroy();
272
+ activePageInputs = null;
273
+ }
274
+ function getPageInputs() {
275
+ try {
276
+ return getPageInputsContext();
277
+ } catch {
278
+ return getActivePageInputs();
279
+ }
280
+ }
281
+ function captureInitial(factory) {
282
+ return factory();
283
+ }
284
+ export {
285
+ PageInputs,
286
+ activatePageInputs,
287
+ captureInitial,
288
+ getActivePageInputs,
289
+ getPageInputs,
290
+ releasePageInputs,
291
+ setPageInputsContext
292
+ };