@altertable/data-app 0.64.0 → 0.66.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 (157) hide show
  1. package/AGENTS.md +40 -0
  2. package/CONTRIBUTING.md +37 -3
  3. package/README.md +2 -2
  4. package/dist/chunks/{contract-mwe7gnmh.js → contract-13j5zb3c.js} +110 -216
  5. package/dist/chunks/contract-13j5zb3c.js.map +13 -0
  6. package/dist/chunks/contract-19ckn3n7.js +78 -0
  7. package/dist/chunks/contract-19ckn3n7.js.map +10 -0
  8. package/dist/chunks/contract-awa2d5b1.js +272 -0
  9. package/dist/chunks/contract-awa2d5b1.js.map +12 -0
  10. package/dist/chunks/{contract-xtxza1fy.js → contract-e11m4y7f.js} +35 -71
  11. package/dist/chunks/contract-e11m4y7f.js.map +12 -0
  12. package/dist/chunks/{contract-zr3jd7mr.js → contract-h8a6f559.js} +100 -75
  13. package/dist/chunks/contract-h8a6f559.js.map +12 -0
  14. package/dist/chunks/contract-havnjmdr.js +13011 -0
  15. package/dist/chunks/contract-havnjmdr.js.map +99 -0
  16. package/dist/chunks/{contract-txjy0en2.js → contract-m6n8ctfc.js} +64 -72
  17. package/dist/chunks/contract-m6n8ctfc.js.map +10 -0
  18. package/dist/chunks/contract-pk603qj7.js +146 -0
  19. package/dist/chunks/contract-pk603qj7.js.map +10 -0
  20. package/dist/chunks/{contract-yxbjea23.js → contract-rfbakka4.js} +179 -2
  21. package/dist/chunks/contract-rfbakka4.js.map +13 -0
  22. package/dist/client/index.js +11 -6
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/core/appearance.js +1 -1
  25. package/dist/core/contract.js +13 -3
  26. package/dist/core/contract.js.map +1 -1
  27. package/dist/embed/index.js +25 -13
  28. package/dist/embed/index.js.map +3 -3
  29. package/dist/local.js +1 -1
  30. package/dist/local.js.map +2 -2
  31. package/dist/react/embed/index.js +5 -2
  32. package/dist/react/embed/index.js.map +3 -3
  33. package/dist/react/index.js +2532 -11273
  34. package/dist/react/index.js.map +16 -88
  35. package/dist/react/ui/index.js +1614 -0
  36. package/dist/react/ui/index.js.map +22 -0
  37. package/dist/server.js +1 -1
  38. package/dist/server.js.map +2 -2
  39. package/dist/types/client/annotations.d.ts +23 -0
  40. package/dist/types/client/iframe.d.ts +2 -0
  41. package/dist/types/client/index.d.ts +2 -0
  42. package/dist/types/client/logger.d.ts +6 -0
  43. package/dist/types/core/annotations.d.ts +92 -0
  44. package/dist/types/core/appearance.d.ts +2 -2
  45. package/dist/types/core/bridge-endpoint.d.ts +21 -0
  46. package/dist/types/core/bridge.d.ts +137 -16
  47. package/dist/types/core/contract.d.ts +2 -0
  48. package/dist/types/core/logger.d.ts +12 -0
  49. package/dist/types/core/presentation.d.ts +2 -0
  50. package/dist/types/core/variables.d.ts +2 -5
  51. package/dist/types/embed/host.d.ts +6 -2
  52. package/dist/types/embed/index.d.ts +1 -0
  53. package/dist/types/embed/source.d.ts +1 -1
  54. package/dist/types/react/annotations/AnnotationBar.d.ts +24 -0
  55. package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
  56. package/dist/types/react/annotations/AnnotationEditor.d.ts +15 -0
  57. package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
  58. package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
  59. package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
  60. package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
  61. package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
  62. package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
  63. package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
  64. package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
  65. package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
  66. package/dist/types/react/annotations/styles.d.ts +3 -0
  67. package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
  68. package/dist/types/react/annotations/useDataAppAnnotations.d.ts +19 -0
  69. package/dist/types/react/bindings.d.ts +110 -0
  70. package/dist/types/react/content.d.ts +24 -11
  71. package/dist/types/react/embed/bridge.d.ts +1 -1
  72. package/dist/types/react/hooks.d.ts +29 -787
  73. package/dist/types/react/index.d.ts +30 -104
  74. package/dist/types/react/source-owner.d.ts +2 -0
  75. package/dist/types/react/style-contract.d.ts +60 -0
  76. package/dist/types/react/style-validation.d.ts +2 -0
  77. package/dist/types/react/ui/AboutData.d.ts +9 -1
  78. package/dist/types/react/ui/AppHeader.d.ts +1 -2
  79. package/dist/types/react/ui/AppLayout.d.ts +3 -9
  80. package/dist/types/react/ui/AppToolbar.d.ts +6 -7
  81. package/dist/types/react/ui/AreaChart.d.ts +7 -0
  82. package/dist/types/react/ui/BarChart.d.ts +6 -0
  83. package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
  84. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
  85. package/dist/types/react/ui/DataApp.d.ts +17 -53
  86. package/dist/types/react/ui/DataAppFrame.d.ts +42 -0
  87. package/dist/types/react/ui/DataBoundary.d.ts +4 -5
  88. package/dist/types/react/ui/DataSection.d.ts +6 -18
  89. package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
  90. package/dist/types/react/ui/DataValue.d.ts +9 -0
  91. package/dist/types/react/ui/DataWidget.d.ts +2 -1
  92. package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
  93. package/dist/types/react/ui/ExportControl.d.ts +4 -0
  94. package/dist/types/react/ui/HelpPopover.d.ts +3 -4
  95. package/dist/types/react/ui/InspectionContext.d.ts +2 -0
  96. package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
  97. package/dist/types/react/ui/LineChart.d.ts +7 -0
  98. package/dist/types/react/ui/MetricWidget.d.ts +4 -3
  99. package/dist/types/react/ui/PieChart.d.ts +8 -0
  100. package/dist/types/react/ui/PresentStory.d.ts +2 -5
  101. package/dist/types/react/ui/RefreshControl.d.ts +1 -2
  102. package/dist/types/react/ui/ScatterChart.d.ts +17 -0
  103. package/dist/types/react/ui/SearchField.d.ts +1 -2
  104. package/dist/types/react/ui/SearchInput.d.ts +3 -1
  105. package/dist/types/react/ui/Sheet.d.ts +2 -1
  106. package/dist/types/react/ui/Skeleton.d.ts +5 -2
  107. package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
  108. package/dist/types/react/ui/TableWidget.d.ts +19 -16
  109. package/dist/types/react/ui/Tabs.d.ts +6 -2
  110. package/dist/types/react/ui/TextWidget.d.ts +2 -1
  111. package/dist/types/react/ui/Toast.d.ts +8 -0
  112. package/dist/types/react/ui/Tooltip.d.ts +4 -3
  113. package/dist/types/react/ui/TrendChart.d.ts +5 -0
  114. package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
  115. package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
  116. package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
  117. package/dist/types/react/ui/chart-data.d.ts +25 -0
  118. package/dist/types/react/ui/csv-export.d.ts +19 -0
  119. package/dist/types/react/ui/data-context.d.ts +18 -7
  120. package/dist/types/react/ui/icons.d.ts +2 -0
  121. package/dist/types/react/ui/index.d.ts +74 -0
  122. package/dist/types/react/ui/metric.d.ts +1 -1
  123. package/dist/types/react/ui/presentation.d.ts +5 -1
  124. package/dist/types/react/ui/shortcuts.d.ts +7 -1
  125. package/dist/types/react/view-runtime.d.ts +29 -0
  126. package/dist/types/react/view.d.ts +21 -4
  127. package/dist/types/react/widgets.d.ts +79 -0
  128. package/dist/worker.js +381 -79
  129. package/docs/app-authoring.md +64 -30
  130. package/docs/data-context.md +89 -0
  131. package/docs/embed.md +1 -1
  132. package/docs/formatting-and-appearance.md +52 -0
  133. package/docs/hosted-apps.md +2 -15
  134. package/docs/layout.md +33 -0
  135. package/docs/local-data-apps.md +1 -1
  136. package/docs/react-embed.md +2 -2
  137. package/docs/react.md +20 -254
  138. package/docs/stories-and-export.md +52 -0
  139. package/docs/styling.md +55 -0
  140. package/docs/ui-quality.md +20 -0
  141. package/docs/ui.md +41 -0
  142. package/docs/variables.md +98 -0
  143. package/docs/views.md +127 -0
  144. package/docs/widgets.md +142 -0
  145. package/examples/starter-data-app/index.tsx +89 -44
  146. package/package.json +13 -4
  147. package/dist/chunks/contract-cxr9t12b.js +0 -18
  148. package/dist/chunks/contract-cxr9t12b.js.map +0 -10
  149. package/dist/chunks/contract-mwe7gnmh.js.map +0 -13
  150. package/dist/chunks/contract-txjy0en2.js.map +0 -10
  151. package/dist/chunks/contract-xtxza1fy.js.map +0 -12
  152. package/dist/chunks/contract-yxbjea23.js.map +0 -12
  153. package/dist/chunks/contract-zr3jd7mr.js.map +0 -12
  154. package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
  155. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
  156. package/docs/releasing.md +0 -79
  157. /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/react/shellStyles.ts", "../src/react/ui/StaticDataApp.tsx", "../src/react/ui/GettingStarted.tsx", "../src/react/ui/DataAppSkeleton.tsx", "../src/react/ui/GlossaryExplanation.tsx", "../src/react/ui/GlossaryDefinition.tsx", "../src/react/ui/TrendChart.tsx", "../src/react/ui/chart-data.ts", "../src/react/ui/LineChart.tsx", "../src/react/ui/AreaChart.tsx", "../src/react/ui/PieChart.tsx", "../src/react/ui/ScatterChart.tsx", "../src/react/ui/BarChart.tsx"],
