@carbonenginejs/runtime-utils 0.1.1 → 0.1.3
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 -21
- package/NOTICE +23 -23
- package/README.md +69 -69
- package/THIRD-PARTY-NOTICES.md +64 -64
- package/docs/README.md +74 -73
- package/docs/architecture.md +101 -101
- package/docs/concepts/foundation-consolidation.md +85 -85
- package/docs/concepts/model-lifecycle.md +111 -0
- package/docs/const-kb.md +85 -87
- package/docs/core-types/DECORATOR-TODOS.md +25 -25
- package/docs/core-types/README.md +229 -168
- package/docs/reference/api.md +105 -102
- package/docs/reference/classes/README.md +135 -135
- package/package.json +139 -139
- package/src/arrays.js +5 -5
- package/src/audio/audioFormats.js +34 -34
- package/src/audio/index.js +1 -1
- package/src/box3.js +331 -331
- package/src/bytes.js +56 -56
- package/src/compression.js +56 -56
- package/src/constants/index.js +7 -7
- package/src/constants/trinity.js +13 -13
- package/src/constants.js +15 -15
- package/src/curve.js +79 -79
- package/src/d3d/dxgiFormats.js +46 -46
- package/src/d3d/index.js +2 -2
- package/src/d3d/primitiveTopology.js +11 -11
- package/src/document/CjsCarbonDocument.js +212 -212
- package/src/document/CjsClassRegistry.js +373 -373
- package/src/document/CjsDocumentDehydrator.js +142 -142
- package/src/document/CjsDocumentHydrator.js +156 -156
- package/src/document/CjsStructRegistry.js +348 -348
- package/src/document/hydrationAdapter.js +129 -129
- package/src/document/index.js +6 -6
- package/src/errors/CjsError.js +286 -286
- package/src/errors/index.js +5 -5
- package/src/geometry/box.js +22 -22
- package/src/geometry/cylinder.js +22 -22
- package/src/geometry/helpers/earcut.js +1 -1
- package/src/geometry/helpers/misc.js +3 -3
- package/src/geometry/index.js +8 -8
- package/src/geometry/json.js +67 -67
- package/src/geometry/lathe.js +44 -44
- package/src/geometry/plane.js +14 -14
- package/src/geometry/shape.js +4 -4
- package/src/geometry/sphere.js +24 -24
- package/src/geometry/torus.js +14 -14
- package/src/graphics/colorSpaces.js +22 -22
- package/src/graphics/index.js +4 -4
- package/src/graphics/pixelFormats.js +158 -158
- package/src/graphics/textureDimensions.js +22 -22
- package/src/graphics/trinityEnums.js +87 -87
- package/src/index.js +62 -62
- package/src/is.js +108 -46
- package/src/json.js +23 -23
- package/src/lifecycle/CjsLifecycleState.js +77 -77
- package/src/lifecycle/index.js +1 -1
- package/src/lne3.js +70 -70
- package/src/lookup.js +48 -48
- package/src/mat3.js +51 -51
- package/src/mat4.js +699 -699
- package/src/math/index.js +25 -25
- package/src/math/scalar.js +63 -63
- package/src/media/index.js +1 -1
- package/src/media/mediaTypes.js +50 -50
- package/src/mesh.js +424 -424
- package/src/model/CjsEventEmitter.js +333 -333
- package/src/model/CjsModel.js +1589 -1544
- package/src/model/CjsModelState.js +72 -72
- package/src/model/index.js +4 -4
- package/src/model/sourceRecordUtils.js +54 -54
- package/src/noise.js +310 -310
- package/src/num.js +827 -827
- package/src/object.js +39 -39
- package/src/path.js +53 -53
- package/src/pln.js +125 -125
- package/src/pool.js +9 -9
- package/src/quat.js +144 -144
- package/src/ray3.js +188 -188
- package/src/renderContext/formats.js +145 -145
- package/src/renderContext/index.js +5 -5
- package/src/renderContext/presentation.js +125 -125
- package/src/renderContext/resources.js +27 -27
- package/src/renderContext/upscaling.js +22 -22
- package/src/renderContext/window.js +20 -20
- package/src/runtime/CjsRuntimeState.js +50 -50
- package/src/schema/CjsSchema.js +555 -318
- package/src/schema/index.js +4 -4
- package/src/shader/index.js +1 -1
- package/src/shader/shaderStages.js +37 -37
- package/src/sph3.js +181 -181
- package/src/tangent.js +288 -288
- package/src/text.js +40 -40
- package/src/tri3.js +98 -98
- package/src/types/carbonTypes.js +635 -635
- package/src/types/index.js +2 -2
- package/src/utils.js +58 -58
- package/src/validation.js +46 -46
- package/src/vec2.js +229 -229
- package/src/vec3.js +1188 -1172
- package/src/vec4.js +347 -347
- package/src/vertex.js +108 -108
- package/src/webgpu/index.js +1 -1
- package/src/webgpu/textureFormats.js +121 -121
package/src/schema/CjsSchema.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
const CLASS_SCHEMA = new WeakMap();
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const CLASS_SCHEMA = new WeakMap();
|
|
2
|
+
|
|
3
|
+
// Exported schemas, memoized per class. SCHEMA_GENERATION is bumped by every
|
|
4
|
+
// metadata definition (see getOrCreateClassSchema), which is what makes a stale
|
|
5
|
+
// memo detectable without tracking which subclasses a base class change reaches.
|
|
6
|
+
const SCHEMA_EXPORTS = new WeakMap();
|
|
7
|
+
let SCHEMA_GENERATION = 0;
|
|
8
|
+
|
|
9
|
+
const CONSTRUCTOR_BY_NAME = new Map();
|
|
10
|
+
const ENUM_SCHEMA_BY_NAME = new Map();
|
|
11
|
+
const ENUM_SCHEMA_BY_OBJECT = new WeakMap();
|
|
12
|
+
const STAGE3_FIELD_METADATA = Symbol("carbonenginejs.schema.stage3Fields");
|
|
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");
|
|
6
18
|
|
|
7
19
|
export const CJS_ENUM_NAME = Symbol.for("carbonenginejs.enum.name");
|
|
8
20
|
|
|
@@ -56,32 +68,32 @@ export class CjsSchema
|
|
|
56
68
|
return this;
|
|
57
69
|
}
|
|
58
70
|
|
|
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
|
-
}
|
|
71
|
+
static defineMethod(Constructor, methodName, namespace, value)
|
|
72
|
+
{
|
|
73
|
+
defineMethodMetadata(Constructor, methodName, namespace, value);
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static getField(Constructor, fieldName)
|
|
78
|
+
{
|
|
79
|
+
return getEffectiveFields(Constructor).find(field => field.name === fieldName) || null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Excludes named inherited fields from the decorated class's schema surface.
|
|
84
|
+
*/
|
|
85
|
+
static hideInherited(fieldNames)
|
|
86
|
+
{
|
|
87
|
+
return hiddenInheritedFieldsDecorator(normalizeHiddenInheritedFields(fieldNames));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether a field is hidden from a class by its inheritance chain.
|
|
92
|
+
*/
|
|
93
|
+
static isFieldHidden(Constructor, fieldName)
|
|
94
|
+
{
|
|
95
|
+
return getHiddenInheritedFieldNames(Constructor).has(fieldName);
|
|
96
|
+
}
|
|
85
97
|
|
|
86
98
|
static getMethod(Constructor, methodName)
|
|
87
99
|
{
|
|
@@ -128,6 +140,9 @@ export class CjsSchema
|
|
|
128
140
|
}
|
|
129
141
|
|
|
130
142
|
CONSTRUCTOR_BY_NAME.set(name.trim(), Constructor);
|
|
143
|
+
// Buckets resolve class references by name, so a late registration
|
|
144
|
+
// changes how already-built schemas should have been bucketed.
|
|
145
|
+
SCHEMA_GENERATION += 1;
|
|
131
146
|
return this;
|
|
132
147
|
}
|
|
133
148
|
|
|
@@ -151,49 +166,40 @@ export class CjsSchema
|
|
|
151
166
|
return name ? ENUM_SCHEMA_BY_NAME.get(name) || null : null;
|
|
152
167
|
}
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
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
|
-
}
|
|
169
|
+
/**
|
|
170
|
+
* Return the exported schema for a class.
|
|
171
|
+
*
|
|
172
|
+
* The schema is the precomputed answer - collapsing the inheritance
|
|
173
|
+
* lineage and merging metadata - so building it per call would defeat its
|
|
174
|
+
* purpose. Callers traverse model graphs and ask once per node, so this is
|
|
175
|
+
* memoized per class and rebuilt only when class metadata is defined.
|
|
176
|
+
*
|
|
177
|
+
* Namespace-filtered exports are not memoized: they are a projection of the
|
|
178
|
+
* full schema requested by tooling, not the hot read path.
|
|
179
|
+
*
|
|
180
|
+
* The result is shared, not copied - treat it as read-only. It is not
|
|
181
|
+
* frozen: deep-cloning and freezing every field on the way out cost far
|
|
182
|
+
* more than the mistakes it guarded against.
|
|
183
|
+
*
|
|
184
|
+
* @param {Function} Constructor
|
|
185
|
+
* @param {object} [options={}]
|
|
186
|
+
* @param {string|Array<string>} [options.namespaces] Restricts exported metadata namespaces.
|
|
187
|
+
* @returns {object} Shared schema export.
|
|
188
|
+
*/
|
|
189
|
+
static getSchema(Constructor, options = {})
|
|
190
|
+
{
|
|
191
|
+
const namespaces = normalizeNamespaces(options.namespaces);
|
|
192
|
+
if (namespaces) return buildSchema(Constructor, namespaces);
|
|
191
193
|
|
|
192
|
-
|
|
194
|
+
const memo = SCHEMA_EXPORTS.get(Constructor);
|
|
195
|
+
if (memo && memo.generation === SCHEMA_GENERATION) return memo.schema;
|
|
193
196
|
|
|
194
|
-
|
|
197
|
+
const schema = buildSchema(Constructor, null);
|
|
198
|
+
SCHEMA_EXPORTS.set(Constructor, { generation: SCHEMA_GENERATION, schema });
|
|
199
|
+
return schema;
|
|
195
200
|
}
|
|
196
201
|
|
|
202
|
+
|
|
197
203
|
static type = Object.freeze({
|
|
198
204
|
array: itemType => fieldDecorator("type", { kind: "array", itemType }),
|
|
199
205
|
boolean: fieldDecorator("type", { kind: "boolean" }),
|
|
@@ -307,7 +313,8 @@ export class CjsSchema
|
|
|
307
313
|
contextual: true,
|
|
308
314
|
contextTiers: Object.freeze(normalized)
|
|
309
315
|
});
|
|
310
|
-
|
|
316
|
+
const described = getDecoratorMetadata(base);
|
|
317
|
+
return describeDecorator(function contextualMethodDecorator(targetOrValue, contextOrMethodName)
|
|
311
318
|
{
|
|
312
319
|
// Contextual methods are validated context-first at decoration
|
|
313
320
|
// time: the first declared parameter must be the frame context.
|
|
@@ -320,7 +327,7 @@ export class CjsSchema
|
|
|
320
327
|
assertContextFirstMethod(targetOrValue[contextOrMethodName], contextOrMethodName);
|
|
321
328
|
}
|
|
322
329
|
return base(targetOrValue, contextOrMethodName);
|
|
323
|
-
};
|
|
330
|
+
}, described.namespace, described.value);
|
|
324
331
|
}
|
|
325
332
|
});
|
|
326
333
|
|
|
@@ -338,22 +345,46 @@ function createComponentsNamespace()
|
|
|
338
345
|
return Object.freeze(components);
|
|
339
346
|
}
|
|
340
347
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
+
|
|
372
|
+
function fieldDecorator(namespace, value)
|
|
373
|
+
{
|
|
374
|
+
return describeDecorator(function schemaFieldDecorator(targetOrValue, contextOrFieldName)
|
|
375
|
+
{
|
|
376
|
+
if (contextOrFieldName && typeof contextOrFieldName === "object")
|
|
377
|
+
{
|
|
378
|
+
const context = contextOrFieldName;
|
|
379
|
+
if (context.kind !== "field") throw new TypeError("CjsSchema decorators only support class fields.");
|
|
380
|
+
recordStage3FieldMetadata(context, namespace, value);
|
|
381
|
+
|
|
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.
|
|
357
388
|
context.addInitializer(function initializeSchemaField()
|
|
358
389
|
{
|
|
359
390
|
defineFieldMetadata(this.constructor, context.name, namespace, value);
|
|
@@ -373,47 +404,47 @@ function fieldDecorator(namespace, value)
|
|
|
373
404
|
}
|
|
374
405
|
|
|
375
406
|
defineFieldMetadata(Constructor, contextOrFieldName, namespace, value);
|
|
376
|
-
};
|
|
407
|
+
}, namespace, value);
|
|
377
408
|
}
|
|
378
409
|
|
|
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
|
-
}
|
|
410
|
+
function classDefinitionDecorator(definition)
|
|
411
|
+
{
|
|
412
|
+
return function schemaClassDefinitionDecorator(value, context)
|
|
413
|
+
{
|
|
414
|
+
if (context && typeof context === "object")
|
|
415
|
+
{
|
|
416
|
+
if (context.kind !== "class") throw new TypeError("CjsSchema type.define only supports classes.");
|
|
417
|
+
registerStage3FieldMetadata(value, context.metadata);
|
|
418
|
+
defineClassMetadata(value, normalizeClassDefinition(value, definition));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
390
421
|
|
|
391
422
|
if (typeof value !== "function")
|
|
392
423
|
{
|
|
393
424
|
throw new TypeError("CjsSchema type.define requires a class constructor.");
|
|
394
425
|
}
|
|
395
426
|
|
|
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
|
-
}
|
|
427
|
+
defineClassMetadata(value, normalizeClassDefinition(value, definition));
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function hiddenInheritedFieldsDecorator(fieldNames)
|
|
432
|
+
{
|
|
433
|
+
return function schemaHiddenInheritedFieldsDecorator(value, context)
|
|
434
|
+
{
|
|
435
|
+
if (context && typeof context === "object")
|
|
436
|
+
{
|
|
437
|
+
if (context.kind !== "class") throw new TypeError("CjsSchema.hideInherited only supports classes.");
|
|
438
|
+
registerStage3FieldMetadata(value, context.metadata);
|
|
439
|
+
}
|
|
440
|
+
else if (typeof value !== "function")
|
|
441
|
+
{
|
|
442
|
+
throw new TypeError("CjsSchema.hideInherited requires a class constructor.");
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
defineHiddenInheritedFields(value, fieldNames);
|
|
446
|
+
};
|
|
447
|
+
}
|
|
417
448
|
|
|
418
449
|
const CONTEXT_FIRST_PARAMETER = /^\(?\s*_?(context|updateContext)\b/;
|
|
419
450
|
|
|
@@ -436,7 +467,7 @@ function assertContextFirstMethod(fn, methodName)
|
|
|
436
467
|
|
|
437
468
|
function methodDecorator(namespace, value)
|
|
438
469
|
{
|
|
439
|
-
return function schemaMethodDecorator(targetOrValue, contextOrMethodName)
|
|
470
|
+
return describeDecorator(function schemaMethodDecorator(targetOrValue, contextOrMethodName)
|
|
440
471
|
{
|
|
441
472
|
if (contextOrMethodName && typeof contextOrMethodName === "object")
|
|
442
473
|
{
|
|
@@ -458,14 +489,14 @@ function methodDecorator(namespace, value)
|
|
|
458
489
|
}
|
|
459
490
|
|
|
460
491
|
defineMethodMetadata(Constructor, contextOrMethodName, namespace, value);
|
|
461
|
-
};
|
|
492
|
+
}, namespace, value);
|
|
462
493
|
}
|
|
463
494
|
|
|
464
495
|
function memberDecorator(namespace, value)
|
|
465
496
|
{
|
|
466
497
|
const forMethods = methodDecorator(namespace, value);
|
|
467
498
|
const forFields = fieldDecorator(namespace, value);
|
|
468
|
-
return function schemaMemberDecorator(targetOrValue, contextOrMemberName)
|
|
499
|
+
return describeDecorator(function schemaMemberDecorator(targetOrValue, contextOrMemberName)
|
|
469
500
|
{
|
|
470
501
|
if (contextOrMemberName && typeof contextOrMemberName === "object")
|
|
471
502
|
{
|
|
@@ -479,7 +510,7 @@ function memberDecorator(namespace, value)
|
|
|
479
510
|
return targetOrValue && contextOrMemberName && typeof targetOrValue[contextOrMemberName] === "function"
|
|
480
511
|
? forMethods(targetOrValue, contextOrMemberName)
|
|
481
512
|
: forFields(targetOrValue, contextOrMemberName);
|
|
482
|
-
};
|
|
513
|
+
}, namespace, value);
|
|
483
514
|
}
|
|
484
515
|
|
|
485
516
|
function defineFieldMetadata(Constructor, fieldName, namespace, value)
|
|
@@ -523,10 +554,10 @@ function defineMethodMetadata(Constructor, methodName, namespace, value)
|
|
|
523
554
|
defineMemberMetadata(Constructor, "methods", "methodsByName", methodName, namespace, value);
|
|
524
555
|
}
|
|
525
556
|
|
|
526
|
-
function defineMemberMetadata(Constructor, listKey, mapKey, name, namespace, value)
|
|
527
|
-
{
|
|
528
|
-
const schema = getOrCreateClassSchema(Constructor);
|
|
529
|
-
let item = schema[mapKey].get(name);
|
|
557
|
+
function defineMemberMetadata(Constructor, listKey, mapKey, name, namespace, value)
|
|
558
|
+
{
|
|
559
|
+
const schema = getOrCreateClassSchema(Constructor);
|
|
560
|
+
let item = schema[mapKey].get(name);
|
|
530
561
|
|
|
531
562
|
if (!item)
|
|
532
563
|
{
|
|
@@ -534,188 +565,324 @@ function defineMemberMetadata(Constructor, listKey, mapKey, name, namespace, val
|
|
|
534
565
|
schema[listKey].push(item);
|
|
535
566
|
schema[mapKey].set(name, item);
|
|
536
567
|
}
|
|
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
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
568
|
+
|
|
569
|
+
item[namespace] = mergeNamespace(item[namespace], value);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function defineHiddenInheritedFields(Constructor, fieldNames)
|
|
573
|
+
{
|
|
574
|
+
if (typeof Constructor !== "function")
|
|
575
|
+
{
|
|
576
|
+
throw new TypeError("CjsSchema.hideInherited requires a class constructor.");
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const Parent = Object.getPrototypeOf(Constructor);
|
|
580
|
+
const inheritedFields = new Set(getEffectiveFields(Parent).map(field => field.name));
|
|
581
|
+
// Declared name only: Constructor.name does not survive minification, and a
|
|
582
|
+
// mangled name in an error reads as a real one and sends you chasing it.
|
|
583
|
+
const className = CLASS_SCHEMA.get(Constructor)?.className || "<undeclared>";
|
|
584
|
+
|
|
585
|
+
for (const fieldName of fieldNames)
|
|
586
|
+
{
|
|
587
|
+
if (!inheritedFields.has(fieldName))
|
|
588
|
+
{
|
|
589
|
+
throw new TypeError(
|
|
590
|
+
`CjsSchema.hideInherited cannot hide "${fieldName}" on ${className}: ` +
|
|
591
|
+
"the parent schema does not expose that field."
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const schema = getOrCreateClassSchema(Constructor);
|
|
597
|
+
for (const fieldName of fieldNames)
|
|
598
|
+
{
|
|
599
|
+
schema.hiddenInherited.add(fieldName);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function getEffectiveFields(Constructor)
|
|
604
|
+
{
|
|
605
|
+
const ordered = [];
|
|
606
|
+
const byName = new Map();
|
|
607
|
+
const hidden = new Set();
|
|
608
|
+
|
|
609
|
+
for (const current of getSchemaLineage(Constructor))
|
|
610
|
+
{
|
|
611
|
+
const schema = CLASS_SCHEMA.get(current);
|
|
612
|
+
for (const field of schema?.fields || [])
|
|
613
|
+
{
|
|
614
|
+
const existing = byName.get(field.name);
|
|
615
|
+
if (existing)
|
|
616
|
+
{
|
|
617
|
+
mergeMemberMetadata(existing, field);
|
|
618
|
+
}
|
|
619
|
+
else
|
|
620
|
+
{
|
|
621
|
+
const merged = mergeMemberMetadata({ name: field.name }, field);
|
|
622
|
+
ordered.push(merged);
|
|
623
|
+
byName.set(field.name, merged);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
for (const fieldName of schema?.hiddenInherited || [])
|
|
628
|
+
{
|
|
629
|
+
hidden.add(fieldName);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return ordered.filter(field => !hidden.has(field.name));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function getHiddenInheritedFieldNames(Constructor)
|
|
637
|
+
{
|
|
638
|
+
const hidden = new Set();
|
|
639
|
+
for (const current of getSchemaLineage(Constructor))
|
|
640
|
+
{
|
|
641
|
+
for (const fieldName of CLASS_SCHEMA.get(current)?.hiddenInherited || [])
|
|
642
|
+
{
|
|
643
|
+
hidden.add(fieldName);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return hidden;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function getSchemaLineage(Constructor)
|
|
650
|
+
{
|
|
651
|
+
const lineage = [];
|
|
652
|
+
let current = Constructor;
|
|
653
|
+
while (typeof current === "function")
|
|
654
|
+
{
|
|
655
|
+
if (CLASS_SCHEMA.has(current)) lineage.push(current);
|
|
656
|
+
current = Object.getPrototypeOf(current);
|
|
657
|
+
}
|
|
658
|
+
return lineage.reverse();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function mergeMemberMetadata(target, source)
|
|
662
|
+
{
|
|
663
|
+
for (const [namespace, value] of Object.entries(source))
|
|
664
|
+
{
|
|
665
|
+
if (namespace === "name") continue;
|
|
666
|
+
target[namespace] = mergeNamespace(target[namespace], value);
|
|
667
|
+
}
|
|
668
|
+
return target;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function buildSchema(Constructor, namespaces)
|
|
672
|
+
{
|
|
673
|
+
const schema = CLASS_SCHEMA.get(Constructor);
|
|
674
|
+
const fields = [];
|
|
675
|
+
const methods = [];
|
|
676
|
+
|
|
677
|
+
for (const field of getEffectiveFields(Constructor))
|
|
678
|
+
{
|
|
679
|
+
fields.push(enrichEnumField(exportField(field, namespaces), Constructor));
|
|
680
|
+
}
|
|
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.
|
|
699
|
+
for (const method of schema?.methods || [])
|
|
700
|
+
{
|
|
701
|
+
methods.push(exportField(method, namespaces));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const result = {
|
|
705
|
+
className: CjsSchema.getClassName(Constructor),
|
|
706
|
+
fields
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
const family = schema?.family || CjsSchema.getClassFamily(Constructor);
|
|
710
|
+
if (family)
|
|
711
|
+
{
|
|
712
|
+
result.family = family;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (schema?.sourceClass && schema.sourceClass !== result.className)
|
|
716
|
+
{
|
|
717
|
+
result.sourceClass = schema.sourceClass;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (schema?.aliases?.length)
|
|
721
|
+
{
|
|
722
|
+
result.aliases = [ ...schema.aliases ];
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (methods.length) result.methods = methods;
|
|
726
|
+
|
|
727
|
+
addSchemaBuckets(result);
|
|
728
|
+
return result;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
// Kinds that hold many values rather than one. `map` and `set` are included
|
|
733
|
+
// because they are iterable collections of the referenced class, same as a list.
|
|
734
|
+
const MANY_KINDS = new Set([ "list", "array", "set", "map" ]);
|
|
735
|
+
|
|
736
|
+
// Kinds that can hold a child model. Bucketing on the KIND rather than on a
|
|
737
|
+
// resolvable class reference keeps traversal conservative: `type.struct(Class)`
|
|
738
|
+
// drops the reference during normalization, and a raw defineField may omit the
|
|
739
|
+
// item type, so requiring a reference would silently stop visiting those.
|
|
740
|
+
// Scalar and math kinds are excluded, which is where the saving comes from.
|
|
741
|
+
const MODEL_KINDS = new Set([
|
|
742
|
+
"struct", "model", "rawStruct", "objectRef", "unknown",
|
|
743
|
+
"list", "array", "set", "map"
|
|
744
|
+
]);
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Precompute the answers consumers would otherwise recompute per traversal.
|
|
749
|
+
*
|
|
750
|
+
* Graph walks ask the same two questions of every node - which fields hold
|
|
751
|
+
* child models, which hold resources - and answering them by scanning the field
|
|
752
|
+
* list and type-testing each value costs more than the walk itself. The class
|
|
753
|
+
* cannot change without rebuilding its schema, so this is solved once.
|
|
754
|
+
*/
|
|
755
|
+
function addSchemaBuckets(schema)
|
|
756
|
+
{
|
|
757
|
+
const byName = new Map();
|
|
758
|
+
const children = [];
|
|
759
|
+
const resources = [];
|
|
760
|
+
|
|
761
|
+
for (const field of schema.fields)
|
|
762
|
+
{
|
|
763
|
+
byName.set(field.name, field);
|
|
764
|
+
|
|
765
|
+
const type = field.type;
|
|
766
|
+
// An undeclared field could hold anything, so it stays traversable.
|
|
767
|
+
const kind = type?.kind;
|
|
768
|
+
if (kind && !MODEL_KINDS.has(kind)) continue;
|
|
769
|
+
|
|
770
|
+
const entry = { name: field.name, many: MANY_KINDS.has(kind) };
|
|
771
|
+
|
|
772
|
+
if (type && resolveFieldClass(type)?.isResource === true)
|
|
773
|
+
{
|
|
774
|
+
resources.push(entry);
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
entry.owned = field.io?.ownership === "owned";
|
|
779
|
+
children.push(entry);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
schema.byName = byName;
|
|
783
|
+
schema.children = children;
|
|
784
|
+
schema.resources = resources;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
// Only string references survive normalization - type.struct(SomeClass) drops
|
|
789
|
+
// the reference entirely - so a field can only be bucketed when it names a
|
|
790
|
+
// class. The one field in the tree that named nothing was a missing
|
|
791
|
+
// declaration, not a deliberate escape.
|
|
792
|
+
function resolveFieldClass(type)
|
|
793
|
+
{
|
|
794
|
+
const ref = type.className || type.itemType || type.valueType;
|
|
795
|
+
return typeof ref === "string" && ref ? CONSTRUCTOR_BY_NAME.get(ref) || null : null;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
function getOrCreateClassSchema(Constructor)
|
|
800
|
+
{
|
|
801
|
+
// The only route by which class metadata is mutated, and therefore the only
|
|
802
|
+
// place exported schemas can go stale. A single global counter rather than
|
|
803
|
+
// per-class invalidation because a base class change invalidates every
|
|
804
|
+
// subclass, and lineage is not tracked in reverse.
|
|
805
|
+
SCHEMA_GENERATION += 1;
|
|
806
|
+
|
|
807
|
+
let schema = CLASS_SCHEMA.get(Constructor);
|
|
808
|
+
if (!schema)
|
|
642
809
|
{
|
|
643
810
|
schema = {
|
|
644
811
|
className: null,
|
|
645
812
|
family: null,
|
|
646
813
|
sourceClass: null,
|
|
647
|
-
aliases: null,
|
|
648
|
-
fields: [],
|
|
649
|
-
fieldsByName: new Map(),
|
|
650
|
-
hiddenInherited: new Set(),
|
|
651
|
-
methods: [],
|
|
652
|
-
methodsByName: new Map()
|
|
653
|
-
};
|
|
814
|
+
aliases: null,
|
|
815
|
+
fields: [],
|
|
816
|
+
fieldsByName: new Map(),
|
|
817
|
+
hiddenInherited: new Set(),
|
|
818
|
+
methods: [],
|
|
819
|
+
methodsByName: new Map()
|
|
820
|
+
};
|
|
654
821
|
CLASS_SCHEMA.set(Constructor, schema);
|
|
655
822
|
}
|
|
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
|
-
{
|
|
823
|
+
return schema;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function normalizeHiddenInheritedFields(fieldNames)
|
|
827
|
+
{
|
|
828
|
+
if (!Array.isArray(fieldNames) || !fieldNames.length)
|
|
829
|
+
{
|
|
830
|
+
throw new TypeError("CjsSchema.hideInherited requires a non-empty array of field names.");
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const normalized = fieldNames.map((fieldName, index) =>
|
|
834
|
+
{
|
|
835
|
+
if (typeof fieldName !== "string" || !fieldName.trim())
|
|
836
|
+
{
|
|
837
|
+
throw new TypeError(`CjsSchema.hideInherited fieldNames[${index}] must be a non-empty string.`);
|
|
838
|
+
}
|
|
839
|
+
return fieldName.trim();
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
return Object.freeze([...new Set(normalized)]);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function recordStage3FieldMetadata(context, namespace, value)
|
|
846
|
+
{
|
|
847
|
+
const metadata = context?.metadata;
|
|
848
|
+
if (!metadata || typeof metadata !== "object") return;
|
|
849
|
+
|
|
850
|
+
let fields;
|
|
851
|
+
if (Object.prototype.hasOwnProperty.call(metadata, STAGE3_FIELD_METADATA))
|
|
852
|
+
{
|
|
853
|
+
fields = metadata[STAGE3_FIELD_METADATA];
|
|
854
|
+
}
|
|
855
|
+
else
|
|
856
|
+
{
|
|
857
|
+
fields = [];
|
|
858
|
+
Object.defineProperty(metadata, STAGE3_FIELD_METADATA, {
|
|
859
|
+
configurable: false,
|
|
860
|
+
enumerable: false,
|
|
861
|
+
value: fields,
|
|
862
|
+
writable: false
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
fields.push({
|
|
867
|
+
name: context.name,
|
|
868
|
+
namespace,
|
|
869
|
+
value
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function registerStage3FieldMetadata(Constructor, metadata)
|
|
874
|
+
{
|
|
875
|
+
if (!metadata || typeof metadata !== "object") return;
|
|
876
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, STAGE3_FIELD_METADATA)) return;
|
|
877
|
+
|
|
878
|
+
for (const field of metadata[STAGE3_FIELD_METADATA])
|
|
879
|
+
{
|
|
880
|
+
defineFieldMetadata(Constructor, field.name, field.namespace, field.value);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function normalizeClassDefinition(Constructor, definition)
|
|
885
|
+
{
|
|
719
886
|
if (typeof definition === "string")
|
|
720
887
|
{
|
|
721
888
|
definition = { className: definition };
|
|
@@ -740,27 +907,98 @@ function normalizeClassDefinition(Constructor, definition)
|
|
|
740
907
|
return result;
|
|
741
908
|
}
|
|
742
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
|
+
*/
|
|
743
923
|
function normalizeManualMembers(members, memberType)
|
|
744
924
|
{
|
|
745
925
|
if (members === undefined || members === null) return [];
|
|
746
|
-
|
|
926
|
+
|
|
927
|
+
if (Array.isArray(members))
|
|
747
928
|
{
|
|
748
|
-
|
|
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
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (!isPlainObject(members))
|
|
941
|
+
{
|
|
942
|
+
throw new TypeError(
|
|
943
|
+
`CjsSchema.define ${memberType} must be a name-keyed object or an array of named records.`
|
|
944
|
+
);
|
|
749
945
|
}
|
|
750
946
|
|
|
751
|
-
return members.map((
|
|
947
|
+
return Object.entries(members).map(([ name, definition ]) =>
|
|
752
948
|
{
|
|
753
|
-
if (!
|
|
949
|
+
if (!name.trim())
|
|
754
950
|
{
|
|
755
|
-
throw new TypeError(`CjsSchema.define ${memberType}
|
|
951
|
+
throw new TypeError(`CjsSchema.define ${memberType} requires a non-empty name.`);
|
|
756
952
|
}
|
|
757
|
-
return
|
|
758
|
-
...member,
|
|
759
|
-
name: member.name.trim()
|
|
760
|
-
};
|
|
953
|
+
return normalizeManualMember(name.trim(), definition, memberType);
|
|
761
954
|
});
|
|
762
955
|
}
|
|
763
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
|
+
|
|
764
1002
|
function registerClassMetadata(Constructor, schema)
|
|
765
1003
|
{
|
|
766
1004
|
if (!Constructor || !schema?.className) return;
|
|
@@ -937,9 +1175,8 @@ function componentIndex(char)
|
|
|
937
1175
|
|
|
938
1176
|
function mergeNamespace(existing, value)
|
|
939
1177
|
{
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
return cloneSchemaValue(value);
|
|
1178
|
+
if (isPlainObject(existing) && isPlainObject(value)) return { ...existing, ...value };
|
|
1179
|
+
return value;
|
|
943
1180
|
}
|
|
944
1181
|
|
|
945
1182
|
// Resolves @schema.enum("X") through the owning class's PascalCase static so
|
|
@@ -984,10 +1221,10 @@ function exportField(field, namespaces)
|
|
|
984
1221
|
{
|
|
985
1222
|
if (key === "name") continue;
|
|
986
1223
|
if (namespaces && !namespaces.has(key)) continue;
|
|
987
|
-
result[key] =
|
|
1224
|
+
result[key] = value;
|
|
988
1225
|
}
|
|
989
1226
|
|
|
990
|
-
return
|
|
1227
|
+
return result;
|
|
991
1228
|
}
|
|
992
1229
|
|
|
993
1230
|
function normalizeNamespaces(namespaces)
|