@uns-kit/core 2.0.40 → 2.0.42

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 (102) hide show
  1. package/README.md +38 -15
  2. package/dist/base-path.js.map +1 -1
  3. package/dist/config/project.config.extension.js.map +1 -1
  4. package/dist/config-file.js.map +1 -1
  5. package/dist/graphql/schema.js.map +1 -1
  6. package/dist/index.d.ts +9 -8
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +8 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/logger.js.map +1 -1
  11. package/dist/tools/auth/auth-client.js.map +1 -1
  12. package/dist/tools/auth/index.js.map +1 -1
  13. package/dist/tools/auth/secure-store.js.map +1 -1
  14. package/dist/tools/base-path.js.map +1 -1
  15. package/dist/tools/datahub/datahub-client.js.map +1 -1
  16. package/dist/tools/file-utils.d.ts +2 -2
  17. package/dist/tools/file-utils.js +24 -24
  18. package/dist/tools/file-utils.js.map +1 -1
  19. package/dist/tools/generate-config-schema.js.map +1 -1
  20. package/dist/tools/generate-uns-dictionary.d.ts +23 -23
  21. package/dist/tools/generate-uns-dictionary.js +147 -147
  22. package/dist/tools/generate-uns-dictionary.js.map +1 -1
  23. package/dist/tools/generate-uns-measurements.d.ts +21 -21
  24. package/dist/tools/generate-uns-measurements.js +119 -119
  25. package/dist/tools/generate-uns-measurements.js.map +1 -1
  26. package/dist/tools/generate-uns-reference.d.ts +2 -2
  27. package/dist/tools/generate-uns-reference.js +76 -76
  28. package/dist/tools/generate-uns-reference.js.map +1 -1
  29. package/dist/tools/pull-request.js.map +1 -1
  30. package/dist/tools/refresh-uns.js.map +1 -1
  31. package/dist/tools/schema.js.map +1 -1
  32. package/dist/tools/sync-uns-schema.d.ts +2 -2
  33. package/dist/tools/sync-uns-schema.js +472 -472
  34. package/dist/tools/sync-uns-schema.js.map +1 -1
  35. package/dist/uns/handover-manager-event-emitter.js.map +1 -1
  36. package/dist/uns/handover-manager.js.map +1 -1
  37. package/dist/uns/process-config.js.map +1 -1
  38. package/dist/uns/service-metadata.d.ts +53 -0
  39. package/dist/uns/service-metadata.d.ts.map +1 -0
  40. package/dist/uns/service-metadata.js +56 -0
  41. package/dist/uns/service-metadata.js.map +1 -0
  42. package/dist/uns/status-monitor.js.map +1 -1
  43. package/dist/uns/uns-asset.d.ts +1 -1
  44. package/dist/uns/uns-asset.js +1 -1
  45. package/dist/uns/uns-asset.js.map +1 -1
  46. package/dist/uns/uns-attributes.d.ts +6 -6
  47. package/dist/uns/uns-attributes.js +9 -9
  48. package/dist/uns/uns-attributes.js.map +1 -1
  49. package/dist/uns/uns-dictionary-registry.d.ts +4 -4
  50. package/dist/uns/uns-dictionary-registry.js +20 -20
  51. package/dist/uns/uns-dictionary-registry.js.map +1 -1
  52. package/dist/uns/uns-dictionary.generated.d.ts +663 -663
  53. package/dist/uns/uns-dictionary.generated.js +745 -745
  54. package/dist/uns/uns-dictionary.generated.js.map +1 -1
  55. package/dist/uns/uns-event-emitter.js.map +1 -1
  56. package/dist/uns/uns-interfaces.d.ts +365 -365
  57. package/dist/uns/uns-interfaces.js.map +1 -1
  58. package/dist/uns/uns-measurements.generated.d.ts +362 -362
  59. package/dist/uns/uns-measurements.generated.js +359 -359
  60. package/dist/uns/uns-measurements.generated.js.map +1 -1
  61. package/dist/uns/uns-measurements.js.map +1 -1
  62. package/dist/uns/uns-object.d.ts +20 -20
  63. package/dist/uns/uns-object.js +9 -9
  64. package/dist/uns/uns-object.js.map +1 -1
  65. package/dist/uns/uns-packet.js +263 -263
  66. package/dist/uns/uns-packet.js.map +1 -1
  67. package/dist/uns/uns-path.d.ts +2 -2
  68. package/dist/uns/uns-path.js +10 -10
  69. package/dist/uns/uns-path.js.map +1 -1
  70. package/dist/uns/uns-proxy-process.d.ts +11 -0
  71. package/dist/uns/uns-proxy-process.d.ts.map +1 -1
  72. package/dist/uns/uns-proxy-process.js +224 -203
  73. package/dist/uns/uns-proxy-process.js.map +1 -1
  74. package/dist/uns/uns-proxy.d.ts +51 -51
  75. package/dist/uns/uns-proxy.js +190 -190
  76. package/dist/uns/uns-proxy.js.map +1 -1
  77. package/dist/uns/uns-tags.js.map +1 -1
  78. package/dist/uns/uns-topic-matcher.js.map +1 -1
  79. package/dist/uns/uns-topics.js.map +1 -1
  80. package/dist/uns-config/config-schema.js.map +1 -1
  81. package/dist/uns-config/host-placeholders.js.map +1 -1
  82. package/dist/uns-config/schema-tolls.js.map +1 -1
  83. package/dist/uns-config/schema-tools.js.map +1 -1
  84. package/dist/uns-config/secret-placeholders.js.map +1 -1
  85. package/dist/uns-config/secret-resolver.js.map +1 -1
  86. package/dist/uns-config/uns-core-schema.d.ts +3276 -3276
  87. package/dist/uns-config/uns-core-schema.js.map +1 -1
  88. package/dist/uns-grpc/uns-gateway-cli.js.map +1 -1
  89. package/dist/uns-grpc/uns-gateway-server.js.map +1 -1
  90. package/dist/uns-mqtt/mqtt-interfaces.js.map +1 -1
  91. package/dist/uns-mqtt/mqtt-proxy.js.map +1 -1
  92. package/dist/uns-mqtt/mqtt-topic-builder.d.ts +6 -0
  93. package/dist/uns-mqtt/mqtt-topic-builder.d.ts.map +1 -1
  94. package/dist/uns-mqtt/mqtt-topic-builder.js +8 -0
  95. package/dist/uns-mqtt/mqtt-topic-builder.js.map +1 -1
  96. package/dist/uns-mqtt/mqtt-worker-init.js.map +1 -1
  97. package/dist/uns-mqtt/mqtt-worker.js.map +1 -1
  98. package/dist/uns-mqtt/throttled-queue.js.map +1 -1
  99. package/dist/uns-mqtt/uns-mqtt-proxy.js +477 -477
  100. package/dist/uns-mqtt/uns-mqtt-proxy.js.map +1 -1
  101. package/dist/uns-mqtt/ws-proxy.js.map +1 -1
  102. package/package.json +1 -1
