@carbonenginejs/runtime-resource 0.18.0 → 0.19.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.
Files changed (81) hide show
  1. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +16 -4
  2. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -1
  3. package/dist/format/carbonEffect/buildCarbonEffectContainer.js +11 -1
  4. package/dist/format/carbonEffect/buildCarbonEffectContainer.js.map +1 -1
  5. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +36 -0
  6. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
  7. package/dist/format/carbonEffect/carbonEffectRecords.js +45 -1
  8. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -1
  9. package/dist/format/carbonEffect/carbonEffectResourceTransform.js +120 -60
  10. package/dist/format/carbonEffect/carbonEffectResourceTransform.js.map +1 -1
  11. package/dist/format/index.js +1 -1
  12. package/dist/formats/dds/core/helpers.js +35 -6
  13. package/dist/formats/dds/core/helpers.js.map +1 -1
  14. package/dist/formats/fbx/core/helpers.js +1 -1
  15. package/dist/formats/fbx/core/helpers.js.map +1 -1
  16. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +53 -1
  17. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -1
  18. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +10 -1
  19. package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -1
  20. package/dist/formats/index.js +3 -0
  21. package/dist/formats/index.js.map +1 -1
  22. package/dist/formats/jpeg/core/helpers.js +1 -1
  23. package/dist/formats/jpeg/core/helpers.js.map +1 -1
  24. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js +205 -44
  25. package/dist/formats/pickle/core/CjsPickleProtocol0Reader.js.map +1 -1
  26. package/dist/formats/schemabound/CjsSchemaBoundFormat.js +236 -0
  27. package/dist/formats/schemabound/CjsSchemaBoundFormat.js.map +1 -0
  28. package/dist/formats/schemabound/core/schemaBoundErrors.js +9 -0
  29. package/dist/formats/schemabound/core/schemaBoundErrors.js.map +1 -0
  30. package/dist/formats/schemabound/core/schemaBoundValues.js +336 -0
  31. package/dist/formats/schemabound/core/schemaBoundValues.js.map +1 -0
  32. package/dist/formats/schemabound/index.js +2 -0
  33. package/dist/formats/schemabound/index.js.map +1 -0
  34. package/dist/formats/sqlite/CjsSqliteFormat.js +244 -0
  35. package/dist/formats/sqlite/CjsSqliteFormat.js.map +1 -0
  36. package/dist/formats/sqlite/core/sqlitePages.js +159 -0
  37. package/dist/formats/sqlite/core/sqlitePages.js.map +1 -0
  38. package/dist/formats/sqlite/core/sqliteRecords.js +174 -0
  39. package/dist/formats/sqlite/core/sqliteRecords.js.map +1 -0
  40. package/dist/formats/sqlite/core/sqliteSchema.js +100 -0
  41. package/dist/formats/sqlite/core/sqliteSchema.js.map +1 -0
  42. package/dist/formats/sqlite/index.js +2 -0
  43. package/dist/formats/sqlite/index.js.map +1 -0
  44. package/dist/formats/static/CjsStaticFormat.js +222 -0
  45. package/dist/formats/static/CjsStaticFormat.js.map +1 -0
  46. package/dist/formats/static/index.js +3 -0
  47. package/dist/formats/static/index.js.map +1 -0
  48. package/dist/formats/static/staticContainers.js +157 -0
  49. package/dist/formats/static/staticContainers.js.map +1 -0
  50. package/dist/formats/webgl/core/buildGlslEffectContainer.js +3 -1
  51. package/dist/formats/webgl/core/buildGlslEffectContainer.js.map +1 -1
  52. package/dist/formats/webgl/core/effectPackage.js +11 -2
  53. package/dist/formats/webgl/core/effectPackage.js.map +1 -1
  54. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +10 -2
  55. package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
  56. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +3 -1
  57. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -1
  58. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js +179 -131
  59. package/dist/formats/webgpu/core/carbonWebgpu/containerViews.js.map +1 -1
  60. package/dist/formats/webgpu/core/effectBackendBodySet.js +20 -20
  61. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  62. package/dist/formats/webgpu/core/packageEffect.js +14 -1
  63. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  64. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +9 -1
  65. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  66. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +14 -0
  67. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  68. package/docs/concepts/shader-resource-model.md +34 -1
  69. package/docs/formats/README.md +21 -0
  70. package/docs/formats/carbon-effect-container.md +54 -27
  71. package/docs/formats/pickle.md +36 -0
  72. package/docs/formats/schemabound.md +123 -0
  73. package/docs/formats/static.md +166 -0
  74. package/docs/formats/webgl/README.md +5 -3
  75. package/docs/formats/webgpu/README.md +2 -2
  76. package/docs/formats/webgpu/architecture.md +2 -2
  77. package/docs/formats/webgpu/formats/carbon-webgpu.md +13 -2
  78. package/docs/formats/webgpu/guides/effect-packaging.md +1 -2
  79. package/docs/formats/webgpu/reference/wgsl-compatibility.md +2 -2
  80. package/docs/reference/classes/formats.md +33 -0
  81. package/package.json +4 -1
