@altertable/data-app 0.66.0 → 0.68.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/CONTRIBUTING.md +22 -16
- package/dist/chunks/{contract-19ckn3n7.js → index-23dktynd.js} +8 -6
- package/dist/chunks/index-23dktynd.js.map +10 -0
- package/dist/chunks/{contract-rfbakka4.js → index-ag0cgnvq.js} +169 -35
- package/dist/chunks/index-ag0cgnvq.js.map +13 -0
- package/dist/chunks/index-dq7b35fn.js +307 -0
- package/dist/chunks/index-dq7b35fn.js.map +13 -0
- package/dist/chunks/{contract-e11m4y7f.js → index-ev2b5aaf.js} +11 -6
- package/dist/chunks/{contract-e11m4y7f.js.map → index-ev2b5aaf.js.map} +3 -3
- package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
- package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
- package/dist/chunks/{contract-havnjmdr.js → index-gvers8jw.js} +2429 -1110
- package/dist/chunks/index-gvers8jw.js.map +109 -0
- package/dist/chunks/{contract-13j5zb3c.js → index-ktmvs916.js} +8 -7
- package/dist/chunks/index-ktmvs916.js.map +13 -0
- package/dist/chunks/index-mt93ff2b.js +28 -0
- package/dist/chunks/index-mt93ff2b.js.map +10 -0
- package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
- package/dist/chunks/index-qd5xgx6y.js.map +13 -0
- package/dist/chunks/{contract-pk603qj7.js → index-x69p7cv7.js} +12 -9
- package/dist/chunks/index-x69p7cv7.js.map +10 -0
- package/dist/client/index.js +6 -6
- package/dist/core/appearance.js +1 -1
- package/dist/core/config.js +7 -3
- package/dist/core/config.js.map +1 -1
- package/dist/core/contract.js +14 -6
- package/dist/core/contract.js.map +1 -1
- package/dist/core/format.js +1 -1
- package/dist/embed/index.js +12 -10
- package/dist/embed/index.js.map +4 -4
- package/dist/index.js +12 -0
- package/dist/index.js.map +9 -0
- package/dist/local.js +20 -30
- package/dist/local.js.map +7 -8
- package/dist/react/embed/index.js +1 -1
- package/dist/react/embed/index.js.map +2 -2
- package/dist/react/index.js +79 -29
- package/dist/react/index.js.map +5 -6
- package/dist/react/ui/index.js +1702 -177
- package/dist/react/ui/index.js.map +20 -9
- package/dist/server.js +13 -26
- package/dist/server.js.map +6 -7
- package/dist/types/client/annotations.d.ts +4 -0
- package/dist/types/client/data-client.d.ts +1 -2
- package/dist/types/client/iframe.d.ts +4 -2
- package/dist/types/core/annotations.d.ts +2 -0
- package/dist/types/core/config.d.ts +48 -4
- package/dist/types/core/contract.d.ts +20 -12
- package/dist/types/core/dimension.d.ts +6 -4
- package/dist/types/core/filters.d.ts +44 -0
- package/dist/types/core/messages.d.ts +2 -0
- package/dist/types/core/operation-types.d.ts +5 -2
- package/dist/types/core/operation.d.ts +3 -4
- package/dist/types/core/queries.d.ts +16 -0
- package/dist/types/core/uuid.d.ts +2 -0
- package/dist/types/core/variables.d.ts +32 -13
- package/dist/types/embed/runtime-html.d.ts +2 -0
- package/dist/types/embed/source.d.ts +2 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/react/annotations/AnnotationBar.d.ts +7 -1
- package/dist/types/react/annotations/AnnotationEditor.d.ts +2 -1
- package/dist/types/react/annotations/useDataAppAnnotations.d.ts +3 -0
- package/dist/types/react/app-context.d.ts +3 -0
- package/dist/types/react/index.d.ts +3 -3
- package/dist/types/react/mount.d.ts +6 -4
- package/dist/types/react/style-contract.d.ts +34 -2
- package/dist/types/react/ui/Button.d.ts +3 -3
- package/dist/types/react/ui/ChartLegend.d.ts +31 -0
- package/dist/types/react/ui/Checkbox.d.ts +15 -4
- package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
- package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
- package/dist/types/react/ui/ComposedChart.d.ts +41 -0
- package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
- package/dist/types/react/ui/DataAppFrame.d.ts +1 -2
- package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
- package/dist/types/react/ui/FilterActions.d.ts +10 -0
- package/dist/types/react/ui/FilterBar.d.ts +3 -0
- package/dist/types/react/ui/FunnelChart.d.ts +20 -0
- package/dist/types/react/ui/GettingStarted.d.ts +2 -4
- package/dist/types/react/ui/JourneyChart.d.ts +10 -0
- package/dist/types/react/ui/Menu.d.ts +12 -0
- package/dist/types/react/ui/NumberField.d.ts +27 -0
- package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
- package/dist/types/react/ui/PieChart.d.ts +5 -2
- package/dist/types/react/ui/RadioGroup.d.ts +18 -0
- package/dist/types/react/ui/RetentionChart.d.ts +27 -0
- package/dist/types/react/ui/SearchInput.d.ts +2 -2
- package/dist/types/react/ui/Select.d.ts +14 -0
- package/dist/types/react/ui/SelectionMark.d.ts +2 -1
- package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
- package/dist/types/react/ui/chart-data.d.ts +10 -1
- package/dist/types/react/ui/chart-primitives.d.ts +22 -0
- package/dist/types/react/ui/chartColor.d.ts +1 -0
- package/dist/types/react/ui/icons.d.ts +1 -0
- package/dist/types/react/ui/index.d.ts +33 -2
- package/dist/types/react/ui/journey-flow.d.ts +49 -0
- package/dist/types/react/ui/keyboard.d.ts +6 -0
- package/dist/types/react/ui/shortcuts.d.ts +7 -0
- package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
- package/dist/types/react/ui/useSvgId.d.ts +2 -0
- package/dist/types/react/ui/variables.d.ts +4 -2
- package/dist/types/react/view-controls.d.ts +3 -1
- package/dist/types/server/handler.d.ts +4 -4
- package/dist/worker.js +21 -738
- package/docs/app-authoring.md +15 -1
- package/docs/client.md +8 -8
- package/docs/contract.md +43 -20
- package/docs/data-context.md +8 -6
- package/docs/embed.md +10 -2
- package/docs/formatting-and-appearance.md +23 -7
- package/docs/hosted-apps.md +9 -2
- package/docs/layout.md +1 -1
- package/docs/react-embed.md +3 -2
- package/docs/react.md +4 -3
- package/docs/server-bun.md +1 -1
- package/docs/server.md +4 -4
- package/docs/styling.md +10 -3
- package/docs/ui.md +176 -3
- package/docs/variables.md +80 -7
- package/docs/views.md +4 -8
- package/docs/widgets.md +19 -13
- package/docs/worker.md +2 -1
- package/examples/starter-data-app/index.tsx +34 -32
- package/package.json +16 -11
- package/dist/chunks/contract-13j5zb3c.js.map +0 -13
- package/dist/chunks/contract-19ckn3n7.js.map +0 -10
- package/dist/chunks/contract-h8a6f559.js +0 -286
- package/dist/chunks/contract-h8a6f559.js.map +0 -12
- package/dist/chunks/contract-havnjmdr.js.map +0 -99
- package/dist/chunks/contract-pk603qj7.js.map +0 -10
- package/dist/chunks/contract-rfbakka4.js.map +0 -13
- package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
- package/dist/chunks/contract-wz59z8pq.js +0 -8
- package/dist/chunks/contract-wz59z8pq.js.map +0 -10
- /package/dist/chunks/{contract-m6n8ctfc.js → index-m6n8ctfc.js} +0 -0
- /package/dist/chunks/{contract-m6n8ctfc.js.map → index-m6n8ctfc.js.map} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
- /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
package/docs/variables.md
CHANGED
|
@@ -16,7 +16,7 @@ available when they have zero matches.
|
|
|
16
16
|
|
|
17
17
|
## Declare controls once
|
|
18
18
|
|
|
19
|
-
Use `
|
|
19
|
+
Use `searchVariable()`, `choiceVariable()`, and `dateRangeVariable()` in a view's
|
|
20
20
|
`variables`. `<DataApp view={view}>` generates their controls and keeps values in the URL.
|
|
21
21
|
Keep local search out of the operation input when it only filters loaded rows.
|
|
22
22
|
|
|
@@ -30,7 +30,7 @@ const activity = defineTimeView({
|
|
|
30
30
|
dataContext,
|
|
31
31
|
operation: 'activity',
|
|
32
32
|
time: { contract: calendar, defaultValue: { kind: 'preset', id: 'last-7' } },
|
|
33
|
-
variables: { region:
|
|
33
|
+
variables: { region: searchVariable({ key: 'region' }) },
|
|
34
34
|
input: values => ({
|
|
35
35
|
filters: { period: values.period, region: values.region },
|
|
36
36
|
}),
|
|
@@ -67,7 +67,7 @@ const region = dimensionFilter({
|
|
|
67
67
|
key: 'region',
|
|
68
68
|
label: 'Region',
|
|
69
69
|
valueType: 'string',
|
|
70
|
-
|
|
70
|
+
selectionMode: 'multiple',
|
|
71
71
|
options: [{ value: 'Europe', label: 'Europe' }],
|
|
72
72
|
});
|
|
73
73
|
```
|
|
@@ -82,7 +82,7 @@ const region = defineFacetFilter({
|
|
|
82
82
|
key: 'region',
|
|
83
83
|
label: 'Region',
|
|
84
84
|
valueType: 'string',
|
|
85
|
-
|
|
85
|
+
selectionMode: 'multiple',
|
|
86
86
|
facet: {
|
|
87
87
|
operation: 'regions',
|
|
88
88
|
input: values => ({ period: values.period as DateRangeRequest }),
|
|
@@ -90,9 +90,82 @@ const region = defineFacetFilter({
|
|
|
90
90
|
});
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
The client
|
|
93
|
+
The client's operation map defines `regions` and its typed period input;
|
|
94
94
|
import `DateRangeRequest` from `/contract`. Put `region` in the view's `variables`
|
|
95
|
-
alongside its period. Use `parseFacetOptions()` to validate facet results
|
|
96
|
-
`dimensionPredicate()` to build a bounded SQL filter from parsed selections.
|
|
95
|
+
alongside its period. Use `parseFacetOptions()` to validate facet results.
|
|
97
96
|
For the operation's input parser, use `parseDimensionSelection(value, region)` from `/contract`.
|
|
98
97
|
A dimension without an explicit selection represents all members.
|
|
98
|
+
Map parsed selections to the query's declared parameters; do not construct SQL
|
|
99
|
+
from filter values. Choose selection modes supported by the registered query.
|
|
100
|
+
|
|
101
|
+
Choose `selectionMode: 'single'` when one value replaces another. Its picker closes
|
|
102
|
+
after choosing a value and shows a checkmark for the current choice. Use
|
|
103
|
+
`selectionMode: 'multiple'` for independent values; checkbox indicators and an open
|
|
104
|
+
popup allow repeated selections. `All` is an explicit choice for a single-value
|
|
105
|
+
filter and the meaning of an empty selection for a multiple-value filter.
|
|
106
|
+
|
|
107
|
+
## Fixed choices
|
|
108
|
+
|
|
109
|
+
`choiceVariable()` requires labeled `options` and a valid `defaultValue`.
|
|
110
|
+
`multiChoiceVariable()` stores unique option IDs with an optional selection limit.
|
|
111
|
+
An empty fixed-choice collection means no choices; categorical filters use
|
|
112
|
+
`dimensionFilter()` when an empty selection should mean all records.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const variables = {
|
|
116
|
+
search: searchVariable({ key: 'q', label: 'Search' }),
|
|
117
|
+
metric: choiceVariable({
|
|
118
|
+
key: 'metric',
|
|
119
|
+
label: 'Metric',
|
|
120
|
+
defaultValue: 'orders',
|
|
121
|
+
options: [
|
|
122
|
+
{ id: 'orders', label: 'Orders' },
|
|
123
|
+
{ id: 'revenue', label: 'Revenue' },
|
|
124
|
+
],
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Fixed single choices generate `<Select>`; multiple choices generate
|
|
130
|
+
`<ChoicePicker selectionMode="multiple">`. Direct controls can present the same
|
|
131
|
+
values as radio groups, checkbox groups, or segmented choices without changing
|
|
132
|
+
URL state.
|
|
133
|
+
|
|
134
|
+
## Numeric and boolean predicates
|
|
135
|
+
|
|
136
|
+
Import `numberFilter()` and `booleanFilter()` from `/contract` and put their
|
|
137
|
+
results in the view's `variables`. Both have an explicit `{ kind: 'all' }`
|
|
138
|
+
unrestricted state. Numeric filters support inclusive, open-ended ranges and
|
|
139
|
+
comparisons: `eq`, `ne`, `gt`, `gte`, `lt`, and `lte`. Boolean filters distinguish
|
|
140
|
+
Any from `{ kind: 'is', value: true }` and `{ kind: 'is', value: false }`.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const amount = numberFilter({ key: 'amount', label: 'Amount', min: 0 });
|
|
144
|
+
const active = booleanFilter({ key: 'active', label: 'Active' });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use `parseNumberSelection()` and `parseBooleanSelection()` in the operation's
|
|
148
|
+
input parser with the same filter declarations. Numeric predicates generate `<NumberFilterPicker>` panels. Draft edits reach
|
|
149
|
+
operation input only after Apply; Cancel and dismissal preserve the applied
|
|
150
|
+
value. Invalid ranges disable Apply.
|
|
151
|
+
Input mappings and bindings must preserve these predicates unchanged, as with
|
|
152
|
+
categorical filters. Map parsed operators and values to declared query parameters;
|
|
153
|
+
offer only operators supported by the registered query.
|
|
154
|
+
|
|
155
|
+
## Exclusion, clear, and reset
|
|
156
|
+
|
|
157
|
+
Set `allowExclusion: true` on `dimensionFilter()` to support
|
|
158
|
+
`{ kind: 'exclude', members }`. `<DimensionPicker>` then exposes Include/Exclude
|
|
159
|
+
alongside selected members. Enable exclusion only when the registered query supports
|
|
160
|
+
it, including the intended behavior for missing values.
|
|
161
|
+
|
|
162
|
+
Generated controls show active values within each filter and one collective
|
|
163
|
+
Clear icon with a tooltip. Clear removes restrictions from search and predicate filters.
|
|
164
|
+
`resetAll()` restores all
|
|
165
|
+
configured variable defaults, including fixed choices and periods. Nonempty
|
|
166
|
+
default filters can therefore be active immediately after Reset.
|
|
167
|
+
|
|
168
|
+
Direct `useAppVariables()` callers can use `clearAll()` and `resetAll()` for one
|
|
169
|
+
atomic history update. `<FilterActions>` also supports app-owned draft state
|
|
170
|
+
with paired Apply/Cancel handlers; derive queries and visible controls from applied
|
|
171
|
+
values while a draft is being edited.
|
package/docs/views.md
CHANGED
|
@@ -26,12 +26,7 @@ const content = activityView.content(result => (
|
|
|
26
26
|
|
|
27
27
|
function App() {
|
|
28
28
|
return (
|
|
29
|
-
<DataApp
|
|
30
|
-
config={config}
|
|
31
|
-
view={activityView}
|
|
32
|
-
story={story}
|
|
33
|
-
datasets={[activityDataset]}
|
|
34
|
-
>
|
|
29
|
+
<DataApp view={activityView} story={story} datasets={[activityDataset]}>
|
|
35
30
|
<DataSection content={content} />
|
|
36
31
|
</DataApp>
|
|
37
32
|
);
|
|
@@ -65,8 +60,9 @@ Bind the whole sentence with `<TextWidget>` when its wording depends on the resu
|
|
|
65
60
|
Use `<DataValue scope={result.scope} />` for the displayed scope label.
|
|
66
61
|
|
|
67
62
|
Use the same [date range contract](contract.md#shared-date-ranges) for the
|
|
68
|
-
operation and its view. For nested inputs
|
|
69
|
-
`
|
|
63
|
+
operation and its view. For nested inputs in `defineTimeView()`, use
|
|
64
|
+
`bindings.period` to extract the selected period; see the
|
|
65
|
+
[time-view example](variables.md#declare-controls-once).
|
|
70
66
|
|
|
71
67
|
Use `result.scope` inside `view.content()` as a reading for scope text. In stories and exports,
|
|
72
68
|
`view.scope(snapshot)` returns the same label from the displayed input, using
|
package/docs/widgets.md
CHANGED
|
@@ -28,22 +28,28 @@ Choose the component by the reader's question. Compose dataset visuals inside
|
|
|
28
28
|
charts come from `/react/ui`; `<Ranking>`, `<Breakdown>`, and `<Comparison>`
|
|
29
29
|
are also available from `/react`.
|
|
30
30
|
|
|
31
|
-
| Question or need | Component
|
|
32
|
-
| --------------------------------------------------------- |
|
|
33
|
-
| How do categories compare? | `<BarChart>`
|
|
34
|
-
| Which items lead? | `<Ranking>`
|
|
35
|
-
| How does a measure change over time? | `<LineChart>`
|
|
36
|
-
| How large is the measure over time? | `<AreaChart>`
|
|
37
|
-
| What makes up the whole? | `<PieChart>`
|
|
38
|
-
| What share of an observed total does each part represent? | `<Breakdown>`
|
|
39
|
-
| How
|
|
40
|
-
| How
|
|
41
|
-
|
|
|
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 do several measures relate on one plot? | `<ComposedChart>` | Compose bars, lines, areas, or scatter series; label units clearly and use separate axes for different units. |
|
|
40
|
+
| How are two numeric measures related? | `<ScatterChart>` | Use independent X/Y observations to explore relationships, clusters, and outliers. |
|
|
41
|
+
| Where do users drop out of a sequence? | `<FunnelChart>` | Use ordered counts from the same population reaching each successive step. |
|
|
42
|
+
| Do cohorts return over time? | `<RetentionChart>` | Use query-defined rates and retained counts by numeric offset; mark incomplete periods. |
|
|
43
|
+
| Which paths do users take? | `<JourneyChart>` | Use full paths with population counts and outcomes; explore branches step by step. |
|
|
44
|
+
| How did one metric change between periods? | `<Comparison>` | Use the metric reading and its displayed comparison period. |
|
|
45
|
+
| What are the exact values or row details? | `<TableWidget>` | Use a bound table for lookup, search, and precise comparisons. |
|
|
42
46
|
|
|
43
47
|
Prefer bars or a ranking when readers need to compare similarly sized categories;
|
|
44
48
|
use a pie for a simple composition question. Prefer a line when the trend is
|
|
45
49
|
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).
|
|
50
|
+
table when they matter. Follow the [chart input constraints](ui.md#charts). For multiple or mixed series,
|
|
51
|
+
use the [composed chart primitives](ui.md#composed-charts) within the same bound
|
|
52
|
+
visualization workflow.
|
|
47
53
|
|
|
48
54
|
Use widget `views` when a chart and another representation answer the same
|
|
49
55
|
question from the same dataset.
|
|
@@ -69,7 +75,7 @@ const total = activityView.metric(
|
|
|
69
75
|
{
|
|
70
76
|
id: 'tracked-identities',
|
|
71
77
|
glossaryId: 'identities',
|
|
72
|
-
format: { kind: 'count' },
|
|
78
|
+
format: { kind: 'count', compact: true },
|
|
73
79
|
},
|
|
74
80
|
data => ({
|
|
75
81
|
current: data.count,
|
package/docs/worker.md
CHANGED
|
@@ -28,7 +28,8 @@ and these string bindings:
|
|
|
28
28
|
| `PARENT_ORIGINS` | Space-separated trusted HTTP(S) origins; first exact origin is the default | `https://app.example.com https://*.preview.example.com` |
|
|
29
29
|
|
|
30
30
|
The Worker serves only `/` on a single preview label under `DOMAIN_NAME` matching
|
|
31
|
-
`/^(?=.{1,63}$)[a-z0-9]+(
|
|
31
|
+
`/^(?=.{1,63}$)[a-z0-9]+(?:-+[a-z0-9]+)+$/`
|
|
32
|
+
(any hyphenated lowercase DNS label; the host keeps each app's label unique). Other hosts and paths
|
|
32
33
|
return 404. GET returns HTML; HEAD returns the same headers with no body. Other
|
|
33
34
|
methods return 405 with `Allow: GET, HEAD`.
|
|
34
35
|
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { formatMetric } from '@altertable/data-app/format';
|
|
2
2
|
import { createDataClient } from '@altertable/data-app/client';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
defineOperation,
|
|
6
|
-
defineQueryNames,
|
|
7
|
-
parseCount,
|
|
8
|
-
} from '@altertable/data-app/contract';
|
|
3
|
+
import { defineDataApp } from '@altertable/data-app';
|
|
4
|
+
import { parseCount } from '@altertable/data-app/contract';
|
|
9
5
|
import {
|
|
10
6
|
createDataContext,
|
|
11
7
|
createDataHooks,
|
|
@@ -19,11 +15,27 @@ import {
|
|
|
19
15
|
injectDataAppStyles,
|
|
20
16
|
mountDataApp,
|
|
21
17
|
MetricWidget,
|
|
22
|
-
|
|
18
|
+
searchVariable,
|
|
23
19
|
} from '@altertable/data-app/react';
|
|
24
20
|
|
|
25
|
-
const
|
|
26
|
-
|
|
21
|
+
const dataApp = defineDataApp({
|
|
22
|
+
title: 'Sample counts',
|
|
23
|
+
description: 'Explore sample counts by group.',
|
|
24
|
+
scope: { organization: 'demo', environment: 'sample' },
|
|
25
|
+
appearance: { theme: 'system' },
|
|
26
|
+
queries: {
|
|
27
|
+
sampleCountsByGroup: {
|
|
28
|
+
statement: `
|
|
29
|
+
WITH sample_counts(group_name, sample_count) AS (VALUES ('Alpha', 2200000), ('Beta', 0))
|
|
30
|
+
SELECT group_name, sample_count FROM sample_counts
|
|
31
|
+
WHERE $groupName = '' OR group_name = $groupName
|
|
32
|
+
ORDER BY group_name LIMIT $limit`,
|
|
33
|
+
params: {
|
|
34
|
+
groupName: { defaultValue: '' },
|
|
35
|
+
limit: { defaultValue: 10 },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
27
39
|
});
|
|
28
40
|
function parseSampleCountFilter(value: unknown) {
|
|
29
41
|
if (
|
|
@@ -51,8 +63,7 @@ function parseSampleCounts(
|
|
|
51
63
|
});
|
|
52
64
|
}
|
|
53
65
|
const operations = {
|
|
54
|
-
sampleCountsByGroup: defineOperation({
|
|
55
|
-
queryNames,
|
|
66
|
+
sampleCountsByGroup: dataApp.defineOperation({
|
|
56
67
|
input: parseSampleCountFilter,
|
|
57
68
|
output: parseSampleCounts,
|
|
58
69
|
checks: [
|
|
@@ -60,18 +71,9 @@ const operations = {
|
|
|
60
71
|
{ groupName: 'Alpha' },
|
|
61
72
|
{ groupName: 'missing' },
|
|
62
73
|
],
|
|
63
|
-
policy: { maxQueryRows: 10, maxDurationMs: 15000
|
|
74
|
+
policy: { maxQueryRows: 10, maxDurationMs: 15000 },
|
|
64
75
|
async run({ query }, { groupName }) {
|
|
65
|
-
|
|
66
|
-
const escapedGroupName = groupName.replaceAll("'", "''");
|
|
67
|
-
const queryResult = await query(
|
|
68
|
-
queryNames.sampleCountsByGroup,
|
|
69
|
-
`
|
|
70
|
-
WITH sample_counts(group_name, sample_count) AS (VALUES ('Alpha', 3), ('Beta', 0))
|
|
71
|
-
SELECT group_name, sample_count FROM sample_counts
|
|
72
|
-
WHERE '${escapedGroupName}' = '' OR group_name = '${escapedGroupName}'
|
|
73
|
-
ORDER BY group_name LIMIT 10`
|
|
74
|
-
);
|
|
76
|
+
const queryResult = await query('sampleCountsByGroup', { groupName });
|
|
75
77
|
return parseSampleCounts(
|
|
76
78
|
queryResult.rows.map(([groupName, sampleCount]) => ({
|
|
77
79
|
groupName,
|
|
@@ -81,18 +83,15 @@ ORDER BY group_name LIMIT 10`
|
|
|
81
83
|
},
|
|
82
84
|
}),
|
|
83
85
|
};
|
|
84
|
-
const
|
|
85
|
-
title: 'Sample counts',
|
|
86
|
-
scope: { organization: 'demo', environment: 'sample' },
|
|
87
|
-
appearance: { theme: 'system' },
|
|
88
|
-
};
|
|
86
|
+
const queryNames = operations.sampleCountsByGroup.queryNames;
|
|
89
87
|
const sampleDataContext = createDataContext(queryNames)({
|
|
90
88
|
description:
|
|
91
89
|
'Two SQL VALUES rows demonstrate the host query path. Replace them with inspected source data before publishing findings.',
|
|
92
90
|
glossary: {
|
|
93
91
|
sampleCount: {
|
|
94
92
|
term: 'Sample count',
|
|
95
|
-
definition:
|
|
93
|
+
definition:
|
|
94
|
+
'A fixture value: Alpha is 2,200,000 and Beta is a measured zero.',
|
|
96
95
|
queryNames: [queryNames.sampleCountsByGroup],
|
|
97
96
|
},
|
|
98
97
|
},
|
|
@@ -102,7 +101,11 @@ const sampleCountsView = defineDataView({
|
|
|
102
101
|
dataContext: sampleDataContext,
|
|
103
102
|
operation: 'sampleCountsByGroup',
|
|
104
103
|
variables: {
|
|
105
|
-
groupName:
|
|
104
|
+
groupName: searchVariable({
|
|
105
|
+
key: 'group',
|
|
106
|
+
label: 'Group',
|
|
107
|
+
defaultValue: '',
|
|
108
|
+
}),
|
|
106
109
|
},
|
|
107
110
|
describeInput: ({ groupName }) =>
|
|
108
111
|
groupName ? `group ${groupName}` : 'all groups',
|
|
@@ -130,7 +133,7 @@ const totalSamples = sampleCountsView.metric(
|
|
|
130
133
|
id: 'total-samples',
|
|
131
134
|
glossaryId: 'sampleCount',
|
|
132
135
|
label: 'Total samples',
|
|
133
|
-
format: { kind: 'count' },
|
|
136
|
+
format: { kind: 'count', compact: true },
|
|
134
137
|
},
|
|
135
138
|
rows => ({ current: rows.reduce((sum, row) => sum + row.sampleCount, 0) })
|
|
136
139
|
);
|
|
@@ -160,7 +163,6 @@ const sampleContent = sampleCountsView.content(result => (
|
|
|
160
163
|
function App() {
|
|
161
164
|
return (
|
|
162
165
|
<DataApp
|
|
163
|
-
config={appConfig}
|
|
164
166
|
view={sampleCountsView}
|
|
165
167
|
datasets={[sampleCounts]}
|
|
166
168
|
story={snapshot => {
|
|
@@ -201,4 +203,4 @@ function App() {
|
|
|
201
203
|
);
|
|
202
204
|
}
|
|
203
205
|
injectDataAppStyles();
|
|
204
|
-
mountDataApp({
|
|
206
|
+
mountDataApp({ app: dataApp, component: App });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altertable/data-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.68.0",
|
|
4
4
|
"description": "Contracts, transport, and React UI for Altertable data apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"altertable",
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"type": "module",
|
|
37
37
|
"sideEffects": false,
|
|
38
38
|
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/types/index.d.ts",
|
|
41
|
+
"import": "./dist/index.js"
|
|
42
|
+
},
|
|
39
43
|
"./contract": {
|
|
40
44
|
"types": "./dist/types/core/contract.d.ts",
|
|
41
45
|
"import": "./dist/core/contract.js"
|
|
@@ -92,16 +96,15 @@
|
|
|
92
96
|
"build": "bun run scripts/build.ts && tsc -p tsconfig.build.json && bun run scripts/build-declarations.ts",
|
|
93
97
|
"dev": "bun run scripts/dev.ts",
|
|
94
98
|
"typecheck": "bun install --frozen-lockfile && tsc --noEmit",
|
|
95
|
-
"lint": "oxlint --type-aware src tests scripts
|
|
99
|
+
"lint": "oxlint --type-aware src tests scripts dev examples/starter-data-app vitest.config.ts",
|
|
96
100
|
"format": "oxfmt .",
|
|
97
101
|
"format:check": "oxfmt --check .",
|
|
98
|
-
"test": "
|
|
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
|
|
100
|
-
"
|
|
102
|
+
"test": "vitest run",
|
|
103
|
+
"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 check:package && bun run check:starter",
|
|
104
|
+
"check:package": "bun run scripts/check-package.ts",
|
|
101
105
|
"check:workflows": "bash scripts/check-workflows.sh",
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"test:starter": "bun run scripts/check-starter.ts",
|
|
106
|
+
"lint:fix": "oxlint --type-aware --fix src tests scripts dev vitest.config.ts",
|
|
107
|
+
"check:starter": "bun run scripts/check-starter.ts",
|
|
105
108
|
"lint:md": "markdownlint-cli2",
|
|
106
109
|
"check:links": "remark . --ext md --rc-path .remarkrc.json --frail --no-stdout",
|
|
107
110
|
"check:links:external": "remark . --ext md --rc-path .remarkrc-external.json --frail --no-stdout",
|
|
@@ -115,10 +118,10 @@
|
|
|
115
118
|
"fuzzysort": "4.0.2",
|
|
116
119
|
"html-to-image": "1.11.13",
|
|
117
120
|
"lucide-react": "1.48.0",
|
|
118
|
-
"react-aria-components": "1.21.1"
|
|
121
|
+
"react-aria-components": "1.21.1",
|
|
122
|
+
"recharts": "3.10.1"
|
|
119
123
|
},
|
|
120
124
|
"devDependencies": {
|
|
121
|
-
"@playwright/test": "1.63.0",
|
|
122
125
|
"@types/bun": "1.4.2",
|
|
123
126
|
"@types/react": "19.3.0",
|
|
124
127
|
"@types/react-dom": "19.3.0",
|
|
@@ -126,6 +129,7 @@
|
|
|
126
129
|
"oxfmt": "0.70.0",
|
|
127
130
|
"oxlint": "1.85.0",
|
|
128
131
|
"oxlint-tsgolint": "7.0.2003",
|
|
132
|
+
"playwright": "1.63.0",
|
|
129
133
|
"react": "19.3.0",
|
|
130
134
|
"react-dom": "19.3.0",
|
|
131
135
|
"remark-cli": "12.0.1",
|
|
@@ -133,7 +137,8 @@
|
|
|
133
137
|
"remark-lint-no-dead-urls": "2.0.1",
|
|
134
138
|
"remark-validate-links": "13.1.0",
|
|
135
139
|
"testcontainers": "12.2.0",
|
|
136
|
-
"typescript": "7.0.2"
|
|
140
|
+
"typescript": "7.0.2",
|
|
141
|
+
"vitest": "4.1.11"
|
|
137
142
|
},
|
|
138
143
|
"peerDependencies": {
|
|
139
144
|
"react": ">=19.2.0",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/client/transport.ts", "../src/client/messages.ts", "../src/client/logger.ts", "../src/client/iframe.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import type { TransportResponse } from '@/src/core/bridge';\n\nexport type DataTransport = (\n operation: string,\n input: unknown,\n signal?: AbortSignal\n) => Promise<TransportResponse>;\n\n// A bootstrap and its app may bundle independent copies in the same window.\nconst errorBrand = Symbol.for('@altertable/data-app/DataAppError');\n\nexport class DataAppError extends Error {\n readonly [errorBrand] = true;\n\n static [Symbol.hasInstance](value: unknown): boolean {\n return (\n value instanceof Error &&\n (value as { [errorBrand]?: unknown })[errorBrand] === true\n );\n }\n\n constructor(\n message: string,\n public readonly code: string,\n public readonly requestId?: string\n ) {\n super(message);\n this.name = 'DataAppError';\n }\n}\n\nexport function createHttpTransport({\n endpoint = '/api/data',\n fetch: request = globalThis.fetch,\n}: {\n endpoint?: string;\n fetch?: typeof fetch;\n} = {}): DataTransport {\n async function requestData(\n operation: string,\n input: unknown,\n signal?: AbortSignal\n ): Promise<TransportResponse> {\n const response = await request(\n `${endpoint.replace(/\\/$/, '')}/${encodeURIComponent(operation)}`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(input),\n signal,\n }\n );\n try {\n return { status: response.status, body: await response.json() };\n } catch (error) {\n if (signal?.aborted) throw error;\n throw new DataAppError(\n response.ok ? 'The data response was invalid.' : 'Could not load data.',\n response.ok ? 'invalid_response' : 'request_failed'\n );\n }\n }\n\n return requestData;\n}\n",
|
|
6
|
-
"import {\n MessageRoutingError,\n type MessageInput,\n type MessageOutput,\n type MessageRoutes,\n type MessageTransport,\n} from '@/src/core/messages';\n\n/** Both ends validate using the same contracts; the transport owns delivery and cancellation. */\nexport function createMessageClient<const Routes extends MessageRoutes>(\n routes: Routes,\n transport: MessageTransport\n) {\n return {\n async request<\n Name extends keyof Routes & string,\n const Input extends MessageInput<NoInfer<Routes[Name]>>,\n >(\n route: Name,\n payload: Input,\n { signal }: { signal?: AbortSignal } = {}\n ): Promise<MessageOutput<Routes[Name], Input>> {\n signal?.throwIfAborted();\n if (!Object.hasOwn(routes, route))\n throw new MessageRoutingError(\n 'unknown_route',\n 'Unknown message route.'\n );\n const contract = routes[route]!;\n let input: unknown;\n try {\n input = contract.input(payload);\n } catch {\n throw new MessageRoutingError(\n 'invalid_payload',\n 'Invalid message payload.'\n );\n }\n const response = await transport({ route, payload: input }, signal);\n signal?.throwIfAborted();\n try {\n return contract.output(response, input as never) as MessageOutput<\n Routes[Name],\n Input\n >;\n } catch (error) {\n if (error instanceof MessageRoutingError) throw error;\n throw new MessageRoutingError(\n 'invalid_response',\n 'Invalid message response.'\n );\n }\n },\n };\n}\n",
|
|
7
|
-
"import type { BridgeMessage } from '@/src/core/bridge';\nimport { type DataAppLogger, type LogEntry } from '@/src/core/logger';\n\n/** Lazy arguments run in the iframe; only resolved values cross the bridge. */\nexport function createBridgeLogger(\n enabled: () => boolean,\n send: (entry: LogEntry) => void\n): DataAppLogger {\n function resolve(args: unknown[]): unknown[] {\n if (args.length === 1 && typeof args[0] === 'function') {\n const value: unknown = args[0]();\n return Array.isArray(value) ? value : [value];\n }\n return args.map(arg => (typeof arg === 'function' ? arg() : arg));\n }\n\n function write(method: keyof DataAppLogger, args: unknown[]) {\n if (!enabled()) return;\n try {\n send({ method, args: resolve(args) });\n } catch {\n // Logging must not interrupt the app or recursively log delivery failures.\n }\n }\n\n return {\n log: (...args) => write('log', args),\n info: (...args) => write('info', args),\n warn: (...args) => write('warn', args),\n error: (...args) => write('error', args),\n };\n}\n\n/** Log only delivery metadata; payloads and session credentials stay private. */\nexport function logBridgeMessage(\n logger: DataAppLogger,\n message: BridgeMessage\n) {\n if (message.type === 'runtime:log') return;\n logger.log(() => {\n const metadata: {\n type: BridgeMessage['type'];\n id?: string;\n route?: string;\n operation?: string;\n } = { type: message.type };\n if ('id' in message) metadata.id = message.id;\n if (message.type === 'bridge:request') {\n metadata.route = message.route;\n const payload = message.payload;\n if (\n message.route === 'data:query' &&\n payload &&\n typeof payload === 'object' &&\n 'operation' in payload &&\n typeof payload.operation === 'string'\n )\n metadata.operation = payload.operation;\n }\n return ['Sending message to parent', metadata];\n });\n}\n",
|
|
8
|
-
"import { createBridgeEndpoint } from '@/src/core/bridge-endpoint';\nimport {\n PARENT_PARAM,\n MAX_PENDING,\n REQUEST_TIMEOUT_MS,\n type TransportResponse,\n} from '@/src/core/bridge';\nimport {\n defineDataQueryRoute,\n sqlQueryRoute,\n MessageRoutingError,\n type RoutedMessage,\n} from '@/src/core/messages';\nimport type { Lakehouse } from '@/src/core/contract';\nimport { createMessageClient } from '@/src/client/messages';\nimport { DataAppError } from '@/src/client/transport';\nimport { createBridgeLogger, logBridgeMessage } from '@/src/client/logger';\n\ntype Pending = {\n start: () => void;\n reject: (error: unknown) => void;\n resolve: (value: unknown) => void;\n};\n\n/** Data clients expose one error type for HTTP and message delivery. */\nfunction rethrowDataMessageError(error: unknown): never {\n if (error instanceof MessageRoutingError)\n throw new DataAppError(error.message, error.code, error.requestId);\n throw error;\n}\n\n/** One bridge per document, shared by all clients and retained across module hot replacement. */\nexport function createIframeTransport({\n parentOrigin,\n window: frame = window,\n timeoutMs = REQUEST_TIMEOUT_MS,\n mode = 'url',\n loadScript,\n}: {\n parentOrigin: string;\n window?: Window;\n timeoutMs?: number;\n mode?: 'url' | 'bundle';\n loadScript?: (javascript: string) => void;\n}) {\n if (new URL(parentOrigin).origin !== parentOrigin)\n throw new Error('Expected an exact parent origin.');\n const documentId = crypto.randomUUID();\n let token: string | undefined;\n let sessionId: string | undefined;\n let disposed = false;\n const pending = new Map<string, Pending>();\n let hostState: unknown;\n const stateListeners = new Set<(state: unknown) => void>();\n const logger = createBridgeLogger(\n () =>\n !disposed &&\n !!sessionId &&\n !!hostState &&\n typeof hostState === 'object' &&\n 'logging' in hostState &&\n hostState.logging === true,\n entry => send('runtimeLog', { payload: entry })\n );\n\n const endpoint = createBridgeEndpoint({\n role: 'app',\n origin: parentOrigin,\n source: () => frame.parent,\n opaque: mode === 'bundle',\n context: () => ({ documentId, sessionId, token }),\n post(message) {\n frame.parent.postMessage(message, parentOrigin);\n logBridgeMessage(logger, message);\n },\n handlers: {\n connect(message) {\n if (mode === 'bundle') token = message.token;\n send('ready', {});\n },\n initialize(message) {\n if (sessionId && sessionId !== message.sessionId) {\n for (const entry of pending.values())\n entry.reject(\n new DataAppError(\n 'The preview reconnected. Retry the request.',\n 'bridge_reset'\n )\n );\n }\n const first = !sessionId;\n sessionId = message.sessionId;\n receiveState(message.state);\n if (first) for (const entry of pending.values()) entry.start();\n if (disposed) return;\n if (mode === 'url') send('runtimeReady', {});\n },\n stateUpdate(message) {\n receiveState(message.state);\n },\n scriptLoad(message) {\n if (mode === 'bundle') loadScript?.(message.javascript);\n },\n result(message) {\n pending.get(message.id)?.resolve(message.response);\n },\n error(message) {\n pending\n .get(message.id)\n ?.reject(\n new MessageRoutingError(\n message.code,\n message.message,\n message.requestId\n )\n );\n },\n },\n });\n const send = endpoint.send;\n const receive = endpoint.receive;\n\n function receiveState(state: unknown) {\n hostState = state;\n for (const listener of stateListeners) listener(state);\n }\n\n frame.addEventListener('message', receive);\n\n function requestMessage(\n message: RoutedMessage,\n signal?: AbortSignal\n ): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (disposed)\n return reject(\n new DataAppError('The preview has closed.', 'bridge_closed')\n );\n if (signal?.aborted) return reject(signal.reason);\n if (pending.size >= MAX_PENDING)\n return reject(\n new DataAppError('Too many pending data requests.', 'bridge_busy')\n );\n const id = crypto.randomUUID();\n let sent = false;\n\n function cleanup() {\n clearTimeout(timer);\n signal?.removeEventListener('abort', abort);\n pending.delete(id);\n }\n\n function abort() {\n if (sent) send('cancel', { id });\n cleanup();\n reject(signal?.reason);\n }\n\n const timer = setTimeout(() => {\n if (sent) send('cancel', { id });\n cleanup();\n reject(\n new DataAppError(\n sessionId\n ? 'The data request timed out.'\n : 'The preview shell is unavailable. Reload the preview.',\n sessionId ? 'timeout' : 'bridge_unavailable'\n )\n );\n }, timeoutMs);\n const entry: Pending = {\n start() {\n if (sent) return;\n try {\n send('request', {\n id,\n route: message.route,\n payload: message.payload,\n });\n sent = true;\n } catch {\n cleanup();\n reject(\n new DataAppError(\n 'The message payload could not be sent.',\n 'invalid_payload'\n )\n );\n }\n },\n resolve(value) {\n cleanup();\n resolve(value);\n },\n reject(error) {\n cleanup();\n reject(error);\n },\n };\n pending.set(id, entry);\n signal?.addEventListener('abort', abort, { once: true });\n if (sessionId) entry.start();\n else if (mode === 'url' || token) send('ready', {});\n });\n }\n\n const messages = createMessageClient(\n { 'data:query': defineDataQueryRoute(), 'data:sql': sqlQueryRoute },\n requestMessage\n );\n\n function queryOperation(\n operation: string,\n input: unknown,\n signal?: AbortSignal\n ): Promise<TransportResponse> {\n return messages\n .request('data:query', { operation, input }, { signal })\n .catch(rethrowDataMessageError);\n }\n\n function disconnect() {\n if (sessionId) send('disconnect', {});\n sessionId = undefined;\n for (const entry of pending.values())\n entry.reject(\n new DataAppError('The preview has closed.', 'bridge_closed')\n );\n }\n\n function resume(event: PageTransitionEvent) {\n if (event.persisted && (mode === 'url' || token)) send('ready', {});\n }\n\n function dispose() {\n if (disposed) return;\n disconnect();\n disposed = true;\n endpoint.dispose();\n frame.removeEventListener('message', receive);\n frame.removeEventListener('pagehide', disconnect);\n frame.removeEventListener('pageshow', resume);\n stateListeners.clear();\n }\n\n frame.addEventListener('pagehide', disconnect);\n frame.addEventListener('pageshow', resume);\n if (mode === 'url') send('ready', {});\n\n return {\n logger,\n request: requestMessage,\n transport: queryOperation,\n lakehouse: {\n queryAll(statement, { limit, signal }) {\n return messages\n .request('data:sql', { statement, limit }, { signal })\n .catch(rethrowDataMessageError);\n },\n } satisfies Lakehouse,\n dispose,\n mode,\n snapshot() {\n return hostState;\n },\n subscribe(listener: (state: unknown) => void) {\n stateListeners.add(listener);\n\n return () => {\n stateListeners.delete(listener);\n };\n },\n ready() {\n if (!disposed && sessionId) send('runtimeReady', {});\n },\n fail() {\n if (!disposed && sessionId) send('runtimeError', {});\n },\n };\n}\n\nexport type IframeTransport = ReturnType<typeof createIframeTransport>;\n\nconst bridgeKey = Symbol.for('altertable.localFrameBridge');\nconst installedKey = Symbol.for('altertable.installedTransport');\ntype LocalBridge = Pick<\n ReturnType<typeof createIframeTransport>,\n 'transport'\n> & { connect?: () => Promise<ReturnType<typeof createIframeTransport>> };\ntype FrameWindow = Window & {\n [bridgeKey]?: LocalBridge;\n [installedKey]?: ReturnType<typeof createIframeTransport>;\n};\n\n/** The URL opts into local preview; only same-origin server configuration establishes trust. */\nexport function localFrameBridge(window?: Window): LocalBridge | undefined {\n const activeWindow =\n window ??\n (typeof globalThis.window === 'undefined' ? undefined : globalThis.window);\n if (!activeWindow) return undefined;\n const frame = activeWindow as FrameWindow;\n if (frame[bridgeKey]) return frame[bridgeKey];\n const parentOrigin =\n new URL(frame.location.href).searchParams.get(PARENT_PARAM) ?? '';\n if (!parentOrigin) return undefined;\n\n async function connect(parentOrigin: string) {\n try {\n const response = await frame.fetch('/__altertable/parent', {\n signal: AbortSignal.timeout(5000),\n });\n const config = (await response.json()) as { origin?: unknown };\n if (\n !response.ok ||\n config.origin !== parentOrigin ||\n frame.parent === frame\n )\n throw new Error('Invalid local parent configuration.');\n // Another owner can install a trusted bridge while configuration is pending.\n if (frame[installedKey]) return frame[installedKey];\n if (frame[bridgeKey] !== proxy)\n throw new Error('Preview transport owner changed.');\n const bridge = createIframeTransport({ parentOrigin, window: frame });\n frame[bridgeKey] = bridge;\n frame[installedKey] = bridge;\n\n return bridge;\n } catch {\n throw new DataAppError(\n 'The preview shell could not be verified. Open the URL printed by altertable app dev.',\n 'bridge_unavailable'\n );\n }\n }\n\n let connection: Promise<ReturnType<typeof createIframeTransport>> | undefined;\n\n function getConnection() {\n const installed = frame[installedKey];\n if (installed) return Promise.resolve(installed);\n if (!connection) {\n const attempt = connect(parentOrigin);\n connection = attempt;\n // Clear only this failed attempt; retained proxy references can explicitly retry.\n void attempt.catch(() => {\n if (connection === attempt) connection = undefined;\n });\n }\n\n return connection;\n }\n\n const proxy: LocalBridge = {\n async transport(operation, input, signal) {\n signal?.throwIfAborted();\n const attempt = getConnection();\n let abort: (() => void) | undefined;\n try {\n const bridge = signal\n ? await Promise.race([\n attempt,\n new Promise<never>((_, reject) => {\n const requestSignal = signal;\n\n function handleAbort() {\n reject(requestSignal.reason);\n }\n\n abort = handleAbort;\n requestSignal.addEventListener('abort', handleAbort, {\n once: true,\n });\n }),\n ])\n : await attempt;\n signal?.throwIfAborted();\n\n return bridge.transport(operation, input, signal);\n } finally {\n if (abort) signal?.removeEventListener('abort', abort);\n }\n },\n connect: getConnection,\n };\n frame[bridgeKey] = proxy;\n // Static mounting still verifies eagerly, without an unhandled rejection.\n void getConnection().catch(() => {});\n\n return proxy;\n}\n\n/** Install an explicitly trusted transport for hosted apps and their URL-backed controls. */\nexport function installDataAppTransport(\n bridge: ReturnType<typeof createIframeTransport>,\n frame: Window = window\n) {\n const target = frame as FrameWindow;\n if (target[installedKey])\n throw new Error('A data app transport is already installed.');\n target[bridgeKey] = bridge;\n target[installedKey] = bridge;\n\n return () => {\n if (target[bridgeKey] === bridge) delete target[bridgeKey];\n if (target[installedKey] === bridge) delete target[installedKey];\n bridge.dispose();\n };\n}\n\n/** The bootstrap installs this before executing app code. URL apps can install an explicit connection. */\nexport function getDataAppTransport(frame: Window = window) {\n return (frame as FrameWindow)[installedKey];\n}\n"
|
|
9
|
-
],
|
|
10
|
-
"mappings": "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",
|
|
11
|
-
"debugId": "F13D3D498CB70AA064756E2164756E21",
|
|
12
|
-
"names": []
|
|
13
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/client/data-client.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
|
-
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;AAoDA,IAAM,iBAAiB,sBAAqB;AAGrC,SAAS,iBAAmD,CACjE,UAAyC,CAAC,GAClB;AAAA,EACxB,IAAI,QAAQ;AAAA,IACV,OAAO,sBAAsB;AAAA,MAC3B,YAAY,QAAQ;AAAA,MACpB,WAAW,QAAQ;AAAA,IACrB,CAAC;AAAA,EACH,MAAM,OAAO,qBAAoB,OAAO;AAAA,EACxC,MAAM,UAAU,QAAQ,aAAa,aAAa,QAAQ,UAAU;AAAA,EAEpE,OAAO;AAAA,SACC,MAAK,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG;AAAA,MACxC,MAAM,YACJ,QAAQ,cACP,UAAU,OAAQ,iBAAiB,GAAG,aAAa;AAAA,MACtD,MAAM,WAAW,MAAM,UAAU,MAAM,OAAO,MAAM;AAAA,MACpD,MAAM,KAAK,SAAS,UAAU,OAAO,SAAS,SAAS;AAAA,MACvD,IAAI;AAAA,MACJ,IAAI;AAAA,QACF,OAAO,eAAe,OAAO,UAAU,EAAE,WAAW,MAAM,MAAM,CAAC,EAC9D;AAAA,QACH,OAAO,OAAO;AAAA,QACd,IAAI,QAAQ;AAAA,UAAS,MAAM;AAAA,QAC3B,IAAI,iBAAiB;AAAA,UACnB,MAAM,IAAI,cAAa,MAAM,SAAS,MAAM,MAAM,MAAM,SAAS;AAAA,QACnE,MAAM,IAAI,cACR,KAAK,mCAAmC,wBACxC,KAAK,qBAAqB,gBAC5B;AAAA;AAAA,MAGF,OAAO,KAAK,MAAM,MAAM;AAAA;AAAA,EAE5B;AAAA;AAGF,SAAS,qBAAwD,CAAC,SAGvC;AAAA,EACzB,OAAO;AAAA,SACC,MAAK,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG;AAAA,MACxC,QAAQ,eAAe;AAAA,MACvB,MAAM,YAAY,OAAO,WAAW;AAAA,MACpC,IAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,IAAI;AAAA,QACzC,MAAM,IAAI,cACR,2BACA,aACA,SACF;AAAA,MACF,MAAM,YACJ,QAAQ,cACP,OAAO,WAAW,cACf,YACA,qBAAoB,GAAG;AAAA,MAC7B,IAAI,CAAC;AAAA,QACH,MAAM,IAAI,cACR,qDACA,sBACA,SACF;AAAA,MAEF,IAAI;AAAA,QACF,QAAQ,SAAS,MAAM,qBACrB,QAAQ,WAAW,OACnB,OACA;AAAA,UACE;AAAA,UACA,QAAQ,UAAU,IAAI,gBAAgB,EAAE;AAAA,UACxC,YAAY;AAAA,UACZ;AAAA,UACA,eAAe;AAAA,QACjB,CACF;AAAA,QAEA,OAAO,KAAK,MAAM,MAAM;AAAA,QAIxB,OAAO,OAAO;AAAA,QACd,IAAI,QAAQ;AAAA,UAAS,MAAM,OAAO;AAAA,QAClC,IAAI,iBAAiB;AAAA,UAAc,MAAM;AAAA,QACzC,MAAM,UAAU,uBAAuB,KAAK;AAAA,QAC5C,MAAM,IAAI,cAAa,QAAQ,SAAS,QAAQ,MAAM,SAAS;AAAA;AAAA;AAAA,EAGrE;AAAA;",
|
|
8
|
-
"debugId": "64AFD24787CC437264756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|