@carbonenginejs/runtime-utils 0.1.2 → 0.1.4

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 (104) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +23 -23
  3. package/README.md +69 -69
  4. package/THIRD-PARTY-NOTICES.md +64 -64
  5. package/docs/README.md +74 -73
  6. package/docs/architecture.md +101 -101
  7. package/docs/concepts/foundation-consolidation.md +85 -85
  8. package/docs/concepts/model-lifecycle.md +154 -0
  9. package/docs/const-kb.md +85 -87
  10. package/docs/core-types/DECORATOR-TODOS.md +25 -25
  11. package/docs/core-types/README.md +241 -168
  12. package/docs/reference/api.md +105 -102
  13. package/docs/reference/classes/README.md +135 -135
  14. package/package.json +139 -139
  15. package/src/arrays.js +5 -5
  16. package/src/audio/audioFormats.js +34 -34
  17. package/src/audio/index.js +1 -1
  18. package/src/box3.js +331 -331
  19. package/src/bytes.js +56 -56
  20. package/src/compression.js +56 -56
  21. package/src/constants/index.js +7 -7
  22. package/src/constants/trinity.js +13 -13
  23. package/src/constants.js +15 -15
  24. package/src/curve.js +79 -79
  25. package/src/d3d/dxgiFormats.js +46 -46
  26. package/src/d3d/index.js +2 -2
  27. package/src/d3d/primitiveTopology.js +11 -11
  28. package/src/document/CjsCarbonDocument.js +212 -212
  29. package/src/document/CjsClassRegistry.js +373 -373
  30. package/src/document/CjsDocumentDehydrator.js +142 -142
  31. package/src/document/CjsDocumentHydrator.js +156 -156
  32. package/src/document/CjsStructRegistry.js +348 -348
  33. package/src/document/hydrationAdapter.js +129 -129
  34. package/src/document/index.js +6 -6
  35. package/src/errors/CjsError.js +286 -286
  36. package/src/errors/index.js +5 -5
  37. package/src/geometry/box.js +22 -22
  38. package/src/geometry/cylinder.js +22 -22
  39. package/src/geometry/helpers/earcut.js +1 -1
  40. package/src/geometry/helpers/misc.js +3 -3
  41. package/src/geometry/index.js +8 -8
  42. package/src/geometry/json.js +67 -67
  43. package/src/geometry/lathe.js +44 -44
  44. package/src/geometry/plane.js +14 -14
  45. package/src/geometry/shape.js +4 -4
  46. package/src/geometry/sphere.js +24 -24
  47. package/src/geometry/torus.js +14 -14
  48. package/src/graphics/colorSpaces.js +22 -22
  49. package/src/graphics/index.js +4 -4
  50. package/src/graphics/pixelFormats.js +158 -158
  51. package/src/graphics/textureDimensions.js +22 -22
  52. package/src/graphics/trinityEnums.js +87 -87
  53. package/src/index.js +62 -62
  54. package/src/is.js +117 -117
  55. package/src/json.js +23 -23
  56. package/src/lifecycle/CjsLifecycleState.js +77 -77
  57. package/src/lifecycle/index.js +1 -1
  58. package/src/lne3.js +70 -70
  59. package/src/lookup.js +48 -48
  60. package/src/mat3.js +51 -51
  61. package/src/mat4.js +699 -699
  62. package/src/math/index.js +25 -25
  63. package/src/math/scalar.js +63 -63
  64. package/src/media/index.js +1 -1
  65. package/src/media/mediaTypes.js +50 -50
  66. package/src/mesh.js +424 -424
  67. package/src/model/CjsEventEmitter.js +333 -333
  68. package/src/model/CjsModel.js +1744 -1463
  69. package/src/model/CjsModelState.js +72 -72
  70. package/src/model/index.js +4 -4
  71. package/src/model/sourceRecordUtils.js +54 -54
  72. package/src/noise.js +310 -310
  73. package/src/num.js +827 -827
  74. package/src/object.js +39 -39
  75. package/src/path.js +53 -53
  76. package/src/pln.js +125 -125
  77. package/src/pool.js +9 -9
  78. package/src/quat.js +144 -144
  79. package/src/ray3.js +188 -188
  80. package/src/renderContext/formats.js +145 -145
  81. package/src/renderContext/index.js +5 -5
  82. package/src/renderContext/presentation.js +125 -125
  83. package/src/renderContext/resources.js +27 -27
  84. package/src/renderContext/upscaling.js +22 -22
  85. package/src/renderContext/window.js +20 -20
  86. package/src/runtime/CjsRuntimeState.js +50 -50
  87. package/src/schema/CjsSchema.js +141 -23
  88. package/src/schema/index.js +4 -4
  89. package/src/shader/index.js +1 -1
  90. package/src/shader/shaderStages.js +37 -37
  91. package/src/sph3.js +181 -181
  92. package/src/tangent.js +288 -288
  93. package/src/text.js +40 -40
  94. package/src/tri3.js +98 -98
  95. package/src/types/carbonTypes.js +635 -635
  96. package/src/types/index.js +2 -2
  97. package/src/utils.js +58 -58
  98. package/src/validation.js +46 -46
  99. package/src/vec2.js +229 -229
  100. package/src/vec3.js +1188 -1188
  101. package/src/vec4.js +347 -347
  102. package/src/vertex.js +108 -108
  103. package/src/webgpu/index.js +1 -1
  104. package/src/webgpu/textureFormats.js +121 -121