4
+ "sourcesContent": [
5
+ "import { injectStyles } from '@/src/react/injectStyles';\nimport type { DataAppStylesOptions } from '@/src/react/styles';\n\n// Replaced with the compiled shell stylesheet by the package build.\ndeclare const SHELL_STYLES: string;\n\n/** Install shared layout, widget surface, and skeleton styles once per document, before mounting. */\nexport function injectDataAppShellStyles(\n options: DataAppStylesOptions = {}\n): HTMLStyleElement {\n return injectStyles({\n name: 'injectDataAppShellStyles',\n attribute: 'data-altertable-shell-styles',\n css: SHELL_STYLES,\n options,\n });\n}\n",
6
+ "import {\n DataAppFrame,\n type StaticDataAppProps,\n} from '@/src/react/ui/DataAppFrame';\n\nexport type DataAppProps = StaticDataAppProps;\n\n/** Setup and static screens; fetched data uses the declared-view DataApp. */\nexport function DataApp(props: DataAppProps) {\n return <DataAppFrame {...props} />;\n}\n",
7
+ "import { createDataContext as registerContext } from '@/src/react/ui/data-context';\nimport { useDeclaredResult } from '@/src/react/view-runtime';\nimport { createDataClient } from '@/src/client/data-client';\nimport type { DataAppConfig } from '@/src/core/config';\nimport { connectionCheck } from '@/src/core/contract';\nimport { createDataHooks } from '@/src/react/hooks';\nimport { AppIcon } from '@/src/react/ui/icons';\nimport { Button } from '@/src/react/ui/Button';\nimport { DataApp } from '@/src/react/ui/StaticDataApp';\nimport type { DataContext } from '@/src/react/ui/data-context';\n\nconst connectionContext = registerContext({})({\n description: 'Connection setup',\n glossary: {},\n});\n\nconst client = /* @__PURE__ */ createDataClient();\nconst { defineDataView } = /* @__PURE__ */ createDataHooks<{\n connection: ReturnType<typeof connectionCheck>;\n}>(client);\n\nconst connectionView = defineDataView({\n dataContext: connectionContext,\n operation: 'connection',\n describeInput: () => 'the connection',\n isEmpty: () => false,\n emptyFallback: { title: 'No connection' },\n});\n\n/** Query-backed connection state and next steps for a newly created app. Mount within\n * `DataAppProvider` and register `connection: connectionCheck()` on the server. */\nexport function GettingStarted({\n config,\n dataContext,\n}: {\n config: DataAppConfig;\n dataContext: DataContext;\n}) {\n const connection = useDeclaredResult(connectionView);\n const state =\n connection.view.kind === 'error' || connection.view.kind === 'stale-error'\n ? 'error'\n : connection.refreshing || !connection.snapshot\n ? 'checking'\n : 'connected';\n\n return (\n <DataApp\n config={config}\n dataContext={dataContext}\n description=\"Check your lakehouse connection, then build a view around a real question.\"\n queries={connection.queries}\n toolbarActions={\n <Button\n disabled={connection.refreshing}\n onClick={() => connection.refetch()}\n >\n Check connection\n </Button>\n }\n >\n <section\n className=\"starter-connection\"\n data-state={state}\n role={state === 'error' ? 'alert' : 'status'}\n >\n <div className=\"starter-connection-icon\">\n <AppIcon\n name={\n state === 'connected'\n ? 'check'\n : state === 'error'\n ? 'error'\n : 'loading'\n }\n size={24}\n />\n </div>\n <div className=\"starter-connection-copy\">\n <span className=\"starter-eyebrow\">Lakehouse connection</span>\n <h2>\n {state === 'connected'\n ? 'Connected'\n : state === 'error'\n ? 'Connection not verified'\n : 'Checking connection…'}\n </h2>\n <p>\n {state === 'connected'\n ? 'A query completed successfully using this app’s current profile. You’re ready to build your first view.'\n : state === 'error'\n ? connection.snapshot\n ? 'An earlier query succeeded, but the latest check failed. Check the profile and try again.'\n : 'The query could not complete. Check the profile and lakehouse access, then try again.'\n : 'Running a lightweight query with this app’s current profile.'}\n </p>\n {state === 'connected' && (\n <small>This check does not verify access to every dataset.</small>\n )}\n </div>\n {state === 'error' && (\n <Button onClick={() => connection.refetch()}>Try again</Button>\n )}\n </section>\n\n <section className=\"starter-next\" aria-labelledby=\"starter-next-title\">\n <div className=\"starter-next-heading\">\n <span className=\"starter-eyebrow\">Next steps</span>\n <h2 id=\"starter-next-title\">Turn this starter into a useful app</h2>\n <p>Start with one question your readers need answered.</p>\n </div>\n <ol className=\"starter-steps\">\n <li>\n <strong>Choose a question</strong>\n <p>\n Decide what someone should learn or do after opening this app.\n </p>\n </li>\n <li>\n <strong>Find the right data</strong>\n <p>\n Inspect the relevant catalogs, tables, and columns before writing\n the query.\n </p>\n </li>\n <li>\n <strong>Build the first view</strong>\n <p>\n Replace the connection check in <code>src/operations.ts</code>,\n then render its result in <code>src/App.tsx</code>.\n </p>\n </li>\n </ol>\n </section>\n </DataApp>\n );\n}\n",
8
+ "import type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { DataWidget } from '@/src/react/ui/DataWidget';\nimport { Grid } from '@/src/react/ui/Grid';\nimport { MetricWidget } from '@/src/react/ui/MetricWidget';\nimport { Skeleton } from '@/src/react/ui/Skeleton';\nimport { VisualizationWidget } from '@/src/react/ui/VisualizationWidget';\nimport { classNames } from '@/src/react/ui/classNames';\n\nexport type DataAppSkeletonProps = Omit<\n ComponentPropsWithRef<'output'>,\n 'children' | 'role' | 'aria-busy'\n> & {\n header?: ReactNode;\n footer?: ReactNode;\n};\n\nconst chartHeights = [40, 60, 50, 80, 60, 100, 80, 60];\n\n/** Host-side placeholder while a data app starts. Header and footer slots remain host-owned; widget placeholders are decorative. */\nexport function DataAppSkeleton({\n header,\n footer,\n className,\n ...props\n}: DataAppSkeletonProps) {\n return (\n <output\n aria-label=\"Loading data app\"\n {...props}\n aria-busy=\"true\"\n className={classNames('altertable-data-app-skeleton', className)}\n >\n <div className=\"altertable-data-app-skeleton-content\">\n {header != null && header !== false && (\n <div className=\"altertable-data-app-skeleton-header\">{header}</div>\n )}\n <div className=\"altertable-data-app-skeleton-body\" aria-hidden=\"true\">\n <Grid columns={3} gap=\"md\">\n {[0, 1, 2].map(item => (\n <MetricWidget key={item} label=\"\" loading />\n ))}\n </Grid>\n <VisualizationWidget\n title={\n <Skeleton className=\"altertable-data-app-skeleton-widget-title\" />\n }\n visual={\n <div className=\"altertable-data-app-skeleton-chart\">\n {chartHeights.map((height, index) => (\n <Skeleton key={index} style={{ height: `${height}%` }} />\n ))}\n </div>\n }\n />\n <DataWidget\n title={\n <Skeleton className=\"altertable-data-app-skeleton-widget-title\" />\n }\n >\n <div className=\"altertable-data-app-skeleton-rows\">\n {[0, 1, 2].map(row => (\n <div className=\"altertable-data-app-skeleton-row\" key={row}>\n <Skeleton />\n <Skeleton />\n <Skeleton />\n </div>\n ))}\n </div>\n </DataWidget>\n </div>\n {footer != null && footer !== false && (\n <div className=\"altertable-data-app-skeleton-footer\">{footer}</div>\n )}\n </div>\n </output>\n );\n}\n",
9
+ "import { AppIcon } from '@/src/react/ui/icons';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport type { DisclosedQuery } from '@/src/core/contract';\nimport type { DataContext, GlossaryEntry } from '@/src/react/ui/data-context';\nimport { AboutData, type AboutEmpty } from '@/src/react/ui/AboutData';\n\nexport type GlossaryExplanationProps = {\n entry: GlossaryEntry;\n empty?: AboutEmpty;\n title?: ReactNode;\n description?: ReactNode;\n visual?: ReactNode;\n queries?: DisclosedQuery[];\n dataContext?: DataContext;\n} & Omit<ComponentPropsWithRef<'button'>, 'children' | 'title'>;\n\n/** Opens the inspect sheet for one glossary term. Prefer a card's evidence slot\n * when the surface already has a title, description, and visual. */\nexport function GlossaryExplanation({\n entry,\n empty,\n title,\n description,\n visual,\n queries,\n dataContext,\n className,\n ...props\n}: GlossaryExplanationProps) {\n return (\n <AboutData\n {...props}\n shortcut={false}\n iconOnly\n variant=\"ghost\"\n title={title ?? entry.term}\n description={description}\n visual={visual}\n references={{ kind: 'entries', entries: [entry] }}\n empty={empty}\n dataContext={dataContext}\n queries={queries}\n className={className ?? 'altertable-inspect-trigger'}\n tooltip=\"Explore this term\"\n aria-label={props['aria-label'] ?? `Explore ${entry.term}`}\n >\n <AppIcon name=\"openDetails\" />\n </AboutData>\n );\n}\n",
10
+ "import type { ReactNode } from 'react';\nimport type { GlossaryEntry } from '@/src/react/ui/data-context';\nimport { HelpPopover } from '@/src/react/ui/HelpPopover';\n\nexport type GlossaryDefinitionProps = {\n entry: GlossaryEntry;\n children?: ReactNode;\n};\n\nexport function GlossaryDefinition({\n entry,\n children,\n}: GlossaryDefinitionProps) {\n return (\n <HelpPopover\n trigger={children ?? entry.term}\n triggerLabel={`Explain ${entry.term}`}\n label={entry.term}\n triggerProps={{\n className: 'altertable-glossary-definition-trigger',\n 'data-atbl-control': 'help',\n }}\n panelProps={{ className: 'altertable-glossary-definition-popover' }}\n >\n <strong>{entry.term}</strong>\n <div>{entry.definition}</div>\n </HelpPopover>\n );\n}\n",
11
+ "import { Tooltip } from '@/src/react/ui/Tooltip';\nimport { useLayoutEffect, useRef } from 'react';\nimport type { ValueChartProps } from '@/src/react/ui/chart-data';\nimport { validateChartItems } from '@/src/react/ui/chart-data';\n\n/** Ordered, equally spaced samples; negative values share an explicit zero baseline. */\nexport function TrendChart({\n items,\n unit,\n ariaLabel,\n formatValue = value => new Intl.NumberFormat().format(value),\n area = false,\n}: ValueChartProps & { area?: boolean }) {\n validateChartItems(area ? 'area' : 'line', items);\n const scrollport = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const element = scrollport.current;\n if (element) element.scrollLeft = element.scrollWidth - element.clientWidth;\n }, [items]);\n const magnitude = items.reduce(\n (max, item) => Math.max(max, Math.abs(item.value)),\n 1\n );\n const low = items.reduce(\n (min, item) => Math.min(min, item.value / magnitude),\n 0\n );\n const high = items.reduce(\n (max, item) => Math.max(max, item.value / magnitude),\n 0\n );\n const span = high - low || 1;\n function y(value: number) {\n return 208 - ((value / magnitude - low) / span) * 196;\n }\n const points = items.map((item, index) => ({\n x: ((index + 0.5) / items.length) * 1000,\n y: y(item.value),\n }));\n const path = points\n .map((point, index) => `${index ? 'L' : 'M'}${point.x},${point.y}`)\n .join(' ');\n const first = points[0];\n const last = points.at(-1);\n return (\n <section className=\"altertable-trend-chart\" aria-label={ariaLabel}>\n {items.length === 0 && <span>No data</span>}\n {items.length > 0 && (\n <>\n <div className=\"altertable-trend-scroll\" ref={scrollport}>\n <div\n className=\"altertable-trend-plot\"\n style={{ minWidth: items.length * 40 }}\n >\n <svg\n viewBox=\"0 0 1000 220\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <line\n className=\"altertable-trend-baseline\"\n x1=\"0\"\n x2=\"1000\"\n y1={y(0)}\n y2={y(0)}\n vectorEffect=\"non-scaling-stroke\"\n />\n {area && first && last && (\n <path\n className=\"altertable-trend-fill\"\n d={`${path} L${last.x},${y(0)} L${first.x},${y(0)} Z`}\n />\n )}\n <path\n className=\"altertable-trend-line\"\n d={path}\n vectorEffect=\"non-scaling-stroke\"\n />\n </svg>\n <div className=\"altertable-trend-points\">\n {items.map((item, index) => (\n <Tooltip\n key={item.id}\n variant=\"chart\"\n content={\n <>\n <strong>{item.label}</strong>\n <span>\n {formatValue(item.value)} {unit}\n </span>\n </>\n }\n className=\"altertable-chart-column\"\n >\n <button\n data-atbl-focus=\"ring\"\n data-atbl-control=\"action\"\n type=\"button\"\n tabIndex={-1}\n className=\"altertable-trend-point\"\n aria-label={`${item.label}: ${formatValue(item.value)} ${unit}`}\n >\n <span className=\"altertable-trend-hit\">\n <span\n className=\"altertable-trend-dot\"\n style={{ top: `${(points[index]!.y / 220) * 100}%` }}\n />\n </span>\n <span className=\"altertable-trend-label\">\n {item.label}\n </span>\n </button>\n </Tooltip>\n ))}\n </div>\n </div>\n </div>\n </>\n )}\n </section>\n );\n}\n",
12
+ "import { invariant } from '@/src/core/invariant';\n\n/** Ordered category or time-series sample. IDs are unique and nonblank; values are finite. */\nexport type ChartItem = { id: string; label: string; value: number };\nexport type ValueChartProps = {\n /** Ordered samples; the chart validates IDs and values when rendering. */\n items: readonly ChartItem[];\n unit: string;\n ariaLabel: string;\n formatValue?: (value: number) => string;\n};\n\n/** Independent observation on two finite numeric axes. */\nexport type ScatterChartItem = {\n id: string;\n label: string;\n x: number;\n y: number;\n};\ntype ChartKind = 'bar' | 'line' | 'area' | 'pie' | 'scatter';\ntype ItemFor<Kind extends ChartKind> = Kind extends 'scatter'\n ? ScatterChartItem\n : ChartItem;\n/** Validate the data consumed by every chart. */\nexport function validateChartItems<Kind extends ChartKind>(\n kind: Kind,\n items: readonly ItemFor<Kind>[]\n): void {\n const ids = new Set<string>();\n for (const item of items) {\n invariant(\n item.id.trim().length > 0,\n `${kind} chart IDs must be nonempty and unique: blank ID.`\n );\n invariant(\n !ids.has(item.id),\n `${kind} chart IDs must be nonempty and unique: duplicate \"${item.id}\".`\n );\n ids.add(item.id);\n if (kind === 'scatter') {\n const point = item as ScatterChartItem;\n invariant(\n Number.isFinite(point.x) && Number.isFinite(point.y),\n `scatter chart \"${item.id}\" requires finite values for x and y.`\n );\n } else {\n const point = item as ChartItem;\n invariant(\n Number.isFinite(point.value),\n `${kind} chart \"${item.id}\" requires finite values.`\n );\n invariant(\n (kind !== 'bar' && kind !== 'pie') || point.value >= 0,\n `${kind} chart \"${item.id}\" requires nonnegative values.`\n );\n }\n }\n}\n",
13
+ "import type { ChartItem, ValueChartProps } from '@/src/react/ui/chart-data';\nimport { TrendChart } from '@/src/react/ui/TrendChart';\n\nexport type LineChartItem = ChartItem;\nexport type LineChartProps = ValueChartProps;\n\n/** Equally spaced finite samples in supplied order; fill missing periods explicitly.\n * Supports negative values, single points, and horizontal scrolling for long series.\n * Compose inside VisualizationWidget; hover/touch tooltips need no caller state. */\nexport function LineChart(props: LineChartProps) {\n return <TrendChart {...props} />;\n}\n",
14
+ "import type { ChartItem, ValueChartProps } from '@/src/react/ui/chart-data';\nimport { TrendChart } from '@/src/react/ui/TrendChart';\n\nexport type AreaChartItem = ChartItem;\nexport type AreaChartProps = ValueChartProps;\n\n/** Ordered finite samples filled to a zero baseline, including negative values.\n * Samples are equally spaced; supply missing periods explicitly.\n * Compose inside VisualizationWidget; hover/touch tooltips need no caller state. */\nexport function AreaChart(props: AreaChartProps) {\n return <TrendChart {...props} area />;\n}\n",
15
+ "import { Tooltip } from '@/src/react/ui/Tooltip';\nimport { useState } from 'react';\nimport type { ChartItem, ValueChartProps } from '@/src/react/ui/chart-data';\nimport { validateChartItems } from '@/src/react/ui/chart-data';\n\nexport type PieChartItem = ChartItem;\nexport type PieChartProps = ValueChartProps;\n\n/** Nonnegative, mutually exclusive parts; shares use the sum of supplied items.\n * Include Other to represent the whole. Zero values remain in the legend; all-zero\n * data shows an empty circle. Colors follow item order. Compose inside VisualizationWidget;\n * hover/touch tooltips coordinate slice and legend emphasis without caller state. */\nexport function PieChart({\n items,\n unit,\n ariaLabel,\n formatValue = value => new Intl.NumberFormat().format(value),\n}: PieChartProps) {\n validateChartItems('pie', items);\n const [previewId, setPreviewId] = useState<string | null>(null);\n const [plotOpen, setPlotOpen] = useState(false);\n const [legendId, setLegendId] = useState<string | null>(null);\n const maximum = items.reduce((max, item) => Math.max(max, item.value), 0);\n const total = maximum\n ? items.reduce((sum, item) => sum + item.value / maximum, 0)\n : 0;\n let cursor = -Math.PI / 2;\n const slices: (PieChartItem & {\n share: number;\n color: string;\n path: string;\n })[] = [];\n for (const [index, item] of items.entries()) {\n const share = total ? item.value / maximum / total : 0;\n const start = cursor;\n cursor += share * Math.PI * 2;\n const middle = (start + cursor) / 2;\n function point(angle: number) {\n return `${110 + Math.cos(angle) * 100},${110 + Math.sin(angle) * 100}`;\n }\n slices.push({\n ...item,\n share,\n color: `var(--atbl-chart-${(index % 8) + 1})`,\n path: `${share === 1 ? 'M' : 'M110,110 L'}${point(start)} A100,100 0 0 1 ${point(middle)} A100,100 0 0 1 ${point(cursor)} Z`,\n });\n }\n function percent(share: number) {\n return new Intl.NumberFormat('en-US', {\n style: 'percent',\n maximumFractionDigits: 1,\n }).format(share);\n }\n function detail(item: (typeof slices)[number]) {\n return (\n <>\n <strong>{item.label}</strong>\n <span>\n {formatValue(item.value)} {unit} · {percent(item.share)}\n </span>\n </>\n );\n }\n const preview = slices.find(item => item.id === previewId);\n return (\n <section className=\"altertable-pie-chart\" aria-label={ariaLabel}>\n {items.length === 0 && <span>No data</span>}\n {items.length > 0 && (\n <>\n <div className=\"altertable-pie-plot\">\n <Tooltip\n variant=\"chart\"\n className=\"altertable-pie-tooltip\"\n onOpenChange={setPlotOpen}\n content={preview ? detail(preview) : null}\n >\n <svg viewBox=\"0 0 220 220\" aria-hidden=\"true\" focusable=\"false\">\n {total === 0 ? (\n <circle\n cx=\"110\"\n cy=\"110\"\n r=\"100\"\n className=\"altertable-pie-empty\"\n />\n ) : (\n slices\n .filter(item => item.share > 0)\n .map(item => (\n <path\n key={item.id}\n d={item.path}\n fill={item.color}\n className=\"altertable-pie-slice\"\n data-active={\n item.id ===\n (plotOpen && previewId ? previewId : legendId) ||\n undefined\n }\n onPointerDown={event => {\n if (plotOpen && previewId !== item.id)\n event.preventDefault();\n setPreviewId(item.id);\n }}\n onPointerEnter={event => {\n if (event.pointerType === 'mouse')\n setPreviewId(item.id);\n }}\n onPointerLeave={event => {\n if (event.pointerType === 'mouse') setPreviewId(null);\n }}\n />\n ))\n )}\n </svg>\n </Tooltip>\n {total === 0 && (\n <span className=\"altertable-pie-empty-label\">\n No nonzero values\n </span>\n )}\n </div>\n <div className=\"altertable-pie-legend\">\n {slices.map(item => (\n <Tooltip\n key={item.id}\n variant=\"chart\"\n content={detail(item)}\n onOpenChange={open =>\n setLegendId(current =>\n open ? item.id : current === item.id ? null : current\n )\n }\n >\n <button\n data-atbl-focus=\"ring\"\n data-atbl-control=\"action\"\n type=\"button\"\n tabIndex={-1}\n aria-label={`${item.label}: ${formatValue(item.value)} ${unit}, ${percent(item.share)}`}\n >\n <span\n className=\"altertable-pie-key\"\n style={{ background: item.color }}\n />\n <span className=\"altertable-pie-label\">{item.label}</span>\n <strong>{formatValue(item.value)}</strong>\n <span>{percent(item.share)}</span>\n </button>\n </Tooltip>\n ))}\n </div>\n </>\n )}\n </section>\n );\n}\n",
16
+ "import { Tooltip } from '@/src/react/ui/Tooltip';\n\nimport { validateChartItems } from '@/src/react/ui/chart-data';\n\nimport type { ScatterChartItem } from '@/src/react/ui/chart-data';\nexport type { ScatterChartItem } from '@/src/react/ui/chart-data';\nexport type ScatterChartProps = {\n items: readonly ScatterChartItem[];\n xLabel: string;\n yLabel: string;\n xUnit: string;\n yUnit: string;\n ariaLabel: string;\n formatX?: (value: number) => string;\n formatY?: (value: number) => string;\n};\n\nfunction formatNumber(value: number) {\n return new Intl.NumberFormat(undefined, { maximumFractionDigits: 1 }).format(\n value\n );\n}\n\nfunction scale(values: readonly number[]) {\n const magnitude = values.reduce(\n (max, value) => Math.max(max, Math.abs(value)),\n 1\n );\n const low = values.reduce(\n (min, value) => Math.min(min, value / magnitude),\n 0\n );\n const high =\n values.reduce((max, value) => Math.max(max, value / magnitude), 0) ||\n (low === 0 ? 1 : 0);\n const span = high - low;\n return {\n position(value: number) {\n return ((value / magnitude - low) / span) * 100;\n },\n ticks: [low, low + span / 2, high].map(value => value * magnitude),\n };\n}\n\n/** Independent finite X/Y observations on linear axes that include zero.\n * Negative and constant coordinates are supported. Axis labels and units describe\n * tooltips as well as the plot; formatX/formatY format ticks and tooltip values.\n * Compose inside VisualizationWidget; hover/touch tooltips need no caller state. */\nexport function ScatterChart({\n items,\n xLabel,\n yLabel,\n xUnit,\n yUnit,\n ariaLabel,\n formatX = formatNumber,\n formatY = formatNumber,\n}: ScatterChartProps) {\n validateChartItems('scatter', items);\n const xScale = scale(items.map(item => item.x));\n const yScale = scale(items.map(item => item.y));\n return (\n <section className=\"altertable-scatter-chart\" aria-label={ariaLabel}>\n {items.length === 0 && <span>No data</span>}\n {items.length > 0 && (\n <>\n <p className=\"altertable-scatter-axis-title\">\n {yLabel}\n {yUnit && ` (${yUnit})`}\n </p>\n <div className=\"altertable-scatter-axes\">\n <div className=\"altertable-scatter-plot\">\n {yScale.ticks.map((value, index) => (\n <div\n key={index}\n className=\"altertable-scatter-gridline\"\n style={{ bottom: `${index * 50}%` }}\n aria-hidden=\"true\"\n >\n <span title={formatY(value)}>{formatY(value)}</span>\n </div>\n ))}\n {xScale.ticks.map((value, index) => (\n <span\n key={index}\n className=\"altertable-scatter-x-tick\"\n data-edge={index}\n style={{ left: `${index * 50}%` }}\n aria-hidden=\"true\"\n title={formatX(value)}\n >\n {formatX(value)}\n </span>\n ))}\n {items.map(item => (\n <Tooltip\n key={item.id}\n variant=\"chart\"\n content={\n <>\n <strong>{item.label}</strong>\n <span>\n {xLabel}: {formatX(item.x)} {xUnit}\n </span>\n <span>\n {yLabel}: {formatY(item.y)} {yUnit}\n </span>\n </>\n }\n className=\"altertable-scatter-tooltip\"\n style={{\n left: `${xScale.position(item.x)}%`,\n bottom: `${yScale.position(item.y)}%`,\n }}\n >\n <button\n data-atbl-focus=\"ring\"\n data-atbl-control=\"action\"\n type=\"button\"\n tabIndex={-1}\n className=\"altertable-scatter-point\"\n aria-label={`${item.label}: ${xLabel} ${formatX(item.x)} ${xUnit}, ${yLabel} ${formatY(item.y)} ${yUnit}`}\n >\n <span />\n </button>\n </Tooltip>\n ))}\n </div>\n </div>\n <p className=\"altertable-scatter-axis-title altertable-scatter-x-title\">\n {xLabel}\n {xUnit && ` (${xUnit})`}\n </p>\n </>\n )}\n </section>\n );\n}\n",
17
+ "import { Tooltip } from '@/src/react/ui/Tooltip';\nimport { useLayoutEffect, useRef } from 'react';\n\nimport {\n validateChartItems,\n type ChartItem,\n type ValueChartProps,\n} from '@/src/react/ui/chart-data';\n\nexport type BarChartItem = ChartItem;\n\nexport type BarChartProps = ValueChartProps;\n\n/** Nonnegative category values. Compose inside VisualizationWidget for a titled panel.\n * Owns hover/touch tooltips; no selection state or keyboard navigation. */\nexport function BarChart({\n items,\n unit,\n ariaLabel,\n formatValue = value => new Intl.NumberFormat().format(value),\n}: BarChartProps) {\n validateChartItems('bar', items);\n const scrollport = useRef<HTMLDivElement>(null);\n\n useLayoutEffect(() => {\n const element = scrollport.current;\n if (element) element.scrollLeft = element.scrollWidth - element.clientWidth;\n }, [items]);\n\n const maximum = items.reduce((max, item) => Math.max(max, item.value), 1);\n\n return (\n <section className=\"altertable-selectable-bars\" aria-label={ariaLabel}>\n {items.length === 0 && <span>No data</span>}\n <div className=\"altertable-selectable-bars-scroll\" ref={scrollport}>\n {items.map(item => (\n <Tooltip\n key={item.id}\n variant=\"chart\"\n content={\n <>\n <strong>{item.label}</strong>\n <span>\n {formatValue(item.value)} {unit}\n </span>\n </>\n }\n className=\"altertable-chart-column\"\n >\n <button\n data-atbl-focus=\"ring\"\n data-atbl-control=\"action\"\n type=\"button\"\n tabIndex={-1}\n className=\"altertable-selectable-bars-item\"\n aria-label={`${item.label}: ${formatValue(item.value)} ${unit}`}\n >\n <span className=\"altertable-selectable-bars-area\">\n <span\n className=\"altertable-selectable-bars-bar\"\n style={{\n height: `${Math.max(item.value > 0 ? 3 : 0, (item.value / maximum) * 100)}%`,\n }}\n />\n </span>\n <span className=\"altertable-selectable-bars-label\">\n {item.label}\n </span>\n </button>\n </Tooltip>\n ))}\n </div>\n </section>\n );\n}\n"
18
+ ],
19
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOO,SAAS,wBAAwB,CACtC,UAAgC,CAAC,GACf;AAAA,EAClB,OAAO,aAAa;AAAA,IAClB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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",
20
+ "debugId": "44AF1347DDC5E0E164756E2164756E21",
21
+ "names": []
22
+ }
package/dist/server.js CHANGED
@@ -300,4 +300,4 @@ export {
300
300
  createDataHandler
301
301
  };
