@matter/node 0.11.9 → 0.12.0-alpha.0-20241211-6d80d8b5c

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 (214) hide show
  1. package/dist/cjs/behavior/internal/BehaviorBacking.d.ts +6 -1
  2. package/dist/cjs/behavior/internal/BehaviorBacking.d.ts.map +1 -1
  3. package/dist/cjs/behavior/internal/BehaviorBacking.js +2 -1
  4. package/dist/cjs/behavior/internal/BehaviorBacking.js.map +1 -1
  5. package/dist/cjs/behavior/internal/ClientBehaviorBacking.d.ts +2 -1
  6. package/dist/cjs/behavior/internal/ClientBehaviorBacking.d.ts.map +1 -1
  7. package/dist/cjs/behavior/internal/ClientBehaviorBacking.js +4 -0
  8. package/dist/cjs/behavior/internal/ClientBehaviorBacking.js.map +1 -1
  9. package/dist/cjs/behavior/internal/ServerBehaviorBacking.d.ts +2 -0
  10. package/dist/cjs/behavior/internal/ServerBehaviorBacking.d.ts.map +1 -1
  11. package/dist/cjs/behavior/internal/ServerBehaviorBacking.js +37 -0
  12. package/dist/cjs/behavior/internal/ServerBehaviorBacking.js.map +1 -1
  13. package/dist/cjs/behavior/state/transaction/Transaction.d.ts +18 -18
  14. package/dist/cjs/behavior/system/events/EventsBehavior.d.ts +26 -0
  15. package/dist/cjs/behavior/system/events/EventsBehavior.d.ts.map +1 -0
  16. package/dist/cjs/behavior/system/events/EventsBehavior.js +74 -0
  17. package/dist/cjs/behavior/system/events/EventsBehavior.js.map +6 -0
  18. package/dist/cjs/behavior/system/events/index.d.ts +7 -0
  19. package/dist/cjs/behavior/system/events/index.d.ts.map +1 -0
  20. package/dist/cjs/behavior/system/events/index.js +24 -0
  21. package/dist/cjs/behavior/system/events/index.js.map +6 -0
  22. package/dist/cjs/behavior/system/network/ServerNetworkRuntime.d.ts.map +1 -1
  23. package/dist/cjs/behavior/system/network/ServerNetworkRuntime.js +3 -0
  24. package/dist/cjs/behavior/system/network/ServerNetworkRuntime.js.map +1 -1
  25. package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts +8 -0
  26. package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts.map +1 -1
  27. package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.js +12 -4
  28. package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.js.map +1 -1
  29. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts +2 -4
  30. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
  31. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +6 -8
  32. package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
  33. package/dist/cjs/endpoint/Agent.d.ts +1 -1
  34. package/dist/cjs/endpoint/Agent.js +1 -1
  35. package/dist/cjs/endpoint/index.d.ts +1 -1
  36. package/dist/cjs/endpoint/index.d.ts.map +1 -1
  37. package/dist/cjs/endpoint/index.js +1 -1
  38. package/dist/cjs/endpoint/index.js.map +1 -1
  39. package/dist/cjs/endpoint/properties/Behaviors.d.ts +13 -0
  40. package/dist/cjs/endpoint/properties/Behaviors.d.ts.map +1 -1
  41. package/dist/cjs/endpoint/properties/Behaviors.js +37 -24
  42. package/dist/cjs/endpoint/properties/Behaviors.js.map +2 -2
  43. package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts +8 -3
  44. package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
  45. package/dist/cjs/endpoint/properties/EndpointInitializer.js +5 -0
  46. package/dist/cjs/endpoint/properties/EndpointInitializer.js.map +1 -1
  47. package/dist/cjs/endpoint/server/BehaviorServer.d.ts +21 -0
  48. package/dist/cjs/endpoint/server/BehaviorServer.d.ts.map +1 -0
  49. package/dist/cjs/endpoint/server/BehaviorServer.js +272 -0
  50. package/dist/cjs/endpoint/server/BehaviorServer.js.map +6 -0
  51. package/dist/{esm/endpoint → cjs/endpoint/server}/EndpointServer.d.ts +5 -6
  52. package/dist/cjs/endpoint/server/EndpointServer.d.ts.map +1 -0
  53. package/dist/cjs/endpoint/{EndpointServer.js → server/EndpointServer.js} +38 -14
  54. package/dist/cjs/endpoint/server/EndpointServer.js.map +6 -0
  55. package/dist/cjs/{node → endpoint}/server/ServerEndpointInitializer.d.ts +4 -4
  56. package/dist/cjs/endpoint/server/ServerEndpointInitializer.d.ts.map +1 -0
  57. package/dist/cjs/{node → endpoint}/server/ServerEndpointInitializer.js +7 -7
  58. package/dist/cjs/endpoint/server/ServerEndpointInitializer.js.map +6 -0
  59. package/dist/cjs/endpoint/server/index.d.ts +8 -0
  60. package/dist/cjs/endpoint/server/index.d.ts.map +1 -0
  61. package/dist/cjs/endpoint/server/index.js +25 -0
  62. package/dist/cjs/endpoint/server/index.js.map +6 -0
  63. package/dist/cjs/endpoint/type/MutableEndpoint.d.ts.map +1 -1
  64. package/dist/cjs/endpoint/type/MutableEndpoint.js +3 -0
  65. package/dist/cjs/endpoint/type/MutableEndpoint.js.map +1 -1
  66. package/dist/cjs/node/Node.d.ts +5 -0
  67. package/dist/cjs/node/Node.d.ts.map +1 -1
  68. package/dist/cjs/node/Node.js +17 -2
  69. package/dist/cjs/node/Node.js.map +1 -1
  70. package/dist/cjs/node/NodeLifecycle.d.ts +11 -1
  71. package/dist/cjs/node/NodeLifecycle.d.ts.map +1 -1
  72. package/dist/cjs/node/NodeLifecycle.js +14 -0
  73. package/dist/cjs/node/NodeLifecycle.js.map +1 -1
  74. package/dist/cjs/node/ServerNode.d.ts +3 -2
  75. package/dist/cjs/node/ServerNode.d.ts.map +1 -1
  76. package/dist/cjs/node/ServerNode.js +9 -9
  77. package/dist/cjs/node/ServerNode.js.map +1 -1
  78. package/dist/cjs/node/server/ServerEnvironment.js +1 -1
  79. package/dist/cjs/node/server/ServerEnvironment.js.map +1 -1
  80. package/dist/cjs/node/server/TransactionalInteractionServer.d.ts +2 -2
  81. package/dist/cjs/node/server/TransactionalInteractionServer.d.ts.map +1 -1
  82. package/dist/cjs/node/server/TransactionalInteractionServer.js +9 -4
  83. package/dist/cjs/node/server/TransactionalInteractionServer.js.map +2 -2
  84. package/dist/cjs/node/server/index.d.ts +0 -1
  85. package/dist/cjs/node/server/index.d.ts.map +1 -1
  86. package/dist/cjs/node/server/index.js +0 -1
  87. package/dist/cjs/node/server/index.js.map +1 -1
  88. package/dist/esm/behavior/internal/BehaviorBacking.d.ts +6 -1
  89. package/dist/esm/behavior/internal/BehaviorBacking.d.ts.map +1 -1
  90. package/dist/esm/behavior/internal/BehaviorBacking.js +2 -1
  91. package/dist/esm/behavior/internal/BehaviorBacking.js.map +1 -1
  92. package/dist/esm/behavior/internal/ClientBehaviorBacking.d.ts +2 -1
  93. package/dist/esm/behavior/internal/ClientBehaviorBacking.d.ts.map +1 -1
  94. package/dist/esm/behavior/internal/ClientBehaviorBacking.js +4 -0
  95. package/dist/esm/behavior/internal/ClientBehaviorBacking.js.map +1 -1
  96. package/dist/esm/behavior/internal/ServerBehaviorBacking.d.ts +2 -0
  97. package/dist/esm/behavior/internal/ServerBehaviorBacking.d.ts.map +1 -1
  98. package/dist/esm/behavior/internal/ServerBehaviorBacking.js +37 -0
  99. package/dist/esm/behavior/internal/ServerBehaviorBacking.js.map +1 -1
  100. package/dist/esm/behavior/state/transaction/Transaction.d.ts +18 -18
  101. package/dist/esm/behavior/system/events/EventsBehavior.d.ts +26 -0
  102. package/dist/esm/behavior/system/events/EventsBehavior.d.ts.map +1 -0
  103. package/dist/esm/behavior/system/events/EventsBehavior.js +54 -0
  104. package/dist/esm/behavior/system/events/EventsBehavior.js.map +6 -0
  105. package/dist/esm/behavior/system/events/index.d.ts +7 -0
  106. package/dist/esm/behavior/system/events/index.d.ts.map +1 -0
  107. package/dist/esm/behavior/system/events/index.js +7 -0
  108. package/dist/esm/behavior/system/events/index.js.map +6 -0
  109. package/dist/esm/behavior/system/network/ServerNetworkRuntime.d.ts.map +1 -1
  110. package/dist/esm/behavior/system/network/ServerNetworkRuntime.js +3 -0
  111. package/dist/esm/behavior/system/network/ServerNetworkRuntime.js.map +1 -1
  112. package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts +8 -0
  113. package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts.map +1 -1
  114. package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.js +12 -4
  115. package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.js.map +1 -1
  116. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts +2 -4
  117. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
  118. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +6 -8
  119. package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
  120. package/dist/esm/endpoint/Agent.d.ts +1 -1
  121. package/dist/esm/endpoint/Agent.js +1 -1
  122. package/dist/esm/endpoint/index.d.ts +1 -1
  123. package/dist/esm/endpoint/index.d.ts.map +1 -1
  124. package/dist/esm/endpoint/index.js +1 -1
  125. package/dist/esm/endpoint/properties/Behaviors.d.ts +13 -0
  126. package/dist/esm/endpoint/properties/Behaviors.d.ts.map +1 -1
  127. package/dist/esm/endpoint/properties/Behaviors.js +38 -27
  128. package/dist/esm/endpoint/properties/Behaviors.js.map +2 -2
  129. package/dist/esm/endpoint/properties/EndpointInitializer.d.ts +8 -3
  130. package/dist/esm/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
  131. package/dist/esm/endpoint/properties/EndpointInitializer.js +5 -0
  132. package/dist/esm/endpoint/properties/EndpointInitializer.js.map +1 -1
  133. package/dist/esm/endpoint/server/BehaviorServer.d.ts +21 -0
  134. package/dist/esm/endpoint/server/BehaviorServer.d.ts.map +1 -0
  135. package/dist/esm/endpoint/server/BehaviorServer.js +269 -0
  136. package/dist/esm/endpoint/server/BehaviorServer.js.map +6 -0
  137. package/dist/{cjs/endpoint → esm/endpoint/server}/EndpointServer.d.ts +5 -6
  138. package/dist/esm/endpoint/server/EndpointServer.d.ts.map +1 -0
  139. package/dist/esm/endpoint/{EndpointServer.js → server/EndpointServer.js} +38 -14
  140. package/dist/esm/endpoint/server/EndpointServer.js.map +6 -0
  141. package/dist/esm/{node → endpoint}/server/ServerEndpointInitializer.d.ts +4 -4
  142. package/dist/esm/endpoint/server/ServerEndpointInitializer.d.ts.map +1 -0
  143. package/dist/esm/{node → endpoint}/server/ServerEndpointInitializer.js +7 -7
  144. package/dist/esm/endpoint/server/ServerEndpointInitializer.js.map +6 -0
  145. package/dist/esm/endpoint/server/index.d.ts +8 -0
  146. package/dist/esm/endpoint/server/index.d.ts.map +1 -0
  147. package/dist/esm/endpoint/server/index.js +8 -0
  148. package/dist/esm/endpoint/server/index.js.map +6 -0
  149. package/dist/esm/endpoint/type/MutableEndpoint.d.ts.map +1 -1
  150. package/dist/esm/endpoint/type/MutableEndpoint.js +3 -0
  151. package/dist/esm/endpoint/type/MutableEndpoint.js.map +1 -1
  152. package/dist/esm/node/Node.d.ts +5 -0
  153. package/dist/esm/node/Node.d.ts.map +1 -1
  154. package/dist/esm/node/Node.js +17 -2
  155. package/dist/esm/node/Node.js.map +1 -1
  156. package/dist/esm/node/NodeLifecycle.d.ts +11 -1
  157. package/dist/esm/node/NodeLifecycle.d.ts.map +1 -1
  158. package/dist/esm/node/NodeLifecycle.js +15 -1
  159. package/dist/esm/node/NodeLifecycle.js.map +1 -1
  160. package/dist/esm/node/ServerNode.d.ts +3 -2
  161. package/dist/esm/node/ServerNode.d.ts.map +1 -1
  162. package/dist/esm/node/ServerNode.js +10 -10
  163. package/dist/esm/node/ServerNode.js.map +1 -1
  164. package/dist/esm/node/server/ServerEnvironment.js +1 -1
  165. package/dist/esm/node/server/ServerEnvironment.js.map +1 -1
  166. package/dist/esm/node/server/TransactionalInteractionServer.d.ts +2 -2
  167. package/dist/esm/node/server/TransactionalInteractionServer.d.ts.map +1 -1
  168. package/dist/esm/node/server/TransactionalInteractionServer.js +9 -4
  169. package/dist/esm/node/server/TransactionalInteractionServer.js.map +2 -2
  170. package/dist/esm/node/server/index.d.ts +0 -1
  171. package/dist/esm/node/server/index.d.ts.map +1 -1
  172. package/dist/esm/node/server/index.js +0 -1
  173. package/dist/esm/node/server/index.js.map +1 -1
  174. package/package.json +7 -7
  175. package/src/behavior/internal/BehaviorBacking.ts +9 -1
  176. package/src/behavior/internal/ClientBehaviorBacking.ts +6 -1
  177. package/src/behavior/internal/ServerBehaviorBacking.ts +51 -0
  178. package/src/behavior/system/events/EventsBehavior.ts +60 -0
  179. package/src/behavior/system/events/index.ts +7 -0
  180. package/src/behavior/system/network/ServerNetworkRuntime.ts +6 -0
  181. package/src/behaviors/administrator-commissioning/AdministratorCommissioningServer.ts +14 -4
  182. package/src/behaviors/general-diagnostics/GeneralDiagnosticsServer.ts +16 -9
  183. package/src/endpoint/Agent.ts +1 -1
  184. package/src/endpoint/index.ts +1 -1
  185. package/src/endpoint/properties/Behaviors.ts +56 -28
  186. package/src/endpoint/properties/EndpointInitializer.ts +9 -3
  187. package/src/endpoint/server/BehaviorServer.ts +387 -0
  188. package/src/endpoint/{EndpointServer.ts → server/EndpointServer.ts} +50 -20
  189. package/src/{node → endpoint}/server/ServerEndpointInitializer.ts +7 -7
  190. package/src/endpoint/server/index.ts +8 -0
  191. package/src/endpoint/type/MutableEndpoint.ts +4 -0
  192. package/src/node/Node.ts +22 -4
  193. package/src/node/NodeLifecycle.ts +17 -1
  194. package/src/node/ServerNode.ts +11 -13
  195. package/src/node/server/ServerEnvironment.ts +1 -1
  196. package/src/node/server/TransactionalInteractionServer.ts +13 -7
  197. package/src/node/server/index.ts +0 -1
  198. package/dist/cjs/behavior/internal/ClusterServerBacking.d.ts +0 -36
  199. package/dist/cjs/behavior/internal/ClusterServerBacking.d.ts.map +0 -1
  200. package/dist/cjs/behavior/internal/ClusterServerBacking.js +0 -342
  201. package/dist/cjs/behavior/internal/ClusterServerBacking.js.map +0 -6
  202. package/dist/cjs/endpoint/EndpointServer.d.ts.map +0 -1
  203. package/dist/cjs/endpoint/EndpointServer.js.map +0 -6
  204. package/dist/cjs/node/server/ServerEndpointInitializer.d.ts.map +0 -1
  205. package/dist/cjs/node/server/ServerEndpointInitializer.js.map +0 -6
  206. package/dist/esm/behavior/internal/ClusterServerBacking.d.ts +0 -36
  207. package/dist/esm/behavior/internal/ClusterServerBacking.d.ts.map +0 -1
  208. package/dist/esm/behavior/internal/ClusterServerBacking.js +0 -330
  209. package/dist/esm/behavior/internal/ClusterServerBacking.js.map +0 -6
  210. package/dist/esm/endpoint/EndpointServer.d.ts.map +0 -1
  211. package/dist/esm/endpoint/EndpointServer.js.map +0 -6
  212. package/dist/esm/node/server/ServerEndpointInitializer.d.ts.map +0 -1
  213. package/dist/esm/node/server/ServerEndpointInitializer.js.map +0 -6
  214. package/src/behavior/internal/ClusterServerBacking.ts +0 -446
