@matter/model 0.11.5-alpha.0-20241112-858f60251 → 0.11.5-alpha.0-20241114-6af665cdd

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 (91) hide show
  1. package/dist/cjs/aspects/Access.d.ts.map +1 -1
  2. package/dist/cjs/aspects/Access.js +1 -0
  3. package/dist/cjs/aspects/Access.js.map +1 -1
  4. package/dist/cjs/aspects/Aspect.d.ts +2 -2
  5. package/dist/cjs/aspects/Aspect.d.ts.map +1 -1
  6. package/dist/cjs/aspects/Aspect.js.map +1 -1
  7. package/dist/cjs/aspects/Conformance.d.ts +6 -3
  8. package/dist/cjs/aspects/Conformance.d.ts.map +1 -1
  9. package/dist/cjs/aspects/Conformance.js +9 -8
  10. package/dist/cjs/aspects/Conformance.js.map +1 -1
  11. package/dist/cjs/aspects/Constraint.d.ts +1 -1
  12. package/dist/cjs/aspects/Constraint.d.ts.map +1 -1
  13. package/dist/cjs/aspects/Constraint.js +1 -0
  14. package/dist/cjs/aspects/Constraint.js.map +1 -1
  15. package/dist/cjs/aspects/Quality.d.ts.map +1 -1
  16. package/dist/cjs/aspects/Quality.js +1 -0
  17. package/dist/cjs/aspects/Quality.js.map +1 -1
  18. package/dist/cjs/logic/ModelTraversal.d.ts.map +1 -1
  19. package/dist/cjs/logic/ModelTraversal.js +25 -15
  20. package/dist/cjs/logic/ModelTraversal.js.map +1 -1
  21. package/dist/cjs/logic/definition-validation/ValueValidator.js +2 -2
  22. package/dist/cjs/logic/definition-validation/ValueValidator.js.map +1 -1
  23. package/dist/cjs/models/Aspects.d.ts +3 -2
  24. package/dist/cjs/models/Aspects.d.ts.map +1 -1
  25. package/dist/cjs/models/Aspects.js +18 -2
  26. package/dist/cjs/models/Aspects.js.map +1 -1
  27. package/dist/cjs/models/ClusterModel.d.ts +0 -1
  28. package/dist/cjs/models/ClusterModel.d.ts.map +1 -1
  29. package/dist/cjs/models/ClusterModel.js +0 -4
  30. package/dist/cjs/models/ClusterModel.js.map +1 -1
  31. package/dist/cjs/models/Model.d.ts +11 -6
  32. package/dist/cjs/models/Model.d.ts.map +1 -1
  33. package/dist/cjs/models/Model.js +23 -4
  34. package/dist/cjs/models/Model.js.map +1 -1
  35. package/dist/cjs/models/ValueModel.d.ts +0 -1
  36. package/dist/cjs/models/ValueModel.d.ts.map +1 -1
  37. package/dist/cjs/models/ValueModel.js +0 -7
  38. package/dist/cjs/models/ValueModel.js.map +1 -1
  39. package/dist/cjs/standard/elements/LevelControl.js +2 -2
  40. package/dist/esm/aspects/Access.d.ts.map +1 -1
  41. package/dist/esm/aspects/Access.js +1 -0
  42. package/dist/esm/aspects/Access.js.map +1 -1
  43. package/dist/esm/aspects/Aspect.d.ts +2 -2
  44. package/dist/esm/aspects/Aspect.d.ts.map +1 -1
  45. package/dist/esm/aspects/Aspect.js.map +1 -1
  46. package/dist/esm/aspects/Conformance.d.ts +6 -3
  47. package/dist/esm/aspects/Conformance.d.ts.map +1 -1
  48. package/dist/esm/aspects/Conformance.js +9 -8
  49. package/dist/esm/aspects/Conformance.js.map +1 -1
  50. package/dist/esm/aspects/Constraint.d.ts +1 -1
  51. package/dist/esm/aspects/Constraint.d.ts.map +1 -1
  52. package/dist/esm/aspects/Constraint.js +1 -0
  53. package/dist/esm/aspects/Constraint.js.map +1 -1
  54. package/dist/esm/aspects/Quality.d.ts.map +1 -1
  55. package/dist/esm/aspects/Quality.js +1 -0
  56. package/dist/esm/aspects/Quality.js.map +1 -1
  57. package/dist/esm/logic/ModelTraversal.d.ts.map +1 -1
  58. package/dist/esm/logic/ModelTraversal.js +25 -15
  59. package/dist/esm/logic/ModelTraversal.js.map +1 -1
  60. package/dist/esm/logic/definition-validation/ValueValidator.js +2 -2
  61. package/dist/esm/logic/definition-validation/ValueValidator.js.map +1 -1
  62. package/dist/esm/models/Aspects.d.ts +3 -2
  63. package/dist/esm/models/Aspects.d.ts.map +1 -1
  64. package/dist/esm/models/Aspects.js +18 -2
  65. package/dist/esm/models/Aspects.js.map +1 -1
  66. package/dist/esm/models/ClusterModel.d.ts +0 -1
  67. package/dist/esm/models/ClusterModel.d.ts.map +1 -1
  68. package/dist/esm/models/ClusterModel.js +0 -4
  69. package/dist/esm/models/ClusterModel.js.map +1 -1
  70. package/dist/esm/models/Model.d.ts +11 -6
  71. package/dist/esm/models/Model.d.ts.map +1 -1
  72. package/dist/esm/models/Model.js +23 -4
  73. package/dist/esm/models/Model.js.map +1 -1
  74. package/dist/esm/models/ValueModel.d.ts +0 -1
  75. package/dist/esm/models/ValueModel.d.ts.map +1 -1
  76. package/dist/esm/models/ValueModel.js +0 -7
  77. package/dist/esm/models/ValueModel.js.map +1 -1
  78. package/dist/esm/standard/elements/LevelControl.js +2 -2
  79. package/package.json +4 -4
  80. package/src/aspects/Access.ts +2 -0
  81. package/src/aspects/Aspect.ts +2 -2
  82. package/src/aspects/Conformance.ts +16 -9
  83. package/src/aspects/Constraint.ts +3 -1
  84. package/src/aspects/Quality.ts +2 -0
  85. package/src/logic/ModelTraversal.ts +30 -15
  86. package/src/logic/definition-validation/ValueValidator.ts +4 -4
  87. package/src/models/Aspects.ts +29 -4
  88. package/src/models/ClusterModel.ts +0 -5
  89. package/src/models/Model.ts +28 -7
  90. package/src/models/ValueModel.ts +0 -8
  91. package/src/standard/elements/LevelControl.ts +2 -2