302
302
 
303
- //# debugId=CBFE1DD1426CC5FA64756E2164756E21
303
+ //# debugId=97D671C00924C2DF64756E2164756E21
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/core/contract.ts", "../src/core/invariant.ts", "../src/core/operation.ts", "../src/server/handler.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Shared operation contracts and input/output validation for browser and server.\n * @module @altertable/data-app/contract\n * @see https://github.com/altertable-ai/data-app/blob/main/docs/contract.md\n */\nimport { parseDate, today } from '@internationalized/date';\nimport { formatDateRange } from '@/src/core/format';\nimport { invariant } from '@/src/core/invariant';\nimport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nimport type {\n QueryResult,\n DataOperation,\n OperationContext,\n} from '@/src/core/operation-types';\nexport type {\n QueryResult,\n DisclosedQuery,\n Lakehouse,\n OperationContext,\n DataOperation,\n DataOperations,\n} from '@/src/core/operation-types';\n\nexport function parseEmptyInput(value: unknown): Record<string, never> {\n invariant(\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0,\n 'This operation takes no inputs.'\n );\n\n return {};\n}\n\nexport function parseTrue(value: unknown): true {\n invariant(value === true, 'Expected a successful check.');\n\n return true;\n}\n\nexport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nexport type DateRangeRequest = {\n range: DateRangeInput;\n comparison: DateRangeInput | null;\n};\n\n/** The immediately preceding, equally long set of calendar days. */\nexport function previousDateRange({\n start,\n end,\n}: DateRangeInput): DateRangeInput {\n const first = parseDate(start);\n const last = parseDate(end);\n const days =\n (last.toDate('UTC').getTime() - first.toDate('UTC').getTime()) /\n 86_400_000 +\n 1;\n invariant(days >= 1, 'Date range must start before it ends.');\n const previousEnd = first.subtract({ days: 1 });\n\n return {\n start: previousEnd.subtract({ days: days - 1 }).toString(),\n end: previousEnd.toString(),\n };\n}\n\nexport type ReportingPeriod =\n | { kind: 'rolling'; amount: number; unit: 'hour' | 'day'; end: string }\n | { kind: 'calendar'; start: string; end: string; timeZone: string };\n\nexport type DateRangeContractOptions = {\n minDate?: string;\n maxDate?: string | (() => string);\n maxRangeDays: number;\n timeZone: string;\n completeDays?: boolean;\n};\n\n/** One source policy for the server parser, URL variable, and displayed reporting period. */\nexport function defineDateRangeContract(options: DateRangeContractOptions) {\n invariant(\n Number.isInteger(options.maxRangeDays) && options.maxRangeDays > 0,\n 'Date range needs a positive maxRangeDays.'\n );\n if (options.minDate) parseDate(options.minDate);\n if (typeof options.maxDate === 'string') parseDate(options.maxDate);\n today(options.timeZone);\n\n function bounds() {\n const latest = today(options.timeZone).subtract({\n days: options.completeDays === false ? 0 : 1,\n });\n const sourceMax =\n typeof options.maxDate === 'function'\n ? options.maxDate()\n : options.maxDate;\n const maxDate =\n sourceMax && parseDate(sourceMax).compare(latest) < 0\n ? parseDate(sourceMax).toString()\n : latest.toString();\n\n return {\n minDate: options.minDate,\n maxDate,\n maxRangeDays: options.maxRangeDays,\n timeZone: options.timeZone,\n };\n }\n\n function period({ start, end }: DateRangeInput): ReportingPeriod {\n return {\n kind: 'calendar',\n start,\n end,\n timeZone: options.timeZone,\n };\n }\n\n function describeInput(input: DateRangeInput) {\n return `${formatDateRange(input)} ${options.timeZone}`;\n }\n\n function comparison(input: DateRangeInput): DateRangeInput | null {\n const previous = previousDateRange(input);\n\n return options.minDate && previous.start < options.minDate\n ? null\n : previous;\n }\n\n function request(value: unknown, compare = false): DateRangeRequest {\n const range = parseDateRangeInput(value, bounds());\n const previous = compare ? comparison(range) : null;\n invariant(\n !compare || previous,\n 'Comparison is outside available source coverage.'\n );\n\n return { range, comparison: previous };\n }\n\n function parseRequest(value: unknown): DateRangeRequest {\n invariant(value && typeof value === 'object', 'Choose a date range.');\n const input = value as Record<string, unknown>;\n const parsed = request(input.range, input.comparison != null);\n if (input.comparison != null) {\n const supplied = parseDateRangeInput(input.comparison, bounds());\n invariant(\n supplied.start === parsed.comparison?.start &&\n supplied.end === parsed.comparison?.end,\n 'Comparison must be the preceding equal-length range.'\n );\n }\n\n return parsed;\n }\n\n return {\n request,\n parseRequest,\n bounds,\n parse(value: unknown) {\n return parseDateRangeInput(value, bounds());\n },\n period,\n comparison,\n describeInput,\n view: { period, describeInput },\n };\n}\n\nexport type DateRangeContract = ReturnType<typeof defineDateRangeContract>;\n\n/** Validate calendar dates and a bounded inclusive range on the server. */\nexport function parseDateRangeInput(\n value: unknown,\n {\n minDate,\n maxDate,\n maxRangeDays,\n }: { minDate?: string; maxDate?: string; maxRangeDays: number }\n): DateRangeInput {\n invariant(\n value && typeof value === 'object' && !Array.isArray(value),\n 'Choose a date range.'\n );\n const { start, end } = value as Record<string, unknown>;\n\n function day(input: unknown): number {\n invariant(\n typeof input === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(input),\n 'Dates must be YYYY-MM-DD.'\n );\n const timestamp = Date.parse(`${input}T00:00:00Z`);\n invariant(\n Number.isFinite(timestamp) &&\n new Date(timestamp).toISOString().slice(0, 10) === input,\n 'Choose valid calendar dates.'\n );\n\n return timestamp;\n }\n const first = day(start);\n const last = day(end);\n invariant(\n Number.isInteger(maxRangeDays) && maxRangeDays > 0,\n 'Maximum range days must be positive.'\n );\n invariant(\n first <= last &&\n (last - first) / 86_400_000 < maxRangeDays &&\n (minDate === undefined || first >= day(minDate)) &&\n (maxDate === undefined || last <= day(maxDate)),\n `Choose up to ${maxRangeDays} days within the available range.`\n );\n\n return { start: start as string, end: end as string };\n}\n\n/** Accepts numeric strings from DuckDB; rejects negative, fractional, and unsafe integers. */\nexport function parseCount(value: unknown): number {\n const number =\n typeof value === 'number' || typeof value === 'string'\n ? Number(value)\n : NaN;\n invariant(\n Number.isSafeInteger(number) &&\n number >= 0 &&\n (typeof value !== 'string' || !!value.trim()),\n 'Invalid count in query result.'\n );\n\n return number;\n}\n\nexport function parseLabel(value: unknown, maxLength = 100): string {\n invariant(\n typeof value === 'string' && !!value && value.length <= maxLength,\n 'Invalid label in query result.'\n );\n\n return value;\n}\n\n/** Rejects missing or duplicate columns and rows whose width differs from the column list. */\nexport function rowsAsRecords(\n result: QueryResult,\n requiredColumns: readonly string[]\n): Record<string, unknown>[] {\n const names = result.columns.map(column => column.name);\n invariant(\n new Set(names).size === names.length &&\n requiredColumns.every(name => names.includes(name)),\n 'Query result columns do not match the expected shape.'\n );\n\n return result.rows.map(row => {\n invariant(\n row.length === names.length,\n 'Query result row has the wrong width.'\n );\n\n return Object.fromEntries(names.map((name, index) => [name, row[index]]));\n });\n}\n\nexport function defineQueryNames<const Names extends Record<string, string>>(\n names: Names\n): Names {\n const values = Object.values(names);\n invariant(\n values.every(name => !!name.trim()) &&\n new Set(values).size === values.length,\n 'Query names must be nonempty and unique.'\n );\n\n return names;\n}\n\nconst sourceErrorBrand = Symbol.for('@altertable/data-app/DataSourceError');\n\nexport class DataSourceError extends Error {\n readonly [sourceErrorBrand] = true;\n queryName?: string;\n\n static [Symbol.hasInstance](value: unknown): boolean {\n return (\n value instanceof Error &&\n (value as { [sourceErrorBrand]?: unknown })[sourceErrorBrand] === true\n );\n }\n\n constructor(\n public readonly reason:\n | 'unauthorized'\n | 'forbidden'\n | 'rate_limited'\n | 'query_rejected'\n | 'unavailable',\n public readonly status?: number\n ) {\n super(`Data source ${reason}${status ? ` (${status})` : ''}.`);\n this.name = 'DataSourceError';\n }\n}\n\nexport type OperationQuery<Names extends Readonly<Record<string, string>>> = (\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n) => Promise<QueryResult>;\n\nexport function defineOperation<\n Input,\n Output,\n const Names extends Readonly<Record<string, string>> = Record<string, never>,\n>(\n operation: Omit<DataOperation<Input, Output>, 'run' | 'queryNames'> & {\n queryNames?: Names;\n run: (\n context: OperationContext & { query: OperationQuery<NoInfer<Names>> },\n input: Input\n ) => Promise<Output>;\n }\n): DataOperation<Input, Output> & { queryNames?: Names } {\n invariant(\n Array.isArray(operation.checks) &&\n operation.checks.length > 0 &&\n Number.isInteger(operation.policy.maxQueryRows) &&\n operation.policy.maxQueryRows > 0 &&\n Number.isInteger(operation.policy.maxDurationMs) &&\n operation.policy.maxDurationMs > 0 &&\n (operation.policy.maxResponseBytes === undefined ||\n (Number.isInteger(operation.policy.maxResponseBytes) &&\n operation.policy.maxResponseBytes > 0)),\n 'Each data operation needs check inputs and positive row and duration limits.'\n );\n if (operation.queryNames) defineQueryNames(operation.queryNames);\n for (const input of operation.checks) operation.input(input);\n\n return {\n ...operation,\n run(context, input) {\n function query(\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n ): Promise<QueryResult> {\n invariant(\n operation.queryNames &&\n Object.values(operation.queryNames).includes(name),\n `Unknown query name: ${name}.`\n );\n\n return context.lakehouse.queryAll(statement, {\n name,\n limit: options?.limit ?? operation.policy.maxQueryRows,\n signal: context.signal,\n });\n }\n\n return operation.run({ ...context, query }, input);\n },\n };\n}\n\nexport const connectionQueryNames = defineQueryNames({\n connection: 'connection-check',\n});\n\n/** Success requires a bounded SQL query; it does not establish access to a particular dataset. */\nexport function connectionCheck(): DataOperation<Record<string, never>, true> {\n return defineOperation({\n input: parseEmptyInput,\n output: parseTrue,\n checks: [{}],\n queryNames: connectionQueryNames,\n policy: { maxQueryRows: 1, maxDurationMs: 15_000, exposeSql: true },\n async run({ query }): Promise<true> {\n await query(\n connectionQueryNames.connection,\n 'SELECT 1 AS connection_check'\n );\n\n return true;\n },\n });\n}\n\nexport {\n dimensionFilter,\n parseDimensionSelection,\n parseFacetOptions,\n dimensionPredicate,\n} from '@/src/core/dimension';\nexport type {\n DimensionSelection,\n DimensionMember,\n DimensionOption,\n DimensionVariable,\n DimensionFilterOptions,\n} from '@/src/core/dimension';\n\nexport {\n createMessageRouter,\n defineMessageRoute,\n defineDataQueryRoute,\n MessageRoutingError,\n dataAppRoutes,\n navigationUpdateRoute,\n sqlQueryRoute,\n} from '@/src/core/messages';\nexport type {\n MessageContext,\n RoutedMessage,\n MessageDispatcher,\n MessageTransport,\n MessageRoute,\n MessageRoutes,\n MessageInput,\n MessageHandlers,\n MessageOutput,\n OperationContracts,\n DataQueryInput,\n DataQueryBody,\n DataQueryRoute,\n NavigationUpdate,\n SqlQueryInput,\n} from '@/src/core/messages';\nexport type { TransportResponse } from '@/src/core/bridge';\n",
5
+ "/**\n * Shared operation contracts and input/output validation for browser and server.\n * @module @altertable/data-app/contract\n * @see https://github.com/altertable-ai/data-app/blob/main/docs/contract.md\n */\nimport { parseDate, today } from '@internationalized/date';\nimport { formatDateRange } from '@/src/core/format';\nimport { invariant } from '@/src/core/invariant';\nimport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nimport type {\n QueryResult,\n DataOperation,\n OperationContext,\n} from '@/src/core/operation-types';\nexport type {\n QueryResult,\n DisclosedQuery,\n Lakehouse,\n OperationContext,\n DataOperation,\n DataOperations,\n} from '@/src/core/operation-types';\n\nexport function parseEmptyInput(value: unknown): Record<string, never> {\n invariant(\n typeof value === 'object' &&\n value !== null &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0,\n 'This operation takes no inputs.'\n );\n\n return {};\n}\n\nexport function parseTrue(value: unknown): true {\n invariant(value === true, 'Expected a successful check.');\n\n return true;\n}\n\nexport type { DateRange as DateRangeInput } from '@/src/core/date-range';\nexport type DateRangeRequest = {\n range: DateRangeInput;\n comparison: DateRangeInput | null;\n};\n\n/** The immediately preceding, equally long set of calendar days. */\nexport function previousDateRange({\n start,\n end,\n}: DateRangeInput): DateRangeInput {\n const first = parseDate(start);\n const last = parseDate(end);\n const days =\n (last.toDate('UTC').getTime() - first.toDate('UTC').getTime()) /\n 86_400_000 +\n 1;\n invariant(days >= 1, 'Date range must start before it ends.');\n const previousEnd = first.subtract({ days: 1 });\n\n return {\n start: previousEnd.subtract({ days: days - 1 }).toString(),\n end: previousEnd.toString(),\n };\n}\n\nexport type ReportingPeriod =\n | { kind: 'rolling'; amount: number; unit: 'hour' | 'day'; end: string }\n | { kind: 'calendar'; start: string; end: string; timeZone: string };\n\nexport type DateRangeContractOptions = {\n minDate?: string;\n maxDate?: string | (() => string);\n maxRangeDays: number;\n timeZone: string;\n completeDays?: boolean;\n};\n\n/** One source policy for the server parser, URL variable, and displayed reporting period. */\nexport function defineDateRangeContract(options: DateRangeContractOptions) {\n invariant(\n Number.isInteger(options.maxRangeDays) && options.maxRangeDays > 0,\n 'Date range needs a positive maxRangeDays.'\n );\n if (options.minDate) parseDate(options.minDate);\n if (typeof options.maxDate === 'string') parseDate(options.maxDate);\n today(options.timeZone);\n\n function bounds() {\n const latest = today(options.timeZone).subtract({\n days: options.completeDays === false ? 0 : 1,\n });\n const sourceMax =\n typeof options.maxDate === 'function'\n ? options.maxDate()\n : options.maxDate;\n const maxDate =\n sourceMax && parseDate(sourceMax).compare(latest) < 0\n ? parseDate(sourceMax).toString()\n : latest.toString();\n\n return {\n minDate: options.minDate,\n maxDate,\n maxRangeDays: options.maxRangeDays,\n timeZone: options.timeZone,\n };\n }\n\n function period({ start, end }: DateRangeInput): ReportingPeriod {\n return {\n kind: 'calendar',\n start,\n end,\n timeZone: options.timeZone,\n };\n }\n\n function describeInput(input: DateRangeInput) {\n return `${formatDateRange(input)} ${options.timeZone}`;\n }\n\n function comparison(input: DateRangeInput): DateRangeInput | null {\n const previous = previousDateRange(input);\n\n return options.minDate && previous.start < options.minDate\n ? null\n : previous;\n }\n\n function request(value: unknown, compare = false): DateRangeRequest {\n const range = parseDateRangeInput(value, bounds());\n const previous = compare ? comparison(range) : null;\n invariant(\n !compare || previous,\n 'Comparison is outside available source coverage.'\n );\n\n return { range, comparison: previous };\n }\n\n function parseRequest(value: unknown): DateRangeRequest {\n invariant(value && typeof value === 'object', 'Choose a date range.');\n const input = value as Record<string, unknown>;\n const parsed = request(input.range, input.comparison != null);\n if (input.comparison != null) {\n const supplied = parseDateRangeInput(input.comparison, bounds());\n invariant(\n supplied.start === parsed.comparison?.start &&\n supplied.end === parsed.comparison?.end,\n 'Comparison must be the preceding equal-length range.'\n );\n }\n\n return parsed;\n }\n\n return {\n request,\n parseRequest,\n bounds,\n parse(value: unknown) {\n return parseDateRangeInput(value, bounds());\n },\n period,\n comparison,\n describeInput,\n view: { period, describeInput },\n };\n}\n\nexport type DateRangeContract = ReturnType<typeof defineDateRangeContract>;\n\n/** Validate calendar dates and a bounded inclusive range on the server. */\nexport function parseDateRangeInput(\n value: unknown,\n {\n minDate,\n maxDate,\n maxRangeDays,\n }: { minDate?: string; maxDate?: string; maxRangeDays: number }\n): DateRangeInput {\n invariant(\n value && typeof value === 'object' && !Array.isArray(value),\n 'Choose a date range.'\n );\n const { start, end } = value as Record<string, unknown>;\n\n function day(input: unknown): number {\n invariant(\n typeof input === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(input),\n 'Dates must be YYYY-MM-DD.'\n );\n const timestamp = Date.parse(`${input}T00:00:00Z`);\n invariant(\n Number.isFinite(timestamp) &&\n new Date(timestamp).toISOString().slice(0, 10) === input,\n 'Choose valid calendar dates.'\n );\n\n return timestamp;\n }\n const first = day(start);\n const last = day(end);\n invariant(\n Number.isInteger(maxRangeDays) && maxRangeDays > 0,\n 'Maximum range days must be positive.'\n );\n invariant(\n first <= last &&\n (last - first) / 86_400_000 < maxRangeDays &&\n (minDate === undefined || first >= day(minDate)) &&\n (maxDate === undefined || last <= day(maxDate)),\n `Choose up to ${maxRangeDays} days within the available range.`\n );\n\n return { start: start as string, end: end as string };\n}\n\n/** Accepts numeric strings from DuckDB; rejects negative, fractional, and unsafe integers. */\nexport function parseCount(value: unknown): number {\n const number =\n typeof value === 'number' || typeof value === 'string'\n ? Number(value)\n : NaN;\n invariant(\n Number.isSafeInteger(number) &&\n number >= 0 &&\n (typeof value !== 'string' || !!value.trim()),\n 'Invalid count in query result.'\n );\n\n return number;\n}\n\nexport function parseLabel(value: unknown, maxLength = 100): string {\n invariant(\n typeof value === 'string' && !!value && value.length <= maxLength,\n 'Invalid label in query result.'\n );\n\n return value;\n}\n\n/** Rejects missing or duplicate columns and rows whose width differs from the column list. */\nexport function rowsAsRecords(\n result: QueryResult,\n requiredColumns: readonly string[]\n): Record<string, unknown>[] {\n const names = result.columns.map(column => column.name);\n invariant(\n new Set(names).size === names.length &&\n requiredColumns.every(name => names.includes(name)),\n 'Query result columns do not match the expected shape.'\n );\n\n return result.rows.map(row => {\n invariant(\n row.length === names.length,\n 'Query result row has the wrong width.'\n );\n\n return Object.fromEntries(names.map((name, index) => [name, row[index]]));\n });\n}\n\nexport function defineQueryNames<const Names extends Record<string, string>>(\n names: Names\n): Names {\n const values = Object.values(names);\n invariant(\n values.every(name => !!name.trim()) &&\n new Set(values).size === values.length,\n 'Query names must be nonempty and unique.'\n );\n\n return names;\n}\n\nconst sourceErrorBrand = Symbol.for('@altertable/data-app/DataSourceError');\n\nexport class DataSourceError extends Error {\n readonly [sourceErrorBrand] = true;\n queryName?: string;\n\n static [Symbol.hasInstance](value: unknown): boolean {\n return (\n value instanceof Error &&\n (value as { [sourceErrorBrand]?: unknown })[sourceErrorBrand] === true\n );\n }\n\n constructor(\n public readonly reason:\n | 'unauthorized'\n | 'forbidden'\n | 'rate_limited'\n | 'query_rejected'\n | 'unavailable',\n public readonly status?: number\n ) {\n super(`Data source ${reason}${status ? ` (${status})` : ''}.`);\n this.name = 'DataSourceError';\n }\n}\n\nexport type OperationQuery<Names extends Readonly<Record<string, string>>> = (\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n) => Promise<QueryResult>;\n\nexport function defineOperation<\n Input,\n Output,\n const Names extends Readonly<Record<string, string>> = Record<string, never>,\n>(\n operation: Omit<DataOperation<Input, Output>, 'run' | 'queryNames'> & {\n queryNames?: Names;\n run: (\n context: OperationContext & { query: OperationQuery<NoInfer<Names>> },\n input: Input\n ) => Promise<Output>;\n }\n): DataOperation<Input, Output> & { queryNames?: Names } {\n invariant(\n Array.isArray(operation.checks) &&\n operation.checks.length > 0 &&\n Number.isInteger(operation.policy.maxQueryRows) &&\n operation.policy.maxQueryRows > 0 &&\n Number.isInteger(operation.policy.maxDurationMs) &&\n operation.policy.maxDurationMs > 0 &&\n (operation.policy.maxResponseBytes === undefined ||\n (Number.isInteger(operation.policy.maxResponseBytes) &&\n operation.policy.maxResponseBytes > 0)),\n 'Each data operation needs check inputs and positive row and duration limits.'\n );\n if (operation.queryNames) defineQueryNames(operation.queryNames);\n for (const input of operation.checks) operation.input(input);\n\n return {\n ...operation,\n run(context, input) {\n function query(\n name: Names[keyof Names],\n statement: string,\n options?: { limit?: number }\n ): Promise<QueryResult> {\n invariant(\n operation.queryNames &&\n Object.values(operation.queryNames).includes(name),\n `Unknown query name: ${name}.`\n );\n\n return context.lakehouse.queryAll(statement, {\n name,\n limit: options?.limit ?? operation.policy.maxQueryRows,\n signal: context.signal,\n });\n }\n\n return operation.run({ ...context, query }, input);\n },\n };\n}\n\nexport const connectionQueryNames = defineQueryNames({\n connection: 'connection-check',\n});\n\n/** Success requires a bounded SQL query; it does not establish access to a particular dataset. */\nexport function connectionCheck(): DataOperation<Record<string, never>, true> {\n return defineOperation({\n input: parseEmptyInput,\n output: parseTrue,\n checks: [{}],\n queryNames: connectionQueryNames,\n policy: { maxQueryRows: 1, maxDurationMs: 15_000, exposeSql: true },\n async run({ query }): Promise<true> {\n await query(\n connectionQueryNames.connection,\n 'SELECT 1 AS connection_check'\n );\n\n return true;\n },\n });\n}\n\nexport {\n dimensionFilter,\n parseDimensionSelection,\n parseFacetOptions,\n dimensionPredicate,\n} from '@/src/core/dimension';\nexport type {\n DimensionSelection,\n DimensionMember,\n DimensionOption,\n DimensionVariable,\n DimensionFilterOptions,\n} from '@/src/core/dimension';\n\nexport {\n createMessageRouter,\n defineMessageRoute,\n defineDataQueryRoute,\n MessageRoutingError,\n dataAppRoutes,\n navigationUpdateRoute,\n sqlQueryRoute,\n} from '@/src/core/messages';\nexport type {\n MessageContext,\n RoutedMessage,\n MessageDispatcher,\n MessageTransport,\n MessageRoute,\n MessageRoutes,\n MessageInput,\n MessageHandlers,\n MessageOutput,\n OperationContracts,\n DataQueryInput,\n DataQueryBody,\n DataQueryRoute,\n NavigationUpdate,\n SqlQueryInput,\n} from '@/src/core/messages';\nexport type { TransportResponse } from '@/src/core/bridge';\n\nexport {\n annotationDraftRoute,\n annotationModeRoute,\n annotationEditorStateRoute,\n annotationUpdateRoute,\n parseDataAppAnnotationDraft,\n} from '@/src/core/annotations';\nexport type {\n DataAppAnnotationDraft,\n DataAppAnnotationPresentation,\n} from '@/src/core/annotations';\n",
6
6
  "/** Assert an author or data contract while preserving a useful failure message. */\nexport function invariant(\n condition: unknown,\n message: string\n): asserts condition {\n if (!condition) throw new Error(`Invariant failed: ${message}`);\n}\n",
