@colixsystems/widget-sdk 0.168.0 → 0.169.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
@@ -73,7 +73,18 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
73
73
 
74
74
  ## Status
75
75
 
76
- `v0.167.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**.
76
+ `v0.169.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.169.0 (contract 1.130.0)
79
+
80
+ **Widgets can create folders and copy files, including between spaces.** Two new hooks over `ctx.filestore`:
81
+
82
+ - `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.
83
+ - `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.
84
+
85
+ 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.
86
+
87
+ 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
88
 
78
89
  ### What's new in 0.167.0 (contract 1.129.0)
79
90
 
@@ -1593,7 +1604,7 @@ Also: `useFileSignatures(fileIds)` is now **self-scoped** (the caller's own sign
1593
1604
  **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
1605
  - `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
1606
  - `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.
1607
+ - `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
1608
  - `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
1609
  - `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
1610
 
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)",
@@ -2692,10 +2742,10 @@ const WIDGET_CONTEXT_SHAPE = {
2692
2742
  filestore: {
2693
2743
  description:
2694
2744
  "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) }, " +
2745
+ "{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), copy(id, body), remove(id), preview(id) }, " +
2696
2746
  "folders: { list, create, update, remove }, shares: { ... }, trash: { ... }, " +
2697
2747
  "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.",
2748
+ "Backs useFilestoreFiles() + useFilestoreFolders() + useFilestoreCreateFolder() + useFilestoreCopy() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
2699
2749
  required: false,
2700
2750
  fields: { files: "object", folders: "object", shares: "object", signatures: "object" },
2701
2751
  },
@@ -4553,7 +4603,11 @@ const CONTRACT = deepFreeze({
4553
4603
  // it removed every `<...>`, so an AI reply lost `Promise<void>`. The
4554
4604
  // emphasis grammar now follows CommonMark flanking rules, so `created_at`
4555
4605
  // keeps its underscore and `2 * 4` keeps its asterisk.
4556
- version: "1.129.0",
4606
+ // 1.130.0: additive (sc-9057) — `useFilestoreCreateFolder` and
4607
+ // `useFilestoreCopy`. Widgets can now create Filestore folders and copy a
4608
+ // file into another folder or space (project / public / personal); the
4609
+ // filestore client gains `files.copy`.
4610
+ version: "1.130.0",
4557
4611
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
4558
4612
  hooks: HOOKS,
4559
4613
  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)",
@@ -2688,10 +2738,10 @@ const WIDGET_CONTEXT_SHAPE = {
2688
2738
  filestore: {
2689
2739
  description:
2690
2740
  "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) }, " +
2741
+ "{ files: { list(query) -> Promise<{ data, meta }>, get(id), upload(formData), update(id, body), copy(id, body), remove(id), preview(id) }, " +
2692
2742
  "folders: { list, create, update, remove }, shares: { ... }, trash: { ... }, " +
2693
2743
  "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.",
2744
+ "Backs useFilestoreFiles() + useFilestoreFolders() + useFilestoreCreateFolder() + useFilestoreCopy() + useFileSignature(). Optional — a host without an end-user filestore omits it and those hooks throw a clear error.",
2695
2745
  required: false,
2696
2746
  fields: { files: "object", folders: "object", shares: "object", signatures: "object" },
2697
2747
  },
@@ -4549,7 +4599,11 @@ const CONTRACT = deepFreeze({
4549
4599
  // it removed every `<...>`, so an AI reply lost `Promise<void>`. The
4550
4600
  // emphasis grammar now follows CommonMark flanking rules, so `created_at`
4551
4601
  // keeps its underscore and `2 * 4` keeps its asterisk.
4552
- version: "1.129.0",
4602
+ // 1.130.0: additive (sc-9057) — `useFilestoreCreateFolder` and
4603
+ // `useFilestoreCopy`. Widgets can now create Filestore folders and copy a
4604
+ // file into another folder or space (project / public / personal); the
4605
+ // filestore client gains `files.copy`.
4606
+ version: "1.130.0",
4553
4607
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
4554
4608
  hooks: HOOKS,
4555
4609
  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, useFileSignature.
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
@@ -44,6 +44,8 @@ export {
44
44
  useFilestoreUpload,
45
45
  usePdfExport,
46
46
  useFilestoreFolders,
47
+ useFilestoreCreateFolder,
48
+ useFilestoreCopy,
47
49
  useFileSignature,
48
50
  useFileSignatures,
49
51
  useFileRoster,
@@ -44,6 +44,8 @@ export {
44
44
  useFilestoreUpload,
45
45
  usePdfExport,
46
46
  useFilestoreFolders,
47
+ useFilestoreCreateFolder,
48
+ useFilestoreCopy,
47
49
  useFileSignature,
48
50
  useFileSignatures,
49
51
  useFileRoster,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.168.0",
3
+ "version": "0.169.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",