@tsonic/cli 0.0.63 → 0.0.65

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 (83) 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 +352 -6
  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 +192 -145
  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 +7 -0
  36. package/dist/commands/generate.js.map +1 -1
  37. package/dist/commands/init.d.ts +6 -2
  38. package/dist/commands/init.d.ts.map +1 -1
  39. package/dist/commands/init.js +76 -12
  40. package/dist/commands/init.js.map +1 -1
  41. package/dist/commands/init.test.js +162 -8
  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 +378 -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 +214 -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 +2252 -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 +19 -2
  65. package/dist/config.js.map +1 -1
  66. package/dist/config.test.js +98 -2
  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 +15 -0
  73. package/dist/surface/profiles.d.ts.map +1 -0
  74. package/dist/surface/profiles.js +218 -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 +175 -0
  79. package/dist/surface/profiles.test.js.map +1 -0
  80. package/dist/types.d.ts +11 -1
  81. package/dist/types.d.ts.map +1 -1
  82. package/package.json +4 -4
  83. package/runtime/nodejs.dll +0 -0
@@ -1,6 +1,7 @@
1
1
  import { copyFileSync, existsSync, readFileSync, readdirSync, writeFileSync, } from "node:fs";
2
2
  import { basename, dirname, join, resolve, posix } from "node:path";
3
3
  import { buildModuleDependencyGraph, } from "@tsonic/frontend";
4
+ import { resolveSurfaceCapabilities } from "../surface/profiles.js";
4
5
  import * as ts from "typescript";
5
6
  const renderDiagnostics = (diags) => {
6
7
  return diags
@@ -21,6 +22,10 @@ const printTypeNodeText = (node, sourceFile) => {
21
22
  .printNode(ts.EmitHint.Unspecified, node, sourceFile)
22
23
  .trim();
23
24
  };
25
+ const textContainsIdentifier = (text, identifier) => {
26
+ const pattern = new RegExp(String.raw `\b${escapeRegExp(identifier)}\b`);
27
+ return pattern.test(text);
28
+ };
24
29
  const ensureUndefinedInType = (typeText) => {
25
30
  const trimmed = typeText.trim();
26
31
  if (/\bundefined\b/.test(trimmed))
@@ -222,6 +227,36 @@ const patchInternalIndexWithMemberOverrides = (internalIndexDtsPath, overrides)
222
227
  `This property was declared in TS source and should exist in CLR metadata.`,
223
228
  };
224
229
  }
230
+ if (o.isOptional &&
231
+ o.emitOptionalPropertySyntax &&
232
+ !o.memberName.startsWith("__tsonic_type_")) {
233
+ const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
234
+ getterMatch?.[2] ??
235
+ setterMatch?.[2] ??
236
+ "";
237
+ const nextType = applyWrappersToBaseType(baseType, o.wrappers);
238
+ const matchForIndent = getterMatch ?? setterMatch;
239
+ const indent = matchForIndent?.[0].match(/^\s*/)?.[0] ?? " ";
240
+ const optionalPropertyLine = `${indent}${o.memberName}?: ${nextType};`;
241
+ if (getterMatch && setterMatch) {
242
+ const getterStart = getterMatch.index ?? 0;
243
+ const setterStart = setterMatch.index ?? 0;
244
+ const start = Math.min(getterStart, setterStart);
245
+ const getterEnd = getterStart + getterMatch[0].length;
246
+ const setterEnd = setterStart + setterMatch[0].length;
247
+ const end = Math.max(getterEnd, setterEnd);
248
+ body = body.slice(0, start) + optionalPropertyLine + body.slice(end);
249
+ continue;
250
+ }
251
+ if (getterMatch) {
252
+ body = body.replace(getterRe, optionalPropertyLine);
253
+ continue;
254
+ }
255
+ if (setterMatch) {
256
+ body = body.replace(setterRe, optionalPropertyLine);
257
+ continue;
258
+ }
259
+ }
225
260
  if (getterMatch) {
226
261
  const baseType = (o.replaceWithSourceType ? o.sourceTypeText : undefined) ??
227
262
  getterMatch[2] ??
@@ -290,7 +325,7 @@ export const patchInternalIndexBrandMarkersOptional = (internalIndexDtsPath, typ
290
325
  }
291
326
  return { ok: true, value: undefined };
292
327
  };
