@matter/model 0.16.0-alpha.0-20251018-dd1ea6a8a → 0.16.0-alpha.0-20251020-3f6e46245

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 (265) hide show
  1. package/dist/cjs/common/Metatype.d.ts +1 -1
  2. package/dist/cjs/decoration/decorators/attribute.d.ts +11 -0
  3. package/dist/cjs/decoration/decorators/attribute.d.ts.map +1 -0
  4. package/dist/cjs/decoration/decorators/attribute.js +32 -0
  5. package/dist/cjs/decoration/decorators/attribute.js.map +6 -0
  6. package/dist/cjs/decoration/decorators/cluster.d.ts +11 -0
  7. package/dist/cjs/decoration/decorators/cluster.d.ts.map +1 -0
  8. package/dist/cjs/decoration/decorators/cluster.js +32 -0
  9. package/dist/cjs/decoration/decorators/cluster.js.map +6 -0
  10. package/dist/cjs/decoration/decorators/command.d.ts +12 -0
  11. package/dist/cjs/decoration/decorators/command.d.ts.map +1 -0
  12. package/dist/cjs/decoration/decorators/command.js +34 -0
  13. package/dist/cjs/decoration/decorators/command.js.map +6 -0
  14. package/dist/cjs/decoration/decorators/datatype.d.ts +11 -0
  15. package/dist/cjs/decoration/decorators/datatype.d.ts.map +1 -0
  16. package/dist/cjs/decoration/decorators/datatype.js +32 -0
  17. package/dist/cjs/decoration/decorators/datatype.js.map +6 -0
  18. package/dist/cjs/decoration/decorators/element.d.ts +40 -0
  19. package/dist/cjs/decoration/decorators/element.d.ts.map +1 -0
  20. package/dist/cjs/decoration/decorators/element.js +101 -0
  21. package/dist/cjs/decoration/decorators/element.js.map +6 -0
  22. package/dist/cjs/decoration/decorators/event.d.ts +14 -0
  23. package/dist/cjs/decoration/decorators/event.d.ts.map +1 -0
  24. package/dist/cjs/decoration/decorators/event.js +32 -0
  25. package/dist/cjs/decoration/decorators/event.js.map +6 -0
  26. package/dist/cjs/decoration/decorators/field.d.ts +14 -0
  27. package/dist/cjs/decoration/decorators/field.d.ts.map +1 -0
  28. package/dist/cjs/decoration/decorators/field.js +32 -0
  29. package/dist/cjs/decoration/decorators/field.js.map +6 -0
  30. package/dist/cjs/decoration/decorators/index.d.ts +16 -0
  31. package/dist/cjs/decoration/decorators/index.d.ts.map +1 -0
  32. package/dist/cjs/decoration/decorators/index.js +33 -0
  33. package/dist/cjs/decoration/decorators/index.js.map +6 -0
  34. package/dist/cjs/decoration/decorators/listOf.d.ts +12 -0
  35. package/dist/cjs/decoration/decorators/listOf.d.ts.map +1 -0
  36. package/dist/cjs/decoration/decorators/listOf.js +47 -0
  37. package/dist/cjs/decoration/decorators/listOf.js.map +6 -0
  38. package/dist/cjs/decoration/decorators/mandatory.d.ts +11 -0
  39. package/dist/cjs/decoration/decorators/mandatory.d.ts.map +1 -0
  40. package/dist/cjs/decoration/decorators/mandatory.js +41 -0
  41. package/dist/cjs/decoration/decorators/mandatory.js.map +6 -0
  42. package/dist/cjs/decoration/decorators/nonvolatile.d.ts +11 -0
  43. package/dist/cjs/decoration/decorators/nonvolatile.d.ts.map +1 -0
  44. package/dist/cjs/decoration/decorators/nonvolatile.js +41 -0
  45. package/dist/cjs/decoration/decorators/nonvolatile.js.map +6 -0
  46. package/dist/cjs/decoration/decorators/nullable.d.ts +11 -0
  47. package/dist/cjs/decoration/decorators/nullable.d.ts.map +1 -0
  48. package/dist/cjs/decoration/decorators/nullable.js +41 -0
  49. package/dist/cjs/decoration/decorators/nullable.js.map +6 -0
  50. package/dist/cjs/decoration/decorators/response.d.ts +12 -0
  51. package/dist/cjs/decoration/decorators/response.d.ts.map +1 -0
  52. package/dist/cjs/decoration/decorators/response.js +49 -0
  53. package/dist/cjs/decoration/decorators/response.js.map +6 -0
  54. package/dist/cjs/decoration/errors.d.ts +29 -0
  55. package/dist/cjs/decoration/errors.d.ts.map +1 -0
  56. package/dist/cjs/decoration/errors.js +44 -0
  57. package/dist/cjs/decoration/errors.js.map +6 -0
  58. package/dist/cjs/decoration/index.d.ts +9 -0
  59. package/dist/cjs/decoration/index.d.ts.map +1 -0
  60. package/dist/cjs/decoration/index.js +26 -0
  61. package/dist/cjs/decoration/index.js.map +6 -0
  62. package/dist/cjs/decoration/semantics/ClassSemantics.d.ts +84 -0
  63. package/dist/cjs/decoration/semantics/ClassSemantics.d.ts.map +1 -0
  64. package/dist/cjs/decoration/semantics/ClassSemantics.js +266 -0
  65. package/dist/cjs/decoration/semantics/ClassSemantics.js.map +6 -0
  66. package/dist/cjs/decoration/semantics/FieldSemantics.d.ts +21 -0
  67. package/dist/cjs/decoration/semantics/FieldSemantics.d.ts.map +1 -0
  68. package/dist/cjs/decoration/semantics/FieldSemantics.js +57 -0
  69. package/dist/cjs/decoration/semantics/FieldSemantics.js.map +6 -0
  70. package/dist/cjs/decoration/semantics/Semantics.d.ts +68 -0
  71. package/dist/cjs/decoration/semantics/Semantics.d.ts.map +1 -0
  72. package/dist/cjs/decoration/semantics/Semantics.js +156 -0
  73. package/dist/cjs/decoration/semantics/Semantics.js.map +6 -0
  74. package/dist/cjs/decoration/semantics/index.d.ts +9 -0
  75. package/dist/cjs/decoration/semantics/index.d.ts.map +1 -0
  76. package/dist/cjs/decoration/semantics/index.js +26 -0
  77. package/dist/cjs/decoration/semantics/index.js.map +6 -0
  78. package/dist/cjs/index.d.ts +1 -0
  79. package/dist/cjs/index.d.ts.map +1 -1
  80. package/dist/cjs/index.js +1 -0
  81. package/dist/cjs/index.js.map +1 -1
  82. package/dist/cjs/models/AttributeModel.d.ts +1 -0
  83. package/dist/cjs/models/AttributeModel.d.ts.map +1 -1
  84. package/dist/cjs/models/AttributeModel.js +1 -0
  85. package/dist/cjs/models/AttributeModel.js.map +1 -1
  86. package/dist/cjs/models/Children.js +3 -3
  87. package/dist/cjs/models/Children.js.map +1 -1
  88. package/dist/cjs/models/CommandModel.d.ts +1 -0
  89. package/dist/cjs/models/CommandModel.d.ts.map +1 -1
  90. package/dist/cjs/models/CommandModel.js +1 -0
  91. package/dist/cjs/models/CommandModel.js.map +1 -1
  92. package/dist/cjs/models/EventModel.d.ts +1 -0
  93. package/dist/cjs/models/EventModel.d.ts.map +1 -1
  94. package/dist/cjs/models/EventModel.js +1 -0
  95. package/dist/cjs/models/EventModel.js.map +1 -1
  96. package/dist/cjs/models/Globals.d.ts +1 -1
  97. package/dist/cjs/models/Model.d.ts +12 -3
  98. package/dist/cjs/models/Model.d.ts.map +1 -1
  99. package/dist/cjs/models/Model.js +16 -10
  100. package/dist/cjs/models/Model.js.map +1 -1
  101. package/dist/cjs/models/Schema.d.ts +14 -0
  102. package/dist/cjs/models/Schema.d.ts.map +1 -1
  103. package/dist/cjs/models/Schema.js +29 -1
  104. package/dist/cjs/models/Schema.js.map +1 -1
  105. package/dist/cjs/models/ScopeModel.d.ts +1 -1
  106. package/dist/cjs/models/ScopeModel.d.ts.map +1 -1
  107. package/dist/cjs/models/ScopeModel.js +2 -2
  108. package/dist/cjs/models/ScopeModel.js.map +1 -1
  109. package/dist/cjs/standard/elements/definitions.d.ts +1 -1
  110. package/dist/cjs/standard/elements/definitions.js +1 -1
  111. package/dist/cjs/standard/elements/{interval.element.d.ts → duration.element.d.ts} +1 -1
  112. package/dist/cjs/standard/elements/duration.element.d.ts.map +1 -0
  113. package/dist/cjs/standard/elements/{interval.element.js → duration.element.js} +4 -4
  114. package/dist/cjs/standard/elements/{interval.element.js.map → duration.element.js.map} +1 -1
  115. package/dist/cjs/standard/elements/models.d.ts +1 -1
  116. package/dist/cjs/standard/elements/models.js +2 -2
  117. package/dist/esm/common/Metatype.d.ts +1 -1
  118. package/dist/esm/decoration/decorators/attribute.d.ts +11 -0
  119. package/dist/esm/decoration/decorators/attribute.d.ts.map +1 -0
  120. package/dist/esm/decoration/decorators/attribute.js +12 -0
  121. package/dist/esm/decoration/decorators/attribute.js.map +6 -0
  122. package/dist/esm/decoration/decorators/cluster.d.ts +11 -0
  123. package/dist/esm/decoration/decorators/cluster.d.ts.map +1 -0
  124. package/dist/esm/decoration/decorators/cluster.js +12 -0
  125. package/dist/esm/decoration/decorators/cluster.js.map +6 -0
  126. package/dist/esm/decoration/decorators/command.d.ts +12 -0
  127. package/dist/esm/decoration/decorators/command.d.ts.map +1 -0
  128. package/dist/esm/decoration/decorators/command.js +14 -0
  129. package/dist/esm/decoration/decorators/command.js.map +6 -0
  130. package/dist/esm/decoration/decorators/datatype.d.ts +11 -0
  131. package/dist/esm/decoration/decorators/datatype.d.ts.map +1 -0
  132. package/dist/esm/decoration/decorators/datatype.js +12 -0
  133. package/dist/esm/decoration/decorators/datatype.js.map +6 -0
  134. package/dist/esm/decoration/decorators/element.d.ts +40 -0
  135. package/dist/esm/decoration/decorators/element.d.ts.map +1 -0
  136. package/dist/esm/decoration/decorators/element.js +81 -0
  137. package/dist/esm/decoration/decorators/element.js.map +6 -0
  138. package/dist/esm/decoration/decorators/event.d.ts +14 -0
  139. package/dist/esm/decoration/decorators/event.d.ts.map +1 -0
  140. package/dist/esm/decoration/decorators/event.js +12 -0
  141. package/dist/esm/decoration/decorators/event.js.map +6 -0
  142. package/dist/esm/decoration/decorators/field.d.ts +14 -0
  143. package/dist/esm/decoration/decorators/field.d.ts.map +1 -0
  144. package/dist/esm/decoration/decorators/field.js +12 -0
  145. package/dist/esm/decoration/decorators/field.js.map +6 -0
  146. package/dist/esm/decoration/decorators/index.d.ts +16 -0
  147. package/dist/esm/decoration/decorators/index.d.ts.map +1 -0
  148. package/dist/esm/decoration/decorators/index.js +16 -0
  149. package/dist/esm/decoration/decorators/index.js.map +6 -0
  150. package/dist/esm/decoration/decorators/listOf.d.ts +12 -0
  151. package/dist/esm/decoration/decorators/listOf.d.ts.map +1 -0
  152. package/dist/esm/decoration/decorators/listOf.js +27 -0
  153. package/dist/esm/decoration/decorators/listOf.js.map +6 -0
  154. package/dist/esm/decoration/decorators/mandatory.d.ts +11 -0
  155. package/dist/esm/decoration/decorators/mandatory.d.ts.map +1 -0
  156. package/dist/esm/decoration/decorators/mandatory.js +21 -0
  157. package/dist/esm/decoration/decorators/mandatory.js.map +6 -0
  158. package/dist/esm/decoration/decorators/nonvolatile.d.ts +11 -0
  159. package/dist/esm/decoration/decorators/nonvolatile.d.ts.map +1 -0
  160. package/dist/esm/decoration/decorators/nonvolatile.js +21 -0
  161. package/dist/esm/decoration/decorators/nonvolatile.js.map +6 -0
  162. package/dist/esm/decoration/decorators/nullable.d.ts +11 -0
  163. package/dist/esm/decoration/decorators/nullable.d.ts.map +1 -0
  164. package/dist/esm/decoration/decorators/nullable.js +21 -0
  165. package/dist/esm/decoration/decorators/nullable.js.map +6 -0
  166. package/dist/esm/decoration/decorators/response.d.ts +12 -0
  167. package/dist/esm/decoration/decorators/response.d.ts.map +1 -0
  168. package/dist/esm/decoration/decorators/response.js +29 -0
  169. package/dist/esm/decoration/decorators/response.js.map +6 -0
  170. package/dist/esm/decoration/errors.d.ts +29 -0
  171. package/dist/esm/decoration/errors.d.ts.map +1 -0
  172. package/dist/esm/decoration/errors.js +24 -0
  173. package/dist/esm/decoration/errors.js.map +6 -0
  174. package/dist/esm/decoration/index.d.ts +9 -0
  175. package/dist/esm/decoration/index.d.ts.map +1 -0
  176. package/dist/esm/decoration/index.js +9 -0
  177. package/dist/esm/decoration/index.js.map +6 -0
  178. package/dist/esm/decoration/semantics/ClassSemantics.d.ts +84 -0
  179. package/dist/esm/decoration/semantics/ClassSemantics.d.ts.map +1 -0
  180. package/dist/esm/decoration/semantics/ClassSemantics.js +246 -0
  181. package/dist/esm/decoration/semantics/ClassSemantics.js.map +6 -0
  182. package/dist/esm/decoration/semantics/FieldSemantics.d.ts +21 -0
  183. package/dist/esm/decoration/semantics/FieldSemantics.d.ts.map +1 -0
  184. package/dist/esm/decoration/semantics/FieldSemantics.js +37 -0
  185. package/dist/esm/decoration/semantics/FieldSemantics.js.map +6 -0
  186. package/dist/esm/decoration/semantics/Semantics.d.ts +68 -0
  187. package/dist/esm/decoration/semantics/Semantics.d.ts.map +1 -0
  188. package/dist/esm/decoration/semantics/Semantics.js +126 -0
  189. package/dist/esm/decoration/semantics/Semantics.js.map +6 -0
  190. package/dist/esm/decoration/semantics/index.d.ts +9 -0
  191. package/dist/esm/decoration/semantics/index.d.ts.map +1 -0
  192. package/dist/esm/decoration/semantics/index.js +9 -0
  193. package/dist/esm/decoration/semantics/index.js.map +6 -0
  194. package/dist/esm/index.d.ts +1 -0
  195. package/dist/esm/index.d.ts.map +1 -1
  196. package/dist/esm/index.js +1 -0
  197. package/dist/esm/index.js.map +1 -1
  198. package/dist/esm/models/AttributeModel.d.ts +1 -0
  199. package/dist/esm/models/AttributeModel.d.ts.map +1 -1
  200. package/dist/esm/models/AttributeModel.js +1 -0
  201. package/dist/esm/models/AttributeModel.js.map +1 -1
  202. package/dist/esm/models/Children.js +3 -3
  203. package/dist/esm/models/Children.js.map +1 -1
  204. package/dist/esm/models/CommandModel.d.ts +1 -0
  205. package/dist/esm/models/CommandModel.d.ts.map +1 -1
  206. package/dist/esm/models/CommandModel.js +1 -0
  207. package/dist/esm/models/CommandModel.js.map +1 -1
  208. package/dist/esm/models/EventModel.d.ts +1 -0
  209. package/dist/esm/models/EventModel.d.ts.map +1 -1
  210. package/dist/esm/models/EventModel.js +1 -0
  211. package/dist/esm/models/EventModel.js.map +1 -1
  212. package/dist/esm/models/Globals.d.ts +1 -1
  213. package/dist/esm/models/Model.d.ts +12 -3
  214. package/dist/esm/models/Model.d.ts.map +1 -1
  215. package/dist/esm/models/Model.js +16 -10
  216. package/dist/esm/models/Model.js.map +1 -1
  217. package/dist/esm/models/Schema.d.ts +14 -0
  218. package/dist/esm/models/Schema.d.ts.map +1 -1
  219. package/dist/esm/models/Schema.js +29 -1
  220. package/dist/esm/models/Schema.js.map +1 -1
  221. package/dist/esm/models/ScopeModel.d.ts +1 -1
  222. package/dist/esm/models/ScopeModel.d.ts.map +1 -1
  223. package/dist/esm/models/ScopeModel.js +2 -2
  224. package/dist/esm/models/ScopeModel.js.map +1 -1
  225. package/dist/esm/standard/elements/definitions.d.ts +1 -1
  226. package/dist/esm/standard/elements/definitions.js +1 -1
  227. package/dist/esm/standard/elements/{interval.element.d.ts → duration.element.d.ts} +1 -1
  228. package/dist/esm/standard/elements/duration.element.d.ts.map +1 -0
  229. package/dist/esm/standard/elements/{interval.element.js → duration.element.js} +1 -1
  230. package/dist/esm/standard/elements/{interval.element.js.map → duration.element.js.map} +1 -1
  231. package/dist/esm/standard/elements/models.d.ts +1 -1
  232. package/dist/esm/standard/elements/models.js +2 -2
  233. package/package.json +4 -4
  234. package/src/decoration/decorators/attribute.ts +13 -0
  235. package/src/decoration/decorators/cluster.ts +13 -0
  236. package/src/decoration/decorators/command.ts +16 -0
  237. package/src/decoration/decorators/datatype.ts +13 -0
  238. package/src/decoration/decorators/element.ts +128 -0
  239. package/src/decoration/decorators/event.ts +16 -0
  240. package/src/decoration/decorators/field.ts +16 -0
  241. package/src/decoration/decorators/index.ts +16 -0
  242. package/src/decoration/decorators/listOf.ts +30 -0
  243. package/src/decoration/decorators/mandatory.ts +22 -0
  244. package/src/decoration/decorators/nonvolatile.ts +22 -0
  245. package/src/decoration/decorators/nullable.ts +22 -0
  246. package/src/decoration/decorators/response.ts +34 -0
  247. package/src/decoration/errors.ts +29 -0
  248. package/src/decoration/index.ts +9 -0
  249. package/src/decoration/semantics/ClassSemantics.ts +374 -0
  250. package/src/decoration/semantics/FieldSemantics.ts +49 -0
  251. package/src/decoration/semantics/Semantics.ts +170 -0
  252. package/src/decoration/semantics/index.ts +9 -0
  253. package/src/index.ts +1 -0
  254. package/src/models/AttributeModel.ts +1 -0
  255. package/src/models/Children.ts +3 -3
  256. package/src/models/CommandModel.ts +1 -0
  257. package/src/models/EventModel.ts +1 -0
  258. package/src/models/Model.ts +34 -12
  259. package/src/models/Schema.ts +51 -0
  260. package/src/models/ScopeModel.ts +2 -2
  261. package/src/standard/elements/definitions.ts +1 -1
  262. package/src/standard/elements/models.ts +1 -1
  263. package/dist/cjs/standard/elements/interval.element.d.ts.map +0 -1
  264. package/dist/esm/standard/elements/interval.element.d.ts.map +0 -1
  265. /package/src/standard/elements/{interval.element.ts → duration.element.ts} +0 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { FieldSemantics } from "#decoration/semantics/FieldSemantics.js";
