@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.
Files changed (138) hide show
  1. package/CONTRIBUTING.md +22 -16
  2. package/dist/chunks/{contract-19ckn3n7.js → index-23dktynd.js} +8 -6
  3. package/dist/chunks/index-23dktynd.js.map +10 -0
  4. package/dist/chunks/{contract-rfbakka4.js → index-ag0cgnvq.js} +169 -35
  5. package/dist/chunks/index-ag0cgnvq.js.map +13 -0
  6. package/dist/chunks/index-dq7b35fn.js +307 -0
  7. package/dist/chunks/index-dq7b35fn.js.map +13 -0
  8. package/dist/chunks/{contract-e11m4y7f.js → index-ev2b5aaf.js} +11 -6
  9. package/dist/chunks/{contract-e11m4y7f.js.map → index-ev2b5aaf.js.map} +3 -3
  10. package/dist/chunks/{contract-awa2d5b1.js → index-f5cnbrjz.js} +19 -2
  11. package/dist/chunks/{contract-awa2d5b1.js.map → index-f5cnbrjz.js.map} +4 -3
  12. package/dist/chunks/{contract-havnjmdr.js → index-gvers8jw.js} +2429 -1110
  13. package/dist/chunks/index-gvers8jw.js.map +109 -0
  14. package/dist/chunks/{contract-13j5zb3c.js → index-ktmvs916.js} +8 -7
  15. package/dist/chunks/index-ktmvs916.js.map +13 -0
  16. package/dist/chunks/index-mt93ff2b.js +28 -0
  17. package/dist/chunks/index-mt93ff2b.js.map +10 -0
  18. package/dist/chunks/{contract-tf8c3qpv.js → index-qd5xgx6y.js} +46 -5
  19. package/dist/chunks/index-qd5xgx6y.js.map +13 -0
  20. package/dist/chunks/{contract-pk603qj7.js → index-x69p7cv7.js} +12 -9
  21. package/dist/chunks/index-x69p7cv7.js.map +10 -0
  22. package/dist/client/index.js +6 -6
  23. package/dist/core/appearance.js +1 -1
  24. package/dist/core/config.js +7 -3
  25. package/dist/core/config.js.map +1 -1
  26. package/dist/core/contract.js +14 -6
  27. package/dist/core/contract.js.map +1 -1
  28. package/dist/core/format.js +1 -1
  29. package/dist/embed/index.js +12 -10
  30. package/dist/embed/index.js.map +4 -4
  31. package/dist/index.js +12 -0
  32. package/dist/index.js.map +9 -0
  33. package/dist/local.js +20 -30
  34. package/dist/local.js.map +7 -8
  35. package/dist/react/embed/index.js +1 -1
  36. package/dist/react/embed/index.js.map +2 -2
  37. package/dist/react/index.js +79 -29
  38. package/dist/react/index.js.map +5 -6
  39. package/dist/react/ui/index.js +1702 -177
  40. package/dist/react/ui/index.js.map +20 -9
  41. package/dist/server.js +13 -26
  42. package/dist/server.js.map +6 -7
  43. package/dist/types/client/annotations.d.ts +4 -0
  44. package/dist/types/client/data-client.d.ts +1 -2
  45. package/dist/types/client/iframe.d.ts +4 -2
  46. package/dist/types/core/annotations.d.ts +2 -0
  47. package/dist/types/core/config.d.ts +48 -4
  48. package/dist/types/core/contract.d.ts +20 -12
  49. package/dist/types/core/dimension.d.ts +6 -4
  50. package/dist/types/core/filters.d.ts +44 -0
  51. package/dist/types/core/messages.d.ts +2 -0
  52. package/dist/types/core/operation-types.d.ts +5 -2
  53. package/dist/types/core/operation.d.ts +3 -4
  54. package/dist/types/core/queries.d.ts +16 -0
  55. package/dist/types/core/uuid.d.ts +2 -0
  56. package/dist/types/core/variables.d.ts +32 -13
  57. package/dist/types/embed/runtime-html.d.ts +2 -0
  58. package/dist/types/embed/source.d.ts +2 -1
  59. package/dist/types/index.d.ts +3 -0
  60. package/dist/types/react/annotations/AnnotationBar.d.ts +7 -1
  61. package/dist/types/react/annotations/AnnotationEditor.d.ts +2 -1
  62. package/dist/types/react/annotations/useDataAppAnnotations.d.ts +3 -0
  63. package/dist/types/react/app-context.d.ts +3 -0
  64. package/dist/types/react/index.d.ts +3 -3
  65. package/dist/types/react/mount.d.ts +6 -4
  66. package/dist/types/react/style-contract.d.ts +34 -2
  67. package/dist/types/react/ui/Button.d.ts +3 -3
  68. package/dist/types/react/ui/ChartLegend.d.ts +31 -0
  69. package/dist/types/react/ui/Checkbox.d.ts +15 -4
  70. package/dist/types/react/ui/CheckboxGroup.d.ts +7 -0
  71. package/dist/types/react/ui/{Combobox.d.ts → ChoicePicker.d.ts} +10 -15
  72. package/dist/types/react/ui/ComposedChart.d.ts +41 -0
  73. package/dist/types/react/ui/ComposedChartLegend.d.ts +8 -0
  74. package/dist/types/react/ui/DataAppFrame.d.ts +1 -2
  75. package/dist/types/react/ui/DimensionPicker.d.ts +1 -1
  76. package/dist/types/react/ui/FilterActions.d.ts +10 -0
  77. package/dist/types/react/ui/FilterBar.d.ts +3 -0
  78. package/dist/types/react/ui/FunnelChart.d.ts +20 -0
  79. package/dist/types/react/ui/GettingStarted.d.ts +2 -4
  80. package/dist/types/react/ui/JourneyChart.d.ts +10 -0
  81. package/dist/types/react/ui/Menu.d.ts +12 -0
  82. package/dist/types/react/ui/NumberField.d.ts +27 -0
  83. package/dist/types/react/ui/NumberFilterPicker.d.ts +9 -0
  84. package/dist/types/react/ui/PieChart.d.ts +5 -2
  85. package/dist/types/react/ui/RadioGroup.d.ts +18 -0
  86. package/dist/types/react/ui/RetentionChart.d.ts +27 -0
  87. package/dist/types/react/ui/SearchInput.d.ts +2 -2
  88. package/dist/types/react/ui/Select.d.ts +14 -0
  89. package/dist/types/react/ui/SelectionMark.d.ts +2 -1
  90. package/dist/types/react/ui/TooltipSurface.d.ts +3 -0
  91. package/dist/types/react/ui/chart-data.d.ts +10 -1
  92. package/dist/types/react/ui/chart-primitives.d.ts +22 -0
  93. package/dist/types/react/ui/chartColor.d.ts +1 -0
  94. package/dist/types/react/ui/icons.d.ts +1 -0
  95. package/dist/types/react/ui/index.d.ts +33 -2
  96. package/dist/types/react/ui/journey-flow.d.ts +49 -0
  97. package/dist/types/react/ui/keyboard.d.ts +6 -0
  98. package/dist/types/react/ui/shortcuts.d.ts +7 -0
  99. package/dist/types/react/ui/useAppAppearance.d.ts +1 -1
  100. package/dist/types/react/ui/useSvgId.d.ts +2 -0
  101. package/dist/types/react/ui/variables.d.ts +4 -2
  102. package/dist/types/react/view-controls.d.ts +3 -1
  103. package/dist/types/server/handler.d.ts +4 -4
  104. package/dist/worker.js +21 -738
  105. package/docs/app-authoring.md +15 -1
  106. package/docs/client.md +8 -8
  107. package/docs/contract.md +43 -20
  108. package/docs/data-context.md +8 -6
  109. package/docs/embed.md +10 -2
  110. package/docs/formatting-and-appearance.md +23 -7
  111. package/docs/hosted-apps.md +9 -2
  112. package/docs/layout.md +1 -1
  113. package/docs/react-embed.md +3 -2
  114. package/docs/react.md +4 -3
  115. package/docs/server-bun.md +1 -1
  116. package/docs/server.md +4 -4
  117. package/docs/styling.md +10 -3
  118. package/docs/ui.md +176 -3
  119. package/docs/variables.md +80 -7
  120. package/docs/views.md +4 -8
  121. package/docs/widgets.md +19 -13
  122. package/docs/worker.md +2 -1
  123. package/examples/starter-data-app/index.tsx +34 -32
  124. package/package.json +16 -11
  125. package/dist/chunks/contract-13j5zb3c.js.map +0 -13
  126. package/dist/chunks/contract-19ckn3n7.js.map +0 -10
  127. package/dist/chunks/contract-h8a6f559.js +0 -286
  128. package/dist/chunks/contract-h8a6f559.js.map +0 -12
  129. package/dist/chunks/contract-havnjmdr.js.map +0 -99
  130. package/dist/chunks/contract-pk603qj7.js.map +0 -10
  131. package/dist/chunks/contract-rfbakka4.js.map +0 -13
  132. package/dist/chunks/contract-tf8c3qpv.js.map +0 -11
  133. package/dist/chunks/contract-wz59z8pq.js +0 -8
  134. package/dist/chunks/contract-wz59z8pq.js.map +0 -10
  135. /package/dist/chunks/{contract-m6n8ctfc.js → index-m6n8ctfc.js} +0 -0
  136. /package/dist/chunks/{contract-m6n8ctfc.js.map → index-m6n8ctfc.js.map} +0 -0
  137. /package/dist/chunks/{contract-mev09s5v.js → index-mev09s5v.js} +0 -0
  138. /package/dist/chunks/{contract-mev09s5v.js.map → index-mev09s5v.js.map} +0 -0
