@lotics/app-sdk 0.105.0 → 0.106.1

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/index.d.ts CHANGED
@@ -6,7 +6,7 @@ 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
8
  export { useQuery, useQueries, queryAll, exportQuery, useFieldOptions } from "./queries.js";
9
- export type { QueryRow, QueryCall, QueryState, RowOf, QueryOptions, ColumnKeyOf, QuerySortKey, QueryFilter, QueryFilterCondition, QueryFilterFieldCondition, QueryFilterRecordIdCondition, QueryFilterGroup, FieldOptions, FieldOptionsState, FieldOptionsOptions, FigureUnits, ExportCall, ExportFile, ExportReport, } from "./queries.js";
9
+ export type { QueryRow, QueryCall, QueryState, RowOf, QueryOptions, ColumnKeyOf, QuerySortKey, QueryFilter, QueryFilterCondition, QueryFilterFieldCondition, QueryFilterRecordIdCondition, QueryFilterGroup, FieldOptions, FieldOptionsState, FieldOptionsOptions, FigureUnits, ExportCall, ExportFile, ExportReport, ExportTab, } from "./queries.js";
10
10
  export type { AttachedFile, AttachmentPiles, AttachmentPilesOptions, AttachmentsOptions, AttachmentsState } from "./attachments.js";
11
11
  export { useComments, useCommentCounts } from "./comments.js";
12
12
  export type { AppComment, AppCommentAuthor, AppCommentFile, CommentsState, UseCommentsArgs, CommentCountsState, UseCommentCountsArgs, } from "./comments.js";
