@tsonic/cli 0.0.63 → 0.0.64

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 (82) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/aikya/bindings.d.ts +44 -0
  3. package/dist/aikya/bindings.d.ts.map +1 -0
  4. package/dist/aikya/bindings.js +777 -0
  5. package/dist/aikya/bindings.js.map +1 -0
  6. package/dist/aikya/bindings.test.d.ts +2 -0
  7. package/dist/aikya/bindings.test.d.ts.map +1 -0
  8. package/dist/aikya/bindings.test.js +554 -0
  9. package/dist/aikya/bindings.test.js.map +1 -0
  10. package/dist/cli/dispatcher.d.ts.map +1 -1
  11. package/dist/cli/dispatcher.js +15 -0
  12. package/dist/cli/dispatcher.js.map +1 -1
  13. package/dist/cli/help.d.ts.map +1 -1
  14. package/dist/cli/help.js +4 -1
  15. package/dist/cli/help.js.map +1 -1
  16. package/dist/cli/parser.d.ts.map +1 -1
  17. package/dist/cli/parser.js +6 -0
  18. package/dist/cli/parser.js.map +1 -1
  19. package/dist/cli/parser.test.js +10 -0
  20. package/dist/cli/parser.test.js.map +1 -1
  21. package/dist/commands/add-npm.d.ts +3 -2
  22. package/dist/commands/add-npm.d.ts.map +1 -1
  23. package/dist/commands/add-npm.js +69 -177
  24. package/dist/commands/add-npm.js.map +1 -1
  25. package/dist/commands/add-npm.test.js +276 -2
  26. package/dist/commands/add-npm.test.js.map +1 -1
  27. package/dist/commands/build-library-bindings-aliases.test.js +349 -4
  28. package/dist/commands/build-library-bindings-aliases.test.js.map +1 -1
  29. package/dist/commands/build.d.ts.map +1 -1
  30. package/dist/commands/build.js +168 -148
  31. package/dist/commands/build.js.map +1 -1
  32. package/dist/commands/build.test.js +22 -3
  33. package/dist/commands/build.test.js.map +1 -1
  34. package/dist/commands/generate.d.ts.map +1 -1
  35. package/dist/commands/generate.js +5 -0
  36. package/dist/commands/generate.js.map +1 -1
  37. package/dist/commands/init.d.ts +5 -2
  38. package/dist/commands/init.d.ts.map +1 -1
  39. package/dist/commands/init.js +42 -7
  40. package/dist/commands/init.js.map +1 -1
  41. package/dist/commands/init.test.js +82 -2
  42. package/dist/commands/init.test.js.map +1 -1
  43. package/dist/commands/library-bindings-augment.d.ts.map +1 -1
  44. package/dist/commands/library-bindings-augment.js +376 -53
  45. package/dist/commands/library-bindings-augment.js.map +1 -1
  46. package/dist/commands/library-bindings-augment.test.js +460 -2
  47. package/dist/commands/library-bindings-augment.test.js.map +1 -1
  48. package/dist/commands/library-bindings-firstparty-regressions.test.d.ts +2 -0
  49. package/dist/commands/library-bindings-firstparty-regressions.test.d.ts.map +1 -0
  50. package/dist/commands/library-bindings-firstparty-regressions.test.js +217 -0
  51. package/dist/commands/library-bindings-firstparty-regressions.test.js.map +1 -0
  52. package/dist/commands/library-bindings-firstparty.d.ts +3 -0
  53. package/dist/commands/library-bindings-firstparty.d.ts.map +1 -0
  54. package/dist/commands/library-bindings-firstparty.js +2250 -0
  55. package/dist/commands/library-bindings-firstparty.js.map +1 -0
  56. package/dist/commands/restore.d.ts.map +1 -1
  57. package/dist/commands/restore.js +3 -1
  58. package/dist/commands/restore.js.map +1 -1
  59. package/dist/commands/restore.test.js +29 -0
  60. package/dist/commands/restore.test.js.map +1 -1
  61. package/dist/commands/run-build-regressions.test.js +72 -0
  62. package/dist/commands/run-build-regressions.test.js.map +1 -1
  63. package/dist/config.d.ts.map +1 -1
  64. package/dist/config.js +16 -2
  65. package/dist/config.js.map +1 -1
  66. package/dist/config.test.js +57 -0
  67. package/dist/config.test.js.map +1 -1
  68. package/dist/dotnet/runtime-dlls.d.ts +1 -0
  69. package/dist/dotnet/runtime-dlls.d.ts.map +1 -1
  70. package/dist/dotnet/runtime-dlls.js +1 -0
  71. package/dist/dotnet/runtime-dlls.js.map +1 -1
  72. package/dist/surface/profiles.d.ts +10 -0
  73. package/dist/surface/profiles.d.ts.map +1 -0
  74. package/dist/surface/profiles.js +61 -0
  75. package/dist/surface/profiles.js.map +1 -0
  76. package/dist/surface/profiles.test.d.ts +2 -0
  77. package/dist/surface/profiles.test.d.ts.map +1 -0
  78. package/dist/surface/profiles.test.js +49 -0
  79. package/dist/surface/profiles.test.js.map +1 -0
  80. package/dist/types.d.ts +10 -0
  81. package/dist/types.d.ts.map +1 -1
  82. package/package.json +4 -4