@@ -1,20 +1,20 @@
1
- // Ported from CarbonEngine (MIT, (c) 2026 CCP Games) - https://github.com/carbonengine/trinity
2
- // trinity/UI/Tr2MainWindow.h (Tr2WindowMode, Tr2WindowShowState, Tr2ImeState_MacOS namespaces)
3
-
4
- export const Tr2WindowMode = Object.freeze({
5
- FULL_SCREEN: 0,
6
- WINDOWED: 1,
7
- FIXED_WINDOW: 2
8
- });
9
-
10
- export const Tr2WindowShowState = Object.freeze({
11
- NORMAL: 0,
12
- MAXIMIZED: 1,
13
- MINIMIZED: 2
14
- });
15
-
16
- export const Tr2ImeState_MacOS = Object.freeze({
17
- DISABLED: 0,
18
- READY: 1,
19
- BLOCKING: 2
20
- });
1
+ // Ported from CarbonEngine (MIT, (c) 2026 CCP Games) - https://github.com/carbonengine/trinity
2
+ // trinity/UI/Tr2MainWindow.h (Tr2WindowMode, Tr2WindowShowState, Tr2ImeState_MacOS namespaces)
3
+
4
+ export const Tr2WindowMode = Object.freeze({
5
+ FULL_SCREEN: 0,
6
+ WINDOWED: 1,
7
+ FIXED_WINDOW: 2
8
+ });
9
+
10
+ export const Tr2WindowShowState = Object.freeze({
11
+ NORMAL: 0,
12
+ MAXIMIZED: 1,
13
+ MINIMIZED: 2
14
+ });
15
+
16
+ export const Tr2ImeState_MacOS = Object.freeze({
17
+ DISABLED: 0,
18
+ READY: 1,
19
+ BLOCKING: 2
20
+ });
@@ -1,50 +1,50 @@
1
- /**
2
- * Gets an object's own CarbonEngineJS runtime state without creating it.
3
- *
4
- * Runtime state is intentionally stored on the object for inspection while
5
- * remaining outside enumeration and serialization.
6
- *
7
- * @param {object|Function} target
8
- * @returns {object|null}
9
- */
10
- export function getRuntimeState(target)
11
- {
12
- assertRuntimeStateTarget(target);
13
- if (!Object.prototype.hasOwnProperty.call(target, "__state")) return null;
14
-
15
- const state = target.__state;
16
- if (!state || typeof state !== "object" || Array.isArray(state))
17
- {
18
- throw new TypeError("Existing __state must be an object.");
19
- }
20
- return state;
21
- }
22
-
23
- /**
24
- * Gets or creates an object's own non-enumerable runtime state.
25
- *
26
- * @param {object|Function} target
27
- * @returns {object}
28
- */
29
- export function ensureRuntimeState(target)
30
- {
31
- const existing = getRuntimeState(target);
32
- if (existing) return existing;
33
-
34
- const state = {};
35
- Object.defineProperty(target, "__state", {
36
- value: state,
37
- enumerable: false,
38
- configurable: false,
39
- writable: false
40
- });
41
- return state;
42
- }
43
-
44
- function assertRuntimeStateTarget(target)
45
- {
46
- if ((typeof target !== "object" || target === null) && typeof target !== "function")
47
- {
48
- throw new TypeError("Runtime state requires an object or function.");
49
- }
50
- }
1
+ /**
2
+ * Gets an object's own CarbonEngineJS runtime state without creating it.
3
+ *
4
+ * Runtime state is intentionally stored on the object for inspection while
5
+ * remaining outside enumeration and serialization.
6
+ *
7
+ * @param {object|Function} target
8
+ * @returns {object|null}
9
+ */
10
+ export function getRuntimeState(target)
11
+ {
12
+ assertRuntimeStateTarget(target);
13
+ if (!Object.prototype.hasOwnProperty.call(target, "__state")) return null;
14
+
15
+ const state = target.__state;
16
+ if (!state || typeof state !== "object" || Array.isArray(state))
17
+ {
18
+ throw new TypeError("Existing __state must be an object.");
19
+ }
20
+ return state;
21
+ }
22
+
23
+ /**
24
+ * Gets or creates an object's own non-enumerable runtime state.
25
+ *
26
+ * @param {object|Function} target
27
+ * @returns {object}
28
+ */
29
+ export function ensureRuntimeState(target)
30
+ {
31
+ const existing = getRuntimeState(target);
32
+ if (existing) return existing;
33
+
34
+ const state = {};
35
+ Object.defineProperty(target, "__state", {
36
+ value: state,
37
+ enumerable: false,
38
+ configurable: false,
39
+ writable: false
40
+ });
41
+ return state;
42
+ }
43
+
44
+ function assertRuntimeStateTarget(target)
45
+ {
46
+ if ((typeof target !== "object" || target === null) && typeof target !== "function")
47
+ {
48
+ throw new TypeError("Runtime state requires an object or function.");
49
+ }
50
+ }
@@ -11,6 +11,11 @@ const ENUM_SCHEMA_BY_NAME = new Map();
11
11
  const ENUM_SCHEMA_BY_OBJECT = new WeakMap();
