@altertable/data-app 0.65.0 → 0.67.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 (202) hide show
  1. package/AGENTS.md +38 -1
  2. package/CONTRIBUTING.md +54 -14
  3. package/README.md +1 -1
  4. package/dist/chunks/index-23dktynd.js +80 -0
  5. package/dist/chunks/index-23dktynd.js.map +10 -0
  6. package/dist/chunks/index-8qfxv0h5.js +14160 -0
  7. package/dist/chunks/index-8qfxv0h5.js.map +108 -0
  8. package/dist/chunks/index-ag0cgnvq.js +639 -0
  9. package/dist/chunks/index-ag0cgnvq.js.map +13 -0
  10. package/dist/chunks/index-dq7b35fn.js +307 -0
  11. package/dist/chunks/index-dq7b35fn.js.map +13 -0
  12. package/dist/chunks/{contract-a061yj6r.js → index-ev2b5aaf.js} +41 -72
  13. package/dist/chunks/index-ev2b5aaf.js.map +12 -0
  14. package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
  15. package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
  16. package/dist/chunks/{contract-sm7j7k95.js → index-ktmvs916.js} +9 -150
  17. package/dist/chunks/index-ktmvs916.js.map +13 -0
  18. package/dist/chunks/{contract-txjy0en2.js → index-m6n8ctfc.js} +64 -72
  19. package/dist/chunks/index-m6n8ctfc.js.map +10 -0
  20. package/dist/chunks/index-mt93ff2b.js +28 -0
  21. package/dist/chunks/index-mt93ff2b.js.map +10 -0
  22. package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
  23. package/dist/chunks/index-qd5xgx6y.js.map +13 -0
  24. package/dist/chunks/index-x69p7cv7.js +149 -0
  25. package/dist/chunks/index-x69p7cv7.js.map +10 -0
  26. package/dist/client/index.js +12 -7
  27. package/dist/client/index.js.map +1 -1
  28. package/dist/core/appearance.js +1 -1
  29. package/dist/core/config.js +7 -3
  30. package/dist/core/config.js.map +1 -1
  31. package/dist/core/contract.js +24 -6
  32. package/dist/core/contract.js.map +1 -1
  33. package/dist/core/format.js +1 -1
  34. package/dist/embed/index.js +16 -12
  35. package/dist/embed/index.js.map +4 -4
  36. package/dist/index.js +12 -0
  37. package/dist/index.js.map +9 -0
  38. package/dist/local.js +20 -30
  39. package/dist/local.js.map +7 -8
  40. package/dist/react/embed/index.js +1 -1
  41. package/dist/react/embed/index.js.map +2 -2
  42. package/dist/react/index.js +2567 -11542
  43. package/dist/react/index.js.map +16 -91
  44. package/dist/react/ui/index.js +2272 -0
  45. package/dist/react/ui/index.js.map +28 -0
  46. package/dist/server.js +13 -26
  47. package/dist/server.js.map +6 -7
  48. package/dist/types/client/annotations.d.ts +27 -0
  49. package/dist/types/client/data-client.d.ts +1 -2
  50. package/dist/types/client/iframe.d.ts +4 -2
  51. package/dist/types/client/index.d.ts +1 -0
  52. package/dist/types/core/annotations.d.ts +94 -0
  53. package/dist/types/core/appearance.d.ts +2 -2
  54. package/dist/types/core/config.d.ts +48 -4
  55. package/dist/types/core/contract.d.ts +21 -11
  56. package/dist/types/core/dimension.d.ts +6 -4
  57. package/dist/types/core/filters.d.ts +44 -0
  58. package/dist/types/core/messages.d.ts +2 -0
  59. package/dist/types/core/operation-types.d.ts +5 -2
  60. package/dist/types/core/operation.d.ts +3 -4
  61. package/dist/types/core/presentation.d.ts +2 -0
  62. package/dist/types/core/queries.d.ts +16 -0
  63. package/dist/types/core/uuid.d.ts +2 -0
  64. package/dist/types/core/variables.d.ts +34 -18
  65. package/dist/types/embed/runtime-html.d.ts +2 -0
  66. package/dist/types/embed/source.d.ts +2 -1
  67. package/dist/types/index.d.ts +3 -0
  68. package/dist/types/react/annotations/AnnotationBar.d.ts +30 -0
  69. package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
  70. package/dist/types/react/annotations/AnnotationEditor.d.ts +16 -0
  71. package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
  72. package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
  73. package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
  74. package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
  75. package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
  76. package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
  77. package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
  78. package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
  79. package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
  80. package/dist/types/react/annotations/styles.d.ts +3 -0
  81. package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
  82. package/dist/types/react/annotations/useDataAppAnnotations.d.ts +22 -0
  83. package/dist/types/react/app-context.d.ts +3 -0
  84. package/dist/types/react/bindings.d.ts +110 -0
  85. package/dist/types/react/content.d.ts +24 -11
  86. package/dist/types/react/hooks.d.ts +29 -787
  87. package/dist/types/react/index.d.ts +31 -106
  88. package/dist/types/react/mount.d.ts +6 -4
  89. package/dist/types/react/source-owner.d.ts +2 -0
  90. package/dist/types/react/style-contract.d.ts +80 -0
  91. package/dist/types/react/style-validation.d.ts +2 -0
  92. package/dist/types/react/ui/AboutData.d.ts +9 -1
  93. package/dist/types/react/ui/AppHeader.d.ts +1 -2
  94. package/dist/types/react/ui/AppLayout.d.ts +3 -9
  95. package/dist/types/react/ui/AppToolbar.d.ts +5 -8
  96. package/dist/types/react/ui/AreaChart.d.ts +7 -0
  97. package/dist/types/react/ui/BarChart.d.ts +6 -0
  98. package/dist/types/react/ui/Button.d.ts +3 -3
  99. package/dist/types/react/ui/ChartLegend.d.ts +31 -0
  100. package/dist/types/react/ui/Checkbox.d.ts +15 -4
  101. package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
  102. package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
  103. package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
  104. package/dist/types/react/ui/ComposedChart.d.ts +41 -0
  105. package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
  106. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
  107. package/dist/types/react/ui/DataApp.d.ts +17 -58
  108. package/dist/types/react/ui/DataAppFrame.d.ts +41 -0
  109. package/dist/types/react/ui/DataBoundary.d.ts +4 -5
  110. package/dist/types/react/ui/DataSection.d.ts +6 -18
  111. package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
  112. package/dist/types/react/ui/DataValue.d.ts +9 -0
  113. package/dist/types/react/ui/DataWidget.d.ts +2 -1
  114. package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
  115. package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
  116. package/dist/types/react/ui/ExportControl.d.ts +1 -1
  117. package/dist/types/react/ui/FilterActions.d.ts +10 -0
  118. package/dist/types/react/ui/FilterBar.d.ts +3 -0
  119. package/dist/types/react/ui/GettingStarted.d.ts +2 -4
  120. package/dist/types/react/ui/HelpPopover.d.ts +3 -4
  121. package/dist/types/react/ui/InspectionContext.d.ts +2 -0
  122. package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
  123. package/dist/types/react/ui/LineChart.d.ts +7 -0
  124. package/dist/types/react/ui/Menu.d.ts +12 -0
  125. package/dist/types/react/ui/MetricWidget.d.ts +4 -3
  126. package/dist/types/react/ui/NumberField.d.ts +27 -0
  127. package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
  128. package/dist/types/react/ui/PieChart.d.ts +11 -0
  129. package/dist/types/react/ui/PresentStory.d.ts +2 -5
  130. package/dist/types/react/ui/RadioGroup.d.ts +18 -0
  131. package/dist/types/react/ui/RefreshControl.d.ts +1 -2
  132. package/dist/types/react/ui/ScatterChart.d.ts +17 -0
  133. package/dist/types/react/ui/SearchField.d.ts +1 -2
  134. package/dist/types/react/ui/SearchInput.d.ts +3 -1
  135. package/dist/types/react/ui/Select.d.ts +14 -0
  136. package/dist/types/react/ui/SelectionMark.d.ts +2 -1
  137. package/dist/types/react/ui/Sheet.d.ts +2 -1
  138. package/dist/types/react/ui/Skeleton.d.ts +5 -2
  139. package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
  140. package/dist/types/react/ui/TableWidget.d.ts +19 -16
  141. package/dist/types/react/ui/Tabs.d.ts +6 -2
  142. package/dist/types/react/ui/TextWidget.d.ts +2 -1
  143. package/dist/types/react/ui/Tooltip.d.ts +4 -3
  144. package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
  145. package/dist/types/react/ui/TrendChart.d.ts +5 -0
  146. package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
  147. package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
  148. package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
  149. package/dist/types/react/ui/chart-data.d.ts +25 -0
  150. package/dist/types/react/ui/chart-primitives.d.ts +20 -0
  151. package/dist/types/react/ui/data-context.d.ts +18 -7
  152. package/dist/types/react/ui/icons.d.ts +2 -0
  153. package/dist/types/react/ui/index.d.ts +98 -0
  154. package/dist/types/react/ui/keyboard.d.ts +6 -0
  155. package/dist/types/react/ui/metric.d.ts +1 -1
  156. package/dist/types/react/ui/presentation.d.ts +5 -1
  157. package/dist/types/react/ui/shortcuts.d.ts +14 -1
  158. package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
  159. package/dist/types/react/ui/variables.d.ts +4 -2
  160. package/dist/types/react/view-controls.d.ts +3 -1
  161. package/dist/types/react/view-runtime.d.ts +29 -0
  162. package/dist/types/react/view.d.ts +21 -4
  163. package/dist/types/react/widgets.d.ts +79 -0
  164. package/dist/types/server/handler.d.ts +4 -4
  165. package/dist/worker.js +20 -737
  166. package/docs/app-authoring.md +72 -50
  167. package/docs/client.md +8 -8
  168. package/docs/contract.md +42 -20
  169. package/docs/data-context.md +91 -0
  170. package/docs/embed.md +10 -2
  171. package/docs/formatting-and-appearance.md +68 -0
  172. package/docs/hosted-apps.md +9 -15
  173. package/docs/layout.md +5 -3
  174. package/docs/react-embed.md +5 -4
  175. package/docs/react.md +22 -317
  176. package/docs/server-bun.md +1 -1
  177. package/docs/server.md +4 -4
  178. package/docs/stories-and-export.md +52 -0
  179. package/docs/styling.md +62 -0
  180. package/docs/ui-quality.md +20 -0
  181. package/docs/ui.md +196 -0
  182. package/docs/variables.md +171 -0
  183. package/docs/views.md +123 -0
  184. package/docs/widgets.md +145 -0
  185. package/examples/starter-data-app/index.tsx +115 -86
  186. package/package.json +25 -12
  187. package/dist/chunks/contract-a061yj6r.js.map +0 -12
  188. package/dist/chunks/contract-g6hky7x6.js +0 -282
  189. package/dist/chunks/contract-g6hky7x6.js.map +0 -12
  190. package/dist/chunks/contract-sm7j7k95.js.map +0 -14
  191. package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
  192. package/dist/chunks/contract-txjy0en2.js.map +0 -10
  193. package/dist/chunks/contract-wz59z8pq.js +0 -8
  194. package/dist/chunks/contract-wz59z8pq.js.map +0 -10
  195. package/dist/chunks/contract-yxbjea23.js +0 -328
  196. package/dist/chunks/contract-yxbjea23.js.map +0 -12
  197. package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
  198. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
  199. package/docs/releasing.md +0 -79
  200. /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
  201. /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
  202. /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
