@colixsystems/widget-sdk 0.142.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 CHANGED
@@ -72,6 +72,10 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
72
72
 
73
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
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
+
75
79
  ### What's new in 0.142.0 (contract 1.110.0)
76
80
 
77
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.
@@ -526,7 +530,7 @@ When you take the `useCanWrite`-only route, omit the USER column for a guest (`i
526
530
 
527
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`).
528
532
 
529
- **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 Blob on both hosts: a browser `File` on web, expo-file-system's `File` on native. (It must be a Blob: Expo's fetch implements the web FormData spec and rejects React Native's legacy `{ uri, name, type }` part.) So the same three lines work everywhere:
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:
530
534
 
531
535
  ```js
532
536
  const fd = new FormData();
@@ -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` — ALSO a `Blob`, and carrying the
14
- // same `uri` / `name` / `type` / `size` the old React Native
15
- // `{ uri, name, type, size }` part did, so a widget reading those is
16
- // unaffected. It must be a Blob: Expo's fetch implements the WEB
17
- // FormData spec and rejects the RN triple outright ("Unsupported
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
- // Both are read off the PART when the multipart is serialised (the third
53
- // `append` argument is ignored for a non-Blob), and both are derived from
54
- // the cache URI — which may be renamed, percent-encoded, or extensionless.
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 BLOB on both hosts — a
3575
- * browser `File` on the web Player, expo-file-system's `File` (also a Blob)
3576
- * on the Expo export, as `<FilePicker>`, `useCamera()` and `useImageEditor()`
3577
- * all hand back. It must be a Blob: Expo's fetch implements the web FormData
3578
- * spec and rejects React Native's legacy `{ uri, name, type }` part with
3579
- * "Unsupported FormDataPart implementation", which failed every native
3580
- * upload. `fileName` overrides the stored name, which otherwise comes from
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.142.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",