@lotics/app-sdk 0.82.1 → 0.82.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/docs/mutations.md CHANGED
@@ -272,18 +272,22 @@ Inside the workflow body, the payload is `trigger.app_workflow.inputs.<name>`, t
272
272
  `T | null | undefined` for an optional input. The triggering member is
273
273
  `runtime.triggered_by_member_id` (`null` for an anonymous public caller).
274
274
 
275
- **Guard on `undefined`, not on nullish.** The three caller states arrive intact, and the loose
276
- `!= null` collapses two of them it skips a clear as well as an omission, which is how a Clear
277
- button ends up doing nothing:
275
+ **Guard on the KEY, not on the value.** The three caller states arrive intact an omitted input
276
+ has no key at all, a cleared one has the key with `null` so a value test cannot tell them apart:
277
+ `!= null` and `isNull(…)` both skip a clear as well as an omission, which is how a Clear button
278
+ ends up doing nothing. Ask whether the key was sent:
278
279
 
279
280
  ```js
280
281
  const i = trigger.app_workflow.inputs;
281
- if (i.title !== undefined) {
282
+ if (includes(keys(i), "title")) {
282
283
  // i.title may be null here — that is the CLEAR, and `set` writes it as one.
283
284
  await update_records({ table_id: "tbl_items", record_ids: [i.record_id], set: { fld_title: i.title } });
284
285
  }
285
286
  ```
286
287
 
288
+ `undefined` is not spellable in a workflow body — the expression subset has no such identifier, and
289
+ a body naming it fails to save with `Unknown identifier "undefined"`. `keys` is the presence test.
290
+
287
291
  When several optional inputs each map to a field, per-field guards become noise. Hand the whole
288
292
  bag to `update_records`' **`set_skip_null`** instead — same object shape as `set`, but entries
289
293
  whose value is `null`/`undefined` are dropped, so only the fields actually provided get written
package/docs/workflows.md CHANGED
@@ -504,15 +504,20 @@ record is the body's decision, and the two write surfaces answer differently:
504
504
  | `set: { fld_x: i.x }` | **clears the field** |
505
505
 
506
506
  So `set_skip_null` is the right default for a bag of optional inputs, and the wrong choice for any
507
- field the screen must be able to blank — put those in `set`, and guard on `undefined` so an input
508
- the caller never sent is not written:
507
+ field the screen must be able to blank — put those in `set`, and test whether the KEY was sent so
508
+ an input the caller never sent is not written:
509
509
 
510
510
  ```js
511
- if (i.phone !== undefined) { // NOT `!= null` — that swallows the clear
511
+ if (includes(keys(i), "phone")) { // NOT `isNull(i.phone)` — that swallows the clear
512
512
  await update_records({ table_id: "tbl_x", record_ids: [i.id], set: { fld_phone: i.phone } });
513
513
  }
514
514
  ```
515
515
 
516
+ An omitted input carries no key at all and a cleared one carries the key with `null`, so presence
517
+ is the only test that separates them. It has to be `keys` rather than a comparison against
518
+ `undefined`: the expression subset has no `undefined` identifier, and a body naming it fails to
519
+ save with `Unknown identifier "undefined"`.
520
+
516
521
  `""` is never a clear on any surface: an HTML input emits it for "the user skipped this", so the
517
522
  validator coerces it to omitted before the body runs. Reaching for `""` to blank a field fails
518
523
  silently — the write reports success and the old value stays. Send `null`.
@@ -627,9 +632,14 @@ The rules that are easy to get wrong because the failing code looks correct.
627
632
  spelling. Start that binding from the first array instead of seeding it `[]`. Appending single
628
633
  **items** (`xs.push(item)`) works on a bare `[]` seed.
629
634
  - **A mutation on the trigger table of an `after_*` workflow can re-trigger itself** — a lint
630
- warning, not an error. Gate it on `trigger.change_origin.type == "member"` (the originating
631
- write's origin). Not `runtime.change_origin`: that is the execution's own origin, always
632
- `table_workflow` here, so it discriminates nothing.
635
+ warning, not an error. Gate it on `trigger.change_origin` the originating write's origin —
636
+ and name every origin you mean to let through, rather than testing for one. `member` is a
637
+ person in the browser and **only** that: the same edit made over MCP, the CLI, or a direct API
638
+ call arrives as `api_client`, and one made by the chat agent as `chat_agent`. A gate meaning
639
+ "someone did this by hand" is
640
+ `includes(["member", "chat_agent", "api_client"], trigger.change_origin.type)`. Not
641
+ `runtime.change_origin`: that is the execution's own origin, always `table_workflow` here, so
642
+ it discriminates nothing.
633
643
 
634
644
  ## The bright line
635
645
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/app-sdk",
3
- "version": "0.82.1",
3
+ "version": "0.82.3",
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": {