@burdenoff/microfe-files 2026.1014.2 → 2026.1014.3
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 +17 -0
- package/dist/files/hooks/useUploadFile.d.ts.map +1 -1
- package/dist/files/hooks/useUploadFile.js +42 -36
- package/dist/files/hooks/useUploadFile.js.map +1 -1
- package/dist/files/utils/uploadDigest.d.ts +3 -0
- package/dist/files/utils/uploadDigest.d.ts.map +1 -0
- package/dist/files/utils/uploadDigest.js +23 -0
- package/dist/files/utils/uploadDigest.js.map +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -7,6 +7,23 @@ Part of the [Burdenoff](https://burdenoff.com) platform by
|
|
|
7
7
|
|
|
8
8
|
See `AGENTS.md` for development commands and architecture details.
|
|
9
9
|
|
|
10
|
+
## Native upload integrity
|
|
11
|
+
|
|
12
|
+
The native uploader hashes the exact selected bytes before creating an upload
|
|
13
|
+
session, using incremental SHA-256 with reads of at most 1 MiB. It sends the same
|
|
14
|
+
client declaration at initiation and completion. Read/hash failures and pauses
|
|
15
|
+
during hashing prevent initiation; existing native upload and quota failures do
|
|
16
|
+
not trigger automatic replay.
|
|
17
|
+
|
|
18
|
+
Hashing uses `@noble/hashes` and does not require secure-context Web Crypto, so
|
|
19
|
+
the local HTTP development host remains supported. It adds a read pass over the
|
|
20
|
+
file, without allocating a whole-file checksum buffer. The Files backend owns
|
|
21
|
+
the distinction between a client-declared digest and a storage-verified checksum;
|
|
22
|
+
the browser declaration alone does not certify storage integrity.
|
|
23
|
+
|
|
24
|
+
`bun run test` exercises independent byte vectors, bounded multi-chunk reads,
|
|
25
|
+
native mutation inputs, quota failures and cancellation without network requests.
|
|
26
|
+
|
|
10
27
|
## License
|
|
11
28
|
|
|
12
29
|
Proprietary — Copyright Burdenoff Consultancy Services Pvt. Ltd.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUploadFile.d.ts","sourceRoot":"","sources":["../../../src/files/hooks/useUploadFile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useUploadFile.d.ts","sourceRoot":"","sources":["../../../src/files/hooks/useUploadFile.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,MAAM,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,aAAa;0BA4BW;QACjC,IAAI,EAAE,IAAI,CAAC;QACX,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;KACzC;;;;;CA4KF,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useFilesContext as e } from "../providers/FilesProvider.js";
|
|
2
2
|
import { useCompleteUploadMutation as t, useInitiateUploadMutation as n } from "../../generated/wspace-operations.js";
|
|
3
3
|
import { isQuotaExhaustedError as r } from "../utils/quotaUtils.js";
|
|
4
|
-
import {
|
|
4
|
+
import { computeUploadSha256 as i } from "../utils/uploadDigest.js";
|
|
5
|
+
import { useCallback as a, useRef as o, useState as s } from "react";
|
|
5
6
|
//#region src/files/hooks/useUploadFile.ts
|
|
6
|
-
var
|
|
7
|
-
let { workspaceId:
|
|
7
|
+
var c = () => {
|
|
8
|
+
let { workspaceId: c } = e(), [l, u] = s(!1), [d, f] = s(null), p = o(null), m = o(!1), [h] = n(), [g] = t({ refetchQueries: [
|
|
8
9
|
"GetFilesRootBrowsePage",
|
|
9
10
|
"GetFilesFolderBrowsePage",
|
|
10
11
|
"GetFilesRecentPage",
|
|
@@ -12,32 +13,36 @@ var s = () => {
|
|
|
12
13
|
] });
|
|
13
14
|
return {
|
|
14
15
|
mutateAsync: async (e) => {
|
|
15
|
-
let { file: t, folderId: n, onProgress:
|
|
16
|
+
let { file: t, folderId: n, onProgress: a } = e, o = t.name.split(".").pop() || "";
|
|
16
17
|
try {
|
|
17
|
-
|
|
18
|
-
let e = await
|
|
18
|
+
u(!0), f(null), m.current = !1, a?.(0);
|
|
19
|
+
let e = await i(t, () => m.current);
|
|
20
|
+
if (m.current) throw Error("Upload paused");
|
|
21
|
+
let s = await h({ variables: { input: {
|
|
19
22
|
fileName: t.name,
|
|
20
23
|
fileSize: t.size.toString(),
|
|
21
24
|
mimeType: t.type || "application/octet-stream",
|
|
22
|
-
extension:
|
|
25
|
+
extension: o,
|
|
26
|
+
sha256Hash: e,
|
|
23
27
|
folderId: n || null
|
|
24
28
|
} } });
|
|
25
|
-
if (
|
|
26
|
-
if (r(
|
|
27
|
-
let
|
|
28
|
-
throw Error(
|
|
29
|
+
if (s.error) {
|
|
30
|
+
if (r(s.error)) throw s.error;
|
|
31
|
+
let e = s.error.message || "";
|
|
32
|
+
throw Error(e || "Failed to initiate upload");
|
|
29
33
|
}
|
|
30
|
-
let
|
|
31
|
-
if (!
|
|
32
|
-
if (
|
|
34
|
+
let c = s.data?.initiateUpload, l = c?.session, d = c?.uploadUrl;
|
|
35
|
+
if (!l || !d) throw Error("Failed to initiate upload session");
|
|
36
|
+
if (m.current) throw Error("Upload paused");
|
|
37
|
+
if (a?.(10), !(await new Promise((e, n) => {
|
|
33
38
|
let r = new XMLHttpRequest();
|
|
34
|
-
|
|
35
|
-
if (e.lengthComputable && !
|
|
39
|
+
p.current = r, r.upload.addEventListener("progress", (e) => {
|
|
40
|
+
if (e.lengthComputable && !m.current) {
|
|
36
41
|
let t = 10 + Math.round(e.loaded / e.total * 80);
|
|
37
|
-
|
|
42
|
+
a?.(t);
|
|
38
43
|
}
|
|
39
44
|
}), r.addEventListener("load", () => {
|
|
40
|
-
r.status >= 200 && r.status < 300 ? (
|
|
45
|
+
r.status >= 200 && r.status < 300 ? (m.current || a?.(90), e(new Response(r.response, {
|
|
41
46
|
status: r.status,
|
|
42
47
|
statusText: r.statusText,
|
|
43
48
|
headers: new Headers(r.getAllResponseHeaders().split("\r\n").reduce((e, t) => {
|
|
@@ -46,37 +51,38 @@ var s = () => {
|
|
|
46
51
|
}, {}))
|
|
47
52
|
}))) : n(/* @__PURE__ */ Error(`Upload failed with status ${r.status}`));
|
|
48
53
|
}), r.addEventListener("error", () => {
|
|
49
|
-
|
|
54
|
+
m.current || n(/* @__PURE__ */ Error("Upload failed"));
|
|
50
55
|
}), r.addEventListener("abort", () => {
|
|
51
|
-
|
|
52
|
-
}), r.open("PUT",
|
|
56
|
+
m.current ? n(/* @__PURE__ */ Error("Upload paused")) : n(/* @__PURE__ */ Error("Upload aborted"));
|
|
57
|
+
}), r.open("PUT", d), r.setRequestHeader("Content-Type", t.type), r.send(t);
|
|
53
58
|
})).ok) throw Error("Failed to upload file to S3");
|
|
54
|
-
if (
|
|
55
|
-
let
|
|
56
|
-
sessionId:
|
|
59
|
+
if (m.current) throw Error("Upload paused");
|
|
60
|
+
let _ = await g({ variables: { input: {
|
|
61
|
+
sessionId: l.id,
|
|
57
62
|
fileSize: t.size.toString(),
|
|
63
|
+
sha256Hash: e,
|
|
58
64
|
folderId: n || null
|
|
59
65
|
} } });
|
|
60
|
-
if (
|
|
61
|
-
let
|
|
62
|
-
if (!
|
|
63
|
-
return
|
|
66
|
+
if (_.error) throw r(_.error) ? _.error : (_.error.message, Error(_.error.message || "Failed to complete upload"));
|
|
67
|
+
let v = _.data?.completeUpload?.file;
|
|
68
|
+
if (!v) throw Error("Upload completed but file data was not returned. The file may not have been created properly.");
|
|
69
|
+
return a?.(100), v;
|
|
64
70
|
} catch (e) {
|
|
65
71
|
let t = e instanceof Error ? e : /* @__PURE__ */ Error("Upload failed");
|
|
66
|
-
throw
|
|
72
|
+
throw f(t), t;
|
|
67
73
|
} finally {
|
|
68
|
-
|
|
74
|
+
u(!1), p.current = null;
|
|
69
75
|
}
|
|
70
76
|
},
|
|
71
|
-
isPending:
|
|
72
|
-
error:
|
|
73
|
-
pause:
|
|
74
|
-
|
|
77
|
+
isPending: l,
|
|
78
|
+
error: d,
|
|
79
|
+
pause: a(() => {
|
|
80
|
+
m.current || (m.current = !0, p.current?.abort(), p.current = null);
|
|
75
81
|
}, []),
|
|
76
|
-
isPaused:
|
|
82
|
+
isPaused: a(() => m.current, [])
|
|
77
83
|
};
|
|
78
84
|
};
|
|
79
85
|
//#endregion
|
|
80
|
-
export {
|
|
86
|
+
export { c as useUploadFile };
|
|
81
87
|
|
|
82
88
|
//# sourceMappingURL=useUploadFile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUploadFile.js","names":[],"sources":["../../../src/files/hooks/useUploadFile.ts"],"sourcesContent":["import { useState, useRef, useCallback } from \"react\";\nimport {\n useInitiateUploadMutation,\n useCompleteUploadMutation,\n} from \"../../generated/wspace-operations\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { isQuotaExhaustedError } from \"../utils/quotaUtils\";\n\nexport interface InitiateUploadInput {\n fileName: string;\n fileSize: number;\n mimeType: string;\n folderId?: string;\n}\n\nexport interface CompleteUploadInput {\n sessionId: string;\n fileSize: string;\n}\n\nexport interface UploadController {\n pause: () => void;\n isPaused: () => boolean;\n}\n\nexport const useUploadFile = () => {\n const { workspaceId } = useFilesContext();\n const [isUploading, setIsUploading] = useState(false);\n const [uploadError, setUploadError] = useState<Error | null>(null);\n\n const activeXhrRef = useRef<XMLHttpRequest | null>(null);\n const isPausedRef = useRef(false);\n\n const [initiateUploadMutation] = useInitiateUploadMutation();\n const [completeUploadMutation] = useCompleteUploadMutation({\n refetchQueries: [\n \"GetFilesRootBrowsePage\",\n \"GetFilesFolderBrowsePage\",\n \"GetFilesRecentPage\",\n \"GetFiles\",\n ],\n });\n\n const pause = useCallback(() => {\n if (
|
|
1
|
+
{"version":3,"file":"useUploadFile.js","names":[],"sources":["../../../src/files/hooks/useUploadFile.ts"],"sourcesContent":["import { useState, useRef, useCallback } from \"react\";\nimport {\n useInitiateUploadMutation,\n useCompleteUploadMutation,\n} from \"../../generated/wspace-operations\";\nimport { useFilesContext } from \"../providers/FilesProvider\";\nimport { isQuotaExhaustedError } from \"../utils/quotaUtils\";\nimport { computeUploadSha256 } from \"../utils/uploadDigest\";\n\nexport interface InitiateUploadInput {\n fileName: string;\n fileSize: number;\n mimeType: string;\n folderId?: string;\n}\n\nexport interface CompleteUploadInput {\n sessionId: string;\n fileSize: string;\n}\n\nexport interface UploadController {\n pause: () => void;\n isPaused: () => boolean;\n}\n\nexport const useUploadFile = () => {\n const { workspaceId } = useFilesContext();\n const [isUploading, setIsUploading] = useState(false);\n const [uploadError, setUploadError] = useState<Error | null>(null);\n\n const activeXhrRef = useRef<XMLHttpRequest | null>(null);\n const isPausedRef = useRef(false);\n\n const [initiateUploadMutation] = useInitiateUploadMutation();\n const [completeUploadMutation] = useCompleteUploadMutation({\n refetchQueries: [\n \"GetFilesRootBrowsePage\",\n \"GetFilesFolderBrowsePage\",\n \"GetFilesRecentPage\",\n \"GetFiles\",\n ],\n });\n\n const pause = useCallback(() => {\n if (!isPausedRef.current) {\n isPausedRef.current = true;\n activeXhrRef.current?.abort();\n activeXhrRef.current = null;\n }\n }, []);\n\n const isPaused = useCallback(() => isPausedRef.current, []);\n\n const mutateAsync = async (params: {\n file: File;\n folderId?: string;\n onProgress?: (progress: number) => void;\n }) => {\n const { file, folderId, onProgress } = params;\n const extension = file.name.split(\".\").pop() || \"\";\n\n try {\n setIsUploading(true);\n setUploadError(null);\n isPausedRef.current = false;\n\n onProgress?.(0);\n\n // Declare the same exact-byte digest before creating a session and on completion.\n // The Files owner separately records whether storage verified this declaration.\n const sha256Hash = await computeUploadSha256(\n file,\n () => isPausedRef.current,\n );\n if (isPausedRef.current) {\n throw new Error(\"Upload paused\");\n }\n\n const initiateResult = await initiateUploadMutation({\n variables: {\n input: {\n fileName: file.name,\n fileSize: file.size.toString(),\n mimeType: file.type || \"application/octet-stream\",\n extension: extension,\n sha256Hash,\n folderId: folderId || null,\n },\n },\n });\n\n // Check for GraphQL errors FIRST - before checking data\n // This ensures backend error messages are properly surfaced to the user\n if (initiateResult.error) {\n if (isQuotaExhaustedError(initiateResult.error)) {\n throw initiateResult.error;\n }\n const errorMsg = initiateResult.error.message || \"\";\n throw new Error(errorMsg || \"Failed to initiate upload\");\n }\n\n const result = initiateResult.data?.initiateUpload;\n const session = result?.session;\n const uploadUrl = result?.uploadUrl;\n\n if (!session || !uploadUrl) {\n throw new Error(\"Failed to initiate upload session\");\n }\n\n if (isPausedRef.current) {\n throw new Error(\"Upload paused\");\n }\n\n onProgress?.(10);\n\n const uploadResponse = await new Promise<Response>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n activeXhrRef.current = xhr;\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable && !isPausedRef.current) {\n const percentComplete = 10 + Math.round((e.loaded / e.total) * 80);\n onProgress?.(percentComplete);\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n if (!isPausedRef.current) {\n onProgress?.(90);\n }\n resolve(\n new Response(xhr.response, {\n status: xhr.status,\n statusText: xhr.statusText,\n headers: new Headers(\n xhr\n .getAllResponseHeaders()\n .split(\"\\r\\n\")\n .reduce(\n (acc, current) => {\n const parts = current.split(\": \");\n if (parts[0]) acc[parts[0]] = parts[1] || \"\";\n return acc;\n },\n {} as Record<string, string>,\n ),\n ),\n }),\n );\n } else {\n reject(new Error(`Upload failed with status ${xhr.status}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n if (!isPausedRef.current) {\n reject(new Error(\"Upload failed\"));\n }\n });\n\n xhr.addEventListener(\"abort\", () => {\n if (isPausedRef.current) {\n reject(new Error(\"Upload paused\"));\n } else {\n reject(new Error(\"Upload aborted\"));\n }\n });\n\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type);\n xhr.send(file);\n });\n\n if (!uploadResponse.ok) {\n throw new Error(\"Failed to upload file to S3\");\n }\n\n if (isPausedRef.current) {\n throw new Error(\"Upload paused\");\n }\n\n const completeResult = await completeUploadMutation({\n variables: {\n input: {\n sessionId: session.id,\n fileSize: file.size.toString(),\n sha256Hash,\n folderId: folderId || null,\n },\n },\n });\n\n if (completeResult.error) {\n if (isQuotaExhaustedError(completeResult.error)) {\n throw completeResult.error;\n }\n const errorMsg = completeResult.error.message || \"\";\n throw new Error(\n completeResult.error.message || \"Failed to complete upload\",\n );\n }\n\n const uploadedFile = completeResult.data?.completeUpload?.file;\n if (!uploadedFile) {\n throw new Error(\n \"Upload completed but file data was not returned. The file may not have been created properly.\",\n );\n }\n\n onProgress?.(100);\n return uploadedFile;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(\"Upload failed\");\n setUploadError(err);\n throw err;\n } finally {\n setIsUploading(false);\n activeXhrRef.current = null;\n }\n };\n\n return {\n mutateAsync,\n isPending: isUploading,\n error: uploadError,\n pause,\n isPaused,\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadDigest.d.ts","sourceRoot":"","sources":["../../../src/files/utils/uploadDigest.ts"],"names":[],"mappings":"AAIA,yFAAyF;AACzF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,IAAI,EACV,WAAW,GAAE,MAAM,OAAqB,GACvC,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { sha256 as e } from "@noble/hashes/sha2.js";
|
|
2
|
+
//#region src/files/utils/uploadDigest.ts
|
|
3
|
+
var t = 1024 * 1024;
|
|
4
|
+
async function n(n, r = () => !1) {
|
|
5
|
+
let i = e.create();
|
|
6
|
+
try {
|
|
7
|
+
if (r()) throw Error("Upload paused");
|
|
8
|
+
for (let e = 0; e < n.size; e += t) {
|
|
9
|
+
if (r()) throw Error("Upload paused");
|
|
10
|
+
let a = Math.min(e + t, n.size), o = await n.slice(e, a).arrayBuffer();
|
|
11
|
+
if (r()) throw Error("Upload paused");
|
|
12
|
+
if (o.byteLength !== a - e) throw Error("Unable to read complete file bytes");
|
|
13
|
+
i.update(new Uint8Array(o));
|
|
14
|
+
}
|
|
15
|
+
return Array.from(i.digest(), (e) => e.toString(16).padStart(2, "0")).join("");
|
|
16
|
+
} finally {
|
|
17
|
+
i.destroy();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { n as computeUploadSha256 };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=uploadDigest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploadDigest.js","names":[],"sources":["../../../src/files/utils/uploadDigest.ts"],"sourcesContent":["import { sha256 } from \"@noble/hashes/sha2.js\";\n\nconst CHUNK_BYTES = 1024 * 1024;\n\n/** Hash selected bytes with bounded reads; declaration alone does not verify storage. */\nexport async function computeUploadSha256(\n file: Blob,\n isCancelled: () => boolean = () => false,\n): Promise<string> {\n const hash = sha256.create();\n try {\n if (isCancelled()) throw new Error(\"Upload paused\");\n for (let offset = 0; offset < file.size; offset += CHUNK_BYTES) {\n if (isCancelled()) throw new Error(\"Upload paused\");\n const end = Math.min(offset + CHUNK_BYTES, file.size);\n const bytes = await file.slice(offset, end).arrayBuffer();\n if (isCancelled()) throw new Error(\"Upload paused\");\n if (bytes.byteLength !== end - offset) {\n throw new Error(\"Unable to read complete file bytes\");\n }\n hash.update(new Uint8Array(bytes));\n }\n return Array.from(hash.digest(), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n } finally {\n hash.destroy();\n }\n}\n"],"mappings":";;AAEA,IAAM,IAAc,OAAO;AAG3B,eAAsB,EACpB,GACA,UAAmC,IAClB;CACjB,IAAM,IAAO,EAAO,QAAQ;AAC5B,KAAI;AACF,MAAI,GAAa,CAAE,OAAU,MAAM,gBAAgB;AACnD,OAAK,IAAI,IAAS,GAAG,IAAS,EAAK,MAAM,KAAU,GAAa;AAC9D,OAAI,GAAa,CAAE,OAAU,MAAM,gBAAgB;GACnD,IAAM,IAAM,KAAK,IAAI,IAAS,GAAa,EAAK,KAAK,EAC/C,IAAQ,MAAM,EAAK,MAAM,GAAQ,EAAI,CAAC,aAAa;AACzD,OAAI,GAAa,CAAE,OAAU,MAAM,gBAAgB;AACnD,OAAI,EAAM,eAAe,IAAM,EAC7B,OAAU,MAAM,qCAAqC;AAEvD,KAAK,OAAO,IAAI,WAAW,EAAM,CAAC;;AAEpC,SAAO,MAAM,KAAK,EAAK,QAAQ,GAAG,MAChC,EAAK,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CACnC,CAAC,KAAK,GAAG;WACF;AACR,IAAK,SAAS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-files",
|
|
3
|
-
"version": "2026.1014.
|
|
3
|
+
"version": "2026.1014.3",
|
|
4
4
|
"description": "Burdenoff Files Microfrontend - Standalone file management module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"imports": {
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
42
42
|
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
|
|
43
43
|
"type:check": "bash scripts/build-limits.sh tsc --noEmit",
|
|
44
|
-
"
|
|
44
|
+
"test": "bun test tests",
|
|
45
|
+
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run lint:sanity && bun run format && bun run type:check && bun run test && bun run build'",
|
|
45
46
|
"clean": "rm -rf dist node_modules src/generated",
|
|
46
47
|
"bump": "bun run scripts/bump-version.ts",
|
|
47
48
|
"deps:status": "bun scripts/deps-mode.ts status",
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@apollo/client": "^4.0.13",
|
|
53
54
|
"@burdenoff/fe-libs": "2026.803.4",
|
|
55
|
+
"@noble/hashes": "2.4.0",
|
|
54
56
|
"@tanstack/react-query": "^5.90.17",
|
|
55
57
|
"graphql": "^16.12.0",
|
|
56
58
|
"lucide-react": "^0.468.0",
|
|
@@ -123,6 +125,7 @@
|
|
|
123
125
|
"yaml": ">=2.8.3",
|
|
124
126
|
"picomatch": ">=2.3.2",
|
|
125
127
|
"immutable": ">=3.8.3",
|
|
126
|
-
"minimatch": ">=3.1.3"
|
|
128
|
+
"minimatch": ">=3.1.3",
|
|
129
|
+
"shell-quote": "^1.11.0"
|
|
127
130
|
}
|
|
128
131
|
}
|