@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.
Files changed (102) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +6 -0
  3. package/dist/cli.d.mts +1 -0
  4. package/dist/cli.mjs +82 -0
  5. package/dist/cli.mjs.map +1 -0
  6. package/dist/client/src/Opcua.d.mts +5 -0
  7. package/dist/client/src/OpcuaClient.d.mts +3 -0
  8. package/dist/client/src/OpcuaError.d.mts +139 -0
  9. package/dist/client/src/OpcuaError.d.mts.map +1 -0
  10. package/dist/client/src/OpcuaMethod.d.mts +78 -0
  11. package/dist/client/src/OpcuaMethod.d.mts.map +1 -0
  12. package/dist/client/src/OpcuaSession.d.mts +215 -0
  13. package/dist/client/src/OpcuaSession.d.mts.map +1 -0
  14. package/dist/client/src/OpcuaSubscription.d.mts +144 -0
  15. package/dist/client/src/OpcuaSubscription.d.mts.map +1 -0
  16. package/dist/client/src/OpcuaVariable.d.mts +140 -0
  17. package/dist/client/src/OpcuaVariable.d.mts.map +1 -0
  18. package/dist/client/src/index.d.mts +7 -0
  19. package/dist/client/src/internal/common/node-id.d.mts +1 -0
  20. package/dist/client/src/internal/events/model.d.mts +51 -0
  21. package/dist/client/src/internal/events/model.d.mts.map +1 -0
  22. package/dist/client/src/internal/monitoring/runtime.d.mts +7 -0
  23. package/dist/client/src/internal/structures/model.d.mts +18 -0
  24. package/dist/client/src/internal/structures/model.d.mts.map +1 -0
  25. package/dist/client/src/internal/structures/runtime.d.mts +5 -0
  26. package/dist/client/src/internal/values/codec.d.mts +21 -0
  27. package/dist/client/src/internal/values/codec.d.mts.map +1 -0
  28. package/dist/client/src/internal/values/normalize.d.mts +2 -0
  29. package/dist/client/src/node-opcua.d.mts +2 -0
  30. package/dist/compile/builtin-types.d.mts +10 -0
  31. package/dist/compile/builtin-types.d.mts.map +1 -0
  32. package/dist/compile/builtin-types.mjs +38 -0
  33. package/dist/compile/builtin-types.mjs.map +1 -0
  34. package/dist/compile/enums.d.mts +16 -0
  35. package/dist/compile/enums.d.mts.map +1 -0
  36. package/dist/compile/enums.mjs +81 -0
  37. package/dist/compile/enums.mjs.map +1 -0
  38. package/dist/compile/model.d.mts +10 -0
  39. package/dist/compile/model.d.mts.map +1 -0
  40. package/dist/compile/model.mjs +92 -0
  41. package/dist/compile/model.mjs.map +1 -0
  42. package/dist/compile/names.d.mts +17 -0
  43. package/dist/compile/names.d.mts.map +1 -0
  44. package/dist/compile/names.mjs +95 -0
  45. package/dist/compile/names.mjs.map +1 -0
  46. package/dist/compile/policy.d.mts +7 -0
  47. package/dist/compile/policy.d.mts.map +1 -0
  48. package/dist/compile/policy.mjs +6 -0
  49. package/dist/compile/policy.mjs.map +1 -0
  50. package/dist/compile/structures.d.mts +16 -0
  51. package/dist/compile/structures.d.mts.map +1 -0
  52. package/dist/compile/structures.mjs +220 -0
  53. package/dist/compile/structures.mjs.map +1 -0
  54. package/dist/compile/type-graph.d.mts +20 -0
  55. package/dist/compile/type-graph.d.mts.map +1 -0
  56. package/dist/compile/type-graph.mjs +78 -0
  57. package/dist/compile/type-graph.mjs.map +1 -0
  58. package/dist/compile/variables.d.mts +16 -0
  59. package/dist/compile/variables.d.mts.map +1 -0
  60. package/dist/compile/variables.mjs +110 -0
  61. package/dist/compile/variables.mjs.map +1 -0
  62. package/dist/compile.d.mts +9 -0
  63. package/dist/compile.d.mts.map +1 -0
  64. package/dist/compile.mjs +29 -0
  65. package/dist/compile.mjs.map +1 -0
  66. package/dist/config.d.mts +12 -0
  67. package/dist/config.d.mts.map +1 -0
  68. package/dist/config.mjs +158 -0
  69. package/dist/config.mjs.map +1 -0
  70. package/dist/diagnostics.d.mts +16 -0
  71. package/dist/diagnostics.d.mts.map +1 -0
  72. package/dist/diagnostics.mjs +45 -0
  73. package/dist/diagnostics.mjs.map +1 -0
  74. package/dist/discover.d.mts +23 -0
  75. package/dist/discover.d.mts.map +1 -0
  76. package/dist/discover.mjs +356 -0
  77. package/dist/discover.mjs.map +1 -0
  78. package/dist/emit.d.mts +8 -0
  79. package/dist/emit.d.mts.map +1 -0
  80. package/dist/emit.mjs +233 -0
  81. package/dist/emit.mjs.map +1 -0
  82. package/dist/errors.d.mts +27 -0
  83. package/dist/errors.d.mts.map +1 -0
  84. package/dist/errors.mjs +18 -0
  85. package/dist/errors.mjs.map +1 -0
  86. package/dist/generate.d.mts +19 -0
  87. package/dist/generate.d.mts.map +1 -0
  88. package/dist/generate.mjs +172 -0
  89. package/dist/generate.mjs.map +1 -0
  90. package/dist/index.d.mts +5 -0
  91. package/dist/index.mjs +5 -0
  92. package/dist/internal/types.d.mts +192 -0
  93. package/dist/internal/types.d.mts.map +1 -0
  94. package/dist/internal/types.mjs +1 -0
  95. package/dist/issue-codes.d.mts +119 -0
  96. package/dist/issue-codes.d.mts.map +1 -0
  97. package/dist/issue-codes.mjs +44 -0
  98. package/dist/issue-codes.mjs.map +1 -0
  99. package/dist/types.d.mts +57 -0
  100. package/dist/types.d.mts.map +1 -0
  101. package/dist/types.mjs +1 -0
  102. package/package.json +58 -0
@@ -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