@altertable/data-app 0.59.1 → 0.62.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 (71) hide show
  1. package/AGENTS.md +5 -3
  2. package/CONTRIBUTING.md +16 -4
  3. package/README.md +20 -14
  4. package/dist/chunks/{contract-jksbmt5q.js → contract-3bnrf5pf.js} +27 -5
  5. package/dist/chunks/contract-3bnrf5pf.js.map +10 -0
  6. package/dist/chunks/{contract-tkkc28tg.js → contract-4vrw9zk9.js} +43 -13
  7. package/dist/chunks/contract-4vrw9zk9.js.map +12 -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-nt819swq.js.map +1 -1
  11. package/dist/chunks/{contract-tqrf3ykr.js → contract-rrm7s5zp.js} +61 -32
  12. package/dist/chunks/contract-rrm7s5zp.js.map +12 -0
  13. package/dist/chunks/{contract-mb5nfzwg.js → contract-xjv197ck.js} +178 -31
  14. package/dist/chunks/contract-xjv197ck.js.map +13 -0
  15. package/dist/client/index.js +6 -4
  16. package/dist/client/index.js.map +1 -1
  17. package/dist/core/appearance.js +1 -1
  18. package/dist/core/contract.js +5 -3
  19. package/dist/core/contract.js.map +1 -1
  20. package/dist/embed/index.js +42 -8
  21. package/dist/embed/index.js.map +5 -4
  22. package/dist/local.js +191 -79
  23. package/dist/local.js.map +8 -7
  24. package/dist/react/embed/index.js +84 -99
  25. package/dist/react/embed/index.js.map +4 -5
  26. package/dist/react/index.css +156 -6
  27. package/dist/react/index.js +313 -152
  28. package/dist/react/index.js.map +17 -13
  29. package/dist/server.js +172 -76
  30. package/dist/server.js.map +7 -6
  31. package/dist/types/client/iframe.d.ts +14 -0
  32. package/dist/types/client/index.d.ts +19 -13
  33. package/dist/types/core/appearance.d.ts +7 -6
  34. package/dist/types/core/contract.d.ts +3 -3
  35. package/dist/types/core/messages.d.ts +9 -3
  36. package/dist/types/core/operation.d.ts +27 -0
  37. package/dist/types/core/presentation.d.ts +7 -0
  38. package/dist/types/embed/bridge.d.ts +12 -0
  39. package/dist/types/embed/host.d.ts +10 -3
  40. package/dist/types/embed/index.d.ts +7 -4
  41. package/dist/types/embed/{shell.d.ts → source.d.ts} +5 -4
  42. package/dist/types/embed/sql.d.ts +4 -0
  43. package/dist/types/react/embed/bridge.d.ts +19 -10
  44. package/dist/types/react/embed/index.d.ts +0 -2
  45. package/dist/types/react/ui/DataAppSkeleton.d.ts +8 -0
  46. package/dist/types/react/ui/SearchInput.d.ts +1 -1
  47. package/dist/types/react/ui/index.d.ts +2 -0
  48. package/dist/types/react/ui/useAppAppearance.d.ts +3 -0
  49. package/dist/types/react/ui/useDataAppPresentation.d.ts +3 -0
  50. package/dist/types/react/view-controls.d.ts +1 -1
  51. package/dist/{bootstrap.js → worker.js} +161 -29
  52. package/docs/app-authoring.md +24 -4
  53. package/docs/appearance.md +21 -8
  54. package/docs/client.md +40 -3
  55. package/docs/contract.md +9 -7
  56. package/docs/embed.md +114 -19
  57. package/docs/react-embed.md +84 -24
  58. package/docs/react.md +51 -0
  59. package/docs/releasing.md +5 -0
  60. package/docs/server-bun.md +5 -0
  61. package/docs/starter-agent-instructions.md +4 -2
  62. package/docs/worker.md +57 -0
  63. package/package.json +9 -5
  64. package/dist/chunks/contract-14vxdcrs.js.map +0 -10
  65. package/dist/chunks/contract-jksbmt5q.js.map +0 -10
  66. package/dist/chunks/contract-mb5nfzwg.js.map +0 -12
  67. package/dist/chunks/contract-tkkc28tg.js.map +0 -12
  68. package/dist/chunks/contract-tqrf3ykr.js.map +0 -11
  69. package/dist/types/embed/standalone.d.ts +0 -1
  70. package/dist/types/react/embed/shell.d.ts +0 -10
  71. package/docs/bootstrap.md +0 -76
