@effect-opcua/codegen 0.1.0-alpha.0
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/LICENSE +201 -0
- package/README.md +6 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +82 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client/src/Opcua.d.mts +5 -0
- package/dist/client/src/OpcuaClient.d.mts +3 -0
- package/dist/client/src/OpcuaError.d.mts +139 -0
- package/dist/client/src/OpcuaError.d.mts.map +1 -0
- package/dist/client/src/OpcuaMethod.d.mts +78 -0
- package/dist/client/src/OpcuaMethod.d.mts.map +1 -0
- package/dist/client/src/OpcuaSession.d.mts +215 -0
- package/dist/client/src/OpcuaSession.d.mts.map +1 -0
- package/dist/client/src/OpcuaSubscription.d.mts +144 -0
- package/dist/client/src/OpcuaSubscription.d.mts.map +1 -0
- package/dist/client/src/OpcuaVariable.d.mts +140 -0
- package/dist/client/src/OpcuaVariable.d.mts.map +1 -0
- package/dist/client/src/index.d.mts +7 -0
- package/dist/client/src/internal/common/node-id.d.mts +1 -0
- package/dist/client/src/internal/events/model.d.mts +51 -0
- package/dist/client/src/internal/events/model.d.mts.map +1 -0
- package/dist/client/src/internal/monitoring/runtime.d.mts +7 -0
- package/dist/client/src/internal/structures/model.d.mts +18 -0
- package/dist/client/src/internal/structures/model.d.mts.map +1 -0
- package/dist/client/src/internal/structures/runtime.d.mts +5 -0
- package/dist/client/src/internal/values/codec.d.mts +21 -0
- package/dist/client/src/internal/values/codec.d.mts.map +1 -0
- package/dist/client/src/internal/values/normalize.d.mts +2 -0
- package/dist/client/src/node-opcua.d.mts +2 -0
- package/dist/compile/builtin-types.d.mts +10 -0
- package/dist/compile/builtin-types.d.mts.map +1 -0
- package/dist/compile/builtin-types.mjs +38 -0
- package/dist/compile/builtin-types.mjs.map +1 -0
- package/dist/compile/enums.d.mts +16 -0
- package/dist/compile/enums.d.mts.map +1 -0
- package/dist/compile/enums.mjs +81 -0
- package/dist/compile/enums.mjs.map +1 -0
- package/dist/compile/model.d.mts +10 -0
- package/dist/compile/model.d.mts.map +1 -0
- package/dist/compile/model.mjs +92 -0
- package/dist/compile/model.mjs.map +1 -0
- package/dist/compile/names.d.mts +17 -0
- package/dist/compile/names.d.mts.map +1 -0
- package/dist/compile/names.mjs +95 -0
- package/dist/compile/names.mjs.map +1 -0
- package/dist/compile/policy.d.mts +7 -0
- package/dist/compile/policy.d.mts.map +1 -0
- package/dist/compile/policy.mjs +6 -0
- package/dist/compile/policy.mjs.map +1 -0
- package/dist/compile/structures.d.mts +16 -0
- package/dist/compile/structures.d.mts.map +1 -0
- package/dist/compile/structures.mjs +220 -0
- package/dist/compile/structures.mjs.map +1 -0
- package/dist/compile/type-graph.d.mts +20 -0
- package/dist/compile/type-graph.d.mts.map +1 -0
- package/dist/compile/type-graph.mjs +78 -0
- package/dist/compile/type-graph.mjs.map +1 -0
- package/dist/compile/variables.d.mts +16 -0
- package/dist/compile/variables.d.mts.map +1 -0
- package/dist/compile/variables.mjs +110 -0
- package/dist/compile/variables.mjs.map +1 -0
- package/dist/compile.d.mts +9 -0
- package/dist/compile.d.mts.map +1 -0
- package/dist/compile.mjs +29 -0
- package/dist/compile.mjs.map +1 -0
- package/dist/config.d.mts +12 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +158 -0
- package/dist/config.mjs.map +1 -0
- package/dist/diagnostics.d.mts +16 -0
- package/dist/diagnostics.d.mts.map +1 -0
- package/dist/diagnostics.mjs +45 -0
- package/dist/diagnostics.mjs.map +1 -0
- package/dist/discover.d.mts +23 -0
- package/dist/discover.d.mts.map +1 -0
- package/dist/discover.mjs +356 -0
- package/dist/discover.mjs.map +1 -0
- package/dist/emit.d.mts +8 -0
- package/dist/emit.d.mts.map +1 -0
- package/dist/emit.mjs +233 -0
- package/dist/emit.mjs.map +1 -0
- package/dist/errors.d.mts +27 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +18 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/generate.d.mts +19 -0
- package/dist/generate.d.mts.map +1 -0
- package/dist/generate.mjs +172 -0
- package/dist/generate.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/internal/types.d.mts +192 -0
- package/dist/internal/types.d.mts.map +1 -0
- package/dist/internal/types.mjs +1 -0
- package/dist/issue-codes.d.mts +119 -0
- package/dist/issue-codes.d.mts.map +1 -0
- package/dist/issue-codes.mjs +44 -0
- package/dist/issue-codes.mjs.map +1 -0
- package/dist/types.d.mts +57 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/types.mjs +1 -0
- package/package.json +58 -0
package/dist/config.mjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { codegenError, invalidConfig } from "./errors.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { unrun } from "unrun";
|
|
5
|
+
|
|
6
|
+
//#region src/config.ts
|
|
7
|
+
const defineConfig = (config) => config;
|
|
8
|
+
const loadConfig = (path = "effect-opcua.codegen.ts") => Effect.gen(function* () {
|
|
9
|
+
const resolved = resolve(path);
|
|
10
|
+
const module = (yield* Effect.tryPromise({
|
|
11
|
+
try: () => unrun({ path: resolved }),
|
|
12
|
+
catch: (cause) => codegenError({
|
|
13
|
+
_tag: "Config",
|
|
14
|
+
path: resolved
|
|
15
|
+
}, [{
|
|
16
|
+
severity: "error",
|
|
17
|
+
code: "config.loadFailed",
|
|
18
|
+
message: `Failed to load config at ${resolved}`,
|
|
19
|
+
file: resolved,
|
|
20
|
+
cause
|
|
21
|
+
}])
|
|
22
|
+
})).module;
|
|
23
|
+
const exportedConfig = isRecord(module) && "default" in module ? module.default : module;
|
|
24
|
+
if (exportedConfig === void 0) return yield* Effect.fail(codegenError({
|
|
25
|
+
_tag: "Config",
|
|
26
|
+
path: resolved
|
|
27
|
+
}, [{
|
|
28
|
+
severity: "error",
|
|
29
|
+
code: "config.loadFailed",
|
|
30
|
+
message: "Config module must have a default export",
|
|
31
|
+
file: resolved
|
|
32
|
+
}]));
|
|
33
|
+
return yield* normalizeConfig(exportedConfig);
|
|
34
|
+
});
|
|
35
|
+
const normalizeConfig = (config) => Effect.gen(function* () {
|
|
36
|
+
if (!isRecord(config)) return yield* Effect.fail(invalidConfig("Config must be an object"));
|
|
37
|
+
const unsupported = unsupportedKeys(config, [
|
|
38
|
+
"endpointUrl",
|
|
39
|
+
"clientOptions",
|
|
40
|
+
"userIdentity",
|
|
41
|
+
"outputDir",
|
|
42
|
+
"roots",
|
|
43
|
+
"exclude",
|
|
44
|
+
"discovery",
|
|
45
|
+
"diagnostics"
|
|
46
|
+
]);
|
|
47
|
+
if (unsupported.length > 0) return yield* Effect.fail(invalidConfig(`Unsupported config keys: ${unsupported.join(", ")}`));
|
|
48
|
+
const endpointUrl = config.endpointUrl;
|
|
49
|
+
if (typeof endpointUrl !== "string" || endpointUrl.trim() === "") return yield* Effect.fail(invalidConfig("Missing endpointUrl"));
|
|
50
|
+
const outputDir = config.outputDir;
|
|
51
|
+
if (typeof outputDir !== "string" || outputDir.trim() === "") return yield* Effect.fail(invalidConfig("Missing outputDir"));
|
|
52
|
+
if (!Array.isArray(config.roots) || config.roots.length === 0) return yield* Effect.fail(invalidConfig("Config roots must not be empty"));
|
|
53
|
+
const roots = yield* Effect.forEach(config.roots, normalizeRoot);
|
|
54
|
+
const excludeInput = config.exclude ?? [];
|
|
55
|
+
if (!Array.isArray(excludeInput)) return yield* Effect.fail(invalidConfig("exclude must be an array"));
|
|
56
|
+
const exclude = yield* Effect.forEach(excludeInput, normalizeExcludeRule);
|
|
57
|
+
const diagnostics = normalizeDiagnostics(config.diagnostics);
|
|
58
|
+
if (diagnostics instanceof Error) return yield* Effect.fail(invalidConfig(diagnostics.message));
|
|
59
|
+
const discovery = normalizeDiscovery(config.discovery);
|
|
60
|
+
if (discovery instanceof Error) return yield* Effect.fail(invalidConfig(discovery.message));
|
|
61
|
+
return {
|
|
62
|
+
endpointUrl,
|
|
63
|
+
clientOptions: config.clientOptions,
|
|
64
|
+
userIdentity: config.userIdentity,
|
|
65
|
+
outputDir,
|
|
66
|
+
roots,
|
|
67
|
+
exclude,
|
|
68
|
+
discovery,
|
|
69
|
+
diagnostics
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const normalizeRoot = (root) => Effect.gen(function* () {
|
|
73
|
+
if (!isRecord(root)) return yield* Effect.fail(invalidConfig("Each root must be an object"));
|
|
74
|
+
const unsupported = unsupportedKeys(root, [
|
|
75
|
+
"path",
|
|
76
|
+
"nodeId",
|
|
77
|
+
"exportPrefix"
|
|
78
|
+
]);
|
|
79
|
+
if (unsupported.length > 0) return yield* Effect.fail(invalidConfig(`Unsupported root keys: ${unsupported.join(", ")}`));
|
|
80
|
+
const value = root;
|
|
81
|
+
if ([value.path !== void 0, value.nodeId !== void 0].filter(Boolean).length !== 1) return yield* Effect.fail(invalidConfig("A root must specify exactly one of path or nodeId"));
|
|
82
|
+
if (value.exportPrefix !== void 0 && (typeof value.exportPrefix !== "string" || value.exportPrefix.trim() === "")) return yield* Effect.fail(invalidConfig("root.exportPrefix must be a non-empty string"));
|
|
83
|
+
if (value.path !== void 0) {
|
|
84
|
+
const path = normalizePath(value.path, "root.path");
|
|
85
|
+
if (path instanceof Error) return yield* Effect.fail(invalidConfig(path.message));
|
|
86
|
+
return {
|
|
87
|
+
path,
|
|
88
|
+
exportPrefix: value.exportPrefix
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (typeof value.nodeId !== "string" || value.nodeId.trim() === "") return yield* Effect.fail(invalidConfig("root.nodeId must be a non-empty string"));
|
|
92
|
+
if (typeof value.exportPrefix !== "string" || value.exportPrefix.trim() === "") return yield* Effect.fail(invalidConfig("root.exportPrefix is required for nodeId roots"));
|
|
93
|
+
return {
|
|
94
|
+
nodeId: value.nodeId,
|
|
95
|
+
exportPrefix: value.exportPrefix
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
const normalizeExcludeRule = (rule) => Effect.gen(function* () {
|
|
99
|
+
if (!isRecord(rule)) return yield* Effect.fail(invalidConfig("Each exclude rule must be an object"));
|
|
100
|
+
const unsupported = unsupportedKeys(rule, ["path", "mode"]);
|
|
101
|
+
if (unsupported.length > 0) return yield* Effect.fail(invalidConfig(`Unsupported exclude keys: ${unsupported.join(", ")}`));
|
|
102
|
+
const value = rule;
|
|
103
|
+
if (value.mode !== "prune" && value.mode !== "omit") return yield* Effect.fail(invalidConfig("Each exclude rule must specify mode \"prune\" or \"omit\""));
|
|
104
|
+
if (value.path === void 0) return yield* Effect.fail(invalidConfig("Each exclude rule must specify path"));
|
|
105
|
+
const path = normalizeExcludePath(value.path);
|
|
106
|
+
if (path instanceof Error) return yield* Effect.fail(invalidConfig(path.message));
|
|
107
|
+
return path.some((segment) => segment instanceof RegExp || segment === "**") ? {
|
|
108
|
+
_tag: "PathPattern",
|
|
109
|
+
pathPattern: path,
|
|
110
|
+
mode: value.mode
|
|
111
|
+
} : {
|
|
112
|
+
_tag: "Path",
|
|
113
|
+
path,
|
|
114
|
+
mode: value.mode
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
const normalizeDiscovery = (value) => {
|
|
118
|
+
if (value === void 0) return { onBrowseFailure: "warn" };
|
|
119
|
+
if (!isRecord(value)) return /* @__PURE__ */ new Error("discovery must be an object");
|
|
120
|
+
const unsupported = unsupportedKeys(value, ["onBrowseFailure"]);
|
|
121
|
+
if (unsupported.length > 0) return /* @__PURE__ */ new Error(`Unsupported discovery keys: ${unsupported.join(", ")}`);
|
|
122
|
+
const onBrowseFailure = value.onBrowseFailure ?? "warn";
|
|
123
|
+
if (onBrowseFailure !== "warn" && onBrowseFailure !== "fail") return /* @__PURE__ */ new Error("discovery.onBrowseFailure must be \"warn\" or \"fail\"");
|
|
124
|
+
return { onBrowseFailure };
|
|
125
|
+
};
|
|
126
|
+
const normalizeDiagnostics = (value) => {
|
|
127
|
+
if (value === void 0) return {
|
|
128
|
+
warningsAsErrors: false,
|
|
129
|
+
typeFallback: "fail"
|
|
130
|
+
};
|
|
131
|
+
if (!isRecord(value)) return /* @__PURE__ */ new Error("diagnostics must be an object");
|
|
132
|
+
const unsupported = unsupportedKeys(value, ["warningsAsErrors", "typeFallback"]);
|
|
133
|
+
if (unsupported.length > 0) return /* @__PURE__ */ new Error(`Unsupported diagnostics keys: ${unsupported.join(", ")}`);
|
|
134
|
+
const warningsAsErrors = value.warningsAsErrors ?? false;
|
|
135
|
+
if (typeof warningsAsErrors !== "boolean") return /* @__PURE__ */ new Error("diagnostics.warningsAsErrors must be a boolean");
|
|
136
|
+
const typeFallback = value.typeFallback ?? "fail";
|
|
137
|
+
if (typeFallback !== "fail" && typeFallback !== "dynamic") return /* @__PURE__ */ new Error("diagnostics.typeFallback must be \"fail\" or \"dynamic\"");
|
|
138
|
+
return {
|
|
139
|
+
warningsAsErrors,
|
|
140
|
+
typeFallback
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
const normalizePath = (value, label) => {
|
|
144
|
+
if (!Array.isArray(value) || value.length === 0) return /* @__PURE__ */ new Error(`${label} must be a non-empty segment array`);
|
|
145
|
+
if (!value.every((segment) => typeof segment === "string" && segment.trim() !== "")) return /* @__PURE__ */ new Error(`${label} segments must be non-empty strings`);
|
|
146
|
+
return [...value];
|
|
147
|
+
};
|
|
148
|
+
const normalizeExcludePath = (value) => {
|
|
149
|
+
if (!Array.isArray(value) || value.length === 0) return /* @__PURE__ */ new Error("exclude.path must be a non-empty segment array");
|
|
150
|
+
if (!value.every((segment) => segment instanceof RegExp || typeof segment === "string" && segment.trim() !== "")) return /* @__PURE__ */ new Error("exclude.path segments must be non-empty strings, RegExp, or \"**\"");
|
|
151
|
+
return [...value];
|
|
152
|
+
};
|
|
153
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
154
|
+
const unsupportedKeys = (record, supported) => Object.keys(record).filter((key) => !supported.includes(key));
|
|
155
|
+
|
|
156
|
+
//#endregion
|
|
157
|
+
export { defineConfig, loadConfig, normalizeConfig };
|
|
158
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { Effect } from \"effect\";\nimport { unrun } from \"unrun\";\n\nimport { codegenError, invalidConfig } from \"./errors.js\";\nimport type {\n CodegenConfig,\n ExcludeRuleConfig,\n PathPatternSegment,\n RootConfig,\n} from \"./types.js\";\nimport type {\n NormalizedCodegenConfig,\n NormalizedExcludeRule,\n NormalizedRootConfig,\n} from \"./internal/types.js\";\n\nexport const defineConfig = (config: CodegenConfig): CodegenConfig => config;\n\nexport const loadConfig = (\n path = \"effect-opcua.codegen.ts\",\n): Effect.Effect<NormalizedCodegenConfig, import(\"./errors.js\").CodegenError> =>\n Effect.gen(function* () {\n const resolved = resolve(path);\n const loaded = yield* Effect.tryPromise({\n try: () => unrun({ path: resolved }),\n catch: (cause) =>\n codegenError({ _tag: \"Config\", path: resolved }, [\n {\n severity: \"error\",\n code: \"config.loadFailed\",\n message: `Failed to load config at ${resolved}`,\n file: resolved,\n cause,\n },\n ]),\n });\n const module = loaded.module as unknown;\n const exportedConfig =\n isRecord(module) && \"default\" in module ? module.default : module;\n if (exportedConfig === undefined) {\n return yield* Effect.fail(\n codegenError({ _tag: \"Config\", path: resolved }, [\n {\n severity: \"error\",\n code: \"config.loadFailed\",\n message: \"Config module must have a default export\",\n file: resolved,\n },\n ]),\n );\n }\n return yield* normalizeConfig(exportedConfig);\n });\n\nexport const normalizeConfig = (\n config: unknown,\n): Effect.Effect<NormalizedCodegenConfig, import(\"./errors.js\").CodegenError> =>\n Effect.gen(function* () {\n if (!isRecord(config)) {\n return yield* Effect.fail(invalidConfig(\"Config must be an object\"));\n }\n const unsupported = unsupportedKeys(config, [\n \"endpointUrl\",\n \"clientOptions\",\n \"userIdentity\",\n \"outputDir\",\n \"roots\",\n \"exclude\",\n \"discovery\",\n \"diagnostics\",\n ]);\n if (unsupported.length > 0) {\n return yield* Effect.fail(\n invalidConfig(`Unsupported config keys: ${unsupported.join(\", \")}`),\n );\n }\n\n const endpointUrl = config.endpointUrl;\n if (typeof endpointUrl !== \"string\" || endpointUrl.trim() === \"\") {\n return yield* Effect.fail(invalidConfig(\"Missing endpointUrl\"));\n }\n\n const outputDir = config.outputDir;\n if (typeof outputDir !== \"string\" || outputDir.trim() === \"\") {\n return yield* Effect.fail(invalidConfig(\"Missing outputDir\"));\n }\n\n if (!Array.isArray(config.roots) || config.roots.length === 0) {\n return yield* Effect.fail(\n invalidConfig(\"Config roots must not be empty\"),\n );\n }\n const roots = yield* Effect.forEach(config.roots, normalizeRoot);\n\n const excludeInput = config.exclude ?? [];\n if (!Array.isArray(excludeInput)) {\n return yield* Effect.fail(invalidConfig(\"exclude must be an array\"));\n }\n const exclude = yield* Effect.forEach(excludeInput, normalizeExcludeRule);\n const diagnostics = normalizeDiagnostics(config.diagnostics);\n if (diagnostics instanceof Error) {\n return yield* Effect.fail(invalidConfig(diagnostics.message));\n }\n const discovery = normalizeDiscovery(config.discovery);\n if (discovery instanceof Error) {\n return yield* Effect.fail(invalidConfig(discovery.message));\n }\n\n return {\n endpointUrl,\n clientOptions: config.clientOptions as\n | NormalizedCodegenConfig[\"clientOptions\"]\n | undefined,\n userIdentity: config.userIdentity as\n | NormalizedCodegenConfig[\"userIdentity\"]\n | undefined,\n outputDir,\n roots,\n exclude,\n discovery,\n diagnostics,\n };\n });\n\nconst normalizeRoot = (\n root: unknown,\n): Effect.Effect<NormalizedRootConfig, import(\"./errors.js\").CodegenError> =>\n Effect.gen(function* () {\n if (!isRecord(root)) {\n return yield* Effect.fail(invalidConfig(\"Each root must be an object\"));\n }\n const unsupported = unsupportedKeys(root, [\n \"path\",\n \"nodeId\",\n \"exportPrefix\",\n ]);\n if (unsupported.length > 0) {\n return yield* Effect.fail(\n invalidConfig(`Unsupported root keys: ${unsupported.join(\", \")}`),\n );\n }\n const value = root as RootConfig;\n const descriptors = [\n value.path !== undefined,\n value.nodeId !== undefined,\n ].filter(Boolean).length;\n if (descriptors !== 1) {\n return yield* Effect.fail(\n invalidConfig(\"A root must specify exactly one of path or nodeId\"),\n );\n }\n if (\n value.exportPrefix !== undefined &&\n (typeof value.exportPrefix !== \"string\" ||\n value.exportPrefix.trim() === \"\")\n ) {\n return yield* Effect.fail(\n invalidConfig(\"root.exportPrefix must be a non-empty string\"),\n );\n }\n if (value.path !== undefined) {\n const path = normalizePath(value.path, \"root.path\");\n if (path instanceof Error)\n return yield* Effect.fail(invalidConfig(path.message));\n return { path, exportPrefix: value.exportPrefix };\n }\n if (typeof value.nodeId !== \"string\" || value.nodeId.trim() === \"\") {\n return yield* Effect.fail(\n invalidConfig(\"root.nodeId must be a non-empty string\"),\n );\n }\n if (\n typeof value.exportPrefix !== \"string\" ||\n value.exportPrefix.trim() === \"\"\n ) {\n return yield* Effect.fail(\n invalidConfig(\"root.exportPrefix is required for nodeId roots\"),\n );\n }\n return { nodeId: value.nodeId, exportPrefix: value.exportPrefix };\n });\n\nconst normalizeExcludeRule = (\n rule: unknown,\n): Effect.Effect<NormalizedExcludeRule, import(\"./errors.js\").CodegenError> =>\n Effect.gen(function* () {\n if (!isRecord(rule)) {\n return yield* Effect.fail(\n invalidConfig(\"Each exclude rule must be an object\"),\n );\n }\n const unsupported = unsupportedKeys(rule, [\"path\", \"mode\"]);\n if (unsupported.length > 0) {\n return yield* Effect.fail(\n invalidConfig(`Unsupported exclude keys: ${unsupported.join(\", \")}`),\n );\n }\n const value = rule as ExcludeRuleConfig;\n if (value.mode !== \"prune\" && value.mode !== \"omit\") {\n return yield* Effect.fail(\n invalidConfig('Each exclude rule must specify mode \"prune\" or \"omit\"'),\n );\n }\n if (value.path === undefined) {\n return yield* Effect.fail(\n invalidConfig(\"Each exclude rule must specify path\"),\n );\n }\n const path = normalizeExcludePath(value.path);\n if (path instanceof Error)\n return yield* Effect.fail(invalidConfig(path.message));\n return path.some((segment) => segment instanceof RegExp || segment === \"**\")\n ? { _tag: \"PathPattern\", pathPattern: path, mode: value.mode }\n : { _tag: \"Path\", path: path as readonly string[], mode: value.mode };\n });\n\nconst normalizeDiscovery = (\n value: unknown,\n): NormalizedCodegenConfig[\"discovery\"] | Error => {\n if (value === undefined) {\n return {\n onBrowseFailure: \"warn\",\n };\n }\n if (!isRecord(value)) return new Error(\"discovery must be an object\");\n const unsupported = unsupportedKeys(value, [\"onBrowseFailure\"]);\n if (unsupported.length > 0) {\n return new Error(`Unsupported discovery keys: ${unsupported.join(\", \")}`);\n }\n const onBrowseFailure = value.onBrowseFailure ?? \"warn\";\n if (onBrowseFailure !== \"warn\" && onBrowseFailure !== \"fail\") {\n return new Error('discovery.onBrowseFailure must be \"warn\" or \"fail\"');\n }\n return { onBrowseFailure };\n};\n\nconst normalizeDiagnostics = (\n value: unknown,\n): NormalizedCodegenConfig[\"diagnostics\"] | Error => {\n if (value === undefined) {\n return { warningsAsErrors: false, typeFallback: \"fail\" };\n }\n if (!isRecord(value)) return new Error(\"diagnostics must be an object\");\n const unsupported = unsupportedKeys(value, [\n \"warningsAsErrors\",\n \"typeFallback\",\n ]);\n if (unsupported.length > 0) {\n return new Error(`Unsupported diagnostics keys: ${unsupported.join(\", \")}`);\n }\n const warningsAsErrors = value.warningsAsErrors ?? false;\n if (typeof warningsAsErrors !== \"boolean\") {\n return new Error(\"diagnostics.warningsAsErrors must be a boolean\");\n }\n const typeFallback = value.typeFallback ?? \"fail\";\n if (typeFallback !== \"fail\" && typeFallback !== \"dynamic\") {\n return new Error('diagnostics.typeFallback must be \"fail\" or \"dynamic\"');\n }\n return { warningsAsErrors, typeFallback };\n};\n\nconst normalizePath = (\n value: unknown,\n label: string,\n): readonly string[] | Error => {\n if (!Array.isArray(value) || value.length === 0) {\n return new Error(`${label} must be a non-empty segment array`);\n }\n if (\n !value.every(\n (segment) => typeof segment === \"string\" && segment.trim() !== \"\",\n )\n ) {\n return new Error(`${label} segments must be non-empty strings`);\n }\n return [...value];\n};\n\nconst normalizeExcludePath = (\n value: unknown,\n): readonly PathPatternSegment[] | Error => {\n if (!Array.isArray(value) || value.length === 0) {\n return new Error(\"exclude.path must be a non-empty segment array\");\n }\n if (\n !value.every(\n (segment) =>\n segment instanceof RegExp ||\n (typeof segment === \"string\" && segment.trim() !== \"\"),\n )\n ) {\n return new Error(\n 'exclude.path segments must be non-empty strings, RegExp, or \"**\"',\n );\n }\n return [...value];\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst unsupportedKeys = (\n record: Record<string, unknown>,\n supported: readonly string[],\n) => Object.keys(record).filter((key) => !supported.includes(key));\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CodegenIssueCode } from "./issue-codes.mjs";
|
|
2
|
+
import { CodegenIssue } from "./types.mjs";
|
|
3
|
+
import { CodegenError } from "./errors.mjs";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src/diagnostics.d.ts
|
|
7
|
+
declare const issue: (code: CodegenIssueCode, input: Omit<CodegenIssue, "severity" | "code"> & {
|
|
8
|
+
readonly severity?: CodegenIssue["severity"];
|
|
9
|
+
}) => CodegenIssue;
|
|
10
|
+
declare const errorIssue: (code: CodegenIssueCode, input: Omit<CodegenIssue, "severity" | "code">) => CodegenIssue;
|
|
11
|
+
declare const sortIssues: (issues: readonly CodegenIssue[]) => readonly CodegenIssue[];
|
|
12
|
+
declare const enforceIssuePolicy: (warningsAsErrors: boolean, issues: readonly CodegenIssue[]) => Effect.Effect<never, CodegenError, never> | Effect.Effect<readonly CodegenIssue[], never, never>;
|
|
13
|
+
declare const displayPath: (path: readonly string[] | undefined) => string;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { displayPath, enforceIssuePolicy, errorIssue, issue, sortIssues };
|
|
16
|
+
//# sourceMappingURL=diagnostics.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.mts","names":[],"sources":["../src/diagnostics.ts"],"sourcesContent":[],"mappings":";;;;;;cAMa,cACL,yBACC,KAAK;sBACU;MAErB;AALU,cAcA,UAFZ,EAAA,CAAA,IAAA,EAGO,gBAHP,EAAA,KAAA,EAIQ,IAJR,CAIa,YAJb,EAAA,UAAA,GAAA,MAAA,CAAA,EAAA,GAKE,YALF;AAXO,cAkBK,UAlBL,EAAA,CAAA,MAAA,EAAA,SAmBW,YAnBX,EAAA,EAAA,GAAA,SAoBI,YApBJ,EAAA;AACM,cAmCD,kBAnCC,EAAA,CAAA,gBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,SAqCK,YArCL,EAAA,EAAA,GAqCmB,MAAA,CAAA,MArCnB,CAAA,KAAA,EAqCiB,YAAA,EArCjB,KAAA,CAAA,GAqCmB,MAAA,CAAA,MArCnB,CAAA,SAqCmB,YArCnB,EAAA,EAAA,KAAA,EAAA,KAAA,CAAA;AAAL,cAmDI,WAnDJ,EAAA,CAAA,IAAA,EAAA,SAAA,MAAA,EAAA,GAAA,SAAA,EAAA,GAAA,MAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { codegenError } from "./errors.mjs";
|
|
2
|
+
import { issueDefinitions } from "./issue-codes.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
|
|
5
|
+
//#region src/diagnostics.ts
|
|
6
|
+
const issue = (code, input) => {
|
|
7
|
+
const { severity, ...rest } = input;
|
|
8
|
+
return {
|
|
9
|
+
severity: severity ?? issueDefinitions[code].severity,
|
|
10
|
+
code,
|
|
11
|
+
...rest
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const errorIssue = (code, input) => issue(code, {
|
|
15
|
+
...input,
|
|
16
|
+
severity: "error"
|
|
17
|
+
});
|
|
18
|
+
const sortIssues = (issues) => [...issues].sort((left, right) => [
|
|
19
|
+
severityRank(left.severity) - severityRank(right.severity),
|
|
20
|
+
displayPath(left.path).localeCompare(displayPath(right.path)),
|
|
21
|
+
displayPath(left.generatedPath).localeCompare(displayPath(right.generatedPath)),
|
|
22
|
+
(left.file ?? "").localeCompare(right.file ?? ""),
|
|
23
|
+
left.code.localeCompare(right.code),
|
|
24
|
+
(left.nodeId ?? "").localeCompare(right.nodeId ?? ""),
|
|
25
|
+
left.message.localeCompare(right.message)
|
|
26
|
+
].find((value) => value !== 0) ?? 0);
|
|
27
|
+
const enforceIssuePolicy = (warningsAsErrors, issues) => {
|
|
28
|
+
const promoted = warningsAsErrors ? issues.map((item) => item.severity === "warning" ? {
|
|
29
|
+
...item,
|
|
30
|
+
severity: "error"
|
|
31
|
+
} : item) : issues;
|
|
32
|
+
return promoted.some((item) => item.severity === "error") ? Effect.fail(codegenError({ _tag: "Compile" }, promoted)) : Effect.succeed(sortIssues(promoted));
|
|
33
|
+
};
|
|
34
|
+
const displayPath = (path) => path?.join(" / ") ?? "";
|
|
35
|
+
const severityRank = (severity) => {
|
|
36
|
+
switch (severity) {
|
|
37
|
+
case "error": return 0;
|
|
38
|
+
case "warning": return 1;
|
|
39
|
+
case "info": return 2;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { displayPath, enforceIssuePolicy, errorIssue, issue, sortIssues };
|
|
45
|
+
//# sourceMappingURL=diagnostics.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.mjs","names":[],"sources":["../src/diagnostics.ts"],"sourcesContent":["import { Effect } from \"effect\";\n\nimport { codegenError } from \"./errors.js\";\nimport { issueDefinitions, type CodegenIssueCode } from \"./issue-codes.js\";\nimport type { CodegenIssue } from \"./types.js\";\n\nexport const issue = (\n code: CodegenIssueCode,\n input: Omit<CodegenIssue, \"severity\" | \"code\"> & {\n readonly severity?: CodegenIssue[\"severity\"];\n },\n): CodegenIssue => {\n const { severity, ...rest } = input;\n return {\n severity: severity ?? issueDefinitions[code].severity,\n code,\n ...rest,\n };\n};\n\nexport const errorIssue = (\n code: CodegenIssueCode,\n input: Omit<CodegenIssue, \"severity\" | \"code\">,\n): CodegenIssue => issue(code, { ...input, severity: \"error\" });\n\nexport const sortIssues = (\n issues: readonly CodegenIssue[],\n): readonly CodegenIssue[] =>\n [...issues].sort(\n (left, right) =>\n [\n severityRank(left.severity) - severityRank(right.severity),\n displayPath(left.path).localeCompare(displayPath(right.path)),\n displayPath(left.generatedPath).localeCompare(\n displayPath(right.generatedPath),\n ),\n (left.file ?? \"\").localeCompare(right.file ?? \"\"),\n left.code.localeCompare(right.code),\n (left.nodeId ?? \"\").localeCompare(right.nodeId ?? \"\"),\n left.message.localeCompare(right.message),\n ].find((value) => value !== 0) ?? 0,\n );\n\nexport const enforceIssuePolicy = (\n warningsAsErrors: boolean,\n issues: readonly CodegenIssue[],\n) => {\n const promoted = warningsAsErrors\n ? issues.map((item) =>\n item.severity === \"warning\"\n ? { ...item, severity: \"error\" as const }\n : item,\n )\n : issues;\n return promoted.some((item) => item.severity === \"error\")\n ? Effect.fail(codegenError({ _tag: \"Compile\" }, promoted))\n : Effect.succeed(sortIssues(promoted));\n};\n\nexport const displayPath = (path: readonly string[] | undefined) =>\n path?.join(\" / \") ?? \"\";\n\nconst severityRank = (severity: CodegenIssue[\"severity\"]) => {\n switch (severity) {\n case \"error\":\n return 0;\n case \"warning\":\n return 1;\n case \"info\":\n return 2;\n }\n};\n"],"mappings":";;;;;AAMA,MAAa,SACX,MACA,UAGiB;CACjB,MAAM,EAAE,UAAU,GAAG,SAAS;AAC9B,QAAO;EACL,UAAU,YAAY,iBAAiB,MAAM;EAC7C;EACA,GAAG;EACJ;;AAGH,MAAa,cACX,MACA,UACiB,MAAM,MAAM;CAAE,GAAG;CAAO,UAAU;CAAS,CAAC;AAE/D,MAAa,cACX,WAEA,CAAC,GAAG,OAAO,CAAC,MACT,MAAM,UACL;CACE,aAAa,KAAK,SAAS,GAAG,aAAa,MAAM,SAAS;CAC1D,YAAY,KAAK,KAAK,CAAC,cAAc,YAAY,MAAM,KAAK,CAAC;CAC7D,YAAY,KAAK,cAAc,CAAC,cAC9B,YAAY,MAAM,cAAc,CACjC;EACA,KAAK,QAAQ,IAAI,cAAc,MAAM,QAAQ,GAAG;CACjD,KAAK,KAAK,cAAc,MAAM,KAAK;EAClC,KAAK,UAAU,IAAI,cAAc,MAAM,UAAU,GAAG;CACrD,KAAK,QAAQ,cAAc,MAAM,QAAQ;CAC1C,CAAC,MAAM,UAAU,UAAU,EAAE,IAAI,EACrC;AAEH,MAAa,sBACX,kBACA,WACG;CACH,MAAM,WAAW,mBACb,OAAO,KAAK,SACV,KAAK,aAAa,YACd;EAAE,GAAG;EAAM,UAAU;EAAkB,GACvC,KACL,GACD;AACJ,QAAO,SAAS,MAAM,SAAS,KAAK,aAAa,QAAQ,GACrD,OAAO,KAAK,aAAa,EAAE,MAAM,WAAW,EAAE,SAAS,CAAC,GACxD,OAAO,QAAQ,WAAW,SAAS,CAAC;;AAG1C,MAAa,eAAe,SAC1B,MAAM,KAAK,MAAM,IAAI;AAEvB,MAAM,gBAAgB,aAAuC;AAC3D,SAAQ,UAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,OACH,QAAO"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { CodegenError } from "./errors.mjs";
|
|
2
|
+
import { OpcuaError as OpcuaError$1 } from "./client/src/OpcuaError.mjs";
|
|
3
|
+
import { Session } from "./client/src/OpcuaSession.mjs";
|
|
4
|
+
import "./client/src/index.mjs";
|
|
5
|
+
import { DiscoveryModel, NormalizedCodegenConfig, NormalizedExcludeRule } from "./internal/types.mjs";
|
|
6
|
+
import { Context, Effect } from "effect";
|
|
7
|
+
|
|
8
|
+
//#region src/discover.d.ts
|
|
9
|
+
type OpcuaError = OpcuaError$1;
|
|
10
|
+
type MetadataTargetReference = {
|
|
11
|
+
readonly nodeId: {
|
|
12
|
+
readonly text: string;
|
|
13
|
+
};
|
|
14
|
+
readonly browseName?: {
|
|
15
|
+
readonly name?: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
type OpcuaSessionRequirement = Context.Service.Identifier<typeof Session>;
|
|
19
|
+
declare const discover: (config: NormalizedCodegenConfig) => Effect.Effect<DiscoveryModel, CodegenError | OpcuaError, OpcuaSessionRequirement>;
|
|
20
|
+
declare const metadataTargetNodeIds: (children: readonly MetadataTargetReference[], exclude: readonly NormalizedExcludeRule[], parentPath: readonly string[]) => string[];
|
|
21
|
+
//#endregion
|
|
22
|
+
export { discover, metadataTargetNodeIds };
|
|
23
|
+
//# sourceMappingURL=discover.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.mts","names":[],"sources":["../src/discover.ts"],"sourcesContent":[],"mappings":";;;;;;;;KA0BK,UAAA,GAAa;KAYb,uBAAA;;;EAZA,CAAA;EAYA,SAAA,UAAA,CAAA,EAAA;IAOA,SAAA,IAAA,CAAA,EAAA,MAAuB;EAaf,CAAA;CACH;KAdL,uBAAA,GAA0B,OAAA,CAAQ,OAAA,CAAQ,UAgB7C,CAAA,OAfO,OAeP,CAAA;AAAc,cAHH,QAGG,EAAA,CAAA,MAAA,EAFN,uBAEM,EAAA,GADb,MAAA,CAAO,MACM,CAAd,cAAc,EAAA,YAAA,GACuB,UADvB,EAEd,uBAFc,CAAA;AACuB,cAsV1B,qBAtV0B,EAAA,CAAA,QAAA,EAAA,SAuVlB,uBAvVkB,EAAA,EAAA,OAAA,EAAA,SAwVnB,qBAxVmB,EAAA,EAAA,UAAA,EAAA,SAAA,MAAA,EAAA,EAAA,GAAA,MAAA,EAAA"}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { codegenError } from "./errors.mjs";
|
|
2
|
+
import { errorIssue, issue, sortIssues } from "./diagnostics.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { OpcuaSession } from "@effect-opcua/client";
|
|
5
|
+
|
|
6
|
+
//#region src/discover.ts
|
|
7
|
+
const objectsFolderNodeId = "i=85";
|
|
8
|
+
const allowedReferenceTypes = new Set([
|
|
9
|
+
"i=35",
|
|
10
|
+
"i=46",
|
|
11
|
+
"i=47",
|
|
12
|
+
"i=49"
|
|
13
|
+
]);
|
|
14
|
+
const metadataPropertyNames = new Set([
|
|
15
|
+
"InputArguments",
|
|
16
|
+
"OutputArguments",
|
|
17
|
+
"EnumStrings",
|
|
18
|
+
"EnumValues",
|
|
19
|
+
"DataTypeVersion",
|
|
20
|
+
"DictionaryFragment",
|
|
21
|
+
"NodeVersion"
|
|
22
|
+
]);
|
|
23
|
+
const discover = (config) => Effect.gen(function* () {
|
|
24
|
+
const session = yield* OpcuaSession.OpcuaSession;
|
|
25
|
+
const issues = [];
|
|
26
|
+
const references = [];
|
|
27
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
28
|
+
const roots = [];
|
|
29
|
+
for (let rootIndex = 0; rootIndex < config.roots.length; rootIndex++) {
|
|
30
|
+
const rootConfig = config.roots[rootIndex];
|
|
31
|
+
const root = yield* resolveRoot(session, rootConfig, rootIndex);
|
|
32
|
+
roots.push({
|
|
33
|
+
...root,
|
|
34
|
+
exportPrefix: rootConfig.exportPrefix
|
|
35
|
+
});
|
|
36
|
+
addOrUpdateNode(nodes, {
|
|
37
|
+
metadata: yield* session.readNodeMetadata(root.nodeId),
|
|
38
|
+
path: root.path,
|
|
39
|
+
parentNodeId: void 0,
|
|
40
|
+
rootIndex,
|
|
41
|
+
rootSegmentCount: root.path.length
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const queue = roots.map((root) => ({
|
|
45
|
+
nodeId: root.nodeId,
|
|
46
|
+
rootIndex: root.rootIndex,
|
|
47
|
+
rootSegmentCount: root.path.length,
|
|
48
|
+
path: root.path
|
|
49
|
+
}));
|
|
50
|
+
const visitedPaths = /* @__PURE__ */ new Set();
|
|
51
|
+
while (queue.length > 0) {
|
|
52
|
+
const current = queue.shift();
|
|
53
|
+
const visitKey = `${current.nodeId}\u0000${current.path.join("\0")}`;
|
|
54
|
+
if (visitedPaths.has(visitKey)) continue;
|
|
55
|
+
visitedPaths.add(visitKey);
|
|
56
|
+
const result = yield* session.browseChildren(current.nodeId);
|
|
57
|
+
if (result._tag === "NonGoodStatus") {
|
|
58
|
+
const browseIssue = issue("browse.failure", {
|
|
59
|
+
message: `Could not browse ${current.nodeId}`,
|
|
60
|
+
nodeId: current.nodeId,
|
|
61
|
+
path: current.path,
|
|
62
|
+
cause: result.status,
|
|
63
|
+
severity: config.discovery.onBrowseFailure === "fail" ? "error" : void 0
|
|
64
|
+
});
|
|
65
|
+
if (browseIssue.severity === "error") return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [browseIssue]));
|
|
66
|
+
issues.push(browseIssue);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const children = selectedChildReferences(result.references);
|
|
70
|
+
const duplicate = duplicateBrowseName(children);
|
|
71
|
+
if (duplicate) return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [errorIssue("browse.ambiguousPath", {
|
|
72
|
+
message: "Multiple children have the same BrowseName segment",
|
|
73
|
+
path: [...current.path, duplicate.name],
|
|
74
|
+
cause: { candidates: duplicate.candidates }
|
|
75
|
+
})]));
|
|
76
|
+
const metadataByNodeId = yield* readChildMetadata(session, children, config.exclude, current.path);
|
|
77
|
+
for (const child of children) {
|
|
78
|
+
const browseName = child.browseName?.name;
|
|
79
|
+
const targetNodeId = child.nodeId.text;
|
|
80
|
+
if (!browseName || !targetNodeId) continue;
|
|
81
|
+
const referenceType = normalizeNamespaceZeroNodeId(child.referenceTypeId ?? "");
|
|
82
|
+
if (referenceType === "i=46" && metadataPropertyNames.has(browseName)) continue;
|
|
83
|
+
const childPath = [...current.path, browseName];
|
|
84
|
+
references.push({
|
|
85
|
+
sourceNodeId: current.nodeId,
|
|
86
|
+
targetNodeId,
|
|
87
|
+
referenceType,
|
|
88
|
+
isForward: child.isForward ?? true,
|
|
89
|
+
browseName
|
|
90
|
+
});
|
|
91
|
+
const exclude = matchingExclude(config.exclude, childPath);
|
|
92
|
+
if (exclude?.mode === "prune") {
|
|
93
|
+
issues.push(issue("branch.pruned", {
|
|
94
|
+
message: `Pruned ${displayPath(childPath)}`,
|
|
95
|
+
path: childPath,
|
|
96
|
+
nodeId: targetNodeId
|
|
97
|
+
}));
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (exclude?.mode !== "omit") {
|
|
101
|
+
const metadata = metadataByNodeId.get(targetNodeId);
|
|
102
|
+
if (!metadata) continue;
|
|
103
|
+
addOrUpdateNode(nodes, {
|
|
104
|
+
metadata,
|
|
105
|
+
path: childPath,
|
|
106
|
+
parentNodeId: current.nodeId,
|
|
107
|
+
rootIndex: current.rootIndex,
|
|
108
|
+
rootSegmentCount: current.rootSegmentCount
|
|
109
|
+
});
|
|
110
|
+
} else issues.push(issue("node.omitted", {
|
|
111
|
+
message: `Omitted ${displayPath(childPath)}`,
|
|
112
|
+
path: childPath,
|
|
113
|
+
nodeId: targetNodeId
|
|
114
|
+
}));
|
|
115
|
+
if (shouldBrowseChildren(child.nodeClass)) queue.push({
|
|
116
|
+
nodeId: targetNodeId,
|
|
117
|
+
parentNodeId: current.nodeId,
|
|
118
|
+
rootIndex: current.rootIndex,
|
|
119
|
+
rootSegmentCount: current.rootSegmentCount,
|
|
120
|
+
path: childPath
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (const node of nodes.values()) if (node.allPaths.length > 1) issues.push(issue("node.multiPath", {
|
|
125
|
+
message: `Node ${node.nodeId} was reached through multiple paths`,
|
|
126
|
+
path: node.path,
|
|
127
|
+
nodeId: node.nodeId
|
|
128
|
+
}));
|
|
129
|
+
const finalizedNodes = finalizeNodes(nodes);
|
|
130
|
+
const dataTypeDefinitions = yield* discoverDataTypeDefinitions(session, finalizedNodes);
|
|
131
|
+
return {
|
|
132
|
+
roots,
|
|
133
|
+
nodes: finalizedNodes,
|
|
134
|
+
references: sortReferences(references),
|
|
135
|
+
dataTypeDefinitions,
|
|
136
|
+
issues: sortIssues(issues)
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
const resolveRoot = (session, root, rootIndex) => root.nodeId !== void 0 ? resolveNodeIdRoot(session, root, rootIndex) : resolvePathRoot(session, root, rootIndex);
|
|
140
|
+
const resolveNodeIdRoot = (session, root, rootIndex) => Effect.gen(function* () {
|
|
141
|
+
const browseName = (yield* session.readNodeMetadata(root.nodeId)).browseName ?? root.nodeId;
|
|
142
|
+
return {
|
|
143
|
+
rootIndex,
|
|
144
|
+
nodeId: root.nodeId,
|
|
145
|
+
path: [browseName],
|
|
146
|
+
exportPrefix: root.exportPrefix
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const resolvePathRoot = (session, root, rootIndex) => Effect.gen(function* () {
|
|
150
|
+
let currentNodeId = objectsFolderNodeId;
|
|
151
|
+
const resolvedSegments = [];
|
|
152
|
+
for (const segment of root.path) {
|
|
153
|
+
const result = yield* session.browseChildren(currentNodeId);
|
|
154
|
+
if (result._tag === "NonGoodStatus") return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [errorIssue("root.resolutionFailed", {
|
|
155
|
+
message: `Could not browse ${currentNodeId} while resolving ${displayPath(root.path)}`,
|
|
156
|
+
path: root.path,
|
|
157
|
+
nodeId: currentNodeId,
|
|
158
|
+
cause: result.status
|
|
159
|
+
})]));
|
|
160
|
+
const matches = selectedChildReferences(result.references).filter((reference) => reference.browseName?.name === segment);
|
|
161
|
+
if (matches.length === 0) return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [errorIssue("root.resolutionFailed", {
|
|
162
|
+
message: `Missing root path segment ${segment}`,
|
|
163
|
+
path: [...resolvedSegments, segment]
|
|
164
|
+
})]));
|
|
165
|
+
if (matches.length > 1) return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [errorIssue("browse.ambiguousPath", {
|
|
166
|
+
message: "Multiple children match the root path segment",
|
|
167
|
+
path: [...resolvedSegments, segment],
|
|
168
|
+
cause: { candidates: matches.map((match) => match.nodeId.text) }
|
|
169
|
+
})]));
|
|
170
|
+
currentNodeId = matches[0].nodeId.text;
|
|
171
|
+
resolvedSegments.push(segment);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
rootIndex,
|
|
175
|
+
nodeId: currentNodeId,
|
|
176
|
+
path: resolvedSegments,
|
|
177
|
+
exportPrefix: root.exportPrefix
|
|
178
|
+
};
|
|
179
|
+
});
|
|
180
|
+
const selectedChildReferences = (references) => references.filter((reference) => reference.isForward !== false).filter((reference) => allowedReferenceTypes.has(normalizeNamespaceZeroNodeId(reference.referenceTypeId ?? ""))).sort(referenceSort);
|
|
181
|
+
const duplicateBrowseName = (references) => {
|
|
182
|
+
const groups = /* @__PURE__ */ new Map();
|
|
183
|
+
for (const reference of references) {
|
|
184
|
+
const name = reference.browseName?.name;
|
|
185
|
+
if (!name) continue;
|
|
186
|
+
const group = groups.get(name) ?? [];
|
|
187
|
+
group.push(reference.nodeId.text);
|
|
188
|
+
groups.set(name, group);
|
|
189
|
+
}
|
|
190
|
+
for (const [name, candidates] of groups) if (candidates.length > 1) return {
|
|
191
|
+
name,
|
|
192
|
+
candidates
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
const referenceSort = (left, right) => (left.browseName?.name ?? "").localeCompare(right.browseName?.name ?? "") || (left.nodeClass ?? "").localeCompare(right.nodeClass ?? "") || left.nodeId.text.localeCompare(right.nodeId.text);
|
|
196
|
+
const matchingExclude = (rules, path) => rules.find((rule) => rule._tag === "Path" ? samePath(rule.path, path) : matchPathPattern(rule.pathPattern, path));
|
|
197
|
+
const readChildMetadata = (session, children, exclude, parentPath) => Effect.gen(function* () {
|
|
198
|
+
const metadataTargets = metadataTargetNodeIds(children, exclude, parentPath);
|
|
199
|
+
if (metadataTargets.length === 0) return /* @__PURE__ */ new Map();
|
|
200
|
+
const results = yield* session.readManyNodeMetadata(metadataTargets);
|
|
201
|
+
const failed = results.find((result) => result._tag === "Failure");
|
|
202
|
+
if (failed?._tag === "Failure") return yield* Effect.fail(codegenError({ _tag: "Discovery" }, [errorIssue("metadata.readFailed", {
|
|
203
|
+
message: `Could not read metadata for ${failed.nodeId}`,
|
|
204
|
+
nodeId: failed.nodeId,
|
|
205
|
+
cause: failed.reason
|
|
206
|
+
})]));
|
|
207
|
+
return new Map(results.flatMap((result) => result._tag === "Success" ? [[result.nodeId, result.metadata]] : []));
|
|
208
|
+
});
|
|
209
|
+
const metadataTargetNodeIds = (children, exclude, parentPath) => children.flatMap((child) => {
|
|
210
|
+
const browseName = child.browseName?.name;
|
|
211
|
+
const targetNodeId = child.nodeId.text;
|
|
212
|
+
if (!browseName || !targetNodeId) return [];
|
|
213
|
+
return matchingExclude(exclude, [...parentPath, browseName]) ? [] : [targetNodeId];
|
|
214
|
+
});
|
|
215
|
+
const matchPathPattern = (pattern, path) => {
|
|
216
|
+
const match = (patternIndex, pathIndex) => {
|
|
217
|
+
if (patternIndex === pattern.length) return pathIndex === path.length;
|
|
218
|
+
const segment = pattern[patternIndex];
|
|
219
|
+
if (segment === "**") return match(patternIndex + 1, pathIndex) || pathIndex < path.length && match(patternIndex, pathIndex + 1);
|
|
220
|
+
if (pathIndex >= path.length) return false;
|
|
221
|
+
return segmentMatches(segment, path[pathIndex]) && match(patternIndex + 1, pathIndex + 1);
|
|
222
|
+
};
|
|
223
|
+
return match(0, 0);
|
|
224
|
+
};
|
|
225
|
+
const segmentMatches = (pattern, segment) => pattern instanceof RegExp ? pattern.test(segment) : pattern === segment;
|
|
226
|
+
const samePath = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
227
|
+
const addOrUpdateNode = (nodes, input) => {
|
|
228
|
+
const key = input.metadata.nodeId;
|
|
229
|
+
const existing = nodes.get(key);
|
|
230
|
+
if (existing) {
|
|
231
|
+
const allPaths = uniquePaths([...existing.allPaths, input.path]);
|
|
232
|
+
if (comparePathRank({
|
|
233
|
+
rootIndex: input.rootIndex,
|
|
234
|
+
rootSegmentCount: input.rootSegmentCount,
|
|
235
|
+
path: input.path,
|
|
236
|
+
nodeId: key
|
|
237
|
+
}, {
|
|
238
|
+
rootIndex: existing.rootIndex ?? Number.MAX_SAFE_INTEGER,
|
|
239
|
+
rootSegmentCount: existing.rootSegmentCount,
|
|
240
|
+
path: existing.path,
|
|
241
|
+
nodeId: existing.nodeId
|
|
242
|
+
}) < 0) nodes.set(key, {
|
|
243
|
+
...existing,
|
|
244
|
+
path: input.path,
|
|
245
|
+
allPaths,
|
|
246
|
+
parentNodeId: input.parentNodeId,
|
|
247
|
+
rootIndex: input.rootIndex,
|
|
248
|
+
rootSegmentCount: input.rootSegmentCount
|
|
249
|
+
});
|
|
250
|
+
else nodes.set(key, {
|
|
251
|
+
...existing,
|
|
252
|
+
allPaths
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const nodeClass = normalizeNodeClass(input.metadata.nodeClass);
|
|
257
|
+
if (!nodeClass) return;
|
|
258
|
+
nodes.set(key, {
|
|
259
|
+
key,
|
|
260
|
+
nodeId: key,
|
|
261
|
+
parsedNodeId: parseNodeId(key, input.metadata.namespaceIndex),
|
|
262
|
+
namespaceIndex: input.metadata.namespaceIndex ?? parseNodeId(key).namespaceIndex,
|
|
263
|
+
namespaceUri: input.metadata.namespaceUri,
|
|
264
|
+
browseName: input.metadata.browseName ?? input.path.at(-1) ?? key,
|
|
265
|
+
browseNameNamespaceIndex: input.metadata.browseNameNamespaceIndex,
|
|
266
|
+
path: input.path,
|
|
267
|
+
allPaths: [input.path],
|
|
268
|
+
nodeClass,
|
|
269
|
+
displayName: input.metadata.displayName,
|
|
270
|
+
description: input.metadata.description,
|
|
271
|
+
dataTypeNodeId: input.metadata.dataType,
|
|
272
|
+
valueRank: input.metadata.valueRank,
|
|
273
|
+
arrayDimensions: input.metadata.arrayDimensions,
|
|
274
|
+
accessLevel: input.metadata.accessLevel,
|
|
275
|
+
userAccessLevel: input.metadata.userAccessLevel,
|
|
276
|
+
parentNodeId: input.parentNodeId,
|
|
277
|
+
rootIndex: input.rootIndex,
|
|
278
|
+
rootSegmentCount: input.rootSegmentCount
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
const normalizeNodeClass = (nodeClass) => {
|
|
282
|
+
switch (nodeClass) {
|
|
283
|
+
case "Object":
|
|
284
|
+
case "Variable":
|
|
285
|
+
case "Method":
|
|
286
|
+
case "DataType":
|
|
287
|
+
case "ObjectType":
|
|
288
|
+
case "VariableType":
|
|
289
|
+
case "ReferenceType": return nodeClass;
|
|
290
|
+
default: return;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
const shouldBrowseChildren = (nodeClass) => nodeClass === "Object";
|
|
294
|
+
const discoverDataTypeDefinitions = (session, nodes) => Effect.gen(function* () {
|
|
295
|
+
const seen = /* @__PURE__ */ new Set();
|
|
296
|
+
const queue = [...nodes.values()].filter((node) => node.nodeClass === "Variable").flatMap((node) => node.dataTypeNodeId ? [node.dataTypeNodeId] : []).filter((nodeId) => !isBuiltInDataType(nodeId));
|
|
297
|
+
const results = [];
|
|
298
|
+
while (queue.length > 0) {
|
|
299
|
+
const batch = [...new Set(queue.splice(0))].filter((nodeId) => !seen.has(nodeId)).sort();
|
|
300
|
+
if (batch.length === 0) continue;
|
|
301
|
+
batch.forEach((nodeId) => seen.add(nodeId));
|
|
302
|
+
const batchResults = yield* session.readManyDataTypeDefinitions(batch);
|
|
303
|
+
results.push(...batchResults);
|
|
304
|
+
for (const result of batchResults) {
|
|
305
|
+
if (result._tag !== "Success" || result.definition._tag !== "Structure") continue;
|
|
306
|
+
for (const field of result.definition.fields) if (field.dataTypeNodeId && !isBuiltInDataType(field.dataTypeNodeId) && !seen.has(field.dataTypeNodeId)) queue.push(field.dataTypeNodeId);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return results;
|
|
310
|
+
});
|
|
311
|
+
const isBuiltInDataType = (nodeId) => {
|
|
312
|
+
const normalized = normalizeNamespaceZeroNodeId(nodeId);
|
|
313
|
+
return /^i=\d+$/.test(normalized);
|
|
314
|
+
};
|
|
315
|
+
const parseNodeId = (nodeId, namespaceIndex) => {
|
|
316
|
+
const match = /^ns=(\d+);(.+)$/.exec(nodeId);
|
|
317
|
+
return {
|
|
318
|
+
namespaceIndex: namespaceIndex ?? (match ? Number(match[1]) : 0),
|
|
319
|
+
identifier: match?.[2] ?? nodeId
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
const comparePathRank = (left, right) => left.rootIndex - right.rootIndex || relativeLength(left) - relativeLength(right) || displayPath(left.path).localeCompare(displayPath(right.path)) || left.nodeId.localeCompare(right.nodeId);
|
|
323
|
+
const relativeLength = (item) => item.path.length - item.rootSegmentCount;
|
|
324
|
+
const finalizeNodes = (nodes) => {
|
|
325
|
+
const entries = [];
|
|
326
|
+
for (const [key, node] of nodes) entries.push([key, {
|
|
327
|
+
key: node.key,
|
|
328
|
+
nodeId: node.nodeId,
|
|
329
|
+
parsedNodeId: node.parsedNodeId,
|
|
330
|
+
namespaceIndex: node.namespaceIndex,
|
|
331
|
+
namespaceUri: node.namespaceUri,
|
|
332
|
+
browseName: node.browseName,
|
|
333
|
+
browseNameNamespaceIndex: node.browseNameNamespaceIndex,
|
|
334
|
+
path: node.path,
|
|
335
|
+
allPaths: uniquePaths(node.allPaths),
|
|
336
|
+
nodeClass: node.nodeClass,
|
|
337
|
+
displayName: node.displayName,
|
|
338
|
+
description: node.description,
|
|
339
|
+
dataTypeNodeId: node.dataTypeNodeId,
|
|
340
|
+
valueRank: node.valueRank,
|
|
341
|
+
arrayDimensions: node.arrayDimensions,
|
|
342
|
+
accessLevel: node.accessLevel,
|
|
343
|
+
userAccessLevel: node.userAccessLevel,
|
|
344
|
+
parentNodeId: node.parentNodeId,
|
|
345
|
+
rootIndex: node.rootIndex
|
|
346
|
+
}]);
|
|
347
|
+
return new Map(entries.sort((left, right) => displayPath(left[1].path).localeCompare(displayPath(right[1].path))));
|
|
348
|
+
};
|
|
349
|
+
const uniquePaths = (paths) => [...new Map(paths.map((path) => [path.join("\0"), [...path]])).values()].sort((left, right) => displayPath(left).localeCompare(displayPath(right)));
|
|
350
|
+
const sortReferences = (references) => [...references].sort((left, right) => left.sourceNodeId.localeCompare(right.sourceNodeId) || left.browseName.localeCompare(right.browseName) || left.targetNodeId.localeCompare(right.targetNodeId));
|
|
351
|
+
const displayPath = (path) => path.join(" / ");
|
|
352
|
+
const normalizeNamespaceZeroNodeId = (nodeId) => nodeId.startsWith("ns=0;") ? nodeId.slice(5) : nodeId;
|
|
353
|
+
|
|
354
|
+
//#endregion
|
|
355
|
+
export { discover, metadataTargetNodeIds };
|
|
356
|
+
//# sourceMappingURL=discover.mjs.map
|