@@ -0,0 +1,166 @@
1
+ # Client `.static` container identification
2
+
3
+ Status: Experimental
4
+ Scope: `@carbonenginejs/runtime-resource/formats/static`
5
+ Audience: Resource integrators reading client static data
6
+ Summary: Identifies which of three unrelated containers a `.static` file holds, so a caller can route it to the format that decodes it.
7
+
8
+ ## Why this exists
9
+
10
+ `.static` names a role, not a format. Three unrelated containers ship under the
11
+ single extension, and each fails differently when guessed at. Measured across
12
+ the 45 `.static` files in one build:
13
+
14
+ | Family | Count | Signature |
15
+ |---|---:|---|
16
+ | SQLite 3 | 14 | `SQLite format 3\0` |
17
+ | Embedded schema | 25 | four-byte schema LENGTH, then `(d` or `(l` |
18
+ | Sibling schema | 6 | no signature; has a `.schema` companion |
19
+
20
+ The six unidentified files are exactly the six with a `.schema` companion —
21
+ `constellations`, `dialogs`, `factionsowningsolarsystems`, `jumps`, `regions`
22
+ and `systems` — so the detector's "unknown" set is not a gap in coverage but the
23
+ family that cannot be read without its companion.
24
+
25
+ ## Boundary
26
+
27
+ `CjsStaticFormat` identifies. It decodes nothing.
28
+
29
+ - **SQLite** containers hold `cache(key, value, time)` and
30
+ `indexes(key, value)`, with a JSON document per record.
31
+ - **Embedded-schema** containers put a schema, not a record, behind that prefix.
32
+ The prefix is the schema's LENGTH: read `[4, 4 + length)` with
33
+ `CjsPickleFormat` and hand the rest to `CjsSchemaBoundFormat`.
34
+ - **Sibling-schema** containers report `unknown` with `requires: "schema"`, and
35
+ are decoded by `CjsSchemaBoundFormat` once the caller has that companion.
36
+
37
+ Detection is signature-based. It never trusts a file name and never executes
38
+ anything.
39
+
40
+ ## This format identifies; it does not decode
41
+
42
+ It reports the family, where the payload starts, and what is still missing. The
43
+ caller takes that to the format that owns the family:
44
+
45
+ ```js
46
+ const probe = await CjsStaticFormat.resolveType(bytes);
47
+
48
+ if (probe.preferred === CJS_STATIC_FAMILIES.SQLITE)
49
+ {
50
+ return CjsSqliteFormat.readJSON(bytes);
51
+ }
52
+
53
+ if (probe.preferred === CJS_STATIC_FAMILIES.PICKLE)
54
+ {
55
+ // The prefix is the SCHEMA's length, not a wrapper to skip. Handing the whole
56
+ // remainder to a pickle reader throws CJS_PICKLE_FORMAT_TRAILING_DATA, on the binary
57
+ // payload, long after the schema has parsed.
58
+ const length = new DataView(bytes.buffer, bytes.byteOffset).getUint32(0, true);
59
+
60
+ return CjsSchemaBoundFormat.read(bytes.subarray(4 + length), {
61
+ schema: CjsPickleFormat.read(bytes.subarray(4, 4 + length))
62
+ });
63
+ }
64
+ ```
65
+
66
+ **This changed on 2026-08-15.** A `read()` here dispatched to those two formats
67
+ itself, and the SQLite family additionally required a driver injected through
68
+ `options.sqlite`. Both are gone. An identification format should not be the
69
+ routing table for two others, and deciding what to decode belongs to whoever
70
+ asked. Nothing outside this format's own tests ever called `read()`.
71
+
72
+
73
+ ## Both of the remaining families are the same container
74
+
75
+ The schema is encoded differently; the payload behind it is identical.
76
+
77
+ **The pickle behind that prefix is a SCHEMA, not a record.** Protocol 0's `c`
78
+ (`GLOBAL`) names a module and an attribute for the unpickler to import, and
79
+ `R` then calls it — the remote-execution vector — so `CjsPickleFormat` refuses
80
+ globals by design. These files need exactly one: `collections.OrderedDict`,
81
+ because a schema's attribute order is its field order. That one name is rebuilt
82
+ as a plain object and every other global is still refused. See
83
+ [the pickle format](pickle.md).
84
+
85
+ **The sibling-schema family states its layout in YAML.** The `.schema` file is
86
+ YAML and states the whole binary layout — sizes, types, optional flags, list item
87
+ sizes, vector precision and a key-to-offset footer — so **nothing needs
88
+ deriving**, unlike a container whose layout is defined outside the file and has
89
+ to be worked out and pinned. `CjsSchemaBoundFormat` reads it:
90
+ [schema-bound containers](schemabound.md). All six datasets decode — the map
91
+ skeleton of regions, constellations and systems. The celestial detail (moons,
92
+ planets, belts, stars, gates) is in the embedded-schema family, not this one.
93
+
94
+ ## Use
95
+
96
+ Identification goes through the shared type-resolution seam rather than a
97
+ private entry point — see [format type resolution](../concepts/format-type-resolution.md).
98
+
99
+ ```js
100
+ import { CjsStaticFormat, CJS_STATIC_FAMILIES } from
101
+ "@carbonenginejs/runtime-resource/formats/static";
102
+
103
+ const probe = await CjsStaticFormat.resolveType(bytes);
104
+
105
+ if (probe.preferred === CJS_STATIC_FAMILIES.PICKLE)
106
+ {
107
+ // See the routing example above: the prefix is a schema length.
108
+ }
109
+ ```
110
+
111
+ `isSupported()` and its `inspect()` alias report on the declaration seam;
112
+ `resolveType()` is the content-verified one. This format is an unusual case for
113
+ that contract: `.static` carries no in-band declaration at all, so there is
114
+ nothing for the content to disagree with. The signature is both claim and
115
+ evidence, `resolveType()` is therefore always `verified`, and `metadata.declared`
116
+ is `null` with `mismatch` always false.
117
+
118
+ `describe()` returns the underlying
119
+ `{ family, byteLength, payloadOffset, prefix, decodable, requires, reason }` without
120
+ building a probe. `payload()` returns the bytes past any wrapper, which is what a
121
+ caller hands to the format that owns the family.
122
+
123
+ ## Reading a container, rather than identifying one
124
+
125
+ Identifying a family and then routing it to the format that owns it is the same
126
+ twenty lines in every caller, so they are written once here and exported from the
127
+ same subpath:
128
+
129
+ ```js
130
+ import {
131
+ ReadStaticContainer, // SQLite family
132
+ ReadEmbeddedSchemaContainer, // schema length, pickled schema, payload
133
+ ReadSchemaBoundContainer, // payload plus its .schema sibling
134
+ } from "@carbonenginejs/runtime-resource/formats/static";
135
+
136
+ const skins = await ReadStaticContainer(bytes, "res:/staticdata/skins.static");
137
+ ```
138
+
139
+ The `path` argument only ever names the file in an error.
140
+
141
+ These import the pickle, schema-bound and SQLite formats, because reading a
142
+ `.static` genuinely needs them - a container format that wraps other containers
143
+ has to reach the formats it wraps. What a format must not do is pull the rest of
144
+ the library in behind it, and nothing here reaches outside `formats/`.
145
+
146
+ `CjsStaticFormat` itself still imports nothing and decodes nothing. Routing is a
147
+ sibling module so that identification is not also the routing table, which is the
148
+ arrangement `read()` was cut back to on 2026-08-15.
149
+
150
+ ### Errors
151
+
152
+ | Code | When |
153
+ | --- | --- |
154
+ | `CJS_STATIC_FORMAT_FAMILY_UNSUPPORTED` | the bytes are a `.static` of a family this reader does not read; carries `family` |
155
+ | `CJS_STATIC_FORMAT_SHAPE_INVALID` | right family, wrong container - no `cache` table, or a schema length running past the end |
156
+ | `CJS_STATIC_FORMAT_RECORD_INVALID` | one stored value is not JSON; carries `key` |
157
+
158
+ The family check is kept even on `ReadSchemaBoundContainer`, where the caller has
159
+ already supplied a schema: those bytes carry no signature at all, so given the
160
+ wrong schema they decode into plausible nonsense rather than failing.
161
+
162
+ ## Related documentation
163
+
164
+ - [Formats](README.md)
165
+ - [Data-only pickle protocol 0](pickle.md)
166
+ - [Schema-bound containers](schemabound.md)
@@ -39,9 +39,11 @@ compiled effect bytes
39
39
  The emitted artifact is a shared Carbon v15 container, not a tagged-chunk
