@lotics/app-sdk 0.124.7 → 0.124.9
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 +24 -26
- 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/ai.md +1 -1
- package/docs/data_fetching.md +5 -2
- package/docs/members_and_options.md +2 -2
- package/docs/mutations.md +6 -6
- package/docs/recipes.md +1 -5
- package/docs/runtime.md +4 -3
- package/docs/workflows.md +12 -12
- 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
|
};
|
|
@@ -7609,7 +7613,11 @@ var TOOL_INPUT_SEMANTICS = {
|
|
|
7609
7613
|
// Each entry's {field, op, value} carries a field-key position, but the
|
|
7610
7614
|
// field is a sibling-dependent runtime value, not a single `table_from`
|
|
7611
7615
|
// kind — the tool's own resolveSurgicalKeys validates it at execute time.
|
|
7612
|
-
field_edits: { kind: "dynamic" }
|
|
7616
|
+
field_edits: { kind: "dynamic" },
|
|
7617
|
+
// The lock the call leaves, the deleted records it brings back, and the approval it files for a locked one: none holds a field key.
|
|
7618
|
+
locked: { kind: "scalar" },
|
|
7619
|
+
restore: { kind: "scalar" },
|
|
7620
|
+
request_change: { kind: "scalar" }
|
|
7613
7621
|
},
|
|
7614
7622
|
create_records: {
|
|
7615
7623
|
table_id: { kind: "table_ref", rows: "change" },
|
|
@@ -7670,21 +7678,11 @@ var TOOL_INPUT_SEMANTICS = {
|
|
|
7670
7678
|
as_member: { kind: "scalar" },
|
|
7671
7679
|
removal: { kind: "table_field_array" }
|
|
7672
7680
|
},
|
|
7673
|
-
restore_records: {
|
|
7674
|
-
table_id: { kind: "table_ref", rows: "change" },
|
|
7675
|
-
record_ids: { kind: "record_refs" },
|
|
7676
|
-
filters: { kind: "filter_tree", table_from: "table_id" }
|
|
7677
|
-
},
|
|
7678
7681
|
lock_records: {
|
|
7679
7682
|
table_id: { kind: "table_ref", rows: "change" },
|
|
7680
7683
|
record_ids: { kind: "record_refs" },
|
|
7681
7684
|
filters: { kind: "filter_tree", table_from: "table_id" }
|
|
7682
7685
|
},
|
|
7683
|
-
unlock_records: {
|
|
7684
|
-
table_id: { kind: "table_ref", rows: "change" },
|
|
7685
|
-
record_ids: { kind: "record_refs" },
|
|
7686
|
-
filters: { kind: "filter_tree", table_from: "table_id" }
|
|
7687
|
-
},
|
|
7688
7686
|
request_locked_record_change: {
|
|
7689
7687
|
table_id: { kind: "table_ref", rows: "none" },
|
|
7690
7688
|
record_id: { kind: "record_ref" },
|
|
@@ -7693,18 +7691,6 @@ var TOOL_INPUT_SEMANTICS = {
|
|
|
7693
7691
|
changes: { kind: "dynamic" },
|
|
7694
7692
|
reason: { kind: "text" }
|
|
7695
7693
|
},
|
|
7696
|
-
// The record names its own table; an entry's id is the trail's.
|
|
7697
|
-
undo_record_change: {
|
|
7698
|
-
record_id: { kind: "record_ref" },
|
|
7699
|
-
entry_id: { kind: "scalar" }
|
|
7700
|
-
},
|
|
7701
|
-
// `table_id` names the table whose rows the undo changes, moves to the trash and brings back; a run's id is the
|
|
7702
|
-
// trail's.
|
|
7703
|
-
undo_record_changes: {
|
|
7704
|
-
table_id: { kind: "table_ref", rows: "remove" },
|
|
7705
|
-
record_ids: { kind: "record_refs" },
|
|
7706
|
-
workflow_execution_id: { kind: "scalar" }
|
|
7707
|
-
},
|
|
7708
7694
|
// ─── Query surfaces ────────────────────────────────────────────────────
|
|
7709
7695
|
query_records: {
|
|
7710
7696
|
table_id: { kind: "table_ref", rows: "none" },
|
|
@@ -7720,6 +7706,10 @@ var TOOL_INPUT_SEMANTICS = {
|
|
|
7720
7706
|
archived_only: { kind: "scalar" },
|
|
7721
7707
|
include_timestamps: { kind: "scalar" },
|
|
7722
7708
|
as_member: { kind: "scalar" },
|
|
7709
|
+
comments: { kind: "scalar" },
|
|
7710
|
+
record_ids: { kind: "record_refs" },
|
|
7711
|
+
// Nested {field_key, operation} shapes and a group_by key: resolved at execute time, as the aggregate tool did.
|
|
7712
|
+
aggregate: { kind: "dynamic" },
|
|
7723
7713
|
// Each entry pairs its OWN table_id with its OWN filter tree, so the table a
|
|
7724
7714
|
// filter resolves against is per-element rather than a sibling kwarg — no
|
|
7725
7715
|
// top-level kind can say that. Opaque to the save-time walker, which is the
|
|
@@ -7771,7 +7761,8 @@ var TOOL_INPUT_SEMANTICS = {
|
|
|
7771
7761
|
// Keys on the record's own table, which `table_id` names when supplied and
|
|
7772
7762
|
// the record resolves otherwise — the same position it holds on a query.
|
|
7773
7763
|
field_keys: { kind: "field_key_refs", table_from: "table_id" },
|
|
7774
|
-
as_member: { kind: "scalar" }
|
|
7764
|
+
as_member: { kind: "scalar" },
|
|
7765
|
+
comments: { kind: "scalar" }
|
|
7775
7766
|
},
|
|
7776
7767
|
// ─── Buttons (table-scoped field with action) ─────────────────────────
|
|
7777
7768
|
press_button: {
|
|
@@ -9678,7 +9669,8 @@ async function tableOf(walk, input) {
|
|
|
9678
9669
|
async function predictUpdate(walk, input) {
|
|
9679
9670
|
const table = await tableOf(walk, input);
|
|
9680
9671
|
const ids = input.record_ids === void 0 ? null : asIds(await attempt(walk, input.record_ids));
|
|
9681
|
-
|
|
9672
|
+
const serverDecided = input.restore !== void 0 || input.locked !== void 0 || input.request_change !== void 0;
|
|
9673
|
+
if (table === null || ids === null || serverDecided || input.filters !== void 0 || input.rows !== void 0 || input.field_edits !== void 0) {
|
|
9682
9674
|
note(walk, { kind: "unknown", table_id: table });
|
|
9683
9675
|
return;
|
|
9684
9676
|
}
|
|
@@ -14217,6 +14209,11 @@ async function exportQuery(alias, call) {
|
|
|
14217
14209
|
if (mocked !== null) return mocked(body);
|
|
14218
14210
|
return (await rpc("export", body)).file;
|
|
14219
14211
|
}
|
|
14212
|
+
async function exportApp(key, narrowing) {
|
|
14213
|
+
const mocked = getMockExportApp(key);
|
|
14214
|
+
if (mocked !== null) return mocked(narrowing);
|
|
14215
|
+
return (await rpc("export", { export: { key, ...narrowing } })).file;
|
|
14216
|
+
}
|
|
14220
14217
|
var NO_UNITS = {};
|
|
14221
14218
|
function useFieldOptions(alias, opts) {
|
|
14222
14219
|
const enabled = opts?.enabled ?? true;
|
|
@@ -14656,6 +14653,7 @@ export {
|
|
|
14656
14653
|
askAi,
|
|
14657
14654
|
buildChoiceOutput,
|
|
14658
14655
|
downloadFile,
|
|
14656
|
+
exportApp,
|
|
14659
14657
|
exportQuery,
|
|
14660
14658
|
hostSpec,
|
|
14661
14659
|
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/ai.md
CHANGED
|
@@ -13,7 +13,7 @@ Don't run a structured extraction through `askAi` (the result is stranded in a c
|
|
|
13
13
|
|
|
14
14
|
## Declared agents — what `useAgentRun` runs
|
|
15
15
|
|
|
16
|
-
`useAgentRun` runs an agent already bound to the app; none can be added or
|
|
16
|
+
`useAgentRun` runs an agent already bound to the app; none can be added, changed or removed any more. A new job for an agent is a declared Query or Workflow node of the app, which an outside agent runs with `run_app_query` and `run_app_workflow`. `.lotics/app_agents.d.ts`, generated from the app's live bindings by `lotics app create --custom` and `lotics app deploy`, types `useAgentRun("<alias>")` by the agent's input and output shapes.
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
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
|
|
@@ -194,10 +194,10 @@ three gates are observable as an `error` on the hook:
|
|
|
194
194
|
resolves to `{ members: [] }`.
|
|
195
195
|
|
|
196
196
|
Name the group by the `grp_…` id the workflow input's `group` declares — the binding below, set
|
|
197
|
-
with `
|
|
197
|
+
with `set_workflow`:
|
|
198
198
|
|
|
199
199
|
```jsonc
|
|
200
|
-
//
|
|
200
|
+
// the header of a set_workflow source — the declaration that unlocks useMembers
|
|
201
201
|
{
|
|
202
202
|
"alias": "assignOrder",
|
|
203
203
|
"inputs": {
|
package/docs/mutations.md
CHANGED
|
@@ -24,7 +24,7 @@ const result = await createOrder({ customer_id, quantity: 3 });
|
|
|
24
24
|
against the alias's declared input schema, and executes the workflow under the **app
|
|
25
25
|
owner's** authority (never the viewer's — see [security](./security.md) for attribution,
|
|
26
26
|
privilege gates, and public-app semantics).
|
|
27
|
-
- Binding is server-side and live: `
|
|
27
|
+
- Binding is server-side and live: `set_workflow` binds an alias as a new version of the app,
|
|
28
28
|
and a deploy never touches it. The declaration also types `useWorkflow` (below).
|
|
29
29
|
- Invoking an alias that is not bound resolves with `status: "error"` and a message naming
|
|
30
30
|
the missing binding.
|
|
@@ -170,7 +170,7 @@ envelope's grammar — `message` is required, `field_errors` never reaches the a
|
|
|
170
170
|
|
|
171
171
|
- **Typed for free — persisted at `set`.** The alias's `outputs` schema is derived at save
|
|
172
172
|
time from the inferred type of the body's `return({ data })`. When the binding declares
|
|
173
|
-
**no** `outputs`, `
|
|
173
|
+
**no** `outputs`, `set_workflow` stores the derived schema as the alias's `outputs`. An
|
|
174
174
|
**explicitly declared** `outputs` is authoritative and never overwritten;
|
|
175
175
|
declare one only to narrow beyond what's inferred. A shape the checker can't pin down degrades
|
|
176
176
|
to untyped `json`, never to a wrong schema.
|
|
@@ -214,7 +214,7 @@ if (r.status === "success" && r.data) {
|
|
|
214
214
|
An alias's declaration is `{ inputs?, outputs?, workflow_id? }` — `workflow_id` is the server's
|
|
215
215
|
half, stamped by the first bind, so an alias an author has only just written carries none.
|
|
216
216
|
`inputs` maps each input name to a typed declaration; it is authored beside the body and
|
|
217
|
-
travels with it (`
|
|
217
|
+
travels with it (`set_workflow` takes both), and the schema the body was verified against is
|
|
218
218
|
canonical. It drives three things at once: compile-time typing of the `useWorkflow` payload,
|
|
219
219
|
compile-time typing of `trigger.app_workflow.inputs.*` inside the body, and runtime payload
|
|
220
220
|
validation at the execute boundary.
|
|
@@ -283,7 +283,7 @@ When the alias declares `inputs`, the server validates the payload before the wo
|
|
|
283
283
|
added after authoring is accepted, a removed one rejected
|
|
284
284
|
(`select input "<path>" value "<v>" is not one of its current options (<label>=<value>, …)`) with no
|
|
285
285
|
redeploy. A `field` that doesn't exist or names a non-select field is rejected at bind time
|
|
286
|
-
(`
|
|
286
|
+
(`set_workflow`) with
|
|
287
287
|
`select input references field "<key>", which does not exist in this workspace` /
|
|
288
288
|
`… which is a <type> field, not a select`. Prefer `field` for any select backed by a real
|
|
289
289
|
field; keep `options` for a fixed enum the app owns. Populate pickers from `useFieldOptions`
|
|
@@ -460,7 +460,7 @@ return({ status: "success", message: "Updated." });
|
|
|
460
460
|
```
|
|
461
461
|
|
|
462
462
|
`update_records` has no bypass flag — a locked record's values change only through the
|
|
463
|
-
approval flow below (or the table's owner or an admin explicitly unlocking the record first; `
|
|
463
|
+
approval flow below (or the table's owner or an admin explicitly unlocking the record first; `update_records` with `locked: false` is
|
|
464
464
|
owner- or admin-gated — in an app's run, judged as the member who pressed, never the app's owner — so don't build an unlock→edit→relock workflow around the approval flow — the
|
|
465
465
|
lock exists to route changes through review).
|
|
466
466
|
|
|
@@ -553,7 +553,7 @@ What the steps cannot tell before they run is never guessed:
|
|
|
553
553
|
looks for a row to reuse draws nothing until the server says which it did. A `catch` block and a
|
|
554
554
|
`while` or counted `for` loop wait too.
|
|
555
555
|
- A pause (`wait`, `wait_for_event`, `wait_for_approval`): every write after it waits for the answer.
|
|
556
|
-
- A step that changes rows it cannot draw — `lock_records`, `
|
|
556
|
+
- A step that changes rows it cannot draw — `lock_records`, `update_records` with `restore` or `locked`, a pressed button, an
|
|
557
557
|
agent whose tools can change rows, a `create_records` matching on a field, a filtered update or
|
|
558
558
|
delete: every read over the tables it may touch (every read, where it names none) is `pending`
|
|
559
559
|
until the answer lands.
|
package/docs/recipes.md
CHANGED
|
@@ -121,11 +121,7 @@ Two **separate** layers; set both.
|
|
|
121
121
|
|
|
122
122
|
**Launcher chrome** — `lotics run set_app '{"app_id":"…","body":{"icon":"truck","color":"blue"}}'`.
|
|
123
123
|
`icon` is any Lucide name (the full library, validated at write time — a wrong name is rejected, not
|
|
124
|
-
silently boxed). Don't guess the kebab spelling:
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
lotics run search_app_icons '{"query":"shipping"}' → ["truck","ship","package",…]
|
|
128
|
-
```
|
|
124
|
+
silently boxed). Don't guess the kebab spelling: a wrong name is refused with the nearest Lucide names.
|
|
129
125
|
|
|
130
126
|
`color` is a named palette colour: `red orange amber yellow lime green emerald teal cyan sky
|
|
131
127
|
blue indigo violet purple fuchsia pink rose slate gray zinc neutral stone`. `null` in `body` clears
|
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/docs/workflows.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Workflows — the body-authoring reference
|
|
2
2
|
|
|
3
|
-
The grammar of a **workflow body**: what `
|
|
3
|
+
The grammar of a **workflow body**: what `set_workflow` takes as a `source` bound to an app's alias, the
|
|
4
4
|
JS subset that is accepted, and the rules that decide whether a body saves.
|
|
5
5
|
[mutations](./mutations.md) owns the *call* side; this document owns everything between the
|
|
6
6
|
braces.
|
|
@@ -17,7 +17,7 @@ engine, no `eval`, no sandbox. Four consequences shape everything below:
|
|
|
17
17
|
- **A successful save may still return `warnings[]`** — advisory lint that does not block.
|
|
18
18
|
Read them; they are the failures that only show up in production.
|
|
19
19
|
- **One canonical form per concept.** Several JS spellings are accepted and *lowered* to one
|
|
20
|
-
stored form. The stored tree renders back to source when it is read (`
|
|
20
|
+
stored form. The stored tree renders back to source when it is read (`get_workflow`), so a
|
|
21
21
|
read body shows the canonical spelling, not the sugar you typed (see *Round-tripping* below).
|
|
22
22
|
- **Opaque keys, never display names.** Fields are `fld_*`, select options are `opt_*`, tables
|
|
23
23
|
are `tbl_*`, groups are `grp_*`. A display name is rejected at save with an error pointing at
|
|
@@ -31,10 +31,10 @@ if (order.data["fld_status"] == "opt_open") { … }
|
|
|
31
31
|
|
|
32
32
|
## Where a body lives
|
|
33
33
|
|
|
34
|
-
A body is the app's, stored on the server and bound to an alias: `
|
|
35
|
-
|
|
36
|
-
`return({ data })` derive it). The server verifies the body and the write lands live, as a new
|
|
37
|
-
version of the app; `
|
|
34
|
+
A body is the app's, stored on the server and bound to an alias: `set_workflow` takes a
|
|
35
|
+
`source` whose `on({ type: "app", app_id, alias, inputs?, outputs? })` header names the alias and its `inputs`
|
|
36
|
+
(leave `outputs` off to let the body's `return({ data })` derive it), then the body. The server verifies the body and the write lands live, as a new
|
|
37
|
+
version of the app; `get_workflow` reads it back, and an earlier body is restored by writing it again. Input declaration vocabulary and validation:
|
|
38
38
|
[mutations](./mutations.md#declaring-workflow-inputs).
|
|
39
39
|
|
|
40
40
|
An app workflow body carries **no trigger declaration** — the binding supplies the trigger
|
|
@@ -250,7 +250,7 @@ only the dialog. What it may name depends on who renders the refusal:
|
|
|
250
250
|
|
|
251
251
|
An app workflow whose alias declares no inputs leaves input-shaped keys unchecked, so a body can
|
|
252
252
|
be written before its declaration. A key outside its row is refused when the body is SAVED — by
|
|
253
|
-
`
|
|
253
|
+
`set_workflow`, by an agent binding one — and by a `dry_run` call before that. `return({ field_errors })` keys are not checked.
|
|
254
254
|
|
|
255
255
|
### `wait_for_approval` — the one wait worth binding
|
|
256
256
|
|
|
@@ -460,7 +460,7 @@ A single-expression helper, **top level only**, inlined at every call site.
|
|
|
460
460
|
value: `lineTotal({ qty: 1, price: 2 })` is rejected because inlining would splice a path onto
|
|
461
461
|
an object literal. Bind it first (`const l = { … };` then `lineTotal(l)`), or pass a row.
|
|
462
462
|
|
|
463
|
-
**A declared `function` does not survive a read.** Inlining erases it: `
|
|
463
|
+
**A declared `function` does not survive a read.** Inlining erases it: `get_workflow` returns
|
|
464
464
|
the body repeated at each call site and no `function` at all. Reach for one when the *saved* logic
|
|
465
465
|
is what matters, and expect to re-extract it if you edit the source read back.
|
|
466
466
|
|
|
@@ -834,7 +834,7 @@ genuinely recursive.
|
|
|
834
834
|
|
|
835
835
|
## The check loop
|
|
836
836
|
|
|
837
|
-
`
|
|
837
|
+
`set_workflow` with `dry_run: true` gives the verdict a save would, and writes nothing. The
|
|
838
838
|
server runs every check a save runs — the subset parse, the type pass against the alias's
|
|
839
839
|
declared inputs, tool and kwarg resolution, field, option, table and group resolution, `switch`
|
|
840
840
|
case validation, the literal `formatDate` probe, waits in loops, table reach, the `field_key` rule and the published-API guard. **Green means the save will take
|
|
@@ -849,8 +849,8 @@ fix-stating messages before you see them.
|
|
|
849
849
|
|
|
850
850
|
`check` and `set` prove parse, types, name resolution, structure, and lint — **none of them
|
|
851
851
|
evaluates an expression**. A body that saves clean can still take the wrong branch, hand a tool a filter it
|
|
852
|
-
rejects, or read a path that is null on real data. The rehearsal for that is `
|
|
853
|
-
`
|
|
852
|
+
rejects, or read a path that is null on real data. The rehearsal for that is `set_workflow` with
|
|
853
|
+
`dry_run: true` and a `record_id` (the row an Act would run on) or `preview_inputs` (the values of a
|
|
854
854
|
workflow that takes inputs and no row): the draft is walked by the engine an Act's preview uses, under the
|
|
855
855
|
app's authority, with the production expression evaluator. Reads are real; writes and service sends are
|
|
856
856
|
said, not made, and a document a step renders is made as a paper no listing shows. It answers `preview`
|
|
@@ -865,7 +865,7 @@ A workflow that files a recording ([mutations](./mutations.md#recording-into-a-w
|
|
|
865
865
|
declares the platform's `recording` input in exactly this shape, beside its own inputs:
|
|
866
866
|
|
|
867
867
|
```jsonc
|
|
868
|
-
//
|
|
868
|
+
// the header of a set_workflow source: on({ type: "app", app_id, alias: "log_visit", inputs: … })
|
|
869
869
|
"inputs": {
|
|
870
870
|
"site": { "type": "record_link", "table_id": "tbl_sites" },
|
|
871
871
|
"recording": { "type": "object", "fields": {
|
package/package.json
CHANGED