@matter/protocol 0.13.1-alpha.0-20250508-047aa0277 → 0.13.1-alpha.0-20250511-74ef153aa

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 (118) hide show
  1. package/dist/cjs/action/protocols.d.ts +59 -2
  2. package/dist/cjs/action/protocols.d.ts.map +1 -1
  3. package/dist/cjs/action/request/Read.d.ts +2 -2
  4. package/dist/cjs/action/request/Read.d.ts.map +1 -1
  5. package/dist/cjs/action/request/Read.js +4 -4
  6. package/dist/cjs/action/request/Read.js.map +1 -1
  7. package/dist/cjs/action/response/ReadResult.d.ts +6 -2
  8. package/dist/cjs/action/response/ReadResult.d.ts.map +1 -1
  9. package/dist/cjs/action/server/AttributeResponse.d.ts +46 -17
  10. package/dist/cjs/action/server/AttributeResponse.d.ts.map +1 -1
  11. package/dist/cjs/action/server/AttributeResponse.js +128 -110
  12. package/dist/cjs/action/server/AttributeResponse.js.map +2 -2
  13. package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts +36 -0
  14. package/dist/cjs/action/server/AttributeSubscriptionResponse.d.ts.map +1 -0
  15. package/dist/cjs/action/server/AttributeSubscriptionResponse.js +86 -0
  16. package/dist/cjs/action/server/AttributeSubscriptionResponse.js.map +6 -0
  17. package/dist/cjs/action/server/DataResponse.d.ts +45 -0
  18. package/dist/cjs/action/server/DataResponse.d.ts.map +1 -0
  19. package/dist/cjs/action/server/DataResponse.js +69 -0
  20. package/dist/cjs/action/server/DataResponse.js.map +6 -0
  21. package/dist/cjs/action/server/EventResponse.d.ts +28 -0
  22. package/dist/cjs/action/server/EventResponse.d.ts.map +1 -0
  23. package/dist/cjs/action/server/EventResponse.js +318 -0
  24. package/dist/cjs/action/server/EventResponse.js.map +6 -0
  25. package/dist/cjs/action/server/ServerInteraction.d.ts.map +1 -1
  26. package/dist/cjs/action/server/ServerInteraction.js +15 -2
  27. package/dist/cjs/action/server/ServerInteraction.js.map +1 -1
  28. package/dist/cjs/action/server/index.d.ts +3 -0
  29. package/dist/cjs/action/server/index.d.ts.map +1 -1
  30. package/dist/cjs/action/server/index.js +3 -0
  31. package/dist/cjs/action/server/index.js.map +1 -1
  32. package/dist/cjs/events/OccurrenceManager.d.ts +20 -11
  33. package/dist/cjs/events/OccurrenceManager.d.ts.map +1 -1
  34. package/dist/cjs/events/OccurrenceManager.js +113 -74
  35. package/dist/cjs/events/OccurrenceManager.js.map +1 -1
  36. package/dist/cjs/interaction/InteractionMessenger.d.ts +14 -2
  37. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  38. package/dist/cjs/interaction/InteractionMessenger.js +87 -3
  39. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  40. package/dist/cjs/interaction/index.d.ts +0 -1
  41. package/dist/cjs/interaction/index.d.ts.map +1 -1
  42. package/dist/cjs/interaction/index.js +0 -1
  43. package/dist/cjs/interaction/index.js.map +1 -1
  44. package/dist/cjs/peer/ControllerCommissioningFlow.js +1 -1
  45. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  46. package/dist/cjs/protocol/MessageExchange.js +11 -1
  47. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  48. package/dist/esm/action/protocols.d.ts +59 -2
  49. package/dist/esm/action/protocols.d.ts.map +1 -1
  50. package/dist/esm/action/request/Read.d.ts +2 -2
  51. package/dist/esm/action/request/Read.d.ts.map +1 -1
  52. package/dist/esm/action/request/Read.js +4 -4
  53. package/dist/esm/action/request/Read.js.map +1 -1
  54. package/dist/esm/action/response/ReadResult.d.ts +6 -2
  55. package/dist/esm/action/response/ReadResult.d.ts.map +1 -1
  56. package/dist/esm/action/server/AttributeResponse.d.ts +46 -17
  57. package/dist/esm/action/server/AttributeResponse.d.ts.map +1 -1
  58. package/dist/esm/action/server/AttributeResponse.js +129 -113
  59. package/dist/esm/action/server/AttributeResponse.js.map +1 -1
  60. package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts +36 -0
  61. package/dist/esm/action/server/AttributeSubscriptionResponse.d.ts.map +1 -0
  62. package/dist/esm/action/server/AttributeSubscriptionResponse.js +66 -0
  63. package/dist/esm/action/server/AttributeSubscriptionResponse.js.map +6 -0
  64. package/dist/esm/action/server/DataResponse.d.ts +45 -0
  65. package/dist/esm/action/server/DataResponse.d.ts.map +1 -0
  66. package/dist/esm/action/server/DataResponse.js +49 -0
  67. package/dist/esm/action/server/DataResponse.js.map +6 -0
  68. package/dist/esm/action/server/EventResponse.d.ts +28 -0
  69. package/dist/esm/action/server/EventResponse.d.ts.map +1 -0
  70. package/dist/esm/action/server/EventResponse.js +305 -0
  71. package/dist/esm/action/server/EventResponse.js.map +6 -0
  72. package/dist/esm/action/server/ServerInteraction.d.ts.map +1 -1
  73. package/dist/esm/action/server/ServerInteraction.js +16 -3
  74. package/dist/esm/action/server/ServerInteraction.js.map +1 -1
  75. package/dist/esm/action/server/index.d.ts +3 -0
  76. package/dist/esm/action/server/index.d.ts.map +1 -1
  77. package/dist/esm/action/server/index.js +3 -0
  78. package/dist/esm/action/server/index.js.map +1 -1
  79. package/dist/esm/events/OccurrenceManager.d.ts +20 -11
  80. package/dist/esm/events/OccurrenceManager.d.ts.map +1 -1
  81. package/dist/esm/events/OccurrenceManager.js +117 -80
  82. package/dist/esm/events/OccurrenceManager.js.map +1 -1
  83. package/dist/esm/interaction/InteractionMessenger.d.ts +14 -2
  84. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  85. package/dist/esm/interaction/InteractionMessenger.js +87 -3
  86. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  87. package/dist/esm/interaction/index.d.ts +0 -1
  88. package/dist/esm/interaction/index.d.ts.map +1 -1
  89. package/dist/esm/interaction/index.js +0 -1
  90. package/dist/esm/interaction/index.js.map +1 -1
  91. package/dist/esm/peer/ControllerCommissioningFlow.js +1 -1
  92. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  93. package/dist/esm/protocol/MessageExchange.js +11 -1
  94. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  95. package/package.json +6 -6
  96. package/src/action/protocols.ts +68 -2
  97. package/src/action/request/Read.ts +2 -2
  98. package/src/action/response/ReadResult.ts +8 -1
  99. package/src/action/server/AttributeResponse.ts +145 -118
  100. package/src/action/server/AttributeSubscriptionResponse.ts +90 -0
  101. package/src/action/server/DataResponse.ts +70 -0
  102. package/src/action/server/EventResponse.ts +381 -0
  103. package/src/action/server/ServerInteraction.ts +18 -4
  104. package/src/action/server/index.ts +3 -0
  105. package/src/events/OccurrenceManager.ts +126 -100
  106. package/src/interaction/InteractionMessenger.ts +93 -8
  107. package/src/interaction/index.ts +0 -1
  108. package/src/peer/ControllerCommissioningFlow.ts +1 -1
  109. package/src/protocol/MessageExchange.ts +13 -1
  110. package/dist/cjs/interaction/ServerSubscription.d.ts +0 -116
  111. package/dist/cjs/interaction/ServerSubscription.d.ts.map +0 -1
  112. package/dist/cjs/interaction/ServerSubscription.js +0 -778
  113. package/dist/cjs/interaction/ServerSubscription.js.map +0 -6
  114. package/dist/esm/interaction/ServerSubscription.d.ts +0 -116
  115. package/dist/esm/interaction/ServerSubscription.d.ts.map +0 -1
  116. package/dist/esm/interaction/ServerSubscription.js +0 -778
  117. package/dist/esm/interaction/ServerSubscription.js.map +0 -6
  118. package/src/interaction/ServerSubscription.ts +0 -1038