40
40
  package; the GLSL replaces the DXBC in each stage's program slot. Backend body
41
41
  keys deliberately retain the historical `body_<offset>_<size>` form.
42
- `EFFECT_PERMUTATION_GRAPH_CHUNK` (`PGRF`) still records every Cartesian
43
- permutation, its option indices, its exact source record, and a
44
- content-deduplicated body identity.
42
+ The permutation graph still records every Cartesian permutation, its option
43
+ indices, its exact source record, and a content-deduplicated body identity — but
44
+ it is a **derived view** (`CJS_EFFECT_PERMUTATION_GRAPH`, built by
45
+ `buildEffectPermutationGraph`), not a stored `PGRF` chunk. That chunk and its
46
+ `EFFECT_PERMUTATION_GRAPH_CHUNK` export are retired; nothing emits or reads one.
45
47
 
46
48
  ## Completeness
47
49
 
@@ -16,8 +16,8 @@ Carbon version-15 records.
16
16
  Unsupported requested shader semantics fail explicitly instead of producing a
17
17
  partially translated selected pass. `BuildEffect` preserves every permutation
18
18
  row and representable non-program description/reflection fields in the Carbon
19
- container. Non-dynamic sampler names are unrecoverable and stage order is
20
- canonicalized. Source-stage DXBC is replaced by WGSL or an empty program slot.
19
+ container, including non-dynamic sampler names and the authored stage order.
20
+ Source-stage DXBC is replaced by WGSL or an empty program slot.
21
21
  Selected mode writes WGSL only for the resolved body's requested passes; all