7
7
  "import { DataSourceError } from '@/src/core/contract';\nimport type {\n DataOperations,\n DisclosedQuery,\n Lakehouse,\n OperationContext,\n DataQueryBody,\n} from '@/src/core/operation-types';\n\ntype Operation = DataOperations[string];\ntype OperationExecutionOptions = {\n operationName: string;\n lakehouse: Lakehouse;\n signal: AbortSignal;\n /** Include query evidence in the response; this does not keep browser-owned SQL private. */\n includeSql: boolean;\n requestId: string;\n};\n\n/** Shared operation execution. Adapters own authorization, delivery, and public errors. */\nexport async function executeDataOperation(\n operation: Operation,\n value: unknown,\n {\n operationName,\n lakehouse,\n signal: requestSignal,\n includeSql,\n requestId,\n }: OperationExecutionOptions\n) {\n requestSignal.throwIfAborted();\n let input: unknown;\n try {\n input = operation.input(value);\n } catch {\n throw new OperationInputError();\n }\n\n const signal = AbortSignal.any([\n requestSignal,\n AbortSignal.timeout(operation.policy.maxDurationMs),\n ]);\n const execution = createOperationLakehouse(\n operation,\n lakehouse,\n signal,\n operationName\n );\n try {\n const output = await runOperation(operation, input, {\n lakehouse: execution.lakehouse,\n signal,\n });\n const body: DataQueryBody<unknown> = {\n data: operation.output(output),\n requestId,\n queriedAt: new Date().toISOString(),\n queryIds: execution.queryIds,\n ...(operation.policy.exposeSql && includeSql\n ? { queries: execution.queries }\n : {}),\n };\n const serializedBody = JSON.stringify(body);\n const responseBytes = new TextEncoder().encode(serializedBody).byteLength;\n if (responseBytes > (operation.policy.maxResponseBytes ?? 1_000_000))\n throw new Error('Response exceeds byte limit.');\n\n return { body, serializedBody };\n } catch (error) {\n if (signal.aborted && !(error instanceof DataSourceError))\n throw new OperationTimeoutError();\n throw error;\n }\n}\n\n/** Query evidence follows statement invocation order; query IDs retain completion order. */\nfunction createOperationLakehouse(\n operation: Operation,\n source: Lakehouse,\n signal: AbortSignal,\n operationName: string\n) {\n const queryIds: string[] = [];\n const queries: DisclosedQuery[] = [];\n const lakehouse: Lakehouse = {\n async queryAll(statement, options) {\n if (!Number.isInteger(options.limit) || options.limit < 1)\n throw new Error('Query needs a positive row limit.');\n if (\n operation.queryNames &&\n !Object.values(operation.queryNames).includes(options.name ?? '')\n )\n throw new Error(\n `Query name is not registered for operation ${operationName}.`\n );\n const query: DisclosedQuery = {\n name: options.name ?? `Query ${queries.length + 1}`,\n statement,\n };\n queries.push(query);\n const limit = Math.min(options.limit, operation.policy.maxQueryRows);\n let result;\n try {\n result = await source.queryAll(statement, {\n limit,\n signal: AbortSignal.any([signal, options.signal]),\n });\n } catch (error) {\n if (error instanceof DataSourceError) error.queryName = query.name;\n throw error;\n }\n if (result.rows.length > limit)\n throw new Error('Result exceeds row limit.');\n if (result.queryId) {\n query.queryId = result.queryId;\n queryIds.push(result.queryId);\n }\n\n return result;\n },\n };\n\n return { lakehouse, queries, queryIds };\n}\n\n/** Settle cancellation even when operation code or its lakehouse ignores the signal. */\nasync function runOperation(\n operation: Operation,\n input: unknown,\n { lakehouse, signal }: OperationContext\n) {\n let abort: (() => void) | undefined;\n try {\n signal.throwIfAborted();\n const cancelled = new Promise<never>((_, reject) => {\n function rejectAborted() {\n reject(new Error('Request aborted.'));\n }\n abort = rejectAborted;\n signal.addEventListener('abort', rejectAborted, { once: true });\n if (signal.aborted) rejectAborted();\n });\n\n return await Promise.race([\n cancelled,\n Promise.resolve().then(() => {\n signal.throwIfAborted();\n\n return operation.run({ lakehouse, signal }, input as never);\n }),\n ]);\n } finally {\n if (abort) signal.removeEventListener('abort', abort);\n }\n}\n\nclass OperationTimeoutError extends Error {}\n\nclass OperationInputError extends Error {}\n\nconst sourceErrorMessages = {\n unauthorized:\n 'This app could not authenticate its data connection. Contact the app owner.',\n forbidden:\n 'This app cannot access the requested data. Contact the app owner.',\n rate_limited: 'The lakehouse is busy. Wait a moment and retry.',\n query_rejected:\n \"A lakehouse query was rejected. Check the app's data operation.\",\n unavailable: 'The lakehouse is unavailable. Check the connection and retry.',\n};\n\nexport function toDataOperationFailure(error: unknown, aborted = false) {\n if (error instanceof OperationInputError)\n return {\n status: 400,\n code: 'invalid_input',\n message: \"Check this operation's inputs.\",\n };\n if (error instanceof DataSourceError)\n return {\n status: error.reason === 'rate_limited' ? 429 : 502,\n code: `source_${error.reason}`,\n message: sourceErrorMessages[error.reason],\n };\n const timedOut = aborted || error instanceof OperationTimeoutError;\n return {\n status: timedOut ? 504 : 502,\n code: timedOut ? 'timeout' : 'query_failed',\n message: timedOut\n ? 'The data request timed out.'\n : 'The data request failed.',\n };\n}\n",