@@ -4,9 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import { Behavior } from "#behavior/Behavior.js";
8
- import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
9
- import { Endpoint } from "../Endpoint.js";
7
+ import type { Behavior } from "#behavior/Behavior.js";
8
+ import type { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
9
+ import type { Agent } from "#endpoint/Agent.js";
10
+ import type { Endpoint } from "../Endpoint.js";
10
11
 
11
12
  /**
12
13
  * Base class for {@link Endpoint} initialization services.
@@ -35,4 +36,9 @@ export abstract class EndpointInitializer {
35
36
  * @returns a new {@link BehaviorBacking}
36
37
  */
37
38
  abstract createBacking(endpoint: Endpoint, type: Behavior.Type): BehaviorBacking;
39
+
40
+ /**
41
+ * Invoked after behaviors are initialized but before the initialization transaction commits.
42
+ */
43
+ behaviorsInitialized(_agent: Agent) {}
38
44
  }
@@ -0,0 +1,387 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { AccessControl } from "#behavior/AccessControl.js";
8
+ import { Behavior } from "#behavior/Behavior.js";
9
+ import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
10
+ import { type ClusterEvents, Contextual, Resource } from "#behavior/index.js";
11
+ import { StructManager } from "#behavior/state/managed/values/StructManager.js";
12
+ import { Status } from "#behavior/state/transaction/Status.js";
13
+ import { Val } from "#behavior/state/Val.js";
14
+ import { Endpoint } from "#endpoint/Endpoint.js";
15
+ import {
16
+ camelize,
17
+ Diagnostic,
18
+ ImplementationError,
19
+ InternalError,
20
+ isObject,
21
+ Logger,
22
+ MaybePromise,
23
+ Observable,
24
+ ObserverGroup,
25
+ } from "#general";
26
+ import { CommandModel, ElementTag } from "#model";
27
+ import {
28
+ AttributeServer,
29
+ ClusterDatasource,
30
+ ClusterServer,
31
+ CommandServer,
32
+ createAttributeServer as ConstructAttributeServer,
33
+ EventServer,
34
+ FabricManager,
35
+ Message,
36
+ OccurrenceManager,
37
+ SecureSession,
38
+ } from "#protocol";
39
+ import { Attribute, Command, Event } from "#types";
40
+ import type { EndpointServer } from "./EndpointServer.js";
41
+
42
+ const logger = Logger.get("BehaviorServer");
43
+
44
+ export interface BehaviorServer extends ClusterServer {
45
+ close(): void;
46
+ }
47
+
48
+ /**
49
+ * Create a {@link ClusterServer} for a {@link ClusterBehavior}.
50
+ *
51
+ * Note that we only create servers for cluster behaviors. Other {@link Behavior} implementations do not have proper
52
+ * metadata to go online.
53
+ *
54
+ * TODO - refactor element server management after we remove the old API
55
+ */
56
+ export function BehaviorServer(endpointServer: EndpointServer, type: ClusterBehavior.Type): BehaviorServer {
57
+ const { id, name, attributes, commands, events } = type.cluster;
58
+
59
+ const { endpoint } = endpointServer;
60
+ const datasource = createClusterDatasource(endpoint, type);
61
+ const observers = new ObserverGroup();
62
+
63
+ const clusterServer: BehaviorServer = {
64
+ id,
65
+ name,
66
+ datasource,
67
+ attributes: {},
68
+ commands: {},
69
+ events: {},
70
+ close() {
71
+ observers.close();
72
+ },
73
+ };
74
+
75
+ // Extract read-only state and observables for setup purposes
76
+ const stateView = endpoint.stateOf(type) as Val.Struct;
77
+ const observables = endpoint.eventsOf(type) as unknown as Record<string, Observable>;
78
+ const elements = endpointServer.endpoint.behaviors.elementsOf(type);
79
+
80
+ // Add attribute servers. Include global attributes as well as cluster attributes
81
+ for (const name of elements.attributes) {
82
+ const attribute = attributes[name];
83
+
84
+ const server = createAttributeServer(
85
+ name,
86
+ attribute,
87
+ endpoint,
88
+ type,
89
+ datasource,
90
+ stateView,
91
+ observables,
92
+ observers,
93
+ );
94
+
95
+ clusterServer.attributes[name] = server;
96
+ server.assignToEndpoint(endpointServer);
97
+ }
98
+
99
+ // Add command servers
100
+ for (const name of elements.commands) {
101
+ const command = commands[name];
102
+
103
+ clusterServer.commands[name] = createCommandServer(name, command, endpoint, type);
104
+ }
105
+
106
+ // Event servers
107
+ for (const name of elements.events) {
108
+ const server = createEventServer(name, events[name], endpoint, type, observables, observers);
109
+
110
+ clusterServer.events[name] = server;
111
+ server.assignToEndpoint(endpointServer);
112
+ }
113
+
114
+ return clusterServer;
115
+ }
116
+
117
+ /**
118
+ * Create the {@link ClusterDatasource} that adapts the Behavior API to the AttributeServer API.
119
+ */
120
+ function createClusterDatasource(endpoint: Endpoint, type: Behavior.Type): ClusterDatasource {
121
+ const env = endpoint.env;
122
+
123
+ return {
124
+ get version() {
125
+ return endpoint.behaviors.versionOf(type);
126
+ },
127
+
128
+ get eventHandler() {
129
+ return env.get(OccurrenceManager);
130
+ },
131
+
132
+ get fabrics() {
133
+ return env.get(FabricManager).fabrics;
134
+ },
135
+
136
+ // We handle change management ourselves
137
+ changed() {},
138
+
139
+ // We handle version management ourselves
140
+ increaseVersion() {
141
+ return this.version;
142
+ },
143
+ };
144
+ }
145
+
146
+ function createAttributeServer(
147
+ name: string,
148
+ definition: Attribute<any, any>,
149
+ endpoint: Endpoint,
150
+ type: ClusterBehavior.Type,
151
+ datasource: ClusterDatasource,
152
+ stateView: Val.Struct,
153
+ observables: Record<string, Observable>,
154
+ observers: ObserverGroup,
155
+ ) {
156
+ function getter(_session: any, _endpoint: any, _isFabricFiltered: any, message?: Message) {
157
+ if (!message) {
158
+ // If there is no message this is getLocal
159
+ return stateView[name];
160
+ }
161
+
162
+ const behavior = behaviorFor(endpoint, type, message);
163
+
164
+ behavior.context.activity?.frame(`read ${name}`);
165
+
166
+ const trace = behavior.context.trace;
167
+ if (trace) {
168
+ trace.path = endpoint.path.at(name);
169
+ }
170
+
171
+ logger.debug("Read", Diagnostic.strong(`${endpoint}.state.${name}`), "via", behavior.context.transaction.via);
172
+
173
+ const state = behavior.state as Val.Struct;
174
+
175
+ StructManager.assertDirectReadAuthorized(state, name);
176
+
177
+ if (trace) {
178
+ trace.output = state[name];
179
+ }
180
+
181
+ return state[name];
182
+ }
183
+
184
+ function setter(value: any, _session: any, _endpoint: any, message?: Message) {
185
+ const behavior = behaviorFor(endpoint, type, message);
186
+
187
+ behavior.context.activity?.frame(`write ${name}`);
188
+
189
+ logger.info("Write", Diagnostic.strong(`${endpoint}.state.${name}`), "via", behavior.context.transaction.via);
190
+
191
+ const trace = behavior.context.trace;
192
+ if (trace) {
193
+ trace.path = endpoint.path.at(name);
194
+ trace.input = value;
195
+ }
196
+
197
+ const state = behavior.state as Val.Struct;
198
+
199
+ state[name] = value;
200
+
201
+ // If the transaction is a write transaction, report that the attribute is updated
202
+ return behavior.context.transaction?.status === Status.Exclusive;
203
+ }
204
+
205
+ const server = ConstructAttributeServer(
206
+ type.cluster,
207
+ definition,
208
+ name,
209
+ stateView[name],
210
+ datasource,
211
+ getter,
212
+ setter,
213
+ );
214
+
215
+ // Wire events (FixedAttributeServer is not an AttributeServer so we skip that)
216
+ if (server instanceof AttributeServer) {
217
+ const observable = observables[`${name}$Changed`] as ClusterEvents.AttributeObservable;
218
+ if (observable !== undefined) {
219
+ observers.on(observable, (_value, _oldValue, context) => {
220
+ const session = context.session;
221
+ if (session instanceof SecureSession) {
222
+ server.updated(session);
223
+ } else {
224
+ server.updatedLocal();
225
+ }
226
+ });
227
+ }
228
+ }
229
+
230
+ return server;
231
+ }
232
+
233
+ function createCommandServer(
234
+ name: string,
235
+ definition: Command<any, any, any>,
236
+ endpoint: Endpoint,
237
+ type: ClusterBehavior.Type,
238
+ ) {
239
+ // TODO: Introduce nicer ways to get command incl caching and such, aka "make api suck less"
240
+ const schema = type.schema?.member(camelize(name, true), [ElementTag.Command]) as CommandModel;
241
+ if (schema === undefined) {
242
+ throw new ImplementationError(`There is no metadata for command ${name}`);
243
+ }
244
+ const access = AccessControl(schema);
245
+
246
+ const handler = (request: unknown, _session: unknown, message: Message) => {
247
+ let requestDiagnostic: unknown;
248
+ if (isObject(request)) {
249
+ requestDiagnostic = Diagnostic.dict(request);
250
+ } else if (request !== undefined) {
251
+ requestDiagnostic = request;
252
+ } else {
253
+ requestDiagnostic = Diagnostic.weak("(no payload)");
254
+ }
255
+
256
+ const behavior = behaviorFor(endpoint, type, message);
257
+
258
+ const path = endpoint.path.at(name);
259
+
260
+ const trace = behavior.context.trace;
261
+ if (trace) {
262
+ trace.path = endpoint.path.at(name);
263
+ trace.input = request;
264
+ }
265
+
266
+ logger.info("Invoke", Diagnostic.strong(path.toString()), behavior.context.transaction.via, requestDiagnostic);
267
+
268
+ access.authorizeInvoke(behavior.context, {
269
+ path,
270
+ cluster: behavior.cluster.id,
271
+ });
272
+
273
+ let isAsync = false;
274
+ let activity: undefined | Disposable;
275
+ let result: unknown;
276
+ try {
277
+ activity = behavior.context?.activity?.frame(`invoke ${name}`);
278
+
279
+ const invoke = (behavior as unknown as Record<string, (arg: unknown) => unknown>)[name].bind(behavior);
280
+
281
+ // Lock if necessary, then invoke
282
+ if ((behavior.constructor as ClusterBehavior.Type).lockOnInvoke) {
283
+ const tx = behavior.context.transaction;
284
+ if (Resource.isLocked(behavior)) {
285
+ // Automatic locking with locked resource; requires async lock acquisition
286
+ result = (async function invokeAsync() {
287
+ await tx.addResources(behavior);
288
+ await tx.begin();
289
+ return invoke(request);
290
+ })();
291
+ } else {
292
+ // Automatic locking on unlocked resource; may proceed synchronously
293
+ tx.addResourcesSync(behavior);
294
+ tx.beginSync();
295
+ result = invoke(request);
296
+ }
297
+ } else {
298
+ // Automatic locking disabled
299
+ result = invoke(request);
300
+ }
301
+
302
+ if (MaybePromise.is(result)) {
303
+ isAsync = true;
304
+ result = Promise.resolve(result)
305
+ .then(result => {
306
+ if (trace) {
307
+ trace.output = result;
308
+ }
309
+ return result;
310
+ })
311
+ .finally(() => activity?.[Symbol.dispose]());
312
+ } else if (trace) {
313
+ trace.output = result;
314
+ }
315
+ } finally {
316
+ if (!isAsync) {
317
+ activity?.[Symbol.dispose]();
318
+ }
319
+ }
320
+
321
+ return result;
322
+ };
323
+
324
+ const server = new CommandServer(
325
+ definition.requestId,
326
+ definition.responseId,
327
+ name,
328
+ definition.requestSchema,
329
+ definition.responseSchema,
330
+ definition.timed,
331
+ definition.invokeAcl,
332
+ handler,
333
+ );
334
+
335
+ // Eliminate redundant diagnostic messages
336
+ server.debug = () => {};
337
+
338
+ return server;
339
+ }
340
+
341
+ function createEventServer(
342
+ name: string,
343
+ definition: Event<any, any>,
344
+ endpoint: Endpoint,
345
+ type: ClusterBehavior.Type,
346
+ observables: Record<string, Observable>,
347
+ observers: ObserverGroup,
348
+ ) {
349
+ const observable = observables[name] as ClusterEvents.EventObservable;
350
+
351
+ const server = new EventServer(
352
+ definition.id,
353
+ type.cluster.id,
354
+ name,
355
+ definition.schema,
356
+ definition.priority,
357
+ definition.readAcl,
358
+ );
359
+
360
+ if (observable !== undefined) {
361
+ observers.on(observable, (payload, _context) => {
362
+ const maybePromise = server.triggerEvent(payload);
363
+ if (MaybePromise.is(maybePromise)) {
364
+ endpoint.env.runtime.add(maybePromise);
365
+ }
366
+ });
367
+ }
368
+
369
+ const promise = server.bindToEventHandler(endpoint.env.get(OccurrenceManager));
370
+ if (MaybePromise.is(promise)) {
371
+ // Current code structure means this should never happen. Refactor after removal of old API will resolve this
372
+ throw new InternalError("Event handler binding returned a promise");
373
+ }
374
+
375
+ return server;
376
+ }
377
+
378
+ function behaviorFor(endpoint: Endpoint, type: ClusterBehavior.Type, message: Message | undefined) {
379
+ const context = Contextual.contextOf(message);
380
+ if (!context) {
381
+ throw new InternalError("Message context not installed");
382
+ }
383
+
384
+ const agent = context.agentFor(endpoint);
385
+
386
+ return agent.get(type);
387
+ }
@@ -6,13 +6,11 @@
6
6
 
