@altertable/data-app 0.61.0 → 0.63.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 (159) hide show
  1. package/AGENTS.md +4 -32
  2. package/CONTRIBUTING.md +63 -72
  3. package/README.md +14 -38
  4. package/dist/chunks/{contract-d9skd1n7.js → contract-8wdybxj7.js} +163 -16
  5. package/dist/chunks/contract-8wdybxj7.js.map +13 -0
  6. package/dist/chunks/{contract-ryyf6dme.js → contract-cxr9t12b.js} +2 -5
  7. package/dist/chunks/contract-cxr9t12b.js.map +10 -0
  8. package/dist/chunks/{contract-14vxdcrs.js → contract-farfe948.js} +17 -21
  9. package/dist/chunks/contract-farfe948.js.map +10 -0
  10. package/dist/chunks/contract-mev09s5v.js.map +2 -2
  11. package/dist/chunks/{contract-ehk50k7e.js → contract-tf8c3qpv.js} +34 -4
  12. package/dist/chunks/contract-tf8c3qpv.js.map +11 -0
  13. package/dist/chunks/{contract-zr4s2g7m.js → contract-tkc552ze.js} +75 -36
  14. package/dist/chunks/contract-tkc552ze.js.map +12 -0
  15. package/dist/chunks/{contract-nt819swq.js → contract-wd8qe3mt.js} +6 -1
  16. package/dist/chunks/{contract-nt819swq.js.map → contract-wd8qe3mt.js.map} +3 -3
  17. package/dist/chunks/contract-wz59z8pq.js.map +1 -1
  18. package/dist/chunks/{contract-7gpsee0v.js → contract-zr3jd7mr.js} +51 -22
  19. package/dist/chunks/contract-zr3jd7mr.js.map +12 -0
  20. package/dist/client/index.js +7 -6
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/core/appearance.js +1 -1
  23. package/dist/core/contract.js +6 -4
  24. package/dist/core/contract.js.map +1 -1
  25. package/dist/embed/index.js +41 -9
  26. package/dist/embed/index.js.map +5 -4
  27. package/dist/local.js +172 -84
  28. package/dist/local.js.map +7 -8
  29. package/dist/react/embed/index.js +83 -99
  30. package/dist/react/embed/index.js.map +4 -5
  31. package/dist/react/index.js +5701 -324
  32. package/dist/react/index.js.map +74 -65
  33. package/dist/server.js +153 -81
  34. package/dist/server.js.map +6 -7
  35. package/dist/types/client/data-client.d.ts +33 -0
  36. package/dist/types/client/iframe.d.ts +14 -0
  37. package/dist/types/client/index.d.ts +4 -34
  38. package/dist/types/client/location.d.ts +4 -6
  39. package/dist/types/client/navigation.d.ts +2 -1
  40. package/dist/types/core/appearance.d.ts +7 -6
  41. package/dist/types/core/bridge.d.ts +2 -8
  42. package/dist/types/core/config.d.ts +1 -1
  43. package/dist/types/core/contract.d.ts +9 -56
  44. package/dist/types/core/format.d.ts +0 -1
  45. package/dist/types/core/messages.d.ts +11 -30
  46. package/dist/types/core/navigation.d.ts +11 -0
  47. package/dist/types/core/operation-types.d.ts +76 -0
  48. package/dist/types/core/operation.d.ts +21 -0
  49. package/dist/types/core/presentation.d.ts +7 -0
  50. package/dist/types/core/variables.d.ts +2 -2
  51. package/dist/types/embed/bridge.d.ts +12 -0
  52. package/dist/types/embed/host.d.ts +14 -5
  53. package/dist/types/embed/index.d.ts +6 -4
  54. package/dist/types/embed/source.d.ts +15 -0
  55. package/dist/types/embed/sql.d.ts +4 -0
  56. package/dist/types/react/content.d.ts +3 -3
  57. package/dist/types/react/embed/bridge.d.ts +19 -10
  58. package/dist/types/react/embed/index.d.ts +0 -2
  59. package/dist/types/react/hooks.d.ts +65 -64
  60. package/dist/types/react/index.d.ts +135 -2
  61. package/dist/types/react/injectStyles.d.ts +7 -0
  62. package/dist/types/react/shellStyles.d.ts +3 -0
  63. package/dist/types/react/styles.d.ts +8 -0
  64. package/dist/types/react/ui/AboutData.d.ts +0 -1
  65. package/dist/types/react/ui/AppFooter.d.ts +0 -1
  66. package/dist/types/react/ui/AppHeader.d.ts +0 -1
  67. package/dist/types/react/ui/AppLayout.d.ts +0 -1
  68. package/dist/types/react/ui/AppScope.d.ts +0 -1
  69. package/dist/types/react/ui/AppToolbar.d.ts +0 -1
  70. package/dist/types/react/ui/Breakdown.d.ts +0 -1
  71. package/dist/types/react/ui/Button.d.ts +0 -1
  72. package/dist/types/react/ui/Checkbox.d.ts +0 -1
  73. package/dist/types/react/ui/Combobox.d.ts +0 -1
  74. package/dist/types/react/ui/ComparisonVisual.d.ts +0 -1
  75. package/dist/types/react/ui/ContentSkeleton.d.ts +2 -5
  76. package/dist/types/react/ui/DataApp.d.ts +2 -2
  77. package/dist/types/react/ui/DataAppSkeleton.d.ts +7 -0
  78. package/dist/types/react/ui/DataBoundary.d.ts +0 -1
  79. package/dist/types/react/ui/DataSection.d.ts +2 -4
  80. package/dist/types/react/ui/DataTable.d.ts +2 -3
  81. package/dist/types/react/ui/DataViewToast.d.ts +0 -1
  82. package/dist/types/react/ui/DataWidget.d.ts +4 -14
  83. package/dist/types/react/ui/DateRangePicker.d.ts +0 -1
  84. package/dist/types/react/ui/DateTimeTooltip.d.ts +0 -1
  85. package/dist/types/react/ui/EmptyState.d.ts +2 -5
  86. package/dist/types/react/ui/GettingStarted.d.ts +0 -1
  87. package/dist/types/react/ui/GlossaryDefinition.d.ts +0 -1
  88. package/dist/types/react/ui/GlossaryExplanation.d.ts +0 -1
  89. package/dist/types/react/ui/GradientScroll.d.ts +0 -1
  90. package/dist/types/react/ui/Grid.d.ts +0 -1
  91. package/dist/types/react/ui/HelpPopover.d.ts +0 -2
  92. package/dist/types/react/ui/Kbd.d.ts +0 -1
  93. package/dist/types/react/ui/LiveControl.d.ts +0 -1
  94. package/dist/types/react/ui/MetricWidget.d.ts +0 -2
  95. package/dist/types/react/ui/PeriodSummary.d.ts +0 -1
  96. package/dist/types/react/ui/PresentStory.d.ts +0 -1
  97. package/dist/types/react/ui/QueryList.d.ts +0 -1
  98. package/dist/types/react/ui/Ranking.d.ts +0 -1
  99. package/dist/types/react/ui/RefreshControl.d.ts +0 -1
  100. package/dist/types/react/ui/RefreshRegion.d.ts +0 -1
  101. package/dist/types/react/ui/RequestHint.d.ts +0 -1
  102. package/dist/types/react/ui/SearchField.d.ts +0 -1
  103. package/dist/types/react/ui/SearchInput.d.ts +1 -2
  104. package/dist/types/react/ui/SearchMatch.d.ts +0 -1
  105. package/dist/types/react/ui/SelectableBarChart.d.ts +0 -1
  106. package/dist/types/react/ui/SelectionMark.d.ts +0 -1
  107. package/dist/types/react/ui/Sheet.d.ts +0 -1
  108. package/dist/types/react/ui/Skeleton.d.ts +0 -1
  109. package/dist/types/react/ui/Stack.d.ts +0 -1
  110. package/dist/types/react/ui/StatusPanel.d.ts +0 -1
  111. package/dist/types/react/ui/TableWidget.d.ts +2 -3
  112. package/dist/types/react/ui/Tabs.d.ts +0 -1
  113. package/dist/types/react/ui/TextContent.d.ts +4 -0
  114. package/dist/types/react/ui/TextWidget.d.ts +19 -0
  115. package/dist/types/react/ui/ThemeSelector.d.ts +0 -1
  116. package/dist/types/react/ui/Tooltip.d.ts +0 -1
  117. package/dist/types/react/ui/UpdatedAt.d.ts +0 -1
  118. package/dist/types/react/ui/VariableBar.d.ts +0 -1
  119. package/dist/types/react/ui/VisualizationWidget.d.ts +5 -13
  120. package/dist/types/react/ui/WidgetDisclosure.d.ts +0 -1
  121. package/dist/types/react/ui/WidgetViewTabs.d.ts +2 -3
  122. package/dist/types/react/ui/data-identifiers.d.ts +0 -1
  123. package/dist/types/react/ui/presentation.d.ts +19 -0
  124. package/dist/types/react/ui/useAppAppearance.d.ts +3 -0
  125. package/dist/types/react/ui/useDataAppPresentation.d.ts +3 -0
  126. package/dist/types/react/view-controls.d.ts +2 -2
  127. package/dist/types/react/view.d.ts +2 -2
  128. package/dist/{bootstrap.js → worker.js} +144 -12
  129. package/docs/app-authoring.md +60 -24
  130. package/docs/client.md +48 -19
  131. package/docs/contract.md +10 -10
  132. package/docs/embed.md +94 -26
  133. package/docs/hosted-apps.md +39 -0
  134. package/docs/local-data-apps.md +14 -0
  135. package/docs/react-embed.md +75 -24
  136. package/docs/react.md +118 -93
  137. package/docs/server-bun.md +4 -1
  138. package/docs/server.md +2 -2
  139. package/docs/worker.md +55 -0
  140. package/examples/starter-data-app/index.tsx +159 -0
  141. package/package.json +18 -12
  142. package/dist/chunks/contract-14vxdcrs.js.map +0 -10
  143. package/dist/chunks/contract-7gpsee0v.js.map +0 -11
  144. package/dist/chunks/contract-d9skd1n7.js.map +0 -12
  145. package/dist/chunks/contract-ehk50k7e.js.map +0 -10
  146. package/dist/chunks/contract-ryyf6dme.js.map +0 -10
  147. package/dist/chunks/contract-zr4s2g7m.js.map +0 -12
  148. package/dist/react/index.css +0 -4478
  149. package/dist/react.css.d.ts +0 -1
  150. package/dist/types/embed/shell.d.ts +0 -21
  151. package/dist/types/embed/standalone.d.ts +0 -1
  152. package/dist/types/react/embed/shell.d.ts +0 -10
  153. package/dist/types/react/ui/index.d.ts +0 -129
  154. package/docs/appearance.md +0 -30
  155. package/docs/bootstrap.md +0 -76
  156. package/docs/config.md +0 -21
  157. package/docs/format.md +0 -29
  158. package/docs/react-styles.md +0 -17
  159. package/docs/starter-agent-instructions.md +0 -53