8
8
  "import {\n executeDataOperation,\n toDataOperationFailure,\n} from '@/src/core/operation';\nimport {\n DataSourceError,\n type DataOperations,\n type Lakehouse,\n} from '@/src/core/contract';\n\n/** Authorization result for one viewer request. A hosted adapter must scope its lakehouse client. */\nexport type RequestAccess = { lakehouse: Lakehouse; canDiscloseSql: boolean };\n\n/**\n * Execute named operations on the server. The app supplies operations and viewer authorization.\n * JSON, Origin, and Fetch Metadata checks reject cross-site browser POSTs; they do not\n * authenticate another local process. SQL is returned only when both the operation and\n * authorization result permit disclosure.\n */\nexport function createDataHandler(\n operations: DataOperations,\n authorize: (request: Request, operation: string) => Promise<RequestAccess>\n) {\n async function handleDataRequest(request: Request): Promise<Response> {\n const requestId = crypto.randomUUID();\n const url = new URL(request.url);\n if (!url.pathname.startsWith('/api/data/'))\n return new Response('Not found', { status: 404 });\n const encodedName = url.pathname.slice('/api/data/'.length);\n if (!encodedName || encodedName.includes('/'))\n return problem(404, 'not_found', 'Unknown data operation.', requestId);\n let name: string;\n try {\n name = decodeURIComponent(encodedName);\n } catch {\n return problem(404, 'not_found', 'Unknown data operation.', requestId);\n }\n if (!Object.hasOwn(operations, name))\n return problem(404, 'not_found', 'Unknown data operation.', requestId);\n if (request.method !== 'POST')\n return problem(405, 'method_not_allowed', 'Use POST.', requestId);\n if (\n request.headers\n .get('content-type')\n ?.split(';')[0]\n ?.trim()\n .toLowerCase() !== 'application/json'\n )\n return problem(\n 415,\n 'unsupported_media_type',\n 'Use application/json.',\n requestId\n );\n const origin = request.headers.get('origin');\n const fetchSite = request.headers.get('sec-fetch-site');\n const fetchMode = request.headers.get('sec-fetch-mode');\n if (\n (origin !== null && origin !== url.origin) ||\n (fetchSite !== null && fetchSite !== 'same-origin') ||\n (fetchSite !== null && origin === null) ||\n (fetchMode !== null &&\n fetchMode !== 'cors' &&\n fetchMode !== 'same-origin')\n )\n return problem(\n 403,\n 'cross_origin_request',\n 'The data request must come from this app.',\n requestId\n );\n let access: RequestAccess;\n try {\n access = await authorize(request, name);\n } catch {\n return problem(\n 403,\n 'forbidden',\n 'You cannot run this data operation.',\n requestId\n );\n }\n const operation = operations[name]!;\n let input: unknown;\n if (request.signal.aborted)\n return problem(504, 'timeout', 'The data request timed out.', requestId);\n try {\n const body = await readInput(request);\n input = JSON.parse(body);\n } catch (error) {\n if (error instanceof InputTooLargeError)\n return problem(\n 413,\n 'input_too_large',\n 'Input is too large.',\n requestId\n );\n\n return problem(\n 400,\n 'invalid_input',\n \"Check this operation's inputs.\",\n requestId\n );\n }\n const signal = request.signal;\n try {\n const { serializedBody } = await executeDataOperation(operation, input, {\n lakehouse: access.lakehouse,\n includeSql: access.canDiscloseSql,\n signal,\n requestId,\n operationName: name,\n });\n return new Response(serializedBody, {\n headers: {\n 'cache-control': 'no-store',\n 'content-type': 'application/json',\n },\n });\n } catch (error) {\n const source = error instanceof DataSourceError ? error : null;\n console.error(\n `Data operation ${name} failed (${requestId}): ${source ? `${source.reason}${source.status ? ` (${source.status})` : ''}${source.queryName ? ` in ${source.queryName}` : ''}` : error instanceof Error ? error.name : 'unknown'}`\n );\n const failure = toDataOperationFailure(error, signal.aborted);\n\n return problem(failure.status, failure.code, failure.message, requestId);\n }\n }\n\n return handleDataRequest;\n}\n\nfunction problem(\n status: number,\n code: string,\n message: string,\n requestId: string\n): Response {\n return Response.json(\n { error: { code, message, requestId } },\n { status, headers: { 'cache-control': 'no-store' } }\n );\n}\n\nconst MAX_INPUT_BYTES = 16_384;\nclass InputTooLargeError extends Error {}\n\n/** Count encoded bytes before retaining chunks; cancellation also interrupts waiting reads. */\nasync function readInput(request: Request): Promise<string> {\n const reader = request.body?.getReader();\n if (!reader) return '';\n const chunks: Uint8Array[] = [];\n let size = 0;\n let abort: (() => void) | undefined;\n\n function cancelReader() {\n // The source's cancel algorithm may never settle; the handler must still respond.\n void reader!.cancel().catch(() => {});\n }\n\n try {\n request.signal.throwIfAborted();\n const cancelled = new Promise<never>((_, reject) => {\n function rejectAborted() {\n reject(new Error('Request aborted.'));\n cancelReader();\n }\n abort = rejectAborted;\n request.signal.addEventListener('abort', rejectAborted, { once: true });\n if (request.signal.aborted) rejectAborted();\n });\n while (true) {\n const { done, value } = await Promise.race([reader.read(), cancelled]);\n if (done) break;\n size += value.byteLength;\n if (size > MAX_INPUT_BYTES) {\n cancelReader();\n throw new InputTooLargeError();\n }\n chunks.push(value);\n }\n const bytes = new Uint8Array(size);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new TextDecoder().decode(bytes);\n } finally {\n if (abort) request.signal.removeEventListener('abort', abort);\n reader.releaseLock();\n }\n}\n"
