@colixsystems/widget-sdk 0.99.0 → 0.100.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 +15 -1
- package/dist/contract.cjs +16 -6
- package/dist/contract.js +16 -6
- package/dist/hooks.js +13 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,20 @@ Host-integration surface only: no author-facing hook, prop, primitive, or manife
|
|
|
99
99
|
|
|
100
100
|
Host-integration surface only: no author-facing hook, prop, primitive, or manifest field changed. `CONTRACT.version` → `1.66.0`.
|
|
101
101
|
|
|
102
|
+
### What's new in 0.100.0 (contract 1.74.0)
|
|
103
|
+
|
|
104
|
+
**Opt out of image compression on upload — `useFilestoreUpload({ compress })` (sc-5402).** A file uploaded through `POST /api/v1/filestore/files` now has its raster images compressed to WebP again (EXIF-stripped, longest edge capped at 4096 px), which is the right default for anything the app renders. When the ORIGINAL bytes matter — a document archive, a photo the user re-downloads, anything with an exact-bytes requirement — pass `compress: false`, either on the hook or per call:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
const { upload } = useFilestoreUpload({ spaceType: 'personal', compress: false });
|
|
108
|
+
// …or per upload, which wins over the hook default:
|
|
109
|
+
await upload(file, { compress: false });
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Only raster images are ever compressed. SVG keeps its vector, and video, audio, and documents are stored verbatim under both values — an upload is never queued for background transcoding. The opt-out is the only value put on the wire, so the backend stays the single source of the default.
|
|
113
|
+
|
|
114
|
+
`CONTRACT.version` → `1.74.0`. Additive — existing callers are byte-for-byte unchanged on the wire.
|
|
115
|
+
|
|
102
116
|
### What's new in 0.98.0 (contract 1.70.0)
|
|
103
117
|
|
|
104
118
|
**Three new hooks close the biggest gaps in the write-gating and query-authoring surface (sc-5206).**
|
|
@@ -640,7 +654,7 @@ Also: `useFileSignatures(fileIds)` is now **self-scoped** (the caller's own sign
|
|
|
640
654
|
- `useFilestoreFiles({ spaceType, folderId?, q?, type? })` → `{ files, loading, error, refetch }` — browses the end-user's Filestore space. The hook resolves `owner_id` from the host context (tenant for a project space, the app user for a personal space), so the widget only picks the space. Every row carries a ready-to-render `url` (its absolutized `presigned_url`), so a gallery renders `files.map(f => f.url)` directly — no per-row `useFilestoreFile` call.
|
|
641
655
|
- `useFilestoreFile(fileId)` → `{ file, url, loading, error, refetch }` — resolves ONE file id to a displayable `url` (its `presigned_url`, absolutized for web + native) via `ctx.filestore.files.get(id)`. **Read the top-level `url`** — the returned `file` is `null` until the fetch resolves (and for an empty id), so `file.url` throws on the first render; it is correct only after a null check. **Never compose a file URL yourself** — the bytes are served only from a server-signed token URL, so a hand-built path like `/api/files/<id>` can never resolve (the linter's `no-host-api-url` rule flags it, including the `${location.origin}/api/files/…` form). A datastore `FILE` column holds — and reads back as — that **bare file-id string**; it is NOT hydrated into an object the way a `RELATION` (`{ id, label }`) or `USER` (`{ id, name }`) column is, so pass the value straight to the hook (no `{ id }` / `{ url }` unwrapping guard). Empty / deleted / not-found ids resolve to `url: null` so a display widget shows a fallback. When you render `url` in an `<Image>` that fills its container, size it with `aspectRatio` (e.g. `{ width: "100%", aspectRatio: 1 }`) or pixels — never a percentage `height`, which React Native collapses to 0 against a content-sized parent, so the image loads but is invisible. Requires `files.read:*`.
|
|
642
656
|
- `useFilestoreFolders({ spaceType, parentFolderId?, q?, enabled? })` → `{ folders, loading, error, refetch }` — the folder-navigation companion to `useFilestoreFiles`; pass `enabled:false` to suspend fetching.
|
|
643
|
-
- `useFilestoreUpload({ spaceType, folderId? })` → `{ upload, uploading, error, lastUploaded }` — POSTs a multipart upload to `ctx.filestore.files.upload`. Resolves `owner_id` from the host context (like the read hooks) so the widget only picks the space + destination folder. Pair with the `<FilePicker>` primitive for the visible trigger. Requires the `files.write:*` scope.
|
|
657
|
+
- `useFilestoreUpload({ spaceType, folderId?, compress? })` → `{ upload, uploading, error, lastUploaded }` — POSTs a multipart upload to `ctx.filestore.files.upload`. Resolves `owner_id` from the host context (like the read hooks) so the widget only picks the space + destination folder. Uploaded images are compressed to WebP by the backend; pass `compress: false` (on the hook, or per `upload(file, { compress })`) to store the file byte-for-byte — use it whenever the original matters. Pair with the `<FilePicker>` primitive for the visible trigger. Requires the `files.write:*` scope.
|
|
644
658
|
- `useFileSignature(fileId)` → `{ status, qr, signerName, verdict, initiate, refresh, cancel, verify, … }` — drives a BankID signing flow for a file (the backend hashes the bytes server-side, binds the digest into the signature, and verifies the proof offline).
|
|
645
659
|
|
|
646
660
|
`CONTRACT.version` → `1.20.0` (additive — no existing hook changed).
|
package/dist/contract.cjs
CHANGED
|
@@ -786,16 +786,21 @@ const HOOKS = [
|
|
|
786
786
|
},
|
|
787
787
|
{
|
|
788
788
|
name: "useFilestoreUpload",
|
|
789
|
-
signature: "useFilestoreUpload({ spaceType, folderId? })",
|
|
789
|
+
signature: "useFilestoreUpload({ spaceType, folderId?, compress? })",
|
|
790
790
|
description:
|
|
791
791
|
"Upload a file into the end-user's Filestore space. The widget passes " +
|
|
792
792
|
"the SPACE (`{ spaceType, folderId? }`); the hook resolves owner_id " +
|
|
793
793
|
"from the host context, builds a multipart FormData with the " +
|
|
794
794
|
"snake_case fields the backend reads verbatim (`space_type`, " +
|
|
795
795
|
"`owner_id`, `folder_id`, plus the binary `file`), and POSTs through " +
|
|
796
|
-
"ctx.filestore.files.upload. `upload(file, { folderId? })`
|
|
797
|
-
"to the created file row or throws the wire error; a 404
|
|
798
|
-
"destination folder denied a write (REQ-FSH canWrite gate). " +
|
|
796
|
+
"ctx.filestore.files.upload. `upload(file, { folderId?, compress? })` " +
|
|
797
|
+
"resolves to the created file row or throws the wire error; a 404 " +
|
|
798
|
+
"means the destination folder denied a write (REQ-FSH canWrite gate). " +
|
|
799
|
+
"Uploaded images are compressed to WebP by the backend; pass " +
|
|
800
|
+
"`compress: false` (on the hook or per upload) to store the file " +
|
|
801
|
+
"byte-for-byte as the user provided it — use that whenever the " +
|
|
802
|
+
"ORIGINAL matters (a document archive, a photo the user re-downloads, " +
|
|
803
|
+
"anything with an exact-bytes requirement). " +
|
|
799
804
|
"ALWAYS pass spaceType explicitly — omitting it falls back to " +
|
|
800
805
|
"'project', which is unreadable by a logged-out visitor. Choose it by " +
|
|
801
806
|
"who must SEE the file: 'public' for content the app displays to " +
|
|
@@ -803,7 +808,7 @@ const HOOKS = [
|
|
|
803
808
|
"'project' for content restricted to signed-in workspace users, " +
|
|
804
809
|
"'personal' for a file private to the uploading app user.",
|
|
805
810
|
returnShape: {
|
|
806
|
-
upload: "(file, { folderId? }) => Promise<FilestoreFile>",
|
|
811
|
+
upload: "(file, { folderId?, compress? }) => Promise<FilestoreFile>",
|
|
807
812
|
uploading: "boolean",
|
|
808
813
|
error: "Error | null",
|
|
809
814
|
lastUploaded: "FilestoreFile | null",
|
|
@@ -3199,7 +3204,12 @@ const CONTRACT = deepFreeze({
|
|
|
3199
3204
|
// `ctx.datastore.myPermissions` client method as a write-permission
|
|
3200
3205
|
// FLOOR, replacing the previous prompt-only "read useUser().groupIds /
|
|
3201
3206
|
// .roles" guidance. No existing export changed signature.
|
|
3202
|
-
|
|
3207
|
+
// 1.74.0: additive (sc-5402) — `useFilestoreUpload` accepts `compress`, on
|
|
3208
|
+
// the hook options and per `upload(file, { compress })`. Uploaded images
|
|
3209
|
+
// are compressed to WebP by default; `compress: false` stores the file
|
|
3210
|
+
// byte-for-byte. Existing callers are unaffected — the field is only sent
|
|
3211
|
+
// when the opt-out is chosen.
|
|
3212
|
+
version: "1.74.0",
|
|
3203
3213
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3204
3214
|
hooks: HOOKS,
|
|
3205
3215
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -786,16 +786,21 @@ const HOOKS = [
|
|
|
786
786
|
},
|
|
787
787
|
{
|
|
788
788
|
name: "useFilestoreUpload",
|
|
789
|
-
signature: "useFilestoreUpload({ spaceType, folderId? })",
|
|
789
|
+
signature: "useFilestoreUpload({ spaceType, folderId?, compress? })",
|
|
790
790
|
description:
|
|
791
791
|
"Upload a file into the end-user's Filestore space. The widget passes " +
|
|
792
792
|
"the SPACE (`{ spaceType, folderId? }`); the hook resolves owner_id " +
|
|
793
793
|
"from the host context, builds a multipart FormData with the " +
|
|
794
794
|
"snake_case fields the backend reads verbatim (`space_type`, " +
|
|
795
795
|
"`owner_id`, `folder_id`, plus the binary `file`), and POSTs through " +
|
|
796
|
-
"ctx.filestore.files.upload. `upload(file, { folderId? })`
|
|
797
|
-
"to the created file row or throws the wire error; a 404
|
|
798
|
-
"destination folder denied a write (REQ-FSH canWrite gate). " +
|
|
796
|
+
"ctx.filestore.files.upload. `upload(file, { folderId?, compress? })` " +
|
|
797
|
+
"resolves to the created file row or throws the wire error; a 404 " +
|
|
798
|
+
"means the destination folder denied a write (REQ-FSH canWrite gate). " +
|
|
799
|
+
"Uploaded images are compressed to WebP by the backend; pass " +
|
|
800
|
+
"`compress: false` (on the hook or per upload) to store the file " +
|
|
801
|
+
"byte-for-byte as the user provided it — use that whenever the " +
|
|
802
|
+
"ORIGINAL matters (a document archive, a photo the user re-downloads, " +
|
|
803
|
+
"anything with an exact-bytes requirement). " +
|
|
799
804
|
"ALWAYS pass spaceType explicitly — omitting it falls back to " +
|
|
800
805
|
"'project', which is unreadable by a logged-out visitor. Choose it by " +
|
|
801
806
|
"who must SEE the file: 'public' for content the app displays to " +
|
|
@@ -803,7 +808,7 @@ const HOOKS = [
|
|
|
803
808
|
"'project' for content restricted to signed-in workspace users, " +
|
|
804
809
|
"'personal' for a file private to the uploading app user.",
|
|
805
810
|
returnShape: {
|
|
806
|
-
upload: "(file, { folderId? }) => Promise<FilestoreFile>",
|
|
811
|
+
upload: "(file, { folderId?, compress? }) => Promise<FilestoreFile>",
|
|
807
812
|
uploading: "boolean",
|
|
808
813
|
error: "Error | null",
|
|
809
814
|
lastUploaded: "FilestoreFile | null",
|
|
@@ -3199,7 +3204,12 @@ const CONTRACT = deepFreeze({
|
|
|
3199
3204
|
// `ctx.datastore.myPermissions` client method as a write-permission
|
|
3200
3205
|
// FLOOR, replacing the previous prompt-only "read useUser().groupIds /
|
|
3201
3206
|
// .roles" guidance. No existing export changed signature.
|
|
3202
|
-
|
|
3207
|
+
// 1.74.0: additive (sc-5402) — `useFilestoreUpload` accepts `compress`, on
|
|
3208
|
+
// the hook options and per `upload(file, { compress })`. Uploaded images
|
|
3209
|
+
// are compressed to WebP by default; `compress: false` stores the file
|
|
3210
|
+
// byte-for-byte. Existing callers are unaffected — the field is only sent
|
|
3211
|
+
// when the opt-out is chosen.
|
|
3212
|
+
version: "1.74.0",
|
|
3203
3213
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3204
3214
|
hooks: HOOKS,
|
|
3205
3215
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -2801,7 +2801,11 @@ export function useFilestoreUpload(options) {
|
|
|
2801
2801
|
"useFilestoreUpload: host did not inject a filestore client (ctx.filestore.files.upload)",
|
|
2802
2802
|
);
|
|
2803
2803
|
}
|
|
2804
|
-
const {
|
|
2804
|
+
const {
|
|
2805
|
+
spaceType = "project",
|
|
2806
|
+
folderId: defaultFolderId = null,
|
|
2807
|
+
compress: defaultCompress = true,
|
|
2808
|
+
} = options || {};
|
|
2805
2809
|
const ownerId = _filestoreOwnerId(ctx, spaceType);
|
|
2806
2810
|
|
|
2807
2811
|
const [uploading, setUploading] = useState(false);
|
|
@@ -2823,10 +2827,17 @@ export function useFilestoreUpload(options) {
|
|
|
2823
2827
|
overrides && Object.prototype.hasOwnProperty.call(overrides, "folderId")
|
|
2824
2828
|
? overrides.folderId
|
|
2825
2829
|
: defaultFolderId;
|
|
2830
|
+
const compress =
|
|
2831
|
+
overrides && Object.prototype.hasOwnProperty.call(overrides, "compress")
|
|
2832
|
+
? overrides.compress
|
|
2833
|
+
: defaultCompress;
|
|
2826
2834
|
const form = new FormData();
|
|
2827
2835
|
form.append("space_type", String(spaceType || "project").toUpperCase());
|
|
2828
2836
|
form.append("owner_id", ownerId);
|
|
2829
2837
|
if (folderId) form.append("folder_id", folderId);
|
|
2838
|
+
// sc-5402: only the opt-out is sent, so the backend stays the single
|
|
2839
|
+
// source of the default (it compresses images to WebP).
|
|
2840
|
+
if (compress === false) form.append("compress", "false");
|
|
2830
2841
|
form.append("file", file);
|
|
2831
2842
|
setUploading(true);
|
|
2832
2843
|
setError(null);
|
|
@@ -2841,7 +2852,7 @@ export function useFilestoreUpload(options) {
|
|
|
2841
2852
|
throw err;
|
|
2842
2853
|
}
|
|
2843
2854
|
},
|
|
2844
|
-
[ownerId, defaultFolderId, spaceType],
|
|
2855
|
+
[ownerId, defaultFolderId, defaultCompress, spaceType],
|
|
2845
2856
|
);
|
|
2846
2857
|
|
|
2847
2858
|
return { upload, uploading, error, lastUploaded };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.100.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",
|