@uns-kit/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/app-config.d.ts +177 -0
  4. package/dist/app-config.js +1 -0
  5. package/dist/base-path.d.ts +1 -0
  6. package/dist/base-path.js +5 -0
  7. package/dist/config/project.config.extension.d.ts +3 -0
  8. package/dist/config/project.config.extension.js +3 -0
  9. package/dist/config-file.d.ts +12 -0
  10. package/dist/config-file.js +44 -0
  11. package/dist/examples/data-example.d.ts +1 -0
  12. package/dist/examples/data-example.js +44 -0
  13. package/dist/examples/load-test-data.d.ts +1 -0
  14. package/dist/examples/load-test-data.js +72 -0
  15. package/dist/examples/table-example.d.ts +4 -0
  16. package/dist/examples/table-example.js +52 -0
  17. package/dist/examples/uns-gateway.d.ts +1 -0
  18. package/dist/examples/uns-gateway.js +5 -0
  19. package/dist/graphql/schema.d.ts +377 -0
  20. package/dist/graphql/schema.js +13 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.js +4 -0
  23. package/dist/logger.d.ts +2 -0
  24. package/dist/logger.js +18 -0
  25. package/dist/tools/auth/auth-client.d.ts +23 -0
  26. package/dist/tools/auth/auth-client.js +172 -0
  27. package/dist/tools/auth/index.d.ts +1 -0
  28. package/dist/tools/auth/index.js +1 -0
  29. package/dist/tools/auth/secure-store.d.ts +17 -0
  30. package/dist/tools/auth/secure-store.js +110 -0
  31. package/dist/tools/base-path.d.ts +1 -0
  32. package/dist/tools/base-path.js +5 -0
  33. package/dist/tools/generate-config-schema.d.ts +1 -0
  34. package/dist/tools/generate-config-schema.js +23 -0
  35. package/dist/tools/initialize.d.ts +1 -0
  36. package/dist/tools/initialize.js +103 -0
  37. package/dist/tools/make.d.ts +1 -0
  38. package/dist/tools/make.js +27 -0
  39. package/dist/tools/pull-request.d.ts +1 -0
  40. package/dist/tools/pull-request.js +157 -0
  41. package/dist/tools/refresh-uns.d.ts +1 -0
  42. package/dist/tools/refresh-uns.js +109 -0
  43. package/dist/tools/schema.d.ts +208 -0
  44. package/dist/tools/schema.js +1 -0
  45. package/dist/tools/update-rtt.d.ts +1 -0
  46. package/dist/tools/update-rtt.js +169 -0
  47. package/dist/tools/update-tools.d.ts +1 -0
  48. package/dist/tools/update-tools.js +72 -0
  49. package/dist/uns/handover-manager-event-emitter.d.ts +6 -0
  50. package/dist/uns/handover-manager-event-emitter.js +19 -0
  51. package/dist/uns/handover-manager.d.ts +34 -0
  52. package/dist/uns/handover-manager.js +227 -0
  53. package/dist/uns/process-config.d.ts +10 -0
  54. package/dist/uns/process-config.js +12 -0
  55. package/dist/uns/process-name-service.d.ts +7 -0
  56. package/dist/uns/process-name-service.js +28 -0
  57. package/dist/uns/status-monitor.d.ts +35 -0
  58. package/dist/uns/status-monitor.js +82 -0
  59. package/dist/uns/uns-event-emitter.d.ts +6 -0
  60. package/dist/uns/uns-event-emitter.js +19 -0
  61. package/dist/uns/uns-interfaces.d.ts +156 -0
  62. package/dist/uns/uns-interfaces.js +5 -0
  63. package/dist/uns/uns-measurements.d.ts +95 -0
  64. package/dist/uns/uns-measurements.js +146 -0
  65. package/dist/uns/uns-packet.d.ts +28 -0
  66. package/dist/uns/uns-packet.js +223 -0
  67. package/dist/uns/uns-proxy-process.d.ts +56 -0
  68. package/dist/uns/uns-proxy-process.js +179 -0
  69. package/dist/uns/uns-proxy.d.ts +31 -0
  70. package/dist/uns/uns-proxy.js +120 -0
  71. package/dist/uns/uns-tags.d.ts +1 -0
  72. package/dist/uns/uns-tags.js +1 -0
  73. package/dist/uns/uns-topic-matcher.d.ts +9 -0
  74. package/dist/uns/uns-topic-matcher.js +34 -0
  75. package/dist/uns/uns-topics.d.ts +1 -0
  76. package/dist/uns/uns-topics.js +1 -0
  77. package/dist/uns-config/config-schema.d.ts +7 -0
  78. package/dist/uns-config/config-schema.js +5 -0
  79. package/dist/uns-config/host-placeholders.d.ts +139 -0
  80. package/dist/uns-config/host-placeholders.js +70 -0
  81. package/dist/uns-config/schema-tolls.d.ts +2 -0
  82. package/dist/uns-config/schema-tolls.js +4 -0
  83. package/dist/uns-config/schema-tools.d.ts +2 -0
  84. package/dist/uns-config/schema-tools.js +18 -0
  85. package/dist/uns-config/secret-placeholders.d.ts +128 -0
  86. package/dist/uns-config/secret-placeholders.js +64 -0
  87. package/dist/uns-config/secret-resolver.d.ts +77 -0
  88. package/dist/uns-config/secret-resolver.js +285 -0
  89. package/dist/uns-config/uns-core-schema.d.ts +705 -0
  90. package/dist/uns-config/uns-core-schema.js +25 -0
  91. package/dist/uns-grpc/uns-gateway-cli.d.ts +2 -0
  92. package/dist/uns-grpc/uns-gateway-cli.js +32 -0
  93. package/dist/uns-grpc/uns-gateway-server.d.ts +47 -0
  94. package/dist/uns-grpc/uns-gateway-server.js +424 -0
  95. package/dist/uns-mqtt/mqtt-interfaces.d.ts +22 -0
  96. package/dist/uns-mqtt/mqtt-interfaces.js +1 -0
  97. package/dist/uns-mqtt/mqtt-proxy.d.ts +34 -0
  98. package/dist/uns-mqtt/mqtt-proxy.js +245 -0
  99. package/dist/uns-mqtt/mqtt-topic-builder.d.ts +51 -0
  100. package/dist/uns-mqtt/mqtt-topic-builder.js +70 -0
  101. package/dist/uns-mqtt/mqtt-worker-init.d.ts +1 -0
  102. package/dist/uns-mqtt/mqtt-worker-init.js +5 -0
  103. package/dist/uns-mqtt/mqtt-worker.d.ts +20 -0
  104. package/dist/uns-mqtt/mqtt-worker.js +120 -0
  105. package/dist/uns-mqtt/throttled-queue.d.ts +166 -0
  106. package/dist/uns-mqtt/throttled-queue.js +388 -0
  107. package/dist/uns-mqtt/uns-mqtt-proxy.d.ts +107 -0
  108. package/dist/uns-mqtt/uns-mqtt-proxy.js +349 -0
  109. package/dist/uns-mqtt/ws-proxy.d.ts +38 -0
  110. package/dist/uns-mqtt/ws-proxy.js +86 -0
  111. package/package.json +48 -0
