@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.
Files changed (174) hide show
  1. package/AGENTS.md +35 -0
  2. package/CONTRIBUTING.md +83 -0
  3. package/LICENSE +21 -0
  4. package/README.md +63 -0
  5. package/dist/bootstrap.js +388 -0
  6. package/dist/chunks/contract-14vxdcrs.js +192 -0
  7. package/dist/chunks/contract-14vxdcrs.js.map +10 -0
  8. package/dist/chunks/contract-8q35dcyh.js +9 -0
  9. package/dist/chunks/contract-8q35dcyh.js.map +10 -0
  10. package/dist/chunks/contract-jksbmt5q.js +133 -0
  11. package/dist/chunks/contract-jksbmt5q.js.map +10 -0
  12. package/dist/chunks/contract-mb5nfzwg.js +375 -0
  13. package/dist/chunks/contract-mb5nfzwg.js.map +12 -0
  14. package/dist/chunks/contract-mev09s5v.js +77 -0
  15. package/dist/chunks/contract-mev09s5v.js.map +10 -0
  16. package/dist/chunks/contract-nt819swq.js +321 -0
  17. package/dist/chunks/contract-nt819swq.js.map +11 -0
  18. package/dist/chunks/contract-ryyf6dme.js +21 -0
  19. package/dist/chunks/contract-ryyf6dme.js.map +10 -0
  20. package/dist/chunks/contract-tkkc28tg.js +213 -0
  21. package/dist/chunks/contract-tkkc28tg.js.map +12 -0
  22. package/dist/chunks/contract-tqrf3ykr.js +232 -0
  23. package/dist/chunks/contract-tqrf3ykr.js.map +11 -0
  24. package/dist/chunks/contract-wz59z8pq.js +8 -0
  25. package/dist/chunks/contract-wz59z8pq.js.map +10 -0
  26. package/dist/client/index.js +27 -0
  27. package/dist/client/index.js.map +9 -0
  28. package/dist/core/appearance.js +12 -0
  29. package/dist/core/appearance.js.map +9 -0
  30. package/dist/core/config.js +8 -0
  31. package/dist/core/config.js.map +9 -0
  32. package/dist/core/contract.js +55 -0
  33. package/dist/core/contract.js.map +9 -0
  34. package/dist/core/format.js +18 -0
  35. package/dist/core/format.js.map +9 -0
  36. package/dist/embed/index.js +91 -0
  37. package/dist/embed/index.js.map +11 -0
  38. package/dist/local.js +332 -0
  39. package/dist/local.js.map +15 -0
  40. package/dist/react/embed/index.js +130 -0
  41. package/dist/react/embed/index.js.map +11 -0
  42. package/dist/react/index.css +4478 -0
  43. package/dist/react/index.js +6105 -0
  44. package/dist/react/index.js.map +90 -0
  45. package/dist/react.css.d.ts +1 -0
  46. package/dist/server.js +231 -0
  47. package/dist/server.js.map +14 -0
  48. package/dist/types/client/iframe.d.ts +39 -0
  49. package/dist/types/client/index.d.ts +42 -0
  50. package/dist/types/client/location.d.ts +15 -0
  51. package/dist/types/client/messages.d.ts +7 -0
  52. package/dist/types/client/navigation.d.ts +14 -0
  53. package/dist/types/client/transport.d.ts +15 -0
  54. package/dist/types/core/appearance.d.ts +28 -0
  55. package/dist/types/core/bridge.d.ts +35 -0
  56. package/dist/types/core/config.d.ts +14 -0
  57. package/dist/types/core/contract.d.ts +139 -0
  58. package/dist/types/core/data-view.d.ts +46 -0
  59. package/dist/types/core/date-range.d.ts +20 -0
  60. package/dist/types/core/dimension.d.ts +60 -0
  61. package/dist/types/core/format.d.ts +40 -0
  62. package/dist/types/core/invariant.d.ts +2 -0
  63. package/dist/types/core/messages.d.ts +77 -0
  64. package/dist/types/core/reading.d.ts +17 -0
  65. package/dist/types/core/variables.d.ts +82 -0
  66. package/dist/types/embed/bootstrap.d.ts +5 -0
  67. package/dist/types/embed/host.d.ts +23 -0
  68. package/dist/types/embed/index.d.ts +11 -0
  69. package/dist/types/embed/navigation.d.ts +6 -0
  70. package/dist/types/embed/shell.d.ts +21 -0
  71. package/dist/types/embed/standalone.d.ts +1 -0
  72. package/dist/types/react/content.d.ts +23 -0
  73. package/dist/types/react/embed/bridge.d.ts +12 -0
  74. package/dist/types/react/embed/index.d.ts +9 -0
  75. package/dist/types/react/embed/shell.d.ts +10 -0
  76. package/dist/types/react/hooks.d.ts +831 -0
  77. package/dist/types/react/index.d.ts +11 -0
  78. package/dist/types/react/mount.d.ts +11 -0
  79. package/dist/types/react/ui/AboutData.d.ts +61 -0
  80. package/dist/types/react/ui/AltertableLogo.d.ts +3 -0
  81. package/dist/types/react/ui/AppFooter.d.ts +7 -0
  82. package/dist/types/react/ui/AppHeader.d.ts +12 -0
  83. package/dist/types/react/ui/AppLayout.d.ts +17 -0
  84. package/dist/types/react/ui/AppScope.d.ts +8 -0
  85. package/dist/types/react/ui/AppToolbar.d.ts +32 -0
  86. package/dist/types/react/ui/Breakdown.d.ts +14 -0
  87. package/dist/types/react/ui/Button.d.ts +12 -0
  88. package/dist/types/react/ui/Checkbox.d.ts +11 -0
  89. package/dist/types/react/ui/Combobox.d.ts +43 -0
  90. package/dist/types/react/ui/ComparisonVisual.d.ts +15 -0
  91. package/dist/types/react/ui/ContentSkeleton.d.ts +8 -0
  92. package/dist/types/react/ui/DataApp.d.ts +56 -0
  93. package/dist/types/react/ui/DataBoundary.d.ts +16 -0
  94. package/dist/types/react/ui/DataSection.d.ts +28 -0
  95. package/dist/types/react/ui/DataTable.d.ts +28 -0
  96. package/dist/types/react/ui/DataViewToast.d.ts +12 -0
  97. package/dist/types/react/ui/DataWidget.d.ts +39 -0
  98. package/dist/types/react/ui/DateRangePicker.d.ts +29 -0
  99. package/dist/types/react/ui/DateTimeTooltip.d.ts +10 -0
  100. package/dist/types/react/ui/DimensionPicker.d.ts +11 -0
  101. package/dist/types/react/ui/EmptyState.d.ts +9 -0
  102. package/dist/types/react/ui/GettingStarted.d.ts +9 -0
  103. package/dist/types/react/ui/GlossaryDefinition.d.ts +8 -0
  104. package/dist/types/react/ui/GlossaryExplanation.d.ts +17 -0
  105. package/dist/types/react/ui/GradientScroll.d.ts +16 -0
  106. package/dist/types/react/ui/Grid.d.ts +13 -0
  107. package/dist/types/react/ui/GridItem.d.ts +7 -0
  108. package/dist/types/react/ui/HelpPopover.d.ts +24 -0
  109. package/dist/types/react/ui/IconButton.d.ts +19 -0
  110. package/dist/types/react/ui/InspectionContext.d.ts +10 -0
  111. package/dist/types/react/ui/Kbd.d.ts +8 -0
  112. package/dist/types/react/ui/LiveControl.d.ts +11 -0
  113. package/dist/types/react/ui/MetricWidget.d.ts +48 -0
  114. package/dist/types/react/ui/PeriodSummary.d.ts +17 -0
  115. package/dist/types/react/ui/PresentStory.d.ts +34 -0
  116. package/dist/types/react/ui/QueryList.d.ts +15 -0
  117. package/dist/types/react/ui/Ranking.d.ts +14 -0
  118. package/dist/types/react/ui/RefreshControl.d.ts +11 -0
  119. package/dist/types/react/ui/RefreshRegion.d.ts +10 -0
  120. package/dist/types/react/ui/RequestHint.d.ts +21 -0
  121. package/dist/types/react/ui/SearchField.d.ts +23 -0
  122. package/dist/types/react/ui/SearchInput.d.ts +9 -0
  123. package/dist/types/react/ui/SearchMatch.d.ts +7 -0
  124. package/dist/types/react/ui/SelectableBarChart.d.ts +16 -0
  125. package/dist/types/react/ui/SelectionMark.d.ts +5 -0
  126. package/dist/types/react/ui/Sheet.d.ts +19 -0
  127. package/dist/types/react/ui/Skeleton.d.ts +6 -0
  128. package/dist/types/react/ui/Stack.d.ts +8 -0
  129. package/dist/types/react/ui/StatusPanel.d.ts +11 -0
  130. package/dist/types/react/ui/TableWidget.d.ts +57 -0
  131. package/dist/types/react/ui/Tabs.d.ts +6 -0
  132. package/dist/types/react/ui/ThemeSelector.d.ts +13 -0
  133. package/dist/types/react/ui/Tooltip.d.ts +23 -0
  134. package/dist/types/react/ui/UpdatedAt.d.ts +10 -0
  135. package/dist/types/react/ui/VariableBar.d.ts +7 -0
  136. package/dist/types/react/ui/VisualizationWidget.d.ts +52 -0
  137. package/dist/types/react/ui/WidgetDisclosure.d.ts +7 -0
  138. package/dist/types/react/ui/WidgetEvidence.d.ts +12 -0
  139. package/dist/types/react/ui/WidgetViewTabs.d.ts +17 -0
  140. package/dist/types/react/ui/chartColor.d.ts +1 -0
  141. package/dist/types/react/ui/classNames.d.ts +1 -0
  142. package/dist/types/react/ui/comparison.d.ts +30 -0
  143. package/dist/types/react/ui/data-context.d.ts +65 -0
  144. package/dist/types/react/ui/data-identifiers.d.ts +33 -0
  145. package/dist/types/react/ui/icons.d.ts +42 -0
  146. package/dist/types/react/ui/index.d.ts +129 -0
  147. package/dist/types/react/ui/metric.d.ts +11 -0
  148. package/dist/types/react/ui/search.d.ts +6 -0
  149. package/dist/types/react/ui/searchItems.d.ts +34 -0
  150. package/dist/types/react/ui/shortcuts.d.ts +32 -0
  151. package/dist/types/react/ui/story.d.ts +20 -0
  152. package/dist/types/react/ui/variables.d.ts +36 -0
  153. package/dist/types/react/ui/widget-views.d.ts +3 -0
  154. package/dist/types/react/view-controls.d.ts +17 -0
  155. package/dist/types/react/view.d.ts +49 -0
  156. package/dist/types/server/handler.d.ts +13 -0
  157. package/dist/types/server/index.d.ts +7 -0
  158. package/dist/types/server/local.d.ts +17 -0
  159. package/docs/app-authoring.md +26 -0
  160. package/docs/appearance.md +30 -0
  161. package/docs/bootstrap.md +76 -0
  162. package/docs/client.md +127 -0
  163. package/docs/config.md +21 -0
  164. package/docs/contract.md +95 -0
  165. package/docs/embed.md +98 -0
  166. package/docs/format.md +29 -0
  167. package/docs/react-embed.md +57 -0
  168. package/docs/react-styles.md +17 -0
  169. package/docs/react.md +248 -0
  170. package/docs/releasing.md +74 -0
  171. package/docs/server-bun.md +26 -0
  172. package/docs/server.md +47 -0
  173. package/docs/starter-agent-instructions.md +51 -0
  174. package/package.json +124 -0