12
12
  const STAGE3_FIELD_METADATA = Symbol("carbonenginejs.schema.stage3Fields");
13
13
 
14
+ // Declared here rather than beside describeDecorator: the CjsSchema class body
15
+ // builds every decorator namespace in its static initializer, which runs before
16
+ // any const declared after the class is initialized.
17
+ const DECORATOR_METADATA = Symbol("carbonenginejs.schema.decoratorMetadata");
18
+
14
19
  export const CJS_ENUM_NAME = Symbol.for("carbonenginejs.enum.name");
15
20
 
16
21
  /**
@@ -308,7 +313,8 @@ export class CjsSchema
308
313
  contextual: true,
309
314
  contextTiers: Object.freeze(normalized)
310
315
  });
311
- return function contextualMethodDecorator(targetOrValue, contextOrMethodName)
316
+ const described = getDecoratorMetadata(base);
317
+ return describeDecorator(function contextualMethodDecorator(targetOrValue, contextOrMethodName)
312
318
  {
313
319
  // Contextual methods are validated context-first at decoration
314
320
  // time: the first declared parameter must be the frame context.
@@ -321,7 +327,7 @@ export class CjsSchema
321
327
  assertContextFirstMethod(targetOrValue[contextOrMethodName], contextOrMethodName);
322
328
  }
323
329
  return base(targetOrValue, contextOrMethodName);
324
- };
330
+ }, described.namespace, described.value);
325
331
  }
326
332
  });
327
333
 
@@ -339,9 +345,33 @@ function createComponentsNamespace()
339
345
  return Object.freeze(components);
340
346
  }
341
347
 
348
+ // Every decorator carries the namespace and value it would install. That is
349
+ // what lets one vocabulary serve both forms: `type.uint32` applied as a
350
+ // decorator, and `type.uint32` written as data in a CjsSchema.define() member.
351
+ // Without it the object form would need its own spelling of every namespace
352
+ // value, and two spellings of `impl.adapted` will eventually disagree.
353
+ function describeDecorator(decorator, namespace, value)
354
+ {
355
+ Object.defineProperty(decorator, DECORATOR_METADATA, {
356
+ value: { namespace, value }
357
+ });
358
+ return decorator;
359
+ }
360
+
361
+ /**
362
+ * The namespace/value a decorator installs, or null when it is not one.
363
+ *
364
+ * @param {*} candidate Possible decorator.
365
+ * @returns {{namespace:string, value:*}|null} Installed metadata.
366
+ */
367
+ function getDecoratorMetadata(candidate)
368
+ {
369
+ return typeof candidate === "function" ? candidate[DECORATOR_METADATA] || null : null;
370
+ }
371
+
342
372
  function fieldDecorator(namespace, value)