@@ -0,0 +1,179 @@
1
+ import logger from "../logger.js";
2
+ import MqttProxy from "../uns-mqtt/mqtt-proxy.js";
3
+ import UnsMqttProxy from "../uns-mqtt/uns-mqtt-proxy.js";
4
+ import { HandoverManager } from "./handover-manager.js";
5
+ // Import configuration and initialization modules.
6
+ import { PACKAGE_INFO, MQTT_UPDATE_INTERVAL } from "./process-config.js";
7
+ import { getProcessName } from "./process-name-service.js";
8
+ import { MqttTopicBuilder } from "../uns-mqtt/mqtt-topic-builder.js";
9
+ import { StatusMonitor } from "./status-monitor.js";
10
+ import { UnsPacket } from "./uns-packet.js";
11
+ class UnsProxyProcess {
12
+ active = false;
13
+ processStatusTopic;
14
+ processName;
15
+ unsMqttProxies;
16
+ unsApiProxies;
17
+ unsTemporalProxies;
18
+ processMqttProxy;
19
+ handoverManager;
20
+ statusMonitor;
21
+ // Plugin
22
+ static pluginApiVersion = 1;
23
+ static registered = new Set();
24
+ static registeredMethodNames = new Set();
25
+ static applied = false;
26
+ static use(plugin) {
27
+ if (this.registered.has(plugin))
28
+ return this;
29
+ this.registered.add(plugin);
30
+ if (this.applied)
31
+ this.applyPlugin(plugin);
32
+ return this;
33
+ }
34
+ static applyPlugin(plugin) {
35
+ const define = (methods) => {
36
+ for (const [name, fn] of Object.entries(methods)) {
37
+ if (typeof fn !== "function") {
38
+ throw new TypeError(`UnsProxyProcess plugin attempted to register non-function member \"${name}\".`);
39
+ }
40
+ if (this.registeredMethodNames.has(name) || Reflect.has(this.prototype, name)) {
41
+ throw new Error(`UnsProxyProcess plugin attempted to overwrite existing member \"${name}\".`);
42
+ }
43
+ Object.defineProperty(this.prototype, name, {
44
+ value: fn,
45
+ writable: true,
46
+ configurable: true,
47
+ enumerable: false,
48
+ });
49
+ this.registeredMethodNames.add(name);
50
+ }
51
+ };
52
+ plugin({
53
+ define,
54
+ version: this.pluginApiVersion,
55
+ UnsProxyProcess: this,
56
+ });
57
+ }
58
+ static applyAll() {
59
+ if (this.applied)
60
+ return;
61
+ this.applied = true;
62
+ for (const p of this.registered)
63
+ this.applyPlugin(p);
64
+ }
65
+ // References for cleanup.
66
+ mqttInputHandler;
67
+ constructor(mqttHost, unsProxyProcessParameters) {
68
+ this.constructor.applyAll(); // Activate all the plugins
69
+ this.unsMqttProxies = [];
70
+ this.unsApiProxies = [];
71
+ this.unsTemporalProxies = [];
72
+ this.processName = unsProxyProcessParameters.processName || getProcessName();
73
+ const { name: packageName, version } = PACKAGE_INFO;
74
+ // Instantiate the topic builder.
75
+ const topicBuilder = new MqttTopicBuilder(`uns-infra/${packageName}/${version}/${this.processName}/`);
76
+ // Generate topics.
77
+ const processStatusTopic = topicBuilder.getProcessStatusTopic();
78
+ const handoverTopic = topicBuilder.getHandoverTopic();
79
+ const wildcardActiveTopic = topicBuilder.getWildcardActiveTopic();
80
+ this.processStatusTopic = processStatusTopic;
81
+ // Configure MQTT topics for subscription.
82
+ const mqttSubToTopics = unsProxyProcessParameters?.mqttSubToTopics ?? [
83
+ wildcardActiveTopic,
84
+ handoverTopic,
85
+ ];
86
+ const mqttParameters = {
87
+ mqttSubToTopics,
88
+ username: unsProxyProcessParameters?.username ?? "",
89
+ password: unsProxyProcessParameters?.password ?? "",
90
+ mqttSSL: unsProxyProcessParameters?.mqttSSL ?? false,
91
+ statusTopic: this.processStatusTopic,
92
+ };
93
+ // Initialize MQTT proxy and start connection.
94
+ this.processMqttProxy = new MqttProxy(mqttHost, this.processName, mqttParameters);
95
+ this.processMqttProxy.start();
96
+ // Instantiate and start the StatusMonitor for memory and status updates.
97
+ this.statusMonitor = new StatusMonitor(this.processMqttProxy, this.processStatusTopic, () => this.active, MQTT_UPDATE_INTERVAL, MQTT_UPDATE_INTERVAL);
98
+ this.statusMonitor.start();
99
+ }
100
+ /**
101
+ * Initializes the HandoverManager instance and sets up event listeners for handover and MQTT input events.
102
+ * Determines handover and force start modes based on process arguments.
103
+ */
104
+ initHandoverManager(instanceMode, handover) {
105
+ const handoverRequestEnabled = instanceMode == "handover" ? true : false;
106
+ const forceStartEnabled = instanceMode == "force" ? true : false;
107
+ this.handoverManager = new HandoverManager(this.processName, this.processMqttProxy, this.unsMqttProxies, handoverRequestEnabled, handover ?? true, forceStartEnabled);
108
+ // Listen for handover events.
109
+ this.handoverManager.event.on("handoverManager", (event) => {
110
+ this.active = event.active;
111
+ });
112
+ // Delegate MQTT events to the HandoverManager.
113
+ this.mqttInputHandler = async (event) => {
114
+ await this.handoverManager.handleMqttMessage(event);
115
+ };
116
+ this.processMqttProxy.event.on("input", this.mqttInputHandler);
117
+ }
118
+ /**
119
+ * Creates a new UNS proxy instance and stores it for future management.
120
+ */
121
+ async createUnsMqttProxy(mqttHost, instanceName, instanceMode, handover, unsParameters) {
122
+ // Wait until the MQTT connection is established before proceeding.
123
+ await this.waitForProcessConnection();
124
+ // Currently handover manager can handle only mqtt events
125
+ if (!this.handoverManager) {
126
+ this.initHandoverManager(instanceMode, handover);
127
+ }
128
+ const unsMqttProxy = new UnsMqttProxy(mqttHost, this.processName, instanceName, unsParameters);
129
+ // Listen for UNS proxy producing topics and publish them via MQTT.
130
+ unsMqttProxy.event.on("unsProxyProducedTopics", (event) => {
131
+ this.processMqttProxy.publish(event.statusTopic, JSON.stringify(event.producedTopics), {
132
+ retain: true,
133
+ properties: { messageExpiryInterval: 120000 },
134
+ });
135
+ });
136
+ // Listen for UNS proxy status events and publish them via MQTT.
137
+ unsMqttProxy.event.on("mqttProxyStatus", (event) => {
138
+ const time = UnsPacket.formatToISO8601(new Date());
139
+ const unsMessage = { data: { time, value: event.value, uom: event.uom } };
140
+ UnsPacket.unsPacketFromUnsMessage(unsMessage).then((packet) => {
141
+ this.processMqttProxy.publish(event.statusTopic, JSON.stringify(packet));
142
+ });
143
+ });
144
+ this.unsMqttProxies.push(unsMqttProxy);
145
+ return unsMqttProxy;
146
+ }
147
+ async waitForProcessConnection() {
148
+ while (!this.processMqttProxy?.isConnected) {
149
+ await new Promise((resolve) => setTimeout(resolve, 100));
150
+ }
151
+ }
152
+ /**
153
+ * Shuts down the process by clearing intervals, timeouts, and removing
154
+ * MQTT event listeners, and stopping the StatusMonitor.
155
+ */
156
+ shutdown() {
157
+ logger.info(`${this.processName} - Shutting down UnsProxyProcess...`);
158
+ try {
159
+ this.statusMonitor.stop();
160
+ }
161
+ catch (e) {
162
+ logger.error(`${this.processName} - Error stopping StatusMonitor: ${e.message}`);
163
+ }
164
+ if (this.mqttInputHandler) {
165
+ this.processMqttProxy.event.off("input", this.mqttInputHandler);
166
+ }
167
+ try {
168
+ if (typeof this.processMqttProxy.stop === "function") {
169
+ this.processMqttProxy.stop();
170
+ }
171
+ }
172
+ catch (e) {
173
+ logger.error(`${this.processName} - Error stopping MQTT proxy: ${e.message}`);
174
+ }
175
+ logger.info(`${this.processName} - Shutdown complete.`);
176
+ }
177
+ }
178
+ export default UnsProxyProcess;
179
+ export { UnsProxyProcess };
@@ -0,0 +1,31 @@
1
+ import { IApiObject, ITopicObject, UnsEvents } from "./uns-interfaces";
2
+ import { UnsEventEmitter } from "./uns-event-emitter.js";
3
+ export default class UnsProxy {
4
+ private publishInterval;
5
+ event: UnsEventEmitter<UnsEvents>;
6
+ protected instanceStatusTopic: string;
7
+ protected instanceNameWithSuffix: string;
8
+ private producedTopics;
9
+ private producedApiEndpoints;
10
+ constructor();
11
+ /**
12
+ * Publishes the list of produced topics to the MQTT broker.
13
+ */
14
+ private emitProducedTopics;
15
+ /**
16
+ * Publishes the list of produced API endpoints to the MQTT broker.
17
+ */
18
+ private emitProducedApiEndpoints;
19
+ /**
20
+ * Registers a unique topic so that it is tracked and published only once.
21
+ *
22
+ * @param topicObject - The object containing topic details.
23
+ */
24
+ protected registerUniqueTopic(topicObject: ITopicObject): void;
25
+ /**
26
+ * Registers an API endpoint to handle requests for a specific topic and attribute.
27
+ */
28
+ protected registerApiEndpoint(apiObject: IApiObject): void;
29
+ protected unregisterApiEndpoint(topic: string, attribute: string): void;
30
+ stop(): Promise<void>;
31
+ }
@@ -0,0 +1,120 @@
1
+ import logger from "../logger.js";
2
+ import { UnsEventEmitter } from "./uns-event-emitter.js";
3
+ import { UnsPacket } from "./uns-packet.js";
4
+ export default class UnsProxy {
5
+ publishInterval = null;
6
+ event = new UnsEventEmitter();
7
+ instanceStatusTopic;
8
+ instanceNameWithSuffix; //was prot
9
+ producedTopics = new Map();
10
+ producedApiEndpoints = new Map();
11
+ constructor() {
12
+ // Set up interval to publish produced topics every 60 seconds.
13
+ this.publishInterval = setInterval(() => {
14
+ this.emitProducedTopics();
15
+ this.emitProducedApiEndpoints();
16
+ }, 60000);
17
+ }
18
+ /**
19
+ * Publishes the list of produced topics to the MQTT broker.
20
+ */
21
+ async emitProducedTopics() {
22
+ if (this.instanceStatusTopic !== "") {
23
+ const topicsArray = [...this.producedTopics.values()];
24
+ if (topicsArray.length > 0) {
25
+ try {
26
+ if (topicsArray.length > 0) {
27
+ this.event.emit("unsProxyProducedTopics", { producedTopics: topicsArray, statusTopic: this.instanceStatusTopic + "topics" });
28
+ }
29
+ }
30
+ catch (error) {
31
+ logger.error(`${this.instanceNameWithSuffix} - Error publishing produced topics: ${error.message}`);
32
+ }
33
+ logger.debug(`${this.instanceNameWithSuffix} - Published produced topics.`);
34
+ }
35
+ }
36
+ }
37
+ /**
38
+ * Publishes the list of produced API endpoints to the MQTT broker.
39
+ */
40
+ async emitProducedApiEndpoints() {
41
+ if (this.instanceStatusTopic !== "") {
42
+ const apiEndpointsArray = [...this.producedApiEndpoints.values()];
43
+ if (apiEndpointsArray.length > 0) {
44
+ try {
45
+ if (apiEndpointsArray.length > 0) {
46
+ this.event.emit("unsProxyProducedApiEndpoints", { producedApiEndpoints: apiEndpointsArray, statusTopic: this.instanceStatusTopic + "api-endpoints" });
47
+ }
48
+ }
49
+ catch (error) {
50
+ logger.error(`${this.instanceNameWithSuffix} - Error publishing produced API endpoints: ${error.message}`);
51
+ }
52
+ logger.debug(`${this.instanceNameWithSuffix} - Published produced API endpoints.`);
53
+ }
54
+ }
55
+ }
56
+ /**
57
+ * Registers a unique topic so that it is tracked and published only once.
58
+ *
59
+ * @param topicObject - The object containing topic details.
60
+ */
61
+ registerUniqueTopic(topicObject) {
62
+ if (this.instanceStatusTopic !== "") {
63
+ const fullTopic = `${topicObject.topic}${topicObject.attribute}`;
64
+ if (!this.producedTopics.has(fullTopic)) {
65
+ this.producedTopics.set(fullTopic, {
66
+ timestamp: topicObject.timestamp,
67
+ topic: topicObject.topic,
68
+ attribute: topicObject.attribute,
69
+ attributeType: topicObject.attributeType,
70
+ description: topicObject.description,
71
+ tags: topicObject.tags,
72
+ attributeNeedsPersistence: topicObject.attributeNeedsPersistence ?? true,
73
+ dataGroup: topicObject.dataGroup ?? ""
74
+ });
75
+ this.emitProducedTopics();
76
+ logger.info(`${this.instanceNameWithSuffix} - Registered new topic: ${fullTopic}`);
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Registers an API endpoint to handle requests for a specific topic and attribute.
82
+ */
83
+ registerApiEndpoint(apiObject) {
84
+ if (this.instanceStatusTopic !== "") {
85
+ const fullTopic = `${apiObject.topic}${apiObject.attribute}`;
86
+ if (!this.producedApiEndpoints.has(fullTopic)) {
87
+ const time = UnsPacket.formatToISO8601(new Date());
88
+ this.producedApiEndpoints.set(fullTopic, {
89
+ timestamp: time,
90
+ topic: apiObject.topic,
91
+ attribute: apiObject.attribute,
92
+ apiHost: apiObject.apiHost,
93
+ apiEndpoint: apiObject.apiEndpoint,
94
+ apiMethod: apiObject.apiMethod,
95
+ apiQueryParams: apiObject.apiQueryParams,
96
+ apiDescription: apiObject.apiDescription,
97
+ attributeType: apiObject.attributeType,
98
+ apiSwaggerEndpoint: apiObject.apiSwaggerEndpoint
99
+ });
100
+ this.emitProducedApiEndpoints();
101
+ logger.info(`${this.instanceNameWithSuffix} - Registered new api endpoint: /${fullTopic}`);
102
+ }
103
+ }
104
+ }
105
+ unregisterApiEndpoint(topic, attribute) {
106
+ const fullTopic = `${topic}${attribute}`;
107
+ if (this.producedApiEndpoints.has(fullTopic)) {
108
+ this.producedApiEndpoints.delete(fullTopic);
109
+ this.emitProducedApiEndpoints();
110
+ logger.info(`${this.instanceNameWithSuffix} - Unregistered API endpoint: ${fullTopic}`);
111
+ }
112
+ }
113
+ async stop() {
114
+ // Clear the publishing interval.
115
+ if (this.publishInterval) {
116
+ clearInterval(this.publishInterval);
117
+ this.publishInterval = null;
118
+ }
119
+ }
120
+ }
@@ -0,0 +1 @@
1
+ export type UnsTags = "" | (string & {});
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export declare class UnsTopicMatcher {
2
+ /**
3
+ * Matches a topic with a topic filter, using MQTT rules.
4
+ * @param topicFilter The MQTT topic filter (may contain `#` or `+` wildcards).
5
+ * @param topic The topic to test against the filter.
6
+ * @returns `true` if the topic matches the filter, `false` otherwise.
7
+ */
8
+ static matches(topicFilter: string, topic: string): boolean;
9
+ }
@@ -0,0 +1,34 @@
1
+ export class UnsTopicMatcher {
2
+ /**
3
+ * Matches a topic with a topic filter, using MQTT rules.
4
+ * @param topicFilter The MQTT topic filter (may contain `#` or `+` wildcards).
5
+ * @param topic The topic to test against the filter.
6
+ * @returns `true` if the topic matches the filter, `false` otherwise.
7
+ */
8
+ static matches(topicFilter, topic) {
9
+ // Normalize leading/trailing slashes and collapse empties
10
+ const filterSegments = topicFilter.split('/').filter(Boolean);
11
+ const topicSegments = topic.split('/').filter(Boolean);
12
+ for (let i = 0; i < filterSegments.length; i++) {
13
+ const filterSegment = filterSegments[i];
14
+ // Match `#` wildcard
15
+ if (filterSegment === '#') {
16
+ return true; // `#` matches everything that follows
17
+ }
18
+ // Match `+` wildcard
19
+ if (filterSegment === '+') {
20
+ if (topicSegments[i] === undefined) {
21
+ return false; // `+` should match exactly one level
22
+ }
23
+ }
24
+ else {
25
+ // Exact match required
26
+ if (filterSegment !== topicSegments[i]) {
27
+ return false;
28
+ }
29
+ }
30
+ }
31
+ // Ensure there are no unmatched segments in the topic
32
+ return filterSegments.length === topicSegments.length;
33
+ }
34
+ }
@@ -0,0 +1 @@
1
+ export type UnsTopics = "" | (string & {});
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+ export declare const baseSchema: z.ZodObject<any, "strict", any, {
3
+ [x: string]: any;
4
+ }, {
5
+ [x: string]: any;
6
+ }>;
7
+ export type AppConfigFromZod = z.infer<typeof baseSchema>;
@@ -0,0 +1,5 @@
1
+ import { composeConfigSchema } from "./schema-tools.js";
2
+ import { unsCoreSchema } from "./uns-core-schema.js";
3
+ import { projectExtrasSchema } from "../config/project.config.extension.js";
4
+ // Plain strict object for generators (no Effects, no dynamic imports)
5
+ export const baseSchema = composeConfigSchema(unsCoreSchema, projectExtrasSchema).strict();
@@ -0,0 +1,139 @@
1
+ import { z } from "zod";
2
+ declare const inlineHostSchema: z.ZodObject<{
3
+ provider: z.ZodLiteral<"inline">;
4
+ value: z.ZodString;
5
+ }, "strict", z.ZodTypeAny, {
6
+ provider?: "inline";
7
+ value?: string;
8
+ }, {
9
+ provider?: "inline";
10
+ value?: string;
11
+ }>;
12
+ declare const externalHostSchema: z.ZodObject<{
13
+ provider: z.ZodLiteral<"external">;
14
+ key: z.ZodString;
15
+ optional: z.ZodOptional<z.ZodBoolean>;
16
+ default: z.ZodOptional<z.ZodString>;
17
+ }, "strict", z.ZodTypeAny, {
18
+ provider?: "external";
19
+ key?: string;
20
+ optional?: boolean;
21
+ default?: string;
22
+ }, {
23
+ provider?: "external";
24
+ key?: string;
25
+ optional?: boolean;
26
+ default?: string;
27
+ }>;
28
+ declare const systemHostSchema: z.ZodObject<{
29
+ provider: z.ZodLiteral<"system">;
30
+ family: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"IPv4">, z.ZodLiteral<"IPv6">]>>;
31
+ interfaceName: z.ZodOptional<z.ZodString>;
32
+ optional: z.ZodOptional<z.ZodBoolean>;
33
+ default: z.ZodOptional<z.ZodString>;
34
+ }, "strict", z.ZodTypeAny, {
35
+ provider?: "system";
36
+ optional?: boolean;
37
+ default?: string;
38
+ family?: "IPv4" | "IPv6";
39
+ interfaceName?: string;
40
+ }, {
41
+ provider?: "system";
42
+ optional?: boolean;
43
+ default?: string;
44
+ family?: "IPv4" | "IPv6";
45
+ interfaceName?: string;
46
+ }>;
47
+ export declare const hostPlaceholderSchema: z.ZodDiscriminatedUnion<"provider", [z.ZodObject<{
48
+ provider: z.ZodLiteral<"inline">;
49
+ value: z.ZodString;
50
+ }, "strict", z.ZodTypeAny, {
51
+ provider?: "inline";
52
+ value?: string;
53
+ }, {
54
+ provider?: "inline";
55
+ value?: string;
56
+ }>, z.ZodObject<{
57
+ provider: z.ZodLiteral<"external">;
58
+ key: z.ZodString;
59
+ optional: z.ZodOptional<z.ZodBoolean>;
60
+ default: z.ZodOptional<z.ZodString>;
61
+ }, "strict", z.ZodTypeAny, {
62
+ provider?: "external";
63
+ key?: string;
64
+ optional?: boolean;
65
+ default?: string;
66
+ }, {
67
+ provider?: "external";
68
+ key?: string;
69
+ optional?: boolean;
70
+ default?: string;
71
+ }>, z.ZodObject<{
72
+ provider: z.ZodLiteral<"system">;
73
+ family: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"IPv4">, z.ZodLiteral<"IPv6">]>>;
74
+ interfaceName: z.ZodOptional<z.ZodString>;
75
+ optional: z.ZodOptional<z.ZodBoolean>;
76
+ default: z.ZodOptional<z.ZodString>;
77
+ }, "strict", z.ZodTypeAny, {
78
+ provider?: "system";
79
+ optional?: boolean;
80
+ default?: string;
81
+ family?: "IPv4" | "IPv6";
82
+ interfaceName?: string;
83
+ }, {
84
+ provider?: "system";
85
+ optional?: boolean;
86
+ default?: string;
87
+ family?: "IPv4" | "IPv6";
88
+ interfaceName?: string;
89
+ }>]>;
90
+ export declare const hostValueSchema: z.ZodUnion<[z.ZodString, z.ZodDiscriminatedUnion<"provider", [z.ZodObject<{
91
+ provider: z.ZodLiteral<"inline">;
92
+ value: z.ZodString;
93
+ }, "strict", z.ZodTypeAny, {
94
+ provider?: "inline";
95
+ value?: string;
96
+ }, {
97
+ provider?: "inline";
98
+ value?: string;
99
+ }>, z.ZodObject<{
100
+ provider: z.ZodLiteral<"external">;
101
+ key: z.ZodString;
102
+ optional: z.ZodOptional<z.ZodBoolean>;
103
+ default: z.ZodOptional<z.ZodString>;
104
+ }, "strict", z.ZodTypeAny, {
105
+ provider?: "external";
106
+ key?: string;
107
+ optional?: boolean;
108
+ default?: string;
109
+ }, {
110
+ provider?: "external";
111
+ key?: string;
112
+ optional?: boolean;
113
+ default?: string;
114
+ }>, z.ZodObject<{
115
+ provider: z.ZodLiteral<"system">;
116
+ family: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<"IPv4">, z.ZodLiteral<"IPv6">]>>;
117
+ interfaceName: z.ZodOptional<z.ZodString>;
118
+ optional: z.ZodOptional<z.ZodBoolean>;
119
+ default: z.ZodOptional<z.ZodString>;
120
+ }, "strict", z.ZodTypeAny, {
121
+ provider?: "system";
122
+ optional?: boolean;
123
+ default?: string;
124
+ family?: "IPv4" | "IPv6";
125
+ interfaceName?: string;
126
+ }, {
127
+ provider?: "system";
128
+ optional?: boolean;
129
+ default?: string;
130
+ family?: "IPv4" | "IPv6";
131
+ interfaceName?: string;
132
+ }>]>]>;
133
+ export type HostPlaceholder = z.infer<typeof hostPlaceholderSchema>;
134
+ export type HostValue = z.infer<typeof hostValueSchema>;
135
+ export type InlineHostPlaceholder = z.infer<typeof inlineHostSchema>;
136
+ export type ExternalHostPlaceholder = z.infer<typeof externalHostSchema>;
137
+ export type SystemHostPlaceholder = z.infer<typeof systemHostSchema>;
138
+ export declare function isHostPlaceholder(value: unknown): value is HostPlaceholder;
139
+ export {};
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ // Host placeholders allow configuration values such as MQTT brokers or database
3
+ // hosts to be defined dynamically. They support in-line values as well as
4
+ // externally resolved entries so projects can avoid storing environment-specific
5
+ // hosts directly inside shared config files.
6
+ const inlineHostSchema = z
7
+ .object({
8
+ provider: z.literal("inline").describe("Use the supplied host or IP address."),
9
+ value: z
10
+ .string()
11
+ .min(1, "Host/IP value is required")
12
+ .describe("Host or IP address that should be used directly."),
13
+ })
14
+ .strict()
15
+ .describe("Host placeholder resolved from an in-line value.");
16
+ const externalHostSchema = z
17
+ .object({
18
+ provider: z.literal("external").describe("Resolve the host from an external mapping."),
19
+ key: z
20
+ .string()
21
+ .min(1, "External host key is required")
22
+ .describe("Identifier used when resolving the host from HostResolverOptions."),
23
+ optional: z
24
+ .boolean()
25
+ .optional()
26
+ .describe("Allow the external host to be missing without throwing during resolution."),
27
+ default: z
28
+ .string()
29
+ .optional()
30
+ .describe("Fallback host when optional is true and the external entry is missing."),
31
+ })
32
+ .strict()
33
+ .describe("Host placeholder resolved via HostResolverOptions.externalHosts or resolveExternal function.");
34
+ const systemHostSchema = z
35
+ .object({
36
+ provider: z.literal("system").describe("Resolve the host from local network interfaces."),
37
+ family: z
38
+ .union([z.literal("IPv4"), z.literal("IPv6")])
39
+ .default("IPv4")
40
+ .describe("Address family to return when scanning interfaces."),
41
+ interfaceName: z
42
+ .string()
43
+ .optional()
44
+ .describe("Specific interface to read (falls back to the first match when omitted)."),
45
+ optional: z
46
+ .boolean()
47
+ .optional()
48
+ .describe("Allow the interface lookup to fail without throwing during resolution."),
49
+ default: z
50
+ .string()
51
+ .optional()
52
+ .describe("Fallback host/IP when optional is true and no interface matches."),
53
+ })
54
+ .strict()
55
+ .describe("Host placeholder resolved from os.networkInterfaces().");
56
+ export const hostPlaceholderSchema = z.discriminatedUnion("provider", [
57
+ inlineHostSchema,
58
+ externalHostSchema,
59
+ systemHostSchema,
60
+ ]);
61
+ export const hostValueSchema = z.union([
62
+ z
63
+ .string()
64
+ .min(1, "Host/IP value is required")
65
+ .describe("Host or IP address used directly without placeholder."),
66
+ hostPlaceholderSchema,
67
+ ]);
68
+ export function isHostPlaceholder(value) {
69
+ return hostPlaceholderSchema.safeParse(value).success;
70
+ }
@@ -0,0 +1,2 @@
1
+ import { z } from "zod";
2
+ export declare function composeConfigSchema<A extends z.AnyZodObject, B extends z.AnyZodObject>(a: A, b: B): z.ZodObject<A["shape"] & B["shape"]>;
@@ -0,0 +1,4 @@
1
+ // Shallow merge of two ZodObjects into one ZodObject
2
+ export function composeConfigSchema(a, b) {
3
+ return a.merge(b);
4
+ }
@@ -0,0 +1,2 @@
1
+ import { z } from "zod";
2
+ export declare function composeConfigSchema(core: z.AnyZodObject, ...extras: z.AnyZodObject[]): z.AnyZodObject;
@@ -0,0 +1,18 @@
1
+ // Helper functions in camelCase
2
+ function shapeKeys(obj) {
3
+ const shape = obj?._def?.shape();
4
+ return Object.keys(shape ?? {});
5
+ }
6
+ // Function in camelCase is fine
7
+ export function composeConfigSchema(core, ...extras) {
8
+ const coreKeys = new Set(shapeKeys(core));
9
+ for (const ext of extras) {
10
+ const keys = shapeKeys(ext);
11
+ const overlaps = keys.filter(k => coreKeys.has(k));
12
+ if (overlaps.length) {
13
+ throw new Error(`Project extras overlap UNS core keys: ${overlaps.join(", ")}`);
14
+ }
15
+ keys.forEach(k => coreKeys.add(k));
16
+ }
17
+ return extras.reduce((acc, ext) => acc.merge(ext), core);
18
+ }