@altertable/data-app 0.61.0 → 0.63.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 (159) hide show
  1. package/AGENTS.md +4 -32
  2. package/CONTRIBUTING.md +63 -72
  3. package/README.md +14 -38
  4. package/dist/chunks/{contract-d9skd1n7.js → contract-8wdybxj7.js} +163 -16
  5. package/dist/chunks/contract-8wdybxj7.js.map +13 -0
  6. package/dist/chunks/{contract-ryyf6dme.js → contract-cxr9t12b.js} +2 -5
  7. package/dist/chunks/contract-cxr9t12b.js.map +10 -0
  8. package/dist/chunks/{contract-14vxdcrs.js → contract-farfe948.js} +17 -21
  9. package/dist/chunks/contract-farfe948.js.map +10 -0
  10. package/dist/chunks/contract-mev09s5v.js.map +2 -2
  11. package/dist/chunks/{contract-ehk50k7e.js → contract-tf8c3qpv.js} +34 -4
  12. package/dist/chunks/contract-tf8c3qpv.js.map +11 -0
  13. package/dist/chunks/{contract-zr4s2g7m.js → contract-tkc552ze.js} +75 -36
  14. package/dist/chunks/contract-tkc552ze.js.map +12 -0
  15. package/dist/chunks/{contract-nt819swq.js → contract-wd8qe3mt.js} +6 -1
  16. package/dist/chunks/{contract-nt819swq.js.map → contract-wd8qe3mt.js.map} +3 -3
  17. package/dist/chunks/contract-wz59z8pq.js.map +1 -1
  18. package/dist/chunks/{contract-7gpsee0v.js → contract-zr3jd7mr.js} +51 -22
  19. package/dist/chunks/contract-zr3jd7mr.js.map +12 -0
  20. package/dist/client/index.js +7 -6
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/core/appearance.js +1 -1
  23. package/dist/core/contract.js +6 -4
  24. package/dist/core/contract.js.map +1 -1
  25. package/dist/embed/index.js +41 -9
  26. package/dist/embed/index.js.map +5 -4
  27. package/dist/local.js +172 -84
  28. package/dist/local.js.map +7 -8
  29. package/dist/react/embed/index.js +83 -99
  30. package/dist/react/embed/index.js.map +4 -5
  31. package/dist/react/index.js +5701 -324
  32. package/dist/react/index.js.map +74 -65
  33. package/dist/server.js +153 -81
  34. package/dist/server.js.map +6 -7
  35. package/dist/types/client/data-client.d.ts +33 -0
  36. package/dist/types/client/iframe.d.ts +14 -0
  37. package/dist/types/client/index.d.ts +4 -34
  38. package/dist/types/client/location.d.ts +4 -6
  39. package/dist/types/client/navigation.d.ts +2 -1
  40. package/dist/types/core/appearance.d.ts +7 -6
  41. package/dist/types/core/bridge.d.ts +2 -8
  42. package/dist/types/core/config.d.ts +1 -1
  43. package/dist/types/core/contract.d.ts +9 -56
  44. package/dist/types/core/format.d.ts +0 -1
  45. package/dist/types/core/messages.d.ts +11 -30
  46. package/dist/types/core/navigation.d.ts +11 -0
  47. package/dist/types/core/operation-types.d.ts +76 -0
  48. package/dist/types/core/operation.d.ts +21 -0
  49. package/dist/types/core/presentation.d.ts +7 -0
  50. package/dist/types/core/variables.d.ts +2 -2
  51. package/dist/types/embed/bridge.d.ts +12 -0
  52. package/dist/types/embed/host.d.ts +14 -5
  53. package/dist/types/embed/index.d.ts +6 -4
  54. package/dist/types/embed/source.d.ts +15 -0
  55. package/dist/types/embed/sql.d.ts +4 -0
  56. package/dist/types/react/content.d.ts +3 -3
  57. package/dist/types/react/embed/bridge.d.ts +19 -10
  58. package/dist/types/react/embed/index.d.ts +0 -2
  59. package/dist/types/react/hooks.d.ts +65 -64
  60. package/dist/types/react/index.d.ts +135 -2
  61. package/dist/types/react/injectStyles.d.ts +7 -0
  62. package/dist/types/react/shellStyles.d.ts +3 -0
  63. package/dist/types/react/styles.d.ts +8 -0
  64. package/dist/types/react/ui/AboutData.d.ts +0 -1
  65. package/dist/types/react/ui/AppFooter.d.ts +0 -1
  66. package/dist/types/react/ui/AppHeader.d.ts +0 -1
  67. package/dist/types/react/ui/AppLayout.d.ts +0 -1
  68. package/dist/types/react/ui/AppScope.d.ts +0 -1
  69. package/dist/types/react/ui/AppToolbar.d.ts +0 -1
  70. package/dist/types/react/ui/Breakdown.d.ts +0 -1
  71. package/dist/types/react/ui/Button.d.ts +0 -1
  72. package/dist/types/react/ui/Checkbox.d.ts +0 -1
  73. package/dist/types/react/ui/Combobox.d.ts +0 -1
  74. package/dist/types/react/ui/ComparisonVisual.d.ts +0 -1
  75. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -5
  76. package/dist/types/react/ui/DataApp.d.ts +2 -2
  77. package/dist/types/react/ui/DataAppSkeleton.d.ts +7 -0
  78. package/dist/types/react/ui/DataBoundary.d.ts +0 -1
  79. package/dist/types/react/ui/DataSection.d.ts +2 -4
  80. package/dist/types/react/ui/DataTable.d.ts +2 -3
  81. package/dist/types/react/ui/DataViewToast.d.ts +0 -1
  82. package/dist/types/react/ui/DataWidget.d.ts +4 -14
  83. package/dist/types/react/ui/DateRangePicker.d.ts +0 -1
  84. package/dist/types/react/ui/DateTimeTooltip.d.ts +0 -1
  85. package/dist/types/react/ui/EmptyState.d.ts +2 -5
  86. package/dist/types/react/ui/GettingStarted.d.ts +0 -1
  87. package/dist/types/react/ui/GlossaryDefinition.d.ts +0 -1
  88. package/dist/types/react/ui/GlossaryExplanation.d.ts +0 -1
  89. package/dist/types/react/ui/GradientScroll.d.ts +0 -1
  90. package/dist/types/react/ui/Grid.d.ts +0 -1
  91. package/dist/types/react/ui/HelpPopover.d.ts +0 -2
  92. package/dist/types/react/ui/Kbd.d.ts +0 -1
  93. package/dist/types/react/ui/LiveControl.d.ts +0 -1
  94. package/dist/types/react/ui/MetricWidget.d.ts +0 -2
  95. package/dist/types/react/ui/PeriodSummary.d.ts +0 -1
  96. package/dist/types/react/ui/PresentStory.d.ts +0 -1
  97. package/dist/types/react/ui/QueryList.d.ts +0 -1
  98. package/dist/types/react/ui/Ranking.d.ts +0 -1
  99. package/dist/types/react/ui/RefreshControl.d.ts +0 -1
  100. package/dist/types/react/ui/RefreshRegion.d.ts +0 -1
  101. package/dist/types/react/ui/RequestHint.d.ts +0 -1
  102. package/dist/types/react/ui/SearchField.d.ts +0 -1
  103. package/dist/types/react/ui/SearchInput.d.ts +1 -2
  104. package/dist/types/react/ui/SearchMatch.d.ts +0 -1
  105. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -1
  106. package/dist/types/react/ui/SelectionMark.d.ts +0 -1
  107. package/dist/types/react/ui/Sheet.d.ts +0 -1
  108. package/dist/types/react/ui/Skeleton.d.ts +0 -1
  109. package/dist/types/react/ui/Stack.d.ts +0 -1
  110. package/dist/types/react/ui/StatusPanel.d.ts +0 -1
  111. package/dist/types/react/ui/TableWidget.d.ts +2 -3
  112. package/dist/types/react/ui/Tabs.d.ts +0 -1
  113. package/dist/types/react/ui/TextContent.d.ts +4 -0
  114. package/dist/types/react/ui/TextWidget.d.ts +19 -0
  115. package/dist/types/react/ui/ThemeSelector.d.ts +0 -1
  116. package/dist/types/react/ui/Tooltip.d.ts +0 -1
  117. package/dist/types/react/ui/UpdatedAt.d.ts +0 -1
  118. package/dist/types/react/ui/VariableBar.d.ts +0 -1
  119. package/dist/types/react/ui/VisualizationWidget.d.ts +5 -13
  120. package/dist/types/react/ui/WidgetDisclosure.d.ts +0 -1
  121. package/dist/types/react/ui/WidgetViewTabs.d.ts +2 -3
  122. package/dist/types/react/ui/data-identifiers.d.ts +0 -1
  123. package/dist/types/react/ui/presentation.d.ts +19 -0
  124. package/dist/types/react/ui/useAppAppearance.d.ts +3 -0
  125. package/dist/types/react/ui/useDataAppPresentation.d.ts +3 -0
  126. package/dist/types/react/view-controls.d.ts +2 -2
  127. package/dist/types/react/view.d.ts +2 -2
  128. package/dist/{bootstrap.js → worker.js} +144 -12
  129. package/docs/app-authoring.md +60 -24
  130. package/docs/client.md +48 -19
  131. package/docs/contract.md +10 -10
  132. package/docs/embed.md +94 -26
  133. package/docs/hosted-apps.md +39 -0
  134. package/docs/local-data-apps.md +14 -0
  135. package/docs/react-embed.md +75 -24
  136. package/docs/react.md +118 -93
  137. package/docs/server-bun.md +4 -1
  138. package/docs/server.md +2 -2
  139. package/docs/worker.md +55 -0
  140. package/examples/starter-data-app/index.tsx +159 -0
  141. package/package.json +18 -12
  142. package/dist/chunks/contract-14vxdcrs.js.map +0 -10
  143. package/dist/chunks/contract-7gpsee0v.js.map +0 -11
  144. package/dist/chunks/contract-d9skd1n7.js.map +0 -12
  145. package/dist/chunks/contract-ehk50k7e.js.map +0 -10
  146. package/dist/chunks/contract-ryyf6dme.js.map +0 -10
  147. package/dist/chunks/contract-zr4s2g7m.js.map +0 -12
  148. package/dist/react/index.css +0 -4478
  149. package/dist/react.css.d.ts +0 -1
  150. package/dist/types/embed/shell.d.ts +0 -21
  151. package/dist/types/embed/standalone.d.ts +0 -1
  152. package/dist/types/react/embed/shell.d.ts +0 -10
  153. package/dist/types/react/ui/index.d.ts +0 -129
  154. package/docs/appearance.md +0 -30
  155. package/docs/bootstrap.md +0 -76
  156. package/docs/config.md +0 -21
  157. package/docs/format.md +0 -29
  158. package/docs/react-styles.md +0 -17
  159. package/docs/starter-agent-instructions.md +0 -53
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/core/appearance.ts
6
6
  var defaults = {
7
- mode: "light",
7
+ theme: "light",
8
8
  baseColor: "neutral",
9
9
  accentColor: "#405d47",
10
10
  darkAccentColor: "#a6c4ad",
@@ -41,7 +41,7 @@ function parseAppearance2(value) {
41
41
  typography: { ...defaults.typography }
42
42
  };
43
43
  invariant(record(value) && Object.keys(value).every((key) => [
44
- "mode",
44
+ "theme",
45
45
  "baseColor",
46
46
  "accentColor",
47
47
  "darkAccentColor",
@@ -52,10 +52,10 @@ function parseAppearance2(value) {
52
52
  "typography"
53
53
  ].includes(key)), "Invalid appearance settings.");
54
54
  const typography = value.typography;
55
- const validFields = (value.mode === undefined || oneOf(value.mode, ["light", "dark", "system"])) && (value.baseColor === undefined || oneOf(value.baseColor, ["neutral", "slate", "warm"])) && (value.accentColor === undefined || color(value.accentColor)) && (value.darkAccentColor === undefined || color(value.darkAccentColor)) && (value.chartColors === undefined || Array.isArray(value.chartColors) && value.chartColors.length >= 1 && value.chartColors.length <= 8 && value.chartColors.every(color)) && (value.density === undefined || oneOf(value.density, ["compact", "comfortable", "spacious"])) && (value.cornerRadius === undefined || oneOf(value.cornerRadius, ["none", "small", "medium", "large"])) && (value.elevation === undefined || oneOf(value.elevation, ["flat", "subtle", "raised"])) && (typography === undefined || record(typography) && Object.keys(typography).every((key) => key === "body" || key === "heading") && (typography.body === undefined || font(typography.body)) && (typography.heading === undefined || font(typography.heading)));
55
+ const validFields = (value.theme === undefined || oneOf(value.theme, ["light", "dark", "system"])) && (value.baseColor === undefined || oneOf(value.baseColor, ["neutral", "slate", "warm"])) && (value.accentColor === undefined || color(value.accentColor)) && (value.darkAccentColor === undefined || color(value.darkAccentColor)) && (value.chartColors === undefined || Array.isArray(value.chartColors) && value.chartColors.length >= 1 && value.chartColors.length <= 8 && value.chartColors.every(color)) && (value.density === undefined || oneOf(value.density, ["compact", "comfortable", "spacious"])) && (value.cornerRadius === undefined || oneOf(value.cornerRadius, ["none", "small", "medium", "large"])) && (value.elevation === undefined || oneOf(value.elevation, ["flat", "subtle", "raised"])) && (typography === undefined || record(typography) && Object.keys(typography).every((key) => key === "body" || key === "heading") && (typography.body === undefined || font(typography.body)) && (typography.heading === undefined || font(typography.heading)));
56
56
  invariant(validFields, "Invalid appearance settings.");
57
57
  return {
58
- mode: value.mode ?? defaults.mode,
58
+ theme: value.theme ?? defaults.theme,
59
59
  baseColor: value.baseColor ?? defaults.baseColor,
60
60
  accentColor: value.accentColor ?? defaults.accentColor,
61
61
  darkAccentColor: value.darkAccentColor ?? (value.accentColor === undefined ? defaults.darkAccentColor : undefined),
@@ -119,7 +119,7 @@ function applyAppearance2(value) {
119
119
  "--at-shadow-surface": settings.elevation === "flat" ? "none" : settings.elevation === "raised" ? "0 8px 28px rgb(20 28 40 / 10%)" : "0 3px 16px rgb(20 28 40 / 5%)"
120
120
  };
121
121
  function applyColors() {
122
- const dark = settings.mode === "dark" || settings.mode === "system" && preference.matches;
122
+ const dark = settings.theme === "dark" || settings.theme === "system" && preference.matches;
123
123
  const [background, surfaceColor, subtle, text, muted, border] = palettes[settings.baseColor][dark ? "dark" : "light"];
124
124
  const accent = dark ? settings.darkAccentColor ?? `color-mix(in srgb, ${settings.accentColor} 50%, white)` : settings.accentColor;
125
125
  settings.chartColors.forEach((chartColor, index) => {
@@ -148,35 +148,31 @@ function applyAppearance2(value) {
148
148
  root.style.setProperty(name, token);
149
149
  }
150
150
  applyColors();
151
- if (settings.mode === "system")
151
+ if (settings.theme === "system")
152
152
  preference.addEventListener("change", applyColors);
153
153
  return () => {
154
154
  preference.removeEventListener("change", applyColors);
155
155
  };
156
156
  }
157
- function createThemeController2(value) {
158
- const settings = parseAppearance2(value);
159
- const storageKey = "altertable.data-app.theme-mode";
160
- let mode = settings.mode;
157
+ function createThemeController2(initialTheme = "light") {
158
+ const storageKey = "altertable.data-app.theme";
159
+ let theme = initialTheme;
161
160
  try {
162
161
  const saved = window.localStorage.getItem(storageKey);
163
162
  if (oneOf(saved, ["light", "dark", "system"]))
164
- mode = saved;
163
+ theme = saved;
165
164
  } catch {}
166
- let stopAppearance = applyAppearance2({ ...settings, mode });
167
165
  const listeners = new Set;
168
166
  return {
169
- getMode() {
170
- return mode;
167
+ getTheme() {
168
+ return theme;
171
169
  },
172
- setMode(nextMode) {
173
- if (nextMode === mode)
170
+ setTheme(nextTheme) {
171
+ if (nextTheme === theme)
174
172
  return;
175
- stopAppearance();
176
- mode = nextMode;
177
- stopAppearance = applyAppearance2({ ...settings, mode });
173
+ theme = nextTheme;
178
174
  try {
179
- window.localStorage.setItem(storageKey, mode);
175
+ window.localStorage.setItem(storageKey, theme);
180
176
  } catch {}
181
177
  listeners.forEach((listener) => listener());
182
178
  },
@@ -189,4 +185,4 @@ function createThemeController2(value) {
189
185
 
190
186
  export { parseAppearance2, applyAppearance2, createThemeController2 };
191
187
 
192
- //# debugId=88E94DEE7384735E64756E2164756E21
188
+ //# debugId=90CD7598768B570C64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/appearance.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * Brand settings; parsing is portable, while theme application requires the browser.\n * @module @altertable/data-app/appearance\n */\nimport { invariant } from '@/src/core/invariant';\n\nexport type Theme = 'light' | 'dark';\nexport type ThemePreference = Theme | 'system';\n\nexport type AppearanceSettings = {\n theme: ThemePreference;\n baseColor: 'neutral' | 'slate' | 'warm';\n accentColor: string;\n darkAccentColor?: string;\n chartColors: string[];\n density: 'compact' | 'comfortable' | 'spacious';\n cornerRadius: 'none' | 'small' | 'medium' | 'large';\n elevation: 'flat' | 'subtle' | 'raised';\n typography: { body: string; heading: string };\n};\n\nexport type ThemeController = {\n getTheme: () => ThemePreference;\n setTheme: (theme: ThemePreference) => void;\n subscribe: (listener: () => void) => () => void;\n};\n\nconst defaults: AppearanceSettings = {\n theme: 'light',\n baseColor: 'neutral',\n accentColor: '#405d47',\n darkAccentColor: '#a6c4ad',\n chartColors: [\n '#285fc0',\n '#a95319',\n '#147862',\n '#7243aa',\n '#aa3958',\n '#475569',\n ],\n density: 'comfortable',\n cornerRadius: 'medium',\n elevation: 'subtle',\n typography: { body: 'system', heading: 'system' },\n};\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction oneOf<T extends string>(\n value: unknown,\n options: readonly T[]\n): value is T {\n return typeof value === 'string' && options.includes(value as T);\n}\n\nfunction color(value: unknown): value is string {\n return typeof value === 'string' && /^#[0-9a-fA-F]{6}$/.test(value);\n}\n\nfunction font(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n value.length <= 120 &&\n !/[;{}()]/.test(value)\n );\n}\n\nexport function parseAppearance(value: unknown): AppearanceSettings {\n if (value === undefined)\n return {\n ...defaults,\n chartColors: [...defaults.chartColors],\n typography: { ...defaults.typography },\n };\n invariant(\n record(value) &&\n Object.keys(value).every(key =>\n [\n 'theme',\n 'baseColor',\n 'accentColor',\n 'darkAccentColor',\n 'chartColors',\n 'density',\n 'cornerRadius',\n 'elevation',\n 'typography',\n ].includes(key)\n ),\n 'Invalid appearance settings.'\n );\n const typography = value.typography;\n const validFields =\n (value.theme === undefined ||\n oneOf(value.theme, ['light', 'dark', 'system'])) &&\n (value.baseColor === undefined ||\n oneOf(value.baseColor, ['neutral', 'slate', 'warm'])) &&\n (value.accentColor === undefined || color(value.accentColor)) &&\n (value.darkAccentColor === undefined || color(value.darkAccentColor)) &&\n (value.chartColors === undefined ||\n (Array.isArray(value.chartColors) &&\n value.chartColors.length >= 1 &&\n value.chartColors.length <= 8 &&\n value.chartColors.every(color))) &&\n (value.density === undefined ||\n oneOf(value.density, ['compact', 'comfortable', 'spacious'])) &&\n (value.cornerRadius === undefined ||\n oneOf(value.cornerRadius, ['none', 'small', 'medium', 'large'])) &&\n (value.elevation === undefined ||\n oneOf(value.elevation, ['flat', 'subtle', 'raised'])) &&\n (typography === undefined ||\n (record(typography) &&\n Object.keys(typography).every(\n key => key === 'body' || key === 'heading'\n ) &&\n (typography.body === undefined || font(typography.body)) &&\n (typography.heading === undefined || font(typography.heading))));\n invariant(validFields, 'Invalid appearance settings.');\n\n return {\n theme: (value.theme ?? defaults.theme) as AppearanceSettings['theme'],\n baseColor: (value.baseColor ??\n defaults.baseColor) as AppearanceSettings['baseColor'],\n accentColor: (value.accentColor ?? defaults.accentColor) as string,\n darkAccentColor: (value.darkAccentColor ??\n (value.accentColor === undefined\n ? defaults.darkAccentColor\n : undefined)) as string | undefined,\n chartColors: (value.chartColors ?? defaults.chartColors) as string[],\n density: (value.density ??\n defaults.density) as AppearanceSettings['density'],\n cornerRadius: (value.cornerRadius ??\n defaults.cornerRadius) as AppearanceSettings['cornerRadius'],\n elevation: (value.elevation ??\n defaults.elevation) as AppearanceSettings['elevation'],\n typography: {\n body:\n record(typography) && typeof typography.body === 'string'\n ? typography.body\n : defaults.typography.body,\n heading:\n record(typography) && typeof typography.heading === 'string'\n ? typography.heading\n : defaults.typography.heading,\n },\n };\n}\n\nconst palettes = {\n neutral: {\n light: ['#ffffff', '#ffffff', '#f6f7f7', '#202124', '#687078', '#e3e6e8'],\n dark: ['#151719', '#202326', '#292d31', '#f2f3f4', '#aeb5bc', '#3b4248'],\n },\n slate: {\n light: ['#f8fafc', '#ffffff', '#f1f5f9', '#17212f', '#607083', '#dce3ea'],\n dark: ['#111820', '#1b2530', '#263340', '#f0f4f8', '#a8b5c3', '#3a4857'],\n },\n warm: {\n light: ['#fbfaf8', '#ffffff', '#f5f2ed', '#292723', '#736e66', '#e8e2da'],\n dark: ['#1b1916', '#25221e', '#302c27', '#f5f1eb', '#bcb3a8', '#494239'],\n },\n} as const;\n\nconst spaces = {\n compact: ['4px', '8px', '12px', '18px', '24px'],\n comfortable: ['5px', '10px', '16px', '24px', '32px'],\n spacious: ['6px', '12px', '20px', '30px', '40px'],\n} as const;\n\nconst radii = {\n none: ['0px', '0px', '0px'],\n small: ['4px', '7px', '9px'],\n medium: ['7px', '12px', '16px'],\n large: ['10px', '18px', '24px'],\n} as const;\n\nfunction fontStack(family: string): string {\n return family === 'system'\n ? \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif\"\n : `${JSON.stringify(family)}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, ui-sans-serif, sans-serif`;\n}\n\n/**\n * Install semantic tokens on the document root, including portaled UI. Returns a cleanup for\n * system-theme listening.\n */\nexport function applyAppearance(value: unknown): () => void {\n const settings = parseAppearance(value);\n const root = document.documentElement;\n const preference = window.matchMedia('(prefers-color-scheme: dark)');\n const [xs, sm, md, lg, xl] = spaces[settings.density];\n const [control, surface, overlay] = radii[settings.cornerRadius];\n const tokens: Record<string, string> = {\n '--at-font': fontStack(settings.typography.body),\n '--at-font-heading': fontStack(settings.typography.heading),\n '--at-space-xs': xs,\n '--at-space-sm': sm,\n '--at-space-md': md,\n '--at-space-lg': lg,\n '--at-space-xl': xl,\n '--at-layout-gap': `clamp(${md}, 2.5vw, ${lg})`,\n '--at-radius-control': control,\n '--at-radius-surface': surface,\n '--at-radius-overlay': overlay,\n '--at-shadow-overlay':\n settings.elevation === 'flat'\n ? 'none'\n : settings.elevation === 'raised'\n ? '0 24px 72px rgb(0 0 0 / 24%)'\n : '0 18px 50px rgb(0 0 0 / 16%)',\n '--at-shadow-surface':\n settings.elevation === 'flat'\n ? 'none'\n : settings.elevation === 'raised'\n ? '0 8px 28px rgb(20 28 40 / 10%)'\n : '0 3px 16px rgb(20 28 40 / 5%)',\n };\n\n function applyColors(): void {\n const dark =\n settings.theme === 'dark' ||\n (settings.theme === 'system' && preference.matches);\n const [background, surfaceColor, subtle, text, muted, border] =\n palettes[settings.baseColor][dark ? 'dark' : 'light'];\n const accent = dark\n ? (settings.darkAccentColor ??\n `color-mix(in srgb, ${settings.accentColor} 50%, white)`)\n : settings.accentColor;\n settings.chartColors.forEach((chartColor, index) => {\n tokens[`--at-chart-${index + 1}`] = dark\n ? `color-mix(in srgb, ${chartColor} 60%, white)`\n : chartColor;\n });\n Object.assign(tokens, {\n '--at-background': background,\n '--at-surface': surfaceColor,\n '--at-subtle': subtle,\n '--at-text': text,\n '--at-muted': muted,\n '--at-border': border,\n '--at-accent': accent,\n '--at-focus-color': muted,\n '--at-control-hover-border': `color-mix(in srgb, ${muted} 45%, ${border})`,\n '--at-accent-hover': `color-mix(in srgb, ${accent} 80%, ${dark ? 'white' : 'black'})`,\n '--at-accent-subtle': `color-mix(in srgb, ${accent} ${dark ? 22 : 12}%, ${surfaceColor})`,\n '--at-on-accent': dark ? background : '#ffffff',\n '--at-danger': dark ? '#f97066' : '#b42318',\n '--at-backdrop': dark ? 'rgb(0 0 0 / 55%)' : 'rgb(15 23 30 / 22%)',\n '--at-code-surface': dark ? '#242a31' : '#f4f6f9',\n '--at-code-text': dark ? '#e5e9ef' : '#273242',\n });\n root.style.colorScheme = dark ? 'dark' : 'light';\n for (const [name, token] of Object.entries(tokens))\n root.style.setProperty(name, token);\n }\n applyColors();\n if (settings.theme === 'system')\n preference.addEventListener('change', applyColors);\n\n return () => {\n preference.removeEventListener('change', applyColors);\n };\n}\n\n/** Persist a viewer theme preference. Applying appearance belongs to the UI owner. */\nexport function createThemeController(\n initialTheme: ThemePreference = 'light'\n): ThemeController {\n const storageKey = 'altertable.data-app.theme';\n let theme = initialTheme;\n try {\n const saved = window.localStorage.getItem(storageKey);\n if (oneOf(saved, ['light', 'dark', 'system'])) theme = saved;\n } catch {\n // Storage can be unavailable in private browsing or embedded contexts.\n }\n const listeners = new Set<() => void>();\n\n return {\n getTheme() {\n return theme;\n },\n setTheme(nextTheme) {\n if (nextTheme === theme) return;\n theme = nextTheme;\n try {\n window.localStorage.setItem(storageKey, theme);\n } catch {\n // The selection still applies to this page.\n }\n listeners.forEach(listener => listener());\n },\n subscribe(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "90CD7598768B570C64756E2164756E21",
9
+ "names": []
10
+ }
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/core/format.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Date and number formatting with explicit units and missing-value behavior.\n * @module @altertable/data-app/format\n * @see https://github.com/altertable-ai/data-app/blob/main/docs/format.md\n */\ntype CommonOptions = { locale?: string; missing?: string };\n\n/** Compact, unambiguous calendar dates for labels and request status. */\nexport function formatDateRange({\n start,\n end,\n}: {\n start: string;\n end: string;\n}): string {\n const first = new Date(`${start}T00:00:00Z`);\n const last = new Date(`${end}T00:00:00Z`);\n\n function format(date: Date, options: Intl.DateTimeFormatOptions) {\n return new Intl.DateTimeFormat('en-US', {\n ...options,\n timeZone: 'UTC',\n }).format(date);\n }\n\n function formatFullDate(date: Date) {\n return format(date, { month: 'short', day: 'numeric', year: 'numeric' });\n }\n\n if (start === end) return formatFullDate(first);\n if (first.getUTCFullYear() !== last.getUTCFullYear())\n return `${formatFullDate(first)}–${formatFullDate(last)}`;\n const firstMonth = format(first, { month: 'short' });\n const lastMonth = format(last, { month: 'short' });\n const firstDay = first.getUTCDate();\n const lastDay = last.getUTCDate();\n\n return firstMonth === lastMonth\n ? `${firstMonth} ${firstDay}–${lastDay}, ${last.getUTCFullYear()}`\n : `${firstMonth} ${firstDay}–${lastMonth} ${lastDay}, ${last.getUTCFullYear()}`;\n}\n\nexport type MetricFormat =\n | { kind: 'count'; compact?: boolean; locale?: string }\n | { kind: 'ratio'; maximumFractionDigits?: number; locale?: string }\n | { kind: 'currency'; currency: string; locale?: string };\n\nexport function formatMetric(value: number, format: MetricFormat): string {\n if (format.kind === 'count') return formatCount(value, format);\n if (format.kind === 'ratio') return formatPercent(value, format);\n\n return formatNumber(value, {\n style: 'currency',\n currency: format.currency,\n locale: format.locale,\n });\n}\n\n/** Missing and non-finite values use the `missing` label; negative zero renders as zero. */\nexport function formatNumber(\n value: number | null | undefined,\n options: CommonOptions & Intl.NumberFormatOptions = {}\n): string {\n const { locale = 'en-US', missing = '—', ...numberOptions } = options;\n\n return value === null || value === undefined || !Number.isFinite(value)\n ? missing\n : new Intl.NumberFormat(locale, numberOptions).format(\n Object.is(value, -0) ? 0 : value\n );\n}\n\n/** Negative and fractional counts render as missing. Compact notation is opt-in. */\nexport function formatCount(\n value: number | null | undefined,\n options: CommonOptions & { compact?: boolean } = {}\n): string {\n const { compact = false, ...common } = options;\n if (\n value !== null &&\n value !== undefined &&\n (!Number.isInteger(value) || value < 0)\n ) {\n return common.missing ?? '—';\n }\n\n return formatNumber(value, {\n ...common,\n notation: compact ? 'compact' : 'standard',\n maximumFractionDigits: compact ? 1 : 0,\n });\n}\n\nconst pluralRules = new Intl.PluralRules();\n\nexport function pluralize(\n count: number,\n singular: string,\n plural = `${singular}s`\n): string {\n return pluralRules.select(count) === 'one' ? singular : plural;\n}\n\n/** Accepts a ratio (0.116), not a percentage (11.6). */\nexport function formatPercent(\n ratio: number | null | undefined,\n options: CommonOptions & { maximumFractionDigits?: number } = {}\n): string {\n if (ratio === null || ratio === undefined || !Number.isFinite(ratio)) {\n return options.missing ?? '—';\n }\n const digits =\n options.maximumFractionDigits ?? (Math.abs(ratio) < 0.01 ? 2 : 1);\n const percentOptions = {\n locale: options.locale,\n style: 'percent' as const,\n maximumFractionDigits: digits,\n };\n const smallestVisibleRatio = 10 ** -digits / 100;\n if (ratio !== 0 && Math.round(Math.abs(ratio) / smallestVisibleRatio) === 0) {\n const boundary = formatNumber(smallestVisibleRatio, percentOptions);\n\n return ratio > 0 ? `<${boundary}` : `>−${boundary}`;\n }\n\n return formatNumber(ratio, percentOptions);\n}\n"
5
+ "/**\n * Date and number formatting with explicit units and missing-value behavior.\n * @module @altertable/data-app/format\n */\ntype CommonOptions = { locale?: string; missing?: string };\n\n/** Compact, unambiguous calendar dates for labels and request status. */\nexport function formatDateRange({\n start,\n end,\n}: {\n start: string;\n end: string;\n}): string {\n const first = new Date(`${start}T00:00:00Z`);\n const last = new Date(`${end}T00:00:00Z`);\n\n function format(date: Date, options: Intl.DateTimeFormatOptions) {\n return new Intl.DateTimeFormat('en-US', {\n ...options,\n timeZone: 'UTC',\n }).format(date);\n }\n\n function formatFullDate(date: Date) {\n return format(date, { month: 'short', day: 'numeric', year: 'numeric' });\n }\n\n if (start === end) return formatFullDate(first);\n if (first.getUTCFullYear() !== last.getUTCFullYear())\n return `${formatFullDate(first)}–${formatFullDate(last)}`;\n const firstMonth = format(first, { month: 'short' });\n const lastMonth = format(last, { month: 'short' });\n const firstDay = first.getUTCDate();\n const lastDay = last.getUTCDate();\n\n return firstMonth === lastMonth\n ? `${firstMonth} ${firstDay}–${lastDay}, ${last.getUTCFullYear()}`\n : `${firstMonth} ${firstDay}–${lastMonth} ${lastDay}, ${last.getUTCFullYear()}`;\n}\n\nexport type MetricFormat =\n | { kind: 'count'; compact?: boolean; locale?: string }\n | { kind: 'ratio'; maximumFractionDigits?: number; locale?: string }\n | { kind: 'currency'; currency: string; locale?: string };\n\nexport function formatMetric(value: number, format: MetricFormat): string {\n if (format.kind === 'count') return formatCount(value, format);\n if (format.kind === 'ratio') return formatPercent(value, format);\n\n return formatNumber(value, {\n style: 'currency',\n currency: format.currency,\n locale: format.locale,\n });\n}\n\n/** Missing and non-finite values use the `missing` label; negative zero renders as zero. */\nexport function formatNumber(\n value: number | null | undefined,\n options: CommonOptions & Intl.NumberFormatOptions = {}\n): string {\n const { locale = 'en-US', missing = '—', ...numberOptions } = options;\n\n return value === null || value === undefined || !Number.isFinite(value)\n ? missing\n : new Intl.NumberFormat(locale, numberOptions).format(\n Object.is(value, -0) ? 0 : value\n );\n}\n\n/** Negative and fractional counts render as missing. Compact notation is opt-in. */\nexport function formatCount(\n value: number | null | undefined,\n options: CommonOptions & { compact?: boolean } = {}\n): string {\n const { compact = false, ...common } = options;\n if (\n value !== null &&\n value !== undefined &&\n (!Number.isInteger(value) || value < 0)\n ) {\n return common.missing ?? '—';\n }\n\n return formatNumber(value, {\n ...common,\n notation: compact ? 'compact' : 'standard',\n maximumFractionDigits: compact ? 1 : 0,\n });\n}\n\nconst pluralRules = new Intl.PluralRules();\n\nexport function pluralize(\n count: number,\n singular: string,\n plural = `${singular}s`\n): string {\n return pluralRules.select(count) === 'one' ? singular : plural;\n}\n\n/** Accepts a ratio (0.116), not a percentage (11.6). */\nexport function formatPercent(\n ratio: number | null | undefined,\n options: CommonOptions & { maximumFractionDigits?: number } = {}\n): string {\n if (ratio === null || ratio === undefined || !Number.isFinite(ratio)) {\n return options.missing ?? '—';\n }\n const digits =\n options.maximumFractionDigits ?? (Math.abs(ratio) < 0.01 ? 2 : 1);\n const percentOptions = {\n locale: options.locale,\n style: 'percent' as const,\n maximumFractionDigits: digits,\n };\n const smallestVisibleRatio = 10 ** -digits / 100;\n if (ratio !== 0 && Math.round(Math.abs(ratio) / smallestVisibleRatio) === 0) {\n const boundary = formatNumber(smallestVisibleRatio, percentOptions);\n\n return ratio > 0 ? `<${boundary}` : `>−${boundary}`;\n }\n\n return formatNumber(ratio, percentOptions);\n}\n"
6
6
  ],
7
- "mappings": "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",
7
+ "mappings": "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",
8
8
  "debugId": "01AB707F92225BAC64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,3 +1,11 @@
1
+ // src/core/navigation.ts
2
+ function validLocation(value) {
3
+ if (!value || typeof value !== "object")
4
+ return false;
5
+ const message = value;
6
+ return typeof message.search === "string" && message.search.length <= 16384 && (message.search === "" || message.search.startsWith("?")) && typeof message.hash === "string" && message.hash.length <= 4096 && (message.hash === "" || message.hash.startsWith("#"));
7
+ }
8
+
1
9
  // src/core/messages.ts
2
10
  var errorBrand = Symbol.for("@altertable/data-app/MessageRoutingError");
3
11
 
@@ -81,7 +89,7 @@ function defineDataQueryRoute2(operations) {
81
89
  if (!value || typeof value !== "object")
82
90
  throw new Error("Invalid data response.");
83
91
  const response = value;
84
- if (!Number.isInteger(response.status) || response.status < 100 || response.status > 599 || !response.body || typeof response.body !== "object")
92
+ if (!Number.isInteger(response.status) || response.status < 100 || response.status > 599 || !response.body || typeof response.body !== "object" || Array.isArray(response.body))
85
93
  throw new Error("Invalid data response.");
86
94
  const body = response.body;
87
95
  if (response.status < 200 || response.status >= 300) {
@@ -108,7 +116,7 @@ var navigationUpdateRoute2 = /* @__PURE__ */ defineMessageRoute2({
108
116
  if (!value || typeof value !== "object")
109
117
  throw new Error("Invalid navigation.");
110
118
  const input = value;
111
- if (typeof input.search !== "string" || input.search.length > 16384 || input.search !== "" && !input.search.startsWith("?") || typeof input.hash !== "string" || input.hash.length > 4096 || input.hash !== "" && !input.hash.startsWith("#") || input.mode !== "push" && input.mode !== "replace" || input.title !== undefined && (typeof input.title !== "string" || input.title.length > 512))
119
+ if (!validLocation(input) || input.mode !== "push" && input.mode !== "replace" || input.title !== undefined && (typeof input.title !== "string" || input.title.length > 512))
112
120
  throw new Error("Invalid navigation.");
113
121
  return {
114
122
  search: input.search,
@@ -123,11 +131,33 @@ var navigationUpdateRoute2 = /* @__PURE__ */ defineMessageRoute2({
123
131
  return null;
124
132
  }
125
133
  });
134
+ var sqlQueryRoute2 = defineMessageRoute2({
135
+ input(value) {
136
+ if (!value || typeof value !== "object")
137
+ throw new Error("Invalid SQL query.");
138
+ const query = value;
139
+ if (typeof query.statement !== "string" || !query.statement.trim() || typeof query.limit !== "number" || !Number.isSafeInteger(query.limit) || query.limit < 1)
140
+ throw new Error("Invalid SQL query.");
141
+ return { statement: query.statement, limit: query.limit };
142
+ },
143
+ output(value, input) {
144
+ if (!value || typeof value !== "object")
145
+ throw new Error("Invalid query result.");
146
+ const result = value;
147
+ if (!Array.isArray(result.columns) || !result.columns.every((column) => column && typeof column.name === "string" && (column.type === undefined || typeof column.type === "string")) || !Array.isArray(result.rows) || result.rows.length > input.limit || !result.rows.every((row) => Array.isArray(row) && row.length === result.columns.length) || result.queryId !== undefined && typeof result.queryId !== "string")
148
+ throw new Error("Invalid query result.");
149
+ return {
150
+ columns: result.columns,
151
+ rows: result.rows,
152
+ ...result.queryId === undefined ? {} : { queryId: result.queryId }
153
+ };
154
+ }
155
+ });
126
156
  var dataAppRoutes2 = {
127
157
  "data:query": /* @__PURE__ */ defineDataQueryRoute2(),
128
158
  "navigation:update": navigationUpdateRoute2
129
159
  };
130
160
 
131
- export { MessageRoutingError2, defineMessageRoute2, createMessageRouter2, defineDataQueryRoute2, navigationUpdateRoute2, dataAppRoutes2 };
161
+ export { validLocation, MessageRoutingError2, defineMessageRoute2, createMessageRouter2, defineDataQueryRoute2, navigationUpdateRoute2, sqlQueryRoute2, dataAppRoutes2 };
132
162
 
133
- //# debugId=294B82BAC69F23DB64756E2164756E21
163
+ //# debugId=7AD0B2977AA9174364756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/core/navigation.ts", "../src/core/messages.ts"],
4
+ "sourcesContent": [
5
+ "/** App-owned URL state shared by controls, frame adapters, and host navigation. */\nexport type AppLocation = { search: string; hash: string };\nexport type HistoryMode = 'push' | 'replace';\nexport type NavigationUpdate = AppLocation & {\n mode: HistoryMode;\n title?: string;\n};\n\nexport function validLocation(value: unknown): value is AppLocation {\n if (!value || typeof value !== 'object') return false;\n const message = value as { search?: unknown; hash?: unknown };\n return (\n typeof message.search === 'string' &&\n message.search.length <= 16_384 &&\n (message.search === '' || message.search.startsWith('?')) &&\n typeof message.hash === 'string' &&\n message.hash.length <= 4096 &&\n (message.hash === '' || message.hash.startsWith('#'))\n );\n}\n",
6
+ "import type {\n TransportResponse,\n QueryResult,\n OperationContracts,\n DataQueryInput,\n DataQueryBody,\n OutputOf,\n} from '@/src/core/operation-types';\nexport type {\n OperationContracts,\n DataQueryInput,\n DataQueryBody,\n} from '@/src/core/operation-types';\nimport { validLocation, type NavigationUpdate } from '@/src/core/navigation';\nexport type { NavigationUpdate } from '@/src/core/navigation';\n\nexport type MessageContext = { signal: AbortSignal };\nexport type RoutedMessage = { route: string; payload: unknown };\nexport type MessageDispatcher = (\n message: unknown,\n context: MessageContext\n) => Promise<unknown>;\nexport type MessageTransport = (\n message: RoutedMessage,\n signal?: AbortSignal\n) => Promise<unknown>;\nexport type MessageRoute<Input, Output> = {\n input: (value: unknown) => Input;\n output: (value: unknown, input: Input) => Output;\n};\nexport type MessageRoutes = Record<\n string,\n {\n input: (value: unknown) => unknown;\n output: (value: unknown, input: never) => unknown;\n }\n>;\nexport type MessageInput<Route extends MessageRoutes[string]> = ReturnType<\n Route['input']\n>;\nexport type MessageHandlers<Routes extends MessageRoutes> = {\n [Name in keyof Routes]: (\n payload: MessageInput<Routes[Name]>,\n context: MessageContext\n ) =>\n | ReturnType<Routes[Name]['output']>\n | Promise<ReturnType<Routes[Name]['output']>>;\n};\n\n// A bootstrap and its app may bundle independent copies in the same window.\nconst errorBrand = Symbol.for('@altertable/data-app/MessageRoutingError');\n\n/** Only these deliberate, public failures cross the message boundary. Other errors stay private. */\nexport class MessageRoutingError extends Error {\n readonly [errorBrand] = true;\n\n static [Symbol.hasInstance](value: unknown): boolean {\n return (\n value instanceof Error &&\n (value as { [errorBrand]?: unknown })[errorBrand] === true\n );\n }\n\n constructor(\n public readonly code: string,\n message: string,\n public readonly requestId?: string\n ) {\n super(message);\n this.name = 'MessageRoutingError';\n }\n}\n\nexport function defineMessageRoute<Input, Output>(\n route: MessageRoute<Input, Output>\n): MessageRoute<Input, Output> {\n return route;\n}\n\n/** Contracts are shared; handlers are supplied by the host and never belong in the app bundle. */\nexport function createMessageRouter<const Routes extends MessageRoutes>(\n routes: Routes,\n handlers: MessageHandlers<NoInfer<Routes>>\n) {\n const registered = new Map(\n Object.entries(routes).map(([name, contract]) => {\n const handler = handlers[name];\n if (!name || name.length > 256 || typeof handler !== 'function')\n throw new Error(\n 'Each message route requires a handler and a bounded name.'\n );\n\n return [name, { contract, handler }] as const;\n })\n );\n\n async function dispatch(\n value: unknown,\n context: MessageContext\n ): Promise<unknown> {\n if (\n !value ||\n typeof value !== 'object' ||\n typeof (value as RoutedMessage).route !== 'string'\n )\n throw new MessageRoutingError(\n 'invalid_message',\n 'Invalid routed message.'\n );\n const message = value as RoutedMessage;\n const entry = registered.get(message.route);\n if (!entry)\n throw new MessageRoutingError('unknown_route', 'Unknown message route.');\n let payload: unknown;\n try {\n payload = entry.contract.input(message.payload);\n } catch {\n throw new MessageRoutingError(\n 'invalid_payload',\n 'Invalid message payload.'\n );\n }\n context.signal.throwIfAborted();\n let result: unknown;\n try {\n result = await entry.handler(payload as never, context);\n } catch (error) {\n if (context.signal.aborted) throw context.signal.reason;\n if (error instanceof MessageRoutingError) throw error;\n throw new MessageRoutingError(\n 'request_failed',\n 'The message request failed. Retry the request.'\n );\n }\n context.signal.throwIfAborted();\n try {\n return entry.contract.output(result, payload as never);\n } catch (error) {\n if (error instanceof MessageRoutingError) throw error;\n throw new MessageRoutingError(\n 'invalid_response',\n 'Invalid message response.'\n );\n }\n }\n\n return { dispatch };\n}\n\nexport type DataQueryRoute<Operations extends OperationContracts> =\n MessageRoute<DataQueryInput<Operations>, TransportResponse> & {\n operations: Operations | undefined;\n };\nexport type MessageOutput<Route extends MessageRoutes[string], Input> =\n Route extends DataQueryRoute<infer Operations>\n ? Input extends { operation: infer Name extends keyof Operations }\n ? TransportResponse<DataQueryBody<OutputOf<Operations[Name]>>>\n : never\n : ReturnType<Route['output']>;\n\n/** Omit contracts only in generic hosts such as the CLI shell, where Bun validates operations. */\nexport function defineDataQueryRoute<\n Operations extends OperationContracts = OperationContracts,\n>(operations?: Operations): DataQueryRoute<Operations> {\n return {\n operations,\n input(value) {\n if (!value || typeof value !== 'object')\n throw new Error('Invalid query.');\n const request = value as { operation?: unknown; input?: unknown };\n if (\n typeof request.operation !== 'string' ||\n !request.operation ||\n request.operation.length > 256 ||\n request.operation.includes('/')\n )\n throw new Error('Invalid operation.');\n const operation =\n operations && Object.hasOwn(operations, request.operation)\n ? operations[request.operation]\n : undefined;\n if (operations && !operation) throw new Error('Unknown operation.');\n\n return {\n operation: request.operation,\n input: operation ? operation.input(request.input) : request.input,\n } as DataQueryInput<Operations>;\n },\n output(value, input) {\n if (!value || typeof value !== 'object')\n throw new Error('Invalid data response.');\n const response = value as TransportResponse;\n if (\n !Number.isInteger(response.status) ||\n response.status < 100 ||\n response.status > 599 ||\n !response.body ||\n typeof response.body !== 'object' ||\n Array.isArray(response.body)\n )\n throw new Error('Invalid data response.');\n const body = response.body as DataQueryBody<unknown> & {\n error?: { code?: unknown; message?: unknown; requestId?: unknown };\n };\n if (response.status < 200 || response.status >= 300) {\n const failure = body.error;\n throw new MessageRoutingError(\n typeof failure?.code === 'string' ? failure.code : 'request_failed',\n typeof failure?.message === 'string'\n ? failure.message\n : 'Could not load data.',\n typeof failure?.requestId === 'string' ? failure.requestId : undefined\n );\n }\n if (\n !('data' in body) ||\n typeof body.requestId !== 'string' ||\n typeof body.queriedAt !== 'string' ||\n !Array.isArray(body.queryIds) ||\n !body.queryIds.every(id => typeof id === 'string')\n )\n throw new Error('Invalid data envelope.');\n if (\n body.queries !== undefined &&\n (!Array.isArray(body.queries) ||\n !body.queries.every(\n query =>\n query &&\n typeof query === 'object' &&\n typeof query.name === 'string' &&\n typeof query.statement === 'string' &&\n (query.queryId === undefined || typeof query.queryId === 'string')\n ))\n )\n throw new Error('Invalid query evidence.');\n const operation = operations?.[input.operation];\n\n return {\n status: response.status,\n body: {\n ...body,\n data: operation ? operation.output(body.data) : body.data,\n },\n };\n },\n };\n}\n\nexport const navigationUpdateRoute = /* @__PURE__ */ defineMessageRoute({\n input(value: unknown): NavigationUpdate {\n if (!value || typeof value !== 'object')\n throw new Error('Invalid navigation.');\n const input = value as NavigationUpdate;\n if (\n !validLocation(input) ||\n (input.mode !== 'push' && input.mode !== 'replace') ||\n (input.title !== undefined &&\n (typeof input.title !== 'string' || input.title.length > 512))\n )\n throw new Error('Invalid navigation.');\n\n return {\n search: input.search,\n hash: input.hash,\n mode: input.mode,\n ...(input.title === undefined ? {} : { title: input.title }),\n };\n },\n output(value: unknown): null {\n if (value !== null) throw new Error('Invalid navigation response.');\n\n return null;\n },\n});\nexport type SqlQueryInput = { statement: string; limit: number };\n\n/** SQL delivery for browser-owned operations. Hosts must enforce backend access and resource limits. */\nexport const sqlQueryRoute = defineMessageRoute({\n input(value: unknown): SqlQueryInput {\n if (!value || typeof value !== 'object')\n throw new Error('Invalid SQL query.');\n const query = value as { statement?: unknown; limit?: unknown };\n if (\n typeof query.statement !== 'string' ||\n !query.statement.trim() ||\n typeof query.limit !== 'number' ||\n !Number.isSafeInteger(query.limit) ||\n query.limit < 1\n )\n throw new Error('Invalid SQL query.');\n\n return { statement: query.statement, limit: query.limit };\n },\n output(value: unknown, input: SqlQueryInput): QueryResult {\n if (!value || typeof value !== 'object')\n throw new Error('Invalid query result.');\n const result = value as QueryResult;\n if (\n !Array.isArray(result.columns) ||\n !result.columns.every(\n column =>\n column &&\n typeof column.name === 'string' &&\n (column.type === undefined || typeof column.type === 'string')\n ) ||\n !Array.isArray(result.rows) ||\n result.rows.length > input.limit ||\n !result.rows.every(\n row => Array.isArray(row) && row.length === result.columns.length\n ) ||\n (result.queryId !== undefined && typeof result.queryId !== 'string')\n )\n throw new Error('Invalid query result.');\n\n return {\n columns: result.columns,\n rows: result.rows,\n ...(result.queryId === undefined ? {} : { queryId: result.queryId }),\n };\n },\n});\n\nexport const dataAppRoutes = {\n 'data:query': /* @__PURE__ */ defineDataQueryRoute(),\n 'navigation:update': navigationUpdateRoute,\n};\n"
7
+ ],
8
+ "mappings": ";AAQO,SAAS,aAAa,CAAC,OAAsC;AAAA,EAClE,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,IAAU,OAAO;AAAA,EAChD,MAAM,UAAU;AAAA,EAChB,OACE,OAAO,QAAQ,WAAW,YAC1B,QAAQ,OAAO,UAAU,UACxB,QAAQ,WAAW,MAAM,QAAQ,OAAO,WAAW,GAAG,MACvD,OAAO,QAAQ,SAAS,YACxB,QAAQ,KAAK,UAAU,SACtB,QAAQ,SAAS,MAAM,QAAQ,KAAK,WAAW,GAAG;AAAA;;;ACiCvD,IAAM,aAAa,OAAO,IAAI,0CAA0C;AAAA;AAGjE,MAAM,6BAA4B,MAAM;AAAA,EAW3B;AAAA,EAEA;AAAA,GAZR,cAAc;AAAA,UAEhB,OAAO,YAAY,CAAC,OAAyB;AAAA,IACnD,OACE,iBAAiB,SAChB,MAAqC,gBAAgB;AAAA;AAAA,EAI1D,WAAW,CACO,MAChB,SACgB,WAChB;AAAA,IACA,MAAM,OAAO;AAAA,IAJG;AAAA,IAEA;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AAEO,SAAS,mBAAiC,CAC/C,OAC6B;AAAA,EAC7B,OAAO;AAAA;AAIF,SAAS,oBAAuD,CACrE,QACA,UACA;AAAA,EACA,MAAM,aAAa,IAAI,IACrB,OAAO,QAAQ,MAAM,EAAE,IAAI,EAAE,MAAM,cAAc;AAAA,IAC/C,MAAM,UAAU,SAAS;AAAA,IACzB,IAAI,CAAC,QAAQ,KAAK,SAAS,OAAO,OAAO,YAAY;AAAA,MACnD,MAAM,IAAI,MACR,2DACF;AAAA,IAEF,OAAO,CAAC,MAAM,EAAE,UAAU,QAAQ,CAAC;AAAA,GACpC,CACH;AAAA,EAEA,eAAe,QAAQ,CACrB,OACA,SACkB;AAAA,IAClB,IACE,CAAC,SACD,OAAO,UAAU,YACjB,OAAQ,MAAwB,UAAU;AAAA,MAE1C,MAAM,IAAI,qBACR,mBACA,yBACF;AAAA,IACF,MAAM,UAAU;AAAA,IAChB,MAAM,QAAQ,WAAW,IAAI,QAAQ,KAAK;AAAA,IAC1C,IAAI,CAAC;AAAA,MACH,MAAM,IAAI,qBAAoB,iBAAiB,wBAAwB;AAAA,IACzE,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,UAAU,MAAM,SAAS,MAAM,QAAQ,OAAO;AAAA,MAC9C,MAAM;AAAA,MACN,MAAM,IAAI,qBACR,mBACA,0BACF;AAAA;AAAA,IAEF,QAAQ,OAAO,eAAe;AAAA,IAC9B,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,SAAS,MAAM,MAAM,QAAQ,SAAkB,OAAO;AAAA,MACtD,OAAO,OAAO;AAAA,MACd,IAAI,QAAQ,OAAO;AAAA,QAAS,MAAM,QAAQ,OAAO;AAAA,MACjD,IAAI,iBAAiB;AAAA,QAAqB,MAAM;AAAA,MAChD,MAAM,IAAI,qBACR,kBACA,gDACF;AAAA;AAAA,IAEF,QAAQ,OAAO,eAAe;AAAA,IAC9B,IAAI;AAAA,MACF,OAAO,MAAM,SAAS,OAAO,QAAQ,OAAgB;AAAA,MACrD,OAAO,OAAO;AAAA,MACd,IAAI,iBAAiB;AAAA,QAAqB,MAAM;AAAA,MAChD,MAAM,IAAI,qBACR,oBACA,2BACF;AAAA;AAAA;AAAA,EAIJ,OAAO,EAAE,SAAS;AAAA;AAeb,SAAS,qBAEf,CAAC,YAAqD;AAAA,EACrD,OAAO;AAAA,IACL;AAAA,IACA,KAAK,CAAC,OAAO;AAAA,MACX,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,QAC7B,MAAM,IAAI,MAAM,gBAAgB;AAAA,MAClC,MAAM,UAAU;AAAA,MAChB,IACE,OAAO,QAAQ,cAAc,YAC7B,CAAC,QAAQ,aACT,QAAQ,UAAU,SAAS,OAC3B,QAAQ,UAAU,SAAS,GAAG;AAAA,QAE9B,MAAM,IAAI,MAAM,oBAAoB;AAAA,MACtC,MAAM,YACJ,cAAc,OAAO,OAAO,YAAY,QAAQ,SAAS,IACrD,WAAW,QAAQ,aACnB;AAAA,MACN,IAAI,cAAc,CAAC;AAAA,QAAW,MAAM,IAAI,MAAM,oBAAoB;AAAA,MAElE,OAAO;AAAA,QACL,WAAW,QAAQ;AAAA,QACnB,OAAO,YAAY,UAAU,MAAM,QAAQ,KAAK,IAAI,QAAQ;AAAA,MAC9D;AAAA;AAAA,IAEF,MAAM,CAAC,OAAO,OAAO;AAAA,MACnB,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,QAC7B,MAAM,IAAI,MAAM,wBAAwB;AAAA,MAC1C,MAAM,WAAW;AAAA,MACjB,IACE,CAAC,OAAO,UAAU,SAAS,MAAM,KACjC,SAAS,SAAS,OAClB,SAAS,SAAS,OAClB,CAAC,SAAS,QACV,OAAO,SAAS,SAAS,YACzB,MAAM,QAAQ,SAAS,IAAI;AAAA,QAE3B,MAAM,IAAI,MAAM,wBAAwB;AAAA,MAC1C,MAAM,OAAO,SAAS;AAAA,MAGtB,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;AAAA,QACnD,MAAM,UAAU,KAAK;AAAA,QACrB,MAAM,IAAI,qBACR,OAAO,SAAS,SAAS,WAAW,QAAQ,OAAO,kBACnD,OAAO,SAAS,YAAY,WACxB,QAAQ,UACR,wBACJ,OAAO,SAAS,cAAc,WAAW,QAAQ,YAAY,SAC/D;AAAA,MACF;AAAA,MACA,IACE,EAAE,UAAU,SACZ,OAAO,KAAK,cAAc,YAC1B,OAAO,KAAK,cAAc,YAC1B,CAAC,MAAM,QAAQ,KAAK,QAAQ,KAC5B,CAAC,KAAK,SAAS,MAAM,QAAM,OAAO,OAAO,QAAQ;AAAA,QAEjD,MAAM,IAAI,MAAM,wBAAwB;AAAA,MAC1C,IACE,KAAK,YAAY,cAChB,CAAC,MAAM,QAAQ,KAAK,OAAO,KAC1B,CAAC,KAAK,QAAQ,MACZ,WACE,SACA,OAAO,UAAU,YACjB,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,cAAc,aAC1B,MAAM,YAAY,aAAa,OAAO,MAAM,YAAY,SAC7D;AAAA,QAEF,MAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C,MAAM,YAAY,aAAa,MAAM;AAAA,MAErC,OAAO;AAAA,QACL,QAAQ,SAAS;AAAA,QACjB,MAAM;AAAA,aACD;AAAA,UACH,MAAM,YAAY,UAAU,OAAO,KAAK,IAAI,IAAI,KAAK;AAAA,QACvD;AAAA,MACF;AAAA;AAAA,EAEJ;AAAA;AAGK,IAAM,yCAAwC,oBAAmB;AAAA,EACtE,KAAK,CAAC,OAAkC;AAAA,IACtC,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,MAC7B,MAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC,MAAM,QAAQ;AAAA,IACd,IACE,CAAC,cAAc,KAAK,KACnB,MAAM,SAAS,UAAU,MAAM,SAAS,aACxC,MAAM,UAAU,cACd,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,SAAS;AAAA,MAE3D,MAAM,IAAI,MAAM,qBAAqB;AAAA,IAEvC,OAAO;AAAA,MACL,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,SACR,MAAM,UAAU,YAAY,CAAC,IAAI,EAAE,OAAO,MAAM,MAAM;AAAA,IAC5D;AAAA;AAAA,EAEF,MAAM,CAAC,OAAsB;AAAA,IAC3B,IAAI,UAAU;AAAA,MAAM,MAAM,IAAI,MAAM,8BAA8B;AAAA,IAElE,OAAO;AAAA;AAEX,CAAC;AAIM,IAAM,iBAAgB,oBAAmB;AAAA,EAC9C,KAAK,CAAC,OAA+B;AAAA,IACnC,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,MAC7B,MAAM,IAAI,MAAM,oBAAoB;AAAA,IACtC,MAAM,QAAQ;AAAA,IACd,IACE,OAAO,MAAM,cAAc,YAC3B,CAAC,MAAM,UAAU,KAAK,KACtB,OAAO,MAAM,UAAU,YACvB,CAAC,OAAO,cAAc,MAAM,KAAK,KACjC,MAAM,QAAQ;AAAA,MAEd,MAAM,IAAI,MAAM,oBAAoB;AAAA,IAEtC,OAAO,EAAE,WAAW,MAAM,WAAW,OAAO,MAAM,MAAM;AAAA;AAAA,EAE1D,MAAM,CAAC,OAAgB,OAAmC;AAAA,IACxD,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,MAC7B,MAAM,IAAI,MAAM,uBAAuB;AAAA,IACzC,MAAM,SAAS;AAAA,IACf,IACE,CAAC,MAAM,QAAQ,OAAO,OAAO,KAC7B,CAAC,OAAO,QAAQ,MACd,YACE,UACA,OAAO,OAAO,SAAS,aACtB,OAAO,SAAS,aAAa,OAAO,OAAO,SAAS,SACzD,KACA,CAAC,MAAM,QAAQ,OAAO,IAAI,KAC1B,OAAO,KAAK,SAAS,MAAM,SAC3B,CAAC,OAAO,KAAK,MACX,SAAO,MAAM,QAAQ,GAAG,KAAK,IAAI,WAAW,OAAO,QAAQ,MAC7D,KACC,OAAO,YAAY,aAAa,OAAO,OAAO,YAAY;AAAA,MAE3D,MAAM,IAAI,MAAM,uBAAuB;AAAA,IAEzC,OAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO;AAAA,SACT,OAAO,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,OAAO,QAAQ;AAAA,IACpE;AAAA;AAEJ,CAAC;AAEM,IAAM,iBAAgB;AAAA,EAC3B,8BAA8B,sBAAqB;AAAA,EACnD,qBAAqB;AACvB;",
9
+ "debugId": "7AD0B2977AA9174364756E2164756E21",
10
+ "names": []
11
+ }
@@ -3,15 +3,80 @@ import {
3
3
  createHttpTransport2,
4
4
  createMessageClient2,
5
5
  localFrameBridge,
6
- getDataAppTransport2
7
- } from "./contract-d9skd1n7.js";
6
+ getDataAppTransport2,
7
+ executeDataOperation,
8
+ toDataOperationFailure
9
+ } from "./contract-8wdybxj7.js";
8
10
  import {
9
- navigationUpdateRoute2
10
- } from "./contract-ehk50k7e.js";
11
+ PARENT_PARAM
12
+ } from "./contract-cxr9t12b.js";
11
13
  import {
12
- PARENT_PARAM,
13
- validLocation
14
- } from "./contract-ryyf6dme.js";
14
+ validLocation,
15
+ MessageRoutingError2,
16
+ defineDataQueryRoute2,
17
+ navigationUpdateRoute2
18
+ } from "./contract-tf8c3qpv.js";
19
+
20
+ // src/client/data-client.ts
21
+ var dataQueryRoute = defineDataQueryRoute2();
22
+ function createDataClient2(options = {}) {
23
+ if (options.operations)
24
+ return createOperationClient({
25
+ operations: options.operations,
26
+ lakehouse: options.lakehouse
27
+ });
28
+ const http = createHttpTransport2(options);
29
+ const useHttp = options.endpoint !== undefined || options.fetch !== undefined;
30
+ return {
31
+ async query(name, input, { signal } = {}) {
32
+ const transport = options.transport ?? (useHttp ? http : localFrameBridge()?.transport ?? http);
33
+ const response = await transport(name, input, signal);
34
+ const ok = response.status >= 200 && response.status < 300;
35
+ let body;
36
+ try {
37
+ body = dataQueryRoute.output(response, { operation: name, input }).body;
38
+ } catch (error) {
39
+ if (signal?.aborted)
40
+ throw error;
41
+ if (error instanceof MessageRoutingError2)
42
+ throw new DataAppError2(error.message, error.code, error.requestId);
43
+ throw new DataAppError2(ok ? "The data response was invalid." : "Could not load data.", ok ? "invalid_response" : "request_failed");
44
+ }
45
+ return { ...body, input };
46
+ }
47
+ };
48
+ }
49
+ function createOperationClient(options) {
50
+ return {
51
+ async query(name, input, { signal } = {}) {
52
+ signal?.throwIfAborted();
53
+ const requestId = crypto.randomUUID();
54
+ if (!Object.hasOwn(options.operations, name))
55
+ throw new DataAppError2("Unknown data operation.", "not_found", requestId);
56
+ const lakehouse = options.lakehouse ?? (typeof window === "undefined" ? undefined : getDataAppTransport2()?.lakehouse);
57
+ if (!lakehouse)
58
+ throw new DataAppError2("An authorized SQL bridge must be installed first.", "bridge_unavailable", requestId);
59
+ try {
60
+ const { body } = await executeDataOperation(options.operations[name], input, {
61
+ lakehouse,
62
+ signal: signal ?? new AbortController().signal,
63
+ includeSql: true,
64
+ requestId,
65
+ operationName: name
66
+ });
67
+ return { ...body, input };
68
+ } catch (error) {
69
+ if (signal?.aborted)
70
+ throw signal.reason;
71
+ if (error instanceof DataAppError2)
72
+ throw error;
73
+ const failure = toDataOperationFailure(error);
74
+ throw new DataAppError2(failure.message, failure.code, requestId);
75
+ }
76
+ }
77
+ };
78
+ }
79
+
15
80
  // src/client/location.ts
16
81
  function createAppLocation(frame, virtual, publish) {
17
82
  let state = { search: "", hash: "" };
@@ -110,7 +175,7 @@ function attachNavigation(frame, virtual, connection) {
110
175
  messages.request("navigation:update", update).catch(() => {});
111
176
  }
112
177
  function receiveState(state) {
113
- if (!state || typeof state !== "object" || !validLocation(state))
178
+ if (!validLocation(state))
114
179
  return;
115
180
  pendingUpdate = undefined;
116
181
  apply(state);
@@ -182,32 +247,6 @@ function getDataAppNavigation2(frame = window) {
182
247
  return adapter.navigation;
183
248
  }
184
249
 
185
- // src/client/index.ts
186
- function createDataClient2(options = {}) {
187
- const http = createHttpTransport2(options);
188
- return {
189
- async query(name, input, { signal } = {}) {
190
- const transport = options.transport ?? (options.endpoint !== undefined || options.fetch !== undefined ? http : localFrameBridge()?.transport ?? http);
191
- const response = await transport(name, input, signal);
192
- const ok = response.status >= 200 && response.status < 300;
193
- let body;
194
- try {
195
- const parsed = response.body;
196
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
197
- throw new Error("Invalid response envelope.");
198
- body = parsed;
199
- } catch (error) {
200
- if (signal?.aborted)
201
- throw error;
202
- throw new DataAppError2(ok ? "The data response was invalid." : "Could not load data.", ok ? "invalid_response" : "request_failed");
203
- }
204
- if (!ok)
205
- throw new DataAppError2(body.error?.message ?? "Could not load data.", body.error?.code ?? "request_failed", body.error?.requestId);
206
- return { ...body, input };
207
- }
208
- };
209
- }
210
-
211
- export { createDataAppNavigation2, getDataAppNavigation2, createDataClient2 };
250
+ export { createDataClient2, createDataAppNavigation2, getDataAppNavigation2 };
212
251
 
213
- //# debugId=604C308B1E4C235964756E2164756E21
252
+ //# debugId=B2D2C0A1B4F5737F64756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/client/data-client.ts", "../src/client/location.ts", "../src/client/navigation.ts"],
4
+ "sourcesContent": [
5
+ "import { createHttpTransport, DataAppError } from '@/src/client/transport';\nimport { getDataAppTransport, localFrameBridge } from '@/src/client/iframe';\nimport {\n executeDataOperation,\n toDataOperationFailure,\n} from '@/src/core/operation';\nimport type { DataOperations, Lakehouse } from '@/src/core/contract';\nimport { defineDataQueryRoute, MessageRoutingError } from '@/src/core/messages';\nimport type { DataTransport } from '@/src/client/transport';\nimport type {\n DataQueryBody,\n InputOf,\n OutputOf,\n} from '@/src/core/operation-types';\nexport type { InputOf, OutputOf } from '@/src/core/operation-types';\n\n/**\n * Parsed operation data and query evidence. `queries` is present only when SQL disclosure is\n * allowed.\n */\nexport type DataResponse<Output, Input = unknown> = DataQueryBody<Output> & {\n /** The exact browser input that produced this response. */\n input: Input;\n};\n\nexport type DataClient<Operations extends DataOperations> = {\n query<Name extends keyof Operations & string>(\n name: Name,\n input: InputOf<Operations[Name]>,\n options?: { signal?: AbortSignal }\n ): Promise<\n DataResponse<OutputOf<Operations[Name]>, InputOf<Operations[Name]>>\n >;\n};\n\n/** Browser operations and named-operation delivery are mutually exclusive configurations. */\nexport type DataClientOptions<Operations extends DataOperations> =\n | {\n operations: Operations;\n lakehouse?: Lakehouse;\n transport?: never;\n endpoint?: never;\n fetch?: never;\n }\n | {\n operations?: never;\n lakehouse?: never;\n transport?: DataTransport;\n endpoint?: string;\n fetch?: typeof fetch;\n };\n\nconst dataQueryRoute = defineDataQueryRoute();\n\n/** Named HTTP operations, or browser-owned operations executed through an authorized SQL bridge. */\nexport function createDataClient<Operations extends DataOperations>(\n options: DataClientOptions<Operations> = {}\n): DataClient<Operations> {\n if (options.operations)\n return createOperationClient({\n operations: options.operations,\n lakehouse: options.lakehouse,\n });\n const http = createHttpTransport(options);\n const useHttp = options.endpoint !== undefined || options.fetch !== undefined;\n\n return {\n async query(name, input, { signal } = {}) {\n const transport =\n options.transport ??\n (useHttp ? http : (localFrameBridge()?.transport ?? http));\n const response = await transport(name, input, signal);\n const ok = response.status >= 200 && response.status < 300;\n let body: Omit<DataResponse<OutputOf<Operations[typeof name]>>, 'input'>;\n try {\n body = dataQueryRoute.output(response, { operation: name, input })\n .body as typeof body;\n } catch (error) {\n if (signal?.aborted) throw error;\n if (error instanceof MessageRoutingError)\n throw new DataAppError(error.message, error.code, error.requestId);\n throw new DataAppError(\n ok ? 'The data response was invalid.' : 'Could not load data.',\n ok ? 'invalid_response' : 'request_failed'\n );\n }\n\n return { ...body, input };\n },\n };\n}\n\nfunction createOperationClient<Operations extends DataOperations>(options: {\n operations: Operations;\n lakehouse?: Lakehouse;\n}): DataClient<Operations> {\n return {\n async query(name, input, { signal } = {}) {\n signal?.throwIfAborted();\n const requestId = crypto.randomUUID();\n if (!Object.hasOwn(options.operations, name))\n throw new DataAppError(\n 'Unknown data operation.',\n 'not_found',\n requestId\n );\n const lakehouse =\n options.lakehouse ??\n (typeof window === 'undefined'\n ? undefined\n : getDataAppTransport()?.lakehouse);\n if (!lakehouse)\n throw new DataAppError(\n 'An authorized SQL bridge must be installed first.',\n 'bridge_unavailable',\n requestId\n );\n\n try {\n const { body } = await executeDataOperation(\n options.operations[name]!,\n input,\n {\n lakehouse,\n signal: signal ?? new AbortController().signal,\n includeSql: true,\n requestId,\n operationName: name,\n }\n );\n\n return { ...body, input } as DataResponse<\n OutputOf<Operations[typeof name]>,\n InputOf<Operations[typeof name]>\n >;\n } catch (error) {\n if (signal?.aborted) throw signal.reason;\n if (error instanceof DataAppError) throw error;\n const failure = toDataOperationFailure(error);\n throw new DataAppError(failure.message, failure.code, requestId);\n }\n },\n };\n}\n",
6
+ "import { PARENT_PARAM } from '@/src/core/bridge';\nimport { validLocation } from '@/src/core/navigation';\n\nimport type { AppLocation, HistoryMode } from '@/src/core/navigation';\nexport type { AppLocation } from '@/src/core/navigation';\nexport type DataAppLocation = {\n snapshot(): AppLocation;\n subscribe(listener: () => void): () => void;\n update(location: AppLocation, mode?: HistoryMode): void;\n};\n\n/** Opaque sandboxes keep app state in memory; URL frames retain their real URL for HMR. */\nexport function createAppLocation(\n frame: Window,\n virtual: boolean,\n publish: (location: AppLocation, mode: HistoryMode) => void\n) {\n let state: AppLocation = { search: '', hash: '' };\n let disposed = false;\n const listeners = new Set<() => void>();\n\n function notify() {\n for (const listener of listeners) listener();\n }\n\n function snapshot(): AppLocation {\n if (virtual) return state;\n const url = new URL(frame.location.href);\n url.searchParams.delete(PARENT_PARAM);\n if (state.search !== url.search || state.hash !== url.hash)\n state = { search: url.search, hash: url.hash };\n\n return state;\n }\n\n function apply(next: AppLocation) {\n const previous = snapshot();\n if (previous.search === next.search && previous.hash === next.hash) return;\n if (virtual) state = next;\n else {\n const url = new URL(frame.location.href);\n const parent = url.searchParams.get(PARENT_PARAM);\n url.search = next.search;\n if (parent) url.searchParams.set(PARENT_PARAM, parent);\n url.hash = next.hash;\n frame.history.replaceState(null, '', url);\n frame.dispatchEvent(new Event('popstate'));\n }\n if (virtual) notify();\n }\n\n const location: DataAppLocation = {\n snapshot,\n subscribe(listener) {\n listeners.add(listener);\n if (!virtual) frame.addEventListener('popstate', listener);\n\n return () => {\n listeners.delete(listener);\n frame.removeEventListener('popstate', listener);\n };\n },\n update(next, mode = 'replace') {\n if (disposed) return;\n if (!validLocation(next)) throw new Error('Invalid app location.');\n const before = snapshot();\n if (before.search === next.search && before.hash === next.hash) return;\n apply(next);\n publish(next, mode);\n },\n };\n\n function dispose() {\n disposed = true;\n for (const listener of listeners)\n frame.removeEventListener('popstate', listener);\n listeners.clear();\n }\n\n return { location, apply, dispose };\n}\n",
7
+ "import type { AppLocation, HistoryMode } from '@/src/core/navigation';\nimport { createAppLocation, type DataAppLocation } from '@/src/client/location';\nimport { validLocation } from '@/src/core/navigation';\nimport {\n navigationUpdateRoute,\n type NavigationUpdate,\n} from '@/src/core/messages';\nimport { createMessageClient } from '@/src/client/messages';\nimport {\n getDataAppTransport,\n localFrameBridge,\n type IframeTransport,\n} from '@/src/client/iframe';\n\nexport type DataAppNavigation = DataAppLocation & {\n /** Publish URL changes made outside the adapter, including the document title. */\n publish(mode?: HistoryMode): void;\n dispose(): void;\n};\n\n/** Attach app URL behavior to an already authenticated bridge. */\nexport function createDataAppNavigation({\n bridge,\n window: frame = window,\n}: {\n bridge: IframeTransport;\n window?: Window;\n}): DataAppNavigation {\n return attachNavigation(frame, bridge.mode === 'bundle', bridge).navigation;\n}\n\nfunction attachNavigation(\n frame: Window,\n virtual: boolean,\n connection: IframeTransport | Promise<IframeTransport>\n) {\n let disposed = false;\n let unsubscribe: (() => void) | undefined;\n let send: ((update: NavigationUpdate) => void) | undefined;\n let pendingUpdate: NavigationUpdate | undefined;\n const { location, apply, dispose } = createAppLocation(\n frame,\n virtual,\n publish\n );\n\n function publish(location: AppLocation, mode: HistoryMode) {\n const update = { ...location, mode, title: frame.document.title };\n if (send) send(update);\n else pendingUpdate = update;\n }\n\n function attach(bridge: IframeTransport) {\n if (disposed) return;\n unsubscribe?.();\n const messages = createMessageClient(\n { 'navigation:update': navigationUpdateRoute },\n bridge.request\n );\n\n function sendUpdate(update: NavigationUpdate) {\n void messages.request('navigation:update', update).catch(() => {});\n }\n\n function receiveState(state: unknown) {\n if (!validLocation(state)) return;\n pendingUpdate = undefined;\n apply(state);\n }\n\n send = sendUpdate;\n unsubscribe = bridge.subscribe(receiveState);\n receiveState(bridge.snapshot());\n if (pendingUpdate) {\n sendUpdate(pendingUpdate);\n pendingUpdate = undefined;\n }\n }\n\n if ('then' in connection) void connection.then(attach).catch(() => {});\n else attach(connection);\n\n const navigation: DataAppNavigation = {\n ...location,\n publish(mode = 'replace') {\n if (!disposed) publish(location.snapshot(), mode);\n },\n dispose() {\n disposed = true;\n unsubscribe?.();\n dispose();\n send = undefined;\n pendingUpdate = undefined;\n },\n };\n\n return {\n navigation,\n attach,\n isDisposed() {\n return disposed;\n },\n };\n}\n\nconst navigationKey = Symbol.for('altertable.appNavigation');\ntype NavigationWindow = Window & {\n [navigationKey]?: ReturnType<typeof attachNavigation> & {\n bridge?: IframeTransport;\n };\n};\n\n/** URL controls attach navigation on demand; bootstrap and data-only clients do not. */\nexport function getDataAppNavigation(\n frame: Window = window\n): DataAppNavigation | undefined {\n const target = frame as NavigationWindow;\n const bridge = getDataAppTransport(frame);\n const cached = target[navigationKey]?.isDisposed()\n ? undefined\n : target[navigationKey];\n if (cached) {\n if (bridge && cached.bridge !== bridge) {\n cached.attach(bridge);\n cached.bridge = bridge;\n }\n\n return cached.navigation;\n }\n if (bridge) {\n const adapter = attachNavigation(frame, bridge.mode === 'bundle', bridge);\n target[navigationKey] = { bridge, ...adapter };\n\n return adapter.navigation;\n }\n const preview = localFrameBridge(frame);\n if (!preview?.connect) return undefined;\n const connection = preview.connect();\n const adapter = attachNavigation(frame, false, connection);\n const entry = {\n ...adapter,\n bridge: undefined as IframeTransport | undefined,\n };\n target[navigationKey] = entry;\n void connection\n .then(bridge => {\n entry.bridge = bridge;\n })\n .catch(() => {});\n\n return adapter.navigation;\n}\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "B2D2C0A1B4F5737F64756E2164756E21",
11
+ "names": []
12
+ }
@@ -266,11 +266,16 @@ function defineQueryNames2(names) {
266
266
  invariant(values.every((name) => !!name.trim()) && new Set(values).size === values.length, "Query names must be nonempty and unique.");
267
267
  return names;
268
268
  }
269
+ var sourceErrorBrand = Symbol.for("@altertable/data-app/DataSourceError");
269
270
 
270
271
  class DataSourceError2 extends Error {
271
272
  reason;
272
273
  status;
274
+ [sourceErrorBrand] = true;
273
275
  queryName;
276
+ static [Symbol.hasInstance](value) {
277
+ return value instanceof Error && value[sourceErrorBrand] === true;
278
+ }
274
279
  constructor(reason, status) {
275
280
  super(`Data source ${reason}${status ? ` (${status})` : ""}.`);
276
281
  this.reason = reason;
@@ -318,4 +323,4 @@ function connectionCheck2() {
318
323
 
319
324
  export { dimensionMemberKey, dimensionFilter2, parseDimensionSelection2, parseFacetOptions2, dimensionPredicate2, parseEmptyInput2, parseTrue2, previousDateRange2, defineDateRangeContract2, parseDateRangeInput2, parseCount2, parseLabel2, rowsAsRecords2, defineQueryNames2, DataSourceError2, defineOperation2, connectionQueryNames2, connectionCheck2 };
320
325
 
321
- //# debugId=65216D55145AC13D64756E2164756E21
326
+ //# debugId=C0344E56C3A0436364756E2164756E21