9
9
  ],
10
10
  "mappings": ";;AAKA;;;ACJO,SAAS,SAAS,CACvB,WACA,SACmB;AAAA,EACnB,IAAI,CAAC;AAAA,IAAW,MAAM,IAAI,MAAM,qBAAqB,SAAS;AAAA;;;ADsQzD,SAAS,gBAA4D,CAC1E,OACO;AAAA,EACP,MAAM,SAAS,OAAO,OAAO,KAAK;AAAA,EAClC,UACE,OAAO,MAAM,UAAQ,CAAC,CAAC,KAAK,KAAK,CAAC,KAChC,IAAI,IAAI,MAAM,EAAE,SAAS,OAAO,QAClC,0CACF;AAAA,EAEA,OAAO;AAAA;AAGT,IAAM,mBAAmB,OAAO,IAAI,sCAAsC;AAAA;AAEnE,MAAM,wBAAwB,MAAM;AAAA,EAYvB;AAAA,EAMA;AAAA,GAjBR,oBAAoB;AAAA,EAC9B;AAAA,UAEQ,OAAO,YAAY,CAAC,OAAyB;AAAA,IACnD,OACE,iBAAiB,SAChB,MAA2C,sBAAsB;AAAA;AAAA,EAItE,WAAW,CACO,QAMA,QAChB;AAAA,IACA,MAAM,eAAe,SAAS,SAAS,KAAK,YAAY,KAAK;AAAA,IAR7C;AAAA,IAMA;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AA8DO,IAAM,uBAAuB,iBAAiB;AAAA,EACnD,YAAY;AACd,CAAC;;;AE7VD,eAAsB,oBAAoB,CACxC,WACA;AAAA,EAEE;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,GAEF;AAAA,EACA,cAAc,eAAe;AAAA,EAC7B,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,QAAQ,UAAU,MAAM,KAAK;AAAA,IAC7B,MAAM;AAAA,IACN,MAAM,IAAI;AAAA;AAAA,EAGZ,MAAM,SAAS,YAAY,IAAI;AAAA,IAC7B;AAAA,IACA,YAAY,QAAQ,UAAU,OAAO,aAAa;AAAA,EACpD,CAAC;AAAA,EACD,MAAM,YAAY,yBAChB,WACA,WACA,QACA,aACF;AAAA,EACA,IAAI;AAAA,IACF,MAAM,SAAS,MAAM,aAAa,WAAW,OAAO;AAAA,MAClD,WAAW,UAAU;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,IACD,MAAM,OAA+B;AAAA,MACnC,MAAM,UAAU,OAAO,MAAM;AAAA,MAC7B;AAAA,MACA,WAAW,IAAI,KAAK,EAAE,YAAY;AAAA,MAClC,UAAU,UAAU;AAAA,SAChB,UAAU,OAAO,aAAa,aAC9B,EAAE,SAAS,UAAU,QAAQ,IAC7B,CAAC;AAAA,IACP;AAAA,IACA,MAAM,iBAAiB,KAAK,UAAU,IAAI;AAAA,IAC1C,MAAM,gBAAgB,IAAI,YAAY,EAAE,OAAO,cAAc,EAAE;AAAA,IAC/D,IAAI,iBAAiB,UAAU,OAAO,oBAAoB;AAAA,MACxD,MAAM,IAAI,MAAM,8BAA8B;AAAA,IAEhD,OAAO,EAAE,MAAM,eAAe;AAAA,IAC9B,OAAO,OAAO;AAAA,IACd,IAAI,OAAO,WAAW,EAAE,iBAAiB;AAAA,MACvC,MAAM,IAAI;AAAA,IACZ,MAAM;AAAA;AAAA;AAKV,SAAS,wBAAwB,CAC/B,WACA,QACA,QACA,eACA;AAAA,EACA,MAAM,WAAqB,CAAC;AAAA,EAC5B,MAAM,UAA4B,CAAC;AAAA,EACnC,MAAM,YAAuB;AAAA,SACrB,SAAQ,CAAC,WAAW,SAAS;AAAA,MACjC,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,KAAK,QAAQ,QAAQ;AAAA,QACtD,MAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD,IACE,UAAU,cACV,CAAC,OAAO,OAAO,UAAU,UAAU,EAAE,SAAS,QAAQ,QAAQ,EAAE;AAAA,QAEhE,MAAM,IAAI,MACR,8CAA8C,gBAChD;AAAA,MACF,MAAM,QAAwB;AAAA,QAC5B,MAAM,QAAQ,QAAQ,SAAS,QAAQ,SAAS;AAAA,QAChD;AAAA,MACF;AAAA,MACA,QAAQ,KAAK,KAAK;AAAA,MAClB,MAAM,QAAQ,KAAK,IAAI,QAAQ,OAAO,UAAU,OAAO,YAAY;AAAA,MACnE,IAAI;AAAA,MACJ,IAAI;AAAA,QACF,SAAS,MAAM,OAAO,SAAS,WAAW;AAAA,UACxC;AAAA,UACA,QAAQ,YAAY,IAAI,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAAA,QAClD,CAAC;AAAA,QACD,OAAO,OAAO;AAAA,QACd,IAAI,iBAAiB;AAAA,UAAiB,MAAM,YAAY,MAAM;AAAA,QAC9D,MAAM;AAAA;AAAA,MAER,IAAI,OAAO,KAAK,SAAS;AAAA,QACvB,MAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C,IAAI,OAAO,SAAS;AAAA,QAClB,MAAM,UAAU,OAAO;AAAA,QACvB,SAAS,KAAK,OAAO,OAAO;AAAA,MAC9B;AAAA,MAEA,OAAO;AAAA;AAAA,EAEX;AAAA,EAEA,OAAO,EAAE,WAAW,SAAS,SAAS;AAAA;AAIxC,eAAe,YAAY,CACzB,WACA,SACE,WAAW,UACb;AAAA,EACA,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,OAAO,eAAe;AAAA,IACtB,MAAM,YAAY,IAAI,QAAe,CAAC,GAAG,WAAW;AAAA,MAClD,SAAS,aAAa,GAAG;AAAA,QACvB,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA;AAAA,MAEtC,QAAQ;AAAA,MACR,OAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AAAA,MAC9D,IAAI,OAAO;AAAA,QAAS,cAAc;AAAA,KACnC;AAAA,IAED,OAAO,MAAM,QAAQ,KAAK;AAAA,MACxB;AAAA,MACA,QAAQ,QAAQ,EAAE,KAAK,MAAM;AAAA,QAC3B,OAAO,eAAe;AAAA,QAEtB,OAAO,UAAU,IAAI,EAAE,WAAW,OAAO,GAAG,KAAc;AAAA,OAC3D;AAAA,IACH,CAAC;AAAA,YACD;AAAA,IACA,IAAI;AAAA,MAAO,OAAO,oBAAoB,SAAS,KAAK;AAAA;AAAA;AAAA;AAIxD,MAAM,8BAA8B,MAAM;AAAC;AAAA;AAE3C,MAAM,4BAA4B,MAAM;AAAC;AAEzC,IAAM,sBAAsB;AAAA,EAC1B,cACE;AAAA,EACF,WACE;AAAA,EACF,cAAc;AAAA,EACd,gBACE;AAAA,EACF,aAAa;AACf;AAEO,SAAS,sBAAsB,CAAC,OAAgB,UAAU,OAAO;AAAA,EACtE,IAAI,iBAAiB;AAAA,IACnB,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,EACF,IAAI,iBAAiB;AAAA,IACnB,OAAO;AAAA,MACL,QAAQ,MAAM,WAAW,iBAAiB,MAAM;AAAA,MAChD,MAAM,UAAU,MAAM;AAAA,MACtB,SAAS,oBAAoB,MAAM;AAAA,IACrC;AAAA,EACF,MAAM,WAAW,WAAW,iBAAiB;AAAA,EAC7C,OAAO;AAAA,IACL,QAAQ,WAAW,MAAM;AAAA,IACzB,MAAM,WAAW,YAAY;AAAA,IAC7B,SAAS,WACL,gCACA;AAAA,EACN;AAAA;;;AC7KK,SAAS,iBAAiB,CAC/B,YACA,WACA;AAAA,EACA,eAAe,iBAAiB,CAAC,SAAqC;AAAA,IACpE,MAAM,YAAY,OAAO,WAAW;AAAA,IACpC,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAAA,IAC/B,IAAI,CAAC,IAAI,SAAS,WAAW,YAAY;AAAA,MACvC,OAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,IAClD,MAAM,cAAc,IAAI,SAAS,MAAM,aAAa,MAAM;AAAA,IAC1D,IAAI,CAAC,eAAe,YAAY,SAAS,GAAG;AAAA,MAC1C,OAAO,QAAQ,KAAK,aAAa,2BAA2B,SAAS;AAAA,IACvE,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,OAAO,mBAAmB,WAAW;AAAA,MACrC,MAAM;AAAA,MACN,OAAO,QAAQ,KAAK,aAAa,2BAA2B,SAAS;AAAA;AAAA,IAEvE,IAAI,CAAC,OAAO,OAAO,YAAY,IAAI;AAAA,MACjC,OAAO,QAAQ,KAAK,aAAa,2BAA2B,SAAS;AAAA,IACvE,IAAI,QAAQ,WAAW;AAAA,MACrB,OAAO,QAAQ,KAAK,sBAAsB,aAAa,SAAS;AAAA,IAClE,IACE,QAAQ,QACL,IAAI,cAAc,GACjB,MAAM,GAAG,EAAE,IACX,KAAK,EACN,YAAY,MAAM;AAAA,MAErB,OAAO,QACL,KACA,0BACA,yBACA,SACF;AAAA,IACF,MAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ;AAAA,IAC3C,MAAM,YAAY,QAAQ,QAAQ,IAAI,gBAAgB;AAAA,IACtD,MAAM,YAAY,QAAQ,QAAQ,IAAI,gBAAgB;AAAA,IACtD,IACG,WAAW,QAAQ,WAAW,IAAI,UAClC,cAAc,QAAQ,cAAc,iBACpC,cAAc,QAAQ,WAAW,QACjC,cAAc,QACb,cAAc,UACd,cAAc;AAAA,MAEhB,OAAO,QACL,KACA,wBACA,6CACA,SACF;AAAA,IACF,IAAI;AAAA,IACJ,IAAI;AAAA,MACF,SAAS,MAAM,UAAU,SAAS,IAAI;AAAA,MACtC,MAAM;AAAA,MACN,OAAO,QACL,KACA,aACA,uCACA,SACF;AAAA;AAAA,IAEF,MAAM,YAAY,WAAW;AAAA,IAC7B,IAAI;AAAA,IACJ,IAAI,QAAQ,OAAO;AAAA,MACjB,OAAO,QAAQ,KAAK,WAAW,+BAA+B,SAAS;AAAA,IACzE,IAAI;AAAA,MACF,MAAM,OAAO,MAAM,UAAU,OAAO;AAAA,MACpC,QAAQ,KAAK,MAAM,IAAI;AAAA,MACvB,OAAO,OAAO;AAAA,MACd,IAAI,iBAAiB;AAAA,QACnB,OAAO,QACL,KACA,mBACA,uBACA,SACF;AAAA,MAEF,OAAO,QACL,KACA,iBACA,kCACA,SACF;AAAA;AAAA,IAEF,MAAM,SAAS,QAAQ;AAAA,IACvB,IAAI;AAAA,MACF,QAAQ,mBAAmB,MAAM,qBAAqB,WAAW,OAAO;AAAA,QACtE,WAAW,OAAO;AAAA,QAClB,YAAY,OAAO;AAAA,QACnB;AAAA,QACA;AAAA,QACA,eAAe;AAAA,MACjB,CAAC;AAAA,MACD,OAAO,IAAI,SAAS,gBAAgB;AAAA,QAClC,SAAS;AAAA,UACP,iBAAiB;AAAA,UACjB,gBAAgB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,MACD,OAAO,OAAO;AAAA,MACd,MAAM,SAAS,iBAAiB,kBAAkB,QAAQ;AAAA,MAC1D,QAAQ,MACN,kBAAkB,gBAAgB,eAAe,SAAS,GAAG,OAAO,SAAS,OAAO,SAAS,KAAK,OAAO,YAAY,KAAK,OAAO,YAAY,OAAO,OAAO,cAAc,OAAO,iBAAiB,QAAQ,MAAM,OAAO,WACxN;AAAA,MACA,MAAM,UAAU,uBAAuB,OAAO,OAAO,OAAO;AAAA,MAE5D,OAAO,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,SAAS,SAAS;AAAA;AAAA;AAAA,EAI3E,OAAO;AAAA;AAGT,SAAS,OAAO,CACd,QACA,MACA,SACA,WACU;AAAA,EACV,OAAO,SAAS,KACd,EAAE,OAAO,EAAE,MAAM,SAAS,UAAU,EAAE,GACtC,EAAE,QAAQ,SAAS,EAAE,iBAAiB,WAAW,EAAE,CACrD;AAAA;AAGF,IAAM,kBAAkB;AAAA;AACxB,MAAM,2BAA2B,MAAM;AAAC;AAGxC,eAAe,SAAS,CAAC,SAAmC;AAAA,EAC1D,MAAM,SAAS,QAAQ,MAAM,UAAU;AAAA,EACvC,IAAI,CAAC;AAAA,IAAQ,OAAO;AAAA,EACpB,MAAM,SAAuB,CAAC;AAAA,EAC9B,IAAI,OAAO;AAAA,EACX,IAAI;AAAA,EAEJ,SAAS,YAAY,GAAG;AAAA,IAEjB,OAAQ,OAAO,EAAE,MAAM,MAAM,EAAE;AAAA;AAAA,EAGtC,IAAI;AAAA,IACF,QAAQ,OAAO,eAAe;AAAA,IAC9B,MAAM,YAAY,IAAI,QAAe,CAAC,GAAG,WAAW;AAAA,MAClD,SAAS,aAAa,GAAG;AAAA,QACvB,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,QACpC,aAAa;AAAA;AAAA,MAEf,QAAQ;AAAA,MACR,QAAQ,OAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;AAAA,MACtE,IAAI,QAAQ,OAAO;AAAA,QAAS,cAAc;AAAA,KAC3C;AAAA,IACD,OAAO,MAAM;AAAA,MACX,QAAQ,MAAM,UAAU,MAAM,QAAQ,KAAK,CAAC,OAAO,KAAK,GAAG,SAAS,CAAC;AAAA,MACrE,IAAI;AAAA,QAAM;AAAA,MACV,QAAQ,MAAM;AAAA,MACd,IAAI,OAAO,iBAAiB;AAAA,QAC1B,aAAa;AAAA,QACb,MAAM,IAAI;AAAA,MACZ;AAAA,MACA,OAAO,KAAK,KAAK;AAAA,IACnB;AAAA,IACA,MAAM,QAAQ,IAAI,WAAW,IAAI;AAAA,IACjC,IAAI,SAAS;AAAA,IACb,WAAW,SAAS,QAAQ;AAAA,MAC1B,MAAM,IAAI,OAAO,MAAM;AAAA,MACvB,UAAU,MAAM;AAAA,IAClB;AAAA,IAEA,OAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,YACrC;AAAA,IACA,IAAI;AAAA,MAAO,QAAQ,OAAO,oBAAoB,SAAS,KAAK;AAAA,IAC5D,OAAO,YAAY;AAAA;AAAA;",
