@rslsp1/fa-app-tools 1.3.16 → 2.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.3.16",
3
+ "version": "2.0.12",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -18,13 +18,13 @@
18
18
  "scripts": {
19
19
  "prebuild": "node scripts/set-version.js",
20
20
  "build": "tsup src/index.ts --format esm,cjs --dts --clean --external react --external @xyflow/react --external motion/react",
21
- "prepublishOnly": "npm run build"
21
+ "prepublishOnly": "npm run build",
22
+ "test": "vitest run"
22
23
  },
23
24
  "peerDependencies": {
24
25
  "react": ">=18"
25
26
  },
26
27
  "dependencies": {
27
- "@huggingface/hub": "^2.13.0",
28
28
  "jszip": "^3.10.1"
29
29
  },
30
30
  "devDependencies": {
@@ -32,7 +32,8 @@
32
32
  "@xyflow/react": "^12.10.2",
33
33
  "motion": "^12.40.0",
34
34
  "tsup": "^8.0.0",
35
- "typescript": "^5.0.0"
35
+ "typescript": "^5.0.0",
36
+ "vitest": "^2.0.0"
36
37
  },
37
38
  "publishConfig": {
38
39
  "access": "public"
@@ -1,232 +0,0 @@
1
- // src/lib/hfStateService.ts
2
- var HF_BASE = "https://huggingface.co";
3
- var HF_REPO = "RolandSch/fa-app-state";
4
- var HF_TOKEN_KEY = "hf-token";
5
- function getHFToken() {
6
- try {
7
- return localStorage.getItem(HF_TOKEN_KEY);
8
- } catch {
9
- return null;
10
- }
11
- }
12
- function setHFToken(token) {
13
- try {
14
- localStorage.setItem(HF_TOKEN_KEY, token);
15
- } catch {
16
- }
17
- }
18
- async function hfListProjects(token) {
19
- const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/tree/main`, {
20
- headers: { Authorization: `Bearer ${token}` }
21
- });
22
- if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
23
- const files = await res.json();
24
- return files.filter((f) => f.type === "file" && f.path.endsWith(".zip")).map((f) => ({
25
- id: f.path.replace(/\.zip$/, ""),
26
- name: f.path.replace(/\.zip$/, ""),
27
- path: f.path,
28
- size: f.size,
29
- isLfs: !!f.lfs
30
- }));
31
- }
32
- async function hfDownloadProject(path, token) {
33
- const res = await fetch(
34
- `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${path}?download=true`,
35
- { headers: { Authorization: `Bearer ${token}` } }
36
- );
37
- if (!res.ok) throw new Error(`HF download failed: ${res.status} ${res.statusText}`);
38
- const blob = await res.blob();
39
- return new File([blob], path, { type: "application/zip" });
40
- }
41
- async function hfUploadProject(zipBase64, name, token) {
42
- const filename = name.endsWith(".zip") ? name : `${name}.zip`;
43
- const binary = atob(zipBase64);
44
- const bytes = new Uint8Array(binary.length);
45
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
46
- const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
47
- const oid = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
48
- const size = bytes.length;
49
- const sampleBytes = bytes.slice(0, 512);
50
- let sampleBinary = "";
51
- for (let i = 0; i < sampleBytes.length; i++) sampleBinary += String.fromCharCode(sampleBytes[i]);
52
- const sample = btoa(sampleBinary);
53
- const preRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/preupload/main`, {
54
- method: "POST",
55
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
56
- body: JSON.stringify({ files: [{ path: filename, size, sample }] })
57
- });
58
- if (!preRes.ok) throw new Error(`HF preupload failed: ${preRes.status} ${preRes.statusText}`);
59
- const preData = await preRes.json();
60
- const fileInfo = preData.files?.[0];
61
- const debugInfo = JSON.stringify({ uploadMode: fileInfo?.uploadMode, hasUploadUrl: !!fileInfo?.uploadUrl, hasVerifyUrl: !!fileInfo?.verifyUrl, headerKeys: Object.keys(fileInfo?.header || {}), verifyHeaderKeys: Object.keys(fileInfo?.verifyHeader || {}) });
62
- if (!fileInfo?.uploadMode) throw new Error(`HF preupload kein fileInfo: ${JSON.stringify(preData)}`);
63
- if (fileInfo.uploadMode === "lfs" && fileInfo.uploadUrl) {
64
- let uploadStatus = 0;
65
- try {
66
- const uploadRes = await fetch(fileInfo.uploadUrl, {
67
- method: "PUT",
68
- redirect: "follow",
69
- headers: { "Content-Type": "application/octet-stream", ...fileInfo.header || {} },
70
- body: bytes
71
- });
72
- uploadStatus = uploadRes.status;
73
- if (!uploadRes.ok) {
74
- const uploadErr = await uploadRes.text().catch(() => "");
75
- throw new Error(`HF LFS upload failed: ${uploadRes.status} \u2014 ${uploadErr.slice(0, 300)}`);
76
- }
77
- } catch (e) {
78
- if (uploadStatus === 0) throw new Error(`HF LFS upload network error (CORS/redirect?): ${e.message}`);
79
- throw e;
80
- }
81
- if (fileInfo.verifyUrl) {
82
- const verifyRes = await fetch(fileInfo.verifyUrl, {
83
- method: "POST",
84
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...fileInfo.verifyHeader || {} },
85
- body: JSON.stringify({ oid, size })
86
- });
87
- if (!verifyRes.ok) {
88
- const verifyErr = await verifyRes.text().catch(() => "");
89
- throw new Error(`HF LFS verify failed: ${verifyRes.status} \u2014 ${verifyErr.slice(0, 200)}`);
90
- }
91
- }
92
- }
93
- const commitRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
94
- method: "POST",
95
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
96
- body: [
97
- JSON.stringify({ key: "header", value: { summary: `Upload ${filename}`, description: "" } }),
98
- JSON.stringify({ key: "lfsFile", value: { path: filename, algo: "sha256", oid, size } })
99
- ].join("\n")
100
- });
101
- if (!commitRes.ok) {
102
- const err = await commitRes.text();
103
- throw new Error(`HF commit failed: ${commitRes.status} \u2014 ${err}`);
104
- }
105
- return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size, isLfs: true };
106
- }
107
- async function hfUploadProjectForm(zipBase64, name, token) {
108
- const filename = name.endsWith(".zip") ? name : `${name}.zip`;
109
- const binary = atob(zipBase64);
110
- const bytes = new Uint8Array(binary.length);
111
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
112
- const blob = new Blob([bytes], { type: "application/zip" });
113
- const { uploadFile } = await import("@huggingface/hub");
114
- await uploadFile({
115
- repo: { type: "dataset", name: HF_REPO },
116
- credentials: { accessToken: token },
117
- file: { path: filename, content: blob },
118
- branch: "main",
119
- commitTitle: `Upload ${filename}`
120
- });
121
- return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size: bytes.length, isLfs: true };
122
- }
123
- async function hfDeleteProject(path, token) {
124
- const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
125
- method: "POST",
126
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
127
- body: [
128
- JSON.stringify({ key: "header", value: { summary: `Delete ${path}`, description: "" } }),
129
- JSON.stringify({ key: "deletedFile", value: { path } })
130
- ].join("\n")
131
- });
132
- if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
133
- }
134
- async function hfSaveTags(workspaceTags, token) {
135
- const content = new Blob([JSON.stringify(workspaceTags, null, 2)], { type: "application/json" });
136
- const { uploadFile } = await import("@huggingface/hub");
137
- await uploadFile({
138
- repo: { type: "dataset", name: HF_REPO },
139
- credentials: { accessToken: token },
140
- file: { path: "tags.json", content },
141
- branch: "main",
142
- commitTitle: "Auto-sync tags"
143
- });
144
- }
145
- async function hfLoadTags(token) {
146
- try {
147
- const res = await fetch(
148
- `${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
149
- { headers: { Authorization: `Bearer ${token}` } }
150
- );
151
- if (!res.ok) return null;
152
- return res.json();
153
- } catch {
154
- return null;
155
- }
156
- }
157
- async function hfSaveMetadata(entries, token) {
158
- const content = new Blob([JSON.stringify(entries, null, 2)], { type: "application/json" });
159
- const { uploadFile } = await import("@huggingface/hub");
160
- await uploadFile({
161
- repo: { type: "dataset", name: HF_REPO },
162
- credentials: { accessToken: token },
163
- file: { path: "metadata.json", content },
164
- branch: "main",
165
- commitTitle: "Auto-sync metadata"
166
- });
167
- }
168
- async function hfLoadMetadata(token) {
169
- try {
170
- const res = await fetch(
171
- `${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
172
- { headers: { Authorization: `Bearer ${token}` } }
173
- );
174
- if (!res.ok) return [];
175
- return res.json();
176
- } catch {
177
- return [];
178
- }
179
- }
180
- async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
181
- const ext = mimeType === "image/png" ? "png" : "jpg";
182
- const binary = atob(base64);
183
- const bytes = new Uint8Array(binary.length);
184
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
185
- const content = new Blob([bytes], { type: mimeType });
186
- const { uploadFile } = await import("@huggingface/hub");
187
- await uploadFile({
188
- repo: { type: "dataset", name: HF_REPO },
189
- credentials: { accessToken: token },
190
- file: { path: `images/${id}.${ext}`, content },
191
- branch: "main",
192
- commitTitle: `Add image ${id}`
193
- });
194
- }
195
- async function hfLoadImageAsBase64(id, token) {
196
- for (const ext of ["jpg", "png"]) {
197
- try {
198
- const res = await fetch(
199
- `${HF_BASE}/datasets/${HF_REPO}/resolve/main/images/${id}.${ext}?download=true`,
200
- { headers: { Authorization: `Bearer ${token}` } }
201
- );
202
- if (!res.ok) continue;
203
- const blob = await res.blob();
204
- return new Promise((resolve, reject) => {
205
- const reader = new FileReader();
206
- reader.onload = () => resolve(reader.result.split(",")[1]);
207
- reader.onerror = reject;
208
- reader.readAsDataURL(blob);
209
- });
210
- } catch {
211
- continue;
212
- }
213
- }
214
- return null;
215
- }
216
-
217
- export {
218
- HF_TOKEN_KEY,
219
- getHFToken,
220
- setHFToken,
221
- hfListProjects,
222
- hfDownloadProject,
223
- hfUploadProject,
224
- hfUploadProjectForm,
225
- hfDeleteProject,
226
- hfSaveTags,
227
- hfLoadTags,
228
- hfSaveMetadata,
229
- hfLoadMetadata,
230
- hfUploadImage,
231
- hfLoadImageAsBase64
232
- };
@@ -1,32 +0,0 @@
1
- import {
2
- HF_TOKEN_KEY,
3
- getHFToken,
4
- hfDeleteProject,
5
- hfDownloadProject,
6
- hfListProjects,
7
- hfLoadImageAsBase64,
8
- hfLoadMetadata,
9
- hfLoadTags,
10
- hfSaveMetadata,
11
- hfSaveTags,
12
- hfUploadImage,
13
- hfUploadProject,
14
- hfUploadProjectForm,
15
- setHFToken
16
- } from "./chunk-X6S5BP36.mjs";
17
- export {
18
- HF_TOKEN_KEY,
19
- getHFToken,
20
- hfDeleteProject,
21
- hfDownloadProject,
22
- hfListProjects,
23
- hfLoadImageAsBase64,
24
- hfLoadMetadata,
25
- hfLoadTags,
26
- hfSaveMetadata,
27
- hfSaveTags,
28
- hfUploadImage,
29
- hfUploadProject,
30
- hfUploadProjectForm,
31
- setHFToken
32
- };