/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js"; import type { ClusterBehaviorType } from "#behavior/cluster/ClusterBehaviorType.js"; import { Datasource } from "#behavior/state/managed/Datasource.js"; import type { ValReference } from "#behavior/state/managed/ValReference.js"; import { Endpoint } from "#endpoint/Endpoint.js"; import type { StateStream } from "#node/integration/StateStream.js"; import type { Node } from "#node/Node.js"; import { Observable } from "@matter/general"; import { type Read, type ReadResult } from "@matter/protocol"; import { AttributeId, EndpointNumber } from "@matter/types"; import type { ClusterId, EventNumber } from "@matter/types"; import type { ClientEventEmitter } from "./ClientEventEmitter.js"; import { PeerBehavior } from "./PeerBehavior.js"; /** * Manages endpoint and behavior structure for the local representation of a local node. * * This class supports update via data sourced from the Matter protocol or from matter.js's remote APIs. */ export declare class ClientStructure { #private; /** * Optional event emitter for Matter protocol events. Set after construction. */ eventEmitter?: ClientEventEmitter; constructor(node: Node, storeFactory: ClientStructure.StoreFactory, options: ClientStructure.Options); get changed(): Observable<[void], void>; /** * True while structural changes are being applied and announced, when the endpoint tree and its behaviors may be * only partly installed. */ get isApplying(): boolean; /** * Whether the peer has reported the Descriptor list `attribute` (DeviceTypeList or PartsList) of `endpoint`, from * the wire or from the cache of an earlier report. */ isDescriptorListReported(endpoint: EndpointNumber, attribute: AttributeId): boolean; /** * The node this structure manages. */ get node(): Node; /** * Load initial structure from cached endpoint stores. * * Each entry in {@link endpointStores} must expose an `id` (stringified endpoint number) and `knownBehaviors` * (iterable of stringified cluster IDs). */ loadCache(endpointStores: Iterable<{ id: string; knownBehaviors: Iterable; }>): void; /** * Obtain the store for a remote cluster. */ storeForRemote(endpoint: Endpoint, type: ClusterBehavior.Type): Datasource.ExternallyMutableStore; /** * Add a data version filter for each cluster whose cached data is current, so the peer omits those clusters from * its response. The caller's own filters are kept as given and its array is not modified; a cluster the caller * filters gets no injected filter, so the peer sees exactly the caller's filter for that cluster. * * The cache holds only data of its own fabric filter ({@link cacheFabricFiltered}), so a request with the other * fabric filter gets no filters. * * A subscription does not report changes of an attribute with the changes-omitted quality, so a matching version * does not mean such an attribute is current. With {@link options.refreshChangesOmitted} a cluster in which the * peer has one gets no filter, so the response carries its current values. */ injectVersionFilters(request: T, options?: { refreshChangesOmitted?: boolean; }): T; /** * The fabric filter of the interactions the cache takes data from. Only interactions with this filter update * cached values and data versions, and only they get versions injected. Defaults to fabric filtered until * {@link adoptFabricFilter} recorded one. */ get cacheFabricFiltered(): boolean; /** * The epoch an interaction starting now belongs to. An interaction records the data versions it reports only if * its epoch is still the current one and trusted when the data is applied. */ get epoch(): ClientStructure.Epoch; /** * Make {@link fabricFiltered} the fabric filter of the cache. Calls run one after the other. * * Data versions do not depend on the fabric filter, but cached values of fabric-scoped data do. So when the cache * was filled under the other filter, every cached data version becomes unknown while values stay, and the next * full report refreshes them. A cache without a recorded filter is trusted to hold data of the first filter * adopted, which is the configured default subscription's, so that filter is recorded and the versions stay. * * Each change of the filter starts a new {@link epoch}. An interaction that started before the change records no * data version; its data is still applied if it was received before the change or matches the new filter, and * data of the old filter received after the change is dropped. While the versions are forgotten the epoch is not * trusted, so no version is recorded or injected. Rejects if storage refuses a write; the old record then stays, * so the next attempt forgets the versions again. */ adoptFabricFilter(fabricFiltered: boolean): Promise; /** * Update the node structure by applying attribute changes from a Matter protocol interaction. * * The interaction's events also go to the node's event observers while {@link options.emitEvents} is true or * returns true, and {@link options.eventEmitted} receives the number of each. {@link options.epoch} defaults to * the current {@link epoch}. * * A local failure to apply data, persist it or notify an event observer is logged and does not stop the processing * of the interaction's other data and events; it never rejects the iteration. Failures of {@link changes} itself * and of applying structural changes still reject. */ mutate(request: Read, changes: ReadResult, options?: { emitEvents?: boolean | (() => boolean); eventEmitted?: (number: EventNumber) => void; epoch?: ClientStructure.Epoch; }): AsyncGenerator; /** * Apply {@link StateStream.WireChange}s to the structure. * * A wire change addresses members by property name. A client behavior's container keys them by attribute ID, so * this is where the two representations meet: values convert on the way in and {@link StateStream.WireChange} * stays name-keyed on the way out. */ applyWireChanges(changes: StateStream.WireChange[]): Promise; /** * Obtain the cluster namespace for an {@link EndpointNumber} and {@link ClusterId}. */ clusterFor(endpoint: EndpointNumber, cluster: ClusterId): import("@matter/types").ClusterType.Concrete | undefined; /** * Obtain the {@link Endpoint} for a {@link EndpointNumber}. */ endpointFor(endpoint: EndpointNumber): Endpoint | undefined; } export declare namespace ClientStructure { /** * A period in which the cache's fabric filter does not change. Data versions are recorded only for interactions * of the current epoch, and only if it is trusted, which it is not while versions are being forgotten. */ class Epoch { readonly trusted: boolean; constructor(trusted: boolean); } /** * The persisted record of the fabric filter the cache was filled under. */ interface CacheRecord { readonly cacheFabricFiltered?: boolean; recordCacheFabricFiltered(fabricFiltered: boolean): Promise; } /** * Creates a {@link Datasource.ExternallyMutableStore} for a behavior on an endpoint. */ type StoreFactory = (endpoint: Endpoint, behaviorId: string, primaryKey: ValReference.PrimaryKey) => Datasource.ExternallyMutableStore; interface Options { /** * Factory for command implementations used when generating peer behaviors. */ commandFactory?: ClusterBehaviorType.CommandFactory; /** * Where the fabric filter of the cache is recorded. */ cacheRecord: CacheRecord; } } interface EndpointStructure { pendingOwner?: EndpointStructure; endpoint: Endpoint; clusters: Map; /** * The Descriptor lists the peer has reported for this endpoint. A mandatory list it has not reported still reads * as empty from the behavior's state. */ reportedLists?: Set; } interface ClusterStructure extends Partial { kind: "discovered"; id: ClusterId; /** * The keying convention {@link store} uses. A cluster the peer names but we cannot resolve to a schema has no * attribute IDs to key by, so its store keys members by property name. */ primaryKey: "id" | "name"; behavior?: ClusterBehavior.Type; pendingBehavior?: ClusterBehavior.Type; pendingDelete?: boolean; store: Datasource.ExternallyMutableStore; /** Whether {@link attributes} is the peer's `AttributeList` rather than the IDs of the values it sent. */ attributeListReported?: boolean; } /** * Queue entry for pending notifications. */ export type PendingEvent = EndpointEvent | ClusterEvent; interface EndpointEvent { kind: "endpoint"; endpoint: EndpointStructure; } interface ClusterEvent { kind: "cluster"; subkind: "add" | "delete" | "replace"; endpoint: EndpointStructure; cluster: ClusterStructure; } export {}; //# sourceMappingURL=ClientStructure.d.ts.map