/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import type { OfflineEvent, OnlineEvent } from "#behavior/Events.js"; import type { Endpoint } from "#endpoint/Endpoint.js"; import type { AttributeModel, EventModel } from "@matter/model"; import type { ClusterType, ClusterTyping } from "@matter/types"; import type { Behavior } from "../Behavior.js"; import type { ActionContext } from "../context/ActionContext.js"; /** * Event instance type for ClusterBehaviors. * * Omit key uses `keyof CompleteProperties` — the full set of event-shaped keys the cluster can ever expose * regardless of feature selection. This keeps the Omit stable across narrowing chains (e.g. * `Base.with(all features).for(Cluster)` → base's Events carries feature-gated keys that must be stripped * even when the narrower N's `Properties` no longer lists them), while letting the optimizer resolve the * key set through nested ClusterEvents types. */ export type ClusterEvents = // Keep observables that aren't contributed by the cluster (e.g. custom observables added on subclasses) Omit, keyof ClusterEvents.CompleteProperties> & // Add observables supplied by the new cluster, filtered by feature selection ClusterEvents.Properties; export namespace ClusterEvents { export interface Type { new (endpoint?: Endpoint, behavior?: Behavior.Type): ClusterEvents; } /** * Constructor type for Complete events — all events from all components, all mandatory. */ export interface CompleteType { new (endpoint?: Endpoint, behavior?: Behavior.Type): Complete; } /** * Properties the cluster contributes to Events. */ export type Properties = ChangingObservables & ChangedObservables & EventObservables; // --- Namespace-based attribute change observable types --- /** * API for events triggered prior to attribute change. */ export interface ChangingObservable extends OfflineEvent< [value: T, oldValue: T, context: ActionContext], AttributeModel > {} /** * API for events triggered after attribute change. */ export interface ChangedObservable extends OnlineEvent< [value: T, oldValue: T, context: ActionContext | undefined], AttributeModel > {} /** * Extract Components tuple from namespace. */ type ComponentsOf = N extends { Components: infer C extends ClusterType.Component[]; } ? C : []; /** * Extract required (non-optional) keys from an interface. */ type RequiredKeysOf = { [K in keyof T]-?: {} extends Pick ? never : K }[keyof T] & string; /** * Collect mandatory attribute keys from applicable components. */ type MandatoryAttrKeys = CA extends [ infer C extends ClusterType.Component, ...infer R extends ClusterType.Component[], ] ? | (S extends C["flags"] ? (C extends { attributes: infer A } ? RequiredKeysOf : never) : never) | MandatoryAttrKeys : never; /** * Attribute keys from applicable components only. */ type ApplicableAttrKeys = CA extends [ infer C extends ClusterType.Component, ...infer R extends ClusterType.Component[], ] ? | (S extends C["flags"] ? (C extends { attributes: infer A } ? keyof A & string : never) : never) | ApplicableAttrKeys : never; /** * Optional = applicable keys minus mandatory. */ type OptionalAttrKeys = Exclude< ApplicableAttrKeys, MandatoryAttrKeys >; /** * Produce changing observables from applicable attributes. */ type ChangingObservables = N extends { Attributes: infer A } ? { [ K in MandatoryAttrKeys, ClusterType.SupportedFeaturesOf> & keyof A as `${K & string}$Changing` ]: ChangingObservable; } & { [ K in OptionalAttrKeys, ClusterType.SupportedFeaturesOf> & keyof A as `${K & string}$Changing` ]?: ChangingObservable; } : {}; /** * Produce changed observables from applicable attributes. */ type ChangedObservables = N extends { Attributes: infer A } ? { [ K in MandatoryAttrKeys, ClusterType.SupportedFeaturesOf> & keyof A as `${K & string}$Changed` ]: ChangedObservable; } & { [ K in OptionalAttrKeys, ClusterType.SupportedFeaturesOf> & keyof A as `${K & string}$Changed` ]?: ChangedObservable; } : {}; // --- Namespace-based event observable types --- /** * Wrap payload type as event observable. */ type EventObservable = OnlineEvent<[payload: T, context: ActionContext], EventModel>; /** * Recursively intersect per-component event interfaces from applicable components. * * Mirrors {@link ClusterState.AppliedAttrsOf} for attributes. */ type AppliedEventsOf = CA extends [ infer C extends ClusterType.Component, ...infer R extends ClusterType.Component[], ] ? (S extends C["flags"] ? (C extends { events: infer E } ? E : {}) : {}) & AppliedEventsOf : {}; /** * Produce event observables from applicable events. * * Uses per-component event interfaces for mandatory/optional classification (via `RequiredKeysOf`) * and the flat `Events` interface for payload types. */ type EventObservables = N extends { Events: infer E } ? ObservableEvents, ClusterType.SupportedFeaturesOf>, E> : {}; /** * Map applied event keys to event observables, preserving mandatory/optional from per-component interfaces. */ type ObservableEvents = { [K in RequiredKeysOf & keyof E]: EventObservable; } & { [K in Exclude> & keyof E]?: EventObservable; }; /** * All attribute keys across all components (used by Complete types). */ type AllAttrKeys = CA extends [ infer C extends ClusterType.Component, ...infer R extends ClusterType.Component[], ] ? (C extends { attributes: infer A } ? keyof A & string : never) | AllAttrKeys : never; /** * Intersect event interfaces from all components (ignoring flags). Used by Complete types. */ type AllEventsOf = CA extends [ infer C extends ClusterType.Component, ...infer R extends ClusterType.Component[], ] ? (C extends { events: infer E } ? E : {}) & AllEventsOf : {}; /** * "Complete" event type — all events from all components, all mandatory. */ export type Complete = Omit< InstanceType, keyof Properties > & CompleteProperties; /** * Complete properties: all attribute change observables + all event observables, all mandatory. */ export type CompleteProperties = CompleteChangingObservables & CompleteChangedObservables & CompleteEventObservables; /** * All changing observables from all components, all mandatory. */ type CompleteChangingObservables = N extends { Attributes: infer A } ? { [K in AllAttrKeys> & keyof A as `${K & string}$Changing`]: ChangingObservable< A[K] >; } : {}; /** * All changed observables from all components, all mandatory. */ type CompleteChangedObservables = N extends { Attributes: infer A } ? { [K in AllAttrKeys> & keyof A as `${K & string}$Changed`]: ChangedObservable< A[K] >; } : {}; /** * All event observables from all components, all mandatory. */ type CompleteEventObservables = N extends { Events: infer E } ? { [K in keyof AllEventsOf> & keyof E]: EventObservable } : {}; }