@carbonenginejs/runtime-utils 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,139 +1,140 @@
1
- {
2
- "name": "@carbonenginejs/runtime-utils",
3
- "version": "0.1.4",
4
- "description": "Browser-safe shared utilities, math, constants, Carbon types, schemas, documents, and runtime model primitives.",
5
- "type": "module",
6
- "main": "src/index.js",
7
- "exports": {
8
- ".": "./src/index.js",
9
- "./arrays": "./src/arrays.js",
10
- "./bytes": "./src/bytes.js",
11
- "./compression": "./src/compression.js",
12
- "./const": "./src/constants/index.js",
13
- "./const/trinity": "./src/constants/trinity.js",
14
- "./const/media": "./src/media/index.js",
15
- "./const/graphics": "./src/graphics/index.js",
16
- "./const/render-context": "./src/renderContext/index.js",
17
- "./const/audio": "./src/audio/index.js",
18
- "./const/shader": "./src/shader/index.js",
19
- "./const/d3d": "./src/d3d/index.js",
20
- "./const/webgpu": "./src/webgpu/index.js",
21
- "./media": "./src/media/index.js",
22
- "./graphics": "./src/graphics/index.js",
23
- "./render-context": "./src/renderContext/index.js",
24
- "./audio": "./src/audio/index.js",
25
- "./shader": "./src/shader/index.js",
26
- "./d3d": "./src/d3d/index.js",
27
- "./webgpu": "./src/webgpu/index.js",
28
- "./document": "./src/document/index.js",
29
- "./errors": "./src/errors/index.js",
30
- "./hydration": "./src/document/hydrationAdapter.js",
31
- "./lifecycle": "./src/lifecycle/index.js",
32
- "./schema": "./src/schema/index.js",
33
- "./types": "./src/types/index.js",
34
- "./model": "./src/model/index.js",
35
- "./is": "./src/is.js",
36
- "./json": "./src/json.js",
37
- "./lookup": "./src/lookup.js",
38
- "./object": "./src/object.js",
39
- "./math": "./src/math/index.js",
40
- "./math/scalar": "./src/math/scalar.js",
41
- "./math/num": "./src/num.js",
42
- "./math/vec2": "./src/vec2.js",
43
- "./math/vec3": "./src/vec3.js",
44
- "./math/vec4": "./src/vec4.js",
45
- "./math/quat": "./src/quat.js",
46
- "./math/mat3": "./src/mat3.js",
47
- "./math/mat4": "./src/mat4.js",
48
- "./math/box3": "./src/box3.js",
49
- "./math/tri3": "./src/tri3.js",
50
- "./math/lne3": "./src/lne3.js",
51
- "./math/pln": "./src/pln.js",
52
- "./math/ray3": "./src/ray3.js",
53
- "./math/sph3": "./src/sph3.js",
54
- "./math/pool": "./src/pool.js",
55
- "./math/utils": "./src/utils.js",
56
- "./math/noise": "./src/noise.js",
57
- "./math/curve": "./src/curve.js",
58
- "./math/vertex": "./src/vertex.js",
59
- "./math/mesh": "./src/mesh.js",
60
- "./math/tangent": "./src/tangent.js",
61
- "./math/geometry": "./src/geometry/index.js",
62
- "./num": "./src/num.js",
63
- "./vec2": "./src/vec2.js",
64
- "./vec3": "./src/vec3.js",
65
- "./vec4": "./src/vec4.js",
66
- "./quat": "./src/quat.js",
67
- "./mat3": "./src/mat3.js",
68
- "./mat4": "./src/mat4.js",
69
- "./box3": "./src/box3.js",
70
- "./tri3": "./src/tri3.js",
71
- "./lne3": "./src/lne3.js",
72
- "./pln": "./src/pln.js",
73
- "./ray3": "./src/ray3.js",
74
- "./sph3": "./src/sph3.js",
75
- "./pool": "./src/pool.js",
76
- "./utils": "./src/utils.js",
77
- "./noise": "./src/noise.js",
78
- "./curve": "./src/curve.js",
79
- "./vertex": "./src/vertex.js",
80
- "./mesh": "./src/mesh.js",
81
- "./tangent": "./src/tangent.js",
82
- "./geometry": "./src/geometry/index.js",
83
- "./path": "./src/path.js",
84
- "./text": "./src/text.js",
85
- "./validation": "./src/validation.js"
86
- },
87
- "sideEffects": false,
88
- "engines": {
89
- "node": ">=18"
90
- },
91
- "scripts": {
92
- "test": "node --test",
93
- "lint": "eslint --ext .js,.mjs src test",
94
- "lint:fix": "eslint --fix --ext .js,.mjs src test",
95
- "check": "npm run lint && npm test"
96
- },
97
- "keywords": [
98
- "carbonenginejs",
99
- "runtime",
100
- "utilities",
101
- "gl-matrix",
102
- "constants",
103
- "schema",
104
- "types",
105
- "errors",
106
- "geometry",
107
- "tangent",
108
- "browser"
109
- ],
110
- "files": [
111
- "src",
112
- "docs",
113
- "README.md",
114
- "LICENSE",
115
- "NOTICE",
116
- "THIRD-PARTY-NOTICES.md"
117
- ],
118
- "license": "MIT",
119
- "dependencies": {
120
- "gl-matrix": "^3.4.4"
121
- },
122
- "devDependencies": {
123
- "eslint": "^8.57.1"
124
- },
125
- "repository": {
126
- "type": "git",
127
- "url": "git+https://github.com/carbonenginejs/runtime-utils.git"
128
- },
129
- "homepage": "https://github.com/carbonenginejs/runtime-utils#readme",
130
- "bugs": {
131
- "url": "https://github.com/carbonenginejs/runtime-utils/issues"
132
- },
133
- "publishConfig": {
134
- "access": "public"
135
- },
136
- "overrides": {
137
- "brace-expansion": "^5.0.8"
138
- }
139
- }
1
+ {
2
+ "name": "@carbonenginejs/runtime-utils",
3
+ "version": "0.1.6",
4
+ "description": "Browser-safe shared utilities, math, constants, Carbon types, schemas, documents, and runtime model primitives.",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./arrays": "./src/arrays.js",
10
+ "./bytes": "./src/bytes.js",
11
+ "./compression": "./src/compression.js",
12
+ "./const": "./src/constants/index.js",
13
+ "./const/trinity": "./src/constants/trinity.js",
14
+ "./const/media": "./src/media/index.js",
15
+ "./const/graphics": "./src/graphics/index.js",
16
+ "./const/render-context": "./src/renderContext/index.js",
17
+ "./const/audio": "./src/audio/index.js",
18
+ "./const/shader": "./src/shader/index.js",
19
+ "./const/d3d": "./src/d3d/index.js",
20
+ "./const/webgpu": "./src/webgpu/index.js",
21
+ "./media": "./src/media/index.js",
22
+ "./graphics": "./src/graphics/index.js",
23
+ "./render-context": "./src/renderContext/index.js",
24
+ "./audio": "./src/audio/index.js",
25
+ "./shader": "./src/shader/index.js",
26
+ "./d3d": "./src/d3d/index.js",
27
+ "./webgpu": "./src/webgpu/index.js",
28
+ "./document": "./src/document/index.js",
29
+ "./errors": "./src/errors/index.js",
30
+ "./hydration": "./src/document/hydrationAdapter.js",
31
+ "./lifecycle": "./src/lifecycle/index.js",
32
+ "./schema": "./src/schema/index.js",
33
+ "./types": "./src/types/index.js",
34
+ "./model": "./src/model/index.js",
35
+ "./is": "./src/is.js",
36
+ "./json": "./src/json.js",
37
+ "./lookup": "./src/lookup.js",
38
+ "./object": "./src/object.js",
39
+ "./math": "./src/math/index.js",
40
+ "./math/scalar": "./src/math/scalar.js",
41
+ "./math/num": "./src/num.js",
42
+ "./math/vec2": "./src/vec2.js",
43
+ "./math/vec3": "./src/vec3.js",
44
+ "./math/vec4": "./src/vec4.js",
45
+ "./math/quat": "./src/quat.js",
46
+ "./math/mat3": "./src/mat3.js",
47
+ "./math/mat4": "./src/mat4.js",
48
+ "./math/box3": "./src/box3.js",
49
+ "./math/tri3": "./src/tri3.js",
50
+ "./math/lne3": "./src/lne3.js",
51
+ "./math/pln": "./src/pln.js",
52
+ "./math/ray3": "./src/ray3.js",
53
+ "./math/sph3": "./src/sph3.js",
54
+ "./math/pool": "./src/pool.js",
55
+ "./math/utils": "./src/utils.js",
56
+ "./math/noise": "./src/noise.js",
57
+ "./math/curve": "./src/curve.js",
58
+ "./math/vertex": "./src/vertex.js",
59
+ "./math/mesh": "./src/mesh.js",
60
+ "./math/tangent": "./src/tangent.js",
61
+ "./math/geometry": "./src/geometry/index.js",
62
+ "./num": "./src/num.js",
63
+ "./vec2": "./src/vec2.js",
64
+ "./vec3": "./src/vec3.js",
65
+ "./vec4": "./src/vec4.js",
66
+ "./quat": "./src/quat.js",
67
+ "./mat3": "./src/mat3.js",
68
+ "./mat4": "./src/mat4.js",
69
+ "./box3": "./src/box3.js",
70
+ "./tri3": "./src/tri3.js",
71
+ "./lne3": "./src/lne3.js",
72
+ "./pln": "./src/pln.js",
73
+ "./ray3": "./src/ray3.js",
74
+ "./sph3": "./src/sph3.js",
75
+ "./pool": "./src/pool.js",
76
+ "./utils": "./src/utils.js",
77
+ "./noise": "./src/noise.js",
78
+ "./curve": "./src/curve.js",
79
+ "./vertex": "./src/vertex.js",
80
+ "./mesh": "./src/mesh.js",
81
+ "./tangent": "./src/tangent.js",
82
+ "./geometry": "./src/geometry/index.js",
83
+ "./path": "./src/path.js",
84
+ "./resfile": "./src/resFile.js",
85
+ "./text": "./src/text.js",
86
+ "./validation": "./src/validation.js"
87
+ },
88
+ "sideEffects": false,
89
+ "engines": {
90
+ "node": ">=18"
91
+ },
92
+ "scripts": {
93
+ "test": "node --test",
94
+ "lint": "eslint --ext .js,.mjs src test",
95
+ "lint:fix": "eslint --fix --ext .js,.mjs src test",
96
+ "check": "npm run lint && npm test"
97
+ },
98
+ "keywords": [
99
+ "carbonenginejs",
100
+ "runtime",
101
+ "utilities",
102
+ "gl-matrix",
103
+ "constants",
104
+ "schema",
105
+ "types",
106
+ "errors",
107
+ "geometry",
108
+ "tangent",
109
+ "browser"
110
+ ],
111
+ "files": [
112
+ "src",
113
+ "docs",
114
+ "README.md",
115
+ "LICENSE",
116
+ "NOTICE",
117
+ "THIRD-PARTY-NOTICES.md"
118
+ ],
119
+ "license": "MIT",
120
+ "dependencies": {
121
+ "gl-matrix": "^3.4.4"
122
+ },
123
+ "devDependencies": {
124
+ "eslint": "^8.57.1"
125
+ },
126
+ "repository": {
127
+ "type": "git",
128
+ "url": "git+https://github.com/carbonenginejs/runtime-utils.git"
129
+ },
130
+ "homepage": "https://github.com/carbonenginejs/runtime-utils#readme",
131
+ "bugs": {
132
+ "url": "https://github.com/carbonenginejs/runtime-utils/issues"
133
+ },
134
+ "publishConfig": {
135
+ "access": "public"
136
+ },
137
+ "overrides": {
138
+ "brace-expansion": "^5.0.8"
139
+ }
140
+ }
package/src/index.js CHANGED
@@ -60,3 +60,4 @@ export * as tangent from "./tangent.js";
60
60
  * TypedArray
