@tsonic/frontend 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/package.json +53 -0
  2. package/src/dependency-graph.ts +18 -0
  3. package/src/dotnet-metadata.ts +121 -0
  4. package/src/graph/builder.ts +81 -0
  5. package/src/graph/circular.ts +58 -0
  6. package/src/graph/extraction/exports.ts +55 -0
  7. package/src/graph/extraction/imports.ts +81 -0
  8. package/src/graph/extraction/index.ts +7 -0
  9. package/src/graph/extraction/orchestrator.ts +99 -0
  10. package/src/graph/extraction.ts +10 -0
  11. package/src/graph/helpers.ts +51 -0
  12. package/src/graph/index.ts +17 -0
  13. package/src/graph/types.ts +13 -0
  14. package/src/index.ts +80 -0
  15. package/src/ir/binding-resolution.test.ts +585 -0
  16. package/src/ir/builder/exports.ts +78 -0
  17. package/src/ir/builder/helpers.ts +27 -0
  18. package/src/ir/builder/imports.ts +153 -0
  19. package/src/ir/builder/index.ts +10 -0
  20. package/src/ir/builder/orchestrator.ts +178 -0
  21. package/src/ir/builder/statements.ts +55 -0
  22. package/src/ir/builder/types.ts +8 -0
  23. package/src/ir/builder/validation.ts +129 -0
  24. package/src/ir/builder.test.ts +581 -0
  25. package/src/ir/builder.ts +14 -0
  26. package/src/ir/converters/expressions/access.ts +99 -0
  27. package/src/ir/converters/expressions/calls.ts +137 -0
  28. package/src/ir/converters/expressions/collections.ts +84 -0
  29. package/src/ir/converters/expressions/functions.ts +62 -0
  30. package/src/ir/converters/expressions/helpers.ts +264 -0
  31. package/src/ir/converters/expressions/index.ts +43 -0
  32. package/src/ir/converters/expressions/literals.ts +22 -0
  33. package/src/ir/converters/expressions/operators.ts +147 -0
  34. package/src/ir/converters/expressions/other.ts +60 -0
  35. package/src/ir/converters/statements/control/blocks.ts +22 -0
  36. package/src/ir/converters/statements/control/conditionals.ts +67 -0
  37. package/src/ir/converters/statements/control/exceptions.ts +43 -0
  38. package/src/ir/converters/statements/control/index.ts +17 -0
  39. package/src/ir/converters/statements/control/loops.ts +99 -0
  40. package/src/ir/converters/statements/control.ts +17 -0
  41. package/src/ir/converters/statements/declarations/classes/constructors.ts +120 -0
  42. package/src/ir/converters/statements/declarations/classes/index.ts +12 -0
  43. package/src/ir/converters/statements/declarations/classes/methods.ts +61 -0
  44. package/src/ir/converters/statements/declarations/classes/orchestrator.ts +166 -0
  45. package/src/ir/converters/statements/declarations/classes/override-detection.ts +116 -0
  46. package/src/ir/converters/statements/declarations/classes/properties.ts +63 -0
  47. package/src/ir/converters/statements/declarations/classes.ts +6 -0
  48. package/src/ir/converters/statements/declarations/enums.ts +29 -0
  49. package/src/ir/converters/statements/declarations/functions.ts +39 -0
  50. package/src/ir/converters/statements/declarations/index.ts +14 -0
  51. package/src/ir/converters/statements/declarations/interfaces.ts +131 -0
  52. package/src/ir/converters/statements/declarations/registry.ts +45 -0
  53. package/src/ir/converters/statements/declarations/type-aliases.ts +25 -0
  54. package/src/ir/converters/statements/declarations/variables.ts +60 -0
  55. package/src/ir/converters/statements/declarations.ts +16 -0
  56. package/src/ir/converters/statements/helpers.ts +174 -0
  57. package/src/ir/converters/statements/index.ts +40 -0
  58. package/src/ir/expression-converter.ts +207 -0
  59. package/src/ir/generic-validator.ts +100 -0
  60. package/src/ir/hierarchical-bindings-e2e.test.ts +163 -0
  61. package/src/ir/index.ts +6 -0
  62. package/src/ir/statement-converter.ts +128 -0
  63. package/src/ir/type-converter/arrays.ts +20 -0
  64. package/src/ir/type-converter/converter.ts +10 -0
  65. package/src/ir/type-converter/functions.ts +22 -0
  66. package/src/ir/type-converter/index.ts +11 -0
  67. package/src/ir/type-converter/inference.ts +122 -0
  68. package/src/ir/type-converter/literals.ts +40 -0
  69. package/src/ir/type-converter/objects.ts +107 -0
  70. package/src/ir/type-converter/orchestrator.ts +85 -0
  71. package/src/ir/type-converter/patterns.ts +73 -0
  72. package/src/ir/type-converter/primitives.ts +57 -0
  73. package/src/ir/type-converter/references.ts +64 -0
  74. package/src/ir/type-converter/unions-intersections.ts +34 -0
  75. package/src/ir/type-converter.ts +13 -0
  76. package/src/ir/types/expressions.ts +215 -0
  77. package/src/ir/types/guards.ts +39 -0
  78. package/src/ir/types/helpers.ts +135 -0
  79. package/src/ir/types/index.ts +108 -0
  80. package/src/ir/types/ir-types.ts +96 -0
  81. package/src/ir/types/module.ts +57 -0
  82. package/src/ir/types/statements.ts +238 -0
  83. package/src/ir/types.ts +97 -0
  84. package/src/metadata/bindings-loader.test.ts +144 -0
  85. package/src/metadata/bindings-loader.ts +357 -0
  86. package/src/metadata/index.ts +15 -0
  87. package/src/metadata/library-loader.ts +153 -0
  88. package/src/metadata/loader.test.ts +156 -0
  89. package/src/metadata/loader.ts +382 -0
  90. package/src/program/bindings.test.ts +512 -0
  91. package/src/program/bindings.ts +253 -0
  92. package/src/program/config.ts +30 -0
  93. package/src/program/creation.ts +249 -0
  94. package/src/program/dependency-graph.ts +245 -0
  95. package/src/program/diagnostics.ts +103 -0
  96. package/src/program/index.ts +19 -0
  97. package/src/program/metadata.ts +68 -0
  98. package/src/program/queries.ts +18 -0
  99. package/src/program/types.ts +38 -0
  100. package/src/program.ts +13 -0
  101. package/src/resolver/dotnet-import-resolver.ts +226 -0
  102. package/src/resolver/import-resolution.ts +177 -0
  103. package/src/resolver/index.ts +18 -0
  104. package/src/resolver/namespace.test.ts +86 -0
  105. package/src/resolver/namespace.ts +42 -0
  106. package/src/resolver/naming.ts +38 -0
  107. package/src/resolver/path-resolution.ts +22 -0
  108. package/src/resolver/types.ts +15 -0
  109. package/src/resolver.test.ts +155 -0
  110. package/src/resolver.ts +14 -0
  111. package/src/symbol-table/builder.ts +114 -0
  112. package/src/symbol-table/creation.ts +42 -0
  113. package/src/symbol-table/helpers.ts +18 -0
  114. package/src/symbol-table/index.ts +13 -0
  115. package/src/symbol-table/queries.ts +42 -0
  116. package/src/symbol-table/types.ts +28 -0
  117. package/src/symbol-table.ts +14 -0
  118. package/src/types/bindings.ts +172 -0
  119. package/src/types/diagnostic.test.ts +164 -0
  120. package/src/types/diagnostic.ts +153 -0
  121. package/src/types/explicit-views.test.ts +113 -0
  122. package/src/types/explicit-views.ts +218 -0
  123. package/src/types/metadata.ts +229 -0
  124. package/src/types/module.ts +99 -0
  125. package/src/types/nested-types.test.ts +194 -0
  126. package/src/types/nested-types.ts +215 -0
  127. package/src/types/parameter-modifiers.ts +173 -0
  128. package/src/types/ref-parameters.test.ts +192 -0
  129. package/src/types/ref-parameters.ts +268 -0
  130. package/src/types/result.test.ts +157 -0
  131. package/src/types/result.ts +48 -0
  132. package/src/types/support-types.test.ts +81 -0
  133. package/src/types/support-types.ts +288 -0
  134. package/src/types/test-harness.ts +180 -0
  135. package/src/validation/exports.ts +98 -0
  136. package/src/validation/features.ts +89 -0
  137. package/src/validation/generics.ts +40 -0
  138. package/src/validation/helpers.ts +31 -0
  139. package/src/validation/imports.ts +97 -0
  140. package/src/validation/index.ts +11 -0
  141. package/src/validation/orchestrator.ts +51 -0
  142. package/src/validation/static-safety.ts +267 -0
  143. package/src/validator.test.ts +468 -0
  144. package/src/validator.ts +15 -0
  145. package/tsconfig.json +13 -0
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Intermediate Representation (IR) types for Tsonic compiler
3
+ * Main dispatcher - re-exports from specialized modules
4
+ */
5
+
6
+ // Re-export everything from types subdirectory for backward compatibility
7
+ export type {
8
+ // Module types
9
+ IrModule,
10
+ IrImport,
11
+ IrImportSpecifier,
12
+ IrExport,
13
+ // Statement types
14
+ IrStatement,
15
+ IrVariableDeclaration,
16
+ IrVariableDeclarator,
17
+ IrFunctionDeclaration,
18
+ IrClassDeclaration,
19
+ IrClassMember,
20
+ IrMethodDeclaration,
21
+ IrPropertyDeclaration,
22
+ IrConstructorDeclaration,
23
+ IrInterfaceDeclaration,
24
+ IrEnumDeclaration,
25
+ IrEnumMember,
26
+ IrTypeAliasDeclaration,
27
+ IrExpressionStatement,
28
+ IrReturnStatement,
29
+ IrIfStatement,
30
+ IrWhileStatement,
31
+ IrForStatement,
32
+ IrForOfStatement,
33
+ IrSwitchStatement,
34
+ IrSwitchCase,
35
+ IrThrowStatement,
36
+ IrTryStatement,
37
+ IrCatchClause,
38
+ IrBlockStatement,
39
+ IrBreakStatement,
40
+ IrContinueStatement,
41
+ IrEmptyStatement,
42
+ // Expression types
43
+ IrExpression,
44
+ IrLiteralExpression,
45
+ IrIdentifierExpression,
46
+ IrArrayExpression,
47
+ IrObjectExpression,
48
+ IrObjectProperty,
49
+ IrFunctionExpression,
50
+ IrArrowFunctionExpression,
51
+ IrMemberExpression,
52
+ IrCallExpression,
53
+ IrNewExpression,
54
+ IrThisExpression,
55
+ IrUpdateExpression,
56
+ IrUnaryExpression,
57
+ IrBinaryExpression,
58
+ IrLogicalExpression,
59
+ IrConditionalExpression,
60
+ IrAssignmentExpression,
61
+ IrTemplateLiteralExpression,
62
+ IrSpreadExpression,
63
+ IrAwaitExpression,
64
+ IrYieldExpression,
65
+ // Type system types
66
+ IrType,
67
+ IrPrimitiveType,
68
+ IrReferenceType,
69
+ IrArrayType,
70
+ IrFunctionType,
71
+ IrObjectType,
72
+ IrDictionaryType,
73
+ IrUnionType,
74
+ IrIntersectionType,
75
+ IrLiteralType,
76
+ IrAnyType,
77
+ IrUnknownType,
78
+ IrVoidType,
79
+ IrNeverType,
80
+ // Helper types
81
+ IrPattern,
82
+ IrIdentifierPattern,
83
+ IrArrayPattern,
84
+ IrObjectPattern,
85
+ IrObjectPatternProperty,
86
+ IrTypeParameter,
87
+ IrParameter,
88
+ IrInterfaceMember,
89
+ IrPropertySignature,
90
+ IrMethodSignature,
91
+ IrAccessibility,
92
+ IrBinaryOperator,
93
+ IrAssignmentOperator,
94
+ } from "./types/index.js";
95
+
96
+ // Re-export type guards
97
+ export { isStatement, isExpression } from "./types/guards.js";
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Tests for bindings JSON loader
3
+ */
4
+
5
+ import { describe, it } from "mocha";
6
+ import { strict as assert } from "assert";
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ import {
11
+ loadBindingsFile,
12
+ buildBindingsRegistry,
13
+ lookupTypeBinding,
14
+ } from "./bindings-loader.js";
15
+ import type { BindingsFile } from "../types/bindings.js";
16
+
17
+ describe("Bindings Loader", () => {
18
+ describe("loadBindingsFile", () => {
19
+ it("should load valid bindings file", () => {
20
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-test-"));
21
+ const bindingsPath = path.join(tmpDir, "test.bindings.json");
22
+
23
+ const bindings: BindingsFile = {
24
+ namespace: "System",
25
+ types: [
26
+ {
27
+ clrName: "System.String",
28
+ tsEmitName: "String",
29
+ assemblyName: "System.Runtime",
30
+ metadataToken: 0x02000001,
31
+ methods: [],
32
+ exposedMethods: [],
33
+ },
34
+ ],
35
+ };
36
+
37
+ fs.writeFileSync(bindingsPath, JSON.stringify(bindings, null, 2));
38
+
39
+ const result = loadBindingsFile(bindingsPath);
40
+
41
+ // Cleanup
42
+ fs.unlinkSync(bindingsPath);
43
+ fs.rmdirSync(tmpDir);
44
+
45
+ assert.ok(result.ok);
46
+ if (result.ok) {
47
+ assert.equal(result.value.namespace, "System");
48
+ assert.equal(result.value.types.length, 1);
49
+ assert.equal(result.value.types[0]?.tsEmitName, "String");
50
+ }
51
+ });
52
+
53
+ it("should return error for non-existent file", () => {
54
+ const result = loadBindingsFile("/nonexistent/file.bindings.json");
55
+
56
+ assert.ok(!result.ok);
57
+ if (!result.ok) {
58
+ assert.equal(result.error[0]?.code, "TSN9101");
59
+ }
60
+ });
61
+
62
+ it("should return error for invalid JSON", () => {
63
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "tsonic-test-"));
64
+ const bindingsPath = path.join(tmpDir, "invalid.bindings.json");
65
+
66
+ fs.writeFileSync(bindingsPath, "{ invalid json }");
67
+
68
+ const result = loadBindingsFile(bindingsPath);
69
+
70
+ // Cleanup
71
+ fs.unlinkSync(bindingsPath);
72
+ fs.rmdirSync(tmpDir);
73
+
74
+ assert.ok(!result.ok);
75
+ if (!result.ok) {
76
+ assert.equal(result.error[0]?.code, "TSN9103");
77
+ }
78
+ });
79
+ });
80
+
81
+ describe("buildBindingsRegistry", () => {
82
+ it("should build registry from bindings files", () => {
83
+ const bindings1: BindingsFile = {
84
+ namespace: "System",
85
+ types: [
86
+ {
87
+ clrName: "System.String",
88
+ tsEmitName: "String",
89
+ assemblyName: "System.Runtime",
90
+ metadataToken: 0x02000001,
91
+ },
92
+ ],
93
+ };
94
+
95
+ const bindings2: BindingsFile = {
96
+ namespace: "System.Collections",
97
+ types: [
98
+ {
99
+ clrName: "System.Collections.ArrayList",
100
+ tsEmitName: "ArrayList",
101
+ assemblyName: "System.Collections",
102
+ metadataToken: 0x02000002,
103
+ },
104
+ ],
105
+ };
106
+
107
+ const registry = buildBindingsRegistry([bindings1, bindings2]);
108
+
109
+ assert.equal(registry.size, 2);
110
+ assert.ok(registry.has("System.String"));
111
+ assert.ok(registry.has("System.Collections.ArrayList"));
112
+ });
113
+ });
114
+
115
+ describe("lookupTypeBinding", () => {
116
+ it("should find type binding in registry", () => {
117
+ const bindings: BindingsFile = {
118
+ namespace: "System",
119
+ types: [
120
+ {
121
+ clrName: "System.String",
122
+ tsEmitName: "String",
123
+ assemblyName: "System.Runtime",
124
+ metadataToken: 0x02000001,
125
+ },
126
+ ],
127
+ };
128
+
129
+ const registry = buildBindingsRegistry([bindings]);
130
+ const binding = lookupTypeBinding(registry, "System.String");
131
+
132
+ assert.ok(binding);
133
+ assert.equal(binding?.tsEmitName, "String");
134
+ assert.equal(binding?.metadataToken, 0x02000001);
135
+ });
136
+
137
+ it("should return undefined for non-existent type", () => {
138
+ const registry = buildBindingsRegistry([]);
139
+ const binding = lookupTypeBinding(registry, "NonExistent.Type");
140
+
141
+ assert.equal(binding, undefined);
142
+ });
143
+ });
144
+ });
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Bindings JSON loader - Reads and validates .bindings.json files from tsbindgen.
3
+ *
4
+ * This module provides pure functions to load runtime binding files and validate
5
+ * their structure against the expected schema.
6
+ *
7
+ * @see spec/bindings.md for complete schema documentation
8
+ */
9
+
10
+ import * as fs from "fs";
11
+ import * as path from "path";
12
+ import type { Result } from "../types/result.ts";
13
+ import type { Diagnostic } from "../types/diagnostic.ts";
14
+ import type { BindingsFile, TypeBinding } from "../types/bindings.ts";
15
+
16
+ /**
17
+ * Load and parse a bindings.json file.
18
+ *
19
+ * @param filePath - Absolute path to the .bindings.json file
20
+ * @returns Result containing parsed bindings or diagnostics
21
+ */
22
+ export const loadBindingsFile = (
23
+ filePath: string
24
+ ): Result<BindingsFile, Diagnostic[]> => {
25
+ // Check file exists
26
+ if (!fs.existsSync(filePath)) {
27
+ return {
28
+ ok: false,
29
+ error: [
30
+ {
31
+ code: "TSN9101",
32
+ message: `Bindings file not found: ${filePath}`,
33
+ severity: "error",
34
+ location: undefined,
35
+ },
36
+ ],
37
+ };
38
+ }
39
+
40
+ // Read file contents
41
+ let content: string;
42
+ try {
43
+ content = fs.readFileSync(filePath, "utf-8");
44
+ } catch (error) {
45
+ return {
46
+ ok: false,
47
+ error: [
48
+ {
49
+ code: "TSN9102",
50
+ message: `Failed to read bindings file: ${error}`,
51
+ severity: "error",
52
+ location: undefined,
53
+ },
54
+ ],
55
+ };
56
+ }
57
+
58
+ // Parse JSON
59
+ let parsed: unknown;
60
+ try {
61
+ parsed = JSON.parse(content);
62
+ } catch (error) {
63
+ return {
64
+ ok: false,
65
+ error: [
66
+ {
67
+ code: "TSN9103",
68
+ message: `Invalid JSON in bindings file: ${error}`,
69
+ severity: "error",
70
+ location: undefined,
71
+ },
72
+ ],
73
+ };
74
+ }
75
+
76
+ // Validate structure
77
+ const validation = validateBindingsFile(parsed, filePath);
78
+ if (!validation.ok) {
79
+ return validation;
80
+ }
81
+
82
+ return { ok: true, value: validation.value };
83
+ };
84
+
85
+ /**
86
+ * Validate that parsed JSON matches BindingsFile schema.
87
+ *
88
+ * @param data - Parsed JSON data
89
+ * @param filePath - File path for error messages
90
+ * @returns Result containing validated bindings or diagnostics
91
+ */
92
+ const validateBindingsFile = (
93
+ data: unknown,
94
+ filePath: string
95
+ ): Result<BindingsFile, Diagnostic[]> => {
96
+ const diagnostics: Diagnostic[] = [];
97
+
98
+ if (typeof data !== "object" || data === null) {
99
+ return {
100
+ ok: false,
101
+ error: [
102
+ {
103
+ code: "TSN9104",
104
+ message: `Bindings file must be an object, got ${typeof data}`,
105
+ severity: "error",
106
+ location: undefined,
107
+ },
108
+ ],
109
+ };
110
+ }
111
+
112
+ const obj = data as Record<string, unknown>;
113
+
114
+ // Validate namespace
115
+ if (typeof obj.namespace !== "string") {
116
+ diagnostics.push({
117
+ code: "TSN9105",
118
+ message: `Missing or invalid 'namespace' field in ${path.basename(filePath)}`,
119
+ severity: "error",
120
+ location: undefined,
121
+ });
122
+ }
123
+
124
+ // Validate types array
125
+ if (!Array.isArray(obj.types)) {
126
+ diagnostics.push({
127
+ code: "TSN9106",
128
+ message: `Missing or invalid 'types' field in ${path.basename(filePath)}`,
129
+ severity: "error",
130
+ location: undefined,
131
+ });
132
+ } else {
133
+ // Validate each type binding
134
+ for (let i = 0; i < obj.types.length; i++) {
135
+ const typeValidation = validateTypeBinding(obj.types[i], filePath, i);
136
+ if (!typeValidation.ok) {
137
+ diagnostics.push(...typeValidation.error);
138
+ }
139
+ }
140
+ }
141
+
142
+ if (diagnostics.length > 0) {
143
+ return { ok: false, error: diagnostics };
144
+ }
145
+
146
+ return { ok: true, value: obj as BindingsFile };
147
+ };
148
+
149
+ /**
150
+ * Validate a single TypeBinding object.
151
+ *
152
+ * @param data - Type binding to validate
153
+ * @param filePath - File path for error messages
154
+ * @param index - Type index in array
155
+ * @returns Result indicating validation success or errors
156
+ */
157
+ const validateTypeBinding = (
158
+ data: unknown,
159
+ filePath: string,
160
+ index: number
161
+ ): Result<TypeBinding, Diagnostic[]> => {
162
+ const diagnostics: Diagnostic[] = [];
163
+ const context = `type binding ${index} in ${path.basename(filePath)}`;
164
+
165
+ if (typeof data !== "object" || data === null) {
166
+ return {
167
+ ok: false,
168
+ error: [
169
+ {
170
+ code: "TSN9107",
171
+ message: `Invalid ${context}: must be an object`,
172
+ severity: "error",
173
+ location: undefined,
174
+ },
175
+ ],
176
+ };
177
+ }
178
+
179
+ const type = data as Record<string, unknown>;
180
+
181
+ // Validate required string fields
182
+ const requiredStringFields = ["clrName", "tsEmitName", "assemblyName"];
183
+ for (const field of requiredStringFields) {
184
+ if (typeof type[field] !== "string") {
185
+ diagnostics.push({
186
+ code: "TSN9108",
187
+ message: `Invalid ${context}: missing or invalid '${field}'`,
188
+ severity: "error",
189
+ location: undefined,
190
+ });
191
+ }
192
+ }
193
+
194
+ // Validate metadataToken
195
+ if (typeof type.metadataToken !== "number") {
196
+ diagnostics.push({
197
+ code: "TSN9109",
198
+ message: `Invalid ${context}: 'metadataToken' must be a number`,
199
+ severity: "error",
200
+ location: undefined,
201
+ });
202
+ }
203
+
204
+ // Validate optional array fields (V1 Definitions)
205
+ const v1ArrayFields = ["methods", "properties", "fields", "events"];
206
+ for (const field of v1ArrayFields) {
207
+ if (type[field] !== undefined && !Array.isArray(type[field])) {
208
+ diagnostics.push({
209
+ code: "TSN9110",
210
+ message: `Invalid ${context}: '${field}' must be an array if present`,
211
+ severity: "error",
212
+ location: undefined,
213
+ });
214
+ }
215
+ }
216
+
217
+ // Validate optional array fields (V2 Exposures)
218
+ const v2ArrayFields = [
219
+ "exposedMethods",
220
+ "exposedProperties",
221
+ "exposedFields",
222
+ "exposedEvents",
223
+ ];
224
+ for (const field of v2ArrayFields) {
225
+ if (type[field] !== undefined && !Array.isArray(type[field])) {
226
+ diagnostics.push({
227
+ code: "TSN9111",
228
+ message: `Invalid ${context}: '${field}' must be an array if present`,
229
+ severity: "error",
230
+ location: undefined,
231
+ });
232
+ }
233
+ }
234
+
235
+ // Note: We're doing basic structural validation here
236
+ // More detailed validation of nested objects (method bindings, etc.)
237
+ // can be added in future iterations if needed
238
+
239
+ if (diagnostics.length > 0) {
240
+ return { ok: false, error: diagnostics };
241
+ }
242
+
243
+ return { ok: true, value: type as TypeBinding };
244
+ };
245
+
246
+ /**
247
+ * Load bindings from a directory containing .bindings.json files.
248
+ *
249
+ * @param directoryPath - Path to directory containing bindings files
250
+ * @returns Result containing array of loaded bindings files or diagnostics
251
+ */
252
+ export const loadBindingsDirectory = (
253
+ directoryPath: string
254
+ ): Result<BindingsFile[], Diagnostic[]> => {
255
+ if (!fs.existsSync(directoryPath)) {
256
+ return {
257
+ ok: false,
258
+ error: [
259
+ {
260
+ code: "TSN9112",
261
+ message: `Bindings directory not found: ${directoryPath}`,
262
+ severity: "error",
263
+ location: undefined,
264
+ },
265
+ ],
266
+ };
267
+ }
268
+
269
+ const stats = fs.statSync(directoryPath);
270
+ if (!stats.isDirectory()) {
271
+ return {
272
+ ok: false,
273
+ error: [
274
+ {
275
+ code: "TSN9113",
276
+ message: `Not a directory: ${directoryPath}`,
277
+ severity: "error",
278
+ location: undefined,
279
+ },
280
+ ],
281
+ };
282
+ }
283
+
284
+ // Find all .bindings.json files
285
+ const files = fs
286
+ .readdirSync(directoryPath)
287
+ .filter((file) => file.endsWith(".bindings.json"))
288
+ .map((file) => path.join(directoryPath, file));
289
+
290
+ if (files.length === 0) {
291
+ return {
292
+ ok: false,
293
+ error: [
294
+ {
295
+ code: "TSN9114",
296
+ message: `No .bindings.json files found in ${directoryPath}`,
297
+ severity: "warning",
298
+ location: undefined,
299
+ },
300
+ ],
301
+ };
302
+ }
303
+
304
+ // Load each file
305
+ const bindingsFiles: BindingsFile[] = [];
306
+ const diagnostics: Diagnostic[] = [];
307
+
308
+ for (const file of files) {
309
+ const result = loadBindingsFile(file);
310
+ if (result.ok) {
311
+ bindingsFiles.push(result.value);
312
+ } else {
313
+ diagnostics.push(...result.error);
314
+ }
315
+ }
316
+
317
+ if (diagnostics.length > 0) {
318
+ return { ok: false, error: diagnostics };
319
+ }
320
+
321
+ return { ok: true, value: bindingsFiles };
322
+ };
323
+
324
+ /**
325
+ * Build a registry of type bindings for fast lookup by TypeScript emit name.
326
+ *
327
+ * @param bindingsFiles - Array of loaded bindings files
328
+ * @returns Map from tsEmitName to TypeBinding
329
+ */
330
+ export const buildBindingsRegistry = (
331
+ bindingsFiles: readonly BindingsFile[]
332
+ ): ReadonlyMap<string, TypeBinding> => {
333
+ const registry = new Map<string, TypeBinding>();
334
+
335
+ for (const file of bindingsFiles) {
336
+ for (const typeBinding of file.types) {
337
+ const key = `${file.namespace}.${typeBinding.tsEmitName}`;
338
+ registry.set(key, typeBinding);
339
+ }
340
+ }
341
+
342
+ return registry;
343
+ };
344
+
345
+ /**
346
+ * Look up a type binding by fully-qualified TypeScript emit name.
347
+ *
348
+ * @param registry - Bindings registry
349
+ * @param qualifiedTsName - Fully qualified TS name (e.g., "System.Collections.Generic.List_1")
350
+ * @returns TypeBinding if found, undefined otherwise
351
+ */
352
+ export const lookupTypeBinding = (
353
+ registry: ReadonlyMap<string, TypeBinding>,
354
+ qualifiedTsName: string
355
+ ): TypeBinding | undefined => {
356
+ return registry.get(qualifiedTsName);
357
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Metadata and Bindings Loaders - Public API
3
+ */
4
+
5
+ export { loadMetadataFile, loadMetadataDirectory } from "./loader.js";
6
+
7
+ export {
8
+ loadBindingsFile,
9
+ loadBindingsDirectory,
10
+ buildBindingsRegistry,
11
+ lookupTypeBinding,
12
+ } from "./bindings-loader.js";
13
+
14
+ export { loadLibrary, loadLibraries } from "./library-loader.js";
15
+ export type { LibraryData } from "./library-loader.js";