@@ -1,13 +1,5 @@
1
- /**
2
- * Typed clients for HTTP and iframe delivery; safe to import in the browser.
3
- * @module @altertable/data-app/client
4
- * @see https://github.com/altertable-ai/data-app/blob/main/docs/client.md
5
- */
6
- import { type DataTransport } from './transport.js';
7
- export { createHttpTransport, DataAppError, type DataTransport, } from './transport.js';
8
- export { createIframeTransport, installDataAppTransport, getDataAppTransport, type IframeTransport, } from './iframe.js';
9
- export { createMessageClient } from './messages.js';
10
- import type { DataOperations, DisclosedQuery } from '../core/contract.js';
1
+ import type { DataOperations, DisclosedQuery, Lakehouse } from '../core/contract.js';
2
+ import type { DataTransport } from './transport.js';
11
3
  export type InputOf<T> = T extends {
12
4
  input: (value: unknown) => infer Input;
13
5
  } ? Input : never;
@@ -32,11 +24,25 @@ export type DataClient<Operations extends DataOperations> = {
32
24
  signal?: AbortSignal;
33
25
  }): Promise<DataResponse<OutputOf<Operations[Name]>, InputOf<Operations[Name]>>>;
34
26
  };
35
- /** Browser client for named operations; it sends inputs, never SQL or lakehouse credentials. */
36
- export declare function createDataClient<Operations extends DataOperations>(options?: {
27
+ /** Browser operations and named-operation delivery are mutually exclusive configurations. */
28
+ export type DataClientOptions<Operations extends DataOperations> = {
29
+ operations: Operations;
30
+ lakehouse?: Lakehouse;
31
+ transport?: never;
32
+ endpoint?: never;
33
+ fetch?: never;
34
+ } | {
35
+ operations?: never;
36
+ lakehouse?: never;
37
37
  transport?: DataTransport;
38
38
  endpoint?: string;
39
39
  fetch?: typeof fetch;
40
- }): DataClient<Operations>;
40
+ };
41
+ /** Named HTTP operations, or browser-owned operations executed through an authorized SQL bridge. */
42
+ export declare function createDataClient<Operations extends DataOperations>(options?: DataClientOptions<Operations>): DataClient<Operations>;
41
43
  export type { DataAppLocation, AppLocation } from './location.js';
42
44
  export { createDataAppNavigation, getDataAppNavigation, type DataAppNavigation, } from './navigation.js';
45
+ export { createHttpTransport, DataAppError, type DataTransport, } from './transport.js';
46
+ export { createIframeTransport, installDataAppTransport, getDataAppTransport, type IframeTransport, } from './iframe.js';
47
+ export { createMessageClient } from './messages.js';
48
+ export type { DataAppPresentation } from '../core/presentation.js';
@@ -1,5 +1,7 @@
1
+ export type Theme = 'light' | 'dark';
2
+ export type ThemePreference = Theme | 'system';
1
3
  export type AppearanceSettings = {
2
- mode: 'light' | 'dark' | 'system';
4
+ theme: ThemePreference;
3
5
  baseColor: 'neutral' | 'slate' | 'warm';
4
6
  accentColor: string;
5
7
  darkAccentColor?: string;
@@ -12,10 +14,9 @@ export type AppearanceSettings = {
12
14
  heading: string;
13
15
  };
14
16
  };
15
- export type ThemeMode = AppearanceSettings['mode'];
16
17
  export type ThemeController = {
17
- getMode: () => ThemeMode;
18
- setMode: (mode: ThemeMode) => void;
18
+ getTheme: () => ThemePreference;
19
+ setTheme: (theme: ThemePreference) => void;
19
20
  subscribe: (listener: () => void) => () => void;
20
21
  };
