@matter/protocol 0.13.1-alpha.0-20250520-d699cd56d → 0.14.0-alpha.0-20250524-51a7e1721
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.
- package/dist/cjs/action/Interactable.d.ts +1 -1
- package/dist/cjs/action/Interactable.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.d.ts +1 -1
- package/dist/cjs/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/cjs/action/client/ClientInteraction.js +50 -8
- package/dist/cjs/action/client/ClientInteraction.js.map +1 -1
- package/dist/cjs/action/protocols.d.ts +49 -15
- package/dist/cjs/action/protocols.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.d.ts +10 -1
- package/dist/cjs/action/request/Invoke.d.ts.map +1 -1
- package/dist/cjs/action/request/Invoke.js +17 -5
- package/dist/cjs/action/request/Invoke.js.map +1 -1
- package/dist/cjs/action/request/Read.d.ts.map +1 -1
- package/dist/cjs/action/request/Read.js +1 -1
- package/dist/cjs/action/request/Read.js.map +1 -1
- package/dist/cjs/action/request/Write.d.ts +2 -0
- package/dist/cjs/action/request/Write.d.ts.map +1 -1
- package/dist/cjs/action/request/Write.js.map +1 -1
- package/dist/cjs/action/response/InvokeResult.d.ts +22 -6
- package/dist/cjs/action/response/InvokeResult.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeReadResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeReadResponse.js +1 -2
- package/dist/cjs/action/server/AttributeReadResponse.js.map +2 -2
- package/dist/cjs/action/server/AttributeWriteResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeWriteResponse.js +1 -14
- package/dist/cjs/action/server/AttributeWriteResponse.js.map +1 -1
- package/dist/cjs/action/server/CommandInvokeResponse.d.ts +25 -0
- package/dist/cjs/action/server/CommandInvokeResponse.d.ts.map +1 -0
- package/dist/cjs/action/server/CommandInvokeResponse.js +331 -0
- package/dist/cjs/action/server/CommandInvokeResponse.js.map +6 -0
- package/dist/cjs/action/server/EventReadResponse.js +1 -1
- package/dist/cjs/action/server/ServerInteraction.d.ts +1 -3
- package/dist/cjs/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/cjs/action/server/ServerInteraction.js +4 -2
- package/dist/cjs/action/server/ServerInteraction.js.map +1 -1
- package/dist/cjs/action/server/index.d.ts +1 -0
- package/dist/cjs/action/server/index.d.ts.map +1 -1
- package/dist/cjs/action/server/index.js +1 -0
- package/dist/cjs/action/server/index.js.map +1 -1
- package/dist/cjs/cluster/client/AttributeClient.d.ts +2 -0
- package/dist/cjs/cluster/client/AttributeClient.d.ts.map +1 -1
- package/dist/cjs/cluster/client/AttributeClient.js +10 -0
- package/dist/cjs/cluster/client/AttributeClient.js.map +1 -1
- package/dist/cjs/cluster/client/ClusterClientTypes.d.ts +0 -4
- package/dist/cjs/cluster/client/ClusterClientTypes.d.ts.map +1 -1
- package/dist/cjs/cluster/index.d.ts +0 -1
- package/dist/cjs/cluster/index.d.ts.map +1 -1
- package/dist/cjs/cluster/index.js +0 -1
- package/dist/cjs/cluster/index.js.map +1 -1
- package/dist/cjs/events/OccurrenceManager.d.ts +10 -2
- package/dist/cjs/events/OccurrenceManager.d.ts.map +1 -1
- package/dist/cjs/events/OccurrenceManager.js +57 -11
- package/dist/cjs/events/OccurrenceManager.js.map +1 -1
- package/dist/cjs/fabric/Fabric.d.ts +2 -11
- package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
- package/dist/cjs/fabric/Fabric.js +1 -38
- package/dist/cjs/fabric/Fabric.js.map +1 -1
- package/dist/cjs/index.d.ts +0 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interaction/AttributeDataEncoder.d.ts +1 -1
- package/dist/cjs/interaction/InteractionClient.d.ts +13 -0
- package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionClient.js +14 -0
- package/dist/cjs/interaction/InteractionClient.js.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.d.ts +8 -8
- package/dist/cjs/interaction/index.d.ts +0 -1
- package/dist/cjs/interaction/index.d.ts.map +1 -1
- package/dist/cjs/interaction/index.js +0 -1
- package/dist/cjs/interaction/index.js.map +1 -1
- package/dist/cjs/mdns/MdnsScanner.d.ts +23 -1
- package/dist/cjs/mdns/MdnsScanner.d.ts.map +1 -1
- package/dist/cjs/mdns/MdnsScanner.js +124 -11
- package/dist/cjs/mdns/MdnsScanner.js.map +2 -2
- package/dist/esm/action/Interactable.d.ts +1 -1
- package/dist/esm/action/Interactable.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts +1 -1
- package/dist/esm/action/client/ClientInteraction.d.ts.map +1 -1
- package/dist/esm/action/client/ClientInteraction.js +50 -8
- package/dist/esm/action/client/ClientInteraction.js.map +1 -1
- package/dist/esm/action/protocols.d.ts +49 -15
- package/dist/esm/action/protocols.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.d.ts +10 -1
- package/dist/esm/action/request/Invoke.d.ts.map +1 -1
- package/dist/esm/action/request/Invoke.js +17 -5
- package/dist/esm/action/request/Invoke.js.map +1 -1
- package/dist/esm/action/request/Read.d.ts.map +1 -1
- package/dist/esm/action/request/Read.js +1 -1
- package/dist/esm/action/request/Read.js.map +1 -1
- package/dist/esm/action/request/Write.d.ts +2 -0
- package/dist/esm/action/request/Write.d.ts.map +1 -1
- package/dist/esm/action/request/Write.js.map +1 -1
- package/dist/esm/action/response/InvokeResult.d.ts +22 -6
- package/dist/esm/action/response/InvokeResult.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeReadResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeReadResponse.js +1 -1
- package/dist/esm/action/server/AttributeReadResponse.js.map +1 -1
- package/dist/esm/action/server/AttributeWriteResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeWriteResponse.js +2 -15
- package/dist/esm/action/server/AttributeWriteResponse.js.map +1 -1
- package/dist/esm/action/server/CommandInvokeResponse.d.ts +25 -0
- package/dist/esm/action/server/CommandInvokeResponse.d.ts.map +1 -0
- package/dist/esm/action/server/CommandInvokeResponse.js +317 -0
- package/dist/esm/action/server/CommandInvokeResponse.js.map +6 -0
- package/dist/esm/action/server/EventReadResponse.js +1 -1
- package/dist/esm/action/server/ServerInteraction.d.ts +1 -3
- package/dist/esm/action/server/ServerInteraction.d.ts.map +1 -1
- package/dist/esm/action/server/ServerInteraction.js +4 -2
- package/dist/esm/action/server/ServerInteraction.js.map +1 -1
- package/dist/esm/action/server/index.d.ts +1 -0
- package/dist/esm/action/server/index.d.ts.map +1 -1
- package/dist/esm/action/server/index.js +1 -0
- package/dist/esm/action/server/index.js.map +1 -1
- package/dist/esm/cluster/client/AttributeClient.d.ts +2 -0
- package/dist/esm/cluster/client/AttributeClient.d.ts.map +1 -1
- package/dist/esm/cluster/client/AttributeClient.js +10 -0
- package/dist/esm/cluster/client/AttributeClient.js.map +1 -1
- package/dist/esm/cluster/client/ClusterClientTypes.d.ts +0 -4
- package/dist/esm/cluster/client/ClusterClientTypes.d.ts.map +1 -1
- package/dist/esm/cluster/index.d.ts +0 -1
- package/dist/esm/cluster/index.d.ts.map +1 -1
- package/dist/esm/cluster/index.js +0 -1
- package/dist/esm/cluster/index.js.map +1 -1
- package/dist/esm/events/OccurrenceManager.d.ts +10 -2
- package/dist/esm/events/OccurrenceManager.d.ts.map +1 -1
- package/dist/esm/events/OccurrenceManager.js +62 -12
- package/dist/esm/events/OccurrenceManager.js.map +1 -1
- package/dist/esm/fabric/Fabric.d.ts +2 -11
- package/dist/esm/fabric/Fabric.d.ts.map +1 -1
- package/dist/esm/fabric/Fabric.js +1 -38
- package/dist/esm/fabric/Fabric.js.map +1 -1
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/interaction/AttributeDataEncoder.d.ts +1 -1
- package/dist/esm/interaction/InteractionClient.d.ts +13 -0
- package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionClient.js +14 -0
- package/dist/esm/interaction/InteractionClient.js.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts +8 -8
- package/dist/esm/interaction/index.d.ts +0 -1
- package/dist/esm/interaction/index.d.ts.map +1 -1
- package/dist/esm/interaction/index.js +0 -1
- package/dist/esm/interaction/index.js.map +1 -1
- package/dist/esm/mdns/MdnsScanner.d.ts +23 -1
- package/dist/esm/mdns/MdnsScanner.d.ts.map +1 -1
- package/dist/esm/mdns/MdnsScanner.js +127 -11
- package/dist/esm/mdns/MdnsScanner.js.map +2 -2
- package/package.json +6 -6
- package/src/action/Interactable.ts +1 -1
- package/src/action/client/ClientInteraction.ts +53 -16
- package/src/action/protocols.ts +70 -16
- package/src/action/request/Invoke.ts +32 -5
- package/src/action/request/Read.ts +1 -1
- package/src/action/request/Write.ts +4 -1
- package/src/action/response/InvokeResult.ts +26 -6
- package/src/action/response/ReadResult.ts +1 -1
- package/src/action/server/AttributeReadResponse.ts +1 -1
- package/src/action/server/AttributeWriteResponse.ts +2 -20
- package/src/action/server/CommandInvokeResponse.ts +427 -0
- package/src/action/server/EventReadResponse.ts +1 -1
- package/src/action/server/ServerInteraction.ts +6 -5
- package/src/action/server/index.ts +1 -0
- package/src/cluster/client/AttributeClient.ts +12 -0
- package/src/cluster/client/ClusterClientTypes.ts +0 -6
- package/src/cluster/index.ts +0 -1
- package/src/events/OccurrenceManager.ts +91 -11
- package/src/fabric/Fabric.ts +1 -49
- package/src/index.ts +0 -1
- package/src/interaction/InteractionClient.ts +24 -0
- package/src/interaction/index.ts +0 -1
- package/src/mdns/MdnsScanner.ts +187 -12
- package/dist/cjs/cluster/server/AttributeServer.d.ts +0 -307
- package/dist/cjs/cluster/server/AttributeServer.d.ts.map +0 -1
- package/dist/cjs/cluster/server/AttributeServer.js +0 -736
- package/dist/cjs/cluster/server/AttributeServer.js.map +0 -6
- package/dist/cjs/cluster/server/ClusterDatasource.d.ts +0 -16
- package/dist/cjs/cluster/server/ClusterDatasource.d.ts.map +0 -1
- package/dist/cjs/cluster/server/ClusterDatasource.js +0 -22
- package/dist/cjs/cluster/server/ClusterDatasource.js.map +0 -6
- package/dist/cjs/cluster/server/ClusterServer.d.ts +0 -40
- package/dist/cjs/cluster/server/ClusterServer.d.ts.map +0 -1
- package/dist/cjs/cluster/server/ClusterServer.js +0 -22
- package/dist/cjs/cluster/server/ClusterServer.js.map +0 -6
- package/dist/cjs/cluster/server/CommandServer.d.ts +0 -34
- package/dist/cjs/cluster/server/CommandServer.d.ts.map +0 -1
- package/dist/cjs/cluster/server/CommandServer.js +0 -76
- package/dist/cjs/cluster/server/CommandServer.js.map +0 -6
- package/dist/cjs/cluster/server/EventServer.d.ts +0 -42
- package/dist/cjs/cluster/server/EventServer.d.ts.map +0 -1
- package/dist/cjs/cluster/server/EventServer.js +0 -152
- package/dist/cjs/cluster/server/EventServer.js.map +0 -6
- package/dist/cjs/cluster/server/index.d.ts +0 -11
- package/dist/cjs/cluster/server/index.d.ts.map +0 -1
- package/dist/cjs/cluster/server/index.js +0 -28
- package/dist/cjs/cluster/server/index.js.map +0 -6
- package/dist/cjs/endpoint/EndpointInterface.d.ts +0 -37
- package/dist/cjs/endpoint/EndpointInterface.d.ts.map +0 -1
- package/dist/cjs/endpoint/EndpointInterface.js +0 -22
- package/dist/cjs/endpoint/EndpointInterface.js.map +0 -6
- package/dist/cjs/endpoint/EndpointStructureLogger.d.ts +0 -31
- package/dist/cjs/endpoint/EndpointStructureLogger.d.ts.map +0 -1
- package/dist/cjs/endpoint/EndpointStructureLogger.js +0 -230
- package/dist/cjs/endpoint/EndpointStructureLogger.js.map +0 -6
- package/dist/cjs/endpoint/index.d.ts +0 -8
- package/dist/cjs/endpoint/index.d.ts.map +0 -1
- package/dist/cjs/endpoint/index.js +0 -25
- package/dist/cjs/endpoint/index.js.map +0 -6
- package/dist/cjs/interaction/InteractionEndpointStructure.d.ts +0 -95
- package/dist/cjs/interaction/InteractionEndpointStructure.d.ts.map +0 -1
- package/dist/cjs/interaction/InteractionEndpointStructure.js +0 -359
- package/dist/cjs/interaction/InteractionEndpointStructure.js.map +0 -6
- package/dist/esm/cluster/server/AttributeServer.d.ts +0 -307
- package/dist/esm/cluster/server/AttributeServer.d.ts.map +0 -1
- package/dist/esm/cluster/server/AttributeServer.js +0 -729
- package/dist/esm/cluster/server/AttributeServer.js.map +0 -6
- package/dist/esm/cluster/server/ClusterDatasource.d.ts +0 -16
- package/dist/esm/cluster/server/ClusterDatasource.d.ts.map +0 -1
- package/dist/esm/cluster/server/ClusterDatasource.js +0 -6
- package/dist/esm/cluster/server/ClusterDatasource.js.map +0 -6
- package/dist/esm/cluster/server/ClusterServer.d.ts +0 -40
- package/dist/esm/cluster/server/ClusterServer.d.ts.map +0 -1
- package/dist/esm/cluster/server/ClusterServer.js +0 -6
- package/dist/esm/cluster/server/ClusterServer.js.map +0 -6
- package/dist/esm/cluster/server/CommandServer.d.ts +0 -34
- package/dist/esm/cluster/server/CommandServer.d.ts.map +0 -1
- package/dist/esm/cluster/server/CommandServer.js +0 -56
- package/dist/esm/cluster/server/CommandServer.js.map +0 -6
- package/dist/esm/cluster/server/EventServer.d.ts +0 -42
- package/dist/esm/cluster/server/EventServer.d.ts.map +0 -1
- package/dist/esm/cluster/server/EventServer.js +0 -141
- package/dist/esm/cluster/server/EventServer.js.map +0 -6
- package/dist/esm/cluster/server/index.d.ts +0 -11
- package/dist/esm/cluster/server/index.d.ts.map +0 -1
- package/dist/esm/cluster/server/index.js +0 -11
- package/dist/esm/cluster/server/index.js.map +0 -6
- package/dist/esm/endpoint/EndpointInterface.d.ts +0 -37
- package/dist/esm/endpoint/EndpointInterface.d.ts.map +0 -1
- package/dist/esm/endpoint/EndpointInterface.js +0 -6
- package/dist/esm/endpoint/EndpointInterface.js.map +0 -6
- package/dist/esm/endpoint/EndpointStructureLogger.d.ts +0 -31
- package/dist/esm/endpoint/EndpointStructureLogger.d.ts.map +0 -1
- package/dist/esm/endpoint/EndpointStructureLogger.js +0 -210
- package/dist/esm/endpoint/EndpointStructureLogger.js.map +0 -6
- package/dist/esm/endpoint/index.d.ts +0 -8
- package/dist/esm/endpoint/index.d.ts.map +0 -1
- package/dist/esm/endpoint/index.js +0 -8
- package/dist/esm/endpoint/index.js.map +0 -6
- package/dist/esm/interaction/InteractionEndpointStructure.d.ts +0 -95
- package/dist/esm/interaction/InteractionEndpointStructure.d.ts.map +0 -1
- package/dist/esm/interaction/InteractionEndpointStructure.js +0 -355
- package/dist/esm/interaction/InteractionEndpointStructure.js.map +0 -6
- package/src/cluster/server/AttributeServer.ts +0 -997
- package/src/cluster/server/ClusterDatasource.ts +0 -17
- package/src/cluster/server/ClusterServer.ts +0 -46
- package/src/cluster/server/CommandServer.ts +0 -89
- package/src/cluster/server/EventServer.ts +0 -202
- package/src/cluster/server/index.ts +0 -11
- package/src/endpoint/EndpointInterface.ts +0 -41
- package/src/endpoint/EndpointStructureLogger.ts +0 -270
- package/src/endpoint/index.ts +0 -8
- package/src/interaction/InteractionEndpointStructure.ts +0 -510
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Project CHIP Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CommandInvokeHandler, Invoke, InvokeResult } from "#action/index.js";
|
|
8
|
+
import { InteractionSession } from "#action/Interactable.js";
|
|
9
|
+
import { CommandTypeProtocol, EndpointProtocol, NodeProtocol } from "#action/protocols.js";
|
|
10
|
+
import { AccessControl } from "#action/server/AccessControl.js";
|
|
11
|
+
import { DataResponse, FallbackLimits } from "#action/server/DataResponse.js";
|
|
12
|
+
import { Diagnostic, InternalError, Logger } from "#general";
|
|
13
|
+
import { CommandModel, DataModelPath, ElementTag, FabricIndex as FabricIndexField } from "#model";
|
|
14
|
+
import {
|
|
15
|
+
CommandPath,
|
|
16
|
+
FabricIndex,
|
|
17
|
+
Status,
|
|
18
|
+
StatusCode,
|
|
19
|
+
StatusResponseError,
|
|
20
|
+
TlvSchema,
|
|
21
|
+
TlvStream,
|
|
22
|
+
ValidationError,
|
|
23
|
+
} from "#types";
|
|
24
|
+
|
|
25
|
+
const logger = Logger.get("CommandInvokeResponse");
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Implements invoking of commands for matter "invoke" interactions.
|
|
29
|
+
*
|
|
30
|
+
* TODO - profile; ensure nested functions are properly JITed and/or inlined
|
|
31
|
+
*/
|
|
32
|
+
export class CommandInvokeResponse<
|
|
33
|
+
SessionT extends InteractionSession = InteractionSession,
|
|
34
|
+
> extends DataResponse<SessionT> {
|
|
35
|
+
#fabricIndex: FabricIndex;
|
|
36
|
+
|
|
37
|
+
// The initial "chunk" may be a list of errors. As producers execute it is a set of records associated with the
|
|
38
|
+
// most recently touched endpoint. When the endpoint changes the previous chunk emits
|
|
39
|
+
#chunk?: InvokeResult.Data[];
|
|
40
|
+
|
|
41
|
+
// Each input CommandDataIB that does not have an error installs a producer. Producers run after validation and
|
|
42
|
+
// generate actual command data
|
|
43
|
+
#invokers?: Array<(this: CommandInvokeResponse) => AsyncIterable<InvokeResult.Chunk>>;
|
|
44
|
+
|
|
45
|
+
// The following state updates as data producers execute. This serves both to convey state between functions and as
|
|
46
|
+
// a cache between producers that touch the same endpoint and/or cluster
|
|
47
|
+
#currentEndpoint?: EndpointProtocol;
|
|
48
|
+
|
|
49
|
+
#registeredPaths = new Set<string>();
|
|
50
|
+
#registeredCommandRefs = new Set<number>();
|
|
51
|
+
|
|
52
|
+
// Count how many command status (on error) and command invokes (success) we have emitted
|
|
53
|
+
#statusCount = 0;
|
|
54
|
+
#successCount = 0;
|
|
55
|
+
#errorCount = 0;
|
|
56
|
+
|
|
57
|
+
constructor(node: NodeProtocol, session: SessionT) {
|
|
58
|
+
super(node, session);
|
|
59
|
+
this.#fabricIndex = session.fabric ?? FabricIndex.NO_FABRIC;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async *process<T extends Invoke>({ invokeRequests, suppressResponse }: T): InvokeResult {
|
|
63
|
+
const multipleInvokes = invokeRequests.length > 1;
|
|
64
|
+
|
|
65
|
+
// Register paths
|
|
66
|
+
for (const command of invokeRequests) {
|
|
67
|
+
const { commandPath: path, commandFields, commandRef } = command;
|
|
68
|
+
if (path.endpointId === undefined || path.clusterId === undefined || path.commandId === undefined) {
|
|
69
|
+
if (multipleInvokes) {
|
|
70
|
+
throw new StatusResponseError(
|
|
71
|
+
"Wildcard path must not be used with multiple invokes",
|
|
72
|
+
StatusCode.InvalidAction,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
this.#processWildcard(path, commandRef, commandFields);
|
|
76
|
+
} else {
|
|
77
|
+
if (multipleInvokes && commandRef === undefined) {
|
|
78
|
+
throw new StatusResponseError(
|
|
79
|
+
"The CommandRef field must be specified for all commands in a batch invoke",
|
|
80
|
+
StatusCode.InvalidAction,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
this.#processConcrete(path as InvokeResult.ConcreteCommandPath, commandRef, commandFields);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.#invokers) {
|
|
88
|
+
for (const invoker of this.#invokers) {
|
|
89
|
+
for await (const chunk of invoker.apply(this)) {
|
|
90
|
+
if (!suppressResponse) {
|
|
91
|
+
yield chunk;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// We emit chunks lazily when the endpoint changes so there may be one remaining chunk. There may also be a
|
|
98
|
+
// chunk with errors even if there are no data producers
|
|
99
|
+
if (!suppressResponse && this.#chunk !== undefined) {
|
|
100
|
+
yield this.#chunk;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get counts() {
|
|
105
|
+
return {
|
|
106
|
+
status: this.#statusCount,
|
|
107
|
+
success: this.#successCount,
|
|
108
|
+
existent: this.#successCount + this.#errorCount,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Process a wildcard path and invoke commands on all endpoints that match the path.
|
|
114
|
+
*/
|
|
115
|
+
#processWildcard(path: CommandPath, commandRef: number | undefined, commandFields: TlvStream | undefined) {
|
|
116
|
+
const { clusterId, commandId } = path;
|
|
117
|
+
|
|
118
|
+
// Formally, according to spec, we should check for node mismatch here but commandPath do not have a nodeId
|
|
119
|
+
|
|
120
|
+
// TODO: Add Group handling and validation
|
|
121
|
+
/*
|
|
122
|
+
if (isGroupSession && endpointId !== undefined) {
|
|
123
|
+
throw new StatusResponseError("Illegal write request with group ID and endpoint ID", StatusCode.InvalidAction);
|
|
124
|
+
}
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
if (clusterId === undefined || commandId === undefined) {
|
|
128
|
+
throw new StatusResponseError(
|
|
129
|
+
"Wildcard path write must specify a clusterId and commandId",
|
|
130
|
+
StatusCode.InvalidAction,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// If we are here, then endpointId must be wildcard aka undefined
|
|
135
|
+
this.#addInvoker(async function* invokeWildcardEndpoints(this: CommandInvokeResponse) {
|
|
136
|
+
for (const endpoint of this.node) {
|
|
137
|
+
yield* this.#processEndpointForWildcard(endpoint, path, commandRef, commandFields);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Invoke a command specified by a concrete path
|
|
144
|
+
*/
|
|
145
|
+
#processConcrete(
|
|
146
|
+
path: InvokeResult.ConcreteCommandPath,
|
|
147
|
+
commandRef: number | undefined,
|
|
148
|
+
commandFields: TlvStream | undefined,
|
|
149
|
+
) {
|
|
150
|
+
const { endpointId, clusterId, commandId } = path;
|
|
151
|
+
|
|
152
|
+
const pathKey = `${endpointId}-${clusterId}-${commandId}`;
|
|
153
|
+
if (this.#registeredPaths.has(pathKey)) {
|
|
154
|
+
throw new StatusResponseError(
|
|
155
|
+
`Duplicate concrete command path ${this.node.inspectPath(path)} on batch invoke`,
|
|
156
|
+
StatusCode.InvalidAction,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
this.#registeredPaths.add(pathKey);
|
|
160
|
+
if (commandRef !== undefined) {
|
|
161
|
+
if (this.#registeredCommandRefs.has(commandRef)) {
|
|
162
|
+
throw new StatusResponseError(
|
|
163
|
+
`Duplicate commandRef ${commandRef} on batch invoke`,
|
|
164
|
+
StatusCode.InvalidAction,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
this.#registeredCommandRefs.add(commandRef);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Formally, according to spec, we should check for node mismatch here but commandPath do not have a nodeId
|
|
171
|
+
|
|
172
|
+
// Resolve path elements
|
|
173
|
+
const endpoint = this.node[endpointId];
|
|
174
|
+
const cluster = endpoint?.[clusterId];
|
|
175
|
+
const command = cluster?.type.commands[commandId];
|
|
176
|
+
let limits;
|
|
177
|
+
if (command === undefined) {
|
|
178
|
+
// We still need to authorize the user for access even though this path doesn't resolve. Spec is not
|
|
179
|
+
// explicit on what privilege level we should require as normally that information comes from the resolved
|
|
180
|
+
// command. So attempt to resolve via the active model
|
|
181
|
+
const modelAttr = this.node.matter
|
|
182
|
+
.member(path.clusterId, [ElementTag.Cluster])
|
|
183
|
+
?.member(path.commandId, [ElementTag.Command]);
|
|
184
|
+
|
|
185
|
+
if (modelAttr) {
|
|
186
|
+
// OK cluster doesn't exist at that location, but we do understand semantically, so use limits from the
|
|
187
|
+
// model
|
|
188
|
+
limits = AccessControl(modelAttr as CommandModel).limits;
|
|
189
|
+
} else {
|
|
190
|
+
// We've got no idea. This effectively falls back to "view" privilege
|
|
191
|
+
limits = FallbackLimits;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
limits = command.limits;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Validate access. Order here prescribed by 1.4 core spec 8.4.3.2
|
|
198
|
+
// We need some fallback location if cluster is not defined
|
|
199
|
+
const location = {
|
|
200
|
+
...(cluster?.location ?? {
|
|
201
|
+
path: DataModelPath.none,
|
|
202
|
+
endpoint: endpointId,
|
|
203
|
+
cluster: clusterId,
|
|
204
|
+
}),
|
|
205
|
+
owningFabric: this.session.fabric,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const permission = this.session.authorityAt(limits.writeLevel, location);
|
|
209
|
+
switch (permission) {
|
|
210
|
+
case AccessControl.Authority.Granted:
|
|
211
|
+
break;
|
|
212
|
+
|
|
213
|
+
case AccessControl.Authority.Unauthorized:
|
|
214
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedAccess);
|
|
215
|
+
|
|
216
|
+
case AccessControl.Authority.Restricted:
|
|
217
|
+
return this.#addStatus(path, commandRef, Status.AccessRestricted);
|
|
218
|
+
|
|
219
|
+
default:
|
|
220
|
+
throw new InternalError(`Unsupported authorization state ${permission}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (endpoint === undefined) {
|
|
224
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedEndpoint);
|
|
225
|
+
}
|
|
226
|
+
if (cluster === undefined) {
|
|
227
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedCluster);
|
|
228
|
+
}
|
|
229
|
+
if (command === undefined || !cluster.type.commands[command.id]) {
|
|
230
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedCommand);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (limits.fabricScoped && this.session.fabric === undefined) {
|
|
234
|
+
this.#errorCount++;
|
|
235
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedAccess);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (limits.timed && !this.session.timed) {
|
|
239
|
+
this.#errorCount++;
|
|
240
|
+
return this.#addStatus(path, commandRef, Status.NeedsTimedInteraction);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// This path contributes an command value
|
|
244
|
+
this.#addInvoker(async function* invokeConcretePath() {
|
|
245
|
+
// Update internal state for target endpoint
|
|
246
|
+
if (this.#currentEndpoint !== endpoint) {
|
|
247
|
+
if (this.#chunk) {
|
|
248
|
+
yield this.#chunk;
|
|
249
|
+
this.#chunk = undefined;
|
|
250
|
+
}
|
|
251
|
+
this.#currentEndpoint = endpoint;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
await this.#invokeCommand(command, path, commandRef, commandFields, cluster.commands[command.id]);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Starts new chunk or adds to current chunk all values from {@link endpoint} selected by {@link path}.
|
|
260
|
+
*
|
|
261
|
+
* Emits previous chunk if it exists and was not for this endpoint. This means that our chunk size is one endpoint
|
|
262
|
+
* worth of data, except for the initial error chunk if there are path errors.
|
|
263
|
+
**
|
|
264
|
+
* TODO - skip endpoints for which subject is unauthorized as optimization
|
|
265
|
+
*/
|
|
266
|
+
async *#processEndpointForWildcard(
|
|
267
|
+
endpoint: EndpointProtocol,
|
|
268
|
+
path: CommandPath,
|
|
269
|
+
commandRef: number | undefined,
|
|
270
|
+
commandFields: TlvStream | undefined,
|
|
271
|
+
) {
|
|
272
|
+
const { clusterId } = path;
|
|
273
|
+
|
|
274
|
+
if (this.#currentEndpoint !== endpoint) {
|
|
275
|
+
if (this.#chunk) {
|
|
276
|
+
yield this.#chunk;
|
|
277
|
+
this.#chunk = undefined;
|
|
278
|
+
}
|
|
279
|
+
this.#currentEndpoint = endpoint;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const cluster = endpoint[clusterId];
|
|
283
|
+
if (cluster !== undefined) {
|
|
284
|
+
const { commandId } = path;
|
|
285
|
+
|
|
286
|
+
const command = cluster.type.commands[commandId];
|
|
287
|
+
if (command !== undefined) {
|
|
288
|
+
if (
|
|
289
|
+
this.session.authorityAt(command.limits.writeLevel, cluster.location) !==
|
|
290
|
+
AccessControl.Authority.Granted ||
|
|
291
|
+
(command.limits.timed && !this.session.timed)
|
|
292
|
+
) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await this.#invokeCommand(
|
|
297
|
+
command,
|
|
298
|
+
{
|
|
299
|
+
...path,
|
|
300
|
+
endpointId: endpoint.id,
|
|
301
|
+
},
|
|
302
|
+
commandRef,
|
|
303
|
+
commandFields,
|
|
304
|
+
cluster.commands[command.id],
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Add a function that invokes commands and produces data. These functions are run after validation of input paths.
|
|
312
|
+
*/
|
|
313
|
+
#addInvoker(producer: (this: CommandInvokeResponse) => AsyncIterable<InvokeResult.Chunk>) {
|
|
314
|
+
if (this.#invokers) {
|
|
315
|
+
this.#invokers.push(producer);
|
|
316
|
+
} else {
|
|
317
|
+
this.#invokers = [producer];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#addResponse(chunk: InvokeResult.Data) {
|
|
322
|
+
if (this.#chunk) {
|
|
323
|
+
this.#chunk.push(chunk);
|
|
324
|
+
} else {
|
|
325
|
+
this.#chunk = [chunk];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Add a status value.
|
|
331
|
+
*/
|
|
332
|
+
#addStatus(
|
|
333
|
+
path: InvokeResult.ConcreteCommandPath,
|
|
334
|
+
commandRef: number | undefined,
|
|
335
|
+
status: Status,
|
|
336
|
+
clusterStatus?: number,
|
|
337
|
+
) {
|
|
338
|
+
if (status !== StatusCode.Success) {
|
|
339
|
+
logger.info(
|
|
340
|
+
() =>
|
|
341
|
+
`Invoke error ${this.node.inspectPath(path)}: Status=${StatusCode[status]}(${status}), ClusterStatus=${clusterStatus}`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const response: InvokeResult.CommandStatus = {
|
|
346
|
+
kind: "cmd-status",
|
|
347
|
+
path,
|
|
348
|
+
status,
|
|
349
|
+
clusterStatus,
|
|
350
|
+
commandRef,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (status !== StatusCode.Success) {
|
|
354
|
+
this.#statusCount++;
|
|
355
|
+
}
|
|
356
|
+
this.#addResponse(response);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async #invokeCommand(
|
|
360
|
+
command: CommandTypeProtocol,
|
|
361
|
+
path: InvokeResult.ConcreteCommandPath,
|
|
362
|
+
commandRef: number | undefined,
|
|
363
|
+
commandFields: TlvStream | undefined,
|
|
364
|
+
invoker: CommandInvokeHandler,
|
|
365
|
+
) {
|
|
366
|
+
try {
|
|
367
|
+
const { requestTlv, responseTlv } = command;
|
|
368
|
+
const request = this.#decodeWithSchema(requestTlv, commandFields);
|
|
369
|
+
requestTlv.validate(request);
|
|
370
|
+
const response = await invoker(request, this.session);
|
|
371
|
+
await this.session.transaction?.commit();
|
|
372
|
+
|
|
373
|
+
this.#successCount++;
|
|
374
|
+
const encodedResponse = responseTlv.encodeTlv(response);
|
|
375
|
+
if (encodedResponse.length === 0) {
|
|
376
|
+
this.#addStatus(path, commandRef, StatusCode.Success);
|
|
377
|
+
} else {
|
|
378
|
+
this.#addResponse({
|
|
379
|
+
kind: "cmd-response",
|
|
380
|
+
path: {
|
|
381
|
+
...path,
|
|
382
|
+
commandId: command.responseId,
|
|
383
|
+
},
|
|
384
|
+
data: encodedResponse,
|
|
385
|
+
commandRef,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
await this.session.transaction?.rollback();
|
|
390
|
+
if (StatusResponseError.is(error)) {
|
|
391
|
+
this.#errorCount++;
|
|
392
|
+
|
|
393
|
+
let errorCode = error.code;
|
|
394
|
+
const errorLogText = `Error ${Diagnostic.hex(errorCode)}${
|
|
395
|
+
error.clusterCode !== undefined ? `/${Diagnostic.hex(error.clusterCode)}` : ""
|
|
396
|
+
} while invoking command: ${error.message}`;
|
|
397
|
+
|
|
398
|
+
if (error instanceof ValidationError) {
|
|
399
|
+
logger.info(
|
|
400
|
+
`Validation-${errorLogText}${error.fieldName !== undefined ? ` in field ${error.fieldName}` : ""}`,
|
|
401
|
+
);
|
|
402
|
+
if (errorCode === StatusCode.InvalidAction) {
|
|
403
|
+
errorCode = StatusCode.InvalidCommand;
|
|
404
|
+
}
|
|
405
|
+
} else {
|
|
406
|
+
logger.info(errorLogText);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.#addStatus(path, commandRef, errorCode, error.clusterCode);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#decodeWithSchema(tlv: TlvSchema<any>, value: TlvStream | undefined) {
|
|
417
|
+
if (value === undefined) {
|
|
418
|
+
return undefined; // The validation will fail if the schema expected data
|
|
419
|
+
}
|
|
420
|
+
return tlv.injectField(
|
|
421
|
+
tlv.decodeTlv(value),
|
|
422
|
+
<number>FabricIndexField.id,
|
|
423
|
+
this.#fabricIndex,
|
|
424
|
+
() => true, // We always inject the current fabricIndex for invokes
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
@@ -50,7 +50,7 @@ export class EventReadResponse<
|
|
|
50
50
|
// Collected allowed and existing event paths to consider when reading events
|
|
51
51
|
#allowedEventPaths = new Map<string, TlvSchema<unknown>>();
|
|
52
52
|
|
|
53
|
-
// Count how many
|
|
53
|
+
// Count how many events status (on error) and event values (on success) we have emitted
|
|
54
54
|
#statusCount = 0;
|
|
55
55
|
#valueCount = 0;
|
|
56
56
|
|
|
@@ -14,6 +14,7 @@ import { InvokeResult } from "#action/response/InvokeResult.js";
|
|
|
14
14
|
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
|
+
import { CommandInvokeResponse } from "#action/server/CommandInvokeResponse.js";
|
|
17
18
|
import { EventReadResponse } from "#action/server/EventReadResponse.js";
|
|
18
19
|
import { Logger, NotImplementedError } from "#general";
|
|
19
20
|
import { AttributeReadResponse } from "./AttributeReadResponse.js";
|
|
@@ -28,8 +29,6 @@ const logger = Logger.get("ServerInteraction");
|
|
|
28
29
|
* completion there will be redundancy with other components including:
|
|
29
30
|
*
|
|
30
31
|
* - InteractionServer (significant overlap with this class)
|
|
31
|
-
*
|
|
32
|
-
* - InteractionEndpointStructure ({@link NodeProtocol} is largely duplicative)
|
|
33
32
|
*/
|
|
34
33
|
export class ServerInteraction<SessionT extends InteractionSession = InteractionSession>
|
|
35
34
|
implements Interactable<SessionT>
|
|
@@ -73,8 +72,10 @@ export class ServerInteraction<SessionT extends InteractionSession = Interaction
|
|
|
73
72
|
return writer.process(request);
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
invoke
|
|
77
|
-
// TODO
|
|
78
|
-
|
|
75
|
+
invoke(request: Invoke, session: SessionT): InvokeResult {
|
|
76
|
+
// TODO - validate request
|
|
77
|
+
|
|
78
|
+
const invoker = new CommandInvokeResponse(this.#node, session);
|
|
79
|
+
return invoker.process(request);
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -8,6 +8,7 @@ export * from "./AccessControl.js";
|
|
|
8
8
|
export * from "./AttributeReadResponse.js";
|
|
9
9
|
export * from "./AttributeSubscriptionResponse.js";
|
|
10
10
|
export * from "./AttributeWriteResponse.js";
|
|
11
|
+
export * from "./CommandInvokeResponse.js";
|
|
11
12
|
export * from "./DataResponse.js";
|
|
12
13
|
export * from "./EventReadResponse.js";
|
|
13
14
|
export * from "./ServerInteraction.js";
|
|
@@ -106,6 +106,18 @@ export class AttributeClient<T = any> {
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
get fabricScoped() {
|
|
110
|
+
return this.#isFabricScoped;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getLocal() {
|
|
114
|
+
return this.#interactionClient.getStoredAttribute({
|
|
115
|
+
endpointId: this.endpointId,
|
|
116
|
+
clusterId: this.clusterId,
|
|
117
|
+
attribute: this.attribute,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
109
121
|
/**
|
|
110
122
|
* Get the value of the attribute. Fabric scoped reads are always done with the remote.
|
|
111
123
|
* The `requestFromRemote` parameter allowed to force or prevent remote reads:
|
|
@@ -229,12 +229,6 @@ export type ClusterClientObj<T extends ClusterType = ClusterType> = {
|
|
|
229
229
|
/** Returns if a given Attribute with provided name is present and supported at the connected cluster server. */
|
|
230
230
|
isAttributeSupportedByName: (attributeName: string) => boolean;
|
|
231
231
|
|
|
232
|
-
/** Returns if a given Event Id is present and supported at the connected cluster server. */
|
|
233
|
-
isEventSupported: (eventId: EventId) => boolean;
|
|
234
|
-
|
|
235
|
-
/** Returns if a given Event with provided name is present and supported at the connected cluster server. */
|
|
236
|
-
isEventSupportedByName: (eventName: string) => boolean;
|
|
237
|
-
|
|
238
232
|
/** Returns if a given Command Id is present and supported at the connected cluster server. */
|
|
239
233
|
isCommandSupported: (commandId: CommandId) => boolean;
|
|
240
234
|
|
package/src/cluster/index.ts
CHANGED
|
@@ -16,7 +16,15 @@ import {
|
|
|
16
16
|
MaybePromise,
|
|
17
17
|
Observable,
|
|
18
18
|
} from "#general";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
EventNumber,
|
|
21
|
+
EventPriority,
|
|
22
|
+
FabricIndex,
|
|
23
|
+
resolveEventName,
|
|
24
|
+
TlvEventFilter,
|
|
25
|
+
TlvEventPath,
|
|
26
|
+
TypeFromSchema,
|
|
27
|
+
} from "#types";
|
|
20
28
|
import { EventStore, OccurrenceSummary } from "./EventStore.js";
|
|
21
29
|
import { NumberedOccurrence, Occurrence } from "./Occurrence.js";
|
|
22
30
|
|
|
@@ -49,7 +57,7 @@ export class OccurrenceManager {
|
|
|
49
57
|
|
|
50
58
|
// As we don't (yet) have storage with secondary indices we currently maintain indices in memory regardless of
|
|
51
59
|
// whether underlying store is volatile
|
|
52
|
-
|
|
60
|
+
#occurrences = new Array<OccurrenceSummary>();
|
|
53
61
|
|
|
54
62
|
#construction: Construction<OccurrenceManager>;
|
|
55
63
|
|
|
@@ -298,23 +306,82 @@ export class OccurrenceManager {
|
|
|
298
306
|
}
|
|
299
307
|
|
|
300
308
|
#dropOldOccurrences() {
|
|
301
|
-
|
|
302
|
-
if (
|
|
309
|
+
let toDelete = this.#storedEventCount - this.#bufferConfig.minEventAllowance;
|
|
310
|
+
if (toDelete <= 0) {
|
|
303
311
|
return;
|
|
304
312
|
}
|
|
305
313
|
|
|
306
|
-
logger.debug(`Event store is full; dropping ${
|
|
314
|
+
logger.debug(`Event store is full; dropping ${toDelete} old occurrence${toDelete === 1 ? "s" : ""}`);
|
|
315
|
+
|
|
316
|
+
const prioData = {
|
|
317
|
+
[EventPriority.Info]: {
|
|
318
|
+
count: this.#bufferConfig.minPriorityEventAllowance["info"],
|
|
319
|
+
minPosition: -1,
|
|
320
|
+
},
|
|
321
|
+
[EventPriority.Debug]: {
|
|
322
|
+
count: this.#bufferConfig.minPriorityEventAllowance["debug"],
|
|
323
|
+
minPosition: -1,
|
|
324
|
+
},
|
|
325
|
+
};
|
|
307
326
|
|
|
308
327
|
const asyncDrops = Array<PromiseLike<void>>();
|
|
309
328
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
329
|
+
// Find out on which index positions we reached the minimum allowed entries for each priority.
|
|
330
|
+
// For that we count the entries for Non-Critical priorities from the end of the list to the beginning.
|
|
331
|
+
for (let i = this.#occurrences.length - 1; i >= 0; i--) {
|
|
332
|
+
const { priority } = this.#occurrences[i];
|
|
333
|
+
|
|
334
|
+
if (priority !== EventPriority.Critical) {
|
|
335
|
+
const data = prioData[priority];
|
|
336
|
+
if (data.count > 0) {
|
|
337
|
+
data.count--;
|
|
338
|
+
if (data.count === 0) {
|
|
339
|
+
data.minPosition = i;
|
|
340
|
+
if (
|
|
341
|
+
prioData[EventPriority.Info].minPosition > -1 &&
|
|
342
|
+
prioData[EventPriority.Debug].minPosition > -1
|
|
343
|
+
) {
|
|
344
|
+
// We have found the minimum position for both priorities, we can stop
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Now we can drop the occurrences from the beginning of the list until we reach the minimum allowed entries
|
|
353
|
+
// for each priority, started with the lowest priority, until we reached the maximum number of entries we need
|
|
354
|
+
// to remove. Critical events are removed last from the beginning as many as needed to reach the maximum number
|
|
355
|
+
// of entries to remove.
|
|
356
|
+
// Deleted entries are marked as undefined in the array, so we can filter them out later in one pass.
|
|
357
|
+
const occurrences = this.#occurrences as Array<OccurrenceSummary | undefined>;
|
|
358
|
+
for (const priority of [EventPriority.Debug, EventPriority.Info, EventPriority.Critical]) {
|
|
359
|
+
const checkUpTo =
|
|
360
|
+
priority === EventPriority.Critical ? this.#storedEventCount : prioData[priority].minPosition;
|
|
361
|
+
if (checkUpTo === -1) {
|
|
362
|
+
// We have less than the minimum of this event type, so we can not remove any
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
for (let i = 0; i < checkUpTo && toDelete > 0; i++) {
|
|
366
|
+
if (occurrences[i] === undefined) continue;
|
|
367
|
+
const { priority: entryPriority, number } = occurrences[i]!;
|
|
368
|
+
if (entryPriority === priority) {
|
|
369
|
+
const drop = MaybePromise.catch(this.#store.delete(number), error =>
|
|
370
|
+
logger.warn(`Error dropping occurrence #${number}: ${error}`),
|
|
371
|
+
);
|
|
372
|
+
if (MaybePromise.is(drop)) {
|
|
373
|
+
asyncDrops.push(drop);
|
|
374
|
+
}
|
|
375
|
+
occurrences[i] = undefined;
|
|
376
|
+
toDelete--;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (toDelete <= 0) {
|
|
380
|
+
break;
|
|
316
381
|
}
|
|
317
382
|
}
|
|
383
|
+
this.#occurrences = occurrences.filter(entry => entry) as OccurrenceSummary[];
|
|
384
|
+
|
|
318
385
|
this.#storedEventCount = this.#occurrences.length;
|
|
319
386
|
|
|
320
387
|
if (asyncDrops.length) {
|
|
@@ -342,10 +409,23 @@ export namespace OccurrenceManager {
|
|
|
342
409
|
* {@link minimumEventAllowance}.
|
|
343
410
|
*/
|
|
344
411
|
maxEventAllowance: number;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Minimum allowances by priority. This ensures a minimum number of non-critical events avoid LRU
|
|
415
|
+
* harvesting. Critical events take the rest of the entries up to the defined global minimum.
|
|
416
|
+
*/
|
|
417
|
+
minPriorityEventAllowance: {
|
|
418
|
+
info: number;
|
|
419
|
+
debug: number;
|
|
420
|
+
};
|
|
345
421
|
}
|
|
346
422
|
|
|
347
423
|
export const DefaultBufferConfig: BufferConfig = {
|
|
348
424
|
minEventAllowance: 10_000,
|
|
349
425
|
maxEventAllowance: 11_000,
|
|
426
|
+
minPriorityEventAllowance: {
|
|
427
|
+
info: 2_000,
|
|
428
|
+
debug: 2_000,
|
|
429
|
+
},
|
|
350
430
|
};
|
|
351
431
|
}
|