u-space 0.0.32 → 0.0.33
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/index.cjs +3 -3
- package/dist/index.js +1174 -938
- package/dist/modelDecodeProtocol-CC2tkS8h.js +405 -0
- package/dist/modelDecodeProtocol-pgjndTph.cjs +1 -0
- package/dist/src/loaders/ModelLoaderManager.d.ts +13 -1
- package/dist/src/loaders/modelPersistentCache.d.ts +35 -0
- package/dist/src/objects/Model.d.ts +4 -0
- package/dist/src/worker/decodeGltfModel.d.ts +7 -1
- package/dist/src/worker/modelDecodeProtocol.d.ts +2 -0
- package/dist/worker/model-decoder.cjs +1 -1
- package/dist/worker/model-decoder.js +231 -205
- package/docs/api-objects.md +11 -2
- package/docs/changelog.md +7 -0
- package/docs/examples-guide.md +2 -2
- package/docs/getting-started.md +1 -1
- package/package.json +1 -1
- package/dist/modelDecodeProtocol-D1Apm9iG.cjs +0 -1
- package/dist/modelDecodeProtocol-wDB7ksYw.js +0 -56
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
const x = "u-space-model-loader-cache", _ = `${x}-v2`, L = `${x}-metadata-v2`, k = "__u_space_cache_variant", A = "x-u-space-cache-stored-at", C = Object.freeze({
|
|
2
|
+
maxAgeMs: 10080 * 60 * 1e3,
|
|
3
|
+
maxEntries: 64,
|
|
4
|
+
maxBytes: 512 * 1024 * 1024
|
|
5
|
+
});
|
|
6
|
+
let T = !1;
|
|
7
|
+
function w(t) {
|
|
8
|
+
if (t?.aborted)
|
|
9
|
+
throw t.reason ?? new DOMException("Aborted", "AbortError");
|
|
10
|
+
}
|
|
11
|
+
function E() {
|
|
12
|
+
return typeof caches < "u";
|
|
13
|
+
}
|
|
14
|
+
function R(t) {
|
|
15
|
+
return !t.startsWith("data:") && !t.startsWith("blob:");
|
|
16
|
+
}
|
|
17
|
+
function z(t) {
|
|
18
|
+
return {
|
|
19
|
+
maxAgeMs: Math.max(0, t?.maxAgeMs ?? C.maxAgeMs),
|
|
20
|
+
maxEntries: Math.max(0, Math.floor(t?.maxEntries ?? C.maxEntries)),
|
|
21
|
+
maxBytes: Math.max(0, t?.maxBytes ?? C.maxBytes)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function I(t) {
|
|
25
|
+
return typeof location < "u" ? new URL(t, location.href).href : new URL(t).href;
|
|
26
|
+
}
|
|
27
|
+
async function j(t) {
|
|
28
|
+
if (globalThis.crypto?.subtle) {
|
|
29
|
+
const r = await globalThis.crypto.subtle.digest(
|
|
30
|
+
"SHA-256",
|
|
31
|
+
new TextEncoder().encode(t)
|
|
32
|
+
);
|
|
33
|
+
return Array.from(
|
|
34
|
+
new Uint8Array(r),
|
|
35
|
+
(s) => s.toString(16).padStart(2, "0")
|
|
36
|
+
).join("");
|
|
37
|
+
}
|
|
38
|
+
let e = 2166136261;
|
|
39
|
+
for (let r = 0; r < t.length; r++)
|
|
40
|
+
e ^= t.charCodeAt(r), e = Math.imul(e, 16777619);
|
|
41
|
+
return (e >>> 0).toString(16).padStart(8, "0");
|
|
42
|
+
}
|
|
43
|
+
async function P(t, e, r = "same-origin", s) {
|
|
44
|
+
const n = I(t), c = new Headers(e), d = Array.from(c.entries()).map(([o, f]) => [o.toLowerCase(), f]).sort(([o], [f]) => o.localeCompare(f));
|
|
45
|
+
if (d.length === 0 && r === "same-origin" && s === void 0)
|
|
46
|
+
return n;
|
|
47
|
+
const i = await j(JSON.stringify([r, d, s])), a = new URL(n);
|
|
48
|
+
return a.searchParams.set(k, i), a.href;
|
|
49
|
+
}
|
|
50
|
+
function N(t, e, r) {
|
|
51
|
+
const s = new Headers(t);
|
|
52
|
+
return s.delete("content-encoding"), s.delete("transfer-encoding"), s.set("content-length", String(e)), r !== void 0 && s.set(A, String(r)), s;
|
|
53
|
+
}
|
|
54
|
+
function G(t, e, r, s, n) {
|
|
55
|
+
return new Response(t.slice(0), {
|
|
56
|
+
headers: N(e, t.byteLength, n),
|
|
57
|
+
status: r,
|
|
58
|
+
statusText: s
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function g(t, e, r, s, n, c) {
|
|
62
|
+
return {
|
|
63
|
+
url: t,
|
|
64
|
+
buffer: e,
|
|
65
|
+
headers: N(r, e.byteLength),
|
|
66
|
+
status: s,
|
|
67
|
+
statusText: n,
|
|
68
|
+
fromCache: c
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function O(t) {
|
|
72
|
+
return new Set(
|
|
73
|
+
(t.get("cache-control") ?? "").toLowerCase().split(",").map((e) => e.trim()).filter(Boolean)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
function W(t) {
|
|
77
|
+
if (t.status !== 200) return !1;
|
|
78
|
+
const e = O(t.headers);
|
|
79
|
+
if (e.has("no-store") || e.has("private")) return !1;
|
|
80
|
+
const r = (t.headers.get("vary") ?? "").toLowerCase().split(",").map((s) => s.trim());
|
|
81
|
+
return !r.includes("*") && !r.includes("cookie");
|
|
82
|
+
}
|
|
83
|
+
function X(t, e) {
|
|
84
|
+
const r = O(t);
|
|
85
|
+
if (r.has("no-cache")) return 0;
|
|
86
|
+
for (const c of r) {
|
|
87
|
+
const d = /^max-age=(\d+)$/.exec(c);
|
|
88
|
+
if (d) return Math.min(Number(d[1]) * 1e3, e.maxAgeMs);
|
|
89
|
+
}
|
|
90
|
+
const s = Date.parse(t.get("expires") ?? ""), n = Date.parse(t.get("date") ?? "");
|
|
91
|
+
return Number.isFinite(s) && Number.isFinite(n) ? Math.min(Math.max(0, s - n), e.maxAgeMs) : e.maxAgeMs;
|
|
92
|
+
}
|
|
93
|
+
function J(t, e) {
|
|
94
|
+
const r = new Headers(t), s = e.headers.get("etag"), n = e.headers.get("last-modified");
|
|
95
|
+
return s && r.set("if-none-match", s), n && r.set("if-modified-since", n), r;
|
|
96
|
+
}
|
|
97
|
+
function K(t, e) {
|
|
98
|
+
const r = new Headers(t);
|
|
99
|
+
return e.forEach((s, n) => r.set(n, s)), r;
|
|
100
|
+
}
|
|
101
|
+
async function V() {
|
|
102
|
+
if (!(T || !E())) {
|
|
103
|
+
T = !0;
|
|
104
|
+
try {
|
|
105
|
+
const t = await caches.keys();
|
|
106
|
+
await Promise.all(
|
|
107
|
+
t.filter((e) => e.startsWith(x) && e !== _ && e !== L).map((e) => caches.delete(e))
|
|
108
|
+
);
|
|
109
|
+
} catch (t) {
|
|
110
|
+
console.warn("[ModelPersistentCache] Failed to remove legacy caches:", t);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function S(t, e) {
|
|
115
|
+
try {
|
|
116
|
+
const r = await t.match(e);
|
|
117
|
+
if (!r) return null;
|
|
118
|
+
const s = await r.json();
|
|
119
|
+
return !Number.isFinite(s.size) || !Number.isFinite(s.storedAt) || !Number.isFinite(s.lastAccess) ? null : s;
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function b(t, e, r) {
|
|
125
|
+
await t.put(e, new Response(JSON.stringify(r), {
|
|
126
|
+
headers: { "content-type": "application/json" }
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
async function M(t, e, r) {
|
|
130
|
+
await Promise.all([t.delete(r), e.delete(r)]);
|
|
131
|
+
}
|
|
132
|
+
async function Y(t, e, r) {
|
|
133
|
+
const s = await t.keys(), n = await Promise.all(s.map(async (i) => {
|
|
134
|
+
const a = await S(e, i.url);
|
|
135
|
+
if (a) return { request: i, metadata: a };
|
|
136
|
+
const o = await t.match(i), f = Number(o?.headers.get("content-length")) || 0, l = Number(o?.headers.get(A)) || 0;
|
|
137
|
+
return {
|
|
138
|
+
request: i,
|
|
139
|
+
metadata: { size: f, storedAt: l, lastAccess: l }
|
|
140
|
+
};
|
|
141
|
+
}));
|
|
142
|
+
n.sort((i, a) => i.metadata.lastAccess - a.metadata.lastAccess);
|
|
143
|
+
let c = n.reduce((i, a) => i + a.metadata.size, 0), d = n.length;
|
|
144
|
+
for (const i of n) {
|
|
145
|
+
if (d <= r.maxEntries && c <= r.maxBytes) break;
|
|
146
|
+
await M(t, e, i.request.url), d--, c -= i.metadata.size;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function U() {
|
|
150
|
+
return await V(), Promise.all([
|
|
151
|
+
caches.open(_),
|
|
152
|
+
caches.open(L)
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
155
|
+
async function p(t, e, r, s, n, c) {
|
|
156
|
+
if (s.buffer.byteLength === 0 || n.maxEntries === 0 || s.buffer.byteLength > n.maxBytes) return;
|
|
157
|
+
const d = G(
|
|
158
|
+
s.buffer,
|
|
159
|
+
s.headers,
|
|
160
|
+
s.status,
|
|
161
|
+
s.statusText,
|
|
162
|
+
c
|
|
163
|
+
);
|
|
164
|
+
await t.put(r, d), await b(e, r, {
|
|
165
|
+
size: s.buffer.byteLength,
|
|
166
|
+
storedAt: c,
|
|
167
|
+
lastAccess: c
|
|
168
|
+
}), await Y(t, e, n);
|
|
169
|
+
}
|
|
170
|
+
async function $(t, e, r) {
|
|
171
|
+
const s = await fetch(t, {
|
|
172
|
+
headers: r,
|
|
173
|
+
credentials: e.credentials ?? "same-origin",
|
|
174
|
+
signal: e.signal
|
|
175
|
+
});
|
|
176
|
+
if (!s.ok)
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Failed to load model resource ${t}: ${s.status} ${s.statusText}`
|
|
179
|
+
);
|
|
180
|
+
const n = await s.arrayBuffer();
|
|
181
|
+
return w(e.signal), g(
|
|
182
|
+
t,
|
|
183
|
+
n,
|
|
184
|
+
s.headers,
|
|
185
|
+
s.status,
|
|
186
|
+
s.statusText,
|
|
187
|
+
!1
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
async function q(t, e = {}) {
|
|
191
|
+
w(e.signal);
|
|
192
|
+
const r = e.cacheMode;
|
|
193
|
+
if (!(e.persistent !== !1 && r !== "no-store") || !E() || !R(t))
|
|
194
|
+
return $(t, e, e.headers);
|
|
195
|
+
const n = z(e.policy), c = await P(t, e.headers, e.credentials, e.cacheKey);
|
|
196
|
+
let d, i;
|
|
197
|
+
try {
|
|
198
|
+
[d, i] = await U();
|
|
199
|
+
} catch (h) {
|
|
200
|
+
return w(e.signal), console.warn(`[ModelPersistentCache] Cache Storage is unavailable for ${t}:`, h), $(t, e, e.headers);
|
|
201
|
+
}
|
|
202
|
+
let a;
|
|
203
|
+
try {
|
|
204
|
+
a = await d.match(c);
|
|
205
|
+
} catch (h) {
|
|
206
|
+
w(e.signal), console.warn(`[ModelPersistentCache] Failed to read ${t}:`, h);
|
|
207
|
+
}
|
|
208
|
+
let o, f = await S(i, c);
|
|
209
|
+
if (a) {
|
|
210
|
+
try {
|
|
211
|
+
o = await a.clone().arrayBuffer();
|
|
212
|
+
} catch {
|
|
213
|
+
o = new ArrayBuffer(0);
|
|
214
|
+
}
|
|
215
|
+
if (w(e.signal), o.byteLength === 0) {
|
|
216
|
+
try {
|
|
217
|
+
await M(d, i, c);
|
|
218
|
+
} catch (h) {
|
|
219
|
+
console.warn(`[ModelPersistentCache] Failed to delete corrupt ${t}:`, h);
|
|
220
|
+
}
|
|
221
|
+
a = void 0, o = void 0, f = null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const l = Date.now();
|
|
225
|
+
if (a && o) {
|
|
226
|
+
const h = f?.storedAt ?? (Number(a.headers.get(A)) || 0), m = l - h, y = m >= 0 && m < X(a.headers, n);
|
|
227
|
+
if (r === "force-cache" || y && r !== "reload" && r !== "revalidate") {
|
|
228
|
+
try {
|
|
229
|
+
await b(i, c, {
|
|
230
|
+
size: o.byteLength,
|
|
231
|
+
storedAt: h,
|
|
232
|
+
lastAccess: l
|
|
233
|
+
});
|
|
234
|
+
} catch (B) {
|
|
235
|
+
console.warn(`[ModelPersistentCache] Failed to update access time for ${t}:`, B);
|
|
236
|
+
}
|
|
237
|
+
return g(
|
|
238
|
+
t,
|
|
239
|
+
o,
|
|
240
|
+
a.headers,
|
|
241
|
+
a.status,
|
|
242
|
+
a.statusText,
|
|
243
|
+
!0
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
let u;
|
|
248
|
+
try {
|
|
249
|
+
u = await fetch(t, {
|
|
250
|
+
headers: a && r !== "reload" ? J(e.headers, a) : e.headers,
|
|
251
|
+
credentials: e.credentials ?? "same-origin",
|
|
252
|
+
signal: e.signal
|
|
253
|
+
});
|
|
254
|
+
} catch (h) {
|
|
255
|
+
if (w(e.signal), a && o) {
|
|
256
|
+
const m = f?.storedAt ?? (Number(a.headers.get(A)) || 0);
|
|
257
|
+
try {
|
|
258
|
+
await b(i, c, {
|
|
259
|
+
size: o.byteLength,
|
|
260
|
+
storedAt: m,
|
|
261
|
+
lastAccess: l
|
|
262
|
+
});
|
|
263
|
+
} catch (y) {
|
|
264
|
+
console.warn(`[ModelPersistentCache] Failed to update access time for ${t}:`, y);
|
|
265
|
+
}
|
|
266
|
+
return g(
|
|
267
|
+
t,
|
|
268
|
+
o,
|
|
269
|
+
a.headers,
|
|
270
|
+
a.status,
|
|
271
|
+
a.statusText,
|
|
272
|
+
!0
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
throw h;
|
|
276
|
+
}
|
|
277
|
+
if (u.status === 304 && a && o) {
|
|
278
|
+
const h = K(a.headers, u.headers), m = g(
|
|
279
|
+
t,
|
|
280
|
+
o,
|
|
281
|
+
h,
|
|
282
|
+
a.status,
|
|
283
|
+
a.statusText,
|
|
284
|
+
!0
|
|
285
|
+
);
|
|
286
|
+
try {
|
|
287
|
+
await p(d, i, c, m, n, l);
|
|
288
|
+
} catch (y) {
|
|
289
|
+
console.warn(`[ModelPersistentCache] Failed to store revalidated ${t}:`, y);
|
|
290
|
+
}
|
|
291
|
+
return m;
|
|
292
|
+
}
|
|
293
|
+
if (!u.ok) {
|
|
294
|
+
if (a && o && u.status >= 500)
|
|
295
|
+
return g(
|
|
296
|
+
t,
|
|
297
|
+
o,
|
|
298
|
+
a.headers,
|
|
299
|
+
a.status,
|
|
300
|
+
a.statusText,
|
|
301
|
+
!0
|
|
302
|
+
);
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Failed to load model resource ${t}: ${u.status} ${u.statusText}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
const H = await u.arrayBuffer();
|
|
308
|
+
w(e.signal);
|
|
309
|
+
const D = g(
|
|
310
|
+
t,
|
|
311
|
+
H,
|
|
312
|
+
u.headers,
|
|
313
|
+
u.status,
|
|
314
|
+
u.statusText,
|
|
315
|
+
!1
|
|
316
|
+
);
|
|
317
|
+
if (W(u))
|
|
318
|
+
try {
|
|
319
|
+
await p(d, i, c, D, n, l);
|
|
320
|
+
} catch (h) {
|
|
321
|
+
w(e.signal), console.warn(`[ModelPersistentCache] Failed to cache ${t}:`, h);
|
|
322
|
+
}
|
|
323
|
+
else if (a)
|
|
324
|
+
try {
|
|
325
|
+
await M(d, i, c);
|
|
326
|
+
} catch (h) {
|
|
327
|
+
console.warn(`[ModelPersistentCache] Failed to remove uncacheable ${t}:`, h);
|
|
328
|
+
}
|
|
329
|
+
return D;
|
|
330
|
+
}
|
|
331
|
+
async function Q(t, e = {}) {
|
|
332
|
+
if (!E() || !R(t)) return;
|
|
333
|
+
const r = await P(t, e.headers, e.credentials, e.cacheKey), [s, n] = await U();
|
|
334
|
+
await M(s, n, r);
|
|
335
|
+
}
|
|
336
|
+
async function Z() {
|
|
337
|
+
if (!E()) return;
|
|
338
|
+
const t = await caches.keys();
|
|
339
|
+
await Promise.all(
|
|
340
|
+
t.filter((e) => e.startsWith(x)).map((e) => caches.delete(e))
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
function ee() {
|
|
344
|
+
return E();
|
|
345
|
+
}
|
|
346
|
+
const te = 3, v = 1e5, F = 512;
|
|
347
|
+
function ae(t) {
|
|
348
|
+
const e = t.nodes ?? [];
|
|
349
|
+
if (e.length > v)
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Decoded glTF contains ${e.length} nodes; the safe limit is ${v}.`
|
|
352
|
+
);
|
|
353
|
+
const r = new Uint8Array(e.length);
|
|
354
|
+
for (let a = 0; a < e.length; a++) {
|
|
355
|
+
const o = e[a].children ?? [], f = /* @__PURE__ */ new Set();
|
|
356
|
+
for (const l of o) {
|
|
357
|
+
if (!Number.isInteger(l) || l < 0 || l >= e.length)
|
|
358
|
+
throw new Error(`Decoded glTF node ${a} references invalid child ${l}.`);
|
|
359
|
+
if (f.has(l))
|
|
360
|
+
throw new Error(`Decoded glTF node ${a} repeats child ${l}.`);
|
|
361
|
+
if (f.add(l), r[l]++, r[l] > 1)
|
|
362
|
+
throw new Error(`Decoded glTF node ${l} has multiple parents.`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const s = new Uint8Array(e.length), n = (a, o) => {
|
|
366
|
+
if (o > F)
|
|
367
|
+
throw new Error(
|
|
368
|
+
`Decoded glTF node graph exceeds the safe depth of ${F}.`
|
|
369
|
+
);
|
|
370
|
+
if (s[a] === 1)
|
|
371
|
+
throw new Error(`Decoded glTF node graph contains a cycle at ${a}.`);
|
|
372
|
+
if (s[a] !== 2) {
|
|
373
|
+
s[a] = 1;
|
|
374
|
+
for (const f of e[a].children ?? []) n(f, o + 1);
|
|
375
|
+
s[a] = 2;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
for (let a = 0; a < e.length; a++) n(a, 1);
|
|
379
|
+
const c = t.scene ?? 0, d = t.scenes?.[c];
|
|
380
|
+
if (!d) throw new Error(`Decoded glTF references missing scene ${c}.`);
|
|
381
|
+
const i = /* @__PURE__ */ new Set();
|
|
382
|
+
for (const a of d.nodes ?? []) {
|
|
383
|
+
if (!Number.isInteger(a) || a < 0 || a >= e.length)
|
|
384
|
+
throw new Error(`Decoded glTF scene ${c} references invalid node ${a}.`);
|
|
385
|
+
if (i.has(a))
|
|
386
|
+
throw new Error(`Decoded glTF scene ${c} repeats root node ${a}.`);
|
|
387
|
+
if (i.add(a), r[a] > 0)
|
|
388
|
+
throw new Error(`Decoded glTF scene root ${a} is also referenced as a child.`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
class re extends Error {
|
|
392
|
+
name = "UnsupportedModelDecodeError";
|
|
393
|
+
constructor(e) {
|
|
394
|
+
super(e);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
export {
|
|
398
|
+
te as M,
|
|
399
|
+
re as U,
|
|
400
|
+
Z as c,
|
|
401
|
+
Q as d,
|
|
402
|
+
q as l,
|
|
403
|
+
ee as m,
|
|
404
|
+
ae as v
|
|
405
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const C="u-space-model-loader-cache",F=`${C}-v2`,R=`${C}-metadata-v2`,k="__u_space_cache_variant",A="x-u-space-cache-stored-at",x=Object.freeze({maxAgeMs:10080*60*1e3,maxEntries:64,maxBytes:512*1024*1024});let p=!1;function w(t){if(t?.aborted)throw t.reason??new DOMException("Aborted","AbortError")}function E(){return typeof caches<"u"}function L(t){return!t.startsWith("data:")&&!t.startsWith("blob:")}function I(t){return{maxAgeMs:Math.max(0,t?.maxAgeMs??x.maxAgeMs),maxEntries:Math.max(0,Math.floor(t?.maxEntries??x.maxEntries)),maxBytes:Math.max(0,t?.maxBytes??x.maxBytes)}}function z(t){return typeof location<"u"?new URL(t,location.href).href:new URL(t).href}async function G(t){if(globalThis.crypto?.subtle){const r=await globalThis.crypto.subtle.digest("SHA-256",new TextEncoder().encode(t));return Array.from(new Uint8Array(r),s=>s.toString(16).padStart(2,"0")).join("")}let e=2166136261;for(let r=0;r<t.length;r++)e^=t.charCodeAt(r),e=Math.imul(e,16777619);return(e>>>0).toString(16).padStart(8,"0")}async function O(t,e,r="same-origin",s){const n=z(t),c=new Headers(e),d=Array.from(c.entries()).map(([o,f])=>[o.toLowerCase(),f]).sort(([o],[f])=>o.localeCompare(f));if(d.length===0&&r==="same-origin"&&s===void 0)return n;const i=await G(JSON.stringify([r,d,s])),a=new URL(n);return a.searchParams.set(k,i),a.href}function P(t,e,r){const s=new Headers(t);return s.delete("content-encoding"),s.delete("transfer-encoding"),s.set("content-length",String(e)),r!==void 0&&s.set(A,String(r)),s}function j(t,e,r,s,n){return new Response(t.slice(0),{headers:P(e,t.byteLength,n),status:r,statusText:s})}function g(t,e,r,s,n,c){return{url:t,buffer:e,headers:P(r,e.byteLength),status:s,statusText:n,fromCache:c}}function N(t){return new Set((t.get("cache-control")??"").toLowerCase().split(",").map(e=>e.trim()).filter(Boolean))}function W(t){if(t.status!==200)return!1;const e=N(t.headers);if(e.has("no-store")||e.has("private"))return!1;const r=(t.headers.get("vary")??"").toLowerCase().split(",").map(s=>s.trim());return!r.includes("*")&&!r.includes("cookie")}function V(t,e){const r=N(t);if(r.has("no-cache"))return 0;for(const c of r){const d=/^max-age=(\d+)$/.exec(c);if(d)return Math.min(Number(d[1])*1e3,e.maxAgeMs)}const s=Date.parse(t.get("expires")??""),n=Date.parse(t.get("date")??"");return Number.isFinite(s)&&Number.isFinite(n)?Math.min(Math.max(0,s-n),e.maxAgeMs):e.maxAgeMs}function X(t,e){const r=new Headers(t),s=e.headers.get("etag"),n=e.headers.get("last-modified");return s&&r.set("if-none-match",s),n&&r.set("if-modified-since",n),r}function J(t,e){const r=new Headers(t);return e.forEach((s,n)=>r.set(n,s)),r}async function K(){if(!(p||!E())){p=!0;try{const t=await caches.keys();await Promise.all(t.filter(e=>e.startsWith(C)&&e!==F&&e!==R).map(e=>caches.delete(e)))}catch(t){console.warn("[ModelPersistentCache] Failed to remove legacy caches:",t)}}}async function S(t,e){try{const r=await t.match(e);if(!r)return null;const s=await r.json();return!Number.isFinite(s.size)||!Number.isFinite(s.storedAt)||!Number.isFinite(s.lastAccess)?null:s}catch{return null}}async function b(t,e,r){await t.put(e,new Response(JSON.stringify(r),{headers:{"content-type":"application/json"}}))}async function M(t,e,r){await Promise.all([t.delete(r),e.delete(r)])}async function Y(t,e,r){const s=await t.keys(),n=await Promise.all(s.map(async i=>{const a=await S(e,i.url);if(a)return{request:i,metadata:a};const o=await t.match(i),f=Number(o?.headers.get("content-length"))||0,l=Number(o?.headers.get(A))||0;return{request:i,metadata:{size:f,storedAt:l,lastAccess:l}}}));n.sort((i,a)=>i.metadata.lastAccess-a.metadata.lastAccess);let c=n.reduce((i,a)=>i+a.metadata.size,0),d=n.length;for(const i of n){if(d<=r.maxEntries&&c<=r.maxBytes)break;await M(t,e,i.request.url),d--,c-=i.metadata.size}}async function U(){return await K(),Promise.all([caches.open(F),caches.open(R)])}async function T(t,e,r,s,n,c){if(s.buffer.byteLength===0||n.maxEntries===0||s.buffer.byteLength>n.maxBytes)return;const d=j(s.buffer,s.headers,s.status,s.statusText,c);await t.put(r,d),await b(e,r,{size:s.buffer.byteLength,storedAt:c,lastAccess:c}),await Y(t,e,n)}async function _(t,e,r){const s=await fetch(t,{headers:r,credentials:e.credentials??"same-origin",signal:e.signal});if(!s.ok)throw new Error(`Failed to load model resource ${t}: ${s.status} ${s.statusText}`);const n=await s.arrayBuffer();return w(e.signal),g(t,n,s.headers,s.status,s.statusText,!1)}async function q(t,e={}){w(e.signal);const r=e.cacheMode;if(!(e.persistent!==!1&&r!=="no-store")||!E()||!L(t))return _(t,e,e.headers);const n=I(e.policy),c=await O(t,e.headers,e.credentials,e.cacheKey);let d,i;try{[d,i]=await U()}catch(h){return w(e.signal),console.warn(`[ModelPersistentCache] Cache Storage is unavailable for ${t}:`,h),_(t,e,e.headers)}let a;try{a=await d.match(c)}catch(h){w(e.signal),console.warn(`[ModelPersistentCache] Failed to read ${t}:`,h)}let o,f=await S(i,c);if(a){try{o=await a.clone().arrayBuffer()}catch{o=new ArrayBuffer(0)}if(w(e.signal),o.byteLength===0){try{await M(d,i,c)}catch(h){console.warn(`[ModelPersistentCache] Failed to delete corrupt ${t}:`,h)}a=void 0,o=void 0,f=null}}const l=Date.now();if(a&&o){const h=f?.storedAt??(Number(a.headers.get(A))||0),m=l-h,y=m>=0&&m<V(a.headers,n);if(r==="force-cache"||y&&r!=="reload"&&r!=="revalidate"){try{await b(i,c,{size:o.byteLength,storedAt:h,lastAccess:l})}catch(B){console.warn(`[ModelPersistentCache] Failed to update access time for ${t}:`,B)}return g(t,o,a.headers,a.status,a.statusText,!0)}}let u;try{u=await fetch(t,{headers:a&&r!=="reload"?X(e.headers,a):e.headers,credentials:e.credentials??"same-origin",signal:e.signal})}catch(h){if(w(e.signal),a&&o){const m=f?.storedAt??(Number(a.headers.get(A))||0);try{await b(i,c,{size:o.byteLength,storedAt:m,lastAccess:l})}catch(y){console.warn(`[ModelPersistentCache] Failed to update access time for ${t}:`,y)}return g(t,o,a.headers,a.status,a.statusText,!0)}throw h}if(u.status===304&&a&&o){const h=J(a.headers,u.headers),m=g(t,o,h,a.status,a.statusText,!0);try{await T(d,i,c,m,n,l)}catch(y){console.warn(`[ModelPersistentCache] Failed to store revalidated ${t}:`,y)}return m}if(!u.ok){if(a&&o&&u.status>=500)return g(t,o,a.headers,a.status,a.statusText,!0);throw new Error(`Failed to load model resource ${t}: ${u.status} ${u.statusText}`)}const H=await u.arrayBuffer();w(e.signal);const D=g(t,H,u.headers,u.status,u.statusText,!1);if(W(u))try{await T(d,i,c,D,n,l)}catch(h){w(e.signal),console.warn(`[ModelPersistentCache] Failed to cache ${t}:`,h)}else if(a)try{await M(d,i,c)}catch(h){console.warn(`[ModelPersistentCache] Failed to remove uncacheable ${t}:`,h)}return D}async function Q(t,e={}){if(!E()||!L(t))return;const r=await O(t,e.headers,e.credentials,e.cacheKey),[s,n]=await U();await M(s,n,r)}async function Z(){if(!E())return;const t=await caches.keys();await Promise.all(t.filter(e=>e.startsWith(C)).map(e=>caches.delete(e)))}function ee(){return E()}const te=3,$=1e5,v=512;function ae(t){const e=t.nodes??[];if(e.length>$)throw new Error(`Decoded glTF contains ${e.length} nodes; the safe limit is ${$}.`);const r=new Uint8Array(e.length);for(let a=0;a<e.length;a++){const o=e[a].children??[],f=new Set;for(const l of o){if(!Number.isInteger(l)||l<0||l>=e.length)throw new Error(`Decoded glTF node ${a} references invalid child ${l}.`);if(f.has(l))throw new Error(`Decoded glTF node ${a} repeats child ${l}.`);if(f.add(l),r[l]++,r[l]>1)throw new Error(`Decoded glTF node ${l} has multiple parents.`)}}const s=new Uint8Array(e.length),n=(a,o)=>{if(o>v)throw new Error(`Decoded glTF node graph exceeds the safe depth of ${v}.`);if(s[a]===1)throw new Error(`Decoded glTF node graph contains a cycle at ${a}.`);if(s[a]!==2){s[a]=1;for(const f of e[a].children??[])n(f,o+1);s[a]=2}};for(let a=0;a<e.length;a++)n(a,1);const c=t.scene??0,d=t.scenes?.[c];if(!d)throw new Error(`Decoded glTF references missing scene ${c}.`);const i=new Set;for(const a of d.nodes??[]){if(!Number.isInteger(a)||a<0||a>=e.length)throw new Error(`Decoded glTF scene ${c} references invalid node ${a}.`);if(i.has(a))throw new Error(`Decoded glTF scene ${c} repeats root node ${a}.`);if(i.add(a),r[a]>0)throw new Error(`Decoded glTF scene root ${a} is also referenced as a child.`)}}class re extends Error{name="UnsupportedModelDecodeError";constructor(e){super(e)}}exports.MODEL_DECODE_PROTOCOL_VERSION=te;exports.UnsupportedModelDecodeError=re;exports.clearModelPersistentCache=Z;exports.deleteModelResourceCache=Q;exports.loadModelResource=q;exports.modelPersistentCacheAvailable=ee;exports.validateDecodedGltfNodeGraph=ae;
|
|
@@ -7,13 +7,16 @@ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
|
7
7
|
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
|
|
8
8
|
import { SBMXLoader } from './SBMXLoader';
|
|
9
9
|
import { SBMLoader } from './SBMLoader';
|
|
10
|
+
import { type ModelCacheMode } from './modelPersistentCache';
|
|
10
11
|
import type { InteractionEventMap } from '../interactions';
|
|
11
12
|
export interface LoadParameters {
|
|
12
13
|
persistent?: boolean;
|
|
14
|
+
cacheMode?: ModelCacheMode;
|
|
15
|
+
cacheKey?: string;
|
|
13
16
|
signal?: AbortSignal;
|
|
14
17
|
}
|
|
18
|
+
export type { ModelCacheMode } from './modelPersistentCache';
|
|
15
19
|
export declare class ModelLoaderManager {
|
|
16
|
-
private static CACHE_NAME;
|
|
17
20
|
static fileLoader: FileLoader<string | ArrayBuffer>;
|
|
18
21
|
static dracoLoader: DRACOLoader;
|
|
19
22
|
static ktx2Loader: KTX2Loader;
|
|
@@ -45,9 +48,18 @@ export declare class ModelLoaderManager {
|
|
|
45
48
|
* Configure KTX2 transcoder path.
|
|
46
49
|
*/
|
|
47
50
|
static setKTX2Path(path: string): void;
|
|
51
|
+
static getMemoryCacheKey(url: string, persistent: boolean, cacheMode?: ModelCacheMode, cacheKey?: string): string;
|
|
52
|
+
static clearPersistentCache(): Promise<void>;
|
|
53
|
+
static deletePersistentCache(url: string, options?: Pick<LoadParameters, 'cacheKey'>): Promise<void>;
|
|
48
54
|
static loadAsync(url: string, parameters?: LoadParameters): Promise<Object3D<InteractionEventMap>>;
|
|
55
|
+
private static loadRegularModel;
|
|
49
56
|
private static configureGltfLoader;
|
|
50
57
|
private static createParseLoadingManager;
|
|
58
|
+
private static rootResourceOptions;
|
|
59
|
+
private static cacheUseFor;
|
|
60
|
+
private static dependencyResourceOptions;
|
|
61
|
+
private static loadManagedResource;
|
|
62
|
+
private static prepareExternalGltfResources;
|
|
51
63
|
private static loadSourceBuffer;
|
|
52
64
|
private static throwIfAborted;
|
|
53
65
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const MODEL_RESOURCE_CACHE_NAME = "u-space-model-loader-cache-v2";
|
|
2
|
+
export declare const DEFAULT_MODEL_CACHE_POLICY: Readonly<{
|
|
3
|
+
maxAgeMs: number;
|
|
4
|
+
maxEntries: 64;
|
|
5
|
+
maxBytes: number;
|
|
6
|
+
}>;
|
|
7
|
+
export interface ModelCachePolicy {
|
|
8
|
+
maxAgeMs: number;
|
|
9
|
+
maxEntries: number;
|
|
10
|
+
maxBytes: number;
|
|
11
|
+
}
|
|
12
|
+
export type ModelCacheMode = 'no-store' | 'reload' | 'revalidate' | 'force-cache';
|
|
13
|
+
export interface ModelResourceLoadOptions {
|
|
14
|
+
persistent?: boolean;
|
|
15
|
+
/** Bypass, refresh, validate, or force use of the persistent resource cache. */
|
|
16
|
+
cacheMode?: ModelCacheMode;
|
|
17
|
+
/** Caller-controlled cache identity for versioned resources at one URL. */
|
|
18
|
+
cacheKey?: string;
|
|
19
|
+
headers?: HeadersInit;
|
|
20
|
+
credentials?: RequestCredentials;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
policy?: Partial<ModelCachePolicy>;
|
|
23
|
+
}
|
|
24
|
+
export interface LoadedModelResource {
|
|
25
|
+
url: string;
|
|
26
|
+
buffer: ArrayBuffer;
|
|
27
|
+
headers: Headers;
|
|
28
|
+
status: number;
|
|
29
|
+
statusText: string;
|
|
30
|
+
fromCache: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function loadModelResource(url: string, options?: ModelResourceLoadOptions): Promise<LoadedModelResource>;
|
|
33
|
+
export declare function deleteModelResourceCache(url: string, options?: Pick<ModelResourceLoadOptions, 'headers' | 'credentials' | 'cacheKey'>): Promise<void>;
|
|
34
|
+
export declare function clearModelPersistentCache(): Promise<void>;
|
|
35
|
+
export declare function modelPersistentCacheAvailable(): boolean;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type Object3D, AnimationMixer, type AnimationAction, type AnimationClip, Box3, Vector3, type Material } from 'three/webgpu';
|
|
2
|
+
import { type ModelCacheMode } from '../loaders';
|
|
2
3
|
import { type InteractionEventMap } from '../interactions';
|
|
3
4
|
import { BaseGroup } from './BaseGroup';
|
|
4
5
|
export interface ModelParameters {
|
|
5
6
|
url: string;
|
|
6
7
|
cache?: boolean;
|
|
7
8
|
persistent?: boolean;
|
|
9
|
+
cacheMode?: ModelCacheMode;
|
|
10
|
+
cacheKey?: string;
|
|
8
11
|
signal?: AbortSignal;
|
|
9
12
|
}
|
|
10
13
|
export interface PlayAnimationOptions {
|
|
@@ -14,6 +17,7 @@ export interface PlayAnimationOptions {
|
|
|
14
17
|
clampWhenFinished?: boolean;
|
|
15
18
|
}
|
|
16
19
|
export declare class Model extends BaseGroup<InteractionEventMap> {
|
|
20
|
+
private static readonly MAX_MEMORY_CACHE_ENTRIES;
|
|
17
21
|
readonly isModel = true;
|
|
18
22
|
type: string;
|
|
19
23
|
private static memoryCache;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DecodedGltfModelPacket, type ModelDecodeRequestOptions } from './modelDecodeProtocol';
|
|
2
|
+
import { type ModelResourceLoadOptions } from '../loaders/modelPersistentCache';
|
|
2
3
|
export interface ModelDecodeResourceHandle {
|
|
3
4
|
url: string;
|
|
4
5
|
complete(error?: unknown): void;
|
|
@@ -6,5 +7,10 @@ export interface ModelDecodeResourceHandle {
|
|
|
6
7
|
export interface ModelDecodeResourceResolver {
|
|
7
8
|
resolve(url: string, signal: AbortSignal): Promise<ModelDecodeResourceHandle>;
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
interface CachedResourceUse {
|
|
11
|
+
url: string;
|
|
12
|
+
options: Pick<ModelResourceLoadOptions, 'headers' | 'credentials' | 'cacheKey'>;
|
|
13
|
+
}
|
|
14
|
+
export declare function decodeGltfModelPacket(url: string, source: ArrayBuffer, options?: ModelDecodeRequestOptions, signal?: AbortSignal, resourceResolver?: ModelDecodeResourceResolver, cacheUses?: CachedResourceUse[]): Promise<DecodedGltfModelPacket>;
|
|
10
15
|
export declare function loadGltfModelPacket(url: string, options?: ModelDecodeRequestOptions, signal?: AbortSignal, resourceResolver?: ModelDecodeResourceResolver): Promise<DecodedGltfModelPacket>;
|
|
16
|
+
export {};
|
|
@@ -6,6 +6,8 @@ export interface SerializedModelDecodeError {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ModelDecodeRequestOptions {
|
|
8
8
|
persistent?: boolean;
|
|
9
|
+
cacheMode?: 'no-store' | 'reload' | 'revalidate' | 'force-cache';
|
|
10
|
+
cacheKey?: string;
|
|
9
11
|
headers?: Record<string, string>;
|
|
10
12
|
credentials?: RequestCredentials;
|
|
11
13
|
/** Original absolute model URL used to resolve relative glTF dependencies. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("../getTransferables-C97JPUVf.cjs"),D=require("../workerImageUtils-lmOMyTQN.cjs"),p=require("../modelDecodeProtocol-pgjndTph.cjs"),U="SBMG----",S=1179937895,C=1313821514,R=5130562,k=new TextDecoder;function g(e){throw new p.UnsupportedModelDecodeError(e)}function L(e,t,n,s=e,a=!1){const i=e.startsWith("data:")||e.startsWith("blob:");let l=!a;if(a&&!i)try{const d=new URL(e),f=new URL(s);l=(d.protocol==="http:"||d.protocol==="https:")&&d.origin===f.origin}catch{l=!1}return i?{signal:n,persistent:!1}:{signal:n,persistent:t.persistent,cacheMode:t.cacheMode,cacheKey:t.cacheKey,headers:l?t.headers:void 0,credentials:l?t.credentials:"omit"}}async function T(e,t,n,s,a,i,l,d){const f=V(e,t),u=i?await i.resolve(f,a):{url:f,complete(){}};try{const r=L(u.url,s,a,n,!0),o=await p.loadModelResource(u.url,r);o.fromCache&&l.push({url:u.url,options:{headers:r.headers,credentials:r.credentials,cacheKey:r.cacheKey}});const c=await d(o);return u.complete(),c}catch(r){throw u.complete(r),r}}function F(e){if(e.byteLength<8||k.decode(new Uint8Array(e,0,8))!==U)return e;const n=new Uint8Array(e.byteLength-8),s=new Uint8Array(e,8);for(let a=0;a<s.length;a++){const i=s[a];n[a]=i>>4&15|i<<4&240}return n.buffer}function B(e){const t=new DataView(e);if(e.byteLength>=12&&t.getUint32(0,!0)===S){const n=t.getUint32(8,!0);if(n>e.byteLength)throw new Error(`Invalid GLB length: expected ${n}, received ${e.byteLength}.`);let s=12,a;const i=[];for(;s+8<=n;){const l=t.getUint32(s,!0),d=t.getUint32(s+4,!0);if(s+=8,s+l>n)throw new Error("Invalid GLB chunk length.");const f=e.slice(s,s+l);d===C?a=JSON.parse(k.decode(f).replace(/\u0000+$/u,"")):d===R&&i.push(f),s+=l}if(!a)throw new Error("GLB does not contain a JSON chunk.");return{document:a,binaryChunks:i}}return{document:JSON.parse(k.decode(e).replace(/^\uFEFF/u,"")),binaryChunks:[]}}function G(e){e.asset?.version?.startsWith("2")||g("Only glTF 2.x static models are supported by the decode worker."),(e.extensionsRequired?.length||e.extensionsUsed?.length)&&g("glTF extensions are not supported by the decode worker."),e.animations?.length&&g("Animated glTF models must use the regular loader."),e.skins?.length&&g("Skinned glTF models must use the regular loader."),e.cameras?.length&&g("glTF cameras must use the regular loader."),p.validateDecodedGltfNodeGraph(e);for(const t of e.accessors??[])t.sparse&&g("Sparse glTF accessors must use the regular loader."),t.bufferView===void 0&&g("Accessors without a bufferView must use the regular loader."),["SCALAR","VEC2","VEC3","VEC4"].includes(t.type)||g(`Accessor type ${t.type} must use the regular loader.`),[5120,5121,5122,5123,5125,5126].includes(t.componentType)||g(`Accessor component type ${t.componentType} must use the regular loader.`);for(const t of e.bufferViews??[])t.extensions&&Object.keys(t.extensions).length>0&&g("Compressed glTF buffer views must use the regular loader.");for(const t of e.nodes??[])(t.skin!==void 0||t.weights?.length||t.camera!==void 0)&&g("Skinned, morphed, or camera glTF nodes must use the regular loader."),t.extensions&&Object.keys(t.extensions).length>0&&g("Extended glTF nodes must use the regular loader.");for(const t of e.meshes??[])for(const n of t.primitives)(n.mode??4)!==4&&g("Only triangle glTF primitives are supported."),n.targets?.length&&g("Morph targets must use the regular loader."),n.extensions&&Object.keys(n.extensions).length>0&&g("Extended glTF primitives must use the regular loader.");for(const t of e.materials??[])t.extensions&&Object.keys(t.extensions).length>0&&g("Extended glTF materials must use the regular loader.");for(const t of e.textures??[])t.extensions&&Object.keys(t.extensions).length>0&&g("Extended glTF textures must use the regular loader.")}function V(e,t){return e.startsWith("data:")||e.startsWith("blob:")?e:new URL(e,t).href}async function _(e,t,n,s,a,i,l,d=[]){const f=[...t],u=[];for(const r of e.buffers??[]){let o;if(r.uri)o=await T(r.uri,n,s,a,i,l,d,c=>c.buffer);else{const c=f.shift();if(!c)throw new Error("glTF buffer is missing its GLB binary chunk.");o=c}if(o.byteLength<r.byteLength)throw new Error(`glTF buffer is truncated: expected ${r.byteLength}, received ${o.byteLength}.`);u.push(o),Reflect.deleteProperty(r,"uri")}return u}function I(e,t,n,s){const a=e.bufferViews?.[n];if(!a)throw new Error(`glTF image references missing bufferView ${n}.`);const i=t[a.buffer];if(!i)throw new Error(`glTF image references missing buffer ${a.buffer}.`);const l=a.byteOffset??0;return new Blob([new Uint8Array(i,l,a.byteLength)],{type:s??"application/octet-stream"})}async function N(e,t,n,s,a,i,l,d=[]){const f=[],u=[];try{for(const r of e.images??[]){let o;if(r.uri)o=await T(r.uri,n,s,a,i,l,d,w=>new Blob([w.buffer],{type:w.headers.get("content-type")??"application/octet-stream"}));else if(r.bufferView!==void 0)o=I(e,t,r.bufferView,r.mimeType);else throw new Error("glTF image is missing both uri and bufferView.");if(i.aborted)throw i.reason??new DOMException("Aborted","AbortError");const c=await globalThis.crypto.subtle.digest("SHA-256",await o.arrayBuffer());if(i.aborted)throw i.reason??new DOMException("Aborted","AbortError");const h=Array.from(new Uint8Array(c),w=>w.toString(16).padStart(2,"0")).join("");u.push(`${o.type}:${o.size}:${h}`),f.push(await D.createWorkerImageBitmap(o,{imageOrientation:"flipY"})),Reflect.deleteProperty(r,"uri")}return{images:f,imageKeys:u}}catch(r){throw f.forEach(o=>o.close()),r}}async function M(e,t,n={},s=new AbortController().signal,a,i=[]){const l=F(t),{document:d,binaryChunks:f}=B(l);G(d);const u=n.resourceBaseUrl??e,r=await _(d,f,u,e,n,s,a,i),{images:o,imageKeys:c}=await N(d,r,u,e,n,s,a,i);return{protocolVersion:p.MODEL_DECODE_PROTOCOL_VERSION,kind:"gltf-static",url:e,document:d,buffers:r,images:o,imageKeys:c}}async function P(e,t={},n=new AbortController().signal,s){const a=L(e,t,n),i=[],l=await p.loadModelResource(e,a);l.fromCache&&i.push({url:e,options:{headers:a.headers,credentials:a.credentials,cacheKey:a.cacheKey}});try{return await M(e,l.buffer,t,n,s,i)}catch(d){if(n.aborted||d instanceof p.UnsupportedModelDecodeError||i.length===0)throw d;await Promise.all(i.map(u=>p.deleteModelResourceCache(u.url,u.options)));const f=await p.loadModelResource(e,a);return M(e,f.buffer,t,n,s)}}function x(e){return e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:{message:String(e)}}function $(e=globalThis){const t=[],n=new Set;let s=null,a=!1,i=!1;const l=r=>new Promise(o=>{r.acknowledge=o}),d=(r,o)=>{for(const c of r.resources.values())c.cleanup(),c.reject(o);r.resources.clear()},f=r=>({async resolve(o,c){if(c.aborted)throw c.reason??new DOMException("Aborted","AbortError");const h=r.nextResourceId++,w=await new Promise((b,E)=>{const O=()=>{r.resources.delete(h),E(c.reason??new DOMException("Aborted","AbortError"))},m=()=>c.removeEventListener("abort",O);r.resources.set(h,{resolve(y){m(),r.resources.delete(h),b(y)},reject(y){m(),r.resources.delete(h),E(y)},cleanup:m}),c.addEventListener("abort",O,{once:!0});try{e.postMessage({type:"resolve-resource",id:r.id,resourceId:h,url:o})}catch(y){m(),r.resources.delete(h),E(y instanceof Error?y:new Error(String(y)))}});let v=!1;return{url:w,complete(b){v||(v=!0,e.postMessage({type:"resource-complete",id:r.id,resourceId:h,ok:b===void 0,error:b===void 0?void 0:x(b)}))}}}}),u=async()=>{if(!(a||i)){a=!0;try{for(;!i&&t.length>0;){const r=t.shift();if(n.delete(r.id))continue;const o={id:r.id,controller:new AbortController,nextResourceId:1,resources:new Map};s=o;try{const c=await P(r.url,r.options,o.controller.signal,f(o));if(n.delete(r.id)||i){c.images.forEach(h=>h.close());continue}try{e.postMessage({type:"decoded",id:r.id,packet:c},A.getTransferables(c))}catch(h){throw c.images.forEach(w=>w.close()),h}await l(o)}catch(c){const h=n.delete(r.id);if(o.controller.signal.aborted||h||i)continue;c instanceof p.UnsupportedModelDecodeError?e.postMessage({type:"unsupported",id:r.id,reason:c.message}):e.postMessage({type:"error",id:r.id,error:x(c)})}finally{d(o,new DOMException("Model decode request ended.","AbortError")),n.delete(r.id),s===o&&(s=null)}}}finally{a=!1,!i&&t.length>0&&u()}}};e.addEventListener("message",r=>{const o=r.data;if(o.type==="decode"){if(i)return;t.push(o),u();return}if(o.type==="cancel"){n.add(o.id),s?.id===o.id&&(s.controller.abort(),s.acknowledge?.());return}if(o.type==="ack"){s?.id===o.id&&s.acknowledge?.();return}if(o.type==="resource-resolved"){if(s?.id!==o.id)return;const c=s.resources.get(o.resourceId);if(!c)return;if(o.url!==void 0)c.resolve(o.url);else{const h=new Error(o.error?.message??"Failed to resolve model resource URL.");h.name=o.error?.name??"Error",h.stack=o.error?.stack,c.reject(h)}return}o.type==="dispose"&&(i=!0,t.length=0,s?.controller.abort(),s?.acknowledge?.(),e.postMessage({type:"disposed"}),e.close())})}exports.MODEL_DECODE_PROTOCOL_VERSION=p.MODEL_DECODE_PROTOCOL_VERSION;exports.UnsupportedModelDecodeError=p.UnsupportedModelDecodeError;exports.validateDecodedGltfNodeGraph=p.validateDecodedGltfNodeGraph;exports.installModelDecodeWorker=$;
|