@burdenoff/fe-libs 2026.812.1 → 2026.813.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.
@@ -17,5 +17,16 @@ export interface UploadedFile {
17
17
  name: string;
18
18
  folderId: string | null;
19
19
  }
20
+ /**
21
+ * Best-effort message extraction, preferring the first GraphQL error's message
22
+ * (Apollo v4 CombinedGraphQLErrors `.errors`, or v3 `.graphQLErrors`) so we surface
23
+ * the real reason a completeUpload failed instead of a generic error envelope.
24
+ *
25
+ * Exported because every `client.mutate` call in this module faces the same
26
+ * problem: under `errorPolicy: 'all'` the failure arrives in-band on
27
+ * `result.error` and the caller's own "that didn't work" string is the LEAST
28
+ * informative thing it could report. See `useSaveToFiles`.
29
+ */
30
+ export declare function extractErrorMessage(err: unknown): string | undefined;
20
31
  export declare function uploadBlobToFiles(params: UploadBlobParams): Promise<UploadedFile>;
21
32
  //# sourceMappingURL=uploadBlob.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../src/storage/uploadBlob.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAyBxD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,UAKpC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AA6CD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8HvF"}
1
+ {"version":3,"file":"uploadBlob.d.ts","sourceRoot":"","sources":["../../src/storage/uploadBlob.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAyBxD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,UAKpC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AA2BD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAWpE;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8HvF"}
@@ -99,4 +99,4 @@ async function o(e) {
99
99
  return d?.(100), _;
100
100
  }
101
101
  //#endregion
102
- export { r as FILES_AFTER_SAVE_REFETCH, o as uploadBlobToFiles };
102
+ export { r as FILES_AFTER_SAVE_REFETCH, a as extractErrorMessage, o as uploadBlobToFiles };
@@ -1,3 +1,4 @@
1
+ import { ApolloClient } from '@apollo/client';
1
2
  import { SaveToFilesSource } from './SaveToFilesButton';
2
3
  export interface SavedFile {
3
4
  id: string;
@@ -13,5 +14,32 @@ export interface UseSaveToFilesResult {
13
14
  error: Error | null;
14
15
  progress: number;
15
16
  }
17
+ /** The Apollo surface `resolveFolderIdByPath` needs — kept narrow so it is trivial to fake. */
18
+ type FolderMutatingClient = Pick<ApolloClient, 'mutate'>;
19
+ /**
20
+ * Resolve (creating if needed) the destination folder for a path, returning its id.
21
+ *
22
+ * Split out of the hook so it is unit-testable the same way `uploadBlobToFiles`
23
+ * is: pure modulo the injected Apollo client.
24
+ *
25
+ * `ensureFolderByPath` is declared `Folder!` in wspace-files-svc, and its
26
+ * resolver creates any missing ancestors and collapses concurrent calls for the
27
+ * same path onto a single row (partial unique index on
28
+ * `(workspace_id, parent_folder_id, name)`). It therefore never resolves to
29
+ * null: a null here means the operation FAILED and GraphQL non-null propagation
30
+ * blanked the field.
31
+ *
32
+ * That distinction is the whole point of this function. The shared Apollo client
33
+ * runs `errorPolicy: 'all'`, so those failures come back IN-BAND on
34
+ * `result.error` rather than being thrown — exactly as `uploadBlobToFiles`
35
+ * already documents for initiateUpload/completeUpload. Reading only `data` and
36
+ * blaming the path turned a gateway OPERATION_TIMEOUT into "Could not resolve
37
+ * folder for path: /HealthyBowl/AI Assistant/Attachments", which reads like a
38
+ * bad path or a missing folder and sent people looking at file-storage quota
39
+ * they had plenty of. Surface the real reason; keep the path as a last resort
40
+ * for the genuinely inexplicable case.
41
+ */
42
+ export declare function resolveFolderIdByPath(client: FolderMutatingClient, folderPath: string): Promise<string>;
16
43
  export declare function useSaveToFiles(): UseSaveToFilesResult;
44
+ export {};
17
45
  //# sourceMappingURL=useSaveToFiles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSaveToFiles.d.ts","sourceRoot":"","sources":["../../src/storage/useSaveToFiles.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAuB7D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,cAAc,IAAI,oBAAoB,CAmErD"}
1
+ {"version":3,"file":"useSaveToFiles.d.ts","sourceRoot":"","sources":["../../src/storage/useSaveToFiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAuB7D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxF,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+FAA+F;AAC/F,KAAK,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,oBAAoB,EAC5B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,wBAAgB,cAAc,IAAI,oBAAoB,CAqErD"}
@@ -1,9 +1,9 @@
1
- import { FILES_AFTER_SAVE_REFETCH as e, uploadBlobToFiles as t } from "./uploadBlob.js";
2
- import { useState as n } from "react";
3
- import { gql as r } from "@apollo/client";
4
- import { useApolloClient as i } from "@apollo/client/react";
1
+ import { FILES_AFTER_SAVE_REFETCH as e, extractErrorMessage as t, uploadBlobToFiles as n } from "./uploadBlob.js";
2
+ import { useState as r } from "react";
3
+ import { gql as i } from "@apollo/client";
4
+ import { useApolloClient as a } from "@apollo/client/react";
5
5
  //#region src/storage/useSaveToFiles.ts
6
- var a = r`
6
+ var o = i`
7
7
  mutation SaveToFiles_EnsureFolderByPath($path: String!) {
8
8
  ensureFolderByPath(path: $path) {
9
9
  id
@@ -11,7 +11,7 @@ var a = r`
11
11
  path
12
12
  }
13
13
  }
14
- `, o = r`
14
+ `, s = i`
15
15
  mutation SaveToFiles_SaveSandboxArtifact($input: SaveSandboxArtifactInput!) {
16
16
  saveSandboxArtifactToFiles(input: $input) {
17
17
  id
@@ -20,54 +20,58 @@ var a = r`
20
20
  }
21
21
  }
22
22
  `;
23
- function s() {
24
- let r = i(), [s, c] = n(!1), [l, u] = n(null), [d, f] = n(0);
23
+ async function c(e, n) {
24
+ let r = await e.mutate({
25
+ mutation: o,
26
+ variables: { path: n }
27
+ }), i = r.data?.ensureFolderByPath?.id;
28
+ if (i) return i;
29
+ throw Error(t(r.error) ?? `Could not resolve folder for path: ${n}`);
30
+ }
31
+ function l() {
32
+ let i = a(), [o, l] = r(!1), [u, d] = r(null), [f, p] = r(0);
25
33
  return {
26
- save: async (n) => {
27
- c(!0), u(null), f(0);
34
+ save: async (r) => {
35
+ l(!0), d(null), p(0);
28
36
  try {
29
- let i = (await r.mutate({
30
- mutation: a,
31
- variables: { path: n.folderPath }
32
- })).data?.ensureFolderByPath?.id;
33
- if (!i) throw Error(`Could not resolve folder for path: ${n.folderPath}`);
34
- if (n.source.kind === "sandbox-artifact") {
35
- let t = (await r.mutate({
36
- mutation: o,
37
+ let a = await c(i, r.folderPath);
38
+ if (r.source.kind === "sandbox-artifact") {
39
+ let n = await i.mutate({
40
+ mutation: s,
37
41
  variables: { input: {
38
- sandboxId: n.source.sandboxId,
39
- sessionId: n.source.sessionId,
40
- path: n.source.path,
41
- folderId: i,
42
- filename: n.source.filename ?? null
42
+ sandboxId: r.source.sandboxId,
43
+ sessionId: r.source.sessionId,
44
+ path: r.source.path,
45
+ folderId: a,
46
+ filename: r.source.filename ?? null
43
47
  } },
44
48
  refetchQueries: e
45
- })).data?.saveSandboxArtifactToFiles;
46
- if (!t) throw Error("saveSandboxArtifactToFiles returned no file");
47
- return f(100), t;
49
+ }), o = n.data?.saveSandboxArtifactToFiles;
50
+ if (!o) throw Error(t(n.error) ?? "saveSandboxArtifactToFiles returned no file");
51
+ return p(100), o;
48
52
  }
49
- let s = await n.source.getBlob();
50
- if (!s) throw Error("Cancelled: no bytes to save");
51
- let c = s.blob;
52
- return await t({
53
- client: r,
54
- blob: c,
55
- filename: s.filename ?? n.source.filename,
56
- mimeType: s.mimeType ?? n.source.mimeType,
57
- folderId: i,
58
- onProgress: f
53
+ let o = await r.source.getBlob();
54
+ if (!o) throw Error("Cancelled: no bytes to save");
55
+ let l = o.blob;
56
+ return await n({
57
+ client: i,
58
+ blob: l,
59
+ filename: o.filename ?? r.source.filename,
60
+ mimeType: o.mimeType ?? r.source.mimeType,
61
+ folderId: a,
62
+ onProgress: p
59
63
  });
60
64
  } catch (e) {
61
65
  let t = e instanceof Error ? e : Error(String(e));
62
- throw u(t), t;
66
+ throw d(t), t;
63
67
  } finally {
64
- c(!1);
68
+ l(!1);
65
69
  }
66
70
  },
67
- isPending: s,
68
- error: l,
69
- progress: d
71
+ isPending: o,
72
+ error: u,
73
+ progress: f
70
74
  };
71
75
  }
72
76
  //#endregion
73
- export { s as useSaveToFiles };
77
+ export { l as useSaveToFiles };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/fe-libs",
3
- "version": "2026.812.1",
3
+ "version": "2026.813.1",
4
4
  "description": "Burdenoff frontend primitives and domain libraries",
5
5
  "type": "module",
6
6
  "bin": {