22
22
  mode attempts every distinct body after the resolved body passes the initial
23
23
  translation gate. Backend and runtime completeness remain broader gates.
@@ -38,8 +38,8 @@ then package data to the engine.
38
38
  - Reading, inspecting, and building Carbon-record Carbon WebGPU containers.
39
39
  - Resolving one compiled-effect permutation and complete pass.
40
40
  - Preserving every source permutation row and representable non-program
41
- description field; non-dynamic sampler names are unrecoverable and stage
42
- order is canonicalized.
41
+ description field, including non-dynamic sampler names and the authored stage
42
+ order both are preserved, whatever Carbon's runtime does with them.
43
43
  Stored-body identity is exact emitted-description-byte identity; it does not
44
44
  promise to retain the source alias partition after programs are replaced.
45
45
  - Writing WGSL to program slots and bind-group/transform data to the optional
@@ -9,12 +9,23 @@ Summary: Defines the Carbon v15 record container used for WebGPU effects, its ba
9
9
 
10
10
  A `.carbonwebgpu` file is a stock Carbon version-15 compiled-effect container whose
11
11
  program slots carry WGSL instead of DXBC. It preserves every permutation row
12
- and representable non-program description/reflection fields. Non-dynamic
13
- sampler names are unrecoverable and stage order is canonicalized. Source-stage
12
+ and representable non-program description/reflection fields, including
13
+ non-dynamic sampler names and the authored stage order. Source-stage
14
14
  programs are not stored: a translated slot contains WGSL and an untranslated slot is empty.
15
15
  Each translated pass may also carry one WebGPU backend block containing
16
16
  bind-group layouts and resource transforms.
17
17
 
18
+ That preservation is **inherited from the shared codec, and now pinned on this
19
+ path.** It holds because the emit round-trips the description through the same
20
+ Carbon classes the plain container uses and mutates only `stage.sourceProgram`
21
+ and `pass.backendBlock`.
22
+
23
+ `carbon-webgpu-emit-fidelity.test.mjs` compares both fields source-to-emitted
24
+ across the corpus rather than trusting that restraint: 2,960 emitted files,
25
+ 39,252 sampler names and 84,912 stage orders, of which 102 are non-canonical —
26
+ the case that distinguishes a preserved order from a re-derived one. Widening
27
+ what the emit touches now fails there instead of surfacing in a consumer later.
28
+
18
29
  There is no Carbon WebGPU-specific magic, envelope, payload tag, or container version.
19
30
  Backend identity comes from the resource path, such as `effect.webgpu/`, just