61
61
  * @typedef {Float64Array|Float32Array|Uint32Array|Uint16Array|Uint8Array|Uint8ClampedArray|Int32Array|Int16Array|Int8Array} TypedArray
62
62
  */
63
+ export * from "./resFile.js";
package/src/resFile.js ADDED
@@ -0,0 +1,149 @@
1
+ /**
2
+ * EVE resource file addressing.
3
+ *
4
+ * A resource's stored name IS its identity: a hash of its logical path, then
5
+ * the md5 of its contents. Two builds either carry the same address for a path
6
+ * or they do not, which makes address comparison - not file hashing - the way
7
+ * to tell whether anything changed.
8
+ *
9
+ * Deliberately no md5 here. Computing it is I/O, and the shape of that I/O
10
+ * differs per environment: a file stream in Node, an ArrayBuffer in a browser.
11
+ * The digest is passed in, so this module stays environment-neutral and every
12
+ * consumer shares one derivation instead of keeping its own.
13
+ */
14
+
15
+ const ADDRESS = /^([a-f0-9]{2})\/([a-f0-9]{16})_([a-f0-9]{32})$/u;
16
+
17
+ /** Lazily built byte-to-hex table. */
18
+ let hex = null;
19
+
20
+ /**
21
+ * FNV-1 (64-bit) over a resource's logical path.
22
+ *
23
+ * FNV-**1**, not 1a: the multiply happens before the XOR. Offset basis
24
+ * 0xcbf29ce484222325, prime 0x100000001b3, emitted as 16 lowercase hex digits.
25
+ *
26
+ * Verified against 1718 content-addressed entries from a real resfileindex.
27
+ *
28
+ * ONLY DEFINED FOR ASCII PATHS. Two implementations exist in the wild - one
29
+ * hashing UTF-8 bytes, one hashing UTF-16 code units - and they agree on every
30
+ * ASCII string and disagree beyond it. No real resource path has ever been
31
+ * non-ASCII, so no evidence exists for which is correct, and guessing would
32
+ * turn a dormant difference into a live wrong answer. A non-ASCII path
33
+ * therefore throws rather than returning something plausible.
34
+ *
35
+ * @param {String} logicalPath - the prefixed path, e.g. "res:/graphics/x.dds"
36
+ * @returns {String} 16 lowercase hex digits
37
+ */
38
+ export function fnv164(logicalPath)
39
+ {
40
+ const value = String(logicalPath);
41
+
42
+ if (!hex)
43
+ {
44
+ hex = [];
45
+ for (let i = 0; i < 256; i++) hex[i] = ((i >> 4) & 15).toString(16) + (i & 15).toString(16);
46
+ }
47
+
48
+ // 16-bit limbs, so the 64-bit multiply stays inside the safe integer range.
49
+ let v0 = 0x2325;
50
+ let v1 = 0x8422;
51
+ let v2 = 0x9ce4;
52
+ let v3 = 0xcbf2;
53
+
54
+ for (let i = 0; i < value.length; i++)
55
+ {
56
+ const code = value.charCodeAt(i);
57
+
58
+ if (code > 0x7f)
59
+ {
60
+ throw new RangeError(
61
+ `Resource path hashing is only defined for ASCII: ${JSON.stringify(value)}`,
62
+ );
63
+ }
64
+
65
+ // Multiply by 0x100000001b3: the low limb contributes 0x1b3 (435) to
66
+ // every limb, and the 0x100000000 term shifts two limbs left.
67
+ let t0 = v0 * 435;
68
+ let t1 = v1 * 435;
69
+ let t2 = v2 * 435 + (v0 << 8);
70
+ const t3 = v3 * 435 + (v1 << 8);
71
+
72
+ t1 += t0 >>> 16;
73
+ v0 = t0 & 0xffff;
74
+ t2 += t1 >>> 16;
75
+ v1 = t1 & 0xffff;
76
+ v3 = (t3 + (t2 >>> 16)) & 0xffff;
77
+ v2 = t2 & 0xffff;
78
+
79
+ v0 ^= code;
80
+ }
81
+
82
+ return hex[v3 >> 8] + hex[v3 & 255]
83
+ + hex[v2 >> 8] + hex[v2 & 255]
84
+ + hex[v1 >> 8] + hex[v1 & 255]
85
+ + hex[v0 >> 8] + hex[v0 & 255];
86
+ }
87
+
88
+ /**
89
+ * Builds a resource's stored address from its path and its content digest.
90
+ *
91
+ * The shard directory is the first two characters of the path hash itself, not
92
+ * a separate value - a detail that is easy to reimplement wrongly.
93
+ *
94
+ * @param {String} logicalPath
95
+ * @param {String} md5 - 32 hex digits, the md5 of the file's contents
96
+ * @returns {String} "<shard>/<pathHash>_<md5>"
97
+ */
98
+ export function resFileAddress(logicalPath, md5)
99
+ {
100
+ const digest = String(md5).toLowerCase();
101
+
102
+ if (!/^[a-f0-9]{32}$/u.test(digest))
103
+ {
104
+ throw new TypeError(`Resource content digest must be 32 hex digits: ${md5}`);
105
+ }
106
+
107
+ const pathHash = fnv164(logicalPath);
108
+
109
+ return `${pathHash.slice(0, 2)}/${pathHash}_${digest}`;
110
+ }
111
+
112
+ /**
113
+ * Splits a stored address into its parts, or null when it is not one.
114
+ *
115
+ * Null rather than throwing: an index legitimately carries plain paths for
116
+ * overlay entries alongside content-addressed ones, so "not an address" is an
117
+ * ordinary answer rather than an error.
118
+ *
119
+ * @param {String} address
120
+ * @returns {{shard: String, pathHash: String, checksum: String}|null}
121
+ */
122
+ export function parseResFileAddress(address)
123
+ {
124
+ const match = String(address ?? "").toLowerCase().match(ADDRESS);
125
+
126
+ return match ? { shard: match[1], pathHash: match[2], checksum: match[3] } : null;
127
+ }
128
+
129
+ /**
130
+ * Checks a stored address against the path it claims to be for.
131
+ *
132
+ * Only the path half can be checked without reading the file; the content half
133
+ * is verifiable only against the bytes. Returns false rather than throwing so a
134
+ * caller can survey an index without exception handling - except for a
135
+ * non-ASCII path, where the answer is genuinely unknown.
136
+ *
137
+ * @param {String} address
138
+ * @param {String} logicalPath
139
+ * @returns {Boolean}
140
+ */
141
+ export function isResFileAddressFor(address, logicalPath)
142
+ {
143
+ const parts = parseResFileAddress(address);
144
+
145
+ if (!parts) return false;
146
+
147
+ return parts.pathHash === fnv164(logicalPath)
148
+ && parts.shard === parts.pathHash.slice(0, 2);
149
+ }
@@ -232,7 +232,19 @@ export class CjsSchema
232
232
  unknown: fieldDecorator("type", { kind: "unknown" }),
233
233
  vec2: fieldDecorator("type", { kind: "vec2" }),
234
234
  vec3: fieldDecorator("type", { kind: "vec3" }),
235
- vec4: fieldDecorator("type", { kind: "vec4" })
235
+ vec4: fieldDecorator("type", { kind: "vec4" }),
236
+
237
+ // An enum names the vocabulary a field's values are drawn from, which
238
+ // is part of its type - it belongs here beside int32 and string rather
239
+ // than under a separate namespace. define and hideInherited show this
240
+ // namespace already carries class decorators as well as field ones.
241
+ enum: values => fieldDecorator("enum", normalizeEnumDefinition(values)),
242
+
243
+ /**
244
+ * Hides named inherited fields from a subclass's schema, for a subclass
245
+ * that genuinely does not carry part of its parent's shape.
246
+ */
247
+ hideInherited: fieldNames => hiddenInheritedFieldsDecorator(normalizeHiddenInheritedFields(fieldNames))
236
248
  });
237
249
 
238
250
  static io = Object.freeze({