@lotics/app-sdk 0.3.0 → 0.6.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.
- package/dist/src/hooks.d.ts +54 -34
- package/dist/src/hooks.js +49 -69
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.js +3 -3
- package/dist/src/rpc.d.ts +3 -4
- package/dist/src/rpc.js +2 -3
- package/dist/src/types.d.ts +25 -47
- package/package.json +1 -1
package/dist/src/hooks.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { AppWorkflows,
|
|
1
|
+
import type { AppWorkflows, AppQueries } from "./types.js";
|
|
2
2
|
interface QueryState<R> {
|
|
3
3
|
rows: R[];
|
|
4
4
|
loading: boolean;
|
|
5
5
|
error: string | null;
|
|
6
6
|
/**
|
|
7
|
-
* Re-run the underlying query against the current AST
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Re-run the underlying query against the current AST. Use after a known
|
|
8
|
+
* mutation point — a successful `useWorkflow(alias)()` call — to pull the
|
|
9
|
+
* latest state.
|
|
10
10
|
*
|
|
11
11
|
* The iframe SDK does not subscribe to realtime invalidation channels in
|
|
12
12
|
* v1; refetch is the explicit refresh path. Future SDK versions may add a
|
|
@@ -14,32 +14,6 @@ interface QueryState<R> {
|
|
|
14
14
|
*/
|
|
15
15
|
refetch: () => void;
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Read all rows from a table. Re-fetches when the table id changes.
|
|
19
|
-
* For derived/joined data, see `useQuery(ast)`.
|
|
20
|
-
*/
|
|
21
|
-
export declare function useTable<K extends keyof WorkspaceTables & string>(table_id: K): QueryState<TableRow<K>>;
|
|
22
|
-
export declare function useTable(table_id: string): QueryState<TableRow<string>>;
|
|
23
|
-
/**
|
|
24
|
-
* Mutation surface for a table. Returns imperative functions; doesn't manage
|
|
25
|
-
* cache invalidation in v1 — components that read via useTable will pick up
|
|
26
|
-
* changes on next remount or via realtime channels in v2.
|
|
27
|
-
*/
|
|
28
|
-
export declare function useMutate<K extends keyof WorkspaceTables & string>(table_id: K): {
|
|
29
|
-
update: (records: Array<{
|
|
30
|
-
id: string;
|
|
31
|
-
data: Partial<RowFor<K>>;
|
|
32
|
-
}>) => Promise<unknown>;
|
|
33
|
-
};
|
|
34
|
-
export declare function useMutate(table_id: string): {
|
|
35
|
-
update: (records: Array<{
|
|
36
|
-
id: string;
|
|
37
|
-
data: Record<string, unknown>;
|
|
38
|
-
}>) => Promise<unknown>;
|
|
39
|
-
};
|
|
40
|
-
type RowFor<K extends keyof WorkspaceTables & string> = WorkspaceTables[K];
|
|
41
|
-
/** Trigger a workflow by action_id. Returns a callable that runs it. */
|
|
42
|
-
export declare function useAction(action_id: string): (inputs?: Record<string, unknown>) => Promise<unknown>;
|
|
43
17
|
/**
|
|
44
18
|
* Trigger a workflow by alias from the app's manifest.
|
|
45
19
|
*
|
|
@@ -63,10 +37,56 @@ export declare function useAction(action_id: string): (inputs?: Record<string, u
|
|
|
63
37
|
export declare function useWorkflow<K extends keyof AppWorkflows & string>(alias: K): UseWorkflowFn<K>;
|
|
64
38
|
export declare function useWorkflow(alias: string): (inputs?: Record<string, unknown>) => Promise<unknown>;
|
|
65
39
|
type UseWorkflowFn<K extends keyof AppWorkflows & string> = AppWorkflows[K] extends Record<string, unknown> ? AppWorkflows[K] extends Record<string, never> ? (inputs?: Record<string, never>) => Promise<unknown> : (inputs: AppWorkflows[K]) => Promise<unknown> : (inputs?: Record<string, unknown>) => Promise<unknown>;
|
|
40
|
+
type UseQueryParams<K extends keyof AppQueries & string> = AppQueries[K] extends Record<string, never> ? [params?: Record<string, never>] : [params: AppQueries[K]];
|
|
66
41
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
42
|
+
* Read rows from a query the app's author declared in `lotics.queries`.
|
|
43
|
+
*
|
|
44
|
+
* The app never sends a raw query AST — it invokes a named query by alias and
|
|
45
|
+
* fills the template's declared `{{params.x}}` value holes. The server holds
|
|
46
|
+
* the canonical AST; this is what bounds a public app's data exposure to
|
|
47
|
+
* exactly the queries the manifest declares.
|
|
48
|
+
*
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const { rows, loading } = useQuery("openOrders", { status: "open" });
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Per-app CLI codegen writes `.lotics/app_queries.d.ts` augmenting `AppQueries`
|
|
54
|
+
* with the declared alias → param-type map, so an undeclared alias is a
|
|
55
|
+
* compile-time error and params are typed per the manifest.
|
|
56
|
+
*/
|
|
57
|
+
export declare function useQuery<K extends keyof AppQueries & string>(alias: K, ...rest: UseQueryParams<K>): QueryState<Record<string, unknown>>;
|
|
58
|
+
export declare function useQuery(alias: string, params?: Record<string, unknown>): QueryState<Record<string, unknown>>;
|
|
59
|
+
/** A file the host has stored and resolved serving URLs for. */
|
|
60
|
+
export interface UploadedFile {
|
|
61
|
+
id: string;
|
|
62
|
+
filename: string;
|
|
63
|
+
mime_type: string;
|
|
64
|
+
url?: string;
|
|
65
|
+
thumbnail_url?: string;
|
|
66
|
+
}
|
|
67
|
+
interface FileUploadState {
|
|
68
|
+
/**
|
|
69
|
+
* Upload one file. Resolves to the stored file; pass `UploadedFile.id` into
|
|
70
|
+
* a `useWorkflow` call to attach it to a record. Rejects on failure — the
|
|
71
|
+
* file is never partially stored.
|
|
72
|
+
*/
|
|
73
|
+
upload: (file: File) => Promise<UploadedFile>;
|
|
74
|
+
/** True while any upload from this hook is in flight. */
|
|
75
|
+
uploading: boolean;
|
|
76
|
+
/** Message of the most recent failed upload, cleared when a new one starts. */
|
|
77
|
+
error: string | null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Upload files from an app. The bytes are stored via a presigned
|
|
81
|
+
* direct-to-storage upload the host mediates; the API server never proxies
|
|
82
|
+
* them. Works the same in a public (anonymous) app and a member-facing one.
|
|
83
|
+
*
|
|
84
|
+
* ```tsx
|
|
85
|
+
* const { upload, uploading } = useFileUpload();
|
|
86
|
+
* const submit = useWorkflow("submitApplication");
|
|
87
|
+
* const cccd = await upload(file);
|
|
88
|
+
* await submit({ ...fields, cccd_file_id: cccd.id });
|
|
89
|
+
* ```
|
|
70
90
|
*/
|
|
71
|
-
export declare function
|
|
91
|
+
export declare function useFileUpload(): FileUploadState;
|
|
72
92
|
export {};
|
package/dist/src/hooks.js
CHANGED
|
@@ -1,77 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typed React hooks for Lotics app data access.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The SDK surface is three hooks: `useQuery` for reads, `useWorkflow` for
|
|
5
|
+
* every mutation, and `useFileUpload` for attaching files. App code never
|
|
6
|
+
* writes records directly — all writes flow through declared workflows, which
|
|
7
|
+
* gives the app owner a typed, audited chokepoint and means a publicly-shared
|
|
8
|
+
* app exposes no anonymous direct-write path. An uploaded file is inert until
|
|
9
|
+
* a workflow attaches it, so file upload keeps that same property.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Every hook is a thin wrapper over the postMessage RPC bridge — the parent
|
|
12
|
+
* does the actual API calls, results stream back through `rpc()`. Hooks manage
|
|
13
|
+
* their own local cache via `useState`; we intentionally don't ship a global
|
|
14
|
+
* store in v1.
|
|
13
15
|
*/
|
|
14
16
|
import { useCallback, useEffect, useState } from "react";
|
|
15
17
|
import { rpc } from "./rpc.js";
|
|
16
|
-
export function useTable(table_id) {
|
|
17
|
-
const [refetchToken, setRefetchToken] = useState(0);
|
|
18
|
-
const [state, setState] = useState({
|
|
19
|
-
rows: [],
|
|
20
|
-
loading: true,
|
|
21
|
-
error: null,
|
|
22
|
-
});
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
let cancelled = false;
|
|
25
|
-
setState((s) => ({ rows: s.rows, loading: true, error: null }));
|
|
26
|
-
// Server's typed-query path accepts `{ kind: "from_table", table_id }`
|
|
27
|
-
// as the simplest AST; the resolver returns rows shaped per the table's
|
|
28
|
-
// schema with source-addressing columns appended.
|
|
29
|
-
rpc("query", {
|
|
30
|
-
ast: { kind: "from_table", table_id },
|
|
31
|
-
})
|
|
32
|
-
.then((result) => {
|
|
33
|
-
if (cancelled)
|
|
34
|
-
return;
|
|
35
|
-
setState({ rows: result.rows ?? [], loading: false, error: null });
|
|
36
|
-
})
|
|
37
|
-
.catch((err) => {
|
|
38
|
-
if (cancelled)
|
|
39
|
-
return;
|
|
40
|
-
setState({ rows: [], loading: false, error: err.message });
|
|
41
|
-
});
|
|
42
|
-
return () => {
|
|
43
|
-
cancelled = true;
|
|
44
|
-
};
|
|
45
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
-
}, [table_id, refetchToken]);
|
|
47
|
-
const refetch = useCallback(() => {
|
|
48
|
-
setRefetchToken((n) => n + 1);
|
|
49
|
-
}, []);
|
|
50
|
-
return { ...state, refetch };
|
|
51
|
-
}
|
|
52
|
-
export function useMutate(table_id) {
|
|
53
|
-
return {
|
|
54
|
-
// The parent's RPC bridge currently dispatches all `mutate` ops to update.
|
|
55
|
-
// We omit the `op` field here rather than ship dead data — when create /
|
|
56
|
-
// delete mutations land, we'll add `op` and a parent-side switch together.
|
|
57
|
-
update: (records) => rpc("mutate", { table_id, records }),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/** Trigger a workflow by action_id. Returns a callable that runs it. */
|
|
61
|
-
export function useAction(action_id) {
|
|
62
|
-
return useCallback((inputs) => rpc("action", { action_id, inputs: inputs ?? {} }), [action_id]);
|
|
63
|
-
}
|
|
64
18
|
export function useWorkflow(alias) {
|
|
65
19
|
return useCallback((inputs) => rpc("workflow", { alias, inputs: inputs ?? {} }), [alias]);
|
|
66
20
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export function useQuery(ast) {
|
|
73
|
-
const astKey = JSON.stringify(ast);
|
|
74
|
-
// Bumping this token re-fires the effect without changing the AST. The
|
|
21
|
+
export function useQuery(alias, params) {
|
|
22
|
+
// Stringified params key — structurally-equal-but-new param objects don't
|
|
23
|
+
// re-fire the effect every render.
|
|
24
|
+
const paramsKey = JSON.stringify(params ?? {});
|
|
25
|
+
// Bumping this token re-fires the effect without changing alias/params. The
|
|
75
26
|
// refetch callback mutates only this counter; state transitions still
|
|
76
27
|
// happen inside the effect so loading / error / rows flow consistently.
|
|
77
28
|
const [refetchToken, setRefetchToken] = useState(0);
|
|
@@ -83,7 +34,7 @@ export function useQuery(ast) {
|
|
|
83
34
|
useEffect(() => {
|
|
84
35
|
let cancelled = false;
|
|
85
36
|
setState((s) => ({ rows: s.rows, loading: true, error: null }));
|
|
86
|
-
rpc("query", {
|
|
37
|
+
rpc("query", { alias, params: params ?? {} })
|
|
87
38
|
.then((result) => {
|
|
88
39
|
if (cancelled)
|
|
89
40
|
return;
|
|
@@ -97,13 +48,42 @@ export function useQuery(ast) {
|
|
|
97
48
|
return () => {
|
|
98
49
|
cancelled = true;
|
|
99
50
|
};
|
|
100
|
-
// Dep is the stringified AST so structurally-equal-but-referentially-new
|
|
101
|
-
// objects don't re-fetch on every render. refetchToken lets the consumer
|
|
102
|
-
// explicitly retrigger without changing the AST identity.
|
|
103
51
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
|
-
}, [
|
|
52
|
+
}, [alias, paramsKey, refetchToken]);
|
|
105
53
|
const refetch = useCallback(() => {
|
|
106
54
|
setRefetchToken((n) => n + 1);
|
|
107
55
|
}, []);
|
|
108
56
|
return { ...state, refetch };
|
|
109
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Upload files from an app. The bytes are stored via a presigned
|
|
60
|
+
* direct-to-storage upload the host mediates; the API server never proxies
|
|
61
|
+
* them. Works the same in a public (anonymous) app and a member-facing one.
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* const { upload, uploading } = useFileUpload();
|
|
65
|
+
* const submit = useWorkflow("submitApplication");
|
|
66
|
+
* const cccd = await upload(file);
|
|
67
|
+
* await submit({ ...fields, cccd_file_id: cccd.id });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export function useFileUpload() {
|
|
71
|
+
const [inFlight, setInFlight] = useState(0);
|
|
72
|
+
const [error, setError] = useState(null);
|
|
73
|
+
const upload = useCallback(async (file) => {
|
|
74
|
+
setInFlight((n) => n + 1);
|
|
75
|
+
setError(null);
|
|
76
|
+
try {
|
|
77
|
+
return await rpc("upload", { file });
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const message = err instanceof Error ? err.message : "Upload failed";
|
|
81
|
+
setError(message);
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
setInFlight((n) => n - 1);
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
88
|
+
return { upload, uploading: inFlight > 0, error };
|
|
89
|
+
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lotics App SDK — the runtime + typed hooks bundled into every custom-code
|
|
3
|
-
* app at build time. Apps `import { mount,
|
|
4
|
-
* and ship the resulting bundle via `lotics app deploy`.
|
|
3
|
+
* app at build time. Apps `import { mount, useQuery, useWorkflow } from
|
|
4
|
+
* "@lotics/app-sdk"` and ship the resulting bundle via `lotics app deploy`.
|
|
5
5
|
*
|
|
6
6
|
* Curated UI primitive re-exports from `@lotics/ui` are deliberately NOT
|
|
7
7
|
* included here. Users import them separately from a published `@lotics/ui`
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* depending on packages/ui's React Native Web setup.
|
|
11
11
|
*/
|
|
12
12
|
export { mount } from "./mount.js";
|
|
13
|
-
export {
|
|
13
|
+
export { useWorkflow, useQuery, useFileUpload } from "./hooks.js";
|
|
14
|
+
export type { UploadedFile } from "./hooks.js";
|
|
14
15
|
export { rpc } from "./rpc.js";
|
|
15
16
|
export type { RpcOp } from "./rpc.js";
|
|
16
|
-
export type {
|
|
17
|
+
export type { AppWorkflows, AppQueries } from "./types.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lotics App SDK — the runtime + typed hooks bundled into every custom-code
|
|
3
|
-
* app at build time. Apps `import { mount,
|
|
4
|
-
* and ship the resulting bundle via `lotics app deploy`.
|
|
3
|
+
* app at build time. Apps `import { mount, useQuery, useWorkflow } from
|
|
4
|
+
* "@lotics/app-sdk"` and ship the resulting bundle via `lotics app deploy`.
|
|
5
5
|
*
|
|
6
6
|
* Curated UI primitive re-exports from `@lotics/ui` are deliberately NOT
|
|
7
7
|
* included here. Users import them separately from a published `@lotics/ui`
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
* depending on packages/ui's React Native Web setup.
|
|
11
11
|
*/
|
|
12
12
|
export { mount } from "./mount.js";
|
|
13
|
-
export {
|
|
13
|
+
export { useWorkflow, useQuery, useFileUpload } from "./hooks.js";
|
|
14
14
|
export { rpc } from "./rpc.js";
|
package/dist/src/rpc.d.ts
CHANGED
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
* iframe → parent: { id: number, op: string, payload: unknown }
|
|
11
11
|
* parent → iframe: { id: number, type: "result", data } | { id, type: "error", message }
|
|
12
12
|
*
|
|
13
|
-
* Bumping protocol version requires a coordinated change in the parent
|
|
14
|
-
*
|
|
15
|
-
* redeploy. Add new ops alongside existing ones; never repurpose them.
|
|
13
|
+
* Bumping protocol version requires a coordinated change in the parent.
|
|
14
|
+
* Add new ops alongside existing ones; never repurpose them.
|
|
16
15
|
*/
|
|
17
|
-
export type RpcOp = "query" | "
|
|
16
|
+
export type RpcOp = "query" | "workflow" | "upload";
|
|
18
17
|
export declare function rpc<T = unknown>(op: RpcOp, payload: unknown): Promise<T>;
|
package/dist/src/rpc.js
CHANGED
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
* iframe → parent: { id: number, op: string, payload: unknown }
|
|
11
11
|
* parent → iframe: { id: number, type: "result", data } | { id, type: "error", message }
|
|
12
12
|
*
|
|
13
|
-
* Bumping protocol version requires a coordinated change in the parent
|
|
14
|
-
*
|
|
15
|
-
* redeploy. Add new ops alongside existing ones; never repurpose them.
|
|
13
|
+
* Bumping protocol version requires a coordinated change in the parent.
|
|
14
|
+
* Add new ops alongside existing ones; never repurpose them.
|
|
16
15
|
*/
|
|
17
16
|
const pending = new Map();
|
|
18
17
|
let nextRpcId = 0;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,71 +1,49 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* App-specific workflow augmentation point.
|
|
3
3
|
*
|
|
4
|
-
* The base SDK ships `
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The base SDK ships `AppWorkflows` empty — `useWorkflow(alias)` accepts any
|
|
5
|
+
* string at runtime. Per-app codegen (folded into `lotics app pull`) emits a
|
|
6
|
+
* file that augments this interface with the aliases declared in the app's
|
|
7
|
+
* `package.json` lotics.workflows, giving compile-time autocomplete +
|
|
8
|
+
* "undeclared alias" errors:
|
|
8
9
|
*
|
|
9
10
|
* ```ts
|
|
10
|
-
* // .lotics/
|
|
11
|
+
* // .lotics/app_workflows.d.ts (generated)
|
|
11
12
|
* import "@lotics/app-sdk";
|
|
12
13
|
* declare module "@lotics/app-sdk" {
|
|
13
|
-
* interface
|
|
14
|
-
* "
|
|
14
|
+
* interface AppWorkflows {
|
|
15
|
+
* "issueInvoiceStorageDrop": { record_id: string };
|
|
16
|
+
* "issueInvoiceReuseLift": Record<string, never>;
|
|
15
17
|
* }
|
|
16
18
|
* }
|
|
17
19
|
* ```
|
|
18
20
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* are `Record<string, unknown>` — code still runs, just untyped.
|
|
21
|
+
* The value type is the workflow's declared input shape — `Record<string, never>`
|
|
22
|
+
* for a workflow that takes no typed inputs, the typed object otherwise.
|
|
22
23
|
*/
|
|
23
|
-
export interface
|
|
24
|
+
export interface AppWorkflows {
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
|
-
* App-specific
|
|
27
|
+
* App-specific named-query augmentation point. Same pattern as AppWorkflows.
|
|
27
28
|
*
|
|
28
|
-
* The base SDK ships `
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* "undeclared alias" errors:
|
|
29
|
+
* The base SDK ships `AppQueries` empty — `useQuery(alias)` accepts any string
|
|
30
|
+
* at runtime. Per-app codegen (`lotics app pull`) emits a file that augments
|
|
31
|
+
* this interface with the queries declared in the app's `package.json`
|
|
32
|
+
* lotics.queries, mapping each alias to its declared param type:
|
|
33
33
|
*
|
|
34
34
|
* ```ts
|
|
35
|
-
* // .lotics/
|
|
35
|
+
* // .lotics/app_queries.d.ts (generated)
|
|
36
36
|
* import "@lotics/app-sdk";
|
|
37
37
|
* declare module "@lotics/app-sdk" {
|
|
38
|
-
* interface
|
|
39
|
-
* "
|
|
40
|
-
* "
|
|
38
|
+
* interface AppQueries {
|
|
39
|
+
* "openOrders": { status: string };
|
|
40
|
+
* "allContainers": Record<string, never>;
|
|
41
41
|
* }
|
|
42
42
|
* }
|
|
43
43
|
* ```
|
|
44
44
|
*
|
|
45
|
-
* The
|
|
46
|
-
*
|
|
47
|
-
* input type. For now the hook's runtime signature is unaffected.
|
|
45
|
+
* The value type is the query's declared param shape — `Record<string, never>`
|
|
46
|
+
* for a query that takes no params, the typed object otherwise.
|
|
48
47
|
*/
|
|
49
|
-
export interface
|
|
48
|
+
export interface AppQueries {
|
|
50
49
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Helper: row type for a known table id, or fallback for unknown ones.
|
|
53
|
-
* Currently unused by exported hooks (useRecord deferred to v2) but kept
|
|
54
|
-
* for the eventual augmented-typing path.
|
|
55
|
-
*/
|
|
56
|
-
export type RowOf<K extends string> = K extends keyof WorkspaceTables ? WorkspaceTables[K] : Record<string, unknown>;
|
|
57
|
-
/** Source addressing emitted by the server for every record-derived row. */
|
|
58
|
-
export interface SourceAddressing {
|
|
59
|
-
__source_table_id?: string;
|
|
60
|
-
__source_record_id?: string;
|
|
61
|
-
__source_locked?: boolean;
|
|
62
|
-
}
|
|
63
|
-
/** A single record row plus source-addressing metadata. */
|
|
64
|
-
export type TableRow<K extends string> = RowOf<K> & SourceAddressing;
|
|
65
|
-
/**
|
|
66
|
-
* Query AST passed to `useQuery` for power-user composition. Mirrors
|
|
67
|
-
* `AppDataSourceQuery.root` server-side. Kept opaque (`unknown`) here
|
|
68
|
-
* because the recursive zod schema doesn't translate to a clean TS type.
|
|
69
|
-
* The server validates via `parseQueryNode`.
|
|
70
|
-
*/
|
|
71
|
-
export type QueryAst = unknown;
|