343
373
  {
344
- return function schemaFieldDecorator(targetOrValue, contextOrFieldName)
374
+ return describeDecorator(function schemaFieldDecorator(targetOrValue, contextOrFieldName)
345
375
  {
346
376
  if (contextOrFieldName && typeof contextOrFieldName === "object")
347
377
  {
@@ -349,12 +379,12 @@ function fieldDecorator(namespace, value)
349
379
  if (context.kind !== "field") throw new TypeError("CjsSchema decorators only support class fields.");
350
380
  recordStage3FieldMetadata(context, namespace, value);
351
381
 
352
- // Register field metadata on instance construction. addInitializer covers
353
- // spec-compliant runtimes; the returned field initializer covers runtimes (e.g.
354
- // Deno/SWC) that do NOT fire field-decorator addInitializer. Both register the same
355
- // metadata (idempotent via mergeNamespace), so whichever the runtime honours, the
356
- // schema is populated. Registration is lazy (first construction); the class decorator
357
- // still registers the class eagerly at definition time.
382
+ // The class decorator replays the Stage-3 metadata recorded above, so
383
+ // schema inspection works before the first instance is constructed.
384
+ // Keep both runtime initializers as fallbacks: addInitializer covers
385
+ // spec-compliant runtimes, while the returned field initializer covers
386
+ // runtimes that do not fire field-decorator addInitializer. All paths
387
+ // register the same metadata idempotently through mergeNamespace.
358
388
  context.addInitializer(function initializeSchemaField()
359
389
  {
360
390
  defineFieldMetadata(this.constructor, context.name, namespace, value);
@@ -374,7 +404,7 @@ function fieldDecorator(namespace, value)
374
404
  }
375
405
 
376
406
  defineFieldMetadata(Constructor, contextOrFieldName, namespace, value);
377
- };
407
+ }, namespace, value);
378
408
  }
379
409
 
380
410
  function classDefinitionDecorator(definition)
@@ -437,7 +467,7 @@ function assertContextFirstMethod(fn, methodName)
437
467
 
438
468
  function methodDecorator(namespace, value)
439
469
  {
440
- return function schemaMethodDecorator(targetOrValue, contextOrMethodName)
470
+ return describeDecorator(function schemaMethodDecorator(targetOrValue, contextOrMethodName)
441
471
  {
442
472
  if (contextOrMethodName && typeof contextOrMethodName === "object")
443
473
  {
@@ -459,14 +489,14 @@ function methodDecorator(namespace, value)
459
489
  }
460
490
 
461
491
  defineMethodMetadata(Constructor, contextOrMethodName, namespace, value);
462
- };
492
+ }, namespace, value);
463
493
  }
464
494
 
465
495
  function memberDecorator(namespace, value)
466
496
  {
467
497
  const forMethods = methodDecorator(namespace, value);
468
498
  const forFields = fieldDecorator(namespace, value);
469
- return function schemaMemberDecorator(targetOrValue, contextOrMemberName)
499
+ return describeDecorator(function schemaMemberDecorator(targetOrValue, contextOrMemberName)
470
500
  {
471
501
  if (contextOrMemberName && typeof contextOrMemberName === "object")
472
502
  {
@@ -480,7 +510,7 @@ function memberDecorator(namespace, value)
480
510
  return targetOrValue && contextOrMemberName && typeof targetOrValue[contextOrMemberName] === "function"
481
511
  ? forMethods(targetOrValue, contextOrMemberName)
482
512
  : forFields(targetOrValue, contextOrMemberName);
483
- };
513
+ }, namespace, value);
484
514
  }
485
515
 
486
516
  function defineFieldMetadata(Constructor, fieldName, namespace, value)
@@ -649,6 +679,23 @@ function buildSchema(Constructor, namespaces)
649
679
  fields.push(enrichEnumField(exportField(field, namespaces), Constructor));
650
680
  }
651
681
 