293
- const splitTopLevelTypeArgs = (text) => {
328
+ const splitTopLevelCommaSeparated = (text) => {
294
329
  const parts = [];
295
330
  let depthAngle = 0;
296
331
  let depthParen = 0;
@@ -327,6 +362,9 @@ const splitTopLevelTypeArgs = (text) => {
327
362
  parts.push(text.slice(start).trim());
328
363
  return parts.filter((p) => p.length > 0);
329
364
  };
365
+ const splitTopLevelTypeArgs = (text) => {
366
+ return splitTopLevelCommaSeparated(text);
367
+ };
330
368
  const expandUnionsDeep = (typeText) => {
331
369
  const unionPrefixRe = /Union_\d+</g;
332
370
  let result = typeText;
@@ -366,6 +404,26 @@ const expandUnionsDeep = (typeText) => {
366
404
  }
367
405
  return result;
368
406
  };
407
+ const collectSourceTypeImportsForSignature = (signature, typeImportsByLocalName) => {
408
+ const required = [];
409
+ for (const [localName, imported] of typeImportsByLocalName) {
410
+ const source = imported.source.trim();
411
+ // Relative source imports do not map to published facade paths.
412
+ if (source.startsWith(".") || source.startsWith("/"))
413
+ continue;
414
+ const appearsInSignature = textContainsIdentifier(signature.typeParametersText, localName) ||
415
+ textContainsIdentifier(signature.parametersText, localName) ||
416
+ textContainsIdentifier(signature.returnTypeText, localName);
417
+ if (!appearsInSignature)
418
+ continue;
419
+ required.push({
420
+ source,
421
+ importedName: imported.importedName,
422
+ localName,
423
+ });
424
+ }
425
+ return required.sort((a, b) => a.localName.localeCompare(b.localName));
426
+ };
369
427
  const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName) => {
370
428
  if (!existsSync(facadeDtsPath)) {
371
429
  return {
@@ -383,47 +441,209 @@ const patchFacadeWithSourceFunctionSignatures = (facadeDtsPath, signaturesByName
383
441
  if (currentMatch) {
384
442
  const existingReturnType = currentMatch[2]?.trim() ?? "";
385
443
  const replacement = Array.from(new Set(signatures.map((sig) => {
386
- const returnType = sig.returnTypeText.includes("{")
387
- ? expandUnionsDeep(existingReturnType)
388
- : sig.returnTypeText;
444
+ const existingIsUnknown = existingReturnType === "unknown";
445
+ const existingHasAnon = /__Anon_/.test(existingReturnType);
446
+ const existingHasGenericAritySuffix = /\b[A-Za-z_$][\w$]*_\d+\s*</.test(existingReturnType);
447
+ const returnType = (() => {
448
+ if (!sig.returnTypeText.includes("{")) {
449
+ return sig.returnTypeText;
450
+ }
451
+ if (existingIsUnknown) {
452
+ return sig.returnTypeText;
453
+ }
454
+ if (existingHasAnon) {
455
+ return expandUnionsDeep(existingReturnType);
456
+ }
457
+ if (existingHasGenericAritySuffix) {
458
+ return sig.returnTypeText;
459
+ }
460
+ return expandUnionsDeep(existingReturnType);
461
+ })();
389
462
  return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
390
463
  }))).join("\n");
391
464
  next = next.replace(fnRe, replacement);
392
465
  continue;
393
466
  }
394
- // If no function declaration match, try const Func<...> pattern
395
- const constFuncRe = new RegExp(String.raw `^export\s+declare\s+const\s+${escapeRegExp(name)}\s*:\s*Func<([\s\S]+?)>\s*;`, "m");
396
- const constMatch = constFuncRe.exec(next);
467
+ // If no function declaration match, try const callable pattern.
468
+ const constDeclRe = new RegExp(String.raw `^export\s+declare\s+const\s+${escapeRegExp(name)}\s*:\s*([^;]+);`, "m");
469
+ const constMatch = constDeclRe.exec(next);
397
470
  if (!constMatch || !constMatch[1])
398
471
  continue;
399
- const funcTypeArgs = splitTopLevelTypeArgs(constMatch[1]);
400
- if (funcTypeArgs.length < 2)
472
+ const constTypeText = constMatch[1].trim();
473
+ let expectedParamCount;
474
+ let forcedReturnType;
475
+ const funcTypeMatch = /^Func<([\s\S]+)>$/.exec(constTypeText);
476
+ if (funcTypeMatch?.[1]) {
477
+ const funcTypeArgs = splitTopLevelTypeArgs(funcTypeMatch[1]);
478
+ if (funcTypeArgs.length < 2)
479
+ continue;
480
+ expectedParamCount = funcTypeArgs.length - 1;
481
+ const lastTypeArg = funcTypeArgs.at(-1);
482
+ if (!lastTypeArg)
483
+ continue;
484
+ forcedReturnType = expandUnionsDeep(lastTypeArg);
485
+ }
486
+ const replacement = Array.from(new Set(signatures
487
+ .filter((sig) => {
488
+ if (expectedParamCount === undefined)
489
+ return true;
490
+ const paramCount = splitTopLevelCommaSeparated(sig.parametersText).length;
491
+ return paramCount === expectedParamCount;
492
+ })
493
+ .map((sig) => {
494
+ const returnType = forcedReturnType ?? sig.returnTypeText;
495
+ return `export declare function ${name}${sig.typeParametersText}(${sig.parametersText}): ${returnType};`;
496
+ }))).join("\n");
497
+ if (replacement.length === 0)
401
498
  continue;
402
- // Last arg = return type, remaining args = parameter types
403
- const facadeParamTypes = funcTypeArgs.slice(0, -1);
404
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
405
- const facadeReturnType = funcTypeArgs[funcTypeArgs.length - 1];
406
- // Use the first source signature for parameter names
407
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
408
- const sourceSig = signatures[0];
409
- const sourceParams = sourceSig.parametersText
410
- .split(",")
411
- .map((p) => p.trim())
412
- .filter((p) => p.length > 0);
413
- // If count mismatch, skip patching for this declaration
414
- if (sourceParams.length !== facadeParamTypes.length)
499
+ next = next.replace(constDeclRe, replacement);
500
+ }
501
+ if (next !== original) {
502
+ writeFileSync(facadeDtsPath, next, "utf-8");
503
+ }
504
+ return { ok: true, value: undefined };
505
+ };
506
+ const parseNamedImports = (namedImportsText) => {
507
+ return namedImportsText
508
+ .split(",")
509
+ .map((part) => part.trim())
510
+ .filter((part) => part.length > 0)
511
+ .map((part) => {
512
+ const source = part.replace(/^\s*type\s+/, "");
513
+ const asMatch = /^\s*([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)\s*$/.exec(source);
514
+ if (asMatch && asMatch[1] && asMatch[2]) {
515
+ return { imported: asMatch[1], local: asMatch[2] };
516
+ }
517
+ const identMatch = /^\s*([A-Za-z_$][\w$]*)\s*$/.exec(source);
518
+ if (identMatch && identMatch[1]) {
519
+ return { imported: identMatch[1], local: identMatch[1] };
520
+ }
521
+ return undefined;
522
+ })
523
+ .filter((specifier) => specifier !== undefined);
524
+ };
525
+ const formatNamedImportSpecifier = (specifier) => {
526
+ if (specifier.imported === specifier.local) {
527
+ return specifier.local;
528
+ }
529
+ return `${specifier.imported} as ${specifier.local}`;
530
+ };
531
+ const ensureInternalTypeImportsForFacade = (facadeDtsPath) => {
532
+ if (!existsSync(facadeDtsPath)) {
533
+ return {
534
+ ok: false,
535
+ error: `Facade declaration file not found at ${facadeDtsPath}`,
536
+ };
537
+ }
538
+ const original = readFileSync(facadeDtsPath, "utf-8");
539
+ const internalImportRe = /^import\s+\*\s+as\s+Internal\s+from\s+['"]([^'"]+)['"];\s*$/m;
540
+ const internalImportMatch = internalImportRe.exec(original);
541
+ if (!internalImportMatch || !internalImportMatch[1]) {
542
+ return { ok: true, value: undefined };
543
+ }
544
+ const internalSpecifier = internalImportMatch[1];
545
+ const exportFromInternalRe = new RegExp(String.raw `^export\s+\{([^}]*)\}\s+from\s+['"]${escapeRegExp(internalSpecifier)}['"];\s*$`, "gm");
546
+ const neededTypeSpecifiers = new Map();
547
+ for (const match of original.matchAll(exportFromInternalRe)) {
548
+ const named = match[1];
549
+ if (!named)
415
550
  continue;
416
- // Pair source parameter NAMES with facade parameter TYPES
417
- const pairedParams = sourceParams.map((param, idx) => {
418
- const colonIdx = param.indexOf(":");
419
- const paramName = colonIdx >= 0 ? param.slice(0, colonIdx).trim() : param.trim();
420
- return `${paramName}: ${facadeParamTypes[idx]}`;
551
+ for (const specifier of parseNamedImports(named)) {
552
+ neededTypeSpecifiers.set(specifier.local, specifier);
553
+ }
554
+ }
555
+ if (neededTypeSpecifiers.size === 0) {
556
+ return { ok: true, value: undefined };
557
+ }
558
+ const existingTypeImportRe = new RegExp(String.raw `^import\s+type\s+\{([^}]*)\}\s+from\s+['"]${escapeRegExp(internalSpecifier)}['"];\s*$`, "m");
559
+ const existingTypeImportMatch = existingTypeImportRe.exec(original);
560
+ if (existingTypeImportMatch && existingTypeImportMatch[1]) {
561
+ for (const specifier of parseNamedImports(existingTypeImportMatch[1])) {
562
+ neededTypeSpecifiers.set(specifier.local, specifier);
563
+ }
564
+ }
565
+ const sortedSpecifiers = Array.from(neededTypeSpecifiers.values()).sort((a, b) => a.local.localeCompare(b.local));
566
+ const importLine = `import type { ${sortedSpecifiers
567
+ .map((specifier) => formatNamedImportSpecifier(specifier))
568
+ .join(", ")} } from '${internalSpecifier}';`;
569
+ let next = original;
570
+ if (existingTypeImportMatch && existingTypeImportMatch[0]) {
571
+ next = next.replace(existingTypeImportRe, importLine);
572
+ }
573
+ else if (internalImportMatch[0]) {
574
+ const anchor = internalImportMatch[0];
575
+ next = next.replace(anchor, `${anchor}\n${importLine}`);
576
+ }
577
+ if (next !== original) {
578
+ writeFileSync(facadeDtsPath, next, "utf-8");
579
+ }
580
+ return { ok: true, value: undefined };
581
+ };
582
+ const ensureSourceTypeImportsForFacade = (facadeDtsPath, importsByLocalName) => {
583
+ if (!existsSync(facadeDtsPath)) {
584
+ return {
585
+ ok: false,
586
+ error: `Facade declaration file not found at ${facadeDtsPath}`,
587
+ };
588
+ }
589
+ const original = readFileSync(facadeDtsPath, "utf-8");
590
+ if (importsByLocalName.size === 0) {
591
+ return { ok: true, value: undefined };
592
+ }
593
+ const existingLocals = new Set();
594
+ const importRe = /^import\s+(.*)\s+from\s+['"][^'"]+['"];\s*$/gm;
595
+ for (const match of original.matchAll(importRe)) {
596
+ const clause = match[1]?.trim();
597
+ if (!clause)
598
+ continue;
599
+ const nsMatch = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(clause);
600
+ if (nsMatch?.[1]) {
601
+ existingLocals.add(nsMatch[1]);
602
+ continue;
603
+ }
604
+ const defaultMatch = /^([A-Za-z_$][\w$]*)$/.exec(clause);
605
+ if (defaultMatch?.[1]) {
606
+ existingLocals.add(defaultMatch[1]);
607
+ continue;
608
+ }
609
+ const namedMatch = /^(?:type\s+)?\{([^}]*)\}$/.exec(clause);
610
+ if (namedMatch?.[1]) {
611
+ for (const specifier of parseNamedImports(namedMatch[1])) {
612
+ existingLocals.add(specifier.local);
613
+ }
614
+ }
615
+ }
616
+ const grouped = new Map();
617
+ for (const binding of Array.from(importsByLocalName.values()).sort((a, b) => a.localName.localeCompare(b.localName))) {
618
+ if (existingLocals.has(binding.localName))
619
+ continue;
620
+ const list = grouped.get(binding.source) ?? [];
621
+ list.push({
622
+ imported: binding.importedName,
623
+ local: binding.localName,
421
624
  });
422
- const expandedReturnType = expandUnionsDeep(facadeReturnType);
423
- const typeParamsText = sourceSig.typeParametersText;
424
- const replacement = `export declare function ${name}${typeParamsText}(${pairedParams.join(", ")}): ${expandedReturnType};`;
425
- next = next.replace(constFuncRe, replacement);
625
+ grouped.set(binding.source, list);
426
626
  }
627
+ if (grouped.size === 0) {
628
+ const startMarker = "// Tsonic source function type imports (generated)";
629
+ const endMarker = "// End Tsonic source function type imports";
630
+ const next = stripExistingSection(original, startMarker, endMarker);
631
+ if (next !== original) {
632
+ writeFileSync(facadeDtsPath, next, "utf-8");
633
+ }
634
+ return { ok: true, value: undefined };
635
+ }
636
+ const lines = [];
637
+ for (const [source, specifiers] of Array.from(grouped.entries()).sort((a, b) => a[0].localeCompare(b[0]))) {
638
+ const unique = Array.from(new Map(specifiers.map((specifier) => [
639
+ `${specifier.imported}|${specifier.local}`,
640
+ specifier,
641
+ ])).values()).sort((a, b) => a.local.localeCompare(b.local));
642
+ lines.push(`import type { ${unique.map((s) => formatNamedImportSpecifier(s)).join(", ")} } from '${source}';`);
643
+ }
644
+ const startMarker = "// Tsonic source function type imports (generated)";
645
+ const endMarker = "// End Tsonic source function type imports";
646
+ const next = upsertSectionAfterImports(original, startMarker, endMarker, lines.join("\n"));
427
647
  if (next !== original) {
428
648
  writeFileSync(facadeDtsPath, next, "utf-8");
429
649
  }
@@ -679,12 +899,14 @@ const buildModuleSourceIndex = (absoluteFilePath, fileKey) => {
679
899
  };
680
900
  };
681
901
  const typeNodeUsesImportedTypeNames = (node, typeImportsByLocalName) => {
902
+ const allowlistedImportSources = new Set(["@tsonic/core/types.js"]);
682
903
  let found = false;
683
904
  const visit = (current) => {
684
905
  if (found)
685
906
  return;
686
907
  if (ts.isTypeReferenceNode(current) && ts.isIdentifier(current.typeName)) {
687
- if (typeImportsByLocalName.has(current.typeName.text)) {
908
+ const imported = typeImportsByLocalName.get(current.typeName.text);
909
+ if (imported && !allowlistedImportSources.has(imported.source.trim())) {
688
910
  found = true;
689
911
  return;
690
912
  }
@@ -903,7 +1125,7 @@ const printIrType = (type, ctx) => {
903
1125
  return "unknown";
904
1126
  }
905
1127
  };
906
- const renderExportedTypeAlias = (stmt, internalIndexDts) => {
1128
+ const renderExportedTypeAlias = (stmt, internalIndexDts, _sourceAlias) => {
907
1129
  const typeParams = printTypeParameters(stmt.typeParameters);
908
1130
  if (stmt.type.kind === "objectType") {
909
1131
  const arity = stmt.typeParameters?.length ?? 0;
@@ -921,11 +1143,17 @@ const renderExportedTypeAlias = (stmt, internalIndexDts) => {
921
1143
  : "";
922
1144
  return {
923
1145
  ok: true,
924
- value: `export type ${stmt.name}${typeParams} = Internal.${internalName}${typeArgs};`,
1146
+ value: {
1147
+ line: `export type ${stmt.name}${typeParams} = ${internalName}${typeArgs};`,
1148
+ internalAliasImport: internalName,
1149
+ },
925
1150
  };
926
1151
  }
927
1152
  const rhs = printIrType(stmt.type, { parentPrecedence: 0 });
928
- return { ok: true, value: `export type ${stmt.name}${typeParams} = ${rhs};` };
1153
+ return {
1154
+ ok: true,
1155
+ value: { line: `export type ${stmt.name}${typeParams} = ${rhs};` },
1156
+ };
929
1157
  };
930
1158
  /**
931
1159
  * Overlay already-augmented bindings from dependency assemblies.
@@ -1008,6 +1236,9 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1008
1236
  }
1009
1237
  const absoluteEntryPoint = resolve(config.projectRoot, entryPoint);
1010
1238
  const absoluteSourceRoot = resolve(config.projectRoot, config.sourceRoot);
1239
+ const surfaceCapabilities = resolveSurfaceCapabilities(config.surface, {
1240
+ workspaceRoot: config.workspaceRoot,
1241
+ });
1011
1242
  const typeLibraries = config.libraries.filter((lib) => !lib.endsWith(".dll"));
1012
1243
  const allTypeRoots = [...config.typeRoots, ...typeLibraries].map((p) => resolve(config.workspaceRoot, p));
1013
1244
  const compilerOptions = {
@@ -1015,6 +1246,8 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1015
1246
  sourceRoot: absoluteSourceRoot,
1016
1247
  rootNamespace: config.rootNamespace,
1017
1248
  typeRoots: allTypeRoots,
1249
+ surface: config.surface,
1250
+ useStandardLib: surfaceCapabilities.useStandardLib,
1018
1251
  verbose: false,
1019
1252
  };
1020
1253
  const graphResult = buildModuleDependencyGraph(absoluteEntryPoint, compilerOptions);
@@ -1071,6 +1304,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1071
1304
  };
1072
1305
  // 1) Per-namespace exported type aliases (including non-structural aliases).
1073
1306
  const exportedAliasesByNamespace = new Map();
1307
+ const sourceIndexByFileKeyForAliases = new Map();
1074
1308
  for (const m of modules) {
1075
1309
  const isExportedTypeAlias = (stmt) => stmt.kind === "typeAliasDeclaration" && stmt.isExported;
1076
1310
  const exportedAliases = m.body.filter(isExportedTypeAlias);
@@ -1080,27 +1314,60 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1080
1314
  const internalIndexDts = existsSync(info.internalIndexDtsPath)
1081
1315
  ? readFileSync(info.internalIndexDtsPath, "utf-8")
1082
1316
  : "";
1317
+ const moduleKey = normalizeModuleFileKey(m.filePath);
1318
+ const sourceIndex = (() => {
1319
+ const cached = sourceIndexByFileKeyForAliases.get(moduleKey);
1320
+ if (cached)
1321
+ return { ok: true, value: cached };
1322
+ const absolutePath = resolve(absoluteSourceRoot, moduleKey);
1323
+ const indexed = buildModuleSourceIndex(absolutePath, moduleKey);
1324
+ if (!indexed.ok)
1325
+ return indexed;
1326
+ sourceIndexByFileKeyForAliases.set(moduleKey, indexed.value);
1327
+ return indexed;
1328
+ })();
1329
+ if (!sourceIndex.ok)
1330
+ return sourceIndex;
1083
1331
  for (const stmt of exportedAliases) {
1084
- const rendered = renderExportedTypeAlias(stmt, internalIndexDts);
1332
+ const rendered = renderExportedTypeAlias(stmt, internalIndexDts, sourceIndex.value.typeAliasesByName.get(stmt.name));
1085
1333
  if (!rendered.ok)
1086
1334
  return rendered;
1087
- const list = exportedAliasesByNamespace.get(m.namespace) ?? [];
1088
- list.push(rendered.value);
1089
- exportedAliasesByNamespace.set(m.namespace, list);
1335
+ const current = exportedAliasesByNamespace.get(m.namespace) ?? {
1336
+ lines: [],
1337
+ internalAliasImports: new Set(),
1338
+ };
1339
+ current.lines.push(rendered.value.line);
1340
+ if (rendered.value.internalAliasImport) {
1341
+ current.internalAliasImports.add(rendered.value.internalAliasImport);
1342
+ }
1343
+ exportedAliasesByNamespace.set(m.namespace, current);
1090
1344
  }
1091
1345
  }
1092
1346
  const aliasStart = "// Tsonic source type aliases (generated)";
1093
1347
  const aliasEnd = "// End Tsonic source type aliases";
1094
- for (const [ns, lines] of exportedAliasesByNamespace) {
1348
+ const aliasImportsStart = "// Tsonic source alias imports (generated)";
1349
+ const aliasImportsEnd = "// End Tsonic source alias imports";
1350
+ for (const [ns, aliasData] of exportedAliasesByNamespace) {
1095
1351
  const info = facadesByNamespace.get(ns);
1096
1352
  if (!info)
1097
1353
  continue;
1098
- if (lines.length === 0)
1354
+ if (aliasData.lines.length === 0)
1099
1355
  continue;
1100
- const unique = Array.from(new Set(lines)).sort((a, b) => a.localeCompare(b));
1356
+ const unique = Array.from(new Set(aliasData.lines)).sort((a, b) => a.localeCompare(b));
1101
1357
  const body = unique.join("\n");
1102
1358
  const current = readFileSync(info.facadeDtsPath, "utf-8");
1103
- const next = upsertSection(current, aliasStart, aliasEnd, body);
1359
+ const internalImportMatch = current.match(/^import\s+\*\s+as\s+Internal\s+from\s+['"](.+)['"];\s*$/m);
1360
+ let next = current;
1361
+ if (internalImportMatch?.[1] && aliasData.internalAliasImports.size > 0) {
1362
+ const internalAliasImportLine = `import type { ${Array.from(aliasData.internalAliasImports)
1363
+ .sort((a, b) => a.localeCompare(b))
1364
+ .join(", ")} } from '${internalImportMatch[1]}';`;
1365
+ next = upsertSectionAfterImports(next, aliasImportsStart, aliasImportsEnd, internalAliasImportLine);
1366
+ }
1367
+ else {
1368
+ next = stripExistingSection(next, aliasImportsStart, aliasImportsEnd);
1369
+ }
1370
+ next = upsertSectionAfterImports(next, aliasStart, aliasEnd, body);
1104
1371
  writeFileSync(info.facadeDtsPath, next, "utf-8");
1105
1372
  }
1106
1373
  // 2) Entry-module re-export surface (matches TS semantics for library entrypoints).
@@ -1176,7 +1443,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1176
1443
  // graph and patching the published internal/index.d.ts:
1177
1444
  // - Re-apply ExtensionMethods wrappers (class + interface members)
1178
1445
  // - Preserve optional (`?`) semantics by allowing `undefined` on patched members
1179
- // - For exported interfaces, preserve source structural member types when safe
1446
+ // - Preserve TS structural typing on interfaces/type-literal aliases (exported + local)
1180
1447
  const sourceIndexByFileKey = new Map();
1181
1448
  for (const m of modules) {
1182
1449
  // Synthetic IR modules (e.g., program-wide anonymous type declarations) do not
@@ -1193,6 +1460,7 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1193
1460
  const overridesByInternalIndex = new Map();
1194
1461
  const brandOptionalTypesByInternalIndex = new Map();
1195
1462
  const functionSignaturesByFacade = new Map();
1463
+ const sourceTypeImportsByFacade = new Map();
1196
1464
  for (const m of modules) {
1197
1465
  if (m.filePath.startsWith("__tsonic/"))
1198
1466
  continue;
@@ -1203,13 +1471,37 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1203
1471
  const sourceIndex = sourceIndexByFileKey.get(moduleKey);
1204
1472
  if (!sourceIndex)
1205
1473
  continue;
1474
+ const allInterfaces = m.body.filter((s) => s.kind === "interfaceDeclaration");
1475
+ const allTypeAliases = m.body.filter((s) => s.kind === "typeAliasDeclaration");
1206
1476
  const hasExportedSourceFunctions = sourceIndex.exportedFunctionSignaturesByName.size > 0;
1207
1477
  if (exportedClasses.length === 0 &&
1208
1478
  exportedInterfaces.length === 0 &&
1209
1479
  exportedAliases.length === 0 &&
1480
+ allInterfaces.length === 0 &&
1481
+ allTypeAliases.length === 0 &&
1210
1482
  !hasExportedSourceFunctions)
1211
1483
  continue;
1212
1484
  const info = facadesByNamespace.get(m.namespace) ?? ensureFacade(m.namespace);
1485
+ const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
1486
+ new Set();
1487
+ for (const iface of allInterfaces) {
1488
+ brandTargets.add(iface.name);
1489
+ }
1490
+ for (const alias of allTypeAliases) {
1491
+ const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
1492
+ if (!sourceAlias)
1493
+ continue;
1494
+ const aliasType = unwrapParens(sourceAlias.type);
1495
+ if (!ts.isTypeLiteralNode(aliasType))
1496
+ continue;
1497
+ const arity = sourceAlias.typeParameters.length;
1498
+ const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
1499
+ brandTargets.add(alias.name);
1500
+ brandTargets.add(internalAliasName);
1501
+ }
1502
+ if (brandTargets.size > 0) {
1503
+ brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
1504
+ }
1213
1505
  for (const [name, signatures,] of sourceIndex.exportedFunctionSignaturesByName) {
1214
1506
  if (signatures.length === 0)
1215
1507
  continue;
@@ -1219,6 +1511,28 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1219
1511
  list.push(...signatures);
1220
1512
  byName.set(name, list);
1221
1513
  functionSignaturesByFacade.set(info.facadeDtsPath, byName);
1514
+ const importsByLocal = sourceTypeImportsByFacade.get(info.facadeDtsPath) ??
1515
+ new Map();
1516
+ for (const signature of signatures) {
1517
+ for (const binding of collectSourceTypeImportsForSignature(signature, sourceIndex.typeImportsByLocalName)) {
1518
+ const existing = importsByLocal.get(binding.localName);
1519
+ if (existing) {
1520
+ if (existing.source !== binding.source ||
1521
+ existing.importedName !== binding.importedName) {
1522
+ return {
1523
+ ok: false,
1524
+ error: `Conflicting source type import alias '${binding.localName}' while augmenting ${info.facadeDtsPath}.\n` +
1525
+ `- ${existing.importedName} from '${existing.source}'\n` +
1526
+ `- ${binding.importedName} from '${binding.source}'\n` +
1527
+ `Fix: disambiguate source type imports for exported function signatures.`,
1528
+ };
1529
+ }
1530
+ continue;
1531
+ }
1532
+ importsByLocal.set(binding.localName, binding);
1533
+ }
1534
+ }
1535
+ sourceTypeImportsByFacade.set(info.facadeDtsPath, importsByLocal);
1222
1536
  }
1223
1537
  for (const cls of exportedClasses) {
1224
1538
  const memberTypes = sourceIndex.memberTypesByClassAndMember.get(cls.name);
@@ -1243,13 +1557,21 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1243
1557
  if (!wrappersResult.ok)
1244
1558
  return wrappersResult;
1245
1559
  const wrappers = wrappersResult.value;
1246
- if (wrappers.length === 0 && !sourceMember.isOptional)
1560
+ const canUseSourceTypeText = !typeNodeUsesImportedTypeNames(sourceMember.typeNode, sourceIndex.typeImportsByLocalName);
1561
+ if (!canUseSourceTypeText &&
1562
+ wrappers.length === 0 &&
1563
+ !sourceMember.isOptional) {
1247
1564
  continue;
1565
+ }
1248
1566
  const list = overridesByInternalIndex.get(info.internalIndexDtsPath) ?? [];
1249
1567
  list.push({
1250
1568
  namespace: m.namespace,
1251
1569
  className: cls.name,
1252
1570
  memberName: member.name,
1571
+ sourceTypeText: canUseSourceTypeText
1572
+ ? sourceMember.typeText
1573
+ : undefined,
1574
+ replaceWithSourceType: canUseSourceTypeText,
1253
1575
  isOptional: sourceMember.isOptional,
1254
1576
  wrappers,
1255
1577
  });
@@ -1288,14 +1610,11 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1288
1610
  : undefined,
1289
1611
  replaceWithSourceType: canUseSourceTypeText,
1290
1612
  isOptional: sourceMember.isOptional,
1613
+ emitOptionalPropertySyntax: true,
1291
1614
  wrappers,
1292
1615
  });
1293
1616
  overridesByInternalIndex.set(info.internalIndexDtsPath, list);
1294
1617
  }
1295
- const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
1296
- new Set();
1297
- brandTargets.add(iface.name);
1298
- brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
1299
1618
  }
1300
1619
  for (const alias of exportedAliases) {
1301
1620
  const sourceAlias = sourceIndex.typeAliasesByName.get(alias.name);
@@ -1306,10 +1625,6 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1306
1625
  continue;
1307
1626
  const arity = sourceAlias.typeParameters.length;
1308
1627
  const internalAliasName = `${alias.name}__Alias${arity > 0 ? `_${arity}` : ""}`;
1309
- const brandTargets = brandOptionalTypesByInternalIndex.get(info.internalIndexDtsPath) ??
1310
- new Set();
1311
- brandTargets.add(internalAliasName);
1312
- brandOptionalTypesByInternalIndex.set(info.internalIndexDtsPath, brandTargets);
1313
1628
  for (const member of aliasType.members) {
1314
1629
  if (!ts.isPropertySignature(member))
1315
1630
  continue;
@@ -1361,6 +1676,16 @@ export const augmentLibraryBindingsFromSource = (config, bindingsOutDir) => {
1361
1676
  if (!result.ok)
1362
1677
  return result;
1363
1678
  }
1679
+ for (const [facadePath, importsByLocalName] of sourceTypeImportsByFacade) {
1680
+ const result = ensureSourceTypeImportsForFacade(facadePath, importsByLocalName);
1681
+ if (!result.ok)
1682
+ return result;
1683
+ }
1684
+ for (const info of facadesByNamespace.values()) {
1685
+ const result = ensureInternalTypeImportsForFacade(info.facadeDtsPath);
1686
+ if (!result.ok)
1687
+ return result;
1688
+ }
1364
1689
  return { ok: true, value: undefined };
1365
1690
  };
1366
1691
  //# sourceMappingURL=library-bindings-augment.js.map