@carbonenginejs/runtime-resource 0.12.2 → 0.14.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/README.md +21 -0
- package/dist/CjsResMan.js +393 -29
- package/dist/CjsResMan.js.map +1 -1
- package/dist/format/carbonEffect/CjsCarbonEffectReader.js +165 -118
- package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -1
- package/dist/format/carbonEffect/buildCarbonEffectContainer.js +182 -0
- package/dist/format/carbonEffect/buildCarbonEffectContainer.js.map +1 -0
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js +7 -113
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
- package/dist/format/carbonEffect/carbonEffectRecords.js +258 -66
- package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -1
- package/dist/format/carbonEffect/carbonEffectResourceTransform.js +197 -0
- package/dist/format/carbonEffect/carbonEffectResourceTransform.js.map +1 -0
- package/dist/format/effect/effectBodyInventory.js +146 -0
- package/dist/format/effect/effectBodyInventory.js.map +1 -0
- package/dist/format/effect/effectPermutationGraph.js +13 -13
- package/dist/format/effect/effectPermutationGraph.js.map +1 -1
- package/dist/format/index.js +4 -4
- package/dist/formats/black/CjsBlackFormat.js +19 -1
- package/dist/formats/black/CjsBlackFormat.js.map +1 -1
- package/dist/formats/bnk/CjsBnkFormat.js +28 -9
- package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
- package/dist/formats/bnk/core/busNodes.js +252 -0
- package/dist/formats/bnk/core/busNodes.js.map +1 -0
- package/dist/formats/bnk/core/effectNodes.js +147 -0
- package/dist/formats/bnk/core/effectNodes.js.map +1 -0
- package/dist/formats/bnk/core/eventAction.js +135 -24
- package/dist/formats/bnk/core/eventAction.js.map +1 -1
- package/dist/formats/bnk/core/globalSettings.js +215 -0
- package/dist/formats/bnk/core/globalSettings.js.map +1 -0
- package/dist/formats/bnk/core/helpers.js +37 -5
- package/dist/formats/bnk/core/helpers.js.map +1 -1
- package/dist/formats/bnk/core/musicNodes.js +43 -19
- package/dist/formats/bnk/core/musicNodes.js.map +1 -1
- package/dist/formats/bnk/core/nodeBase.js +28 -7
- package/dist/formats/bnk/core/nodeBase.js.map +1 -1
- package/dist/formats/dds/core/helpers.js +40 -1
- package/dist/formats/dds/core/helpers.js.map +1 -1
- package/dist/formats/dxbc/CjsDxbcFormat.js +45 -0
- package/dist/formats/dxbc/CjsDxbcFormat.js.map +1 -1
- package/dist/formats/dxbc/core/disassemble.js +240 -0
- package/dist/formats/dxbc/core/disassemble.js.map +1 -0
- package/dist/formats/gltf/CjsGltfFormat.js +1 -39
- package/dist/formats/gltf/CjsGltfFormat.js.map +1 -1
- package/dist/formats/gltf/core/helpers.js +1 -21
- package/dist/formats/gltf/core/helpers.js.map +1 -1
- package/dist/formats/hlsl/CjsHlslFormat.js +0 -21
- package/dist/formats/hlsl/CjsHlslFormat.js.map +1 -1
- package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +118 -49
- package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -1
- package/dist/formats/hlsl/core/detailMapFamily.js +128 -0
- package/dist/formats/hlsl/core/detailMapFamily.js.map +1 -0
- package/dist/formats/hlsl/core/localLightFamily.js +133 -0
- package/dist/formats/hlsl/core/localLightFamily.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +79 -73
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +8 -1
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -1
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js +11 -691
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js.map +1 -1
- package/dist/formats/index.js +4 -2
- package/dist/formats/index.js.map +1 -1
- package/dist/formats/obj/CjsObjFormat.js +1 -22
- package/dist/formats/obj/CjsObjFormat.js.map +1 -1
- package/dist/formats/obj/core/helpers.js +1 -21
- package/dist/formats/obj/core/helpers.js.map +1 -1
- package/dist/formats/pickle/CjsPickleFormat.js +214 -0
- package/dist/formats/pickle/CjsPickleFormat.js.map +1 -0
- package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +551 -0
- package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js.map +1 -0
- package/dist/formats/pickle/index.js +2 -0
- package/dist/formats/pickle/index.js.map +1 -0
- package/dist/formats/red/core/redGraph.js +11 -8
- package/dist/formats/red/core/redGraph.js.map +1 -1
- package/dist/formats/stl/CjsStlFormat.js +1 -46
- package/dist/formats/stl/CjsStlFormat.js.map +1 -1
- package/dist/formats/stl/core/helpers.js +1 -21
- package/dist/formats/stl/core/helpers.js.map +1 -1
- package/dist/formats/webgl/CjsWebglFormat.js +33 -55
- package/dist/formats/webgl/CjsWebglFormat.js.map +1 -1
- package/dist/formats/webgl/core/buildGlslEffectContainer.js +70 -0
- package/dist/formats/webgl/core/buildGlslEffectContainer.js.map +1 -0
- package/dist/formats/webgl/core/effectPackage.js +216 -108
- package/dist/formats/webgl/core/effectPackage.js.map +1 -1
- package/dist/formats/webgl/core/errors.js +1 -1
- package/dist/formats/webgl/core/errors.js.map +1 -1
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +166 -24
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
- package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +63 -5
- package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js.map +1 -1
- package/dist/formats/webgl/core/glsl/packedLightFixups.js +98 -0
- package/dist/formats/webgl/core/glsl/packedLightFixups.js.map +1 -0
- package/dist/formats/webgl/core/glslBackendBlock.js +552 -0
- package/dist/formats/webgl/core/glslBackendBlock.js.map +1 -0
- package/dist/formats/webgl/core/glslBackendBodySet.js +243 -0
- package/dist/formats/webgl/core/glslBackendBodySet.js.map +1 -0
- package/dist/formats/webgl/core/{cewgCompleteness.js → glslEffectCompleteness.js} +12 -8
- package/dist/formats/webgl/core/glslEffectCompleteness.js.map +1 -0
- package/dist/formats/webgl/core/helpers.js +33 -172
- package/dist/formats/webgl/core/helpers.js.map +1 -1
- package/dist/formats/webgl/core/inspectGlslEffectContainer.js +122 -0
- package/dist/formats/webgl/core/inspectGlslEffectContainer.js.map +1 -0
- package/dist/formats/webgl/core/readGlslEffectContainer.js +249 -0
- package/dist/formats/webgl/core/readGlslEffectContainer.js.map +1 -0
- package/dist/formats/webgpu/CjsWebgpuFormat.js +15 -15
- package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
- package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +37 -145
- package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -1
- package/dist/formats/webgpu/core/{cewgpu/CewgpuContainer.js → carbonWebgpu/CarbonWebgpuContainer.js} +13 -27
- package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js.map +1 -0
- package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/containerViews.js +1 -1
- package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js.map +1 -0
- package/dist/formats/webgpu/core/{cewgpu → carbonWebgpu}/validateContainer.js +4 -4
- package/dist/formats/webgpu/core/carbonWebgpu/validateContainer.js.map +1 -0
- package/dist/formats/webgpu/core/effectBackendBodySet.js +18 -18
- package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
- package/dist/formats/webgpu/core/errors.js +1 -1
- package/dist/formats/webgpu/core/errors.js.map +1 -1
- package/dist/formats/webgpu/core/helpers.js +22 -22
- package/dist/formats/webgpu/core/helpers.js.map +1 -1
- package/dist/formats/webgpu/core/packageEffect.js +11 -15
- package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
- package/dist/formats/webgpu/core/packageEffectSelection.js +1 -1
- package/dist/formats/webgpu/core/packageEffectSelection.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +23 -23
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +6 -6
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
- package/dist/formats/yaml/CjsYamlFormat.js +3 -3
- package/dist/formats/yaml/CjsYamlFormat.js.map +1 -1
- package/dist/formats/yaml/core/CjsYamlReader.js +4 -6
- package/dist/formats/yaml/core/CjsYamlReader.js.map +1 -1
- package/dist/formats/yaml/core/helpers.js +27 -1
- package/dist/formats/yaml/core/helpers.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resource/CjsLoadingObject.js +19 -0
- package/dist/resource/CjsLoadingObject.js.map +1 -0
- package/dist/resource/CjsResource.js +4 -0
- package/dist/resource/CjsResource.js.map +1 -1
- package/dist/resource/ResourceHandlerMode.js +14 -0
- package/dist/resource/ResourceHandlerMode.js.map +1 -0
- package/dist/resource/index.js +2 -0
- package/dist/resource/index.js.map +1 -1
- package/dist/resource/shader/Tr2EffectRes.js +118 -213
- package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
- package/dist/resource/shader/Tr2Shader.js +37 -15
- package/dist/resource/shader/Tr2Shader.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectConstant.js +37 -29
- package/dist/resource/shader/reflection/Tr2EffectConstant.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectDescription.js +39 -34
- package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js +51 -30
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +45 -25
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectResource.js +51 -17
- package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js +219 -88
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js +28 -18
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2Pass.js +96 -39
- package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
- package/dist/resource/shader/reflection/carbonRecordFields.js +159 -0
- package/dist/resource/shader/reflection/carbonRecordFields.js.map +1 -0
- package/dist/resource/shader/reflection/shaderStage.js +22 -0
- package/dist/resource/shader/reflection/shaderStage.js.map +1 -0
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js +68 -10
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
- package/dist/worker/CjsResManMainThreadLoader.js +9 -7
- package/dist/worker/CjsResManMainThreadLoader.js.map +1 -1
- package/dist/worker/CjsResManWorker.js +5 -4
- package/dist/worker/CjsResManWorker.js.map +1 -1
- package/dist/worker/CjsResManWorkerLoader.js +9 -6
- package/dist/worker/CjsResManWorkerLoader.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/architecture.md +12 -3
- package/docs/concepts/resource-lifecycle.md +9 -8
- package/docs/concepts/shader-resource-model.md +14 -17
- package/docs/concepts/writing-an-engine-adapter.md +1 -1
- package/docs/formats/README.md +4 -2
- package/docs/formats/carbon-effect-container.md +127 -26
- package/docs/formats/dxbc/architecture.md +1 -1
- package/docs/formats/dxbc/reference/api.md +28 -0
- package/docs/formats/hlsl/README.md +3 -3
- package/docs/formats/hlsl/architecture.md +8 -9
- package/docs/formats/hlsl/guides/hydrating-json-output.md +3 -5
- package/docs/formats/hlsl/guides/reading-effects.md +5 -1
- package/docs/formats/hlsl/reference/advanced-analysis.md +20 -25
- package/docs/formats/hlsl/reference/api.md +0 -7
- package/docs/formats/hlsl/reference/json-graph.md +1 -4
- package/docs/formats/pickle.md +82 -0
- package/docs/formats/webgl/README.md +71 -13
- package/docs/formats/webgl/architecture.md +16 -17
- package/docs/formats/webgl/carbon-constant-layouts.md +3 -3
- package/docs/formats/webgl/decl-io.md +3 -3
- package/docs/formats/webgl/memory-structured.md +27 -8
- package/docs/formats/webgl/texture-sample.md +1 -1
- package/docs/formats/webgpu/README.md +3 -3
- package/docs/formats/webgpu/architecture.md +5 -6
- package/docs/formats/webgpu/formats/{cewgpu.md → carbon-webgpu.md} +10 -11
- package/docs/formats/webgpu/guides/effect-packaging.md +6 -8
- package/docs/formats/webgpu/reference/api.md +11 -12
- package/docs/formats/webgpu/reference/wgsl-compatibility.md +11 -8
- package/docs/formats/wwise.md +63 -2
- package/docs/reference/classes/core.md +10 -0
- package/docs/reference/classes/formats.md +40 -48
- package/docs/reference/classes/resources.md +2 -2
- package/docs/reference/queues.md +88 -4
- package/docs/reference/workers.md +7 -0
- package/package.json +61 -61
- package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +0 -372
- package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +0 -1
- package/dist/format/effect/effectReflectionPackage.js +0 -636
- package/dist/format/effect/effectReflectionPackage.js.map +0 -1
- package/dist/formats/hlsl/core/HlslReader.js +0 -16
- package/dist/formats/hlsl/core/HlslReader.js.map +0 -1
- package/dist/formats/hlsl/core/portableReflection.js +0 -848
- package/dist/formats/hlsl/core/portableReflection.js.map +0 -1
- package/dist/formats/hlsl/portable.js +0 -2
- package/dist/formats/hlsl/portable.js.map +0 -1
- package/dist/formats/webgl/core/cewg/CewgPackage.js +0 -340
- package/dist/formats/webgl/core/cewg/CewgPackage.js.map +0 -1
- package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js +0 -104
- package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js.map +0 -1
- package/dist/formats/webgl/core/cewg/binary.js +0 -19
- package/dist/formats/webgl/core/cewg/binary.js.map +0 -1
- package/dist/formats/webgl/core/cewgCompleteness.js.map +0 -1
- package/dist/formats/webgl/core/effectPackageValidation.js +0 -331
- package/dist/formats/webgl/core/effectPackageValidation.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +0 -1
- package/dist/resource/shader/portable.js +0 -33
- package/dist/resource/shader/portable.js.map +0 -1
- package/docs/formats/hlsl/reference/portable-reflection.md +0 -141
- package/docs/formats/webgl/effect-reflection.md +0 -127
package/README.md
CHANGED
|
@@ -40,6 +40,27 @@ const resource = resMan.GetResource("res:/video/intro.mp4");
|
|
|
40
40
|
const video = await resource.Ready();
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
Extensions can also choose the resource/object handler and an ordered reader
|
|
44
|
+
route explicitly. This is useful for Carbon object streams, where either file
|
|
45
|
+
suffix may contain Black bytes or Red/YAML text:
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import { CjsLoadingObject } from "@carbonenginejs/runtime-resource";
|
|
49
|
+
import { CjsBlackFormat } from "@carbonenginejs/runtime-resource/formats/black";
|
|
50
|
+
import { CjsRedFormat } from "@carbonenginejs/runtime-resource/formats/red";
|
|
51
|
+
|
|
52
|
+
resMan.RegisterExtension("red", CjsLoadingObject, [
|
|
53
|
+
CjsBlackFormat,
|
|
54
|
+
CjsRedFormat
|
|
55
|
+
]);
|
|
56
|
+
resMan.RegisterExtension("black", CjsLoadingObject, [
|
|
57
|
+
CjsBlackFormat,
|
|
58
|
+
CjsRedFormat
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const object = await resMan.Fetch("res:/definition/example.red");
|
|
62
|
+
```
|
|
63
|
+
|
|
43
64
|
Browser consumers use worker-backed fetch and declared worker-safe format
|
|
44
65
|
readers by default, with deterministic main-thread fallback; see
|
|
45
66
|
[browser worker execution](docs/reference/workers.md).
|
package/dist/CjsResMan.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CjsEventEmitter } from '@carbonenginejs/runtime-utils/model';
|
|
|
3
3
|
import { hasOwnThen } from '@carbonenginejs/runtime-utils/object';
|
|
4
4
|
import { normalizeResourcePath, normalizePath, normalizeResourceExtension, getResourceExtension } from '@carbonenginejs/runtime-utils/path';
|
|
5
5
|
import { CjsResource } from './resource/CjsResource.js';
|
|
6
|
+
import { ResourceHandlerMode } from './resource/ResourceHandlerMode.js';
|
|
6
7
|
import { CjsResManWorkQueue, CjsResManQueue } from './CjsResManWorkQueue.js';
|
|
7
8
|
import { CjsResManMainThreadLoader } from './worker/CjsResManMainThreadLoader.js';
|
|
8
9
|
import { CjsResManWorkerLoader } from './worker/CjsResManWorkerLoader.js';
|
|
@@ -33,6 +34,10 @@ let nextLocalValueIdentity = 1;
|
|
|
33
34
|
* @property {CjsResMan} resMan Owning manager.
|
|
34
35
|
* @property {object|Function} source Selected source implementation.
|
|
35
36
|
* @property {string} path Normalized Carbon-style source path.
|
|
37
|
+
* @property {string} resFilePath Normalized lowercase Carbon-style source path.
|
|
38
|
+
* @property {string} ext Normalized lowercase resource extension.
|
|
39
|
+
* @property {string} fileName Normalized lowercase final path component.
|
|
40
|
+
* @property {string|null} url Exact URL passed to a URL-backed source, otherwise `null`.
|
|
36
41
|
* @property {string} sourcePath Resource path or URL passed to the selected source.
|
|
37
42
|
* @property {string|number|undefined} sourceRevision Caller content token.
|
|
38
43
|
* @property {string} revisionKey Type-stable internal form of the content token.
|
|
@@ -165,6 +170,9 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
165
170
|
#reloadCandidates = new WeakMap();
|
|
166
171
|
#reloadGenerations = new Map();
|
|
167
172
|
#reloadOperations = new WeakMap();
|
|
173
|
+
#extensionRoutes = new Map();
|
|
174
|
+
#resourceExtensionRoutes = new WeakMap();
|
|
175
|
+
#resourceHandlerModes = new WeakMap();
|
|
168
176
|
|
|
169
177
|
/**
|
|
170
178
|
* Create a GPU-free resource manager and apply its initial registration.
|
|
@@ -226,6 +234,7 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
226
234
|
* @param {object} [options.cacheCleanup] Cleanup policy used if a smaller configured budget evicts cached identities.
|
|
227
235
|
* @param {object} [options.mainThreadLoader] Direct execution strategy.
|
|
228
236
|
* @param {object|Map<string,string>} [options.paths] Resource-prefix URL bases.
|
|
237
|
+
* @param {object|Map|object[]} [options.extensions] Extension route registrations.
|
|
229
238
|
* @param {Function|null} [options.pathResolver] Optional complete resource-path-to-URL resolver.
|
|
230
239
|
* @param {object} [options.workerLoader] Worker loader instance or construction options.
|
|
231
240
|
* @param {boolean} [options.useWorkerLoading=true] Whether worker-backed execution is selected.
|
|
@@ -295,6 +304,14 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
295
304
|
for (const [ext, loader] of Object.entries(options.objectLoaders || {})) {
|
|
296
305
|
this.RegisterObjectLoader(ext, loader);
|
|
297
306
|
}
|
|
307
|
+
for (const [ext, registration] of normalizeExtensionRegistrationEntries(options.extensions)) {
|
|
308
|
+
const {
|
|
309
|
+
Handler,
|
|
310
|
+
handler,
|
|
311
|
+
...routeOptions
|
|
312
|
+
} = registration;
|
|
313
|
+
this.RegisterExtension(ext, Handler || handler, routeOptions);
|
|
314
|
+
}
|
|
298
315
|
return this;
|
|
299
316
|
}
|
|
300
317
|
|
|
@@ -840,10 +857,7 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
840
857
|
if (!Array.isArray(Format.inputTypes) || Format.inputTypes.length === 0) {
|
|
841
858
|
throw new TypeError(`${Format.name || "Format"} must declare non-empty inputTypes.`);
|
|
842
859
|
}
|
|
843
|
-
const descriptor =
|
|
844
|
-
Format,
|
|
845
|
-
defaults: snapshotFormatDefaults(defaults)
|
|
846
|
-
});
|
|
860
|
+
const descriptor = createFormatDescriptor(Format, defaults);
|
|
847
861
|
for (const inputType of Format.inputTypes) {
|
|
848
862
|
const key = normalizeResourceExtension(inputType);
|
|
849
863
|
if (!key) continue;
|
|
@@ -855,6 +869,83 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
855
869
|
return this;
|
|
856
870
|
}
|
|
857
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Register one extension-selected resource or object handler and its reader
|
|
874
|
+
* route. A format constructor and an ordered format array are short forms
|
|
875
|
+
* for `{ Format }` and `{ Formats }` respectively.
|
|
876
|
+
*
|
|
877
|
+
* Re-registration replaces the complete route for future uncached handles.
|
|
878
|
+
* Existing handles retain the route snapshot captured at construction.
|
|
879
|
+
*
|
|
880
|
+
* @param {string} extension Input extension with or without a leading dot.
|
|
881
|
+
* @param {Function} Handler CjsResource-compatible handler constructor.
|
|
882
|
+
* @param {Function|Function[]|object} [formatOrFormatsOrOptions={}] Reader and optional target configuration.
|
|
883
|
+
* @returns {CjsResMan} This resource manager.
|
|
884
|
+
* @throws {TypeError} If the extension, handler, formats, or target policy is invalid.
|
|
885
|
+
*/
|
|
886
|
+
RegisterExtension(extension, Handler, formatOrFormatsOrOptions = {}) {
|
|
887
|
+
const ext = normalizeResourceExtension(extension);
|
|
888
|
+
if (!ext) {
|
|
889
|
+
throw new TypeError("CjsResMan.RegisterExtension requires an extension.");
|
|
890
|
+
}
|
|
891
|
+
if (typeof Handler !== "function" || typeof Handler.prototype?.Initialize !== "function") {
|
|
892
|
+
throw new TypeError("CjsResMan.RegisterExtension requires a CjsResource-compatible handler constructor.");
|
|
893
|
+
}
|
|
894
|
+
const handlerMode = Handler.handlerMode;
|
|
895
|
+
if (!Object.values(ResourceHandlerMode).includes(handlerMode)) {
|
|
896
|
+
throw new TypeError("CjsResMan.RegisterExtension Handler.handlerMode must be ResourceHandlerMode.RESOURCE or ResourceHandlerMode.OBJECT.");
|
|
897
|
+
}
|
|
898
|
+
const options = normalizeExtensionRouteOptions(formatOrFormatsOrOptions);
|
|
899
|
+
const hasFormat = options.Format !== undefined || options.format !== undefined;
|
|
900
|
+
const hasFormats = options.Formats !== undefined || options.formats !== undefined;
|
|
901
|
+
if (hasFormat && hasFormats) {
|
|
902
|
+
throw new TypeError("CjsResMan.RegisterExtension accepts either Format or Formats, not both.");
|
|
903
|
+
}
|
|
904
|
+
const Target = options.Target || options.target || null;
|
|
905
|
+
const Identify = options.Identify || options.identify || null;
|
|
906
|
+
if (Target && Identify) {
|
|
907
|
+
throw new TypeError("CjsResMan.RegisterExtension accepts either Target or Identify, not both.");
|
|
908
|
+
}
|
|
909
|
+
if ((Target || Identify) && handlerMode !== ResourceHandlerMode.OBJECT) {
|
|
910
|
+
throw new TypeError("CjsResMan extension Target and Identify are valid only for object handlers.");
|
|
911
|
+
}
|
|
912
|
+
if (Target) assertExtensionTarget(Target, "Target");
|
|
913
|
+
if (Identify && typeof Identify !== "function") {
|
|
914
|
+
throw new TypeError("CjsResMan extension Identify must be a function.");
|
|
915
|
+
}
|
|
916
|
+
const explicitFormats = hasFormat ? [options.Format || options.format] : hasFormats ? options.Formats || options.formats : null;
|
|
917
|
+
if (explicitFormats !== null && !Array.isArray(explicitFormats)) {
|
|
918
|
+
throw new TypeError("CjsResMan extension Formats must be an array.");
|
|
919
|
+
}
|
|
920
|
+
const loader = explicitFormats === null ? this.GetObjectLoader(ext) : null;
|
|
921
|
+
const formats = explicitFormats === null ? loader ? [] : this.GetFormatDescriptors(ext) : explicitFormats.map((entry, index) => createExtensionFormatDescriptor(entry, options.defaults || {}, `Formats[${index}]`));
|
|
922
|
+
if (!loader && formats.length === 0) {
|
|
923
|
+
throw new TypeError(`CjsResMan extension .${ext} requires a Format, Formats, or registered reader.`);
|
|
924
|
+
}
|
|
925
|
+
validateOrderedExtensionFormats(formats, ext);
|
|
926
|
+
const route = Object.freeze({
|
|
927
|
+
extension: ext,
|
|
928
|
+
Handler,
|
|
929
|
+
handlerMode,
|
|
930
|
+
loader,
|
|
931
|
+
formats: Object.freeze([...formats]),
|
|
932
|
+
Target,
|
|
933
|
+
Identify
|
|
934
|
+
});
|
|
935
|
+
this.#extensionRoutes.set(ext, route);
|
|
936
|
+
return this;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Return the current immutable explicit route for one extension.
|
|
941
|
+
*
|
|
942
|
+
* @param {string} extension Input extension with or without a leading dot.
|
|
943
|
+
* @returns {object|null} Captured route descriptor, or `null`.
|
|
944
|
+
*/
|
|
945
|
+
GetExtensionRoute(extension) {
|
|
946
|
+
return this.#extensionRoutes.get(normalizeResourceExtension(extension)) || null;
|
|
947
|
+
}
|
|
948
|
+
|
|
858
949
|
/**
|
|
859
950
|
* Return registered format facades for one normalized input extension.
|
|
860
951
|
*
|
|
@@ -899,20 +990,21 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
899
990
|
const key = normalizeResourcePath(path);
|
|
900
991
|
const variant = this.GetResourceVariant(options);
|
|
901
992
|
const ext = normalizeResourceExtension(options.ext || getResourceExtension(key));
|
|
993
|
+
const cacheKey = getMotherLodeKey(key, variant);
|
|
994
|
+
const existing = this.motherLode.Lookup(cacheKey);
|
|
995
|
+
const extensionRoute = existing ? this.#resourceExtensionRoutes.get(existing) || null : this.GetExtensionRoute(ext);
|
|
902
996
|
if (options.emit !== undefined) {
|
|
903
|
-
const objectLoader = this.GetObjectLoader(ext);
|
|
997
|
+
const objectLoader = extensionRoute?.loader || !extensionRoute && this.GetObjectLoader(ext);
|
|
904
998
|
if (objectLoader) {
|
|
905
999
|
throw createObjectLoaderOutputMissingError(ext, options.emit);
|
|
906
1000
|
} else {
|
|
907
|
-
const descriptors = this.GetFormatDescriptors(ext);
|
|
1001
|
+
const descriptors = extensionRoute ? extensionRoute.formats : this.GetFormatDescriptors(ext);
|
|
908
1002
|
const candidates = filterFormatDescriptors(descriptors, options);
|
|
909
1003
|
if (descriptors.length > 0 && candidates.length === 0) {
|
|
910
1004
|
throw createFormatOutputMissingError(ext, options.emit, descriptors);
|
|
911
1005
|
}
|
|
912
1006
|
}
|
|
913
1007
|
}
|
|
914
|
-
const cacheKey = getMotherLodeKey(key, variant);
|
|
915
|
-
const existing = this.motherLode.Lookup(cacheKey);
|
|
916
1008
|
if (existing && options.reload !== true) {
|
|
917
1009
|
this.#BindResourceLifecycle(cacheKey, existing);
|
|
918
1010
|
this.motherLode.KeepAlive?.(cacheKey);
|
|
@@ -923,8 +1015,8 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
923
1015
|
source: options.source || this.source
|
|
924
1016
|
});
|
|
925
1017
|
}
|
|
926
|
-
const Constructor = this.ResolveResourceConstructor(options);
|
|
927
|
-
const resource = this.CreateResource(Constructor, key, ext, options, existing && options.reload === true ? existing : null);
|
|
1018
|
+
const Constructor = this.ResolveResourceConstructor(options, extensionRoute);
|
|
1019
|
+
const resource = this.CreateResource(Constructor, key, ext, options, existing && options.reload === true ? existing : null, extensionRoute);
|
|
928
1020
|
if (existing && options.reload === true) {
|
|
929
1021
|
this.#BindResourceLifecycle(cacheKey, existing);
|
|
930
1022
|
this.motherLode.KeepAlive?.(cacheKey);
|
|
@@ -996,7 +1088,7 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
996
1088
|
}
|
|
997
1089
|
if (resource.HasPayload?.()) {
|
|
998
1090
|
resource.KeepPayloadAlive?.();
|
|
999
|
-
return Promise.resolve(getPublishedResourceObject(resource));
|
|
1091
|
+
return Promise.resolve(getPublishedResourceObject(resource, this.#resourceExtensionRoutes.get(resource) || null, this.#resourceHandlerModes.get(resource) || null));
|
|
1000
1092
|
}
|
|
1001
1093
|
const promise = this.QueueResourceObject(resource, operationOptions);
|
|
1002
1094
|
const operation = {
|
|
@@ -1305,7 +1397,16 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1305
1397
|
* resource manager.
|
|
1306
1398
|
*/
|
|
1307
1399
|
Fetch(path, options = {}) {
|
|
1308
|
-
|
|
1400
|
+
if (options.resource === true || options.requirement !== undefined || options.payload !== undefined) {
|
|
1401
|
+
return this.FetchResource(path, options);
|
|
1402
|
+
}
|
|
1403
|
+
const normalizedPath = normalizeResourcePath(path);
|
|
1404
|
+
const variant = this.GetResourceVariant(options);
|
|
1405
|
+
const cacheKey = getMotherLodeKey(normalizedPath, variant);
|
|
1406
|
+
const existing = this.motherLode.Lookup(cacheKey);
|
|
1407
|
+
const ext = normalizeResourceExtension(options.ext || getResourceExtension(normalizedPath));
|
|
1408
|
+
const route = existing ? this.#resourceExtensionRoutes.get(existing) || null : this.GetExtensionRoute(ext);
|
|
1409
|
+
return route?.handlerMode === ResourceHandlerMode.RESOURCE ? this.FetchResource(path, options) : this.FetchObject(path, options);
|
|
1309
1410
|
}
|
|
1310
1411
|
|
|
1311
1412
|
/**
|
|
@@ -1470,7 +1571,9 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1470
1571
|
*/
|
|
1471
1572
|
async #PrepareResourceObject(resource, bytes, options, ownership) {
|
|
1472
1573
|
this.#AssertOptionalResourceOwnership(ownership, "prepare:read");
|
|
1473
|
-
const
|
|
1574
|
+
const resolved = this.#ResolveResourceObjectRead(resource, bytes, options);
|
|
1575
|
+
const decoded = await this.#ReadResolvedResourceObjectPayload(resource, bytes, options, resolved);
|
|
1576
|
+
const object = this.#HydrateExtensionObject(resource, decoded, options, resolved);
|
|
1474
1577
|
this.#AssertOptionalResourceOwnership(ownership, "prepare:read-settled");
|
|
1475
1578
|
return this.#PublishResourceObject(ownership, resource, object, options);
|
|
1476
1579
|
}
|
|
@@ -1490,7 +1593,8 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1490
1593
|
let object = bytes;
|
|
1491
1594
|
const resolved = this.#ResolveResourceObjectRead(resource, bytes, options);
|
|
1492
1595
|
const formatOptions = resolved.descriptor ? createFormatReadOptions(resolved.descriptor, options) : null;
|
|
1493
|
-
const
|
|
1596
|
+
const formatContext = resolved.descriptor ? createResourcePathContext(resource.GetPath(), READ_CONTEXTS.get(options)) : null;
|
|
1597
|
+
const runInWorker = Boolean(resolved.descriptor && typeof this.resourceLoader?.CanReadFormat === "function" && this.resourceLoader.CanReadFormat(resolved.descriptor, formatOptions, formatContext));
|
|
1494
1598
|
let read;
|
|
1495
1599
|
if (runInWorker) {
|
|
1496
1600
|
this.#AssertOptionalResourceOwnership(ownership, "worker-stage:read:run");
|
|
@@ -1512,7 +1616,8 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1512
1616
|
this.#AssertOptionalResourceOwnership(ownership, "queue-stage:publish:enqueue");
|
|
1513
1617
|
const publishTask = this.QueueTask(CjsResManQueue.MAIN, () => {
|
|
1514
1618
|
this.#AssertOptionalResourceOwnership(ownership, "queue-stage:publish:run");
|
|
1515
|
-
|
|
1619
|
+
const hydrated = this.#HydrateExtensionObject(resource, object, options, resolved);
|
|
1620
|
+
return this.#PublishResourceObject(ownership, resource, hydrated, options);
|
|
1516
1621
|
}, resource, {
|
|
1517
1622
|
kind: "prepare",
|
|
1518
1623
|
stage: "publish",
|
|
@@ -1546,25 +1651,31 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1546
1651
|
* @param {CjsResource} resource Resource whose CPU outcome is requested.
|
|
1547
1652
|
* @param {*} bytes Source input.
|
|
1548
1653
|
* @param {object} options Requested output options.
|
|
1549
|
-
* @returns {{loader: Function|null, descriptor: object|null}} Resolved reader.
|
|
1654
|
+
* @returns {{loader: Function|null, descriptor: object|null, route: object|null}} Resolved reader.
|
|
1550
1655
|
*/
|
|
1551
1656
|
#ResolveResourceObjectRead(resource, bytes, options) {
|
|
1552
|
-
const
|
|
1657
|
+
const route = this.#resourceExtensionRoutes.get(resource) || null;
|
|
1658
|
+
const explicitLoader = route?.loader || !route && this.GetObjectLoader(resource.GetExt());
|
|
1553
1659
|
if (explicitLoader) {
|
|
1554
1660
|
if (options.emit !== undefined) {
|
|
1555
1661
|
throw createObjectLoaderOutputMissingError(resource.GetExt(), options.emit);
|
|
1556
1662
|
}
|
|
1557
1663
|
return {
|
|
1558
1664
|
loader: explicitLoader,
|
|
1559
|
-
descriptor: null
|
|
1665
|
+
descriptor: null,
|
|
1666
|
+
route
|
|
1560
1667
|
};
|
|
1561
1668
|
}
|
|
1562
1669
|
return {
|
|
1563
1670
|
loader: null,
|
|
1564
|
-
descriptor:
|
|
1671
|
+
descriptor: route ? resolveOrderedExtensionFormatDescriptor(route.formats, resource.GetExt(), {
|
|
1565
1672
|
...options,
|
|
1566
1673
|
bytes
|
|
1567
|
-
})
|
|
1674
|
+
}) : this.ResolveFormatDescriptor(resource.GetExt(), {
|
|
1675
|
+
...options,
|
|
1676
|
+
bytes
|
|
1677
|
+
}),
|
|
1678
|
+
route
|
|
1568
1679
|
};
|
|
1569
1680
|
}
|
|
1570
1681
|
|
|
@@ -1584,6 +1695,56 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1584
1695
|
return this.ReadFormatOnce(resource, resolved.descriptor, bytes, options);
|
|
1585
1696
|
}
|
|
1586
1697
|
|
|
1698
|
+
/**
|
|
1699
|
+
* Apply a fixed target or dynamic identification policy after format reading
|
|
1700
|
+
* and before payload publication. Worker decoding has settled by this point,
|
|
1701
|
+
* so constructors and Identify functions remain on the caller thread.
|
|
1702
|
+
*
|
|
1703
|
+
* @param {CjsResource} resource Route handler receiving the decoded value.
|
|
1704
|
+
* @param {*} values Decoded format result.
|
|
1705
|
+
* @param {object} options Request options.
|
|
1706
|
+
* @param {{descriptor: object|null, route: object|null}} resolved Captured reader route.
|
|
1707
|
+
* @returns {*} Original or hydrated object value.
|
|
1708
|
+
*/
|
|
1709
|
+
#HydrateExtensionObject(resource, values, options, resolved) {
|
|
1710
|
+
const route = resolved.route;
|
|
1711
|
+
if (!route || !route.Target && !route.Identify) return values;
|
|
1712
|
+
const Format = resolved.descriptor?.Format || null;
|
|
1713
|
+
const context = Object.freeze({
|
|
1714
|
+
...createResourcePathContext(resource.GetPath(), READ_CONTEXTS.get(options)),
|
|
1715
|
+
Format,
|
|
1716
|
+
format: Format?.id || Format?.name || "",
|
|
1717
|
+
options,
|
|
1718
|
+
resMan: this
|
|
1719
|
+
});
|
|
1720
|
+
let Target = route.Target;
|
|
1721
|
+
if (route.Identify) {
|
|
1722
|
+
try {
|
|
1723
|
+
Target = route.Identify(values, context);
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
throw createExtensionTargetError(resource, "Identify threw an error.", error);
|
|
1726
|
+
}
|
|
1727
|
+
if (Target === true) return values;
|
|
1728
|
+
if (Target === false || Target === null || Target === undefined) {
|
|
1729
|
+
throw createExtensionTargetError(resource, "Identify did not resolve a target constructor.");
|
|
1730
|
+
}
|
|
1731
|
+
try {
|
|
1732
|
+
assertExtensionTarget(Target, "Identify result");
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
throw createExtensionTargetError(resource, "Identify returned an invalid target constructor.", error);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
try {
|
|
1738
|
+
const hydrated = typeof Target.fromYAML === "function" ? Target.fromYAML(values, context) : Target.from(values);
|
|
1739
|
+
if (hydrated && typeof hydrated.then === "function") {
|
|
1740
|
+
throw new TypeError("Extension target hydration must be synchronous.");
|
|
1741
|
+
}
|
|
1742
|
+
return hydrated;
|
|
1743
|
+
} catch (error) {
|
|
1744
|
+
throw createExtensionTargetError(resource, "Target hydration failed.", error);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1587
1748
|
/**
|
|
1588
1749
|
* Publish the final CPU object outcome and loaded state. Every
|
|
1589
1750
|
* CjsResource-compatible handle owns the reader/converter result through
|
|
@@ -1640,7 +1801,16 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1640
1801
|
*/
|
|
1641
1802
|
#PublishResourceObjectValue(resource, object, options) {
|
|
1642
1803
|
let result = object;
|
|
1643
|
-
|
|
1804
|
+
const route = this.#resourceExtensionRoutes.get(resource) || null;
|
|
1805
|
+
const handlerMode = route ? this.#resourceHandlerModes.get(resource) : null;
|
|
1806
|
+
if (handlerMode === ResourceHandlerMode.RESOURCE) {
|
|
1807
|
+
resource.SetPayload?.(object, options);
|
|
1808
|
+
resource.object = resource;
|
|
1809
|
+
result = resource;
|
|
1810
|
+
} else if (handlerMode === ResourceHandlerMode.OBJECT) {
|
|
1811
|
+
resource.SetPayload?.(object, options);
|
|
1812
|
+
resource.object = object;
|
|
1813
|
+
} else if (resource.constructor !== CjsResource && typeof resource.SetPayload === "function") {
|
|
1644
1814
|
resource.SetPayload(object, options);
|
|
1645
1815
|
resource.object = resource;
|
|
1646
1816
|
result = resource;
|
|
@@ -1716,9 +1886,14 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1716
1886
|
* @param {object} [options={}] Output, class, and `formatOptions` overrides.
|
|
1717
1887
|
* @returns {Promise<*>} Parsed format outcome.
|
|
1718
1888
|
* @throws {TypeError|Error} If the facade lacks a reader or reading fails.
|
|
1889
|
+
* @remarks Resource-backed calls pass normalized path context as the
|
|
1890
|
+
* reader's third argument; direct descriptor reads without a resource pass
|
|
1891
|
+
* `null`.
|
|
1719
1892
|
*/
|
|
1720
1893
|
async ReadFormat(descriptor, bytes, options = {}) {
|
|
1721
|
-
|
|
1894
|
+
const readContext = READ_CONTEXTS.get(options);
|
|
1895
|
+
const context = readContext ? createResourcePathContext(readContext.path, readContext) : null;
|
|
1896
|
+
return this.resourceLoader.ReadFormat(descriptor, bytes, createFormatReadOptions(descriptor, options), context);
|
|
1722
1897
|
}
|
|
1723
1898
|
|
|
1724
1899
|
/**
|
|
@@ -1986,11 +2161,19 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
1986
2161
|
const operationOptions = {
|
|
1987
2162
|
...options
|
|
1988
2163
|
};
|
|
2164
|
+
const ext = normalizeResourceExtension(options.ext || getResourceExtension(normalizedPath));
|
|
2165
|
+
const fileName = getResourceFileName(normalizedPath);
|
|
2166
|
+
const requiresUrl = sourceRequiresUrl(source);
|
|
2167
|
+
const url = requiresUrl ? this.BuildUrl(normalizedPath) : null;
|
|
1989
2168
|
const context = Object.freeze({
|
|
1990
2169
|
resMan: this,
|
|
1991
2170
|
source,
|
|
1992
2171
|
path: normalizedPath,
|
|
1993
|
-
|
|
2172
|
+
resFilePath: normalizedPath,
|
|
2173
|
+
ext,
|
|
2174
|
+
fileName,
|
|
2175
|
+
url,
|
|
2176
|
+
sourcePath: url || normalizedPath,
|
|
1994
2177
|
sourceRevision,
|
|
1995
2178
|
revisionKey
|
|
1996
2179
|
});
|
|
@@ -2152,10 +2335,11 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
2152
2335
|
* @param {string} ext Normalized resource extension.
|
|
2153
2336
|
* @param {object} [options={}] Constructor values, requested output, semantic requirement, and source provenance.
|
|
2154
2337
|
* @param {object|Function|null} [disallowedAlias=null] Existing owner that a staged candidate must not reuse.
|
|
2338
|
+
* @param {object|null} [extensionRoute=null] Immutable explicit extension route captured for this handle.
|
|
2155
2339
|
* @returns {CjsResource} Initialized resource-compatible handle.
|
|
2156
2340
|
* @throws {TypeError|Error} If construction, candidate identity, or initialization is invalid.
|
|
2157
2341
|
*/
|
|
2158
|
-
CreateResource(Constructor, path, ext, options = {}, disallowedAlias = null) {
|
|
2342
|
+
CreateResource(Constructor, path, ext, options = {}, disallowedAlias = null, extensionRoute = null) {
|
|
2159
2343
|
const resource = new Constructor(options.values);
|
|
2160
2344
|
if (!resource || typeof resource.Initialize !== "function") {
|
|
2161
2345
|
throw new TypeError("Resource constructor must create a CjsResource-compatible object.");
|
|
@@ -2163,6 +2347,11 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
2163
2347
|
if (disallowedAlias && resource === disallowedAlias) {
|
|
2164
2348
|
throw reloadCandidateAliasError(path, resource);
|
|
2165
2349
|
}
|
|
2350
|
+
if (extensionRoute) {
|
|
2351
|
+
this.#resourceExtensionRoutes.set(resource, extensionRoute);
|
|
2352
|
+
const handlerMode = Constructor === extensionRoute.Handler ? extensionRoute.handlerMode : Object.values(ResourceHandlerMode).includes(Constructor.handlerMode) ? Constructor.handlerMode : ResourceHandlerMode.RESOURCE;
|
|
2353
|
+
this.#resourceHandlerModes.set(resource, handlerMode);
|
|
2354
|
+
}
|
|
2166
2355
|
resource.Initialize(path, ext, normalizeRequirement(options.requirement || options.payload || ""));
|
|
2167
2356
|
if (typeof resource.SetObjectLoader === "function") {
|
|
2168
2357
|
const loaderOptions = getResourceLoaderOptions(options, options.source || this.source);
|
|
@@ -2177,13 +2366,15 @@ class CjsResMan extends CjsEventEmitter {
|
|
|
2177
2366
|
* otherwise the generic `CjsResource` handle is used.
|
|
2178
2367
|
*
|
|
2179
2368
|
* @param {object} [options={}] Semantic requirement, payload, and emit request.
|
|
2369
|
+
* @param {object|null} [extensionRoute=null] Captured extension-selected default handler.
|
|
2180
2370
|
* @returns {Function} Registered resource constructor or `CjsResource`.
|
|
2181
2371
|
*/
|
|
2182
|
-
ResolveResourceConstructor(options = {}) {
|
|
2372
|
+
ResolveResourceConstructor(options = {}, extensionRoute = null) {
|
|
2183
2373
|
const requested = normalizeRequirement(options.requirement || options.payload);
|
|
2184
2374
|
if (requested && this.resourceTypes.has(requested)) return this.resourceTypes.get(requested);
|
|
2185
2375
|
const emitted = normalizeRequirement(options.emit);
|
|
2186
2376
|
if (emitted && this.resourceTypes.has(emitted)) return this.resourceTypes.get(emitted);
|
|
2377
|
+
if (extensionRoute) return extensionRoute.Handler;
|
|
2187
2378
|
return CjsResource;
|
|
2188
2379
|
}
|
|
2189
2380
|
|
|
@@ -2969,6 +3160,56 @@ function filterFormatDescriptors(descriptors, options) {
|
|
|
2969
3160
|
return candidates;
|
|
2970
3161
|
}
|
|
2971
3162
|
|
|
3163
|
+
/**
|
|
3164
|
+
* Resolve an ordered explicit extension route. Probes are evaluated in route
|
|
3165
|
+
* order and the sole unprobed final descriptor is the fallback. Selection is
|
|
3166
|
+
* final: reader failures are never interpreted as a reason to try another
|
|
3167
|
+
* format.
|
|
3168
|
+
*
|
|
3169
|
+
* @param {readonly object[]} descriptors Captured ordered route descriptors.
|
|
3170
|
+
* @param {string} ext Normalized resource extension.
|
|
3171
|
+
* @param {object} options Output filters and source bytes.
|
|
3172
|
+
* @returns {object} Selected immutable format descriptor.
|
|
3173
|
+
*/
|
|
3174
|
+
function resolveOrderedExtensionFormatDescriptor(descriptors, ext, options) {
|
|
3175
|
+
const candidates = filterFormatDescriptors(descriptors, options);
|
|
3176
|
+
if (descriptors.length > 0 && candidates.length === 0 && options.emit !== undefined) {
|
|
3177
|
+
throw createFormatOutputMissingError(ext, options.emit, descriptors);
|
|
3178
|
+
}
|
|
3179
|
+
if (candidates.length === 0) {
|
|
3180
|
+
const error = new Error(`No format registered for .${ext}`);
|
|
3181
|
+
error.code = "CJS_RESOURCE_FORMAT_MISSING";
|
|
3182
|
+
error.ext = ext;
|
|
3183
|
+
throw error;
|
|
3184
|
+
}
|
|
3185
|
+
for (const descriptor of candidates) {
|
|
3186
|
+
const {
|
|
3187
|
+
Format,
|
|
3188
|
+
defaults
|
|
3189
|
+
} = descriptor;
|
|
3190
|
+
if (typeof Format.isSupported !== "function") return descriptor;
|
|
3191
|
+
const report = Format.isSupported(options.bytes, {
|
|
3192
|
+
...defaults,
|
|
3193
|
+
...(options.formatOptions || {})
|
|
3194
|
+
});
|
|
3195
|
+
if (isPositiveFormatProbe(report)) return descriptor;
|
|
3196
|
+
}
|
|
3197
|
+
const error = new Error(`No registered format supports the content for .${ext}`);
|
|
3198
|
+
error.code = "CJS_RESOURCE_FORMAT_UNSUPPORTED";
|
|
3199
|
+
error.ext = ext;
|
|
3200
|
+
error.formats = candidates.map(({
|
|
3201
|
+
Format
|
|
3202
|
+
}) => Format.name);
|
|
3203
|
+
throw error;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
/** Tests the supported forms returned by current format probes. */
|
|
3207
|
+
function isPositiveFormatProbe(report) {
|
|
3208
|
+
if (report === true) return true;
|
|
3209
|
+
if (!report || report === false || typeof report !== "object") return false;
|
|
3210
|
+
return report.supported === true || report.supported === "full" || report.supported === "partial";
|
|
3211
|
+
}
|
|
3212
|
+
|
|
2972
3213
|
/**
|
|
2973
3214
|
* Return one format facade's normal and diagnostic output declarations.
|
|
2974
3215
|
*
|
|
@@ -3058,7 +3299,7 @@ function resolveFormatDescriptorCandidates(descriptors, ext, options) {
|
|
|
3058
3299
|
...defaults,
|
|
3059
3300
|
...(options.formatOptions || {})
|
|
3060
3301
|
});
|
|
3061
|
-
return report
|
|
3302
|
+
return isPositiveFormatProbe(report);
|
|
3062
3303
|
});
|
|
3063
3304
|
if (supported.length === 1) candidates = supported;
|
|
3064
3305
|
}
|
|
@@ -3104,24 +3345,61 @@ function normalizeResourceVariant(value) {
|
|
|
3104
3345
|
function createPrepareContext(resMan, resource, bytes, options, stage) {
|
|
3105
3346
|
return Object.freeze({
|
|
3106
3347
|
...options,
|
|
3348
|
+
...createResourcePathContext(resource.GetPath(), READ_CONTEXTS.get(options)),
|
|
3107
3349
|
stage,
|
|
3108
3350
|
bytes,
|
|
3109
|
-
path: resource.GetPath(),
|
|
3110
|
-
ext: resource.GetExt(),
|
|
3111
3351
|
resource,
|
|
3112
3352
|
resMan
|
|
3113
3353
|
});
|
|
3114
3354
|
}
|
|
3115
3355
|
|
|
3356
|
+
/**
|
|
3357
|
+
* Create the immutable, source-neutral path context shared by format readers,
|
|
3358
|
+
* direct object loaders, target identification, and target hydration.
|
|
3359
|
+
*
|
|
3360
|
+
* @param {string} path Normalized or caller-supplied resource path.
|
|
3361
|
+
* @param {CjsResourceReadContext|null|undefined} [readContext] Active source read context.
|
|
3362
|
+
* @returns {Readonly<{path: string, resFilePath: string, ext: string, fileName: string, url: string|null}>}
|
|
3363
|
+
*/
|
|
3364
|
+
function createResourcePathContext(path, readContext = null) {
|
|
3365
|
+
const resFilePath = normalizeResourcePath(path);
|
|
3366
|
+
return Object.freeze({
|
|
3367
|
+
path: resFilePath,
|
|
3368
|
+
resFilePath,
|
|
3369
|
+
ext: readContext?.ext ?? getResourceExtension(resFilePath),
|
|
3370
|
+
fileName: getResourceFileName(resFilePath),
|
|
3371
|
+
url: readContext?.url ?? null
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
/**
|
|
3376
|
+
* Return the normalized final component of a resource path without query or
|
|
3377
|
+
* fragment material.
|
|
3378
|
+
*
|
|
3379
|
+
* @param {string} path Resource path.
|
|
3380
|
+
* @returns {string} Lowercase normalized filename, or an empty string.
|
|
3381
|
+
*/
|
|
3382
|
+
function getResourceFileName(path) {
|
|
3383
|
+
const normalized = normalizeResourcePath(path);
|
|
3384
|
+
const queryIndex = normalized.search(/[?#]/u);
|
|
3385
|
+
const cleanPath = queryIndex === -1 ? normalized : normalized.slice(0, queryIndex);
|
|
3386
|
+
const slashIndex = cleanPath.lastIndexOf("/");
|
|
3387
|
+
return cleanPath.slice(slashIndex + 1);
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3116
3390
|
/**
|
|
3117
3391
|
* Recover the public object result represented by a resident resource payload.
|
|
3118
3392
|
* The publication contract returns semantic subclasses as their resource
|
|
3119
3393
|
* handle and base resources as their plain payload.
|
|
3120
3394
|
*
|
|
3121
3395
|
* @param {CjsResource} resource Resource with an attached CPU payload.
|
|
3396
|
+
* @param {object|null} route Captured explicit extension route.
|
|
3397
|
+
* @param {string|null} handlerMode Captured effective handler mode.
|
|
3122
3398
|
* @returns {*} Resident public object outcome.
|
|
3123
3399
|
*/
|
|
3124
|
-
function getPublishedResourceObject(resource) {
|
|
3400
|
+
function getPublishedResourceObject(resource, route = null, handlerMode = null) {
|
|
3401
|
+
if (route && handlerMode === ResourceHandlerMode.RESOURCE) return resource;
|
|
3402
|
+
if (route && handlerMode === ResourceHandlerMode.OBJECT) return resource.GetPayload();
|
|
3125
3403
|
return resource.constructor !== CjsResource ? resource : resource.GetPayload();
|
|
3126
3404
|
}
|
|
3127
3405
|
function assertPositiveInteger(value, name) {
|
|
@@ -3490,5 +3768,91 @@ function normalizeRegistrationEntries(value, keyed = false) {
|
|
|
3490
3768
|
});
|
|
3491
3769
|
}
|
|
3492
3770
|
|
|
3771
|
+
/** Normalize `Register({ extensions })` into extension/registration pairs. */
|
|
3772
|
+
function normalizeExtensionRegistrationEntries(value) {
|
|
3773
|
+
if (value === null || value === undefined) return [];
|
|
3774
|
+
if (value instanceof Map) {
|
|
3775
|
+
return [...value.entries()].map(([ext, registration]) => [ext, normalizeExtensionRegistration(registration)]);
|
|
3776
|
+
}
|
|
3777
|
+
if (Array.isArray(value)) {
|
|
3778
|
+
return value.map((registration, index) => {
|
|
3779
|
+
if (!registration || typeof registration !== "object" || Array.isArray(registration)) {
|
|
3780
|
+
throw new TypeError(`CjsResMan extensions[${index}] must be an object.`);
|
|
3781
|
+
}
|
|
3782
|
+
const ext = registration.extension || registration.ext;
|
|
3783
|
+
if (!ext) {
|
|
3784
|
+
throw new TypeError(`CjsResMan extensions[${index}] requires extension or ext.`);
|
|
3785
|
+
}
|
|
3786
|
+
return [ext, normalizeExtensionRegistration(registration)];
|
|
3787
|
+
});
|
|
3788
|
+
}
|
|
3789
|
+
if (!value || typeof value !== "object") {
|
|
3790
|
+
throw new TypeError("CjsResMan extensions must be an object, Map, or array.");
|
|
3791
|
+
}
|
|
3792
|
+
return Object.entries(value).map(([ext, registration]) => [ext, normalizeExtensionRegistration(registration)]);
|
|
3793
|
+
}
|
|
3794
|
+
function normalizeExtensionRegistration(registration) {
|
|
3795
|
+
if (typeof registration === "function") return {
|
|
3796
|
+
Handler: registration
|
|
3797
|
+
};
|
|
3798
|
+
if (!registration || typeof registration !== "object" || Array.isArray(registration)) {
|
|
3799
|
+
throw new TypeError("CjsResMan extension registration must be a handler or object.");
|
|
3800
|
+
}
|
|
3801
|
+
return registration;
|
|
3802
|
+
}
|
|
3803
|
+
function normalizeExtensionRouteOptions(value) {
|
|
3804
|
+
if (typeof value === "function") return {
|
|
3805
|
+
Format: value
|
|
3806
|
+
};
|
|
3807
|
+
if (Array.isArray(value)) return {
|
|
3808
|
+
Formats: value
|
|
3809
|
+
};
|
|
3810
|
+
if (value === null || value === undefined) return {};
|
|
3811
|
+
if (!value || typeof value !== "object") {
|
|
3812
|
+
throw new TypeError("CjsResMan extension route must be a format, format array, or object.");
|
|
3813
|
+
}
|
|
3814
|
+
return value;
|
|
3815
|
+
}
|
|
3816
|
+
function createFormatDescriptor(Format, defaults = {}) {
|
|
3817
|
+
if (typeof Format !== "function") {
|
|
3818
|
+
throw new TypeError("CjsResMan format descriptor requires a format class.");
|
|
3819
|
+
}
|
|
3820
|
+
return Object.freeze({
|
|
3821
|
+
Format,
|
|
3822
|
+
defaults: snapshotFormatDefaults(defaults)
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
function createExtensionFormatDescriptor(entry, defaults, label) {
|
|
3826
|
+
if (typeof entry === "function") return createFormatDescriptor(entry, defaults);
|
|
3827
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
3828
|
+
throw new TypeError(`CjsResMan extension ${label} must be a format class or descriptor.`);
|
|
3829
|
+
}
|
|
3830
|
+
const Format = entry.Format || entry.format;
|
|
3831
|
+
return createFormatDescriptor(Format, entry.defaults === undefined ? defaults : entry.defaults);
|
|
3832
|
+
}
|
|
3833
|
+
function validateOrderedExtensionFormats(descriptors, ext) {
|
|
3834
|
+
for (let index = 0; index < descriptors.length - 1; index++) {
|
|
3835
|
+
const Format = descriptors[index].Format;
|
|
3836
|
+
if (typeof Format.isSupported !== "function") {
|
|
3837
|
+
throw new TypeError(`CjsResMan extension .${ext} format ${Format.name || index} has no support probe and must be last.`);
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
function assertExtensionTarget(Target, label) {
|
|
3842
|
+
if (typeof Target !== "function" || typeof Target.from !== "function" && typeof Target.fromYAML !== "function") {
|
|
3843
|
+
throw new TypeError(`CjsResMan extension ${label} must provide static from(values) or fromYAML(values, context).`);
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
function createExtensionTargetError(resource, message, cause = null) {
|
|
3847
|
+
const path = getResourceDiagnosticPath(resource);
|
|
3848
|
+
const error = new Error(`CjsResMan extension target failed for ${path}: ${message}`, cause ? {
|
|
3849
|
+
cause
|
|
3850
|
+
} : undefined);
|
|
3851
|
+
error.code = "CJS_RESOURCE_EXTENSION_TARGET_FAILED";
|
|
3852
|
+
error.path = path;
|
|
3853
|
+
if (cause) error.cause = cause;
|
|
3854
|
+
return error;
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3493
3857
|
export { CjsResMan };
|
|
3494
3858
|
//# sourceMappingURL=CjsResMan.js.map
|