8
+ import { CommandElement } from "#elements/CommandElement.js";
9
+ import { Decorator } from "#general";
10
+ import { CommandModel } from "#models/CommandModel.js";
11
+ import { DatatypeModel } from "#models/DatatypeModel.js";
12
+
13
+ /**
14
+ * Specify the response type for a command.
15
+ */
16
+ export function response(type: DatatypeModel): Decorator.ClassMethod {
17
+ return Decorator((_target, context) => {
18
+ const requestSemantics = FieldSemantics.of(context);
19
+
20
+ requestSemantics.modelType = CommandModel;
21
+ const request = requestSemantics.mutableModel as CommandModel;
22
+
23
+ const name = `${request.name}Response`;
24
+ new CommandModel({
25
+ name,
26
+ id: request.id,
27
+ parent: request.parent,
28
+ direction: CommandElement.Direction.Response,
29
+ operationalBase: type,
30
+ });
31
+
32
+ request.response = name;
33
+ });
34
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { ImplementationError } from "#general";
8
+
9
+ export class MetadataError extends ImplementationError {}
10
+
11
+ /**
12
+ * Thrown when a type is assigned to a class or property that is already typed.
13
+ */
14
+ export class MetadataConflictError extends MetadataError {}
15
+
16
+ /**
17
+ * Thrown when metadata is inappropriate for the decorated language element.
18
+ */
19
+ export class InvalidMetadataError extends MetadataError {}
20
+
21
+ /**
22
+ * Thrown when an error occurs instantiating a dummy instance of a class for introspection purposes.
23
+ */
24
+ export class ClassIntrospectionError extends MetadataError {}
25
+
26
+ /**
27
+ * Thrown when required metadata is missing.
28
+ */
29
+ export class MissingMetadataError extends MetadataError {}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./decorators/index.js";
8
+ export * from "./errors.js";
9
+ export * from "./semantics/index.js";
@@ -0,0 +1,374 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ // Must import these via index to ensure proper initialization
8
+ import { DatatypeModel, FieldModel, Model } from "#models/index.js";
9
+
10
+ import { camelize } from "#general";
11
+ import { Scope } from "#logic/Scope.js";
12
+ import { any, struct } from "#standard/elements/models.js";
13
+ import { InvalidMetadataError, MetadataConflictError } from "../errors.js";
14
+ import { FieldSemantics } from "./FieldSemantics.js";
15
+ import { Semantics } from "./Semantics.js";
16
+
17
+ /**
18
+ * Our key into {@link DecoratorContext.metadata}.
19
+ *
20
+ * This is where we store the {@link ClassSemantics} associated with a decorated class.
21
+ */
22
+ const matter = Symbol("matter");
23
+
24
+ /**
25
+ * Our view of {@link DecoratorContext.metadata}.
26
+ *
27
+ * Note that in the case of class inheritance, the metadata also inherits from the parent metadata. So we need to use
28
+ * {@link Object.hasOwn} to differentiate between the {@link ClassSemantics} for a class and that of its base class.
29
+ */
30
+ interface MatterMetadata {
31
+ [matter]?: ClassSemantics;
32
+ }
33
+
34
+ /**
35
+ * Matter semantic metadata attached to a class via decorators.
36
+ *
37
+ * We use decorators to allow for definition of model elements in the context of a specific type.
38
+ *
39
+ * Currently there are JavaScript/TypeScript limitations to be aware of when decorating type definitions:
40
+ *
41
+ * * Decorators may only be applied to JavaScript classes. So if you want to define an interface without
42
+ * implementation, you must implement as a class and use as an interface. That's why this is "class" metadata.
43
+ *
44
+ * * Decorators may not affect the TypeScript type of an object. This means that you must define both the Matter type
45
+ * (e.g. {@link uint32}) and TypeScript type (e.g. `number`).
46
+ */
47
+ export class ClassSemantics extends Semantics {
48
+ #new?: ClassSemantics.Constructor;
49
+ #definedFields?: Map<string, FieldSemantics>;
50
+
51
+ /**
52
+ * The model that represents the semantics for this class.
53
+ *
54
+ * This may be {@link localModel} or {@link prototypeBaseModel}
55
+ */
56
+ get semanticModel() {
57
+ if (this.localModel) {
58
+ // Model is defined here
59
+ return this.localModel;
60
+ }
61
+
62
+ if (this.#definedFields) {
63
+ // Model has not been defined but should be due to decoration, so define it now
64
+ return this.mutableModel;
65
+ }
66
+
67
+ // Return model inherited via prototype, if any. Otherwise we do not express semantics
68
+ return this.prototypeBaseModel;
69
+ }
70
+
71
+ /**
72
+ * Get the class constructor
73
+ */
74
+ get new(): ClassSemantics.Constructor | undefined {
75
+ return this.#new;
76
+ }
77
+
78
+ /**
79
+ * Assign the constructor for the class.
80
+ */
81
+ set new(fn: ClassSemantics.Constructor) {
82
+ if (this.#new === fn) {
83
+ return;
84
+ }
85
+
86
+ if (this.isFinal) {
87
+ throw new MetadataConflictError(
88
+ `Cannot install semantic constructor ${fn.name} because semantics are final`,
89
+ );
90
+ }
91
+
92
+ this.#new = fn;
93
+
94
+ // Set name to match class
95
+ if (this.localModel && !this.localModel.isFinal && this.localModel.name !== this.#new?.name) {
96
+ this.localModel.name = this.#new?.name;
97
+ }
98
+
99
+ // Update local semantics based on inherited semantics
100
+ if (this.localModel) {
101
+ // I have a model defined already so update according to base semantics
102
+ this.#applyBaseSemantics();
103
+ } else {
104
+ // I have no model defined yet so I am semantically equivalent to my base
105
+ const base = this.prototypeBaseModel;
106
+ if (base !== undefined) {
107
+ this.mutableModel = base;
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * The class {@link Model} inherited from {@link new}'s prototype chain, if any.
114
+ */
115
+ get prototypeBaseModel(): Model | undefined {
116
+ let current = this.#new?.prototype;
117
+ let base;
118
+
119
+ while (current) {
120
+ current = Object.getPrototypeOf(current) as unknown;
121
+ if (typeof current !== "object" || current === null) {
122
+ return;
123
+ }
124
+ const constructor = current.constructor;
125
+ if (constructor === Object) {
126
+ return;
127
+ }
128
+ base = ClassSemantics.maybeOf(constructor);
129
+
130
+ if (base?.localModel) {
131
+ // Semantics and model may not mutate once acting as a base
132
+ base.finalize();
133
+
134
+ return base.localModel;
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Obtain a {@link FieldSemantics} for the named field.
141
+ */
142
+ fieldFor(name: string | symbol) {
143
+ if (typeof name !== "string") {
144
+ throw new InvalidMetadataError(`Cannot decorate symbolic function ${String(name)}`);
145
+ }
146
+
147
+ if (this.#definedFields === undefined) {
148
+ this.#definedFields = new Map();
149
+ }
150
+ let field = this.#definedFields.get(name);
151
+ if (field === undefined) {
152
+ if (this.isFinal) {
153
+ throw new MetadataConflictError(`Cannot install field ${name} because semantics are final`);
154
+ }
155
+
156
+ this.#definedFields.set(name, (field = new FieldSemantics(this, name)));
157
+ }
158
+ return field;
159
+ }
160
+
161
+ /**
162
+ * Add logical fields for unrecognized members of an object.
163
+ *
164
+ * This extends the model with untyped fields for properties that are not otherwise typed.
165
+ */
166
+ defineUnknownMembers(instance: unknown) {
167
+ if (instance === undefined || instance === null) {
168
+ return;
169
+ }
170
+
171
+ const known = this.semanticModel
172
+ ? new Set(
173
+ Scope(this.semanticModel)
174
+ .membersOf(this.semanticModel)
175
+ .map(model => camelize(model.name)),
176
+ )
177
+ : new Set();
178
+
179
+ const descriptors = Object.getOwnPropertyDescriptors(instance);
180
+
181
+ for (const name in descriptors) {
182
+ // Skip if name is already known
183
+ if (known.has(camelize(name))) {
184
+ continue;
185
+ }
186
+
187
+ // We only model string properties
188
+ if (typeof name !== "string") {
189
+ continue;
190
+ }
191
+
192
+ // We do not support write-only fields
193
+ const descriptor = descriptors[name];
194
+ if (descriptor.set && !descriptor.get) {
195
+ continue;
196
+ }
197
+
198
+ // Methods are not fields
199
+ try {
200
+ if (typeof (instance as Record<string, unknown>)[name] === "function") {
201
+ continue;
202
+ }
203
+ } catch (e) {
204
+ // We do not support inaccessible fields
205
+ continue;
206
+ }
207
+
208
+ const model = this.fieldFor(name).mutableModel as FieldModel;
209
+
210
+ model.operationalBase = any;
211
+
212
+ // Default to fixed if read-only
213
+ if (!descriptor.writable && !descriptor.set) {
214
+ model.quality = { ...model.quality, fixed: true };
215
+ }
216
+
217
+ this.fieldFor(name).mutableModel.operationalBase = any;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Obtain the {@link ClassSemantics} for {@link source}.
223
+ */
224
+ static override of(source: ClassSemantics.Source) {
225
+ // Source is the semantics
226
+ if (source instanceof ClassSemantics) {
227
+ return source;
228
+ }
229
+
230
+ // Source is decorator context
231
+ if (typeof source !== "function") {
232
+ const metadata = source.metadata as MatterMetadata;
233
+ if (Object.hasOwn(metadata, matter)) {
234
+ return metadata[matter]!;
235
+ }
236
+ return (metadata[matter] = new ClassSemantics());
237
+ }
238
+
239
+ // Source is a constructor
240
+ let metadata: MatterMetadata;
241
+ if (!Object.hasOwn(source, Symbol.metadata)) {
242
+ metadata = source[Symbol.metadata] = {};
243
+ } else {
244
+ metadata = source[Symbol.metadata] as MatterMetadata;
245
+ }
246
+
247
+ let semantics: ClassSemantics;
248
+ if (!Object.hasOwn(metadata, matter)) {
249
+ semantics = metadata[matter] = new ClassSemantics();
250
+ } else {
251
+ semantics = metadata[matter] as ClassSemantics;
252
+ }
253
+
254
+ // If the parent class is not decorated then this may be the first time we've seen the constructor associated
255
+ // with the metadata. So always inform the metadata of its constructor
256
+ if (!semantics.new) {
257
+ semantics.new = source;
258
+ }
259
+
260
+ return semantics;
261
+ }
262
+
263
+ /**
264
+ * Determine {@link new} has semantic decoration.
265
+ */
266
+ static hasOwnSemantics(source: ClassSemantics.Constructor) {
267
+ return Object.hasOwn(source, Symbol.metadata) && Object.hasOwn(source[Symbol.metadata]!, matter);
268
+ }
269
+
270
+ /**
271
+ * Access the {@link ClassSemantics} of {@link new} if it is defined.
272
+ */
273
+ static maybeOf(source: ClassSemantics.Constructor) {
274
+ if (this.hasOwnSemantics(source)) {
275
+ return ClassSemantics.of(source);
276
+ }
277
+ }
278
+
279
+ static {
280
+ Semantics.classOf = this.of;
281
+ }
282
+
283
+ override finalize() {
284
+ if (this.isFinal) {
285
+ return;
286
+ }
287
+
288
+ // Invoke any custom extension logic
289
+ this.#new?.[ClassSemantics.extend]?.(this);
290
+
291
+ // Apply base finalization
292
+ super.finalize();
293
+
294
+ // Finalize fields
295
+ if (this.#definedFields) {
296
+ for (const field of this.#definedFields.values()) {
297
+ field.finalize();
298
+ }
299
+ }
300
+ }
301
+
302
+ #applyBaseSemantics() {
303
+ const base = this.prototypeBaseModel;
304
+ if (base === undefined) {
305
+ return;
306
+ }
307
+
308
+ // If my model does not yet have an explicit base, I extend my parent class's model
309
+ const { type, base: currentBase } = this.mutableModel;
310
+ if (type === undefined && (currentBase === undefined || currentBase === struct)) {
311
+ const operationalBase = this.prototypeBaseModel;
312
+ if (operationalBase) {
313
+ this.mutableModel.operationalBase = operationalBase;
314
+ }
315
+ }
316
+
317
+ // If my base is not a datatype, it forces the type of my model
318
+ if (base && base.tag !== "datatype" && this.mutableModel.tag !== base.tag) {
319
+ this.modelType = base.constructor as Model.ConcreteType;
320
+ }
321
+
322
+ // If ID is not already set, force ID to match base
323
+ if (this.mutableModel.id === undefined && base.id !== undefined) {
324
+ this.mutableModel.id = base.id;
325
+ }
326
+ }
327
+
328
+ protected override integrateModel(model: Model) {
329
+ // If local model is not yet set or is final, we have not extended; this is "swap base with no decoration" case
330
+ if (this.localModel === undefined || this.localModel.isFinal) {
331
+ return model;
332
+ }
333
+
334
+ // If incoming model is final, replace operational base of local model. This is "swap base with decoration"
335
+ // case
336
+ if (model.isFinal) {
337
+ this.localModel.operationalBase = model;
338
+ return this.localModel;
339
+ }
340
+
341
+ // Neither model is final. Move any children to new model. This is "replace temporary model created by
342
+ // decorators" case
343
+ model.children.push(...this.localModel.children);
344
+
345
+ return model;
346
+ }
347
+
348
+ protected override createModel(type: Model.ConcreteType = DatatypeModel) {
349
+ let name = this.#new?.name;
350
+ if (name === undefined || name === "") {
351
+ name = "Unnamed";
352
+ }
353
+ return new type({ name, operationalBase: struct });
354
+ }
355
+ }
356
+
357
+ export namespace ClassSemantics {
358
+ /**
359
+ * A standard constructor with an optional decoration extension point.
360
+ */
361
+ export interface Constructor extends NewableFunction {
362
+ /**
363
+ * If present, invoked for custom decoration after installing constructor.
364
+ */
365
+ [extend]?: (semantics: ClassSemantics) => void;
366
+ }
367
+
368
+ /**
369
+ * An object for which you may obtain {@link ClassSemantics}.
370
+ */
371
+ export type Source = Constructor | DecoratorContext | ClassSemantics;
372
+
373
+ export const extend = Symbol("extend");
374
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { InvalidMetadataError } from "#decoration/errors.js";
8
+ import { InternalError } from "#general";
9
+ import { FieldModel } from "#models/FieldModel.js";
10
+ import type { Model } from "#models/Model.js";
11
+ import type { ClassSemantics } from "./ClassSemantics.js";
12
+ import { Semantics } from "./Semantics.js";
13
+
14
+ /**
15
+ * Decorator metadata associated with a specific class field.
16
+ */
17
+ export class FieldSemantics extends Semantics {
18
+ constructor(owner: ClassSemantics, name: string) {
19
+ super();
20
+
21
+ this.mutableModel = new FieldModel({ name, parent: owner.mutableModel });
22
+ }
23
+
24
+ protected override createModel(): Model {
25
+ // We create our model unconditionally so this shouldn't happen
26
+ throw new InternalError("Unexpected FieldSemantics.createModel");
27
+ }
28
+
29
+ protected override integrateModel(model: Model): Model {
30
+ if (this.localModel === undefined) {
31
+ return model;
32
+ }
33
+ this.mutableModel.operationalBase = model;
34
+ return this.mutableModel;
35
+ }
36
+
37
+ static override of(source: FieldSemantics.Source) {
38
+ if (source.kind === "class") {
39
+ throw new InvalidMetadataError(
40
+ `Cannot retrieve field semantics for class decorator ${source.name ?? "of anonymous class"}`,
41
+ );
42
+ }
43
+ return Semantics.classOf(source).fieldFor(source.name);
44
+ }
45
+ }
46
+
47
+ export namespace FieldSemantics {
48
+ export type Source = DecoratorContext;
49
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MetadataConflictError } from "#decoration/errors.js";
8
+ import { InternalError } from "#general";
9
+ import type { Model } from "#models/Model.js";
10
+ import * as models from "#standard/elements/models.js";
11
+ import type { ClassSemantics } from "./ClassSemantics.js";
12
+
13
+ const standardModels = new Set(Object.values(models) as Model[]);
14
+
15
+ /**
16
+ * Base class for Matter semantics associated with JavaScript classes and properties.
17
+ *
18
+ * We model semantics using {@link Model}. The model may be built declaratively with decorators or programmatically
19
+ * using this interface directly.
20
+ */
21
+ export abstract class Semantics {
22
+ #localModel?: Model;
23
+ #isFinal = false;
24
+
25
+ /**
26
+ * Determine whether these semantics are final.
27
+ *
28
+ * Once final no further mutation is allowed.
29
+ */
30
+ get isFinal(): boolean {
31
+ return this.#isFinal;
32
+ }
33
+
34
+ /**
35
+ * Finalize the model.
36
+ */
37
+ finalize() {
38
+ if (this.#isFinal) {
39
+ return;
40
+ }
41
+
42
+ this.#isFinal = true;
43
+ Object.freeze(this);
44
+ this.#localModel?.finalize();
45
+ }
46
+
47
+ /**
48
+ * The {@link Model} defined by local decoration, if any.
49
+ */
50
+ get localModel() {
51
+ return this.#localModel;
52
+ }
53
+
54
+ /**
55
+ * Set the model.
56
+ *
57
+ * When "setting" the model we need to merge semantics coming from decorators, the prototype hierarchy, and models
58
+ * provided programmatically.
59
+ *
60
+ * This may set the local model, change it or set its operational base. We use local state and the finalization
61
+ * status of the model to determine how to integrate.
62
+ */
63
+ set mutableModel(model: Model) {
64
+ if (this.#isFinal) {
65
+ throw new MetadataConflictError(`Cannot modify final semantics of ${this.#localModel?.name ?? "(none)"}`);
66
+ }
67
+
68
+ if (this.#localModel === model) {
69
+ return;
70
+ }
71
+
72
+ // We don't finalize global models by default for performance reasons, but do finalize them here to prevent
73
+ // accidental mutation
74
+ if (standardModels.has(model)) {
75
+ model.finalize();
76
+ }
77
+
78
+ this.#localModel = this.integrateModel(model);
79
+ }
80
+
81
+ /**
82
+ * Replace the "base" of {@link localModel} and return an updated model.
83
+ */
84
+ protected abstract integrateModel(model: Model): Model;
85
+
86
+ /**
87
+ * Obtain a model unconditionally for mutation purposes.
88
+ */
89
+ get mutableModel() {
90
+ if (this.#isFinal) {
91
+ throw new MetadataConflictError(`Cannot modify final semantics of ${this.#localModel?.name ?? "(none)"}`);
92
+ }
93
+
94
+ if (this.#localModel === undefined) {
95
+ this.#localModel = this.createModel();
96
+ } else if (this.#localModel.isFinal) {
97
+ this.#localModel = this.#localModel.extend();
98
+ }
99
+ return this.#localModel;
100
+ }
101
+
102
+ /**
103
+ * The model's {@link Model.Type}.
104
+ *
105
+ * Field decorators operate prior to class decorators, so we may need to transition the model to a new type when the
106
+ * class is decorated. This is slightly lossy so type-specific decoration should always occur after assigning kind.
107
+ */
108
+ get modelType(): Model.Type | undefined {
109
+ if (this.#localModel === undefined) {
110
+ return;
111
+ }
112
+ return this.mutableModel.constructor as Model.ConcreteType;
113
+ }
114
+
115
+ set modelType(type: Model.ConcreteType) {
116
+ if (this.#localModel === undefined) {
117
+ this.#localModel = this.createModel(type);
118
+ return;
119
+ }
120
+
121
+ if (this.#localModel instanceof type) {
122
+ return;
123
+ }
124
+
125
+ // Detach original model
126
+ const original = this.#localModel as Model;
127
+ const { parent } = original;
128
+ original.parent = undefined;
129
+
130
+ // Create replacement
131
+ const replacement = new type({
132
+ name: original.name,
133
+ id: original.id,
134
+ type: original.type,
135
+ parent,
136
+ operationalBase: original.operationalBase,
137
+ children: original.children,
138
+ });
139
+
140
+ if ("quality" in original && "quality" in replacement) {
141
+ replacement.quality = original.quality;
142
+ }
143
+
144
+ this.#localModel = replacement;
145
+ }
146
+
147
+ protected abstract createModel(type?: Model.ConcreteType): Model;
148
+ }
149
+
150
+ export namespace Semantics {
151
+ /**
152
+ * Access the {@link Semantics} of a {@link DecoratorContext}.
153
+ */
154
+ export function of(context: DecoratorContext) {
155
+ const classSemantics = classOf(context);
156
+ if (context.kind === "class") {
157
+ return classSemantics;
158
+ }
159
+ return classSemantics.fieldFor(context.name);
160
+ }
161
+
162
+ /**
163
+ * Access the {@link ClassSemantics} of a constructor or decorator context.
164
+ */
165
+ // eslint-disable-next-line prefer-const
166
+ export let classOf = (_source: ClassSemantics.Source): ClassSemantics => {
167
+ // This should be replaced by ClassSemantics
168
+ throw new InternalError(`Class decoration lookup not installed`);
169
+ };
170
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./ClassSemantics.js";
8
+ export * from "./FieldSemantics.js";
9
+ export * from "./Semantics.js";
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  export * from "./aspects/index.js";
8
8
  export * from "./common/index.js";
9
+ export * from "./decoration/index.js";
9
10
  export * from "./elements/index.js";
10
11
  export * from "./logic/index.js";
11
12
  export * from "./Matter.js";
@@ -51,6 +51,7 @@ export class AttributeModel extends PropertyModel<AttributeElement> implements A
51
51
  }
52
52
 
53
53
  static Tag = AttributeElement.Tag;
54
+ static requiresId = true;
54
55
  }
55
56
 
56
57
  AttributeModel.register();