@@ -0,0 +1,381 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Project CHIP Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { ClusterProtocol, EndpointProtocol, EventTypeProtocol, NodeProtocol } from "#action/protocols.js";
8
+ import { Read } from "#action/request/Read.js";
9
+ import { ReadResult } from "#action/response/ReadResult.js";
10
+ import { AccessControl } from "#action/server/AccessControl.js";
11
+ import { DataResponse, FallbackLimits } from "#action/server/DataResponse.js";
12
+ import { NumberedOccurrence } from "#events/index.js";
13
+ import { InternalError, isObject, Logger } from "#general";
14
+ import { DataModelPath, ElementTag, EventModel } from "#model";
15
+ import {
16
+ EventNumber,
17
+ EventPath,
18
+ FabricIndex,
19
+ NodeId,
20
+ Status,
21
+ StatusCode,
22
+ StatusResponseError,
23
+ TlvSchema,
24
+ } from "#types";
25
+
26
+ const logger = Logger.get("EventResponse");
27
+
28
+ /**
29
+ * Implements read of event data for Matter "read" and "subscribe" interactions.
30
+ *
31
+ * We collect all allowed event paths at first and then iterate over all events and use the ones matching to the paths.
32
+ *
33
+ * TODO - profile; ensure nested functions are properly JITed and/or inlined
34
+ */
35
+ export class EventResponse<
36
+ SessionT extends AccessControl.Session = AccessControl.Session,
37
+ > extends DataResponse<SessionT> {
38
+ // Normalized Event Filter to just our node-id
39
+ #eventMinVersion?: EventNumber;
40
+
41
+ // The Fabric filtering is done when we read the data from OccurrenceManager, so we can determine the parameter once
42
+ #filteredForFabricIndex?: FabricIndex;
43
+
44
+ // The following state updates as data producers execute. This serves both to convey state between functions and as
45
+ // a cache between producers that touch the same endpoint and/or cluster
46
+ #currentEndpoint?: EndpointProtocol;
47
+ #currentCluster?: ClusterProtocol;
48
+
49
+ // Collected allowed and existing event paths to consider when reading events
50
+ #allowedEventPaths = new Map<string, TlvSchema<unknown>>();
51
+
52
+ // Count how many attribute status (on error) and attribute values (on success) we have emitted
53
+ #statusCount = 0;
54
+ #valueCount = 0;
55
+
56
+ constructor(node: NodeProtocol, session: SessionT) {
57
+ super(node, session);
58
+ }
59
+
60
+ async *process({
61
+ eventFilters,
62
+ eventRequests,
63
+ isFabricFiltered,
64
+ }: Read.Events): AsyncGenerator<ReadResult.Chunk, void, void> {
65
+ const nodeId = this.session.fabric === undefined ? NodeId.UNSPECIFIED_NODE_ID : this.nodeId;
66
+
67
+ if (eventFilters !== undefined) {
68
+ for (const { nodeId: filterNodeId, eventMin } of eventFilters) {
69
+ if (filterNodeId === undefined || filterNodeId === nodeId) {
70
+ this.#eventMinVersion = EventNumber(eventMin);
71
+ break;
72
+ }
73
+ }
74
+ }
75
+
76
+ if (isFabricFiltered) {
77
+ this.#filteredForFabricIndex = this.session.fabric ?? FabricIndex.NO_FABRIC;
78
+ }
79
+
80
+ // This path contributes an event value
81
+ // Register paths
82
+ for (const path of eventRequests) {
83
+ if (path.endpointId === undefined || path.clusterId === undefined || path.eventId === undefined) {
84
+ this.#addWildcard(path);
85
+ } else {
86
+ const status = this.#addConcrete(path as ReadResult.ConcreteEventPath);
87
+ if (status !== undefined) {
88
+ // This path is not valid, so emit a status response
89
+ yield [status];
90
+ this.#statusCount++;
91
+ }
92
+ }
93
+ }
94
+
95
+ // Perform actual read of all events
96
+ for await (const data of this.#readAllowedEvents()) {
97
+ yield [data];
98
+ this.#valueCount++;
99
+ }
100
+ }
101
+
102
+ get counts() {
103
+ return {
104
+ status: this.#statusCount,
105
+ value: this.#valueCount,
106
+ existent: this.#allowedEventPaths.size,
107
+ };
108
+ }
109
+
110
+ /** Guarded accessor for this.#currentEndpoint. This should never be undefined */
111
+ get #guardedCurrentEndpoint() {
112
+ if (this.#currentEndpoint === undefined) {
113
+ throw new InternalError("currentEndpoint is not set. Should never happen");
114
+ }
115
+ return this.#currentEndpoint;
116
+ }
117
+
118
+ /** Guarded accessor for this.#currentCluster. This should never be undefined */
119
+ get #guardedCurrentCluster(): ClusterProtocol {
120
+ if (this.#currentCluster === undefined) {
121
+ throw new InternalError("currentCluster is not set. Should never happen");
122
+ }
123
+ return this.#currentCluster;
124
+ }
125
+
126
+ /**
127
+ * Validate a wildcard path and update the internal state.
128
+ */
129
+ #addWildcard(path: EventPath) {
130
+ const { nodeId, clusterId, endpointId, eventId } = path;
131
+
132
+ if (clusterId === undefined && eventId !== undefined) {
133
+ throw new StatusResponseError("Illegal read request with wildcard cluster ID", StatusCode.InvalidAction);
134
+ }
135
+
136
+ if (nodeId !== undefined && nodeId !== this.nodeId) {
137
+ return;
138
+ }
139
+
140
+ if (endpointId === undefined) {
141
+ for (const endpoint of this.node) {
142
+ this.#addEndpointForWildcard(endpoint, path);
143
+ }
144
+ return;
145
+ }
146
+
147
+ const endpoint = this.node[endpointId];
148
+ if (endpoint) {
149
+ return this.#addEndpointForWildcard(endpoint, path);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Validate a concrete path and update the internal state.
155
+ */
156
+ #addConcrete(path: ReadResult.ConcreteEventPath) {
157
+ const { nodeId, endpointId, clusterId, eventId } = path;
158
+ if (nodeId !== undefined && this.nodeId !== nodeId) {
159
+ return this.#asStatus(path, Status.UnsupportedNode);
160
+ }
161
+
162
+ // Resolve path elements
163
+ const endpoint = this.node[endpointId];
164
+ const cluster = endpoint?.[clusterId];
165
+ const event = cluster?.type.events[eventId];
166
+ let limits;
167
+ if (event === undefined) {
168
+ // We still need to authorize the user for access even though this path doesn't resolve. Spec is not
169
+ // explicit on what privilege level we should require as normally that information comes from the resolved
170
+ // event. So attempt to resolve via the active model
171
+ const modelEvent = this.node.matter
172
+ .member(path.clusterId, [ElementTag.Cluster])
173
+ ?.member(path.eventId, [ElementTag.Event]);
174
+
175
+ if (modelEvent) {
176
+ // OK cluster doesn't exist at that location, but we do understand semantically, so use limits from the
177
+ // model
178
+ limits = AccessControl(modelEvent as EventModel).limits;
179
+ } else {
180
+ // We've got no idea. This effectively falls back to "view" privilege
181
+ limits = FallbackLimits;
182
+ }
183
+ } else {
184
+ limits = event.limits;
185
+ }
186
+
187
+ // Validate access. Order here prescribed by 1.4 core spec 8.4.3.2
188
+ // We need some fallback location if cluster is not defined
189
+ const location = {
190
+ ...(cluster?.location ?? {
191
+ path: DataModelPath.none,
192
+ endpoint: endpointId,
193
+ cluster: clusterId,
194
+ }),
195
+ owningFabric: this.session.fabric,
196
+ };
197
+ const permission = this.session.authorityAt(limits.readLevel, location);
198
+ switch (permission) {
199
+ case AccessControl.Authority.Granted:
200
+ break;
201
+
202
+ case AccessControl.Authority.Unauthorized:
203
+ return this.#asStatus(path, Status.UnsupportedAccess);
204
+
205
+ case AccessControl.Authority.Restricted:
206
+ return this.#asStatus(path, Status.AccessRestricted);
207
+
208
+ default:
209
+ throw new InternalError(`Unsupported authorization state ${permission}`);
210
+ }
211
+ if (endpoint === undefined) {
212
+ return this.#asStatus(path, Status.UnsupportedEndpoint);
213
+ }
214
+ if (cluster === undefined) {
215
+ return this.#asStatus(path, Status.UnsupportedCluster);
216
+ }
217
+ if (event === undefined || !cluster.type.events[event.id]) {
218
+ return this.#asStatus(path, Status.UnsupportedEvent);
219
+ }
220
+
221
+ if (this.#currentEndpoint !== endpoint) {
222
+ this.#currentEndpoint = endpoint;
223
+ this.#currentCluster = cluster;
224
+ } else if (this.#currentCluster !== cluster) {
225
+ this.#currentCluster = cluster;
226
+ }
227
+
228
+ // Register allowed event-path
229
+ this.#registerEventPath(path);
230
+ }
231
+
232
+ /**
233
+ * Starts new chunk or adds to current chunk all values from {@link endpoint} selected by {@link path}.
234
+ *
235
+ * Emits previous chunk if it exists and was not for this endpoint. This means that our chunk size is one endpoint
236
+ * worth of data, except for the initial error chunk if there are path errors.
237
+ *
238
+ * TODO - skip endpoints for which subject is unauthorized
239
+ */
240
+ #addEndpointForWildcard(endpoint: EndpointProtocol, path: EventPath) {
241
+ if (this.#currentEndpoint !== endpoint) {
242
+ this.#currentEndpoint = endpoint;
243
+ this.#currentCluster = undefined;
244
+ }
245
+
246
+ const { clusterId } = path;
247
+ if (clusterId === undefined) {
248
+ for (const cluster of endpoint) {
249
+ this.#addClusterForWildcard(cluster, { ...path, endpointId: endpoint.id });
250
+ }
251
+ } else {
252
+ const cluster = endpoint[clusterId];
253
+ if (cluster !== undefined) {
254
+ this.#addClusterForWildcard(cluster, { ...path, endpointId: endpoint.id });
255
+ }
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Read values from a specific {@link cluster} for a wildcard path.
261
+ *
262
+ * Depends on state initialized by {@link #addEndpointForWildcard}.
263
+ *
264
+ * TODO - skip endpoints for which subject is unauthorized as optimization
265
+ */
266
+ #addClusterForWildcard(cluster: ClusterProtocol, path: EventPath) {
267
+ if (this.#currentCluster !== cluster) {
268
+ this.#currentCluster = cluster;
269
+ }
270
+
271
+ const { eventId } = path;
272
+ if (eventId === undefined) {
273
+ for (const event of cluster.type.events) {
274
+ this.#addEventForWildcard(event, {
275
+ ...path,
276
+ clusterId: cluster.type.id,
277
+ });
278
+ }
279
+ } else {
280
+ const event = cluster.type.events[eventId];
281
+ if (event !== undefined) {
282
+ this.#addEventForWildcard(event, {
283
+ ...path,
284
+ clusterId: cluster.type.id,
285
+ });
286
+ }
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Read values from a specific {@link event} for a wildcard path.
292
+ *
293
+ * Depends on state initialized by {@link #addClusterForWildcard}.
294
+ */
295
+ #addEventForWildcard(event: EventTypeProtocol, path: EventPath) {
296
+ if (!this.#guardedCurrentCluster.type.events[event.id]) {
297
+ return; // EVent is not active, so ignore
298
+ }
299
+ if (
300
+ this.session.authorityAt(event.limits.readLevel, this.#guardedCurrentCluster.location) !==
301
+ AccessControl.Authority.Granted
302
+ ) {
303
+ return;
304
+ }
305
+
306
+ this.#registerEventPath({ ...path, eventId: event.id } as ReadResult.ConcreteEventPath);
307
+ }
308
+
309
+ #createEventKey(path: ReadResult.ConcreteEventPath) {
310
+ const { endpointId, clusterId, eventId } = path;
311
+ return `${endpointId}:${clusterId}:${eventId}`;
312
+ }
313
+
314
+ #registerEventPath(path: ReadResult.ConcreteEventPath) {
315
+ const { eventId } = path;
316
+ this.#allowedEventPaths.set(
317
+ this.#createEventKey({
318
+ ...path,
319
+ endpointId: this.#guardedCurrentEndpoint.id,
320
+ clusterId: this.#guardedCurrentCluster.type.id,
321
+ eventId,
322
+ }),
323
+ this.#guardedCurrentCluster.type.events[eventId]!.tlv,
324
+ );
325
+ }
326
+
327
+ async *#readAllowedEvents() {
328
+ for await (const event of this.node.eventHandler.get(this.#eventMinVersion)) {
329
+ const tlv = this.#allowedEventPaths.get(this.#createEventKey(event));
330
+ if (tlv === undefined) {
331
+ // This event is not in the allowed list, so skip it
332
+ continue;
333
+ }
334
+ // Filter out if we need to do fabric filtering and the event is not for the current fabric
335
+ if (this.#filteredForFabricIndex !== undefined) {
336
+ const { payload } = event;
337
+ if (!isObject(payload)) {
338
+ continue;
339
+ }
340
+ const { fabricIndex } = payload;
341
+ if (fabricIndex !== undefined && fabricIndex !== this.#filteredForFabricIndex) {
342
+ continue;
343
+ }
344
+ }
345
+ yield this.#asValue(event, tlv);
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Add a status value.
351
+ */
352
+ #asStatus(path: ReadResult.ConcreteEventPath, status: Status) {
353
+ logger.debug(`Error reading event ${this.node.inspectPath(path)}: Status=${StatusCode[status]}(${status})`);
354
+
355
+ const report: ReadResult.GlobalEventStatus = {
356
+ kind: "event-status",
357
+ path,
358
+ status,
359
+ };
360
+
361
+ return report;
362
+ }
363
+
364
+ /**
365
+ * Add event values as separate entries to the result.
366
+ */
367
+ #asValue(event: NumberedOccurrence, tlv: TlvSchema<unknown>) {
368
+ const { number, epochTimestamp: timestamp, priority, payload: value, endpointId, clusterId, eventId } = event;
369
+ const report: ReadResult.EventValue = {
370
+ kind: "event-value",
371
+ path: { endpointId, clusterId, eventId },
372
+ number,
373
+ priority,
374
+ timestamp,
375
+ value,
376
+ tlv,
377
+ };
378
+
379
+ return report;
380
+ }
381
+ }
@@ -15,9 +15,12 @@ import { ReadResult } from "#action/response/ReadResult.js";
15
15
  import { SubscribeResult } from "#action/response/SubscribeResult.js";
