@carbonenginejs/runtime-resource 0.18.1 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"CjsStaticFormat.js","sources":["../../../../src/formats/static/CjsStaticFormat.js"],"sourcesContent":["\nconst SQLITE_SIGNATURE = \"SQLite format 3\\0\";\nconst PICKLE_PREFIX_BYTES = 4;\n\n/** Container families found behind the single `.static` extension. */\nexport const CJS_STATIC_FAMILIES = Object.freeze({\n SQLITE: \"sqlite\",\n PICKLE: \"pickle\",\n UNKNOWN: \"unknown\"\n});\n\n/**\n * Identifies which container a client `.static` file actually holds.\n *\n * The extension names a role, not a format. Three unrelated containers ship\n * under it, and each fails in its own way when guessed at:\n *\n * - **SQLite 3** — a `cache(key, value, time)` plus `indexes(key, value)`\n * database whose values are JSON documents. `CjsSqliteFormat` reads it.\n * - **Prefixed pickle** — a four-byte little-endian prefix followed by a\n * protocol-0 pickle. Many of these carry class-construction opcodes, which\n * the data-only pickle reader refuses by design; that refusal is correct and\n * is surfaced rather than worked around.\n * - **Schema-bound** — a binary record container whose `.schema` companion is\n * YAML describing its own layout: attribute sizes and types, optional flags,\n * lists with a fixed item size, vectors with a precision, and a key footer of\n * key-to-offset pairs. Nothing needs deriving, because the build ships the\n * layout. Six datasets are stored this way, the celestial tables among them.\n * `CjsSchemaBoundFormat` reads it, given that companion.\n *\n * Detection is signature-based and never executes or trusts file names.\n *\n * **This class identifies and does not decode.** It reports the family, the\n * payload offset and what is still missing; the caller takes that answer to the\n * format that owns the family. It carried a `read()` that dispatched to\n * `CjsPickleFormat` and `CjsSqliteFormat` until 2026-08-15, which made an\n * identification format the routing table for two others and put the decision\n * about what to decode in the wrong place. Nothing outside its own tests ever\n * called it.\n */\nexport class CjsStaticFormat\n{\n\n /**\n * Report which family a container holds, on the declaration seam.\n *\n * `.static` declares nothing: the extension names a role and every family\n * wears it. The signature is therefore the only claim there is, which is why\n * this format reads it here rather than trusting a name.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {CjsResourceProbe} Declaration-derived probe.\n */\n static isSupported(input, options = null)\n {\n return Probe(this.describe(input), false, options);\n }\n\n /**\n * Alias for canonical naming.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {CjsResourceProbe} Declaration-derived probe.\n */\n static inspect(input, options = null)\n {\n return this.isSupported(input, options);\n }\n\n /**\n * Content-verified family resolution.\n *\n * Contract: docs/concepts/format-type-resolution.md. There is no declared\n * type to disagree with here — the extension is silent — so the signature is\n * both the claim and the evidence, and the resolution is always verified.\n * `preferred` names the decode route, and all three now lead into this\n * package: `CjsPickleFormat`, `CjsSqliteFormat`, and `CjsSchemaBoundFormat`\n * once the caller has the `.schema` companion to hand it.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {Promise<CjsResourceProbe>} Verified probe.\n */\n static async resolveType(input, options = null)\n {\n return Probe(this.describe(input), true, options);\n }\n\n /**\n * Describe one container without decoding it or building a probe.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @returns {object} Family, payload offset, and whether this package decodes it.\n */\n static describe(input)\n {\n const bytes = Normalize(input);\n\n if (MatchesSqlite(bytes))\n {\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.SQLITE,\n byteLength: bytes.byteLength,\n payloadOffset: 0,\n prefix: null,\n // Decodable outright since CjsSqliteFormat landed. This reported\n // `requires: \"sqlite\"` while a caller had to supply an engine, and was\n // the example behind the argument that a capability can depend on the\n // caller's environment - an argument that now needs a different one.\n decodable: true,\n requires: null,\n reason: \"Recognized a SQLite container.\"\n });\n }\n\n if (MatchesPickle(bytes))\n {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.PICKLE,\n byteLength: bytes.byteLength,\n payloadOffset: PICKLE_PREFIX_BYTES,\n prefix: view.getUint32(0, true),\n decodable: true,\n requires: null,\n reason: null\n });\n }\n\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.UNKNOWN,\n byteLength: bytes.byteLength,\n payloadOffset: 0,\n prefix: null,\n decodable: false,\n requires: \"schema\",\n // `decodable` is this format's own answer, and it stays false: these bytes\n // carry nothing to identify, so the family is an inference from the absence\n // of the other two signatures rather than a reading. `requires` names what\n // closes that gap - with the companion, CjsSchemaBoundFormat decodes it.\n reason: \"No signature. A schema-bound container needs its .schema companion, \"\n + \"which is YAML describing the layout. CjsSchemaBoundFormat reads it from there.\"\n });\n }\n\n /**\n * Return the decodable payload for a container, without its wrapper.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @returns {Uint8Array} Payload bytes.\n */\n static payload(input)\n {\n const bytes = Normalize(input);\n const detected = this.describe(bytes);\n\n return bytes.subarray(detected.payloadOffset);\n }\n\n\n static id = \"static\";\n static extensions = Object.freeze([ \".static\" ]);\n static type = Object.freeze([ \"data\" ]);\n static mediaTypes = Object.freeze([ \"data\" ]);\n\n /**\n * The extension is the only input this format claims.\n *\n * Naming the three families here would be a lie in the other direction: a\n * caller cannot hand this format \"a pickle\" or \"a sqlite\" and expect it to\n * route, because the whole point of the class is that `.static` declares\n * nothing and the signature has to be read. The family belongs in\n * `describe()`, which measures it, not in a static that asserts it.\n */\n static inputTypes = Object.freeze([ \"static\" ]);\n static outputTypes = Object.freeze([ \"json\", \"payload\" ]);\n static debugOutputTypes = Object.freeze([ \"raw\" ]);\n\n}\n\n/**\n * Build the shared probe payload.\n *\n * Formats report a plain probe-shaped object rather than constructing a\n * `CjsResourceProbe`, which keeps this module free of the decorated class and\n * of the build transform it needs.\n */\nfunction Probe(detected, verified, options)\n{\n return {\n format: \"static\",\n source: \"buffer\",\n supported: detected.decodable ? \"full\" : \"partial\",\n confidence: detected.family === CJS_STATIC_FAMILIES.UNKNOWN ? 0.5 : 1,\n preferred: detected.family,\n verified,\n reason: detected.reason ?? `Recognized a ${detected.family} container.`,\n variants: [ { kind: \"container\", codec: detected.family, supported: detected.decodable } ],\n metadata: verified\n ? { ...detected, declared: null, resolved: detected.family, mismatch: false }\n : detected,\n ...(options || {})\n };\n}\n\nfunction MatchesSqlite(bytes)\n{\n if (bytes.byteLength < SQLITE_SIGNATURE.length) return false;\n\n for (let index = 0; index < SQLITE_SIGNATURE.length; index++)\n {\n if (bytes[index] !== SQLITE_SIGNATURE.charCodeAt(index)) return false;\n }\n\n return true;\n}\n\nfunction MatchesPickle(bytes)\n{\n // A protocol-0 pickle opens a container then names it: \"(dp1\\n\" or \"(lp1\\n\".\n if (bytes.byteLength < PICKLE_PREFIX_BYTES + 3) return false;\n if (bytes[PICKLE_PREFIX_BYTES] !== 0x28) return false;\n\n const kind = bytes[PICKLE_PREFIX_BYTES + 1];\n\n return kind === 0x64 || kind === 0x6c;\n}\n\nfunction Normalize(input)\n{\n if (input instanceof ArrayBuffer) return new Uint8Array(input);\n\n if (ArrayBuffer.isView(input))\n {\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n }\n\n const error = new TypeError(\"A .static container must be an ArrayBuffer or a view over one.\");\n\n error.code = \"CJS_STATIC_INPUT_INVALID\";\n throw error;\n}\n"],"names":["SQLITE_SIGNATURE","PICKLE_PREFIX_BYTES","CJS_STATIC_FAMILIES","Object","freeze","SQLITE","PICKLE","UNKNOWN","CjsStaticFormat","isSupported","input","options","Probe","describe","inspect","resolveType","bytes","Normalize","MatchesSqlite","family","byteLength","payloadOffset","prefix","decodable","requires","reason","MatchesPickle","view","DataView","buffer","byteOffset","getUint32","payload","detected","subarray","id","extensions","type","mediaTypes","inputTypes","outputTypes","debugOutputTypes","verified","format","source","supported","confidence","preferred","variants","kind","codec","metadata","declared","resolved","mismatch","length","index","charCodeAt","ArrayBuffer","Uint8Array","isView","error","TypeError","code"],"mappings":"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"}
1
+ {"version":3,"file":"CjsStaticFormat.js","sources":["../../../../src/formats/static/CjsStaticFormat.js"],"sourcesContent":["\nconst SQLITE_SIGNATURE = \"SQLite format 3\\0\";\nconst PICKLE_PREFIX_BYTES = 4;\n\n/** Container families found behind the single `.static` extension. */\nexport const CJS_STATIC_FAMILIES = Object.freeze({\n SQLITE: \"sqlite\",\n PICKLE: \"pickle\",\n UNKNOWN: \"unknown\"\n});\n\n/**\n * Identifies which container a client `.static` file actually holds.\n *\n * The extension names a role, not a format. Three unrelated containers ship\n * under it, and each fails in its own way when guessed at:\n *\n * - **SQLite 3** — a `cache(key, value, time)` plus `indexes(key, value)`\n * database whose values are JSON documents. `CjsSqliteFormat` reads it.\n * - **Embedded schema** — a four-byte little-endian SCHEMA LENGTH followed by\n * that schema as a protocol-0 pickle, and then a payload. The prefix is not a\n * wrapper to skip: slice `[4, 4 + length)` for the schema and give the rest\n * to `CjsSchemaBoundFormat`. All 25 of these name one global,\n * `collections.OrderedDict`, which the pickle reader rebuilds as plain data.\n * - **Schema-bound** — a binary record container whose `.schema` companion is\n * YAML describing its own layout: attribute sizes and types, optional flags,\n * lists with a fixed item size, vectors with a precision, and a key footer of\n * key-to-offset pairs. Nothing needs deriving, because the build ships the\n * layout. Six datasets are stored this way - the map skeleton of regions,\n * constellations and systems. `CjsSchemaBoundFormat` reads it, given that\n * companion, and reads the embedded-schema family above from the same code.\n *\n * Detection is signature-based and never executes or trusts file names.\n *\n * **This class identifies and does not decode.** It reports the family, the\n * payload offset and what is still missing; the caller takes that answer to the\n * format that owns the family. It carried a `read()` that dispatched to\n * `CjsPickleFormat` and `CjsSqliteFormat` until 2026-08-15, which made an\n * identification format the routing table for two others and put the decision\n * about what to decode in the wrong place. Nothing outside its own tests ever\n * called it.\n */\nexport class CjsStaticFormat\n{\n\n /**\n * Report which family a container holds, on the declaration seam.\n *\n * `.static` declares nothing: the extension names a role and every family\n * wears it. The signature is therefore the only claim there is, which is why\n * this format reads it here rather than trusting a name.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {CjsResourceProbe} Declaration-derived probe.\n */\n static isSupported(input, options = null)\n {\n return Probe(this.describe(input), false, options);\n }\n\n /**\n * Alias for canonical naming.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {CjsResourceProbe} Declaration-derived probe.\n */\n static inspect(input, options = null)\n {\n return this.isSupported(input, options);\n }\n\n /**\n * Content-verified family resolution.\n *\n * Contract: docs/concepts/format-type-resolution.md. There is no declared\n * type to disagree with here — the extension is silent — so the signature is\n * both the claim and the evidence, and the resolution is always verified.\n * `preferred` names the decode route, and all three now lead into this\n * package: `CjsPickleFormat`, `CjsSqliteFormat`, and `CjsSchemaBoundFormat`\n * once the caller has the `.schema` companion to hand it.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @param {object} [options] Probe options.\n * @returns {Promise<CjsResourceProbe>} Verified probe.\n */\n static async resolveType(input, options = null)\n {\n return Probe(this.describe(input), true, options);\n }\n\n /**\n * Describe one container without decoding it or building a probe.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @returns {object} Family, payload offset, and whether this package decodes it.\n */\n static describe(input)\n {\n const bytes = Normalize(input);\n\n if (MatchesSqlite(bytes))\n {\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.SQLITE,\n byteLength: bytes.byteLength,\n payloadOffset: 0,\n prefix: null,\n // Decodable outright since CjsSqliteFormat landed. This reported\n // `requires: \"sqlite\"` while a caller had to supply an engine, and was\n // the example behind the argument that a capability can depend on the\n // caller's environment - an argument that now needs a different one.\n decodable: true,\n requires: null,\n reason: \"Recognized a SQLite container.\"\n });\n }\n\n if (MatchesPickle(bytes))\n {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.PICKLE,\n byteLength: bytes.byteLength,\n payloadOffset: PICKLE_PREFIX_BYTES,\n prefix: view.getUint32(0, true),\n decodable: true,\n requires: null,\n reason: null\n });\n }\n\n return Object.freeze({\n family: CJS_STATIC_FAMILIES.UNKNOWN,\n byteLength: bytes.byteLength,\n payloadOffset: 0,\n prefix: null,\n decodable: false,\n requires: \"schema\",\n // `decodable` is this format's own answer, and it stays false: these bytes\n // carry nothing to identify, so the family is an inference from the absence\n // of the other two signatures rather than a reading. `requires` names what\n // closes that gap - with the companion, CjsSchemaBoundFormat decodes it.\n reason: \"No signature. A schema-bound container needs its .schema companion, \"\n + \"which is YAML describing the layout. CjsSchemaBoundFormat reads it from there.\"\n });\n }\n\n /**\n * Return the decodable payload for a container, without its wrapper.\n *\n * @param {ArrayBuffer|ArrayBufferView} input Container bytes.\n * @returns {Uint8Array} Payload bytes.\n */\n static payload(input)\n {\n const bytes = Normalize(input);\n const detected = this.describe(bytes);\n\n return bytes.subarray(detected.payloadOffset);\n }\n\n\n static id = \"static\";\n static extensions = Object.freeze([ \".static\" ]);\n static type = Object.freeze([ \"data\" ]);\n static mediaTypes = Object.freeze([ \"data\" ]);\n\n /**\n * The extension is the only input this format claims.\n *\n * Naming the three families here would be a lie in the other direction: a\n * caller cannot hand this format \"a pickle\" or \"a sqlite\" and expect it to\n * route, because the whole point of the class is that `.static` declares\n * nothing and the signature has to be read. The family belongs in\n * `describe()`, which measures it, not in a static that asserts it.\n */\n static inputTypes = Object.freeze([ \"static\" ]);\n static outputTypes = Object.freeze([ \"json\", \"payload\" ]);\n static debugOutputTypes = Object.freeze([ \"raw\" ]);\n\n}\n\n/**\n * Build the shared probe payload.\n *\n * Formats report a plain probe-shaped object rather than constructing a\n * `CjsResourceProbe`, which keeps this module free of the decorated class and\n * of the build transform it needs.\n */\nfunction Probe(detected, verified, options)\n{\n return {\n format: \"static\",\n source: \"buffer\",\n supported: detected.decodable ? \"full\" : \"partial\",\n confidence: detected.family === CJS_STATIC_FAMILIES.UNKNOWN ? 0.5 : 1,\n preferred: detected.family,\n verified,\n reason: detected.reason ?? `Recognized a ${detected.family} container.`,\n variants: [ { kind: \"container\", codec: detected.family, supported: detected.decodable } ],\n metadata: verified\n ? { ...detected, declared: null, resolved: detected.family, mismatch: false }\n : detected,\n ...(options || {})\n };\n}\n\nfunction MatchesSqlite(bytes)\n{\n if (bytes.byteLength < SQLITE_SIGNATURE.length) return false;\n\n for (let index = 0; index < SQLITE_SIGNATURE.length; index++)\n {\n if (bytes[index] !== SQLITE_SIGNATURE.charCodeAt(index)) return false;\n }\n\n return true;\n}\n\nfunction MatchesPickle(bytes)\n{\n // A protocol-0 pickle opens a container then names it: \"(dp1\\n\" or \"(lp1\\n\".\n if (bytes.byteLength < PICKLE_PREFIX_BYTES + 3) return false;\n if (bytes[PICKLE_PREFIX_BYTES] !== 0x28) return false;\n\n const kind = bytes[PICKLE_PREFIX_BYTES + 1];\n\n return kind === 0x64 || kind === 0x6c;\n}\n\nfunction Normalize(input)\n{\n if (input instanceof ArrayBuffer) return new Uint8Array(input);\n\n if (ArrayBuffer.isView(input))\n {\n return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);\n }\n\n const error = new TypeError(\"A .static container must be an ArrayBuffer or a view over one.\");\n\n error.code = \"CJS_STATIC_FORMAT_INPUT_INVALID\";\n throw error;\n}\n"],"names":["SQLITE_SIGNATURE","PICKLE_PREFIX_BYTES","CJS_STATIC_FAMILIES","Object","freeze","SQLITE","PICKLE","UNKNOWN","CjsStaticFormat","isSupported","input","options","Probe","describe","inspect","resolveType","bytes","Normalize","MatchesSqlite","family","byteLength","payloadOffset","prefix","decodable","requires","reason","MatchesPickle","view","DataView","buffer","byteOffset","getUint32","payload","detected","subarray","id","extensions","type","mediaTypes","inputTypes","outputTypes","debugOutputTypes","verified","format","source","supported","confidence","preferred","variants","kind","codec","metadata","declared","resolved","mismatch","length","index","charCodeAt","ArrayBuffer","Uint8Array","isView","error","TypeError","code"],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  export { CJS_STATIC_FAMILIES, CjsStaticFormat, CjsStaticFormat as default } from './CjsStaticFormat.js';
2
+ export { ReadEmbeddedSchemaContainer, ReadSchemaBoundContainer, ReadStaticContainer } from './staticContainers.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,157 @@
1
+ import { CjsPickleFormat } from '../pickle/CjsPickleFormat.js';
2
+ import { CjsSchemaBoundFormat } from '../schemabound/CjsSchemaBoundFormat.js';
3
+ import { CjsSqliteFormat } from '../sqlite/CjsSqliteFormat.js';
4
+ import { CjsStaticFormat, CJS_STATIC_FAMILIES } from './CjsStaticFormat.js';
5
+
6
+ /**
7
+ * Reads a client `.static` container of whichever family it holds.
8
+ *
9
+ * `.static` names a role rather than a format, and three unrelated containers
10
+ * wear the extension, so identification and decoding are separate jobs done by
11
+ * separate formats. `CjsStaticFormat` says which family the bytes hold and
12
+ * decodes nothing; these functions take that answer to the format that owns the
13
+ * family. Nothing routes on the extension, which proves nothing.
14
+ *
15
+ * Reading a `.static` needs the three formats that own its families, so this
16
+ * module imports them. That is the format depending on formats, which is what a
17
+ * container format that wraps other containers has to do; what a format must not
18
+ * do is drag the rest of the library in behind it, and nothing here reaches
19
+ * outside `formats/`.
20
+ *
21
+ * It is a sibling of `CjsStaticFormat` rather than part of it, which keeps the
22
+ * 2026-08-15 decision intact: that class dispatched to two other formats and was
23
+ * cut back to identifying only, so that identification is not also the routing
24
+ * table. Routing lives here and the identification stays clean.
25
+ *
26
+ * Nothing here is specific to one publisher or one export. Measured 2026-08-16
27
+ * across three publishers at builds 3466501, 3466054 and 3466057: 45 `.static`
28
+ * files on each, split 14 SQLite, 25 embedded-schema and 6 schema-companion,
29
+ * with not one file unique to a publisher and not one that changes family
30
+ * between them.
31
+ */
32
+
33
+ const CACHE_TABLE = "cache";
34
+
35
+ /** Raises a family mismatch, naming what was found and what was wanted. */
36
+ function FamilyError(path, found, wanted, reason) {
37
+ const error = new TypeError(`${path} is a ${found} .static container, not a ${wanted} one.${reason ? ` ${reason}` : ""}`);
38
+ error.code = "CJS_STATIC_FORMAT_FAMILY_UNSUPPORTED";
39
+ error.family = found;
40
+ error.path = path;
41
+ return error;
42
+ }
43
+
44
+ /**
45
+ * Reads the SQLite family into records keyed by their container key.
46
+ *
47
+ * The record shape is the container's own: a `cache` table of `key`, `value`
48
+ * and `time`, where every `value` is a JSON document.
49
+ *
50
+ * @param {Uint8Array|ArrayBuffer} bytes Container bytes.
51
+ * @param {string} [path] Logical path, for error messages.
52
+ * @returns {Promise<object>} Records keyed by their container key.
53
+ */
54
+ async function ReadStaticContainer(bytes, path = ".static input") {
55
+ const probe = await CjsStaticFormat.resolveType(bytes);
56
+ if (probe.preferred !== CJS_STATIC_FAMILIES.SQLITE) {
57
+ throw FamilyError(path, probe.preferred, CJS_STATIC_FAMILIES.SQLITE, probe.reason);
58
+ }
59
+ const tables = CjsSqliteFormat.readJSON(bytes, {
60
+ tables: [CACHE_TABLE]
61
+ });
62
+ const rows = tables[CACHE_TABLE];
63
+ if (!Array.isArray(rows)) {
64
+ const error = new TypeError(`${path} has no ${CACHE_TABLE} table.`);
65
+ error.code = "CJS_STATIC_FORMAT_SHAPE_INVALID";
66
+ error.path = path;
67
+ throw error;
68
+ }
69
+ const records = {};
70
+ for (const row of rows) {
71
+ records[String(row.key)] = ParseRecord(row.value, row.key, path);
72
+ }
73
+ return records;
74
+ }
75
+
76
+ /**
77
+ * Reads the family whose schema is embedded ahead of the payload.
78
+ *
79
+ * The framing is a `uint32` schema length, the schema as a protocol-0 pickle,
80
+ * and then a payload in exactly the container `CjsSchemaBoundFormat` reads. It
81
+ * is the same format as the `.schema` sibling family; only the schema's encoding
82
+ * differs.
83
+ *
84
+ * **Slice the schema exactly.** Handing the whole file to a pickle reader fails,
85
+ * but it fails on the payload's bytes long after the schema has parsed, so the
86
+ * error names an opcode and points nowhere near the real boundary. That reading
87
+ * is what left this family classified as a separate self-describing format for
88
+ * as long as it was.
89
+ *
90
+ * @param {Uint8Array|ArrayBuffer} bytes Container bytes.
91
+ * @param {string} [path] Logical path, for error messages.
92
+ * @returns {object|Array} Decoded records.
93
+ */
94
+ function ReadEmbeddedSchemaContainer(bytes, path = ".static input") {
95
+ const source = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
96
+ if (source.byteLength < 4) {
97
+ const error = new TypeError(`${path} is too short to carry a schema length.`);
98
+ error.code = "CJS_STATIC_FORMAT_SHAPE_INVALID";
99
+ error.path = path;
100
+ throw error;
101
+ }
102
+ const view = new DataView(source.buffer, source.byteOffset, source.byteLength);
103
+ const length = view.getUint32(0, true);
104
+ if (length <= 0 || length + 4 > source.byteLength) {
105
+ const error = new TypeError(`${path} does not begin with a usable schema length.`);
106
+ error.code = "CJS_STATIC_FORMAT_SHAPE_INVALID";
107
+ error.path = path;
108
+ throw error;
109
+ }
110
+ return CjsSchemaBoundFormat.read(source.subarray(4 + length), {
111
+ schema: CjsPickleFormat.read(source.subarray(4, 4 + length))
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Reads the family whose layout lives in a `.schema` sibling.
117
+ *
118
+ * The family check is worth keeping even though the caller already fetched a
119
+ * `.schema` alongside the payload, because these bytes carry no signature at
120
+ * all - given the wrong schema they decode into plausible nonsense rather than
121
+ * failing, so the one cheap guard there is belongs in the path.
122
+ *
123
+ * @param {Uint8Array|ArrayBuffer} bytes Container bytes.
124
+ * @param {Uint8Array|ArrayBuffer|string|object} schema The sibling `.schema`.
125
+ * @param {string} [path] Logical path, for error messages.
126
+ * @returns {Promise<object|Array>} Decoded records.
127
+ */
128
+ async function ReadSchemaBoundContainer(bytes, schema, path = ".static input") {
129
+ const probe = await CjsStaticFormat.resolveType(bytes);
130
+
131
+ // The schema-bound family is the one with nothing to recognize, so it is
132
+ // reported as `unknown` with `requires: "schema"` rather than named outright.
133
+ if (probe.preferred !== CJS_STATIC_FAMILIES.UNKNOWN) {
134
+ throw FamilyError(path, probe.preferred, "schema-bound", null);
135
+ }
136
+ return CjsSchemaBoundFormat.read(bytes, {
137
+ schema
138
+ });
139
+ }
140
+
141
+ /** Parses one stored JSON document, naming the record that failed. */
142
+ function ParseRecord(value, key, path) {
143
+ try {
144
+ return JSON.parse(value);
145
+ } catch (cause) {
146
+ const error = new TypeError(`${path} record ${key} is not JSON.`, {
147
+ cause
148
+ });
149
+ error.code = "CJS_STATIC_FORMAT_RECORD_INVALID";
150
+ error.key = String(key);
151
+ error.path = path;
152
+ throw error;
153
+ }
154
+ }
155
+
156
+ export { ReadEmbeddedSchemaContainer, ReadSchemaBoundContainer, ReadStaticContainer };
157
+ //# sourceMappingURL=staticContainers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticContainers.js","sources":["../../../../src/formats/static/staticContainers.js"],"sourcesContent":["import { CjsPickleFormat } from \"../pickle/index.js\";\nimport { CjsSchemaBoundFormat } from \"../schemabound/index.js\";\nimport { CjsSqliteFormat } from \"../sqlite/index.js\";\nimport { CJS_STATIC_FAMILIES, CjsStaticFormat } from \"./CjsStaticFormat.js\";\n\n/**\n * Reads a client `.static` container of whichever family it holds.\n *\n * `.static` names a role rather than a format, and three unrelated containers\n * wear the extension, so identification and decoding are separate jobs done by\n * separate formats. `CjsStaticFormat` says which family the bytes hold and\n * decodes nothing; these functions take that answer to the format that owns the\n * family. Nothing routes on the extension, which proves nothing.\n *\n * Reading a `.static` needs the three formats that own its families, so this\n * module imports them. That is the format depending on formats, which is what a\n * container format that wraps other containers has to do; what a format must not\n * do is drag the rest of the library in behind it, and nothing here reaches\n * outside `formats/`.\n *\n * It is a sibling of `CjsStaticFormat` rather than part of it, which keeps the\n * 2026-08-15 decision intact: that class dispatched to two other formats and was\n * cut back to identifying only, so that identification is not also the routing\n * table. Routing lives here and the identification stays clean.\n *\n * Nothing here is specific to one publisher or one export. Measured 2026-08-16\n * across three publishers at builds 3466501, 3466054 and 3466057: 45 `.static`\n * files on each, split 14 SQLite, 25 embedded-schema and 6 schema-companion,\n * with not one file unique to a publisher and not one that changes family\n * between them.\n */\n\nconst CACHE_TABLE = \"cache\";\n\n/** Raises a family mismatch, naming what was found and what was wanted. */\nfunction FamilyError(path, found, wanted, reason)\n{\n const error = new TypeError(\n `${path} is a ${found} .static container, not a ${wanted} one.${reason ? ` ${reason}` : \"\"}`,\n );\n\n error.code = \"CJS_STATIC_FORMAT_FAMILY_UNSUPPORTED\";\n error.family = found;\n error.path = path;\n\n return error;\n}\n\n/**\n * Reads the SQLite family into records keyed by their container key.\n *\n * The record shape is the container's own: a `cache` table of `key`, `value`\n * and `time`, where every `value` is a JSON document.\n *\n * @param {Uint8Array|ArrayBuffer} bytes Container bytes.\n * @param {string} [path] Logical path, for error messages.\n * @returns {Promise<object>} Records keyed by their container key.\n */\nexport async function ReadStaticContainer(bytes, path = \".static input\")\n{\n const probe = await CjsStaticFormat.resolveType(bytes);\n\n if (probe.preferred !== CJS_STATIC_FAMILIES.SQLITE)\n {\n throw FamilyError(path, probe.preferred, CJS_STATIC_FAMILIES.SQLITE, probe.reason);\n }\n\n const tables = CjsSqliteFormat.readJSON(bytes, { tables: [ CACHE_TABLE ] });\n const rows = tables[CACHE_TABLE];\n\n if (!Array.isArray(rows))\n {\n const error = new TypeError(`${path} has no ${CACHE_TABLE} table.`);\n\n error.code = \"CJS_STATIC_FORMAT_SHAPE_INVALID\";\n error.path = path;\n throw error;\n }\n\n const records = {};\n\n for (const row of rows)\n {\n records[String(row.key)] = ParseRecord(row.value, row.key, path);\n }\n\n return records;\n}\n\n/**\n * Reads the family whose schema is embedded ahead of the payload.\n *\n * The framing is a `uint32` schema length, the schema as a protocol-0 pickle,\n * and then a payload in exactly the container `CjsSchemaBoundFormat` reads. It\n * is the same format as the `.schema` sibling family; only the schema's encoding\n * differs.\n *\n * **Slice the schema exactly.** Handing the whole file to a pickle reader fails,\n * but it fails on the payload's bytes long after the schema has parsed, so the\n * error names an opcode and points nowhere near the real boundary. That reading\n * is what left this family classified as a separate self-describing format for\n * as long as it was.\n *\n * @param {Uint8Array|ArrayBuffer} bytes Container bytes.\n * @param {string} [path] Logical path, for error messages.\n * @returns {object|Array} Decoded records.\n */\nexport function ReadEmbeddedSchemaContainer(bytes, path = \".static input\")\n{\n const source = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n\n if (source.byteLength < 4)\n {\n const error = new TypeError(`${path} is too short to carry a schema length.`);\n\n error.code = \"CJS_STATIC_FORMAT_SHAPE_INVALID\";\n error.path = path;\n throw error;\n }\n\n const view = new DataView(source.buffer, source.byteOffset, source.byteLength);\n const length = view.getUint32(0, true);\n\n if (length <= 0 || length + 4 > source.byteLength)\n {\n const error = new TypeError(`${path} does not begin with a usable schema length.`);\n\n error.code = \"CJS_STATIC_FORMAT_SHAPE_INVALID\";\n error.path = path;\n throw error;\n }\n\n return CjsSchemaBoundFormat.read(source.subarray(4 + length), {\n schema: CjsPickleFormat.read(source.subarray(4, 4 + length)),\n });\n}\n\n/**\n * Reads the family whose layout lives in a `.schema` sibling.\n *\n * The family check is worth keeping even though the caller already fetched a\n * `.schema` alongside the payload, because these bytes carry no signature at\n * all - given the wrong schema they decode into plausible nonsense rather than\n * failing, so the one cheap guard there is belongs in the path.\n *\n * @param {Uint8Array|ArrayBuffer} bytes Container bytes.\n * @param {Uint8Array|ArrayBuffer|string|object} schema The sibling `.schema`.\n * @param {string} [path] Logical path, for error messages.\n * @returns {Promise<object|Array>} Decoded records.\n */\nexport async function ReadSchemaBoundContainer(bytes, schema, path = \".static input\")\n{\n const probe = await CjsStaticFormat.resolveType(bytes);\n\n // The schema-bound family is the one with nothing to recognize, so it is\n // reported as `unknown` with `requires: \"schema\"` rather than named outright.\n if (probe.preferred !== CJS_STATIC_FAMILIES.UNKNOWN)\n {\n throw FamilyError(path, probe.preferred, \"schema-bound\", null);\n }\n\n return CjsSchemaBoundFormat.read(bytes, { schema });\n}\n\n/** Parses one stored JSON document, naming the record that failed. */\nfunction ParseRecord(value, key, path)\n{\n try\n {\n return JSON.parse(value);\n }\n catch (cause)\n {\n const error = new TypeError(`${path} record ${key} is not JSON.`, { cause });\n\n error.code = \"CJS_STATIC_FORMAT_RECORD_INVALID\";\n error.key = String(key);\n error.path = path;\n throw error;\n }\n}\n"],"names":["CACHE_TABLE","FamilyError","path","found","wanted","reason","error","TypeError","code","family","ReadStaticContainer","bytes","probe","CjsStaticFormat","resolveType","preferred","CJS_STATIC_FAMILIES","SQLITE","tables","CjsSqliteFormat","readJSON","rows","Array","isArray","records","row","String","key","ParseRecord","value","ReadEmbeddedSchemaContainer","source","Uint8Array","byteLength","view","DataView","buffer","byteOffset","length","getUint32","CjsSchemaBoundFormat","read","subarray","schema","CjsPickleFormat","ReadSchemaBoundContainer","UNKNOWN","JSON","parse","cause"],"mappings":"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"}
@@ -13,6 +13,42 @@ module, resolves a global, calls a reducer, follows a persistent ID, or
13
13
  constructs a Python object. Every executable, object-bearing, newer-protocol,
14
14
  or unknown opcode fails at its exact byte offset.
15
15
 
16
+ ## One global is rebuilt, and it is a closed set
17
+
18
+ `GLOBAL` is the opcode that makes a pickle dangerous: it names a module and an
19
+ attribute for the unpickler to import, and `REDUCE` then calls it. That is the
20
+ remote-execution vector, and the general form stays refused — `os.system` fails
21
+ at the `GLOBAL`, before its argument is read and long before `REDUCE` could do
22
+ anything with it.
23
+
24
+ **`collections.OrderedDict` is the single exception**, because it is not a
25
+ behaviour. It is a dictionary that remembers insertion order, which a JavaScript
26
+ object already is, so the reader builds that object directly. Nothing is
27
+ imported, resolved or invoked, and `REDUCE` applied to anything else is refused
28
+ in its own right so it cannot be used to step around the `GLOBAL` check.
29
+
30
+ An integer-like key is rejected rather than accepted, because those sort ahead
31
+ of every other key in a JavaScript object and order is the whole point of the
32
+ type.
33
+
34
+ Adding a second name to that set is not a small change. A name qualifies only if
35
+ reconstructing it is pure data with no behaviour of its own, and the entry has to
36
+ build that data directly rather than defer to anything callable.
37
+
38
+ Why it matters: every one of the 25 embedded-schema static-data containers in one
39
+ client build was scanned for the `GLOBAL` opcode's module and attribute lines,
40
+ and this is the **only** name any of them uses — once per file, 25 occurrences,
41
+ no second name. They use it because a schema's attribute order is its field order, which
42
+ an ordinary dictionary would lose. Refusing it left 25 containers unreadable,
43
+ including one of 88 MB holding roughly 477,000 records.
44
+
45
+ Two limits exist because of this opcode and are worth knowing before raising
46
+ either: `REDUCE` is the only path that builds many properties for a constant
47
+ number of opcodes, so rebuilt properties are budgeted **across the whole decode**
48
+ rather than per container, and a global may only ever be consumed by a `REDUCE` —
49
+ appending one to a list or leaving it as the result is refused, because it would
50
+ reach the caller as an empty object indistinguishable from an empty dictionary.
51
+
16
52
  The initial reader accepts the protocol-0 scalar, string, list, tuple,
17
53
  dictionary, memo, append, and set-item operations required by inert data
18
54
  graphs. Lists and tuples become JavaScript arrays. Integers outside the safe
@@ -36,6 +36,14 @@ text, or an already-parsed object. YAML is parsed with `CjsYamlFormat`, and
36
36
  anchors and aliases are rejoined — these schemas share repeated declarations that
37
37
  way, and left unresolved an anchor reads as one more field.
38
38
 
39
+ **The schema does not always ship as a separate file.** Some containers embed it:
40
+ a `uint32` schema length, then the schema as a protocol-0 pickle, then the
41
+ payload. Read the pickle with [`CjsPickleFormat`](pickle.md) and hand the result
42
+ in as `schema`, with the payload being everything past `4 + length`. It is the
43
+ same format either way — only the schema's own encoding differs, and the type
44
+ vocabulary is a little richer because those schemas name what a number means
45
+ rather than only how wide it is.
46
+
39
47
  - `read` / `readJSON` — plain JSON-compatible values; a wide integer becomes a
40
48
  decimal string.
41
49
  - `readPayload` — the same, with wide integers left as `BigInt`.
@@ -88,19 +96,22 @@ Two smaller rules:
88
96
  | `float` | number, single or double by declared size |
89
97
  | `bool` | boolean |
90
98
  | `enum` | the member's name, or its number when `readEnumValue` is set |
91
- | `vector3` | an object keyed by the schema's own component aliases |
92
- | `string`, `resPath` | length-prefixed UTF-8 |
99
+ | `vector2`, `vector3` | an object keyed by the schema's own component aliases |
100
+ | `string`, `resPath`, `unicode` | length-prefixed UTF-8 |
101
+ | `localizationID`, `typeID`, `factionID`, `groupID`, `categoryID`, `graphicID`, `iconID`, `fsdReference` | unsigned key into another table, four bytes unless the schema says otherwise. A name outside this closed set throws rather than being guessed at |
93
102
  | `list` | array, strided or offset-indexed as above |
94
103
  | `dict` | object, framed exactly as the file's own root |
95
104
  | `object` | record, as above |
96
105
 
97
106
  ## Evidence
98
107
 
99
- The reader was verified field for field against the published static data export
100
- for every container of this family in one build, and cross-checked between
101
- containers that describe the same relationships from different sides. The
102
- detailed measurements are recorded in the organization documentation rather than
103
- here.
108
+ The reader was verified field for field against a published static data export,
109
+ for every container of this family that HAS a corresponding export table — three
110
+ of six, plus the six that carry the celestial data. Two of the remaining three
111
+ have no table to check against at all; the third was cross-checked against a
112
+ different container describing the same relationships from the other side, which
113
+ is evidence but not the same evidence. The measurements are recorded in the
114
+ organization documentation rather than here.
104
115
 
105
116
  The tests in this package hold the structure instead, on containers laid out byte
106
117
  by byte: the shrinking offset table, both list framings, declared defaults,
@@ -14,8 +14,8 @@ the 45 `.static` files in one build:
14
14
  | Family | Count | Signature |
15
15
  |---|---:|---|
16
16
  | SQLite 3 | 14 | `SQLite format 3\0` |
17
- | Prefixed pickle | 25 | four-byte little-endian prefix, then `(d` or `(l` |
18
- | Schema-bound | 6 | no signature; has a `.schema` companion |
17
+ | Embedded schema | 25 | four-byte schema LENGTH, then `(d` or `(l` |
18
+ | Sibling schema | 6 | no signature; has a `.schema` companion |
19
19
 
20
20
  The six unidentified files are exactly the six with a `.schema` companion —
21
21
  `constellations`, `dialogs`, `factionsowningsolarsystems`, `jumps`, `regions`
@@ -28,9 +28,10 @@ family that cannot be read without its companion.
28
28
 
29
29
  - **SQLite** containers hold `cache(key, value, time)` and
30
30
  `indexes(key, value)`, with a JSON document per record.
31
- - **Prefixed pickle** containers are decoded through `CjsPickleFormat` after
32
- the four-byte prefix.
33
- - **Schema-bound** containers report `unknown` with `requires: "schema"`, and
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
34
35
  are decoded by `CjsSchemaBoundFormat` once the caller has that companion.
35
36
 
36
37
  Detection is signature-based. It never trusts a file name and never executes
@@ -51,7 +52,14 @@ if (probe.preferred === CJS_STATIC_FAMILIES.SQLITE)
51
52
 
52
53
  if (probe.preferred === CJS_STATIC_FAMILIES.PICKLE)
53
54
  {
54
- return CjsPickleFormat.read(CjsStaticFormat.payload(bytes));
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
+ });
55
63
  }
56
64
  ```
57
65
 
@@ -62,26 +70,26 @@ routing table for two others, and deciding what to decode belongs to whoever
62
70
  asked. Nothing outside this format's own tests ever called `read()`.
63
71
 
64
72
 
65
- ## The pickles name classes, and the schemas describe layouts
73
+ ## Both of the remaining families are the same container
66
74
 
67
- Two notes that decide how the remaining families get decoded.
75
+ The schema is encoded differently; the payload behind it is identical.
68
76
 
69
- **The pickle family carries class-construction opcodes.** Protocol 0's `c`
77
+ **The pickle behind that prefix is a SCHEMA, not a record.** Protocol 0's `c`
70
78
  (`GLOBAL`) names a module and an attribute for the unpickler to import, and
71
- `R`/`i`/`o`/`b` then call it. That is the pickle remote-execution vector, so
72
- `CjsPickleFormat` rejects those opcodes by design. Client `.static` pickles use
73
- them legitimately, to name the classes their records are constructed from, so
74
- decoding this family fully means mapping each named global to an inert
75
- descriptor and never invoking it. **Not implemented**; the rejection is
76
- surfaced rather than worked around, and widening it is a deliberate decision
77
- rather than a bug fix.
78
-
79
- **The schema-bound family is self-describing.** Its `.schema` companion is
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
80
86
  YAML and states the whole binary layout — sizes, types, optional flags, list item
81
87
  sizes, vector precision and a key-to-offset footer — so **nothing needs
82
- deriving**, unlike an FSD container. `CjsSchemaBoundFormat` reads it:
83
- [schema-bound containers](schemabound.md). All six datasets decode, the celestial
84
- tables among them.
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.
85
93
 
86
94
  ## Use
87
95
 
@@ -96,7 +104,7 @@ const probe = await CjsStaticFormat.resolveType(bytes);
96
104
 
97
105
  if (probe.preferred === CJS_STATIC_FAMILIES.PICKLE)
98
106
  {
99
- const value = CjsPickleFormat.read(CjsStaticFormat.payload(bytes));
107
+ // See the routing example above: the prefix is a schema length.
100
108
  }
101
109
  ```
102
110
 
@@ -112,6 +120,45 @@ is `null` with `mismatch` always false.
112
120
  building a probe. `payload()` returns the bytes past any wrapper, which is what a
113
121
  caller hands to the format that owns the family.
114
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
+
115
162
  ## Related documentation
116
163
 
117
164
  - [Formats](README.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carbonenginejs/runtime-resource",
3
- "version": "0.18.1",
3
+ "version": "0.19.0",
4
4
  "description": "CarbonEngineJS resource lifecycle, cache, source, and object loading contracts.",
5
5
  "type": "module",
6
6
  "exports": {