21
22
  export declare function parseAppearance(value: unknown): AppearanceSettings;
@@ -24,5 +25,5 @@ export declare function parseAppearance(value: unknown): AppearanceSettings;
24
25
  * system-theme listening.
25
26
  */
26
27
  export declare function applyAppearance(value: unknown): () => void;
27
- /** Viewer color mode persists independently of app-authored brand tokens. */
28
- export declare function createThemeController(value: unknown): ThemeController;
28
+ /** Persist a viewer theme preference. Applying appearance belongs to the UI owner. */
29
+ export declare function createThemeController(initialTheme?: ThemePreference): ThemeController;
@@ -80,7 +80,7 @@ export declare class DataSourceError extends Error {
80
80
  queryName?: string;
81
81
  constructor(reason: 'unauthorized' | 'forbidden' | 'rate_limited' | 'query_rejected' | 'unavailable', status?: number | undefined);
82
82
  }
83
- /** Server-only query interface supplied by a local or hosted adapter. */
83
+ /** Query interface supplied by a server adapter or an authorized iframe bridge. */
84
84
  export type Lakehouse = {
85
85
  queryAll(statement: string, options: {
86
86
  limit: number;
@@ -93,8 +93,8 @@ export type OperationContext = {
93
93
  signal: AbortSignal;
94
94
  };
95
95
  /**
96
- * Both parsers run on the server before results cross the JSON boundary. Schema libraries can be
97
- * used inside either parser.
96
+ * Parsers run in the operation executor's runtime: server for HTTP apps, browser for bundle apps.
97
+ * Browser validation does not replace backend authorization or query limits.
98
98
  */
99
99
  export type DataOperation<Input, Output> = {
100
100
  input: (value: unknown) => Input;
@@ -1,5 +1,5 @@
1
1
  import type { TransportResponse } from './bridge.js';
2
- import type { DisclosedQuery } from './contract.js';
2
+ import type { DisclosedQuery, QueryResult } from './contract.js';
3
3
  export type MessageContext = {
4
4
  signal: AbortSignal;
5
5
  };
@@ -70,8 +70,14 @@ export type NavigationUpdate = {
70
70
  title?: string;
71
71
  };
72
72
  export declare const navigationUpdateRoute: MessageRoute<NavigationUpdate, null>;
73
+ export type SqlQueryInput = {
74
+ statement: string;
75
+ limit: number;
76
+ };
77
+ /** SQL delivery for browser-owned operations. Hosts must enforce backend access and resource limits. */
78
+ export declare const sqlQueryRoute: MessageRoute<SqlQueryInput, QueryResult>;
73
79
  export declare const dataAppRoutes: {
74
- 'data.query': DataQueryRoute<OperationContracts>;
75
- 'navigation.update': MessageRoute<NavigationUpdate, null>;
80
+ 'data:query': DataQueryRoute<OperationContracts>;
81
+ 'navigation:update': MessageRoute<NavigationUpdate, null>;
76
82
  };
77
83
  export {};
@@ -0,0 +1,27 @@
1
+ import { type DataOperations, type DisclosedQuery, type Lakehouse } from './contract.js';
2
+ type Operation = DataOperations[string];
3
+ type OperationExecutionOptions = {
4
+ operationName: string;
5
+ lakehouse: Lakehouse;
6
+ signal: AbortSignal;
7
+ /** Include query evidence in the response; this does not keep browser-owned SQL private. */
8
+ includeSql: boolean;
9
+ requestId: string;
10
+ };
11
+ /** Shared operation execution. Adapters own authorization, delivery, and public errors. */
12
+ export declare function executeDataOperation(operation: Operation, value: unknown, { operationName, lakehouse, signal: requestSignal, includeSql, requestId, }: OperationExecutionOptions): Promise<{
13
+ body: {
14
+ data: unknown;
15
+ requestId: string;
16
+ queriedAt: string;
17
+ queryIds: string[];
18
+ queries?: DisclosedQuery[] | undefined;
19
+ };
20
+ serializedBody: string;
21
+ }>;
22
+ export declare function toDataOperationFailure(error: unknown, aborted?: boolean): {
23
+ status: number;
24
+ code: string;
25
+ message: string;
26
+ };
27
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { Theme } from './appearance.js';
2
+ /** Presentation owned by the parent shell, delivered only over a trusted bridge. */
3
+ export type DataAppPresentation = {
4
+ surface: 'embedded' | 'standalone';
5
+ theme: Theme;
6
+ };
7
+ export declare function isDataAppPresentation(value: unknown): value is DataAppPresentation;
@@ -0,0 +1,12 @@
1
+ import { type DataAppConnectionOptions } from './host.js';
2
+ import { type DataAppSourceOptions } from './source.js';
3
+ export type DataAppBridgeOptions = (DataAppSourceOptions & {
4
+ connection?: never;
5
+ window?: never;
6
+ javascript?: never;
7
+ }) | (DataAppConnectionOptions & {
8
+ source?: never;
9
+ startupTimeoutMs?: never;
10
+ });
11
+ /** Source mode configures and loads the iframe; connection mode attaches to a host-owned document. */
12
+ export declare function attachDataAppBridge(options: DataAppBridgeOptions): import("./host.js").DataAppHost;
@@ -1,3 +1,4 @@
1
+ import type { DataAppPresentation } from '../core/presentation.js';
1
2
  import { type MessageDispatcher } from '../core/messages.js';
2
3
  export type DataAppConnection = {
3
4
  type: 'origin';
@@ -11,13 +12,19 @@ export type DataAppDiagnostic = {
11
12
  direction: 'send' | 'receive';
12
13
  type: string;
13
14
  };
14
- /** The bridge owns delivery and cancellation; the routed handler owns validation, authorization and execution. */
15
- export declare function attachDataAppBridge({ iframe, connection, javascript, onStatusChange, onDiagnostic, onMessage, window: host, }: {
15
+ export type DataAppHost = {
16
+ dispose: () => void;
17
+ setPresentation: (presentation?: DataAppPresentation) => void;
18
+ };
19
+ export type DataAppConnectionOptions = {
16
20
  iframe: HTMLIFrameElement;
17
21
  connection: DataAppConnection;
18
22
  javascript?: string;
23
+ presentation?: DataAppPresentation;
19
24
  onStatusChange?: (status: DataAppStatus) => void;
20
25
  onDiagnostic?: (event: DataAppDiagnostic) => void;
21
26
  onMessage: MessageDispatcher;
22
27
  window?: Window;
23
- }): () => void;
28
+ };
29
+ /** The bridge owns delivery and cancellation; the routed handler owns validation, authorization and execution. */
30
+ export declare function attachDataAppConnection({ iframe, connection, javascript, presentation, onStatusChange, onDiagnostic, onMessage, window: host, }: DataAppConnectionOptions): DataAppHost;
@@ -3,9 +3,12 @@
3
3
  * @module @altertable/data-app/embed
4
4
  * @see https://github.com/altertable-ai/data-app/blob/main/docs/embed.md
5
5
  */
6
- export { attachDataAppBridge } from './host.js';
7
- export type { DataAppConnection, DataAppStatus, DataAppDiagnostic, } from './host.js';
8
- export { attachDataAppShell } from './shell.js';
9
- export type { DataAppSource, DataAppShellOptions } from './shell.js';
6
+ export { attachDataAppBridge } from './bridge.js';
7
+ export type { DataAppBridgeOptions } from './bridge.js';
8
+ export type { DataAppHost, DataAppConnection, DataAppStatus, DataAppDiagnostic, } from './host.js';
9
+ export type { DataAppSource } from './source.js';
10
10
  export { startDataAppBootstrap } from './bootstrap.js';
11
11
  export { createNavigationHandler } from './navigation.js';
12
+ export { sqlQueryRoute, type SqlQueryInput } from '../core/messages.js';
13
+ export { createSqlQueryHandler } from './sql.js';
14
+ export type { DataAppPresentation } from '../core/presentation.js';
@@ -1,5 +1,6 @@
1
+ import type { DataAppPresentation } from '../core/presentation.js';
1
2
  import type { MessageDispatcher } from '../core/messages.js';
2
- import { type DataAppStatus, type DataAppDiagnostic } from './host.js';
3
+ import { type DataAppStatus, type DataAppHost, type DataAppDiagnostic } from './host.js';
3
4
  export type DataAppSource = {
4
5
  type: 'url';
5
6
  url: string;
@@ -7,15 +8,15 @@ export type DataAppSource = {
7
8
  type: 'bundle';
8
9
  bootstrapUrl: string;
9
10
  javascript: string;
10
- revision: string;
11
11
  };
12
- export type DataAppShellOptions = {
12
+ export type DataAppSourceOptions = {
13
13
  iframe: HTMLIFrameElement;
14
14
  source: DataAppSource;
15
+ presentation?: DataAppPresentation;
15
16
  onMessage: MessageDispatcher;
16
17
  onStatusChange?: (status: DataAppStatus) => void;
17
18
  onDiagnostic?: (event: DataAppDiagnostic) => void;
18
19
  startupTimeoutMs?: number;
19
20
  };
20
21
  /** Owns loading and sandbox policy. Dispose before replacing the source or retrying. */
21
- export declare function attachDataAppShell({ iframe, source, onMessage, onStatusChange, onDiagnostic, startupTimeoutMs, }: DataAppShellOptions): () => void;
22
+ export declare function attachDataAppSource({ iframe, source, presentation, onMessage, onStatusChange, onDiagnostic, startupTimeoutMs, }: DataAppSourceOptions): DataAppHost;
@@ -0,0 +1,4 @@
1
+ import type { Lakehouse } from '../core/contract.js';
2
+ import { type MessageContext, type SqlQueryInput } from '../core/messages.js';
3
+ /** Authorize every SQL request and supply a backend that independently enforces access and query limits. */
4
+ export declare function createSqlQueryHandler(authorize: (query: SqlQueryInput, context: MessageContext) => Promise<Lakehouse>): (input: SqlQueryInput, context: MessageContext) => Promise<import("../core/contract.js").QueryResult>;
@@ -1,12 +1,21 @@
1
- import { type ComponentRef } from 'react';
2
- import { type DataAppConnection, type DataAppStatus, type DataAppDiagnostic } from '../../embed/host.js';
3
- import type { MessageDispatcher } from '../../core/messages.js';
4
- export type DataAppBridgeProps = {
1
+ import { type ComponentRef, type ComponentPropsWithoutRef } from 'react';
2
+ import { type DataAppBridgeOptions } from '../../embed/bridge.js';
3
+ import type { DataAppConnection } from '../../embed/host.js';
4
+ import type { DataAppSource } from '../../embed/source.js';
5
+ export type DataAppBridgeProps = Pick<DataAppBridgeOptions, 'onMessage' | 'onStatusChange' | 'onDiagnostic' | 'presentation'> & ({
6
+ source: DataAppSource;
7
+ title: string;
8
+ startupTimeoutMs?: number;
9
+ iframeProps?: Omit<ComponentPropsWithoutRef<'iframe'>, 'src' | 'srcDoc' | 'sandbox' | 'referrerPolicy' | 'loading' | 'children' | 'title'>;
10
+ iframe?: never;
11
+ connection?: never;
12
+ } | {
5
13
  iframe: ComponentRef<'iframe'> | null;
6
14
  connection: DataAppConnection;
7
- onMessage: MessageDispatcher;
8
- onStatusChange?: (status: DataAppStatus) => void;
9
- onDiagnostic?: (event: DataAppDiagnostic) => void;
10
- };
11
- /** Delivery only; the host owns the iframe and route handlers, including navigation. */
12
- export declare function DataAppBridge({ iframe, connection, onMessage, onStatusChange, onDiagnostic, }: DataAppBridgeProps): null;
15
+ source?: never;
16
+ title?: never;
17
+ startupTimeoutMs?: never;
18
+ iframeProps?: never;
19
+ });
20
+ /** Owns iframe setup and delivery. The consuming shell owns loading, error UI, and retries. */
21
+ export declare function DataAppBridge(props: DataAppBridgeProps): import("react").JSX.Element;
@@ -5,5 +5,3 @@
5
5
  */
6
6
  export { DataAppBridge } from './bridge.js';
7
7
  export type { DataAppBridgeProps } from './bridge.js';
8
- export { DataAppShell } from './shell.js';
9
- export type { DataAppShellProps } from './shell.js';
@@ -0,0 +1,8 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+
3
+ export type DataAppSkeletonProps = Omit<ComponentPropsWithRef<'output'>, 'children' | 'role' | 'aria-busy'> & {
4
+ header?: ReactNode;
5
+ footer?: ReactNode;
6
+ };
7
+ /** Host-side placeholder while a data app starts. Header and footer slots remain host-owned; widget placeholders are decorative. */
8
+ export declare function DataAppSkeleton({ header, footer, className, ...props }: DataAppSkeletonProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import type { ComponentPropsWithRef, ReactNode } from 'react';
2
2
 
3
3
  /** One search control surface for picker search and table/page search. */
4
- export declare function SearchInput({ size, endAction, loading, className, ...props }: Omit<ComponentPropsWithRef<'input'>, 'size' | 'children'> & {
4
+ export declare function SearchInput({ size, endAction, loading, className, onKeyDown, ...props }: Omit<ComponentPropsWithRef<'input'>, 'size' | 'children'> & {
5
5
  size?: 'default' | 'compact';
6
6
  endAction?: ReactNode;
7
7
  /** Replaces the search glyph in its existing space; input and results remain usable. */
@@ -56,6 +56,8 @@ export { EmptyState } from './EmptyState.js';
56
56
  export type { EmptyStateProps } from './EmptyState.js';
57
57
  export { Skeleton } from './Skeleton.js';
58
58
  export type { SkeletonProps } from './Skeleton.js';
59
+ export { DataAppSkeleton } from './DataAppSkeleton.js';
60
+ export type { DataAppSkeletonProps } from './DataAppSkeleton.js';
59
61
  export { DataBoundary } from './DataBoundary.js';
60
62
  export { resolveDataView } from '../../core/data-view.js';
61
63
  export { displayedSnapshot } from '../../core/data-view.js';
@@ -0,0 +1,3 @@
1
+ import { type Theme } from '../../core/appearance.js';
2
+ /** The parent theme takes precedence; only standalone viewers receive controls. */
3
+ export declare function useAppAppearance(appearance: unknown, hostTheme?: Theme): import("../../core/appearance.js").ThemeController | undefined;
@@ -0,0 +1,3 @@
1
+ import { type DataAppPresentation } from '../../core/presentation.js';
2
+ /** Local preview verifies its parent before accepting presentation context. */
3
+ export declare function useDataAppPresentation(): DataAppPresentation | undefined;
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
2
2
  import { type AppVariableValues, type VariableCollection } from './ui/variables.js';
3
3
  import type { ResolvedVariables } from './view.js';
4
4
  /** URL values resolve before facets; facet keys include their dependent inputs for cached, bounded loading. */
5
- export declare function useViewVariables<Variables extends VariableCollection>(definitions: Variables, loadFacet: (operation: string, input: unknown, signal: AbortSignal) => Promise<unknown>): {
5
+ export declare function useViewVariables<Variables extends VariableCollection>(definitions: Variables, clientScope: string, loadFacet: (operation: string, input: unknown, signal: AbortSignal) => Promise<unknown>): {
6
6
  values: AppVariableValues<Variables>;
7
7
  set: <Key extends keyof Variables & string>(name: Key, value: AppVariableValues<Variables>[Key]) => void;
8
8
  reset: <Key extends keyof Variables & string>(name: Key) => void;