@rslsp1/fa-app-tools 5.1.0 → 7.0.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.
- package/dist/{chunk-3MUI4ZV7.mjs → chunk-B2YV4TNI.mjs} +914 -76
- package/dist/{hfEventTypes-Dkfrcd_t.d.mts → hfEventTypes-BUztJySs.d.mts} +1 -11
- package/dist/{hfEventTypes-C_PX6RFR.d.ts → hfEventTypes-fAdA-0A9.d.ts} +1 -11
- package/dist/index.d.mts +18 -49
- package/dist/index.d.ts +18 -49
- package/dist/index.js +1221 -1548
- package/dist/index.mjs +208 -671
- package/dist/szenen.d.mts +197 -4
- package/dist/szenen.d.ts +197 -4
- package/dist/szenen.js +697 -115
- package/dist/szenen.mjs +63 -4
- package/package.json +1 -1
- package/dist/chunk-NBUS7AWR.mjs +0 -418
- package/dist/chunk-UFXDXENC.mjs +0 -218
- package/dist/hfStateService-W7KO2FOH.mjs +0 -56
- package/dist/project-O4ORKXY5.mjs +0 -8
package/dist/szenen.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CHAIN_EDGES,
|
|
5
5
|
CHAIN_LINK_FIELDS,
|
|
6
6
|
CapabilityAnchorError,
|
|
7
|
+
DEFAULT_SEED_VORLAGE,
|
|
7
8
|
DERIVABLE_TYPES,
|
|
8
9
|
ELEMENT_VERSION_SCHEMA_FILE,
|
|
9
10
|
FLOW_EXECUTION_PATH,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
SZENEN_SCHEMAS,
|
|
23
24
|
SzenenView,
|
|
24
25
|
UNCHECKED_PUBLISH_RULES,
|
|
26
|
+
UPLOAD_MAX_BYTES,
|
|
25
27
|
UnsupportedSchemaError,
|
|
26
28
|
acceptProposalTitle,
|
|
27
29
|
areasFor,
|
|
@@ -99,8 +101,10 @@ import {
|
|
|
99
101
|
findSceneDerivationRecovery,
|
|
100
102
|
firstError,
|
|
101
103
|
formatSchemaErrors,
|
|
104
|
+
formatSeedListe,
|
|
102
105
|
freezeDraftChain,
|
|
103
106
|
freezeSceneWorkingState,
|
|
107
|
+
gesamtHerkunft,
|
|
104
108
|
getSzenenSchemas,
|
|
105
109
|
gruppiereZuItems,
|
|
106
110
|
hasPlacementCapability,
|
|
@@ -108,16 +112,21 @@ import {
|
|
|
108
112
|
isOk,
|
|
109
113
|
isProjectionCurrent,
|
|
110
114
|
isSzenenItemId,
|
|
115
|
+
istZipDatei,
|
|
111
116
|
itemIdForRun,
|
|
112
117
|
ladeVorschauen,
|
|
118
|
+
leseBilderAusZip,
|
|
113
119
|
linkFor,
|
|
114
120
|
locatorTeile,
|
|
115
121
|
logRevisionOf,
|
|
116
122
|
mergeSzenenBatchOrigin,
|
|
123
|
+
mitDefaultVorlage,
|
|
117
124
|
noticeFor,
|
|
118
125
|
ordersForPromptRun,
|
|
126
|
+
ordneSeedsZu,
|
|
119
127
|
parseDerivationResponse,
|
|
120
128
|
parseRunResponse,
|
|
129
|
+
parseSeedListe,
|
|
121
130
|
preferredHeadRevisionOf,
|
|
122
131
|
promptDiagnosticsOf,
|
|
123
132
|
publishedHeadsOf,
|
|
@@ -137,6 +146,7 @@ import {
|
|
|
137
146
|
runSceneDerivation,
|
|
138
147
|
sceneOnlyCapabilities,
|
|
139
148
|
scopeOfPath,
|
|
149
|
+
seedFehlerText,
|
|
140
150
|
selectableTargetModels,
|
|
141
151
|
showChain,
|
|
142
152
|
simulatedAnswerFor,
|
|
@@ -148,7 +158,7 @@ import {
|
|
|
148
158
|
validateElementVersion,
|
|
149
159
|
validatePayload,
|
|
150
160
|
withReference
|
|
151
|
-
} from "./chunk-
|
|
161
|
+
} from "./chunk-B2YV4TNI.mjs";
|
|
152
162
|
import {
|
|
153
163
|
ContractViolation,
|
|
154
164
|
STYLE_PAYLOAD,
|
|
@@ -162,7 +172,6 @@ import {
|
|
|
162
172
|
preferredVersionId,
|
|
163
173
|
resolvePreferredHead
|
|
164
174
|
} from "./chunk-FUY2OCVC.mjs";
|
|
165
|
-
import "./chunk-NBUS7AWR.mjs";
|
|
166
175
|
|
|
167
176
|
// src/lib/szenen/providerCheckStore.ts
|
|
168
177
|
function collectProviderChecks(events) {
|
|
@@ -824,11 +833,16 @@ function createMemoryViewPort(options = {}) {
|
|
|
824
833
|
const neueBilder = input.images.map((bild, offset) => ({
|
|
825
834
|
media_ref: `${input.generationBatchId}-${startPosition + offset}`,
|
|
826
835
|
position: startPosition + offset,
|
|
836
|
+
generation_batch_id: input.generationBatchId,
|
|
827
837
|
model: bild.model ?? null,
|
|
828
838
|
seed: bild.seed ?? null,
|
|
829
839
|
width: bild.width ?? null,
|
|
830
840
|
height: bild.height ?? null
|
|
831
841
|
}));
|
|
842
|
+
const importOrigins = [
|
|
843
|
+
...vorhandenesItem?.origins ?? [],
|
|
844
|
+
{ generation_batch_id: input.generationBatchId, source_kind: "external_import" }
|
|
845
|
+
];
|
|
832
846
|
const neu = {
|
|
833
847
|
item_id: vorhandenesItem?.item_id ?? `szn-mem-batch-${input.generationBatchId}`,
|
|
834
848
|
prompt_run_id: input.runId,
|
|
@@ -836,7 +850,8 @@ function createMemoryViewPort(options = {}) {
|
|
|
836
850
|
...vorhandenesItem?.generation_batch_ids ?? [],
|
|
837
851
|
input.generationBatchId
|
|
838
852
|
],
|
|
839
|
-
source_kind:
|
|
853
|
+
source_kind: gesamtHerkunft(importOrigins),
|
|
854
|
+
origins: importOrigins,
|
|
840
855
|
title: vorhandenesItem?.title ?? input.title ?? `Batch aus Lauf ${input.runId}`,
|
|
841
856
|
images: [...vorhandenesItem?.images ?? [], ...neueBilder]
|
|
842
857
|
};
|
|
@@ -890,11 +905,16 @@ function createMemoryViewPort(options = {}) {
|
|
|
890
905
|
const neueBilder = input.images.map((bild, offset) => ({
|
|
891
906
|
media_ref: `${input.generationBatchId}-${startPosition + offset}`,
|
|
892
907
|
position: startPosition + offset,
|
|
908
|
+
generation_batch_id: input.generationBatchId,
|
|
893
909
|
model: bild.model ?? null,
|
|
894
910
|
seed: bild.seed ?? null,
|
|
895
911
|
width: bild.width ?? null,
|
|
896
912
|
height: bild.height ?? null
|
|
897
913
|
}));
|
|
914
|
+
const genOrigins = [
|
|
915
|
+
...vorhandenesItem?.origins ?? [],
|
|
916
|
+
{ generation_batch_id: input.generationBatchId, source_kind: "generated_in_app" }
|
|
917
|
+
];
|
|
898
918
|
const neu = {
|
|
899
919
|
item_id: vorhandenesItem?.item_id ?? `szn-mem-gen-${input.generationBatchId}`,
|
|
900
920
|
prompt_run_id: input.runId,
|
|
@@ -902,7 +922,8 @@ function createMemoryViewPort(options = {}) {
|
|
|
902
922
|
...vorhandenesItem?.generation_batch_ids ?? [],
|
|
903
923
|
input.generationBatchId
|
|
904
924
|
],
|
|
905
|
-
source_kind:
|
|
925
|
+
source_kind: gesamtHerkunft(genOrigins),
|
|
926
|
+
origins: genOrigins,
|
|
906
927
|
title: vorhandenesItem?.title ?? `Batch aus Lauf ${input.runId}`,
|
|
907
928
|
images: [...vorhandenesItem?.images ?? [], ...neueBilder]
|
|
908
929
|
};
|
|
@@ -933,6 +954,31 @@ function createMemoryViewPort(options = {}) {
|
|
|
933
954
|
}
|
|
934
955
|
}
|
|
935
956
|
});
|
|
957
|
+
const seedVorlagen = [...options.seedTemplates ?? []];
|
|
958
|
+
let seedVorlagenZaehler = 0;
|
|
959
|
+
const seedTemplatePort = {
|
|
960
|
+
async listSeedTemplates() {
|
|
961
|
+
if (options.seedTemplatesFehler) {
|
|
962
|
+
return { kind: "error", message: "Vorlagenbestand nicht lesbar (Simulation).", retryable: true };
|
|
963
|
+
}
|
|
964
|
+
return ok(seedVorlagen);
|
|
965
|
+
},
|
|
966
|
+
async saveSeedTemplate(input) {
|
|
967
|
+
const vorlage = {
|
|
968
|
+
id: `seedtpl-${seedVorlagenZaehler += 1}`,
|
|
969
|
+
name: input.name,
|
|
970
|
+
seeds: input.seeds
|
|
971
|
+
};
|
|
972
|
+
seedVorlagen.push(vorlage);
|
|
973
|
+
return done(vorlage);
|
|
974
|
+
},
|
|
975
|
+
async deleteSeedTemplate(id) {
|
|
976
|
+
const index = seedVorlagen.findIndex((v) => String(v.id) === String(id));
|
|
977
|
+
if (index < 0) return { kind: "error", message: `Seed-Vorlage nicht gefunden: ${id}`, retryable: false };
|
|
978
|
+
seedVorlagen.splice(index, 1);
|
|
979
|
+
return done(null);
|
|
980
|
+
}
|
|
981
|
+
};
|
|
936
982
|
const port = {
|
|
937
983
|
elements,
|
|
938
984
|
context: {
|
|
@@ -950,6 +996,9 @@ function createMemoryViewPort(options = {}) {
|
|
|
950
996
|
if (options.derivation !== false) port.derivation = derivationPort;
|
|
951
997
|
if (options.imageBatchHistory !== false) port.imageBatchHistory = imageBatchHistoryPort;
|
|
952
998
|
if (options.imageBatchWrite !== false) port.imageBatchWrite = imageBatchWritePort;
|
|
999
|
+
if (options.seedTemplates !== void 0 || options.seedTemplatesFehler) {
|
|
1000
|
+
port.seedTemplates = seedTemplatePort;
|
|
1001
|
+
}
|
|
953
1002
|
if (options.imageGeneration !== false) port.imageGeneration = imageGenerationPort;
|
|
954
1003
|
return port;
|
|
955
1004
|
}
|
|
@@ -1598,6 +1647,7 @@ export {
|
|
|
1598
1647
|
CHAIN_LINK_FIELDS,
|
|
1599
1648
|
CapabilityAnchorError,
|
|
1600
1649
|
ContractViolation,
|
|
1650
|
+
DEFAULT_SEED_VORLAGE,
|
|
1601
1651
|
DERIVABLE_TYPES,
|
|
1602
1652
|
ELEMENT_VERSION_SCHEMA_FILE,
|
|
1603
1653
|
FLOW_EXECUTION_PATH,
|
|
@@ -1617,6 +1667,7 @@ export {
|
|
|
1617
1667
|
SZENEN_SCHEMAS,
|
|
1618
1668
|
SzenenView,
|
|
1619
1669
|
UNCHECKED_PUBLISH_RULES,
|
|
1670
|
+
UPLOAD_MAX_BYTES,
|
|
1620
1671
|
UnsupportedSchemaError,
|
|
1621
1672
|
VersionDagError,
|
|
1622
1673
|
areasFor,
|
|
@@ -1705,8 +1756,10 @@ export {
|
|
|
1705
1756
|
findSceneDerivationRecovery,
|
|
1706
1757
|
firstError,
|
|
1707
1758
|
formatSchemaErrors,
|
|
1759
|
+
formatSeedListe,
|
|
1708
1760
|
freezeDraftChain,
|
|
1709
1761
|
freezeSceneWorkingState,
|
|
1762
|
+
gesamtHerkunft,
|
|
1710
1763
|
getSzenenSchemas,
|
|
1711
1764
|
gruppiereZuItems,
|
|
1712
1765
|
hasPlacementCapability,
|
|
@@ -1714,16 +1767,21 @@ export {
|
|
|
1714
1767
|
isOk,
|
|
1715
1768
|
isProjectionCurrent,
|
|
1716
1769
|
isSzenenItemId,
|
|
1770
|
+
istZipDatei,
|
|
1717
1771
|
itemIdForRun,
|
|
1718
1772
|
ladeVorschauen,
|
|
1773
|
+
leseBilderAusZip,
|
|
1719
1774
|
linkFor,
|
|
1720
1775
|
locatorTeile,
|
|
1721
1776
|
logRevisionOf,
|
|
1722
1777
|
mergeSzenenBatchOrigin,
|
|
1778
|
+
mitDefaultVorlage,
|
|
1723
1779
|
noticeFor,
|
|
1724
1780
|
ordersForPromptRun,
|
|
1781
|
+
ordneSeedsZu,
|
|
1725
1782
|
parseDerivationResponse,
|
|
1726
1783
|
parseRunResponse,
|
|
1784
|
+
parseSeedListe,
|
|
1727
1785
|
preferredHeadRevisionOf,
|
|
1728
1786
|
preferredVersionId,
|
|
1729
1787
|
promptDiagnosticsOf,
|
|
@@ -1747,6 +1805,7 @@ export {
|
|
|
1747
1805
|
runStoreDifferential,
|
|
1748
1806
|
sceneOnlyCapabilities,
|
|
1749
1807
|
scopeOfPath,
|
|
1808
|
+
seedFehlerText,
|
|
1750
1809
|
selectableTargetModels,
|
|
1751
1810
|
showChain,
|
|
1752
1811
|
simulatedAnswerFor,
|
package/package.json
CHANGED
package/dist/chunk-NBUS7AWR.mjs
DELETED
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
// src/lib/hfStateService.ts
|
|
2
|
-
import JSZip from "jszip";
|
|
3
|
-
import { uploadFile } from "@huggingface/hub";
|
|
4
|
-
|
|
5
|
-
// src/lib/hfEventTypes.ts
|
|
6
|
-
var HF_BASE = "https://huggingface.co";
|
|
7
|
-
var HF_REPO = "RolandSch/fa-app-state";
|
|
8
|
-
var CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
|
|
9
|
-
var SPEICHERBARE_BILDTYPEN = ["image/png", "image/jpeg", "image/webp"];
|
|
10
|
-
function bildEndungFuer(mimeType) {
|
|
11
|
-
if (mimeType === "image/png") return "png";
|
|
12
|
-
if (mimeType === "image/webp") return "webp";
|
|
13
|
-
return "jpg";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// src/lib/hfEventLog.ts
|
|
17
|
-
function treeUrl(namespace, subdir = "") {
|
|
18
|
-
const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
|
|
19
|
-
return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
|
|
20
|
-
}
|
|
21
|
-
var DROSSEL_VERSUCHE = 4;
|
|
22
|
-
var DROSSEL_BASIS_MS = 400;
|
|
23
|
-
var schlafen = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
24
|
-
var HF_LESE_ZEITLIMIT_MS = 2e4;
|
|
25
|
-
async function holeMitZeitlimit(url, init) {
|
|
26
|
-
const abbruch = new AbortController();
|
|
27
|
-
const timer = setTimeout(() => abbruch.abort(), HF_LESE_ZEITLIMIT_MS);
|
|
28
|
-
try {
|
|
29
|
-
return await fetch(url, { ...init, signal: abbruch.signal });
|
|
30
|
-
} catch (error) {
|
|
31
|
-
if (abbruch.signal.aborted) {
|
|
32
|
-
throw new Error(`HF-Anfrage ohne Antwort nach ${HF_LESE_ZEITLIMIT_MS}ms abgebrochen: ${url}`);
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
} finally {
|
|
36
|
-
clearTimeout(timer);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
async function mitGeduld(anfrage) {
|
|
40
|
-
let letzte = null;
|
|
41
|
-
for (let versuch = 0; versuch < DROSSEL_VERSUCHE; versuch += 1) {
|
|
42
|
-
const res = await anfrage();
|
|
43
|
-
if (res.status !== 429 && res.status < 500) return res;
|
|
44
|
-
letzte = res;
|
|
45
|
-
if (versuch === DROSSEL_VERSUCHE - 1) break;
|
|
46
|
-
const angesagt = Number(res.headers.get("retry-after"));
|
|
47
|
-
const warten = Number.isFinite(angesagt) && angesagt > 0 ? angesagt * 1e3 : DROSSEL_BASIS_MS * 2 ** versuch;
|
|
48
|
-
await schlafen(warten);
|
|
49
|
-
}
|
|
50
|
-
return letzte;
|
|
51
|
-
}
|
|
52
|
-
async function hfListDir(namespace, subdir, token) {
|
|
53
|
-
const res = await mitGeduld(() => holeMitZeitlimit(treeUrl(namespace, subdir), {
|
|
54
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
55
|
-
}));
|
|
56
|
-
if (res.status === 404) return [];
|
|
57
|
-
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
58
|
-
return res.json();
|
|
59
|
-
}
|
|
60
|
-
async function hfDownloadJsonByPath(repoPath, token) {
|
|
61
|
-
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
62
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
63
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
64
|
-
));
|
|
65
|
-
if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
|
|
66
|
-
return res.json();
|
|
67
|
-
}
|
|
68
|
-
async function hfDownloadBinaryByPath(repoPath, token) {
|
|
69
|
-
const res = await mitGeduld(() => holeMitZeitlimit(
|
|
70
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
71
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
72
|
-
));
|
|
73
|
-
if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
|
|
74
|
-
return new Uint8Array(await res.arrayBuffer());
|
|
75
|
-
}
|
|
76
|
-
function toBase64(text) {
|
|
77
|
-
const bytes = new TextEncoder().encode(text);
|
|
78
|
-
let binary = "";
|
|
79
|
-
bytes.forEach((b) => binary += String.fromCharCode(b));
|
|
80
|
-
return btoa(binary);
|
|
81
|
-
}
|
|
82
|
-
async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
|
|
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: [
|
|
87
|
-
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
88
|
-
JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: toBase64(content) } })
|
|
89
|
-
].join("\n")
|
|
90
|
-
});
|
|
91
|
-
if (!res.ok) {
|
|
92
|
-
const err = await res.text().catch(() => "");
|
|
93
|
-
throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async function hfBatchArchive(moves, token, summary) {
|
|
97
|
-
const lines = [
|
|
98
|
-
JSON.stringify({ key: "header", value: { summary, description: "" } }),
|
|
99
|
-
...moves.flatMap(({ from, to, content }) => [
|
|
100
|
-
JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: toBase64(content) } }),
|
|
101
|
-
JSON.stringify({ key: "deletedFile", value: { path: from } })
|
|
102
|
-
])
|
|
103
|
-
];
|
|
104
|
-
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
105
|
-
method: "POST",
|
|
106
|
-
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
107
|
-
body: lines.join("\n")
|
|
108
|
-
});
|
|
109
|
-
if (!res.ok) {
|
|
110
|
-
const err = await res.text().catch(() => "");
|
|
111
|
-
throw new Error(`HF batch archive failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function tsFromEventPath(repoPath) {
|
|
115
|
-
const filename = repoPath.split("/").pop() || "";
|
|
116
|
-
const base = filename.replace(/_[^_]+\.json$/, "");
|
|
117
|
-
const iso = base.replace(/T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, "T$1:$2:$3.$4Z");
|
|
118
|
-
const ts = Date.parse(iso);
|
|
119
|
-
return isNaN(ts) ? 0 : ts;
|
|
120
|
-
}
|
|
121
|
-
var EVENTDOWNLOAD_PARALLELITAET = 3;
|
|
122
|
-
async function mitBegrenzterParallelitaet(count, limit, task) {
|
|
123
|
-
let cursor = 0;
|
|
124
|
-
let ersterFehler;
|
|
125
|
-
let fehlerAufgetreten = false;
|
|
126
|
-
const worker = async () => {
|
|
127
|
-
while (cursor < count) {
|
|
128
|
-
const index = cursor++;
|
|
129
|
-
try {
|
|
130
|
-
await task(index);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
if (!fehlerAufgetreten) {
|
|
133
|
-
fehlerAufgetreten = true;
|
|
134
|
-
ersterFehler = error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
await Promise.all(Array.from({ length: Math.min(limit, count) }, worker));
|
|
140
|
-
if (fehlerAufgetreten) throw ersterFehler;
|
|
141
|
-
}
|
|
142
|
-
async function loadPendingEvents(namespace, token, sinceTs, knownPaths) {
|
|
143
|
-
const files = await hfListDir(namespace, "events", token);
|
|
144
|
-
const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs && !knownPaths?.has(f.path)).sort((a, b) => a.path.localeCompare(b.path));
|
|
145
|
-
if (!pending.length) return [];
|
|
146
|
-
const raw = new Array(pending.length);
|
|
147
|
-
await mitBegrenzterParallelitaet(pending.length, EVENTDOWNLOAD_PARALLELITAET, async (index) => {
|
|
148
|
-
raw[index] = await hfDownloadJsonByPath(pending[index].path, token);
|
|
149
|
-
});
|
|
150
|
-
for (const f of pending) knownPaths?.add(f.path);
|
|
151
|
-
return raw.flatMap((e) => Array.isArray(e) ? e : [e]);
|
|
152
|
-
}
|
|
153
|
-
async function listEventPaths(namespace, token) {
|
|
154
|
-
const files = await hfListDir(namespace, "events", token);
|
|
155
|
-
return files.filter((f) => f.type === "file").map((f) => f.path).sort((a, b) => a.localeCompare(b));
|
|
156
|
-
}
|
|
157
|
-
var SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
|
|
158
|
-
function getSessionClientId() {
|
|
159
|
-
return SESSION_CLIENT_ID;
|
|
160
|
-
}
|
|
161
|
-
function tsToFilename(ts) {
|
|
162
|
-
return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
|
|
163
|
-
}
|
|
164
|
-
var writeSeq = 0;
|
|
165
|
-
var lastOrderedTs = 0;
|
|
166
|
-
async function writeHFEvent(namespace, token, type, payload, prevTs, options) {
|
|
167
|
-
const wallTs = Date.now();
|
|
168
|
-
const ts = options?.ordered ? Math.max(wallTs, lastOrderedTs) : wallTs;
|
|
169
|
-
if (options?.ordered) lastOrderedTs = ts;
|
|
170
|
-
const uuid = crypto.randomUUID().slice(0, 8);
|
|
171
|
-
const event = {
|
|
172
|
-
v: CURRENT_EVENT_VERSION,
|
|
173
|
-
type,
|
|
174
|
-
ts,
|
|
175
|
-
prevTs,
|
|
176
|
-
clientId: SESSION_CLIENT_ID,
|
|
177
|
-
payload
|
|
178
|
-
};
|
|
179
|
-
const suffix = options?.ordered ? `${String(++writeSeq % 1e6).padStart(6, "0")}${uuid}` : uuid;
|
|
180
|
-
const filename = `${tsToFilename(ts)}_${suffix}.json`;
|
|
181
|
-
const repoPath = `${namespace}events/${filename}`;
|
|
182
|
-
await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
|
|
183
|
-
return event;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// src/lib/hfStateService.ts
|
|
187
|
-
var HF_TOKEN_KEY = "hf-token";
|
|
188
|
-
function getHFToken() {
|
|
189
|
-
try {
|
|
190
|
-
return localStorage.getItem(HF_TOKEN_KEY);
|
|
191
|
-
} catch {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function setHFToken(token) {
|
|
196
|
-
try {
|
|
197
|
-
localStorage.setItem(HF_TOKEN_KEY, token);
|
|
198
|
-
} catch {
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
async function loadHFState(namespace, token) {
|
|
202
|
-
const files = await hfListDir(namespace, "", token);
|
|
203
|
-
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));
|
|
204
|
-
if (!stateFiles.length) return null;
|
|
205
|
-
const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
|
|
206
|
-
const zip = await JSZip.loadAsync(zipBytes);
|
|
207
|
-
const [metadataStr, tagsStr, metaStr, openTasksStr] = await Promise.all([
|
|
208
|
-
zip.file("metadata.json")?.async("string"),
|
|
209
|
-
zip.file("tags.json")?.async("string"),
|
|
210
|
-
zip.file("state_meta.json")?.async("string"),
|
|
211
|
-
zip.file("open_tasks.json")?.async("string")
|
|
212
|
-
// optional — ältere Snapshots haben es nicht
|
|
213
|
-
]);
|
|
214
|
-
if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
|
|
215
|
-
return {
|
|
216
|
-
metadata: JSON.parse(metadataStr),
|
|
217
|
-
tags: JSON.parse(tagsStr),
|
|
218
|
-
meta: JSON.parse(metaStr),
|
|
219
|
-
openTasks: openTasksStr ? JSON.parse(openTasksStr) : {}
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
async function hfBootstrapFromLegacy(namespace, token, onProgress) {
|
|
223
|
-
const log = (msg) => {
|
|
224
|
-
if (onProgress) onProgress(msg);
|
|
225
|
-
};
|
|
226
|
-
log("Lese tags.json \u2026");
|
|
227
|
-
const tags = await fetch(
|
|
228
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
|
|
229
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
230
|
-
).then((r) => r.ok ? r.json() : null).catch(() => null);
|
|
231
|
-
log("Lese metadata.json \u2026");
|
|
232
|
-
const metadata = await fetch(
|
|
233
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
|
|
234
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
235
|
-
).then((r) => r.ok ? r.json() : []).catch(() => []);
|
|
236
|
-
const snapshot = {
|
|
237
|
-
metadata: Array.isArray(metadata) ? metadata : [],
|
|
238
|
-
tags: tags?.by_category ? tags : { by_category: {}, all: [] },
|
|
239
|
-
meta: { consolidatedAt: Date.now(), version: 1 }
|
|
240
|
-
};
|
|
241
|
-
log(`Erstelle state.zip (${snapshot.metadata.length} Bilder, ${snapshot.tags.all?.length ?? 0} Tags) \u2026`);
|
|
242
|
-
const zip = new JSZip();
|
|
243
|
-
zip.file("metadata.json", JSON.stringify(snapshot.metadata, null, 2));
|
|
244
|
-
zip.file("tags.json", JSON.stringify(snapshot.tags, null, 2));
|
|
245
|
-
zip.file("state_meta.json", JSON.stringify(snapshot.meta, null, 2));
|
|
246
|
-
const zipBytes = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
247
|
-
const ts = snapshot.meta.consolidatedAt;
|
|
248
|
-
const stateFilename = `state-${new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-")}.zip`;
|
|
249
|
-
const repoPath = `${namespace}${stateFilename}`;
|
|
250
|
-
log("Lade state.zip hoch \u2026");
|
|
251
|
-
await uploadFile({
|
|
252
|
-
repo: { type: "dataset", name: HF_REPO },
|
|
253
|
-
credentials: { accessToken: token },
|
|
254
|
-
file: { path: repoPath, content: new Blob([zipBytes.buffer], { type: "application/zip" }) },
|
|
255
|
-
branch: "main",
|
|
256
|
-
commitTitle: `Bootstrap: initialer State aus Legacy-Daten`
|
|
257
|
-
});
|
|
258
|
-
log(`Fertig \u2014 ${stateFilename}`);
|
|
259
|
-
}
|
|
260
|
-
async function hfListProjects(token) {
|
|
261
|
-
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/tree/main`, {
|
|
262
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
263
|
-
});
|
|
264
|
-
if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
|
|
265
|
-
const files = await res.json();
|
|
266
|
-
return files.filter((f) => f.type === "file" && f.path.endsWith(".zip")).map((f) => ({
|
|
267
|
-
id: f.path.replace(/\.zip$/, ""),
|
|
268
|
-
name: f.path.replace(/\.zip$/, ""),
|
|
269
|
-
path: f.path,
|
|
270
|
-
size: f.size,
|
|
271
|
-
isLfs: !!f.lfs
|
|
272
|
-
}));
|
|
273
|
-
}
|
|
274
|
-
async function hfDownloadProject(path, token) {
|
|
275
|
-
const res = await fetch(
|
|
276
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${path}?download=true`,
|
|
277
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
278
|
-
);
|
|
279
|
-
if (!res.ok) throw new Error(`HF download failed: ${res.status} ${res.statusText}`);
|
|
280
|
-
const blob = await res.blob();
|
|
281
|
-
return new File([blob], path, { type: "application/zip" });
|
|
282
|
-
}
|
|
283
|
-
async function hfUploadProject(zipBase64, name, token) {
|
|
284
|
-
const filename = name.endsWith(".zip") ? name : `${name}.zip`;
|
|
285
|
-
const binary = atob(zipBase64);
|
|
286
|
-
const bytes = new Uint8Array(binary.length);
|
|
287
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
288
|
-
await uploadFile({
|
|
289
|
-
repo: { type: "dataset", name: HF_REPO },
|
|
290
|
-
credentials: { accessToken: token },
|
|
291
|
-
file: { path: filename, content: new Blob([bytes], { type: "application/zip" }) },
|
|
292
|
-
branch: "main",
|
|
293
|
-
commitTitle: `Upload ${filename}`
|
|
294
|
-
});
|
|
295
|
-
return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size: bytes.length, isLfs: true };
|
|
296
|
-
}
|
|
297
|
-
async function hfUploadProjectForm(_zipBase64, _name, _token) {
|
|
298
|
-
throw new Error("hfUploadProjectForm is deprecated. Use hfUploadProject instead.");
|
|
299
|
-
}
|
|
300
|
-
async function hfDeleteProject(path, token) {
|
|
301
|
-
const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
|
|
302
|
-
method: "POST",
|
|
303
|
-
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
|
|
304
|
-
body: [
|
|
305
|
-
JSON.stringify({ key: "header", value: { summary: `Delete ${path}`, description: "" } }),
|
|
306
|
-
JSON.stringify({ key: "deletedFile", value: { path } })
|
|
307
|
-
].join("\n")
|
|
308
|
-
});
|
|
309
|
-
if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
|
|
310
|
-
}
|
|
311
|
-
async function hfSaveTags(_workspaceTags, _token) {
|
|
312
|
-
throw new Error("hfSaveTags is deprecated. Use writeHFEvent instead.");
|
|
313
|
-
}
|
|
314
|
-
async function hfLoadTags(token) {
|
|
315
|
-
try {
|
|
316
|
-
const res = await fetch(
|
|
317
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
|
|
318
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
319
|
-
);
|
|
320
|
-
if (!res.ok) return null;
|
|
321
|
-
return res.json();
|
|
322
|
-
} catch {
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
async function hfSaveMetadata(_entries, _token) {
|
|
327
|
-
throw new Error("hfSaveMetadata is deprecated. Use writeHFEvent instead.");
|
|
328
|
-
}
|
|
329
|
-
async function hfLoadMetadata(token) {
|
|
330
|
-
try {
|
|
331
|
-
const res = await fetch(
|
|
332
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
|
|
333
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
334
|
-
);
|
|
335
|
-
if (!res.ok) return [];
|
|
336
|
-
return res.json();
|
|
337
|
-
} catch {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
|
|
342
|
-
const ext = bildEndungFuer(mimeType);
|
|
343
|
-
const binary = atob(base64);
|
|
344
|
-
const bytes = new Uint8Array(binary.length);
|
|
345
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
346
|
-
await uploadFile({
|
|
347
|
-
repo: { type: "dataset", name: HF_REPO },
|
|
348
|
-
credentials: { accessToken: token },
|
|
349
|
-
file: { path: `images/${id}.${ext}`, content: new Blob([bytes], { type: mimeType }) },
|
|
350
|
-
branch: "main",
|
|
351
|
-
commitTitle: `Add image ${id}`
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
async function hfLoadImageAsBase64(id, token, namespace, filename, on404, mimeType, hasThumb) {
|
|
355
|
-
const fetchBase64 = async (repoPath) => {
|
|
356
|
-
try {
|
|
357
|
-
const res = await fetch(
|
|
358
|
-
`${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
|
|
359
|
-
{ headers: { Authorization: `Bearer ${token}` } }
|
|
360
|
-
);
|
|
361
|
-
if (!res.ok) {
|
|
362
|
-
if (on404 && on404()) return "ABORT";
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
const blob = await res.blob();
|
|
366
|
-
return new Promise((resolve, reject) => {
|
|
367
|
-
const reader = new FileReader();
|
|
368
|
-
reader.onload = () => resolve(reader.result.split(",")[1]);
|
|
369
|
-
reader.onerror = reject;
|
|
370
|
-
reader.readAsDataURL(blob);
|
|
371
|
-
});
|
|
372
|
-
} catch {
|
|
373
|
-
return null;
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
if (filename) {
|
|
377
|
-
if (hasThumb) {
|
|
378
|
-
const thumb = await fetchBase64(`thumbs/${filename}`);
|
|
379
|
-
if (thumb && thumb !== "ABORT") return thumb;
|
|
380
|
-
}
|
|
381
|
-
const result2 = await fetchBase64(`images/${filename}`);
|
|
382
|
-
return result2 === "ABORT" ? null : result2;
|
|
383
|
-
}
|
|
384
|
-
const ext = bildEndungFuer(mimeType);
|
|
385
|
-
const result = await fetchBase64(`images/${id}.${ext}`);
|
|
386
|
-
return result === "ABORT" ? null : result;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export {
|
|
390
|
-
SPEICHERBARE_BILDTYPEN,
|
|
391
|
-
bildEndungFuer,
|
|
392
|
-
hfListDir,
|
|
393
|
-
hfDownloadJsonByPath,
|
|
394
|
-
hfDownloadBinaryByPath,
|
|
395
|
-
hfUploadSmallFile,
|
|
396
|
-
hfBatchArchive,
|
|
397
|
-
tsFromEventPath,
|
|
398
|
-
loadPendingEvents,
|
|
399
|
-
listEventPaths,
|
|
400
|
-
getSessionClientId,
|
|
401
|
-
writeHFEvent,
|
|
402
|
-
HF_TOKEN_KEY,
|
|
403
|
-
getHFToken,
|
|
404
|
-
setHFToken,
|
|
405
|
-
loadHFState,
|
|
406
|
-
hfBootstrapFromLegacy,
|
|
407
|
-
hfListProjects,
|
|
408
|
-
hfDownloadProject,
|
|
409
|
-
hfUploadProject,
|
|
410
|
-
hfUploadProjectForm,
|
|
411
|
-
hfDeleteProject,
|
|
412
|
-
hfSaveTags,
|
|
413
|
-
hfLoadTags,
|
|
414
|
-
hfSaveMetadata,
|
|
415
|
-
hfLoadMetadata,
|
|
416
|
-
hfUploadImage,
|
|
417
|
-
hfLoadImageAsBase64
|
|
418
|
-
};
|