package/docs/embed.md CHANGED
@@ -6,13 +6,13 @@ app UI stylesheet.
6
6
 
7
7
  ## Sources
8
8
 
9
- `attachDataAppShell` owns iframe loading, sandbox policy, startup timeout, and
9
+ `attachDataAppBridge()` in source mode owns iframe loading, sandbox policy, startup timeout, and
10
10
  message delivery. The host supplies an iframe and a validated message dispatcher:
11
11
 
12
12
  ```ts
13
- import { attachDataAppShell } from '@altertable/data-app/embed';
13
+ import { attachDataAppBridge } from '@altertable/data-app/embed';
14
14
 
15
- const dispose = attachDataAppShell({
15
+ const host = attachDataAppBridge({
16
16
  iframe,
17
17
  source: { type: 'url', url: 'https://apps.example.com/report' },
18
18
  onMessage: router.dispatch,
@@ -20,8 +20,8 @@ const dispose = attachDataAppShell({
20
20
  ```
21
21
 
22
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
23
+ Call `host.dispose()` before replacing the source or retrying. A URL source requires HTTP(S)
24
+ and a different origin from its host. The source bridge adds `__altertable_parent` to the
25
25
  app URL. A hosted app must explicitly install a transport to its configured,
26
26
  trusted parent origin using the [client API](client.md#iframe-transport).
27
27
  The query parameter alone does not establish trust.
@@ -31,24 +31,25 @@ A bundle source has this shape:
31
31
  ```ts
32
32
  const source = {
33
33
  type: 'bundle' as const,
34
- bootstrapUrl: 'https://preview.example.com/bootstrap',
34
+ bootstrapUrl: 'https://my-report-app-1.apps.example.net/',
35
35
  javascript: bundle.javascript,
36
- revision: bundle.revision,
37
36
  };
38
37
  ```
39
38
 
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.
39
+ The host provides a self-contained JavaScript bundle. React bridges replace the
40
+ iframe whenever its JavaScript content changes. Serve the bootstrap page with a CSP compatible with the bundle.
42
41
  Bundle mode uses `sandbox="allow-scripts"` and an opaque origin; it cannot read
43
42
  the host document or use same-origin privileges. Both modes use
44
43
  `referrerPolicy="no-referrer"`.
45
44
 
46
45
  ## Trusted bootstrap
47
46
 
48
- For backend HTML that embeds a ready-made script without bundling, use the
49
- [standalone bootstrap asset](bootstrap.md).
47
+ For Cloudflare hosting, upload the [Worker asset](worker.md). It includes the
48
+ bootstrap HTML and security policy; deployments supply the runtime domain and
49
+ trusted parent origins through bindings.
50
50
 
51
- Bundle this initializer into the trusted bootstrap document, before any app code:
51
+ For other hosts that own their HTML and security policy, bundle this initializer
52
+ into the bootstrap document before any app code:
52
53
 
53
54
  ```ts
54
55
  import { startDataAppBootstrap } from '@altertable/data-app/embed';
@@ -59,22 +60,21 @@ const dispose = startDataAppBootstrap({
59
60
  ```
60
61
 
61
62
  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
+ session token. Its hosting service must enforce its CSP. The bridge cannot impose
63
64
  CSP on a remote response. A starting policy for self-contained scripts and styles
64
65
  is `default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline';
65
66
  img-src data: blob:; connect-src 'none'; base-uri 'none'; form-action 'none'`.
66
67
  The initializer installs a shared transport before evaluating the app script;
67
68
  data clients discover it even when independently bundled. The bootstrap contains
68
69
  no app navigation adapter. React mounting or URL controls attach navigation in the
69
- app bundle; non-React apps use `createDataAppNavigation` from `/client`.
70
+ app bundle; non-React apps use `createDataAppNavigation()` from `/client`.
70
71
 
71
72
  ## Delivery and navigation
72
73
 
73
- `attachDataAppBridge` is the lower-level API for a host-owned iframe. Supply
74
+ `attachDataAppBridge()` also supports connection mode for a host-owned iframe. Supply
74
75
  `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
76
+ `onMessage` dispatcher. Both modes use the same transport. It returns `dispose()` and `setPresentation()` methods and owns source/origin checks, request
77
+ correlation, cancellation, bounded pending requests, and reconnection. Use source mode for bundle loading and token rotation. The opaque destination requires
78
78
  wildcard delivery, but incoming messages still require the exact iframe window,
79
79
  null origin, token, document, and session to match.
80
80
 
@@ -91,16 +91,84 @@ opaque apps keep that state in memory rather than modifying their document URL.
91
91
  `disconnected`. `connected` means transport initialization; `ready` means a URL
92
92
  client acknowledged initialization or a bundle script finished evaluation. It
93
93
  does not mean asynchronous data queries finished. Script errors report `failed`.
94
- The shell's `startupTimeoutMs` defaults to 30 seconds.
94
+ The source bridge's `startupTimeoutMs` defaults to 30 seconds.
95
95
 
96
96
  `onDiagnostic` receives only message `direction` and `type`, never tokens or
97
97
  payloads. Routed handlers must authorize every data request. Message validation
98
98
  and iframe isolation do not grant access to data or execute SQL.
99
99
 
100
- Message types follow `{scope}:{action}`: `bridge:connect`, `bridge:ready`,
101
- `bridge:initialize`, `bridge:request`, `bridge:result`, `bridge:error`,
102
- `bridge:cancel`, `bridge:disconnect`, `runtime:ready`, `runtime:error`,
103
- `script:load`, and `state:update`. Routed requests use the same convention,
104
- including `data:query` and `navigation:update`. Hosts, apps, and bootstrap scripts
105
- must use matching names; the former unscoped and dot-separated names are no
106
- longer supported.
100
+ ## SQL query route
101
+
102
+ Hosts serving browser-owned operations register `sqlQueryRoute` explicitly:
103
+
104
+ ```ts
105
+ import {
106
+ createMessageRouter,
107
+ sqlQueryRoute,
108
+ } from '@altertable/data-app/contract';
109
+ import { createSqlQueryHandler } from '@altertable/data-app/embed';
110
+
111
+ const router = createMessageRouter(
112
+ { 'data:sql': sqlQueryRoute },
113
+ {
114
+ 'data:sql': createSqlQueryHandler(async (query, { signal }) => {
115
+ return authorizedLakehouseForCurrentViewer(query, signal);
116
+ }),
117
+ }
118
+ );
119
+ // Supply router.dispatch as the shell's onMessage handler.
120
+ ```
121
+
122
+ The host supplies `authorizedLakehouseForCurrentViewer()`. Its backend must enforce
123
+ viewer/dataset permissions, permitted query behavior, maximum rows, execution
124
+ time, concurrency, and response size independently of browser policy. Route
125
+ validation is not SQL authorization. `createSqlQueryHandler()` calls authorization
126
+ for each query, forwards cancellation, and preserves `DataSourceError` reasons
127
+ as public `source_*` errors with request IDs. Authorization failures return
128
+ `forbidden`; unknown query errors are hidden. Custom handlers can return deliberate
129
+ public failures with `MessageRoutingError`.
130
+
131
+ `SqlQueryInput` (exported from `/contract`) carries
132
+ `{ statement: string, limit: number }`; responses are
133
+ `{ columns: { name: string, type?: string }[], rows: unknown[][], queryId?: string }`.
134
+ The route rejects empty statements, unsafe or nonpositive limits, malformed
135
+ results, and results exceeding the requested limit. The bridge's existing payload
136
+ and pending-call limits apply, and cancellation reaches the handler's signal.
137
+ Operation names and inputs stay in the app; query evidence is assembled there.
138
+
139
+ `dataAppRoutes` retains named `data:query` and navigation routes for existing
140
+ server-backed hosts. SQL hosts opt into `data:sql`; they need no named-operation
141
+ handler unless they also serve HTTP-style apps. Update the host before switching
142
+ an app to browser-owned operations. Older hosts reject `data:sql` as unknown.
143
+
144
+ ## Parent presentation
145
+
146
+ The parent declares where the iframe is mounted and owns its resolved theme:
147
+
148
+ ```ts
149
+ const host = attachDataAppBridge({
150
+ iframe,
151
+ source: { type: 'url', url: 'https://apps.example.com/report' },
152
+ presentation: { surface: 'embedded', theme: 'dark' },
153
+ onMessage: router.dispatch,
154
+ });
155
+
156
+ // Update presentation without replacing the iframe or its bridge session.
157
+ host.setPresentation({ surface: 'embedded', theme: 'light' });
158
+ ```
159
+
160
+ Both source and connection modes support the `presentation` option and
161
+ `host.setPresentation(presentation)` method. `DataAppPresentation` is exported from
162
+ `/embed`. Use `surface: 'embedded'` when the parent provides page chrome, as in the
163
+ Altertable frontend, and `'standalone'` when the app provides its own header and
164
+ footer. `theme` must be resolved to `'light'` or
165
+ `'dark'`; the parent decides how its system preference is resolved.
166
+
167
+ Presentation travels over `postMessage()` in the authenticated `bridge:initialize` and
168
+ `state:update` messages, alongside `search` and `hash`. Framework-neutral apps
169
+ can narrow the unknown state returned by `bridge.snapshot()` to read its
170
+ `presentation` field, and subscribe through `bridge.subscribe()`. React `<DataApp>` consumes it automatically.
171
+ An embedded surface renders toolbar actions without the page header or footer.
172
+ Both surfaces follow the parent's theme, including presentation mode, without
173
+ changing saved viewer preferences. Omitting presentation preserves standalone behavior;
174
+ `host.setPresentation(undefined)` restores it.
@@ -0,0 +1,39 @@
1
+ # Author a data app
2
+
3
+ Follow the [shared authoring flow](app-authoring.md) using [index.tsx](../examples/starter-data-app/index.tsx) for a hosted / remote / cloud data app. Keep the app in
4
+ one file with public package imports; omit server files, HTML, credentials, and
5
+ relative or app-alias imports.
6
+
7
+ Replace the sample SQL, parsers, filters, data context, story, and configuration with
8
+ an exploration of the source data you inspected. The starter uses two SQL
9
+ `VALUES` rows, so it needs no production table.
10
+
11
+ For execution details, see [browser-owned operations](client.md#browser-owned-operations-for-bundle-apps).
12
+
13
+ ## Convert a local data app
14
+
15
+ 1. Combine the app's operations and parsers, data context, views, story,
16
+ configuration, and browser entry into one `index.tsx`, following the
17
+ [single-file starter](../examples/starter-data-app/index.tsx).
18
+ 2. Replace the HTTP client with `createDataClient({ operations })`, using the
19
+ operation registry as a value. See [browser-owned operations](client.md#browser-owned-operations-for-bundle-apps)
20
+ for execution through the host.
21
+ 3. Remove the Bun server, HTML, server adapters, credentials, and relative or
22
+ app-alias imports. Rewrite any operation that depends on server-only code
23
+ to use the operation's `query()` helper.
24
+ 4. Confirm the host can query the same catalogs, tables, and fields.
25
+ [Verify the app](app-authoring.md#verify-the-app) in the hosted runtime against
26
+ the local version's filters and findings.
27
+
28
+ ## Preview in this repository
29
+
30
+ ```fish
31
+ bun install --frozen-lockfile
32
+ bun run build
33
+ bun browser-tests/server.ts
34
+ ```
35
+
36
+ Open [the starter preview](http://127.0.0.1:27418/starter-data-app).
37
+ Its test host executes the sample SQL through the iframe bridge using SQLite.
38
+
39
+ For checks, see [Contributing](../CONTRIBUTING.md).
@@ -0,0 +1,14 @@
1
+ # Author a local data app
2
+
3
+ Start from the CLI scaffold or the [local starter](https://github.com/altertable-ai/data-app/tree/main/examples/starter-local-data-app).
4
+ Use the starter's README for setup and its AGENTS.md to find app-owned files.
5
+ Replace the connectivity screen with the exploration and story from the
6
+ [shared authoring flow](app-authoring.md).
7
+
8
+ | Task | Documentation |
9
+ | --------------------------------------- | ---------------------------------------- |
10
+ | Serve locally with Bun | [Bun server](server-bun.md) |
11
+ | Execute and authorize server operations | [Server](server.md) |
12
+ | Call operations from the browser | [HTTP client](client.md#http-operations) |
13
+
14
+ You can also [convert the local app to a hosted data app](hosted-apps.md#convert-a-local-data-app).
@@ -1,36 +1,70 @@
1
1
  # React embedding
2
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.
3
+ Import `<DataAppBridge>` from `@altertable/data-app/react/embed`. This entry depends
4
+ on React and the embedding engine, and does not load the app's widgets, React
5
+ Query, or CSS.
6
6
 
7
- ## Shell
7
+ The bridge owns iframe setup and `postMessage()` communication. The consuming
8
+ frontend or CLI owns its shell: fetching a bundle, subscriptions, layout, loading
9
+ and error UI, and retry controls. Both hosts use the same bridge and transport.
10
+
11
+ ## Source-managed iframe
12
+
13
+ Use `source` for local URL apps or hosted bundles. The bridge creates the iframe,
14
+ configures its sandbox, loads its source, and reports connection status. It renders
15
+ only the iframe; it adds no loading, error, or retry UI and does not hide the frame.
8
16
 
9
17
  ```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
- />;
18
+ import { useReducer, useState } from 'react';
19
+ import { DataAppBridge } from '@altertable/data-app/react/embed';
20
+ import type { DataAppStatus } from '@altertable/data-app/embed';
21
+
22
+ function Shell() {
23
+ const [status, setStatus] = useState<DataAppStatus>('connecting');
24
+ const [attempt, retry] = useReducer(value => value + 1, 0);
25
+
26
+ return (
27
+ <div className="app-shell">
28
+ {status !== 'ready' && status !== 'failed' && <p>Loading report…</p>}
29
+ {status === 'failed' && <button onClick={retry}>Retry report</button>}
30
+ <DataAppBridge
31
+ key={attempt}
32
+ title="Activity report"
33
+ source={{ type: 'url', url: 'http://127.0.0.1:25837/' }}
34
+ onMessage={router.dispatch}
35
+ onStatusChange={setStatus}
36
+ iframeProps={{ className: 'app-frame', hidden: status !== 'ready' }}
37
+ />
38
+ </div>
39
+ );
40
+ }
19
41
  ```
20
42
 
21
43
  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.
44
+ The local URL must have a different origin from its shell. For hosted bundles,
45
+ replace `source` with:
46
+
47
+ ```tsx
48
+ source={{ type: 'bundle', bootstrapUrl, javascript }}
49
+ ```
50
+
51
+ The host supplies those bundle values. See [embedding](embed.md) for trust,
52
+ sandbox, CSP, and bootstrap setup.
24
53
 
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.
54
+ Source URL or JavaScript content changes replace the entire iframe. Change the
55
+ bridge's React `key` to retry with a fresh frame. Handler changes use the latest
56
+ callbacks without resetting the session. `startupTimeoutMs`, `onStatusChange`, and
57
+ `onDiagnostic` have the same meaning as in the framework-neutral API.
58
+
59
+ `iframeProps` forwards presentation and accessibility attributes to the iframe,
60
+ including `className`, `style`, and `hidden`. The bridge controls `src`, `srcDoc`,
61
+ `sandbox`, `referrerPolicy`, `loading`, and the callback ref. Loading is always
62
+ `eager` so an iframe hidden until ready can start. Supply `title` directly.
30
63
 
31
64
  ## Host-owned iframe
32
65
 
33
- Use `DataAppBridge` when the host owns iframe rendering:
66
+ Use the same `<DataAppBridge>` with `iframe` and `connection` when the host already
67
+ owns a loaded iframe and its security policy:
34
68
 
35
69
  ```tsx
36
70
  import { useState, type ComponentRef } from 'react';
@@ -52,6 +86,23 @@ function Host() {
52
86
  ```
53
87
 
54
88
  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.
89
+ mounting and replacement; listeners attach to the iframe's owner document. This
90
+ mode renders nothing and handles delivery only. Use source mode for bundle
91
+ loading, sandbox policy, token rotation, and startup timeout. The two prop modes
92
+ are mutually exclusive.
93
+
94
+ ## Loading an embedded app
95
+
96
+ Use `<DataAppSkeleton>` from `/react` while the host builds or starts an app.
97
+ Call `injectDataAppShellStyles()` from `/react` before rendering the placeholder.
98
+ The host owns when to show it and supplies any surrounding header or footer.
99
+ `/react/embed` itself remains independent of UI components and styles.
100
+
101
+ ## Parent-owned presentation
102
+
103
+ Pass `presentation={{ surface: 'embedded', theme: resolvedTheme }}`
104
+ to `<DataAppBridge>` in either source or connection mode. Use `'standalone'` when the app should render its own page chrome.
105
+ Prop updates publish trusted state without reloading the iframe or reconnecting
106
+ the session. Resolve system preference in the parent to `'light'` or `'dark'`.
107
+ Inside an embedded surface, `<DataApp>` retains toolbar actions and hides its header
108
+ and footer. See [parent presentation](embed.md#parent-presentation).