7
7
  import { Behavior } from "#behavior/Behavior.js";
8
8
  import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
9
- import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
10
- import { ClusterServerBacking } from "#behavior/internal/ClusterServerBacking.js";
11
- import { ServerBehaviorBacking } from "#behavior/internal/ServerBehaviorBacking.js";
12
9
  import { ImplementationError, InternalError, NotImplementedError } from "#general";
13
10
  import { ClusterClientObj, ClusterServer, EndpointInterface } from "#protocol";
14
11
  import { ClusterId, ClusterType, EndpointNumber } from "#types";
15
- import { Endpoint } from "./Endpoint.js";
12
+ import { Endpoint } from "../Endpoint.js";
13
+ import { BehaviorServer } from "./BehaviorServer.js";
16
14
 
17
15
  const SERVER = Symbol("server");
18
16
  interface ServerEndpoint extends Endpoint {
@@ -25,7 +23,7 @@ interface ServerEndpoint extends Endpoint {
25
23
  export class EndpointServer implements EndpointInterface {
26
24
  #endpoint: Endpoint;
27
25
  #name = "";
28
- readonly #clusterServers = new Map<ClusterId, ClusterServer>();
26
+ readonly #clusterServers = new Map<ClusterId, BehaviorServer>();
29
27
 
30
28
  get endpoint() {
31
29
  return this.#endpoint;
@@ -36,26 +34,24 @@ export class EndpointServer implements EndpointInterface {
36
34
  }
37
35
 
38
36
  constructor(endpoint: Endpoint) {
39
- (endpoint as ServerEndpoint)[SERVER] = this;
37
+ // Sanity checks
38
+ if ((endpoint as ServerEndpoint)[SERVER] !== undefined) {
39
+ throw new InternalError(`Endpoint ${endpoint} cluster server is already initialized`);
40
+ }
41
+ endpoint.construction.assert();
42
+
40
43
  this.#endpoint = endpoint;
41
44
  this.#name = endpoint.type.name;
42
- }
43
45
 
44
- createBacking(type: Behavior.Type): BehaviorBacking {
45
- let backing: BehaviorBacking;
46
- if (type.prototype instanceof ClusterBehavior) {
47
- const cluster = (type as ClusterBehavior.Type).cluster;
46
+ (endpoint as ServerEndpoint)[SERVER] = this;
48
47
 
49
- // Sanity check
50
- if (this.#clusterServers.has(cluster.id)) {
51
- throw new InternalError(`${this.#endpoint}.${cluster.id} initialized multiple times`);
48
+ for (const type of Object.values(endpoint.behaviors.supported)) {
49
+ if (!(type.prototype instanceof ClusterBehavior)) {
50
+ continue;
52
51
  }
53
52
 
54
- backing = new ClusterServerBacking(this, type as ClusterBehavior.Type);
55
- } else {
56
- backing = new ServerBehaviorBacking(this.#endpoint, type, this.#endpoint.behaviors.optionsFor(type));
53
+ this.#serve(type);
57
54
  }
58
- return backing;
59
55
  }
60
56
 
61
57
  get number() {
@@ -66,6 +62,20 @@ export class EndpointServer implements EndpointInterface {
66
62
  return this.#name;
67
63
  }
68
64
 
65
+ updateServers() {
66
+ for (const type of Object.values(this.#endpoint.behaviors.supported)) {
67
+ if (!(type.prototype instanceof ClusterBehavior)) {
68
+ continue;
69
+ }
70
+
71
+ if (this.#clusterServers.has((type as ClusterBehavior.Type).cluster?.id)) {
72
+ continue;
73
+ }
74
+
75
+ this.#serve(type);
76
+ }
77
+ }
78
+
69
79
  getNumber(): EndpointNumber {
70
80
  if (this.number === undefined) {
71
81
  throw new InternalError("Endpoint ID has not been assigned yet");
@@ -103,7 +113,9 @@ export class EndpointServer implements EndpointInterface {
103
113
  }
104
114
 
105
115
  async [Symbol.asyncDispose]() {
106
- // I believe the cluster servers are effectively disposed when the structure is emptied
116
+ for (const server of this.#clusterServers.values()) {
117
+ server.close();
118
+ }
107
119
  this.#clusterServers.clear();
108
120
  delete (this.#endpoint as ServerEndpoint)[SERVER];
109
121
  for (const endpoint of this.#endpoint.parts) {
@@ -118,7 +130,7 @@ export class EndpointServer implements EndpointInterface {
118
130
  // Unused, should move out of EndpointInterface
119
131
  }
120
132
 
121
- addClusterServer(server: ClusterServer): void {
133
+ addClusterServer(server: BehaviorServer): void {
122
134
  this.#clusterServers.set(server.id, server);
123
135
  }
124
136
 
@@ -173,6 +185,24 @@ export class EndpointServer implements EndpointInterface {
173
185
  }
174
186
  }
175
187
 
188
+ /**
189
+ * Install a ClusterServer for a behavior.
190
+ */
191
+ #serve(type: Behavior.Type) {
192
+ // Sanity checks
193
+ const { cluster } = type as ClusterBehavior.Type;
194
+ if (cluster === undefined) {
195
+ throw new InternalError(`Cannot serve ${this.endpoint} behavior ${type.id}} because it is not a cluster`);
196
+ }
197
+ if (this.#clusterServers.has(cluster.id)) {
198
+ throw new ImplementationError(`Duplicate behaviors for ${this.endpoint} define cluster ${cluster.id}`);
199
+ }
200
+
201
+ // Create ClusterServer
202
+ const clusterServer = BehaviorServer(this, type as ClusterBehavior.Type);
203
+ this.#clusterServers.set(cluster.id, clusterServer);
204
+ }
205
+
176
206
  /**
177
207
  * Retrieve the server for an endpoint.
178
208
  */
@@ -6,12 +6,12 @@
6
6
 
7
7
  import { Behavior } from "#behavior/Behavior.js";
8
8
  import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
9
+ import { ServerBehaviorBacking } from "#behavior/internal/ServerBehaviorBacking.js";
9
10
  import { Endpoint } from "#endpoint/Endpoint.js";
10
- import { EndpointServer } from "#endpoint/EndpointServer.js";
11
11
  import { EndpointInitializer } from "#endpoint/properties/EndpointInitializer.js";
12
12
  import { Environment, InternalError, Logger } from "#general";
13
13
  import { DescriptorServer } from "../../behaviors/descriptor/DescriptorServer.js";
14
- import { ServerNodeStore } from "../storage/ServerNodeStore.js";
14
+ import { ServerNodeStore } from "../../node/storage/ServerNodeStore.js";
15
15
 
16
16
  const logger = Logger.get("BehaviorInit");
17
17
 
@@ -50,13 +50,13 @@ export class ServerEndpointInitializer extends EndpointInitializer {
50
50
  }
51
51
 
52
52
  /**
53
- * If a {@link Endpoint} does not yet have a {@link EndpointServer}, create one now, then create a
54
- * {@link BehaviorBacking} for a specific {@link Behavior}.
53
+ * Create the backing.
55
54
  *
56
- * This is where we adapt endpoints and behaviors for a server role.
55
+ * If the behavior is a cluster behavior and the node is already initialized, create a server when the behavior
56
+ * initializes.
57
57
  */
58
- createBacking(endpoint: Endpoint, behavior: Behavior.Type): BehaviorBacking {
59
- return EndpointServer.forEndpoint(endpoint).createBacking(behavior);
58
+ createBacking(endpoint: Endpoint, type: Behavior.Type): BehaviorBacking {
59
+ return new ServerBehaviorBacking(endpoint, type, endpoint.behaviors.optionsFor(type));
60
60
  }
61
61
 
62
62
  /**
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2024 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./EndpointServer.js";
8
+ export * from "./ServerEndpointInitializer.js";
@@ -75,6 +75,10 @@ export function MutableEndpoint<const T extends EndpointType.Options>(options: T
75
75
  behaviors: SupportedBehaviors.extend(this.behaviors, behaviors),
76
76
  });
77
77
  },
78
+
79
+ withBehaviors(this: MutableEndpoint, ...behaviors: Behavior.Type[]) {
80
+ return this.with(...behaviors);
81
+ },
78
82
  } as unknown as MutableEndpoint.With<
79
83
  EndpointType.For<T>,
80
84
  T["behaviors"] extends SupportedBehaviors ? T["behaviors"] : {}
package/src/node/Node.ts CHANGED
@@ -82,6 +82,10 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
82
82
  * Bring the node online.
83
83
  */
84
84
  async start() {
85
+ await this.lifecycle.mutex.produce(this.startWithMutex.bind(this));
86
+ }
87
+
88
+ protected async startWithMutex() {
85
89
  this.env.runtime.add(this);
86
90
 
87
91
  try {
@@ -123,6 +127,10 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
123
127
  * Once the node is offline you may use {@link start} to bring the node online again.
124
128
  */
125
129
  async cancel() {
130
+ await this.lifecycle.mutex.produce(this.cancelWithMutex.bind(this));
131
+ }
132
+
133
+ protected async cancelWithMutex() {
126
134
  if (!this.#runtime) {
127
135
  return;
128
136
  }
@@ -133,17 +141,27 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
133
141
  }
134
142
 
135
143
  override async close() {
144
+ await this.lifecycle.mutex.produce(this.closeWithMutex.bind(this));
145
+ }
146
+
147
+ protected async closeWithMutex() {
136
148
  // The runtime is not designed to operate with a node that is shutting down so destroy it before performing
137
149
  // actual close
138
- //
139
- // TODO - this should probably block other functions like start()
140
150
  if (this.#runtime) {
141
- await this.cancel();
151
+ await this.cancelWithMutex();
142
152
  }
143
153
 
144
154
  await super.close();
145
155
  }
146
156
 
157
+ override async reset() {
158
+ await this.lifecycle.mutex.produce(this.resetWithMutex.bind(this));
159
+ }
160
+
161
+ protected async resetWithMutex() {
162
+ return super.reset();
163
+ }
164
+
147
165
  /**
148
166
  * Create the network runtime.
149
167
  */
@@ -177,7 +195,7 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
177
195
  }
178
196
 
179
197
  override async [Construction.destruct]() {
180
- await this.cancel();
198
+ await this.cancelWithMutex();
181
199
  await super[Construction.destruct]();
182
200
  DiagnosticSource.delete(this);
183
201
  }
@@ -7,7 +7,7 @@
7
7
  import { ActionContext } from "#behavior/context/ActionContext.js";
8
8
  import { Endpoint } from "#endpoint/Endpoint.js";
9
9
  import { EndpointLifecycle } from "#endpoint/properties/EndpointLifecycle.js";
10
- import { AsyncObservable, Observable } from "#general";
10
+ import { AsyncObservable, Mutex, Observable } from "#general";
11
11
 
12
12
  /**
13
13
  * Extended lifecycle information that only applies to root endpoints.
@@ -21,10 +21,13 @@ export class NodeLifecycle extends EndpointLifecycle {
21
21
  #initialized = Observable<[isCommissioned: boolean]>();
22
22
  #isOnline = false;
23
23
  #isCommissioned = false;
24
+ #mutex: Mutex;
24
25
 
25
26
  constructor(endpoint: Endpoint) {
26
27
  super(endpoint);
27
28
 
29
+ this.#mutex = new Mutex(endpoint);
30
+
28
31
  this.#online.on(() => {
29
32
  this.#isOnline = true;
30
33
  });
@@ -97,4 +100,17 @@ export class NodeLifecycle extends EndpointLifecycle {
97
100
  get decommissioned() {
98
101
  return this.#decommissioned;
99
102
  }
103
+
104
+ /**
105
+ * Mutex for protecting node lifecycle transitions.
106
+ *
107
+ * Methods that implement complex async lifecycle transitions use this mutex to ensure conflicting operations cannot
108
+ * intermingle.
109
+ *
110
+ * Generally methods that hold this mutex have a protected "*WithMutex" variant. This allows for nesting of logic
111
+ * that requires the mutex without causing deadlock.
112
+ */
113
+ get mutex() {
114
+ return this.#mutex;
115
+ }
100
116
  }