@altertable/data-app 0.59.1
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 +35 -0
- package/CONTRIBUTING.md +83 -0
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/dist/bootstrap.js +388 -0
- package/dist/chunks/contract-14vxdcrs.js +192 -0
- package/dist/chunks/contract-14vxdcrs.js.map +10 -0
- package/dist/chunks/contract-8q35dcyh.js +9 -0
- package/dist/chunks/contract-8q35dcyh.js.map +10 -0
- package/dist/chunks/contract-jksbmt5q.js +133 -0
- package/dist/chunks/contract-jksbmt5q.js.map +10 -0
- package/dist/chunks/contract-mb5nfzwg.js +375 -0
- package/dist/chunks/contract-mb5nfzwg.js.map +12 -0
- package/dist/chunks/contract-mev09s5v.js +77 -0
- package/dist/chunks/contract-mev09s5v.js.map +10 -0
- package/dist/chunks/contract-nt819swq.js +321 -0
- package/dist/chunks/contract-nt819swq.js.map +11 -0
- package/dist/chunks/contract-ryyf6dme.js +21 -0
- package/dist/chunks/contract-ryyf6dme.js.map +10 -0
- package/dist/chunks/contract-tkkc28tg.js +213 -0
- package/dist/chunks/contract-tkkc28tg.js.map +12 -0
- package/dist/chunks/contract-tqrf3ykr.js +232 -0
- package/dist/chunks/contract-tqrf3ykr.js.map +11 -0
- package/dist/chunks/contract-wz59z8pq.js +8 -0
- package/dist/chunks/contract-wz59z8pq.js.map +10 -0
- package/dist/client/index.js +27 -0
- package/dist/client/index.js.map +9 -0
- package/dist/core/appearance.js +12 -0
- package/dist/core/appearance.js.map +9 -0
- package/dist/core/config.js +8 -0
- package/dist/core/config.js.map +9 -0
- package/dist/core/contract.js +55 -0
- package/dist/core/contract.js.map +9 -0
- package/dist/core/format.js +18 -0
- package/dist/core/format.js.map +9 -0
- package/dist/embed/index.js +91 -0
- package/dist/embed/index.js.map +11 -0
- package/dist/local.js +332 -0
- package/dist/local.js.map +15 -0
- package/dist/react/embed/index.js +130 -0
- package/dist/react/embed/index.js.map +11 -0
- package/dist/react/index.css +4478 -0
- package/dist/react/index.js +6105 -0
- package/dist/react/index.js.map +90 -0
- package/dist/react.css.d.ts +1 -0
- package/dist/server.js +231 -0
- package/dist/server.js.map +14 -0
- package/dist/types/client/iframe.d.ts +39 -0
- package/dist/types/client/index.d.ts +42 -0
- package/dist/types/client/location.d.ts +15 -0
- package/dist/types/client/messages.d.ts +7 -0
- package/dist/types/client/navigation.d.ts +14 -0
- package/dist/types/client/transport.d.ts +15 -0
- package/dist/types/core/appearance.d.ts +28 -0
- package/dist/types/core/bridge.d.ts +35 -0
- package/dist/types/core/config.d.ts +14 -0
- package/dist/types/core/contract.d.ts +139 -0
- package/dist/types/core/data-view.d.ts +46 -0
- package/dist/types/core/date-range.d.ts +20 -0
- package/dist/types/core/dimension.d.ts +60 -0
- package/dist/types/core/format.d.ts +40 -0
- package/dist/types/core/invariant.d.ts +2 -0
- package/dist/types/core/messages.d.ts +77 -0
- package/dist/types/core/reading.d.ts +17 -0
- package/dist/types/core/variables.d.ts +82 -0
- package/dist/types/embed/bootstrap.d.ts +5 -0
- package/dist/types/embed/host.d.ts +23 -0
- package/dist/types/embed/index.d.ts +11 -0
- package/dist/types/embed/navigation.d.ts +6 -0
- package/dist/types/embed/shell.d.ts +21 -0
- package/dist/types/embed/standalone.d.ts +1 -0
- package/dist/types/react/content.d.ts +23 -0
- package/dist/types/react/embed/bridge.d.ts +12 -0
- package/dist/types/react/embed/index.d.ts +9 -0
- package/dist/types/react/embed/shell.d.ts +10 -0
- package/dist/types/react/hooks.d.ts +831 -0
- package/dist/types/react/index.d.ts +11 -0
- package/dist/types/react/mount.d.ts +11 -0
- package/dist/types/react/ui/AboutData.d.ts +61 -0
- package/dist/types/react/ui/AltertableLogo.d.ts +3 -0
- package/dist/types/react/ui/AppFooter.d.ts +7 -0
- package/dist/types/react/ui/AppHeader.d.ts +12 -0
- package/dist/types/react/ui/AppLayout.d.ts +17 -0
- package/dist/types/react/ui/AppScope.d.ts +8 -0
- package/dist/types/react/ui/AppToolbar.d.ts +32 -0
- package/dist/types/react/ui/Breakdown.d.ts +14 -0
- package/dist/types/react/ui/Button.d.ts +12 -0
- package/dist/types/react/ui/Checkbox.d.ts +11 -0
- package/dist/types/react/ui/Combobox.d.ts +43 -0
- package/dist/types/react/ui/ComparisonVisual.d.ts +15 -0
- package/dist/types/react/ui/ContentSkeleton.d.ts +8 -0
- package/dist/types/react/ui/DataApp.d.ts +56 -0
- package/dist/types/react/ui/DataBoundary.d.ts +16 -0
- package/dist/types/react/ui/DataSection.d.ts +28 -0
- package/dist/types/react/ui/DataTable.d.ts +28 -0
- package/dist/types/react/ui/DataViewToast.d.ts +12 -0
- package/dist/types/react/ui/DataWidget.d.ts +39 -0
- package/dist/types/react/ui/DateRangePicker.d.ts +29 -0
- package/dist/types/react/ui/DateTimeTooltip.d.ts +10 -0
- package/dist/types/react/ui/DimensionPicker.d.ts +11 -0
- package/dist/types/react/ui/EmptyState.d.ts +9 -0
- package/dist/types/react/ui/GettingStarted.d.ts +9 -0
- package/dist/types/react/ui/GlossaryDefinition.d.ts +8 -0
- package/dist/types/react/ui/GlossaryExplanation.d.ts +17 -0
- package/dist/types/react/ui/GradientScroll.d.ts +16 -0
- package/dist/types/react/ui/Grid.d.ts +13 -0
- package/dist/types/react/ui/GridItem.d.ts +7 -0
- package/dist/types/react/ui/HelpPopover.d.ts +24 -0
- package/dist/types/react/ui/IconButton.d.ts +19 -0
- package/dist/types/react/ui/InspectionContext.d.ts +10 -0
- package/dist/types/react/ui/Kbd.d.ts +8 -0
- package/dist/types/react/ui/LiveControl.d.ts +11 -0
- package/dist/types/react/ui/MetricWidget.d.ts +48 -0
- package/dist/types/react/ui/PeriodSummary.d.ts +17 -0
- package/dist/types/react/ui/PresentStory.d.ts +34 -0
- package/dist/types/react/ui/QueryList.d.ts +15 -0
- package/dist/types/react/ui/Ranking.d.ts +14 -0
- package/dist/types/react/ui/RefreshControl.d.ts +11 -0
- package/dist/types/react/ui/RefreshRegion.d.ts +10 -0
- package/dist/types/react/ui/RequestHint.d.ts +21 -0
- package/dist/types/react/ui/SearchField.d.ts +23 -0
- package/dist/types/react/ui/SearchInput.d.ts +9 -0
- package/dist/types/react/ui/SearchMatch.d.ts +7 -0
- package/dist/types/react/ui/SelectableBarChart.d.ts +16 -0
- package/dist/types/react/ui/SelectionMark.d.ts +5 -0
- package/dist/types/react/ui/Sheet.d.ts +19 -0
- package/dist/types/react/ui/Skeleton.d.ts +6 -0
- package/dist/types/react/ui/Stack.d.ts +8 -0
- package/dist/types/react/ui/StatusPanel.d.ts +11 -0
- package/dist/types/react/ui/TableWidget.d.ts +57 -0
- package/dist/types/react/ui/Tabs.d.ts +6 -0
- package/dist/types/react/ui/ThemeSelector.d.ts +13 -0
- package/dist/types/react/ui/Tooltip.d.ts +23 -0
- package/dist/types/react/ui/UpdatedAt.d.ts +10 -0
- package/dist/types/react/ui/VariableBar.d.ts +7 -0
- package/dist/types/react/ui/VisualizationWidget.d.ts +52 -0
- package/dist/types/react/ui/WidgetDisclosure.d.ts +7 -0
- package/dist/types/react/ui/WidgetEvidence.d.ts +12 -0
- package/dist/types/react/ui/WidgetViewTabs.d.ts +17 -0
- package/dist/types/react/ui/chartColor.d.ts +1 -0
- package/dist/types/react/ui/classNames.d.ts +1 -0
- package/dist/types/react/ui/comparison.d.ts +30 -0
- package/dist/types/react/ui/data-context.d.ts +65 -0
- package/dist/types/react/ui/data-identifiers.d.ts +33 -0
- package/dist/types/react/ui/icons.d.ts +42 -0
- package/dist/types/react/ui/index.d.ts +129 -0
- package/dist/types/react/ui/metric.d.ts +11 -0
- package/dist/types/react/ui/search.d.ts +6 -0
- package/dist/types/react/ui/searchItems.d.ts +34 -0
- package/dist/types/react/ui/shortcuts.d.ts +32 -0
- package/dist/types/react/ui/story.d.ts +20 -0
- package/dist/types/react/ui/variables.d.ts +36 -0
- package/dist/types/react/ui/widget-views.d.ts +3 -0
- package/dist/types/react/view-controls.d.ts +17 -0
- package/dist/types/react/view.d.ts +49 -0
- package/dist/types/server/handler.d.ts +13 -0
- package/dist/types/server/index.d.ts +7 -0
- package/dist/types/server/local.d.ts +17 -0
- package/docs/app-authoring.md +26 -0
- package/docs/appearance.md +30 -0
- package/docs/bootstrap.md +76 -0
- package/docs/client.md +127 -0
- package/docs/config.md +21 -0
- package/docs/contract.md +95 -0
- package/docs/embed.md +98 -0
- package/docs/format.md +29 -0
- package/docs/react-embed.md +57 -0
- package/docs/react-styles.md +17 -0
- package/docs/react.md +248 -0
- package/docs/releasing.md +74 -0
- package/docs/server-bun.md +26 -0
- package/docs/server.md +47 -0
- package/docs/starter-agent-instructions.md +51 -0
- package/package.json +124 -0
package/docs/react.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# React
|
|
2
|
+
|
|
3
|
+
Import hooks, components, and UI helpers from `@altertable/data-app/react`.
|
|
4
|
+
Import `@altertable/data-app/react/styles.css` once in the browser entry.
|
|
5
|
+
React 19.2 or newer and React DOM 19.2 or newer are peer dependencies.
|
|
6
|
+
|
|
7
|
+
`mountDataApp({ config, component })` mounts into `#root`, sets the document
|
|
8
|
+
title and language, attaches navigation to an available iframe transport, and
|
|
9
|
+
installs `DataAppProvider`. When mounting through another
|
|
10
|
+
framework, wrap the app in `DataAppProvider` yourself.
|
|
11
|
+
|
|
12
|
+
## Find UI by task
|
|
13
|
+
|
|
14
|
+
All of these APIs are exported from `/react`. Each component's stylesheet lives beside its implementation.
|
|
15
|
+
|
|
16
|
+
| Task | Start here | Related APIs |
|
|
17
|
+
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| Primary request and page shell | [DataApp](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/DataApp.tsx) | AppLayout, AppHeader, AppToolbar, AppFooter, AppScope, ThemeToggle |
|
|
19
|
+
| Initial connection check | [GettingStarted](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/GettingStarted.tsx) | Pair with `connectionCheck()` from `/contract` |
|
|
20
|
+
| Arrange content | [Grid](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Grid.tsx), [Stack](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Stack.tsx) | DataWidget |
|
|
21
|
+
| Show a key number | [MetricWidget](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/MetricWidget.tsx) | ComparisonVisual |
|
|
22
|
+
| Show charts and collections | [VisualizationWidget](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/VisualizationWidget.tsx), [TableWidget](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/TableWidget.tsx) | DataTable, Ranking, Breakdown, chartColor |
|
|
23
|
+
| Handle a request's loading, error, and stale data | [DataSection](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/DataSection.tsx) | DataBoundary, DataViewToast, EmptyState, StatusPanel, Skeleton |
|
|
24
|
+
| Show freshness and refresh | [UpdatedAt](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/UpdatedAt.tsx), [AppToolbar](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/AppToolbar.tsx) | RefreshRegion, LiveControl |
|
|
25
|
+
| Bind filters to the URL | [variables](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/variables.ts), [DateRangePicker](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/DateRangePicker.tsx) | Combobox, PeriodSummary, Tabs, useViewTab |
|
|
26
|
+
| Search a loaded collection | [searchItems](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/searchItems.ts), [SearchMatch](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/SearchMatch.tsx) | SearchField |
|
|
27
|
+
| Explain context, glossary, and queries | [AboutData](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/AboutData.tsx), [DataContext](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/data-context.ts) | [GlossaryDefinition](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/GlossaryDefinition.tsx), GlossaryExplanation, [defineDataIdentifiers](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/data-identifiers.tsx) |
|
|
28
|
+
| Present loaded findings | [PresentStory](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/PresentStory.tsx) | StoryFinding |
|
|
29
|
+
| Build custom controls and overlays | [Button](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Button.tsx), [Sheet](https://github.com/altertable-ai/data-app/blob/main/src/react/ui/Sheet.tsx) | IconButton, Tooltip, HelpPopover, Kbd |
|
|
30
|
+
|
|
31
|
+
## Bound views and widgets
|
|
32
|
+
|
|
33
|
+
| Definition | Runtime owns |
|
|
34
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
35
|
+
| `defineOperation` | Input/output validation, check inputs, query limits and cancellation; `query(name, sql)` accepts registered names and records executed evidence. |
|
|
36
|
+
| `defineDataView` | URL variables, operation input, emptiness and the primary date binding. `useView` connects the result and controls to `DataApp`. |
|
|
37
|
+
| `DataApp` | Header, variable bar, refresh state, stale-result notice and dimming, default inspection empty states. |
|
|
38
|
+
| `view.content` | One loading/ready layout. `result.select` never evaluates loading data; `result.metric` binds comparisons to the displayed input. |
|
|
39
|
+
| `context.metric` | Label, numeric format, glossary evidence and optional direction of improvement. |
|
|
40
|
+
| `WidgetViewTabs` | Valid, unique selection IDs and a required empty state per tab. |
|
|
41
|
+
|
|
42
|
+
SQL and business definitions belong to the app. Hosted adapters authorize every request; local development can use the CLI proxy. SQL disclosure also requires server permission.
|
|
43
|
+
|
|
44
|
+
A measured zero and unavailable data have different meanings. Metric readings use `null` for an unavailable previous value. The app defines whether a result is empty. `Breakdown` shows parts of a total; `Ranking` scales against its largest value. Percent formats accept ratios.
|
|
45
|
+
|
|
46
|
+
## Bind a view
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { createDataClient } from '@altertable/data-app/client';
|
|
50
|
+
import {
|
|
51
|
+
createDataHooks,
|
|
52
|
+
dateRangeVariable,
|
|
53
|
+
DataApp,
|
|
54
|
+
Grid,
|
|
55
|
+
MetricWidget,
|
|
56
|
+
VisualizationWidget,
|
|
57
|
+
Ranking,
|
|
58
|
+
} from '@altertable/data-app/react';
|
|
59
|
+
import type { operations } from '#app/operations.ts';
|
|
60
|
+
import { calendar } from '#app/contracts.ts';
|
|
61
|
+
import { dataContext, actions } from '#app/data-context.tsx';
|
|
62
|
+
import config from '#config';
|
|
63
|
+
|
|
64
|
+
const period = dateRangeVariable({
|
|
65
|
+
key: 'period',
|
|
66
|
+
contract: calendar,
|
|
67
|
+
comparison: true,
|
|
68
|
+
defaultValue: { kind: 'preset', id: 'last-30' },
|
|
69
|
+
});
|
|
70
|
+
const { defineDataView, useView } =
|
|
71
|
+
createDataHooks(createDataClient<typeof operations>());
|
|
72
|
+
const activityView = defineDataView({
|
|
73
|
+
operation: 'activity',
|
|
74
|
+
variables: { period },
|
|
75
|
+
input: ({ period }) => period,
|
|
76
|
+
date: { variable: 'period', input: input => input },
|
|
77
|
+
isEmpty: data => data.features.length === 0,
|
|
78
|
+
empty: { title: 'No activity in this range' },
|
|
79
|
+
});
|
|
80
|
+
const featureEvidence = dataContext.evidence({
|
|
81
|
+
id: 'feature-use',
|
|
82
|
+
queryNames: [dataContext.queryNames.activity],
|
|
83
|
+
});
|
|
84
|
+
const content = activityView.content(result => (
|
|
85
|
+
<Grid columns={2}>
|
|
86
|
+
<MetricWidget
|
|
87
|
+
metric={actions}
|
|
88
|
+
reading={result.metric(data => ({
|
|
89
|
+
current: data.count,
|
|
90
|
+
previous: data.previousCount,
|
|
91
|
+
}))}
|
|
92
|
+
/>
|
|
93
|
+
<VisualizationWidget
|
|
94
|
+
title="Feature use"
|
|
95
|
+
evidence={featureEvidence}
|
|
96
|
+
reading={result.select(data => data.features)}
|
|
97
|
+
isEmpty={features => features.length === 0}
|
|
98
|
+
empty={{ title: 'No features' }}
|
|
99
|
+
skeleton={{ variant: 'ranking', rows: 6 }}
|
|
100
|
+
>
|
|
101
|
+
{features => <Ranking items={features} />}
|
|
102
|
+
</VisualizationWidget>
|
|
103
|
+
</Grid>
|
|
104
|
+
));
|
|
105
|
+
function App() {
|
|
106
|
+
const activity = useView(activityView);
|
|
107
|
+
return (
|
|
108
|
+
<DataApp
|
|
109
|
+
config={config}
|
|
110
|
+
dataContext={dataContext}
|
|
111
|
+
request={activity}
|
|
112
|
+
{...content}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The `date` binding identifies the controlling variable and extracts its range from the operation input. Nested inputs use, for example, `input: (input) => input.period`. The runtime rejects mappings that silently change the selected range or comparison. Non-date views supply `describeInput`; date views can override it when other inputs also need describing.
|
|
119
|
+
|
|
120
|
+
The shared calendar lives in a browser-safe module:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { defineDateRangeContract } from '@altertable/data-app/contract';
|
|
124
|
+
export const calendar = defineDateRangeContract({
|
|
125
|
+
minDate: '2026-01-01',
|
|
126
|
+
maxRangeDays: 90,
|
|
127
|
+
timeZone: 'UTC',
|
|
128
|
+
});
|
|
129
|
+
// Server operation: input: calendar.parseRequest
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`useView` generates controls for date, text and fixed-option select variables; custom controls use `result.variables.bind(name)`. `input` chooses which variables reach the operation, so local search can stay local. The callback in `view.content` receives the displayed result, including its original input during refreshes and failures. Hooks belong in the enclosing component.
|
|
133
|
+
|
|
134
|
+
`TableWidget` also accepts `reading={result.select((data) => data.rows)}` and optional `skeletonRows`. Columns and empty states are declared once for both loading and ready layouts. Bounded tables default to 10 rows per page with a bottom footer shared with inspection. Set `pagination={{ pageSize: 8 }}` to change the page size or `pagination={false}` to show all rows. Search runs before pagination; the footer counts only the supplied rows. `limit` remains a separate, mutually exclusive display cap. Large catalogs need query-backed pagination with a stable sort and total count.
|
|
135
|
+
|
|
136
|
+
Bound `VisualizationWidget` and `TableWidget` calls require `evidence` from `context.evidence(...)`. A bound `MetricWidget` gets evidence from its metric definition. Evidence must name at least one glossary entry or query. Static widgets may omit it.
|
|
137
|
+
|
|
138
|
+
`MetricWidget` and `ComparisonVisual` both accept the same `metric` and `reading`. The comparison is enabled by the displayed result's range. The definition supplies formatting and evidence; a reading cannot override those or provide a second value. `favorableDirection` is optional; changes are neutral until the author defines whether up or down is favorable.
|
|
139
|
+
|
|
140
|
+
`defineDataContent` remains available for manually managed requests. Its optional `{ date: (input) => rangeRequest }` binds comparison readings. `DataSection` handles independent requests. Low-level widgets, tabs and layout components remain available for custom interfaces.
|
|
141
|
+
|
|
142
|
+
## Bind evidence
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const queries = defineQueryNames({ activity: 'feature-activity' });
|
|
146
|
+
// In the server operation: queryNames: queries
|
|
147
|
+
const identifiers = defineDataIdentifiers({
|
|
148
|
+
tables: {
|
|
149
|
+
events: {
|
|
150
|
+
catalog: 'product_analytics',
|
|
151
|
+
schema: 'analytics',
|
|
152
|
+
name: 'events',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
columns: { identity: { table: 'events', name: 'identity_uuid' } },
|
|
156
|
+
});
|
|
157
|
+
const { DataIdentifier } = identifiers;
|
|
158
|
+
const context = createDataContext(queries)({
|
|
159
|
+
identifiers: identifiers.definitions,
|
|
160
|
+
description: (
|
|
161
|
+
<>
|
|
162
|
+
Explore activity in <DataIdentifier id="tables.events" />.
|
|
163
|
+
</>
|
|
164
|
+
),
|
|
165
|
+
glossary: {
|
|
166
|
+
identities: {
|
|
167
|
+
term: 'Tracked identities',
|
|
168
|
+
definition: (
|
|
169
|
+
<>
|
|
170
|
+
Distinct <DataIdentifier id="columns.events.identity" /> values.
|
|
171
|
+
</>
|
|
172
|
+
),
|
|
173
|
+
queryNames: [queries.activity],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
const evidence = context.evidence({
|
|
178
|
+
id: 'identities',
|
|
179
|
+
glossaryIds: ['identities'],
|
|
180
|
+
queryNames: [queries.activity],
|
|
181
|
+
});
|
|
182
|
+
const actions = context.metric({
|
|
183
|
+
id: 'actions',
|
|
184
|
+
glossaryId: 'identities',
|
|
185
|
+
label: 'Tracked identities',
|
|
186
|
+
format: { kind: 'count' },
|
|
187
|
+
});
|
|
188
|
+
const finding = context.finding({
|
|
189
|
+
id: 'activity',
|
|
190
|
+
headline: 'What people do',
|
|
191
|
+
visual: <ActivityChart />,
|
|
192
|
+
evidence: { id: 'activity-evidence', queryNames: [queries.activity] },
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Import `defineQueryNames` from `/contract` and the context/identifier factories from `/react`. Use the same registry in `defineOperation({ queryNames: queries, ... })`. Unknown glossary/query references fail type checks and registry validation; the server also validates returned query names. Physical source identity stays in the identifier registry for future linking.
|
|
197
|
+
|
|
198
|
+
## Authoring constraints
|
|
199
|
+
|
|
200
|
+
- Date views declare `date: { variable: "period", input: (input) => input }`, or supply `describeInput` for a non-date view.
|
|
201
|
+
- Numeric metrics use `value={count} format={{ kind: "count" }}`. Custom formatted JSX or strings use `content={...}` instead of `value`.
|
|
202
|
+
- Supply `empty` to secondary `DataSection` requests or pass a bound `useView` result. A primary `DataApp` accepts it either from `useView` or as an explicit prop.
|
|
203
|
+
- For alternate views of the same bound result, pass `views={[{ id, label, render }]}` and `viewLabel` to `VisualizationWidget`. Its required `isEmpty` and `empty` apply to the whole result; the widget owns selection. Use `WidgetViewTabs` directly only when views have independent empty states.
|
|
204
|
+
- Variable URL keys cannot use `view`, `about`, `tab`, `present`, or `step`.
|
|
205
|
+
|
|
206
|
+
## Time views and dimension filters
|
|
207
|
+
|
|
208
|
+
`createDataHooks(client).defineTimeView` owns the `period` variable, calendar
|
|
209
|
+
controls, and displayed-period label. Declare `time: { contract, defaultValue }`,
|
|
210
|
+
an operation, `isEmpty`, and `empty`. With no additional variables, its default
|
|
211
|
+
input is the calendar request. With additional variables, it is `{ period, ...variables }`.
|
|
212
|
+
Supply an `input` mapper for a different operation shape and `bindings` to extract
|
|
213
|
+
nested period or dimension inputs. Mappings must preserve the selected values.
|
|
214
|
+
|
|
215
|
+
`dimensionFilter` requires exactly one option source: fixed `options` or a `facet`.
|
|
216
|
+
Use `defineFacetFilter` to bind a facet operation and its typed input. The generated
|
|
217
|
+
`DimensionPicker` preserves cached options during refresh and failure, offers
|
|
218
|
+
missing values separately, and retains selected values absent from a result with
|
|
219
|
+
zero counts. `SelectableBarChart` can share controlled selection with the picker.
|
|
220
|
+
|
|
221
|
+
## Findings and inspection
|
|
222
|
+
|
|
223
|
+
`DataWidget` composes a body, toolbar feedback, and footer. Widgets and their
|
|
224
|
+
inspection sheets render the same visual and controls. Keep interactive state
|
|
225
|
+
above both mounts when authoring custom children.
|
|
226
|
+
|
|
227
|
+
`DataApp.story` receives the displayed snapshot, including its original input
|
|
228
|
+
during refresh or failure. Return one to four `StoryFinding` values with unique
|
|
229
|
+
IDs and registered evidence. `PresentStory` presents those findings directly;
|
|
230
|
+
`context.finding` validates their evidence against the context registry.
|
|
231
|
+
|
|
232
|
+
### Migration from the earlier runtime
|
|
233
|
+
|
|
234
|
+
- `DataWidget` replaces the internal `DataPanel` shell and `StorySection` layout.
|
|
235
|
+
- `PresentStory` replaces `PlayStory`; provide `findings` with explicit `evidence`.
|
|
236
|
+
- `context.finding` replaces `context.storyStep`.
|
|
237
|
+
- Table pagination is enabled by default; use `pagination={false}` for complete tables.
|
|
238
|
+
|
|
239
|
+
## Component gallery
|
|
240
|
+
|
|
241
|
+
Contributors can preview `/gallery` on the browser fixture server with
|
|
242
|
+
`bun browser-tests/server.ts`. The gallery covers control, widget, request,
|
|
243
|
+
inspection, and narrow-layout defaults. `bun run test:browser` verifies desktop
|
|
244
|
+
and phone interactions. Fixtures are excluded from the published package.
|
|
245
|
+
|
|
246
|
+
Previous data and evidence are retained only when the input changes within the
|
|
247
|
+
same operation. Switching to another operation shows its own cached response or
|
|
248
|
+
an initial loading/error state; it never inherits another operation's result.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
This repository releases one npm package, `@altertable/data-app`. All public
|
|
4
|
+
entries share its version. Release Please manages `package.json`, the release
|
|
5
|
+
manifest, `CHANGELOG.md`, GitHub tags, and release notes.
|
|
6
|
+
|
|
7
|
+
## Repository setup
|
|
8
|
+
|
|
9
|
+
Enable GitHub Actions and allow it to create pull requests. Configure a
|
|
10
|
+
`RELEASE_PLEASE_TOKEN` repository secret with a fine-grained PAT that can write
|
|
11
|
+
repository contents and pull requests. The
|
|
12
|
+
workflow falls back to `GITHUB_TOKEN`, but PRs created or updated with that token
|
|
13
|
+
do not trigger subsequent GitHub Actions workflows. Use the dedicated token
|
|
14
|
+
when requiring CI checks on release PRs.
|
|
15
|
+
|
|
16
|
+
Configure the npm trusted publisher for `@altertable/data-app`:
|
|
17
|
+
|
|
18
|
+
| Setting | Value |
|
|
19
|
+
| ----------------- | --------------------------------------------------- |
|
|
20
|
+
| Provider | GitHub Actions |
|
|
21
|
+
| Organization | `altertable-ai` |
|
|
22
|
+
| Repository | `data-app` |
|
|
23
|
+
| Workflow filename | `release-please.yml` |
|
|
24
|
+
| Environment | Leave empty; the workflow has no GitHub environment |
|
|
25
|
+
|
|
26
|
+
The publish job grants `id-token: write`, uses npm 11.18.0, and publishes with
|
|
27
|
+
provenance. It uses neither `NPM_TOKEN` nor `NODE_AUTH_TOKEN`. Do not add
|
|
28
|
+
`registry-url` to its `setup-node` step: that creates token authentication
|
|
29
|
+
configuration that interferes with OIDC. See
|
|
30
|
+
[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
|
|
31
|
+
|
|
32
|
+
If the npm package does not exist yet, establish it and configure its trusted
|
|
33
|
+
publisher before expecting automated publication to succeed. Package and npm
|
|
34
|
+
account setup are maintainer actions outside repository CI.
|
|
35
|
+
|
|
36
|
+
The manifest starts at the imported runtime version `0.59.1`. This records the
|
|
37
|
+
version baseline, not a claim that it is published. Release Please computes the
|
|
38
|
+
next version from release-relevant commits on `main`.
|
|
39
|
+
|
|
40
|
+
## Release flow
|
|
41
|
+
|
|
42
|
+
1. Merge Conventional Commits into `main`.
|
|
43
|
+
2. Release Please opens or updates a release PR with the version and changelog.
|
|
44
|
+
3. Review and merge that PR. Release Please creates its `v<version>` tag and
|
|
45
|
+
GitHub release.
|
|
46
|
+
4. The workflow resolves the tag to a commit SHA and runs the same source,
|
|
47
|
+
package, and workflow checks against that revision. The publish job checks out
|
|
48
|
+
that SHA, confirms the tag has not moved, builds, and publishes to npm.
|
|
49
|
+
|
|
50
|
+
GitHub release creation precedes the publication checks. A failed check blocks
|
|
51
|
+
npm publication and leaves the GitHub release available for a retry.
|
|
52
|
+
|
|
53
|
+
The publish job checks that the tag matches `package.json`, that the GitHub
|
|
54
|
+
release exists and is not a draft, and that GitHub OIDC is available. An existing
|
|
55
|
+
npm version is skipped. Registry errors other than a missing version stop the
|
|
56
|
+
job rather than being treated as permission to publish.
|
|
57
|
+
|
|
58
|
+
CodeQL and dependency review are separate workflows. Branch protection can
|
|
59
|
+
require `Source and packed package`, `Validate workflows`, and `Validate PR
|
|
60
|
+
title`, plus the security checks your repository policy requires.
|
|
61
|
+
|
|
62
|
+
## Retry a failed publication
|
|
63
|
+
|
|
64
|
+
Run the **Release Please** workflow manually from `main` and enter the existing
|
|
65
|
+
GitHub release tag in its `tag` input. The workflow verifies and publishes that
|
|
66
|
+
tag, rather than the current contents of `main`. Already-published versions are
|
|
67
|
+
skipped, so a retry after a successful publication is safe.
|
|
68
|
+
|
|
69
|
+
Workflow shell logic lives in `scripts/release/`; Node and Bun versions come
|
|
70
|
+
from `.node-version` and `.bun-version`. The npm version is pinned in
|
|
71
|
+
`scripts/release/setup-npm.sh`.
|
|
72
|
+
|
|
73
|
+
To validate repository configuration locally, run `bun run check:workflows` and
|
|
74
|
+
`bun run check`. Local checks do not invoke Release Please or publish to npm.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Local Bun server
|
|
2
|
+
|
|
3
|
+
Import `serveLocalApp` and `localLakehouse` from
|
|
4
|
+
`@altertable/data-app/server/bun`. This entry requires Bun; install `@types/bun`
|
|
5
|
+
when typechecking a Bun app.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { serveLocalApp } from '@altertable/data-app/server/bun';
|
|
9
|
+
import page from './index.html';
|
|
10
|
+
import { operations } from './operations';
|
|
11
|
+
|
|
12
|
+
serveLocalApp({ page, operations, title: 'Activity' });
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The server binds to `127.0.0.1`. Its default port is `25837`, overridable through
|
|
16
|
+
`PORT` or the `port` option. It serves the Bun HTML bundle and operation requests.
|
|
17
|
+
|
|
18
|
+
`localLakehouse()` reads the CLI proxy URL and token from
|
|
19
|
+
`ALTERTABLE_DATA_PROXY_URL` and `ALTERTABLE_DATA_PROXY_TOKEN`. It also supports
|
|
20
|
+
server-only `ALTERTABLE_LAKEHOUSE_USERNAME` and
|
|
21
|
+
`ALTERTABLE_LAKEHOUSE_PASSWORD`, with an optional `ALTERTABLE_API_BASE`.
|
|
22
|
+
Missing credentials fail the query. Keep these variables out of browser code.
|
|
23
|
+
|
|
24
|
+
Local serving permits SQL disclosure and does not authenticate hosted viewers.
|
|
25
|
+
Use [the portable server handler](server.md) with per-request authorization when
|
|
26
|
+
hosting an app for other people.
|
package/docs/server.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Server
|
|
2
|
+
|
|
3
|
+
Import `createDataHandler` and the `RequestAccess` type from
|
|
4
|
+
`@altertable/data-app/server`. The handler accepts a Web `Request` and returns a
|
|
5
|
+
`Promise<Response>`. This entry can be imported in Node and Bun without loading
|
|
6
|
+
React or the Bun local-development adapter.
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createDataHandler } from '@altertable/data-app/server';
|
|
10
|
+
import { operations } from './operations';
|
|
11
|
+
|
|
12
|
+
export const handleDataRequest = createDataHandler(
|
|
13
|
+
operations,
|
|
14
|
+
async (request, operation) => {
|
|
15
|
+
const viewer = await authenticate(request);
|
|
16
|
+
return {
|
|
17
|
+
lakehouse: await lakehouseFor(viewer, operation),
|
|
18
|
+
canDiscloseSql: false,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The app supplies `authenticate` and `lakehouseFor`, then routes `/api/data/*`
|
|
25
|
+
requests to the handler. Authorize every viewer and operation, and scope the
|
|
26
|
+
returned lakehouse to the viewer's permitted data. Origin and Fetch Metadata
|
|
27
|
+
checks reject cross-site browser requests; they do not authenticate viewers.
|
|
28
|
+
|
|
29
|
+
The handler validates operation input and output, enforces query row and duration
|
|
30
|
+
bounds, propagates cancellation, and returns request IDs with errors. SQL is
|
|
31
|
+
disclosed only when both the operation policy and `canDiscloseSql` allow it.
|
|
32
|
+
Keep credentials and operation implementations on the server.
|
|
33
|
+
|
|
34
|
+
See [operation contracts](contract.md), the [client](client.md), and the
|
|
35
|
+
[local Bun adapter](server-bun.md).
|
|
36
|
+
|
|
37
|
+
## Request input limits
|
|
38
|
+
|
|
39
|
+
JSON request bodies are limited to **16,384 encoded bytes**, including multibyte
|
|
40
|
+
UTF-8 content. The handler counts bytes as it reads and cancels remaining
|
|
41
|
+
consumption on overflow; it does not rely on `Content-Length`. Authorization
|
|
42
|
+
runs before reading. Request cancellation interrupts waiting reads. Invalid or
|
|
43
|
+
interrupted bodies return `400/invalid_input`; oversized bodies return
|
|
44
|
+
`413/input_too_large`.
|
|
45
|
+
|
|
46
|
+
`maxDurationMs` bounds execution after input parsing. It does not add a separate
|
|
47
|
+
body-read deadline. The hosting runtime controls the size of individual chunks.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Starter agent instructions
|
|
2
|
+
|
|
3
|
+
The package ships `AGENTS.md`, public documentation in `docs/`, and declarations
|
|
4
|
+
referenced by its export map. Its agent guide describes package usage and includes
|
|
5
|
+
a separate section for contributors working in the package source repository.
|
|
6
|
+
|
|
7
|
+
A starter application's `AGENTS.md` owns its file paths, commands, and analysis
|
|
8
|
+
context. Keep it in the consuming app and explicitly direct agents to the
|
|
9
|
+
installed package guide. Dependency instructions are not guaranteed to be loaded
|
|
10
|
+
automatically: for example, [Codex discovers project instructions along the path
|
|
11
|
+
from the project root to its working directory](https://learn.chatgpt.com/docs/agent-configuration/agents-md),
|
|
12
|
+
not by scanning every dependency.
|
|
13
|
+
|
|
14
|
+
These installed docs replace references to the old vendored `.altertable/runtime`
|
|
15
|
+
layout for apps using this package.
|
|
16
|
+
|
|
17
|
+
Adapt this template to the starter's actual files and scripts:
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
# Build this data app
|
|
21
|
+
|
|
22
|
+
Inspect source data, time coverage, and existing definitions before choosing an
|
|
23
|
+
exploration. Build conclusions from observed results; do not present association
|
|
24
|
+
as cause. Keep copy concise and relevant to the reader.
|
|
25
|
+
|
|
26
|
+
Read `node_modules/@altertable/data-app/AGENTS.md` first, then
|
|
27
|
+
`node_modules/@altertable/data-app/docs/app-authoring.md`.
|
|
28
|
+
|
|
29
|
+
| Task | App-owned files | Package documentation |
|
|
30
|
+
| ---------------------------------------- | ------------------------------------------- | -------------------------------------- |
|
|
31
|
+
| Define queries and inputs | `src/operations.ts`, shared input contracts | `docs/contract.md`, `docs/server.md` |
|
|
32
|
+
| Build views, filters, and request states | `src/App.tsx` | `docs/react.md` |
|
|
33
|
+
| Explain terms and query evidence | `src/data-context.ts` or `.tsx` | `docs/react.md#bind-evidence` |
|
|
34
|
+
| Change identity and brand | App configuration | `docs/config.md`, `docs/appearance.md` |
|
|
35
|
+
| Configure local serving or hosting | `src/server.ts` | `docs/server-bun.md`, `docs/server.md` |
|
|
36
|
+
|
|
37
|
+
Package documentation paths above are relative to
|
|
38
|
+
`node_modules/@altertable/data-app/`.
|
|
39
|
+
|
|
40
|
+
Edit app-owned source and import public package entries. Keep SQL and credentials
|
|
41
|
+
on the server, and import operation types with `import type` in browser code.
|
|
42
|
+
Do not edit installed package files. The connectivity screen is scaffolding,
|
|
43
|
+
not an example analysis.
|
|
44
|
+
|
|
45
|
+
Run the app's typecheck, lint, and build scripts. Inspect its findings and
|
|
46
|
+
interactions at phone and desktop widths, including loading, empty, error, and
|
|
47
|
+
stale states. Verify that the exploration answers the user's question.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The CLI's existing starter remains owned by its repository. Updating its template
|
|
51
|
+
and checks to consume the published package is a separate migration.
|
package/package.json
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@altertable/data-app",
|
|
3
|
+
"version": "0.59.1",
|
|
4
|
+
"description": "Contracts, transport, and React UI for Altertable data apps",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"altertable",
|
|
7
|
+
"analytics",
|
|
8
|
+
"data-app",
|
|
9
|
+
"react",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/altertable-ai/data-app",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/altertable-ai/data-app/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Altertable",
|
|
19
|
+
"url": "https://altertable.ai"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/altertable-ai/data-app.git"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"docs",
|
|
28
|
+
"README.md",
|
|
29
|
+
"CONTRIBUTING.md",
|
|
30
|
+
"AGENTS.md"
|
|
31
|
+
],
|
|
32
|
+
"type": "module",
|
|
33
|
+
"exports": {
|
|
34
|
+
"./contract": {
|
|
35
|
+
"types": "./dist/types/core/contract.d.ts",
|
|
36
|
+
"import": "./dist/core/contract.js"
|
|
37
|
+
},
|
|
38
|
+
"./config": {
|
|
39
|
+
"types": "./dist/types/core/config.d.ts",
|
|
40
|
+
"import": "./dist/core/config.js"
|
|
41
|
+
},
|
|
42
|
+
"./format": {
|
|
43
|
+
"types": "./dist/types/core/format.d.ts",
|
|
44
|
+
"import": "./dist/core/format.js"
|
|
45
|
+
},
|
|
46
|
+
"./appearance": {
|
|
47
|
+
"types": "./dist/types/core/appearance.d.ts",
|
|
48
|
+
"import": "./dist/core/appearance.js"
|
|
49
|
+
},
|
|
50
|
+
"./client": {
|
|
51
|
+
"types": "./dist/types/client/index.d.ts",
|
|
52
|
+
"import": "./dist/client/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./server": {
|
|
55
|
+
"types": "./dist/types/server/index.d.ts",
|
|
56
|
+
"import": "./dist/server.js"
|
|
57
|
+
},
|
|
58
|
+
"./server/bun": {
|
|
59
|
+
"types": "./dist/types/server/local.d.ts",
|
|
60
|
+
"import": "./dist/local.js"
|
|
61
|
+
},
|
|
62
|
+
"./react": {
|
|
63
|
+
"types": "./dist/types/react/index.d.ts",
|
|
64
|
+
"import": "./dist/react/index.js"
|
|
65
|
+
},
|
|
66
|
+
"./react/styles.css": {
|
|
67
|
+
"types": "./dist/react.css.d.ts",
|
|
68
|
+
"default": "./dist/react/index.css"
|
|
69
|
+
},
|
|
70
|
+
"./embed": {
|
|
71
|
+
"types": "./dist/types/embed/index.d.ts",
|
|
72
|
+
"import": "./dist/embed/index.js"
|
|
73
|
+
},
|
|
74
|
+
"./bootstrap": {
|
|
75
|
+
"default": "./dist/bootstrap.js"
|
|
76
|
+
},
|
|
77
|
+
"./react/embed": {
|
|
78
|
+
"types": "./dist/types/react/embed/index.d.ts",
|
|
79
|
+
"import": "./dist/react/embed/index.js"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"publishConfig": {
|
|
83
|
+
"access": "public",
|
|
84
|
+
"registry": "https://registry.npmjs.org"
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"build": "bun run scripts/build.ts && tsc -p tsconfig.build.json && bun run scripts/build-declarations.ts",
|
|
88
|
+
"typecheck": "tsc --noEmit",
|
|
89
|
+
"lint": "oxlint --type-aware src tests scripts browser-tests",
|
|
90
|
+
"format": "oxfmt .",
|
|
91
|
+
"format:check": "oxfmt --check .",
|
|
92
|
+
"test": "bun test ./tests",
|
|
93
|
+
"check": "bun run typecheck && bun run lint && bun run format:check && bun run test && bun run build && bun run test:package",
|
|
94
|
+
"test:package": "bun run scripts/check-package.ts",
|
|
95
|
+
"check:workflows": "bash scripts/check-workflows.sh",
|
|
96
|
+
"test:browser": "playwright test --config browser-tests/playwright.config.ts",
|
|
97
|
+
"lint:fix": "oxlint --type-aware --fix src tests scripts browser-tests"
|
|
98
|
+
},
|
|
99
|
+
"dependencies": {
|
|
100
|
+
"@floating-ui/react": "0.27.20",
|
|
101
|
+
"@internationalized/date": "3.12.4",
|
|
102
|
+
"@tanstack/react-query": "5.104.0",
|
|
103
|
+
"fuzzysort": "4.0.2",
|
|
104
|
+
"lucide-react": "1.48.0",
|
|
105
|
+
"react-aria-components": "1.21.1"
|
|
106
|
+
},
|
|
107
|
+
"devDependencies": {
|
|
108
|
+
"@playwright/test": "1.63.0",
|
|
109
|
+
"@types/bun": "1.4.2",
|
|
110
|
+
"@types/react": "19.3.0",
|
|
111
|
+
"@types/react-dom": "19.3.0",
|
|
112
|
+
"oxfmt": "0.70.0",
|
|
113
|
+
"oxlint": "1.85.0",
|
|
114
|
+
"oxlint-tsgolint": "7.0.2003",
|
|
115
|
+
"react": "19.3.0",
|
|
116
|
+
"react-dom": "19.3.0",
|
|
117
|
+
"typescript": "7.0.2"
|
|
118
|
+
},
|
|
119
|
+
"peerDependencies": {
|
|
120
|
+
"react": ">=19.2.0",
|
|
121
|
+
"react-dom": ">=19.2.0"
|
|
122
|
+
},
|
|
123
|
+
"packageManager": "bun@1.4.2"
|
|
124
|
+
}
|