@matter/node 0.11.9 → 0.12.0-alpha.0-20241211-6d80d8b5c
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/behavior/internal/BehaviorBacking.d.ts +6 -1
- package/dist/cjs/behavior/internal/BehaviorBacking.d.ts.map +1 -1
- package/dist/cjs/behavior/internal/BehaviorBacking.js +2 -1
- package/dist/cjs/behavior/internal/BehaviorBacking.js.map +1 -1
- package/dist/cjs/behavior/internal/ClientBehaviorBacking.d.ts +2 -1
- package/dist/cjs/behavior/internal/ClientBehaviorBacking.d.ts.map +1 -1
- package/dist/cjs/behavior/internal/ClientBehaviorBacking.js +4 -0
- package/dist/cjs/behavior/internal/ClientBehaviorBacking.js.map +1 -1
- package/dist/cjs/behavior/internal/ServerBehaviorBacking.d.ts +2 -0
- package/dist/cjs/behavior/internal/ServerBehaviorBacking.d.ts.map +1 -1
- package/dist/cjs/behavior/internal/ServerBehaviorBacking.js +37 -0
- package/dist/cjs/behavior/internal/ServerBehaviorBacking.js.map +1 -1
- package/dist/cjs/behavior/state/transaction/Transaction.d.ts +18 -18
- package/dist/cjs/behavior/system/events/EventsBehavior.d.ts +26 -0
- package/dist/cjs/behavior/system/events/EventsBehavior.d.ts.map +1 -0
- package/dist/cjs/behavior/system/events/EventsBehavior.js +74 -0
- package/dist/cjs/behavior/system/events/EventsBehavior.js.map +6 -0
- package/dist/cjs/behavior/system/events/index.d.ts +7 -0
- package/dist/cjs/behavior/system/events/index.d.ts.map +1 -0
- package/dist/cjs/behavior/system/events/index.js +24 -0
- package/dist/cjs/behavior/system/events/index.js.map +6 -0
- package/dist/cjs/behavior/system/network/ServerNetworkRuntime.d.ts.map +1 -1
- package/dist/cjs/behavior/system/network/ServerNetworkRuntime.js +3 -0
- package/dist/cjs/behavior/system/network/ServerNetworkRuntime.js.map +1 -1
- package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts +8 -0
- package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts.map +1 -1
- package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.js +12 -4
- package/dist/cjs/behaviors/administrator-commissioning/AdministratorCommissioningServer.js.map +1 -1
- package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts +2 -4
- package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
- package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +6 -8
- package/dist/cjs/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
- package/dist/cjs/endpoint/Agent.d.ts +1 -1
- package/dist/cjs/endpoint/Agent.js +1 -1
- package/dist/cjs/endpoint/index.d.ts +1 -1
- package/dist/cjs/endpoint/index.d.ts.map +1 -1
- package/dist/cjs/endpoint/index.js +1 -1
- package/dist/cjs/endpoint/index.js.map +1 -1
- package/dist/cjs/endpoint/properties/Behaviors.d.ts +13 -0
- package/dist/cjs/endpoint/properties/Behaviors.d.ts.map +1 -1
- package/dist/cjs/endpoint/properties/Behaviors.js +37 -24
- package/dist/cjs/endpoint/properties/Behaviors.js.map +2 -2
- package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts +8 -3
- package/dist/cjs/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
- package/dist/cjs/endpoint/properties/EndpointInitializer.js +5 -0
- package/dist/cjs/endpoint/properties/EndpointInitializer.js.map +1 -1
- package/dist/cjs/endpoint/server/BehaviorServer.d.ts +21 -0
- package/dist/cjs/endpoint/server/BehaviorServer.d.ts.map +1 -0
- package/dist/cjs/endpoint/server/BehaviorServer.js +272 -0
- package/dist/cjs/endpoint/server/BehaviorServer.js.map +6 -0
- package/dist/{esm/endpoint → cjs/endpoint/server}/EndpointServer.d.ts +5 -6
- package/dist/cjs/endpoint/server/EndpointServer.d.ts.map +1 -0
- package/dist/cjs/endpoint/{EndpointServer.js → server/EndpointServer.js} +38 -14
- package/dist/cjs/endpoint/server/EndpointServer.js.map +6 -0
- package/dist/cjs/{node → endpoint}/server/ServerEndpointInitializer.d.ts +4 -4
- package/dist/cjs/endpoint/server/ServerEndpointInitializer.d.ts.map +1 -0
- package/dist/cjs/{node → endpoint}/server/ServerEndpointInitializer.js +7 -7
- package/dist/cjs/endpoint/server/ServerEndpointInitializer.js.map +6 -0
- package/dist/cjs/endpoint/server/index.d.ts +8 -0
- package/dist/cjs/endpoint/server/index.d.ts.map +1 -0
- package/dist/cjs/endpoint/server/index.js +25 -0
- package/dist/cjs/endpoint/server/index.js.map +6 -0
- package/dist/cjs/endpoint/type/MutableEndpoint.d.ts.map +1 -1
- package/dist/cjs/endpoint/type/MutableEndpoint.js +3 -0
- package/dist/cjs/endpoint/type/MutableEndpoint.js.map +1 -1
- package/dist/cjs/node/Node.d.ts +5 -0
- package/dist/cjs/node/Node.d.ts.map +1 -1
- package/dist/cjs/node/Node.js +17 -2
- package/dist/cjs/node/Node.js.map +1 -1
- package/dist/cjs/node/NodeLifecycle.d.ts +11 -1
- package/dist/cjs/node/NodeLifecycle.d.ts.map +1 -1
- package/dist/cjs/node/NodeLifecycle.js +14 -0
- package/dist/cjs/node/NodeLifecycle.js.map +1 -1
- package/dist/cjs/node/ServerNode.d.ts +3 -2
- package/dist/cjs/node/ServerNode.d.ts.map +1 -1
- package/dist/cjs/node/ServerNode.js +9 -9
- package/dist/cjs/node/ServerNode.js.map +1 -1
- package/dist/cjs/node/server/ServerEnvironment.js +1 -1
- package/dist/cjs/node/server/ServerEnvironment.js.map +1 -1
- package/dist/cjs/node/server/TransactionalInteractionServer.d.ts +2 -2
- package/dist/cjs/node/server/TransactionalInteractionServer.d.ts.map +1 -1
- package/dist/cjs/node/server/TransactionalInteractionServer.js +9 -4
- package/dist/cjs/node/server/TransactionalInteractionServer.js.map +2 -2
- package/dist/cjs/node/server/index.d.ts +0 -1
- package/dist/cjs/node/server/index.d.ts.map +1 -1
- package/dist/cjs/node/server/index.js +0 -1
- package/dist/cjs/node/server/index.js.map +1 -1
- package/dist/esm/behavior/internal/BehaviorBacking.d.ts +6 -1
- package/dist/esm/behavior/internal/BehaviorBacking.d.ts.map +1 -1
- package/dist/esm/behavior/internal/BehaviorBacking.js +2 -1
- package/dist/esm/behavior/internal/BehaviorBacking.js.map +1 -1
- package/dist/esm/behavior/internal/ClientBehaviorBacking.d.ts +2 -1
- package/dist/esm/behavior/internal/ClientBehaviorBacking.d.ts.map +1 -1
- package/dist/esm/behavior/internal/ClientBehaviorBacking.js +4 -0
- package/dist/esm/behavior/internal/ClientBehaviorBacking.js.map +1 -1
- package/dist/esm/behavior/internal/ServerBehaviorBacking.d.ts +2 -0
- package/dist/esm/behavior/internal/ServerBehaviorBacking.d.ts.map +1 -1
- package/dist/esm/behavior/internal/ServerBehaviorBacking.js +37 -0
- package/dist/esm/behavior/internal/ServerBehaviorBacking.js.map +1 -1
- package/dist/esm/behavior/state/transaction/Transaction.d.ts +18 -18
- package/dist/esm/behavior/system/events/EventsBehavior.d.ts +26 -0
- package/dist/esm/behavior/system/events/EventsBehavior.d.ts.map +1 -0
- package/dist/esm/behavior/system/events/EventsBehavior.js +54 -0
- package/dist/esm/behavior/system/events/EventsBehavior.js.map +6 -0
- package/dist/esm/behavior/system/events/index.d.ts +7 -0
- package/dist/esm/behavior/system/events/index.d.ts.map +1 -0
- package/dist/esm/behavior/system/events/index.js +7 -0
- package/dist/esm/behavior/system/events/index.js.map +6 -0
- package/dist/esm/behavior/system/network/ServerNetworkRuntime.d.ts.map +1 -1
- package/dist/esm/behavior/system/network/ServerNetworkRuntime.js +3 -0
- package/dist/esm/behavior/system/network/ServerNetworkRuntime.js.map +1 -1
- package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts +8 -0
- package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.d.ts.map +1 -1
- package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.js +12 -4
- package/dist/esm/behaviors/administrator-commissioning/AdministratorCommissioningServer.js.map +1 -1
- package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts +2 -4
- package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.d.ts.map +1 -1
- package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js +6 -8
- package/dist/esm/behaviors/general-diagnostics/GeneralDiagnosticsServer.js.map +1 -1
- package/dist/esm/endpoint/Agent.d.ts +1 -1
- package/dist/esm/endpoint/Agent.js +1 -1
- package/dist/esm/endpoint/index.d.ts +1 -1
- package/dist/esm/endpoint/index.d.ts.map +1 -1
- package/dist/esm/endpoint/index.js +1 -1
- package/dist/esm/endpoint/properties/Behaviors.d.ts +13 -0
- package/dist/esm/endpoint/properties/Behaviors.d.ts.map +1 -1
- package/dist/esm/endpoint/properties/Behaviors.js +38 -27
- package/dist/esm/endpoint/properties/Behaviors.js.map +2 -2
- package/dist/esm/endpoint/properties/EndpointInitializer.d.ts +8 -3
- package/dist/esm/endpoint/properties/EndpointInitializer.d.ts.map +1 -1
- package/dist/esm/endpoint/properties/EndpointInitializer.js +5 -0
- package/dist/esm/endpoint/properties/EndpointInitializer.js.map +1 -1
- package/dist/esm/endpoint/server/BehaviorServer.d.ts +21 -0
- package/dist/esm/endpoint/server/BehaviorServer.d.ts.map +1 -0
- package/dist/esm/endpoint/server/BehaviorServer.js +269 -0
- package/dist/esm/endpoint/server/BehaviorServer.js.map +6 -0
- package/dist/{cjs/endpoint → esm/endpoint/server}/EndpointServer.d.ts +5 -6
- package/dist/esm/endpoint/server/EndpointServer.d.ts.map +1 -0
- package/dist/esm/endpoint/{EndpointServer.js → server/EndpointServer.js} +38 -14
- package/dist/esm/endpoint/server/EndpointServer.js.map +6 -0
- package/dist/esm/{node → endpoint}/server/ServerEndpointInitializer.d.ts +4 -4
- package/dist/esm/endpoint/server/ServerEndpointInitializer.d.ts.map +1 -0
- package/dist/esm/{node → endpoint}/server/ServerEndpointInitializer.js +7 -7
- package/dist/esm/endpoint/server/ServerEndpointInitializer.js.map +6 -0
- package/dist/esm/endpoint/server/index.d.ts +8 -0
- package/dist/esm/endpoint/server/index.d.ts.map +1 -0
- package/dist/esm/endpoint/server/index.js +8 -0
- package/dist/esm/endpoint/server/index.js.map +6 -0
- package/dist/esm/endpoint/type/MutableEndpoint.d.ts.map +1 -1
- package/dist/esm/endpoint/type/MutableEndpoint.js +3 -0
- package/dist/esm/endpoint/type/MutableEndpoint.js.map +1 -1
- package/dist/esm/node/Node.d.ts +5 -0
- package/dist/esm/node/Node.d.ts.map +1 -1
- package/dist/esm/node/Node.js +17 -2
- package/dist/esm/node/Node.js.map +1 -1
- package/dist/esm/node/NodeLifecycle.d.ts +11 -1
- package/dist/esm/node/NodeLifecycle.d.ts.map +1 -1
- package/dist/esm/node/NodeLifecycle.js +15 -1
- package/dist/esm/node/NodeLifecycle.js.map +1 -1
- package/dist/esm/node/ServerNode.d.ts +3 -2
- package/dist/esm/node/ServerNode.d.ts.map +1 -1
- package/dist/esm/node/ServerNode.js +10 -10
- package/dist/esm/node/ServerNode.js.map +1 -1
- package/dist/esm/node/server/ServerEnvironment.js +1 -1
- package/dist/esm/node/server/ServerEnvironment.js.map +1 -1
- package/dist/esm/node/server/TransactionalInteractionServer.d.ts +2 -2
- package/dist/esm/node/server/TransactionalInteractionServer.d.ts.map +1 -1
- package/dist/esm/node/server/TransactionalInteractionServer.js +9 -4
- package/dist/esm/node/server/TransactionalInteractionServer.js.map +2 -2
- package/dist/esm/node/server/index.d.ts +0 -1
- package/dist/esm/node/server/index.d.ts.map +1 -1
- package/dist/esm/node/server/index.js +0 -1
- package/dist/esm/node/server/index.js.map +1 -1
- package/package.json +7 -7
- package/src/behavior/internal/BehaviorBacking.ts +9 -1
- package/src/behavior/internal/ClientBehaviorBacking.ts +6 -1
- package/src/behavior/internal/ServerBehaviorBacking.ts +51 -0
- package/src/behavior/system/events/EventsBehavior.ts +60 -0
- package/src/behavior/system/events/index.ts +7 -0
- package/src/behavior/system/network/ServerNetworkRuntime.ts +6 -0
- package/src/behaviors/administrator-commissioning/AdministratorCommissioningServer.ts +14 -4
- package/src/behaviors/general-diagnostics/GeneralDiagnosticsServer.ts +16 -9
- package/src/endpoint/Agent.ts +1 -1
- package/src/endpoint/index.ts +1 -1
- package/src/endpoint/properties/Behaviors.ts +56 -28
- package/src/endpoint/properties/EndpointInitializer.ts +9 -3
- package/src/endpoint/server/BehaviorServer.ts +387 -0
- package/src/endpoint/{EndpointServer.ts → server/EndpointServer.ts} +50 -20
- package/src/{node → endpoint}/server/ServerEndpointInitializer.ts +7 -7
- package/src/endpoint/server/index.ts +8 -0
- package/src/endpoint/type/MutableEndpoint.ts +4 -0
- package/src/node/Node.ts +22 -4
- package/src/node/NodeLifecycle.ts +17 -1
- package/src/node/ServerNode.ts +11 -13
- package/src/node/server/ServerEnvironment.ts +1 -1
- package/src/node/server/TransactionalInteractionServer.ts +13 -7
- package/src/node/server/index.ts +0 -1
- package/dist/cjs/behavior/internal/ClusterServerBacking.d.ts +0 -36
- package/dist/cjs/behavior/internal/ClusterServerBacking.d.ts.map +0 -1
- package/dist/cjs/behavior/internal/ClusterServerBacking.js +0 -342
- package/dist/cjs/behavior/internal/ClusterServerBacking.js.map +0 -6
- package/dist/cjs/endpoint/EndpointServer.d.ts.map +0 -1
- package/dist/cjs/endpoint/EndpointServer.js.map +0 -6
- package/dist/cjs/node/server/ServerEndpointInitializer.d.ts.map +0 -1
- package/dist/cjs/node/server/ServerEndpointInitializer.js.map +0 -6
- package/dist/esm/behavior/internal/ClusterServerBacking.d.ts +0 -36
- package/dist/esm/behavior/internal/ClusterServerBacking.d.ts.map +0 -1
- package/dist/esm/behavior/internal/ClusterServerBacking.js +0 -330
- package/dist/esm/behavior/internal/ClusterServerBacking.js.map +0 -6
- package/dist/esm/endpoint/EndpointServer.d.ts.map +0 -1
- package/dist/esm/endpoint/EndpointServer.js.map +0 -6
- package/dist/esm/node/server/ServerEndpointInitializer.d.ts.map +0 -1
- package/dist/esm/node/server/ServerEndpointInitializer.js.map +0 -6
- package/src/behavior/internal/ClusterServerBacking.ts +0 -446
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Behavior } from "#behavior/Behavior.js";
|
|
8
|
-
import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
|
|
9
|
-
import {
|
|
7
|
+
import type { Behavior } from "#behavior/Behavior.js";
|
|
8
|
+
import type { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
|
|
9
|
+
import type { Agent } from "#endpoint/Agent.js";
|
|
10
|
+
import type { Endpoint } from "../Endpoint.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Base class for {@link Endpoint} initialization services.
|
|
@@ -35,4 +36,9 @@ export abstract class EndpointInitializer {
|
|
|
35
36
|
* @returns a new {@link BehaviorBacking}
|
|
36
37
|
*/
|
|
37
38
|
abstract createBacking(endpoint: Endpoint, type: Behavior.Type): BehaviorBacking;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Invoked after behaviors are initialized but before the initialization transaction commits.
|
|
42
|
+
*/
|
|
43
|
+
behaviorsInitialized(_agent: Agent) {}
|
|
38
44
|
}
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2024 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { AccessControl } from "#behavior/AccessControl.js";
|
|
8
|
+
import { Behavior } from "#behavior/Behavior.js";
|
|
9
|
+
import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
|
|
10
|
+
import { type ClusterEvents, Contextual, Resource } from "#behavior/index.js";
|
|
11
|
+
import { StructManager } from "#behavior/state/managed/values/StructManager.js";
|
|
12
|
+
import { Status } from "#behavior/state/transaction/Status.js";
|
|
13
|
+
import { Val } from "#behavior/state/Val.js";
|
|
14
|
+
import { Endpoint } from "#endpoint/Endpoint.js";
|
|
15
|
+
import {
|
|
16
|
+
camelize,
|
|
17
|
+
Diagnostic,
|
|
18
|
+
ImplementationError,
|
|
19
|
+
InternalError,
|
|
20
|
+
isObject,
|
|
21
|
+
Logger,
|
|
22
|
+
MaybePromise,
|
|
23
|
+
Observable,
|
|
24
|
+
ObserverGroup,
|
|
25
|
+
} from "#general";
|
|
26
|
+
import { CommandModel, ElementTag } from "#model";
|
|
27
|
+
import {
|
|
28
|
+
AttributeServer,
|
|
29
|
+
ClusterDatasource,
|
|
30
|
+
ClusterServer,
|
|
31
|
+
CommandServer,
|
|
32
|
+
createAttributeServer as ConstructAttributeServer,
|
|
33
|
+
EventServer,
|
|
34
|
+
FabricManager,
|
|
35
|
+
Message,
|
|
36
|
+
OccurrenceManager,
|
|
37
|
+
SecureSession,
|
|
38
|
+
} from "#protocol";
|
|
39
|
+
import { Attribute, Command, Event } from "#types";
|
|
40
|
+
import type { EndpointServer } from "./EndpointServer.js";
|
|
41
|
+
|
|
42
|
+
const logger = Logger.get("BehaviorServer");
|
|
43
|
+
|
|
44
|
+
export interface BehaviorServer extends ClusterServer {
|
|
45
|
+
close(): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a {@link ClusterServer} for a {@link ClusterBehavior}.
|
|
50
|
+
*
|
|
51
|
+
* Note that we only create servers for cluster behaviors. Other {@link Behavior} implementations do not have proper
|
|
52
|
+
* metadata to go online.
|
|
53
|
+
*
|
|
54
|
+
* TODO - refactor element server management after we remove the old API
|
|
55
|
+
*/
|
|
56
|
+
export function BehaviorServer(endpointServer: EndpointServer, type: ClusterBehavior.Type): BehaviorServer {
|
|
57
|
+
const { id, name, attributes, commands, events } = type.cluster;
|
|
58
|
+
|
|
59
|
+
const { endpoint } = endpointServer;
|
|
60
|
+
const datasource = createClusterDatasource(endpoint, type);
|
|
61
|
+
const observers = new ObserverGroup();
|
|
62
|
+
|
|
63
|
+
const clusterServer: BehaviorServer = {
|
|
64
|
+
id,
|
|
65
|
+
name,
|
|
66
|
+
datasource,
|
|
67
|
+
attributes: {},
|
|
68
|
+
commands: {},
|
|
69
|
+
events: {},
|
|
70
|
+
close() {
|
|
71
|
+
observers.close();
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Extract read-only state and observables for setup purposes
|
|
76
|
+
const stateView = endpoint.stateOf(type) as Val.Struct;
|
|
77
|
+
const observables = endpoint.eventsOf(type) as unknown as Record<string, Observable>;
|
|
78
|
+
const elements = endpointServer.endpoint.behaviors.elementsOf(type);
|
|
79
|
+
|
|
80
|
+
// Add attribute servers. Include global attributes as well as cluster attributes
|
|
81
|
+
for (const name of elements.attributes) {
|
|
82
|
+
const attribute = attributes[name];
|
|
83
|
+
|
|
84
|
+
const server = createAttributeServer(
|
|
85
|
+
name,
|
|
86
|
+
attribute,
|
|
87
|
+
endpoint,
|
|
88
|
+
type,
|
|
89
|
+
datasource,
|
|
90
|
+
stateView,
|
|
91
|
+
observables,
|
|
92
|
+
observers,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
clusterServer.attributes[name] = server;
|
|
96
|
+
server.assignToEndpoint(endpointServer);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add command servers
|
|
100
|
+
for (const name of elements.commands) {
|
|
101
|
+
const command = commands[name];
|
|
102
|
+
|
|
103
|
+
clusterServer.commands[name] = createCommandServer(name, command, endpoint, type);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Event servers
|
|
107
|
+
for (const name of elements.events) {
|
|
108
|
+
const server = createEventServer(name, events[name], endpoint, type, observables, observers);
|
|
109
|
+
|
|
110
|
+
clusterServer.events[name] = server;
|
|
111
|
+
server.assignToEndpoint(endpointServer);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return clusterServer;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create the {@link ClusterDatasource} that adapts the Behavior API to the AttributeServer API.
|
|
119
|
+
*/
|
|
120
|
+
function createClusterDatasource(endpoint: Endpoint, type: Behavior.Type): ClusterDatasource {
|
|
121
|
+
const env = endpoint.env;
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
get version() {
|
|
125
|
+
return endpoint.behaviors.versionOf(type);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
get eventHandler() {
|
|
129
|
+
return env.get(OccurrenceManager);
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
get fabrics() {
|
|
133
|
+
return env.get(FabricManager).fabrics;
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// We handle change management ourselves
|
|
137
|
+
changed() {},
|
|
138
|
+
|
|
139
|
+
// We handle version management ourselves
|
|
140
|
+
increaseVersion() {
|
|
141
|
+
return this.version;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function createAttributeServer(
|
|
147
|
+
name: string,
|
|
148
|
+
definition: Attribute<any, any>,
|
|
149
|
+
endpoint: Endpoint,
|
|
150
|
+
type: ClusterBehavior.Type,
|
|
151
|
+
datasource: ClusterDatasource,
|
|
152
|
+
stateView: Val.Struct,
|
|
153
|
+
observables: Record<string, Observable>,
|
|
154
|
+
observers: ObserverGroup,
|
|
155
|
+
) {
|
|
156
|
+
function getter(_session: any, _endpoint: any, _isFabricFiltered: any, message?: Message) {
|
|
157
|
+
if (!message) {
|
|
158
|
+
// If there is no message this is getLocal
|
|
159
|
+
return stateView[name];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const behavior = behaviorFor(endpoint, type, message);
|
|
163
|
+
|
|
164
|
+
behavior.context.activity?.frame(`read ${name}`);
|
|
165
|
+
|
|
166
|
+
const trace = behavior.context.trace;
|
|
167
|
+
if (trace) {
|
|
168
|
+
trace.path = endpoint.path.at(name);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
logger.debug("Read", Diagnostic.strong(`${endpoint}.state.${name}`), "via", behavior.context.transaction.via);
|
|
172
|
+
|
|
173
|
+
const state = behavior.state as Val.Struct;
|
|
174
|
+
|
|
175
|
+
StructManager.assertDirectReadAuthorized(state, name);
|
|
176
|
+
|
|
177
|
+
if (trace) {
|
|
178
|
+
trace.output = state[name];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return state[name];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function setter(value: any, _session: any, _endpoint: any, message?: Message) {
|
|
185
|
+
const behavior = behaviorFor(endpoint, type, message);
|
|
186
|
+
|
|
187
|
+
behavior.context.activity?.frame(`write ${name}`);
|
|
188
|
+
|
|
189
|
+
logger.info("Write", Diagnostic.strong(`${endpoint}.state.${name}`), "via", behavior.context.transaction.via);
|
|
190
|
+
|
|
191
|
+
const trace = behavior.context.trace;
|
|
192
|
+
if (trace) {
|
|
193
|
+
trace.path = endpoint.path.at(name);
|
|
194
|
+
trace.input = value;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const state = behavior.state as Val.Struct;
|
|
198
|
+
|
|
199
|
+
state[name] = value;
|
|
200
|
+
|
|
201
|
+
// If the transaction is a write transaction, report that the attribute is updated
|
|
202
|
+
return behavior.context.transaction?.status === Status.Exclusive;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const server = ConstructAttributeServer(
|
|
206
|
+
type.cluster,
|
|
207
|
+
definition,
|
|
208
|
+
name,
|
|
209
|
+
stateView[name],
|
|
210
|
+
datasource,
|
|
211
|
+
getter,
|
|
212
|
+
setter,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Wire events (FixedAttributeServer is not an AttributeServer so we skip that)
|
|
216
|
+
if (server instanceof AttributeServer) {
|
|
217
|
+
const observable = observables[`${name}$Changed`] as ClusterEvents.AttributeObservable;
|
|
218
|
+
if (observable !== undefined) {
|
|
219
|
+
observers.on(observable, (_value, _oldValue, context) => {
|
|
220
|
+
const session = context.session;
|
|
221
|
+
if (session instanceof SecureSession) {
|
|
222
|
+
server.updated(session);
|
|
223
|
+
} else {
|
|
224
|
+
server.updatedLocal();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return server;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function createCommandServer(
|
|
234
|
+
name: string,
|
|
235
|
+
definition: Command<any, any, any>,
|
|
236
|
+
endpoint: Endpoint,
|
|
237
|
+
type: ClusterBehavior.Type,
|
|
238
|
+
) {
|
|
239
|
+
// TODO: Introduce nicer ways to get command incl caching and such, aka "make api suck less"
|
|
240
|
+
const schema = type.schema?.member(camelize(name, true), [ElementTag.Command]) as CommandModel;
|
|
241
|
+
if (schema === undefined) {
|
|
242
|
+
throw new ImplementationError(`There is no metadata for command ${name}`);
|
|
243
|
+
}
|
|
244
|
+
const access = AccessControl(schema);
|
|
245
|
+
|
|
246
|
+
const handler = (request: unknown, _session: unknown, message: Message) => {
|
|
247
|
+
let requestDiagnostic: unknown;
|
|
248
|
+
if (isObject(request)) {
|
|
249
|
+
requestDiagnostic = Diagnostic.dict(request);
|
|
250
|
+
} else if (request !== undefined) {
|
|
251
|
+
requestDiagnostic = request;
|
|
252
|
+
} else {
|
|
253
|
+
requestDiagnostic = Diagnostic.weak("(no payload)");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const behavior = behaviorFor(endpoint, type, message);
|
|
257
|
+
|
|
258
|
+
const path = endpoint.path.at(name);
|
|
259
|
+
|
|
260
|
+
const trace = behavior.context.trace;
|
|
261
|
+
if (trace) {
|
|
262
|
+
trace.path = endpoint.path.at(name);
|
|
263
|
+
trace.input = request;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
logger.info("Invoke", Diagnostic.strong(path.toString()), behavior.context.transaction.via, requestDiagnostic);
|
|
267
|
+
|
|
268
|
+
access.authorizeInvoke(behavior.context, {
|
|
269
|
+
path,
|
|
270
|
+
cluster: behavior.cluster.id,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
let isAsync = false;
|
|
274
|
+
let activity: undefined | Disposable;
|
|
275
|
+
let result: unknown;
|
|
276
|
+
try {
|
|
277
|
+
activity = behavior.context?.activity?.frame(`invoke ${name}`);
|
|
278
|
+
|
|
279
|
+
const invoke = (behavior as unknown as Record<string, (arg: unknown) => unknown>)[name].bind(behavior);
|
|
280
|
+
|
|
281
|
+
// Lock if necessary, then invoke
|
|
282
|
+
if ((behavior.constructor as ClusterBehavior.Type).lockOnInvoke) {
|
|
283
|
+
const tx = behavior.context.transaction;
|
|
284
|
+
if (Resource.isLocked(behavior)) {
|
|
285
|
+
// Automatic locking with locked resource; requires async lock acquisition
|
|
286
|
+
result = (async function invokeAsync() {
|
|
287
|
+
await tx.addResources(behavior);
|
|
288
|
+
await tx.begin();
|
|
289
|
+
return invoke(request);
|
|
290
|
+
})();
|
|
291
|
+
} else {
|
|
292
|
+
// Automatic locking on unlocked resource; may proceed synchronously
|
|
293
|
+
tx.addResourcesSync(behavior);
|
|
294
|
+
tx.beginSync();
|
|
295
|
+
result = invoke(request);
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
// Automatic locking disabled
|
|
299
|
+
result = invoke(request);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (MaybePromise.is(result)) {
|
|
303
|
+
isAsync = true;
|
|
304
|
+
result = Promise.resolve(result)
|
|
305
|
+
.then(result => {
|
|
306
|
+
if (trace) {
|
|
307
|
+
trace.output = result;
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
})
|
|
311
|
+
.finally(() => activity?.[Symbol.dispose]());
|
|
312
|
+
} else if (trace) {
|
|
313
|
+
trace.output = result;
|
|
314
|
+
}
|
|
315
|
+
} finally {
|
|
316
|
+
if (!isAsync) {
|
|
317
|
+
activity?.[Symbol.dispose]();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return result;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const server = new CommandServer(
|
|
325
|
+
definition.requestId,
|
|
326
|
+
definition.responseId,
|
|
327
|
+
name,
|
|
328
|
+
definition.requestSchema,
|
|
329
|
+
definition.responseSchema,
|
|
330
|
+
definition.timed,
|
|
331
|
+
definition.invokeAcl,
|
|
332
|
+
handler,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
// Eliminate redundant diagnostic messages
|
|
336
|
+
server.debug = () => {};
|
|
337
|
+
|
|
338
|
+
return server;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function createEventServer(
|
|
342
|
+
name: string,
|
|
343
|
+
definition: Event<any, any>,
|
|
344
|
+
endpoint: Endpoint,
|
|
345
|
+
type: ClusterBehavior.Type,
|
|
346
|
+
observables: Record<string, Observable>,
|
|
347
|
+
observers: ObserverGroup,
|
|
348
|
+
) {
|
|
349
|
+
const observable = observables[name] as ClusterEvents.EventObservable;
|
|
350
|
+
|
|
351
|
+
const server = new EventServer(
|
|
352
|
+
definition.id,
|
|
353
|
+
type.cluster.id,
|
|
354
|
+
name,
|
|
355
|
+
definition.schema,
|
|
356
|
+
definition.priority,
|
|
357
|
+
definition.readAcl,
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
if (observable !== undefined) {
|
|
361
|
+
observers.on(observable, (payload, _context) => {
|
|
362
|
+
const maybePromise = server.triggerEvent(payload);
|
|
363
|
+
if (MaybePromise.is(maybePromise)) {
|
|
364
|
+
endpoint.env.runtime.add(maybePromise);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const promise = server.bindToEventHandler(endpoint.env.get(OccurrenceManager));
|
|
370
|
+
if (MaybePromise.is(promise)) {
|
|
371
|
+
// Current code structure means this should never happen. Refactor after removal of old API will resolve this
|
|
372
|
+
throw new InternalError("Event handler binding returned a promise");
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return server;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function behaviorFor(endpoint: Endpoint, type: ClusterBehavior.Type, message: Message | undefined) {
|
|
379
|
+
const context = Contextual.contextOf(message);
|
|
380
|
+
if (!context) {
|
|
381
|
+
throw new InternalError("Message context not installed");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const agent = context.agentFor(endpoint);
|
|
385
|
+
|
|
386
|
+
return agent.get(type);
|
|
387
|
+
}
|
|
@@ -6,13 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import { Behavior } from "#behavior/Behavior.js";
|
|
8
8
|
import { ClusterBehavior } from "#behavior/cluster/ClusterBehavior.js";
|
|
9
|
-
import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
|
|
10
|
-
import { ClusterServerBacking } from "#behavior/internal/ClusterServerBacking.js";
|
|
11
|
-
import { ServerBehaviorBacking } from "#behavior/internal/ServerBehaviorBacking.js";
|
|
12
9
|
import { ImplementationError, InternalError, NotImplementedError } from "#general";
|
|
13
10
|
import { ClusterClientObj, ClusterServer, EndpointInterface } from "#protocol";
|
|
14
11
|
import { ClusterId, ClusterType, EndpointNumber } from "#types";
|
|
15
|
-
import { Endpoint } from "
|
|
12
|
+
import { Endpoint } from "../Endpoint.js";
|
|
13
|
+
import { BehaviorServer } from "./BehaviorServer.js";
|
|
16
14
|
|
|
17
15
|
const SERVER = Symbol("server");
|
|
18
16
|
interface ServerEndpoint extends Endpoint {
|
|
@@ -25,7 +23,7 @@ interface ServerEndpoint extends Endpoint {
|
|
|
25
23
|
export class EndpointServer implements EndpointInterface {
|
|
26
24
|
#endpoint: Endpoint;
|
|
27
25
|
#name = "";
|
|
28
|
-
readonly #clusterServers = new Map<ClusterId,
|
|
26
|
+
readonly #clusterServers = new Map<ClusterId, BehaviorServer>();
|
|
29
27
|
|
|
30
28
|
get endpoint() {
|
|
31
29
|
return this.#endpoint;
|
|
@@ -36,26 +34,24 @@ export class EndpointServer implements EndpointInterface {
|
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
constructor(endpoint: Endpoint) {
|
|
39
|
-
|
|
37
|
+
// Sanity checks
|
|
38
|
+
if ((endpoint as ServerEndpoint)[SERVER] !== undefined) {
|
|
39
|
+
throw new InternalError(`Endpoint ${endpoint} cluster server is already initialized`);
|
|
40
|
+
}
|
|
41
|
+
endpoint.construction.assert();
|
|
42
|
+
|
|
40
43
|
this.#endpoint = endpoint;
|
|
41
44
|
this.#name = endpoint.type.name;
|
|
42
|
-
}
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
let backing: BehaviorBacking;
|
|
46
|
-
if (type.prototype instanceof ClusterBehavior) {
|
|
47
|
-
const cluster = (type as ClusterBehavior.Type).cluster;
|
|
46
|
+
(endpoint as ServerEndpoint)[SERVER] = this;
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
48
|
+
for (const type of Object.values(endpoint.behaviors.supported)) {
|
|
49
|
+
if (!(type.prototype instanceof ClusterBehavior)) {
|
|
50
|
+
continue;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
} else {
|
|
56
|
-
backing = new ServerBehaviorBacking(this.#endpoint, type, this.#endpoint.behaviors.optionsFor(type));
|
|
53
|
+
this.#serve(type);
|
|
57
54
|
}
|
|
58
|
-
return backing;
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
get number() {
|
|
@@ -66,6 +62,20 @@ export class EndpointServer implements EndpointInterface {
|
|
|
66
62
|
return this.#name;
|
|
67
63
|
}
|
|
68
64
|
|
|
65
|
+
updateServers() {
|
|
66
|
+
for (const type of Object.values(this.#endpoint.behaviors.supported)) {
|
|
67
|
+
if (!(type.prototype instanceof ClusterBehavior)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (this.#clusterServers.has((type as ClusterBehavior.Type).cluster?.id)) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.#serve(type);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
getNumber(): EndpointNumber {
|
|
70
80
|
if (this.number === undefined) {
|
|
71
81
|
throw new InternalError("Endpoint ID has not been assigned yet");
|
|
@@ -103,7 +113,9 @@ export class EndpointServer implements EndpointInterface {
|
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
async [Symbol.asyncDispose]() {
|
|
106
|
-
|
|
116
|
+
for (const server of this.#clusterServers.values()) {
|
|
117
|
+
server.close();
|
|
118
|
+
}
|
|
107
119
|
this.#clusterServers.clear();
|
|
108
120
|
delete (this.#endpoint as ServerEndpoint)[SERVER];
|
|
109
121
|
for (const endpoint of this.#endpoint.parts) {
|
|
@@ -118,7 +130,7 @@ export class EndpointServer implements EndpointInterface {
|
|
|
118
130
|
// Unused, should move out of EndpointInterface
|
|
119
131
|
}
|
|
120
132
|
|
|
121
|
-
addClusterServer(server:
|
|
133
|
+
addClusterServer(server: BehaviorServer): void {
|
|
122
134
|
this.#clusterServers.set(server.id, server);
|
|
123
135
|
}
|
|
124
136
|
|
|
@@ -173,6 +185,24 @@ export class EndpointServer implements EndpointInterface {
|
|
|
173
185
|
}
|
|
174
186
|
}
|
|
175
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Install a ClusterServer for a behavior.
|
|
190
|
+
*/
|
|
191
|
+
#serve(type: Behavior.Type) {
|
|
192
|
+
// Sanity checks
|
|
193
|
+
const { cluster } = type as ClusterBehavior.Type;
|
|
194
|
+
if (cluster === undefined) {
|
|
195
|
+
throw new InternalError(`Cannot serve ${this.endpoint} behavior ${type.id}} because it is not a cluster`);
|
|
196
|
+
}
|
|
197
|
+
if (this.#clusterServers.has(cluster.id)) {
|
|
198
|
+
throw new ImplementationError(`Duplicate behaviors for ${this.endpoint} define cluster ${cluster.id}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Create ClusterServer
|
|
202
|
+
const clusterServer = BehaviorServer(this, type as ClusterBehavior.Type);
|
|
203
|
+
this.#clusterServers.set(cluster.id, clusterServer);
|
|
204
|
+
}
|
|
205
|
+
|
|
176
206
|
/**
|
|
177
207
|
* Retrieve the server for an endpoint.
|
|
178
208
|
*/
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
import { Behavior } from "#behavior/Behavior.js";
|
|
8
8
|
import { BehaviorBacking } from "#behavior/internal/BehaviorBacking.js";
|
|
9
|
+
import { ServerBehaviorBacking } from "#behavior/internal/ServerBehaviorBacking.js";
|
|
9
10
|
import { Endpoint } from "#endpoint/Endpoint.js";
|
|
10
|
-
import { EndpointServer } from "#endpoint/EndpointServer.js";
|
|
11
11
|
import { EndpointInitializer } from "#endpoint/properties/EndpointInitializer.js";
|
|
12
12
|
import { Environment, InternalError, Logger } from "#general";
|
|
13
13
|
import { DescriptorServer } from "../../behaviors/descriptor/DescriptorServer.js";
|
|
14
|
-
import { ServerNodeStore } from "
|
|
14
|
+
import { ServerNodeStore } from "../../node/storage/ServerNodeStore.js";
|
|
15
15
|
|
|
16
16
|
const logger = Logger.get("BehaviorInit");
|
|
17
17
|
|
|
@@ -50,13 +50,13 @@ export class ServerEndpointInitializer extends EndpointInitializer {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
* {@link BehaviorBacking} for a specific {@link Behavior}.
|
|
53
|
+
* Create the backing.
|
|
55
54
|
*
|
|
56
|
-
*
|
|
55
|
+
* If the behavior is a cluster behavior and the node is already initialized, create a server when the behavior
|
|
56
|
+
* initializes.
|
|
57
57
|
*/
|
|
58
|
-
createBacking(endpoint: Endpoint,
|
|
59
|
-
return
|
|
58
|
+
createBacking(endpoint: Endpoint, type: Behavior.Type): BehaviorBacking {
|
|
59
|
+
return new ServerBehaviorBacking(endpoint, type, endpoint.behaviors.optionsFor(type));
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -75,6 +75,10 @@ export function MutableEndpoint<const T extends EndpointType.Options>(options: T
|
|
|
75
75
|
behaviors: SupportedBehaviors.extend(this.behaviors, behaviors),
|
|
76
76
|
});
|
|
77
77
|
},
|
|
78
|
+
|
|
79
|
+
withBehaviors(this: MutableEndpoint, ...behaviors: Behavior.Type[]) {
|
|
80
|
+
return this.with(...behaviors);
|
|
81
|
+
},
|
|
78
82
|
} as unknown as MutableEndpoint.With<
|
|
79
83
|
EndpointType.For<T>,
|
|
80
84
|
T["behaviors"] extends SupportedBehaviors ? T["behaviors"] : {}
|
package/src/node/Node.ts
CHANGED
|
@@ -82,6 +82,10 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
|
|
|
82
82
|
* Bring the node online.
|
|
83
83
|
*/
|
|
84
84
|
async start() {
|
|
85
|
+
await this.lifecycle.mutex.produce(this.startWithMutex.bind(this));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected async startWithMutex() {
|
|
85
89
|
this.env.runtime.add(this);
|
|
86
90
|
|
|
87
91
|
try {
|
|
@@ -123,6 +127,10 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
|
|
|
123
127
|
* Once the node is offline you may use {@link start} to bring the node online again.
|
|
124
128
|
*/
|
|
125
129
|
async cancel() {
|
|
130
|
+
await this.lifecycle.mutex.produce(this.cancelWithMutex.bind(this));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected async cancelWithMutex() {
|
|
126
134
|
if (!this.#runtime) {
|
|
127
135
|
return;
|
|
128
136
|
}
|
|
@@ -133,17 +141,27 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
|
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
override async close() {
|
|
144
|
+
await this.lifecycle.mutex.produce(this.closeWithMutex.bind(this));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
protected async closeWithMutex() {
|
|
136
148
|
// The runtime is not designed to operate with a node that is shutting down so destroy it before performing
|
|
137
149
|
// actual close
|
|
138
|
-
//
|
|
139
|
-
// TODO - this should probably block other functions like start()
|
|
140
150
|
if (this.#runtime) {
|
|
141
|
-
await this.
|
|
151
|
+
await this.cancelWithMutex();
|
|
142
152
|
}
|
|
143
153
|
|
|
144
154
|
await super.close();
|
|
145
155
|
}
|
|
146
156
|
|
|
157
|
+
override async reset() {
|
|
158
|
+
await this.lifecycle.mutex.produce(this.resetWithMutex.bind(this));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
protected async resetWithMutex() {
|
|
162
|
+
return super.reset();
|
|
163
|
+
}
|
|
164
|
+
|
|
147
165
|
/**
|
|
148
166
|
* Create the network runtime.
|
|
149
167
|
*/
|
|
@@ -177,7 +195,7 @@ export abstract class Node<T extends Node.CommonRootEndpoint = Node.CommonRootEn
|
|
|
177
195
|
}
|
|
178
196
|
|
|
179
197
|
override async [Construction.destruct]() {
|
|
180
|
-
await this.
|
|
198
|
+
await this.cancelWithMutex();
|
|
181
199
|
await super[Construction.destruct]();
|
|
182
200
|
DiagnosticSource.delete(this);
|
|
183
201
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { ActionContext } from "#behavior/context/ActionContext.js";
|
|
8
8
|
import { Endpoint } from "#endpoint/Endpoint.js";
|
|
9
9
|
import { EndpointLifecycle } from "#endpoint/properties/EndpointLifecycle.js";
|
|
10
|
-
import { AsyncObservable, Observable } from "#general";
|
|
10
|
+
import { AsyncObservable, Mutex, Observable } from "#general";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Extended lifecycle information that only applies to root endpoints.
|
|
@@ -21,10 +21,13 @@ export class NodeLifecycle extends EndpointLifecycle {
|
|
|
21
21
|
#initialized = Observable<[isCommissioned: boolean]>();
|
|
22
22
|
#isOnline = false;
|
|
23
23
|
#isCommissioned = false;
|
|
24
|
+
#mutex: Mutex;
|
|
24
25
|
|
|
25
26
|
constructor(endpoint: Endpoint) {
|
|
26
27
|
super(endpoint);
|
|
27
28
|
|
|
29
|
+
this.#mutex = new Mutex(endpoint);
|
|
30
|
+
|
|
28
31
|
this.#online.on(() => {
|
|
29
32
|
this.#isOnline = true;
|
|
30
33
|
});
|
|
@@ -97,4 +100,17 @@ export class NodeLifecycle extends EndpointLifecycle {
|
|
|
97
100
|
get decommissioned() {
|
|
98
101
|
return this.#decommissioned;
|
|
99
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Mutex for protecting node lifecycle transitions.
|
|
106
|
+
*
|
|
107
|
+
* Methods that implement complex async lifecycle transitions use this mutex to ensure conflicting operations cannot
|
|
108
|
+
* intermingle.
|
|
109
|
+
*
|
|
110
|
+
* Generally methods that hold this mutex have a protected "*WithMutex" variant. This allows for nesting of logic
|
|
111
|
+
* that requires the mutex without causing deadlock.
|
|
112
|
+
*/
|
|
113
|
+
get mutex() {
|
|
114
|
+
return this.#mutex;
|
|
115
|
+
}
|
|
100
116
|
}
|