11
- "debugId": "CBFE1DD1426CC5FA64756E2164756E21",
11
+ "debugId": "97D671C00924C2DF64756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -0,0 +1,23 @@
1
+ import { type DataAppAnnotationDraft } from '../core/annotations.js';
2
+ import type { MessageTransport } from '../core/messages.js';
3
+ /** Deliver annotations to the authenticated host. Acknowledgement means retained, not sent to an agent. */
4
+ export declare function createAnnotationClient(transport: MessageTransport): {
5
+ /** Report the iframe editor's status so the host can guard batch submission. */
6
+ reportAnnotationEditorState(state: {
7
+ hasUnsavedChanges: boolean;
8
+ }, options?: {
9
+ signal?: AbortSignal;
10
+ }): Promise<null>;
11
+ addAnnotation(draft: DataAppAnnotationDraft, options?: {
12
+ signal?: AbortSignal;
13
+ }): Promise<null>;
14
+ updateAnnotation(id: string, comment: string, options?: {
15
+ signal?: AbortSignal;
16
+ }): Promise<null>;
17
+ /** Ask the host to change its controlled annotation mode. */
18
+ requestAnnotationModeChange(request: {
19
+ active: boolean;
20
+ }, options?: {
21
+ signal?: AbortSignal;
22
+ }): Promise<null>;
23
+ };
@@ -8,6 +8,7 @@ export declare function createIframeTransport({ parentOrigin, window: frame, tim
8
8
  mode?: 'url' | 'bundle';
9
9
  loadScript?: (javascript: string) => void;
10
10
  }): {
11
+ logger: import(".").DataAppLogger;
11
12
  request: (message: RoutedMessage, signal?: AbortSignal) => Promise<unknown>;
12
13
  transport: (operation: string, input: unknown, signal?: AbortSignal) => Promise<TransportResponse>;
13
14
  lakehouse: {
@@ -34,6 +35,7 @@ export declare function localFrameBridge(window?: Window): LocalBridge | undefin
34
35
  export declare function installDataAppTransport(bridge: ReturnType<typeof createIframeTransport>, frame?: Window): () => void;
35
36
  /** The bootstrap installs this before executing app code. URL apps can install an explicit connection. */
36
37
  export declare function getDataAppTransport(frame?: Window): {
38
+ logger: import(".").DataAppLogger;
37
39
  request: (message: RoutedMessage, signal?: AbortSignal) => Promise<unknown>;
38
40
  transport: (operation: string, input: unknown, signal?: AbortSignal) => Promise<TransportResponse>;
39
41
  lakehouse: {
@@ -10,3 +10,5 @@ export { createDataAppNavigation, getDataAppNavigation, type DataAppNavigation,
10
10
  export { createHttpTransport, DataAppError, type DataTransport, } from './transport.js';
11
11
  export { createIframeTransport, installDataAppTransport, getDataAppTransport, type IframeTransport, } from './iframe.js';
12
12
  export { createMessageClient } from './messages.js';
13
+ export type { DataAppLogger } from '../core/logger.js';
14
+ export { createAnnotationClient } from './annotations.js';
@@ -0,0 +1,6 @@
1
+ import type { BridgeMessage } from '../core/bridge.js';
2
+ import { type DataAppLogger, type LogEntry } from '../core/logger.js';
3
+ /** Lazy arguments run in the iframe; only resolved values cross the bridge. */
4
+ export declare function createBridgeLogger(enabled: () => boolean, send: (entry: LogEntry) => void): DataAppLogger;
5
+ /** Log only delivery metadata; payloads and session credentials stay private. */
6
+ export declare function logBridgeMessage(logger: DataAppLogger, message: BridgeMessage): void;
@@ -0,0 +1,92 @@
1
+ export type DataAppAnnotationDraft = {
2
+ id: string;
3
+ target: {
4
+ id: string;
5
+ label: string;
6
+ kind: 'widget' | 'element' | 'app';
7
+ text: string;
8
+ queryNames: string[];
9
+ glossaryIds: string[];
10
+ };
11
+ context: {
12
+ search: string;
13
+ hash: string;
14
+ displayedInput?: unknown;
15
+ /** Normalized position within the selected target, stable across scrolling/resizing. */
16
+ anchor?: {
17
+ x: number;
18
+ y: number;
19
+ };
20
+ cursor?: {
21
+ x: number;
22
+ y: number;
23
+ };
24
+ /** Normalized custom selection rectangle within the iframe document. */
25
+ region?: {
26
+ x: number;
27
+ y: number;
28
+ width: number;
29
+ height: number;
30
+ };
31
+ screenshot?: {
32
+ mimeType: 'image/png';
33
+ dataUrl: string;
34
+ width: number;
35
+ height: number;
36
+ };
37
+ view?: string;
38
+ viewport: {
39
+ width: number;
40
+ height: number;
41
+ };
42
+ rect: {
43
+ x: number;
44
+ y: number;
45
+ width: number;
46
+ height: number;
47
+ };
48
+ };
49
+ comment: string;
50
+ };
51
+ export type DataAppAnnotationPresentation = {
52
+ enabled: boolean;
53
+ /** When provided, the host owns the mode and renders the annotation trigger. */
54
+ active?: boolean;
55
+ pinsVisible?: boolean;
56
+ showHint?: boolean;
57
+ readOnly?: boolean;
58
+ targets?: {
59
+ id: string;
60
+ targetId: string;
61
+ number: number;
62
+ comment?: string;
63
+ anchor?: {
64
+ x: number;
65
+ y: number;
66
+ };
67
+ region?: {
68
+ x: number;
69
+ y: number;
70
+ width: number;
71
+ height: number;
72
+ };
73
+ }[];
74
+ selectedAnnotationId?: string;
75
+ selectedTargetId?: string;
76
+ selectionId?: string;
77
+ };
78
+ /** Reconstruct at the boundary; discard extra fields and bound the entire JSON payload. */
79
+ export declare function parseDataAppAnnotationDraft(value: unknown): DataAppAnnotationDraft;
80
+ export declare const annotationDraftRoute: import("./messages.js").MessageRoute<DataAppAnnotationDraft, null>;
81
+ export declare const annotationEditorStateRoute: import("./messages.js").MessageRoute<{
82
+ hasUnsavedChanges: boolean;
83
+ }, null>;
84
+ export declare const annotationUpdateRoute: import("./messages.js").MessageRoute<{
85
+ id: string;
86
+ comment: string;
87
+ }, null>;
88
+ /** A controlled shell acknowledges local exits (for example Escape). */
89
+ export declare const annotationModeRoute: import("./messages.js").MessageRoute<{
90
+ active: boolean;
91
+ }, null>;
92
+ export declare function isAnnotationPresentation(value: unknown): value is DataAppAnnotationPresentation;
@@ -32,8 +32,8 @@ export declare function parseAppearance(value: unknown): AppearanceSettings;
32
32
  /** Resolve typed app settings without repeating authoring checks at runtime. */
33
33
  export declare function normalizeAppearance(value?: AppearanceOptions): AppearanceSettings;
34
34
  /**
35
- * Install semantic tokens on the document root, including portaled UI. Returns a cleanup for
36
- * system-theme listening.
35
+ * Select document-owned appearance presets and brand inputs. Public CSS tokens remain
36
+ * overridable in stylesheets. Cleanup restores the previous document appearance.
37
37
  */
38
38
  export declare function applyAppearance(value?: AppearanceOptions): () => void;
39
39
  /** Persist a viewer theme preference. Applying appearance belongs to the UI owner. */
@@ -0,0 +1,21 @@
1
+ import { type BridgeMessage, type BridgeRole, type BridgeEventsFrom, type BridgePayload, type BridgeEnvelope, type BridgeHandlers } from './bridge.js';
2
+ type ConnectionContext = Pick<BridgeEnvelope, 'sessionId' | 'token'> & {
3
+ documentId?: string;
4
+ };
5
+ /** The connection owns IDs and token rotation; callers can only supply event payloads. */
6
+ export declare function createBridgeEndpoint<const Role extends BridgeRole>({ role, origin, source, opaque, context, post, diagnostic, invalidRequest, handlers, }: {
7
+ role: Role;
8
+ handlers: BridgeHandlers<NoInfer<Role>>;
9
+ origin: string;
10
+ source: () => MessageEventSource | null;
11
+ opaque: boolean;
12
+ context: () => ConnectionContext;
13
+ post: (message: BridgeMessage) => void;
14
+ diagnostic?: (direction: 'send' | 'receive', type: BridgeMessage['type']) => void;
15
+ invalidRequest?: (id: string) => void;
16
+ }): {
17
+ send: <Name extends BridgeEventsFrom<Role>, const Payload extends BridgePayload<NoInfer<Name>>>(name: Name, payload: Payload & Record<Exclude<keyof Payload, keyof BridgePayload<NoInfer<Name>>>, never>) => void;
18
+ receive: (event: MessageEvent) => void;
19
+ dispose(): void;
20
+ };
21
+ export {};
@@ -6,24 +6,145 @@ export declare const PARENT_PARAM = "__altertable_parent";
6
6
  export declare const MAX_PENDING = 128;
7
7
  export declare const REQUEST_TIMEOUT_MS = 60000;
8
8
  export type { TransportResponse } from './operation-types.js';
9
- export type BridgeMessage = {
9
+ export declare function validId(value: unknown): value is string;
10
+ type Fields = Record<string, unknown>;
11
+ export type BridgeRole = 'host' | 'app';
12
+ /** Parsers project known fields; opaque values are validated by their owning route or adapter. */
13
+ export declare const bridgeProtocol: {
14
+ readonly channel: "altertable:data-app";
15
+ readonly version: 1;
16
+ readonly events: {
17
+ readonly connect: {
18
+ wire: "bridge:connect";
19
+ from: "host";
20
+ session: "none";
21
+ parse: (fields: Fields) => {};
22
+ };
23
+ readonly ready: {
24
+ wire: "bridge:ready";
25
+ from: "app";
26
+ session: "none";
27
+ parse: (fields: Fields) => {};
28
+ };
29
+ readonly initialize: {
30
+ wire: "bridge:initialize";
31
+ from: "host";
32
+ session: "establish";
33
+ parse: (fields: Fields) => {
34
+ state: unknown;
35
+ };
36
+ };
37
+ readonly request: {
38
+ wire: "bridge:request";
39
+ from: "app";
40
+ session: "current";
41
+ parse: (fields: Fields) => {
42
+ id: string;
43
+ route: string;
44
+ payload: unknown;
45
+ };
46
+ };
47
+ readonly result: {
48
+ wire: "bridge:result";
49
+ from: "host";
50
+ session: "current";
51
+ parse: (fields: Fields) => {
52
+ id: string;
53
+ response: unknown;
54
+ };
55
+ };
56
+ readonly error: {
57
+ wire: "bridge:error";
58
+ from: "host";
59
+ session: "current";
60
+ parse: (fields: Fields) => {
61
+ id: string;
62
+ code: string;
63
+ message: string;
64
+ requestId?: string | undefined;
65
+ };
66
+ };
67
+ readonly cancel: {
68
+ wire: "bridge:cancel";
69
+ from: "app";
70
+ session: "current";
71
+ parse: (fields: Fields) => {
72
+ id: string;
73
+ };
74
+ };
75
+ readonly disconnect: {
76
+ wire: "bridge:disconnect";
77
+ from: "app";
78
+ session: "current";
79
+ parse: (fields: Fields) => {};
80
+ };
81
+ readonly scriptLoad: {
82
+ wire: "script:load";
83
+ from: "host";
84
+ session: "current";
85
+ parse: (fields: Fields) => {
86
+ javascript: string;
87
+ };
88
+ };
89
+ readonly stateUpdate: {
90
+ wire: "state:update";
91
+ from: "host";
92
+ session: "current";
93
+ parse: (fields: Fields) => {
94
+ state: unknown;
95
+ };
96
+ };
97
+ readonly runtimeLog: {
98
+ wire: "runtime:log";
99
+ from: "app";
100
+ session: "current";
101
+ parse: (fields: Fields) => {
102
+ payload: import("./logger.js").LogEntry;
103
+ };
104
+ };
105
+ readonly runtimeReady: {
106
+ wire: "runtime:ready";
107
+ from: "app";
108
+ session: "current";
109
+ parse: (fields: Fields) => {};
110
+ };
111
+ readonly runtimeError: {
112
+ wire: "runtime:error";
113
+ from: "app";
114
+ session: "current";
115
+ parse: (fields: Fields) => {};
116
+ };
117
+ };
118
+ };
119
+ type Events = typeof bridgeProtocol.events;
120
+ export type BridgeEventName = keyof Events;
121
+ export type BridgePayload<Name extends BridgeEventName> = ReturnType<Events[Name]['parse']>;
122
+ export type BridgeEnvelope = {
10
123
  channel: typeof BRIDGE;
11
- version: 1;
12
- type: string;
124
+ version: typeof bridgeProtocol.version;
13
125
  documentId: string;
14
126
  sessionId?: string;
15
- id?: string;
16
- route?: string;
17
- payload?: unknown;
18
- response?: unknown;
19
- code?: string;
20
- message?: string;
21
- /** Opaque host context, interpreted by optional app adapters. */
22
- state?: unknown;
23
- requestId?: string;
24
127
  token?: string;
25
- javascript?: string;
26
128
  };
27
- export declare function isBridgeMessage(value: unknown): value is BridgeMessage;
28
- export declare function validId(value: unknown): value is string;
29
- export { validLocation } from './navigation.js';
129
+ export type BridgeEventMessage<Name extends BridgeEventName> = Name extends BridgeEventName ? BridgeEnvelope & {
130
+ type: Events[Name]['wire'];
131
+ } & (Events[Name]['session'] extends 'none' ? {} : {
132
+ sessionId: string;
133
+ }) & BridgePayload<Name> : never;
134
+ export type BridgeMessage = {
135
+ [Name in BridgeEventName]: BridgeEventMessage<Name>;
136
+ }[BridgeEventName];
137
+ export type BridgeEventsFrom<Role extends BridgeRole> = {
138
+ [Name in BridgeEventName]: Events[Name]['from'] extends Role ? Name : never;
139
+ }[BridgeEventName];
140
+ export type BridgeHandlers<Role extends BridgeRole> = {
141
+ [Name in BridgeEventsFrom<Role extends 'host' ? 'app' : 'host'>]: (message: BridgeEventMessage<Name>) => void;
142
+ };
143
+ export declare function bridgeEventName(wire: string): "cancel" | "connect" | "disconnect" | "error" | "initialize" | "ready" | "request" | "result" | "runtimeError" | "runtimeLog" | "runtimeReady" | "scriptLoad" | "stateUpdate" | undefined;
144
+ /** Envelope parsing precedes authentication; event-specific fields remain untrusted. */
145
+ export declare function parseBridgeEnvelope(value: unknown): (BridgeEnvelope & Fields & {
146
+ type: string;
147
+ }) | undefined;
148
+ /** Project event fields after the caller has checked envelope eligibility. */
149
+ export declare function parseBridgePayload(envelope: NonNullable<ReturnType<typeof parseBridgeEnvelope>>, definition: Events[BridgeEventName]): BridgeMessage | undefined;
150
+ export declare function parseBridgeMessage(value: unknown): BridgeMessage | undefined;
@@ -90,3 +90,5 @@ export type { DimensionSelection, DimensionMember, DimensionOption, DimensionVar
90
90
  export { createMessageRouter, defineMessageRoute, defineDataQueryRoute, MessageRoutingError, dataAppRoutes, navigationUpdateRoute, sqlQueryRoute, } from './messages.js';
91
91
  export type { MessageContext, RoutedMessage, MessageDispatcher, MessageTransport, MessageRoute, MessageRoutes, MessageInput, MessageHandlers, MessageOutput, OperationContracts, DataQueryInput, DataQueryBody, DataQueryRoute, NavigationUpdate, SqlQueryInput, } from './messages.js';
92
92
  export type { TransportResponse } from './bridge.js';
93
+ export { annotationDraftRoute, annotationModeRoute, annotationEditorStateRoute, annotationUpdateRoute, parseDataAppAnnotationDraft, } from './annotations.js';
94
+ export type { DataAppAnnotationDraft, DataAppAnnotationPresentation, } from './annotations.js';