@@ -0,0 +1,76 @@
1
+ # Standalone bootstrap
2
+
3
+ `@altertable/data-app/bootstrap` resolves to a self-contained classic browser
4
+ script. It has no imports, React dependencies, or app navigation behavior. The
5
+ package builds it once; a backend can embed the published file without running a
6
+ bundler.
7
+
8
+ This entry is a script asset, not a module API. Resolve and read it on the server;
9
+ do not import it for execution in Node.js or Bun. For a bootstrap you bundle
10
+ yourself, use `startDataAppBootstrap` from [embedding](embed.md).
11
+
12
+ ## Generate the backend HTML
13
+
14
+ Install an exact package version in the backend runtime's `package.json`, commit
15
+ the lockfile, and run this generation step before building or deploying the worker:
16
+
17
+ ```js
18
+ // scripts/build-runtime.mjs
19
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
20
+
21
+ const parentOrigin = 'https://app.altertable.ai';
22
+ const path = import.meta.resolve('@altertable/data-app/bootstrap');
23
+ const javascript = await readFile(new URL(path), 'utf8');
24
+ const inline = javascript.replace(/<\/script/gi, '<\\/script');
25
+ const originAttribute = parentOrigin
26
+ .replaceAll('&', '&amp;')
27
+ .replaceAll('"', '&quot;')
28
+ .replaceAll('<', '&lt;')
29
+ .replaceAll('>', '&gt;');
30
+
31
+ const html = `<!doctype html>
32
+ <html>
33
+ <head>
34
+ <meta charset="utf-8">
35
+ <meta name="viewport" content="width=device-width, initial-scale=1">
36
+ </head>
37
+ <body>
38
+ <div id="root"></div>
39
+ <script data-parent-origin="${originAttribute}">${inline}</script>
40
+ </body>
41
+ </html>`;
42
+
43
+ await mkdir('generated', { recursive: true });
44
+ await writeFile(
45
+ 'generated/runtime-html.js',
46
+ `export const RUNTIME_HTML = ${JSON.stringify(html)};\n`
47
+ );
48
+ ```
49
+
50
+ ```fish
51
+ node scripts/build-runtime.mjs
52
+ ```
53
+
54
+ The worker imports `RUNTIME_HTML` and serves it as `text/html; charset=utf-8`.
55
+ The resulting HTML needs no package imports or network fetches at runtime.
56
+
57
+ ## Configuration and security
58
+
59
+ Use a regular inline `<script>` with `data-parent-origin` on that same element,
60
+ after the app's mount element. The script reads `document.currentScript` and
61
+ starts immediately. Do not use `type="module"`.
62
+
63
+ The hosting service must supply an exact trusted origin, such as
64
+ `https://app.altertable.ai`, through deployment configuration. Missing or invalid
65
+ origins fail before installing a transport. Do not derive this value from an
66
+ unverified query parameter, referrer, or incoming message.
67
+
68
+ Serve the HTML with the [bootstrap CSP](embed.md#trusted-bootstrap), including a
69
+ `frame-ancestors` header restricted to the trusted host. The script installs the
70
+ authenticated transport, retains opaque host state, evaluates the app bundle
71
+ sent by the host, and reports readiness or failure. The app attaches its own
72
+ navigation adapter. Backend authorization still applies to every data request.
73
+
74
+ Upgrade the host package and bootstrap artifact together when changing the bridge
75
+ protocol. See [client navigation](client.md#app-navigation) and
76
+ [embedding](embed.md) for the communication boundaries.
package/docs/client.md ADDED
@@ -0,0 +1,127 @@
1
+ # Client
2
+
3
+ Import `createDataClient` and `DataAppError` from
4
+ `@altertable/data-app/client`. The client uses Fetch APIs and has no React or
5
+ server dependency.
6
+
7
+ ```ts
8
+ import { createDataClient } from '@altertable/data-app/client';
9
+ import type { operations } from './operations';
10
+
11
+ const client = createDataClient<typeof operations>();
12
+ const response = await client.query('activity', input, { signal });
13
+ ```
14
+
15
+ The app defines `operations`, `input`, and an optional cancellation `signal`.
16
+ Operation input and output types are inferred from the server registry. Keep the
17
+ registry import type-only so its SQL and implementation stay on the server.
18
+
19
+ The default endpoint is `/api/data`. Override it with
20
+ `createDataClient({ endpoint, fetch })` to change the base URL or supply a Fetch
21
+ implementation. Calls POST JSON to `/api/data/:operation`; the client sends
22
+ operation inputs rather than SQL or credentials.
23
+
24
+ `DataResponse` contains `data`, the exact request `input`, `requestId`,
25
+ `queriedAt`, and `queryIds`. `queries` is present only when the server permits
26
+ SQL disclosure. Use the returned input when labeling stale data during a refresh.
27
+ `DataAppError` exposes a `code` and optional `requestId`; cancellation follows
28
+ the supplied abort signal.
29
+
30
+ See [contracts](contract.md), [server handlers](server.md), and
31
+ [React bindings](react.md).
32
+
33
+ ## Iframe transport
34
+
35
+ `createDataClient({ transport })` accepts a `DataTransport`. Without an explicit
36
+ transport, endpoint, or Fetch implementation, it discovers an installed iframe
37
+ transport before falling back to HTTP. Explicit endpoint/Fetch options select
38
+ HTTP. `createHttpTransport` provides the underlying operation delivery adapter.
39
+
40
+ A URL-hosted app configures trust and installs the bridge before mounting:
41
+
42
+ ```ts
43
+ import {
44
+ createIframeTransport,
45
+ installDataAppTransport,
46
+ } from '@altertable/data-app/client';
47
+
48
+ const bridge = createIframeTransport({
49
+ parentOrigin: 'https://host.example.com',
50
+ });
51
+ const uninstall = installDataAppTransport(bridge);
52
+ ```
53
+
54
+ Use a configured trusted origin, not an unverified URL parameter. Installation makes data clients discover the connection. React mounting and
55
+ URL controls attach the optional navigation adapter to that connection. Cleanup removes
56
+ the installation and disposes pending work. Bundle apps receive this installation
57
+ from the [trusted bootstrap](embed.md#trusted-bootstrap).
58
+
59
+ `getDataAppTransport()` returns the explicitly installed bridge. Its `request`
60
+ transport supports custom typed routes:
61
+
62
+ ```ts
63
+ import {
64
+ createMessageClient,
65
+ getDataAppTransport,
66
+ } from '@altertable/data-app/client';
67
+
68
+ const bridge = getDataAppTransport();
69
+ if (!bridge) throw new Error('An iframe transport must be installed first.');
70
+ const messages = createMessageClient(routes, bridge.request);
71
+ const result = await messages.request('echo', 'hello', { signal });
72
+ ```
73
+
74
+ The app supplies shared `routes` and optional `signal`. Both client and host
75
+ validate messages. The bridge retains opaque host state through `snapshot()` and
76
+ `subscribe(listener)`; only authenticated, current-session state reaches subscribers.
77
+ It never changes browser history or interprets the host state.
78
+
79
+ ## App navigation
80
+
81
+ Navigation is an optional app adapter, separate from bootstrap and data delivery:
82
+
83
+ ```ts
84
+ import { createDataAppNavigation } from '@altertable/data-app/client';
85
+
86
+ const navigation = createDataAppNavigation({ bridge });
87
+ const currentLocation = navigation.snapshot();
88
+ navigation.update({ search: '?period=last-7', hash: '#daily' }, 'push');
89
+ // If app code changes its URL directly:
90
+ navigation.publish('replace');
91
+ // Before removing the app or its transport:
92
+ navigation.dispose();
93
+ ```
94
+
95
+ The adapter provides `snapshot`, `subscribe`, and `update`. Opaque sandboxes keep
96
+ search/hash in memory; URL frames preserve their URL and local-preview parent
97
+ marker. Host Back/Forward state is applied without publishing it back.
98
+
99
+ `getDataAppNavigation()` discovers an installed or verified local-preview bridge
100
+ and shares one adapter per document. React mounting and URL-backed controls call
101
+ it automatically. Apps that only use data delivery do not attach navigation.
102
+
103
+ Migration: replace `bridge.appLocation` with the navigation adapter and
104
+ `bridge.location(mode)` with `navigation.publish(mode)`. The version-1 wire envelope
105
+ now carries host context in `initialize.state` and subsequent `state` messages;
106
+ upgrade independently deployed hosts and runtimes together.
107
+ Transport state is shared per window across separately bundled entry points;
108
+ install only one transport per document. Public error classes retain `instanceof`
109
+ recognition across independent bootstrap and app bundles in that window.
110
+
111
+ ## Pending request limits
112
+
113
+ Each iframe bridge accepts at most 128 unresolved requests, including requests
114
+ waiting for the connection handshake. Further calls reject with `bridge_busy`
115
+ until a pending call completes, is cancelled, or times out. This bounds the
116
+ client's promises, timers, and queued messages.
117
+
118
+ The host independently limits pending requests to 128: an iframe can send
119
+ messages directly without using the client helper. The shared cap is a resource
120
+ policy, not a requirement of the message protocol. It rejects excess requests;
121
+ it does not queue them or limit the total number of calls over a session.
122
+
123
+ A failed local-preview verification can be retried by the next explicit data
124
+ request. Concurrent callers share the current verification attempt; aborting
125
+ one caller does not cancel the others. Retrying does not automatically repeat
126
+ a data operation or bypass the configured parent-origin check, and verification
127
+ failure never falls back to untrusted iframe or HTTP delivery.
package/docs/config.md ADDED
@@ -0,0 +1,21 @@
1
+ # App configuration
2
+
3
+ Import `DataAppConfig` and `dataAppTitle` from `@altertable/data-app/config`.
4
+ Configuration is shared by the app shell and browser mounting code.
5
+
6
+ ```ts
7
+ import type { DataAppConfig } from '@altertable/data-app/config';
8
+
9
+ export const config = {
10
+ title: 'Activity',
11
+ scope: { organization: 'example', environment: 'production' },
12
+ appearance: {},
13
+ } satisfies DataAppConfig;
14
+ ```
15
+
16
+ `title` names the exploration. `scope` identifies its organization and
17
+ environment for display; it does not grant access to data.
18
+ `appearance` is validated by the [appearance APIs](appearance.md).
19
+
20
+ `dataAppTitle(config)` produces a document title containing the app title and
21
+ scope. [React mounting](react.md) applies it automatically.
@@ -0,0 +1,95 @@
1
+ # Contracts
2
+
3
+ Import operation definitions, parsers, and shared types from
4
+ `@altertable/data-app/contract`. This entry is safe to import in browser and server
5
+ modules. Keep SQL and operation implementations on the server; browser modules
6
+ should import their operation types using `import type`.
7
+
8
+ ## Execute named queries
9
+
10
+ The app supplies `calendar`, `parseActivity`, `checkInput`, `buildActivitySql`,
11
+ and `parseActivityRows` in this example.
12
+
13
+ ```ts
14
+ import {
15
+ defineOperation,
16
+ defineQueryNames,
17
+ } from '@altertable/data-app/contract';
18
+
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 },
26
+ async run({ query }, input) {
27
+ const result = await query(queries.activity, buildActivitySql(input));
28
+ return parseActivityRows(result);
29
+ },
30
+ });
31
+ ```
32
+
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. The server records the SQL and query ID when execution occurs, so evidence does not need a separate result field. Browser modules import operation types with `import type`; they never import server implementations.
34
+
35
+ ## Shared date ranges
36
+
37
+ Use `defineDateRangeContract` in a browser-safe module to share source coverage,
38
+ time zone, maximum range, and comparison rules between the server parser and
39
+ React variables. The server uses `calendar.parseRequest`; React uses the same
40
+ contract with `dateRangeVariable`.
41
+
42
+ ```ts
43
+ import { defineDateRangeContract } from '@altertable/data-app/contract';
44
+
45
+ export const calendar = defineDateRangeContract({
46
+ minDate: '2026-01-01',
47
+ maxRangeDays: 90,
48
+ timeZone: 'UTC',
49
+ });
50
+ ```
51
+
52
+ `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
55
+ analysis result.
56
+
57
+ See [server authorization](server.md) and [React views](react.md) for the two
58
+ sides of an operation.
59
+
60
+ ## Message routes
61
+
62
+ Message contracts describe the payloads allowed between an embedded app and its
63
+ host. Share these contracts with the app; keep handlers and authorization in the
64
+ host/server.
65
+
66
+ ```ts
67
+ import {
68
+ createMessageRouter,
69
+ defineMessageRoute,
70
+ navigationUpdateRoute,
71
+ } from '@altertable/data-app/contract';
72
+ import { createNavigationHandler } from '@altertable/data-app/embed';
73
+
74
+ const routes = {
75
+ echo: defineMessageRoute({ input: parseString, output: parseString }),
76
+ 'navigation.update': navigationUpdateRoute,
77
+ };
78
+ const router = createMessageRouter(routes, {
79
+ echo: value => value,
80
+ 'navigation.update': createNavigationHandler(),
81
+ });
82
+ ```
83
+
84
+ The app defines `parseString` to validate unknown values. `router.dispatch` checks
85
+ registered routes, input, output, and cancellation. `MessageRoutingError` exposes
86
+ an intentional public code, message, and optional request ID; other handler
87
+ errors are replaced with a generic failure.
88
+
89
+ `defineDataQueryRoute(operationContracts)` validates the selected operation's
90
+ input and output and preserves its response evidence. It infers the result type
91
+ from the selected operation when used with `createMessageClient`. Share input and
92
+ output parsers, not operation implementations containing SQL or credentials.
93
+ `dataAppRoutes` supplies generic `data.query` and `navigation.update` contracts;
94
+ generic hosts must delegate operation validation and authorization to their
95
+ server. Request handlers receive `{ signal }` for cancellation.
package/docs/embed.md ADDED
@@ -0,0 +1,98 @@
1
+ # Embed a data app
2
+
3
+ Import framework-neutral host APIs from `@altertable/data-app/embed`. For React
4
+ hosts, see [React embedding](react-embed.md). Neither embedding entry requires the
5
+ app UI stylesheet.
6
+
7
+ ## Sources
8
+
9
+ `attachDataAppShell` owns iframe loading, sandbox policy, startup timeout, and
10
+ message delivery. The host supplies an iframe and a validated message dispatcher:
11
+
12
+ ```ts
13
+ import { attachDataAppShell } from '@altertable/data-app/embed';
14
+
15
+ const dispose = attachDataAppShell({
16
+ iframe,
17
+ source: { type: 'url', url: 'https://apps.example.com/report' },
18
+ onMessage: router.dispatch,
19
+ });
20
+ ```
21
+
22
+ The host defines `iframe` and `router`; see [message contracts](contract.md#message-routes).
23
+ Dispose before replacing the source or retrying. A URL source requires HTTP(S)
24
+ and a different origin from its host. The shell adds `__altertable_parent` to the
25
+ app URL. A hosted app must explicitly install a transport to its configured,
26
+ trusted parent origin using the [client API](client.md#iframe-transport).
27
+ The query parameter alone does not establish trust.
28
+
29
+ A bundle source has this shape:
30
+
31
+ ```ts
32
+ const source = {
33
+ type: 'bundle' as const,
34
+ bootstrapUrl: 'https://preview.example.com/bootstrap',
35
+ javascript: bundle.javascript,
36
+ revision: bundle.revision,
37
+ };
38
+ ```
39
+
40
+ The host provides a self-contained JavaScript bundle and a revision identifying
41
+ that bundle. Serve the bootstrap page with a CSP compatible with the bundle.
42
+ Bundle mode uses `sandbox="allow-scripts"` and an opaque origin; it cannot read
43
+ the host document or use same-origin privileges. Both modes use
44
+ `referrerPolicy="no-referrer"`.
45
+
46
+ ## Trusted bootstrap
47
+
48
+ For backend HTML that embeds a ready-made script without bundling, use the
49
+ [standalone bootstrap asset](bootstrap.md).
50
+
51
+ Bundle this initializer into the trusted bootstrap document, before any app code:
52
+
53
+ ```ts
54
+ import { startDataAppBootstrap } from '@altertable/data-app/embed';
55
+
56
+ const dispose = startDataAppBootstrap({
57
+ parentOrigin: 'https://host.example.com',
58
+ });
59
+ ```
60
+
61
+ The bootstrap URL is trusted executable code: it receives the app script and
62
+ session token. Its hosting service must enforce its CSP. The shell cannot impose
63
+ CSP on a remote response. A starting policy for self-contained scripts and styles
64
+ is `default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline';
65
+ img-src data: blob:; connect-src 'none'; base-uri 'none'; form-action 'none'`.
66
+ The initializer installs a shared transport before evaluating the app script;
67
+ data clients discover it even when independently bundled. The bootstrap contains
68
+ no app navigation adapter. React mounting or URL controls attach navigation in the
69
+ app bundle; non-React apps use `createDataAppNavigation` from `/client`.
70
+
71
+ ## Delivery and navigation
72
+
73
+ `attachDataAppBridge` is the lower-level API for a host-owned iframe. Supply
74
+ `connection: { type: 'origin', origin }` or `{ type: 'opaque', token }`, and an
75
+ `onMessage` dispatcher. It returns cleanup and owns source/origin checks, request
76
+ correlation, cancellation, bounded pending requests, and reconnection. Use the
77
+ shell for bundle loading and token rotation. The opaque destination requires
78
+ wildcard delivery, but incoming messages still require the exact iframe window,
79
+ null origin, token, document, and session to match.
80
+
81
+ `createNavigationHandler({ window, reservedSearchParams })` handles the shared
82
+ `navigation.update` route using browser history. Reserved host query parameters
83
+ survive app changes. Supply your own handler when integrating a framework router.
84
+ Host Back/Forward events publish opaque host state through the bridge. The app's
85
+ optional navigation adapter interprets search/hash and synchronizes controls;
86
+ opaque apps keep that state in memory rather than modifying their document URL.
87
+
88
+ ## Status and diagnostics
89
+
90
+ `onStatusChange` receives `connecting`, `connected`, `ready`, `failed`, or
91
+ `disconnected`. `connected` means transport initialization; `ready` means a URL
92
+ client acknowledged initialization or a bundle script finished evaluation. It
93
+ does not mean asynchronous data queries finished. Script errors report `failed`.
94
+ The shell's `startupTimeoutMs` defaults to 30 seconds.
95
+
96
+ `onDiagnostic` receives only message `direction` and `type`, never tokens or
97
+ payloads. Routed handlers must authorize every data request. Message validation
98
+ and iframe isolation do not grant access to data or execute SQL.
package/docs/format.md ADDED
@@ -0,0 +1,29 @@
1
+ # Formatting
2
+
3
+ Import number and date helpers from `@altertable/data-app/format`. They use
4
+ JavaScript `Intl` APIs and can run in the browser or on the server.
5
+
6
+ ```ts
7
+ import {
8
+ formatCount,
9
+ formatDateRange,
10
+ formatNumber,
11
+ formatPercent,
12
+ } from '@altertable/data-app/format';
13
+
14
+ formatCount(1200); // "1,200"
15
+ formatPercent(0.116); // "11.6%"
16
+ formatNumber(null); // "—"
17
+ formatDateRange({ start: '2026-01-01', end: '2026-01-03' });
18
+ ```
19
+
20
+ Percent values are ratios: `0.116` means 11.6%. Counts must be non-negative
21
+ integers. Missing or non-finite numbers render as `—`, customizable with
22
+ `missing`. Number formatting defaults to `en-US`; pass `locale` to override it.
23
+
24
+ `formatMetric` accepts a `MetricFormat` with `kind: 'count'`, `'ratio'`, or
25
+ `'currency'`; currency formats also require a currency code. `formatDateRange`
26
+ formats ISO calendar dates in UTC and retains the year in its labels.
27
+ `pluralize(count, singular, plural?)` selects a label for the count.
28
+
29
+ See [React metric definitions](react.md#bound-views-and-widgets).
@@ -0,0 +1,57 @@
1
+ # React embedding
2
+
3
+ Import `DataAppShell` and `DataAppBridge` from
4
+ `@altertable/data-app/react/embed`. This entry depends on React and the embedding
5
+ engine, and does not load the app's widgets, React Query, or CSS.
6
+
7
+ ## Shell
8
+
9
+ ```tsx
10
+ import { DataAppShell } from '@altertable/data-app/react/embed';
11
+
12
+ <DataAppShell
13
+ title="Activity report"
14
+ source={{ type: 'url', url: 'https://apps.example.com/activity' }}
15
+ onMessage={router.dispatch}
16
+ loading={<p>Loading report…</p>}
17
+ renderError={retry => <button onClick={retry}>Retry report</button>}
18
+ />;
19
+ ```
20
+
21
+ The host supplies `router` using [message contracts](contract.md#message-routes).
22
+ For bundles, use `{ type: 'bundle', bootstrapUrl, javascript, revision }` as
23
+ `source`. See [embedding](embed.md) for trust, sandbox, CSP, and bootstrap setup.
24
+
25
+ The shell creates and owns the iframe. Source changes, bundle revision changes,
26
+ and retries replace the entire frame. Handler changes use the latest callbacks
27
+ without resetting the session. `startupTimeoutMs`, `onStatusChange`, and
28
+ `onDiagnostic` have the same meaning as in the framework-neutral API. The iframe
29
+ remains hidden until ready; loading and error UI have sensible defaults.
30
+
31
+ ## Host-owned iframe
32
+
33
+ Use `DataAppBridge` when the host owns iframe rendering:
34
+
35
+ ```tsx
36
+ import { useState, type ComponentRef } from 'react';
37
+ import { DataAppBridge } from '@altertable/data-app/react/embed';
38
+
39
+ function Host() {
40
+ const [iframe, setIframe] = useState<ComponentRef<'iframe'> | null>(null);
41
+ return (
42
+ <>
43
+ <iframe ref={setIframe} title="Report" src={appUrl} />
44
+ <DataAppBridge
45
+ iframe={iframe}
46
+ connection={{ type: 'origin', origin: new URL(appUrl).origin }}
47
+ onMessage={router.dispatch}
48
+ />
49
+ </>
50
+ );
51
+ }
52
+ ```
53
+
54
+ The app supplies `appUrl` and `router`. A callback ref lets the bridge observe late
55
+ mounting and replacement; listeners attach to the iframe's owner document. The
56
+ bridge handles delivery only. Use `DataAppShell` to manage source loading,
57
+ sandbox policy, bundle tokens, and startup errors.
@@ -0,0 +1,17 @@
1
+ # React styles
2
+
3
+ Import the stylesheet once from the browser entry:
4
+
5
+ ```ts
6
+ import '@altertable/data-app/react/styles.css';
7
+ ```
8
+
9
+ This entry is a CSS asset, with a declaration for TypeScript side-effect imports.
10
+ The app's bundler must process CSS imports. The React JavaScript entry does not
11
+ load it automatically, so server rendering can import components without
12
+ importing CSS into Node.
13
+
14
+ Stylesheets live beside their components in `src/react/ui` and are combined into
15
+ the published stylesheet during the build. Brand settings install semantic CSS
16
+ variables through [appearance](appearance.md); the [React app shell](react.md)
17
+ manages these for normal app usage.