@@ -0,0 +1,639 @@
1
+ import {
2
+ formatDateRange2
3
+ } from "./index-mev09s5v.js";
4
+ import {
5
+ invariant,
6
+ snapshotQueries,
7
+ resolveQueryParameters,
8
+ defineMessageRoute2
9
+ } from "./index-qd5xgx6y.js";
10
+
11
+ // src/core/contract.ts
12
+ import { parseDate, today } from "@internationalized/date";
13
+
14
+ // src/core/dimension.ts
15
+ var allSelection = { kind: "all" };
16
+ function dimensionMemberKey(member) {
17
+ return member.kind === "missing" ? "missing" : `value:${typeof member.value}:${member.value}`;
18
+ }
19
+ function dimensionFilter2(config) {
20
+ const { key, label, selectionMode, valueType } = config;
21
+ const options = config.options ?? [];
22
+ const optionKeys = options.map((option) => dimensionMemberKey({ kind: "value", value: option.value }));
23
+ invariant(!!key && options.length <= 200 && new Set(optionKeys).size === options.length, "Dimension options need a key, at most 200 distinct values.");
24
+ invariant(options.every((option) => typeof option.value === valueType && !!option.label.trim() && (option.count === undefined || Number.isSafeInteger(option.count) && option.count >= 0)), "Dimension options need typed values, labels, and nonnegative counts.");
25
+ const allowed = new Set(optionKeys);
26
+ const maxSelected = config.maxSelected ?? (selectionMode === "single" ? 1 : 20);
27
+ invariant(Number.isInteger(maxSelected) && maxSelected >= 1 && maxSelected <= 50 && (selectionMode !== "single" || maxSelected === 1), "Dimension selection limit is invalid.");
28
+ const allowMissing = config.allowMissing ?? false;
29
+ const defaultValue = allSelection;
30
+ function validMember(member) {
31
+ if (member.kind === "missing")
32
+ return allowMissing;
33
+ if (member.kind !== "value" || typeof member.value !== valueType)
34
+ return false;
35
+ if (!config.facet)
36
+ return allowed.has(dimensionMemberKey(member));
37
+ return typeof member.value === "number" ? Number.isFinite(member.value) : member.value.length <= 100;
38
+ }
39
+ function valid(value) {
40
+ if (!value || typeof value !== "object")
41
+ return false;
42
+ if (value.kind === "all")
43
+ return true;
44
+ if (value.kind !== "include" && !(config.allowExclusion && value.kind === "exclude") || !Array.isArray(value.members) || !value.members.length || value.members.length > maxSelected)
45
+ return false;
46
+ const ids = value.members.map(dimensionMemberKey);
47
+ return new Set(ids).size === ids.length && value.members.every(validMember);
48
+ }
49
+ function encode(value) {
50
+ return value.kind === "all" ? null : JSON.stringify({
51
+ kind: value.kind,
52
+ members: value.members.map((member) => member.kind === "missing" ? ["m"] : ["v", member.value])
53
+ });
54
+ }
55
+ function parse(raw) {
56
+ if (raw === null)
57
+ return defaultValue;
58
+ if (raw.length > 4096)
59
+ return defaultValue;
60
+ try {
61
+ const parsed = JSON.parse(raw);
62
+ const tokens = Array.isArray(parsed) ? { kind: "include", members: parsed } : parsed;
63
+ if (!tokens || !Array.isArray(tokens.members))
64
+ return defaultValue;
65
+ const value = {
66
+ kind: tokens.kind,
67
+ members: tokens.members.map((token) => Array.isArray(token) && token.length === 1 && token[0] === "m" ? { kind: "missing" } : Array.isArray(token) && token.length === 2 && token[0] === "v" ? { kind: "value", value: token[1] } : { kind: "invalid" })
68
+ };
69
+ return valid(value) ? value : defaultValue;
70
+ } catch {
71
+ return defaultValue;
72
+ }
73
+ }
74
+ return {
75
+ kind: "dimension",
76
+ label,
77
+ valueType,
78
+ selectionMode,
79
+ options,
80
+ facet: config.facet,
81
+ allowMissing,
82
+ allowExclusion: config.allowExclusion ?? false,
83
+ maxSelected,
84
+ urlKeys: [key],
85
+ defaultValue,
86
+ clearValue: allSelection,
87
+ history: config.history ?? "push",
88
+ valid,
89
+ same(left, right) {
90
+ return encode(left) === encode(right);
91
+ },
92
+ read(params) {
93
+ return parse(params.get(key));
94
+ },
95
+ write(value) {
96
+ return { [key]: encode(value) };
97
+ },
98
+ describe(value) {
99
+ return value.kind === "all" ? `All ${label.toLocaleLowerCase()}` : `${value.kind === "exclude" ? "Exclude: " : ""}${value.members.map((member) => member.kind === "missing" ? "Missing" : options.find((option) => option.value === member.value)?.label ?? String(member.value)).join(", ")}`;
100
+ }
101
+ };
102
+ }
103
+ function parseDimensionSelection2(value, filter) {
104
+ invariant(filter.valid(value), `Invalid ${filter.label} selection.`);
105
+ return value;
106
+ }
107
+ function parseFacetOptions2(value, filter) {
108
+ invariant(Array.isArray(value) && value.length <= 200, "Facet options exceed their bound.");
109
+ const options = [];
110
+ const seen = new Set;
111
+ for (const item of value) {
112
+ invariant(item && typeof item === "object", "Invalid facet option.");
113
+ const option = item;
114
+ invariant(typeof option.value === filter.valueType && filter.valid({
115
+ kind: "include",
116
+ members: [{ kind: "value", value: option.value }]
117
+ }) && typeof option.label === "string" && !!option.label.trim() && option.label.length <= 100 && (option.count === undefined || Number.isSafeInteger(option.count) && option.count >= 0), "Invalid facet option.");
118
+ const id = dimensionMemberKey({ kind: "value", value: option.value });
119
+ invariant(!seen.has(id), "Duplicate facet option.");
120
+ seen.add(id);
121
+ options.push(option);
122
+ }
123
+ return options;
124
+ }
125
+ function dimensionPredicate2(column, selection, allowedColumns) {
126
+ invariant(allowedColumns.includes(column) && /^[A-Za-z_][A-Za-z_0-9]*(?:\.[A-Za-z_][A-Za-z_0-9]*)*$/.test(column), "Invalid dimension column.");
127
+ if (selection.kind === "all")
128
+ return "";
129
+ invariant(selection.members.length > 0, "Empty dimension inclusion is invalid.");
130
+ const values = selection.members.filter((member) => member.kind === "value");
131
+ const missing = selection.members.some((member) => member.kind === "missing");
132
+ const literals = values.map(({ value }) => {
133
+ if (typeof value === "number") {
134
+ invariant(Number.isFinite(value), "Invalid numeric dimension value.");
135
+ return String(value);
136
+ }
137
+ const text = value;
138
+ for (let index = 0;index < text.length; index++)
139
+ invariant(text.charCodeAt(index) >= 32 && text.charCodeAt(index) !== 127, "Invalid control character in dimension value.");
140
+ return `'${text.replaceAll("'", "''")}'`;
141
+ });
142
+ const clauses = [
143
+ ...literals.length ? [`${column} IN (${literals.join(", ")})`] : [],
144
+ ...missing ? [`${column} IS NULL`] : []
145
+ ];
146
+ const predicate = `(${clauses.join(" OR ")})`;
147
+ return selection.kind === "exclude" ? missing ? `NOT ${predicate}` : `(${column} IS NULL OR NOT ${predicate})` : predicate;
148
+ }
149
+ // src/core/annotations.ts
150
+ function object(value) {
151
+ if (!value || typeof value !== "object" || Array.isArray(value))
152
+ throw new Error("Invalid annotation.");
153
+ return value;
154
+ }
155
+ function text(value, limit, required = false) {
156
+ if (typeof value !== "string" || value.length > limit || required && !value.trim())
157
+ throw new Error("Invalid annotation text.");
158
+ return value;
159
+ }
160
+ function names(value) {
161
+ if (!Array.isArray(value) || value.length > 20)
162
+ throw new Error("Invalid annotation references.");
163
+ return value.map((name) => text(name, 128, true));
164
+ }
165
+ function number(value, positive = false) {
166
+ if (typeof value !== "number" || !Number.isFinite(value) || Math.abs(value) > 1e6 || positive && value < 0)
167
+ throw new Error("Invalid annotation geometry.");
168
+ return value;
169
+ }
170
+ function parseDataAppAnnotationDraft2(value) {
171
+ const input = object(value);
172
+ const target = object(input.target);
173
+ const context = object(input.context);
174
+ const viewport = object(context.viewport);
175
+ const rect = object(context.rect);
176
+ if (target.kind !== "widget" && target.kind !== "element" && target.kind !== "app")
177
+ throw new Error("Invalid annotation target.");
178
+ let screenshot;
179
+ if (context.screenshot !== undefined) {
180
+ const image = object(context.screenshot);
181
+ const dataUrl = text(image.dataUrl, 350000, true);
182
+ if (image.mimeType !== "image/png" || !/^data:image\/png;base64,iVBORw0KGgo[A-Za-z0-9+/]*={0,2}$/.test(dataUrl))
183
+ throw new Error("Invalid annotation screenshot.");
184
+ const width = number(image.width, true);
185
+ const height = number(image.height, true);
186
+ if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width > 1024 || height > 1024)
187
+ throw new Error("Invalid screenshot dimensions.");
188
+ const encoded = dataUrl.slice("data:image/png;base64,".length);
189
+ if (encoded.length % 4 !== 0 || encoded.length / 4 * 3 - (encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0) > 262144)
190
+ throw new Error("Screenshot is too large.");
191
+ const header = atob(encoded.slice(0, 44));
192
+ if (header.length < 24 || header.slice(12, 16) !== "IHDR")
193
+ throw new Error("Invalid PNG header.");
194
+ const bytes = Uint8Array.from(header, (character) => character.charCodeAt(0));
195
+ const dimensions = new DataView(bytes.buffer);
196
+ if (dimensions.getUint32(16) !== width || dimensions.getUint32(20) !== height)
197
+ throw new Error("Screenshot dimensions do not match the image.");
198
+ screenshot = { mimeType: "image/png", dataUrl, width, height };
199
+ }
200
+ let anchor;
201
+ if (context.anchor !== undefined) {
202
+ const point = object(context.anchor);
203
+ const x = number(point.x, true);
204
+ const y = number(point.y, true);
205
+ if (x > 1 || y > 1)
206
+ throw new Error("Invalid annotation anchor.");
207
+ anchor = { x, y };
208
+ }
209
+ let cursor;
210
+ if (context.cursor !== undefined) {
211
+ const point = object(context.cursor);
212
+ cursor = { x: number(point.x), y: number(point.y) };
213
+ }
214
+ let region;
215
+ if (context.region !== undefined) {
216
+ const area = object(context.region);
217
+ region = {
218
+ x: number(area.x, true),
219
+ y: number(area.y, true),
220
+ width: number(area.width, true),
221
+ height: number(area.height, true)
222
+ };
223
+ if (target.kind !== "app" || region.width <= 0 || region.height <= 0 || region.x + region.width > 1.000001 || region.y + region.height > 1.000001)
224
+ throw new Error("Invalid annotation selection.");
225
+ }
226
+ const draft = {
227
+ id: text(input.id, 128, true),
228
+ target: {
229
+ id: text(target.id, 128, true),
230
+ label: text(target.label, 256, true),
231
+ kind: target.kind,
232
+ text: text(target.text, 1024),
233
+ queryNames: names(target.queryNames),
234
+ glossaryIds: names(target.glossaryIds)
235
+ },
236
+ context: {
237
+ ...anchor ? { anchor } : {},
238
+ ...region ? { region } : {},
239
+ ...cursor ? { cursor } : {},
240
+ ...screenshot ? { screenshot } : {},
241
+ search: text(context.search, 2048),
242
+ hash: text(context.hash, 1024),
243
+ viewport: {
244
+ width: number(viewport.width, true),
245
+ height: number(viewport.height, true)
246
+ },
247
+ rect: {
248
+ x: number(rect.x),
249
+ y: number(rect.y),
250
+ width: number(rect.width, true),
251
+ height: number(rect.height, true)
252
+ },
253
+ ...context.view === undefined ? {} : { view: text(context.view, 128) },
254
+ ...context.displayedInput === undefined ? {} : { displayedInput: context.displayedInput }
255
+ },
256
+ comment: text(input.comment, 2000, true).trim()
257
+ };
258
+ const json = JSON.stringify({
259
+ ...draft,
260
+ context: { ...draft.context, screenshot: undefined }
261
+ });
262
+ if (new TextEncoder().encode(json).byteLength > 12000)
263
+ throw new Error("Annotation is too large.");
264
+ return JSON.parse(JSON.stringify(draft));
265
+ }
266
+ var annotationDraftRoute2 = /* @__PURE__ */ defineMessageRoute2({
267
+ input: parseDataAppAnnotationDraft2,
268
+ output(value) {
269
+ if (value !== null)
270
+ throw new Error("Invalid annotation response.");
271
+ return null;
272
+ }
273
+ });
274
+ var annotationEditorStateRoute2 = /* @__PURE__ */ defineMessageRoute2({
275
+ input(value) {
276
+ const input = object(value);
277
+ if (typeof input.hasUnsavedChanges !== "boolean")
278
+ throw new Error("Invalid annotation editor state.");
279
+ return { hasUnsavedChanges: input.hasUnsavedChanges };
280
+ },
281
+ output(value) {
282
+ if (value !== null)
283
+ throw new Error("Invalid annotation response.");
284
+ return null;
285
+ }
286
+ });
287
+ var annotationSendRoute2 = /* @__PURE__ */ defineMessageRoute2({
288
+ input(value) {
289
+ if (value !== null)
290
+ throw new Error("Invalid annotation send request.");
291
+ return null;
292
+ },
293
+ output(value) {
294
+ if (value !== null)
295
+ throw new Error("Invalid annotation response.");
296
+ return null;
297
+ }
298
+ });
299
+ var annotationUpdateRoute2 = /* @__PURE__ */ defineMessageRoute2({
300
+ input(value) {
301
+ const input = object(value);
302
+ return {
303
+ id: text(input.id, 128, true),
304
+ comment: text(input.comment, 2000, true).trim()
305
+ };
306
+ },
307
+ output(value) {
308
+ if (value !== null)
309
+ throw new Error("Invalid annotation response.");
310
+ return null;
311
+ }
312
+ });
313
+ var annotationModeRoute2 = /* @__PURE__ */ defineMessageRoute2({
314
+ input(value) {
315
+ const input = object(value);
316
+ if (typeof input.active !== "boolean")
317
+ throw new Error("Invalid annotation mode.");
318
+ return { active: input.active };
319
+ },
320
+ output(value) {
321
+ if (value !== null)
322
+ throw new Error("Invalid annotation response.");
323
+ return null;
324
+ }
325
+ });
326
+ function isAnnotationPresentation(value) {
327
+ if (!value || typeof value !== "object")
328
+ return false;
329
+ const input = value;
330
+ return typeof input.enabled === "boolean" && (input.active === undefined || typeof input.active === "boolean") && (input.pinsVisible === undefined || typeof input.pinsVisible === "boolean") && (input.showHint === undefined || typeof input.showHint === "boolean") && (input.readOnly === undefined || typeof input.readOnly === "boolean") && (input.selectedAnnotationId === undefined || typeof input.selectedAnnotationId === "string" && input.selectedAnnotationId.length <= 128) && (input.selectionId === undefined || typeof input.selectionId === "string" && input.selectionId.length <= 128) && (input.selectedTargetId === undefined || typeof input.selectedTargetId === "string" && input.selectedTargetId.length <= 128) && (input.targets === undefined || Array.isArray(input.targets) && input.targets.length <= 50 && input.targets.every((target) => target && typeof target.id === "string" && target.id.length <= 128 && typeof target.targetId === "string" && target.targetId.length <= 128 && Number.isSafeInteger(target.number) && target.number > 0 && (target.anchor === undefined || typeof target.anchor.x === "number" && Number.isFinite(target.anchor.x) && target.anchor.x >= 0 && target.anchor.x <= 1 && typeof target.anchor.y === "number" && Number.isFinite(target.anchor.y) && target.anchor.y >= 0 && target.anchor.y <= 1) && (target.region === undefined || typeof target.region === "object" && target.region !== null && ["x", "y", "width", "height"].every((key) => {
331
+ const value = target.region[key];
332
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
333
+ }) && target.region.width > 0 && target.region.height > 0 && target.region.x + target.region.width <= 1.000001 && target.region.y + target.region.height <= 1.000001) && (target.comment === undefined || typeof target.comment === "string" && target.comment.length <= 2000)));
334
+ }
335
+ // src/core/filters.ts
336
+ var all = { kind: "all" };
337
+ var numberOperatorLabels = {
338
+ eq: "Equals",
339
+ ne: "Does not equal",
340
+ gt: "Greater than",
341
+ gte: "At least",
342
+ lt: "Less than",
343
+ lte: "At most"
344
+ };
345
+ function numberFilter2({
346
+ key,
347
+ label,
348
+ min,
349
+ max,
350
+ defaultValue = all,
351
+ history = "push"
352
+ }) {
353
+ invariant(!!key.trim(), "Numeric filter requires a URL key.");
354
+ invariant((min === undefined || Number.isFinite(min)) && (max === undefined || Number.isFinite(max)) && (min === undefined || max === undefined || min <= max), "Invalid numeric filter bounds.");
355
+ function validNumber(value) {
356
+ return typeof value === "number" && Number.isFinite(value) && (min === undefined || value >= min) && (max === undefined || value <= max);
357
+ }
358
+ function valid(value) {
359
+ if (!value || typeof value !== "object")
360
+ return false;
361
+ if (value.kind === "all")
362
+ return true;
363
+ if (value.kind === "comparison")
364
+ return typeof value.operator === "string" && Object.hasOwn(numberOperatorLabels, value.operator) && validNumber(value.value);
365
+ return value.kind === "range" && (value.min !== undefined || value.max !== undefined) && (value.min === undefined || validNumber(value.min)) && (value.max === undefined || validNumber(value.max)) && (value.min === undefined || value.max === undefined || value.min <= value.max);
366
+ }
367
+ function encode(value) {
368
+ return value.kind === "all" ? null : value.kind === "range" ? JSON.stringify({ kind: "range", min: value.min, max: value.max }) : JSON.stringify({
369
+ kind: "comparison",
370
+ operator: value.operator,
371
+ value: value.value
372
+ });
373
+ }
374
+ invariant(valid(defaultValue), `Invalid default for ${label}.`);
375
+ return {
376
+ kind: "numberFilter",
377
+ label,
378
+ min,
379
+ max,
380
+ defaultValue,
381
+ clearValue: all,
382
+ urlKeys: [key],
383
+ history,
384
+ valid,
385
+ same: (left, right) => encode(left) === encode(right),
386
+ describe(value) {
387
+ return value.kind === "all" ? "Any" : value.kind === "comparison" ? `${numberOperatorLabels[value.operator]} ${value.value}` : value.min === undefined ? `At most ${value.max}` : value.max === undefined ? `At least ${value.min}` : `${value.min}–${value.max}`;
388
+ },
389
+ read(params) {
390
+ const raw = params.get(key);
391
+ if (raw === null)
392
+ return defaultValue;
393
+ if (raw.length > 512)
394
+ return defaultValue;
395
+ try {
396
+ const value = raw === "all" ? all : JSON.parse(raw);
397
+ return valid(value) ? value : defaultValue;
398
+ } catch {
399
+ return defaultValue;
400
+ }
401
+ },
402
+ write(value) {
403
+ return {
404
+ [key]: encode(value) === encode(defaultValue) ? null : value.kind === "all" ? "all" : encode(value)
405
+ };
406
+ }
407
+ };
408
+ }
409
+ function booleanFilter2({
410
+ key,
411
+ label,
412
+ defaultValue = all,
413
+ history = "push"
414
+ }) {
415
+ invariant(!!key.trim(), "Boolean filter requires a URL key.");
416
+ function valid(value) {
417
+ return !!value && typeof value === "object" && (value.kind === "all" || value.kind === "is" && typeof value.value === "boolean");
418
+ }
419
+ function encode(value) {
420
+ return value.kind === "all" ? "all" : String(value.value);
421
+ }
422
+ invariant(valid(defaultValue), `Invalid default for ${label}.`);
423
+ return {
424
+ kind: "booleanFilter",
425
+ label,
426
+ defaultValue,
427
+ clearValue: all,
428
+ urlKeys: [key],
429
+ history,
430
+ valid,
431
+ describe: (value) => value.kind === "all" ? "Any" : value.value ? "Yes" : "No",
432
+ same: (left, right) => encode(left) === encode(right),
433
+ read(params) {
434
+ const raw = params.get(key);
435
+ return raw === "all" ? all : raw === "true" || raw === "false" ? { kind: "is", value: raw === "true" } : defaultValue;
436
+ },
437
+ write(value) {
438
+ return {
439
+ [key]: encode(value) === encode(defaultValue) ? null : encode(value)
440
+ };
441
+ }
442
+ };
443
+ }
444
+ function parseNumberSelection2(value, filter) {
445
+ invariant(filter.valid(value), `Invalid ${filter.label} selection.`);
446
+ return value;
447
+ }
448
+ function parseBooleanSelection2(value, filter) {
449
+ invariant(filter.valid(value), `Invalid ${filter.label} selection.`);
450
+ return value;
451
+ }
452
+
453
+ // src/core/contract.ts
454
+ function parseEmptyInput2(value) {
455
+ invariant(typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 0, "This operation takes no inputs.");
456
+ return {};
457
+ }
458
+ function parseTrue2(value) {
459
+ invariant(value === true, "Expected a successful check.");
460
+ return true;
461
+ }
462
+ function previousDateRange2({
463
+ start,
464
+ end
465
+ }) {
466
+ const first = parseDate(start);
467
+ const last = parseDate(end);
468
+ const days = (last.toDate("UTC").getTime() - first.toDate("UTC").getTime()) / 86400000 + 1;
469
+ invariant(days >= 1, "Date range must start before it ends.");
470
+ const previousEnd = first.subtract({ days: 1 });
471
+ return {
472
+ start: previousEnd.subtract({ days: days - 1 }).toString(),
473
+ end: previousEnd.toString()
474
+ };
475
+ }
476
+ function defineDateRangeContract2(options) {
477
+ invariant(Number.isInteger(options.maxRangeDays) && options.maxRangeDays > 0, "Date range needs a positive maxRangeDays.");
478
+ if (options.minDate)
479
+ parseDate(options.minDate);
480
+ if (typeof options.maxDate === "string")
481
+ parseDate(options.maxDate);
482
+ today(options.timeZone);
483
+ function bounds() {
484
+ const latest = today(options.timeZone).subtract({
485
+ days: options.completeDays === false ? 0 : 1
486
+ });
487
+ const sourceMax = typeof options.maxDate === "function" ? options.maxDate() : options.maxDate;
488
+ const maxDate = sourceMax && parseDate(sourceMax).compare(latest) < 0 ? parseDate(sourceMax).toString() : latest.toString();
489
+ return {
490
+ minDate: options.minDate,
491
+ maxDate,
492
+ maxRangeDays: options.maxRangeDays,
493
+ timeZone: options.timeZone
494
+ };
495
+ }
496
+ function period({ start, end }) {
497
+ return {
498
+ kind: "calendar",
499
+ start,
500
+ end,
501
+ timeZone: options.timeZone
502
+ };
503
+ }
504
+ function describeInput(input) {
505
+ return `${formatDateRange2(input)} ${options.timeZone}`;
506
+ }
507
+ function comparison(input) {
508
+ const previous = previousDateRange2(input);
509
+ return options.minDate && previous.start < options.minDate ? null : previous;
510
+ }
511
+ function request(value, compare = false) {
512
+ const range = parseDateRangeInput2(value, bounds());
513
+ const previous = compare ? comparison(range) : null;
514
+ invariant(!compare || previous, "Comparison is outside available source coverage.");
515
+ return { range, comparison: previous };
516
+ }
517
+ function parseRequest(value) {
518
+ invariant(value && typeof value === "object", "Choose a date range.");
519
+ const input = value;
520
+ const parsed = request(input.range, input.comparison != null);
521
+ if (input.comparison != null) {
522
+ const supplied = parseDateRangeInput2(input.comparison, bounds());
523
+ invariant(supplied.start === parsed.comparison?.start && supplied.end === parsed.comparison?.end, "Comparison must be the preceding equal-length range.");
524
+ }
525
+ return parsed;
526
+ }
527
+ return {
528
+ request,
529
+ parseRequest,
530
+ bounds,
531
+ parse(value) {
532
+ return parseDateRangeInput2(value, bounds());
533
+ },
534
+ period,
535
+ comparison,
536
+ describeInput,
537
+ view: { period, describeInput }
538
+ };
539
+ }
540
+ function parseDateRangeInput2(value, {
541
+ minDate,
542
+ maxDate,
543
+ maxRangeDays
544
+ }) {
545
+ invariant(value && typeof value === "object" && !Array.isArray(value), "Choose a date range.");
546
+ const { start, end } = value;
547
+ function day(input) {
548
+ invariant(typeof input === "string" && /^\d{4}-\d{2}-\d{2}$/.test(input), "Dates must be YYYY-MM-DD.");
549
+ const timestamp = Date.parse(`${input}T00:00:00Z`);
550
+ invariant(Number.isFinite(timestamp) && new Date(timestamp).toISOString().slice(0, 10) === input, "Choose valid calendar dates.");
551
+ return timestamp;
552
+ }
553
+ const first = day(start);
554
+ const last = day(end);
555
+ invariant(Number.isInteger(maxRangeDays) && maxRangeDays > 0, "Maximum range days must be positive.");
556
+ invariant(first <= last && (last - first) / 86400000 < maxRangeDays && (minDate === undefined || first >= day(minDate)) && (maxDate === undefined || last <= day(maxDate)), `Choose up to ${maxRangeDays} days within the available range.`);
557
+ return { start, end };
558
+ }
559
+ function parseCount2(value) {
560
+ const number = typeof value === "number" || typeof value === "string" ? Number(value) : NaN;
561
+ invariant(Number.isSafeInteger(number) && number >= 0 && (typeof value !== "string" || !!value.trim()), "Invalid count in query result.");
562
+ return number;
563
+ }
564
+ function parseLabel2(value, maxLength = 100) {
565
+ invariant(typeof value === "string" && !!value && value.length <= maxLength, "Invalid label in query result.");
566
+ return value;
567
+ }
568
+ function rowsAsRecords2(result, requiredColumns) {
569
+ const names = result.columns.map((column) => column.name);
570
+ invariant(new Set(names).size === names.length && requiredColumns.every((name) => names.includes(name)), "Query result columns do not match the expected shape.");
571
+ return result.rows.map((row) => {
572
+ invariant(row.length === names.length, "Query result row has the wrong width.");
573
+ return Object.fromEntries(names.map((name, index) => [name, row[index]]));
574
+ });
575
+ }
576
+ function defineQueryNames2(names) {
577
+ const values = Object.values(names);
578
+ invariant(values.every((name) => !!name.trim()) && new Set(values).size === values.length, "Query names must be nonempty and unique.");
579
+ return names;
580
+ }
581
+ var sourceErrorBrand = Symbol.for("@altertable/data-app/DataSourceError");
582
+
583
+ class DataSourceError2 extends Error {
584
+ reason;
585
+ status;
586
+ [sourceErrorBrand] = true;
587
+ queryName;
588
+ static [Symbol.hasInstance](value) {
589
+ return value instanceof Error && value[sourceErrorBrand] === true;
590
+ }
591
+ constructor(reason, status) {
592
+ super(`Data source ${reason}${status ? ` (${status})` : ""}.`);
593
+ this.reason = reason;
594
+ this.status = status;
595
+ this.name = "DataSourceError";
596
+ }
597
+ }
598
+ function defineOperation2(operation) {
599
+ invariant(Array.isArray(operation.checks) && operation.checks.length > 0 && Number.isInteger(operation.policy.maxQueryRows) && operation.policy.maxQueryRows > 0 && Number.isInteger(operation.policy.maxDurationMs) && operation.policy.maxDurationMs > 0 && (operation.policy.maxResponseBytes === undefined || Number.isInteger(operation.policy.maxResponseBytes) && operation.policy.maxResponseBytes > 0), "Each data operation needs check inputs and positive row and duration limits.");
600
+ const queries = snapshotQueries(operation.queries);
601
+ const queryNames = Object.fromEntries(Object.keys(queries).map((name) => [name, name]));
602
+ for (const input of operation.checks)
603
+ operation.input(input);
604
+ return {
605
+ ...operation,
606
+ queryNames,
607
+ run(context, input) {
608
+ function query(name, values = {}, options) {
609
+ invariant(Object.hasOwn(queries, name), `Unknown query name: ${name}.`);
610
+ const definition = queries[name];
611
+ const params = resolveQueryParameters(definition.params, values, context.queryParams ?? {});
612
+ return context.lakehouse.queryAll(definition.statement, {
613
+ name,
614
+ params,
615
+ limit: options?.limit ?? operation.policy.maxQueryRows,
616
+ signal: context.signal
617
+ });
618
+ }
619
+ return operation.run({ signal: context.signal, query }, input);
620
+ }
621
+ };
622
+ }
623
+ function connectionCheck2(queries) {
624
+ return defineOperation2({
625
+ queries,
626
+ input: parseEmptyInput2,
627
+ output: parseTrue2,
628
+ checks: [{}],
629
+ policy: { maxQueryRows: 1, maxDurationMs: 15000 },
630
+ async run({ query }) {
631
+ await query("connection");
632
+ return true;
633
+ }
634
+ });
635
+ }
636
+
637
+ export { dimensionMemberKey, dimensionFilter2, parseDimensionSelection2, parseFacetOptions2, dimensionPredicate2, parseDataAppAnnotationDraft2, annotationDraftRoute2, annotationEditorStateRoute2, annotationSendRoute2, annotationUpdateRoute2, annotationModeRoute2, isAnnotationPresentation, numberOperatorLabels, numberFilter2, booleanFilter2, parseNumberSelection2, parseBooleanSelection2, parseEmptyInput2, parseTrue2, previousDateRange2, defineDateRangeContract2, parseDateRangeInput2, parseCount2, parseLabel2, rowsAsRecords2, defineQueryNames2, DataSourceError2, defineOperation2, connectionCheck2 };
638
+
639
+ //# debugId=87015EDE1552300964756E2164756E21