682
+ // KNOWN DEFECT: methods are read from this class only, while fields resolve
683
+ // through the whole lineage above. A subclass therefore reports no inherited
684
+ // methods, and the decorated form hides that behind a second bug that
685
+ // cancels it out: method decorators register through addInitializer, where
686
+ // `this.constructor` is the *instance's* class, so constructing one
687
+ // Tr2LightProfileRes writes CjsResource's methods onto Tr2LightProfileRes.
688
+ // Which class owns which methods then depends on construction order, and
689
+ // before any instance exists a class reports no methods at all.
690
+ //
691
+ // Declaring metadata as data (CjsSchema.define fields/methods) registers on
692
+ // the declaring class at module load, so it is deterministic - and it makes
693
+ // the missing inheritance visible rather than accidentally papered over.
694
+ //
695
+ // Left unfixed deliberately: nothing reads .methods off a runtime schema
696
+ // today (tools-core classTool parses source documents, not these), and the
697
+ // fix - walking the lineage here as getEffectiveFields does - changes
698
+ // exported schemas for every decorator-using class in the org.
652
699
  for (const method of schema?.methods || [])
653
700
  {
654
701
  methods.push(exportField(method, namespaces));
@@ -860,27 +907,98 @@ function normalizeClassDefinition(Constructor, definition)
860
907
  return result;
861
908
  }
862
909
 
910
+ /**
911
+ * Normalize a definition's `fields`/`methods` into internal member records.
912
+ *
913
+ * Two spellings are accepted. A name-keyed object is the one to write:
914
+ * declaration order is key order, which is what drives GetValues() export
915
+ * order, and the name appears once rather than as a property of its own record.
916
+ * The array of `{name, ...}` records predates it and still parses, because it
917
+ * is what the internal schema stores.
918
+ *
919
+ * @param {object|Array<object>|null} members Declared members.
920
+ * @param {string} memberType Either "fields" or "methods", for messages.
921
+ * @returns {Array<object>} Internal member records.
922
+ */
863
923
  function normalizeManualMembers(members, memberType)
