@lotics/app-sdk 0.124.7 → 0.124.8
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/AGENTS.md +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +11 -1
- package/dist/mock.d.ts +7 -0
- package/dist/queries.d.ts +8 -0
- package/dist/rpc.d.ts +2 -2
- package/dist/shared_types.d.ts +4 -2
- package/docs/data_fetching.md +5 -2
- package/docs/runtime.md +4 -3
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -14,7 +14,7 @@ This file is the index. The **exact type** of anything is its shipped declaratio
|
|
|
14
14
|
|
|
15
15
|
| Doc | Read it for |
|
|
16
16
|
|---|---|
|
|
17
|
-
| [docs/data_fetching.md](./docs/data_fetching.md) | The reads: `useQuery(alias, params, opts)` — its rows (`limit`), numbered pages (`page`), a keyset feed (`more`), its `total` or the total alone, with `total: { by }` a count per value of one column and `total: { where }` a count per named filter, from one scan — `useQueries` for reads known only at render, `queryAll` outside React, `exportQuery` for a file of them the server makes; every hook answers one `QueryState`. The ROW type (`RowOf` — the alias's projected columns and nothing else), runtime `sort`/`filter` keys, cell readers (`row.*`, `readSelect`, `readMembers`, `readLinks`, `readFiles`, `readLocked`/`readLockedBy`, `readCreatedAt`/`readUpdatedAt`), the SDK's own cache — **arrival revalidates** — **realtime push** and `answeredAt` (the server answering again what a screen drew), a write drawn on every read from the press, a count as its own full scan, and the search-as-you-type and record-picker patterns. |
|
|
17
|
+
| [docs/data_fetching.md](./docs/data_fetching.md) | The reads: `useQuery(alias, params, opts)` — its rows (`limit`), numbered pages (`page`), a keyset feed (`more`), its `total` or the total alone, with `total: { by }` a count per value of one column and `total: { where }` a count per named filter, from one scan — `useQueries` for reads known only at render, `queryAll` outside React, `exportQuery` for a file of them the server makes, `exportApp` for the file of an Export of a JSON app; every hook answers one `QueryState`. The ROW type (`RowOf` — the alias's projected columns and nothing else), runtime `sort`/`filter` keys, cell readers (`row.*`, `readSelect`, `readMembers`, `readLinks`, `readFiles`, `readLocked`/`readLockedBy`, `readCreatedAt`/`readUpdatedAt`), the SDK's own cache — **arrival revalidates** — **realtime push** and `answeredAt` (the server answering again what a screen drew), a write drawn on every read from the press, a count as its own full scan, and the search-as-you-type and record-picker patterns. |
|
|
18
18
|
| [docs/queries.md](./docs/queries.md) | **The query engine reference** — AST node kinds, per-field-type operators, filters/params/pruning, free-text search, combining tables, shaping (aggregates, date buckets, windows), runtime refinement bounds, limits and the efficiency playbook. |
|
|
19
19
|
| [docs/mutations.md](./docs/mutations.md) | `useWorkflow` (the ONLY write path; `useWorkflows` for writes that are data), the `WorkflowResult` resolve-never-throw contract (`field_errors`), typed inputs, every write drawn on every read from the press — predicted from the workflow's own steps, taken back on a refusal, `pending` meanwhile — the re-read a successful write triggers over the tables its body names, diff-before-update, locked records, `useNewRecord`, read-after-write ordering (`writesSettled()`), `useRecording`, `useRecordings` over several aliases. |
|
|
20
20
|
| [docs/workflows.md](./docs/workflows.md) | **The workflow-BODY reference** — the JS subset a body may use, opaque `fld_*`/`opt_*` keys, every step form, the accepted sugar, helpers, record-write surfaces, the traps and the verify loop. |
|
package/dist/index.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ export { reportAppError } from "./error_report.js";
|
|
|
5
5
|
export type { AppErrorKind } from "./error_report.js";
|
|
6
6
|
export { useWorkflow, useWorkflows, writesSettled, useFileUpload, useAttachments, useAttachmentPiles, useMembers, useAgentRun, useAgentRuns, useAiContext, buildChoiceOutput, } from "./hooks.js";
|
|
7
7
|
export type { UploadedFile, WorkflowResult, MembersOptions, AgentRunOptions, UseAgentRun, AgentRunLanding, AgentRunRecord, AgentRunState, AgentUIPart, PendingChoice, ChoiceQuestion, ChoiceOption, AskUserChoiceOutput, } from "./hooks.js";
|
|
8
|
-
export { useQuery, useQueries, queryAll, exportQuery, useFieldOptions } from "./queries.js";
|
|
8
|
+
export { useQuery, useQueries, queryAll, exportQuery, exportApp, useFieldOptions } from "./queries.js";
|
|
9
9
|
export type { QueryCall, QueryState, RowOf, QueryOptions, ColumnKeyOf, FieldOptions, FieldOptionsState, FieldOptionsOptions, FigureUnits, } from "./queries.js";
|
|
10
10
|
export type { QueryRow, QuerySortKey, QueryFieldSortKey, QueryFirstSortKey, QueryFilter, QueryFilterCondition, QueryFilterFieldCondition, QueryFilterRecordIdCondition, QueryFilterGroup, ExportCall, ExportFile, ExportReport, ExportTab, } from "./shared_types.js";
|
|
11
11
|
export type { ExportedFile } from "./shared_types.js";
|
|
12
|
+
export type { ExportNarrowing } from "./shared_types.js";
|
|
12
13
|
export type { AttachedFile, AttachmentPiles, AttachmentPilesOptions, AttachmentsOptions, AttachmentsState } from "./attachments.js";
|
|
13
14
|
export { useComments, useCommentCounts } from "./comments.js";
|
|
14
15
|
export type { AppComment, AppCommentAuthor, AppCommentFile, CommentsState, UseCommentsArgs, CommentCountsState, UseCommentCountsArgs, } from "./comments.js";
|
|
@@ -30,7 +31,7 @@ export { downloadFile } from "./download.js";
|
|
|
30
31
|
export { renameFile } from "./rename_file.js";
|
|
31
32
|
export { readFiles, readMembers, readSelect } from "./shared_types.js";
|
|
32
33
|
export type { AppFile, PhotoPlace, ResolvedMember, ResolvedOption } from "./shared_types.js";
|
|
33
|
-
export type { AppFixture, MockExport, MockQuery, MockQueryCall, MockWorkflow } from "./mock.js";
|
|
34
|
+
export type { AppFixture, MockExport, MockExportApp, MockQuery, MockQueryCall, MockWorkflow } from "./mock.js";
|
|
34
35
|
export type { MockAgent, MockAgentRun } from "./mock_agent_run.js";
|
|
35
36
|
export type { AppWorkflows, AppWorkflowResults, AppQueries, AppQueryColumns, AppAgents, AppAgentResults } from "./types.js";
|
|
36
37
|
export { row, readLinks } from "./shared_types.js";
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,10 @@ function getMockExport(alias) {
|
|
|
95
95
|
if (!isMockMode()) return null;
|
|
96
96
|
return registeredFixture?.exports?.[alias] ?? null;
|
|
97
97
|
}
|
|
98
|
+
function getMockExportApp(key) {
|
|
99
|
+
if (!isMockMode()) return null;
|
|
100
|
+
return registeredFixture?.exportApps?.[key] ?? null;
|
|
101
|
+
}
|
|
98
102
|
function getMockRecordings() {
|
|
99
103
|
if (!isMockMode()) return null;
|
|
100
104
|
return registeredFixture?.recordings ?? null;
|
|
@@ -712,7 +716,7 @@ function useKey(key, fetcher, opts = {}) {
|
|
|
712
716
|
if (slot === null) return;
|
|
713
717
|
const answered2 = slot.askedAt > 0;
|
|
714
718
|
slot.mounts.set(id, { focus });
|
|
715
|
-
if (!answered2 || arrival && seeded.current !== key
|
|
719
|
+
if (slot.outdated || slot.error === null && (!answered2 || arrival && seeded.current !== key)) void ask(slot, slot.outdated);
|
|
716
720
|
return () => {
|
|
717
721
|
slot.mounts.delete(id);
|
|
718
722
|
};
|
|
@@ -14217,6 +14221,11 @@ async function exportQuery(alias, call) {
|
|
|
14217
14221
|
if (mocked !== null) return mocked(body);
|
|
14218
14222
|
return (await rpc("export", body)).file;
|
|
14219
14223
|
}
|
|
14224
|
+
async function exportApp(key, narrowing) {
|
|
14225
|
+
const mocked = getMockExportApp(key);
|
|
14226
|
+
if (mocked !== null) return mocked(narrowing);
|
|
14227
|
+
return (await rpc("export", { export: { key, ...narrowing } })).file;
|
|
14228
|
+
}
|
|
14220
14229
|
var NO_UNITS = {};
|
|
14221
14230
|
function useFieldOptions(alias, opts) {
|
|
14222
14231
|
const enabled = opts?.enabled ?? true;
|
|
@@ -14656,6 +14665,7 @@ export {
|
|
|
14656
14665
|
askAi,
|
|
14657
14666
|
buildChoiceOutput,
|
|
14658
14667
|
downloadFile,
|
|
14668
|
+
exportApp,
|
|
14659
14669
|
exportQuery,
|
|
14660
14670
|
hostSpec,
|
|
14661
14671
|
installRpcTransport,
|
package/dist/mock.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type { QueryAggregate } from "./shared_types.js";
|
|
|
10
10
|
import type { WorkflowResult } from "./hooks.js";
|
|
11
11
|
import type { MockAgent } from "./mock_agent_run.js";
|
|
12
12
|
import type { ExportedFile } from "./shared_types.js";
|
|
13
|
+
import type { ExportNarrowing } from "./shared_types.js";
|
|
13
14
|
import type { ExportCall, QuerySortKey } from "./shared_types.js";
|
|
14
15
|
import type { RecordingState } from "./recording_state.js";
|
|
15
16
|
/** A result, or a function of the inputs — return a slow promise to review the pending state. */
|
|
@@ -29,6 +30,8 @@ export type MockQuery = Array<Record<string, unknown>> | ((call: MockQueryCall)
|
|
|
29
30
|
export type MockExport = (call: ExportCall & {
|
|
30
31
|
alias: string;
|
|
31
32
|
}) => ExportedFile | Promise<ExportedFile>;
|
|
33
|
+
/** The file an Export of the app makes of its narrowing, as a function of the call — return a slow promise to review the pending state. */
|
|
34
|
+
export type MockExportApp = (narrowing: ExportNarrowing) => ExportedFile | Promise<ExportedFile>;
|
|
32
35
|
export interface AppFixture {
|
|
33
36
|
queries?: Record<string, MockQuery>;
|
|
34
37
|
workflows?: Record<string, MockWorkflow>;
|
|
@@ -36,6 +39,8 @@ export interface AppFixture {
|
|
|
36
39
|
agents?: Record<string, MockAgent>;
|
|
37
40
|
/** By the query exported. */
|
|
38
41
|
exports?: Record<string, MockExport>;
|
|
42
|
+
/** By the key of the Export made. */
|
|
43
|
+
exportApps?: Record<string, MockExportApp>;
|
|
39
44
|
/** `useRecording(alias)` reads as available; `start`/`stop` change nothing. */
|
|
40
45
|
recordings?: Record<string, RecordingState>;
|
|
41
46
|
}
|
|
@@ -50,4 +55,6 @@ export declare function getMockWorkflow(alias: string): MockWorkflow | null;
|
|
|
50
55
|
export declare function getMockAgent(alias: string): MockAgent | null;
|
|
51
56
|
/** Null for an unmocked alias, which the server exports. */
|
|
52
57
|
export declare function getMockExport(alias: string): MockExport | null;
|
|
58
|
+
/** Null for an unmocked key, which the server exports. */
|
|
59
|
+
export declare function getMockExportApp(key: string): MockExportApp | null;
|
|
53
60
|
export declare function getMockRecordings(): Record<string, RecordingState> | null;
|
package/dist/queries.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { QueryAggregate } from "./shared_types.js";
|
|
|
2
2
|
import type { AppQueries, AppQueryColumns } from "./types.js";
|
|
3
3
|
import type { ResolvedOption } from "./shared_types.js";
|
|
4
4
|
import type { ExportedFile } from "./shared_types.js";
|
|
5
|
+
import type { ExportNarrowing } from "./shared_types.js";
|
|
5
6
|
import type { ExportCall, QueryFilter, QueryRow, QuerySortKey } from "./shared_types.js";
|
|
6
7
|
/**
|
|
7
8
|
* The row type of alias `K`: its projected columns plus the two addressing
|
|
@@ -152,6 +153,13 @@ export declare function queryAll(alias: string, params?: Record<string, unknown>
|
|
|
152
153
|
* Resolves a link to download the file, signed for at most an hour.
|
|
153
154
|
*/
|
|
154
155
|
export declare function exportQuery(alias: string, call: ExportCall): Promise<ExportedFile>;
|
|
156
|
+
/**
|
|
157
|
+
* The file the server makes of one Export of the app — named by its `key` — over the register as `narrowing` states it
|
|
158
|
+
* (the tab, status, chips, search, period, window, order, grouping and folder; `per` the value a report of one is made
|
|
159
|
+
* for). The server reads the rows, the band's readings and the names, under the caller's own access. Resolves a link to
|
|
160
|
+
* download the file, signed for at most an hour.
|
|
161
|
+
*/
|
|
162
|
+
export declare function exportApp(key: string, narrowing: ExportNarrowing): Promise<ExportedFile>;
|
|
155
163
|
/** One select column's full option set. */
|
|
156
164
|
export interface FieldOptions {
|
|
157
165
|
/** The source field's display name. */
|
package/dist/rpc.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AppAbsence } from "./shared_types.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { SpecFile } from "./shared_types.js";
|
|
3
3
|
import type { AskUserChoiceOutput } from "./agent_stream.js";
|
|
4
4
|
import { type UrlParams, type UrlParamsPatch } from "./url_params.js";
|
|
5
5
|
/**
|
|
@@ -104,7 +104,7 @@ export type RpcTransport = (op: RpcOp, payload: unknown) => Promise<unknown>;
|
|
|
104
104
|
* last install wins, and a page installs its own before it mounts. */
|
|
105
105
|
export declare function installRpcTransport(transport: RpcTransport): void;
|
|
106
106
|
/** What the host answers `spec`: the app's compiled spec itself, never the route's wrapper around it. */
|
|
107
|
-
export type HostSpec =
|
|
107
|
+
export type HostSpec = SpecFile;
|
|
108
108
|
/** The app's spec, as the host hands it over the bridge. */
|
|
109
109
|
export declare function hostSpec(): Promise<HostSpec>;
|
|
110
110
|
export declare function rpc<T = unknown>(op: RpcOp, payload: unknown): Promise<T>;
|
package/dist/shared_types.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export type ExportCall = {
|
|
|
41
41
|
*/
|
|
42
42
|
labels?: { readonly [x: string]: Readonly<Record<string, string>>; };
|
|
43
43
|
};
|
|
44
|
+
export type ExportNarrowing = { status?: string[] | undefined; filters?: Record<string, { kind: "ids"; ids: string[]; unset?: true | undefined; } | { kind: "members"; mine: boolean; ids: string[]; unset?: true | undefined; } | { kind: "dates"; from: string | null; to: string | null; unset?: true | undefined; } | { kind: "yes_no"; value: boolean; } | { kind: "number"; min: number | null; max: number | null; unit?: string | undefined; } | { kind: "bands"; bands: { min: number | null; max: number | null; }[]; } | null> | undefined; search?: string | undefined; overdue?: true | undefined; waiting?: boolean | undefined; tab?: string | undefined; period?: { from: string; to: string; } | undefined; window?: { from: string; to: string; } | undefined; sort?: { field: string; dir: "asc" | "desc"; } | undefined; group?: string | null | undefined; folder?: string | undefined; per?: string | undefined; };
|
|
44
45
|
/**
|
|
45
46
|
* What an export states of its rows beside them: the page's title, one line per thing narrowing them, and the
|
|
46
47
|
* figures over them as the screen drew them.
|
|
@@ -141,7 +142,7 @@ export type QueryRow = {
|
|
|
141
142
|
};
|
|
142
143
|
/** One key of a read's order; each breaks the ties of the ones before it. */
|
|
143
144
|
export type QuerySortKey = QueryFieldSortKey | QueryFirstSortKey;
|
|
144
|
-
export type QueryWriteShape = { columns: Record<string, { table_id: string; field_key: string; } | { table_id: string; via: string; target_table_id: string; field_key: string; } | null>; rows?: { table_id: string; closed: boolean; sort: { field_key: string; order: "asc" | "desc"; }[]; filter?: TableRecordFilters | undefined; } | undefined; totals?: { table_id: string; by: { output: string; field_key: string; }[]; aggregates: { output: string; operation: "min" | "
|
|
145
|
+
export type QueryWriteShape = { columns: Record<string, { table_id: string; field_key: string; } | { table_id: string; via: string; target_table_id: string; field_key: string; } | null>; rows?: { table_id: string; closed: boolean; sort: { field_key: string; order: "asc" | "desc"; }[]; filter?: TableRecordFilters | undefined; } | undefined; totals?: { table_id: string; by: { output: string; field_key: string; }[]; aggregates: { output: string; operation: "min" | "max" | "empty" | "sum" | "unique" | "date_range" | "count" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "avg" | "median" | "range" | "earliest" | "latest" | "selected" | "checked" | "unchecked" | "percent_checked" | "percent_unchecked" | "string_agg"; field_key?: string | undefined; }[]; filter?: TableRecordFilters | undefined; } | undefined; };
|
|
145
146
|
/**
|
|
146
147
|
* A member, in the one shape a `select_member` cell and the `useMembers`
|
|
147
148
|
* roster both return. `email`, `image`, `groups`, `role` and `joined` reach
|
|
@@ -184,7 +185,8 @@ export type ResolvedOption = {
|
|
|
184
185
|
/** A brand or icon mark, from `useFieldOptions` only. */
|
|
185
186
|
mark?: { kind: "brand"; name: string; } | { kind: "icon"; name: string; };
|
|
186
187
|
};
|
|
187
|
-
export type TableField = { key: string; name: string; description: string; type: "number"; format: "number" | "currency" | "percentage"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; currency?: string | null | undefined; unit?: string | null | undefined; unit_field?: string | null | undefined; currency_field?: string | null | undefined; default_value?: number | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "text"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; unique?: boolean | undefined; format?: "text" | "link" | "markdown" | undefined; default_value?: string | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "date"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; format?: "date" | "datetime" | "date_range" | "datetime_range" | undefined; timezone?: string | undefined; derive_from?: "created_at" | "updated_at" | undefined; default_value?: string | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "boolean"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; default_value?: boolean | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select"; options: { key: string; name: string; color: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone"; alias?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[]; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; multi?: boolean | undefined; default_value?: string[] | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_member"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; multi?: boolean | undefined; default_value?: string[] | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_record_link"; table_id: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; paired_field_key?: string | undefined; cardinality?: "one" | "many" | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "rollup"; source_field_key: string; aggregate_option: { operation: "count"; field_key?: string | undefined; } | { operation: "unique" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique"; field_key: string; } | { operation: "min" | "unique" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "sum" | "avg" | "median" | "max" | "range"; field_key: string; } | { operation: "unique" | "date_range" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "earliest" | "latest"; field_key: string; } | { operation: "unique" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "selected"; field_key: string; } | { operation: "empty" | "filled" | "percent_empty" | "percent_filled" | "selected"; field_key: string; } | { operation: "checked" | "unchecked" | "percent_checked" | "percent_unchecked"; field_key: string; } | { operation: "min" | "unique" | "date_range" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "sum" | "avg" | "median" | "max" | "range" | "earliest" | "latest" | "checked" | "unchecked" | "percent_checked" | "percent_unchecked"; field_key: string; } | { operation: "min" | "unique" | "date_range" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "sum" | "avg" | "median" | "max" | "range" | "earliest" | "latest"; field_key: string; }; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; filter?: TableRecordFiltersGroupNode | undefined; aggregate_field_type?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "rollup" | "files" | "formula" | "lookup" | "button" | "autonumber" | undefined; aggregate_field_notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; aggregate_field_format?: string | undefined; aggregate_field_currency?: string | undefined; aggregate_field_unit?: string | undefined; aggregate_field_unit_field?: string | undefined; aggregate_field_currency_field?: string | undefined; } | { key: string; name: string; description: string; type: "lookup"; source_field_key: string; lookup_field_key: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; lookup_field_type?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "rollup" | "files" | "formula" | "lookup" | "button" | "autonumber" | undefined; lookup_field_notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; lookup_field_format?: string | undefined; lookup_field_currency?: string | undefined; lookup_field_unit?: string | undefined; lookup_field_options?: { key: string; name: string; color?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; lookup_field_autonumber?: boolean | undefined; order_by?: { field_key: string; direction: "asc" | "desc"; } | undefined; } | { key: string; name: string; description: string; type: "files"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; } | { key: string; name: string; description: string; type: "formula"; formula: { expression: string; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; link?: true | undefined; format?: "number" | "currency" | "percentage" | "link" | undefined; currency?: string | undefined; unit?: string | undefined; unit_field?: string | undefined; currency_field?: string | undefined; options?: { key: string; name: string; color: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone"; alias?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; output_type?: "number" | "boolean" | "date" | "datetime" | "text" | "select" | undefined; volatile?: boolean | undefined; }; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; } | { key: string; name: string; description: string; type: "button"; text: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; workflow_id?: string | null | undefined; } | { key: string; name: string; description: string; type: "autonumber"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; prefix?: string | undefined; padding?: number | undefined; template?: string | undefined; };
|
|
188
|
+
export type SpecFile = { version: 29; app: { alias: string; label: string; locale: "vi" | "en"; description?: string | undefined; }; entities: Record<string, { alias: string; label: string; table: string; fields: Record<string, { alias: string; id: string; type: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber"; label: string; result?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber" | undefined; description?: string | undefined; format?: "datetime" | "text" | "link" | "markdown" | undefined; multi?: true | undefined; required?: true | undefined; readonly?: true | undefined; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; limit?: { field: string; } | { value: number; } | undefined; gate?: { field: string; } | { value: number; } | undefined; tolerance?: number | undefined; due?: true | undefined; frees?: true | undefined; summed?: true | undefined; accumulates?: true | undefined; whole?: true | undefined; restates?: string | undefined; starts?: "today" | "me" | undefined; default?: string | number | boolean | string[] | undefined; options?: { alias: string; label: string; id: string; color?: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone" | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; link?: string | undefined; accept?: string | undefined; image?: string | undefined; party?: "person" | "organization" | undefined; pictures?: { query: string; param: string; } | undefined; face?: Record<string, string> | undefined; default_from?: { link: string; field: string; } | undefined; reads?: string[] | undefined; through?: { link: string; field: string; } | undefined; pick?: { query: string; param?: string | undefined; same?: { field: string; param: string; }[] | undefined; } | undefined; picks_only?: true | undefined; suggest?: { query: string; param: string; column: string; } | undefined; applies?: ({ field: string; options: string[]; } | { field: string; value: boolean; })[] | undefined; }>; title: string; list: { query: string; param?: string | undefined; }; singular?: string | undefined; subtitle?: string[] | undefined; image?: string | undefined; mark?: string | undefined; description?: string | undefined; party?: "person" | "organization" | undefined; mine?: string | undefined; status?: { field: string; closed?: string[] | undefined; history?: string | undefined; dates?: Record<string, string> | undefined; stages?: Record<string, { owner: { kind: "member"; column: string; label: string; } | { kind: "group"; group: string; label: string; }; within?: number | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; }> | undefined; maker?: string | undefined; moves?: { query: string; param: string; link: string; status: string; at: string; went: Record<string, string>; by?: string | undefined; } | undefined; } | undefined; settled?: string | undefined; task?: { close?: { to: string; act?: string | undefined; } | undefined; reopen?: { to: string; act?: string | undefined; } | undefined; member?: string | undefined; } | undefined; progress?: { entity: string; via: string; read: { query: string; param: string; }; passed?: string | undefined; }[] | undefined; figure?: string | undefined; scan?: { read: "text" | "iso6346"; agent: string; } | { read: "code"; } | undefined; place?: { address: string; latitude: string; longitude: string; accuracy?: string | undefined; } | undefined; on_site?: { photos: string[]; latitude: string; longitude: string; radius: number; place?: string | undefined; } | undefined; pin?: { on: string; x: string; y: string; } | undefined; size?: { across: string; along: string; up: string; } | undefined; listed?: string[] | undefined; one?: { query: string; param?: string | undefined; } | undefined; create?: { workflow: string; fields: string[]; picker?: string[] | undefined; } | undefined; update?: { workflow: string; fields?: string[] | undefined; } | undefined; remove?: { workflow: string; with?: string[] | undefined; } | undefined; }>; lists: { entity: string; register: { columns: string[]; filters: (string | { field: string; tiers: number[]; })[]; layout: "table" | "cards" | "calendar" | "roster" | "lanes" | "gantt" | "plan"; book?: true | undefined; search?: string[] | undefined; sort?: { field: string; desc?: true | undefined; }[] | undefined; group?: string | undefined; tabs?: { tab: string; label: string; term?: { kind: "in"; field: string; } | { kind: "open"; from: string; to: string; } | undefined; totals?: { via: string; read: { query: string; }; columns: { alias: string; label: string; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; }[]; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; member: "me"; })[] | undefined; sort?: { field: string; desc?: true | undefined; }[] | undefined; }[] | undefined; period?: "today" | "week" | "month" | "quarter" | "year" | { days: number; } | undefined; roster?: { entity: string; via: string; read: { query: string; param: string; }; day?: string | undefined; end?: string | undefined; expect?: string | undefined; of?: string | undefined; cadence?: { on: ("mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun")[]; from?: string | undefined; until?: string | undefined; } | undefined; create?: string[] | undefined; } | undefined; lanes?: { field: string; start: string; end?: string | undefined; loads?: { value: string; against: string; }[] | undefined; read?: { query: string; param: string; } | undefined; } | undefined; gantt?: { start: string; end: string; milestones?: string[] | undefined; planned?: { start?: string | undefined; end?: string | undefined; } | undefined; progress?: string | undefined; after?: string | undefined; } | undefined; plan?: { at: string; across: string; up: string; place: { entity: string; kind: string; kinds: { bay: string; area: string; landmark: string; }; x: string; y: string; across: string; up: string; sheet?: string | undefined; scale?: string | undefined; width?: string | undefined; depth?: string | undefined; reach_from?: string | undefined; stacked?: string | undefined; reaches?: { first: string; last: string; both: string; } | undefined; size?: { across: string; along: string; up: string; } | undefined; }; fill?: string | undefined; mark?: { field: string; option: string; } | undefined; size?: { field: string; options: Record<string, { across: number; along: number; up: number; }>; } | undefined; load?: { query: string; param?: string | undefined; } | undefined; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; opens?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; member: "me"; })[] | undefined; remove?: false | undefined; create?: string[] | undefined; add_label?: string | undefined; heading?: string | undefined; export?: { query: string; key: string; label?: string | undefined; description?: string | undefined; template?: string | undefined; filename?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; member: "me"; })[] | undefined; per?: string | undefined; per_row?: string[] | undefined; books?: string[] | undefined; }[] | undefined; readings?: ({ entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "metric"; value?: string | undefined; average?: true | undefined; over?: string | undefined; target?: number | undefined; better?: "up" | "down" | undefined; bound?: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "breakdown"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { over: string; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "trend"; value?: string | undefined; ahead?: true | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { rows: string | { field: string; tiers: number[]; }; columns: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "pivot"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string; on_hand: { entity: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; by: string; across?: string | undefined; value?: string | undefined; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "shortfall"; across?: string | undefined; value?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { planned: { start: string; end: string; }; done: string; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "progress"; finished?: string | undefined; weight?: string | undefined; measured?: { via: string; read: { query: string; param: string; }; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { columns: string[]; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "list"; app?: { id: string; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; })[] | undefined; attention?: false | undefined; }; key?: string | undefined; label?: string | undefined; scope?: { field: string; entity: string; param: string; counts: { query: string; param?: string | undefined; }; } | undefined; books?: { entity: string; fields?: Record<string, string> | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; create?: string[] | undefined; }[] | undefined; owns?: { queries: string[]; workflows: string[]; } | undefined; }[]; pages: Record<string, { sections: { fields: string[]; blocks: ({ kind: "timeline"; entity: string; via: string; read: { query: string; param?: string | undefined; }; title: string; at: string; by?: string | undefined; create?: string[] | undefined; add_label?: string | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "rows"; entity: string; via: string; read: { query: string; param?: string | undefined; }; title: string; columns: string[]; create?: string[] | undefined; add_label?: string | undefined; totals?: Record<string, string> | undefined; narrower?: string[] | undefined; soonest?: string[] | undefined; expect?: string | undefined; of?: string | undefined; where?: { field: string; options: string[]; }[] | undefined; filters?: (string | { field: string; tiers: number[]; })[] | undefined; search?: string[] | undefined; group?: string | undefined; sort?: { field: string; desc?: true | undefined; }[] | undefined; edits?: string[] | undefined; remove?: false | undefined; pick?: { read: { query: string; param?: string | undefined; }; back: string; same?: { field: string; param: string; }[] | undefined; admits?: { query: string; param?: string | undefined; } | undefined; } | undefined; checklist?: { photo?: string | undefined; clause?: string | undefined; words?: string[] | undefined; failed?: string[] | undefined; passed?: string | undefined; } | undefined; compare?: { before: string; after: string; } | undefined; current?: string | undefined; post?: { text: string; media: string; network: string; networks: Record<string, "x" | "facebook" | "instagram" | "threads" | "linkedin">; bodies?: string[] | undefined; compute?: { expression: string; reads: Record<string, { from: "row"; field: string; } | { from: "record"; field: string; } | { from: "place"; field: string; } | { from: "formula"; expression: string; }>; } | undefined; link?: string | undefined; group?: string | undefined; } | undefined; under?: { field: string; entity: string; via: string; back: string; read: { query: string; param?: string | undefined; }; where?: { field: string; options: string[]; }[] | undefined; } | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "agenda"; entity: string; via: string; read: { query: string; param?: string | undefined; }; title: string; columns: string[]; day: string; time?: string | undefined; part?: string | undefined; start?: string | undefined; create?: string[] | undefined; add_label?: string | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "files"; fields: string[]; title: string; add_label?: string | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "text"; field: string; title: string; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "figures"; fields: string[]; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "statement"; title: string; lines: { field: string; op?: "plus" | "minus" | "times" | "equals" | undefined; rate?: string | undefined; parts?: { entity: string; read: { query: string; param: string; }; same: string; columns: string[]; figure: string; before?: { field: string; param: string; } | undefined; } | undefined; }[]; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "reading"; reading: { entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "metric"; value?: string | undefined; average?: true | undefined; over?: string | undefined; target?: number | undefined; better?: "up" | "down" | undefined; bound?: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "breakdown"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { over: string; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "trend"; value?: string | undefined; ahead?: true | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { rows: string | { field: string; tiers: number[]; }; columns: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "pivot"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string; on_hand: { entity: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; by: string; across?: string | undefined; value?: string | undefined; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "shortfall"; across?: string | undefined; value?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { planned: { start: string; end: string; }; done: string; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "progress"; finished?: string | undefined; weight?: string | undefined; measured?: { via: string; read: { query: string; param: string; }; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { columns: string[]; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "list"; app?: { id: string; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; }; via: string; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "list"; reading: { columns: string[]; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "list"; app?: { id: string; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; }; via: string; same?: { field: string; param: string; } | undefined; bounds?: { field: string; param: string; of: "number" | "date"; lookup?: true | undefined; }[] | undefined; open?: "in_place" | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | { kind: "funnel"; entity: string; read: { query: string; param?: string | undefined; }; label: string; steps: string[]; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; })[]; acts: string[]; title?: string | undefined; description?: string | undefined; at?: string[] | undefined; reads?: string[] | undefined; entry?: string[] | undefined; acts_last?: true | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; page?: string | undefined; side?: true | undefined; }[]; door?: "page" | "drawer" | "beside" | undefined; outside?: { key: string; entity: string; title: string; at: string[]; fields: string[]; one: { query: string; param: string; }; blocks: { kind: "list"; reading: { columns: string[]; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "list"; app?: { id: string; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; }; via: string; same?: { field: string; param: string; } | undefined; bounds?: { field: string; param: string; of: "number" | "date"; lookup?: true | undefined; }[] | undefined; open?: "in_place" | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; }[]; acts: string[]; description?: string | undefined; }[] | undefined; comments?: true | undefined; moves?: { kind: "timeline"; entity: string; via: string; read: { query: string; param?: string | undefined; }; title: string; at: string; by?: string | undefined; create?: string[] | undefined; add_label?: string | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; } | undefined; compose?: { use: "Rows"; child: string; create?: false | undefined; remove?: true | undefined; } | { use: "Title"; } | { use: "Status"; } | { use: "Field"; field: string; } | { use: "Document"; field: string; } | { use: "Act"; act: string; } | { use: "Copy"; field: string; as: "plain" | "raw"; } | { use: "Column"; children: ComposeNode[]; } | { use: "Bar"; children: ComposeNode[]; } | { use: "Group"; title: string; children: ComposeNode[]; } | { use: "Panel"; title: string; children: ComposeNode[]; } | undefined; }>; acts: { alias: string; label: string; on: "record" | "rows" | "view"; of: string; workflow?: string | undefined; open?: { app: string; } | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; to?: string | undefined; writes?: true | undefined; into?: string | undefined; paper?: { entity: string; option: string; } | undefined; marks?: Record<string, boolean> | undefined; requires?: string[] | undefined; recommends?: string[] | undefined; asks?: (string | { input: string; label: string; type: "number" | "member" | "date" | "file" | "text" | "select" | "link" | "long_text"; options?: { alias: string; label: string; color?: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone" | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; entity?: string | undefined; required?: true | undefined; default?: string | undefined; accept?: string | undefined; })[] | undefined; starts?: Record<string, "today" | "me"> | undefined; makes?: string | undefined; papers?: { template: string; label: string; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; })[] | undefined; }[] | undefined; archive?: string | undefined; intake?: { agent: string; papers: { entity: string; kind: string; files: string; }; fields: string[]; parties?: { link: string; fields: string[]; }[] | undefined; rows?: { entity: string; fields: string[]; key?: string[] | undefined; }[] | undefined; } | undefined; recording?: { into: string; via: string; audio: string; transcript: string; video?: string | undefined; agent?: string | undefined; } | undefined; sign?: { into: string; by: string; at: string; witness?: string | undefined; } | undefined; review?: true | undefined; confirm?: string | true | undefined; danger?: true | undefined; }[]; checks: { field: string; of: string; blocks?: string[] | "all" | undefined; resolve?: string | undefined; reads?: string[] | undefined; }[]; } | { version: 29; app: { alias: string; label: string; locale: "vi" | "en"; description?: string | undefined; }; dashboard: ({ entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "metric"; value?: string | undefined; average?: true | undefined; over?: string | undefined; target?: number | undefined; better?: "up" | "down" | undefined; bound?: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "breakdown"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { over: string; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "trend"; value?: string | undefined; ahead?: true | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { rows: string | { field: string; tiers: number[]; }; columns: string | { field: string; tiers: number[]; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "pivot"; value?: string | undefined; over?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { by: string; on_hand: { entity: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; by: string; across?: string | undefined; value?: string | undefined; }; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "shortfall"; across?: string | undefined; value?: string | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; } | { columns: string[]; entity: string; label: string; read: { query: string; param?: string | undefined; aggregate?: { by?: string[] | undefined; bands?: Record<string, number[]> | undefined; day?: string | undefined; sum?: string | undefined; } | undefined; }; kind: "list"; app?: { id: string; } | undefined; where?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; } | { field: string; member: "me"; })[] | undefined; tab?: string | undefined; })[]; entities: Record<string, { alias: string; label: string; table: string; fields: Record<string, { alias: string; id: string; type: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber"; label: string; result?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber" | undefined; description?: string | undefined; format?: "datetime" | "text" | "link" | "markdown" | undefined; multi?: true | undefined; required?: true | undefined; readonly?: true | undefined; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; limit?: { field: string; } | { value: number; } | undefined; gate?: { field: string; } | { value: number; } | undefined; tolerance?: number | undefined; due?: true | undefined; frees?: true | undefined; summed?: true | undefined; accumulates?: true | undefined; whole?: true | undefined; restates?: string | undefined; starts?: "today" | "me" | undefined; default?: string | number | boolean | string[] | undefined; options?: { alias: string; label: string; id: string; color?: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone" | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; link?: string | undefined; accept?: string | undefined; image?: string | undefined; party?: "person" | "organization" | undefined; pictures?: { query: string; param: string; } | undefined; face?: Record<string, string> | undefined; default_from?: { link: string; field: string; } | undefined; reads?: string[] | undefined; through?: { link: string; field: string; } | undefined; pick?: { query: string; param?: string | undefined; same?: { field: string; param: string; }[] | undefined; } | undefined; picks_only?: true | undefined; suggest?: { query: string; param: string; column: string; } | undefined; applies?: ({ field: string; options: string[]; } | { field: string; value: boolean; })[] | undefined; }>; title: string; list: { query: string; param?: string | undefined; }; singular?: string | undefined; subtitle?: string[] | undefined; image?: string | undefined; mark?: string | undefined; description?: string | undefined; party?: "person" | "organization" | undefined; mine?: string | undefined; status?: { field: string; closed?: string[] | undefined; history?: string | undefined; dates?: Record<string, string> | undefined; stages?: Record<string, { owner: { kind: "member"; column: string; label: string; } | { kind: "group"; group: string; label: string; }; within?: number | undefined; when?: ({ field: string; options: string[]; } | { field: string; value: boolean; } | { field: string; compare: Compared; of: "number" | "date" | "datetime"; zone?: string | undefined; } | { field: string; empty: boolean; })[] | undefined; }> | undefined; maker?: string | undefined; moves?: { query: string; param: string; link: string; status: string; at: string; went: Record<string, string>; by?: string | undefined; } | undefined; } | undefined; settled?: string | undefined; task?: { close?: { to: string; act?: string | undefined; } | undefined; reopen?: { to: string; act?: string | undefined; } | undefined; member?: string | undefined; } | undefined; progress?: { entity: string; via: string; read: { query: string; param: string; }; passed?: string | undefined; }[] | undefined; figure?: string | undefined; scan?: { read: "text" | "iso6346"; agent: string; } | { read: "code"; } | undefined; place?: { address: string; latitude: string; longitude: string; accuracy?: string | undefined; } | undefined; on_site?: { photos: string[]; latitude: string; longitude: string; radius: number; place?: string | undefined; } | undefined; pin?: { on: string; x: string; y: string; } | undefined; size?: { across: string; along: string; up: string; } | undefined; listed?: string[] | undefined; one?: { query: string; param?: string | undefined; } | undefined; create?: { workflow: string; fields: string[]; picker?: string[] | undefined; } | undefined; update?: { workflow: string; fields?: string[] | undefined; } | undefined; remove?: { workflow: string; with?: string[] | undefined; } | undefined; }>; tabs?: { tab: string; label: string; }[] | undefined; period?: "month" | "quarter" | "year" | { days: 30; } | undefined; };
|
|
189
|
+
export type TableField = { key: string; name: string; description: string; type: "number"; format: "number" | "currency" | "percentage"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; currency?: string | null | undefined; unit?: string | null | undefined; unit_field?: string | null | undefined; currency_field?: string | null | undefined; default_value?: number | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "text"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; unique?: boolean | undefined; format?: "text" | "link" | "markdown" | undefined; default_value?: string | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "date"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; format?: "date" | "datetime" | "date_range" | "datetime_range" | undefined; timezone?: string | undefined; derive_from?: "created_at" | "updated_at" | undefined; default_value?: string | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "boolean"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; default_value?: boolean | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select"; options: { key: string; name: string; color: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone"; alias?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[]; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; multi?: boolean | undefined; default_value?: string[] | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_member"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; multi?: boolean | undefined; default_value?: string[] | null | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_record_link"; table_id: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; paired_field_key?: string | undefined; cardinality?: "one" | "many" | undefined; default_from?: { link_field_key: string; field_key: string; through?: string[] | undefined; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "rollup"; source_field_key: string; aggregate_option: { operation: "count"; field_key?: string | undefined; } | { operation: "empty" | "unique" | "filled" | "percent_empty" | "percent_filled" | "percent_unique"; field_key: string; } | { operation: "min" | "max" | "empty" | "sum" | "unique" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "avg" | "median" | "range"; field_key: string; } | { operation: "empty" | "unique" | "date_range" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "earliest" | "latest"; field_key: string; } | { operation: "empty" | "unique" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "selected"; field_key: string; } | { operation: "empty" | "filled" | "percent_empty" | "percent_filled" | "selected"; field_key: string; } | { operation: "checked" | "unchecked" | "percent_checked" | "percent_unchecked"; field_key: string; } | { operation: "min" | "max" | "empty" | "sum" | "unique" | "date_range" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "avg" | "median" | "range" | "earliest" | "latest" | "checked" | "unchecked" | "percent_checked" | "percent_unchecked"; field_key: string; } | { operation: "min" | "max" | "empty" | "sum" | "unique" | "date_range" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "avg" | "median" | "range" | "earliest" | "latest"; field_key: string; }; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; filter?: TableRecordFiltersGroupNode | undefined; aggregate_field_type?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber" | "button" | undefined; aggregate_field_notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; aggregate_field_format?: string | undefined; aggregate_field_currency?: string | undefined; aggregate_field_unit?: string | undefined; aggregate_field_unit_field?: string | undefined; aggregate_field_currency_field?: string | undefined; } | { key: string; name: string; description: string; type: "lookup"; source_field_key: string; lookup_field_key: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; lookup_field_type?: "number" | "boolean" | "date" | "text" | "select" | "select_member" | "select_record_link" | "files" | "formula" | "rollup" | "lookup" | "autonumber" | "button" | undefined; lookup_field_notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; lookup_field_format?: string | undefined; lookup_field_currency?: string | undefined; lookup_field_unit?: string | undefined; lookup_field_options?: { key: string; name: string; color?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; lookup_field_autonumber?: boolean | undefined; order_by?: { field_key: string; direction: "asc" | "desc"; } | undefined; } | { key: string; name: string; description: string; type: "files"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; } | { key: string; name: string; description: string; type: "formula"; formula: { expression: string; notation?: { style: "decimal"; } | { style: "currency"; currency: string | { per_row: string; }; } | { style: "unit"; unit: string | { per_row: string; }; } | undefined; link?: true | undefined; format?: "number" | "link" | "currency" | "percentage" | undefined; currency?: string | undefined; unit?: string | undefined; unit_field?: string | undefined; currency_field?: string | undefined; options?: { key: string; name: string; color: "red" | "orange" | "amber" | "yellow" | "lime" | "green" | "emerald" | "teal" | "cyan" | "sky" | "blue" | "indigo" | "violet" | "purple" | "fuchsia" | "pink" | "rose" | "slate" | "gray" | "zinc" | "neutral" | "stone"; alias?: string | undefined; mark?: { kind: string; name: string; } | undefined; }[] | undefined; output_type?: "number" | "boolean" | "date" | "datetime" | "text" | "select" | undefined; volatile?: boolean | undefined; }; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; } | { key: string; name: string; description: string; type: "button"; text: string; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; workflow_id?: string | null | undefined; } | { key: string; name: string; description: string; type: "autonumber"; alias?: string | undefined; confirm_before_update?: boolean | undefined; required?: boolean | undefined; prefix?: string | undefined; padding?: number | undefined; template?: string | undefined; };
|
|
188
190
|
export type TableRecordFilters = TableRecordFiltersConditionNode | TableRecordFiltersTraversalNode | TableRecordFiltersGroupNode;
|
|
189
191
|
export type TableRecordFiltersConditionNode = { node_type: "condition"; field_key?: string | undefined; type?: string | undefined; operator: string; value?: unknown; unit_option?: string | undefined; };
|
|
190
192
|
export type TableRecordFiltersGroupNode = { node_type: "group"; logic: "and" | "or"; children: (TableRecordFilters)[]; };
|
package/docs/data_fetching.md
CHANGED
|
@@ -25,6 +25,7 @@ untyped. It is written from the app's live bindings by `lotics app create --cust
|
|
|
25
25
|
| `useQueries(calls, opts?)` | one state per `{ alias, params?, filter?, sort?, aggregate?, total?, rows? }`, in order — a `sort` orders that call's rows before the cap cuts them | reads that are DATA — a list known only at render, one read per item, or one count per stage (`rows: false, total: true`, each state's `.total`); the deploy's alias scan reads a list as dynamic, so every alias in it is still declared. With `aggregate` a call answers the groups of its filtered rows instead of the rows ([queries](./queries.md)) |
|
|
26
26
|
| `queryAll(alias, params?, { filter, sort })` | a promise of every row | outside React, for a job that must hold the whole narrowed set in the browser (the ids an act runs on): it asks page after page until the server says the set ended, never stopping at the 10,000-row cap. The server's rows as stored — no write of this app is drawn over them |
|
|
27
27
|
| `exportQuery(alias, { params, filter, sort, tabs?, report, file, labels? })` | a promise of an `ExportedFile`: `{ filename, mime_type, url, size, created_at }` | every row the narrowed set holds, made into one file by the server — no row reaches the browser. `tabs` (2 to 5, each `{ tab, label, filter?, sort?, totals? }`; `totals` is `{ query, params, via, columns }`, the read of a totals tab beside its rows, joined on the link column `via`, refused where that read lacks `via` or a column or answers two groups for one row) are each read as the export's own rows are — same query and params, under the tab's own filter and sort (absent, the declared order) — and a template reads each one's rows under its `tab`, a name none of the report's keys nor the template's `rows` key holds. `report` is `{ title, lines, readings, dates?, period?, per?, per_row?, filename? }` — `dates` each date filter's `{ from?, to? }` by field alias, each bound a day (`yyyy-MM-dd`) or a minute of one (`yyyy-MM-ddTHH:mm`) with no zone; `period` the tabs' `{ from, to }`, bounds of the same form; `per` the value the report was picked for, in the reader's words; `per_row` the picked row's fields by field alias, each a text, number, boolean or `null`, which `filename` and a template read as `per_row.<field>`; `filename` the file's name in the template grammar over one value each — `title`, `at` (the moment the server made the file), `dates.<field>.from` or `.to`, `period.from` or `.to`, `per`, `per_row.<field>`, `lines | lookup:<n>`, with `| add_days:<n>` shifting a date by whole days — at most 200 bytes once filled (absent, the template's name, or `title`); `file` is `{ kind: "workbook", language, sheet, columns }` (the default report workbook, its head closing with the moment the server made it, each column `{ key, header, type }` an output column of the query — a `number` one on a number column, a `date` one on a date or datetime; with `tabs`, one sheet per tab named by its `label` in place of `sheet`) or `{ kind: "template", template }`, one the query declares under `templates` by name ([queries](./queries.md)). `labels`, by `select` output column, the words each option key prints as in either file — a cell no field of the query names (a union's literal column) arrives as its bare key; a cell its field's option names prints as that, a key the map lacks as itself, and a column no select of the query holds as it is. Past 20,000 rows, in any tab, it rejects, never cuts; each caller makes at most 60 exports a minute. The file has no `id` — it is a download, not a workspace file a workflow or a field takes: its `url` is signed for the caller for at most an hour, so download it with `downloadFileFromUrl(url, filename)` (`@lotics/ui/download`) when the promise resolves — a tab `openExternal` opens seconds after the click is a pop-up the browser blocks; the server deletes the file a day after it is made |
|
|
28
|
+
| `exportApp(key, narrowing)` | a promise of an `ExportedFile`, as `exportQuery`'s | the file the server makes of an Export `key` of a JSON app's tree (a custom-code app has no Export and makes its files with `exportQuery`) over the register as `narrowing` states it (`tab`, `status`, `filters`, `search`, `overdue`, `waiting`, `period`, `window`, `sort`, `group`, `folder`, `per`; a `filters` entry `null` clears the chip the register opens on): the server reads the rows, the band's readings and the names under the caller's own access. Download it as `exportQuery`'s |
|
|
28
29
|
|
|
29
30
|
Every hook answers the same `QueryState`: `rows`, `truncated`, `total`, `counts`, `loading`,
|
|
30
31
|
`isValidating`, `answeredAt`, `error`, `pending`, `refetch`, `page`, `setPage`, `pageCount`, `hasMore`,
|
|
@@ -144,7 +145,8 @@ write it as `{ node_type: "condition", type: "record_id", operator, value }`.
|
|
|
144
145
|
inline `{ status: "open" }` each render is the same key; you never need to memoize params.
|
|
145
146
|
- **The cache survives unmount/remount**, and **arrival is a refresh event**: returning to a screen
|
|
146
147
|
renders the cached rows instantly *and* re-reads them in the background, so a list reflects what
|
|
147
|
-
another screen changed while you were away.
|
|
148
|
+
another screen changed while you were away. Arrival re-reads a read that answered, not one whose
|
|
149
|
+
last ask failed. Identical concurrent reads share one request, and reads asked within a few milliseconds of each other go to the server together, each answered or refused on its own.
|
|
148
150
|
Freshness comes from four places — arrival, window focus / tab return / network reconnect
|
|
149
151
|
(`revalidateOnFocus`, default on; a read asked within the last minute is not asked again,
|
|
150
152
|
and the reads it asks again re-render together, as a push's do — so what changed while the tab
|
|
@@ -188,7 +190,8 @@ write it as `{ node_type: "condition", type: "record_id", operator, value }`.
|
|
|
188
190
|
where `answeredAt` moved and the call is the same.
|
|
189
191
|
- **`error`** is a `string | null`. A failed query surfaces immediately — there is **no automatic
|
|
190
192
|
retry** (no retry loop that masks the error). The last successful rows for the same key stay
|
|
191
|
-
rendered.
|
|
193
|
+
rendered. It stays failed until a realtime push or this app's own write names it, a focus or
|
|
194
|
+
reconnect re-read, or an explicit `refetch()`; mounting the screen again does not re-run it.
|
|
192
195
|
- **Never derive a FIGURE from `rows` without gating on `error`.** A first read that fails leaves
|
|
193
196
|
`rows` at `[]`, and `[]` is indistinguishable from a genuinely empty result — so `rows.reduce(…)`
|
|
194
197
|
returns `0`, `rows.length` returns `0`, and a screen states a confident number that no query
|
package/docs/runtime.md
CHANGED
|
@@ -68,7 +68,7 @@ Activation is a **two-step gate** — both must hold, so demo data shipping in t
|
|
|
68
68
|
bundle never leaks into normal traffic:
|
|
69
69
|
|
|
70
70
|
1. A fixture is registered via `mount({ fixture })` (`AppFixture` type:
|
|
71
|
-
`dist/mock.d.ts` — `{ queries?, workflows?, agents?, exports?, recordings? }`).
|
|
71
|
+
`dist/mock.d.ts` — `{ queries?, workflows?, agents?, exports?, exportApps?, recordings? }`).
|
|
72
72
|
2. The page URL carries `?__mock=1` (exactly `1`). Without the flag the fixture
|
|
73
73
|
is completely inert.
|
|
74
74
|
|
|
@@ -79,7 +79,7 @@ the fixture nothing, as it asks the server nothing. **Partial mocking is
|
|
|
79
79
|
supported**: an alias absent from the fixture still flows through the real
|
|
80
80
|
transport. `fixture.exports[alias]` answers `exportQuery(alias, call)` — a
|
|
81
81
|
function of the call returning an `ExportedFile`, or a promise of one to review the
|
|
82
|
-
pending state. Calling `mount` again (HMR) replaces the registration last-write-wins.
|
|
82
|
+
pending state; `fixture.exportApps[key]` answers `exportApp(key, narrowing)` the same way. Calling `mount` again (HMR) replaces the registration last-write-wins.
|
|
83
83
|
|
|
84
84
|
- **Fixture rows stand in for wire rows.** They pass through the same code path
|
|
85
85
|
as fetched rows, so shape them exactly like the query's real output — the same
|
|
@@ -258,7 +258,8 @@ semantics are documented:
|
|
|
258
258
|
| Op | Payload | Resolves to | Owning doc |
|
|
259
259
|
|---|---|---|---|
|
|
260
260
|
| `query` | `{ alias, params?, limit?, offset?, … }` | `{ rows }` | [queries](./queries.md), [data fetching](./data_fetching.md) |
|
|
261
|
-
| `export` | `{ alias, params?, filter?, sort?, report, file }` | `{ file }` | [data fetching](./data_fetching.md) |
|
|
261
|
+
| `export` | `{ alias, params?, filter?, sort?, report, file }` (`exportQuery`) or `{ export: { key, … } }` (`exportApp`) | `{ file }` | [data fetching](./data_fetching.md) |
|
|
262
|
+
| `spec` | `{}` | `HostSpec`: the app's compiled `app.json` itself (format 29: `lists`, `pages` by entity, `entities`, flat `acts` and `checks`; a dashboard's readings), never the route's wrapper | this doc |
|
|
262
263
|
| `field_options` | `{ alias }` | `{ fields }` | [members & options](./members_and_options.md) |
|
|
263
264
|
| `workflow` | `{ alias, inputs }` | `WorkflowResult` | [mutations](./mutations.md) |
|
|
264
265
|
| `upload` | `{ file: File }` (the `File` crosses the bridge by structured clone) | uploaded-file object | [files](./files.md) |
|
package/package.json
CHANGED