@lotics/app-sdk 0.123.1 → 0.123.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-WM76DHGT.js → chunk-COWN73CO.js} +103 -8
- package/dist/index.js +8628 -27989
- package/dist/router.js +1 -1
- package/dist/rpc.d.ts +4 -1
- package/dist/shared_types.d.ts +2 -1
- package/dist/written.d.ts +1 -1
- package/docs/queries.md +5 -5
- package/docs/runtime.md +9 -0
- package/package.json +1 -1
package/dist/router.js
CHANGED
package/dist/rpc.d.ts
CHANGED
|
@@ -6,7 +6,10 @@ import { type UrlParams, type UrlParamsPatch } from "./url_params.js";
|
|
|
6
6
|
* postMessage and the host, holding the member's session, calls the API.
|
|
7
7
|
* Standalone: the public `/v1/apps/{id}/*` endpoints are called directly, with
|
|
8
8
|
* no credentials. The bridged wire must match `app_iframe_host.tsx`:
|
|
9
|
-
* app → host: { id, op, payload }, the first also carrying `load` (`AppLoadTiming`) when
|
|
9
|
+
* app → host: { id, op, payload }, the first also carrying `reportsDrawn: true` and `load` (`AppLoadTiming`) when
|
|
10
|
+
* the browser states any
|
|
11
|
+
* app → host: { type: "drawn" }, once, after the first answer when no request has been in flight for two frames:
|
|
12
|
+
* the app's first screen has its data
|
|
10
13
|
* host → app: { id, type: "result", data } | { id, type: "error", message }
|
|
11
14
|
*/
|
|
12
15
|
export type RpcOp = "query" | "export" | "field_options" | "workflow" | "agentRuns" | "agentRun.get" | "agentRun.cancel" | "upload" | "file.rename" | "members" | "context" | "write_model" | "prediction_miss" | "openExternal" | "openApp" | "peekApp" | "askAi" | "urlState.get" | "urlState.set" | "folder.get" | "folder.set" | "comments.list" | "comments.create" | "comments.update" | "comments.delete" | "comments.counts" | "activity.list" | "activity.undo" | "recording.start" | "recording.stop";
|
package/dist/shared_types.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type QueryAggregate = {
|
|
|
29
29
|
day?: string;
|
|
30
30
|
sum?: string;
|
|
31
31
|
};
|
|
32
|
+
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" | "unique" | "date_range" | "count" | "empty" | "filled" | "percent_empty" | "percent_filled" | "percent_unique" | "sum" | "avg" | "median" | "max" | "range" | "earliest" | "latest" | "selected" | "checked" | "unchecked" | "percent_checked" | "percent_unchecked" | "string_agg"; field_key?: string | undefined; }[]; filter?: TableRecordFilters | undefined; } | undefined; };
|
|
32
33
|
export type ReportColumn = {
|
|
33
34
|
/** Each row's cell by it. */
|
|
34
35
|
key: string;
|
|
@@ -76,7 +77,7 @@ export type ReportReadingHead = {
|
|
|
76
77
|
/** The read behind it was cut: its figures stand, every total over them is left out (`null`). */
|
|
77
78
|
truncated: boolean;
|
|
78
79
|
};
|
|
79
|
-
export type TableField = { key: string; name: string; description: string; type: "number"; format: "number" | "currency" | "percentage"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "text"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "date"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "boolean"; confirm_before_update?: boolean | undefined; required?: boolean | undefined; default_value?: boolean | null | undefined; default_from?: { link_field_key: string; field_key: string; 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"; mark?: { kind: string; name: 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_member"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_record_link"; table_id: string; 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; 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: "
|
|
80
|
+
export type TableField = { key: string; name: string; description: string; type: "number"; format: "number" | "currency" | "percentage"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "text"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "date"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "boolean"; confirm_before_update?: boolean | undefined; required?: boolean | undefined; default_value?: boolean | null | undefined; default_from?: { link_field_key: string; field_key: string; 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"; mark?: { kind: string; name: 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_member"; 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; options?: Record<string, string> | undefined; } | null | undefined; } | { key: string; name: string; description: string; type: "select_record_link"; table_id: string; 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; 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; }; confirm_before_update?: boolean | undefined; required?: boolean | undefined; filter?: TableRecordFiltersGroupNode | undefined; aggregate_field_type?: "number" | "boolean" | "date" | "files" | "text" | "select" | "select_member" | "select_record_link" | "rollup" | "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; confirm_before_update?: boolean | undefined; required?: boolean | undefined; lookup_field_type?: "number" | "boolean" | "date" | "files" | "text" | "select" | "select_member" | "select_record_link" | "rollup" | "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"; 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"; mark?: { kind: string; name: string; } | undefined; }[] | undefined; output_type?: "number" | "boolean" | "date" | "text" | "datetime" | "select" | undefined; volatile?: boolean | undefined; }; confirm_before_update?: boolean | undefined; required?: boolean | undefined; } | { key: string; name: string; description: string; type: "button"; text: string; confirm_before_update?: boolean | undefined; required?: boolean | undefined; workflow_id?: string | null | undefined; } | { key: string; name: string; description: string; type: "autonumber"; confirm_before_update?: boolean | undefined; required?: boolean | undefined; prefix?: string | undefined; padding?: number | undefined; template?: string | undefined; };
|
|
80
81
|
export type TableRecordFilters = TableRecordFiltersConditionNode | TableRecordFiltersTraversalNode | TableRecordFiltersGroupNode;
|
|
81
82
|
export type TableRecordFiltersConditionNode = { node_type: "condition"; field_key?: string | undefined; type?: string | undefined; operator: string; value?: unknown; unit_option?: string | undefined; };
|
|
82
83
|
export type TableRecordFiltersGroupNode = { node_type: "group"; logic: "and" | "or"; children: (TableRecordFilters)[]; };
|
package/dist/written.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BodyTable } from "./shared_types.js";
|
|
2
2
|
import type { TableField } from "./shared_types.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { QueryWriteShape } from "./shared_types.js";
|
|
4
4
|
export type Row = Record<string, unknown>;
|
|
5
5
|
/** A predicted write in the shape rows store it: an option as `["opt_…"]`, a link as `[{ id, display }]`. */
|
|
6
6
|
export type Staged = {
|
package/docs/queries.md
CHANGED
|
@@ -398,14 +398,14 @@ as one. `record_id` is the sole exception, and declaring anything but `text` on
|
|
|
398
398
|
|
|
399
399
|
`{ link: { source, field } }` requires a direct `from_table` parent, and `source` must be a
|
|
400
400
|
`select_record_link` field on it. `field` names a field on the **link target table** —
|
|
401
|
-
resolved **by display name first, then by key
|
|
401
|
+
resolved **by display name first, then by key** (exactly: `SIZE` is not `Size`). A save refuses a
|
|
402
|
+
`field` the target holds under neither, naming its fields. The target row is pinned to the declared
|
|
402
403
|
table, the workspace, live (non-archived, non-draft) rows, and the target table's row scope —
|
|
403
404
|
a scoped-out or stale link extracts as NULL.
|
|
404
405
|
|
|
405
406
|
**Warning:** only the **first** link in the cell is followed. Multi-link analytics go through
|
|
406
|
-
`unnest` + join (§8).
|
|
407
|
-
|
|
408
|
-
target's names are volatile.
|
|
407
|
+
`unnest` + join (§8). Renaming the field a `field` names by its name rewrites that `field` to
|
|
408
|
+
the field's key; a field renamed to a name another read uses re-points that read — prefer keys.
|
|
409
409
|
|
|
410
410
|
**Cost.** Every `link` column over one `source` shares one read of the linked record per row, and
|
|
411
411
|
every `link_agg` column over one `source` shares one aggregate, so ten columns through `customer` cost
|
|
@@ -487,7 +487,7 @@ derived surfaces share one implementation.
|
|
|
487
487
|
| boolean | `boolean` | `equals` (value `true`/`false`; `false` matches NULL/missing) | same | `checked`/`unchecked`/`percent_*` aggregate (`unchecked` counts false **or** empty). |
|
|
488
488
|
| select (single & multi) | `select` | `has_any_of`, `has_none_of`, `has_all_of`, `is_empty`, `is_not_empty` — values are **option keys** (`opt_*`), never labels | the **runtime** `filter` (§9) also takes option **names**, resolving each to its key — an unknown one is refused rather than matching nothing, and the value stays an array either way; a select column the query DERIVES instead of projecting from a field or a select literal carries no option set, so addressing one there is refused | Cells are arrays even for single-selects. Sorting a select column orders by its options' order (a multi-select by its first option), an empty cell last, on every page, keyset included; through a `union` whose arms project different selects, each row by its own select's order (§2 `union`); a select the query derives carries no options and orders by raw JSON. `unnest` fans option keys. |
|
|
489
489
|
| select_member | `select_member` | select ops + `is_current_member`, `is_not_current_member` (field-scoped, no value; `is_not_current_member` matches every cell not holding the requester, an empty one included) | same — `is_current_member` **works at the runtime layer** too | Sorting orders by the first member's name as a reader is shown it (the one the organization gave, else their profile's; a member it does not name reads as empty text), folded like text, then by member id; an empty cell last, on every page, keyset included. `unnest` fans member ids. Anonymous public request: current-member binds the app owner (§1). |
|
|
490
|
-
| select_record_link | `select_record_link` | membership by linked-record **id**: `has_any_of`, `has_none_of`, `has_all_of`; text over the cached **display
|
|
490
|
+
| select_record_link | `select_record_link` | membership by linked-record **id**: `has_any_of`, `has_none_of`, `has_all_of`; text over the cached **display** (deprecated — filter by id, and a linked record's field through a lookup): `contains`, `not_contains`, `starts_with`, `ends_with`; `is_empty`, `is_not_empty` | same — id-membership works at the runtime layer (converged `[{id}]` containment) | Sorting orders by the first linked record's display, folded like text, then by its id; an empty cell last, on every page, keyset included. `unnest` fans link ids (+ display). |
|
|
491
491
|
| files | `files` | `has_filename`, `has_mime_type` (substring), `has_file_count` (exact count), `is_empty`, `is_not_empty` | same | Presign-enriched at delivery (§1); `unnest` fans file ids. Only presence-counting aggregates. |
|
|
492
492
|
| formula | its declared output type (`number`/`text`/`boolean`/`date`/`datetime`, or `select` for one stating its own options, its cell a single select's; `json` until inferred) | filtered by the output type's operators; a cell holding an error (`#ERROR: …`) holds no value — it projects as `null` whatever the output type, and matches what an empty cell matches (`is_empty`, a negation such as `not_equals`) and nothing that needs a value | same | Extracted as a real scalar, so numeric formulas feed `sum`/`avg`/sorts. A **datetime-output** formula matches day-level date filters (full-day expansion). |
|
|
493
493
|
| rollup | decided by the OPERATION, never by the aggregated field: `earliest`/`latest` → `date`/`datetime` per that field's format; **every** other op → `number` | number or date operators per output type; text operators route as text | same | Datetime-format rollups match day-level date filters. `date_range` counts DAYS between the extremes, so it is a number despite its date input. A presence count (`filled`/`empty`/`percent_*`) is a number whatever it counts — including over `files`. |
|
package/docs/runtime.md
CHANGED
|
@@ -156,6 +156,15 @@ outgoing message is targeted at that origin — a third frame can neither inject
|
|
|
156
156
|
results nor observe payloads. A host error with no message rejects as
|
|
157
157
|
`"RPC failed"`.
|
|
158
158
|
|
|
159
|
+
The host covers the app with its loading skeleton until the app's first screen
|
|
160
|
+
has its data. The SDK says so for you: its first request carries
|
|
161
|
+
`reportsDrawn: true`, and once every request is answered and none has been sent
|
|
162
|
+
for two animation frames it posts `{ type: "drawn" }` to the host, once per page.
|
|
163
|
+
A screen that issues its reads in its first render or effects is covered until
|
|
164
|
+
they land; one that starts a read later than two frames after the previous
|
|
165
|
+
answers is shown before it, and the host lifts the cover after a bounded time
|
|
166
|
+
following the first request.
|
|
167
|
+
|
|
159
168
|
**Warning:** a bridged call has **no timeout**. An op the host doesn't
|
|
160
169
|
implement does reject (`"Unknown RPC op: …"`), but a message the host drops
|
|
161
170
|
without replying (a crashed handler, a malformed envelope) leaves the returned
|
package/package.json
CHANGED