@carbonenginejs/runtime-utils 0.1.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.
- package/LICENSE +21 -0
- package/NOTICE +23 -0
- package/README.md +56 -0
- package/THIRD-PARTY-NOTICES.md +38 -0
- package/docs/README.md +81 -0
- package/docs/architecture.md +101 -0
- package/docs/concepts/foundation-consolidation.md +86 -0
- package/docs/const-kb.md +92 -0
- package/docs/core-types/DECORATOR-TODOS.md +25 -0
- package/docs/core-types/README.md +203 -0
- package/docs/reference/api.md +70 -0
- package/docs/reference/classes/README.md +115 -0
- package/package.json +132 -0
- package/src/arrays.js +5 -0
- package/src/audio/audioFormats.js +34 -0
- package/src/audio/index.js +1 -0
- package/src/box3.js +1385 -0
- package/src/bytes.js +56 -0
- package/src/compression.js +56 -0
- package/src/constants/index.js +6 -0
- package/src/constants.js +15 -0
- package/src/curve.js +419 -0
- package/src/d3d/dxgiFormats.js +46 -0
- package/src/d3d/index.js +2 -0
- package/src/d3d/primitiveTopology.js +11 -0
- package/src/document/CjsCarbonDocument.js +212 -0
- package/src/document/CjsClassRegistry.js +373 -0
- package/src/document/CjsDocumentDehydrator.js +142 -0
- package/src/document/CjsDocumentHydrator.js +156 -0
- package/src/document/CjsStructRegistry.js +348 -0
- package/src/document/hydrationAdapter.js +129 -0
- package/src/document/index.js +6 -0
- package/src/geometry/box.js +137 -0
- package/src/geometry/cylinder.js +244 -0
- package/src/geometry/helpers/LICENSE +15 -0
- package/src/geometry/helpers/earcut.js +766 -0
- package/src/geometry/helpers/misc.js +103 -0
- package/src/geometry/index.js +8 -0
- package/src/geometry/json.js +165 -0
- package/src/geometry/lathe.js +172 -0
- package/src/geometry/octahedron.js +0 -0
- package/src/geometry/plane.js +81 -0
- package/src/geometry/shape.js +95 -0
- package/src/geometry/sphere.js +123 -0
- package/src/geometry/torus.js +96 -0
- package/src/graphics/colorSpaces.js +22 -0
- package/src/graphics/index.js +4 -0
- package/src/graphics/pixelFormats.js +158 -0
- package/src/graphics/textureDimensions.js +22 -0
- package/src/graphics/trinityEnums.js +87 -0
- package/src/index.js +58 -0
- package/src/is.js +524 -0
- package/src/json.js +23 -0
- package/src/lifecycle/CjsLifecycleState.js +77 -0
- package/src/lifecycle/index.js +1 -0
- package/src/lne3.js +497 -0
- package/src/lookup.js +48 -0
- package/src/mat3.js +131 -0
- package/src/mat4.js +699 -0
- package/src/math/index.js +25 -0
- package/src/math/scalar.js +63 -0
- package/src/media/index.js +1 -0
- package/src/media/mediaTypes.js +50 -0
- package/src/mesh.js +394 -0
- package/src/model/CjsEventEmitter.js +333 -0
- package/src/model/CjsModel.js +1544 -0
- package/src/model/CjsModelState.js +72 -0
- package/src/model/index.js +4 -0
- package/src/model/sourceRecordUtils.js +54 -0
- package/src/noise.js +310 -0
- package/src/num.js +827 -0
- package/src/path.js +21 -0
- package/src/pln.js +762 -0
- package/src/pool.js +160 -0
- package/src/quat.js +144 -0
- package/src/ray3.js +1085 -0
- package/src/renderContext/formats.js +145 -0
- package/src/renderContext/index.js +5 -0
- package/src/renderContext/presentation.js +125 -0
- package/src/renderContext/resources.js +27 -0
- package/src/renderContext/upscaling.js +22 -0
- package/src/renderContext/window.js +20 -0
- package/src/runtime/CjsRuntimeState.js +50 -0
- package/src/schema/CjsSchema.js +1009 -0
- package/src/schema/index.js +17 -0
- package/src/shader/index.js +1 -0
- package/src/shader/shaderStages.js +37 -0
- package/src/sph3.js +754 -0
- package/src/tangent.js +288 -0
- package/src/text.js +40 -0
- package/src/tri3.js +650 -0
- package/src/types/carbonTypes.js +635 -0
- package/src/types/index.js +2 -0
- package/src/utils.js +58 -0
- package/src/validation.js +46 -0
- package/src/vec2.js +229 -0
- package/src/vec3.js +1172 -0
- package/src/vec4.js +347 -0
- package/src/vertex.js +108 -0
- package/src/webgpu/index.js +1 -0
- package/src/webgpu/textureFormats.js +121 -0
|
@@ -0,0 +1,1009 @@
|
|
|
1
|
+
const CLASS_SCHEMA = new WeakMap();
|
|
2
|
+
const CONSTRUCTOR_BY_NAME = new Map();
|
|
3
|
+
const ENUM_SCHEMA_BY_NAME = new Map();
|
|
4
|
+
const ENUM_SCHEMA_BY_OBJECT = new WeakMap();
|
|
5
|
+
const STAGE3_FIELD_METADATA = Symbol("carbonenginejs.schema.stage3Fields");
|
|
6
|
+
|
|
7
|
+
export const CJS_ENUM_NAME = Symbol.for("carbonenginejs.enum.name");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reusable schema/decorator metadata surface.
|
|
11
|
+
*
|
|
12
|
+
* Decorators are namespace-scoped so consumers can export only the parts they
|
|
13
|
+
* understand. The functions support stage-3 field decorators and direct tool
|
|
14
|
+
* registration through decorateField().
|
|
15
|
+
*/
|
|
16
|
+
export class CjsSchema
|
|
17
|
+
{
|
|
18
|
+
static define(Constructor, definition = {})
|
|
19
|
+
{
|
|
20
|
+
defineClassMetadata(Constructor, normalizeClassDefinition(Constructor, definition));
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static enum(values)
|
|
25
|
+
{
|
|
26
|
+
return fieldDecorator("enum", normalizeEnumDefinition(values));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static defineEnum(values, definition = {})
|
|
30
|
+
{
|
|
31
|
+
defineEnumMetadata(values, normalizeEnumSchema(values, definition));
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static decorateField(Constructor, fieldName, ...decorators)
|
|
36
|
+
{
|
|
37
|
+
for (const decorator of decorators)
|
|
38
|
+
{
|
|
39
|
+
decorator(Constructor.prototype, fieldName);
|
|
40
|
+
}
|
|
41
|
+
return Constructor;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static decorateMethod(Constructor, methodName, ...decorators)
|
|
45
|
+
{
|
|
46
|
+
for (const decorator of decorators)
|
|
47
|
+
{
|
|
48
|
+
decorator(Constructor.prototype, methodName);
|
|
49
|
+
}
|
|
50
|
+
return Constructor;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static defineField(Constructor, fieldName, namespace, value)
|
|
54
|
+
{
|
|
55
|
+
defineFieldMetadata(Constructor, fieldName, namespace, value);
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static defineMethod(Constructor, methodName, namespace, value)
|
|
60
|
+
{
|
|
61
|
+
defineMethodMetadata(Constructor, methodName, namespace, value);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static getField(Constructor, fieldName)
|
|
66
|
+
{
|
|
67
|
+
return getEffectiveFields(Constructor).find(field => field.name === fieldName) || null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Excludes named inherited fields from the decorated class's schema surface.
|
|
72
|
+
*/
|
|
73
|
+
static hideInherited(fieldNames)
|
|
74
|
+
{
|
|
75
|
+
return hiddenInheritedFieldsDecorator(normalizeHiddenInheritedFields(fieldNames));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Checks whether a field is hidden from a class by its inheritance chain.
|
|
80
|
+
*/
|
|
81
|
+
static isFieldHidden(Constructor, fieldName)
|
|
82
|
+
{
|
|
83
|
+
return getHiddenInheritedFieldNames(Constructor).has(fieldName);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static getMethod(Constructor, methodName)
|
|
87
|
+
{
|
|
88
|
+
const schema = CLASS_SCHEMA.get(Constructor);
|
|
89
|
+
return schema?.methodsByName.get(methodName) || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static getClassName(Constructor)
|
|
93
|
+
{
|
|
94
|
+
let current = Constructor;
|
|
95
|
+
while (typeof current === "function")
|
|
96
|
+
{
|
|
97
|
+
const className = CLASS_SCHEMA.get(current)?.className || null;
|
|
98
|
+
if (className)
|
|
99
|
+
{
|
|
100
|
+
return current !== Constructor && className === "CjsModel" ? null : className;
|
|
101
|
+
}
|
|
102
|
+
current = Object.getPrototypeOf(current);
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static getClassFamily(Constructor)
|
|
108
|
+
{
|
|
109
|
+
let current = Constructor;
|
|
110
|
+
while (typeof current === "function")
|
|
111
|
+
{
|
|
112
|
+
const family = CLASS_SCHEMA.get(current)?.family || null;
|
|
113
|
+
if (family) return family;
|
|
114
|
+
current = Object.getPrototypeOf(current);
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static SetConstructor(name, Constructor)
|
|
120
|
+
{
|
|
121
|
+
if (typeof name !== "string" || !name.trim())
|
|
122
|
+
{
|
|
123
|
+
throw new TypeError("CjsSchema.SetConstructor requires a non-empty name.");
|
|
124
|
+
}
|
|
125
|
+
if (typeof Constructor !== "function")
|
|
126
|
+
{
|
|
127
|
+
throw new TypeError(`CjsSchema constructor ${name.trim()} must be a function.`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
CONSTRUCTOR_BY_NAME.set(name.trim(), Constructor);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static GetConstructor(name)
|
|
135
|
+
{
|
|
136
|
+
if (typeof name !== "string" || !name.trim()) return null;
|
|
137
|
+
return CONSTRUCTOR_BY_NAME.get(name.trim()) || null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static getEnumName(values)
|
|
141
|
+
{
|
|
142
|
+
if (typeof values === "string") return values;
|
|
143
|
+
return values && typeof values === "object"
|
|
144
|
+
? ENUM_SCHEMA_BY_OBJECT.get(values)?.name || values[CJS_ENUM_NAME] || values.Source?.name || values.name || null
|
|
145
|
+
: null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static getEnum(values)
|
|
149
|
+
{
|
|
150
|
+
const name = CjsSchema.getEnumName(values);
|
|
151
|
+
return name ? ENUM_SCHEMA_BY_NAME.get(name) || null : null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static getSchema(Constructor, options = {})
|
|
155
|
+
{
|
|
156
|
+
const schema = CLASS_SCHEMA.get(Constructor);
|
|
157
|
+
const namespaces = normalizeNamespaces(options.namespaces);
|
|
158
|
+
const fields = [];
|
|
159
|
+
const methods = [];
|
|
160
|
+
|
|
161
|
+
for (const field of getEffectiveFields(Constructor))
|
|
162
|
+
{
|
|
163
|
+
fields.push(enrichEnumField(exportField(field, namespaces), Constructor));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const method of schema?.methods || [])
|
|
167
|
+
{
|
|
168
|
+
methods.push(exportField(method, namespaces));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const result = {
|
|
172
|
+
className: CjsSchema.getClassName(Constructor),
|
|
173
|
+
fields: Object.freeze(fields)
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const family = schema?.family || CjsSchema.getClassFamily(Constructor);
|
|
177
|
+
if (family)
|
|
178
|
+
{
|
|
179
|
+
result.family = family;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (schema?.sourceClass && schema.sourceClass !== result.className)
|
|
183
|
+
{
|
|
184
|
+
result.sourceClass = schema.sourceClass;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (schema?.aliases?.length)
|
|
188
|
+
{
|
|
189
|
+
result.aliases = Object.freeze([...schema.aliases]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (methods.length) result.methods = Object.freeze(methods);
|
|
193
|
+
|
|
194
|
+
return Object.freeze(result);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static type = Object.freeze({
|
|
198
|
+
array: itemType => fieldDecorator("type", { kind: "array", itemType }),
|
|
199
|
+
boolean: fieldDecorator("type", { kind: "boolean" }),
|
|
200
|
+
color: fieldDecorator("type", { kind: "color" }),
|
|
201
|
+
define: definition => classDefinitionDecorator(definition),
|
|
202
|
+
expression: fieldDecorator("type", { kind: "expression", js: "string" }),
|
|
203
|
+
float32: fieldDecorator("type", { kind: "float32" }),
|
|
204
|
+
float64: fieldDecorator("type", { kind: "float64" }),
|
|
205
|
+
int8: fieldDecorator("type", { kind: "int8" }),
|
|
206
|
+
int16: fieldDecorator("type", { kind: "int16" }),
|
|
207
|
+
int32: fieldDecorator("type", { kind: "int32" }),
|
|
208
|
+
int64: fieldDecorator("type", { kind: "int64" }),
|
|
209
|
+
list: itemType => fieldDecorator("type", { kind: "list", itemType }),
|
|
210
|
+
mat3: fieldDecorator("type", { kind: "mat3" }),
|
|
211
|
+
mat4: fieldDecorator("type", { kind: "mat4" }),
|
|
212
|
+
map: valueType => fieldDecorator("type", { kind: "map", valueType }),
|
|
213
|
+
model: className => fieldDecorator("type", { kind: "model", className }),
|
|
214
|
+
objectRef: className => fieldDecorator("type", { kind: "objectRef", className }),
|
|
215
|
+
path: fieldDecorator("type", { kind: "path" }),
|
|
216
|
+
quat: fieldDecorator("type", { kind: "quat" }),
|
|
217
|
+
rawStruct: className => fieldDecorator("type", { kind: "rawStruct", className }),
|
|
218
|
+
set: itemType => fieldDecorator("type", { kind: "set", itemType }),
|
|
219
|
+
string: fieldDecorator("type", { kind: "string" }),
|
|
220
|
+
struct: className => fieldDecorator("type", { kind: "struct", className }),
|
|
221
|
+
typedArray: arrayType => fieldDecorator("type", { kind: "typedArray", arrayType }),
|
|
222
|
+
uint8: fieldDecorator("type", { kind: "uint8" }),
|
|
223
|
+
uint16: fieldDecorator("type", { kind: "uint16" }),
|
|
224
|
+
uint32: fieldDecorator("type", { kind: "uint32" }),
|
|
225
|
+
uint64: fieldDecorator("type", { kind: "uint64" }),
|
|
226
|
+
unknown: fieldDecorator("type", { kind: "unknown" }),
|
|
227
|
+
vec2: fieldDecorator("type", { kind: "vec2" }),
|
|
228
|
+
vec3: fieldDecorator("type", { kind: "vec3" }),
|
|
229
|
+
vec4: fieldDecorator("type", { kind: "vec4" })
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
static io = Object.freeze({
|
|
233
|
+
always: fieldDecorator("io", { always: true }),
|
|
234
|
+
notify: fieldDecorator("io", { notify: true }),
|
|
235
|
+
owned: fieldDecorator("io", { ownership: "owned" }),
|
|
236
|
+
persist: fieldDecorator("io", { read: true, write: true, persist: true }),
|
|
237
|
+
persistOnly: fieldDecorator("io", { persist: true, persistOnly: true }),
|
|
238
|
+
read: fieldDecorator("io", { read: true }),
|
|
239
|
+
readwrite: fieldDecorator("io", { read: true, write: true }),
|
|
240
|
+
reference: fieldDecorator("io", { ownership: "reference" }),
|
|
241
|
+
// Declares the lazy-invalidation token(s) a change to this field
|
|
242
|
+
// implies ("bounds is stale"). Added to __state.flags at write time;
|
|
243
|
+
// cleared ONLY by the getter that recomputes the derived value.
|
|
244
|
+
flag: (...tokens) => fieldDecorator("io", { flag: tokens.flat().map(String) }),
|
|
245
|
+
// Declares the rebuild requirement token(s) a change to this field
|
|
246
|
+
// implies ("vertices need rebuilding"). Added to __state.rebuild at
|
|
247
|
+
// write time; cleared ONLY by the specific work method that succeeds
|
|
248
|
+
// (typically driven from Update / per-frame passes).
|
|
249
|
+
rebuild: (...tokens) => fieldDecorator("io", { rebuild: tokens.flat().map(String) }),
|
|
250
|
+
write: fieldDecorator("io", { write: true })
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
static jessica = Object.freeze({
|
|
254
|
+
group: name => fieldDecorator("jessica", { group: name }),
|
|
255
|
+
hidden: fieldDecorator("jessica", { hidden: true }),
|
|
256
|
+
readOnly: fieldDecorator("jessica", { readOnly: true }),
|
|
257
|
+
widget: name => fieldDecorator("jessica", { widget: name })
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// impl decorators apply to methods AND fields: a promoted/diverging field
|
|
261
|
+
// (e.g. a Carbon-hidden authored value exposed for values interchange) is
|
|
262
|
+
// an implementation decision, so it carries impl.adapted/impl.custom +
|
|
263
|
+
// impl.reason just like a diverging method. carbon.* stays factual
|
|
264
|
+
// provenance and remains method-only.
|
|
265
|
+
static impl = Object.freeze({
|
|
266
|
+
abstract: memberDecorator("impl", { abstract: true, status: "abstract" }),
|
|
267
|
+
adapted: memberDecorator("impl", { adapted: true, status: "adapted" }),
|
|
268
|
+
custom: memberDecorator("impl", { custom: true, status: "custom" }),
|
|
269
|
+
implemented: memberDecorator("impl", { implemented: true, status: "implemented" }),
|
|
270
|
+
noop: memberDecorator("impl", { noop: true, status: "noop" }),
|
|
271
|
+
notImplemented: memberDecorator("impl", { notImplemented: true, status: "notImplemented" }),
|
|
272
|
+
notSupported: memberDecorator("impl", { notSupported: true, status: "notSupported" }),
|
|
273
|
+
note: text => memberDecorator("impl", { note: String(text) }),
|
|
274
|
+
reason: text => memberDecorator("impl", { reason: String(text) })
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
static carbon = Object.freeze({
|
|
278
|
+
method: methodDecorator("carbon", { method: true }),
|
|
279
|
+
renamed: originalName => {
|
|
280
|
+
if (typeof originalName !== "string" || !originalName.trim())
|
|
281
|
+
{
|
|
282
|
+
throw new TypeError("CjsSchema.carbon.renamed requires a non-empty original method name.");
|
|
283
|
+
}
|
|
284
|
+
return methodDecorator("carbon", {
|
|
285
|
+
method: true,
|
|
286
|
+
renamed: true,
|
|
287
|
+
originalName: originalName.trim()
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
contextual: tiers => {
|
|
291
|
+
const list = Array.isArray(tiers) ? tiers : [tiers];
|
|
292
|
+
const normalized = [];
|
|
293
|
+
for (const tier of list)
|
|
294
|
+
{
|
|
295
|
+
if (typeof tier !== "string" || !tier.trim())
|
|
296
|
+
{
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
normalized.push(tier.trim());
|
|
300
|
+
}
|
|
301
|
+
if (!normalized.length)
|
|
302
|
+
{
|
|
303
|
+
throw new TypeError("CjsSchema.carbon.contextual requires at least one context tier name.");
|
|
304
|
+
}
|
|
305
|
+
const base = methodDecorator("carbon", {
|
|
306
|
+
method: true,
|
|
307
|
+
contextual: true,
|
|
308
|
+
contextTiers: Object.freeze(normalized)
|
|
309
|
+
});
|
|
310
|
+
return function contextualMethodDecorator(targetOrValue, contextOrMethodName)
|
|
311
|
+
{
|
|
312
|
+
// Contextual methods are validated context-first at decoration
|
|
313
|
+
// time: the first declared parameter must be the frame context.
|
|
314
|
+
if (contextOrMethodName && typeof contextOrMethodName === "object")
|
|
315
|
+
{
|
|
316
|
+
assertContextFirstMethod(targetOrValue, contextOrMethodName.name);
|
|
317
|
+
}
|
|
318
|
+
else if (targetOrValue && contextOrMethodName)
|
|
319
|
+
{
|
|
320
|
+
assertContextFirstMethod(targetOrValue[contextOrMethodName], contextOrMethodName);
|
|
321
|
+
}
|
|
322
|
+
return base(targetOrValue, contextOrMethodName);
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
static components = createComponentsNamespace();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function createComponentsNamespace()
|
|
331
|
+
{
|
|
332
|
+
const components = definition => fieldDecorator("components", normalizeComponentDefinition(definition));
|
|
333
|
+
Object.defineProperties(components, {
|
|
334
|
+
get: { value: getComponentValue },
|
|
335
|
+
indices: { value: getComponentIndices },
|
|
336
|
+
set: { value: setComponentValue }
|
|
337
|
+
});
|
|
338
|
+
return Object.freeze(components);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function fieldDecorator(namespace, value)
|
|
342
|
+
{
|
|
343
|
+
return function schemaFieldDecorator(targetOrValue, contextOrFieldName)
|
|
344
|
+
{
|
|
345
|
+
if (contextOrFieldName && typeof contextOrFieldName === "object")
|
|
346
|
+
{
|
|
347
|
+
const context = contextOrFieldName;
|
|
348
|
+
if (context.kind !== "field") throw new TypeError("CjsSchema decorators only support class fields.");
|
|
349
|
+
recordStage3FieldMetadata(context, namespace, value);
|
|
350
|
+
|
|
351
|
+
// Register field metadata on instance construction. addInitializer covers
|
|
352
|
+
// spec-compliant runtimes; the returned field initializer covers runtimes (e.g.
|
|
353
|
+
// Deno/SWC) that do NOT fire field-decorator addInitializer. Both register the same
|
|
354
|
+
// metadata (idempotent via mergeNamespace), so whichever the runtime honours, the
|
|
355
|
+
// schema is populated. Registration is lazy (first construction); the class decorator
|
|
356
|
+
// still registers the class eagerly at definition time.
|
|
357
|
+
context.addInitializer(function initializeSchemaField()
|
|
358
|
+
{
|
|
359
|
+
defineFieldMetadata(this.constructor, context.name, namespace, value);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
return function initializeSchemaFieldValue(initialValue)
|
|
363
|
+
{
|
|
364
|
+
defineFieldMetadata(this.constructor, context.name, namespace, value);
|
|
365
|
+
return initialValue;
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const Constructor = targetOrValue?.constructor;
|
|
370
|
+
if (!Constructor || !contextOrFieldName)
|
|
371
|
+
{
|
|
372
|
+
throw new TypeError("CjsSchema field decorators require a class field target.");
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
defineFieldMetadata(Constructor, contextOrFieldName, namespace, value);
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function classDefinitionDecorator(definition)
|
|
380
|
+
{
|
|
381
|
+
return function schemaClassDefinitionDecorator(value, context)
|
|
382
|
+
{
|
|
383
|
+
if (context && typeof context === "object")
|
|
384
|
+
{
|
|
385
|
+
if (context.kind !== "class") throw new TypeError("CjsSchema type.define only supports classes.");
|
|
386
|
+
registerStage3FieldMetadata(value, context.metadata);
|
|
387
|
+
defineClassMetadata(value, normalizeClassDefinition(value, definition));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (typeof value !== "function")
|
|
392
|
+
{
|
|
393
|
+
throw new TypeError("CjsSchema type.define requires a class constructor.");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
defineClassMetadata(value, normalizeClassDefinition(value, definition));
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function hiddenInheritedFieldsDecorator(fieldNames)
|
|
401
|
+
{
|
|
402
|
+
return function schemaHiddenInheritedFieldsDecorator(value, context)
|
|
403
|
+
{
|
|
404
|
+
if (context && typeof context === "object")
|
|
405
|
+
{
|
|
406
|
+
if (context.kind !== "class") throw new TypeError("CjsSchema.hideInherited only supports classes.");
|
|
407
|
+
registerStage3FieldMetadata(value, context.metadata);
|
|
408
|
+
}
|
|
409
|
+
else if (typeof value !== "function")
|
|
410
|
+
{
|
|
411
|
+
throw new TypeError("CjsSchema.hideInherited requires a class constructor.");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
defineHiddenInheritedFields(value, fieldNames);
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const CONTEXT_FIRST_PARAMETER = /^\(?\s*_?(context|updateContext)\b/;
|
|
419
|
+
|
|
420
|
+
function assertContextFirstMethod(fn, methodName)
|
|
421
|
+
{
|
|
422
|
+
if (typeof fn !== "function")
|
|
423
|
+
{
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const source = String(fn);
|
|
427
|
+
const parameterList = source.slice(source.indexOf("("));
|
|
428
|
+
if (fn.length < 1 || !CONTEXT_FIRST_PARAMETER.test(parameterList))
|
|
429
|
+
{
|
|
430
|
+
throw new TypeError(
|
|
431
|
+
`CjsSchema.carbon.contextual method "${String(methodName)}" must be context-first ` +
|
|
432
|
+
"(first parameter named context or updateContext)."
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function methodDecorator(namespace, value)
|
|
438
|
+
{
|
|
439
|
+
return function schemaMethodDecorator(targetOrValue, contextOrMethodName)
|
|
440
|
+
{
|
|
441
|
+
if (contextOrMethodName && typeof contextOrMethodName === "object")
|
|
442
|
+
{
|
|
443
|
+
const context = contextOrMethodName;
|
|
444
|
+
if (context.kind !== "method") throw new TypeError("CjsSchema method decorators only support class methods.");
|
|
445
|
+
|
|
446
|
+
context.addInitializer(function initializeSchemaMethod()
|
|
447
|
+
{
|
|
448
|
+
const Constructor = context.static ? this : this.constructor;
|
|
449
|
+
defineMethodMetadata(Constructor, context.name, namespace, value);
|
|
450
|
+
});
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const Constructor = targetOrValue?.constructor;
|
|
455
|
+
if (!Constructor || !contextOrMethodName)
|
|
456
|
+
{
|
|
457
|
+
throw new TypeError("CjsSchema method decorators require a class method target.");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
defineMethodMetadata(Constructor, contextOrMethodName, namespace, value);
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function memberDecorator(namespace, value)
|
|
465
|
+
{
|
|
466
|
+
const forMethods = methodDecorator(namespace, value);
|
|
467
|
+
const forFields = fieldDecorator(namespace, value);
|
|
468
|
+
return function schemaMemberDecorator(targetOrValue, contextOrMemberName)
|
|
469
|
+
{
|
|
470
|
+
if (contextOrMemberName && typeof contextOrMemberName === "object")
|
|
471
|
+
{
|
|
472
|
+
return contextOrMemberName.kind === "field"
|
|
473
|
+
? forFields(targetOrValue, contextOrMemberName)
|
|
474
|
+
: forMethods(targetOrValue, contextOrMemberName);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Legacy (non-2023-11) path: a method target resolves to a function
|
|
478
|
+
// on the prototype; anything else is treated as a field.
|
|
479
|
+
return targetOrValue && contextOrMemberName && typeof targetOrValue[contextOrMemberName] === "function"
|
|
480
|
+
? forMethods(targetOrValue, contextOrMemberName)
|
|
481
|
+
: forFields(targetOrValue, contextOrMemberName);
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function defineFieldMetadata(Constructor, fieldName, namespace, value)
|
|
486
|
+
{
|
|
487
|
+
defineMemberMetadata(Constructor, "fields", "fieldsByName", fieldName, namespace, value);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function defineClassMetadata(Constructor, definition)
|
|
491
|
+
{
|
|
492
|
+
const schema = getOrCreateClassSchema(Constructor);
|
|
493
|
+
if (definition.className) schema.className = definition.className;
|
|
494
|
+
if (definition.family) schema.family = definition.family;
|
|
495
|
+
if (definition.sourceClass) schema.sourceClass = definition.sourceClass;
|
|
496
|
+
if (definition.aliases) schema.aliases = Object.freeze([...definition.aliases]);
|
|
497
|
+
|
|
498
|
+
for (const field of definition.fields || [])
|
|
499
|
+
{
|
|
500
|
+
defineManualMemberMetadata(Constructor, "fields", field);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
for (const method of definition.methods || [])
|
|
504
|
+
{
|
|
505
|
+
defineManualMemberMetadata(Constructor, "methods", method);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
registerClassMetadata(Constructor, schema);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function defineManualMemberMetadata(Constructor, memberType, definition)
|
|
512
|
+
{
|
|
513
|
+
const define = memberType === "methods" ? defineMethodMetadata : defineFieldMetadata;
|
|
514
|
+
for (const [namespace, value] of Object.entries(definition))
|
|
515
|
+
{
|
|
516
|
+
if (namespace === "name") continue;
|
|
517
|
+
define(Constructor, definition.name, namespace, value);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function defineMethodMetadata(Constructor, methodName, namespace, value)
|
|
522
|
+
{
|
|
523
|
+
defineMemberMetadata(Constructor, "methods", "methodsByName", methodName, namespace, value);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function defineMemberMetadata(Constructor, listKey, mapKey, name, namespace, value)
|
|
527
|
+
{
|
|
528
|
+
const schema = getOrCreateClassSchema(Constructor);
|
|
529
|
+
let item = schema[mapKey].get(name);
|
|
530
|
+
|
|
531
|
+
if (!item)
|
|
532
|
+
{
|
|
533
|
+
item = { name };
|
|
534
|
+
schema[listKey].push(item);
|
|
535
|
+
schema[mapKey].set(name, item);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
item[namespace] = mergeNamespace(item[namespace], value);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function defineHiddenInheritedFields(Constructor, fieldNames)
|
|
542
|
+
{
|
|
543
|
+
if (typeof Constructor !== "function")
|
|
544
|
+
{
|
|
545
|
+
throw new TypeError("CjsSchema.hideInherited requires a class constructor.");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const Parent = Object.getPrototypeOf(Constructor);
|
|
549
|
+
const inheritedFields = new Set(getEffectiveFields(Parent).map(field => field.name));
|
|
550
|
+
const className = CLASS_SCHEMA.get(Constructor)?.className || Constructor.name || "<anonymous>";
|
|
551
|
+
|
|
552
|
+
for (const fieldName of fieldNames)
|
|
553
|
+
{
|
|
554
|
+
if (!inheritedFields.has(fieldName))
|
|
555
|
+
{
|
|
556
|
+
throw new TypeError(
|
|
557
|
+
`CjsSchema.hideInherited cannot hide "${fieldName}" on ${className}: ` +
|
|
558
|
+
"the parent schema does not expose that field."
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const schema = getOrCreateClassSchema(Constructor);
|
|
564
|
+
for (const fieldName of fieldNames)
|
|
565
|
+
{
|
|
566
|
+
schema.hiddenInherited.add(fieldName);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function getEffectiveFields(Constructor)
|
|
571
|
+
{
|
|
572
|
+
const ordered = [];
|
|
573
|
+
const byName = new Map();
|
|
574
|
+
const hidden = new Set();
|
|
575
|
+
|
|
576
|
+
for (const current of getSchemaLineage(Constructor))
|
|
577
|
+
{
|
|
578
|
+
const schema = CLASS_SCHEMA.get(current);
|
|
579
|
+
for (const field of schema?.fields || [])
|
|
580
|
+
{
|
|
581
|
+
const existing = byName.get(field.name);
|
|
582
|
+
if (existing)
|
|
583
|
+
{
|
|
584
|
+
mergeMemberMetadata(existing, field);
|
|
585
|
+
}
|
|
586
|
+
else
|
|
587
|
+
{
|
|
588
|
+
const merged = mergeMemberMetadata({ name: field.name }, field);
|
|
589
|
+
ordered.push(merged);
|
|
590
|
+
byName.set(field.name, merged);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
for (const fieldName of schema?.hiddenInherited || [])
|
|
595
|
+
{
|
|
596
|
+
hidden.add(fieldName);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return ordered.filter(field => !hidden.has(field.name));
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function getHiddenInheritedFieldNames(Constructor)
|
|
604
|
+
{
|
|
605
|
+
const hidden = new Set();
|
|
606
|
+
for (const current of getSchemaLineage(Constructor))
|
|
607
|
+
{
|
|
608
|
+
for (const fieldName of CLASS_SCHEMA.get(current)?.hiddenInherited || [])
|
|
609
|
+
{
|
|
610
|
+
hidden.add(fieldName);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return hidden;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function getSchemaLineage(Constructor)
|
|
617
|
+
{
|
|
618
|
+
const lineage = [];
|
|
619
|
+
let current = Constructor;
|
|
620
|
+
while (typeof current === "function")
|
|
621
|
+
{
|
|
622
|
+
if (CLASS_SCHEMA.has(current)) lineage.push(current);
|
|
623
|
+
current = Object.getPrototypeOf(current);
|
|
624
|
+
}
|
|
625
|
+
return lineage.reverse();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function mergeMemberMetadata(target, source)
|
|
629
|
+
{
|
|
630
|
+
for (const [namespace, value] of Object.entries(source))
|
|
631
|
+
{
|
|
632
|
+
if (namespace === "name") continue;
|
|
633
|
+
target[namespace] = mergeNamespace(target[namespace], value);
|
|
634
|
+
}
|
|
635
|
+
return target;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function getOrCreateClassSchema(Constructor)
|
|
639
|
+
{
|
|
640
|
+
let schema = CLASS_SCHEMA.get(Constructor);
|
|
641
|
+
if (!schema)
|
|
642
|
+
{
|
|
643
|
+
schema = {
|
|
644
|
+
className: null,
|
|
645
|
+
family: null,
|
|
646
|
+
sourceClass: null,
|
|
647
|
+
aliases: null,
|
|
648
|
+
fields: [],
|
|
649
|
+
fieldsByName: new Map(),
|
|
650
|
+
hiddenInherited: new Set(),
|
|
651
|
+
methods: [],
|
|
652
|
+
methodsByName: new Map()
|
|
653
|
+
};
|
|
654
|
+
CLASS_SCHEMA.set(Constructor, schema);
|
|
655
|
+
}
|
|
656
|
+
return schema;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function normalizeHiddenInheritedFields(fieldNames)
|
|
660
|
+
{
|
|
661
|
+
if (!Array.isArray(fieldNames) || !fieldNames.length)
|
|
662
|
+
{
|
|
663
|
+
throw new TypeError("CjsSchema.hideInherited requires a non-empty array of field names.");
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const normalized = fieldNames.map((fieldName, index) =>
|
|
667
|
+
{
|
|
668
|
+
if (typeof fieldName !== "string" || !fieldName.trim())
|
|
669
|
+
{
|
|
670
|
+
throw new TypeError(`CjsSchema.hideInherited fieldNames[${index}] must be a non-empty string.`);
|
|
671
|
+
}
|
|
672
|
+
return fieldName.trim();
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
return Object.freeze([...new Set(normalized)]);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function recordStage3FieldMetadata(context, namespace, value)
|
|
679
|
+
{
|
|
680
|
+
const metadata = context?.metadata;
|
|
681
|
+
if (!metadata || typeof metadata !== "object") return;
|
|
682
|
+
|
|
683
|
+
let fields;
|
|
684
|
+
if (Object.prototype.hasOwnProperty.call(metadata, STAGE3_FIELD_METADATA))
|
|
685
|
+
{
|
|
686
|
+
fields = metadata[STAGE3_FIELD_METADATA];
|
|
687
|
+
}
|
|
688
|
+
else
|
|
689
|
+
{
|
|
690
|
+
fields = [];
|
|
691
|
+
Object.defineProperty(metadata, STAGE3_FIELD_METADATA, {
|
|
692
|
+
configurable: false,
|
|
693
|
+
enumerable: false,
|
|
694
|
+
value: fields,
|
|
695
|
+
writable: false
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
fields.push({
|
|
700
|
+
name: context.name,
|
|
701
|
+
namespace,
|
|
702
|
+
value
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function registerStage3FieldMetadata(Constructor, metadata)
|
|
707
|
+
{
|
|
708
|
+
if (!metadata || typeof metadata !== "object") return;
|
|
709
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, STAGE3_FIELD_METADATA)) return;
|
|
710
|
+
|
|
711
|
+
for (const field of metadata[STAGE3_FIELD_METADATA])
|
|
712
|
+
{
|
|
713
|
+
defineFieldMetadata(Constructor, field.name, field.namespace, field.value);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function normalizeClassDefinition(Constructor, definition)
|
|
718
|
+
{
|
|
719
|
+
if (typeof definition === "string")
|
|
720
|
+
{
|
|
721
|
+
definition = { className: definition };
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const result = { ...(definition || {}) };
|
|
725
|
+
if (typeof result.className !== "string" || !result.className.trim())
|
|
726
|
+
{
|
|
727
|
+
throw new TypeError("CjsSchema.define requires an explicit non-empty className.");
|
|
728
|
+
}
|
|
729
|
+
result.className = result.className.trim();
|
|
730
|
+
if (!result.sourceClass && result.className) result.sourceClass = result.className;
|
|
731
|
+
const aliases = [
|
|
732
|
+
...(result.aliases === undefined ? [] : Array.isArray(result.aliases) ? result.aliases : [result.aliases]),
|
|
733
|
+
...(result.alias === undefined ? [] : Array.isArray(result.alias) ? result.alias : [result.alias])
|
|
734
|
+
].filter(alias => typeof alias === "string" && alias.trim()).map(alias => alias.trim())
|
|
735
|
+
.filter(alias => alias !== result.className);
|
|
736
|
+
result.aliases = aliases.length ? [...new Set(aliases)] : null;
|
|
737
|
+
result.fields = normalizeManualMembers(result.fields, "fields");
|
|
738
|
+
result.methods = normalizeManualMembers(result.methods, "methods");
|
|
739
|
+
delete result.alias;
|
|
740
|
+
return result;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function normalizeManualMembers(members, memberType)
|
|
744
|
+
{
|
|
745
|
+
if (members === undefined || members === null) return [];
|
|
746
|
+
if (!Array.isArray(members))
|
|
747
|
+
{
|
|
748
|
+
throw new TypeError(`CjsSchema.define ${memberType} must be an array.`);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return members.map((member, index) =>
|
|
752
|
+
{
|
|
753
|
+
if (!isPlainObject(member) || typeof member.name !== "string" || !member.name.trim())
|
|
754
|
+
{
|
|
755
|
+
throw new TypeError(`CjsSchema.define ${memberType}[${index}] requires a non-empty name.`);
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
...member,
|
|
759
|
+
name: member.name.trim()
|
|
760
|
+
};
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function registerClassMetadata(Constructor, schema)
|
|
765
|
+
{
|
|
766
|
+
if (!Constructor || !schema?.className) return;
|
|
767
|
+
|
|
768
|
+
CjsSchema.SetConstructor(schema.className, Constructor);
|
|
769
|
+
|
|
770
|
+
for (const alias of schema.aliases || [])
|
|
771
|
+
{
|
|
772
|
+
CjsSchema.SetConstructor(alias, Constructor);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function defineEnumMetadata(values, schema)
|
|
777
|
+
{
|
|
778
|
+
if (!values || typeof values !== "object" || !schema?.name) return;
|
|
779
|
+
|
|
780
|
+
ENUM_SCHEMA_BY_NAME.set(schema.name, schema);
|
|
781
|
+
ENUM_SCHEMA_BY_OBJECT.set(values, schema);
|
|
782
|
+
|
|
783
|
+
if (Object.isExtensible(values) && !Object.prototype.hasOwnProperty.call(values, CJS_ENUM_NAME))
|
|
784
|
+
{
|
|
785
|
+
Object.defineProperty(values, CJS_ENUM_NAME, {
|
|
786
|
+
value: schema.name
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function normalizeEnumSchema(values, definition)
|
|
792
|
+
{
|
|
793
|
+
const type = values?.Type || values;
|
|
794
|
+
const members = Array.isArray(definition.members)
|
|
795
|
+
? Object.freeze(definition.members.map(member => Object.freeze({ ...member })))
|
|
796
|
+
: Object.freeze([]);
|
|
797
|
+
const result = {
|
|
798
|
+
name: definition.name || values?.[CJS_ENUM_NAME] || values?.Source?.name || values?.name || null,
|
|
799
|
+
type,
|
|
800
|
+
members
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
if (definition.source) result.source = definition.source;
|
|
804
|
+
if (definition.family) result.family = definition.family;
|
|
805
|
+
if (definition.line !== undefined && definition.line !== null) result.line = definition.line;
|
|
806
|
+
|
|
807
|
+
return Object.freeze(result);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function normalizeEnumDefinition(values)
|
|
811
|
+
{
|
|
812
|
+
if (typeof values === "string")
|
|
813
|
+
{
|
|
814
|
+
return Object.freeze({ enumType: values });
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const type = values?.Type || (isPlainObject(values) ? values : null);
|
|
818
|
+
if (type && typeof type === "object")
|
|
819
|
+
{
|
|
820
|
+
const result = {
|
|
821
|
+
values: type
|
|
822
|
+
};
|
|
823
|
+
const enumType = CjsSchema.getEnumName(values);
|
|
824
|
+
if (enumType) result.enumType = enumType;
|
|
825
|
+
return Object.freeze(result);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
return Object.freeze({ values });
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function normalizeComponentDefinition(definition)
|
|
832
|
+
{
|
|
833
|
+
if (!isPlainObject(definition))
|
|
834
|
+
{
|
|
835
|
+
throw new TypeError("CjsSchema.components requires a plain object definition.");
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return Object.freeze(Object.fromEntries(Object.entries(definition).map(([swizzle, value]) => [
|
|
839
|
+
normalizeSwizzle(swizzle),
|
|
840
|
+
normalizeComponentEntry(value)
|
|
841
|
+
])));
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function normalizeComponentEntry(value)
|
|
845
|
+
{
|
|
846
|
+
if (typeof value === "string") return Object.freeze({ name: value });
|
|
847
|
+
if (isPlainObject(value)) return cloneSchemaValue(value);
|
|
848
|
+
return value;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function getComponentValue(value, swizzle)
|
|
852
|
+
{
|
|
853
|
+
const indices = getComponentIndices(swizzle);
|
|
854
|
+
if (indices.length === 1) return value?.[indices[0]];
|
|
855
|
+
return indices.map(index => value?.[index]);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function setComponentValue(target, swizzle, value)
|
|
859
|
+
{
|
|
860
|
+
if (!target)
|
|
861
|
+
{
|
|
862
|
+
throw new TypeError("CjsSchema.components.set requires a target vector.");
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const indices = getComponentIndices(swizzle);
|
|
866
|
+
if (indices.length === 1)
|
|
867
|
+
{
|
|
868
|
+
target[indices[0]] = value;
|
|
869
|
+
return target;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (!value || typeof value[Symbol.iterator] !== "function")
|
|
873
|
+
{
|
|
874
|
+
throw new TypeError(`CjsSchema.components.set requires an iterable value for '${swizzle}'.`);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
let offset = 0;
|
|
878
|
+
for (const item of value)
|
|
879
|
+
{
|
|
880
|
+
if (offset >= indices.length) break;
|
|
881
|
+
target[indices[offset++]] = item;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (offset !== indices.length)
|
|
885
|
+
{
|
|
886
|
+
throw new RangeError(`CjsSchema.components.set expected ${indices.length} values for '${swizzle}' but received ${offset}.`);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return target;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function getComponentIndices(swizzle)
|
|
893
|
+
{
|
|
894
|
+
return Object.freeze([...normalizeSwizzle(swizzle)].map(componentIndex));
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function normalizeSwizzle(swizzle)
|
|
898
|
+
{
|
|
899
|
+
const text = String(swizzle || "").trim().toLowerCase();
|
|
900
|
+
if (!text)
|
|
901
|
+
{
|
|
902
|
+
throw new TypeError("Component swizzle cannot be empty.");
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
for (const char of text)
|
|
906
|
+
{
|
|
907
|
+
componentIndex(char);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return text;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function componentIndex(char)
|
|
914
|
+
{
|
|
915
|
+
switch (char)
|
|
916
|
+
{
|
|
917
|
+
case "x":
|
|
918
|
+
case "r":
|
|
919
|
+
case "0":
|
|
920
|
+
return 0;
|
|
921
|
+
case "y":
|
|
922
|
+
case "g":
|
|
923
|
+
case "1":
|
|
924
|
+
return 1;
|
|
925
|
+
case "z":
|
|
926
|
+
case "b":
|
|
927
|
+
case "2":
|
|
928
|
+
return 2;
|
|
929
|
+
case "w":
|
|
930
|
+
case "a":
|
|
931
|
+
case "3":
|
|
932
|
+
return 3;
|
|
933
|
+
default:
|
|
934
|
+
throw new RangeError(`Unsupported component '${char}'.`);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function mergeNamespace(existing, value)
|
|
939
|
+
{
|
|
940
|
+
if (!existing) return cloneSchemaValue(value);
|
|
941
|
+
if (isPlainObject(existing) && isPlainObject(value)) return Object.freeze({ ...existing, ...value });
|
|
942
|
+
return cloneSchemaValue(value);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Resolves @schema.enum("X") through the owning class's PascalCase static so
|
|
946
|
+
// exported schemas are self-describing: adds the class-scoped identity and a
|
|
947
|
+
// reference to the frozen member map when the static resolves.
|
|
948
|
+
function enrichEnumField(exported, Constructor)
|
|
949
|
+
{
|
|
950
|
+
const enumType = exported?.enum?.enumType;
|
|
951
|
+
if (!enumType) return exported;
|
|
952
|
+
const members = Constructor?.[enumType];
|
|
953
|
+
if (!members || typeof members !== "object") return exported;
|
|
954
|
+
|
|
955
|
+
let owner = Constructor;
|
|
956
|
+
let current = Constructor;
|
|
957
|
+
while (typeof current === "function")
|
|
958
|
+
{
|
|
959
|
+
if (Object.prototype.hasOwnProperty.call(current, enumType))
|
|
960
|
+
{
|
|
961
|
+
owner = current;
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
current = Object.getPrototypeOf(current);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return Object.freeze({
|
|
968
|
+
...exported,
|
|
969
|
+
enum: Object.freeze({
|
|
970
|
+
...exported.enum,
|
|
971
|
+
identity: `${CjsSchema.getClassName(owner) || owner.name}.${enumType}`,
|
|
972
|
+
members
|
|
973
|
+
})
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function exportField(field, namespaces)
|
|
978
|
+
{
|
|
979
|
+
const result = {
|
|
980
|
+
name: field.name
|
|
981
|
+
};
|
|
982
|
+
|
|
983
|
+
for (const [key, value] of Object.entries(field))
|
|
984
|
+
{
|
|
985
|
+
if (key === "name") continue;
|
|
986
|
+
if (namespaces && !namespaces.has(key)) continue;
|
|
987
|
+
result[key] = cloneSchemaValue(value);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
return Object.freeze(result);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function normalizeNamespaces(namespaces)
|
|
994
|
+
{
|
|
995
|
+
if (!namespaces) return null;
|
|
996
|
+
return new Set(Array.isArray(namespaces) ? namespaces : [namespaces]);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function cloneSchemaValue(value)
|
|
1000
|
+
{
|
|
1001
|
+
if (Array.isArray(value)) return Object.freeze(value.map(cloneSchemaValue));
|
|
1002
|
+
if (isPlainObject(value)) return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneSchemaValue(item)])));
|
|
1003
|
+
return value;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function isPlainObject(value)
|
|
1007
|
+
{
|
|
1008
|
+
return Boolean(value) && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
|
|
1009
|
+
}
|