864
924
  {
865
925
  if (members === undefined || members === null) return [];
866
- if (!Array.isArray(members))
926
+
927
+ if (Array.isArray(members))
867
928
  {
868
- throw new TypeError(`CjsSchema.define ${memberType} must be an array.`);
929
+ return members.map((member, index) =>
930
+ {
931
+ if (!isPlainObject(member) || typeof member.name !== "string" || !member.name.trim())
932
+ {
933
+ throw new TypeError(`CjsSchema.define ${memberType}[${index}] requires a non-empty name.`);
934
+ }
935
+ const { name, ...namespaces } = member;
936
+ return normalizeManualMember(name.trim(), namespaces, memberType);
937
+ });
869
938
  }
870
939
 
871
- return members.map((member, index) =>
940
+ if (!isPlainObject(members))
872
941
  {
873
- if (!isPlainObject(member) || typeof member.name !== "string" || !member.name.trim())
942
+ throw new TypeError(
943
+ `CjsSchema.define ${memberType} must be a name-keyed object or an array of named records.`
944
+ );
945
+ }
946
+
947
+ return Object.entries(members).map(([ name, definition ]) =>
948
+ {
949
+ if (!name.trim())
874
950
  {
875
- throw new TypeError(`CjsSchema.define ${memberType}[${index}] requires a non-empty name.`);
951
+ throw new TypeError(`CjsSchema.define ${memberType} requires a non-empty name.`);
876
952
  }
877
- return {
878
- ...member,
879
- name: member.name.trim()
880
- };
953
+ return normalizeManualMember(name.trim(), definition, memberType);
881
954
  });
882
955
  }
883
956
 
957
+ /**
958
+ * Collapse one member's declaration into a namespace map.
959
+ *
960
+ * A declaration is a decorator, a namespace object, or an array mixing both.
961
+ * Decorators are accepted as data so the object form reuses the vocabulary the
962
+ * decorators already define rather than restating it: `impl.adapted` written
963
+ * twice in two spellings is two things that can disagree.
964
+ *
965
+ * @param {string} name Member name.
966
+ * @param {*} definition Declared metadata.
967
+ * @param {string} memberType Either "fields" or "methods", for messages.
968
+ * @returns {object} Internal member record.
969
+ */
970
+ function normalizeManualMember(name, definition, memberType)
971
+ {
972
+ const member = { name };
973
+
974
+ for (const entry of Array.isArray(definition) ? definition : [ definition ])
975
+ {
976
+ if (entry === undefined || entry === null) continue;
977
+
978
+ const decorator = getDecoratorMetadata(entry);
979
+ if (decorator)
980
+ {
981
+ member[decorator.namespace] = mergeNamespace(member[decorator.namespace], decorator.value);
982
+ continue;
983
+ }
984
+
985
+ if (!isPlainObject(entry))
986
+ {
987
+ throw new TypeError(
988
+ `CjsSchema.define ${memberType} "${name}" accepts schema decorators, ` +
989
+ "namespace objects, or an array of them."
990
+ );
991
+ }
992
+
993
+ for (const [ namespace, value ] of Object.entries(entry))
994
+ {
995
+ member[namespace] = mergeNamespace(member[namespace], value);
996
+ }
997
+ }
998
+
999
+ return member;
1000
+ }
1001
+
884
1002
  function registerClassMetadata(Constructor, schema)
885
1003
  {
886
1004
  if (!Constructor || !schema?.className) return;
@@ -2,12 +2,12 @@ import { CJS_ENUM_NAME, CjsSchema } from "./CjsSchema.js";
2
2
 
3
3
  // Namespace decorators re-exported as named bindings so consumers can write
4
4
  // `import { type, io } from ".../schema"` and `@type.string` instead of `@CjsSchema.type.string`.
5
- const { type, io, jessica, impl, carbon, components } = CjsSchema;
5
+ const { type, io, jessica, impl, carbon, components } = CjsSchema;
6
6
 
7
7
  export {
8
- carbon,
9
- components,
10
- CJS_ENUM_NAME,
8
+ carbon,
9
+ components,
10
+ CJS_ENUM_NAME,
11
11
  CjsSchema,
12
12
  CjsSchema as schema,
13
13
  impl,
@@ -1 +1 @@
1
- export * from "./shaderStages.js";
1
+ export * from "./shaderStages.js";
@@ -1,37 +1,37 @@
1
- /**
2
- * Canonical shader stage tokens.
3
- */
4
- export const ShaderStage = Object.freeze({
5
- VERTEX: "vertex",
6
- FRAGMENT: "fragment",
7
- PIXEL: "pixel",
8
- COMPUTE: "compute",
9
- GEOMETRY: "geometry",
10
- HULL: "hull",
11
- DOMAIN: "domain"
12
- });
13
-
14
- /**
15
- * Canonical shader model/profile tokens.
16
- */
17
- export const ShaderModel = Object.freeze({
18
- UNKNOWN: "unknown",
19
- SM2: "sm2",
20
- SM3: "sm3",
21
- SM4: "sm4",
22
- SM5: "sm5",
23
- SM6: "sm6",
24
- GLSL_ES_300: "glsl-es-300",
25
- WGSL: "wgsl"
26
- });
27
-
28
- /**
29
- * Normalize a shader stage token.
30
- *
31
- * @param {string} stage Shader stage token.
32
- * @returns {string} Canonical lowercase token.
33
- */
34
- export function normalizeShaderStage(stage)
35
- {
36
- return stage ? String(stage).trim().toLowerCase() : "";
37
- }
1
+ /**
2
+ * Canonical shader stage tokens.
3
+ */
4
+ export const ShaderStage = Object.freeze({
5
+ VERTEX: "vertex",
6
+ FRAGMENT: "fragment",
7
+ PIXEL: "pixel",
8
+ COMPUTE: "compute",
9
+ GEOMETRY: "geometry",
10
+ HULL: "hull",
11
+ DOMAIN: "domain"
12
+ });
13
+
14
+ /**
15
+ * Canonical shader model/profile tokens.
16
+ */
17
+ export const ShaderModel = Object.freeze({
18
+ UNKNOWN: "unknown",
19
+ SM2: "sm2",
20
+ SM3: "sm3",
21
+ SM4: "sm4",
22
+ SM5: "sm5",
23
+ SM6: "sm6",
24
+ GLSL_ES_300: "glsl-es-300",
25
+ WGSL: "wgsl"
26
+ });
27
+
28
+ /**
29
+ * Normalize a shader stage token.
30
+ *
31
+ * @param {string} stage Shader stage token.
32
+ * @returns {string} Canonical lowercase token.
33
+ */
34
+ export function normalizeShaderStage(stage)
35
+ {
36
+ return stage ? String(stage).trim().toLowerCase() : "";
37
+ }