20
31
  as Carbon selects `effect.dx11/`, `effect.dx12/`, or `effect.metal/`.
@@ -50,8 +50,7 @@ The emitted bytes are a stock Carbon v15 effect container:
50
50
  - each distinct emitted description body is stored once, based on exact emitted
51
51
  bytes rather than the source alias partition;
52
52
  - representable non-program description/reflection fields remain in the Carbon
53
- tree; non-dynamic sampler names are unrecoverable and stage order is
54
- canonicalized;
53
+ tree, non-dynamic sampler names and the authored stage order included;
55
54
  - translated stage program slots contain UTF-8 WGSL;
56
55
  - untranslated or unsupported stage program slots have zero length; and
57
56
  - translated passes may carry a WebGPU backend block with bind-group layouts
@@ -1403,8 +1403,8 @@ program slots and coverage degrades to `partial`. The in-memory build result
1403
1403
  retains its specific reason; the wire does not. A reread can say only that the
1404
1404
  body carries no translated programs. Permutation topology and representable
1405
1405
  non-program description fields remain present, but source-stage programs are
1406
- not stored. Non-dynamic sampler names are unrecoverable and stage order is
1407
- canonicalized.
1406
+ not stored. Non-dynamic sampler names and the authored stage order are both
1407
+ preserved.
1408
1408
 
1409
1409
  Translating every body is deliberately **not** treated as backend completeness.
1410
1410
  `backendComplete` and `runtimeComplete` remain false, matching the sibling
@@ -689,6 +689,39 @@ Data-only Python pickle format facade that currently decodes protocol 0 into JSO
689
689
  - Visibility: Public
690
690
  - Kind: Original CarbonEngineJS class
691
691
 
692
+ <!-- class:CjsSchemaBoundFormat -->
693
+ ## `CjsSchemaBoundFormat`
694
+
695
+ Reads a binary record container against the separate schema document that describes its layout.
696
+
697
+ - Export: `@carbonenginejs/runtime-resource/formats/schemabound`
698
+ - Source: `src/formats/schemabound/CjsSchemaBoundFormat.js`
699
+ - Visibility: Public
700
+ - Kind: Original CarbonEngineJS class
701
+ - Notes: The schema is required and there is no default. These bytes carry no signature, so the wrong schema decodes into plausible nonsense rather than failing.
702
+
703
+ <!-- class:CjsSqliteFormat -->
704
+ ## `CjsSqliteFormat`
705
+
706
+ Reads a SQLite 3 container as data: the tables it holds and every row of them, with no SQL and no query engine.
707
+
708
+ - Export: `@carbonenginejs/runtime-resource/formats/sqlite`
709
+ - Source: `src/formats/sqlite/CjsSqliteFormat.js`
710
+ - Visibility: Public
711
+ - Kind: Original CarbonEngineJS class
712
+ - Notes: Read-only and full-scan. Index b-trees are never walked, and nothing here needs a driver or a filesystem.
713
+
714
+ <!-- class:CjsStaticFormat -->
715
+ ## `CjsStaticFormat`
716
+
717
+ Identifies which of three unrelated containers a client `.static` file holds, so a caller can route it to the format that decodes it.
718
+
719
+ - Export: `@carbonenginejs/runtime-resource/formats/static`
720
+ - Source: `src/formats/static/CjsStaticFormat.js`
721
+ - Visibility: Public
722
+ - Kind: Original CarbonEngineJS class
723
+ - Notes: Signature-based, and it decodes nothing at all since 2026-08-15. It reports the family and the payload offset; `CjsPickleFormat`, `CjsSqliteFormat` and `CjsSchemaBoundFormat` decode.
724
+
692
725
  <!-- class:CjsPickleProtocol0Reader -->
693
726
  ## `CjsPickleProtocol0Reader`
694
727
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbonenginejs/runtime-resource",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -39,6 +39,9 @@
39
39
  "./formats/png": "./dist/formats/png/index.js",
40
40
  "./formats/red": "./dist/formats/red/index.js",
41
41
  "./formats/red/schema": "./dist/formats/red/core/blackDefinitions.js",
42
+ "./formats/schemabound": "./dist/formats/schemabound/index.js",
43
+ "./formats/sqlite": "./dist/formats/sqlite/index.js",
44
+ "./formats/static": "./dist/formats/static/index.js",
42
45
  "./formats/stl": "./dist/formats/stl/index.js",
43
46
  "./formats/tga": "./dist/formats/tga/index.js",
44
47
  "./formats/wav": "./dist/formats/wav/index.js",