@@ -1,255 +1,255 @@
1
- #!/usr/bin/env node
2
- import { access, readFile } from "node:fs/promises";
3
- import path from "node:path";
4
- import process from "node:process";
5
- import { fileURLToPath } from "node:url";
6
- import { readTextFileIfExists, writeTextFileIfChanged } from "./file-utils.js";
7
- import { renderDictionaryTs } from "./generate-uns-dictionary.js";
8
- import { renderMeasurementsTs } from "./generate-uns-measurements.js";
9
- const DEFAULT_STATUS = "active";
10
- const GENERATOR_LANG = "sl";
11
- const STATUS_VALUES = ["active", "draft", "deprecated", "all"];
12
- const isDirectExecution = process.argv[1]
13
- ? path.resolve(process.argv[1]).endsWith(path.basename(fileURLToPath(import.meta.url)))
14
- : false;
15
- async function main() {
16
- const args = await parseArgs(process.argv.slice(2));
17
- if (args.help) {
18
- printHelp();
19
- return;
20
- }
21
- const target = await resolveSyncTarget(args);
22
- const shouldSyncDictionary = !args.measurementsOnly;
23
- const shouldSyncMeasurements = !args.dictionaryOnly;
24
- const [dictionaryDocument, measurementsDocument] = await Promise.all([
25
- shouldSyncDictionary ? fetchDictionaryDocument(args.controllerUrl, args.token, args.status) : Promise.resolve(undefined),
26
- shouldSyncMeasurements ? fetchMeasurementsDocument(args.controllerUrl, args.token) : Promise.resolve(undefined),
27
- ]);
28
- let dictionarySummary;
29
- let measurementsSummary;
30
- if (dictionaryDocument) {
31
- const content = formatJsonDocument(dictionaryDocument);
32
- const jsonResult = await updateFile(target.dictionaryJson, content, target.rootDir, args.dryRun);
33
- const generatedChanged = args.skipGenerate
34
- ? []
35
- : await updateGeneratedFiles(target.dictionaryGenerated, renderDictionaryTs(dictionaryDocument, GENERATOR_LANG), target.rootDir, args.dryRun);
36
- dictionarySummary = {
37
- objectTypeCount: Object.keys(dictionaryDocument.objectTypes ?? {}).length,
38
- attributeCount: Object.keys(dictionaryDocument.attributes ?? {}).length,
39
- jsonChanged: jsonResult.changed,
40
- generatedChanged,
41
- };
42
- }
43
- if (measurementsDocument) {
44
- const content = formatJsonDocument(measurementsDocument);
45
- const jsonResult = await updateFile(target.measurementsJson, content, target.rootDir, args.dryRun);
46
- const generatedChanged = args.skipGenerate
47
- ? []
48
- : await updateGeneratedFiles(target.measurementsGenerated, renderMeasurementsTs(measurementsDocument, GENERATOR_LANG), target.rootDir, args.dryRun);
49
- measurementsSummary = {
50
- categoryCount: countMeasurementCategories(measurementsDocument),
51
- jsonChanged: jsonResult.changed,
52
- generatedChanged,
53
- };
54
- }
55
- printSummary({
56
- controllerUrl: args.controllerUrl,
57
- targetLabel: target.label,
58
- targetRoot: target.rootDir,
59
- dryRun: args.dryRun,
60
- skipGenerate: args.skipGenerate,
61
- status: args.status,
62
- dictionary: dictionarySummary,
63
- measurements: measurementsSummary,
64
- });
65
- }
66
- async function getControllerUrlFromConfig() {
67
- const currentDir = process.cwd();
68
- const configPath = path.join(currentDir, "config.json");
69
- try {
70
- const raw = await readFile(configPath, "utf8");
71
- const config = JSON.parse(raw);
72
- const unsConfig = config.uns;
73
- const restUrl = unsConfig?.rest;
74
- if (restUrl && typeof restUrl === "string") {
75
- // Remove trailing /api from the rest URL to get the base controller URL
76
- return restUrl.endsWith("/api") ? restUrl.slice(0, -4) : restUrl;
77
- }
78
- }
79
- catch (error) {
80
- // Ignore missing or invalid config.json
81
- }
82
- return undefined;
83
- }
84
- async function promptForToken() {
85
- return new Promise((resolve) => {
86
- const stdin = process.stdin;
87
- const wasRaw = stdin.isRaw;
88
- stdin.setRawMode(true);
89
- stdin.resume();
90
- process.stderr.write("Enter controller token: ");
91
- let token = "";
92
- stdin.on("data", onChar);
93
- function onChar(char) {
94
- const code = char[0];
95
- // Enter (13) or newline (10) — done
96
- if (code === 13 || code === 10) {
97
- stdin.removeListener("data", onChar);
98
- stdin.setRawMode(wasRaw ?? false);
99
- stdin.pause();
100
- process.stderr.write("\n");
101
- resolve(token);
102
- return;
103
- }
104
- // Ctrl+C — abort
105
- if (code === 3) {
106
- stdin.removeListener("data", onChar);
107
- stdin.setRawMode(wasRaw ?? false);
108
- process.stderr.write("\n");
109
- process.exit(1);
110
- }
111
- // Backspace (127) or Delete (8)
112
- if (code === 127 || code === 8) {
113
- token = token.slice(0, -1);
114
- }
115
- else if (code >= 32) {
116
- token += char.toString("utf8");
117
- }
118
- }
119
- });
120
- }
121
- async function parseArgs(argv) {
122
- let controllerUrl = process.env.UNS_CONTROLLER_URL?.trim() ?? "";
123
- let token = process.env.UNS_CONTROLLER_TOKEN?.trim() ?? "";
124
- let status = normalizeStatus(process.env.UNS_SCHEMA_STATUS, DEFAULT_STATUS);
125
- let projectRoot = process.env.UNS_SCHEMA_PROJECT_ROOT?.trim() || undefined;
126
- let dryRun = false;
127
- let dictionaryOnly = false;
128
- let measurementsOnly = false;
129
- let skipGenerate = false;
130
- let help = false;
131
- for (let index = 0; index < argv.length; index += 1) {
132
- const arg = argv[index];
133
- if (arg === "--help" || arg === "-h") {
134
- help = true;
135
- continue;
136
- }
137
- if (arg === "--dry-run") {
138
- dryRun = true;
139
- continue;
140
- }
141
- if (arg === "--dictionary-only") {
142
- dictionaryOnly = true;
143
- continue;
144
- }
145
- if (arg === "--measurements-only") {
146
- measurementsOnly = true;
147
- continue;
148
- }
149
- if (arg === "--skip-generate") {
150
- skipGenerate = true;
151
- continue;
152
- }
153
- if (arg === "--project-root") {
154
- projectRoot = readRequiredValue(argv, ++index, "--project-root");
155
- continue;
156
- }
157
- if (arg.startsWith("--project-root=")) {
158
- projectRoot = arg.slice("--project-root=".length);
159
- continue;
160
- }
161
- if (arg === "--controller-url") {
162
- controllerUrl = readRequiredValue(argv, ++index, "--controller-url");
163
- continue;
164
- }
165
- if (arg.startsWith("--controller-url=")) {
166
- controllerUrl = arg.slice("--controller-url=".length);
167
- continue;
168
- }
169
- if (arg === "--token") {
170
- token = readRequiredValue(argv, ++index, "--token");
171
- continue;
172
- }
173
- if (arg.startsWith("--token=")) {
174
- token = arg.slice("--token=".length);
175
- continue;
176
- }
177
- if (arg === "--status") {
178
- status = normalizeStatus(readRequiredValue(argv, ++index, "--status"), DEFAULT_STATUS);
179
- continue;
180
- }
181
- if (arg.startsWith("--status=")) {
182
- status = normalizeStatus(arg.slice("--status=".length), DEFAULT_STATUS);
183
- continue;
184
- }
185
- throw new Error(`Unknown argument: ${arg}`);
186
- }
187
- // Try to load controller URL from config.json if not provided
188
- if (!controllerUrl) {
189
- const configUrl = await getControllerUrlFromConfig();
190
- if (configUrl) {
191
- controllerUrl = configUrl;
192
- }
193
- }
194
- if (!help) {
195
- if (dictionaryOnly && measurementsOnly) {
196
- throw new Error("Choose either --dictionary-only or --measurements-only, not both.");
197
- }
198
- if (!controllerUrl) {
199
- throw new Error("Missing controller URL. Use --controller-url, set UNS_CONTROLLER_URL, or provide config.json with uns.rest.");
200
- }
201
- if (!token) {
202
- // Prompt for token if stdin is a TTY (interactive terminal)
203
- if (process.stdin.isTTY) {
204
- token = await promptForToken();
205
- }
206
- if (!token) {
207
- throw new Error("Missing controller token. Use --token, set UNS_CONTROLLER_TOKEN, or provide it interactively.");
208
- }
209
- }
210
- // Strip "Bearer " prefix if the user included it
211
- if (token.toLowerCase().startsWith("bearer ")) {
212
- token = token.slice(7).trim();
213
- }
214
- assertValidUrl(controllerUrl);
215
- }
216
- return {
217
- controllerUrl,
218
- token,
219
- status,
220
- projectRoot,
221
- dryRun,
222
- dictionaryOnly,
223
- measurementsOnly,
224
- skipGenerate,
225
- help,
226
- };
227
- }
228
- function readRequiredValue(argv, index, flag) {
229
- const value = argv[index];
230
- if (!value) {
231
- throw new Error(`Missing value for ${flag}.`);
232
- }
233
- return value;
234
- }
235
- function normalizeStatus(value, fallback) {
236
- if (!value)
237
- return fallback;
238
- const normalized = value.trim().toLowerCase();
239
- if (STATUS_VALUES.includes(normalized)) {
240
- return normalized;
241
- }
242
- throw new Error(`Invalid --status value "${value}". Expected one of: ${STATUS_VALUES.join(", ")}.`);
243
- }
244
- function assertValidUrl(value) {
245
- try {
246
- new URL(value);
247
- }
248
- catch (error) {
249
- throw new Error(`Invalid controller URL "${value}". Expected an absolute URL such as http://localhost:3200.`);
250
- }
251
- }
252
- function printHelp() {
1
+ #!/usr/bin/env node
2
+ import { access, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { fileURLToPath } from "node:url";
6
+ import { readTextFileIfExists, writeTextFileIfChanged } from "./file-utils.js";
7
+ import { renderDictionaryTs } from "./generate-uns-dictionary.js";
8
+ import { renderMeasurementsTs } from "./generate-uns-measurements.js";
9
+ const DEFAULT_STATUS = "active";
10
+ const GENERATOR_LANG = "sl";
11
+ const STATUS_VALUES = ["active", "draft", "deprecated", "all"];
12
+ const isDirectExecution = process.argv[1]
13
+ ? path.resolve(process.argv[1]).endsWith(path.basename(fileURLToPath(import.meta.url)))
14
+ : false;
15
+ async function main() {
16
+ const args = await parseArgs(process.argv.slice(2));
17
+ if (args.help) {
18
+ printHelp();
19
+ return;
20
+ }
21
+ const target = await resolveSyncTarget(args);
22
+ const shouldSyncDictionary = !args.measurementsOnly;
23
+ const shouldSyncMeasurements = !args.dictionaryOnly;
24
+ const [dictionaryDocument, measurementsDocument] = await Promise.all([
25
+ shouldSyncDictionary ? fetchDictionaryDocument(args.controllerUrl, args.token, args.status) : Promise.resolve(undefined),
26
+ shouldSyncMeasurements ? fetchMeasurementsDocument(args.controllerUrl, args.token) : Promise.resolve(undefined),
27
+ ]);
28
+ let dictionarySummary;
29
+ let measurementsSummary;
30
+ if (dictionaryDocument) {
31
+ const content = formatJsonDocument(dictionaryDocument);
32
+ const jsonResult = await updateFile(target.dictionaryJson, content, target.rootDir, args.dryRun);
33
+ const generatedChanged = args.skipGenerate
34
+ ? []
35
+ : await updateGeneratedFiles(target.dictionaryGenerated, renderDictionaryTs(dictionaryDocument, GENERATOR_LANG), target.rootDir, args.dryRun);
36
+ dictionarySummary = {
37
+ objectTypeCount: Object.keys(dictionaryDocument.objectTypes ?? {}).length,
38
+ attributeCount: Object.keys(dictionaryDocument.attributes ?? {}).length,
39
+ jsonChanged: jsonResult.changed,
40
+ generatedChanged,
41
+ };
42
+ }
43
+ if (measurementsDocument) {
44
+ const content = formatJsonDocument(measurementsDocument);
45
+ const jsonResult = await updateFile(target.measurementsJson, content, target.rootDir, args.dryRun);
46
+ const generatedChanged = args.skipGenerate
47
+ ? []
48
+ : await updateGeneratedFiles(target.measurementsGenerated, renderMeasurementsTs(measurementsDocument, GENERATOR_LANG), target.rootDir, args.dryRun);
49
+ measurementsSummary = {
50
+ categoryCount: countMeasurementCategories(measurementsDocument),
51
+ jsonChanged: jsonResult.changed,
52
+ generatedChanged,
53
+ };
54
+ }
55
+ printSummary({
56
+ controllerUrl: args.controllerUrl,
57
+ targetLabel: target.label,
58
+ targetRoot: target.rootDir,
59
+ dryRun: args.dryRun,
60
+ skipGenerate: args.skipGenerate,
61
+ status: args.status,
62
+ dictionary: dictionarySummary,
63
+ measurements: measurementsSummary,
64
+ });
65
+ }
66
+ async function getControllerUrlFromConfig() {
67
+ const currentDir = process.cwd();
68
+ const configPath = path.join(currentDir, "config.json");
69
+ try {
70
+ const raw = await readFile(configPath, "utf8");
71
+ const config = JSON.parse(raw);
72
+ const unsConfig = config.uns;
73
+ const restUrl = unsConfig?.rest;
74
+ if (restUrl && typeof restUrl === "string") {
75
+ // Remove trailing /api from the rest URL to get the base controller URL
76
+ return restUrl.endsWith("/api") ? restUrl.slice(0, -4) : restUrl;
77
+ }
78
+ }
79
+ catch (error) {
80
+ // Ignore missing or invalid config.json
81
+ }
82
+ return undefined;
83
+ }
84
+ async function promptForToken() {
85
+ return new Promise((resolve) => {
86
+ const stdin = process.stdin;
87
+ const wasRaw = stdin.isRaw;
88
+ stdin.setRawMode(true);
89
+ stdin.resume();
90
+ process.stderr.write("Enter controller token: ");
91
+ let token = "";
92
+ stdin.on("data", onChar);
93
+ function onChar(char) {
94
+ const code = char[0];
95
+ // Enter (13) or newline (10) — done
96
+ if (code === 13 || code === 10) {
97
+ stdin.removeListener("data", onChar);
98
+ stdin.setRawMode(wasRaw ?? false);
99
+ stdin.pause();
100
+ process.stderr.write("\n");
101
+ resolve(token);
102
+ return;
103
+ }
104
+ // Ctrl+C — abort
105
+ if (code === 3) {
106
+ stdin.removeListener("data", onChar);
107
+ stdin.setRawMode(wasRaw ?? false);
108
+ process.stderr.write("\n");
109
+ process.exit(1);
110
+ }
111
+ // Backspace (127) or Delete (8)
112
+ if (code === 127 || code === 8) {
113
+ token = token.slice(0, -1);
114
+ }
115
+ else if (code >= 32) {
116
+ token += char.toString("utf8");
117
+ }
118
+ }
119
+ });
120
+ }
121
+ async function parseArgs(argv) {
122
+ let controllerUrl = process.env.UNS_CONTROLLER_URL?.trim() ?? "";
123
+ let token = process.env.UNS_CONTROLLER_TOKEN?.trim() ?? "";
124
+ let status = normalizeStatus(process.env.UNS_SCHEMA_STATUS, DEFAULT_STATUS);
125
+ let projectRoot = process.env.UNS_SCHEMA_PROJECT_ROOT?.trim() || undefined;
126
+ let dryRun = false;
127
+ let dictionaryOnly = false;
128
+ let measurementsOnly = false;
129
+ let skipGenerate = false;
130
+ let help = false;
131
+ for (let index = 0; index < argv.length; index += 1) {
132
+ const arg = argv[index];
133
+ if (arg === "--help" || arg === "-h") {
134
+ help = true;
135
+ continue;
136
+ }
137
+ if (arg === "--dry-run") {
138
+ dryRun = true;
139
+ continue;
140
+ }
141
+ if (arg === "--dictionary-only") {
142
+ dictionaryOnly = true;
143
+ continue;
144
+ }
145
+ if (arg === "--measurements-only") {
146
+ measurementsOnly = true;
147
+ continue;
148
+ }
149
+ if (arg === "--skip-generate") {
150
+ skipGenerate = true;
151
+ continue;
152
+ }
153
+ if (arg === "--project-root") {
154
+ projectRoot = readRequiredValue(argv, ++index, "--project-root");
155
+ continue;
156
+ }
157
+ if (arg.startsWith("--project-root=")) {
158
+ projectRoot = arg.slice("--project-root=".length);
159
+ continue;
160
+ }
161
+ if (arg === "--controller-url") {
162
+ controllerUrl = readRequiredValue(argv, ++index, "--controller-url");
163
+ continue;
164
+ }
165
+ if (arg.startsWith("--controller-url=")) {
166
+ controllerUrl = arg.slice("--controller-url=".length);
167
+ continue;
168
+ }
169
+ if (arg === "--token") {
170
+ token = readRequiredValue(argv, ++index, "--token");
171
+ continue;
172
+ }
173
+ if (arg.startsWith("--token=")) {
174
+ token = arg.slice("--token=".length);
175
+ continue;
176
+ }
177
+ if (arg === "--status") {
178
+ status = normalizeStatus(readRequiredValue(argv, ++index, "--status"), DEFAULT_STATUS);
179
+ continue;
180
+ }
181
+ if (arg.startsWith("--status=")) {
182
+ status = normalizeStatus(arg.slice("--status=".length), DEFAULT_STATUS);
183
+ continue;
184
+ }
185
+ throw new Error(`Unknown argument: ${arg}`);
186
+ }
187
+ // Try to load controller URL from config.json if not provided
188
+ if (!controllerUrl) {
189
+ const configUrl = await getControllerUrlFromConfig();
190
+ if (configUrl) {
191
+ controllerUrl = configUrl;
192
+ }
193
+ }
194
+ if (!help) {
195
+ if (dictionaryOnly && measurementsOnly) {
196
+ throw new Error("Choose either --dictionary-only or --measurements-only, not both.");
197
+ }
198
+ if (!controllerUrl) {
199
+ throw new Error("Missing controller URL. Use --controller-url, set UNS_CONTROLLER_URL, or provide config.json with uns.rest.");
200
+ }
201
+ if (!token) {
202
+ // Prompt for token if stdin is a TTY (interactive terminal)
203
+ if (process.stdin.isTTY) {
204
+ token = await promptForToken();
205
+ }
206
+ if (!token) {
207
+ throw new Error("Missing controller token. Use --token, set UNS_CONTROLLER_TOKEN, or provide it interactively.");
208
+ }
209
+ }
210
+ // Strip "Bearer " prefix if the user included it
211
+ if (token.toLowerCase().startsWith("bearer ")) {
212
+ token = token.slice(7).trim();
213
+ }
214
+ assertValidUrl(controllerUrl);
215
+ }
216
+ return {
217
+ controllerUrl,
218
+ token,
219
+ status,
220
+ projectRoot,
221
+ dryRun,
222
+ dictionaryOnly,
223
+ measurementsOnly,
224
+ skipGenerate,
225
+ help,
226
+ };
227
+ }
228
+ function readRequiredValue(argv, index, flag) {
229
+ const value = argv[index];
230
+ if (!value) {
231
+ throw new Error(`Missing value for ${flag}.`);
232
+ }
233
+ return value;
234
+ }
235
+ function normalizeStatus(value, fallback) {
236
+ if (!value)
237
+ return fallback;
238
+ const normalized = value.trim().toLowerCase();
239
+ if (STATUS_VALUES.includes(normalized)) {
240
+ return normalized;
241
+ }
242
+ throw new Error(`Invalid --status value "${value}". Expected one of: ${STATUS_VALUES.join(", ")}.`);
243
+ }
244
+ function assertValidUrl(value) {
245
+ try {
246
+ new URL(value);
247
+ }
248
+ catch (error) {
249
+ throw new Error(`Invalid controller URL "${value}". Expected an absolute URL such as http://localhost:3200.`);
250
+ }
251
+ }
252
+ function printHelp() {
253
253
  console.log(`Usage: tsx packages/uns-core/src/tools/sync-uns-schema.ts [options]
254
254
 
255
255
  Pull the canonical UNS schema export from a controller and refresh the local
@@ -267,224 +267,224 @@ Options:
267
267
  --measurements-only Sync only the UNS measurements export
268
268
  --skip-generate Skip generated TypeScript refresh
269
269
  --help, -h Show this help
270
- `);
271
- }
272
- async function findRepoRoot() {
273
- const currentDir = path.dirname(fileURLToPath(import.meta.url));
274
- let probeDir = currentDir;
275
- while (true) {
276
- const packageJsonPath = path.join(probeDir, "package.json");
277
- try {
278
- const raw = await readFile(packageJsonPath, "utf8");
279
- const pkg = JSON.parse(raw);
280
- if (pkg.name === "@uns-kit/core") {
281
- return path.resolve(probeDir, "../..");
282
- }
283
- }
284
- catch (error) {
285
- // Ignore missing package.json and continue walking upward.
286
- }
287
- const parentDir = path.dirname(probeDir);
288
- if (parentDir === probeDir) {
289
- break;
290
- }
291
- probeDir = parentDir;
292
- }
293
- throw new Error("Could not locate the @uns-kit/core package directory from sync-uns-schema.ts.");
294
- }
295
- async function resolveSyncTarget(args) {
296
- if (args.projectRoot) {
297
- return buildProjectTarget(path.resolve(process.cwd(), args.projectRoot));
298
- }
299
- const currentDir = process.cwd();
300
- if (await looksLikeGeneratedProjectRoot(currentDir)) {
301
- return buildProjectTarget(currentDir);
302
- }
303
- return buildRepoTarget(await findRepoRoot());
304
- }
305
- function buildRepoTarget(repoRoot) {
306
- return {
307
- rootDir: repoRoot,
308
- label: "uns-kit repo templates",
309
- dictionaryJson: path.join(repoRoot, "packages/uns-cli/templates/uns-dictionary/uns-dictionary.json"),
310
- measurementsJson: path.join(repoRoot, "packages/uns-cli/templates/uns-measurements/uns-measurements.json"),
311
- dictionaryGenerated: [
312
- path.join(repoRoot, "packages/uns-core/src/uns/uns-dictionary.generated.ts"),
313
- path.join(repoRoot, "packages/uns-cli/templates/default/src/uns/uns-dictionary.generated.ts"),
314
- ],
315
- measurementsGenerated: [
316
- path.join(repoRoot, "packages/uns-core/src/uns/uns-measurements.generated.ts"),
317
- path.join(repoRoot, "packages/uns-cli/templates/default/src/uns/uns-measurements.generated.ts"),
318
- ],
319
- };
320
- }
321
- function buildProjectTarget(projectRoot) {
322
- return {
323
- rootDir: projectRoot,
324
- label: "generated microservice project",
325
- dictionaryJson: path.join(projectRoot, "uns-dictionary.json"),
326
- measurementsJson: path.join(projectRoot, "uns-measurements.json"),
327
- dictionaryGenerated: [path.join(projectRoot, "src/uns/uns-dictionary.generated.ts")],
328
- measurementsGenerated: [path.join(projectRoot, "src/uns/uns-measurements.generated.ts")],
329
- };
330
- }
331
- async function looksLikeGeneratedProjectRoot(rootDir) {
332
- const [hasPackageJson, hasConfigJson, hasUnsDir] = await Promise.all([
333
- pathExists(path.join(rootDir, "package.json")),
334
- pathExists(path.join(rootDir, "config.json")),
335
- pathExists(path.join(rootDir, "src/uns")),
336
- ]);
337
- return hasPackageJson && hasConfigJson && hasUnsDir;
338
- }
339
- async function pathExists(targetPath) {
340
- try {
341
- await access(targetPath);
342
- return true;
343
- }
344
- catch (error) {
345
- return false;
346
- }
347
- }
348
- function buildControllerUrl(controllerUrl, relativePath) {
349
- const base = new URL(controllerUrl);
350
- if (!base.pathname.endsWith("/")) {
351
- base.pathname = `${base.pathname}/`;
352
- }
353
- return new URL(relativePath, base);
354
- }
355
- async function fetchDictionaryDocument(controllerUrl, token, status) {
356
- const url = buildControllerUrl(controllerUrl, `api/schema/export/uns-dictionary?status=${encodeURIComponent(status)}`);
357
- const document = await fetchJson(url, token, "UNS dictionary");
358
- if (!document || typeof document !== "object" || Array.isArray(document)) {
359
- throw new Error(`Controller returned an invalid UNS dictionary document from ${url.toString()}.`);
360
- }
361
- return document;
362
- }
363
- async function fetchMeasurementsDocument(controllerUrl, token) {
364
- const url = buildControllerUrl(controllerUrl, "api/schema/export/uns-measurements");
365
- const document = await fetchJson(url, token, "UNS measurements");
366
- if (!document || typeof document !== "object" || Array.isArray(document)) {
367
- throw new Error(`Controller returned an invalid UNS measurements document from ${url.toString()}.`);
368
- }
369
- return document;
370
- }
371
- async function fetchJson(url, token, label) {
372
- let response;
373
- try {
374
- response = await fetch(url, {
375
- headers: {
376
- Accept: "application/json",
377
- Authorization: `Bearer ${token}`,
378
- },
379
- });
380
- }
381
- catch (error) {
382
- throw new Error(`Network failure while fetching ${label} export from ${url.toString()}: ${error.message}`);
383
- }
384
- const text = await response.text();
385
- if (!response.ok) {
386
- throw new Error(formatHttpError(response, url, label, text));
387
- }
388
- try {
389
- return JSON.parse(text);
390
- }
391
- catch (error) {
392
- throw new Error(`Controller returned invalid JSON for the ${label} export from ${url.toString()}.`);
393
- }
394
- }
395
- function formatHttpError(response, url, label, body) {
396
- if (response.status === 401) {
397
- return `Token is invalid or expired (401). Provide a valid token via --token, UNS_CONTROLLER_TOKEN, or the interactive prompt.`;
398
- }
399
- if (response.status === 403) {
400
- return `Forbidden (403) while fetching ${label} export from ${url.toString()}. The token must have admin access.`;
401
- }
402
- const detail = extractErrorDetail(body);
403
- return `Failed to fetch ${label} export from ${url.toString()}: HTTP ${response.status}${detail ? ` - ${detail}` : ""}`;
404
- }
405
- function extractErrorDetail(body) {
406
- const trimmed = body.trim();
407
- if (!trimmed)
408
- return "";
409
- try {
410
- const parsed = JSON.parse(trimmed);
411
- if (typeof parsed.error === "string" && parsed.error.trim().length > 0) {
412
- return parsed.error.trim();
413
- }
414
- if (typeof parsed.message === "string" && parsed.message.trim().length > 0) {
415
- return parsed.message.trim();
416
- }
417
- }
418
- catch (error) {
419
- // Fall back to plain text handling below.
420
- }
421
- return trimmed.replace(/\s+/g, " ").slice(0, 200);
422
- }
423
- function formatJsonDocument(document) {
424
- return `${JSON.stringify(document, null, 2)}\n`;
425
- }
426
- async function updateGeneratedFiles(filePaths, content, repoRoot, dryRun) {
427
- return Promise.all(filePaths.map((filePath) => updateFile(filePath, content, repoRoot, dryRun)));
428
- }
429
- async function updateFile(filePath, content, repoRoot, dryRun) {
430
- const current = await readTextFileIfExists(filePath);
431
- const changed = current !== content;
432
- const relativePath = path.relative(repoRoot, filePath);
433
- if (dryRun) {
434
- console.log(`${changed ? "Would update" : "No change"} ${relativePath}`);
435
- return { changed, relativePath };
436
- }
437
- if (!changed) {
438
- console.log(`Unchanged ${relativePath}`);
439
- return { changed: false, relativePath };
440
- }
441
- await writeTextFileIfChanged(filePath, content);
442
- console.log(`Updated ${relativePath}`);
443
- return { changed: true, relativePath };
444
- }
445
- function countMeasurementCategories(document) {
446
- return Object.entries(document).filter(([key, value]) => {
447
- if (key === "schemaVersion")
448
- return false;
449
- return !!value && typeof value === "object" && !Array.isArray(value);
450
- }).length;
451
- }
452
- function printSummary(summary) {
453
- console.log("");
454
- console.log(summary.dryRun ? "Dry-run summary" : "Sync summary");
455
- console.log(` Controller: ${summary.controllerUrl}`);
456
- console.log(` Target: ${summary.targetLabel}`);
457
- console.log(` Root: ${summary.targetRoot}`);
458
- console.log(` Dictionary status: ${summary.status}`);
459
- if (summary.dictionary) {
460
- console.log(` Dictionary: ${summary.dictionary.objectTypeCount} object types, ${summary.dictionary.attributeCount} attributes, JSON ${summary.dictionary.jsonChanged ? "changed" : "unchanged"}`);
461
- if (summary.skipGenerate) {
462
- console.log(" Dictionary TS: skipped");
463
- }
464
- else {
465
- console.log(` Dictionary TS: ${renderGeneratedSummary(summary.dictionary.generatedChanged)}`);
466
- }
467
- }
468
- if (summary.measurements) {
469
- console.log(` Measurements: ${summary.measurements.categoryCount} categories, JSON ${summary.measurements.jsonChanged ? "changed" : "unchanged"}`);
470
- if (summary.skipGenerate) {
471
- console.log(" Measurements TS: skipped");
472
- }
473
- else {
474
- console.log(` Measurements TS: ${renderGeneratedSummary(summary.measurements.generatedChanged)}`);
475
- }
476
- }
477
- }
478
- function renderGeneratedSummary(results) {
479
- if (!results.length)
480
- return "none";
481
- const changedCount = results.filter((result) => result.changed).length;
482
- return `${changedCount}/${results.length} file${results.length === 1 ? "" : "s"} changed`;
483
- }
484
- if (isDirectExecution) {
485
- main().catch((error) => {
486
- console.error("Failed to sync UNS schema:", error instanceof Error ? error.message : error);
487
- process.exitCode = 1;
488
- });
489
- }
270
+ `);
271
+ }
272
+ async function findRepoRoot() {
273
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
274
+ let probeDir = currentDir;
275
+ while (true) {
276
+ const packageJsonPath = path.join(probeDir, "package.json");
277
+ try {
278
+ const raw = await readFile(packageJsonPath, "utf8");
279
+ const pkg = JSON.parse(raw);
280
+ if (pkg.name === "@uns-kit/core") {
281
+ return path.resolve(probeDir, "../..");
282
+ }
283
+ }
284
+ catch (error) {
285
+ // Ignore missing package.json and continue walking upward.
286
+ }
287
+ const parentDir = path.dirname(probeDir);
288
+ if (parentDir === probeDir) {
289
+ break;
290
+ }
291
+ probeDir = parentDir;
292
+ }
293
+ throw new Error("Could not locate the @uns-kit/core package directory from sync-uns-schema.ts.");
294
+ }
295
+ async function resolveSyncTarget(args) {
296
+ if (args.projectRoot) {
297
+ return buildProjectTarget(path.resolve(process.cwd(), args.projectRoot));
298
+ }
299
+ const currentDir = process.cwd();
300
+ if (await looksLikeGeneratedProjectRoot(currentDir)) {
301
+ return buildProjectTarget(currentDir);
302
+ }
303
+ return buildRepoTarget(await findRepoRoot());
304
+ }
305
+ function buildRepoTarget(repoRoot) {
306
+ return {
307
+ rootDir: repoRoot,
308
+ label: "uns-kit repo templates",
309
+ dictionaryJson: path.join(repoRoot, "packages/uns-cli/templates/uns-dictionary/uns-dictionary.json"),
310
+ measurementsJson: path.join(repoRoot, "packages/uns-cli/templates/uns-measurements/uns-measurements.json"),
311
+ dictionaryGenerated: [
312
+ path.join(repoRoot, "packages/uns-core/src/uns/uns-dictionary.generated.ts"),
313
+ path.join(repoRoot, "packages/uns-cli/templates/default/src/uns/uns-dictionary.generated.ts"),
314
+ ],
315
+ measurementsGenerated: [
316
+ path.join(repoRoot, "packages/uns-core/src/uns/uns-measurements.generated.ts"),
317
+ path.join(repoRoot, "packages/uns-cli/templates/default/src/uns/uns-measurements.generated.ts"),
318
+ ],
319
+ };
320
+ }
321
+ function buildProjectTarget(projectRoot) {
322
+ return {
323
+ rootDir: projectRoot,
324
+ label: "generated microservice project",
325
+ dictionaryJson: path.join(projectRoot, "uns-dictionary.json"),
326
+ measurementsJson: path.join(projectRoot, "uns-measurements.json"),
327
+ dictionaryGenerated: [path.join(projectRoot, "src/uns/uns-dictionary.generated.ts")],
328
+ measurementsGenerated: [path.join(projectRoot, "src/uns/uns-measurements.generated.ts")],
329
+ };
330
+ }
331
+ async function looksLikeGeneratedProjectRoot(rootDir) {
332
+ const [hasPackageJson, hasConfigJson, hasUnsDir] = await Promise.all([
333
+ pathExists(path.join(rootDir, "package.json")),
334
+ pathExists(path.join(rootDir, "config.json")),
335
+ pathExists(path.join(rootDir, "src/uns")),
336
+ ]);
337
+ return hasPackageJson && hasConfigJson && hasUnsDir;
338
+ }
339
+ async function pathExists(targetPath) {
340
+ try {
341
+ await access(targetPath);
342
+ return true;
343
+ }
344
+ catch (error) {
345
+ return false;
346
+ }
347
+ }
348
+ function buildControllerUrl(controllerUrl, relativePath) {
349
+ const base = new URL(controllerUrl);
350
+ if (!base.pathname.endsWith("/")) {
351
+ base.pathname = `${base.pathname}/`;
352
+ }
353
+ return new URL(relativePath, base);
354
+ }
355
+ async function fetchDictionaryDocument(controllerUrl, token, status) {
356
+ const url = buildControllerUrl(controllerUrl, `api/schema/export/uns-dictionary?status=${encodeURIComponent(status)}`);
357
+ const document = await fetchJson(url, token, "UNS dictionary");
358
+ if (!document || typeof document !== "object" || Array.isArray(document)) {
359
+ throw new Error(`Controller returned an invalid UNS dictionary document from ${url.toString()}.`);
360
+ }
361
+ return document;
362
+ }
363
+ async function fetchMeasurementsDocument(controllerUrl, token) {
364
+ const url = buildControllerUrl(controllerUrl, "api/schema/export/uns-measurements");
365
+ const document = await fetchJson(url, token, "UNS measurements");
366
+ if (!document || typeof document !== "object" || Array.isArray(document)) {
367
+ throw new Error(`Controller returned an invalid UNS measurements document from ${url.toString()}.`);
368
+ }
369
+ return document;
370
+ }
371
+ async function fetchJson(url, token, label) {
372
+ let response;
373
+ try {
374
+ response = await fetch(url, {
375
+ headers: {
376
+ Accept: "application/json",
377
+ Authorization: `Bearer ${token}`,
378
+ },
379
+ });
380
+ }
381
+ catch (error) {
382
+ throw new Error(`Network failure while fetching ${label} export from ${url.toString()}: ${error.message}`);
383
+ }
384
+ const text = await response.text();
385
+ if (!response.ok) {
386
+ throw new Error(formatHttpError(response, url, label, text));
387
+ }
388
+ try {
389
+ return JSON.parse(text);
390
+ }
391
+ catch (error) {
392
+ throw new Error(`Controller returned invalid JSON for the ${label} export from ${url.toString()}.`);
393
+ }
394
+ }
395
+ function formatHttpError(response, url, label, body) {
396
+ if (response.status === 401) {
397
+ return `Token is invalid or expired (401). Provide a valid token via --token, UNS_CONTROLLER_TOKEN, or the interactive prompt.`;
398
+ }
399
+ if (response.status === 403) {
400
+ return `Forbidden (403) while fetching ${label} export from ${url.toString()}. The token must have admin access.`;
401
+ }
402
+ const detail = extractErrorDetail(body);
403
+ return `Failed to fetch ${label} export from ${url.toString()}: HTTP ${response.status}${detail ? ` - ${detail}` : ""}`;
404
+ }
405
+ function extractErrorDetail(body) {
406
+ const trimmed = body.trim();
407
+ if (!trimmed)
408
+ return "";
409
+ try {
410
+ const parsed = JSON.parse(trimmed);
411
+ if (typeof parsed.error === "string" && parsed.error.trim().length > 0) {
412
+ return parsed.error.trim();
413
+ }
414
+ if (typeof parsed.message === "string" && parsed.message.trim().length > 0) {
415
+ return parsed.message.trim();
416
+ }
417
+ }
418
+ catch (error) {
419
+ // Fall back to plain text handling below.
420
+ }
421
+ return trimmed.replace(/\s+/g, " ").slice(0, 200);
422
+ }
423
+ function formatJsonDocument(document) {
424
+ return `${JSON.stringify(document, null, 2)}\n`;
425
+ }
426
+ async function updateGeneratedFiles(filePaths, content, repoRoot, dryRun) {
427
+ return Promise.all(filePaths.map((filePath) => updateFile(filePath, content, repoRoot, dryRun)));
428
+ }
429
+ async function updateFile(filePath, content, repoRoot, dryRun) {
430
+ const current = await readTextFileIfExists(filePath);
431
+ const changed = current !== content;
432
+ const relativePath = path.relative(repoRoot, filePath);
433
+ if (dryRun) {
434
+ console.log(`${changed ? "Would update" : "No change"} ${relativePath}`);
435
+ return { changed, relativePath };
436
+ }
437
+ if (!changed) {
438
+ console.log(`Unchanged ${relativePath}`);
439
+ return { changed: false, relativePath };
440
+ }
441
+ await writeTextFileIfChanged(filePath, content);
442
+ console.log(`Updated ${relativePath}`);
443
+ return { changed: true, relativePath };
444
+ }
445
+ function countMeasurementCategories(document) {
446
+ return Object.entries(document).filter(([key, value]) => {
447
+ if (key === "schemaVersion")
448
+ return false;
449
+ return !!value && typeof value === "object" && !Array.isArray(value);
450
+ }).length;
451
+ }
452
+ function printSummary(summary) {
453
+ console.log("");
454
+ console.log(summary.dryRun ? "Dry-run summary" : "Sync summary");
455
+ console.log(` Controller: ${summary.controllerUrl}`);
456
+ console.log(` Target: ${summary.targetLabel}`);
457
+ console.log(` Root: ${summary.targetRoot}`);
458
+ console.log(` Dictionary status: ${summary.status}`);
459
+ if (summary.dictionary) {
460
+ console.log(` Dictionary: ${summary.dictionary.objectTypeCount} object types, ${summary.dictionary.attributeCount} attributes, JSON ${summary.dictionary.jsonChanged ? "changed" : "unchanged"}`);
461
+ if (summary.skipGenerate) {
462
+ console.log(" Dictionary TS: skipped");
463
+ }
464
+ else {
465
+ console.log(` Dictionary TS: ${renderGeneratedSummary(summary.dictionary.generatedChanged)}`);
466
+ }
467
+ }
468
+ if (summary.measurements) {
469
+ console.log(` Measurements: ${summary.measurements.categoryCount} categories, JSON ${summary.measurements.jsonChanged ? "changed" : "unchanged"}`);
470
+ if (summary.skipGenerate) {
471
+ console.log(" Measurements TS: skipped");
472
+ }
473
+ else {
474
+ console.log(` Measurements TS: ${renderGeneratedSummary(summary.measurements.generatedChanged)}`);
475
+ }
476
+ }
477
+ }
478
+ function renderGeneratedSummary(results) {
479
+ if (!results.length)
480
+ return "none";
481
+ const changedCount = results.filter((result) => result.changed).length;
482
+ return `${changedCount}/${results.length} file${results.length === 1 ? "" : "s"} changed`;
483
+ }
484
+ if (isDirectExecution) {
485
+ main().catch((error) => {
486
+ console.error("Failed to sync UNS schema:", error instanceof Error ? error.message : error);
487
+ process.exitCode = 1;
488
+ });
489
+ }
490
490
  //# sourceMappingURL=sync-uns-schema.js.map