@carbonenginejs/runtime 0.1.0-alpha.1 → 0.1.0-alpha.3

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.
Files changed (49) hide show
  1. package/dist/global/model/CjsModel.js +3 -1
  2. package/dist/global/model/CjsModel.js.map +1 -1
  3. package/dist/global/model/document/CjsDocumentHydrator.js +5 -1
  4. package/dist/global/model/document/CjsDocumentHydrator.js.map +1 -1
  5. package/dist/global/schema/CjsSchema.js +100 -2
  6. package/dist/global/schema/CjsSchema.js.map +1 -1
  7. package/dist/global/schema/hydration.js +16 -0
  8. package/dist/global/schema/hydration.js.map +1 -1
  9. package/dist/index.js +2 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/resource/CjsResMan.js +177 -4
  12. package/dist/resource/CjsResMan.js.map +1 -1
  13. package/dist/resource/format/CjsBlueReader.js +8 -1
  14. package/dist/resource/format/CjsBlueReader.js.map +1 -1
  15. package/dist/resource/formats/webgl/core/glsl/DxbcGlslEmitter.js +23 -5
  16. package/dist/resource/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
  17. package/dist/resource/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +12 -3
  18. package/dist/resource/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js.map +1 -1
  19. package/dist/resource/index.js +2 -0
  20. package/dist/resource/index.js.map +1 -1
  21. package/dist/resource/texture/SolidColorTextureConstructor.js +46 -0
  22. package/dist/resource/texture/SolidColorTextureConstructor.js.map +1 -0
  23. package/dist/resource/texture/TriTextureRes.js +43 -0
  24. package/dist/resource/texture/TriTextureRes.js.map +1 -1
  25. package/dist/resource/texture/index.js +2 -0
  26. package/dist/resource/texture/index.js.map +1 -1
  27. package/dist/resource/texture/solidColorTexture.js +111 -0
  28. package/dist/resource/texture/solidColorTexture.js.map +1 -0
  29. package/dist/trinity/eve/attachment/decal/EveSpaceObjectDecal.js +19 -8
  30. package/dist/trinity/eve/attachment/decal/EveSpaceObjectDecal.js.map +1 -1
  31. package/dist/trinity/eve/child/EveChildMesh.js +17 -3
  32. package/dist/trinity/eve/child/EveChildMesh.js.map +1 -1
  33. package/dist/trinity/eve/locator/EveLocatorSets.js +12 -3
  34. package/dist/trinity/eve/locator/EveLocatorSets.js.map +1 -1
  35. package/dist/trinity/eve/spaceObject/EveSpaceObject2.js +15 -2
  36. package/dist/trinity/eve/spaceObject/EveSpaceObject2.js.map +1 -1
  37. package/dist/trinity/eve/spaceObject/IEveSpaceObject2ParentData.js +44 -52
  38. package/dist/trinity/eve/spaceObject/IEveSpaceObject2ParentData.js.map +1 -1
  39. package/docs/resource/concepts/resource-lifecycle.md +10 -8
  40. package/docs/resource/formats/geometry-interchange.md +50 -0
  41. package/docs/resource/formats/webgl/decl-io.md +241 -397
  42. package/docs/resource/formats/webgl/memory-structured.md +91 -128
  43. package/docs/resource/formats/webgl/texture-sample.md +306 -469
  44. package/docs/resource/formats/webgpu/reference/wgsl-compatibility.md +54 -85
  45. package/docs/resource/reference/classes/texture.md +11 -1
  46. package/docs/resource/reference/motherlode-cache.md +24 -11
  47. package/docs/resource/reference/queues.md +23 -1
  48. package/docs/trinityal/reference/classes/dropped.md +11 -1
  49. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import { CjsSchema } from './CjsSchema.js';