package/dist/index.js CHANGED
@@ -23712,8 +23712,8 @@ var templatePartSchema = zod_default.lazy(
23712
23712
  var MODEL_TIERS = ["haiku", "sonnet", "opus"];
23713
23713
  var TIER_MODELS = {
23714
23714
  haiku: "claude-haiku-4-5",
23715
- sonnet: "claude-sonnet-5",
23716
- opus: "claude-opus-5"
23715
+ sonnet: "claude-sonnet-5-5",
23716
+ opus: "claude-opus-5-5"
23717
23717
  };
23718
23718
  var RESOLVED_MODEL_IDS = MODEL_TIERS.map(
23719
23719
  (tier) => TIER_MODELS[tier]
@@ -24214,13 +24214,20 @@ var appWorkflowContractSchema = zod_default.object({
24214
24214
  inputs: zod_default.record(zod_default.string(), appWorkflowInputSchema).optional(),
24215
24215
  outputs: zod_default.record(zod_default.string(), appWorkflowOutputSchema).optional()
24216
24216
  });
24217
- var APP_EXPORT_REPORT_KEYS = ["title", "lines", "readings", "at", "dates"];
24217
+ var APP_EXPORT_REPORT_KEYS = ["title", "lines", "readings", "at", "dates", "period", "per"];
24218
24218
  var appExportTemplateSchema = zod_default.object({
24219
24219
  template_id: zod_default.string().min(1).describe("The document template (`dtl_\u2026`) filled: an Excel workbook, or an HTML page made a PDF."),
24220
24220
  rows: zod_default.string().regex(APP_ALIAS_REGEX).refine((key) => !APP_EXPORT_REPORT_KEYS.some((taken) => taken === key), {
24221
24221
  message: `The rows are listed under a key of their own, never ${APP_EXPORT_REPORT_KEYS.join(", ")}.`
24222
- }).describe(`The key the template lists the rows under, beside ${APP_EXPORT_REPORT_KEYS.join(", ")}.`)
24223
- }).strict();
24222
+ }).describe(`The key the template lists the rows under, beside ${APP_EXPORT_REPORT_KEYS.join(", ")}.`),
24223
+ tabs: zod_default.array(
24224
+ zod_default.string().regex(APP_ALIAS_REGEX).refine((key) => !APP_EXPORT_REPORT_KEYS.some((taken) => taken === key), {
24225
+ message: `A tab's rows are listed under a key of their own, never ${APP_EXPORT_REPORT_KEYS.join(", ")}.`
24226
+ })
24227
+ ).min(1).optional().describe("The keys each of a register's tabs lists its rows under, beside the rows; every export filling the template sends exactly these.")
24228
+ }).strict().refine((declared) => declared.tabs === void 0 || new Set(declared.tabs).size === declared.tabs.length && !declared.tabs.includes(declared.rows), {
24229
+ message: "Each tab is listed under a key of its own, apart from the rows."
24230
+ });
24224
24231
  var appQueryDeclarationSchema = zod_default.object({
24225
24232
  ast: zod_default.unknown().describe(
24226
24233
  "Query AST template (a QueryNode). Validated server-side via parseQueryNode at deploy. May embed {{params.<name>}} tokens in filter value positions."
@@ -24529,6 +24536,8 @@ var TOOL_INPUT_SEMANTICS = {
24529
24536
  // Also `{fld_*: value}` — a signed number rather than an item array, but the
24530
24537
  // key half is what this classification governs, so it validates the same way.
24531
24538
  increment: { kind: "record_data", table_from: "table_id" },
24539
+ // `{fld_*: [document template id, ...]}` — the key half is a field of the table, as the ops above.
24540
+ remove_made: { kind: "record_data", table_from: "table_id" },
24532
24541
  // Per-record mode — field_keys names the columns; rows holds values
24533
24542
  // positionally. Each cell's semantic depends on field_keys[i], so the
24534
24543
  // tool resolves option names at execute time. Annotated `dynamic` so the
@@ -24655,6 +24664,12 @@ var TOOL_INPUT_SEMANTICS = {
24655
24664
  aggregations: { kind: "dynamic" },
24656
24665
  group_by: { kind: "field_key_ref", table_from: "table_id" }
24657
24666
  },
24667
+ evaluate_formula: {
24668
+ table_id: { kind: "table_ref", rows: "none" },
24669
+ record_id: { kind: "record_ref" },
24670
+ // `{fld_…}` tokens of `table_id`, resolved against its fields at execute time.
24671
+ expression: { kind: "dynamic" }
24672
+ },
24658
24673
  get_record: {
24659
24674
  // Schema only takes `record_id`; table_id is optional/derived.
24660
24675
  record_id: { kind: "record_ref" },
@@ -27042,6 +27057,10 @@ function adjusted(current, items, op) {
27042
27057
  const gone = new Set(given.map(keyOf));
27043
27058
  return held2.filter((item) => !gone.has(keyOf(item)));
27044
27059
  }
27060
+ function withoutMade(current, templates) {
27061
+ const gone = new Set(asList(templates));
27062
+ return asList(current).filter((item) => !(item !== null && typeof item === "object" && "document_template_id" in item && gone.has(item.document_template_id)));
27063
+ }
27045
27064
  function note(walk, effect) {
27046
27065
  if (walk.unsure && effect.kind !== "unknown") {
27047
27066
  walk.effects.push({ kind: "unknown", table_id: effect.table_id });
@@ -27074,6 +27093,7 @@ async function predictUpdate(walk, input) {
27074
27093
  const removed = await fieldValues(walk, input.remove_from);
27075
27094
  const increments = await fieldValues(walk, input.increment);
27076
27095
  const replaced = await fieldValues(walk, input.replace);
27096
+ const unmade = await fieldValues(walk, input.remove_made);
27077
27097
  for (const id of ids) {
27078
27098
  const before = recordOf(walk, id);
27079
27099
  const cells = {};
@@ -27100,6 +27120,7 @@ async function predictUpdate(walk, input) {
27100
27120
  };
27101
27121
  relative(added, (current, given) => adjusted(current, given, "add_to"));
27102
27122
  relative(removed, (current, given) => adjusted(current, given, "remove_from"));
27123
+ relative(unmade, (current, templates) => withoutMade(current, templates));
27103
27124
  relative(increments, (current, given) => typeof given === "number" ? (typeof current === "number" ? current : 0) + given : current);
27104
27125
  if (unknown2.includes("*")) {
27105
27126
  note(walk, { kind: "unknown", table_id: table });
@@ -30975,7 +30996,8 @@ async function queryAll(alias, params = {}, opts = {}) {
30975
30996
  }
30976
30997
  async function exportQuery(alias, call) {
30977
30998
  const sort = call.sort === void 0 || call.sort.length === 0 ? void 0 : [...call.sort];
30978
- const body = { alias, params: call.params ?? {}, filter: call.filter, sort, report: call.report, file: call.file };
30999
+ const tabs = call.tabs?.map((tab) => ({ ...tab, sort: tab.sort === void 0 || tab.sort.length === 0 ? void 0 : [...tab.sort] }));
31000
+ const body = { alias, params: call.params ?? {}, filter: call.filter, sort, ...tabs === void 0 ? {} : { tabs }, report: call.report, file: call.file };
30979
31001
  const mocked = getMockExport(alias);
30980
31002
  if (mocked !== null) return mocked(body);
30981
31003
  return (await rpc("export", body)).file;
package/dist/queries.d.ts CHANGED
@@ -191,6 +191,20 @@ export interface ExportReport {
191
191
  }>;
192
192
  /** The file's name, in the template grammar over the report; absent, the template's name, or `title`. */
193
193
  filename?: string;
194
+ /** The period a register's tabs read their rows over: its bounds, a day or a minute of one, with no zone. */
195
+ period?: {
196
+ from: string;
197
+ to: string;
198
+ };
199
+ /** The value the report was picked for (its `per`), as the reader names it. */
200
+ per?: string;
201
+ }
202
+ /** One tab of the register: its rows read as the export's own are — same query and params — under its own filter and order. */
203
+ export interface ExportTab {
204
+ tab: string;
205
+ label: string;
206
+ filter?: QueryFilter;
207
+ sort?: readonly QuerySortKey[];
194
208
  }
195
209
  /** The file an export makes: the default report workbook — its words in `language`, the rows under `columns` on the
196
210
  * tab `sheet` — or a template the query declares under `templates`, by its name there. */
@@ -207,6 +221,9 @@ export interface ExportCall {
207
221
  params?: Record<string, unknown>;
208
222
  filter?: QueryFilter;
209
223
  sort?: readonly QuerySortKey[];
224
+ /** A register's tabs, each one's rows beside the call's own (the open tab's): a template reads them under each
225
+ * tab's name, the workbook holds one sheet per tab. */
226
+ tabs?: readonly ExportTab[];
210
227
  report: ExportReport;
211
228
  file: ExportFile;
212
229
  }
@@ -24,7 +24,7 @@ untyped. It is written from the app's live bindings by `lotics app create --cust
24
24
  | `useQuery(alias, params?, { rows: false, total: true })` | `total` only, no rows (`total: { by: column }` adds `counts`) | a facet chip, a queue badge, an "N awaiting approval" tile — the size of a set you are not listing |
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
- | `exportQuery(alias, { params, filter, sort, report, file })` | a promise of the file | every row the narrowed set holds, made into one file by the server — no row reaches the browser. `report` is `{ title, lines, readings, dates?, 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; `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`, `lines | lookup:<n>` — 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) or `{ kind: "template", template }`, one the query declares under `templates` by name ([queries](./queries.md)). Past 20,000 rows it rejects, never cuts; each caller makes at most 60 exports a minute. The file's `url` is signed for the caller: open it with `openExternal` |
27
+ | `exportQuery(alias, { params, filter, sort, tabs?, report, file })` | a promise of the file | 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? }`) 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?, 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; `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`, `lines | lookup:<n>` — 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)). Past 20,000 rows, in any tab, it rejects, never cuts; each caller makes at most 60 exports a minute. The file's `url` is signed for the caller: open it with `openExternal` |
28
28
 
29
29
  Every hook answers the same `QueryState`: `rows`, `truncated`, `total`, `counts`, `loading`,
30
30
  `isValidating`, `error`, `pending`, `refetch`, `page`, `setPage`, `pageCount`, `hasMore`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.105.0",
3
+ "version": "0.106.1",
4
4
  "description": "The SDK a Lotics custom-code app reads and writes through \u2014 typed hooks over the host bridge, cell readers, mount() and AppRouter",
5
5
  "type": "module",
6
6
  "exports": {