16
16
  import { WriteResult } from "#action/response/WriteResult.js";
17
17
  import { AccessControl } from "#action/server/AccessControl.js";
18
- import { NotImplementedError } from "#general";
18
+ import { EventResponse } from "#action/server/EventResponse.js";
19
+ import { Logger, NotImplementedError } from "#general";
19
20
  import { AttributeResponse } from "./AttributeResponse.js";
20
21
 
22
+ const logger = Logger.get("ServerInteraction");
23
+
21
24
  /**
22
25
  * Implementation of server interaction.
23
26
  *
@@ -40,11 +43,22 @@ export class ServerInteraction<SessionT extends AccessControl.Session = AccessCo
40
43
  async *read(request: Read, session: SessionT): ReadResult {
41
44
  // TODO - validate request
42
45
 
43
- if (Read.isAttribute(request)) {
44
- yield* new AttributeResponse(this.#node, session, request);
46
+ let readInfo = "";
47
+ if (Read.containsAttribute(request)) {
48
+ const attributeReader = new AttributeResponse(this.#node, session);
49
+ yield* attributeReader.process(request);
50
+
51
+ const { existent, status, value } = attributeReader.counts;
52
+ readInfo = `${existent} matching attributes (${status ? `${status} status, ` : ""}${value ? `${value} values` : ""})`;
45
53
  }
46
54
 
47
- // TODO - event reads
55
+ if (Read.containsEvent(request)) {
56
+ const eventReader = new EventResponse(this.#node, session);
57
+ yield* eventReader.process(request);
58
+ const { existent, status, value } = eventReader.counts;
59
+ readInfo += `${readInfo.length > 0 ? ", " : ""}${existent} matching events (${status ? `${status} status, ` : ""}${value ? `${value} values` : ""})`;
60
+ }
61
+ logger.debug(`Read request resolved to ${readInfo}`);
48
62
  }
49
63
 
50
64
  subscribe(_request: Subscribe, _session?: SessionT): SubscribeResult {
@@ -6,4 +6,7 @@
6
6
 
7
7
  export * from "./AccessControl.js";
8
8
  export * from "./AttributeResponse.js";
9
+ export * from "./AttributeSubscriptionResponse.js";
10
+ export * from "./DataResponse.js";
11
+ export * from "./EventResponse.js";
9
12
  export * from "./ServerInteraction.js";