@godot-scene-web/project 0.1.0
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/LICENSE +21 -0
- package/dist/fetch.d.ts +147 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +535 -0
- package/dist/fetch.js.map +1 -0
- package/dist/node.d.ts +53 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +316 -0
- package/dist/node.js.map +1 -0
- package/dist/shared-Dgi2oc_H.d.ts +34 -0
- package/dist/shared-Dgi2oc_H.d.ts.map +1 -0
- package/dist/theme-query-ClAPxgZ7.js +43 -0
- package/dist/theme-query-ClAPxgZ7.js.map +1 -0
- package/package.json +62 -0
package/dist/fetch.js
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import { t as queryTheme } from "./theme-query-ClAPxgZ7.js";
|
|
2
|
+
import { asBoolean, asNumber, asRect2, asResourceRef, asString, asVector2, decodeFromNativeValue } from "@godot-scene-web/core";
|
|
3
|
+
import { MOUNTED_INNER_SCENE_PATH_ATTRIBUTE, SOURCE_SCENE_PATH_ATTRIBUTE, tagSceneNodes } from "@godot-scene-web/scene-graph";
|
|
4
|
+
import { parseGodotResource, parseGodotTextScene } from "@godot-scene-web/tscn-parser";
|
|
5
|
+
//#region src/fetch.ts
|
|
6
|
+
function encodeCroppedAtlasKey(atlasUrl, region, margin) {
|
|
7
|
+
const rect = (r) => `${r.x},${r.y},${r.width},${r.height}`;
|
|
8
|
+
return `${atlasUrl}\0${rect(region)}\0${rect(margin)}`;
|
|
9
|
+
}
|
|
10
|
+
function decodeCroppedAtlasKey(key) {
|
|
11
|
+
const [atlasUrl, regionPart, marginPart] = key.split("\0");
|
|
12
|
+
const rect = (part) => {
|
|
13
|
+
const [x, y, width, height] = (part ?? "").split(",").map(Number);
|
|
14
|
+
return {
|
|
15
|
+
x: x || 0,
|
|
16
|
+
y: y || 0,
|
|
17
|
+
width: width || 0,
|
|
18
|
+
height: height || 0
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
atlasUrl: atlasUrl ?? "",
|
|
23
|
+
region: rect(regionPart),
|
|
24
|
+
margin: rect(marginPart)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function createGodotFetchProjectResolver(options = {}) {
|
|
28
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
29
|
+
const notify = () => {
|
|
30
|
+
for (const listener of listeners) listener();
|
|
31
|
+
};
|
|
32
|
+
const subscribe = (listener) => {
|
|
33
|
+
listeners.add(listener);
|
|
34
|
+
return () => listeners.delete(listener);
|
|
35
|
+
};
|
|
36
|
+
let emitScheduled = false;
|
|
37
|
+
const scheduledCacheFlushes = /* @__PURE__ */ new Set();
|
|
38
|
+
const scheduleEmit = (flushCacheListeners) => {
|
|
39
|
+
scheduledCacheFlushes.add(flushCacheListeners);
|
|
40
|
+
if (emitScheduled) return;
|
|
41
|
+
emitScheduled = true;
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
emitScheduled = false;
|
|
44
|
+
const flushes = [...scheduledCacheFlushes];
|
|
45
|
+
scheduledCacheFlushes.clear();
|
|
46
|
+
notify();
|
|
47
|
+
for (const flush of flushes) flush();
|
|
48
|
+
}, 0);
|
|
49
|
+
};
|
|
50
|
+
const fetchText = async (resourcePath) => {
|
|
51
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
52
|
+
if (!fetchImpl) throw new Error("No fetch implementation is available.");
|
|
53
|
+
const response = await fetchImpl(resourcePathToUrl(resourcePath));
|
|
54
|
+
if (!response.ok) throw new Error(`Failed to fetch ${resourcePath}: HTTP ${response.status}`);
|
|
55
|
+
return response.text();
|
|
56
|
+
};
|
|
57
|
+
const urlCache = /* @__PURE__ */ new Map();
|
|
58
|
+
const resourcePathToUrl = (resourcePath) => {
|
|
59
|
+
let url = urlCache.get(resourcePath);
|
|
60
|
+
if (url === void 0) {
|
|
61
|
+
assertResourcePath(resourcePath);
|
|
62
|
+
if (typeof options.assetBaseUrl === "function") url = options.assetBaseUrl(resourcePath);
|
|
63
|
+
else {
|
|
64
|
+
const relativePath = resourcePath.replace(/^res:\/\//, "");
|
|
65
|
+
url = `${(options.assetBaseUrl ?? "/").replace(/\/?$/, "/")}${relativePath}`;
|
|
66
|
+
}
|
|
67
|
+
urlCache.set(resourcePath, url);
|
|
68
|
+
}
|
|
69
|
+
return url;
|
|
70
|
+
};
|
|
71
|
+
let scenesGeneration = 0;
|
|
72
|
+
let resourcesGeneration = 0;
|
|
73
|
+
const scenes = createDocumentCache(async (resourcePath) => {
|
|
74
|
+
const scene = parseSceneBody(await fetchText(resourcePath), resourcePath);
|
|
75
|
+
tagSceneNodes(scene, resourcePath);
|
|
76
|
+
preloadDependenciesOf(scene.extResources);
|
|
77
|
+
return scene;
|
|
78
|
+
}, scheduleEmit, () => {
|
|
79
|
+
scenesGeneration += 1;
|
|
80
|
+
});
|
|
81
|
+
const resources = createDocumentCache((resourcePath) => fetchText(resourcePath).then((text) => {
|
|
82
|
+
const document = parseResourceBody(text, resourcePath);
|
|
83
|
+
preloadDependenciesOf(document.extResources);
|
|
84
|
+
return document;
|
|
85
|
+
}), scheduleEmit, () => {
|
|
86
|
+
resourcesGeneration += 1;
|
|
87
|
+
});
|
|
88
|
+
let croppedAtlasGeneration = 0;
|
|
89
|
+
const croppedAtlas = createDocumentCache((key) => {
|
|
90
|
+
const crop = options.cropAtlasRegion;
|
|
91
|
+
if (!crop) return Promise.reject(/* @__PURE__ */ new Error("cropAtlasRegion seam is not configured"));
|
|
92
|
+
const { atlasUrl, region, margin } = decodeCroppedAtlasKey(key);
|
|
93
|
+
return crop(atlasUrl, region, margin);
|
|
94
|
+
}, scheduleEmit, () => {
|
|
95
|
+
croppedAtlasGeneration += 1;
|
|
96
|
+
});
|
|
97
|
+
let imageSizesGeneration = 0;
|
|
98
|
+
const imageSizes = createDocumentCache((resourcePath) => new Promise((resolve, reject) => {
|
|
99
|
+
const image = new Image();
|
|
100
|
+
image.onload = () => {
|
|
101
|
+
if (image.naturalWidth && image.naturalHeight) resolve({
|
|
102
|
+
width: image.naturalWidth,
|
|
103
|
+
height: image.naturalHeight
|
|
104
|
+
});
|
|
105
|
+
else reject(/* @__PURE__ */ new Error(`Image has no intrinsic size: ${resourcePath}`));
|
|
106
|
+
};
|
|
107
|
+
image.onerror = () => reject(/* @__PURE__ */ new Error(`Image failed to load: ${resourcePath}`));
|
|
108
|
+
image.src = resourcePathToUrl(resourcePath);
|
|
109
|
+
}), scheduleEmit, () => {
|
|
110
|
+
imageSizesGeneration += 1;
|
|
111
|
+
});
|
|
112
|
+
const measuredImageSize = (resourcePath) => {
|
|
113
|
+
if (typeof Image === "undefined" || !isRasterImagePath(resourcePath)) return;
|
|
114
|
+
let snapshot = imageSizes.peek(resourcePath);
|
|
115
|
+
if (!snapshot) {
|
|
116
|
+
imageSizes.load(resourcePath).catch(() => void 0);
|
|
117
|
+
snapshot = imageSizes.peek(resourcePath);
|
|
118
|
+
}
|
|
119
|
+
if (snapshot?.status === "ready") return { size: snapshot.value };
|
|
120
|
+
return snapshot?.status === "pending" ? { status: "pending" } : void 0;
|
|
121
|
+
};
|
|
122
|
+
function preloadDependenciesOf(extResources) {
|
|
123
|
+
if (!options.preloadDependencies) return;
|
|
124
|
+
for (const resource of extResources) {
|
|
125
|
+
const path = resource.path;
|
|
126
|
+
if (!path) continue;
|
|
127
|
+
if (path.endsWith(".tscn")) scenes.preload(path);
|
|
128
|
+
else if (path.endsWith(".tres") || path.endsWith(".res")) resources.preload(path);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const extResource = (scene, ref) => ref.type === "ExtResource" && ref.id !== void 0 ? scene.extResources.find((resource) => resource.id === ref.id) : void 0;
|
|
132
|
+
const extTarget = (extResources, ref) => {
|
|
133
|
+
if (ref.type !== "ExtResource") return;
|
|
134
|
+
if (ref.path) return { path: ref.path };
|
|
135
|
+
const resource = extResources.find((candidate) => candidate.id === ref.id);
|
|
136
|
+
return resource?.path ? {
|
|
137
|
+
path: resource.path,
|
|
138
|
+
type: resource.type
|
|
139
|
+
} : void 0;
|
|
140
|
+
};
|
|
141
|
+
const resolveExternalScene = (scene, ref, _node) => {
|
|
142
|
+
const target = extTarget(scene.extResources, ref);
|
|
143
|
+
if (!target?.path.endsWith(".tscn")) return;
|
|
144
|
+
const snapshot = scenes.peek(target.path);
|
|
145
|
+
if (snapshot?.status === "ready") return {
|
|
146
|
+
status: "ready",
|
|
147
|
+
scene: snapshot.value,
|
|
148
|
+
path: target.path
|
|
149
|
+
};
|
|
150
|
+
if (snapshot?.status === "error") return {
|
|
151
|
+
status: "error",
|
|
152
|
+
path: target.path,
|
|
153
|
+
message: snapshot.message
|
|
154
|
+
};
|
|
155
|
+
if (!snapshot) scenes.load(target.path).catch(() => void 0);
|
|
156
|
+
return {
|
|
157
|
+
status: "pending",
|
|
158
|
+
path: target.path
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
const resolveResource = (scene, ref, _node) => {
|
|
162
|
+
if (ref.type === "SubResource") {
|
|
163
|
+
const resource = scene.subResources.find((candidate) => candidate.id === ref.id);
|
|
164
|
+
return resource ? {
|
|
165
|
+
type: resource.type,
|
|
166
|
+
document: subResourceDocument(resource)
|
|
167
|
+
} : void 0;
|
|
168
|
+
}
|
|
169
|
+
const target = extTarget(scene.extResources, ref);
|
|
170
|
+
return target ? resolvePathResource(target.path, target.type) : void 0;
|
|
171
|
+
};
|
|
172
|
+
const resolveDocumentResource = (document, ref, seen) => {
|
|
173
|
+
if (ref.type === "SubResource") {
|
|
174
|
+
const resource = document.subResources.find((candidate) => candidate.id === ref.id);
|
|
175
|
+
return resource ? {
|
|
176
|
+
type: resource.type,
|
|
177
|
+
document: subResourceDocument(resource)
|
|
178
|
+
} : void 0;
|
|
179
|
+
}
|
|
180
|
+
const target = extTarget(document.extResources, ref);
|
|
181
|
+
return target ? resolvePathResource(target.path, target.type, seen) : void 0;
|
|
182
|
+
};
|
|
183
|
+
const resolvePathResource = (resourcePath, type, seen = /* @__PURE__ */ new Set()) => {
|
|
184
|
+
if (seen.has(resourcePath)) return {
|
|
185
|
+
type,
|
|
186
|
+
path: resourcePath,
|
|
187
|
+
url: resourcePathToUrl(resourcePath)
|
|
188
|
+
};
|
|
189
|
+
const nextSeen = new Set(seen).add(resourcePath);
|
|
190
|
+
if (resourcePath.endsWith(".tscn")) return {
|
|
191
|
+
type,
|
|
192
|
+
path: resourcePath,
|
|
193
|
+
url: resourcePathToUrl(resourcePath)
|
|
194
|
+
};
|
|
195
|
+
if (resourcePath.endsWith(".tres") || resourcePath.endsWith(".res") || resourcePath.includes("::")) {
|
|
196
|
+
const snapshot = resources.peek(resourcePath);
|
|
197
|
+
if (snapshot?.status === "error") return {
|
|
198
|
+
type,
|
|
199
|
+
path: resourcePath,
|
|
200
|
+
url: resourcePathToUrl(resourcePath),
|
|
201
|
+
status: "error",
|
|
202
|
+
message: snapshot.message
|
|
203
|
+
};
|
|
204
|
+
if (snapshot?.status !== "ready") {
|
|
205
|
+
if (!snapshot) resources.load(resourcePath).catch(() => void 0);
|
|
206
|
+
return {
|
|
207
|
+
type,
|
|
208
|
+
path: resourcePath,
|
|
209
|
+
url: resourcePathToUrl(resourcePath),
|
|
210
|
+
status: "pending"
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const document = snapshot.value;
|
|
214
|
+
const resourceType = asString(document.header?.attributes.type) ?? type;
|
|
215
|
+
const baseResource = {
|
|
216
|
+
type: resourceType,
|
|
217
|
+
path: resourcePath,
|
|
218
|
+
url: resourcePathToUrl(resourcePath),
|
|
219
|
+
document
|
|
220
|
+
};
|
|
221
|
+
if (resourceType === "AtlasTexture") {
|
|
222
|
+
const region = asRect2(document.properties.region);
|
|
223
|
+
const margin = asRect2(document.properties.margin) ?? {
|
|
224
|
+
x: 0,
|
|
225
|
+
y: 0,
|
|
226
|
+
width: 0,
|
|
227
|
+
height: 0
|
|
228
|
+
};
|
|
229
|
+
const atlasRef = asResourceRef(document.properties.atlas);
|
|
230
|
+
let atlas = atlasRef ? resolveDocumentResource(document, atlasRef, nextSeen) : void 0;
|
|
231
|
+
const docAtlasSize = asVector2(document.properties.atlas_size);
|
|
232
|
+
if (atlas && !atlas.size && docAtlasSize) atlas = {
|
|
233
|
+
...atlas,
|
|
234
|
+
size: {
|
|
235
|
+
width: docAtlasSize.x,
|
|
236
|
+
height: docAtlasSize.y
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
if (region && atlas?.url && !atlas.url.startsWith("data:") && options.cropAtlasRegion) {
|
|
240
|
+
const key = encodeCroppedAtlasKey(atlas.url, region, margin);
|
|
241
|
+
const snapshot = croppedAtlas.peek(key);
|
|
242
|
+
if (snapshot?.status === "ready") return {
|
|
243
|
+
...baseResource,
|
|
244
|
+
url: snapshot.value,
|
|
245
|
+
atlas,
|
|
246
|
+
size: {
|
|
247
|
+
width: region.width,
|
|
248
|
+
height: region.height
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
if (snapshot?.status !== "error") {
|
|
252
|
+
if (!snapshot) croppedAtlas.load(key).catch(() => void 0);
|
|
253
|
+
return {
|
|
254
|
+
...baseResource,
|
|
255
|
+
url: void 0,
|
|
256
|
+
atlas,
|
|
257
|
+
region,
|
|
258
|
+
size: {
|
|
259
|
+
width: region.width,
|
|
260
|
+
height: region.height
|
|
261
|
+
},
|
|
262
|
+
status: "pending"
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
...baseResource,
|
|
268
|
+
url: void 0,
|
|
269
|
+
atlas,
|
|
270
|
+
region,
|
|
271
|
+
size: region ? {
|
|
272
|
+
width: region.width,
|
|
273
|
+
height: region.height
|
|
274
|
+
} : atlas?.size
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (resourceType === "FontVariation") {
|
|
278
|
+
const baseRef = asResourceRef(document.properties.base_font);
|
|
279
|
+
const base = baseRef ? resolveDocumentResource(document, baseRef, nextSeen) : void 0;
|
|
280
|
+
const variationWeight = fontWeightFromVariation(document);
|
|
281
|
+
const glyphSpacing = asNumber(document.properties.spacing_glyph);
|
|
282
|
+
const fontMsdf = asBoolean(document.properties.multichannel_signed_distance_field);
|
|
283
|
+
return {
|
|
284
|
+
...base,
|
|
285
|
+
...baseResource,
|
|
286
|
+
status: base?.status,
|
|
287
|
+
message: base?.message,
|
|
288
|
+
fontFamily: base?.fontFamily ?? fontMetadataFromPath(resourcePath).fontFamily,
|
|
289
|
+
fontUrl: base?.fontUrl,
|
|
290
|
+
fontStyle: base?.fontStyle,
|
|
291
|
+
fontWeight: variationWeight ?? base?.fontWeight,
|
|
292
|
+
glyphSpacing: glyphSpacing ?? base?.glyphSpacing,
|
|
293
|
+
fontMsdf: fontMsdf ?? base?.fontMsdf
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
if (resourceType === "FontFile") {
|
|
297
|
+
const fontPath = asString(document.properties.font_path);
|
|
298
|
+
const metadata = fontPath ? fontMetadataFromPath(fontPath) : fontMetadataFromPath(resourcePath);
|
|
299
|
+
return {
|
|
300
|
+
...baseResource,
|
|
301
|
+
fontUrl: fontPath && isFontPath(fontPath) ? resourcePathToUrl(fontPath) : void 0,
|
|
302
|
+
fontMsdf: asBoolean(document.properties.multichannel_signed_distance_field),
|
|
303
|
+
...metadata
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
if (resourceType === "ShaderMaterial") {
|
|
307
|
+
const shaderRef = asResourceRef(document.properties.shader);
|
|
308
|
+
return {
|
|
309
|
+
...baseResource,
|
|
310
|
+
shader: shaderRef ? resolveDocumentResource(document, shaderRef, nextSeen) : void 0
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const fontPath = asString(document.properties.font_path);
|
|
314
|
+
return {
|
|
315
|
+
type: resourceType,
|
|
316
|
+
path: resourcePath,
|
|
317
|
+
url: resourcePathToUrl(resourcePath),
|
|
318
|
+
fontUrl: fontPath && isFontPath(fontPath) ? resourcePathToUrl(fontPath) : void 0,
|
|
319
|
+
document,
|
|
320
|
+
...fontMetadataFromPath(resourcePath)
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const hostSize = options.resourceSize?.(resourcePath);
|
|
324
|
+
const measured = hostSize ? void 0 : measuredImageSize(resourcePath);
|
|
325
|
+
return {
|
|
326
|
+
type,
|
|
327
|
+
path: resourcePath,
|
|
328
|
+
url: resourcePathToUrl(resourcePath),
|
|
329
|
+
fontUrl: isFontPath(resourcePath) ? resourcePathToUrl(resourcePath) : void 0,
|
|
330
|
+
size: hostSize ?? measured?.size,
|
|
331
|
+
status: measured?.status,
|
|
332
|
+
...fontMetadataFromPath(resourcePath)
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
const sceneForNode = (node) => {
|
|
336
|
+
const sourcePath = asString(node.properties[SOURCE_SCENE_PATH_ATTRIBUTE]);
|
|
337
|
+
const snapshot = sourcePath ? scenes.peek(sourcePath) : void 0;
|
|
338
|
+
return snapshot?.status === "ready" ? snapshot.value : void 0;
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
resourcePathToUrl,
|
|
342
|
+
scenes,
|
|
343
|
+
resources,
|
|
344
|
+
peekScene: scenes.peek,
|
|
345
|
+
loadScene: scenes.load,
|
|
346
|
+
preload: async (resourcePath) => {
|
|
347
|
+
if (resourcePath.endsWith(".tscn")) await scenes.preload(resourcePath);
|
|
348
|
+
else if (resourcePath.endsWith(".tres") || resourcePath.endsWith(".res")) await resources.preload(resourcePath);
|
|
349
|
+
},
|
|
350
|
+
subscribe,
|
|
351
|
+
generations: () => ({
|
|
352
|
+
scenes: scenesGeneration,
|
|
353
|
+
resources: resourcesGeneration,
|
|
354
|
+
croppedAtlas: croppedAtlasGeneration,
|
|
355
|
+
imageSizes: imageSizesGeneration
|
|
356
|
+
}),
|
|
357
|
+
extResource,
|
|
358
|
+
resolveResource,
|
|
359
|
+
resolveResourcePath: resolvePathResource,
|
|
360
|
+
resolveExternalScene,
|
|
361
|
+
sceneOptions: (scene) => ({
|
|
362
|
+
resolveExternalScene: ({ ref, node }) => {
|
|
363
|
+
const resolved = resolveExternalScene(sceneForNode(node) ?? scene, ref, node);
|
|
364
|
+
if (resolved) return resolved;
|
|
365
|
+
const innerScenePath = node ? asString(node.properties[MOUNTED_INNER_SCENE_PATH_ATTRIBUTE]) : void 0;
|
|
366
|
+
if (innerScenePath) {
|
|
367
|
+
const innerSnap = scenes.peek(innerScenePath);
|
|
368
|
+
if (innerSnap?.status === "ready") return resolveExternalScene(innerSnap.value, ref, node);
|
|
369
|
+
}
|
|
370
|
+
return resolved;
|
|
371
|
+
},
|
|
372
|
+
resolveResource: (ref, node) => {
|
|
373
|
+
const resolved = resolveResource(sceneForNode(node) ?? scene, ref, node);
|
|
374
|
+
if (resolved) return resolved;
|
|
375
|
+
const innerScenePath = asString(node?.properties[MOUNTED_INNER_SCENE_PATH_ATTRIBUTE]);
|
|
376
|
+
if (innerScenePath) {
|
|
377
|
+
const innerSnap = scenes.peek(innerScenePath);
|
|
378
|
+
if (innerSnap?.status === "ready") return resolveResource(innerSnap.value, ref, node);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
resolveResourcePath: (path) => resolvePathResource(path),
|
|
382
|
+
resolveTheme: (node, name) => {
|
|
383
|
+
const themeRef = asResourceRef(node.properties?.theme);
|
|
384
|
+
if (!themeRef) return void 0;
|
|
385
|
+
const doc = resolveResource(sceneForNode(node) ?? scene, themeRef, node)?.document;
|
|
386
|
+
return doc ? queryTheme(doc, node.type, asString(node.properties?.theme_type_variation), name) : void 0;
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function createDocumentCache(loadDocument, scheduleEmit, onSettle) {
|
|
392
|
+
const entries = /* @__PURE__ */ new Map();
|
|
393
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
394
|
+
const flushListeners = () => {
|
|
395
|
+
for (const listener of listeners) listener();
|
|
396
|
+
};
|
|
397
|
+
const load = (resourcePath) => {
|
|
398
|
+
const cached = entries.get(resourcePath);
|
|
399
|
+
if (cached?.status === "ready") return Promise.resolve(cached.value);
|
|
400
|
+
if (cached?.status === "pending") return cached.promise;
|
|
401
|
+
const promise = loadDocument(resourcePath).then((value) => {
|
|
402
|
+
entries.set(resourcePath, {
|
|
403
|
+
status: "ready",
|
|
404
|
+
path: resourcePath,
|
|
405
|
+
value
|
|
406
|
+
});
|
|
407
|
+
onSettle?.();
|
|
408
|
+
scheduleEmit(flushListeners);
|
|
409
|
+
return value;
|
|
410
|
+
}).catch((error) => {
|
|
411
|
+
entries.set(resourcePath, {
|
|
412
|
+
status: "error",
|
|
413
|
+
path: resourcePath,
|
|
414
|
+
message: errorMessage(error),
|
|
415
|
+
error
|
|
416
|
+
});
|
|
417
|
+
onSettle?.();
|
|
418
|
+
scheduleEmit(flushListeners);
|
|
419
|
+
throw error;
|
|
420
|
+
});
|
|
421
|
+
entries.set(resourcePath, {
|
|
422
|
+
status: "pending",
|
|
423
|
+
path: resourcePath,
|
|
424
|
+
promise
|
|
425
|
+
});
|
|
426
|
+
return promise;
|
|
427
|
+
};
|
|
428
|
+
return {
|
|
429
|
+
peek: (resourcePath) => entries.get(resourcePath),
|
|
430
|
+
load,
|
|
431
|
+
preload: (resourcePath) => load(resourcePath).then(() => void 0).catch(() => void 0),
|
|
432
|
+
subscribe: (listener) => {
|
|
433
|
+
listeners.add(listener);
|
|
434
|
+
return () => listeners.delete(listener);
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function parseSceneBody(body, resourcePath) {
|
|
439
|
+
const trimmed = body.trimStart();
|
|
440
|
+
if (trimmed.startsWith("{")) try {
|
|
441
|
+
const parsed = JSON.parse(trimmed);
|
|
442
|
+
if (parsed?.kind === "scene" && Array.isArray(parsed.nodes)) {
|
|
443
|
+
const state = {
|
|
444
|
+
kind: "scene",
|
|
445
|
+
nodes: parsed.nodes,
|
|
446
|
+
connections: parsed.connections ?? [],
|
|
447
|
+
extResources: parsed.extResources ?? [],
|
|
448
|
+
subResources: parsed.subResources ?? [],
|
|
449
|
+
editableInstances: parsed.editableInstances ?? [],
|
|
450
|
+
basePath: parsed.basePath,
|
|
451
|
+
diagnostics: parsed.diagnostics ?? []
|
|
452
|
+
};
|
|
453
|
+
if (parsed.valueEncoding === "from_native") normalizeFromNativeSceneValues(state);
|
|
454
|
+
return state;
|
|
455
|
+
}
|
|
456
|
+
} catch {}
|
|
457
|
+
return parseGodotTextScene(body, { path: resourcePath });
|
|
458
|
+
}
|
|
459
|
+
function normalizeFromNativeSceneValues(state) {
|
|
460
|
+
for (const node of state.nodes) for (const property of node.properties) property.value = decodeFromNativeValue(property.value);
|
|
461
|
+
for (const subResource of state.subResources) {
|
|
462
|
+
if (subResource.attributes) subResource.attributes = mapRecordValues(subResource.attributes);
|
|
463
|
+
if (subResource.properties) subResource.properties = mapRecordValues(subResource.properties);
|
|
464
|
+
}
|
|
465
|
+
for (const connection of state.connections) if (connection.binds) connection.binds = connection.binds.map(decodeFromNativeValue);
|
|
466
|
+
}
|
|
467
|
+
function mapRecordValues(record) {
|
|
468
|
+
const result = {};
|
|
469
|
+
for (const [key, value] of Object.entries(record)) result[key] = decodeFromNativeValue(value);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
function parseResourceBody(body, resourcePath) {
|
|
473
|
+
const trimmed = body.trimStart();
|
|
474
|
+
if (trimmed.startsWith("{")) try {
|
|
475
|
+
const parsed = JSON.parse(trimmed);
|
|
476
|
+
if (parsed?.kind === "resource") {
|
|
477
|
+
const resource = {
|
|
478
|
+
type: parsed.type ?? asString(parsed.header?.attributes?.type),
|
|
479
|
+
header: parsed.header ?? null,
|
|
480
|
+
extResources: parsed.extResources ?? [],
|
|
481
|
+
subResources: parsed.subResources ?? [],
|
|
482
|
+
properties: parsed.properties ?? {},
|
|
483
|
+
diagnostics: parsed.diagnostics ?? []
|
|
484
|
+
};
|
|
485
|
+
if (parsed.valueEncoding === "from_native") {
|
|
486
|
+
resource.properties = mapRecordValues(resource.properties);
|
|
487
|
+
for (const subResource of resource.subResources) if (subResource.properties) subResource.properties = mapRecordValues(subResource.properties);
|
|
488
|
+
}
|
|
489
|
+
return resource;
|
|
490
|
+
}
|
|
491
|
+
} catch {}
|
|
492
|
+
return parseGodotResource(body, { path: resourcePath });
|
|
493
|
+
}
|
|
494
|
+
function assertResourcePath(resourcePath) {
|
|
495
|
+
if (!resourcePath.startsWith("res://")) throw new Error(`Expected a Godot resource path starting with res://, got ${resourcePath}`);
|
|
496
|
+
}
|
|
497
|
+
function subResourceDocument(resource) {
|
|
498
|
+
return {
|
|
499
|
+
type: resource.type,
|
|
500
|
+
header: resource.type ? {
|
|
501
|
+
section: "gd_resource",
|
|
502
|
+
attributes: { type: resource.type }
|
|
503
|
+
} : null,
|
|
504
|
+
extResources: [],
|
|
505
|
+
subResources: [],
|
|
506
|
+
properties: resource.properties,
|
|
507
|
+
diagnostics: []
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function isFontPath(path) {
|
|
511
|
+
return /\.(?:ttf|otf|woff2?|ttc)$/i.test(path);
|
|
512
|
+
}
|
|
513
|
+
function isRasterImagePath(path) {
|
|
514
|
+
return /\.(?:png|webp|jpe?g|gif|bmp)$/i.test(path);
|
|
515
|
+
}
|
|
516
|
+
function fontMetadataFromPath(path) {
|
|
517
|
+
if (!isFontPath(path)) return {};
|
|
518
|
+
return {
|
|
519
|
+
fontFamily: path.split("/").at(-1)?.replace(/\.[^.]+$/, ""),
|
|
520
|
+
fontStyle: /italic/i.test(path) ? "italic" : "normal",
|
|
521
|
+
fontWeight: /bold/i.test(path) ? "700" : "400"
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function fontWeightFromVariation(document) {
|
|
525
|
+
const variation = document.properties.variation_opentype;
|
|
526
|
+
if (!variation || typeof variation !== "object" || Array.isArray(variation) || "type" in variation) return;
|
|
527
|
+
return asNumber(variation["2003265652"]);
|
|
528
|
+
}
|
|
529
|
+
function errorMessage(error) {
|
|
530
|
+
return error instanceof Error ? error.message : String(error);
|
|
531
|
+
}
|
|
532
|
+
//#endregion
|
|
533
|
+
export { createGodotFetchProjectResolver };
|
|
534
|
+
|
|
535
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","names":[],"sources":["../src/fetch.ts"],"sourcesContent":["import {\n asBoolean,\n asNumber,\n asRect2,\n asResourceRef,\n asString,\n asVector2,\n decodeFromNativeValue,\n type GodotExtResource,\n type GodotNode,\n type GodotResource,\n type GodotResourceRefValue,\n type GodotSceneState,\n type GodotVariant,\n} from \"@godot-scene-web/core\";\nimport {\n MOUNTED_INNER_SCENE_PATH_ATTRIBUTE,\n SOURCE_SCENE_PATH_ATTRIBUTE,\n tagSceneNodes,\n} from \"@godot-scene-web/scene-graph\";\nimport {\n parseGodotResource,\n parseGodotTextScene,\n} from \"@godot-scene-web/tscn-parser\";\nimport type { GodotProjectResolvedResource } from \"./shared\";\nimport { queryTheme } from \"./theme-query\";\n\nexport type { GodotProjectResolvedResource } from \"./shared\";\n\n// Set by scene-graph's `mergeMountedNode` on a merged mount (instance root OR\n// overridden child): the INNER (instanced) scene a node was authored in, when the\n// merged node's `source_scene_path` took the OUTER placeholder scene. Lets the\n// resolver fall back to the inner scene for the node's own base-authored ext refs.\n\ntype AtlasRect = { x: number; y: number; width: number; height: number };\n\n// The `croppedAtlas` cache is keyed by (atlas page url + region + margin) so each\n// distinct sprite crop loads once. Encoded as a single string (the cache key type) with\n// a NUL separator that can't appear in a url or number.\nfunction encodeCroppedAtlasKey(\n atlasUrl: string,\n region: AtlasRect,\n margin: AtlasRect,\n): string {\n const rect = (r: AtlasRect): string => `${r.x},${r.y},${r.width},${r.height}`;\n return `${atlasUrl}\\0${rect(region)}\\0${rect(margin)}`;\n}\n\nfunction decodeCroppedAtlasKey(key: string): {\n atlasUrl: string;\n region: AtlasRect;\n margin: AtlasRect;\n} {\n const [atlasUrl, regionPart, marginPart] = key.split(\"\\0\");\n const rect = (part: string | undefined): AtlasRect => {\n const [x, y, width, height] = (part ?? \"\").split(\",\").map(Number);\n return { x: x || 0, y: y || 0, width: width || 0, height: height || 0 };\n };\n return {\n atlasUrl: atlasUrl ?? \"\",\n region: rect(regionPart),\n margin: rect(marginPart),\n };\n}\n\nexport type GodotFetchCacheSnapshot<T> =\n | { status: \"ready\"; path: string; value: T }\n | { status: \"pending\"; path: string; promise: Promise<T> }\n | { status: \"error\"; path: string; message: string; error: unknown };\n\nexport interface GodotFetchDocumentCache<T> {\n peek: (resourcePath: string) => GodotFetchCacheSnapshot<T> | undefined;\n load: (resourcePath: string) => Promise<T>;\n preload: (resourcePath: string) => Promise<void>;\n subscribe: (listener: () => void) => () => void;\n}\n\nexport interface GodotFetchProjectResolverOptions {\n assetBaseUrl?: string | ((resourcePath: string) => string);\n fetch?: (input: string) => Promise<Pick<Response, \"ok\" | \"status\" | \"text\">>;\n /**\n * Transitive prefetch: the moment a scene/resource document parses, kick loads\n * for every `.tscn`/`.tres`/`.res` it references, so a tree's dependencies fan\n * out level-parallel instead of being discovered one consumer render at a time.\n * Off by default — lazy consumers rely on hidden external scenes NOT fetching.\n */\n preloadDependencies?: boolean;\n /**\n * Intrinsic pixel size for a direct image resource (a `Texture2D` ExtResource\n * pointing at a `.png`/`.webp`/…), from host asset metadata (captured/extracted).\n * Used so `NinePatchRect` 9-slice margins that exceed the texture (event_button.png:\n * 284px wide, 192px L/R margins) clamp to the source the way Godot does, and so a\n * `Sprite2D` box can size to its texture. Return `undefined` when unknown — in a\n * browser the resolver then falls back to measuring the decoded image itself (see\n * the `imageSizes` cache); outside a browser the texture resolves without a size,\n * as before.\n */\n resourceSize?: (\n resourcePath: string,\n ) => { width: number; height: number } | undefined;\n /**\n * Crop an AtlasTexture sprite (a region of a larger atlas page) into a standalone\n * image, returning a usable URL (e.g. a `blob:`/`data:` URL the host produced with a\n * canvas). Used ONLY when the atlas page resolves to an EXTERNAL url (not a `data:`\n * URL): the renderer's built-in SVG crop embeds the atlas via `<image href>`, which\n * browsers block for external refs inside CSS-image SVGs, and CSS `border-image`\n * cannot 9-slice a sub-region of an atlas — so a NinePatch atlas sprite over an\n * external atlas can't be cropped in pure CSS. The host fetches each atlas page ONCE\n * (cached) and crops regions on a canvas, so the page is reused across its sprites.\n * Async: the result settles through the resolver's cache like any other resource, so\n * the consumer re-renders when the crop is ready. Absent ⇒ unchanged behavior (the\n * sprite keeps its embedded-SVG crop / CSS-offset fallback), so goldens and hosts that\n * pre-crop server-side are byte-identical.\n */\n cropAtlasRegion?: (\n atlasUrl: string,\n region: { x: number; y: number; width: number; height: number },\n margin: { x: number; y: number; width: number; height: number },\n ) => Promise<string>;\n}\n\nexport interface GodotFetchExternalSceneResolveContext {\n ref: GodotResourceRefValue;\n node: GodotNode;\n nodePath: string;\n props: Record<string, GodotVariant>;\n}\n\nexport interface GodotFetchProjectSceneOptions {\n resolveExternalScene: (\n context: GodotFetchExternalSceneResolveContext,\n ) =>\n | GodotSceneState\n | { status: \"ready\"; scene: GodotSceneState; path?: string }\n | { status: \"pending\"; path?: string; message?: string }\n | { status: \"error\"; path?: string; message: string }\n | undefined;\n resolveResource: (\n ref: GodotResourceRefValue,\n node: GodotNode,\n ) => GodotProjectResolvedResource | undefined;\n resolveResourcePath: (\n path: string,\n node: GodotNode,\n ) => GodotProjectResolvedResource | undefined;\n /**\n * Resolve a theme item (e.g. a font size) for a node from its assigned `theme`\n * resource, following Godot's type-variation -> type -> default cascade. Optional:\n * absent ⇒ the renderer keeps its built-in fallbacks (the prior behavior). The HTML\n * renderer calls this for `font_size`/`normal_font_size`/… when a node carries no\n * inline `theme_override_font_sizes/*`.\n */\n resolveTheme?: (node: GodotNode, name: string) => GodotVariant | undefined;\n}\n\nexport interface GodotFetchProjectResolver {\n resourcePathToUrl: (resourcePath: string) => string;\n scenes: GodotFetchDocumentCache<GodotSceneState>;\n resources: GodotFetchDocumentCache<GodotResource>;\n peekScene: (\n resourcePath: string,\n ) => GodotFetchCacheSnapshot<GodotSceneState> | undefined;\n loadScene: (resourcePath: string) => Promise<GodotSceneState>;\n preload: (resourcePath: string) => Promise<void>;\n subscribe: (listener: () => void) => () => void;\n extResource: (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n ) => GodotExtResource | undefined;\n resolveResource: (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n node?: GodotNode,\n ) => GodotProjectResolvedResource | undefined;\n resolveResourcePath: (\n resourcePath: string,\n type?: string,\n ) => GodotProjectResolvedResource | undefined;\n resolveExternalScene: (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n node?: GodotNode,\n ) =>\n | GodotSceneState\n | { status: \"ready\"; scene: GodotSceneState; path?: string }\n | { status: \"pending\"; path?: string; message?: string }\n | { status: \"error\"; path?: string; message: string }\n | undefined;\n sceneOptions: (scene: GodotSceneState) => GodotFetchProjectSceneOptions;\n /**\n * Monotonic settle counters, one per document cache. A consumer can use them\n * for cache-state-keyed memoization: scene-graph derivation depends on scene\n * documents but never on `.tres` resource content, so a derive memo stays\n * valid while `scenes` is unchanged even as `resources` advances.\n */\n generations: () => {\n scenes: number;\n resources: number;\n croppedAtlas: number;\n imageSizes: number;\n };\n}\n\nexport function createGodotFetchProjectResolver(\n options: GodotFetchProjectResolverOptions = {},\n): GodotFetchProjectResolver {\n const listeners = new Set<() => void>();\n const notify = (): void => {\n for (const listener of listeners) {\n listener();\n }\n };\n const subscribe = (listener: () => void): (() => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n // ONE deferred emit per settle burst, shared across the scene + resource caches —\n // every emit makes subscribers re-derive expensive state, so a burst of N settles\n // across both caches must cost one notification, not N (and never a synchronous\n // one: an emit inside a consumer's walk would re-enter it). setTimeout rather than\n // queueMicrotask so settles landing in separate macrotasks still merge.\n let emitScheduled = false;\n const scheduledCacheFlushes = new Set<() => void>();\n const scheduleEmit = (flushCacheListeners: () => void): void => {\n scheduledCacheFlushes.add(flushCacheListeners);\n if (emitScheduled) {\n return;\n }\n emitScheduled = true;\n setTimeout(() => {\n emitScheduled = false;\n const flushes = [...scheduledCacheFlushes];\n scheduledCacheFlushes.clear();\n notify();\n for (const flush of flushes) {\n flush();\n }\n }, 0);\n };\n const fetchText = async (resourcePath: string): Promise<string> => {\n const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);\n if (!fetchImpl) {\n throw new Error(\"No fetch implementation is available.\");\n }\n const response = await fetchImpl(resourcePathToUrl(resourcePath));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch ${resourcePath}: HTTP ${response.status}`,\n );\n }\n return response.text();\n };\n // Memoized by path: `resolvePathResource` resolves every node's resource ref each render and\n // calls this once per ref, with a small recurring set of paths. `assetBaseUrl` is fixed for\n // the client's lifetime, so the URL is stable per path.\n const urlCache = new Map<string, string>();\n const resourcePathToUrl = (resourcePath: string): string => {\n let url = urlCache.get(resourcePath);\n if (url === undefined) {\n assertResourcePath(resourcePath);\n if (typeof options.assetBaseUrl === \"function\") {\n url = options.assetBaseUrl(resourcePath);\n } else {\n const relativePath = resourcePath.replace(/^res:\\/\\//, \"\");\n const base = options.assetBaseUrl ?? \"/\";\n url = `${base.replace(/\\/?$/, \"/\")}${relativePath}`;\n }\n urlCache.set(resourcePath, url);\n }\n return url;\n };\n let scenesGeneration = 0;\n let resourcesGeneration = 0;\n const scenes = createDocumentCache(\n async (resourcePath) => {\n const scene = parseSceneBody(await fetchText(resourcePath), resourcePath);\n tagSceneNodes(scene, resourcePath);\n preloadDependenciesOf(scene.extResources);\n return scene;\n },\n scheduleEmit,\n () => {\n scenesGeneration += 1;\n },\n );\n const resources = createDocumentCache(\n (resourcePath) =>\n fetchText(resourcePath).then((text) => {\n const document = parseResourceBody(text, resourcePath);\n preloadDependenciesOf(document.extResources);\n return document;\n }),\n scheduleEmit,\n () => {\n resourcesGeneration += 1;\n },\n );\n // Host-cropped AtlasTexture sprites (the `cropAtlasRegion` seam). Each (atlas page,\n // region, margin) crops once; the result settles through the SAME shared emit as the\n // scene/resource caches, so a consumer that rendered a pending sprite re-renders when\n // its crop is ready. Only used for EXTERNAL atlas pages (see resolvePathResource).\n let croppedAtlasGeneration = 0;\n const croppedAtlas = createDocumentCache<string>(\n (key) => {\n const crop = options.cropAtlasRegion;\n if (!crop) {\n return Promise.reject(\n new Error(\"cropAtlasRegion seam is not configured\"),\n );\n }\n const { atlasUrl, region, margin } = decodeCroppedAtlasKey(key);\n return crop(atlasUrl, region, margin);\n },\n scheduleEmit,\n () => {\n croppedAtlasGeneration += 1;\n },\n );\n // Measured intrinsic raster sizes — the browser-only fallback when the host\n // supplies no `resourceSize` for a direct image. A Sprite2D has no anchors or\n // offsets: Godot derives its rect from the TEXTURE — `Sprite2D::get_rect()` is\n // texture size × the node's scale, CENTERED on `position` unless\n // `centered = false`/`offset` shifts it (scene/2d/sprite_2d.cpp) — so a resolver\n // that never learns raster dimensions collapses such sprites to a 0×0 box, and\n // anything sized by that box (e.g. a SCREEN_TEXTURE shader canvas on the Neow\n // event's `water effect` sprites) never shows. Measuring costs no extra bytes:\n // the SAME url is already painted by the page's CSS, so the browser shares one\n // fetch/decode — this only reads the intrinsic size. Each path measures ONCE;\n // the result settles through the shared deferred emit like any other cache, and\n // the resolved resource carries `status: \"pending\"` while the measure is in\n // flight so per-node consumer caches re-derive on settle (the same contract as\n // `croppedAtlas` above). Errors settle to \"no size\" — the prior behavior.\n let imageSizesGeneration = 0;\n const imageSizes = createDocumentCache<{ width: number; height: number }>(\n (resourcePath) =>\n new Promise((resolve, reject) => {\n const image = new Image();\n // No `crossOrigin`: a cross-origin texture served without CORS headers\n // still reports its intrinsic size (pixels are never read) — the same\n // choice as tint-bake's nine-patch size loader.\n image.onload = () => {\n if (image.naturalWidth && image.naturalHeight) {\n resolve({\n width: image.naturalWidth,\n height: image.naturalHeight,\n });\n } else {\n reject(new Error(`Image has no intrinsic size: ${resourcePath}`));\n }\n };\n image.onerror = () =>\n reject(new Error(`Image failed to load: ${resourcePath}`));\n image.src = resourcePathToUrl(resourcePath);\n }),\n scheduleEmit,\n () => {\n imageSizesGeneration += 1;\n },\n );\n const measuredImageSize = (\n resourcePath: string,\n ):\n | { size: { width: number; height: number }; status?: undefined }\n | { size?: undefined; status: \"pending\" }\n | undefined => {\n if (typeof Image === \"undefined\" || !isRasterImagePath(resourcePath)) {\n return undefined;\n }\n let snapshot = imageSizes.peek(resourcePath);\n if (!snapshot) {\n void imageSizes.load(resourcePath).catch(() => undefined);\n snapshot = imageSizes.peek(resourcePath);\n }\n if (snapshot?.status === \"ready\") {\n return { size: snapshot.value };\n }\n return snapshot?.status === \"pending\" ? { status: \"pending\" } : undefined;\n };\n\n // Hoisted (function declaration) so the cache loaders above can call it: it\n // needs both caches, the caches need their loaders first.\n function preloadDependenciesOf(extResources: GodotExtResource[]): void {\n if (!options.preloadDependencies) {\n return;\n }\n for (const resource of extResources) {\n const path = resource.path;\n if (!path) {\n continue;\n }\n if (path.endsWith(\".tscn\")) {\n void scenes.preload(path);\n } else if (path.endsWith(\".tres\") || path.endsWith(\".res\")) {\n void resources.preload(path);\n }\n }\n }\n\n // Public id-based accessor: returns the ext-resource table entry for a\n // scene-local `id` ref (path-only runtime refs have no table entry).\n const extResource = (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n ): GodotExtResource | undefined =>\n ref.type === \"ExtResource\" && ref.id !== undefined\n ? scene.extResources.find((resource) => resource.id === ref.id)\n : undefined;\n\n // Resolve an ExtResource ref to a res:// path (+ table type when known).\n // Path-first, id-fallback: a runtime producer supplies `ref.path` directly; a\n // text producer supplies a scene-local `id` we look up in the ext-resource\n // table. A path-only ref has no table entry, so its `type` is unknown.\n const extTarget = (\n extResources: GodotExtResource[],\n ref: GodotResourceRefValue,\n ): { path: string; type?: string } | undefined => {\n if (ref.type !== \"ExtResource\") {\n return undefined;\n }\n if (ref.path) {\n return { path: ref.path };\n }\n const resource = extResources.find((candidate) => candidate.id === ref.id);\n return resource?.path\n ? { path: resource.path, type: resource.type }\n : undefined;\n };\n\n const resolveExternalScene = (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n _node?: GodotNode,\n ): ReturnType<GodotFetchProjectResolver[\"resolveExternalScene\"]> => {\n const target = extTarget(scene.extResources, ref);\n if (!target?.path.endsWith(\".tscn\")) {\n return undefined;\n }\n const snapshot = scenes.peek(target.path);\n if (snapshot?.status === \"ready\") {\n return { status: \"ready\", scene: snapshot.value, path: target.path };\n }\n if (snapshot?.status === \"error\") {\n return {\n status: \"error\",\n path: target.path,\n message: snapshot.message,\n };\n }\n if (!snapshot) {\n void scenes.load(target.path).catch(() => undefined);\n }\n return { status: \"pending\", path: target.path };\n };\n\n const resolveResource = (\n scene: GodotSceneState,\n ref: GodotResourceRefValue,\n _node?: GodotNode,\n ): GodotProjectResolvedResource | undefined => {\n if (ref.type === \"SubResource\") {\n const resource = scene.subResources.find(\n (candidate) => candidate.id === ref.id,\n );\n return resource\n ? { type: resource.type, document: subResourceDocument(resource) }\n : undefined;\n }\n const target = extTarget(scene.extResources, ref);\n return target ? resolvePathResource(target.path, target.type) : undefined;\n };\n\n const resolveDocumentResource = (\n document: GodotResource,\n ref: GodotResourceRefValue,\n seen: Set<string>,\n ): GodotProjectResolvedResource | undefined => {\n if (ref.type === \"SubResource\") {\n const resource = document.subResources.find(\n (candidate) => candidate.id === ref.id,\n );\n return resource\n ? { type: resource.type, document: subResourceDocument(resource) }\n : undefined;\n }\n const target = extTarget(document.extResources, ref);\n return target\n ? resolvePathResource(target.path, target.type, seen)\n : undefined;\n };\n\n const resolvePathResource = (\n resourcePath: string,\n type?: string,\n seen: Set<string> = new Set(),\n ): GodotProjectResolvedResource | undefined => {\n if (seen.has(resourcePath)) {\n return { type, path: resourcePath, url: resourcePathToUrl(resourcePath) };\n }\n const nextSeen = new Set(seen).add(resourcePath);\n if (resourcePath.endsWith(\".tscn\")) {\n return { type, path: resourcePath, url: resourcePathToUrl(resourcePath) };\n }\n // `.tres`/`.res` files and scene/resource-local sub-resources (`res://x.tscn::SubId`,\n // emitted by a runtime producer that holds the loaded sub-resource) are fetched as\n // resource DOCUMENTS so e.g. a scene-local FontVariation resolves its base_font -> .ttf.\n if (\n resourcePath.endsWith(\".tres\") ||\n resourcePath.endsWith(\".res\") ||\n resourcePath.includes(\"::\")\n ) {\n const snapshot = resources.peek(resourcePath);\n if (snapshot?.status === \"error\") {\n return {\n type,\n path: resourcePath,\n url: resourcePathToUrl(resourcePath),\n status: \"error\",\n message: snapshot.message,\n };\n }\n if (snapshot?.status !== \"ready\") {\n if (!snapshot) {\n void resources.load(resourcePath).catch(() => undefined);\n }\n return {\n type,\n path: resourcePath,\n url: resourcePathToUrl(resourcePath),\n status: \"pending\",\n };\n }\n const document = snapshot.value;\n const resourceType = asString(document.header?.attributes.type) ?? type;\n const baseResource = {\n type: resourceType,\n path: resourcePath,\n url: resourcePathToUrl(resourcePath),\n document,\n };\n if (resourceType === \"AtlasTexture\") {\n const region = asRect2(document.properties.region);\n const margin = asRect2(document.properties.margin) ?? {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n };\n const atlasRef = asResourceRef(document.properties.atlas);\n let atlas = atlasRef\n ? resolveDocumentResource(document, atlasRef, nextSeen)\n : undefined;\n // The atlas PAGE size lets a downstream consumer scale the cropped region into a box that\n // differs from the sprite's native size (CSS background scaling). A host that fetches only\n // URLs (no `resourceSize`) can't size the page image, so the document carries `atlas_size`\n // (a runtime producer emits it). Attach it to the resolved atlas when the host didn't supply\n // a size, so `imageResource`/`regionBackgroundStyle` scale instead of falling back to native px.\n const docAtlasSize = asVector2(document.properties.atlas_size);\n if (atlas && !atlas.size && docAtlasSize) {\n atlas = {\n ...atlas,\n size: { width: docAtlasSize.x, height: docAtlasSize.y },\n };\n }\n // External atlas page + host crop seam → crop the region into a standalone\n // sprite url (a `blob:`/`data:` the host's canvas produced). Needed because an\n // external atlas can't be referenced from a CSS-image SVG and CSS can't 9-slice\n // a sub-region. The crop settles through `croppedAtlas`, so a pending sprite\n // re-renders when ready. `data:` atlases (embedded) keep the built-in SVG crop.\n if (\n region &&\n atlas?.url &&\n !atlas.url.startsWith(\"data:\") &&\n options.cropAtlasRegion\n ) {\n const key = encodeCroppedAtlasKey(atlas.url, region, margin);\n const snapshot = croppedAtlas.peek(key);\n if (snapshot?.status === \"ready\") {\n return {\n ...baseResource,\n url: snapshot.value,\n atlas,\n size: { width: region.width, height: region.height },\n };\n }\n if (snapshot?.status !== \"error\") {\n if (!snapshot) {\n void croppedAtlas.load(key).catch(() => undefined);\n }\n return {\n ...baseResource,\n url: undefined,\n atlas,\n region,\n size: { width: region.width, height: region.height },\n status: \"pending\",\n };\n }\n // Crop errored: fall through to the standard region/atlas resolution so the\n // sprite still paints its CSS-offset / embedded fallback.\n }\n return {\n ...baseResource,\n url: undefined,\n atlas,\n region,\n size: region\n ? { width: region.width, height: region.height }\n : atlas?.size,\n };\n }\n if (resourceType === \"FontVariation\") {\n const baseRef = asResourceRef(document.properties.base_font);\n const base = baseRef\n ? resolveDocumentResource(document, baseRef, nextSeen)\n : undefined;\n const variationWeight = fontWeightFromVariation(document);\n const glyphSpacing = asNumber(document.properties.spacing_glyph);\n const fontMsdf = asBoolean(\n document.properties.multichannel_signed_distance_field,\n );\n return {\n ...base,\n ...baseResource,\n status: base?.status,\n message: base?.message,\n fontFamily:\n base?.fontFamily ?? fontMetadataFromPath(resourcePath).fontFamily,\n fontUrl: base?.fontUrl,\n fontStyle: base?.fontStyle,\n fontWeight: variationWeight ?? base?.fontWeight,\n glyphSpacing: glyphSpacing ?? base?.glyphSpacing,\n fontMsdf: fontMsdf ?? base?.fontMsdf,\n };\n }\n if (resourceType === \"FontFile\") {\n const fontPath = asString(document.properties.font_path);\n const metadata = fontPath\n ? fontMetadataFromPath(fontPath)\n : fontMetadataFromPath(resourcePath);\n return {\n ...baseResource,\n fontUrl:\n fontPath && isFontPath(fontPath)\n ? resourcePathToUrl(fontPath)\n : undefined,\n fontMsdf: asBoolean(\n document.properties.multichannel_signed_distance_field,\n ),\n ...metadata,\n };\n }\n if (resourceType === \"ShaderMaterial\") {\n const shaderRef = asResourceRef(document.properties.shader);\n return {\n ...baseResource,\n shader: shaderRef\n ? resolveDocumentResource(document, shaderRef, nextSeen)\n : undefined,\n };\n }\n const fontPath = asString(document.properties.font_path);\n return {\n type: resourceType,\n path: resourcePath,\n url: resourcePathToUrl(resourcePath),\n fontUrl:\n fontPath && isFontPath(fontPath)\n ? resourcePathToUrl(fontPath)\n : undefined,\n document,\n ...fontMetadataFromPath(resourcePath),\n };\n }\n // A direct raster ExtResource (`Texture2D` → .png): the host-supplied intrinsic\n // size wins (authoritative metadata, works outside browsers); otherwise fall\n // back to the one-shot browser measure (see `imageSizes`), whose in-flight\n // window is surfaced as `status: \"pending\"` so consumers re-resolve on settle.\n const hostSize = options.resourceSize?.(resourcePath);\n const measured = hostSize ? undefined : measuredImageSize(resourcePath);\n return {\n type,\n path: resourcePath,\n url: resourcePathToUrl(resourcePath),\n fontUrl: isFontPath(resourcePath)\n ? resourcePathToUrl(resourcePath)\n : undefined,\n size: hostSize ?? measured?.size,\n status: measured?.status,\n ...fontMetadataFromPath(resourcePath),\n };\n };\n\n const sceneForNode = (node: GodotNode): GodotSceneState | undefined => {\n const sourcePath = asString(node.properties[SOURCE_SCENE_PATH_ATTRIBUTE]);\n const snapshot = sourcePath ? scenes.peek(sourcePath) : undefined;\n return snapshot?.status === \"ready\" ? snapshot.value : undefined;\n };\n\n return {\n resourcePathToUrl,\n scenes,\n resources,\n peekScene: scenes.peek,\n loadScene: scenes.load,\n preload: async (resourcePath) => {\n if (resourcePath.endsWith(\".tscn\")) {\n await scenes.preload(resourcePath);\n } else if (\n resourcePath.endsWith(\".tres\") ||\n resourcePath.endsWith(\".res\")\n ) {\n await resources.preload(resourcePath);\n }\n },\n subscribe,\n generations: () => ({\n scenes: scenesGeneration,\n resources: resourcesGeneration,\n croppedAtlas: croppedAtlasGeneration,\n imageSizes: imageSizesGeneration,\n }),\n extResource,\n resolveResource,\n resolveResourcePath: resolvePathResource,\n resolveExternalScene,\n sceneOptions: (scene) => ({\n // Resource resolution is NOT gated by visibility: the renderer resolves resources for\n // ALL nodes (a hidden node still renders with `visibility:hidden` and needs its styles\n // — see html.test.ts \"renders ... hidden styles\"). Only EXTERNAL SCENE fetches are\n // visibility-gated, by the caller's EFFECTIVE/merged `effectivelyVisible`\n // (scene-index.ts). A prior guard here gated resource resolution on the raw\n // SOURCE-authored `node.properties.visible`, which a catalog `visible` override does\n // NOT change — so it dropped authored textures on override-shown nodes (the char-select\n // selection outline), diverging from the eager/CEL path. Resolve unconditionally.\n resolveExternalScene: ({ ref, node }) => {\n const resolved = resolveExternalScene(\n sceneForNode(node) ?? scene,\n ref,\n node,\n );\n if (resolved) return resolved;\n // Inner-scene fallback for a merged mount (instance ROOT or OVERRIDDEN CHILD): the\n // merged node takes the OUTER placeholder scene's `source_scene_path`, but its OWN\n // `instance=ExtResource(...)` id is LOCAL to the INNER instanced scene — which the\n // outer scene doesn't define (deck_view overriding card_grid's `Scrollbar`, whose\n // instance id belongs to card_grid; resolving it against deck_view fails, so the\n // scrollbar.tscn instance never mounts → a childless `Node`). Outer is tried FIRST so\n // override-authored refs win; mirrors the `resolveResource` fallback below.\n const innerScenePath = node\n ? asString(node.properties[MOUNTED_INNER_SCENE_PATH_ATTRIBUTE])\n : undefined;\n if (innerScenePath) {\n const innerSnap = scenes.peek(innerScenePath);\n if (innerSnap?.status === \"ready\") {\n return resolveExternalScene(innerSnap.value, ref, node);\n }\n }\n return resolved;\n },\n resolveResource: (ref, node) => {\n const scope = sceneForNode(node) ?? scene;\n const resolved = resolveResource(scope, ref, node);\n if (resolved) return resolved;\n // Inner-scene fallback for a merged mount (instance ROOT or OVERRIDDEN CHILD):\n // the merged node takes the OUTER placeholder scene's `source_scene_path`, but\n // its own base-authored refs (e.g. a TextureRect's `texture`) use ext ids LOCAL\n // to the INNER instanced scene, which the outer scene doesn't define.\n // `mergeMountedNode` records that inner scene; retry the ref there. Outer is\n // tried FIRST, so override-authored refs (e.g. an outer-scene SubResource\n // material) still resolve against the outer scene. (Regression: defeat-screen\n // banner root + Continue-button `Image` child rendered blank.)\n const innerScenePath = asString(\n node?.properties[MOUNTED_INNER_SCENE_PATH_ATTRIBUTE],\n );\n if (innerScenePath) {\n const innerSnap = scenes.peek(innerScenePath);\n if (innerSnap?.status === \"ready\") {\n return resolveResource(innerSnap.value, ref, node);\n }\n }\n return undefined;\n },\n resolveResourcePath: (path) => resolvePathResource(path),\n // A node's own theme resource (`theme = ExtResource(...)`) resolves through the\n // SAME on-demand resource cache as any other `.tres`; query the parsed Theme doc\n // for the requested item. Pending/absent theme ⇒ undefined, and the renderer\n // re-queries once the resource cache settles (same as textures/fonts).\n resolveTheme: (node, name) => {\n const themeRef = asResourceRef(node.properties?.theme);\n if (!themeRef) return undefined;\n const doc = resolveResource(\n sceneForNode(node) ?? scene,\n themeRef,\n node,\n )?.document;\n return doc\n ? queryTheme(\n doc,\n node.type,\n asString(node.properties?.theme_type_variation),\n name,\n )\n : undefined;\n },\n }),\n };\n}\n\nfunction createDocumentCache<T>(\n loadDocument: (resourcePath: string) => Promise<T>,\n // Settle notifications go through the resolver's shared deferred emitter (one\n // notification per burst across caches); a load KICK never emits at all — the\n // caller just observed the pending status itself, and a synchronous emit\n // mid-walk would re-enter the consumer's walk (walks kick loads on cache miss).\n scheduleEmit: (flushCacheListeners: () => void) => void,\n // Synchronous per-settle hook (generation counters): runs the moment the cache\n // entry flips, BEFORE the deferred emit, so listeners always observe a counter\n // that already covers the settle they are being notified about.\n onSettle?: () => void,\n): GodotFetchDocumentCache<T> {\n const entries = new Map<string, GodotFetchCacheSnapshot<T>>();\n const listeners = new Set<() => void>();\n const flushListeners = (): void => {\n for (const listener of listeners) {\n listener();\n }\n };\n const load = (resourcePath: string): Promise<T> => {\n const cached = entries.get(resourcePath);\n if (cached?.status === \"ready\") {\n return Promise.resolve(cached.value);\n }\n if (cached?.status === \"pending\") {\n return cached.promise;\n }\n const promise = loadDocument(resourcePath)\n .then((value) => {\n entries.set(resourcePath, {\n status: \"ready\",\n path: resourcePath,\n value,\n });\n onSettle?.();\n scheduleEmit(flushListeners);\n return value;\n })\n .catch((error: unknown) => {\n entries.set(resourcePath, {\n status: \"error\",\n path: resourcePath,\n message: errorMessage(error),\n error,\n });\n onSettle?.();\n scheduleEmit(flushListeners);\n throw error;\n });\n entries.set(resourcePath, {\n status: \"pending\",\n path: resourcePath,\n promise,\n });\n return promise;\n };\n return {\n peek: (resourcePath) => entries.get(resourcePath),\n load,\n preload: (resourcePath) =>\n load(resourcePath)\n .then(() => undefined)\n .catch(() => undefined),\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\n// A scene body may arrive two ways: as Godot `[gd_scene …]` source text (the text\n// parser produces a GodotSceneState), or as a pre-parsed GodotSceneState serialized to\n// JSON by a runtime producer (e.g. a live game mod walking `PackedScene.GetState()`).\n// Both reach the same GodotSceneState boundary. Sniff: a JSON object with `kind:\"scene\"`\n// is used directly; anything else (and any malformed JSON) falls back to the .tscn text\n// parser.\n//\n// Live-producer ingest contract (everything a faithful `GetState()` dump needs):\n// - Values use gsw's canonical Variant shape (raw scalars, `{ type, args }` math,\n// `{ type, id|path }` resource refs). A producer that pipes Godot `JSON.from_native`\n// (which tags scalars as `i:`/`f:`/`s:`/`sn:`/`np:`) can set top-level\n// `valueEncoding:\"from_native\"` to have those tags decoded on ingest here; without\n// the flag, values are taken verbatim.\n// - Nested-node `parent` paths may be the live `GetNodePath(…, for_parent=true)`\n// NodePath form with a leading `./`; that is normalized in layout's\n// `sceneNodesFromState`, so both `./Panel/Flow` and bare `Panel/Flow` resolve.\n// - Scene-local sub-resources can be supplied inline in `subResources` as\n// `{ id, type, properties }` (no `res://` path); a node property referencing one as\n// `{ type:\"SubResource\", id }` resolves through the same path as a `.tres` sub-resource.\nfunction parseSceneBody(body: string, resourcePath: string): GodotSceneState {\n const trimmed = body.trimStart();\n if (trimmed.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(trimmed) as Partial<GodotSceneState> & {\n valueEncoding?: string;\n };\n if (parsed?.kind === \"scene\" && Array.isArray(parsed.nodes)) {\n const state: GodotSceneState = {\n kind: \"scene\",\n nodes: parsed.nodes,\n connections: parsed.connections ?? [],\n extResources: parsed.extResources ?? [],\n subResources: parsed.subResources ?? [],\n editableInstances: parsed.editableInstances ?? [],\n basePath: parsed.basePath,\n diagnostics: parsed.diagnostics ?? [],\n };\n if (parsed.valueEncoding === \"from_native\") {\n normalizeFromNativeSceneValues(state);\n }\n return state;\n }\n } catch {\n // Not a JSON scene document — fall through to the text parser.\n }\n }\n return parseGodotTextScene(body, { path: resourcePath });\n}\n\n// Rewrite a JSON-ingested scene's Variant value positions from Godot's\n// `JSON.from_native` tagged form into gsw's canonical contract (see\n// `decodeFromNativeValue`). Touches only value positions — node properties,\n// sub-resource attributes/properties, and connection binds — never structural fields\n// (names, types, parent paths). Gated by `valueEncoding:\"from_native\"` so raw producers\n// (and the text path) keep strings like `\"i:3\"` verbatim.\nfunction normalizeFromNativeSceneValues(state: GodotSceneState): void {\n for (const node of state.nodes) {\n for (const property of node.properties) {\n property.value = decodeFromNativeValue(property.value);\n }\n }\n for (const subResource of state.subResources) {\n if (subResource.attributes) {\n subResource.attributes = mapRecordValues(subResource.attributes);\n }\n if (subResource.properties) {\n subResource.properties = mapRecordValues(subResource.properties);\n }\n }\n for (const connection of state.connections) {\n if (connection.binds) {\n connection.binds = connection.binds.map(decodeFromNativeValue);\n }\n }\n}\n\nfunction mapRecordValues(\n record: Record<string, GodotVariant>,\n): Record<string, GodotVariant> {\n const result: Record<string, GodotVariant> = {};\n for (const [key, value] of Object.entries(record)) {\n result[key] = decodeFromNativeValue(value);\n }\n return result;\n}\n\n// A resource body may arrive two ways: as Godot `[gd_resource …]` source text (the text\n// parser produces a GodotResource), or as a pre-parsed GodotResource serialized to JSON by\n// a runtime producer (e.g. a live game mod loading a `Resource` and walking its properties —\n// fonts/materials/styleboxes). Both reach the same GodotResource boundary. Sniff: a JSON\n// object with `kind:\"resource\"` is used directly; anything else (and any malformed JSON)\n// falls back to the `.tres` text parser. Mirrors `parseSceneBody`; the same\n// `valueEncoding:\"from_native\"` opt-in decodes Godot-tagged scalars on ingest.\nfunction parseResourceBody(body: string, resourcePath: string): GodotResource {\n const trimmed = body.trimStart();\n if (trimmed.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(trimmed) as Partial<GodotResource> & {\n kind?: string;\n valueEncoding?: string;\n };\n if (parsed?.kind === \"resource\") {\n const resource: GodotResource = {\n type: parsed.type ?? asString(parsed.header?.attributes?.type),\n header: parsed.header ?? null,\n extResources: parsed.extResources ?? [],\n subResources: parsed.subResources ?? [],\n properties: parsed.properties ?? {},\n diagnostics: parsed.diagnostics ?? [],\n };\n if (parsed.valueEncoding === \"from_native\") {\n resource.properties = mapRecordValues(resource.properties);\n for (const subResource of resource.subResources) {\n if (subResource.properties) {\n subResource.properties = mapRecordValues(subResource.properties);\n }\n }\n }\n return resource;\n }\n } catch {\n // Not a JSON resource document — fall through to the text parser.\n }\n }\n return parseGodotResource(body, { path: resourcePath });\n}\n\nfunction assertResourcePath(resourcePath: string): void {\n if (!resourcePath.startsWith(\"res://\")) {\n throw new Error(\n `Expected a Godot resource path starting with res://, got ${resourcePath}`,\n );\n }\n}\n\nfunction subResourceDocument(resource: {\n type?: string;\n properties: Record<string, unknown>;\n}): GodotResource {\n return {\n type: resource.type,\n header: resource.type\n ? { section: \"gd_resource\", attributes: { type: resource.type } }\n : null,\n extResources: [],\n subResources: [],\n properties: resource.properties as GodotResource[\"properties\"],\n diagnostics: [],\n };\n}\n\nfunction isFontPath(path: string): boolean {\n return /\\.(?:ttf|otf|woff2?|ttc)$/i.test(path);\n}\n\n// Raster formats whose intrinsic size an `Image` decode reports reliably. SVG is\n// deliberately excluded (its intrinsic size is optional and browser-dependent).\nfunction isRasterImagePath(path: string): boolean {\n return /\\.(?:png|webp|jpe?g|gif|bmp)$/i.test(path);\n}\n\nfunction fontMetadataFromPath(\n path: string,\n): Partial<GodotProjectResolvedResource> {\n if (!isFontPath(path)) {\n return {};\n }\n return {\n fontFamily: path\n .split(\"/\")\n .at(-1)\n ?.replace(/\\.[^.]+$/, \"\"),\n fontStyle: /italic/i.test(path) ? \"italic\" : \"normal\",\n fontWeight: /bold/i.test(path) ? \"700\" : \"400\",\n };\n}\n\nfunction fontWeightFromVariation(document: GodotResource): number | undefined {\n const variation = document.properties.variation_opentype;\n if (\n !variation ||\n typeof variation !== \"object\" ||\n Array.isArray(variation) ||\n \"type\" in variation\n ) {\n return undefined;\n }\n return asNumber((variation as Record<string, GodotVariant>)[\"2003265652\"]);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
|