@altertable/data-app 0.65.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.
- package/AGENTS.md +38 -1
- package/CONTRIBUTING.md +37 -3
- package/README.md +1 -1
- package/dist/chunks/{contract-sm7j7k95.js → contract-13j5zb3c.js} +2 -144
- package/dist/chunks/contract-13j5zb3c.js.map +13 -0
- package/dist/chunks/contract-19ckn3n7.js +78 -0
- package/dist/chunks/contract-19ckn3n7.js.map +10 -0
- package/dist/chunks/{contract-a061yj6r.js → contract-e11m4y7f.js} +34 -70
- package/dist/chunks/contract-e11m4y7f.js.map +12 -0
- package/dist/chunks/{contract-g6hky7x6.js → contract-h8a6f559.js} +8 -4
- package/dist/chunks/contract-h8a6f559.js.map +12 -0
- package/dist/chunks/contract-havnjmdr.js +13011 -0
- package/dist/chunks/contract-havnjmdr.js.map +99 -0
- package/dist/chunks/{contract-txjy0en2.js → contract-m6n8ctfc.js} +64 -72
- package/dist/chunks/contract-m6n8ctfc.js.map +10 -0
- package/dist/chunks/contract-pk603qj7.js +146 -0
- package/dist/chunks/contract-pk603qj7.js.map +10 -0
- package/dist/chunks/{contract-yxbjea23.js → contract-rfbakka4.js} +179 -2
- package/dist/chunks/contract-rfbakka4.js.map +13 -0
- package/dist/client/index.js +11 -6
- package/dist/client/index.js.map +1 -1
- package/dist/core/appearance.js +1 -1
- package/dist/core/contract.js +13 -3
- package/dist/core/contract.js.map +1 -1
- package/dist/embed/index.js +9 -7
- package/dist/embed/index.js.map +2 -2
- package/dist/local.js +1 -1
- package/dist/local.js.map +2 -2
- package/dist/react/embed/index.js +1 -1
- package/dist/react/index.js +2532 -11545
- package/dist/react/index.js.map +16 -91
- package/dist/react/ui/index.js +1614 -0
- package/dist/react/ui/index.js.map +22 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +2 -2
- package/dist/types/client/annotations.d.ts +23 -0
- package/dist/types/client/index.d.ts +1 -0
- package/dist/types/core/annotations.d.ts +92 -0
- package/dist/types/core/appearance.d.ts +2 -2
- package/dist/types/core/contract.d.ts +2 -0
- package/dist/types/core/presentation.d.ts +2 -0
- package/dist/types/core/variables.d.ts +2 -5
- package/dist/types/react/annotations/AnnotationBar.d.ts +24 -0
- package/dist/types/react/annotations/AnnotationControls.d.ts +10 -0
- package/dist/types/react/annotations/AnnotationEditor.d.ts +15 -0
- package/dist/types/react/annotations/AnnotationMarkers.d.ts +19 -0
- package/dist/types/react/annotations/AnnotationSelectionLayer.d.ts +12 -0
- package/dist/types/react/annotations/AnnotationTarget.d.ts +9 -0
- package/dist/types/react/annotations/AnnotationTooltip.d.ts +9 -0
- package/dist/types/react/annotations/AnnotationTrigger.d.ts +10 -0
- package/dist/types/react/annotations/annotation-editor-state.d.ts +56 -0
- package/dist/types/react/annotations/annotation-screenshot.d.ts +4 -0
- package/dist/types/react/annotations/annotation-targets.d.ts +33 -0
- package/dist/types/react/annotations/getAnnotationProps.d.ts +15 -0
- package/dist/types/react/annotations/styles.d.ts +3 -0
- package/dist/types/react/annotations/useAnnotationGeometry.d.ts +22 -0
- package/dist/types/react/annotations/useDataAppAnnotations.d.ts +19 -0
- package/dist/types/react/bindings.d.ts +110 -0
- package/dist/types/react/content.d.ts +24 -11
- package/dist/types/react/hooks.d.ts +29 -787
- package/dist/types/react/index.d.ts +30 -105
- package/dist/types/react/source-owner.d.ts +2 -0
- package/dist/types/react/style-contract.d.ts +60 -0
- package/dist/types/react/style-validation.d.ts +2 -0
- package/dist/types/react/ui/AboutData.d.ts +9 -1
- package/dist/types/react/ui/AppHeader.d.ts +1 -2
- package/dist/types/react/ui/AppLayout.d.ts +3 -9
- package/dist/types/react/ui/AppToolbar.d.ts +5 -8
- package/dist/types/react/ui/AreaChart.d.ts +7 -0
- package/dist/types/react/ui/BarChart.d.ts +6 -0
- package/dist/types/react/ui/{ComparisonVisual.d.ts → Comparison.d.ts} +3 -3
- package/dist/types/react/ui/ContentSkeleton.d.ts +2 -0
- package/dist/types/react/ui/DataApp.d.ts +17 -58
- package/dist/types/react/ui/DataAppFrame.d.ts +42 -0
- package/dist/types/react/ui/DataBoundary.d.ts +4 -5
- package/dist/types/react/ui/DataSection.d.ts +6 -18
- package/dist/types/react/ui/DataSectionBoundary.d.ts +30 -0
- package/dist/types/react/ui/DataValue.d.ts +9 -0
- package/dist/types/react/ui/DataWidget.d.ts +2 -1
- package/dist/types/react/ui/DateTimeTooltip.d.ts +1 -1
- package/dist/types/react/ui/ExportControl.d.ts +1 -1
- package/dist/types/react/ui/HelpPopover.d.ts +3 -4
- package/dist/types/react/ui/InspectionContext.d.ts +2 -0
- package/dist/types/react/ui/InspectionProvider.d.ts +29 -0
- package/dist/types/react/ui/LineChart.d.ts +7 -0
- package/dist/types/react/ui/MetricWidget.d.ts +4 -3
- package/dist/types/react/ui/PieChart.d.ts +8 -0
- package/dist/types/react/ui/PresentStory.d.ts +2 -5
- package/dist/types/react/ui/RefreshControl.d.ts +1 -2
- package/dist/types/react/ui/ScatterChart.d.ts +17 -0
- package/dist/types/react/ui/SearchField.d.ts +1 -2
- package/dist/types/react/ui/SearchInput.d.ts +3 -1
- package/dist/types/react/ui/Sheet.d.ts +2 -1
- package/dist/types/react/ui/Skeleton.d.ts +5 -2
- package/dist/types/react/ui/StaticDataApp.d.ts +4 -0
- package/dist/types/react/ui/TableWidget.d.ts +19 -16
- package/dist/types/react/ui/Tabs.d.ts +6 -2
- package/dist/types/react/ui/TextWidget.d.ts +2 -1
- package/dist/types/react/ui/Tooltip.d.ts +4 -3
- package/dist/types/react/ui/TrendChart.d.ts +5 -0
- package/dist/types/react/ui/UpdatedAt.d.ts +1 -1
- package/dist/types/react/ui/VisualizationWidget.d.ts +12 -13
- package/dist/types/react/ui/WidgetViewTabs.d.ts +1 -1
- package/dist/types/react/ui/chart-data.d.ts +25 -0
- package/dist/types/react/ui/data-context.d.ts +18 -7
- package/dist/types/react/ui/icons.d.ts +1 -0
- package/dist/types/react/ui/index.d.ts +74 -0
- package/dist/types/react/ui/metric.d.ts +1 -1
- package/dist/types/react/ui/presentation.d.ts +5 -1
- package/dist/types/react/ui/shortcuts.d.ts +7 -1
- package/dist/types/react/view-runtime.d.ts +29 -0
- package/dist/types/react/view.d.ts +21 -4
- package/dist/types/react/widgets.d.ts +79 -0
- package/docs/app-authoring.md +58 -50
- package/docs/data-context.md +89 -0
- package/docs/formatting-and-appearance.md +52 -0
- package/docs/hosted-apps.md +0 -13
- package/docs/layout.md +4 -2
- package/docs/react-embed.md +2 -2
- package/docs/react.md +20 -316
- package/docs/stories-and-export.md +52 -0
- package/docs/styling.md +55 -0
- package/docs/ui-quality.md +20 -0
- package/docs/ui.md +41 -0
- package/docs/variables.md +98 -0
- package/docs/views.md +127 -0
- package/docs/widgets.md +142 -0
- package/examples/starter-data-app/index.tsx +82 -55
- package/package.json +12 -4
- package/dist/chunks/contract-a061yj6r.js.map +0 -12
- package/dist/chunks/contract-g6hky7x6.js.map +0 -12
- package/dist/chunks/contract-sm7j7k95.js.map +0 -14
- package/dist/chunks/contract-txjy0en2.js.map +0 -10
- package/dist/chunks/contract-yxbjea23.js.map +0 -12
- package/dist/types/react/ui/RefreshRegion.d.ts +0 -9
- package/dist/types/react/ui/SelectableBarChart.d.ts +0 -15
- package/docs/releasing.md +0 -79
- /package/dist/types/react/ui/{comparison.d.ts → metric-comparison.d.ts} +0 -0
package/docs/views.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Views and displayed results
|
|
2
|
+
|
|
3
|
+
## Bind a view
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { createDataClient } from '@altertable/data-app/client';
|
|
7
|
+
import {
|
|
8
|
+
createDataHooks,
|
|
9
|
+
DataApp,
|
|
10
|
+
DataSection,
|
|
11
|
+
} from '@altertable/data-app/react';
|
|
12
|
+
import type { operations } from '#app/operations.ts';
|
|
13
|
+
|
|
14
|
+
const client = createDataClient<typeof operations>();
|
|
15
|
+
const { defineDataView } = createDataHooks(client);
|
|
16
|
+
const activityView = defineDataView({
|
|
17
|
+
dataContext,
|
|
18
|
+
operation: 'activity',
|
|
19
|
+
describeInput: () => 'all activity',
|
|
20
|
+
isEmpty: data => data.rows.length === 0,
|
|
21
|
+
emptyFallback: { title: 'No activity' },
|
|
22
|
+
});
|
|
23
|
+
const content = activityView.content(result => (
|
|
24
|
+
<ActivityWidgets source={result} />
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
return (
|
|
29
|
+
<DataApp
|
|
30
|
+
config={config}
|
|
31
|
+
view={activityView}
|
|
32
|
+
story={story}
|
|
33
|
+
datasets={[activityDataset]}
|
|
34
|
+
>
|
|
35
|
+
<DataSection content={content} />
|
|
36
|
+
</DataApp>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Omit `variables` when there are no controls. Omit `input` when the resolved
|
|
42
|
+
variables match the operation input; nested or different inputs need a mapper.
|
|
43
|
+
The operation and `ActivityWidgets` are app-owned. The bound widgets derive
|
|
44
|
+
loading content from their source; see [widgets](widgets.md) and the
|
|
45
|
+
[complete starter](../examples/starter-data-app/index.tsx). Define
|
|
46
|
+
[filters](variables.md), [context and evidence](data-context.md), and
|
|
47
|
+
[story and datasets](stories-and-export.md) for the app's question.
|
|
48
|
+
|
|
49
|
+
Render static text immediately; skeletonize only dynamic content. Keep section
|
|
50
|
+
introductions outside request boundaries so they remain visible on empty and
|
|
51
|
+
error states.
|
|
52
|
+
|
|
53
|
+
Use `<DataSection>` for each independently fetched subtree and `view.content()`
|
|
54
|
+
to share its loading and ready layout. Refreshes retain displayed content.
|
|
55
|
+
|
|
56
|
+
Use `<DataValue>` for a dynamic value within static prose:
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<p>
|
|
60
|
+
Orders in the last 7 days: <DataValue metric={weeklyOrders} source={result} />
|
|
61
|
+
</p>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Bind the whole sentence with `<TextWidget>` when its wording depends on the result.
|
|
65
|
+
Use `<DataValue scope={result.scope} />` for the displayed scope label.
|
|
66
|
+
|
|
67
|
+
Use the same [date range contract](contract.md#shared-date-ranges) for the
|
|
68
|
+
operation and its view. For nested inputs, bind the range with
|
|
69
|
+
`input: input => input.period`.
|
|
70
|
+
|
|
71
|
+
Use `result.scope` inside `view.content()` as a reading for scope text. In stories and exports,
|
|
72
|
+
`view.scope(snapshot)` returns the same label from the displayed input, using
|
|
73
|
+
`describeInput` or the view's date label.
|
|
74
|
+
|
|
75
|
+
Declare standard controls in the view's [variables](variables.md). Keep
|
|
76
|
+
local-only filters out of the operation's `input`.
|
|
77
|
+
|
|
78
|
+
For large tables, use query-backed pagination with a stable sort and total
|
|
79
|
+
count. Client pagination and search cover only the rows already returned.
|
|
80
|
+
|
|
81
|
+
Declare evidence references on `view.dataset()` for charts and tables. `<MetricWidget>` uses
|
|
82
|
+
its metric definition for the label, format, and evidence; keep view-specific
|
|
83
|
+
descriptions on the widget.
|
|
84
|
+
|
|
85
|
+
`<MetricWidget>` and `<Comparison>` share a metric reading. Comparisons
|
|
86
|
+
follow the displayed result's range.
|
|
87
|
+
|
|
88
|
+
Use the [format helpers](formatting-and-appearance.md) for metric formats and values in tables,
|
|
89
|
+
charts, and custom views.
|
|
90
|
+
|
|
91
|
+
## Preserve displayed results
|
|
92
|
+
|
|
93
|
+
The callback in `view.content()` receives a displayed source with `loading`,
|
|
94
|
+
`data`, `input`, and `scope`. Bind widgets to that source. Its input remains the
|
|
95
|
+
one that produced the visible data during refreshes and failures.
|
|
96
|
+
|
|
97
|
+
Keep the client stable across renders; create it outside the component.
|
|
98
|
+
|
|
99
|
+
## Request ownership
|
|
100
|
+
|
|
101
|
+
`<DataApp>` owns the toolbar, controls, inspection, and page feedback for its
|
|
102
|
+
primary `view`. It always renders children, so introductions and static
|
|
103
|
+
context remain visible while a section loads. It requires both
|
|
104
|
+
[story and CSV export](stories-and-export.md) for a data request.
|
|
105
|
+
|
|
106
|
+
Place `<DataSection content={content}>` around each independently fetched subtree.
|
|
107
|
+
Declare its shared loading and ready layout with `view.content()`. The section
|
|
108
|
+
inherits empty copy from its view, with an optional local override, and handles
|
|
109
|
+
initial errors and retries. Primary content shares the app's displayed result;
|
|
110
|
+
independent content uses its own context and executed queries.
|
|
111
|
+
|
|
112
|
+
Keep related datasets in one view so export and story share a coherent snapshot.
|
|
113
|
+
|
|
114
|
+
| State | Display |
|
|
115
|
+
| --------------- | --------------------------------------------------------- |
|
|
116
|
+
| Initial loading | The authored loading fallback; selectors do not run |
|
|
117
|
+
| Initial error | A source-aware error with retry when recovery is possible |
|
|
118
|
+
| Empty result | The authored empty fallback |
|
|
119
|
+
| Ready | Data and the input that produced it |
|
|
120
|
+
| Updating | The prior result and its original input, fully readable |
|
|
121
|
+
| Failed refresh | The prior result and its evidence, with recovery feedback |
|
|
122
|
+
|
|
123
|
+
`isEmpty` is app-owned. A measured zero is valid data unless the analysis explicitly
|
|
124
|
+
says otherwise. Use the displayed input in content, export, and story callbacks for scope labels.
|
|
125
|
+
|
|
126
|
+
`<DataApp>` owns request execution, refresh, and cancellation. Keep raw request
|
|
127
|
+
state out of app code.
|
package/docs/widgets.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Widgets and narrative
|
|
2
|
+
|
|
3
|
+
## Choose a widget
|
|
4
|
+
|
|
5
|
+
Metrics, tables, and visualizations from `/react` consume view bindings and a source. For static displays, use
|
|
6
|
+
[direct UI composition](ui.md).
|
|
7
|
+
Manage requests in the [view and section](views.md); show skeletons for loading
|
|
8
|
+
readings without inventing values. Use `isEmpty` for the predicate and
|
|
9
|
+
`emptyFallback` for empty-result copy.
|
|
10
|
+
|
|
11
|
+
| Need | Component |
|
|
12
|
+
| ------------------------------------------------ | ----------------------- |
|
|
13
|
+
| Metric with formatting, comparison, and evidence | `<MetricWidget>` |
|
|
14
|
+
| Custom chart or alternate chart views | `<VisualizationWidget>` |
|
|
15
|
+
| Defined columns, local search, and pagination | `<TableWidget>` |
|
|
16
|
+
| Narrative panel | `<TextWidget>` |
|
|
17
|
+
| Borderless explanatory prose | `<TextContent>` |
|
|
18
|
+
| One data-dependent phrase within static prose | `<DataValue>` |
|
|
19
|
+
|
|
20
|
+
Use [layout](layout.md) for `<Stack>`, `<Grid>`, and responsive `<GridItem>` spans.
|
|
21
|
+
`<Skeleton>` supports custom fallback content; `/react/ui` supplies
|
|
22
|
+
`<DataAppSkeleton>` for iframe startup.
|
|
23
|
+
|
|
24
|
+
## Choose a visualization
|
|
25
|
+
|
|
26
|
+
Choose the component by the reader's question. Compose dataset visuals inside
|
|
27
|
+
`<VisualizationWidget>`; use `<MetricWidget>` for a headline value. Built-in
|
|
28
|
+
charts come from `/react/ui`; `<Ranking>`, `<Breakdown>`, and `<Comparison>`
|
|
29
|
+
are also available from `/react`.
|
|
30
|
+
|
|
31
|
+
| Question or need | Component | Guidance |
|
|
32
|
+
| --------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
33
|
+
| How do categories compare? | `<BarChart>` | Compare nonnegative category values; order by value for rank or by a meaningful category order. |
|
|
34
|
+
| Which items lead? | `<Ranking>` | Use a compact ordered list with values and optional detail. Tracks scale to the largest visible item, not a total. |
|
|
35
|
+
| How does a measure change over time? | `<LineChart>` | Use ordered, equally spaced samples to emphasize the trend. |
|
|
36
|
+
| How large is the measure over time? | `<AreaChart>` | Use the same samples as a line chart when filled magnitude relative to zero helps answer the question. |
|
|
37
|
+
| What makes up the whole? | `<PieChart>` | Use a few mutually exclusive parts of one total; include the remainder as Other. |
|
|
38
|
+
| What share of an observed total does each part represent? | `<Breakdown>` | Use a compact display of values and shares with an explicit total, including when only some parts are shown. |
|
|
39
|
+
| How are two numeric measures related? | `<ScatterChart>` | Use independent X/Y observations to explore relationships, clusters, and outliers. |
|
|
40
|
+
| How did one metric change between periods? | `<Comparison>` | Use the metric reading and its displayed comparison period. |
|
|
41
|
+
| What are the exact values or row details? | `<TableWidget>` | Use a bound table for lookup, search, and precise comparisons. |
|
|
42
|
+
|
|
43
|
+
Prefer bars or a ranking when readers need to compare similarly sized categories;
|
|
44
|
+
use a pie for a simple composition question. Prefer a line when the trend is
|
|
45
|
+
enough; use area when the fill adds meaning. Keep exact values available in a
|
|
46
|
+
table when they matter. Follow the [chart input constraints](ui.md#charts).
|
|
47
|
+
|
|
48
|
+
Use widget `views` when a chart and another representation answer the same
|
|
49
|
+
question from the same dataset.
|
|
50
|
+
|
|
51
|
+
## Declare datasets and metrics
|
|
52
|
+
|
|
53
|
+
Bind reusable selections to the view. Declare evidence references directly;
|
|
54
|
+
the view validates them against its own context. A dataset's columns supply both formatted
|
|
55
|
+
table cells and raw CSV values. Widgets derive their readings, evidence, and empty fallback from the binding.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
const countries = activityView.dataset({
|
|
59
|
+
name: 'Countries',
|
|
60
|
+
select: data => data.countries,
|
|
61
|
+
rowKey: row => row.country,
|
|
62
|
+
evidence: { id: 'countries', glossaryIds: ['identities'] },
|
|
63
|
+
columns: {
|
|
64
|
+
country: { value: row => row.country },
|
|
65
|
+
count: { value: row => row.count, format: { kind: 'count' } },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const total = activityView.metric(
|
|
69
|
+
{
|
|
70
|
+
id: 'tracked-identities',
|
|
71
|
+
glossaryId: 'identities',
|
|
72
|
+
format: { kind: 'count' },
|
|
73
|
+
},
|
|
74
|
+
data => ({
|
|
75
|
+
current: data.count,
|
|
76
|
+
previous: data.previousCount,
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
const content = activityView.content(result => (
|
|
80
|
+
<Grid columns={2}>
|
|
81
|
+
<MetricWidget metric={total} source={result} />
|
|
82
|
+
<TableWidget dataset={countries} source={result} />
|
|
83
|
+
<VisualizationWidget dataset={countries} source={result}>
|
|
84
|
+
{rows => <CountryChart rows={rows} />}
|
|
85
|
+
</VisualizationWidget>
|
|
86
|
+
</Grid>
|
|
87
|
+
));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Column keys supply unique IDs and readable default labels. Use `label` for a
|
|
91
|
+
specific display/export header. Declare selectors, raw value accessors, and
|
|
92
|
+
row-key callbacks explicitly; row keys must be stable and unique.
|
|
93
|
+
Metric comparisons require a
|
|
94
|
+
view date binding. `read()` returns a loading-aware value for custom prose.
|
|
95
|
+
Pass the binding and `source={snapshot}` to reuse a widget in a story. Use the
|
|
96
|
+
source supplied by that binding's own view; reconstructed or foreign sources
|
|
97
|
+
are rejected before selection.
|
|
98
|
+
Selectors do not run during loading. Table search, pagination, descriptions, and
|
|
99
|
+
actions remain local choices. For custom cells, use `format: row => ...`; CSV
|
|
100
|
+
still uses the raw `value` accessor. Datasets default to “No results”; supply
|
|
101
|
+
`emptyFallback` for specific copy. See [export](stories-and-export.md).
|
|
102
|
+
|
|
103
|
+
## Narrative text
|
|
104
|
+
|
|
105
|
+
Use `<TextContent>` for prose within a page or custom layout, and `<TextWidget>`
|
|
106
|
+
when the explanation belongs in a titled panel alongside other widgets.
|
|
107
|
+
|
|
108
|
+
Give text a purpose: frame the question, explain how to interpret a comparison,
|
|
109
|
+
qualify a finding, or suggest what to explore next. Choose the content for the
|
|
110
|
+
reader's question.
|
|
111
|
+
|
|
112
|
+
For data-dependent text, pass `metric` or `dataset` and `source` to `<TextWidget>`.
|
|
113
|
+
It derives its title and evidence. A metric formats its current value by default;
|
|
114
|
+
use a child renderer for authored prose. Dataset renderers receive displayed rows,
|
|
115
|
+
including an empty selection, so they can explain zero activity.
|
|
116
|
+
|
|
117
|
+
Use `<DataValue metric={total} source={result} />` for a formatted value inside
|
|
118
|
+
static prose, or pass a dataset and row renderer. Its default loading fallback is
|
|
119
|
+
an inline skeleton. Use `<DataValue scope={result.scope} />` for a scope label.
|
|
120
|
+
All selectors use the displayed source; loading selectors do not run.
|
|
121
|
+
Use `<TextContent>` for static instructions; local filters should feed the same
|
|
122
|
+
filtered data to the text and its related visualization.
|
|
123
|
+
|
|
124
|
+
## Custom visuals and controls
|
|
125
|
+
|
|
126
|
+
`<Ranking>`, `<Breakdown>`, and `<Comparison>`
|
|
127
|
+
compose inside authored widgets. Use [built-in charts](ui.md#charts) from `/react/ui`
|
|
128
|
+
inside `<VisualizationWidget dataset={dataset} source={result}>` for displayed rows.
|
|
129
|
+
The dataset owns evidence and empty copy; an empty row selection shows that fallback. A visualization's `views` declaration keeps
|
|
130
|
+
alternate-view selection shared with its inspection sheet. Give views stable IDs
|
|
131
|
+
and keep interactive state above the widget, since inspection may render it again.
|
|
132
|
+
|
|
133
|
+
`<TableWidget>` searches and paginates only the supplied rows. Use query-backed
|
|
134
|
+
pagination with a stable sort and total count for larger datasets. The `limit`
|
|
135
|
+
mode shows a bounded preview without local pagination. Custom tables can use
|
|
136
|
+
`<DataTable>`, `<DataTableEmptyRow>`, `<DataTableTimestamp>`, and `<DataTableShare>`.
|
|
137
|
+
|
|
138
|
+
For custom controls, tables, and overlays, see [direct UI composition](ui.md).
|
|
139
|
+
For embedded theme and chrome, see [parent presentation](embed.md#parent-presentation).
|
|
140
|
+
|
|
141
|
+
`<TooltipProvider>` shares hover timing outside `<DataApp>`; the app already
|
|
142
|
+
provides it.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatMetric } from '@altertable/data-app/format';
|
|
1
2
|
import { createDataClient } from '@altertable/data-app/client';
|
|
2
3
|
import type { DataAppConfig } from '@altertable/data-app/config';
|
|
3
4
|
import {
|
|
@@ -9,6 +10,9 @@ import {
|
|
|
9
10
|
createDataContext,
|
|
10
11
|
createDataHooks,
|
|
11
12
|
DataApp,
|
|
13
|
+
DataSection,
|
|
14
|
+
DataValue,
|
|
15
|
+
TableWidget,
|
|
12
16
|
Grid,
|
|
13
17
|
Stack,
|
|
14
18
|
TextContent,
|
|
@@ -93,83 +97,106 @@ const sampleDataContext = createDataContext(queryNames)({
|
|
|
93
97
|
},
|
|
94
98
|
},
|
|
95
99
|
});
|
|
96
|
-
const { defineDataView
|
|
97
|
-
createDataClient({ operations })
|
|
98
|
-
);
|
|
100
|
+
const { defineDataView } = createDataHooks(createDataClient({ operations }));
|
|
99
101
|
const sampleCountsView = defineDataView({
|
|
102
|
+
dataContext: sampleDataContext,
|
|
100
103
|
operation: 'sampleCountsByGroup',
|
|
101
104
|
variables: {
|
|
102
105
|
groupName: textVariable({ key: 'group', label: 'Group', defaultValue: '' }),
|
|
103
106
|
},
|
|
104
|
-
input: ({ groupName }) => ({ groupName }),
|
|
105
107
|
describeInput: ({ groupName }) =>
|
|
106
108
|
groupName ? `group ${groupName}` : 'all groups',
|
|
107
109
|
isEmpty: sampleCounts => sampleCounts.length === 0,
|
|
108
|
-
|
|
110
|
+
emptyFallback: {
|
|
109
111
|
title: 'No matching groups',
|
|
110
112
|
description: 'Try Alpha, Beta, or clear the group filter.',
|
|
111
113
|
},
|
|
112
114
|
});
|
|
115
|
+
const sampleCounts = sampleCountsView.dataset({
|
|
116
|
+
name: 'Sample counts',
|
|
117
|
+
select: rows => rows,
|
|
118
|
+
rowKey: row => row.groupName,
|
|
119
|
+
columns: {
|
|
120
|
+
groupName: { label: 'Group', value: row => row.groupName },
|
|
121
|
+
sampleCount: { value: row => row.sampleCount, format: { kind: 'count' } },
|
|
122
|
+
},
|
|
123
|
+
evidence: {
|
|
124
|
+
id: 'counts-by-group',
|
|
125
|
+
glossaryIds: ['sampleCount'],
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
const totalSamples = sampleCountsView.metric(
|
|
129
|
+
{
|
|
130
|
+
id: 'total-samples',
|
|
131
|
+
glossaryId: 'sampleCount',
|
|
132
|
+
label: 'Total samples',
|
|
133
|
+
format: { kind: 'count' },
|
|
134
|
+
},
|
|
135
|
+
rows => ({ current: rows.reduce((sum, row) => sum + row.sampleCount, 0) })
|
|
136
|
+
);
|
|
137
|
+
const sampleContent = sampleCountsView.content(result => (
|
|
138
|
+
<Stack aria-label="Sample results">
|
|
139
|
+
<TextContent>
|
|
140
|
+
<p>
|
|
141
|
+
Showing <DataValue scope={result.scope} />
|
|
142
|
+
</p>
|
|
143
|
+
</TextContent>
|
|
144
|
+
<Grid columns={2}>
|
|
145
|
+
<MetricWidget
|
|
146
|
+
metric={totalSamples}
|
|
147
|
+
source={result}
|
|
148
|
+
description="Sum of the fixture counts in the selected groups."
|
|
149
|
+
/>
|
|
150
|
+
<TableWidget
|
|
151
|
+
dataset={sampleCounts}
|
|
152
|
+
source={result}
|
|
153
|
+
title="Counts by group"
|
|
154
|
+
description="Alpha and Beta demonstrate measured values, including zero."
|
|
155
|
+
pagination={false}
|
|
156
|
+
/>
|
|
157
|
+
</Grid>
|
|
158
|
+
</Stack>
|
|
159
|
+
));
|
|
113
160
|
function App() {
|
|
114
|
-
const sampleCountsRequest = useView(sampleCountsView);
|
|
115
161
|
return (
|
|
116
162
|
<DataApp
|
|
117
163
|
config={appConfig}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
164
|
+
view={sampleCountsView}
|
|
165
|
+
datasets={[sampleCounts]}
|
|
166
|
+
story={snapshot => {
|
|
167
|
+
const total = totalSamples.read(snapshot);
|
|
168
|
+
const scope = sampleCountsView.scope(snapshot);
|
|
169
|
+
return [
|
|
123
170
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
171
|
+
id: 'total-samples',
|
|
172
|
+
headline: `Total samples: ${formatMetric(total.value.current, totalSamples.definition.format)}`,
|
|
173
|
+
context: `Demonstration values for ${scope}.`,
|
|
174
|
+
visual: <MetricWidget metric={totalSamples} source={snapshot} />,
|
|
175
|
+
visualKind: 'metric',
|
|
176
|
+
evidence: totalSamples,
|
|
130
177
|
},
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
sampleDataContext.finding({
|
|
136
|
-
id: `sample-count-${groupName}`,
|
|
137
|
-
headline: `${groupName} has ${sampleCount} samples`,
|
|
138
|
-
context: `Demonstration values for ${displayedInput.groupName || 'all groups'}.`,
|
|
178
|
+
{
|
|
179
|
+
id: 'counts-by-group',
|
|
180
|
+
headline: 'Counts by group',
|
|
181
|
+
context: `Demonstration values for ${scope}.`,
|
|
139
182
|
visual: (
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
183
|
+
<TableWidget
|
|
184
|
+
dataset={sampleCounts}
|
|
185
|
+
source={snapshot}
|
|
186
|
+
pagination={false}
|
|
144
187
|
/>
|
|
145
188
|
),
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
})
|
|
152
|
-
)
|
|
153
|
-
}
|
|
189
|
+
evidence: sampleCounts,
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}}
|
|
154
193
|
>
|
|
155
|
-
|
|
156
|
-
<
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<Grid columns={2}>
|
|
162
|
-
{sampleCounts.map(({ groupName, sampleCount }) => (
|
|
163
|
-
<MetricWidget
|
|
164
|
-
key={groupName}
|
|
165
|
-
label={`${groupName}: ${sampleCount}`}
|
|
166
|
-
value={sampleCount}
|
|
167
|
-
format={{ kind: 'count' }}
|
|
168
|
-
/>
|
|
169
|
-
))}
|
|
170
|
-
</Grid>
|
|
171
|
-
</Stack>
|
|
172
|
-
)}
|
|
194
|
+
<Stack>
|
|
195
|
+
<TextContent>
|
|
196
|
+
<h2>Sample counts</h2>
|
|
197
|
+
</TextContent>
|
|
198
|
+
<DataSection content={sampleContent} />
|
|
199
|
+
</Stack>
|
|
173
200
|
</DataApp>
|
|
174
201
|
);
|
|
175
202
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altertable/data-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.66.0",
|
|
4
4
|
"description": "Contracts, transport, and React UI for Altertable data apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"altertable",
|
|
@@ -78,6 +78,10 @@
|
|
|
78
78
|
"./react/embed": {
|
|
79
79
|
"types": "./dist/types/react/embed/index.d.ts",
|
|
80
80
|
"import": "./dist/react/embed/index.js"
|
|
81
|
+
},
|
|
82
|
+
"./react/ui": {
|
|
83
|
+
"types": "./dist/types/react/ui/index.d.ts",
|
|
84
|
+
"import": "./dist/react/ui/index.js"
|
|
81
85
|
}
|
|
82
86
|
},
|
|
83
87
|
"publishConfig": {
|
|
@@ -86,12 +90,13 @@
|
|
|
86
90
|
},
|
|
87
91
|
"scripts": {
|
|
88
92
|
"build": "bun run scripts/build.ts && tsc -p tsconfig.build.json && bun run scripts/build-declarations.ts",
|
|
93
|
+
"dev": "bun run scripts/dev.ts",
|
|
89
94
|
"typecheck": "bun install --frozen-lockfile && tsc --noEmit",
|
|
90
|
-
"lint": "oxlint --type-aware src tests scripts browser-tests examples/starter-data-app",
|
|
95
|
+
"lint": "oxlint --type-aware src tests scripts browser-tests dev examples/starter-data-app",
|
|
91
96
|
"format": "oxfmt .",
|
|
92
97
|
"format:check": "oxfmt --check .",
|
|
93
98
|
"test": "bun test ./tests",
|
|
94
|
-
"check": "bun run build && bun run typecheck && bun run lint && bun run lint:md && bun run check:links && bun run format:check && bun run test && bun run test:package && bun run test:starter",
|
|
99
|
+
"check": "bun run build && bun run typecheck && bun run lint && bun run check:styles && bun run lint:md && bun run check:links && bun run format:check && bun run test && bun run test:package && bun run test:starter",
|
|
95
100
|
"test:package": "bun run scripts/check-package.ts",
|
|
96
101
|
"check:workflows": "bash scripts/check-workflows.sh",
|
|
97
102
|
"test:browser": "playwright test --config browser-tests/playwright.config.ts",
|
|
@@ -99,7 +104,8 @@
|
|
|
99
104
|
"test:starter": "bun run scripts/check-starter.ts",
|
|
100
105
|
"lint:md": "markdownlint-cli2",
|
|
101
106
|
"check:links": "remark . --ext md --rc-path .remarkrc.json --frail --no-stdout",
|
|
102
|
-
"check:links:external": "remark . --ext md --rc-path .remarkrc-external.json --frail --no-stdout"
|
|
107
|
+
"check:links:external": "remark . --ext md --rc-path .remarkrc-external.json --frail --no-stdout",
|
|
108
|
+
"check:styles": "bun run scripts/check-app-styles.ts"
|
|
103
109
|
},
|
|
104
110
|
"dependencies": {
|
|
105
111
|
"@floating-ui/react": "0.27.20",
|
|
@@ -107,6 +113,7 @@
|
|
|
107
113
|
"@tanstack/react-query": "5.104.0",
|
|
108
114
|
"fflate": "0.8.3",
|
|
109
115
|
"fuzzysort": "4.0.2",
|
|
116
|
+
"html-to-image": "1.11.13",
|
|
110
117
|
"lucide-react": "1.48.0",
|
|
111
118
|
"react-aria-components": "1.21.1"
|
|
112
119
|
},
|
|
@@ -125,6 +132,7 @@
|
|
|
125
132
|
"remark-gfm": "4.0.1",
|
|
126
133
|
"remark-lint-no-dead-urls": "2.0.1",
|
|
127
134
|
"remark-validate-links": "13.1.0",
|
|
135
|
+
"testcontainers": "12.2.0",
|
|
128
136
|
"typescript": "7.0.2"
|
|
129
137
|
},
|
|
130
138
|
"peerDependencies": {
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/client/data-client.ts", "../src/client/location.ts", "../src/client/navigation.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import { createHttpTransport, DataAppError } from '@/src/client/transport';\nimport { getDataAppTransport, localFrameBridge } from '@/src/client/iframe';\nimport {\n executeDataOperation,\n toDataOperationFailure,\n} from '@/src/core/operation';\nimport type { DataOperations, Lakehouse } from '@/src/core/contract';\nimport { defineDataQueryRoute, MessageRoutingError } from '@/src/core/messages';\nimport type { DataTransport } from '@/src/client/transport';\nimport type {\n DataQueryBody,\n InputOf,\n OutputOf,\n} from '@/src/core/operation-types';\nexport type { InputOf, OutputOf } from '@/src/core/operation-types';\n\n/**\n * Parsed operation data and query evidence. `queries` is present only when SQL disclosure is\n * allowed.\n */\nexport type DataResponse<Output, Input = unknown> = DataQueryBody<Output> & {\n /** The exact browser input that produced this response. */\n input: Input;\n};\n\nexport type DataClient<Operations extends DataOperations> = {\n query<Name extends keyof Operations & string>(\n name: Name,\n input: InputOf<Operations[Name]>,\n options?: { signal?: AbortSignal }\n ): Promise<\n DataResponse<OutputOf<Operations[Name]>, InputOf<Operations[Name]>>\n >;\n};\n\n/** Browser operations and named-operation delivery are mutually exclusive configurations. */\nexport type DataClientOptions<Operations extends DataOperations> =\n | {\n operations: Operations;\n lakehouse?: Lakehouse;\n transport?: never;\n endpoint?: never;\n fetch?: never;\n }\n | {\n operations?: never;\n lakehouse?: never;\n transport?: DataTransport;\n endpoint?: string;\n fetch?: typeof fetch;\n };\n\nconst dataQueryRoute = defineDataQueryRoute();\n\n/** Named HTTP operations, or browser-owned operations executed through an authorized SQL bridge. */\nexport function createDataClient<Operations extends DataOperations>(\n options: DataClientOptions<Operations> = {}\n): DataClient<Operations> {\n if (options.operations)\n return createOperationClient({\n operations: options.operations,\n lakehouse: options.lakehouse,\n });\n const http = createHttpTransport(options);\n const useHttp = options.endpoint !== undefined || options.fetch !== undefined;\n\n return {\n async query(name, input, { signal } = {}) {\n const transport =\n options.transport ??\n (useHttp ? http : (localFrameBridge()?.transport ?? http));\n const response = await transport(name, input, signal);\n const ok = response.status >= 200 && response.status < 300;\n let body: Omit<DataResponse<OutputOf<Operations[typeof name]>>, 'input'>;\n try {\n body = dataQueryRoute.output(response, { operation: name, input })\n .body as typeof body;\n } catch (error) {\n if (signal?.aborted) throw error;\n if (error instanceof MessageRoutingError)\n throw new DataAppError(error.message, error.code, error.requestId);\n throw new DataAppError(\n ok ? 'The data response was invalid.' : 'Could not load data.',\n ok ? 'invalid_response' : 'request_failed'\n );\n }\n\n return { ...body, input };\n },\n };\n}\n\nfunction createOperationClient<Operations extends DataOperations>(options: {\n operations: Operations;\n lakehouse?: Lakehouse;\n}): DataClient<Operations> {\n return {\n async query(name, input, { signal } = {}) {\n signal?.throwIfAborted();\n const requestId = crypto.randomUUID();\n if (!Object.hasOwn(options.operations, name))\n throw new DataAppError(\n 'Unknown data operation.',\n 'not_found',\n requestId\n );\n const lakehouse =\n options.lakehouse ??\n (typeof window === 'undefined'\n ? undefined\n : getDataAppTransport()?.lakehouse);\n if (!lakehouse)\n throw new DataAppError(\n 'An authorized SQL bridge must be installed first.',\n 'bridge_unavailable',\n requestId\n );\n\n try {\n const { body } = await executeDataOperation(\n options.operations[name]!,\n input,\n {\n lakehouse,\n signal: signal ?? new AbortController().signal,\n includeSql: true,\n requestId,\n operationName: name,\n }\n );\n\n return { ...body, input } as DataResponse<\n OutputOf<Operations[typeof name]>,\n InputOf<Operations[typeof name]>\n >;\n } catch (error) {\n if (signal?.aborted) throw signal.reason;\n if (error instanceof DataAppError) throw error;\n const failure = toDataOperationFailure(error);\n throw new DataAppError(failure.message, failure.code, requestId);\n }\n },\n };\n}\n",
|
|
6
|
-
"import { PARENT_PARAM } from '@/src/core/bridge';\nimport { validLocation } from '@/src/core/navigation';\n\nimport type { AppLocation, HistoryMode } from '@/src/core/navigation';\nexport type { AppLocation } from '@/src/core/navigation';\nexport type DataAppLocation = {\n snapshot(): AppLocation;\n subscribe(listener: () => void): () => void;\n update(location: AppLocation, mode?: HistoryMode): void;\n};\n\n/** Opaque sandboxes keep app state in memory; URL frames retain their real URL for HMR. */\nexport function createAppLocation(\n frame: Window,\n virtual: boolean,\n publish: (location: AppLocation, mode: HistoryMode) => void\n) {\n let state: AppLocation = { search: '', hash: '' };\n let disposed = false;\n const listeners = new Set<() => void>();\n\n function notify() {\n for (const listener of listeners) listener();\n }\n\n function snapshot(): AppLocation {\n if (virtual) return state;\n const url = new URL(frame.location.href);\n url.searchParams.delete(PARENT_PARAM);\n if (state.search !== url.search || state.hash !== url.hash)\n state = { search: url.search, hash: url.hash };\n\n return state;\n }\n\n function apply(next: AppLocation) {\n const previous = snapshot();\n if (previous.search === next.search && previous.hash === next.hash) return;\n if (virtual) state = next;\n else {\n const url = new URL(frame.location.href);\n const parent = url.searchParams.get(PARENT_PARAM);\n url.search = next.search;\n if (parent) url.searchParams.set(PARENT_PARAM, parent);\n url.hash = next.hash;\n frame.history.replaceState(null, '', url);\n frame.dispatchEvent(new Event('popstate'));\n }\n if (virtual) notify();\n }\n\n const location: DataAppLocation = {\n snapshot,\n subscribe(listener) {\n listeners.add(listener);\n if (!virtual) frame.addEventListener('popstate', listener);\n\n return () => {\n listeners.delete(listener);\n frame.removeEventListener('popstate', listener);\n };\n },\n update(next, mode = 'replace') {\n if (disposed) return;\n if (!validLocation(next)) throw new Error('Invalid app location.');\n const before = snapshot();\n if (before.search === next.search && before.hash === next.hash) return;\n apply(next);\n publish(next, mode);\n },\n };\n\n function dispose() {\n disposed = true;\n for (const listener of listeners)\n frame.removeEventListener('popstate', listener);\n listeners.clear();\n }\n\n return { location, apply, dispose };\n}\n",
|
|
7
|
-
"import type { AppLocation, HistoryMode } from '@/src/core/navigation';\nimport { createAppLocation, type DataAppLocation } from '@/src/client/location';\nimport { validLocation } from '@/src/core/navigation';\nimport {\n navigationUpdateRoute,\n type NavigationUpdate,\n} from '@/src/core/messages';\nimport { createMessageClient } from '@/src/client/messages';\nimport {\n getDataAppTransport,\n localFrameBridge,\n type IframeTransport,\n} from '@/src/client/iframe';\n\nexport type DataAppNavigation = DataAppLocation & {\n /** Publish URL changes made outside the adapter, including the document title. */\n publish(mode?: HistoryMode): void;\n dispose(): void;\n};\n\n/** Attach app URL behavior to an already authenticated bridge. */\nexport function createDataAppNavigation({\n bridge,\n window: frame = window,\n}: {\n bridge: IframeTransport;\n window?: Window;\n}): DataAppNavigation {\n return attachNavigation(frame, bridge.mode === 'bundle', bridge).navigation;\n}\n\nfunction attachNavigation(\n frame: Window,\n virtual: boolean,\n connection: IframeTransport | Promise<IframeTransport>\n) {\n let disposed = false;\n let unsubscribe: (() => void) | undefined;\n let send: ((update: NavigationUpdate) => void) | undefined;\n let pendingUpdate: NavigationUpdate | undefined;\n const { location, apply, dispose } = createAppLocation(\n frame,\n virtual,\n publish\n );\n\n function publish(location: AppLocation, mode: HistoryMode) {\n const update = { ...location, mode, title: frame.document.title };\n if (send) send(update);\n else pendingUpdate = update;\n }\n\n function attach(bridge: IframeTransport) {\n if (disposed) return;\n unsubscribe?.();\n const messages = createMessageClient(\n { 'navigation:update': navigationUpdateRoute },\n bridge.request\n );\n\n function sendUpdate(update: NavigationUpdate) {\n void messages.request('navigation:update', update).catch(() => {});\n }\n\n function receiveState(state: unknown) {\n if (!validLocation(state)) return;\n pendingUpdate = undefined;\n apply(state);\n }\n\n send = sendUpdate;\n unsubscribe = bridge.subscribe(receiveState);\n receiveState(bridge.snapshot());\n if (pendingUpdate) {\n sendUpdate(pendingUpdate);\n pendingUpdate = undefined;\n }\n }\n\n if ('then' in connection) void connection.then(attach).catch(() => {});\n else attach(connection);\n\n const navigation: DataAppNavigation = {\n ...location,\n publish(mode = 'replace') {\n if (!disposed) publish(location.snapshot(), mode);\n },\n dispose() {\n disposed = true;\n unsubscribe?.();\n dispose();\n send = undefined;\n pendingUpdate = undefined;\n },\n };\n\n return {\n navigation,\n attach,\n isDisposed() {\n return disposed;\n },\n };\n}\n\nconst navigationKey = Symbol.for('altertable.appNavigation');\ntype NavigationWindow = Window & {\n [navigationKey]?: ReturnType<typeof attachNavigation> & {\n bridge?: IframeTransport;\n };\n};\n\n/** URL controls attach navigation on demand; bootstrap and data-only clients do not. */\nexport function getDataAppNavigation(\n frame: Window = window\n): DataAppNavigation | undefined {\n const target = frame as NavigationWindow;\n const bridge = getDataAppTransport(frame);\n const cached = target[navigationKey]?.isDisposed()\n ? undefined\n : target[navigationKey];\n if (cached) {\n if (bridge && cached.bridge !== bridge) {\n cached.attach(bridge);\n cached.bridge = bridge;\n }\n\n return cached.navigation;\n }\n if (bridge) {\n const adapter = attachNavigation(frame, bridge.mode === 'bundle', bridge);\n target[navigationKey] = { bridge, ...adapter };\n\n return adapter.navigation;\n }\n const preview = localFrameBridge(frame);\n if (!preview?.connect) return undefined;\n const connection = preview.connect();\n const adapter = attachNavigation(frame, false, connection);\n const entry = {\n ...adapter,\n bridge: undefined as IframeTransport | undefined,\n };\n target[navigationKey] = entry;\n void connection\n .then(bridge => {\n entry.bridge = bridge;\n })\n .catch(() => {});\n\n return adapter.navigation;\n}\n"
|
|
8
|
-
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "B2D2C0A1B4F5737F64756E2164756E21",
|
|
11
|
-
"names": []
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/embed/host.ts", "../src/embed/source.ts", "../src/embed/bridge.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import { createBridgeEndpoint } from '@/src/core/bridge-endpoint';\nimport type { DataAppPresentation } from '@/src/core/presentation';\nimport type { DataAppLogger } from '@/src/core/logger';\nimport {\n MAX_PENDING,\n REQUEST_TIMEOUT_MS,\n validId,\n type BridgeEventMessage,\n type BridgeMessage,\n} from '@/src/core/bridge';\n\nimport {\n MessageRoutingError,\n type MessageDispatcher,\n} from '@/src/core/messages';\n\nexport type DataAppConnection =\n | { type: 'origin'; origin: string }\n | { type: 'opaque'; token: string };\nexport type DataAppStatus =\n | 'connecting'\n | 'connected'\n | 'ready'\n | 'failed'\n | 'disconnected';\nexport type DataAppDiagnostic = {\n direction: 'send' | 'receive';\n type: BridgeMessage['type'];\n};\n\nexport type DataAppHost = {\n dispose: () => void;\n setPresentation: (presentation?: DataAppPresentation) => void;\n setLogger: (logger?: DataAppLogger) => void;\n};\n\nexport type DataAppHostOptions = {\n iframe: HTMLIFrameElement;\n presentation?: DataAppPresentation;\n logger?: DataAppLogger;\n onStatusChange?: (status: DataAppStatus) => void;\n onDiagnostic?: (event: DataAppDiagnostic) => void;\n onMessage: MessageDispatcher;\n};\n\nexport type DataAppConnectionOptions = DataAppHostOptions & {\n connection: DataAppConnection;\n javascript?: string;\n window?: Window;\n};\n\n/** The bridge owns delivery and cancellation; the routed handler owns validation, authorization and execution. */\nexport function attachDataAppConnection({\n iframe,\n connection,\n javascript,\n presentation,\n logger,\n onStatusChange,\n onDiagnostic,\n onMessage,\n window: host = window,\n}: DataAppConnectionOptions): DataAppHost {\n const frameOrigin = connection.type === 'origin' ? connection.origin : 'null';\n if (\n connection.type === 'origin' &&\n new URL(frameOrigin).origin !== frameOrigin\n )\n throw new Error('Expected an exact frame origin.');\n if (connection.type === 'opaque' && !validId(connection.token))\n throw new Error('Invalid sandbox token.');\n let token = connection.type === 'opaque' ? connection.token : undefined;\n const targetOrigin = connection.type === 'opaque' ? '*' : frameOrigin;\n let currentPresentation: DataAppPresentation | undefined;\n let disposed = false;\n let documentId: string | undefined;\n let sessionId: string | undefined;\n const pending = new Map<string, AbortController>();\n\n function hostState() {\n return {\n search: host.location.search,\n hash: host.location.hash,\n ...(currentPresentation ? { presentation: currentPresentation } : {}),\n logging: logger !== undefined,\n };\n }\n\n function setLogger(value: DataAppLogger | undefined) {\n if (disposed || value === logger) return;\n const wasEnabled = logger !== undefined;\n logger = value;\n if (wasEnabled !== (logger !== undefined)) publishState();\n }\n\n function setPresentation(value: DataAppPresentation | undefined) {\n if (disposed) return;\n if (\n value?.surface === currentPresentation?.surface &&\n value?.theme === currentPresentation?.theme\n )\n return;\n currentPresentation = value && { ...value };\n publishState();\n }\n\n function cancelAll() {\n for (const controller of pending.values()) controller.abort();\n pending.clear();\n }\n\n const endpoint = createBridgeEndpoint({\n role: 'host',\n origin: frameOrigin,\n source: () => iframe.contentWindow,\n opaque: connection.type === 'opaque',\n context: () => ({ documentId, sessionId, token }),\n post: message => iframe.contentWindow?.postMessage(message, targetOrigin),\n diagnostic: (direction, type) => onDiagnostic?.({ direction, type }),\n invalidRequest: id => {\n if (pending.has(id)) return;\n if (pending.size >= MAX_PENDING)\n error(id, 'bridge_busy', 'Too many pending data requests.');\n else error(id, 'invalid_message', 'Invalid message route.');\n },\n handlers: {\n ready(message) {\n if (message.documentId !== documentId) {\n cancelAll();\n documentId = message.documentId;\n sessionId = crypto.randomUUID();\n }\n onStatusChange?.('connected');\n if (disposed) return;\n send('initialize', { state: hostState() });\n if (disposed) return;\n if (javascript !== undefined) send('scriptLoad', { javascript });\n },\n runtimeLog({ payload }) {\n try {\n logger?.[payload.method](...payload.args);\n } catch {\n // A host logger failure must not break bridge delivery.\n }\n },\n runtimeReady() {\n onStatusChange?.('ready');\n },\n runtimeError() {\n onStatusChange?.('failed');\n },\n request(message) {\n void request(message);\n },\n cancel(message) {\n pending.get(message.id)?.abort();\n pending.delete(message.id);\n },\n disconnect() {\n cancelAll();\n documentId = undefined;\n sessionId = undefined;\n onStatusChange?.('disconnected');\n },\n },\n });\n const send = endpoint.send;\n const receive = endpoint.receive;\n\n function error(\n id: string,\n code: string,\n message: string,\n requestId?: string\n ) {\n send('error', { id, code, message, requestId });\n }\n\n async function request(message: BridgeEventMessage<'request'>) {\n const id = message.id;\n if (pending.has(id)) return;\n if (pending.size >= MAX_PENDING)\n return error(id, 'bridge_busy', 'Too many pending data requests.');\n try {\n const body = JSON.stringify(message.payload);\n if (\n message.payload !== undefined &&\n (typeof body !== 'string' || body.length > 16_384)\n )\n throw new Error('Invalid payload');\n } catch {\n return error(\n id,\n 'invalid_payload',\n 'The message payload is invalid or too large.'\n );\n }\n const controller = new AbortController();\n pending.set(id, controller);\n const timer = setTimeout(() => {\n if (pending.get(id) !== controller) return;\n pending.delete(id);\n controller.abort();\n error(id, 'timeout', 'The data request timed out.');\n }, REQUEST_TIMEOUT_MS);\n controller.signal.addEventListener('abort', () => clearTimeout(timer), {\n once: true,\n });\n try {\n const response = await onMessage(\n { route: message.route, payload: message.payload },\n { signal: controller.signal }\n );\n if (pending.get(id) === controller) send('result', { id, response });\n } catch (failure) {\n if (pending.get(id) === controller) {\n if (failure instanceof MessageRoutingError)\n error(id, failure.code, failure.message, failure.requestId);\n else\n error(\n id,\n 'request_failed',\n 'The message request failed. Retry the request.'\n );\n }\n } finally {\n clearTimeout(timer);\n if (pending.get(id) === controller) pending.delete(id);\n }\n }\n\n function publishState() {\n if (disposed || !sessionId) return;\n send('stateUpdate', { state: hostState() });\n }\n\n function load() {\n if (connection.type === 'opaque') token = crypto.randomUUID();\n cancelAll();\n documentId = undefined;\n sessionId = undefined;\n onStatusChange?.('connecting');\n send('connect', {});\n }\n\n function dispose() {\n disposed = true;\n endpoint.dispose();\n cancelAll();\n iframe.removeEventListener('load', load);\n host.removeEventListener('message', receive);\n host.removeEventListener('popstate', publishState);\n host.removeEventListener('pagehide', cancelAll);\n }\n\n setPresentation(presentation);\n iframe.addEventListener('load', load);\n host.addEventListener('message', receive);\n host.addEventListener('popstate', publishState);\n host.addEventListener('pagehide', cancelAll);\n // Reconnect an already-loaded iframe when its host bridge mounts again.\n onStatusChange?.('connecting');\n send('connect', {});\n\n return { dispose, setPresentation, setLogger };\n}\n",
|
|
6
|
-
"import { PARENT_PARAM } from '@/src/core/bridge';\nimport {\n attachDataAppConnection,\n type DataAppStatus,\n type DataAppHost,\n type DataAppHostOptions,\n} from '@/src/embed/host';\n\nexport type DataAppSource =\n | { type: 'url'; url: string }\n | {\n type: 'bundle';\n bootstrapUrl: string;\n javascript: string;\n };\nexport type DataAppSourceOptions = DataAppHostOptions & {\n source: DataAppSource;\n startupTimeoutMs?: number;\n};\n\n/** Owns loading and sandbox policy. Dispose before replacing the source or retrying. */\nexport function attachDataAppSource({\n iframe,\n source,\n presentation,\n logger,\n onMessage,\n onStatusChange,\n onDiagnostic,\n startupTimeoutMs = 30_000,\n}: DataAppSourceOptions): DataAppHost {\n const host = iframe.ownerDocument.defaultView;\n if (!host) throw new Error('The iframe requires a host window.');\n const url = new URL(\n source.type === 'url' ? source.url : source.bootstrapUrl,\n host.location.href\n );\n if (!/^https?:$/.test(url.protocol))\n throw new Error('Data apps require an HTTP(S) URL.');\n const opaque = source.type === 'bundle';\n if (!opaque && url.origin === host.location.origin)\n throw new Error('URL data apps require a separate origin from the host.');\n iframe.setAttribute(\n 'sandbox',\n opaque\n ? 'allow-scripts'\n : 'allow-scripts allow-same-origin allow-downloads allow-popups allow-popups-to-escape-sandbox'\n );\n iframe.referrerPolicy = 'no-referrer';\n if (!opaque) url.searchParams.set(PARENT_PARAM, host.location.origin);\n let timer: ReturnType<typeof setTimeout> | undefined;\n let failed = false;\n\n function status(value: DataAppStatus) {\n if (value === 'connecting') failed = false;\n if (failed && value !== 'connecting') return;\n clearTimeout(timer);\n if (value === 'connecting' || value === 'connected')\n timer = setTimeout(() => {\n failed = true;\n bridge.dispose();\n onStatusChange?.('failed');\n }, startupTimeoutMs);\n if (value === 'failed') {\n failed = true;\n bridge.dispose();\n }\n onStatusChange?.(value);\n }\n\n const bridge = attachDataAppConnection({\n iframe,\n connection: opaque\n ? { type: 'opaque', token: crypto.randomUUID() }\n : { type: 'origin', origin: url.origin },\n javascript: opaque ? source.javascript : undefined,\n presentation,\n logger,\n onMessage,\n onStatusChange: status,\n onDiagnostic,\n window: host,\n });\n\n function error() {\n return status('failed');\n }\n\n iframe.addEventListener('error', error);\n iframe.src = url.href;\n\n function dispose() {\n clearTimeout(timer);\n iframe.removeEventListener('error', error);\n bridge.dispose();\n }\n\n return {\n dispose,\n setPresentation: bridge.setPresentation,\n setLogger: bridge.setLogger,\n };\n}\n",
|
|
7
|
-
"import {\n attachDataAppConnection,\n type DataAppConnectionOptions,\n} from '@/src/embed/host';\nimport {\n attachDataAppSource,\n type DataAppSourceOptions,\n} from '@/src/embed/source';\n\nexport type DataAppBridgeOptions =\n | (DataAppSourceOptions & {\n connection?: never;\n window?: never;\n javascript?: never;\n })\n | (DataAppConnectionOptions & { source?: never; startupTimeoutMs?: never });\n\n/** Source mode configures and loads the iframe; connection mode attaches to a host-owned document. */\nexport function attachDataAppBridge(options: DataAppBridgeOptions) {\n return options.source\n ? attachDataAppSource(options)\n : attachDataAppConnection(options);\n}\n"
|
|
8
|
-
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "5826F4790D0666BF64756E2164756E21",
|
|
11
|
-
"names": []
|
|
12
|
-
}
|