@lotics/app-sdk 0.87.4 → 0.87.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/hooks.js CHANGED
@@ -71,6 +71,14 @@ function swrConfig(revalidateOnFocus) {
71
71
  revalidateOnFocus,
72
72
  revalidateOnReconnect: revalidateOnFocus,
73
73
  shouldRetryOnError: false,
74
+ // A query's KEY carries its params, filter and sort, so anything derived
75
+ // from the rows on screen re-keys the moment they change. Without this SWR
76
+ // answers a new key with `undefined`, `rows` falls to `[]`, and every list
77
+ // renders empty for a frame — unmounting each row's images, which remount
78
+ // blank. That is the behaviour the `loading` contract above already
79
+ // promises against, and what the product app sets globally for the same
80
+ // reason.
81
+ keepPreviousData: true,
74
82
  };
75
83
  }
76
84
  /**
@@ -289,9 +297,7 @@ export function usePaginatedQuery(alias, params, opts) {
289
297
  offset: page * pageSize,
290
298
  sort,
291
299
  filter,
292
- }),
293
- // Keep the previous page's rows on screen while the next page loads.
294
- { keepPreviousData: true, ...swrConfig(revalidateOnFocus) });
300
+ }), swrConfig(revalidateOnFocus));
295
301
  // Not counted when the caller supplies the total, when the hook is disabled,
296
302
  // or under a fixture — see `useCountRead` for why the key drops page and sort.
297
303
  const countSwr = useCountRead(alias, params, filter, !mockRows && enabled && !callerOwnsTotal, revalidateOnFocus);
package/dist/src/row.d.ts CHANGED
@@ -86,7 +86,9 @@ export interface AppFile {
86
86
  /**
87
87
  * files field → the attached files with their presigned `url` (empty if none).
88
88
  * Skips entries the server didn't presign (no `url`) so a consumer never renders
89
- * an unservable file. Map to `@lotics/ui` `DisplayFile` for FileThumbnail/Gallery.
89
+ * an unservable file. Pass one to `toDisplayFile` from `@lotics/ui/file_thumbnail`
90
+ * for FileThumbnail/Gallery — the kit owns that conversion, so don't re-declare
91
+ * it per app.
90
92
  */
91
93
  export declare function readFiles(v: unknown): AppFile[];
92
94
  /**
package/dist/src/row.js CHANGED
@@ -113,7 +113,9 @@ export function readLinks(v) {
113
113
  /**
114
114
  * files field → the attached files with their presigned `url` (empty if none).
115
115
  * Skips entries the server didn't presign (no `url`) so a consumer never renders
116
- * an unservable file. Map to `@lotics/ui` `DisplayFile` for FileThumbnail/Gallery.
116
+ * an unservable file. Pass one to `toDisplayFile` from `@lotics/ui/file_thumbnail`
117
+ * for FileThumbnail/Gallery — the kit owns that conversion, so don't re-declare
118
+ * it per app.
117
119
  */
118
120
  export function readFiles(v) {
119
121
  if (!Array.isArray(v))
@@ -136,8 +136,11 @@ server validates system conditions by `type` and never reads `field_key` on them
136
136
  are no rows yet. It stays `false` during background revalidation of a key that already has rows,
137
137
  so consumers never blank loaded data to a spinner on refetch. A key *change* (new params, sort,
138
138
  filter, or page) is a fresh load: `loading` goes `true` again unless that key is already cached —
139
- `usePaginatedQuery` keeps the previous page's rows on screen during it, which is why skeletons
140
- gate on `loading && rows.length === 0`, never `loading` alone. **`isValidating`** is `true`
139
+ but the previous key's rows STAY on screen while it resolves, for every query, so `rows` never
140
+ empties mid-flight. That is why skeletons gate on `loading && rows.length === 0`, never `loading`
141
+ alone: a filter derived from what is on screen re-keys on every keystroke, and gating on
142
+ `loading` swaps the list for a spinner each time — which unmounts everything the rows contained,
143
+ images included. **`isValidating`** is `true`
141
144
  whenever any request is in flight — use it for a subtle refresh indicator.
142
145
  - **`error`** is a `string | null`. A failed query surfaces immediately — there is **no automatic
143
146
  retry** (no retry loop that masks the error). The last successful rows for the same key stay
package/docs/workflows.md CHANGED
@@ -489,7 +489,7 @@ differ from `<` / `>` (code-unit order) and from the database's collation.
489
489
  | `set` | `{ fld_x: value }` | write the whole value. `null` **clears** the field |
490
490
  | `set_skip_null` | `{ fld_x: value }` | same shape, but `null`/`undefined` entries are **dropped** — absent means "leave unchanged" |
491
491
  | `add_to` / `remove_from` / `replace` | `{ fld_x: [items] }` | surgical edits on multi-value fields (files, multi select, multi member, record links) |
492
- | `increment` | `{ fld_x: delta }` | move a **number** field by a signed amount. Negative decrements; there is no separate decrement op |
492
+ | `increment` | `{ fld_x: delta }` | move a **number** field by a signed amount, the SAME delta on every record named. Negative decrements; there is no separate decrement op |
493
493
  | `field_edits` | `[{ field, op, value }]` | the multi-value ops with the field named by a **string expression** — the only way to target a field chosen at run time. `increment` has no value-form: its payload is a scalar, not an item list |
494
494
 
495
495
  `add_to`, `remove_from` and `increment` are RELATIVE: the server resolves them against the record
@@ -505,6 +505,23 @@ stating its value.
505
505
  and visibly so. It is rejected on formula, rollup, lookup and autonumber fields: those come from
506
506
  their own definition, so change what they aggregate instead.
507
507
 
508
+ A **different** delta per record is per-record mode with `row_op: "increment"` — every row value is
509
+ read as a delta rather than a value to write. That is the shape a stock move needs, since each item
510
+ moves by its own quantity:
511
+
512
+ ```ts
513
+ await update_records({
514
+ table_id: "tbl_items",
515
+ record_ids: ids,
516
+ field_keys: ["fld_stock"],
517
+ rows: [[-12], [-3]],
518
+ row_op: "increment",
519
+ });
520
+ ```
521
+
522
+ It also removes the read: the deltas are what the workflow already computed, so there is nothing to
523
+ look up first.
524
+
508
525
  Inside `set` and `create_records.records`: `null` clears (persisted), `undefined` or an omitted
509
526
  key preserves. So passing a possibly-null read straight through is safe. Use
510
527
  `coalesce(x, fallback)` only when you want a real fallback, never to "strip" null.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.87.4",
3
+ "version": "0.87.6",
4
4
  "description": "Runtime SDK for Lotics custom-code apps \u2014 typed hooks, postMessage bridge, mount entry point",
5
5
  "type": "module",
6
6
  "exports": {