@colixsystems/widget-sdk 0.141.0 → 0.142.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/README.md +25 -2
- package/dist/contract.cjs +23 -1
- package/dist/contract.js +19 -1
- package/dist/filepicker.native.js +8 -9
- package/dist/hooks.js +7 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,30 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
70
70
|
|
|
71
71
|
## Status
|
|
72
72
|
|
|
73
|
-
`v0.
|
|
73
|
+
`v0.142.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**.
|
|
74
|
+
|
|
75
|
+
### What's new in 0.142.1
|
|
76
|
+
|
|
77
|
+
**A native file upload no longer crashes the app (sc-7906).** Making the part a `File` let the upload through, and then the app died on any real file: Expo's `fetch` builds a multipart body by reading the WHOLE file into the JS heap — `convertFormDataAsync` awaits `part.bytes()` and concatenates — so a send costs about three times the file's size on the JS thread. The native host now streams the file off disk through expo-file-system's upload task instead. `<FilePicker>`, `useCamera()` and `useImageEditor()` are unchanged for a widget author — the picked file stays where the picker put it, so `asset.uri` still renders, and the name the user chose travels to the backend as an explicit `file_name` field.
|
|
78
|
+
|
|
79
|
+
### What's new in 0.142.0 (contract 1.110.0)
|
|
80
|
+
|
|
81
|
+
**An action script can finally tell WHO started the run — `triggeredBy` joins `CONTRACT.actionScriptGlobals` (sc-7885).** The identity was already captured (the run history has recorded an `actorId` since REQ-ACTION-RUNLOG) but never reached the sandbox, so a script could do the work and not attribute it: no "X submitted this" notification back to the submitter, no audit row naming the actor. Authors worked around it by writing the user id into a record first and switching to a `record_*` trigger — plumbing in the app's data model, and not available to a plain button press at all.
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
if (triggeredBy && triggeredBy.type === "app_user") {
|
|
85
|
+
await notifications.notifyUser(triggeredBy.id, {
|
|
86
|
+
title: "Request received",
|
|
87
|
+
body: "Thanks " + triggeredBy.name + ", we have started on it.",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- The shape is `{ type, id, name, email, role }`, frozen, or **`null` when nothing initiated the run** — a `schedule` tick or a `record_*` event. `null` is the normal case for the automation triggers, so guard before reading it.
|
|
93
|
+
- **`type` is load-bearing, not decoration.** It is `"app_user"` on an `app` button press and an `http_post` webhook, and `"studio_user"` on an operator's Run now. One id column, two directories behind it: check `type` before handing the id to `notifications.notifyUser` or a USER column, or a studio user's id will silently resolve to nobody. `role` carries the app-user vocabulary (`USER` / `INTEGRATION`) and is `null` for a studio user, whose workspace role is a different axis entirely. `email` is likewise `null` for a studio user: nothing in a script can address an operator, so shipping their real address to an installed third-party Action would be PII with no consumer.
|
|
94
|
+
- **It grants nothing.** The script still runs as the Action's bound integration identity, exactly as before; `triggeredBy` describes the caller and does not widen the data the script can reach. Resolution is host-side and tenant-scoped, so a stale or foreign id resolves to `null` rather than leaking a row across workspaces.
|
|
95
|
+
- Actions never render, so this has **no effect on Player ↔ export parity** (CLAUDE.md §8).
|
|
96
|
+
- Also in this release: **`properties` was added to `CONTRACT.actionScriptGlobals`**, which the `@colixsystems/action-sdk` copy of the list had carried since 1.50.0 while this one had not — the two hand-mirrored lists had drifted. A parity test now pins them together. `CONTRACT.version` → `1.110.0`. Additive for every existing manifest and script.
|
|
74
97
|
|
|
75
98
|
### What's new in 0.141.0 (contract 1.109.0)
|
|
76
99
|
|
|
@@ -507,7 +530,7 @@ When you take the `useCanWrite`-only route, omit the USER column for a guest (`i
|
|
|
507
530
|
|
|
508
531
|
**Dismissing the picker resolves `null`, not an error.** Backing out is the most common outcome, so it is deliberately not a rejection — your happy path needs no `try/catch`. A genuine failure (permission refused, no host broker) rejects with a structured `CameraError` (new named export) carrying a stable `.code` (`PERMISSION_DENIED` / `UNSUPPORTED` / `INTERNAL`).
|
|
509
532
|
|
|
510
|
-
**One upload path on both hosts.** The resolved asset is normalised to `{ uri, name, mimeType, width, height, size, file }`, where `uri` is directly displayable (`<Image source={{ uri }} />`) and `file` is already the right upload part for the platform — a
|
|
533
|
+
**One upload path on both hosts.** The resolved asset is normalised to `{ uri, name, mimeType, width, height, size, file }`, where `uri` is directly displayable (`<Image source={{ uri }} />`) and `file` is already the right upload part for the platform — a browser `File` on web, expo-file-system's `File` on native. (Never React Native's legacy `{ uri, name, type }` part: the native host streams the file off disk, and Expo's fetch rejects that part outright anyway. Do not branch on the part's type — an expo-file-system `File` is NOT `instanceof Blob` at runtime.) So the same three lines work everywhere:
|
|
511
534
|
|
|
512
535
|
```js
|
|
513
536
|
const fd = new FormData();
|
package/dist/contract.cjs
CHANGED
|
@@ -1990,12 +1990,23 @@ const ACTION_TRIGGER_TYPES = [
|
|
|
1990
1990
|
// Globals the action script runs against (the runner's surface) — distinct
|
|
1991
1991
|
// from the React/SDK widget surface, so the component import/banned-API
|
|
1992
1992
|
// linter does NOT scan action scripts.
|
|
1993
|
+
// sc-7877 deliberately does NOT add `directory` here. That global is
|
|
1994
|
+
// reachable only by a MARKETPLACE action whose install consented to a
|
|
1995
|
+
// `directory:` scope; a widget-declared action has no install, so
|
|
1996
|
+
// resolveForActionRow grants it nothing and the global is never defined.
|
|
1993
1997
|
const ACTION_SCRIPT_GLOBALS = [
|
|
1994
1998
|
"datastore",
|
|
1995
1999
|
"secrets",
|
|
2000
|
+
// Was missing here while action-sdk listed it — the two hand-mirrored lists
|
|
2001
|
+
// had drifted. action-script-globals-parity.test.cjs now pins them together.
|
|
2002
|
+
"properties",
|
|
1996
2003
|
"fetch",
|
|
1997
2004
|
"connectors",
|
|
1998
2005
|
"notifications",
|
|
2006
|
+
// Conditionally defined — only when the install consented to a `directory:`
|
|
2007
|
+
// scope. Added to the WIDGET-sdk copy here: sc-7891 put it in the action-sdk
|
|
2008
|
+
// list alone, the same drift this commit's parity test now catches.
|
|
2009
|
+
"directory",
|
|
1999
2010
|
"console",
|
|
2000
2011
|
"record",
|
|
2001
2012
|
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
@@ -2004,6 +2015,8 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
2004
2015
|
"tenantId",
|
|
2005
2016
|
"triggerType",
|
|
2006
2017
|
"triggerTableId",
|
|
2018
|
+
// sc-7885 — who initiated the run; null on `schedule` / `record_*`.
|
|
2019
|
+
"triggeredBy",
|
|
2007
2020
|
];
|
|
2008
2021
|
// Mirrors action.service.js SCRIPT_MAX_BYTES.
|
|
2009
2022
|
const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
|
|
@@ -3970,7 +3983,16 @@ const CONTRACT = deepFreeze({
|
|
|
3970
3983
|
// and the sc-7352 backfill deletes the stored key (a bottom-tabs app's
|
|
3971
3984
|
// strip becomes the sidebar's quick bar) BEFORE this ships.
|
|
3972
3985
|
// `menuItemCap` / `quickBarCap` keep their signatures.
|
|
3973
|
-
|
|
3986
|
+
// 1.110.0: additive (sc-7885) — `triggeredBy` joins `actionScriptGlobals`:
|
|
3987
|
+
// WHO initiated this run, as `{ type, id, name, email, role }`, or `null`
|
|
3988
|
+
// on `schedule` / `record_*` where nothing did. `type` is `"app_user"` (an
|
|
3989
|
+
// `app` button press or an `http_post` webhook) or `"studio_user"` (an
|
|
3990
|
+
// operator's Run now) — load-bearing, because those are two different
|
|
3991
|
+
// directories behind one id. The script still runs as the Action's bound
|
|
3992
|
+
// integration identity; this describes the caller, it does not grant
|
|
3993
|
+
// anything. The same commit adds `properties` to this list, which the
|
|
3994
|
+
// action-sdk copy had carried since 1.50.0 while this one had not.
|
|
3995
|
+
version: "1.110.0",
|
|
3974
3996
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3975
3997
|
hooks: HOOKS,
|
|
3976
3998
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1993,9 +1993,16 @@ const ACTION_TRIGGER_TYPES = [
|
|
|
1993
1993
|
const ACTION_SCRIPT_GLOBALS = [
|
|
1994
1994
|
"datastore",
|
|
1995
1995
|
"secrets",
|
|
1996
|
+
// Was missing here while action-sdk listed it — the two hand-mirrored lists
|
|
1997
|
+
// had drifted. action-script-globals-parity.test.cjs now pins them together.
|
|
1998
|
+
"properties",
|
|
1996
1999
|
"fetch",
|
|
1997
2000
|
"connectors",
|
|
1998
2001
|
"notifications",
|
|
2002
|
+
// Conditionally defined — only when the install consented to a `directory:`
|
|
2003
|
+
// scope. Added to the WIDGET-sdk copy here: sc-7891 put it in the action-sdk
|
|
2004
|
+
// list alone, the same drift this commit's parity test now catches.
|
|
2005
|
+
"directory",
|
|
1999
2006
|
"console",
|
|
2000
2007
|
"record",
|
|
2001
2008
|
// sc-5366 — `{ body }` on a run fired by the inbound `http_post` trigger,
|
|
@@ -2004,6 +2011,8 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
2004
2011
|
"tenantId",
|
|
2005
2012
|
"triggerType",
|
|
2006
2013
|
"triggerTableId",
|
|
2014
|
+
// sc-7885 — who initiated the run; null on `schedule` / `record_*`.
|
|
2015
|
+
"triggeredBy",
|
|
2007
2016
|
];
|
|
2008
2017
|
// Mirrors action.service.js SCRIPT_MAX_BYTES.
|
|
2009
2018
|
const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
|
|
@@ -3970,7 +3979,16 @@ const CONTRACT = deepFreeze({
|
|
|
3970
3979
|
// and the sc-7352 backfill deletes the stored key (a bottom-tabs app's
|
|
3971
3980
|
// strip becomes the sidebar's quick bar) BEFORE this ships.
|
|
3972
3981
|
// `menuItemCap` / `quickBarCap` keep their signatures.
|
|
3973
|
-
|
|
3982
|
+
// 1.110.0: additive (sc-7885) — `triggeredBy` joins `actionScriptGlobals`:
|
|
3983
|
+
// WHO initiated this run, as `{ type, id, name, email, role }`, or `null`
|
|
3984
|
+
// on `schedule` / `record_*` where nothing did. `type` is `"app_user"` (an
|
|
3985
|
+
// `app` button press or an `http_post` webhook) or `"studio_user"` (an
|
|
3986
|
+
// operator's Run now) — load-bearing, because those are two different
|
|
3987
|
+
// directories behind one id. The script still runs as the Action's bound
|
|
3988
|
+
// integration identity; this describes the caller, it does not grant
|
|
3989
|
+
// anything. The same commit adds `properties` to this list, which the
|
|
3990
|
+
// action-sdk copy had carried since 1.50.0 while this one had not.
|
|
3991
|
+
version: "1.110.0",
|
|
3974
3992
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3975
3993
|
hooks: HOOKS,
|
|
3976
3994
|
primitives: PRIMITIVES,
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
//
|
|
11
11
|
// `onPick` receives the same shape on both platforms:
|
|
12
12
|
// - web → the browser's `File` object (a `Blob` with name + type).
|
|
13
|
-
// - native → expo-file-system's `File
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
13
|
+
// - native → expo-file-system's `File`, carrying the same `uri` / `name` /
|
|
14
|
+
// `type` / `size` the old React Native `{ uri, name, type, size }` part
|
|
15
|
+
// did, so a widget reading those is unaffected. It must be a file on
|
|
16
|
+
// disk: the host uploads it by streaming it off disk (sc-7906), and
|
|
17
|
+
// Expo's fetch rejects the RN triple outright anyway ("Unsupported
|
|
18
18
|
// FormDataPart implementation"), which failed every native upload.
|
|
19
19
|
// `useFilestoreUpload` appends whichever verbatim into the multipart body.
|
|
20
20
|
|
|
@@ -49,10 +49,9 @@ function _mapAccept(accept) {
|
|
|
49
49
|
// keeping it as a fallback would only defer the failure.
|
|
50
50
|
function _asset(picked) {
|
|
51
51
|
const file = new FsFile(picked.uri);
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// the
|
|
55
|
-
// The picker's own values are the ones the user and the backend expect.
|
|
52
|
+
// The File derives both from the cache URI, which may be renamed,
|
|
53
|
+
// percent-encoded or extensionless. The upload sends the part's `name` as
|
|
54
|
+
// the stored name (sc-7906), so the picker's values are the ones that win.
|
|
56
55
|
_shadow(file, "name", picked.name || "upload");
|
|
57
56
|
_shadow(file, "type", picked.mimeType || "application/octet-stream");
|
|
58
57
|
return file;
|
package/dist/hooks.js
CHANGED
|
@@ -3571,14 +3571,13 @@ export function useFilestoreFile(fileId) {
|
|
|
3571
3571
|
* same way the read hooks do (tenant for PROJECT, app user for PERSONAL)
|
|
3572
3572
|
* and posts a multipart FormData to `ctx.filestore.files.upload`.
|
|
3573
3573
|
*
|
|
3574
|
-
* `upload(file, { folderId?, fileName? })` takes a
|
|
3575
|
-
*
|
|
3576
|
-
*
|
|
3577
|
-
*
|
|
3578
|
-
*
|
|
3579
|
-
* "Unsupported FormDataPart implementation"
|
|
3580
|
-
*
|
|
3581
|
-
* the Blob's own `.name`.
|
|
3574
|
+
* `upload(file, { folderId?, fileName? })` takes a browser `File` on the web
|
|
3575
|
+
* Player and expo-file-system's `File` on the Expo export, as `<FilePicker>`,
|
|
3576
|
+
* `useCamera()` and `useImageEditor()` all hand back. Never React Native's
|
|
3577
|
+
* legacy `{ uri, name, type }` part: the native host streams the file off
|
|
3578
|
+
* disk (sc-7906), and Expo's fetch rejects that part outright anyway with
|
|
3579
|
+
* "Unsupported FormDataPart implementation". `fileName` overrides the stored
|
|
3580
|
+
* name, which otherwise comes from the part's own `.name`.
|
|
3582
3581
|
*
|
|
3583
3582
|
* The destination can refuse the add two ways (sc-6977). BRANCH ON
|
|
3584
3583
|
* `err.status`: 403 means the caller may know why — most often a public space
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.142.1",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"homepage": "https://github.com/Colix-AB/AppStudio",
|
|
6
6
|
"type": "module",
|