@@ -7,6 +7,13 @@ queries, definitions, and evidence.
7
7
 
8
8
  ## Inspect the data
9
9
 
10
+ Declare the query source of truth with `defineDataApp()`. Generate
11
+ operations with `dataApp.defineOperation()` and execute queries by
12
+ name with parameter values. When a query registry is supplied, use only its queries
13
+ and preserve its SQL. Otherwise, inspect the data and declare the queries the app needs.
14
+ Derive displays, exports, and findings from their results. See
15
+ [named queries](contract.md#execute-named-queries).
16
+
10
17
  Inspect the relevant catalogs, tables, and fields, their time coverage, and
11
18
  existing definitions. Choose a question the available data can answer.
12
19
 
@@ -21,13 +28,20 @@ Choose the execution path:
21
28
 
22
29
  Lead with a supported finding and expose the relevant fields as filter variables. Use a date filter for questions worth exploring over time,
23
30
  or a fixed period snapshot for a deliberate historical analysis.
31
+
32
+ Use relevant user-controlled query parameters as app filters. Map filter values to
33
+ declared parameters through operation inputs, and reuse query defaults when they
34
+ match the initial selection. One filter may supply several parameters, such as a
35
+ date range supplying `start` and `end`. Keep protected parameters, such as `orgId`,
36
+ and internal execution settings out of filter controls.
37
+
24
38
  Register inspected tables and fields with `defineDataIdentifiers()` and use
25
39
  `<DataIdentifier>` when naming sources. Register terms and query evidence so
26
40
  readers can inspect the source of each claim.
27
41
 
28
42
  Connect visualizations with introductions and explanations. Use `<TextWidget>`
29
43
  for a narrative panel with the standard widget frame, or `<TextContent>` for
30
- borderless prose. Render static titles, descriptions, and instructions immediately. Use metric and dataset
44
+ borderless prose. Render the app title, description, and instructions immediately. Use metric and dataset
31
45
  bindings for dynamic values and `<DataValue>` for values within static prose.
32
46
  Skeletonize only the content that needs data. Reuse bindings and the displayed
33
47
  source in narrative so values, formatting, and evidence follow filter changes,
package/docs/client.md CHANGED
@@ -15,8 +15,9 @@ const response = await client.query('activity', input, { signal });
15
15
  ```
16
16
 
17
17
  The app defines `operations`, `input`, and an optional cancellation `signal`.
18
- Operation input and output types are inferred from the server registry. Keep the
19
- registry import type-only so its SQL and implementation stay on the server.
18
+ Operation input and output types are inferred from the server operation map. Keep the
19
+ server operation map import type-only so its implementations stay on the server.
20
+ The shared app declaration contains the query registry and is browser-safe.
20
21
 
21
22
  The default endpoint is `/api/data`. Override it with
22
23
  `createDataClient({ endpoint, fetch })` to change the base URL or supply a Fetch
@@ -24,8 +25,7 @@ implementation. Calls POST JSON to `/api/data/:operation`; the client sends
24
25
  operation inputs rather than SQL or credentials.
25
26
 
26
27
  `DataResponse` contains `data`, the exact request `input`, `requestId`,
27
- `queriedAt`, and `queryIds`. `queries` is present when the operation exposes SQL
28
- and the execution runtime permits disclosure. Use the returned input when labeling stale data during a refresh.
28
+ `queriedAt`, `queryIds`, and executed `queries`. Use the returned input when labeling stale data during a refresh.
29
29
  HTTP and iframe delivery validate the same success envelope: data, request ID,
30
30
  query timestamp, query IDs, and optional query evidence. Malformed responses
31
31
  reject with `invalid_response`.
@@ -46,17 +46,17 @@ Bundle apps pass their operation registry as a value:
46
46
  import { connectionCheck } from '@altertable/data-app/contract';
47
47
  import { createDataClient } from '@altertable/data-app/client';
48
48
 
49
+ // dataApp declares a connection query.
49
50
  const client = createDataClient({
50
- operations: { connection: connectionCheck() },
51
+ operations: { connection: connectionCheck(dataApp.queries) },
51
52
  });
52
53
  const response = await client.query('connection', {});
53
54
  ```
54
55
 
55
56
  The client runs input parsing, operation logic, and output parsing in the browser.
56
- Operation policy bounds rows, duration, and response size and records query evidence. Each query sends `{ statement, limit }`
57
+ Operation policy bounds rows, duration, and response size and records query evidence. Each query sends `{ statement, limit, params? }`
57
58
  to the installed iframe bridge's `data:sql` route; the host needs no operation
58
- registry. SQL is visible in the browser, even when `exposeSql` is false; that flag
59
- only controls evidence in the returned response. Credentials remain backend-owned.
59
+ registry. Results include query evidence. Credentials remain backend-owned.
60
60
 
61
61
  The trusted bootstrap installs the bridge for bundle apps. A custom runtime must
62
62
  install it before querying. An explicit `lakehouse` can supply another authorized
package/docs/contract.md CHANGED
@@ -2,35 +2,59 @@
2
2
 
3
3
  Import operation definitions, parsers, and shared types from
4
4
  `@altertable/data-app/contract`. This entry is safe to import in browser and server
5
- modules. For HTTP apps, keep SQL and operation implementations on the server; browser modules
6
- should import their operation types using `import type`.
5
+ modules. The app declaration is browser-safe and includes its SQL registry. For HTTP apps,
6
+ keep operation implementations, credentials, and authorization on the server; browser modules
7
+ import operation types using `import type`.
7
8
 
8
9
  ## Execute named queries
9
10
 
10
- The app supplies `calendar`, `parseActivity()`, `checkInput`, `buildActivitySql()`,
11
- and `parseActivityRows()` in this example.
11
+ Declare SQL once with `defineDataApp()` to preserve exact query and parameter
12
+ names. Use stable `lowerCamelCase` IDs describing the result, such as `products`,
13
+ `productsByCategory`, or `dailyRevenue`. Use plural names for row lists; keep parameter
14
+ values in `params`. Every query must include `params`; use `{}` when it declares no
15
+ parameters.
16
+ The app owns an immutable registry snapshot. Define operations with
17
+ `dataApp.defineOperation()`.
12
18
 
13
19
  ```ts
14
- import {
15
- defineOperation,
16
- defineQueryNames,
17
- } from '@altertable/data-app/contract';
20
+ import { defineDataApp } from '@altertable/data-app';
21
+ import { rowsAsRecords } from '@altertable/data-app/contract';
22
+
23
+ const dataApp = defineDataApp({
24
+ title: 'Products',
25
+ description: 'Explore products for the selected organization.',
26
+ scope: { organization: 'demo', environment: 'production' },
27
+ appearance: { theme: 'system' },
28
+ queries: {
29
+ products: {
30
+ statement: 'SELECT * FROM products WHERE org_id = $orgId LIMIT $limit',
31
+ params: { orgId: {}, limit: { defaultValue: 10 } },
32
+ },
33
+ },
34
+ });
18
35
 
19
- const queries = defineQueryNames({ activity: 'feature-activity' });
20
- const activity = defineOperation({
21
- queryNames: queries,
22
- input: calendar.parseRequest,
23
- output: parseActivity,
24
- checks: [checkInput],
25
- policy: { maxQueryRows: 100, maxDurationMs: 15000, exposeSql: true },
36
+ const products = dataApp.defineOperation({
37
+ input: parseProductInput,
38
+ output: parseProducts,
39
+ checks: [{}],
40
+ policy: { maxQueryRows: 100, maxDurationMs: 15000 },
26
41
  async run({ query }, input) {
27
- const result = await query(queries.activity, buildActivitySql(input));
28
- return parseActivityRows(result);
42
+ const result = await query('products', input);
43
+ return rowsAsRecords(result, ['org_id']);
29
44
  },
30
45
  });
31
46
  ```
32
47
 
33
- `query()` inherits the operation's limit and cancellation signal; `{ limit }` can lower a particular query's bound. Names are checked by TypeScript and at runtime. Responses include executed SQL and query IDs when disclosure is allowed. HTTP browser modules import operation types with `import type`. Bundle apps import their browser-owned operation registry as a value and use [browser execution](client.md#browser-owned-operations-for-bundle-apps). Never bundle credentials or server adapters.
48
+ `parseProductInput()` and `parseProducts()` are app-owned example functions, not
49
+ package exports. Validate
50
+ filter values in the input parser. `{}` declares a required parameter; `{ defaultValue }` supplies
51
+ a fallback. `query(name, params, { limit })` executes only registered queries and
52
+ inherits the operation's row limit and cancellation signal.
53
+
54
+ SQL and resolved parameter values pass unchanged to the backend. Results include
55
+ query evidence; use `products.queryNames` with `createDataContext()` to bind it.
56
+ For HTTP apps, authorization can supply protected `queryParams`, such as `orgId`.
57
+ The host/backend enforces data access and limits.
34
58
 
35
59
  ## Shared date ranges
36
60
 
@@ -50,8 +74,7 @@ export const calendar = defineDateRangeContract({
50
74
  ```
51
75
 
52
76
  `parseEmptyInput()`, `parseTrue()`, `parseCount()`, and `parseDateRangeInput()` validate
53
- common inputs and results. `connectionCheck()` defines a bounded connectivity
54
- operation. A successful connectivity check confirms access; it is not an
77
+ common inputs and results. `connectionCheck(dataApp.queries)` runs the registered `connection` query. A successful connectivity check confirms access; it is not an
55
78
  analysis result.
56
79
 
57
80
  See [server authorization](server.md) and [React views](react.md) for the two
@@ -1,7 +1,8 @@
1
1
  # Data context and evidence
2
2
 
3
- Set `dataContext: context` in each view. The app and its sections inherit the
4
- view's description, glossary, and permitted query evidence. Render registered identifiers where `<DataIdentifier>` is used.
3
+ Set `dataContext: context` in each view for source descriptions, glossary, and
4
+ permitted query evidence. App inspection and the view's sections use that context.
5
+ Render registered identifiers where `<DataIdentifier>` is used.
5
6
 
6
7
  Physical source identifiers name inspected tables and columns. Glossary entries
7
8
  explain business meaning. Query names link those definitions and displayed claims
@@ -38,8 +39,7 @@ registry.
38
39
  ## Bind evidence
39
40
 
40
41
  ```tsx
41
- const queries = defineQueryNames({ activity: 'feature-activity' });
42
- // In the server operation: queryNames: queries
42
+ const queries = activity.queryNames;
43
43
  const context = createDataContext(queries)({
44
44
  identifiers: identifiers.definitions,
45
45
  description: (
@@ -69,7 +69,7 @@ const trackedIdentities = activityView.metric(
69
69
  {
70
70
  id: 'identities',
71
71
  glossaryId: 'identities',
72
- format: { kind: 'count' },
72
+ format: { kind: 'count', compact: true },
73
73
  },
74
74
  data => ({ current: data.count })
75
75
  );
@@ -81,7 +81,9 @@ const finding = context.finding({
81
81
  });
82
82
  ```
83
83
 
84
- Import `defineQueryNames()` from `/contract` and the context/identifier factories from `/react`. Use the same registry in `defineOperation({ queryNames: queries, ... })`.
84
+ Query inspection shows the executed SQL and resolved parameters; copy actions include both.
85
+
86
+ Import context and identifier factories from `/react`. Use the operation’s derived `queryNames` for evidence.
85
87
 
86
88
  Use `view.metric(definition, select)` to register and bind a metric in one call.
87
89
  Declare dataset evidence references inside `view.dataset()`; the view registers
package/docs/embed.md CHANGED
@@ -31,11 +31,18 @@ A bundle source has this shape:
31
31
  ```ts
32
32
  const source = {
33
33
  type: 'bundle' as const,
34
- bootstrapUrl: 'https://my-report-app-1.apps.example.net/',
35
34
  javascript: bundle.javascript,
36
35
  };
37
36
  ```
38
37
 
38
+ Omit `bootstrapUrl` to load the SDK's packaged bootstrap as a `data:` document.
39
+ This supports MCP hosts that permit opaque child frames but block remote frame
40
+ URLs. The SDK embeds the exact host origin and applies a CSP that prohibits
41
+ direct network access. Data requests go through the host's dispatcher.
42
+
43
+ Supply an HTTP(S) `bootstrapUrl` to use an externally served trusted bootstrap,
44
+ such as the [Worker asset](worker.md).
45
+
39
46
  The host provides a self-contained JavaScript bundle. React bridges replace the
40
47
  iframe whenever its JavaScript content changes. Serve the bootstrap page with a CSP compatible with the bundle.
41
48
  Bundle mode uses `sandbox="allow-scripts"` and an opaque origin; it cannot read
@@ -129,7 +136,8 @@ as public `source_*` errors with request IDs. Authorization failures return
129
136
  public failures with `MessageRoutingError`.
130
137
 
131
138
  `SqlQueryInput` (exported from `/contract`) carries
132
- `{ statement: string, limit: number }`; responses are
139
+ `{ statement: string, limit: number, params?: QueryParameters }`; the backend
140
+ interprets the unchanged statement and parameter values. Responses are
133
141
  `{ columns: { name: string, type?: string }[], rows: unknown[][], queryId?: string }`.
134
142
  The route rejects empty statements, unsafe or nonpositive limits, malformed
135
143
  results, and results exceeding the requested limit. The bridge's existing payload
@@ -16,10 +16,21 @@ Import formatting helpers from `@altertable/data-app/format`.
16
16
  | `formatDateRange()` | Inclusive calendar ranges |
17
17
  | `pluralize()` | Count-dependent labels |
18
18
 
19
- Missing values render distinctly from measured zero. Define metric formatting
20
- once with `view.metric()`; comparisons derive from its displayed source. Previous
21
- values, range labels, and favorable direction belong to that metric and displayed
22
- input; see [data context](data-context.md) and [views](views.md).
19
+ Missing values render distinctly from measured zero. Declare formatting once on
20
+ `view.metric()` so widgets, comparisons, and narrative share it; reuse the
21
+ definition with `formatMetric()` in story headlines.
22
+
23
+ Use compact counts for headline metrics and chart labels. Declare count metrics
24
+ with `format: { kind: 'count', compact: true }`. Keep full counts in tables used
25
+ for precise comparisons and raw numeric values in datasets for CSV export.
26
+
27
+ ```ts
28
+ formatCount(2_200_000, { compact: true }); // "2.2M"
29
+ formatCount(2_200_000); // "2,200,000"
30
+ ```
31
+
32
+ For signed or fractional measures, use `formatNumber()` with
33
+ `notation: 'compact'` and preserve the measure's units.
23
34
 
24
35
  Import `chartColor()` from `/react` to select colors from the configured palette.
25
36
  `<PeriodSummary>` describes reporting periods; `<UpdatedAt>` and
@@ -29,22 +40,27 @@ Import `chartColor()` from `/react` to select colors from the configured palette
29
40
  ## Configure identity and appearance
30
41
 
31
42
  ```ts
32
- import type { DataAppConfig } from '@altertable/data-app/config';
43
+ import { defineDataApp } from '@altertable/data-app';
33
44
 
34
- const config = {
45
+ const dataApp = defineDataApp({
35
46
  title: 'Product activity',
47
+ description: 'Explore product usage and trends.',
36
48
  scope: { organization: 'Acme', environment: 'Production' },
37
49
  appearance: {
38
50
  theme: 'system',
39
51
  accentColor: '#405d47',
40
52
  density: 'comfortable',
41
53
  },
42
- } satisfies DataAppConfig;
54
+ queries: {},
55
+ });
43
56
  ```
44
57
 
45
58
  Scope labels describe the configured connection; they do not grant access.
46
59
  `dataAppTitle()` produces the scoped document title used by `mountDataApp()`.
47
60
 
61
+ Title and description explain the app and can be revised when regenerating it.
62
+ Omit appearance to use the standard defaults.
63
+
48
64
  Appearance controls the brand palette, typography, density, corner radius, and
49
65
  elevation. Those settings apply consistently to the app instead of tuning each
50
66
  widget. Theme preference is reader-owned in standalone apps. A trusted parent's
@@ -4,7 +4,14 @@ Follow the [shared authoring flow](app-authoring.md) using [index.tsx](../exampl
4
4
  one file with public package imports; omit server files, HTML, credentials, and
5
5
  relative or app-alias imports.
6
6
 
7
- Replace the sample SQL, parsers, filters, data context, CSV export, story, and configuration with
7
+ Declare exactly one top-level const initialized with `defineDataApp({ ... })`, imported
8
+ from `@altertable/data-app`. Keep executable composition outside its literal argument.
9
+ For the future backend extractor, use schema-valid literals without spreads, computed keys,
10
+ references, calls, or template interpolation. Extraction will read and validate the argument
11
+ before bundling, without evaluating app code or parsing SQL. Import aliases and any variable
12
+ name are allowed; export only for other modules. Static apps use `queries: {}`.
13
+
14
+ Replace the sample app declaration, operations, filters, data context, CSV export, and story with
8
15
  an exploration of the source data you inspected. The starter uses two SQL
9
16
  `VALUES` rows, so it needs no production table.
10
17
 
@@ -13,7 +20,7 @@ For execution details, see [browser-owned operations](client.md#browser-owned-op
13
20
  ## Convert a local data app
14
21
 
15
22
  1. Combine the app's operations and parsers, data context, views, story,
16
- CSV export, configuration, and browser entry into one `index.tsx`, following the
23
+ CSV export, app declaration, and browser entry into one `index.tsx`, following the
17
24
  [single-file starter](../examples/starter-data-app/index.tsx).
18
25
  2. Replace the HTTP client with `createDataClient({ operations })`, using the
19
26
  operation registry as a value. See [browser-owned operations](client.md#browser-owned-operations-for-bundle-apps)
package/docs/layout.md CHANGED
@@ -7,7 +7,7 @@ external spacing. Keep widget customization inside the widget so its parent can
7
7
  spacing between sections and cards.
8
8
 
9
9
  By default, section and widget gaps use `--atbl-layout-gap`, with density configured once
10
- in `DataAppConfig.appearance`. The package owns wrapping and span collapse based
10
+ in `app.appearance`. The package owns wrapping and span collapse based
11
11
  on the available container width and configured gap. Custom length values for
12
12
  `--atbl-layout-gap`, `--atbl-space-sm`, and `--atbl-space-md` also drive span
13
13
  collapse; a two-column span activates only when two minimum-width tracks fit.
@@ -45,10 +45,11 @@ The local URL must have a different origin from its shell. For hosted bundles,
45
45
  replace `source` with:
46
46
 
47
47
  ```tsx
48
- source={{ type: 'bundle', bootstrapUrl, javascript }}
48
+ source={{ type: 'bundle', javascript }}
49
49
  ```
50
50
 
51
- The host supplies those bundle values. See [embedding](embed.md) for trust,
51
+ The host supplies the JavaScript bundle. Omit `bootstrapUrl` for the packaged
52
+ opaque bootstrap, or supply an HTTP(S) URL for an external bootstrap. See [embedding](embed.md) for trust,
52
53
  sandbox, CSP, and bootstrap setup.
53
54
 
54
55
  Source URL or JavaScript content changes replace the entire iframe. Change the
package/docs/react.md CHANGED
@@ -5,14 +5,15 @@ React 19.2 or newer and React DOM 19.2 or newer are peer dependencies.
5
5
 
6
6
  ## Mount the app
7
7
 
8
- Use `mountDataApp({ config, component })` to mount into `#root`. When mounting
9
- through another framework, import `<DataAppProvider>` from `/react/ui`.
8
+ Use `mountDataApp({ app, component })` to mount into `#root`. When mounting
9
+ through another framework, wrap the root in `<DataAppProvider app={app}>` from `/react/ui`.
10
+ `<DataApp>` inherits identity and appearance from that root. Mounting sets the document title before React renders.
10
11
 
11
12
  ```tsx
12
13
  import { injectDataAppStyles, mountDataApp } from '@altertable/data-app/react';
13
14
 
14
15
  injectDataAppStyles();
15
- mountDataApp({ config, component: App });
16
+ mountDataApp({ app, component: App });
16
17
  ```
17
18
 
18
19
  Call `injectDataAppStyles()` before mounting; no separate stylesheet is needed.
@@ -24,6 +24,6 @@ server-only `ALTERTABLE_LAKEHOUSE_USERNAME` and
24
24
  `ALTERTABLE_LAKEHOUSE_PASSWORD`, with an optional `ALTERTABLE_API_BASE`.
25
25
  Missing credentials fail the query. Keep these variables out of browser code.
26
26
 
27
- Local serving permits SQL disclosure and does not authenticate hosted viewers.
27
+ Local serving does not authenticate hosted viewers.
28
28
  Use [the portable server handler](server.md) with per-request authorization when
29
29
  hosting an app for other people.
package/docs/server.md CHANGED
@@ -15,7 +15,7 @@ export const handleDataRequest = createDataHandler(
15
15
  const viewer = await authenticate(request);
16
16
  return {
17
17
  lakehouse: await lakehouseFor(viewer, operation),
18
- canDiscloseSql: false,
18
+ queryParams: { orgId: viewer.organizationId },
19
19
  };
20
20
  }
21
21
  );
@@ -23,12 +23,12 @@ export const handleDataRequest = createDataHandler(
23
23
 
24
24
  The app supplies `authenticate()` and `lakehouseFor()`, then routes `/api/data/*`
25
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
26
+ returned lakehouse to the viewer's permitted data. Supply trusted registry
27
+ parameters through `queryParams`; query callers cannot override these values. Origin and Fetch Metadata
27
28
  checks reject cross-site browser requests; they do not authenticate viewers.
28
29
 
29
30
  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.
31
+ bounds, propagates cancellation, and returns query evidence and request IDs.
32
32
  Keep credentials and operation implementations on the server.
33
33
 
34
34
  See [operation contracts](contract.md), the [client](client.md), and the
package/docs/styling.md CHANGED
@@ -5,7 +5,7 @@ and `<TextContent>` for prose. Standard widgets use [views and bindings](widgets
5
5
  custom controls and direct widget shells use [`/react/ui`](ui.md).
6
6
 
7
7
  Configure theme, palette, accent, typography, density, radius, and elevation through
8
- `config.appearance`; see [appearance](formatting-and-appearance.md). Call
8
+ `app.appearance`; see [appearance](formatting-and-appearance.md). Call
9
9
  `injectDataAppStyles()` before mounting. Use one `<DataApp>` per document.
10
10
 
11
11
  ## Customize
@@ -36,6 +36,12 @@ are consumed, so local colors, fonts, and spacing compose. Portals inherit from
36
36
  their actual DOM ancestors. Normal unlayered app CSS overrides package rules.
37
37
  `DataAppStyle` checks public inline custom-property names.
38
38
 
39
+ `--atbl-control-text-size` controls editable text, including compact search fields,
40
+ date segments, and annotation editors. It defaults to the body text size.
41
+ Package controls and custom controls with `data-atbl-control="text"` enforce a
42
+ 16px minimum on narrow or touch viewports to prevent browser focus zoom. Keep
43
+ that minimum when adding app-owned input styles.
44
+
39
45
  When overriding `--atbl-accent` directly, also choose `--atbl-on-accent` with
40
46
  sufficient contrast. Prefer appearance configuration for brand and chart colors.
41
47
 
@@ -51,5 +57,6 @@ Use package controls when possible. For a custom native control:
51
57
 
52
58
  These hooks provide cursor and focus styling; keep native semantics, accessible
53
59
  names, disabled state, and keyboard behavior. `DataAppStyleHooks` checks hook
54
- values. Use `inset` focus inside clipped surfaces and `group` when a wrapper owns
55
- an input's outline. See [UI quality](ui-quality.md) for rendered verification.
60
+ values. Use `default` for tooltip targets that only reveal information on hover
61
+ or tap, and `action` for controls that perform an action. Use `inset` focus inside
62
+ clipped surfaces and `group` when a wrapper owns an input's outline. See [UI quality](ui-quality.md) for rendered verification.
package/docs/ui.md CHANGED
@@ -13,22 +13,82 @@ request states to populate a shell.
13
13
  Direct `<DateRangePicker>`, `<DimensionPicker>`, and `useAppVariables()` support
14
14
  custom control ownership. The caller supplies values and change handlers.
15
15
  Custom tables use `<DataTable>` and its cell helpers; controls and overlays
16
- include `<SearchField>`, `<Combobox>`, `<Tabs>`, `<Sheet>`, and `<HelpPopover>`.
16
+ include `<SearchField>`, `<ChoicePicker>`, `<Tabs>`, `<Sheet>`, and `<HelpPopover>`.
17
17
 
18
18
  Standalone `<AboutData>`, glossary components, and `<PresentStory>` support
19
19
  custom inspection and presentation. Supply registered context and evidence, and
20
20
  derive findings from the displayed data. Standard widgets and `<DataApp>`
21
21
  already own these experiences.
22
22
 
23
- For framework mounting, `<DataAppProvider>` supplies shared requests and one inspection sheet. Wrap custom shells in it.
23
+ For framework mounting, `<DataAppProvider app={app}>` supplies app identity, shared requests and one inspection sheet. Wrap custom shells in it; app components inherit identity and appearance.
24
24
  Call `injectDataAppStyles()` before mounting either entry; imports do not install
25
25
  styles.
26
26
 
27
+ ## Keyboard actions
28
+
29
+ Use `isPlainKeyEvent()` from `/react/ui` in custom key handlers so local actions
30
+ leave shortcut modifiers and IME composition untouched:
31
+
32
+ ```tsx
33
+ onKeyDown={event => {
34
+ if (event.key === 'Enter' && isPlainKeyEvent(event.nativeEvent)) {
35
+ event.preventDefault();
36
+ submit();
37
+ }
38
+ }}
39
+ ```
40
+
41
+ Pass `{ allowShift: true }` when the handler also supports Shift gestures.
42
+
43
+ ## Choose a selection control
44
+
45
+ Use `<DimensionPicker>` for typed field filters and `<ChoicePicker>` for searchable
46
+ values. A single value uses a checkmark and closes after selection; multiple
47
+ values use checkbox indicators and keep the popup open. These pickers expose
48
+ listbox options, including search and loading feedback.
49
+
50
+ Use `<MenuTrigger>`, `<MenuButton>`, `<MenuPopover>`, `<Menu>`, and `<MenuItem>`
51
+ for commands or short choice menus
52
+ such as sort order or display mode. Actions use `onAction`. Set
53
+ `selectionMode="single"` for mutually exclusive choices (radio menu items), or
54
+ `selectionMode="multiple"` for independent toggles (checkbox menu items). Menus
55
+ support arrow keys, typeahead, Escape, and focus return to their trigger. Keep
56
+ search fields and other form inputs outside menus.
57
+
58
+ ```tsx
59
+ <MenuTrigger>
60
+ <MenuButton>Sort order</MenuButton>
61
+ <MenuPopover>
62
+ <Menu
63
+ aria-label="Sort order"
64
+ selectionMode="single"
65
+ selectedKeys={[sortOrder]}
66
+ onSelectionChange={keys => {
67
+ if (keys !== 'all') setSortOrder(String([...keys][0]));
68
+ }}
69
+ >
70
+ <MenuItem id="highest">Highest first</MenuItem>
71
+ <MenuItem id="lowest">Lowest first</MenuItem>
72
+ </Menu>
73
+ </MenuPopover>
74
+ </MenuTrigger>
75
+ ```
76
+
77
+ `<MenuButton>` accepts text or icons; give icon-only buttons an accessible name.
78
+ `<MenuPopover>` owns placement and popup styling.
79
+
80
+ Use `<MenuSection>` to group choices with their own selection state and
81
+ `<MenuSeparator>` between groups or commands. Menu selection closes the popup
82
+ by default; set `shouldCloseOnSelect={false}` for repeated toggles. Selection
83
+ indicators derive from the menu or section's selection mode; do not add checkbox
84
+ markup to single-choice items.
85
+
27
86
  ## Charts
28
87
 
29
88
  Choose a chart using the [visualization guide](widgets.md#choose-a-visualization).
30
89
  Import `<BarChart>`, `<LineChart>`, `<AreaChart>`, `<PieChart>`, or `<ScatterChart>` from
31
- `/react/ui`. Compose the visual inside `<VisualizationWidget dataset={dataset} source={result}>`
90
+ `/react/ui`; `<ComposedChart>` exposes the series primitives for custom plots.
91
+ Compose the visual inside `<VisualizationWidget dataset={dataset} source={result}>`
32
92
  so rows, loading state, evidence, and inspection come from that dataset.
33
93
  Pass ordered items with unique, nonblank IDs and finite numbers. Bar and pie
34
94
  values must be nonnegative. Use `formatValue` for domain formatting.
@@ -39,3 +99,116 @@ intervals. Distinguish a missing observation from measured zero when preparing
39
99
  the samples. Pie slices represent mutually exclusive parts of one total; shares
40
100
  use the sum of supplied items, so include Other when showing a subset of the
41
101
  whole. Scatter points represent independent X/Y observations.
102
+
103
+ Use `useSvgId()` for stable IDs when composing custom SVG definitions.
104
+
105
+ ### Product analytics charts
106
+
107
+ Use `<FunnelChart>`, `<RetentionChart>`, and `<JourneyChart>` from `/react/ui`
108
+ inside `<VisualizationWidget>`. Derive inputs from the displayed rows; the
109
+ binding owns loading, evidence, CSV, and story content. Types and JSDoc describe
110
+ input constraints; the gallery includes complete examples.
111
+
112
+ - `<FunnelChart>` compares ordered step counts across populations. Each series
113
+ uses its first step as the baseline; hatching shows previous-step drop-off.
114
+ - `<RetentionChart>` plots query-defined rates and retained counts by interval
115
+ offset. Use `null` for unobserved periods; `incomplete` produces a dotted tail.
116
+ - `<JourneyChart>` derives expandable branches and outcomes from full paths.
117
+ Truncated paths stop at the last loaded event.
118
+
119
+ ### Composed charts
120
+
121
+ Use `<ComposedChart>` from `/react/ui` for multiple series or mixed marks inside
122
+ `<VisualizationWidget>`. Pass the displayed rows and select fields with `dataKey`.
123
+ `<ComposedChart.Bar>`, `<ComposedChart.Line>`, and `<ComposedChart.Area>` share the
124
+ standalone charts' themed series primitives; axes, scatter, tooltip, and reference
125
+ primitives are also available.
126
+
127
+ ```tsx
128
+ <VisualizationWidget dataset={revenue} source={result}>
129
+ {rows => (
130
+ <ComposedChart data={rows} ariaLabel="Monthly revenue and target in euros">
131
+ <ComposedChart.XAxis dataKey="month" />
132
+ <ComposedChart.YAxis />
133
+ <ComposedChart.Tooltip />
134
+ <ComposedChart.Legend />
135
+ <ComposedChart.Bar dataKey="revenue" name="Revenue (€)" />
136
+ <ComposedChart.Line
137
+ dataKey="target"
138
+ name="Target (€)"
139
+ stroke="var(--atbl-chart-2)"
140
+ />
141
+ </ComposedChart>
142
+ )}
143
+ </VisualizationWidget>
144
+ ```
145
+
146
+ Label series and units clearly. See component JSDoc and the
147
+ [Recharts API](https://recharts.github.io/en-US/api/ComposedChart/) for options.
148
+ The dataset owns loading, empty results, evidence, CSV, and story content; keep
149
+ its columns aligned with the displayed measures.
150
+
151
+ ### Legends
152
+
153
+ Include `<ComposedChart.Legend />` to derive labels and colored square markers from the series;
154
+ omit it when no legend is needed. For any chart, compose `<ChartLegend>` with
155
+ `<ChartLegend.Item>`, `<ChartLegend.Marker>`, and `<ChartLegend.Label>` using the
156
+ same names and colors as the plot. Square markers are
157
+ shared with chart tooltips and funnel summaries. Pass `kind="square"` explicitly
158
+ when composing a marker.
159
+ `<PieChart>` includes a legend by default; `showLegend={false}` omits it.
160
+
161
+ Legends align cells in a container-responsive grid, truncate labels, and reserve
162
+ an overflow cell for “+X more.” Use `maxVisibleItems` to choose the limit or
163
+ `layout="vertical"` for a list. Legends describe series; authored toggles or a
164
+ composed legend's `onClick` leave series visibility to the app.
165
+
166
+ ## Filter controls
167
+
168
+ Direct controls own accessible input and call the supplied change handler;
169
+ filter declarations own URL state, validation, and operation meaning.
170
+
171
+ | Control | Use |
172
+ | ---------------------------------- | ----------------------------------------------------------------- |
173
+ | `<SearchField>` | Search text |
174
+ | `<Select>` | One choice from a small fixed list |
175
+ | `<ChoicePicker>` | Searchable choices with explicit `selectionMode` |
176
+ | `<RadioGroup>` and `<Radio>` | Visible exclusive choices |
177
+ | `<CheckboxGroup>` and `<Checkbox>` | Visible independent choices |
178
+ | `<SegmentedControl>` | Compact radio choices |
179
+ | `<NumberField>` | Localized numeric input; empty is `null` |
180
+ | `<NumberRangeField>` | Exact, inclusive minimum/maximum; omitted bounds are unrestricted |
181
+ | `<DateRangePicker>` | Explicit or relative periods |
182
+ | `<DimensionPicker>` | Typed categorical predicates with fixed or facet options |
183
+
184
+ ```tsx
185
+ <RadioGroup label="Metric" value={metric} onChange={setMetric}>
186
+ <Radio value="orders">Orders</Radio>
187
+ <Radio value="revenue">Revenue</Radio>
188
+ </RadioGroup>
189
+
190
+ <CheckboxGroup label="Statuses" values={statuses} onChange={setStatuses}>
191
+ <Checkbox value="paid" label="Paid" />
192
+ <Checkbox value="pending" label="Pending" />
193
+ </CheckboxGroup>
194
+ ```
195
+
196
+ A standalone `<Checkbox>` uses `checked` and `onChange`; a group item supplies
197
+ `value`. `<SegmentedControl>` accepts the same labeled options as `<Select>` and
198
+ keeps radio-group semantics. Numeric range controls retain invalid draft bounds
199
+ and emit only valid intervals. Numeric input and compact selects respect the
200
+ shared mobile text-size minimum.
201
+
202
+ Use `<FilterBar>` to arrange predicates and `<VariableBar>` for mixed app
203
+ parameters. Each filter shows its current value; categorical single selection includes All.
204
+ `<FilterActions>` provides one Clear icon with a tooltip, plus optional paired
205
+ Apply/Cancel actions for app-owned drafts. The caller owns those actions; the
206
+ components do not infer query state.
207
+
208
+ `<NumberFilterPicker>` composes numeric fields inside a dedicated popover. Edits
209
+ remain local until Apply; Cancel and dismissal discard them. Use `<NumberField>`
210
+ and `<NumberRangeField>` as inline input primitives when composing forms.
211
+
212
+ Use `<Button variant="primary">` for the main committing action, such as Apply.
213
+ Primary buttons use the theme foreground as their fill and the theme background
214
+ for contrasting text. Use outline and ghost buttons for secondary actions.