@@ -0,0 +1,2250 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
2
+ import { join, posix, resolve } from "node:path";
3
+ import { buildModuleDependencyGraph } from "@tsonic/frontend";
4
+ import * as ts from "typescript";
5
+ import { resolveSurfaceCapabilities } from "../surface/profiles.js";
6
+ import { overlayDependencyBindings } from "./library-bindings-augment.js";
7
+ const primitiveImportLine = "import type { sbyte, byte, short, ushort, int, uint, long, ulong, int128, uint128, half, float, double, decimal, nint, nuint, char } from '@tsonic/core/types.js';";
8
+ const typePrinter = ts.createPrinter({ removeComments: true });
9
+ const printTypeNodeText = (node, sourceFile) => {
10
+ return typePrinter
11
+ .printNode(ts.EmitHint.Unspecified, node, sourceFile)
12
+ .trim();
13
+ };
14
+ const ensureUndefinedInType = (typeText) => {
15
+ const trimmed = typeText.trim();
16
+ if (/\bundefined\b/.test(trimmed))
17
+ return trimmed;
18
+ return `${trimmed} | undefined`;
19
+ };
20
+ const applyWrappersToBaseType = (baseType, wrappers) => {
21
+ let expr = baseType.trim();
22
+ for (const w of wrappers.slice().reverse()) {
23
+ expr = `${w.aliasName}<${expr}>`;
24
+ }
25
+ return expr;
26
+ };
27
+ const getPropertyNameText = (name) => {
28
+ if (ts.isIdentifier(name))
29
+ return name.text;
30
+ if (ts.isStringLiteral(name) || ts.isNumericLiteral(name))
31
+ return name.text;
32
+ return undefined;
33
+ };
34
+ const sanitizeForBrand = (value) => {
35
+ const normalized = value.replace(/[^A-Za-z0-9_]/g, "_");
36
+ return normalized.length > 0 ? normalized : "_";
37
+ };
38
+ const printTypeParameters = (typeParameters) => {
39
+ if (!typeParameters || typeParameters.length === 0)
40
+ return "";
41
+ return `<${typeParameters.map((typeParameter) => typeParameter.name).join(", ")}>`;
42
+ };
43
+ const normalizeTypeReferenceName = (name, arity) => {
44
+ const withoutNamespace = (() => {
45
+ const dotIndex = Math.max(name.lastIndexOf("."), name.lastIndexOf("+"));
46
+ return dotIndex >= 0 ? name.slice(dotIndex + 1) : name;
47
+ })();
48
+ const backtickNormalized = withoutNamespace.replace(/`(\d+)$/, "_$1");
49
+ if (!arity || arity <= 0)
50
+ return backtickNormalized;
51
+ if (new RegExp(`_${arity}$`).test(backtickNormalized)) {
52
+ return backtickNormalized;
53
+ }
54
+ return `${backtickNormalized}_${arity}`;
55
+ };
56
+ const renderReferenceType = (referenceName, typeArguments, typeParametersInScope) => {
57
+ if (typeParametersInScope.includes(referenceName))
58
+ return referenceName;
59
+ if (referenceName === "unknown")
60
+ return "unknown";
61
+ if (referenceName === "any")
62
+ return "unknown";
63
+ if (referenceName === "object")
64
+ return "object";
65
+ if (referenceName === "string")
66
+ return "string";
67
+ if (referenceName === "boolean")
68
+ return "boolean";
69
+ if (referenceName === "number")
70
+ return "number";
71
+ let normalizedName = normalizeTypeReferenceName(referenceName);
72
+ if (typeArguments && typeArguments.length > 0) {
73
+ const arityMatch = normalizedName.match(/_(\d+)$/);
74
+ if (arityMatch &&
75
+ arityMatch[1] &&
76
+ Number(arityMatch[1]) === typeArguments.length &&
77
+ !normalizedName.includes("__Alias_") &&
78
+ !normalizedName.includes("__")) {
79
+ normalizedName = normalizedName.slice(0, -arityMatch[0].length);
80
+ }
81
+ else if (normalizedName.endsWith("_") &&
82
+ !normalizedName.includes("__Alias_")) {
83
+ normalizedName = normalizedName.slice(0, -1);
84
+ }
85
+ }
86
+ if (!typeArguments || typeArguments.length === 0)
87
+ return normalizedName;
88
+ return `${normalizedName}<${typeArguments
89
+ .map((arg) => renderPortableType(arg, typeParametersInScope))
90
+ .join(", ")}>`;
91
+ };
92
+ const renderPortableType = (type, typeParametersInScope = []) => {
93
+ if (!type)
94
+ return "object";
95
+ switch (type.kind) {
96
+ case "primitiveType":
97
+ return type.name;
98
+ case "literalType":
99
+ return typeof type.value === "string"
100
+ ? JSON.stringify(type.value)
101
+ : String(type.value);
102
+ case "voidType":
103
+ return "void";
104
+ case "neverType":
105
+ return "never";
106
+ case "unknownType":
107
+ case "anyType":
108
+ return "unknown";
109
+ case "typeParameterType":
110
+ return type.name;
111
+ case "arrayType":
112
+ return `${renderPortableType(type.elementType, typeParametersInScope)}[]`;
113
+ case "tupleType":
114
+ return `[${type.elementTypes
115
+ .map((item) => renderPortableType(item, typeParametersInScope))
116
+ .join(", ")}]`;
117
+ case "unionType":
118
+ return type.types
119
+ .map((item) => renderPortableType(item, typeParametersInScope))
120
+ .join(" | ");
121
+ case "intersectionType":
122
+ return type.types
123
+ .map((item) => renderPortableType(item, typeParametersInScope))
124
+ .join(" & ");
125
+ case "dictionaryType":
126
+ return `Record<${renderPortableType(type.keyType, typeParametersInScope)}, ${renderPortableType(type.valueType, typeParametersInScope)}>`;
127
+ case "functionType":
128
+ return `(${type.parameters
129
+ .map((parameter, index) => {
130
+ const parameterName = parameter.pattern.kind === "identifierPattern"
131
+ ? parameter.pattern.name
132
+ : `p${index + 1}`;
133
+ return `${parameterName}: ${renderPortableType(parameter.type, typeParametersInScope)}`;
134
+ })
135
+ .join(", ")}) => ${renderPortableType(type.returnType, typeParametersInScope)}`;
136
+ case "objectType":
137
+ return `{ ${type.members
138
+ .map((member) => {
139
+ if (member.kind === "methodSignature") {
140
+ return `${member.name}${printTypeParameters(member.typeParameters)}(${member.parameters
141
+ .map((parameter, index) => {
142
+ const parameterName = parameter.pattern.kind === "identifierPattern"
143
+ ? parameter.pattern.name
144
+ : `p${index + 1}`;
145
+ const optionalMark = parameter.isOptional ? "?" : "";
146
+ return `${parameterName}${optionalMark}: ${renderPortableType(parameter.type, typeParametersInScope)}`;
147
+ })
148
+ .join(", ")}): ${renderPortableType(member.returnType, typeParametersInScope)}`;
149
+ }
150
+ const optionalMark = member.isOptional ? "?" : "";
151
+ const readonlyMark = member.isReadonly ? "readonly " : "";
152
+ return `${readonlyMark}${member.name}${optionalMark}: ${renderPortableType(member.type, typeParametersInScope)}`;
153
+ })
154
+ .join("; ")} }`;
155
+ case "referenceType": {
156
+ return renderReferenceType(type.name, type.typeArguments, typeParametersInScope);
157
+ }
158
+ default:
159
+ return "object";
160
+ }
161
+ };
162
+ const renderUnknownParameters = (parameters, typeParametersInScope) => {
163
+ return parameters
164
+ .map((parameter, index) => {
165
+ const baseName = parameter.pattern.kind === "identifierPattern"
166
+ ? parameter.pattern.name
167
+ : `p${index + 1}`;
168
+ const restPrefix = parameter.isRest ? "..." : "";
169
+ const optionalMark = parameter.isOptional && !parameter.isRest ? "?" : "";
170
+ const parameterType = renderPortableType(parameter.type, typeParametersInScope);
171
+ const typeSuffix = parameter.isRest
172
+ ? `${parameterType}[]`
173
+ : parameterType;
174
+ return `${restPrefix}${baseName}${optionalMark}: ${typeSuffix}`;
175
+ })
176
+ .join(", ");
177
+ };
178
+ const renderMethodSignature = (name, typeParameters, parameters, returnType) => {
179
+ const typeParametersText = printTypeParameters(typeParameters);
180
+ const typeParameterNames = typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
181
+ const parametersText = renderUnknownParameters(parameters, typeParameterNames);
182
+ const returnTypeText = renderPortableType(returnType, typeParameterNames);
183
+ return `${name}${typeParametersText}(${parametersText}): ${returnTypeText};`;
184
+ };
185
+ const declarationNameOf = (statement) => {
186
+ switch (statement.kind) {
187
+ case "functionDeclaration":
188
+ case "classDeclaration":
189
+ case "interfaceDeclaration":
190
+ case "enumDeclaration":
191
+ case "typeAliasDeclaration":
192
+ return statement.name;
193
+ default:
194
+ return undefined;
195
+ }
196
+ };
197
+ const resolveModuleLocalDeclaration = (module, localName) => {
198
+ for (const statement of module.body) {
199
+ const statementName = declarationNameOf(statement);
200
+ if (statementName === localName)
201
+ return statement;
202
+ if (statement.kind === "variableDeclaration") {
203
+ for (const declarator of statement.declarations) {
204
+ if (declarator.name.kind === "identifierPattern" &&
205
+ declarator.name.name === localName) {
206
+ return statement;
207
+ }
208
+ }
209
+ }
210
+ }
211
+ return undefined;
212
+ };
213
+ const classifyDeclarationKind = (statement, filePath, exportName) => {
214
+ switch (statement.kind) {
215
+ case "functionDeclaration":
216
+ return { ok: true, value: "function" };
217
+ case "variableDeclaration":
218
+ return { ok: true, value: "variable" };
219
+ case "classDeclaration":
220
+ return { ok: true, value: "class" };
221
+ case "interfaceDeclaration":
222
+ return { ok: true, value: "interface" };
223
+ case "enumDeclaration":
224
+ return { ok: true, value: "enum" };
225
+ case "typeAliasDeclaration":
226
+ return { ok: true, value: "typeAlias" };
227
+ default:
228
+ return {
229
+ ok: false,
230
+ error: `Unsupported export '${exportName}' in ${filePath}: ${statement.kind}.\n` +
231
+ "First-party bindings generation requires explicit support for each exported declaration kind.",
232
+ };
233
+ }
234
+ };
235
+ const collectModuleExports = (module, modulesByFileKey) => {
236
+ const exportedSymbols = [];
237
+ const seen = new Set();
238
+ const pushExport = (symbol) => {
239
+ const key = `${symbol.exportName}|${symbol.localName}|${symbol.kind}`;
240
+ if (seen.has(key))
241
+ return;
242
+ seen.add(key);
243
+ exportedSymbols.push(symbol);
244
+ };
245
+ for (const item of module.exports) {
246
+ if (item.kind === "default") {
247
+ return {
248
+ ok: false,
249
+ error: `Unsupported default export in ${module.filePath}.\n` +
250
+ "First-party bindings generation currently requires named/declaration exports for deterministic namespace facades.",
251
+ };
252
+ }
253
+ if (item.kind === "declaration") {
254
+ const declaration = item.declaration;
255
+ if (declaration.kind === "variableDeclaration") {
256
+ for (const declarator of declaration.declarations) {
257
+ if (declarator.name.kind !== "identifierPattern") {
258
+ return {
259
+ ok: false,
260
+ error: `Unsupported exported variable declarator in ${module.filePath}: ${declarator.name.kind}.\n` +
261
+ "First-party bindings generation requires identifier-based exported variables.",
262
+ };
263
+ }
264
+ const localName = declarator.name.name;
265
+ pushExport({
266
+ exportName: localName,
267
+ localName,
268
+ kind: "variable",
269
+ declaration,
270
+ declaringNamespace: module.namespace,
271
+ declaringClassName: module.className,
272
+ declaringFilePath: module.filePath,
273
+ });
274
+ }
275
+ continue;
276
+ }
277
+ const declarationName = declarationNameOf(declaration);
278
+ if (!declarationName) {
279
+ return {
280
+ ok: false,
281
+ error: `Unsupported exported declaration in ${module.filePath}: ${declaration.kind}.\n` +
282
+ "First-party bindings generation requires explicit support for each exported declaration kind.",
283
+ };
284
+ }
285
+ const declarationKind = classifyDeclarationKind(declaration, module.filePath, declarationName);
286
+ if (!declarationKind.ok)
287
+ return declarationKind;
288
+ pushExport({
289
+ exportName: declarationName,
290
+ localName: declarationName,
291
+ kind: declarationKind.value,
292
+ declaration,
293
+ declaringNamespace: module.namespace,
294
+ declaringClassName: module.className,
295
+ declaringFilePath: module.filePath,
296
+ });
297
+ continue;
298
+ }
299
+ if (item.kind === "reexport")
300
+ continue;
301
+ const resolved = resolveExportedDeclaration(module, item.name, modulesByFileKey);
302
+ if (!resolved.ok)
303
+ return resolved;
304
+ const declaration = resolved.value.declaration;
305
+ const declarationName = declarationNameOf(declaration);
306
+ if (!declarationName && declaration.kind !== "variableDeclaration") {
307
+ return {
308
+ ok: false,
309
+ error: `Unsupported named export '${item.name}' in ${module.filePath}: ${declaration.kind}.\n` +
310
+ "First-party bindings generation requires explicit support for each exported declaration kind.",
311
+ };
312
+ }
313
+ const declarationKind = classifyDeclarationKind(declaration, module.filePath, item.name);
314
+ if (!declarationKind.ok)
315
+ return declarationKind;
316
+ pushExport({
317
+ exportName: item.name,
318
+ localName: resolved.value.clrName,
319
+ kind: declarationKind.value,
320
+ declaration,
321
+ declaringNamespace: resolved.value.module.namespace,
322
+ declaringClassName: resolved.value.module.className,
323
+ declaringFilePath: resolved.value.module.filePath,
324
+ });
325
+ }
326
+ return {
327
+ ok: true,
328
+ value: exportedSymbols.sort((left, right) => left.exportName.localeCompare(right.exportName)),
329
+ };
330
+ };
331
+ const moduleNamespacePath = (namespace) => {
332
+ return namespace.length > 0 ? namespace : "index";
333
+ };
334
+ const normalizeModuleFileKey = (filePath) => {
335
+ return filePath
336
+ .replace(/\\/g, "/")
337
+ .replace(/^(\.\/)+/, "")
338
+ .replace(/^\/+/, "");
339
+ };
340
+ const resolveLocalModuleFile = (fromModule, fromFile, modulesByFile) => {
341
+ const dir = posix.dirname(fromFile);
342
+ const candidates = [];
343
+ const raw = fromModule.startsWith("/")
344
+ ? posix.normalize(fromModule.slice(1))
345
+ : posix.normalize(posix.join(dir, fromModule));
346
+ candidates.push(raw);
347
+ if (raw.endsWith(".js")) {
348
+ candidates.push(raw.replace(/\.js$/, ".ts"));
349
+ }
350
+ if (!raw.endsWith(".ts") && !raw.endsWith(".js")) {
351
+ candidates.push(raw + ".ts");
352
+ candidates.push(raw + ".js");
353
+ candidates.push(posix.join(raw, "index.ts"));
354
+ candidates.push(posix.join(raw, "index.js"));
355
+ }
356
+ for (const cand of candidates) {
357
+ const normalized = normalizeModuleFileKey(cand);
358
+ const found = modulesByFile.get(normalized);
359
+ if (found)
360
+ return found;
361
+ }
362
+ return undefined;
363
+ };
364
+ const resolveReexportModuleKey = (fromFilePath, fromModule) => {
365
+ const fromDir = posix.dirname(normalizeModuleFileKey(fromFilePath));
366
+ return normalizeModuleFileKey(posix.normalize(posix.join(fromDir, fromModule)));
367
+ };
368
+ const isRelativeModuleSpecifier = (specifier) => specifier.startsWith(".") || specifier.startsWith("/");
369
+ const resolveImportedLocalDeclaration = (module, localName, modulesByFileKey, visited) => {
370
+ for (const importEntry of module.imports) {
371
+ for (const specifier of importEntry.specifiers) {
372
+ if (specifier.localName !== localName)
373
+ continue;
374
+ if (specifier.kind === "namespace") {
375
+ return {
376
+ ok: false,
377
+ error: `Unable to re-export '${localName}' from ${module.filePath}: namespace imports are not supported for first-party bindings generation.`,
378
+ };
379
+ }
380
+ if (!importEntry.isLocal) {
381
+ return {
382
+ ok: false,
383
+ error: `Unsupported re-export in ${module.filePath}: '${localName}' resolves to non-local module '${importEntry.source}'.\n` +
384
+ "First-party bindings generation currently supports only local source-module exports.",
385
+ };
386
+ }
387
+ const targetModule = modulesByFileKey.get(resolveReexportModuleKey(module.filePath, importEntry.source));
388
+ if (!targetModule) {
389
+ return {
390
+ ok: false,
391
+ error: `Unable to resolve local import target for '${localName}' in ${module.filePath}: '${importEntry.source}'.\n` +
392
+ "First-party bindings generation requires local import targets to resolve deterministically.",
393
+ };
394
+ }
395
+ const importedName = specifier.kind === "named" ? specifier.name : "default";
396
+ return resolveExportedDeclaration(targetModule, importedName, modulesByFileKey, visited);
397
+ }
398
+ }
399
+ return {
400
+ ok: false,
401
+ error: `Unable to resolve local symbol '${localName}' in ${module.filePath}.\n` +
402
+ "First-party bindings generation requires resolvable local exports and aliases.",
403
+ };
404
+ };
405
+ const resolveExportedDeclaration = (module, exportName, modulesByFileKey, visited = new Set()) => {
406
+ const cycleKey = `${normalizeModuleFileKey(module.filePath)}::${exportName}`;
407
+ if (visited.has(cycleKey)) {
408
+ return {
409
+ ok: false,
410
+ error: `Cyclic re-export detected while resolving '${exportName}' in ${module.filePath}.\n` +
411
+ "First-party bindings generation requires acyclic local re-export graphs.",
412
+ };
413
+ }
414
+ const nextVisited = new Set(visited);
415
+ nextVisited.add(cycleKey);
416
+ for (const item of module.exports) {
417
+ if (item.kind === "declaration") {
418
+ const declaration = item.declaration;
419
+ if (declaration.kind === "variableDeclaration") {
420
+ for (const declarator of declaration.declarations) {
421
+ if (declarator.name.kind !== "identifierPattern")
422
+ continue;
423
+ if (declarator.name.name !== exportName)
424
+ continue;
425
+ return {
426
+ ok: true,
427
+ value: {
428
+ declaration,
429
+ module,
430
+ clrName: declarator.name.name,
431
+ },
432
+ };
433
+ }
434
+ continue;
435
+ }
436
+ const declarationName = declarationNameOf(declaration);
437
+ if (declarationName !== exportName)
438
+ continue;
439
+ return {
440
+ ok: true,
441
+ value: {
442
+ declaration,
443
+ module,
444
+ clrName: declarationName,
445
+ },
446
+ };
447
+ }
448
+ if (item.kind === "named") {
449
+ if (item.name !== exportName)
450
+ continue;
451
+ const declaration = resolveModuleLocalDeclaration(module, item.localName);
452
+ if (declaration) {
453
+ return {
454
+ ok: true,
455
+ value: {
456
+ declaration,
457
+ module,
458
+ clrName: item.localName,
459
+ },
460
+ };
461
+ }
462
+ return resolveImportedLocalDeclaration(module, item.localName, modulesByFileKey, nextVisited);
463
+ }
464
+ if (item.kind === "reexport") {
465
+ if (item.name !== exportName)
466
+ continue;
467
+ if (!isRelativeModuleSpecifier(item.fromModule)) {
468
+ return {
469
+ ok: false,
470
+ error: `Unsupported re-export in ${module.filePath}: '${item.name}' from '${item.fromModule}'.\n` +
471
+ "First-party bindings generation currently supports only relative re-exports from local source modules.",
472
+ };
473
+ }
474
+ const targetModule = modulesByFileKey.get(resolveReexportModuleKey(module.filePath, item.fromModule));
475
+ if (!targetModule) {
476
+ return {
477
+ ok: false,
478
+ error: `Unable to resolve local re-export target for '${item.name}' in ${module.filePath}: '${item.fromModule}'.\n` +
479
+ "First-party bindings generation requires local re-export targets to resolve deterministically.",
480
+ };
481
+ }
482
+ return resolveExportedDeclaration(targetModule, item.originalName, modulesByFileKey, nextVisited);
483
+ }
484
+ if (item.kind === "default" && exportName === "default") {
485
+ return {
486
+ ok: false,
487
+ error: `Unsupported default export in ${module.filePath}.\n` +
488
+ "First-party bindings generation currently requires named/declaration exports for deterministic namespace facades.",
489
+ };
490
+ }
491
+ }
492
+ return {
493
+ ok: false,
494
+ error: `Unable to resolve exported symbol '${exportName}' in ${module.filePath}.\n` +
495
+ "First-party bindings generation requires explicit resolvable exports.",
496
+ };
497
+ };
498
+ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
499
+ if (!existsSync(absoluteFilePath)) {
500
+ return {
501
+ ok: false,
502
+ error: `Failed to read source file for bindings generation: ${absoluteFilePath}`,
503
+ };
504
+ }
505
+ const content = readFileSync(absoluteFilePath, "utf-8");
506
+ const scriptKind = absoluteFilePath.endsWith(".tsx")
507
+ ? ts.ScriptKind.TSX
508
+ : absoluteFilePath.endsWith(".js")
509
+ ? ts.ScriptKind.JS
510
+ : ts.ScriptKind.TS;
511
+ const sourceFile = ts.createSourceFile(absoluteFilePath, content, ts.ScriptTarget.Latest, true, scriptKind);
512
+ const wrapperImportsByLocalName = new Map();
513
+ const typeImportsByLocalName = new Map();
514
+ const typeAliasesByName = new Map();
515
+ const exportedFunctionSignaturesByName = new Map();
516
+ const memberTypesByClassAndMember = new Map();
517
+ const printTypeParametersText = (typeParameters) => {
518
+ if (!typeParameters || typeParameters.length === 0)
519
+ return "";
520
+ return `<${typeParameters.map((tp) => tp.getText(sourceFile)).join(", ")}>`;
521
+ };
522
+ const printParameterText = (param) => {
523
+ const rest = param.dotDotDotToken ? "..." : "";
524
+ const name = param.name.getText(sourceFile);
525
+ const optional = param.questionToken ? "?" : "";
526
+ const type = param.type
527
+ ? printTypeNodeText(param.type, sourceFile)
528
+ : "unknown";
529
+ return `${rest}${name}${optional}: ${type}`;
530
+ };
531
+ const addExportedFunctionSignature = (name, signature) => {
532
+ const signatures = exportedFunctionSignaturesByName.get(name) ?? [];
533
+ signatures.push(signature);
534
+ exportedFunctionSignaturesByName.set(name, signatures);
535
+ };
536
+ for (const stmt of sourceFile.statements) {
537
+ if (ts.isImportDeclaration(stmt)) {
538
+ const moduleSpecifier = ts.isStringLiteral(stmt.moduleSpecifier)
539
+ ? stmt.moduleSpecifier.text
540
+ : undefined;
541
+ if (!moduleSpecifier)
542
+ continue;
543
+ const clause = stmt.importClause;
544
+ if (!clause)
545
+ continue;
546
+ const namedBindings = clause.namedBindings;
547
+ if (!namedBindings || !ts.isNamedImports(namedBindings))
548
+ continue;
549
+ for (const spec of namedBindings.elements) {
550
+ const localName = spec.name.text;
551
+ const importedName = (spec.propertyName ?? spec.name).text;
552
+ const isTypeOnly = clause.isTypeOnly || spec.isTypeOnly;
553
+ if (!isTypeOnly)
554
+ continue;
555
+ typeImportsByLocalName.set(localName, {
556
+ source: moduleSpecifier,
557
+ importedName,
558
+ });
559
+ if (importedName === "ExtensionMethods") {
560
+ wrapperImportsByLocalName.set(localName, {
561
+ source: moduleSpecifier,
562
+ importedName,
563
+ });
564
+ }
565
+ }
566
+ continue;
567
+ }
568
+ if (ts.isTypeAliasDeclaration(stmt)) {
569
+ const aliasName = stmt.name.text;
570
+ const typeParameterNames = (stmt.typeParameters ?? []).map((tp) => tp.name.text);
571
+ typeAliasesByName.set(aliasName, {
572
+ typeParametersText: printTypeParametersText(stmt.typeParameters),
573
+ typeParameterNames,
574
+ type: stmt.type,
575
+ typeText: printTypeNodeText(stmt.type, sourceFile),
576
+ });
577
+ continue;
578
+ }
579
+ if (ts.isFunctionDeclaration(stmt)) {
580
+ const hasExport = stmt.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
581
+ if (!hasExport || !stmt.name || !stmt.type)
582
+ continue;
583
+ const parametersText = stmt.parameters.map(printParameterText).join(", ");
584
+ addExportedFunctionSignature(stmt.name.text, {
585
+ typeParametersText: printTypeParametersText(stmt.typeParameters),
586
+ parametersText,
587
+ returnTypeText: printTypeNodeText(stmt.type, sourceFile),
588
+ });
589
+ continue;
590
+ }
591
+ if (ts.isVariableStatement(stmt)) {
592
+ const hasExport = stmt.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
593
+ if (!hasExport)
594
+ continue;
595
+ for (const declaration of stmt.declarationList.declarations) {
596
+ if (!ts.isIdentifier(declaration.name))
597
+ continue;
598
+ const exportName = declaration.name.text;
599
+ const initializer = declaration.initializer;
600
+ if (!initializer)
601
+ continue;
602
+ if (!ts.isArrowFunction(initializer) &&
603
+ !ts.isFunctionExpression(initializer)) {
604
+ continue;
605
+ }
606
+ if (!initializer.type)
607
+ continue;
608
+ const parametersText = initializer.parameters
609
+ .map(printParameterText)
610
+ .join(", ");
611
+ addExportedFunctionSignature(exportName, {
612
+ typeParametersText: printTypeParametersText(initializer.typeParameters),
613
+ parametersText,
614
+ returnTypeText: printTypeNodeText(initializer.type, sourceFile),
615
+ });
616
+ }
617
+ continue;
618
+ }
619
+ if (ts.isClassDeclaration(stmt) && stmt.name) {
620
+ const className = stmt.name.text;
621
+ const members = memberTypesByClassAndMember.get(className) ??
622
+ new Map();
623
+ for (const member of stmt.members) {
624
+ if (ts.isGetAccessorDeclaration(member)) {
625
+ if (!member.name || !member.type)
626
+ continue;
627
+ const name = getPropertyNameText(member.name);
628
+ if (!name)
629
+ continue;
630
+ members.set(name, {
631
+ typeNode: member.type,
632
+ typeText: printTypeNodeText(member.type, sourceFile),
633
+ isOptional: false,
634
+ });
635
+ continue;
636
+ }
637
+ if (ts.isPropertyDeclaration(member)) {
638
+ if (!member.name || !member.type)
639
+ continue;
640
+ const name = getPropertyNameText(member.name);
641
+ if (!name)
642
+ continue;
643
+ members.set(name, {
644
+ typeNode: member.type,
645
+ typeText: printTypeNodeText(member.type, sourceFile),
646
+ isOptional: member.questionToken !== undefined,
647
+ });
648
+ }
649
+ }
650
+ if (members.size > 0) {
651
+ memberTypesByClassAndMember.set(className, members);
652
+ }
653
+ continue;
654
+ }
655
+ if (ts.isInterfaceDeclaration(stmt)) {
656
+ const interfaceName = stmt.name.text;
657
+ const members = memberTypesByClassAndMember.get(interfaceName) ??
658
+ new Map();
659
+ for (const member of stmt.members) {
660
+ if (!ts.isPropertySignature(member))
661
+ continue;
662
+ if (!member.name || !member.type)
663
+ continue;
664
+ const name = getPropertyNameText(member.name);
665
+ if (!name)
666
+ continue;
667
+ members.set(name, {
668
+ typeNode: member.type,
669
+ typeText: printTypeNodeText(member.type, sourceFile),
670
+ isOptional: member.questionToken !== undefined,
671
+ });
672
+ }
673
+ if (members.size > 0) {
674
+ memberTypesByClassAndMember.set(interfaceName, members);
675
+ }
676
+ }
677
+ }
678
+ return {
679
+ ok: true,
680
+ value: {
681
+ fileKey,
682
+ wrapperImportsByLocalName,
683
+ typeImportsByLocalName,
684
+ typeAliasesByName,
685
+ exportedFunctionSignaturesByName,
686
+ memberTypesByClassAndMember,
687
+ },
688
+ };
689
+ };
690
+ const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
691
+ const allowlistedImportSources = new Set(["@tsonic/core/types.js"]);
692
+ let found = false;
693
+ const visit = (current) => {
694
+ if (found)
695
+ return;
696
+ if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
697
+ const imported = typeImportsByLocalName.get(current.typeName.text);
698
+ if (imported && !allowlistedImportSources.has(imported.source.trim())) {
699
+ found = true;
700
+ return;
701
+ }
702
+ }
703
+ ts.forEachChild(current, visit);
704
+ };
705
+ visit(node);
706
+ return found;
707
+ };
708
+ const unwrapParens = (node) => {
709
+ let current = node;
710
+ while (ts.isParenthesizedTypeNode(current)) {
711
+ current = current.type;
712
+ }
713
+ return current;
714
+ };
715
+ const collectExtensionWrapperImportsFromSourceType = (opts) => {
716
+ const wrappers = [];
717
+ let currentModuleKey = opts.startModuleKey;
718
+ let currentNode = opts.typeNode;
719
+ let subst = new Map();
720
+ const aliasStack = [];
721
+ while (true) {
722
+ currentNode = unwrapParens(currentNode);
723
+ if (!ts.isTypeReferenceNode(currentNode))
724
+ break;
725
+ if (!ts.isIdentifier(currentNode.typeName))
726
+ break;
727
+ const ident = currentNode.typeName.text;
728
+ const info = opts.sourceIndexByFileKey.get(currentModuleKey);
729
+ if (!info)
730
+ break;
731
+ const substituted = subst.get(ident);
732
+ if (substituted) {
733
+ currentNode = substituted;
734
+ continue;
735
+ }
736
+ const expandAlias = (aliasKey, alias, typeArgs) => {
737
+ if (aliasStack.includes(aliasKey))
738
+ return;
739
+ aliasStack.push(aliasKey);
740
+ if (alias.typeParameterNames.length === typeArgs.length) {
741
+ const next = new Map(subst);
742
+ for (let i = 0; i < alias.typeParameterNames.length; i += 1) {
743
+ const paramName = alias.typeParameterNames[i];
744
+ const arg = typeArgs[i];
745
+ if (!paramName || !arg)
746
+ continue;
747
+ next.set(paramName, arg);
748
+ }
749
+ subst = next;
750
+ }
751
+ currentNode = alias.type;
752
+ };
753
+ const localAlias = info.typeAliasesByName.get(ident);
754
+ if (localAlias) {
755
+ expandAlias(`${currentModuleKey}:${ident}`, localAlias, currentNode.typeArguments ?? []);
756
+ continue;
757
+ }
758
+ const imported = info.typeImportsByLocalName.get(ident);
759
+ if (imported &&
760
+ (imported.source.startsWith(".") || imported.source.startsWith("/"))) {
761
+ const targetModule = resolveLocalModuleFile(imported.source, currentModuleKey, opts.modulesByFileKey);
762
+ if (targetModule) {
763
+ const targetKey = normalizeModuleFileKey(targetModule.filePath);
764
+ const targetInfo = opts.sourceIndexByFileKey.get(targetKey);
765
+ const targetAlias = targetInfo?.typeAliasesByName.get(imported.importedName);
766
+ if (targetAlias) {
767
+ currentModuleKey = targetKey;
768
+ expandAlias(`${targetKey}:${imported.importedName}`, targetAlias, currentNode.typeArguments ?? []);
769
+ continue;
770
+ }
771
+ }
772
+ }
773
+ const wrapperImport = info.wrapperImportsByLocalName.get(ident);
774
+ if (!wrapperImport)
775
+ break;
776
+ const args = currentNode.typeArguments ?? [];
777
+ if (args.length !== 1) {
778
+ return {
779
+ ok: false,
780
+ error: `ExtensionMethods wrapper '${ident}' must have exactly 1 type argument.\n` +
781
+ `Found: ${args.length} in ${currentModuleKey}.`,
782
+ };
783
+ }
784
+ wrappers.push({
785
+ source: wrapperImport.source,
786
+ importedName: wrapperImport.importedName,
787
+ localName: ident,
788
+ aliasName: `__TsonicExt_${ident}`,
789
+ });
790
+ const nextNode = args[0];
791
+ if (!nextNode) {
792
+ return {
793
+ ok: false,
794
+ error: `ExtensionMethods wrapper '${ident}' is missing its type argument in ${currentModuleKey}.`,
795
+ };
796
+ }
797
+ currentNode = nextNode;
798
+ }
799
+ return { ok: true, value: wrappers };
800
+ };
801
+ const classifyExportKind = (module, name) => {
802
+ const isNamed = (stmt) => typeof stmt.name === "string";
803
+ const findDecl = () => {
804
+ for (const stmt of module.body) {
805
+ if (!("isExported" in stmt) ||
806
+ stmt.isExported !== true)
807
+ continue;
808
+ if (isNamed(stmt) && stmt.name === name)
809
+ return stmt;
810
+ if (stmt.kind === "variableDeclaration") {
811
+ for (const decl of stmt.declarations) {
812
+ if (decl.name.kind === "identifierPattern" &&
813
+ decl.name.name === name) {
814
+ return stmt;
815
+ }
816
+ }
817
+ }
818
+ }
819
+ return undefined;
820
+ };
821
+ const decl = findDecl();
822
+ if (!decl)
823
+ return "unknown";
824
+ switch (decl.kind) {
825
+ case "typeAliasDeclaration":
826
+ case "interfaceDeclaration":
827
+ return "type";
828
+ case "classDeclaration":
829
+ case "enumDeclaration":
830
+ case "functionDeclaration":
831
+ case "variableDeclaration":
832
+ return "value";
833
+ default:
834
+ return "unknown";
835
+ }
836
+ };
837
+ const moduleNamespaceToInternalSpecifier = (namespace) => {
838
+ const nsPath = moduleNamespacePath(namespace);
839
+ return `./${nsPath}/internal/index.js`;
840
+ };
841
+ const toClrTypeName = (namespace, typeName, arity) => {
842
+ const suffix = arity && arity > 0 ? `\`${arity}` : "";
843
+ return `${namespace}.${typeName}${suffix}`;
844
+ };
845
+ const toStableId = (assemblyName, clrName) => {
846
+ return `${assemblyName}:${clrName}`;
847
+ };
848
+ const primitiveSignatureType = (name) => {
849
+ const map = {
850
+ string: "System.String",
851
+ boolean: "System.Boolean",
852
+ number: "System.Double",
853
+ int: "System.Int32",
854
+ char: "System.Char",
855
+ null: "System.Object",
856
+ undefined: "System.Object",
857
+ };
858
+ return map[name] ?? name;
859
+ };
860
+ const isNumericValueType = (name) => {
861
+ return (name === "System.Int32" ||
862
+ name === "System.Double" ||
863
+ name === "System.Single" ||
864
+ name === "System.Decimal" ||
865
+ name === "System.Int64" ||
866
+ name === "System.Int16" ||
867
+ name === "System.UInt16" ||
868
+ name === "System.UInt32" ||
869
+ name === "System.UInt64" ||
870
+ name === "System.Byte" ||
871
+ name === "System.SByte");
872
+ };
873
+ const toSignatureType = (type, typeParametersInScope) => {
874
+ if (!type)
875
+ return "System.Object";
876
+ switch (type.kind) {
877
+ case "primitiveType":
878
+ return primitiveSignatureType(type.name);
879
+ case "literalType":
880
+ if (typeof type.value === "string")
881
+ return "System.String";
882
+ if (typeof type.value === "boolean")
883
+ return "System.Boolean";
884
+ if (typeof type.value === "number")
885
+ return "System.Double";
886
+ return "System.Object";
887
+ case "voidType":
888
+ return "System.Void";
889
+ case "neverType":
890
+ case "unknownType":
891
+ case "anyType":
892
+ return "System.Object";
893
+ case "typeParameterType":
894
+ return type.name;
895
+ case "arrayType":
896
+ return `${toSignatureType(type.elementType, typeParametersInScope)}[]`;
897
+ case "tupleType":
898
+ case "objectType":
899
+ case "functionType":
900
+ case "dictionaryType":
901
+ return "System.Object";
902
+ case "intersectionType":
903
+ return toSignatureType(type.types[0], typeParametersInScope);
904
+ case "unionType": {
905
+ const nonUndefined = type.types.filter((candidate) => {
906
+ return !(candidate.kind === "primitiveType" && candidate.name === "undefined");
907
+ });
908
+ if (nonUndefined.length === 1 && nonUndefined[0]) {
909
+ const single = toSignatureType(nonUndefined[0], typeParametersInScope);
910
+ if (isNumericValueType(single)) {
911
+ return `System.Nullable\`1[[${single}]]`;
912
+ }
913
+ return single;
914
+ }
915
+ return "System.Object";
916
+ }
917
+ case "referenceType": {
918
+ if (typeParametersInScope.includes(type.name))
919
+ return type.name;
920
+ const normalizedName = normalizeTypeReferenceName(type.name, type.typeArguments?.length);
921
+ if (!type.typeArguments || type.typeArguments.length === 0) {
922
+ return normalizedName;
923
+ }
924
+ const args = type.typeArguments
925
+ .map((arg) => toSignatureType(arg, typeParametersInScope))
926
+ .join(",");
927
+ return `${normalizedName}[[${args}]]`;
928
+ }
929
+ default:
930
+ return "System.Object";
931
+ }
932
+ };
933
+ const buildParameterModifiers = (parameters) => {
934
+ const modifiers = parameters
935
+ .map((parameter, index) => {
936
+ if (parameter.passing === "value")
937
+ return undefined;
938
+ return { index, modifier: parameter.passing };
939
+ })
940
+ .filter((modifier) => modifier !== undefined);
941
+ return modifiers;
942
+ };
943
+ const makeMethodBinding = (opts) => {
944
+ const typeParameterScope = Array.from(new Set(opts.parameters
945
+ .map((parameter) => parameter.type?.kind === "typeParameterType"
946
+ ? parameter.type.name
947
+ : undefined)
948
+ .filter((name) => name !== undefined)));
949
+ const normalizedSignature = `${opts.methodName}|(${opts.parameters
950
+ .map((parameter) => toSignatureType(parameter.type, typeParameterScope))
951
+ .join(",")}):${toSignatureType(opts.returnType, typeParameterScope)}|static=${opts.isStatic ? "true" : "false"}`;
952
+ const stableId = `${toStableId(opts.declaringAssemblyName, opts.declaringClrType)}::method:${opts.methodName}|${normalizedSignature}`;
953
+ return {
954
+ stableId,
955
+ clrName: opts.methodName,
956
+ normalizedSignature,
957
+ arity: opts.arity,
958
+ parameterCount: opts.parameters.length,
959
+ isStatic: opts.isStatic,
960
+ isAbstract: opts.isAbstract ?? false,
961
+ isVirtual: opts.isVirtual ?? false,
962
+ isOverride: opts.isOverride ?? false,
963
+ isSealed: opts.isSealed ?? false,
964
+ declaringClrType: opts.declaringClrType,
965
+ declaringAssemblyName: opts.declaringAssemblyName,
966
+ parameterModifiers: opts.parameterModifiers.length > 0 ? opts.parameterModifiers : undefined,
967
+ isExtensionMethod: false,
968
+ };
969
+ };
970
+ const renderClassInternal = (declaration, namespace, memberOverrides) => {
971
+ const lines = [];
972
+ const typeParameterScope = (declaration.typeParameters ?? []).map((typeParameter) => typeParameter.name);
973
+ const typeParameters = printTypeParameters(declaration.typeParameters);
974
+ const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(declaration.name)}`;
975
+ const heritageNames = [
976
+ declaration.superClass
977
+ ? renderPortableType(declaration.superClass, typeParameterScope)
978
+ : undefined,
979
+ ...declaration.implements.map((implementedType) => renderPortableType(implementedType, typeParameterScope)),
980
+ ]
981
+ .filter((name) => name !== undefined)
982
+ .map((name) => name.trim())
983
+ .filter((name) => name.length > 0 &&
984
+ name !== "unknown" &&
985
+ name !== "never" &&
986
+ name !== "void");
987
+ const extendsClause = heritageNames.length > 0
988
+ ? ` extends ${Array.from(new Set(heritageNames)).join(", ")}`
989
+ : "";
990
+ lines.push(`export interface ${declaration.name}$instance${typeParameters}${extendsClause} {`);
991
+ lines.push(` readonly ${markerName}: never;`);
992
+ const instanceMembers = declaration.members.filter((member) => {
993
+ if (member.kind === "constructorDeclaration")
994
+ return false;
995
+ if ("isStatic" in member && member.isStatic)
996
+ return false;
997
+ return true;
998
+ });
999
+ for (const member of instanceMembers) {
1000
+ if (member.kind === "methodDeclaration") {
1001
+ lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
1002
+ continue;
1003
+ }
1004
+ if (member.kind === "propertyDeclaration") {
1005
+ const memberOverride = memberOverrides.get(member.name);
1006
+ const hasAccessorBody = member.getterBody !== undefined || member.setterBody !== undefined;
1007
+ const hasGetter = hasAccessorBody
1008
+ ? member.getterBody !== undefined
1009
+ : true;
1010
+ const hasSetter = hasAccessorBody
1011
+ ? member.setterBody !== undefined
1012
+ : !member.isReadonly;
1013
+ const baseType = (memberOverride?.replaceWithSourceType
1014
+ ? memberOverride.sourceTypeText
1015
+ : undefined) ?? renderPortableType(member.type);
1016
+ const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
1017
+ const memberType = memberOverride?.isOptional === true
1018
+ ? ensureUndefinedInType(wrappedType)
1019
+ : wrappedType;
1020
+ if (hasGetter && !hasSetter) {
1021
+ lines.push(` readonly ${member.name}: ${memberType};`);
1022
+ continue;
1023
+ }
1024
+ lines.push(` ${member.name}: ${memberType};`);
1025
+ }
1026
+ }
1027
+ lines.push("}");
1028
+ lines.push("");
1029
+ lines.push(`export const ${declaration.name}: {`);
1030
+ lines.push(` new(...args: unknown[]): ${declaration.name}${typeParameters};`);
1031
+ const staticMembers = declaration.members.filter((member) => {
1032
+ if (member.kind === "constructorDeclaration")
1033
+ return false;
1034
+ return "isStatic" in member && member.isStatic;
1035
+ });
1036
+ for (const member of staticMembers) {
1037
+ if (member.kind === "methodDeclaration") {
1038
+ lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
1039
+ continue;
1040
+ }
1041
+ if (member.kind === "propertyDeclaration") {
1042
+ lines.push(` ${member.name}: ${renderPortableType(member.type)};`);
1043
+ }
1044
+ }
1045
+ lines.push("};");
1046
+ lines.push("");
1047
+ lines.push(`export type ${declaration.name}${typeParameters} = ${declaration.name}$instance${typeParameters};`);
1048
+ lines.push("");
1049
+ return lines;
1050
+ };
1051
+ const renderInterfaceInternal = (declaration, namespace, memberOverrides) => {
1052
+ const lines = [];
1053
+ const typeParameterScope = (declaration.typeParameters ?? []).map((typeParameter) => typeParameter.name);
1054
+ const typeParameters = printTypeParameters(declaration.typeParameters);
1055
+ const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(declaration.name)}`;
1056
+ const extendsNames = declaration.extends
1057
+ .map((baseType) => renderPortableType(baseType, typeParameterScope).trim())
1058
+ .filter((name) => name.length > 0 &&
1059
+ name !== "unknown" &&
1060
+ name !== "never" &&
1061
+ name !== "void");
1062
+ const extendsClause = extendsNames.length > 0
1063
+ ? ` extends ${Array.from(new Set(extendsNames)).join(", ")}`
1064
+ : "";
1065
+ lines.push(`export interface ${declaration.name}$instance${typeParameters}${extendsClause} {`);
1066
+ lines.push(` readonly ${markerName}?: never;`);
1067
+ for (const member of declaration.members) {
1068
+ if (member.kind === "methodSignature") {
1069
+ lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
1070
+ continue;
1071
+ }
1072
+ if (member.kind === "propertySignature") {
1073
+ const memberOverride = memberOverrides.get(member.name);
1074
+ const optionalBySource = memberOverride?.emitOptionalPropertySyntax === true &&
1075
+ memberOverride.isOptional === true &&
1076
+ !member.name.startsWith("__tsonic_type_");
1077
+ const optionalMark = optionalBySource || member.isOptional ? "?" : "";
1078
+ const baseType = (memberOverride?.replaceWithSourceType
1079
+ ? memberOverride.sourceTypeText
1080
+ : undefined) ?? renderPortableType(member.type);
1081
+ const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
1082
+ const memberType = memberOverride?.isOptional && !optionalBySource
1083
+ ? ensureUndefinedInType(wrappedType)
1084
+ : wrappedType;
1085
+ lines.push(` ${member.name}${optionalMark}: ${memberType};`);
1086
+ }
1087
+ }
1088
+ lines.push("}");
1089
+ lines.push("");
1090
+ lines.push(`export type ${declaration.name}${typeParameters} = ${declaration.name}$instance${typeParameters};`);
1091
+ lines.push("");
1092
+ return lines;
1093
+ };
1094
+ const renderEnumInternal = (declaration) => {
1095
+ const lines = [];
1096
+ lines.push(`export enum ${declaration.name} {`);
1097
+ declaration.members.forEach((member, index) => {
1098
+ lines.push(` ${member.name} = ${index},`);
1099
+ });
1100
+ lines.push("}");
1101
+ lines.push("");
1102
+ return lines;
1103
+ };
1104
+ const renderStructuralAliasInternal = (declaration, namespace, memberOverrides) => {
1105
+ if (declaration.type.kind !== "objectType")
1106
+ return [];
1107
+ const lines = [];
1108
+ const arity = declaration.typeParameters?.length ?? 0;
1109
+ const typeParameters = printTypeParameters(declaration.typeParameters);
1110
+ const internalAliasName = `${declaration.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
1111
+ const markerName = `__tsonic_type_${sanitizeForBrand(namespace)}_${sanitizeForBrand(internalAliasName)}`;
1112
+ lines.push(`export interface ${internalAliasName}$instance${typeParameters} {`);
1113
+ lines.push(` readonly ${markerName}?: never;`);
1114
+ for (const member of declaration.type.members) {
1115
+ if (member.kind === "methodSignature") {
1116
+ lines.push(` ${renderMethodSignature(member.name, member.typeParameters, member.parameters, member.returnType)}`);
1117
+ continue;
1118
+ }
1119
+ if (member.kind === "propertySignature") {
1120
+ const memberOverride = memberOverrides.get(member.name);
1121
+ const optionalMark = member.isOptional ? "?" : "";
1122
+ const baseType = (memberOverride?.replaceWithSourceType
1123
+ ? memberOverride.sourceTypeText
1124
+ : undefined) ?? renderPortableType(member.type);
1125
+ const wrappedType = applyWrappersToBaseType(baseType, memberOverride?.wrappers ?? []);
1126
+ const memberType = memberOverride?.isOptional === true
1127
+ ? ensureUndefinedInType(wrappedType)
1128
+ : wrappedType;
1129
+ lines.push(` ${member.name}${optionalMark}: ${memberType};`);
1130
+ }
1131
+ }
1132
+ lines.push("}");
1133
+ lines.push("");
1134
+ lines.push(`export type ${internalAliasName}${typeParameters} = ${internalAliasName}$instance${typeParameters};`);
1135
+ lines.push("");
1136
+ return lines;
1137
+ };
1138
+ const renderContainerInternal = (entry) => {
1139
+ const lines = [];
1140
+ lines.push(`export abstract class ${entry.module.className}$instance {`);
1141
+ for (const method of entry.methods) {
1142
+ lines.push(` static ${renderMethodSignature(method.localName, method.declaration.typeParameters, method.declaration.parameters, method.declaration.returnType)}`);
1143
+ }
1144
+ for (const variable of entry.variables) {
1145
+ lines.push(` static ${variable.localName}: ${renderPortableType(variable.declarator?.type)};`);
1146
+ }
1147
+ lines.push("}");
1148
+ lines.push("");
1149
+ lines.push(`export type ${entry.module.className} = ${entry.module.className}$instance;`);
1150
+ lines.push("");
1151
+ return lines;
1152
+ };
1153
+ const buildTypeBindingFromClass = (declaration, namespace, assemblyName) => {
1154
+ const declaringClrType = toClrTypeName(namespace, declaration.name, declaration.typeParameters?.length ?? 0);
1155
+ const typeStableId = toStableId(assemblyName, declaringClrType);
1156
+ const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1157
+ [];
1158
+ const methods = [];
1159
+ const properties = [];
1160
+ const constructors = [];
1161
+ for (const member of declaration.members) {
1162
+ if (member.kind === "constructorDeclaration") {
1163
+ constructors.push({
1164
+ normalizedSignature: `.ctor|(${member.parameters
1165
+ .map((parameter) => toSignatureType(parameter.type, typeParameterScope))
1166
+ .join(",")})|static=false`,
1167
+ isStatic: false,
1168
+ parameterCount: member.parameters.length,
1169
+ });
1170
+ continue;
1171
+ }
1172
+ if (member.kind === "methodDeclaration") {
1173
+ methods.push(makeMethodBinding({
1174
+ declaringClrType,
1175
+ declaringAssemblyName: assemblyName,
1176
+ methodName: member.name,
1177
+ parameters: member.parameters,
1178
+ returnType: member.returnType,
1179
+ arity: member.typeParameters?.length ?? 0,
1180
+ parameterModifiers: buildParameterModifiers(member.parameters),
1181
+ isStatic: member.isStatic,
1182
+ isAbstract: member.body === undefined,
1183
+ isVirtual: member.isVirtual,
1184
+ isOverride: member.isOverride,
1185
+ }));
1186
+ continue;
1187
+ }
1188
+ if (member.kind === "propertyDeclaration") {
1189
+ const hasAccessorBody = member.getterBody !== undefined || member.setterBody !== undefined;
1190
+ const hasGetter = hasAccessorBody
1191
+ ? member.getterBody !== undefined
1192
+ : true;
1193
+ const hasSetter = hasAccessorBody
1194
+ ? member.setterBody !== undefined
1195
+ : !member.isReadonly;
1196
+ const propertyType = toSignatureType(member.type, typeParameterScope);
1197
+ properties.push({
1198
+ stableId: `${typeStableId}::property:${member.name}`,
1199
+ clrName: member.name,
1200
+ normalizedSignature: `${member.name}|:${propertyType}|static=${member.isStatic ? "true" : "false"}|accessor=${hasGetter && hasSetter ? "getset" : hasSetter ? "set" : "get"}`,
1201
+ isStatic: member.isStatic,
1202
+ isAbstract: member.getterBody === undefined && member.setterBody === undefined
1203
+ ? false
1204
+ : false,
1205
+ isVirtual: member.isVirtual ?? false,
1206
+ isOverride: member.isOverride ?? false,
1207
+ isIndexer: false,
1208
+ hasGetter,
1209
+ hasSetter,
1210
+ declaringClrType,
1211
+ declaringAssemblyName: assemblyName,
1212
+ });
1213
+ }
1214
+ }
1215
+ return {
1216
+ stableId: typeStableId,
1217
+ clrName: declaringClrType,
1218
+ assemblyName,
1219
+ kind: declaration.isStruct ? "Struct" : "Class",
1220
+ accessibility: "Public",
1221
+ isAbstract: false,
1222
+ isSealed: false,
1223
+ isStatic: false,
1224
+ arity: declaration.typeParameters?.length ?? 0,
1225
+ typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1226
+ [],
1227
+ methods,
1228
+ properties,
1229
+ fields: [],
1230
+ events: [],
1231
+ constructors: constructors.length > 0
1232
+ ? constructors
1233
+ : [
1234
+ {
1235
+ normalizedSignature: ".ctor|()|static=false",
1236
+ isStatic: false,
1237
+ parameterCount: 0,
1238
+ },
1239
+ ],
1240
+ };
1241
+ };
1242
+ const buildTypeBindingFromInterface = (declaration, namespace, assemblyName) => {
1243
+ const declaringClrType = toClrTypeName(namespace, declaration.name, declaration.typeParameters?.length ?? 0);
1244
+ const typeStableId = toStableId(assemblyName, declaringClrType);
1245
+ const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1246
+ [];
1247
+ const methods = [];
1248
+ const properties = [];
1249
+ for (const member of declaration.members) {
1250
+ if (member.kind === "methodSignature") {
1251
+ methods.push(makeMethodBinding({
1252
+ declaringClrType,
1253
+ declaringAssemblyName: assemblyName,
1254
+ methodName: member.name,
1255
+ parameters: member.parameters,
1256
+ returnType: member.returnType,
1257
+ arity: member.typeParameters?.length ?? 0,
1258
+ parameterModifiers: buildParameterModifiers(member.parameters),
1259
+ isStatic: false,
1260
+ isAbstract: true,
1261
+ }));
1262
+ continue;
1263
+ }
1264
+ properties.push({
1265
+ stableId: `${typeStableId}::property:${member.name}`,
1266
+ clrName: member.name,
1267
+ normalizedSignature: `${member.name}|:${toSignatureType(member.type, typeParameterScope)}|static=false|accessor=${member.isReadonly ? "get" : "getset"}`,
1268
+ isStatic: false,
1269
+ isAbstract: true,
1270
+ isVirtual: false,
1271
+ isOverride: false,
1272
+ isIndexer: false,
1273
+ hasGetter: true,
1274
+ hasSetter: !member.isReadonly,
1275
+ declaringClrType,
1276
+ declaringAssemblyName: assemblyName,
1277
+ });
1278
+ }
1279
+ return {
1280
+ stableId: typeStableId,
1281
+ clrName: declaringClrType,
1282
+ assemblyName,
1283
+ kind: declaration.isStruct ? "Struct" : "Interface",
1284
+ accessibility: "Public",
1285
+ isAbstract: false,
1286
+ isSealed: false,
1287
+ isStatic: false,
1288
+ arity: declaration.typeParameters?.length ?? 0,
1289
+ typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1290
+ [],
1291
+ methods,
1292
+ properties,
1293
+ fields: [],
1294
+ events: [],
1295
+ constructors: [],
1296
+ };
1297
+ };
1298
+ const buildTypeBindingFromEnum = (declaration, namespace, assemblyName) => {
1299
+ const declaringClrType = toClrTypeName(namespace, declaration.name);
1300
+ const typeStableId = toStableId(assemblyName, declaringClrType);
1301
+ const fields = declaration.members.map((member) => ({
1302
+ stableId: `${typeStableId}::field:${member.name}`,
1303
+ clrName: member.name,
1304
+ normalizedSignature: `${member.name}|${declaringClrType}|static=true|const=true`,
1305
+ isStatic: true,
1306
+ isReadOnly: true,
1307
+ isLiteral: true,
1308
+ declaringClrType,
1309
+ declaringAssemblyName: assemblyName,
1310
+ }));
1311
+ return {
1312
+ stableId: typeStableId,
1313
+ clrName: declaringClrType,
1314
+ assemblyName,
1315
+ kind: "Enum",
1316
+ accessibility: "Public",
1317
+ isAbstract: false,
1318
+ isSealed: true,
1319
+ isStatic: false,
1320
+ arity: 0,
1321
+ typeParameters: [],
1322
+ methods: [],
1323
+ properties: [],
1324
+ fields,
1325
+ events: [],
1326
+ constructors: [],
1327
+ };
1328
+ };
1329
+ const buildTypeBindingFromStructuralAlias = (declaration, namespace, assemblyName) => {
1330
+ if (declaration.type.kind !== "objectType")
1331
+ return undefined;
1332
+ const arity = declaration.typeParameters?.length ?? 0;
1333
+ const internalAliasName = `${declaration.name}__Alias`;
1334
+ const declaringClrType = toClrTypeName(namespace, internalAliasName, arity);
1335
+ const typeStableId = toStableId(assemblyName, declaringClrType);
1336
+ const typeParameterScope = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1337
+ [];
1338
+ const methods = [];
1339
+ const properties = [];
1340
+ for (const member of declaration.type.members) {
1341
+ if (member.kind === "methodSignature") {
1342
+ methods.push(makeMethodBinding({
1343
+ declaringClrType,
1344
+ declaringAssemblyName: assemblyName,
1345
+ methodName: member.name,
1346
+ parameters: member.parameters,
1347
+ returnType: member.returnType,
1348
+ arity: member.typeParameters?.length ?? 0,
1349
+ parameterModifiers: buildParameterModifiers(member.parameters),
1350
+ isStatic: false,
1351
+ isAbstract: true,
1352
+ }));
1353
+ continue;
1354
+ }
1355
+ properties.push({
1356
+ stableId: `${typeStableId}::property:${member.name}`,
1357
+ clrName: member.name,
1358
+ normalizedSignature: `${member.name}|:${toSignatureType(member.type, typeParameterScope)}|static=false|accessor=${member.isReadonly ? "get" : "getset"}`,
1359
+ isStatic: false,
1360
+ isAbstract: true,
1361
+ isVirtual: false,
1362
+ isOverride: false,
1363
+ isIndexer: false,
1364
+ hasGetter: true,
1365
+ hasSetter: !member.isReadonly,
1366
+ declaringClrType,
1367
+ declaringAssemblyName: assemblyName,
1368
+ });
1369
+ }
1370
+ return {
1371
+ stableId: typeStableId,
1372
+ clrName: declaringClrType,
1373
+ assemblyName,
1374
+ kind: declaration.isStruct ? "Struct" : "Class",
1375
+ accessibility: "Public",
1376
+ isAbstract: false,
1377
+ isSealed: false,
1378
+ isStatic: false,
1379
+ arity,
1380
+ typeParameters: declaration.typeParameters?.map((typeParameter) => typeParameter.name) ??
1381
+ [],
1382
+ methods,
1383
+ properties,
1384
+ fields: [],
1385
+ events: [],
1386
+ constructors: [],
1387
+ };
1388
+ };
1389
+ const buildTypeBindingFromContainer = (entry, namespace, assemblyName) => {
1390
+ const declaringClrType = toClrTypeName(namespace, entry.module.className);
1391
+ const typeStableId = toStableId(assemblyName, declaringClrType);
1392
+ const methods = entry.methods.map((method) => makeMethodBinding({
1393
+ declaringClrType,
1394
+ declaringAssemblyName: assemblyName,
1395
+ methodName: method.localName,
1396
+ parameters: method.declaration.parameters,
1397
+ returnType: method.declaration.returnType,
1398
+ arity: method.declaration.typeParameters?.length ?? 0,
1399
+ parameterModifiers: buildParameterModifiers(method.declaration.parameters),
1400
+ isStatic: true,
1401
+ }));
1402
+ const properties = entry.variables.map((variable) => ({
1403
+ stableId: `${typeStableId}::property:${variable.localName}`,
1404
+ clrName: variable.localName,
1405
+ normalizedSignature: `${variable.localName}|:${toSignatureType(variable.declarator?.type, [])}|static=true|accessor=getset`,
1406
+ isStatic: true,
1407
+ isAbstract: false,
1408
+ isVirtual: false,
1409
+ isOverride: false,
1410
+ isIndexer: false,
1411
+ hasGetter: true,
1412
+ hasSetter: true,
1413
+ declaringClrType,
1414
+ declaringAssemblyName: assemblyName,
1415
+ }));
1416
+ return {
1417
+ stableId: typeStableId,
1418
+ clrName: declaringClrType,
1419
+ assemblyName,
1420
+ kind: "Class",
1421
+ accessibility: "Public",
1422
+ isAbstract: true,
1423
+ isSealed: false,
1424
+ isStatic: true,
1425
+ arity: 0,
1426
+ typeParameters: [],
1427
+ methods,
1428
+ properties,
1429
+ fields: [],
1430
+ events: [],
1431
+ constructors: [],
1432
+ };
1433
+ };
1434
+ const collectNamespacePlans = (modules, assemblyName, rootNamespace, sourceIndexByFileKey) => {
1435
+ const modulesByNamespace = new Map();
1436
+ modulesByNamespace.set(rootNamespace, []);
1437
+ const modulesByFileKey = new Map();
1438
+ for (const module of modules) {
1439
+ const syntheticAnonymousModule = module.filePath.startsWith("__tsonic/") &&
1440
+ module.body.some((statement) => statement.kind === "classDeclaration" &&
1441
+ statement.name.startsWith("__Anon_"));
1442
+ if (module.filePath.startsWith("__tsonic/") && !syntheticAnonymousModule) {
1443
+ continue;
1444
+ }
1445
+ const list = modulesByNamespace.get(module.namespace) ?? [];
1446
+ list.push(module);
1447
+ modulesByNamespace.set(module.namespace, list);
1448
+ modulesByFileKey.set(normalizeModuleFileKey(module.filePath), module);
1449
+ }
1450
+ const plans = [];
1451
+ for (const [namespace, moduleList] of Array.from(modulesByNamespace.entries())) {
1452
+ const typeDeclarations = [];
1453
+ const moduleContainers = [];
1454
+ const valueExportsMap = new Map();
1455
+ const seenTypeDeclarationKeys = new Set();
1456
+ const sourceAliasLines = new Set();
1457
+ const sourceAliasInternalImports = new Set();
1458
+ const memberOverrides = [];
1459
+ const wrapperImportByAlias = new Map();
1460
+ const registerValueExport = (valueExport) => {
1461
+ const existing = valueExportsMap.get(valueExport.exportName);
1462
+ if (!existing) {
1463
+ valueExportsMap.set(valueExport.exportName, valueExport);
1464
+ return { ok: true, value: undefined };
1465
+ }
1466
+ const sameBinding = existing.binding.kind === valueExport.binding.kind &&
1467
+ existing.binding.clrName === valueExport.binding.clrName &&
1468
+ existing.binding.declaringClrType ===
1469
+ valueExport.binding.declaringClrType &&
1470
+ existing.binding.declaringAssemblyName ===
1471
+ valueExport.binding.declaringAssemblyName;
1472
+ const normalizeFunctionFacade = (facade) => {
1473
+ const declaration = facade.declaration;
1474
+ const typeParametersText = printTypeParameters(declaration.typeParameters);
1475
+ const typeParameterNames = declaration.typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
1476
+ const parametersText = renderUnknownParameters(declaration.parameters, typeParameterNames);
1477
+ const returnTypeText = renderPortableType(declaration.returnType, typeParameterNames);
1478
+ const sourceSignatures = (facade.sourceSignatures ?? [])
1479
+ .map((signature) => `${signature.typeParametersText}(${signature.parametersText}):${signature.returnTypeText}`)
1480
+ .sort((left, right) => left.localeCompare(right))
1481
+ .join("||");
1482
+ return `${typeParametersText}(${parametersText}):${returnTypeText}|source=${sourceSignatures}`;
1483
+ };
1484
+ const normalizeVariableFacade = (declarator) => renderPortableType(declarator?.type);
1485
+ const sameFacade = (() => {
1486
+ if (existing.facade.kind !== valueExport.facade.kind)
1487
+ return false;
1488
+ if (existing.facade.kind === "function" &&
1489
+ valueExport.facade.kind === "function") {
1490
+ return (normalizeFunctionFacade(existing.facade) ===
1491
+ normalizeFunctionFacade(valueExport.facade));
1492
+ }
1493
+ if (existing.facade.kind === "variable" &&
1494
+ valueExport.facade.kind === "variable") {
1495
+ return (normalizeVariableFacade(existing.facade.declarator) ===
1496
+ normalizeVariableFacade(valueExport.facade.declarator));
1497
+ }
1498
+ return false;
1499
+ })();
1500
+ if (sameBinding && sameFacade) {
1501
+ return { ok: true, value: undefined };
1502
+ }
1503
+ return {
1504
+ ok: false,
1505
+ error: `Conflicting value export '${valueExport.exportName}' in namespace ${namespace}.\n` +
1506
+ "First-party bindings generation requires each exported value name to map deterministically to exactly one CLR member.",
1507
+ };
1508
+ };
1509
+ const registerWrapperImports = (wrappers, moduleFilePath) => {
1510
+ for (const wrapper of wrappers) {
1511
+ const existing = wrapperImportByAlias.get(wrapper.aliasName);
1512
+ if (existing) {
1513
+ if (existing.source !== wrapper.source ||
1514
+ existing.importedName !== wrapper.importedName) {
1515
+ return {
1516
+ ok: false,
1517
+ error: `Conflicting wrapper import alias '${wrapper.aliasName}' while generating ${moduleFilePath}.\n` +
1518
+ `- ${existing.importedName} from '${existing.source}'\n` +
1519
+ `- ${wrapper.importedName} from '${wrapper.source}'\n` +
1520
+ "Disambiguate ExtensionMethods aliases in source code.",
1521
+ };
1522
+ }
1523
+ continue;
1524
+ }
1525
+ wrapperImportByAlias.set(wrapper.aliasName, wrapper);
1526
+ }
1527
+ return { ok: true, value: undefined };
1528
+ };
1529
+ for (const module of moduleList.sort((left, right) => left.filePath.localeCompare(right.filePath))) {
1530
+ const moduleKey = normalizeModuleFileKey(module.filePath);
1531
+ const sourceIndex = sourceIndexByFileKey.get(moduleKey);
1532
+ if (sourceIndex) {
1533
+ const exportedAliasDecls = module.body.filter((stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported);
1534
+ for (const alias of exportedAliasDecls) {
1535
+ const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
1536
+ const sourceTypeParams = sourceAlias?.typeParametersText ??
1537
+ printTypeParameters(alias.typeParameters);
1538
+ if (alias.type.kind === "objectType") {
1539
+ const arity = alias.typeParameters?.length ?? 0;
1540
+ const internalName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
1541
+ const typeArgs = sourceAlias && sourceAlias.typeParameterNames.length > 0
1542
+ ? `<${sourceAlias.typeParameterNames.join(", ")}>`
1543
+ : alias.typeParameters && alias.typeParameters.length > 0
1544
+ ? `<${alias.typeParameters.map((tp) => tp.name).join(", ")}>`
1545
+ : "";
1546
+ sourceAliasLines.add(`export type ${alias.name}${sourceTypeParams} = ${internalName}${typeArgs};`);
1547
+ sourceAliasInternalImports.add(internalName);
1548
+ continue;
1549
+ }
1550
+ const rhs = renderPortableType(alias.type, alias.typeParameters?.map((tp) => tp.name) ?? []);
1551
+ const shouldPreferSourceAliasText = sourceAlias !== undefined &&
1552
+ !typeNodeUsesImportedTypeNames(sourceAlias.type, sourceIndex.typeImportsByLocalName) &&
1553
+ /__\d+\b|\$instance\b/.test(rhs);
1554
+ sourceAliasLines.add(`export type ${alias.name}${sourceTypeParams} = ${shouldPreferSourceAliasText ? sourceAlias.typeText : rhs};`);
1555
+ }
1556
+ const exportedClasses = module.body.filter((stmt) => stmt.kind === "classDeclaration" && stmt.isExported);
1557
+ const exportedInterfaces = module.body.filter((stmt) => stmt.kind === "interfaceDeclaration" && stmt.isExported);
1558
+ const exportedAliases = module.body.filter((stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported);
1559
+ for (const cls of exportedClasses) {
1560
+ const sourceMembers = sourceIndex.memberTypesByClassAndMember.get(cls.name);
1561
+ if (!sourceMembers)
1562
+ continue;
1563
+ for (const member of cls.members) {
1564
+ if (member.kind !== "propertyDeclaration")
1565
+ continue;
1566
+ if (member.isStatic || member.accessibility === "private")
1567
+ continue;
1568
+ const sourceMember = sourceMembers.get(member.name);
1569
+ if (!sourceMember)
1570
+ continue;
1571
+ const wrappersResult = collectExtensionWrapperImportsFromSourceType({
1572
+ startModuleKey: moduleKey,
1573
+ typeNode: sourceMember.typeNode,
1574
+ sourceIndexByFileKey,
1575
+ modulesByFileKey,
1576
+ });
1577
+ if (!wrappersResult.ok)
1578
+ return wrappersResult;
1579
+ const wrappers = wrappersResult.value;
1580
+ const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
1581
+ if (!canUseSourceTypeText &&
1582
+ wrappers.length === 0 &&
1583
+ !sourceMember.isOptional) {
1584
+ continue;
1585
+ }
1586
+ const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
1587
+ if (!wrapperRegistered.ok)
1588
+ return wrapperRegistered;
1589
+ memberOverrides.push({
1590
+ className: cls.name,
1591
+ memberName: member.name,
1592
+ sourceTypeText: canUseSourceTypeText
1593
+ ? sourceMember.typeText
1594
+ : undefined,
1595
+ replaceWithSourceType: canUseSourceTypeText,
1596
+ isOptional: sourceMember.isOptional,
1597
+ wrappers,
1598
+ });
1599
+ }
1600
+ }
1601
+ for (const iface of exportedInterfaces) {
1602
+ const sourceMembers = sourceIndex.memberTypesByClassAndMember.get(iface.name);
1603
+ if (!sourceMembers)
1604
+ continue;
1605
+ for (const member of iface.members) {
1606
+ if (member.kind !== "propertySignature")
1607
+ continue;
1608
+ const sourceMember = sourceMembers.get(member.name);
1609
+ if (!sourceMember)
1610
+ continue;
1611
+ const wrappersResult = collectExtensionWrapperImportsFromSourceType({
1612
+ startModuleKey: moduleKey,
1613
+ typeNode: sourceMember.typeNode,
1614
+ sourceIndexByFileKey,
1615
+ modulesByFileKey,
1616
+ });
1617
+ if (!wrappersResult.ok)
1618
+ return wrappersResult;
1619
+ const wrappers = wrappersResult.value;
1620
+ const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
1621
+ if (!canUseSourceTypeText && wrappers.length === 0)
1622
+ continue;
1623
+ const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
1624
+ if (!wrapperRegistered.ok)
1625
+ return wrapperRegistered;
1626
+ memberOverrides.push({
1627
+ className: iface.name,
1628
+ memberName: member.name,
1629
+ sourceTypeText: canUseSourceTypeText
1630
+ ? sourceMember.typeText
1631
+ : undefined,
1632
+ replaceWithSourceType: canUseSourceTypeText,
1633
+ isOptional: sourceMember.isOptional,
1634
+ emitOptionalPropertySyntax: true,
1635
+ wrappers,
1636
+ });
1637
+ }
1638
+ }
1639
+ for (const alias of exportedAliases) {
1640
+ const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
1641
+ if (!sourceAlias)
1642
+ continue;
1643
+ const aliasType = unwrapParens(sourceAlias.type);
1644
+ if (!ts.isTypeLiteralNode(aliasType))
1645
+ continue;
1646
+ const arity = sourceAlias.typeParameterNames.length;
1647
+ const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
1648
+ for (const member of aliasType.members) {
1649
+ if (!ts.isPropertySignature(member))
1650
+ continue;
1651
+ if (!member.name || !member.type)
1652
+ continue;
1653
+ const memberName = getPropertyNameText(member.name);
1654
+ if (!memberName)
1655
+ continue;
1656
+ const wrappersResult = collectExtensionWrapperImportsFromSourceType({
1657
+ startModuleKey: moduleKey,
1658
+ typeNode: member.type,
1659
+ sourceIndexByFileKey,
1660
+ modulesByFileKey,
1661
+ });
1662
+ if (!wrappersResult.ok)
1663
+ return wrappersResult;
1664
+ const wrappers = wrappersResult.value;
1665
+ const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(member.type, sourceIndex.typeImportsByLocalName);
1666
+ if (!canUseSourceTypeText && wrappers.length === 0)
1667
+ continue;
1668
+ const wrapperRegistered = registerWrapperImports(wrappers, module.filePath);
1669
+ if (!wrapperRegistered.ok)
1670
+ return wrapperRegistered;
1671
+ memberOverrides.push({
1672
+ className: internalAliasName,
1673
+ memberName,
1674
+ sourceTypeText: canUseSourceTypeText
1675
+ ? printTypeNodeText(member.type, member.getSourceFile())
1676
+ : undefined,
1677
+ replaceWithSourceType: canUseSourceTypeText,
1678
+ isOptional: member.questionToken !== undefined,
1679
+ wrappers,
1680
+ });
1681
+ }
1682
+ }
1683
+ }
1684
+ for (const exportItem of module.exports) {
1685
+ if (exportItem.kind !== "reexport")
1686
+ continue;
1687
+ const resolved = resolveExportedDeclaration(module, exportItem.name, modulesByFileKey);
1688
+ if (!resolved.ok)
1689
+ return resolved;
1690
+ const declaration = resolved.value.declaration;
1691
+ const declarationModule = resolved.value.module;
1692
+ const exportKind = classifyDeclarationKind(declaration, declarationModule.filePath, exportItem.name);
1693
+ if (!exportKind.ok)
1694
+ return exportKind;
1695
+ if (exportKind.value === "function") {
1696
+ const functionDeclaration = declaration.kind === "functionDeclaration"
1697
+ ? declaration
1698
+ : undefined;
1699
+ if (!functionDeclaration) {
1700
+ return {
1701
+ ok: false,
1702
+ error: `Invalid function export '${exportItem.name}' in ${declarationModule.filePath}: expected function declaration.`,
1703
+ };
1704
+ }
1705
+ const registered = registerValueExport({
1706
+ exportName: exportItem.name,
1707
+ binding: {
1708
+ kind: "method",
1709
+ clrName: resolved.value.clrName,
1710
+ declaringClrType: toClrTypeName(declarationModule.namespace, declarationModule.className),
1711
+ declaringAssemblyName: assemblyName,
1712
+ },
1713
+ facade: {
1714
+ kind: "function",
1715
+ declaration: functionDeclaration,
1716
+ sourceSignatures: sourceIndexByFileKey
1717
+ .get(normalizeModuleFileKey(declarationModule.filePath))
1718
+ ?.exportedFunctionSignaturesByName.get(resolved.value.clrName) ?? [],
1719
+ },
1720
+ });
1721
+ if (!registered.ok)
1722
+ return registered;
1723
+ continue;
1724
+ }
1725
+ if (exportKind.value === "variable") {
1726
+ const declarationStatement = declaration.kind === "variableDeclaration"
1727
+ ? declaration
1728
+ : undefined;
1729
+ if (!declarationStatement) {
1730
+ return {
1731
+ ok: false,
1732
+ error: `Invalid variable export '${exportItem.name}' in ${declarationModule.filePath}: expected variable declaration.`,
1733
+ };
1734
+ }
1735
+ const declarator = declarationStatement.declarations.find((candidate) => candidate.name.kind === "identifierPattern" &&
1736
+ candidate.name.name === resolved.value.clrName);
1737
+ const registered = registerValueExport({
1738
+ exportName: exportItem.name,
1739
+ binding: {
1740
+ kind: "field",
1741
+ clrName: resolved.value.clrName,
1742
+ declaringClrType: toClrTypeName(declarationModule.namespace, declarationModule.className),
1743
+ declaringAssemblyName: assemblyName,
1744
+ },
1745
+ facade: {
1746
+ kind: "variable",
1747
+ declarator: declarator && declarator.name.kind === "identifierPattern"
1748
+ ? {
1749
+ kind: declarator.kind,
1750
+ name: declarator.name,
1751
+ type: declarator.type,
1752
+ }
1753
+ : undefined,
1754
+ },
1755
+ });
1756
+ if (!registered.ok)
1757
+ return registered;
1758
+ continue;
1759
+ }
1760
+ if (exportKind.value === "class" ||
1761
+ exportKind.value === "interface" ||
1762
+ exportKind.value === "enum" ||
1763
+ exportKind.value === "typeAlias") {
1764
+ if (exportKind.value === "typeAlias" &&
1765
+ declaration.kind === "typeAliasDeclaration" &&
1766
+ declaration.type.kind !== "objectType") {
1767
+ continue;
1768
+ }
1769
+ const typeKey = `${declarationModule.namespace}|${declarationModule.className}|${resolved.value.clrName}|${exportKind.value}`;
1770
+ if (seenTypeDeclarationKeys.has(typeKey))
1771
+ continue;
1772
+ seenTypeDeclarationKeys.add(typeKey);
1773
+ typeDeclarations.push({
1774
+ exportName: exportItem.name,
1775
+ localName: resolved.value.clrName,
1776
+ kind: exportKind.value,
1777
+ declaration,
1778
+ declaringNamespace: declarationModule.namespace,
1779
+ declaringClassName: declarationModule.className,
1780
+ declaringFilePath: declarationModule.filePath,
1781
+ });
1782
+ }
1783
+ }
1784
+ const moduleExports = collectModuleExports(module, modulesByFileKey);
1785
+ if (!moduleExports.ok)
1786
+ return moduleExports;
1787
+ const containerMethods = [];
1788
+ const containerVariables = [];
1789
+ if (module.filePath.startsWith("__tsonic/")) {
1790
+ for (const statement of module.body) {
1791
+ if (statement.kind !== "classDeclaration")
1792
+ continue;
1793
+ if (!statement.name.startsWith("__Anon_"))
1794
+ continue;
1795
+ const key = `${statement.name}|class`;
1796
+ if (seenTypeDeclarationKeys.has(key))
1797
+ continue;
1798
+ seenTypeDeclarationKeys.add(key);
1799
+ typeDeclarations.push({
1800
+ exportName: statement.name,
1801
+ localName: statement.name,
1802
+ kind: "class",
1803
+ declaration: statement,
1804
+ declaringNamespace: module.namespace,
1805
+ declaringClassName: module.className,
1806
+ declaringFilePath: module.filePath,
1807
+ });
1808
+ }
1809
+ }
1810
+ for (const symbol of moduleExports.value) {
1811
+ if (symbol.kind === "class" ||
1812
+ symbol.kind === "interface" ||
1813
+ symbol.kind === "enum" ||
1814
+ symbol.kind === "typeAlias") {
1815
+ if (symbol.kind === "typeAlias" &&
1816
+ symbol.declaration.kind === "typeAliasDeclaration" &&
1817
+ symbol.declaration.type.kind !== "objectType") {
1818
+ continue;
1819
+ }
1820
+ const key = `${symbol.declaringNamespace}|${symbol.declaringClassName}|${symbol.localName}|${symbol.kind}`;
1821
+ if (!seenTypeDeclarationKeys.has(key)) {
1822
+ seenTypeDeclarationKeys.add(key);
1823
+ typeDeclarations.push(symbol);
1824
+ }
1825
+ }
1826
+ if (symbol.kind === "function") {
1827
+ const functionDeclaration = symbol.declaration.kind === "functionDeclaration"
1828
+ ? symbol.declaration
1829
+ : undefined;
1830
+ if (!functionDeclaration)
1831
+ continue;
1832
+ const isLocalContainerMember = symbol.declaringNamespace === module.namespace &&
1833
+ symbol.declaringClassName === module.className;
1834
+ if (isLocalContainerMember) {
1835
+ containerMethods.push({
1836
+ exportName: symbol.exportName,
1837
+ localName: symbol.localName,
1838
+ declaration: functionDeclaration,
1839
+ });
1840
+ }
1841
+ const registered = registerValueExport({
1842
+ exportName: symbol.exportName,
1843
+ binding: {
1844
+ kind: "method",
1845
+ clrName: symbol.localName,
1846
+ declaringClrType: toClrTypeName(symbol.declaringNamespace, symbol.declaringClassName),
1847
+ declaringAssemblyName: assemblyName,
1848
+ },
1849
+ facade: {
1850
+ kind: "function",
1851
+ declaration: functionDeclaration,
1852
+ sourceSignatures: sourceIndexByFileKey
1853
+ .get(normalizeModuleFileKey(symbol.declaringFilePath))
1854
+ ?.exportedFunctionSignaturesByName.get(symbol.localName) ??
1855
+ [],
1856
+ },
1857
+ });
1858
+ if (!registered.ok)
1859
+ return registered;
1860
+ continue;
1861
+ }
1862
+ if (symbol.kind === "variable") {
1863
+ const declaration = symbol.declaration.kind === "variableDeclaration"
1864
+ ? symbol.declaration
1865
+ : undefined;
1866
+ if (!declaration)
1867
+ continue;
1868
+ const declarator = declaration.declarations.find((candidate) => candidate.name.kind === "identifierPattern" &&
1869
+ candidate.name.name === symbol.localName);
1870
+ const isLocalContainerMember = symbol.declaringNamespace === module.namespace &&
1871
+ symbol.declaringClassName === module.className;
1872
+ if (isLocalContainerMember) {
1873
+ containerVariables.push({
1874
+ exportName: symbol.exportName,
1875
+ localName: symbol.localName,
1876
+ declaration,
1877
+ declarator: declarator && declarator.name.kind === "identifierPattern"
1878
+ ? {
1879
+ kind: declarator.kind,
1880
+ name: declarator.name,
1881
+ type: declarator.type,
1882
+ }
1883
+ : undefined,
1884
+ });
1885
+ }
1886
+ const registered = registerValueExport({
1887
+ exportName: symbol.exportName,
1888
+ binding: {
1889
+ kind: "field",
1890
+ clrName: symbol.localName,
1891
+ declaringClrType: toClrTypeName(symbol.declaringNamespace, symbol.declaringClassName),
1892
+ declaringAssemblyName: assemblyName,
1893
+ },
1894
+ facade: {
1895
+ kind: "variable",
1896
+ declarator: declarator && declarator.name.kind === "identifierPattern"
1897
+ ? {
1898
+ kind: declarator.kind,
1899
+ name: declarator.name,
1900
+ type: declarator.type,
1901
+ }
1902
+ : undefined,
1903
+ },
1904
+ });
1905
+ if (!registered.ok)
1906
+ return registered;
1907
+ }
1908
+ }
1909
+ if (containerMethods.length > 0 || containerVariables.length > 0) {
1910
+ moduleContainers.push({
1911
+ module,
1912
+ methods: containerMethods,
1913
+ variables: containerVariables,
1914
+ });
1915
+ }
1916
+ }
1917
+ plans.push({
1918
+ namespace,
1919
+ typeDeclarations: typeDeclarations.sort((left, right) => left.exportName.localeCompare(right.exportName)),
1920
+ moduleContainers: moduleContainers.sort((left, right) => left.module.className.localeCompare(right.module.className)),
1921
+ sourceAliasLines: Array.from(sourceAliasLines.values()).sort((left, right) => left.localeCompare(right)),
1922
+ sourceAliasInternalImports: Array.from(sourceAliasInternalImports.values()).sort((left, right) => left.localeCompare(right)),
1923
+ memberOverrides: memberOverrides.sort((left, right) => {
1924
+ const classCmp = left.className.localeCompare(right.className);
1925
+ if (classCmp !== 0)
1926
+ return classCmp;
1927
+ return left.memberName.localeCompare(right.memberName);
1928
+ }),
1929
+ wrapperImports: Array.from(wrapperImportByAlias.values()).sort((left, right) => left.aliasName.localeCompare(right.aliasName)),
1930
+ valueExports: Array.from(valueExportsMap.values()).sort((left, right) => left.exportName.localeCompare(right.exportName)),
1931
+ });
1932
+ }
1933
+ return {
1934
+ ok: true,
1935
+ value: plans.sort((left, right) => left.namespace.localeCompare(right.namespace)),
1936
+ };
1937
+ };
1938
+ const collectEntrypointReexports = (entryModule, modulesByFileKey) => {
1939
+ const grouped = new Map();
1940
+ for (const exported of entryModule.exports) {
1941
+ if (exported.kind !== "reexport")
1942
+ continue;
1943
+ const targetModule = resolveLocalModuleFile(exported.fromModule, entryModule.filePath, modulesByFileKey);
1944
+ if (!targetModule) {
1945
+ return {
1946
+ ok: false,
1947
+ error: `Failed to resolve re-export '${exported.name}' from '${exported.fromModule}' in ${entryModule.filePath}.`,
1948
+ };
1949
+ }
1950
+ if (targetModule.namespace === entryModule.namespace)
1951
+ continue;
1952
+ const kind = classifyExportKind(targetModule, exported.originalName);
1953
+ if (kind === "unknown") {
1954
+ return {
1955
+ ok: false,
1956
+ error: `Failed to classify re-export '${exported.name}' from '${exported.fromModule}' in ${entryModule.filePath}.`,
1957
+ };
1958
+ }
1959
+ const moduleSpecifier = `./${moduleNamespacePath(targetModule.namespace)}.js`;
1960
+ const key = `${moduleSpecifier}|${kind}`;
1961
+ const specifier = exported.name === exported.originalName
1962
+ ? exported.name
1963
+ : `${exported.originalName} as ${exported.name}`;
1964
+ const list = grouped.get(key) ?? [];
1965
+ list.push(specifier);
1966
+ grouped.set(key, list);
1967
+ }
1968
+ const dtsStatements = [];
1969
+ const jsValueStatements = [];
1970
+ for (const [key, specs] of Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
1971
+ const [moduleSpecifier, kind] = key.split("|");
1972
+ const unique = Array.from(new Set(specs)).sort((a, b) => a.localeCompare(b));
1973
+ if (kind === "type") {
1974
+ dtsStatements.push(`export type { ${unique.join(", ")} } from '${moduleSpecifier}';`);
1975
+ continue;
1976
+ }
1977
+ const statement = `export { ${unique.join(", ")} } from '${moduleSpecifier}';`;
1978
+ dtsStatements.push(statement);
1979
+ jsValueStatements.push(statement);
1980
+ }
1981
+ return {
1982
+ ok: true,
1983
+ value: { dtsStatements, jsValueStatements },
1984
+ };
1985
+ };
1986
+ const writeNamespaceArtifacts = (config, outDir, plan, entrypointReexports) => {
1987
+ const namespacePath = moduleNamespacePath(plan.namespace);
1988
+ const namespaceDir = join(outDir, namespacePath);
1989
+ const internalDir = join(namespaceDir, "internal");
1990
+ mkdirSync(internalDir, { recursive: true });
1991
+ const internalIndexPath = join(internalDir, "index.d.ts");
1992
+ const facadeDtsPath = join(outDir, `${namespacePath}.d.ts`);
1993
+ const facadeJsPath = join(outDir, `${namespacePath}.js`);
1994
+ const bindingsPath = join(namespaceDir, "bindings.json");
1995
+ const internalLines = [];
1996
+ const memberOverridesByClass = new Map();
1997
+ for (const override of plan.memberOverrides) {
1998
+ const byMember = memberOverridesByClass.get(override.className) ??
1999
+ new Map();
2000
+ byMember.set(override.memberName, override);
2001
+ memberOverridesByClass.set(override.className, byMember);
2002
+ }
2003
+ internalLines.push("// Generated by Tsonic - Source bindings");
2004
+ internalLines.push(`// Namespace: ${plan.namespace}`);
2005
+ internalLines.push(`// Assembly: ${config.outputName}`);
2006
+ internalLines.push("");
2007
+ internalLines.push(primitiveImportLine);
2008
+ if (plan.wrapperImports.length > 0) {
2009
+ internalLines.push("");
2010
+ internalLines.push("// Tsonic source member type imports (generated)");
2011
+ for (const wrapperImport of plan.wrapperImports) {
2012
+ if (wrapperImport.importedName === wrapperImport.aliasName) {
2013
+ internalLines.push(`import type { ${wrapperImport.importedName} } from '${wrapperImport.source}';`);
2014
+ continue;
2015
+ }
2016
+ internalLines.push(`import type { ${wrapperImport.importedName} as ${wrapperImport.aliasName} } from '${wrapperImport.source}';`);
2017
+ }
2018
+ internalLines.push("// End Tsonic source member type imports");
2019
+ }
2020
+ internalLines.push("");
2021
+ const typeBindings = [];
2022
+ for (const symbol of plan.typeDeclarations) {
2023
+ if (symbol.kind === "class" &&
2024
+ symbol.declaration.kind === "classDeclaration") {
2025
+ internalLines.push(...renderClassInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(symbol.declaration.name) ?? new Map()));
2026
+ typeBindings.push(buildTypeBindingFromClass(symbol.declaration, plan.namespace, config.outputName));
2027
+ continue;
2028
+ }
2029
+ if (symbol.kind === "interface" &&
2030
+ symbol.declaration.kind === "interfaceDeclaration") {
2031
+ internalLines.push(...renderInterfaceInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(symbol.declaration.name) ?? new Map()));
2032
+ typeBindings.push(buildTypeBindingFromInterface(symbol.declaration, plan.namespace, config.outputName));
2033
+ continue;
2034
+ }
2035
+ if (symbol.kind === "enum" &&
2036
+ symbol.declaration.kind === "enumDeclaration") {
2037
+ internalLines.push(...renderEnumInternal(symbol.declaration));
2038
+ typeBindings.push(buildTypeBindingFromEnum(symbol.declaration, plan.namespace, config.outputName));
2039
+ continue;
2040
+ }
2041
+ if (symbol.kind === "typeAlias" &&
2042
+ symbol.declaration.kind === "typeAliasDeclaration") {
2043
+ internalLines.push(...renderStructuralAliasInternal(symbol.declaration, plan.namespace, memberOverridesByClass.get(`${symbol.declaration.name}__Alias${(symbol.declaration.typeParameters?.length ?? 0) > 0
2044
+ ? `_${symbol.declaration.typeParameters?.length ?? 0}`
2045
+ : ""}`) ?? new Map()));
2046
+ const binding = buildTypeBindingFromStructuralAlias(symbol.declaration, plan.namespace, config.outputName);
2047
+ if (binding)
2048
+ typeBindings.push(binding);
2049
+ }
2050
+ }
2051
+ for (const container of plan.moduleContainers) {
2052
+ internalLines.push(...renderContainerInternal(container));
2053
+ typeBindings.push(buildTypeBindingFromContainer(container, plan.namespace, config.outputName));
2054
+ }
2055
+ writeFileSync(internalIndexPath, internalLines.join("\n").trimEnd() + "\n", "utf-8");
2056
+ const internalSpecifier = moduleNamespaceToInternalSpecifier(plan.namespace);
2057
+ const facadeLines = [];
2058
+ facadeLines.push(`// Namespace: ${plan.namespace}`);
2059
+ facadeLines.push("// Generated by Tsonic - Source bindings");
2060
+ facadeLines.push("");
2061
+ facadeLines.push(`import * as Internal from '${internalSpecifier}';`);
2062
+ facadeLines.push("");
2063
+ for (const symbol of plan.typeDeclarations) {
2064
+ if (symbol.kind === "typeAlias") {
2065
+ continue;
2066
+ }
2067
+ const isValueType = symbol.kind === "class" || symbol.kind === "enum";
2068
+ const isSyntheticAnonymousClass = symbol.kind === "class" && symbol.localName.startsWith("__Anon_");
2069
+ if (isValueType) {
2070
+ const specifier = symbol.exportName === symbol.localName
2071
+ ? symbol.exportName
2072
+ : `${symbol.localName} as ${symbol.exportName}`;
2073
+ if (!isSyntheticAnonymousClass) {
2074
+ facadeLines.push(`export { ${specifier} } from '${internalSpecifier}';`);
2075
+ }
2076
+ facadeLines.push(`export type { ${specifier} } from '${internalSpecifier}';`);
2077
+ if (symbol.kind === "class") {
2078
+ facadeLines.push(`export type { ${symbol.localName}$instance } from '${internalSpecifier}';`);
2079
+ }
2080
+ continue;
2081
+ }
2082
+ const specifier = symbol.exportName === symbol.localName
2083
+ ? symbol.exportName
2084
+ : `${symbol.localName} as ${symbol.exportName}`;
2085
+ facadeLines.push(`export type { ${specifier} } from '${internalSpecifier}';`);
2086
+ if (symbol.kind === "interface") {
2087
+ facadeLines.push(`export type { ${symbol.localName}$instance } from '${internalSpecifier}';`);
2088
+ }
2089
+ }
2090
+ for (const container of plan.moduleContainers) {
2091
+ facadeLines.push(`export { ${container.module.className}$instance as ${container.module.className} } from '${internalSpecifier}';`);
2092
+ }
2093
+ const valueBindings = new Map();
2094
+ const localTypeImports = new Set();
2095
+ for (const symbol of plan.typeDeclarations) {
2096
+ if (symbol.kind === "typeAlias")
2097
+ continue;
2098
+ localTypeImports.add(symbol.localName);
2099
+ if (symbol.kind === "class" || symbol.kind === "interface") {
2100
+ localTypeImports.add(`${symbol.localName}$instance`);
2101
+ }
2102
+ }
2103
+ for (const internalImport of plan.sourceAliasInternalImports) {
2104
+ localTypeImports.add(internalImport);
2105
+ }
2106
+ if (localTypeImports.size > 0) {
2107
+ facadeLines.push("");
2108
+ facadeLines.push("// Tsonic source alias imports (generated)");
2109
+ facadeLines.push(`import type { ${Array.from(localTypeImports.values())
2110
+ .sort((left, right) => left.localeCompare(right))
2111
+ .join(", ")} } from '${internalSpecifier}';`);
2112
+ facadeLines.push("// End Tsonic source alias imports");
2113
+ }
2114
+ if (plan.sourceAliasLines.length > 0) {
2115
+ facadeLines.push("");
2116
+ facadeLines.push("// Tsonic source type aliases (generated)");
2117
+ facadeLines.push(...plan.sourceAliasLines);
2118
+ facadeLines.push("// End Tsonic source type aliases");
2119
+ }
2120
+ if (entrypointReexports && entrypointReexports.dtsStatements.length > 0) {
2121
+ facadeLines.push("");
2122
+ facadeLines.push("// Tsonic entrypoint re-exports (generated)");
2123
+ facadeLines.push(...entrypointReexports.dtsStatements);
2124
+ facadeLines.push("// End Tsonic entrypoint re-exports");
2125
+ }
2126
+ for (const valueExport of plan.valueExports) {
2127
+ valueBindings.set(valueExport.exportName, valueExport.binding);
2128
+ if (valueExport.facade.kind === "function") {
2129
+ const typeParametersText = printTypeParameters(valueExport.facade.declaration.typeParameters);
2130
+ const typeParameterNames = valueExport.facade.declaration.typeParameters?.map((typeParameter) => typeParameter.name) ?? [];
2131
+ const parametersText = renderUnknownParameters(valueExport.facade.declaration.parameters, typeParameterNames);
2132
+ const returnTypeText = renderPortableType(valueExport.facade.declaration.returnType, typeParameterNames);
2133
+ facadeLines.push(`export declare function ${valueExport.exportName}${typeParametersText}(${parametersText}): ${returnTypeText};`);
2134
+ continue;
2135
+ }
2136
+ facadeLines.push(`export declare const ${valueExport.exportName}: ${renderPortableType(valueExport.facade.declarator?.type)};`);
2137
+ }
2138
+ if (plan.typeDeclarations.length === 0 &&
2139
+ plan.moduleContainers.length === 0 &&
2140
+ plan.valueExports.length === 0 &&
2141
+ plan.sourceAliasLines.length === 0 &&
2142
+ (!entrypointReexports || entrypointReexports.dtsStatements.length === 0)) {
2143
+ facadeLines.push("export {};");
2144
+ }
2145
+ writeFileSync(facadeDtsPath, facadeLines.join("\n").trimEnd() + "\n", "utf-8");
2146
+ writeFileSync(facadeJsPath, [
2147
+ `// Namespace: ${plan.namespace}`,
2148
+ "// Generated by Tsonic - Source bindings",
2149
+ "// Module Stub - Do Not Execute",
2150
+ "",
2151
+ ...(entrypointReexports &&
2152
+ entrypointReexports.jsValueStatements.length > 0
2153
+ ? [
2154
+ "// Tsonic entrypoint value re-exports (generated)",
2155
+ ...entrypointReexports.jsValueStatements,
2156
+ "// End Tsonic entrypoint value re-exports",
2157
+ "",
2158
+ ]
2159
+ : []),
2160
+ "throw new Error(",
2161
+ ` 'Cannot import CLR namespace ${plan.namespace} in JavaScript runtime. ' +`,
2162
+ " 'This module provides TypeScript type definitions only. ' +",
2163
+ " 'Actual implementation requires .NET runtime via Tsonic compiler.'",
2164
+ ");",
2165
+ "",
2166
+ ].join("\n"), "utf-8");
2167
+ const bindings = {
2168
+ namespace: plan.namespace,
2169
+ contributingAssemblies: [config.outputName],
2170
+ types: typeBindings.sort((left, right) => left.clrName.localeCompare(right.clrName)),
2171
+ exports: valueBindings.size > 0
2172
+ ? Object.fromEntries(Array.from(valueBindings.entries()).sort((left, right) => left[0].localeCompare(right[0])))
2173
+ : undefined,
2174
+ producer: {
2175
+ tool: "tsonic",
2176
+ mode: "aikya-firstparty",
2177
+ },
2178
+ };
2179
+ writeFileSync(bindingsPath, JSON.stringify(bindings, null, 2) + "\n", "utf-8");
2180
+ return { ok: true, value: undefined };
2181
+ };
2182
+ export const generateFirstPartyLibraryBindings = (config, bindingsOutDir) => {
2183
+ if (!config.entryPoint) {
2184
+ return {
2185
+ ok: false,
2186
+ error: "Library bindings generation requires an entryPoint in tsonic.json.",
2187
+ };
2188
+ }
2189
+ const absoluteEntryPoint = resolve(config.projectRoot, config.entryPoint);
2190
+ const absoluteSourceRoot = resolve(config.projectRoot, config.sourceRoot);
2191
+ const surfaceCapabilities = resolveSurfaceCapabilities(config.surface);
2192
+ const typeLibraries = config.libraries.filter((library) => !library.endsWith(".dll"));
2193
+ const allTypeRoots = [...config.typeRoots, ...typeLibraries].map((typeRoot) => resolve(config.workspaceRoot, typeRoot));
2194
+ const compilerOptions = {
2195
+ projectRoot: config.projectRoot,
2196
+ sourceRoot: absoluteSourceRoot,
2197
+ rootNamespace: config.rootNamespace,
2198
+ typeRoots: allTypeRoots,
2199
+ surface: config.surface,
2200
+ useStandardLib: surfaceCapabilities.useStandardLib,
2201
+ verbose: false,
2202
+ };
2203
+ const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
2204
+ if (!graphResult.ok) {
2205
+ const message = graphResult.error
2206
+ .map((diagnostic) => diagnostic.location
2207
+ ? `${diagnostic.location.file}:${diagnostic.location.line}:${diagnostic.location.column} ${diagnostic.message}`
2208
+ : diagnostic.message)
2209
+ .join("\n");
2210
+ return {
2211
+ ok: false,
2212
+ error: `Failed to generate first-party bindings from source:\n${message}`,
2213
+ };
2214
+ }
2215
+ rmSync(bindingsOutDir, { recursive: true, force: true });
2216
+ mkdirSync(bindingsOutDir, { recursive: true });
2217
+ const modulesByFileKey = new Map();
2218
+ for (const module of graphResult.value.modules) {
2219
+ modulesByFileKey.set(normalizeModuleFileKey(module.filePath), module);
2220
+ }
2221
+ const sourceIndexByFileKey = new Map();
2222
+ for (const module of graphResult.value.modules) {
2223
+ if (module.filePath.startsWith("__tsonic/"))
2224
+ continue;
2225
+ const moduleKey = normalizeModuleFileKey(module.filePath);
2226
+ const absolutePath = resolve(absoluteSourceRoot, moduleKey);
2227
+ const indexed = buildModuleSourceIndex(absolutePath, moduleKey);
2228
+ if (!indexed.ok)
2229
+ return indexed;
2230
+ sourceIndexByFileKey.set(moduleKey, indexed.value);
2231
+ }
2232
+ const plansResult = collectNamespacePlans(graphResult.value.modules, config.outputName, config.rootNamespace, sourceIndexByFileKey);
2233
+ if (!plansResult.ok)
2234
+ return plansResult;
2235
+ const entrypointReexportsResult = collectEntrypointReexports(graphResult.value.entryModule, modulesByFileKey);
2236
+ if (!entrypointReexportsResult.ok)
2237
+ return entrypointReexportsResult;
2238
+ for (const plan of plansResult.value) {
2239
+ const result = writeNamespaceArtifacts(config, bindingsOutDir, plan, plan.namespace === graphResult.value.entryModule.namespace
2240
+ ? entrypointReexportsResult.value
2241
+ : undefined);
2242
+ if (!result.ok)
2243
+ return result;
2244
+ }
2245
+ const overlayResult = overlayDependencyBindings(config, bindingsOutDir);
2246
+ if (!overlayResult.ok)
2247
+ return overlayResult;
2248
+ return { ok: true, value: undefined };
2249
+ };
2250
+ //# sourceMappingURL=library-bindings-firstparty.js.map