@@ -4,19 +4,40 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import type { Aspect } from "#aspects/Aspect.js";
7
8
  import { ModelTraversal } from "../logic/ModelTraversal.js";
8
9
  import { Model } from "./Model.js";
9
10
 
11
+ // Aspects are immutable, there are not many permutations, and their definitions are largely normalized strings. So we
12
+ // cache them to keep the object count down
13
+ const aspectCache: Record<string, Record<string, Aspect>> = {
14
+ Access: {},
15
+ Conformance: {},
16
+ Constraint: {},
17
+ Quality: {},
18
+ };
19
+
20
+ const emptyAspects: Record<string, Aspect> = {};
21
+
10
22
  export namespace Aspects {
11
- export function getAspect<T>(model: Model, symbol: symbol, constructor: new (definition: any) => T) {
12
- return ((model as any)[symbol] as T) || ((model as any)[symbol] = new constructor(undefined));
23
+ export function getAspect<T extends Aspect>(model: Model, symbol: symbol, constructor: new (definition: any) => T) {
24
+ const aspect = (model as any)[symbol] ?? emptyAspects[constructor.name];
25
+ if (aspect) {
26
+ return aspect as T;
27
+ }
28
+ return (emptyAspects[constructor.name] = new constructor(undefined));
13
29
  }
14
30
 
15
31
  export function setAspect(model: Model, symbol: symbol, constructor: new (definition: any) => any, value: any) {
16
32
  if (value instanceof constructor) {
17
33
  (model as any)[symbol] = value;
18
34
  } else {
19
- (model as any)[symbol] = new constructor(value);
35
+ const cacheKey = typeof value === "string" ? value : JSON.stringify(value);
36
+ let aspect = aspectCache[constructor.name][value];
37
+ if (!aspect) {
38
+ aspect = aspectCache[constructor.name][cacheKey] = new constructor(value);
39
+ }
40
+ (model as any)[symbol] = aspect;
20
41
  }
21
42
  }
22
43
 
@@ -29,7 +50,11 @@ export namespace Aspects {
29
50
  }
30
51
  }
31
52
 
32
- export function getEffectiveAspect<T>(model: Model, symbol: symbol, constructor: new (definition: any) => T) {
53
+ export function getEffectiveAspect<T extends Aspect>(
54
+ model: Model,
55
+ symbol: symbol,
56
+ constructor: new (definition: any) => T,
57
+ ) {
33
58
  const aspect = new ModelTraversal().findAspect(model, symbol) as T | undefined;
34
59
  if (aspect) {
35
60
  return aspect;
@@ -202,11 +202,6 @@ export class ClusterModel extends Model<ClusterElement> implements ClusterElemen
202
202
  return result as ClusterElement;
203
203
  }
204
204
 
205
- override freeze() {
206
- this.quality.freeze();
207
- super.freeze();
208
- }
209
-
210
205
  constructor(definition: ClusterElement.Properties) {
211
206
  super(definition);
212
207
 
@@ -32,6 +32,7 @@ export abstract class Model<T extends BaseElement = BaseElement> {
32
32
 
33
33
  #id?: number = undefined;
34
34
  #name: string;
35
+ #frozen?: boolean;
35
36
 
36
37
  /**
37
38
  * Indicates that an element may have type definitions as children.
@@ -44,13 +45,19 @@ export abstract class Model<T extends BaseElement = BaseElement> {
44
45
  isType?: boolean;
45
46
 
46
47
  /**
47
- * Normally {@link base} performs lookup based on {@link type}. If instead a model is installed it is used as the
48
- * base.
48
+ * Normally {@link base} performs lookup based on {@link type}. Setting this field short circuits this resolution.
49
49
  *
50
50
  * The operational base also enables resolution from the operational base's tree. This enables resolution on
51
51
  * operational models that are not installed in a parent hierarchy.
52
+ *
53
+ * We set the operational base when freezing the model.
52
54
  */
53
- operationalBase?: Model;
55
+ operationalBase?: Model | null;
56
+
57
+ /**
58
+ * This is like {@link operationalBase} except for the element's shadow.
59
+ */
60
+ operationalShadow?: Model | null;
54
61
 
55
62
  #children?: Children<Model>;
56
63
  #parent?: Model;
@@ -224,14 +231,20 @@ export abstract class Model<T extends BaseElement = BaseElement> {
224
231
  /**
225
232
  * Get a model for my base type as defined by {@link type}, if any.
226
233
  */
227
- get base() {
234
+ get base(): Model | undefined {
235
+ if (this.operationalBase !== undefined) {
236
+ return this.operationalBase ?? undefined;
237
+ }
228
238
  return new ModelTraversal().findBase(this);
229
239
  }
230
240
 
231
241
  /**
232
242
  * Get shadow model, if any. A "shadow" is an element in my parent's inheritance hierarchy that I override.
233
243
  */
234
- get shadow() {
244
+ get shadow(): Model | undefined {
245
+ if (this.operationalShadow !== undefined) {
246
+ return this.operationalShadow ?? undefined;
247
+ }
235
248
  return new ModelTraversal().findShadow(this);
236
249
  }
237
250
 
@@ -453,14 +466,22 @@ export abstract class Model<T extends BaseElement = BaseElement> {
453
466
  * Freeze the model hierarchy rooted at this model.
454
467
  *
455
468
  * When using a model as operational schema we implement various optimizations that assume the schema is immutable.
456
- * This function enforces that assumption.
469
+ * This function enforces that assumption and caches a few values that only make sense with frozen schema.
457
470
  *
458
471
  * To make changes to a frozen model use {@link clone}.
459
472
  */
460
473
  freeze() {
474
+ if (this.#frozen) {
475
+ return;
476
+ }
477
+
478
+ const base = this.operationalBase ?? (this.operationalBase = this.base ?? null);
479
+ const shadow = this.operationalShadow ?? (this.operationalShadow = this.shadow ?? null);
480
+ this.#frozen = true;
461
481
  Object.freeze(this);
462
482
  this.children.freeze();
463
- this.base?.freeze();
483
+ base?.freeze();
484
+ shadow?.freeze();
464
485
  }
465
486
 
466
487
  toString() {
@@ -304,14 +304,6 @@ export abstract class ValueModel<T extends ValueElement = ValueElement> extends
304
304
  return result as T;
305
305
  }
306
306
 
307
- override freeze() {
308
- this.constraint.freeze();
309
- this.conformance.freeze();
310
- this.access.freeze();
311
- this.quality.freeze();
312
- super.freeze();
313
- }
314
-
315
307
  constructor(definition: BaseElement.Properties<T>) {
316
308
  super(definition);
317
309
 
@@ -224,9 +224,9 @@ export const LevelControl = Cluster({
224
224
  children: [
225
225
  Field({ name: "Level", id: 0x0, type: "uint8", conformance: "M", constraint: "0 to 254" }),
226
226
  Field({ name: "TransitionTime", id: 0x1, type: "uint16", conformance: "M", quality: "X" }),
227
- Field({ name: "OptionsMask", id: 0x2, type: "Options", conformance: "O", constraint: "desc", default: 0 }),
227
+ Field({ name: "OptionsMask", id: 0x2, type: "Options", conformance: "M", constraint: "desc", default: 0 }),
228
228
  Field({
229
- name: "OptionsOverride", id: 0x3, type: "Options", conformance: "O", constraint: "desc", default: 0
229
+ name: "OptionsOverride", id: 0x3, type: "Options", conformance: "M", constraint: "desc", default: 0
230
230
  })
231
231
  ]
232
232
  }),