@rslsp1/fa-app-tools 1.3.18 → 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/dist/chunk-WCFXXLKN.mjs +409 -0
- package/dist/hfStateService-6YYT6ATO.mjs +54 -0
- package/dist/index.d.mts +103 -9
- package/dist/index.d.ts +103 -9
- package/dist/index.js +810 -297
- package/dist/index.mjs +573 -251
- package/package.json +5 -4
- package/dist/chunk-X6S5BP36.mjs +0 -232
- package/dist/hfStateService-B62RV5K3.mjs +0 -32
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
// src/lib/hfStateService.ts
|
|
2
|
+
import JSZip from "jszip";
|
|
3
|
+
|
|
4
|
+
// src/lib/hfEventTypes.ts
|
|
5
|
+
var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
|
|
6
|
+
|
|
7
|
+
// src/lib/hfStateService.ts
|
|
8
|
+
var HF_BASE = "https://huggingface.co";
|
|
9
|
+
var HF_REPO = "RolandSch/fa-app-state";
|
|
10
|
+
var HF_TOKEN_KEY = "hf-token";
|
|
11
|
+
function getHFToken() {
|
|
12
|
+
try {
|
|
13
|
+
return localStorage.getItem(HF_TOKEN_KEY);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function setHFToken(token) {
|
|
19
|
+
try {
|
|
20
|
+
localStorage.setItem(HF_TOKEN_KEY, token);
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function treeUrl(namespace, subdir = "") {
|
|
25
|
+
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
26
|
+
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
|
|
27
|
+
}
|
|
28
|
+
async function hfListDir(namespace, subdir, token) {
|
|
29
|
+
const res = await fetch(treeUrl(namespace, subdir), {
|
|
30
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
31
|
+
});
|
|
32
|
+
if (res.status === 404) return [];
|
|
33
|
+
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
34
|
+
return res.json();
|
|
35
|
+
}
|
|
36
|
+
async function hfDownloadJsonByPath(repoPath, token) {
|
|
37
|
+
const res = await fetch(
|
|
38
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
39
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
40
|
+
);
|
|
41
|
+
if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
async function hfDownloadBinaryByPath(repoPath, token) {
|
|
45
|
+
const res = await fetch(
|
|
46
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
47
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
48
|
+
);
|
|
49
|
+
if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
|
|
50
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
51
|
+
}
|
|
52
|
+
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
|
|
53
|
+
const bytes = new TextEncoder().encode(content);
|
|
54
|
+
let binary = "";
|
|
55
|
+
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
56
|
+
const b64 = btoa(binary);
|
|
57
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
60
|
+
body: [
|
|
61
|
+
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
62
|
+
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: b64 } })
|
|
63
|
+
].join("\n")
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
const err = await res.text().catch(() => "");
|
|
67
|
+
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function hfBatchArchive(moves, token, summary) {
|
|
71
|
+
const lines = [
|
|
72
|
+
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
73
|
+
...moves.flatMap(({ from, to, content }) => {
|
|
74
|
+
const bytes = new TextEncoder().encode(content);
|
|
75
|
+
let binary = "";
|
|
76
|
+
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
77
|
+
return [
|
|
78
|
+
JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: btoa(binary) } }),
|
|
79
|
+
JSON.stringify({ key: "deletedFile", value: { path: from } })
|
|
80
|
+
];
|
|
81
|
+
})
|
|
82
|
+
];
|
|
83
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
86
|
+
body: lines.join("\n")
|
|
87
|
+
});
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const err = await res.text().catch(() => "");
|
|
90
|
+
throw new Error(`HF batch archive failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function tsFromEventPath(repoPath) {
|
|
94
|
+
const filename = repoPath.split("/").pop() || "";
|
|
95
|
+
const iso = filename.replace(/_[^_]+\.json$/, "").replace(/-/g, (m, i) => i > 7 ? ":" : m);
|
|
96
|
+
const ts = Date.parse(iso.replace(/T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z/, "T$1:$2:$3.$4Z"));
|
|
97
|
+
return isNaN(ts) ? 0 : ts;
|
|
98
|
+
}
|
|
99
|
+
async function loadHFState(namespace, token) {
|
|
100
|
+
const files = await hfListDir(namespace, "", token);
|
|
101
|
+
const stateFiles = files.filter((f) => f.type === "file" && /state-[\dT\-]+Z\.zip$/.test(f.path.split("/").pop() || "")).sort((a, b) => b.path.localeCompare(a.path));
|
|
102
|
+
if (!stateFiles.length) return null;
|
|
103
|
+
const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
|
|
104
|
+
const zip = await JSZip.loadAsync(zipBytes);
|
|
105
|
+
const [metadataStr, tagsStr, metaStr] = await Promise.all([
|
|
106
|
+
zip.file("metadata.json")?.async("string"),
|
|
107
|
+
zip.file("tags.json")?.async("string"),
|
|
108
|
+
zip.file("state_meta.json")?.async("string")
|
|
109
|
+
]);
|
|
110
|
+
if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
|
|
111
|
+
return {
|
|
112
|
+
metadata: JSON.parse(metadataStr),
|
|
113
|
+
tags: JSON.parse(tagsStr),
|
|
114
|
+
meta: JSON.parse(metaStr)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async function loadPendingEvents(namespace, token, sinceTs) {
|
|
118
|
+
const files = await hfListDir(namespace, "events", token);
|
|
119
|
+
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs).sort((a, b) => a.path.localeCompare(b.path));
|
|
120
|
+
const events = await Promise.all(
|
|
121
|
+
pending.map((f) => hfDownloadJsonByPath(f.path, token))
|
|
122
|
+
);
|
|
123
|
+
return events;
|
|
124
|
+
}
|
|
125
|
+
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
126
|
+
function getSessionClientId() {
|
|
127
|
+
return SESSION_CLIENT_ID;
|
|
128
|
+
}
|
|
129
|
+
function tsToFilename(ts) {
|
|
130
|
+
return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
131
|
+
}
|
|
132
|
+
async function writeHFEvent(namespace, token, type, payload, prevTs) {
|
|
133
|
+
const ts = Date.now();
|
|
134
|
+
const uuid = crypto.randomUUID().slice(0, 8);
|
|
135
|
+
const event = {
|
|
136
|
+
v: CURRENT_EVENT_VERSION,
|
|
137
|
+
type,
|
|
138
|
+
ts,
|
|
139
|
+
prevTs,
|
|
140
|
+
clientId: SESSION_CLIENT_ID,
|
|
141
|
+
payload
|
|
142
|
+
};
|
|
143
|
+
const filename = `${tsToFilename(ts)}_${uuid}.json`;
|
|
144
|
+
const repoPath = `${namespace}events/${filename}`;
|
|
145
|
+
await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
|
|
146
|
+
return event;
|
|
147
|
+
}
|
|
148
|
+
async function hfBootstrapFromLegacy(namespace, token, onProgress) {
|
|
149
|
+
const log = (msg) => {
|
|
150
|
+
if (onProgress) onProgress(msg);
|
|
151
|
+
};
|
|
152
|
+
log("Lese tags.json \u2026");
|
|
153
|
+
const tags = await fetch(
|
|
154
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
|
|
155
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
156
|
+
).then((r) => r.ok ? r.json() : null).catch(() => null);
|
|
157
|
+
log("Lese metadata.json \u2026");
|
|
158
|
+
const metadata = await fetch(
|
|
159
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
|
|
160
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
161
|
+
).then((r) => r.ok ? r.json() : []).catch(() => []);
|
|
162
|
+
const snapshot = {
|
|
163
|
+
metadata: Array.isArray(metadata) ? metadata : [],
|
|
164
|
+
tags: tags?.by_category ? tags : { by_category: {}, all: [] },
|
|
165
|
+
meta: { consolidatedAt: Date.now(), version: 1 }
|
|
166
|
+
};
|
|
167
|
+
log(`Erstelle state.zip (${snapshot.metadata.length} Bilder, ${snapshot.tags.all?.length ?? 0} Tags) \u2026`);
|
|
168
|
+
const zip = new JSZip();
|
|
169
|
+
zip.file("metadata.json", JSON.stringify(snapshot.metadata, null, 2));
|
|
170
|
+
zip.file("tags.json", JSON.stringify(snapshot.tags, null, 2));
|
|
171
|
+
zip.file("state_meta.json", JSON.stringify(snapshot.meta, null, 2));
|
|
172
|
+
const zipBytes = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
173
|
+
const ts = snapshot.meta.consolidatedAt;
|
|
174
|
+
const stateFilename = `state-${new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-")}.zip`;
|
|
175
|
+
const repoPath = `${namespace}${stateFilename}`;
|
|
176
|
+
let binary = "";
|
|
177
|
+
zipBytes.forEach((b) => {
|
|
178
|
+
binary += String.fromCharCode(b);
|
|
179
|
+
});
|
|
180
|
+
const b64 = btoa(binary);
|
|
181
|
+
log("Lade state.zip hoch \u2026");
|
|
182
|
+
const commitRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
185
|
+
body: [
|
|
186
|
+
JSON.stringify({ key: "header", value: { summary: `Bootstrap: initialer State aus Legacy-Daten`, description: "" } }),
|
|
187
|
+
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: b64 } })
|
|
188
|
+
].join("\n")
|
|
189
|
+
});
|
|
190
|
+
if (!commitRes.ok) {
|
|
191
|
+
const err = await commitRes.text().catch(() => "");
|
|
192
|
+
throw new Error(`HF commit failed: ${commitRes.status} \u2014 ${err.slice(0, 300)}`);
|
|
193
|
+
}
|
|
194
|
+
log(`Fertig \u2014 ${stateFilename}`);
|
|
195
|
+
}
|
|
196
|
+
async function hfListProjects(token) {
|
|
197
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/tree/main`, {
|
|
198
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
199
|
+
});
|
|
200
|
+
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
201
|
+
const files = await res.json();
|
|
202
|
+
return files.filter((f) => f.type === "file" && f.path.endsWith(".zip")).map((f) => ({
|
|
203
|
+
id: f.path.replace(/\.zip$/, ""),
|
|
204
|
+
name: f.path.replace(/\.zip$/, ""),
|
|
205
|
+
path: f.path,
|
|
206
|
+
size: f.size,
|
|
207
|
+
isLfs: !!f.lfs
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
async function hfDownloadProject(path, token) {
|
|
211
|
+
const res = await fetch(
|
|
212
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${path}?download=true`,
|
|
213
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
214
|
+
);
|
|
215
|
+
if (!res.ok) throw new Error(`HF download failed: ${res.status} ${res.statusText}`);
|
|
216
|
+
const blob = await res.blob();
|
|
217
|
+
return new File([blob], path, { type: "application/zip" });
|
|
218
|
+
}
|
|
219
|
+
async function hfUploadProject(zipBase64, name, token) {
|
|
220
|
+
const filename = name.endsWith(".zip") ? name : `${name}.zip`;
|
|
221
|
+
const binary = atob(zipBase64);
|
|
222
|
+
const bytes = new Uint8Array(binary.length);
|
|
223
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
224
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
|
|
225
|
+
const oid = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
226
|
+
const size = bytes.length;
|
|
227
|
+
const sampleBytes = bytes.slice(0, 512);
|
|
228
|
+
let sampleBinary = "";
|
|
229
|
+
for (let i = 0; i < sampleBytes.length; i++) sampleBinary += String.fromCharCode(sampleBytes[i]);
|
|
230
|
+
const sample = btoa(sampleBinary);
|
|
231
|
+
const preRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/preupload/main`, {
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
234
|
+
body: JSON.stringify({ files: [{ path: filename, size, sample }] })
|
|
235
|
+
});
|
|
236
|
+
if (!preRes.ok) throw new Error(`HF preupload failed: ${preRes.status} ${preRes.statusText}`);
|
|
237
|
+
const preData = await preRes.json();
|
|
238
|
+
const fileInfo = preData.files?.[0];
|
|
239
|
+
if (!fileInfo?.uploadMode) throw new Error(`HF preupload kein fileInfo: ${JSON.stringify(preData)}`);
|
|
240
|
+
if (fileInfo.uploadMode === "lfs" && fileInfo.uploadUrl) {
|
|
241
|
+
let uploadStatus = 0;
|
|
242
|
+
try {
|
|
243
|
+
const uploadRes = await fetch(fileInfo.uploadUrl, {
|
|
244
|
+
method: "PUT",
|
|
245
|
+
redirect: "follow",
|
|
246
|
+
headers: { "Content-Type": "application/octet-stream", ...fileInfo.header || {} },
|
|
247
|
+
body: bytes
|
|
248
|
+
});
|
|
249
|
+
uploadStatus = uploadRes.status;
|
|
250
|
+
if (!uploadRes.ok) {
|
|
251
|
+
const uploadErr = await uploadRes.text().catch(() => "");
|
|
252
|
+
throw new Error(`HF LFS upload failed: ${uploadRes.status} \u2014 ${uploadErr.slice(0, 300)}`);
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {
|
|
255
|
+
if (uploadStatus === 0) throw new Error(`HF LFS upload network error (CORS/redirect?): ${e.message}`);
|
|
256
|
+
throw e;
|
|
257
|
+
}
|
|
258
|
+
if (fileInfo.verifyUrl) {
|
|
259
|
+
const verifyRes = await fetch(fileInfo.verifyUrl, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...fileInfo.verifyHeader || {} },
|
|
262
|
+
body: JSON.stringify({ oid, size })
|
|
263
|
+
});
|
|
264
|
+
if (!verifyRes.ok) {
|
|
265
|
+
const verifyErr = await verifyRes.text().catch(() => "");
|
|
266
|
+
throw new Error(`HF LFS verify failed: ${verifyRes.status} \u2014 ${verifyErr.slice(0, 200)}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const commitRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
273
|
+
body: [
|
|
274
|
+
JSON.stringify({ key: "header", value: { summary: `Upload ${filename}`, description: "" } }),
|
|
275
|
+
JSON.stringify({ key: "lfsFile", value: { path: filename, algo: "sha256", oid, size } })
|
|
276
|
+
].join("\n")
|
|
277
|
+
});
|
|
278
|
+
if (!commitRes.ok) {
|
|
279
|
+
const err = await commitRes.text();
|
|
280
|
+
throw new Error(`HF commit failed: ${commitRes.status} \u2014 ${err}`);
|
|
281
|
+
}
|
|
282
|
+
return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size, isLfs: true };
|
|
283
|
+
}
|
|
284
|
+
async function hfUploadProjectForm(_zipBase64, _name, _token) {
|
|
285
|
+
throw new Error("hfUploadProjectForm is deprecated. Use hfUploadProject instead.");
|
|
286
|
+
}
|
|
287
|
+
async function hfDeleteProject(path, token) {
|
|
288
|
+
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
289
|
+
method: "POST",
|
|
290
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
291
|
+
body: [
|
|
292
|
+
JSON.stringify({ key: "header", value: { summary: `Delete ${path}`, description: "" } }),
|
|
293
|
+
JSON.stringify({ key: "deletedFile", value: { path } })
|
|
294
|
+
].join("\n")
|
|
295
|
+
});
|
|
296
|
+
if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
|
|
297
|
+
}
|
|
298
|
+
async function hfSaveTags(_workspaceTags, _token) {
|
|
299
|
+
throw new Error("hfSaveTags is deprecated. Use writeHFEvent instead.");
|
|
300
|
+
}
|
|
301
|
+
async function hfLoadTags(token) {
|
|
302
|
+
try {
|
|
303
|
+
const res = await fetch(
|
|
304
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
|
|
305
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
306
|
+
);
|
|
307
|
+
if (!res.ok) return null;
|
|
308
|
+
return res.json();
|
|
309
|
+
} catch {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function hfSaveMetadata(_entries, _token) {
|
|
314
|
+
throw new Error("hfSaveMetadata is deprecated. Use writeHFEvent instead.");
|
|
315
|
+
}
|
|
316
|
+
async function hfLoadMetadata(token) {
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch(
|
|
319
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
|
|
320
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
321
|
+
);
|
|
322
|
+
if (!res.ok) return [];
|
|
323
|
+
return res.json();
|
|
324
|
+
} catch {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
|
|
329
|
+
const ext = mimeType === "image/png" ? "png" : "jpg";
|
|
330
|
+
const binary = atob(base64);
|
|
331
|
+
const bytes = new Uint8Array(binary.length);
|
|
332
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
333
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
|
|
334
|
+
const oid = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
335
|
+
const size = bytes.length;
|
|
336
|
+
const filename = `images/${id}.${ext}`;
|
|
337
|
+
const preRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/preupload/main`, {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
340
|
+
body: JSON.stringify({ files: [{ path: filename, size }] })
|
|
341
|
+
});
|
|
342
|
+
if (!preRes.ok) throw new Error(`HF preupload failed: ${preRes.status}`);
|
|
343
|
+
const preData = await preRes.json();
|
|
344
|
+
const fileInfo = preData.files?.[0];
|
|
345
|
+
if (fileInfo?.uploadUrl) {
|
|
346
|
+
await fetch(fileInfo.uploadUrl, {
|
|
347
|
+
method: "PUT",
|
|
348
|
+
headers: { "Content-Type": mimeType, ...fileInfo.header || {} },
|
|
349
|
+
body: bytes
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
355
|
+
body: [
|
|
356
|
+
JSON.stringify({ key: "header", value: { summary: `Add image ${id}`, description: "" } }),
|
|
357
|
+
JSON.stringify({ key: "lfsFile", value: { path: filename, algo: "sha256", oid, size } })
|
|
358
|
+
].join("\n")
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
async function hfLoadImageAsBase64(id, token) {
|
|
362
|
+
for (const ext of ["jpg", "png"]) {
|
|
363
|
+
try {
|
|
364
|
+
const res = await fetch(
|
|
365
|
+
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/images/${id}.${ext}?download=true`,
|
|
366
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
367
|
+
);
|
|
368
|
+
if (!res.ok) continue;
|
|
369
|
+
const blob = await res.blob();
|
|
370
|
+
return new Promise((resolve, reject) => {
|
|
371
|
+
const reader = new FileReader();
|
|
372
|
+
reader.onload = () => resolve(reader.result.split(",")[1]);
|
|
373
|
+
reader.onerror = reject;
|
|
374
|
+
reader.readAsDataURL(blob);
|
|
375
|
+
});
|
|
376
|
+
} catch {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export {
|
|
384
|
+
HF_TOKEN_KEY,
|
|
385
|
+
getHFToken,
|
|
386
|
+
setHFToken,
|
|
387
|
+
hfListDir,
|
|
388
|
+
hfDownloadJsonByPath,
|
|
389
|
+
hfDownloadBinaryByPath,
|
|
390
|
+
hfUploadSmallFile,
|
|
391
|
+
hfBatchArchive,
|
|
392
|
+
tsFromEventPath,
|
|
393
|
+
loadHFState,
|
|
394
|
+
loadPendingEvents,
|
|
395
|
+
getSessionClientId,
|
|
396
|
+
writeHFEvent,
|
|
397
|
+
hfBootstrapFromLegacy,
|
|
398
|
+
hfListProjects,
|
|
399
|
+
hfDownloadProject,
|
|
400
|
+
hfUploadProject,
|
|
401
|
+
hfUploadProjectForm,
|
|
402
|
+
hfDeleteProject,
|
|
403
|
+
hfSaveTags,
|
|
404
|
+
hfLoadTags,
|
|
405
|
+
hfSaveMetadata,
|
|
406
|
+
hfLoadMetadata,
|
|
407
|
+
hfUploadImage,
|
|
408
|
+
hfLoadImageAsBase64
|
|
409
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HF_TOKEN_KEY,
|
|
3
|
+
getHFToken,
|
|
4
|
+
getSessionClientId,
|
|
5
|
+
hfBatchArchive,
|
|
6
|
+
hfBootstrapFromLegacy,
|
|
7
|
+
hfDeleteProject,
|
|
8
|
+
hfDownloadBinaryByPath,
|
|
9
|
+
hfDownloadJsonByPath,
|
|
10
|
+
hfDownloadProject,
|
|
11
|
+
hfListDir,
|
|
12
|
+
hfListProjects,
|
|
13
|
+
hfLoadImageAsBase64,
|
|
14
|
+
hfLoadMetadata,
|
|
15
|
+
hfLoadTags,
|
|
16
|
+
hfSaveMetadata,
|
|
17
|
+
hfSaveTags,
|
|
18
|
+
hfUploadImage,
|
|
19
|
+
hfUploadProject,
|
|
20
|
+
hfUploadProjectForm,
|
|
21
|
+
hfUploadSmallFile,
|
|
22
|
+
loadHFState,
|
|
23
|
+
loadPendingEvents,
|
|
24
|
+
setHFToken,
|
|
25
|
+
tsFromEventPath,
|
|
26
|
+
writeHFEvent
|
|
27
|
+
} from "./chunk-WCFXXLKN.mjs";
|
|
28
|
+
export {
|
|
29
|
+
HF_TOKEN_KEY,
|
|
30
|
+
getHFToken,
|
|
31
|
+
getSessionClientId,
|
|
32
|
+
hfBatchArchive,
|
|
33
|
+
hfBootstrapFromLegacy,
|
|
34
|
+
hfDeleteProject,
|
|
35
|
+
hfDownloadBinaryByPath,
|
|
36
|
+
hfDownloadJsonByPath,
|
|
37
|
+
hfDownloadProject,
|
|
38
|
+
hfListDir,
|
|
39
|
+
hfListProjects,
|
|
40
|
+
hfLoadImageAsBase64,
|
|
41
|
+
hfLoadMetadata,
|
|
42
|
+
hfLoadTags,
|
|
43
|
+
hfSaveMetadata,
|
|
44
|
+
hfSaveTags,
|
|
45
|
+
hfUploadImage,
|
|
46
|
+
hfUploadProject,
|
|
47
|
+
hfUploadProjectForm,
|
|
48
|
+
hfUploadSmallFile,
|
|
49
|
+
loadHFState,
|
|
50
|
+
loadPendingEvents,
|
|
51
|
+
setHFToken,
|
|
52
|
+
tsFromEventPath,
|
|
53
|
+
writeHFEvent
|
|
54
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -310,12 +310,14 @@ interface AvatarArchitectAppProps {
|
|
|
310
310
|
onSelectMedia: () => Promise<MediaItem[]>;
|
|
311
311
|
buildInfo?: string;
|
|
312
312
|
initialHfToken?: string;
|
|
313
|
+
hfNamespace?: string;
|
|
314
|
+
allowDevNamespace?: boolean;
|
|
313
315
|
onFetchServerProjects?: () => Promise<ProjectMeta[]>;
|
|
314
316
|
onServerSave?: (zipBase64: string, name: string) => Promise<ProjectMeta>;
|
|
315
317
|
onServerLoad?: (id: string) => Promise<File>;
|
|
316
318
|
onServerDelete?: (id: string) => Promise<void>;
|
|
317
319
|
}
|
|
318
|
-
declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
|
|
320
|
+
declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
|
|
319
321
|
|
|
320
322
|
interface FaAppProps {
|
|
321
323
|
onGenerateImage: (params: Record<string, any>) => Promise<{
|
|
@@ -347,13 +349,15 @@ interface FaAppProps {
|
|
|
347
349
|
mediaId: string;
|
|
348
350
|
}>;
|
|
349
351
|
libToken?: string;
|
|
352
|
+
allowDevNamespace?: boolean;
|
|
353
|
+
serverBaseUrl?: string;
|
|
350
354
|
onFetchServerProjects?: () => Promise<ProjectMeta[]>;
|
|
351
355
|
onServerSave?: (zipBase64: string, name: string) => Promise<ProjectMeta>;
|
|
352
356
|
onServerLoad?: (id: string) => Promise<File>;
|
|
353
357
|
onServerDelete?: (id: string) => Promise<void>;
|
|
354
358
|
buildInfo?: string;
|
|
355
359
|
}
|
|
356
|
-
declare function FaApp({ onGenerateImage, onGeneratePrompt, onGenerateVideo: _onGenerateVideo, onDownload, onSelectMedia, onFlowSave: _onFlowSave, onFlowUpload: _onFlowUpload, onFlowMediaUpload: _onFlowMediaUpload, libToken, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete, buildInfo, }: FaAppProps): react_jsx_runtime.JSX.Element;
|
|
360
|
+
declare function FaApp({ onGenerateImage, onGeneratePrompt, onGenerateVideo: _onGenerateVideo, onDownload, onSelectMedia, onFlowSave: _onFlowSave, onFlowUpload: _onFlowUpload, onFlowMediaUpload: _onFlowMediaUpload, libToken, allowDevNamespace, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete, buildInfo, }: FaAppProps): react_jsx_runtime.JSX.Element;
|
|
357
361
|
|
|
358
362
|
interface CollapsibleCardProps {
|
|
359
363
|
title: string;
|
|
@@ -394,6 +398,7 @@ interface PromptTabProps {
|
|
|
394
398
|
declare const PromptTab: React.FC<PromptTabProps>;
|
|
395
399
|
|
|
396
400
|
interface ProjectSyncTabProps {
|
|
401
|
+
topSlot?: React.ReactNode;
|
|
397
402
|
onProjectExport?: () => Promise<void>;
|
|
398
403
|
onProjectImport?: (file: File) => Promise<void>;
|
|
399
404
|
onWorkspaceImport?: (file: File) => void;
|
|
@@ -498,6 +503,46 @@ declare function buildLoopInstruction(rounds: Array<{
|
|
|
498
503
|
declare function autoLabel(index: number): string;
|
|
499
504
|
declare function buildReferenceImageMediaIds(images: SelectedLabImage[]): string[];
|
|
500
505
|
|
|
506
|
+
interface HFEventVersion {
|
|
507
|
+
major: number;
|
|
508
|
+
minor: number;
|
|
509
|
+
}
|
|
510
|
+
interface HFEvent<P = unknown> {
|
|
511
|
+
v: HFEventVersion;
|
|
512
|
+
type: string;
|
|
513
|
+
ts: number;
|
|
514
|
+
prevTs: number[];
|
|
515
|
+
clientId: string;
|
|
516
|
+
payload: P;
|
|
517
|
+
}
|
|
518
|
+
interface HFStateMeta {
|
|
519
|
+
consolidatedAt: number;
|
|
520
|
+
version: number;
|
|
521
|
+
}
|
|
522
|
+
interface HFStateSnapshot {
|
|
523
|
+
metadata: HFMetadataEntry[];
|
|
524
|
+
tags: WorkspaceTags;
|
|
525
|
+
meta: HFStateMeta;
|
|
526
|
+
}
|
|
527
|
+
interface HFFileInfo$1 {
|
|
528
|
+
type: 'file' | 'directory';
|
|
529
|
+
path: string;
|
|
530
|
+
size: number;
|
|
531
|
+
}
|
|
532
|
+
interface ImageAddedPayload extends HFMetadataEntry {
|
|
533
|
+
}
|
|
534
|
+
interface TagUpsertedPayload {
|
|
535
|
+
category: string;
|
|
536
|
+
label: string;
|
|
537
|
+
value: string;
|
|
538
|
+
is_user_created?: boolean;
|
|
539
|
+
is_deleted?: boolean;
|
|
540
|
+
}
|
|
541
|
+
interface MetadataUpdatedPayload {
|
|
542
|
+
id: string;
|
|
543
|
+
delta: Partial<HFMetadataEntry>;
|
|
544
|
+
}
|
|
545
|
+
|
|
501
546
|
declare function getHFToken(): string | null;
|
|
502
547
|
declare function setHFToken(token: string): void;
|
|
503
548
|
interface HFProjectMeta {
|
|
@@ -507,17 +552,66 @@ interface HFProjectMeta {
|
|
|
507
552
|
size: number;
|
|
508
553
|
isLfs: boolean;
|
|
509
554
|
}
|
|
555
|
+
interface HFFileInfo {
|
|
556
|
+
type: 'file' | 'directory';
|
|
557
|
+
path: string;
|
|
558
|
+
size: number;
|
|
559
|
+
}
|
|
560
|
+
declare function hfListDir(namespace: string, subdir: string, token: string): Promise<HFFileInfo[]>;
|
|
561
|
+
declare function hfUploadSmallFile(repoPath: string, content: string, token: string, summary?: string): Promise<void>;
|
|
562
|
+
declare function hfBatchArchive(moves: Array<{
|
|
563
|
+
from: string;
|
|
564
|
+
to: string;
|
|
565
|
+
content: string;
|
|
566
|
+
}>, token: string, summary: string): Promise<void>;
|
|
567
|
+
declare function tsFromEventPath(repoPath: string): number;
|
|
568
|
+
declare function loadHFState(namespace: string, token: string): Promise<HFStateSnapshot | null>;
|
|
569
|
+
declare function loadPendingEvents(namespace: string, token: string, sinceTs: number): Promise<HFEvent[]>;
|
|
570
|
+
declare function getSessionClientId(): string;
|
|
571
|
+
declare function writeHFEvent<P>(namespace: string, token: string, type: string, payload: P, prevTs: number[]): Promise<HFEvent<P>>;
|
|
572
|
+
declare function hfBootstrapFromLegacy(namespace: string, token: string, onProgress?: (msg: string) => void): Promise<void>;
|
|
510
573
|
declare function hfListProjects(token: string): Promise<HFProjectMeta[]>;
|
|
511
574
|
declare function hfDownloadProject(path: string, token: string): Promise<File>;
|
|
512
|
-
declare function hfUploadProjectForm(
|
|
575
|
+
declare function hfUploadProjectForm(_zipBase64: string, _name: string, _token: string): Promise<HFProjectMeta>;
|
|
513
576
|
declare function hfDeleteProject(path: string, token: string): Promise<void>;
|
|
514
|
-
declare function hfSaveTags(workspaceTags: any, token: string): Promise<void>;
|
|
515
|
-
declare function hfLoadTags(token: string): Promise<any | null>;
|
|
516
|
-
declare function hfSaveMetadata(entries: any[], token: string): Promise<void>;
|
|
517
|
-
declare function hfLoadMetadata(token: string): Promise<any[]>;
|
|
518
577
|
declare function hfUploadImage(base64: string, id: string, token: string, mimeType?: string): Promise<void>;
|
|
519
578
|
declare function hfLoadImageAsBase64(id: string, token: string): Promise<string | null>;
|
|
520
579
|
|
|
521
|
-
|
|
580
|
+
interface HFStateResult {
|
|
581
|
+
state: HFStateSnapshot | null;
|
|
582
|
+
isLoading: boolean;
|
|
583
|
+
error: string | null;
|
|
584
|
+
pendingBufferCount: number;
|
|
585
|
+
eventCount: number;
|
|
586
|
+
forks: Array<{
|
|
587
|
+
parentTs: number;
|
|
588
|
+
childTs: number[];
|
|
589
|
+
}>;
|
|
590
|
+
allEvents: HFEvent[];
|
|
591
|
+
writeEvent: (type: string, payload: any) => Promise<void>;
|
|
592
|
+
refresh: () => Promise<void>;
|
|
593
|
+
lastEventTs: number;
|
|
594
|
+
hasStateZip: boolean;
|
|
595
|
+
}
|
|
596
|
+
declare function useHFState(token: string, namespace: string): HFStateResult;
|
|
597
|
+
|
|
598
|
+
declare function applyEvent(state: HFStateSnapshot, event: HFEvent): HFStateSnapshot;
|
|
599
|
+
declare function applyEvents(state: HFStateSnapshot, events: HFEvent[]): HFStateSnapshot;
|
|
600
|
+
|
|
601
|
+
interface DagNode {
|
|
602
|
+
event: HFEvent;
|
|
603
|
+
children: number[];
|
|
604
|
+
}
|
|
605
|
+
interface DagFork {
|
|
606
|
+
parentTs: number;
|
|
607
|
+
childTs: number[];
|
|
608
|
+
}
|
|
609
|
+
type Dag = Map<number, DagNode>;
|
|
610
|
+
declare function buildDag(events: HFEvent[]): Dag;
|
|
611
|
+
declare function findTips(dag: Dag): number[];
|
|
612
|
+
declare function findForks(dag: Dag): DagFork[];
|
|
613
|
+
declare function topoSort(events: HFEvent[]): HFEvent[];
|
|
614
|
+
|
|
615
|
+
declare const LIB_VERSION = "2.0.12";
|
|
522
616
|
|
|
523
|
-
export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaApp, type FaAppProps, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFMetadataEntry, HistoryPanel, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type WorkspaceTags, autoLabel, buildBlendInstruction, buildCompareInstruction, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken,
|
|
617
|
+
export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type DagFork, type ExtractedCharacter, FaApp, type FaAppProps, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFEvent, type HFEventVersion, type HFFileInfo$1 as HFFileInfo, type HFMetadataEntry, type HFStateMeta, type HFStateResult, type HFStateSnapshot, HistoryPanel, type ImageAddedPayload, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, type MetadataUpdatedPayload, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type TagUpsertedPayload, type WorkspaceTags, applyEvent, applyEvents, autoLabel, buildBlendInstruction, buildCompareInstruction, buildDag, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, findForks, findTips, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken, getSessionClientId, groupGenerationsToLabItems, hfBatchArchive, hfBootstrapFromLegacy, hfDeleteProject, hfDownloadProject, hfListDir, hfListProjects, hfLoadImageAsBase64, hfUploadImage, hfUploadProjectForm, hfUploadSmallFile, importProjectFromZip, injectXMPMetadata, interpretSdkError, loadHFState, loadPendingEvents, parsePromptFile, parsePromptResponse, setHFToken, topoSort, tsFromEventPath, useHFState, useKeyboardNavigation, useOnClickOutside, writeHFEvent };
|