@colixsystems/widget-sdk 0.168.0 → 0.170.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 +26 -2
- package/dist/contract.cjs +63 -3
- package/dist/contract.js +63 -3
- package/dist/hooks.js +149 -1
- package/dist/index.js +2 -0
- package/dist/index.native.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,31 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
73
73
|
|
|
74
74
|
## Status
|
|
75
75
|
|
|
76
|
-
`v0.
|
|
76
|
+
`v0.170.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**.
|
|
77
|
+
|
|
78
|
+
### What's new in 0.170.0 (contract 1.131.0)
|
|
79
|
+
|
|
80
|
+
**An action script receives the parameters a Run action event sent — `input` joins `CONTRACT.actionScriptGlobals` (sc-9031).** A button or widget event bound to "Run action" could start an Action but could not tell it anything, so authors wrote the value into a row and switched to a record trigger. The binding now carries `params`, and a widget event's payload is forwarded with them:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
if (!input || typeof input.client_id !== "string") return;
|
|
84
|
+
await connectors.call("crm", { path: `/clients/${encodeURIComponent(input.client_id)}/sync`, method: "POST" });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- **Deep-frozen, null elsewhere.** `input` is `null` on `schedule`, `record_*`, `manual` and `http_post` runs, so `if (input)` is the guard.
|
|
88
|
+
- **Untrusted.** It comes from the app user's device; validate it. The run still executes as the Action's bound integration identity.
|
|
89
|
+
- **Widget side unchanged.** A widget passes data with the emitter it already has: `useWidgetEvent("slotChosen")({ slot })`. The author binds that event to Run action.
|
|
90
|
+
|
|
91
|
+
### What's new in 0.169.0 (contract 1.130.0)
|
|
92
|
+
|
|
93
|
+
**Widgets can create folders and copy files, including between spaces.** Two new hooks over `ctx.filestore`:
|
|
94
|
+
|
|
95
|
+
- `useFilestoreCreateFolder({ spaceType, parentFolderId? })` → `{ createFolder, creating, error, lastCreated }`. `createFolder(name, { parentFolderId? })` resolves to the new folder row; `parentFolderId` omitted or `null` places it at the space root. Refetch your `useFilestoreFolders` afterwards to show it.
|
|
96
|
+
- `useFilestoreCopy()` → `{ copy, copying, error, lastCopied }`. `copy(fileId, { spaceType, folderId?, name? })` duplicates a file into any space the user may add files to — project, public, or their own personal space — and resolves to the **new** file row with its own `id` and `url`. `spaceType` names the destination and is required. The bytes are duplicated server-side, so the copy and its source are independent afterwards. A name already taken at the destination lands as `name (2).ext` and never replaces the existing file.
|
|
97
|
+
|
|
98
|
+
The public space keeps its usual gate: copying into it needs the same upload access the workspace operator grants for uploads, and creating a folder there needs the right to manage that space, so an app user's public `createFolder()` is refused unless they were given it. Copying into the user's own personal space works whenever they may download the source.
|
|
99
|
+
|
|
100
|
+
Refusals keep the usual shape: branch on `err.status`, with the server's code at `err.details.code`. The copy-specific codes are `FILE_COPY_NOT_PERMITTED` (the user may only view the source), `UPLOAD_NOT_PERMITTED` (the destination refuses the add), `STORAGE_QUOTA_EXCEEDED` (413) and `FILE_PROCESSING` (409, a video still being processed). Both hooks need the `files.write:*` scope and behave identically on the web Player and the native export. Needs `@colixsystems/filestore-client` 0.10.0, which adds `files.copy(id, body)`.
|
|
77
101
|
|
|
78
102
|
### What's new in 0.167.0 (contract 1.129.0)
|
|
79
103
|
|
|
@@ -1593,7 +1617,7 @@ Also: `useFileSignatures(fileIds)` is now **self-scoped** (the caller's own sign
|
|
|
1593
1617
|
**Filestore browsing + BankID file signing for widgets (REQ-FS / REQ-SIGN).** Three new hooks read a newly-injected `ctx.filestore` (the `@colixsystems/filestore-client`, now constructed by both the web and native hosts):
|
|
1594
1618
|
- `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. Every row ALSO carries `urls` — the size ladder `{ thumbnail, card, large, hero }` at 128 / 512 / 1024 / 2048px longest edge — so a thumbnail grid should render `f.urls.thumbnail` rather than pulling the full-size original for every tile.
|
|
1595
1619
|
- `useFilestoreFile(fileId)` → `{ file, url, urls, 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:*`.
|
|
1596
|
-
- `useFilestoreFolders({ spaceType, parentFolderId?, q?, enabled? })` → `{ folders, loading, error, refetch }` — the folder-navigation companion to `useFilestoreFiles`; pass `enabled:false` to suspend fetching.
|
|
1620
|
+
- `useFilestoreFolders({ spaceType, parentFolderId?, q?, enabled? })` → `{ folders, loading, error, refetch }` — the folder-navigation companion to `useFilestoreFiles`; pass `enabled:false` to suspend fetching. (Folder creation and file copy arrived later, in 0.169.0 — see above.)
|
|
1597
1621
|
- `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.
|
|
1598
1622
|
- `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).
|
|
1599
1623
|
|
package/dist/contract.cjs
CHANGED
|
@@ -1217,6 +1217,56 @@ const HOOKS = [
|
|
|
1217
1217
|
requiredContextSlice: ["filestore.folders"],
|
|
1218
1218
|
scopes: ["files.read:*"],
|
|
1219
1219
|
},
|
|
1220
|
+
{
|
|
1221
|
+
name: "useFilestoreCreateFolder",
|
|
1222
|
+
signature: "useFilestoreCreateFolder({ spaceType, parentFolderId })",
|
|
1223
|
+
optionalArgs: ["parentFolderId"],
|
|
1224
|
+
description:
|
|
1225
|
+
"Create a folder in the end-user's Filestore space. The widget passes the " +
|
|
1226
|
+
"SPACE (`{ spaceType, parentFolderId? }`); the hook resolves owner_id from " +
|
|
1227
|
+
"the host context and posts `{ name, space_type, owner_id, parent_folder_id }` " +
|
|
1228
|
+
"through ctx.filestore.folders.create. `createFolder(name, { parentFolderId? })` " +
|
|
1229
|
+
"resolves to the created folder row or throws the wire error: 422 for an " +
|
|
1230
|
+
"invalid name, 404 for a parent that is not in this space, 403/404 when the " +
|
|
1231
|
+
"caller may not add a folder there (the server's FOLDER_CREATE_NOT_PERMITTED " +
|
|
1232
|
+
"is at `err.details.code`). In the public space only managers of that space " +
|
|
1233
|
+
"may create folders. Refetch useFilestoreFolders afterwards to show it.",
|
|
1234
|
+
returnShape: {
|
|
1235
|
+
createFolder: "(name, { parentFolderId? }) => Promise<FilestoreFolder>",
|
|
1236
|
+
creating: "boolean",
|
|
1237
|
+
error: "Error | null",
|
|
1238
|
+
lastCreated: "FilestoreFolder | null",
|
|
1239
|
+
},
|
|
1240
|
+
requiredContextSlice: ["filestore.folders"],
|
|
1241
|
+
scopes: ["files.write:*"],
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
name: "useFilestoreCopy",
|
|
1245
|
+
signature: "useFilestoreCopy()",
|
|
1246
|
+
description:
|
|
1247
|
+
"Copy a Filestore file into any space and folder the caller may add files " +
|
|
1248
|
+
"to, including ACROSS spaces (project, public, and the signed-in user's " +
|
|
1249
|
+
"personal space). `copy(fileId, { spaceType, folderId?, name? })` names the " +
|
|
1250
|
+
"DESTINATION (spaceType is required); the hook resolves its owner_id from " +
|
|
1251
|
+
"the host context and posts through ctx.filestore.files.copy. Resolves to " +
|
|
1252
|
+
"the NEW file row (its own id and url) — the source is untouched and the " +
|
|
1253
|
+
"bytes are duplicated, so later changes to one never affect the other. A " +
|
|
1254
|
+
"name already taken at the destination lands as \"name (2).ext\" instead of " +
|
|
1255
|
+
"replacing it. Copying INTO public needs the same operator upload grant as " +
|
|
1256
|
+
"uploading there. Errors (code at `err.details.code`): 404 source not visible; " +
|
|
1257
|
+
"403 FILE_COPY_NOT_PERMITTED when the caller may only view the source; " +
|
|
1258
|
+
"403/404 UPLOAD_NOT_PERMITTED when the destination refuses the add; 413 " +
|
|
1259
|
+
"STORAGE_QUOTA_EXCEEDED when the destination space is full; 409 " +
|
|
1260
|
+
"FILE_PROCESSING while a video is still being processed.",
|
|
1261
|
+
returnShape: {
|
|
1262
|
+
copy: "(fileId, { spaceType, folderId?, name? }) => Promise<FilestoreFile>",
|
|
1263
|
+
copying: "boolean",
|
|
1264
|
+
error: "Error | null",
|
|
1265
|
+
lastCopied: "FilestoreFile | null",
|
|
1266
|
+
},
|
|
1267
|
+
requiredContextSlice: ["filestore.files"],
|
|
1268
|
+
scopes: ["files.write:*"],
|
|
1269
|
+
},
|
|
1220
1270
|
{
|
|
1221
1271
|
name: "useFileSignature",
|
|
1222
1272
|
signature: "useFileSignature(fileId, existingSignatureId)",
|
|
@@ -2401,6 +2451,8 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
2401
2451
|
"triggerTableId",
|
|
2402
2452
|
// sc-7885 — who initiated the run; null on `schedule` / `record_*`.
|
|
2403
2453
|
"triggeredBy",
|
|
2454
|
+
// sc-9031 — a Run action event's parameters; null on every other trigger.
|
|
2455
|
+
"input",
|
|
2404
2456
|
];
|
|
2405
2457
|
// Mirrors action.service.js SCRIPT_MAX_BYTES.
|
|
2406
2458
|
const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
|
|
@@ -2692,10 +2744,10 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
2692
2744
|
filestore: {
|
|
2693
2745
|
description:
|
|
2694
2746
|
"Injected @colixsystems/filestore-client instance — the end-user file archive (project / personal / public spaces) + BankID file signing. " +
|
|
2695
|
-
"{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), remove(id), preview(id) }, " +
|
|
2747
|
+
"{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), copy(id, body), remove(id), preview(id) }, " +
|
|
2696
2748
|
"folders: { list, create, update, remove }, shares: { ... }, trash: { ... }, " +
|
|
2697
2749
|
"signatures: { initiate(fileId), status(id), cancel(id), verify(id) }, objectUrl(token), fetchObject(token) }. " +
|
|
2698
|
-
"Backs useFilestoreFiles() + useFilestoreFolders() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
|
|
2750
|
+
"Backs useFilestoreFiles() + useFilestoreFolders() + useFilestoreCreateFolder() + useFilestoreCopy() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
|
|
2699
2751
|
required: false,
|
|
2700
2752
|
fields: { files: "object", folders: "object", shares: "object", signatures: "object" },
|
|
2701
2753
|
},
|
|
@@ -4553,7 +4605,15 @@ const CONTRACT = deepFreeze({
|
|
|
4553
4605
|
// it removed every `<...>`, so an AI reply lost `Promise<void>`. The
|
|
4554
4606
|
// emphasis grammar now follows CommonMark flanking rules, so `created_at`
|
|
4555
4607
|
// keeps its underscore and `2 * 4` keeps its asterisk.
|
|
4556
|
-
|
|
4608
|
+
// 1.130.0: additive (sc-9057) — `useFilestoreCreateFolder` and
|
|
4609
|
+
// `useFilestoreCopy`. Widgets can now create Filestore folders and copy a
|
|
4610
|
+
// file into another folder or space (project / public / personal); the
|
|
4611
|
+
// filestore client gains `files.copy`.
|
|
4612
|
+
// 1.131.0: additive (sc-9031) — `input` joins `actionScriptGlobals`: the
|
|
4613
|
+
// parameters a Run action event sent (the binding's `params` merged over
|
|
4614
|
+
// the emitting widget's payload), deep-frozen, null on every non-`app`
|
|
4615
|
+
// trigger. Additive: a script that never reads it runs unchanged.
|
|
4616
|
+
version: "1.131.0",
|
|
4557
4617
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4558
4618
|
hooks: HOOKS,
|
|
4559
4619
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1217,6 +1217,56 @@ const HOOKS = [
|
|
|
1217
1217
|
requiredContextSlice: ["filestore.folders"],
|
|
1218
1218
|
scopes: ["files.read:*"],
|
|
1219
1219
|
},
|
|
1220
|
+
{
|
|
1221
|
+
name: "useFilestoreCreateFolder",
|
|
1222
|
+
signature: "useFilestoreCreateFolder({ spaceType, parentFolderId })",
|
|
1223
|
+
optionalArgs: ["parentFolderId"],
|
|
1224
|
+
description:
|
|
1225
|
+
"Create a folder in the end-user's Filestore space. The widget passes the " +
|
|
1226
|
+
"SPACE (`{ spaceType, parentFolderId? }`); the hook resolves owner_id from " +
|
|
1227
|
+
"the host context and posts `{ name, space_type, owner_id, parent_folder_id }` " +
|
|
1228
|
+
"through ctx.filestore.folders.create. `createFolder(name, { parentFolderId? })` " +
|
|
1229
|
+
"resolves to the created folder row or throws the wire error: 422 for an " +
|
|
1230
|
+
"invalid name, 404 for a parent that is not in this space, 403/404 when the " +
|
|
1231
|
+
"caller may not add a folder there (the server's FOLDER_CREATE_NOT_PERMITTED " +
|
|
1232
|
+
"is at `err.details.code`). In the public space only managers of that space " +
|
|
1233
|
+
"may create folders. Refetch useFilestoreFolders afterwards to show it.",
|
|
1234
|
+
returnShape: {
|
|
1235
|
+
createFolder: "(name, { parentFolderId? }) => Promise<FilestoreFolder>",
|
|
1236
|
+
creating: "boolean",
|
|
1237
|
+
error: "Error | null",
|
|
1238
|
+
lastCreated: "FilestoreFolder | null",
|
|
1239
|
+
},
|
|
1240
|
+
requiredContextSlice: ["filestore.folders"],
|
|
1241
|
+
scopes: ["files.write:*"],
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
name: "useFilestoreCopy",
|
|
1245
|
+
signature: "useFilestoreCopy()",
|
|
1246
|
+
description:
|
|
1247
|
+
"Copy a Filestore file into any space and folder the caller may add files " +
|
|
1248
|
+
"to, including ACROSS spaces (project, public, and the signed-in user's " +
|
|
1249
|
+
"personal space). `copy(fileId, { spaceType, folderId?, name? })` names the " +
|
|
1250
|
+
"DESTINATION (spaceType is required); the hook resolves its owner_id from " +
|
|
1251
|
+
"the host context and posts through ctx.filestore.files.copy. Resolves to " +
|
|
1252
|
+
"the NEW file row (its own id and url) — the source is untouched and the " +
|
|
1253
|
+
"bytes are duplicated, so later changes to one never affect the other. A " +
|
|
1254
|
+
"name already taken at the destination lands as \"name (2).ext\" instead of " +
|
|
1255
|
+
"replacing it. Copying INTO public needs the same operator upload grant as " +
|
|
1256
|
+
"uploading there. Errors (code at `err.details.code`): 404 source not visible; " +
|
|
1257
|
+
"403 FILE_COPY_NOT_PERMITTED when the caller may only view the source; " +
|
|
1258
|
+
"403/404 UPLOAD_NOT_PERMITTED when the destination refuses the add; 413 " +
|
|
1259
|
+
"STORAGE_QUOTA_EXCEEDED when the destination space is full; 409 " +
|
|
1260
|
+
"FILE_PROCESSING while a video is still being processed.",
|
|
1261
|
+
returnShape: {
|
|
1262
|
+
copy: "(fileId, { spaceType, folderId?, name? }) => Promise<FilestoreFile>",
|
|
1263
|
+
copying: "boolean",
|
|
1264
|
+
error: "Error | null",
|
|
1265
|
+
lastCopied: "FilestoreFile | null",
|
|
1266
|
+
},
|
|
1267
|
+
requiredContextSlice: ["filestore.files"],
|
|
1268
|
+
scopes: ["files.write:*"],
|
|
1269
|
+
},
|
|
1220
1270
|
{
|
|
1221
1271
|
name: "useFileSignature",
|
|
1222
1272
|
signature: "useFileSignature(fileId, existingSignatureId)",
|
|
@@ -2397,6 +2447,8 @@ const ACTION_SCRIPT_GLOBALS = [
|
|
|
2397
2447
|
"triggerTableId",
|
|
2398
2448
|
// sc-7885 — who initiated the run; null on `schedule` / `record_*`.
|
|
2399
2449
|
"triggeredBy",
|
|
2450
|
+
// sc-9031 — a Run action event's parameters; null on every other trigger.
|
|
2451
|
+
"input",
|
|
2400
2452
|
];
|
|
2401
2453
|
// Mirrors action.service.js SCRIPT_MAX_BYTES.
|
|
2402
2454
|
const ACTION_SCRIPT_MAX_BYTES = 200 * 1024;
|
|
@@ -2688,10 +2740,10 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
2688
2740
|
filestore: {
|
|
2689
2741
|
description:
|
|
2690
2742
|
"Injected @colixsystems/filestore-client instance — the end-user file archive (project / personal / public spaces) + BankID file signing. " +
|
|
2691
|
-
"{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), remove(id), preview(id) }, " +
|
|
2743
|
+
"{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), copy(id, body), remove(id), preview(id) }, " +
|
|
2692
2744
|
"folders: { list, create, update, remove }, shares: { ... }, trash: { ... }, " +
|
|
2693
2745
|
"signatures: { initiate(fileId), status(id), cancel(id), verify(id) }, objectUrl(token), fetchObject(token) }. " +
|
|
2694
|
-
"Backs useFilestoreFiles() + useFilestoreFolders() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
|
|
2746
|
+
"Backs useFilestoreFiles() + useFilestoreFolders() + useFilestoreCreateFolder() + useFilestoreCopy() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
|
|
2695
2747
|
required: false,
|
|
2696
2748
|
fields: { files: "object", folders: "object", shares: "object", signatures: "object" },
|
|
2697
2749
|
},
|
|
@@ -4549,7 +4601,15 @@ const CONTRACT = deepFreeze({
|
|
|
4549
4601
|
// it removed every `<...>`, so an AI reply lost `Promise<void>`. The
|
|
4550
4602
|
// emphasis grammar now follows CommonMark flanking rules, so `created_at`
|
|
4551
4603
|
// keeps its underscore and `2 * 4` keeps its asterisk.
|
|
4552
|
-
|
|
4604
|
+
// 1.130.0: additive (sc-9057) — `useFilestoreCreateFolder` and
|
|
4605
|
+
// `useFilestoreCopy`. Widgets can now create Filestore folders and copy a
|
|
4606
|
+
// file into another folder or space (project / public / personal); the
|
|
4607
|
+
// filestore client gains `files.copy`.
|
|
4608
|
+
// 1.131.0: additive (sc-9031) — `input` joins `actionScriptGlobals`: the
|
|
4609
|
+
// parameters a Run action event sent (the binding's `params` merged over
|
|
4610
|
+
// the emitting widget's payload), deep-frozen, null on every non-`app`
|
|
4611
|
+
// trigger. Additive: a script that never reads it runs unchanged.
|
|
4612
|
+
version: "1.131.0",
|
|
4553
4613
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
4554
4614
|
hooks: HOOKS,
|
|
4555
4615
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -3850,7 +3850,8 @@ export function useAssetsByTag(tag, options) {
|
|
|
3850
3850
|
* FILESTORE CLIENT — ctx.filestore (@colixsystems/filestore-client)
|
|
3851
3851
|
*
|
|
3852
3852
|
* The end-user file archive (project / personal spaces) + BankID file signing.
|
|
3853
|
-
* Covers: useFilestoreFiles,
|
|
3853
|
+
* Covers: useFilestoreFiles, useFilestoreFolders, useFilestoreCreateFolder,
|
|
3854
|
+
* useFilestoreCopy, useFilestoreUpload, useFileSignature.
|
|
3854
3855
|
* ==========================================================================*/
|
|
3855
3856
|
|
|
3856
3857
|
// Resolve the owner_id a filestore query needs from the host context: a PROJECT
|
|
@@ -4302,6 +4303,153 @@ export function useFilestoreFolders(options) {
|
|
|
4302
4303
|
return { folders, loading, error, refetch };
|
|
4303
4304
|
}
|
|
4304
4305
|
|
|
4306
|
+
/**
|
|
4307
|
+
* Create a folder in the end-user's Filestore space. Returns `{ createFolder,
|
|
4308
|
+
* creating, error, lastCreated }`. The widget passes the SPACE (`{ spaceType,
|
|
4309
|
+
* parentFolderId? }`); the hook resolves owner_id from the host context and
|
|
4310
|
+
* posts `{ name, space_type, owner_id, parent_folder_id }` through
|
|
4311
|
+
* `ctx.filestore.folders.create`.
|
|
4312
|
+
*
|
|
4313
|
+
* `createFolder(name, { parentFolderId? })` resolves to the created folder row
|
|
4314
|
+
* or throws the wire error: 422 for an invalid name, 404 for a parent that is
|
|
4315
|
+
* not in this space, and 403/404 when the caller may not add a folder there
|
|
4316
|
+
* (403 when they can see the position). The server's
|
|
4317
|
+
* `FOLDER_CREATE_NOT_PERMITTED` is at `err.details.code`.
|
|
4318
|
+
*/
|
|
4319
|
+
export function useFilestoreCreateFolder(options) {
|
|
4320
|
+
const ctx = useWidgetContextOrThrow("useFilestoreCreateFolder");
|
|
4321
|
+
if (
|
|
4322
|
+
!ctx.filestore ||
|
|
4323
|
+
!ctx.filestore.folders ||
|
|
4324
|
+
typeof ctx.filestore.folders.create !== "function"
|
|
4325
|
+
) {
|
|
4326
|
+
throw new Error(
|
|
4327
|
+
"useFilestoreCreateFolder: host did not inject a filestore client (ctx.filestore.folders.create)",
|
|
4328
|
+
);
|
|
4329
|
+
}
|
|
4330
|
+
const { spaceType = "project", parentFolderId: defaultParentId = null } =
|
|
4331
|
+
options || {};
|
|
4332
|
+
const ownerId = _filestoreOwnerId(ctx, spaceType);
|
|
4333
|
+
|
|
4334
|
+
const [creating, setCreating] = useState(false);
|
|
4335
|
+
const [error, setError] = useState(null);
|
|
4336
|
+
const [lastCreated, setLastCreated] = useState(null);
|
|
4337
|
+
|
|
4338
|
+
const foldersRef = useRef(ctx.filestore.folders);
|
|
4339
|
+
foldersRef.current = ctx.filestore.folders;
|
|
4340
|
+
|
|
4341
|
+
const createFolder = useCallback(
|
|
4342
|
+
async (name, overrides) => {
|
|
4343
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
4344
|
+
throw new Error("useFilestoreCreateFolder: name is required");
|
|
4345
|
+
}
|
|
4346
|
+
if (!ownerId) {
|
|
4347
|
+
const err = new Error("Sign in to create a folder");
|
|
4348
|
+
setError(err);
|
|
4349
|
+
throw err;
|
|
4350
|
+
}
|
|
4351
|
+
const parentFolderId =
|
|
4352
|
+
overrides &&
|
|
4353
|
+
Object.prototype.hasOwnProperty.call(overrides, "parentFolderId")
|
|
4354
|
+
? overrides.parentFolderId
|
|
4355
|
+
: defaultParentId;
|
|
4356
|
+
const body = {
|
|
4357
|
+
name: name.trim(),
|
|
4358
|
+
space_type: String(spaceType || "project").toUpperCase(),
|
|
4359
|
+
owner_id: ownerId,
|
|
4360
|
+
};
|
|
4361
|
+
if (parentFolderId) body.parent_folder_id = parentFolderId;
|
|
4362
|
+
setCreating(true);
|
|
4363
|
+
setError(null);
|
|
4364
|
+
try {
|
|
4365
|
+
const created = await foldersRef.current.create(body);
|
|
4366
|
+
setLastCreated(created || null);
|
|
4367
|
+
setCreating(false);
|
|
4368
|
+
return created;
|
|
4369
|
+
} catch (err) {
|
|
4370
|
+
setError(err);
|
|
4371
|
+
setCreating(false);
|
|
4372
|
+
throw err;
|
|
4373
|
+
}
|
|
4374
|
+
},
|
|
4375
|
+
[ownerId, defaultParentId, spaceType],
|
|
4376
|
+
);
|
|
4377
|
+
|
|
4378
|
+
return { createFolder, creating, error, lastCreated };
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4381
|
+
/**
|
|
4382
|
+
* Copy a Filestore file into any space and folder the caller may add files
|
|
4383
|
+
* to — including ACROSS spaces (project, public, the user's personal space).
|
|
4384
|
+
* Returns `{ copy, copying, error, lastCopied }`.
|
|
4385
|
+
*
|
|
4386
|
+
* `copy(fileId, { spaceType, folderId?, name? })` names the DESTINATION;
|
|
4387
|
+
* `spaceType` is required. The hook resolves the destination owner_id the same
|
|
4388
|
+
* way the upload hook does and posts through `ctx.filestore.files.copy`. It
|
|
4389
|
+
* resolves to the NEW file row (its own id and url); the source is untouched.
|
|
4390
|
+
* A name already taken at the destination lands as "name (2).ext" rather than
|
|
4391
|
+
* replacing that file.
|
|
4392
|
+
*
|
|
4393
|
+
* Errors: 404 when the source is not visible, 403 `FILE_COPY_NOT_PERMITTED`
|
|
4394
|
+
* when it is view-only, 403/404 `UPLOAD_NOT_PERMITTED` when the destination
|
|
4395
|
+
* refuses the add, 413 `STORAGE_QUOTA_EXCEEDED` when the destination space is
|
|
4396
|
+
* full, 409 `FILE_PROCESSING` while a video is still being processed. The code
|
|
4397
|
+
* is at `err.details.code`.
|
|
4398
|
+
*/
|
|
4399
|
+
export function useFilestoreCopy() {
|
|
4400
|
+
const ctx = useWidgetContextOrThrow("useFilestoreCopy");
|
|
4401
|
+
if (
|
|
4402
|
+
!ctx.filestore ||
|
|
4403
|
+
!ctx.filestore.files ||
|
|
4404
|
+
typeof ctx.filestore.files.copy !== "function"
|
|
4405
|
+
) {
|
|
4406
|
+
throw new Error(
|
|
4407
|
+
"useFilestoreCopy: host did not inject a filestore client (ctx.filestore.files.copy)",
|
|
4408
|
+
);
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4411
|
+
const [copying, setCopying] = useState(false);
|
|
4412
|
+
const [error, setError] = useState(null);
|
|
4413
|
+
const [lastCopied, setLastCopied] = useState(null);
|
|
4414
|
+
|
|
4415
|
+
const ctxRef = useRef(ctx);
|
|
4416
|
+
ctxRef.current = ctx;
|
|
4417
|
+
|
|
4418
|
+
const copy = useCallback(async (fileId, destination) => {
|
|
4419
|
+
if (!fileId) throw new Error("useFilestoreCopy: fileId is required");
|
|
4420
|
+
const { spaceType, folderId, name } = destination || {};
|
|
4421
|
+
if (!spaceType) {
|
|
4422
|
+
throw new Error("useFilestoreCopy: destination spaceType is required");
|
|
4423
|
+
}
|
|
4424
|
+
const ownerId = _filestoreOwnerId(ctxRef.current, spaceType);
|
|
4425
|
+
if (!ownerId) {
|
|
4426
|
+
const err = new Error("Sign in to copy files");
|
|
4427
|
+
setError(err);
|
|
4428
|
+
throw err;
|
|
4429
|
+
}
|
|
4430
|
+
const body = {
|
|
4431
|
+
space_type: String(spaceType).toUpperCase(),
|
|
4432
|
+
owner_id: ownerId,
|
|
4433
|
+
};
|
|
4434
|
+
if (folderId) body.folder_id = folderId;
|
|
4435
|
+
if (name) body.name = name;
|
|
4436
|
+
setCopying(true);
|
|
4437
|
+
setError(null);
|
|
4438
|
+
try {
|
|
4439
|
+
const created = await ctxRef.current.filestore.files.copy(fileId, body);
|
|
4440
|
+
setLastCopied(created || null);
|
|
4441
|
+
setCopying(false);
|
|
4442
|
+
return created;
|
|
4443
|
+
} catch (err) {
|
|
4444
|
+
setError(err);
|
|
4445
|
+
setCopying(false);
|
|
4446
|
+
throw err;
|
|
4447
|
+
}
|
|
4448
|
+
}, []);
|
|
4449
|
+
|
|
4450
|
+
return { copy, copying, error, lastCopied };
|
|
4451
|
+
}
|
|
4452
|
+
|
|
4305
4453
|
/**
|
|
4306
4454
|
* Drive a BankID signing flow for one Filestore file. Returns
|
|
4307
4455
|
* `{ status, qr, autoStartToken, message, signerName, signedAt, verdict,
|
package/dist/index.js
CHANGED
package/dist/index.native.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.170.0",
|
|
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",
|