@colixsystems/widget-sdk 0.81.0 → 0.82.0

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/README.md CHANGED
@@ -62,6 +62,14 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
62
62
 
63
63
  `v0.77.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
64
64
 
65
+ ### What's new in 0.82.0 (contract 1.57.0)
66
+
67
+ **Server-action scripts can notify a record's permission subjects (REQ-ACTION-NOTIFY-SUBJECTS, sc-4586).** `await notifications.notifyRecordSubjects(tableId, recordId, { title, body, link, emit_email, emit_push, exclude_user_id })` resolves to `{ recipients }` and notifies every app user whose per-record grant lets them **read** that record. This addresses the one audience the other two primitives cannot name: when membership *is* the ACL, there is no recipient column to read. The Chat widget is the case in point — a channel's participants ARE that channel record's grants, so neither a `recipient_expr` (which resolves only a fixed id or a single user/group reference column) nor a `notifyUser` loop over a column that does not exist can reach them. Subject kinds follow REQ-ACL-09: a `user` grant notifies its user, a `group` grant expands through its memberships, and the two synthetic kinds (`authenticated`, `everyone`) are skipped because they address the whole workspace rather than a membership. Recipients are **deduplicated**, so someone reachable through both a direct grant and a granted group is notified once, and `exclude_user_id` drops one — pass the author so nobody is notified of their own write. Dispatch goes through the same `notifyUser` path as before, so the always-written inbox row, the preference-gated email + push mirrors, the link sanitiser and the title/body caps are inherited unchanged, and written rows count toward the same per-run cap of 500. The tenant is bound host-side and the table/record pair is verified against it, so a foreign or missing id resolves to `{ recipients: 0 }` rather than distinguishing "absent" from "not yours". **The script never receives the member list — only the count.** `CONTRACT.version` → `1.57.0`. Additive; no widget hook, primitive, manifest field, or token changed shape.
68
+
69
+ ### 0.82.0 also carries (contract 1.56.0)
70
+
71
+ **Server-action scripts gain a `notifications` global (REQ-ACTION-NOTIFY, sc-4514).** A `scriptSource` action can now send a real notification to a recipient it resolves at run time: `await notifications.notifyUser(userId, { title, body, link, emit_email, emit_push })` and `await notifications.notifyGroup(groupId, opts)`. Options are **snake_case**, matching every other shape a script sees. `notifyUser` resolves to the created notification row (or `null` when the recipient was skipped); `notifyGroup` resolves to `{ recipients }`. This is a directness change rather than a new capability: an action could already notify indirectly by writing into a table carrying an enabled `NotificationRule`, but that costs a throwaway table, a per-table rule, a recipient expressible only as a fixed id or one reference column — and it fails silently, since with no rule attached the row just lands and the run still reports success. `POST /notifications/send` is no alternative (it needs an app-user JWT an action cannot hold). The direct call removes the intermediary and makes a non-delivery throw. Both methods delegate to the platform's one notification dispatch path, so the inbox row is always written, the email + push mirrors respect the recipient's channel preferences, and the push ping never carries the title or body. The tenant is bound host-side: a recipient in another workspace, a soft-deleted group, or a deactivated user is a silent skip, never a cross-tenant write. A blank `title`, a non-string `body`, or exceeding the per-run cap of 500 written notifications throws a catchable Error. New entry in `CONTRACT.actionScriptGlobals`; `CONTRACT.version` → `1.56.0`. Additive — no widget hook, primitive, manifest field, or token changed shape.
72
+
65
73
  ### What's new in 0.78.0
66
74
 
67
75
  **New `useIdentification()` hook — identify a visitor who is NOT signed in (REQ-IDENT, sc-4313).** A new IDENTIFICATION hook reading a newly-injected `ctx.identification` slice (the new `@colixsystems/identification-client`, constructed by both the web Player and the native Expo export). Returns `{ available, availabilityLoading, status, qr, autoStartToken, message, identity, identificationId, loading, error, start, refresh, cancel, reset }`.
package/dist/contract.cjs CHANGED
@@ -1269,6 +1269,7 @@ const ACTION_SCRIPT_GLOBALS = [
1269
1269
  "secrets",
1270
1270
  "fetch",
1271
1271
  "connectors",
1272
+ "notifications",
1272
1273
  "console",
1273
1274
  "record",
1274
1275
  "tenantId",
@@ -2506,7 +2507,24 @@ const CONTRACT = deepFreeze({
2506
2507
  // already measured its own box with onLayout; this promotes that one
2507
2508
  // pattern to the SDK so custom and marketplace widgets get it too,
2508
2509
  // instead of each rolling its own.
2509
- version: "1.55.0",
2510
+ // 1.55.0: additive (sc-4505) — `useWidgetInput(inputName)` + the
2511
+ // `PAYLOAD_VALUE_TYPES` a widget event payload field may declare, so
2512
+ // widgets on one page relate through declared inputs.
2513
+ // 1.56.0: additive (sc-4514) — `notifications` joins
2514
+ // `actionScriptGlobals`. `notifications.notifyUser(id, opts)` /
2515
+ // `notifyGroup(id, opts)` delegate to the one notification dispatch path.
2516
+ // Directness, not a new capability: an action could already notify by
2517
+ // writing into a table carrying a NotificationRule, but that needs a
2518
+ // throwaway table plus a per-table rule and fails SILENTLY when no rule
2519
+ // is attached — the row lands and the run still reports success.
2520
+ // 1.57.0: additive (sc-4586) — `notifications.notifyRecordSubjects(tableId,
2521
+ // recordId, opts)`, resolving to { recipients }. Notifies whoever a
2522
+ // record's per-record grants let read it, which is the only way to address
2523
+ // membership-by-ACL: a Chat channel's participants ARE its grants, so no
2524
+ // `recipient_expr` shape and no recipient column can name them. The member
2525
+ // list stays host-side — a script receives a count, never the ids — and
2526
+ // `exclude_user_id` keeps an author off their own message.
2527
+ version: "1.57.0",
2510
2528
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
2511
2529
  hooks: HOOKS,
2512
2530
  primitives: PRIMITIVES,
package/dist/contract.js CHANGED
@@ -1269,6 +1269,7 @@ const ACTION_SCRIPT_GLOBALS = [
1269
1269
  "secrets",
1270
1270
  "fetch",
1271
1271
  "connectors",
1272
+ "notifications",
1272
1273
  "console",
1273
1274
  "record",
1274
1275
  "tenantId",
@@ -2506,7 +2507,24 @@ const CONTRACT = deepFreeze({
2506
2507
  // already measured its own box with onLayout; this promotes that one
2507
2508
  // pattern to the SDK so custom and marketplace widgets get it too,
2508
2509
  // instead of each rolling its own.
2509
- version: "1.55.0",
2510
+ // 1.55.0: additive (sc-4505) — `useWidgetInput(inputName)` + the
2511
+ // `PAYLOAD_VALUE_TYPES` a widget event payload field may declare, so
2512
+ // widgets on one page relate through declared inputs.
2513
+ // 1.56.0: additive (sc-4514) — `notifications` joins
2514
+ // `actionScriptGlobals`. `notifications.notifyUser(id, opts)` /
2515
+ // `notifyGroup(id, opts)` delegate to the one notification dispatch path.
2516
+ // Directness, not a new capability: an action could already notify by
2517
+ // writing into a table carrying a NotificationRule, but that needs a
2518
+ // throwaway table plus a per-table rule and fails SILENTLY when no rule
2519
+ // is attached — the row lands and the run still reports success.
2520
+ // 1.57.0: additive (sc-4586) — `notifications.notifyRecordSubjects(tableId,
2521
+ // recordId, opts)`, resolving to { recipients }. Notifies whoever a
2522
+ // record's per-record grants let read it, which is the only way to address
2523
+ // membership-by-ACL: a Chat channel's participants ARE its grants, so no
2524
+ // `recipient_expr` shape and no recipient column can name them. The member
2525
+ // list stays host-side — a script receives a count, never the ids — and
2526
+ // `exclude_user_id` keeps an author off their own message.
2527
+ version: "1.57.0",
2510
2528
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
2511
2529
  hooks: HOOKS,
2512
2530
  primitives: PRIMITIVES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",