@matter/node 0.16.0-alpha.0-20251003-dc6d5523d → 0.16.0-alpha.0-20251006-3fe1e7c57

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 (87) hide show
  1. package/dist/cjs/behavior/internal/BehaviorBacking.d.ts +4 -0
  2. package/dist/cjs/behavior/internal/BehaviorBacking.d.ts.map +1 -1
  3. package/dist/cjs/behavior/internal/BehaviorBacking.js +6 -0
  4. package/dist/cjs/behavior/internal/BehaviorBacking.js.map +1 -1
  5. package/dist/cjs/behavior/supervision/RootSupervisor.d.ts +4 -4
  6. package/dist/cjs/behavior/supervision/RootSupervisor.d.ts.map +1 -1
  7. package/dist/cjs/behavior/supervision/RootSupervisor.js +8 -10
  8. package/dist/cjs/behavior/supervision/RootSupervisor.js.map +1 -1
  9. package/dist/cjs/behavior/supervision/ValueSupervisor.d.ts +1 -1
  10. package/dist/cjs/behavior/system/commissioning/CommissioningClient.d.ts.map +1 -1
  11. package/dist/cjs/behavior/system/commissioning/CommissioningClient.js +15 -0
  12. package/dist/cjs/behavior/system/commissioning/CommissioningClient.js.map +1 -1
  13. package/dist/cjs/behavior/system/controller/ControllerBehavior.d.ts +7 -0
  14. package/dist/cjs/behavior/system/controller/ControllerBehavior.d.ts.map +1 -1
  15. package/dist/cjs/behavior/system/controller/ControllerBehavior.js +10 -0
  16. package/dist/cjs/behavior/system/controller/ControllerBehavior.js.map +1 -1
  17. package/dist/cjs/behavior/system/subscriptions/SubscriptionsServer.js +1 -1
  18. package/dist/cjs/behavior/system/subscriptions/SubscriptionsServer.js.map +1 -1
  19. package/dist/cjs/endpoint/Endpoint.d.ts +12 -1
  20. package/dist/cjs/endpoint/Endpoint.d.ts.map +1 -1
  21. package/dist/cjs/endpoint/Endpoint.js +25 -15
  22. package/dist/cjs/endpoint/Endpoint.js.map +1 -1
  23. package/dist/cjs/endpoint/properties/Behaviors.d.ts +5 -1
  24. package/dist/cjs/endpoint/properties/Behaviors.d.ts.map +1 -1
  25. package/dist/cjs/endpoint/properties/Behaviors.js +7 -0
  26. package/dist/cjs/endpoint/properties/Behaviors.js.map +1 -1
  27. package/dist/cjs/node/ClientNode.d.ts +2 -0
  28. package/dist/cjs/node/ClientNode.d.ts.map +1 -1
  29. package/dist/cjs/node/ClientNode.js +14 -0
  30. package/dist/cjs/node/ClientNode.js.map +1 -1
  31. package/dist/cjs/node/client/ClientStructure.d.ts.map +1 -1
  32. package/dist/cjs/node/client/ClientStructure.js.map +1 -1
  33. package/dist/cjs/node/server/ProtocolService.js +1 -1
  34. package/dist/cjs/storage/client/ClientEndpointStore.d.ts +4 -0
  35. package/dist/cjs/storage/client/ClientEndpointStore.d.ts.map +1 -1
  36. package/dist/cjs/storage/client/ClientEndpointStore.js +6 -0
  37. package/dist/cjs/storage/client/ClientEndpointStore.js.map +1 -1
  38. package/dist/esm/behavior/internal/BehaviorBacking.d.ts +4 -0
  39. package/dist/esm/behavior/internal/BehaviorBacking.d.ts.map +1 -1
  40. package/dist/esm/behavior/internal/BehaviorBacking.js +6 -0
  41. package/dist/esm/behavior/internal/BehaviorBacking.js.map +1 -1
  42. package/dist/esm/behavior/supervision/RootSupervisor.d.ts +4 -4
  43. package/dist/esm/behavior/supervision/RootSupervisor.d.ts.map +1 -1
  44. package/dist/esm/behavior/supervision/RootSupervisor.js +8 -11
  45. package/dist/esm/behavior/supervision/RootSupervisor.js.map +1 -1
  46. package/dist/esm/behavior/supervision/ValueSupervisor.d.ts +1 -1
  47. package/dist/esm/behavior/system/commissioning/CommissioningClient.d.ts.map +1 -1
  48. package/dist/esm/behavior/system/commissioning/CommissioningClient.js +19 -1
  49. package/dist/esm/behavior/system/commissioning/CommissioningClient.js.map +1 -1
  50. package/dist/esm/behavior/system/controller/ControllerBehavior.d.ts +7 -0
  51. package/dist/esm/behavior/system/controller/ControllerBehavior.d.ts.map +1 -1
  52. package/dist/esm/behavior/system/controller/ControllerBehavior.js +10 -0
  53. package/dist/esm/behavior/system/controller/ControllerBehavior.js.map +1 -1
  54. package/dist/esm/behavior/system/subscriptions/SubscriptionsServer.js +1 -1
  55. package/dist/esm/behavior/system/subscriptions/SubscriptionsServer.js.map +1 -1
  56. package/dist/esm/endpoint/Endpoint.d.ts +12 -1
  57. package/dist/esm/endpoint/Endpoint.d.ts.map +1 -1
  58. package/dist/esm/endpoint/Endpoint.js +25 -15
  59. package/dist/esm/endpoint/Endpoint.js.map +1 -1
  60. package/dist/esm/endpoint/properties/Behaviors.d.ts +5 -1
  61. package/dist/esm/endpoint/properties/Behaviors.d.ts.map +1 -1
  62. package/dist/esm/endpoint/properties/Behaviors.js +7 -0
  63. package/dist/esm/endpoint/properties/Behaviors.js.map +1 -1
  64. package/dist/esm/node/ClientNode.d.ts +2 -0
  65. package/dist/esm/node/ClientNode.d.ts.map +1 -1
  66. package/dist/esm/node/ClientNode.js +16 -2
  67. package/dist/esm/node/ClientNode.js.map +1 -1
  68. package/dist/esm/node/client/ClientStructure.d.ts.map +1 -1
  69. package/dist/esm/node/client/ClientStructure.js.map +1 -1
  70. package/dist/esm/node/server/ProtocolService.js +1 -1
  71. package/dist/esm/storage/client/ClientEndpointStore.d.ts +4 -0
  72. package/dist/esm/storage/client/ClientEndpointStore.d.ts.map +1 -1
  73. package/dist/esm/storage/client/ClientEndpointStore.js +6 -0
  74. package/dist/esm/storage/client/ClientEndpointStore.js.map +1 -1
  75. package/package.json +7 -7
  76. package/src/behavior/internal/BehaviorBacking.ts +7 -0
  77. package/src/behavior/supervision/RootSupervisor.ts +16 -11
  78. package/src/behavior/supervision/ValueSupervisor.ts +1 -1
  79. package/src/behavior/system/commissioning/CommissioningClient.ts +22 -0
  80. package/src/behavior/system/controller/ControllerBehavior.ts +13 -0
  81. package/src/behavior/system/subscriptions/SubscriptionsServer.ts +1 -1
  82. package/src/endpoint/Endpoint.ts +36 -13
  83. package/src/endpoint/properties/Behaviors.ts +9 -0
  84. package/src/node/ClientNode.ts +28 -2
  85. package/src/node/client/ClientStructure.ts +7 -0
  86. package/src/node/server/ProtocolService.ts +1 -1
  87. package/src/storage/client/ClientEndpointStore.ts +7 -0