2
+
1
3
  /**
2
4
  * Hydration adapter seam - the population contract for readers that build
3
5
  * runtime objects incrementally (black/red). The ordering guarantee:
@@ -24,8 +26,18 @@
24
26
  * SetValues is called directly rather than through CjsSchema.setValues so a
25
27
  * reader works with only the schema layer loaded; the two are the same path
26
28
  * for every class that has the method.
29
+ *
30
+ * A schema-declared class without SetValues - one that no longer extends
31
+ * CjsModel - populates through CjsSchema.setValues, which answers without the
32
+ * model layer. The READER says so, as `ctx.declared`: it resolved the kind to
33
+ * a constructor itself and asks CjsSchema whether that class declares fields,
34
+ * so the adapter never inspects the object. Raw assignment stays for what
35
+ * declares nothing - the reader's fallback carrier, and a caller-supplied
36
+ * plain class. Without this arm, taking a class off the base would silently
37
+ * drop it to Object.assign: no coercion, no writability gate, no settle.
27
38
  */
28
39
 
40
+
29
41
  /**
30
42
  * Resolves a normalized adapter from hydration options. The returned object
31
43
  * always exposes construct/applyValues/finalize with the documented defaults.
@@ -47,6 +59,10 @@ function resolveHydrationAdapter(options = {}) {
47
59
  instance.SetValues(values, ctx?.options);
48
60
  return instance;
49
61
  }
62
+ if (ctx?.declared === true) {
63
+ CjsSchema.setValues(instance, values, ctx.options);
64
+ return instance;
65
+ }
50
66
  return Object.assign(instance, values);
51
67
  },
52
68
  finalize(instance, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"hydration.js","sources":["../../../../src/global/schema/hydration.js"],"sourcesContent":["/**\n * Hydration adapter seam - the population contract for readers that build\n * runtime objects incrementally (black/red). The ordering guarantee:\n *\n * 1. construct - build every instance (register refs before values, so\n * cycles/back-references resolve)\n * 2. applyValues - apply each node's field values\n * 3. finalize - run once per instance AFTER the whole graph is built,\n * so references are already resolved\n *\n * DEFAULTS (operator ruling, 2026-09-05; resource-population.md owns the\n * decision): a resolved class is populated through its own validated setter -\n * \"if someone is using Object.assign, they probably should be using\n * SetValues\" - and a class-owned Initialize runs once post-graph, which is\n * the incremental-reader counterpart of the reader+IInitialize handshake\n * Blue performs and CjsModel.from performs for whole-bag JSON. Plain\n * fallback carriers (unresolved kinds) keep raw assignment: that IS their\n * contract, not a bypass.\n *\n * The single escape hatch is `options.adapter`, per-hook optional. The\n * per-kind adapters map and the hydrationAdapter alias are gone - they had\n * zero consumers. Callers wanting raw assignment pass their own adapter.\n *\n * SetValues is called directly rather than through CjsSchema.setValues so a\n * reader works with only the schema layer loaded; the two are the same path\n * for every class that has the method.\n */\n\n/**\n * Resolves a normalized adapter from hydration options. The returned object\n * always exposes construct/applyValues/finalize with the documented defaults.\n *\n * @param {Object} [options]\n * @param {Object} [options.adapter] Per-hook overrides: construct/applyValues/finalize.\n * @returns {{ construct: Function, applyValues: Function, finalize: Function }}\n */\nexport function resolveHydrationAdapter(options = {})\n{\n const custom = options.adapter || null;\n\n return {\n construct(kind, ctx)\n {\n if (custom && typeof custom.construct === \"function\") return custom.construct(kind, ctx);\n return undefined;\n },\n applyValues(instance, values, ctx)\n {\n if (custom && typeof custom.applyValues === \"function\") return custom.applyValues(instance, values, ctx);\n if (instance && typeof instance.SetValues === \"function\")\n {\n instance.SetValues(values, ctx?.options);\n return instance;\n }\n return Object.assign(instance, values);\n },\n finalize(instance, ctx)\n {\n if (custom && typeof custom.finalize === \"function\")\n {\n custom.finalize(instance, ctx);\n return;\n }\n if (instance && typeof instance.Initialize === \"function\") instance.Initialize();\n }\n };\n}\n"],"names":["resolveHydrationAdapter","options","custom","adapter","construct","kind","ctx","undefined","applyValues","instance","values","SetValues","Object","assign","finalize","Initialize"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,uBAAuBA,CAACC,OAAO,GAAG,EAAE,EACpD;AACI,EAAA,MAAMC,MAAM,GAAGD,OAAO,CAACE,OAAO,IAAI,IAAI;EAEtC,OAAO;AACHC,IAAAA,SAASA,CAACC,IAAI,EAAEC,GAAG,EACnB;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACE,SAAS,KAAK,UAAU,EAAE,OAAOF,MAAM,CAACE,SAAS,CAACC,IAAI,EAAEC,GAAG,CAAC;AACxF,MAAA,OAAOC,SAAS;IACpB,CAAC;AACDC,IAAAA,WAAWA,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,EACjC;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACM,WAAW,KAAK,UAAU,EAAE,OAAON,MAAM,CAACM,WAAW,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,CAAC;MACxG,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACE,SAAS,KAAK,UAAU,EACxD;QACIF,QAAQ,CAACE,SAAS,CAACD,MAAM,EAAEJ,GAAG,EAAEL,OAAO,CAAC;AACxC,QAAA,OAAOQ,QAAQ;AACnB,MAAA;AACA,MAAA,OAAOG,MAAM,CAACC,MAAM,CAACJ,QAAQ,EAAEC,MAAM,CAAC;IAC1C,CAAC;AACDI,IAAAA,QAAQA,CAACL,QAAQ,EAAEH,GAAG,EACtB;MACI,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACY,QAAQ,KAAK,UAAU,EACnD;AACIZ,QAAAA,MAAM,CAACY,QAAQ,CAACL,QAAQ,EAAEH,GAAG,CAAC;AAC9B,QAAA;AACJ,MAAA;AACA,MAAA,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACM,UAAU,KAAK,UAAU,EAAEN,QAAQ,CAACM,UAAU,EAAE;AACpF,IAAA;GACH;AACL;;;;"}
1
+ {"version":3,"file":"hydration.js","sources":["../../../../src/global/schema/hydration.js"],"sourcesContent":["/**\n * Hydration adapter seam - the population contract for readers that build\n * runtime objects incrementally (black/red). The ordering guarantee:\n *\n * 1. construct - build every instance (register refs before values, so\n * cycles/back-references resolve)\n * 2. applyValues - apply each node's field values\n * 3. finalize - run once per instance AFTER the whole graph is built,\n * so references are already resolved\n *\n * DEFAULTS (operator ruling, 2026-09-05; resource-population.md owns the\n * decision): a resolved class is populated through its own validated setter -\n * \"if someone is using Object.assign, they probably should be using\n * SetValues\" - and a class-owned Initialize runs once post-graph, which is\n * the incremental-reader counterpart of the reader+IInitialize handshake\n * Blue performs and CjsModel.from performs for whole-bag JSON. Plain\n * fallback carriers (unresolved kinds) keep raw assignment: that IS their\n * contract, not a bypass.\n *\n * The single escape hatch is `options.adapter`, per-hook optional. The\n * per-kind adapters map and the hydrationAdapter alias are gone - they had\n * zero consumers. Callers wanting raw assignment pass their own adapter.\n *\n * SetValues is called directly rather than through CjsSchema.setValues so a\n * reader works with only the schema layer loaded; the two are the same path\n * for every class that has the method.\n *\n * A schema-declared class without SetValues - one that no longer extends\n * CjsModel - populates through CjsSchema.setValues, which answers without the\n * model layer. The READER says so, as `ctx.declared`: it resolved the kind to\n * a constructor itself and asks CjsSchema whether that class declares fields,\n * so the adapter never inspects the object. Raw assignment stays for what\n * declares nothing - the reader's fallback carrier, and a caller-supplied\n * plain class. Without this arm, taking a class off the base would silently\n * drop it to Object.assign: no coercion, no writability gate, no settle.\n */\n\nimport { CjsSchema } from \"./CjsSchema.js\";\n\n/**\n * Resolves a normalized adapter from hydration options. The returned object\n * always exposes construct/applyValues/finalize with the documented defaults.\n *\n * @param {Object} [options]\n * @param {Object} [options.adapter] Per-hook overrides: construct/applyValues/finalize.\n * @returns {{ construct: Function, applyValues: Function, finalize: Function }}\n */\nexport function resolveHydrationAdapter(options = {})\n{\n const custom = options.adapter || null;\n\n return {\n construct(kind, ctx)\n {\n if (custom && typeof custom.construct === \"function\") return custom.construct(kind, ctx);\n return undefined;\n },\n applyValues(instance, values, ctx)\n {\n if (custom && typeof custom.applyValues === \"function\") return custom.applyValues(instance, values, ctx);\n if (instance && typeof instance.SetValues === \"function\")\n {\n instance.SetValues(values, ctx?.options);\n return instance;\n }\n if (ctx?.declared === true)\n {\n CjsSchema.setValues(instance, values, ctx.options);\n return instance;\n }\n return Object.assign(instance, values);\n },\n finalize(instance, ctx)\n {\n if (custom && typeof custom.finalize === \"function\")\n {\n custom.finalize(instance, ctx);\n return;\n }\n if (instance && typeof instance.Initialize === \"function\") instance.Initialize();\n }\n };\n}\n"],"names":["resolveHydrationAdapter","options","custom","adapter","construct","kind","ctx","undefined","applyValues","instance","values","SetValues","declared","CjsSchema","setValues","Object","assign","finalize","Initialize"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,uBAAuBA,CAACC,OAAO,GAAG,EAAE,EACpD;AACI,EAAA,MAAMC,MAAM,GAAGD,OAAO,CAACE,OAAO,IAAI,IAAI;EAEtC,OAAO;AACHC,IAAAA,SAASA,CAACC,IAAI,EAAEC,GAAG,EACnB;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACE,SAAS,KAAK,UAAU,EAAE,OAAOF,MAAM,CAACE,SAAS,CAACC,IAAI,EAAEC,GAAG,CAAC;AACxF,MAAA,OAAOC,SAAS;IACpB,CAAC;AACDC,IAAAA,WAAWA,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,EACjC;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACM,WAAW,KAAK,UAAU,EAAE,OAAON,MAAM,CAACM,WAAW,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,CAAC;MACxG,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACE,SAAS,KAAK,UAAU,EACxD;QACIF,QAAQ,CAACE,SAAS,CAACD,MAAM,EAAEJ,GAAG,EAAEL,OAAO,CAAC;AACxC,QAAA,OAAOQ,QAAQ;AACnB,MAAA;AACA,MAAA,IAAIH,GAAG,EAAEM,QAAQ,KAAK,IAAI,EAC1B;QACIC,SAAS,CAACC,SAAS,CAACL,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,CAACL,OAAO,CAAC;AAClD,QAAA,OAAOQ,QAAQ;AACnB,MAAA;AACA,MAAA,OAAOM,MAAM,CAACC,MAAM,CAACP,QAAQ,EAAEC,MAAM,CAAC;IAC1C,CAAC;AACDO,IAAAA,QAAQA,CAACR,QAAQ,EAAEH,GAAG,EACtB;MACI,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACe,QAAQ,KAAK,UAAU,EACnD;AACIf,QAAAA,MAAM,CAACe,QAAQ,CAACR,QAAQ,EAAEH,GAAG,CAAC;AAC9B,QAAA;AACJ,MAAA;AACA,MAAA,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACS,UAAU,KAAK,UAAU,EAAET,QAAQ,CAACS,UAAU,EAAE;AACpF,IAAA;GACH;AACL;;;;"}
package/dist/index.js CHANGED
@@ -134,6 +134,7 @@ export { Tr2SamplerSetup } from './resource/shader/sampler/Tr2SamplerSetup.js';
134
134
  export { RegisterShaderResources, ShaderResourceExtensions } from './resource/shader/registerShaderResources.js';
135
135
  export { CjsTextureArrayRes } from './resource/texture/CjsTextureArrayRes.js';
136
136
  export { CjsTextureArrayResParameterProxy } from './resource/texture/CjsTextureArrayResParameterProxy.js';
137
+ export { RegisterSolidColorTexture, SolidColorTextureConstructor } from './resource/texture/SolidColorTextureConstructor.js';
137
138
  export { Tr2ImageRes } from './resource/texture/Tr2ImageRes.js';
138
139
  export { Tr2TextureLodManager } from './resource/texture/Tr2TextureLodManager.js';
139
140
  export { Tr2TextureLodUpdateRequest } from './resource/texture/Tr2TextureLodUpdateRequest.js';
@@ -146,6 +147,7 @@ export { Tr2TexturePipelineStepLimitSize } from './resource/texture/Tr2TexturePi
146
147
  export { Tr2TexturePipelineStepLoad } from './resource/texture/Tr2TexturePipelineStepLoad.js';
147
148
  export { Tr2TexturePipelineStepPack } from './resource/texture/Tr2TexturePipelineStepPack.js';
148
149
  export { TriTextureRes } from './resource/texture/TriTextureRes.js';
150
+ export { ColorPrefix, IsSolidColorTexturePath, ParseColor, RasterizeSolidColor } from './resource/texture/solidColorTexture.js';
149
151
  export { CjsResourceProbe } from './resource/format/CjsResourceProbe.js';
150
152
  export { ResourcePayloadType, ResourcePayloadValues, validateAudioPayload, validateRgbaPayload, validateTexturePayload, validateVideoPayload } from './resource/format/payloadContract.js';
151
153
  export { CjsFormat, CjsFormatOutputRole, CjsFormatReadMode } from './resource/format/CjsFormat.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -157,6 +157,30 @@ let nextLocalValueIdentity = 1;
157
157
  * @typedef {CjsResourceOwnership|CjsResourceReloadCandidate} CjsResourceMutationAuthority
158
158
  */
159
159
 
160
+ // `dynamic:` protocol (BlueResMan.cpp:219-223): the name is the segment after
161
+ // this prefix, and everything after the following slash is the query.
162
+ const DYNAMIC_RESOURCE_PREFIX = "dynamic:/";
163
+
164
+ /**
165
+ * Carbon logs and returns null for an unknown dynamic name or a constructor that
166
+ * yields nothing (BlueResMan.cpp:225-235). There is no log channel here, and a
167
+ * null handle breaks every caller that composes on GetResource, so the same
168
+ * condition is raised, named.
169
+ *
170
+ * @param {string} path Normalized dynamic path.
171
+ * @param {string} name Constructor name.
172
+ * @param {string} code Error code.
173
+ * @param {string} message Error message.
174
+ * @returns {Error}
175
+ */
176
+ function dynamicResourceError(path, name, code, message) {
177
+ const error = new Error(`CjsResMan ${message} (${path})`);
178
+ error.code = code;
179
+ error.path = path;
180
+ error.name = "CjsResManDynamicResourceError";
181
+ error.constructorName = name;
182
+ return error;
183
+ }
160
184
  class CjsResMan {
161
185
  // Carbon reaches its manager through the process-wide `BeResMan`
162
186
  // (Tr2Effect.cpp:397). Ours needs the same reach for the same reason: a
@@ -216,6 +240,14 @@ class CjsResMan {
216
240
  #resourceExtensionRoutes = new WeakMap();
217
241
  #resourceHandlerModes = new WeakMap();
218
242
  #resourceTypeCandidates = new Map();
243
+ // BlueResMan::m_dynamicConstructors (BlueResMan.h:154-155).
244
+ #dynamicConstructors = new Map();
245
+ #dynamicResources = new WeakSet();
246
+ // Carbon caches a LoadObject BUILDER and creates a new object per call
247
+ // (BlueResMan.cpp:653-795). For an OBJECT-mode route that hydrates, the
248
+ // payload is the decoded plain values - the builder - and this records what
249
+ // builds from them.
250
+ #objectBuilders = new WeakMap();
219
251
 
220
252
  /**
221
253
  * Create a GPU-free resource manager and apply its initial registration.
@@ -997,6 +1029,39 @@ class CjsResMan {
997
1029
  return this.#extensionRoutes.get(normalizeResourceExtension(extension)) || null;
998
1030
  }
999
1031
 
1032
+ /**
1033
+ * Registers the constructor that builds `dynamic:/<name>/<query>` resources
1034
+ * (BlueResMan.cpp:296-305). The name is lowercased, as Carbon's is; the
1035
+ * constructor's `GetResource(query)` returns a resource for that query.
1036
+ *
1037
+ * @param {string} name Name following `dynamic:/`.
1038
+ * @param {{GetResource: function(string): object}} constructor Resource constructor.
1039
+ * @returns {CjsResMan} This resource manager.
1040
+ * @throws {TypeError} If the name is empty or the constructor has no GetResource.
1041
+ */
1042
+ RegisterResourceConstructor(name, constructor) {
1043
+ const key = String(name ?? "").toLowerCase();
1044
+ if (!key) {
1045
+ throw new TypeError("CjsResMan.RegisterResourceConstructor requires a name.");
1046
+ }
1047
+ if (typeof constructor?.GetResource !== "function") {
1048
+ throw new TypeError(`CjsResMan dynamic resource constructor "${key}" must implement GetResource.`);
1049
+ }
1050
+ this.#dynamicConstructors.set(key, constructor);
1051
+ return this;
1052
+ }
1053
+
1054
+ /**
1055
+ * Removes a dynamic resource constructor (BlueResMan.cpp:312-325).
1056
+ *
1057
+ * @param {string} name Name following `dynamic:/`.
1058
+ * @returns {CjsResMan} This resource manager.
1059
+ */
1060
+ UnregisterResourceConstructor(name) {
1061
+ this.#dynamicConstructors.delete(String(name ?? "").toLowerCase());
1062
+ return this;
1063
+ }
1064
+
1000
1065
  /**
1001
1066
  * Return registered format facades for one normalized input extension.
1002
1067
  *
@@ -1061,6 +1126,11 @@ class CjsResMan {
1061
1126
  this.motherLode.KeepAlive(cacheKey);
1062
1127
  return existing;
1063
1128
  }
1129
+ // Below the cache lookup, as in BlueResMan::GetResourceHelper, so identical
1130
+ // queries share one resource.
1131
+ if (key.startsWith(DYNAMIC_RESOURCE_PREFIX)) {
1132
+ return this.#CreateDynamicResource(key, cacheKey);
1133
+ }
1064
1134
  if (!existing && options.reload === true) {
1065
1135
  this.InvalidateReadCache(key, {
1066
1136
  source: options.source || this.source
@@ -1133,12 +1203,25 @@ class CjsResMan {
1133
1203
  const ownership = this.#RequireResourceOwnership(resource, "object:begin");
1134
1204
  const existing = this.objectOperations.get(resource);
1135
1205
  if (existing?.ownership === ownership) {
1136
- return existing.promise;
1206
+ // Joining an in-flight load shares its promise - unless the route builds
1207
+ // per caller, when the load's own caller receives the instance built at
1208
+ // publication and a joiner gets its own.
1209
+ const route = this.#resourceExtensionRoutes.get(resource);
1210
+ if (!route?.Target && !route?.Identify) return existing.promise;
1211
+ return existing.promise.then(result => this.#objectBuilders.has(resource) ? this.#BuildObject(resource) : result);
1137
1212
  }
1138
1213
  if (resource.HasPayload?.()) {
1139
1214
  resource.KeepPayloadAlive?.();
1215
+ if (this.#objectBuilders.has(resource)) {
1216
+ return Promise.resolve().then(() => this.#BuildObject(resource));
1217
+ }
1140
1218
  return Promise.resolve(getPublishedResourceObject(resource, this.#resourceExtensionRoutes.get(resource) || null, this.#resourceHandlerModes.get(resource) || null));
1141
1219
  }
1220
+ if (this.#dynamicResources.has(resource)) {
1221
+ // A dynamic resource is built by its constructor, never read from a
1222
+ // source: without a payload it failed, and there is nothing to fetch.
1223
+ return Promise.reject(resource.error || dynamicResourceError(resource.GetPath(), "", "CJS_RESMAN_DYNAMIC_RESOURCE_UNAVAILABLE", "dynamic resource has no payload"));
1224
+ }
1142
1225
  const promise = this.QueueResourceObject(resource, operationOptions);
1143
1226
  const operation = {
1144
1227
  promise,
@@ -1754,6 +1837,7 @@ class CjsResMan {
1754
1837
  * @returns {*} Original or hydrated object value.
1755
1838
  */
1756
1839
  #HydrateExtensionObject(resource, values, options, resolved) {
1840
+ this.#objectBuilders.delete(resource);
1757
1841
  const route = resolved.route;
1758
1842
  if (!route || !route.Target && !route.Identify) return values;
1759
1843
  const Format = resolved.descriptor?.Format || null;
@@ -1781,8 +1865,39 @@ class CjsResMan {
1781
1865
  throw createExtensionTargetError(resource, "Identify returned an invalid target constructor.", error);
1782
1866
  }
1783
1867
  }
1868
+ const hydrated = this.#HydrateTarget(resource, Target, values, context);
1869
+ // Publication decides whether this route builds per caller: only an
1870
+ // OBJECT-mode handle hands its object out, so only it keeps the builder.
1871
+ this.#objectBuilders.set(resource, {
1872
+ Target,
1873
+ context,
1874
+ values,
1875
+ hydrated
1876
+ });
1877
+ return hydrated;
1878
+ }
1879
+
1880
+ /**
1881
+ * Build one object from decoded values. Every build receives its own
1882
+ * structured copy, so no two callers share a nested array, typed array or
1883
+ * reader carrier however `from` treats its input - Carbon's objects are
1884
+ * likewise independent, each read out of the cached reader's bytes.
1885
+ *
1886
+ * @param {CjsResource} resource Resource whose values are built.
1887
+ * @param {Function} Target Resolved target constructor.
1888
+ * @param {*} values Decoded plain values.
1889
+ * @param {object} context Hydration context captured at publication.
1890
+ * @returns {*} A new hydrated object.
1891
+ */
1892
+ #HydrateTarget(resource, Target, values, context) {
1893
+ let copy;
1894
+ try {
1895
+ copy = structuredClone(values);
1896
+ } catch (error) {
1897
+ throw createExtensionTargetError(resource, "Decoded values could not be copied for a per-caller build.", error);
1898
+ }
1784
1899
  try {
1785
- const hydrated = typeof Target.fromYAML === "function" ? Target.fromYAML(values, context) : Target.from(values);
1900
+ const hydrated = typeof Target.fromYAML === "function" ? Target.fromYAML(copy, context) : Target.from(copy);
1786
1901
  if (hydrated && typeof hydrated.then === "function") {
1787
1902
  throw new TypeError("Extension target hydration must be synchronous.");
1788
1903
  }
@@ -1792,6 +1907,53 @@ class CjsResMan {
1792
1907
  }
1793
1908
  }
1794
1909
 
1910
+ /**
1911
+ * A fresh object from a resident builder: Carbon's
1912
+ * `builder->CreateObjectWithYield` (BlueResMan.cpp:785).
1913
+ *
1914
+ * @param {CjsResource} resource Resource holding the decoded values as payload.
1915
+ * @returns {*} A new hydrated object.
1916
+ */
1917
+ #BuildObject(resource) {
1918
+ const builder = this.#objectBuilders.get(resource);
1919
+ return this.#HydrateTarget(resource, builder.Target, resource.GetPayload(), builder.context);
1920
+ }
1921
+
1922
+ /**
1923
+ * `dynamic:` branch of BlueResMan::GetResourceHelper (BlueResMan.cpp:219-245).
1924
+ * Inserted not cacheable, Carbon's `CACHING_NOT_ALLOWED`: a dynamic resource is
1925
+ * never admitted to the byte cache.
1926
+ *
1927
+ * @param {string} key Normalized `dynamic:/<name>/<query>` path.
1928
+ * @param {string} cacheKey MotherLode key.
1929
+ * @returns {CjsResource} Canonical dynamic resource.
1930
+ * @throws {Error} If no constructor is registered or it yields no resource.
1931
+ */
1932
+ #CreateDynamicResource(key, cacheKey) {
1933
+ const rest = key.slice(DYNAMIC_RESOURCE_PREFIX.length);
1934
+ const slash = rest.indexOf("/");
1935
+ const name = slash === -1 ? rest : rest.slice(0, slash);
1936
+ const query = slash === -1 ? "" : rest.slice(slash + 1);
1937
+ const constructor = this.#dynamicConstructors.get(name);
1938
+ if (!constructor) {
1939
+ throw dynamicResourceError(key, name, "CJS_RESMAN_DYNAMIC_CONSTRUCTOR_MISSING", `no dynamic constructor is registered for "${name}"`);
1940
+ }
1941
+ const resource = constructor.GetResource(query);
1942
+ if (!resource || typeof resource.Initialize !== "function" || typeof resource.SetObjectLoader !== "function") {
1943
+ throw dynamicResourceError(key, name, "CJS_RESMAN_DYNAMIC_RESOURCE_UNAVAILABLE", `dynamic constructor "${name}" returned no CjsResource-compatible resource`);
1944
+ }
1945
+ this.#dynamicResources.add(resource);
1946
+ resource.SetObjectLoader(() => this.GetObject(key));
1947
+ const insertion = this.motherLode.Insert(cacheKey, resource, {
1948
+ replace: true,
1949
+ cacheable: false
1950
+ });
1951
+ const canonical = insertion?.resource || resource;
1952
+ this.#BindResourceLifecycle(cacheKey, canonical);
1953
+ this.motherLode.KeepAlive(cacheKey);
1954
+ return canonical;
1955
+ }
1956
+
1795
1957
  /**
1796
1958
  * Publish the final CPU object outcome and loaded state. Every
1797
1959
  * CjsResource-compatible handle owns the reader/converter result through
@@ -1848,12 +2010,23 @@ class CjsResMan {
1848
2010
  */
1849
2011
  #PublishResourceObjectValue(resource, object, options) {
1850
2012
  const mode = resolveResourceHandlerMode(resource, this.#resourceExtensionRoutes.get(resource) ? this.#resourceHandlerModes.get(resource) : null);
1851
- resource.SetPayload?.(object, options);
2013
+ const builder = this.#objectBuilders.get(resource);
2014
+ const buildsPerCaller = mode === ResourceHandlerMode.OBJECT && builder?.hydrated === object;
2015
+ if (builder && !buildsPerCaller) this.#objectBuilders.delete(resource);
2016
+ // A per-caller route retains the decoded values - the builder - and hands
2017
+ // `object` to this publication's caller only. The values then live under the
2018
+ // payload lease, like any payload.
2019
+ const payload = buildsPerCaller ? builder.values : object;
2020
+ if (buildsPerCaller) {
2021
+ delete builder.values;
2022
+ delete builder.hydrated;
2023
+ }
2024
+ resource.SetPayload?.(payload, options);
1852
2025
  // The two modes differ in one thing only: what `object` names and what the
1853
2026
  // caller is handed back. RESOURCE publishes the stable handle, so both
1854
2027
  // point at the resource; OBJECT publishes the reader outcome.
1855
2028
  const published = mode === ResourceHandlerMode.RESOURCE ? resource : object;
1856
- resource.object = published;
2029
+ resource.object = buildsPerCaller ? payload : published;
1857
2030
  // PREPARED, not LOADED: `object` is the reader/converter OUTCOME, so the
1858
2031
  // bytes have already been turned into whatever they needed to become.
1859
2032
  // LOADED means raw source data is in hand and still has to be prepared - a