@@ -8,11 +8,11 @@ import { camelize, InternalError } from "#general";
8
8
  import {
9
9
  AttributeModel,
10
10
  ClusterModel,
11
- ElementTag,
12
11
  FeatureMap,
13
12
  FeatureSet,
14
13
  Matter,
15
14
  Model,
15
+ ModelIndex,
16
16
  Schema,
17
17
  Scope,
18
18
  ValueModel,
@@ -59,7 +59,7 @@ export class RootSupervisor implements ValueSupervisor {
59
59
  #rootSchema: Schema;
60
60
  #root: ValueSupervisor;
61
61
  #memberNames?: Set<string>;
62
- #attributeNamesToIds?: Map<string, AttributeId>; // Whenever we need more than Attributes and Fields, we need to generalize
62
+ #propertyNamesAndIds?: Map<string, AttributeId | undefined>;
63
63
 
64
64
  /**
65
65
  * Create a new supervisor.
@@ -86,12 +86,20 @@ export class RootSupervisor implements ValueSupervisor {
86
86
  /**
87
87
  * Obtain the supervisor for schema. The result is cached.
88
88
  */
89
- static for(schema: Schema) {
89
+ static for<T extends Schema | undefined>(
90
+ schema: T,
91
+ ): T extends undefined ? RootSupervisor | undefined : RootSupervisor {
92
+ if (!schema) {
93
+ return undefined as any;
94
+ }
95
+
90
96
  if (cache.has(schema)) {
91
97
  return cache.get(schema)!;
92
98
  }
99
+
93
100
  const supervisor = new RootSupervisor(schema);
94
101
  cache.set(schema, supervisor);
102
+
95
103
  return supervisor;
96
104
  }
97
105
 
@@ -168,17 +176,14 @@ export class RootSupervisor implements ValueSupervisor {
168
176
  return persistent;
169
177
  }
170
178
 
171
- get attributeNamesToIds() {
172
- let names = this.#attributeNamesToIds;
179
+ get propertyNamesAndIds() {
180
+ let names = this.#propertyNamesAndIds;
173
181
  if (!names) {
174
182
  names = new Map();
175
183
  for (const member of this.#members) {
176
- if (member.id === undefined || member.tag !== ElementTag.Attribute) {
177
- continue;
178
- }
179
- names.set(camelize(member.name), AttributeId(member.id));
184
+ names.set(camelize(member.name), member.id as AttributeId | undefined);
180
185
  }
181
- this.#attributeNamesToIds = names;
186
+ this.#propertyNamesAndIds = names;
182
187
  }
183
188
  return names;
184
189
  }
@@ -188,7 +193,7 @@ export class RootSupervisor implements ValueSupervisor {
188
193
  *
189
194
  * The {@link Scope.ConformanceMode} defaults to "deconflicted" if you do not override.
190
195
  */
191
- membersOf<T extends Schema>(schema: T, options: Scope.MemberOptions = {}): Model.ChildOf<T>[] {
196
+ membersOf<T extends Schema>(schema: T, options: Scope.MemberOptions = {}): ModelIndex<Model.ChildOf<T>> {
192
197
  if (options.conformance === undefined) {
193
198
  options = { ...options, conformance: "deconflicted" };
194
199
  }
@@ -54,7 +54,7 @@ export interface ValueSupervisor {
54
54
  readonly manage: ValueSupervisor.Manage;
55
55
 
56
56
  /**
57
- * Apply changes. Does not validate perform validation.
57
+ * Apply changes. Does not perform validation.
58
58
  */
59
59
  readonly patch: ValueSupervisor.Patch;
60
60
 
@@ -8,8 +8,10 @@ import { Behavior } from "#behavior/Behavior.js";
8
8
  import { Events as BaseEvents } from "#behavior/Events.js";
9
9
  import { OperationalCredentialsClient } from "#behaviors/operational-credentials";
10
10
  import {
11
+ Diagnostic,
11
12
  Duration,
12
13
  ImplementationError,
14
+ Logger,
13
15
  NotImplementedError,
14
16
  Observable,
15
17
  ServerAddress,
@@ -45,6 +47,8 @@ import { ControllerBehavior } from "../controller/ControllerBehavior.js";
45
47
  import { NetworkClient } from "../network/NetworkClient.js";
46
48
  import { RemoteDescriptor } from "./RemoteDescriptor.js";
47
49
 
50
+ const logger = Logger.get("CommissioningClient");
51
+
48
52
  /**
49
53
  * Client functionality related to commissioning.
50
54
  *
@@ -155,6 +159,13 @@ export class CommissioningClient extends Behavior {
155
159
  network.state.startupSubscription = opts.startupSubscription;
156
160
  network.state.caseAuthenticatedTags = opts.caseAuthenticatedTags;
157
161
 
162
+ logger.notice(
163
+ "Commissioned",
164
+ Diagnostic.strong(this.endpoint.id),
165
+ "as",
166
+ Diagnostic.strong(this.endpoint.identity),
167
+ );
168
+
158
169
  node.lifecycle.commissioned.emit(this.context);
159
170
 
160
171
  await node.start();
@@ -177,11 +188,22 @@ export class CommissioningClient extends Behavior {
177
188
  throw new ImplementationError("Cannot decommission node that is not commissioned");
178
189
  }
179
190
 
191
+ const formerAddress = PeerAddress(peerAddress).toString();
192
+
180
193
  const opcreds = this.agent.get(OperationalCredentialsClient);
181
194
 
182
195
  await opcreds.removeFabric({ fabricIndex: opcreds.state.currentFabricIndex });
183
196
 
184
197
  this.state.peerAddress = undefined;
198
+
199
+ await this.context.transaction.commit();
200
+
201
+ logger.info(
202
+ "Decommissioned",
203
+ Diagnostic.strong(this.endpoint.id),
204
+ "formerly",
205
+ Diagnostic.strong(formerAddress),
206
+ );
185
207
  }
186
208
 
187
209
  /**
@@ -23,6 +23,7 @@ import {
23
23
  Scanner,
24
24
  ScannerSet,
25
25
  } from "#protocol";
26
+ import { FabricId } from "@matter/types";
26
27
  import type { CommissioningClient } from "../commissioning/CommissioningClient.js";
27
28
  import { CommissioningServer } from "../commissioning/CommissioningServer.js";
28
29
  import { NetworkServer } from "../network/NetworkServer.js";
@@ -48,6 +49,7 @@ export class ControllerBehavior extends Behavior {
48
49
  throw new ImplementationError("adminFabricLabel must be set for ControllerBehavior.");
49
50
  }
50
51
  const adminFabricLabel = this.state.adminFabricLabel;
52
+ const adminFabricId = this.state.adminFabricId;
51
53
 
52
54
  // Configure discovery transports
53
55
  if (this.state.ip === undefined) {
@@ -77,6 +79,10 @@ export class ControllerBehavior extends Behavior {
77
79
  override get adminFabricLabel() {
78
80
  return adminFabricLabel;
79
81
  }
82
+
83
+ get fabricId() {
84
+ return adminFabricId;
85
+ }
80
86
  })(),
81
87
  );
82
88
  }
@@ -185,5 +191,12 @@ export namespace ControllerBehavior {
185
191
  * Contains the label of the admin fabric which is set for all commissioned devices
186
192
  */
187
193
  adminFabricLabel = "matter.js";
194
+
195
+ /**
196
+ * Contains the FabricId of the admin fabric when a defined number needs to be used because special Certificates
197
+ * are used.
198
+ * If not provided, a random FabricId will be generated.
199
+ */
200
+ adminFabricId?: FabricId = undefined;
188
201
  }
189
202
  }
@@ -281,7 +281,7 @@ export class SubscriptionsBehavior extends Behavior {
281
281
  interactionServer.subscriptionEstablishmentStarted.off(blockHandler);
282
282
 
283
283
  logger.info(
284
- `Re-established ${successfullReEstablishments.length} ${successfullReEstablishments.length ? `(${successfullReEstablishments.join(",")})` : ""} of ${formerSubscriptions.length} former subscriptions successfully`,
284
+ `Re-established ${successfullReEstablishments.length}${successfullReEstablishments.length ? ` (${successfullReEstablishments.join(",")})` : ""} of ${formerSubscriptions.length} former subscriptions successfully`,
285
285
  );
286
286
  }
287
287
  }
@@ -24,6 +24,7 @@ import { DataModelPath } from "#model";
24
24
  import type { Node } from "#node/Node.js";
25
25
  import { IdentityService } from "#node/server/IdentityService.js";
26
26
  import { ProtocolService } from "#node/server/ProtocolService.js";
27
+ import { Val } from "#protocol";
27
28
  import { EndpointNumber } from "#types";
28
29
  import { RootEndpoint } from "../endpoints/root.js";
29
30
  import { Agent } from "./Agent.js";
@@ -170,14 +171,28 @@ export class Endpoint<T extends EndpointType = EndpointType.Empty> {
170
171
  return this.#stateView;
171
172
  }
172
173
 
174
+ /**
175
+ * Current state for a specific behavior ID.
176
+ */
177
+ stateOf(type: string): Immutable<Val.Struct>;
178
+
173
179
  /**
174
180
  * Current state for a specific behavior.
175
181
  */
176
- stateOf<T extends Behavior.Type>(type: T) {
177
- if (!this.behaviors.has(type)) {
178
- throw new ImplementationError(`Behavior ${type.id} is not supported by ${this}`);
182
+ stateOf<T extends Behavior.Type>(type: T): Immutable<Behavior.StateOf<T>>;
183
+
184
+ stateOf(type: Behavior.Type | string) {
185
+ if (typeof type === "string") {
186
+ if (!(type in this.#stateView)) {
187
+ throw new ImplementationError(`Behavior ${type} is not supported by ${this}`);
188
+ }
189
+ } else {
190
+ if (!this.behaviors.has(type)) {
191
+ throw new ImplementationError(`Behavior ${type.id} is not supported by ${this}`);
192
+ }
193
+ type = type.id;
179
194
  }
180
- return (this.#stateView as Record<string, unknown>)[type.id] as Immutable<Behavior.StateOf<T>>;
195
+ return (this.#stateView as Record<string, unknown>)[type];
181
196
  }
182
197
 
183
198
  /**
@@ -719,20 +734,28 @@ export class Endpoint<T extends EndpointType = EndpointType.Empty> {
719
734
  * Path identifying the endpoint in the Matter data model.
720
735
  */
721
736
  get path(): DataModelPath {
722
- let ident;
737
+ if (this.#owner) {
738
+ return this.#owner.path.at(this.identity, this.#type.name);
739
+ }
740
+
741
+ return DataModelPath(this.identity, this.type?.name);
742
+ }
743
+
744
+ /**
745
+ * Diagnostic identity.
746
+ *
747
+ * This is an unqualified path segment.
748
+ */
749
+ get identity() {
723
750
  if (this.lifecycle?.hasId) {
724
- ident = this.id;
725
- } else if (this.lifecycle?.hasNumber) {
726
- ident = this.number;
727
- } else {
728
- ident = "?";
751
+ return this.id;
729
752
  }
730
753
 
731
- if (this.#owner) {
732
- return this.#owner.path.at(ident, this.#type.name);
754
+ if (this.lifecycle?.hasNumber) {
755
+ return this.number;
733
756
  }
734
757
 
735
- return DataModelPath(ident, this.type?.name);
758
+ return "?";
736
759
  }
737
760
 
738
761
  /**
@@ -19,6 +19,7 @@ import {
19
19
  describeList,
20
20
  Diagnostic,
21
21
  EventEmitter,
22
+ Immutable,
22
23
  ImplementationError,
23
24
  Lifecycle,
24
25
  Logger,
@@ -557,6 +558,14 @@ export class Behaviors {
557
558
  return elements;
558
559
  }
559
560
 
561
+ /**
562
+ * Access the state view of a behavior if loaded.
563
+ */
564
+ maybeStateOf(behaviorId: string): Immutable<Val.Struct> | undefined {
565
+ const backing = this.#backings[behaviorId];
566
+ return backing?.maybeDatasource?.view;
567
+ }
568
+
560
569
  [Symbol.iterator]() {
561
570
  return Object.values(this.#supported)[Symbol.iterator]();
562
571
  }
@@ -11,8 +11,8 @@ import { NetworkClient } from "#behavior/system/network/NetworkClient.js";
11
11
  import { NetworkRuntime } from "#behavior/system/network/NetworkRuntime.js";
12
12
  import { Agent } from "#endpoint/Agent.js";
13
13
  import { EndpointInitializer } from "#endpoint/properties/EndpointInitializer.js";
14
- import { Identity, Lifecycle, MaybePromise } from "#general";
15
- import { Interactable, OccurrenceManager } from "#protocol";
14
+ import { Diagnostic, Identity, Lifecycle, Logger, MaybePromise } from "#general";
15
+ import { Interactable, OccurrenceManager, PeerAddress } from "#protocol";
16
16
  import { ClientNodeStore } from "#storage/client/ClientNodeStore.js";
17
17
  import { RemoteWriter } from "#storage/client/RemoteWriter.js";
18
18
  import { ServerNodeStore } from "#storage/server/ServerNodeStore.js";
@@ -22,6 +22,8 @@ import { ClientNodeInteraction } from "./client/ClientNodeInteraction.js";
22
22
  import { Node } from "./Node.js";
23
23
  import type { ServerNode } from "./ServerNode.js";
24
24
 
25
+ const logger = Logger.get("ClientNode");
26
+
25
27
  /**
26
28
  * A remote Matter {@link Node}.
27
29
  *
@@ -188,6 +190,30 @@ export class ClientNode extends Node<ClientNode.RootEndpoint> {
188
190
 
189
191
  return this.#interaction;
190
192
  }
193
+
194
+ override get identity() {
195
+ // If commissioned, use the peer address for logging purposes
196
+ let address = this.behaviors.maybeStateOf("commissioning")?.peerAddress as PeerAddress | undefined;
197
+
198
+ // During early initialization commissioning state may not be loaded, so check directly in storage too
199
+ if (!address) {
200
+ address = this.env.get(ClientNodeStore).storeForEndpoint(this).peerAddress as PeerAddress | undefined;
201
+ }
202
+
203
+ // Use the peer address as a log identifier if present
204
+ if (address) {
205
+ return PeerAddress(address).toString();
206
+ }
207
+
208
+ // Fall back to persistence ID
209
+ return super.identity;
210
+ }
211
+
212
+ protected override statusUpdate(message: string): void {
213
+ // Log client node status updates as info rather than notice and change the log facility to make clear it's a
214
+ // client
215
+ logger.info(Diagnostic.strong(this.toString()), message);
216
+ }
191
217
  }
192
218
 
193
219
  export namespace ClientNode {
@@ -236,6 +236,7 @@ export class ClientStructure {
236
236
  const attrs = cluster.store.initialValues ?? {};
237
237
 
238
238
  // Generate a behavior if enough information is available
239
+ // TODO: Detect changes in revision/features/attributes/commands and update behavior if needed
239
240
  if (cluster.behavior === undefined) {
240
241
  const {
241
242
  [ClusterRevision.id]: clusterRevision,
@@ -291,6 +292,9 @@ export class ClientStructure {
291
292
 
292
293
  const serverList = attrs[SERVER_LIST_ATTR_ID];
293
294
  if (Array.isArray(serverList)) {
295
+ // TODO: Remove clusters that are no longer present
296
+ // Including events vis parts/endpoints on node (per endpoint and generic "changed")?
297
+ // Including data cleanup
294
298
  for (const cluster of serverList) {
295
299
  if (typeof cluster === "number") {
296
300
  this.#clusterFor(endpoint, cluster as ClusterId);
@@ -307,6 +311,9 @@ export class ClientStructure {
307
311
 
308
312
  const part = this.#endpointFor(partNo as EndpointNumber);
309
313
 
314
+ // TODO - remove endpoints/parts that are no longer present
315
+ // Including events vis parts/endpoints on node (per endpoint and generic "changed")?
316
+ // Including data cleanup
310
317
  let isAlreadyDescendant = false;
311
318
  for (let owner = part.endpoint.owner; owner; owner = owner.owner) {
312
319
  if (owner === endpoint.endpoint) {
@@ -303,7 +303,7 @@ class ClusterState implements DisposableClusterProtocol {
303
303
  this.#datasource = backing.datasource;
304
304
  this.#endpointId = backing.endpoint.number;
305
305
 
306
- const attributeNameToIdMap = backing.type.supervisor.attributeNamesToIds;
306
+ const attributeNameToIdMap = backing.type.supervisor.propertyNamesAndIds;
307
307
  // For quieter attributes, we need to use the online events to get real state changes
308
308
  for (const attr of type.attributes) {
309
309
  attributeNameToIdMap.set(attr.name, attr.id);
@@ -26,6 +26,13 @@ export class ClientEndpointStore extends EndpointStore {
26
26
  return this.#number;
27
27
  }
28
28
 
29
+ /**
30
+ * Shortcut to persisted peer address so we can use in logging prior to full initialization.
31
+ */
32
+ get peerAddress() {
33
+ return this.initialValues.get("commissioning")?.["peerAddress"];
34
+ }
35
+
29
36
  participantFor(transaction: Transaction) {
30
37
  let participant = transaction.getParticipant(this.#owner);
31
38
  if (participant === undefined) {