tywrap 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/dist/cli.js +8 -6
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/index.js +236 -147
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/core/annotation-parser.d.ts.map +1 -1
  7. package/dist/core/annotation-parser.js +93 -83
  8. package/dist/core/annotation-parser.js.map +1 -1
  9. package/dist/core/discovery.d.ts.map +1 -1
  10. package/dist/core/discovery.js +26 -0
  11. package/dist/core/discovery.js.map +1 -1
  12. package/dist/core/emit-call.d.ts +58 -0
  13. package/dist/core/emit-call.d.ts.map +1 -0
  14. package/dist/core/emit-call.js +103 -0
  15. package/dist/core/emit-call.js.map +1 -0
  16. package/dist/core/generator.d.ts +2 -0
  17. package/dist/core/generator.d.ts.map +1 -1
  18. package/dist/core/generator.js +54 -211
  19. package/dist/core/generator.js.map +1 -1
  20. package/dist/core/mapper.d.ts.map +1 -1
  21. package/dist/core/mapper.js +118 -104
  22. package/dist/core/mapper.js.map +1 -1
  23. package/dist/index.d.ts +6 -25
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +9 -31
  26. package/dist/index.js.map +1 -1
  27. package/dist/runtime/bounded-context.d.ts +2 -2
  28. package/dist/runtime/bounded-context.js +2 -2
  29. package/dist/runtime/{safe-codec.d.ts → bridge-codec.d.ts} +11 -11
  30. package/dist/runtime/bridge-codec.d.ts.map +1 -0
  31. package/dist/runtime/{safe-codec.js → bridge-codec.js} +26 -26
  32. package/dist/runtime/bridge-codec.js.map +1 -0
  33. package/dist/runtime/{http-io.d.ts → http-transport.d.ts} +6 -6
  34. package/dist/runtime/http-transport.d.ts.map +1 -0
  35. package/dist/runtime/{http-io.js → http-transport.js} +3 -3
  36. package/dist/runtime/http-transport.js.map +1 -0
  37. package/dist/runtime/http.d.ts +3 -3
  38. package/dist/runtime/http.d.ts.map +1 -1
  39. package/dist/runtime/http.js +4 -4
  40. package/dist/runtime/http.js.map +1 -1
  41. package/dist/runtime/index.d.ts +9 -1
  42. package/dist/runtime/index.d.ts.map +1 -1
  43. package/dist/runtime/index.js +9 -1
  44. package/dist/runtime/index.js.map +1 -1
  45. package/dist/runtime/node.d.ts +6 -6
  46. package/dist/runtime/node.d.ts.map +1 -1
  47. package/dist/runtime/node.js +7 -7
  48. package/dist/runtime/node.js.map +1 -1
  49. package/dist/runtime/pooled-transport.d.ts +11 -11
  50. package/dist/runtime/pooled-transport.d.ts.map +1 -1
  51. package/dist/runtime/pooled-transport.js +10 -10
  52. package/dist/runtime/pooled-transport.js.map +1 -1
  53. package/dist/runtime/pyodide-bootstrap-core.generated.d.ts.map +1 -1
  54. package/dist/runtime/pyodide-bootstrap-core.generated.js +1 -1
  55. package/dist/runtime/pyodide-bootstrap-core.generated.js.map +1 -1
  56. package/dist/runtime/{pyodide-io.d.ts → pyodide-transport.d.ts} +8 -8
  57. package/dist/runtime/pyodide-transport.d.ts.map +1 -0
  58. package/dist/runtime/{pyodide-io.js → pyodide-transport.js} +5 -5
  59. package/dist/runtime/pyodide-transport.js.map +1 -0
  60. package/dist/runtime/pyodide.d.ts +3 -3
  61. package/dist/runtime/pyodide.d.ts.map +1 -1
  62. package/dist/runtime/pyodide.js +5 -5
  63. package/dist/runtime/pyodide.js.map +1 -1
  64. package/dist/runtime/rpc-client.d.ts +5 -5
  65. package/dist/runtime/rpc-client.d.ts.map +1 -1
  66. package/dist/runtime/rpc-client.js +6 -7
  67. package/dist/runtime/rpc-client.js.map +1 -1
  68. package/dist/runtime/{process-io.d.ts → subprocess-transport.d.ts} +9 -9
  69. package/dist/runtime/subprocess-transport.d.ts.map +1 -0
  70. package/dist/runtime/{process-io.js → subprocess-transport.js} +6 -6
  71. package/dist/runtime/subprocess-transport.js.map +1 -0
  72. package/dist/runtime/{worker-pool.d.ts → transport-pool.d.ts} +15 -15
  73. package/dist/runtime/transport-pool.d.ts.map +1 -0
  74. package/dist/runtime/{worker-pool.js → transport-pool.js} +6 -6
  75. package/dist/runtime/transport-pool.js.map +1 -0
  76. package/dist/runtime/transport.d.ts +14 -8
  77. package/dist/runtime/transport.d.ts.map +1 -1
  78. package/dist/runtime/transport.js +10 -4
  79. package/dist/runtime/transport.js.map +1 -1
  80. package/dist/types/index.d.ts +10 -1
  81. package/dist/types/index.d.ts.map +1 -1
  82. package/dist/utils/cache.d.ts +2 -2
  83. package/dist/utils/cache.d.ts.map +1 -1
  84. package/dist/utils/cache.js +2 -2
  85. package/dist/utils/cache.js.map +1 -1
  86. package/dist/utils/codec.d.ts +1 -1
  87. package/dist/utils/codec.d.ts.map +1 -1
  88. package/dist/utils/codec.js +181 -182
  89. package/dist/utils/codec.js.map +1 -1
  90. package/dist/version.d.ts +12 -0
  91. package/dist/version.d.ts.map +1 -0
  92. package/dist/version.js +12 -0
  93. package/dist/version.js.map +1 -0
  94. package/package.json +3 -2
  95. package/runtime/__pycache__/safe_codec.cpython-311.pyc +0 -0
  96. package/runtime/__pycache__/tywrap_bridge_core.cpython-311.pyc +0 -0
  97. package/runtime/python_bridge.py +54 -6
  98. package/runtime/safe_codec.py +8 -8
  99. package/runtime/tywrap_bridge_core.py +159 -23
  100. package/src/cli.ts +8 -12
  101. package/src/config/index.ts +271 -165
  102. package/src/core/annotation-parser.ts +112 -105
  103. package/src/core/discovery.ts +32 -0
  104. package/src/core/emit-call.ts +171 -0
  105. package/src/core/generator.ts +67 -263
  106. package/src/core/mapper.ts +125 -110
  107. package/src/index.ts +11 -68
  108. package/src/runtime/bounded-context.ts +2 -2
  109. package/src/runtime/{safe-codec.ts → bridge-codec.ts} +27 -27
  110. package/src/runtime/{http-io.ts → http-transport.ts} +5 -5
  111. package/src/runtime/http.ts +5 -5
  112. package/src/runtime/index.ts +23 -1
  113. package/src/runtime/node.ts +15 -15
  114. package/src/runtime/pooled-transport.ts +15 -15
  115. package/src/runtime/pyodide-bootstrap-core.generated.ts +1 -1
  116. package/src/runtime/{pyodide-io.ts → pyodide-transport.ts} +7 -7
  117. package/src/runtime/pyodide.ts +6 -6
  118. package/src/runtime/rpc-client.ts +12 -8
  119. package/src/runtime/{process-io.ts → subprocess-transport.ts} +8 -8
  120. package/src/runtime/{worker-pool.ts → transport-pool.ts} +26 -26
  121. package/src/runtime/transport.ts +15 -8
  122. package/src/types/index.ts +10 -1
  123. package/src/utils/cache.ts +2 -2
  124. package/src/utils/codec.ts +217 -196
  125. package/src/version.ts +12 -0
  126. package/dist/runtime/http-io.d.ts.map +0 -1
  127. package/dist/runtime/http-io.js.map +0 -1
  128. package/dist/runtime/optimized-node.d.ts +0 -23
  129. package/dist/runtime/optimized-node.d.ts.map +0 -1
  130. package/dist/runtime/optimized-node.js +0 -23
  131. package/dist/runtime/optimized-node.js.map +0 -1
  132. package/dist/runtime/process-io.d.ts.map +0 -1
  133. package/dist/runtime/process-io.js.map +0 -1
  134. package/dist/runtime/protocol.d.ts +0 -3
  135. package/dist/runtime/protocol.d.ts.map +0 -1
  136. package/dist/runtime/protocol.js +0 -3
  137. package/dist/runtime/protocol.js.map +0 -1
  138. package/dist/runtime/pyodide-io.d.ts.map +0 -1
  139. package/dist/runtime/pyodide-io.js.map +0 -1
  140. package/dist/runtime/safe-codec.d.ts.map +0 -1
  141. package/dist/runtime/safe-codec.js.map +0 -1
  142. package/dist/runtime/worker-pool.d.ts.map +0 -1
  143. package/dist/runtime/worker-pool.js.map +0 -1
  144. package/dist/utils/bundle-optimizer.d.ts +0 -182
  145. package/dist/utils/bundle-optimizer.d.ts.map +0 -1
  146. package/dist/utils/bundle-optimizer.js +0 -680
  147. package/dist/utils/bundle-optimizer.js.map +0 -1
  148. package/dist/utils/memory-profiler.d.ts +0 -123
  149. package/dist/utils/memory-profiler.d.ts.map +0 -1
  150. package/dist/utils/memory-profiler.js +0 -426
  151. package/dist/utils/memory-profiler.js.map +0 -1
  152. package/src/runtime/optimized-node.ts +0 -25
  153. package/src/runtime/protocol.ts +0 -2
  154. package/src/utils/bundle-optimizer.ts +0 -876
  155. package/src/utils/memory-profiler.ts +0 -582
@@ -14,6 +14,12 @@ import type {
14
14
  } from '../types/index.js';
15
15
  import { globalCache } from '../utils/cache.js';
16
16
 
17
+ import {
18
+ emitArgGuards,
19
+ emitCallPrelude,
20
+ type CallDescriptor,
21
+ type CallEmitHelpers,
22
+ } from './emit-call.js';
17
23
  import { TypeMapper } from './mapper.js';
18
24
 
19
25
  interface GenericRenderParam {
@@ -336,6 +342,21 @@ export class CodeGenerator {
336
342
  return `(${base} && (${keyCheck}))`;
337
343
  }
338
344
 
345
+ /** Helpers passed to the shared call-emission module (they depend on `this`). */
346
+ private callEmitHelpers(): CallEmitHelpers {
347
+ return {
348
+ escapeIdentifier: (name: string): string => this.escapeIdentifier(name),
349
+ renderLooksLikeKwargsExpr: (
350
+ valueExpr: string,
351
+ options: {
352
+ keywordOnlyNames: string[];
353
+ requiredKwOnlyNames: string[];
354
+ hasVarKwArgs: boolean;
355
+ }
356
+ ): string => this.renderLooksLikeKwargsExpr(valueExpr, options),
357
+ };
358
+ }
359
+
339
360
  generateFunctionWrapper(
340
361
  func: PythonFunction,
341
362
  moduleName?: string,
@@ -492,96 +513,26 @@ export class CodeGenerator {
492
513
  }
493
514
 
494
515
  const overloadDecl = overloads.length > 0 ? `${overloads.join('\n')}\n` : '';
495
- const guardLines: string[] = [];
496
- if (hasKwArgs && positionalOnlyNames.length > 0) {
497
- guardLines.push(
498
- ` const __positionalOnly = ${JSON.stringify(positionalOnlyNames)} as const;`
499
- );
500
- guardLines.push(` for (const key of __positionalOnly) {`);
501
- guardLines.push(` if (__kwargs && Object.prototype.hasOwnProperty.call(__kwargs, key)) {`);
502
- guardLines.push(
503
- ` throw new Error(\`${func.name} does not accept positional-only argument "\${key}" as a keyword argument\`);`
504
- );
505
- guardLines.push(` }`);
506
- guardLines.push(` }`);
507
- }
508
- if (hasKwArgs && requiredKwOnlyNames.length > 0) {
509
- guardLines.push(
510
- ` const __requiredKwOnly = ${JSON.stringify(requiredKwOnlyNames)} as const;`
511
- );
512
- guardLines.push(` const __missing: string[] = [];`);
513
- guardLines.push(` for (const key of __requiredKwOnly) {`);
514
- guardLines.push(
515
- ` if (!__kwargs || !Object.prototype.hasOwnProperty.call(__kwargs, key)) {`
516
- );
517
- guardLines.push(` __missing.push(key);`);
518
- guardLines.push(` }`);
519
- guardLines.push(` }`);
520
- guardLines.push(` if (__missing.length > 0) {`);
521
- guardLines.push(
522
- ` throw new Error(\`Missing required keyword-only arguments for ${func.name}: \${__missing.join(', ')}\`);`
523
- );
524
- guardLines.push(` }`);
525
- }
526
- const guards = guardLines.length > 0 ? `${guardLines.join('\n')}\n` : '';
527
516
 
528
517
  const firstOptionalPosIndex = positionalParams.findIndex(p => p.optional);
529
518
  const requiredPosCount =
530
519
  firstOptionalPosIndex >= 0 ? firstOptionalPosIndex : positionalParams.length;
531
- const callPreludeLines: string[] = [];
532
- if (hasKwArgs) {
533
- callPreludeLines.push(` let __kwargs = kwargs;`);
534
- }
535
- const positionalArgExprs = positionalParams.map(p => this.escapeIdentifier(p.name));
536
- callPreludeLines.push(` const __args: unknown[] = [${positionalArgExprs.join(', ')}];`);
537
- if (requiredPosCount < positionalParams.length) {
538
- callPreludeLines.push(
539
- ` while (__args.length > ${requiredPosCount} && __args[__args.length - 1] === undefined) {`
540
- );
541
- callPreludeLines.push(` __args.pop();`);
542
- callPreludeLines.push(` }`);
543
- }
544
- if (hasKwArgs && requiredPosCount < positionalParams.length) {
545
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr('__candidate', {
546
- keywordOnlyNames,
547
- requiredKwOnlyNames,
548
- hasVarKwArgs,
549
- });
550
- callPreludeLines.push(
551
- ` if (__kwargs === undefined && __args.length > ${requiredPosCount}) {`
552
- );
553
- callPreludeLines.push(` const __candidate = __args[__args.length - 1];`);
554
- callPreludeLines.push(` if (${looksLikeKwargs}) {`);
555
- callPreludeLines.push(` __kwargs = __candidate as any;`);
556
- callPreludeLines.push(` __args.pop();`);
557
- callPreludeLines.push(` }`);
558
- callPreludeLines.push(` }`);
559
- }
560
- if (varArgsParam) {
561
- const vname = this.escapeIdentifier(varArgsParam.name);
562
- if (needsVarArgsArray) {
563
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr(vname, {
564
- keywordOnlyNames,
565
- requiredKwOnlyNames,
566
- hasVarKwArgs,
567
- });
568
- callPreludeLines.push(` let __varargs: unknown[] = [];`);
569
- callPreludeLines.push(` if (${vname} !== undefined) {`);
570
- callPreludeLines.push(` if (globalThis.Array.isArray(${vname})) {`);
571
- callPreludeLines.push(` __varargs = ${vname};`);
572
- callPreludeLines.push(` } else if (__kwargs === undefined && ${looksLikeKwargs}) {`);
573
- callPreludeLines.push(` __kwargs = ${vname} as any;`);
574
- callPreludeLines.push(` } else {`);
575
- callPreludeLines.push(
576
- ` throw new Error(\`${func.name} expected ${varArgsParam.name} to be an array\`);`
577
- );
578
- callPreludeLines.push(` }`);
579
- callPreludeLines.push(` }`);
580
- callPreludeLines.push(` __args.push(...__varargs);`);
581
- } else {
582
- callPreludeLines.push(` __args.push(...${vname});`);
583
- }
584
- }
520
+ const callDescriptor: CallDescriptor = {
521
+ positionalParams,
522
+ varArgsParam,
523
+ needsVarArgsArray,
524
+ hasKwArgs,
525
+ hasVarKwArgs,
526
+ keywordOnlyNames,
527
+ requiredKwOnlyNames,
528
+ positionalOnlyNames,
529
+ requiredPosCount,
530
+ indent: ' ',
531
+ errorLabel: func.name,
532
+ };
533
+ const guardLines = emitArgGuards(callDescriptor);
534
+ const guards = guardLines.length > 0 ? `${guardLines.join('\n')}\n` : '';
535
+ const callPreludeLines = emitCallPrelude(callDescriptor, this.callEmitHelpers());
585
536
  const callPrelude = callPreludeLines.length > 0 ? `${callPreludeLines.join('\n')}\n` : '';
586
537
 
587
538
  const ts = `${jsdoc}${overloadDecl}export async function ${fname}${typeParamDecl}(${paramDecl}): Promise<${returnType}> {
@@ -797,97 +748,23 @@ ${callPrelude}${guards} return getRuntimeBridge().call('${moduleId}', '${func.n
797
748
  }
798
749
  const overloadDecl = overloads.length > 0 ? `${overloads.join('\n')}\n` : '';
799
750
 
800
- const callPreludeLines: string[] = [];
801
- if (needsKwargsParam) {
802
- callPreludeLines.push(` let __kwargs = kwargs;`);
803
- }
804
- const positionalArgExprs = positionalParams.map(p => this.escapeIdentifier(p.name));
805
- callPreludeLines.push(` const __args: unknown[] = [${positionalArgExprs.join(', ')}];`);
806
- if (requiredPosCount < positionalParams.length) {
807
- callPreludeLines.push(
808
- ` while (__args.length > ${requiredPosCount} && __args[__args.length - 1] === undefined) {`
809
- );
810
- callPreludeLines.push(` __args.pop();`);
811
- callPreludeLines.push(` }`);
812
- }
813
- if (needsKwargsParam && requiredPosCount < positionalParams.length) {
814
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr('__candidate', {
815
- keywordOnlyNames,
816
- requiredKwOnlyNames,
817
- hasVarKwArgs,
818
- });
819
- callPreludeLines.push(
820
- ` if (__kwargs === undefined && __args.length > ${requiredPosCount}) {`
821
- );
822
- callPreludeLines.push(` const __candidate = __args[__args.length - 1];`);
823
- callPreludeLines.push(` if (${looksLikeKwargs}) {`);
824
- callPreludeLines.push(` __kwargs = __candidate as any;`);
825
- callPreludeLines.push(` __args.pop();`);
826
- callPreludeLines.push(` }`);
827
- callPreludeLines.push(` }`);
828
- }
829
- if (varArgsParam) {
830
- const vname = this.escapeIdentifier(varArgsParam.name);
831
- if (needsVarArgsArray) {
832
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr(vname, {
833
- keywordOnlyNames,
834
- requiredKwOnlyNames,
835
- hasVarKwArgs,
836
- });
837
- callPreludeLines.push(` let __varargs: unknown[] = [];`);
838
- callPreludeLines.push(` if (${vname} !== undefined) {`);
839
- callPreludeLines.push(` if (globalThis.Array.isArray(${vname})) {`);
840
- callPreludeLines.push(` __varargs = ${vname};`);
841
- callPreludeLines.push(
842
- ` } else if (__kwargs === undefined && ${looksLikeKwargs}) {`
843
- );
844
- callPreludeLines.push(` __kwargs = ${vname} as any;`);
845
- callPreludeLines.push(` } else {`);
846
- callPreludeLines.push(
847
- ` throw new Error(\`${method.name} expected ${varArgsParam.name} to be an array\`);`
848
- );
849
- callPreludeLines.push(` }`);
850
- callPreludeLines.push(` }`);
851
- callPreludeLines.push(` __args.push(...__varargs);`);
852
- } else {
853
- callPreludeLines.push(` __args.push(...${vname});`);
854
- }
855
- }
751
+ const callDescriptor: CallDescriptor = {
752
+ positionalParams,
753
+ varArgsParam,
754
+ needsVarArgsArray,
755
+ hasKwArgs: needsKwargsParam,
756
+ hasVarKwArgs,
757
+ keywordOnlyNames,
758
+ requiredKwOnlyNames,
759
+ positionalOnlyNames,
760
+ requiredPosCount,
761
+ indent: ' ',
762
+ errorLabel: method.name,
763
+ };
764
+ const callPreludeLines = emitCallPrelude(callDescriptor, this.callEmitHelpers());
856
765
  const callPrelude = callPreludeLines.length > 0 ? `${callPreludeLines.join('\n')}\n` : '';
857
766
 
858
- const guardLines: string[] = [];
859
- if (needsKwargsParam && positionalOnlyNames.length > 0) {
860
- guardLines.push(
861
- ` const __positionalOnly = ${JSON.stringify(positionalOnlyNames)} as const;`
862
- );
863
- guardLines.push(` for (const key of __positionalOnly) {`);
864
- guardLines.push(
865
- ` if (__kwargs && Object.prototype.hasOwnProperty.call(__kwargs, key)) {`
866
- );
867
- guardLines.push(
868
- ` throw new Error(\`${method.name} does not accept positional-only argument "\${key}" as a keyword argument\`);`
869
- );
870
- guardLines.push(` }`);
871
- guardLines.push(` }`);
872
- }
873
- if (needsKwargsParam && requiredKwOnlyNames.length > 0) {
874
- guardLines.push(
875
- ` const __requiredKwOnly = ${JSON.stringify(requiredKwOnlyNames)} as const;`
876
- );
877
- guardLines.push(` const __missing: string[] = [];`);
878
- guardLines.push(` for (const key of __requiredKwOnly) {`);
879
- guardLines.push(
880
- ` if (!__kwargs || !Object.prototype.hasOwnProperty.call(__kwargs, key)) {`
881
- );
882
- guardLines.push(` __missing.push(key);`);
883
- guardLines.push(` }`);
884
- guardLines.push(` }`);
885
- guardLines.push(` if (__missing.length > 0) {`);
886
- guardLines.push(
887
- ` throw new Error(\`Missing required keyword-only arguments for ${method.name}: \${__missing.join(', ')}\`);`
888
- );
889
- guardLines.push(` }`);
890
- }
767
+ const guardLines = emitArgGuards(callDescriptor);
891
768
  const guards = guardLines.length > 0 ? `${guardLines.join('\n')}\n` : '';
892
769
 
893
770
  methodBodies.push(`${overloadDecl} async ${mname}${methodTypeParamDecl}(${paramsDecl}): Promise<${returnType}> {
@@ -994,95 +871,22 @@ ${callPrelude}${guards} return getRuntimeBridge().callMethod(this.__handle, '
994
871
  ? overloadDecl
995
872
  : ` static create${classTypeParamDecl}(${paramsDecl}): Promise<${classSelfType}>;\n`;
996
873
 
997
- const guardLines: string[] = [];
998
- const callPreludeLines: string[] = [];
999
- if (needsKwargsParam) {
1000
- callPreludeLines.push(` let __kwargs = kwargs;`);
1001
- }
1002
- const positionalArgExprs = positionalParams.map(p => this.escapeIdentifier(p.name));
1003
- callPreludeLines.push(` const __args: unknown[] = [${positionalArgExprs.join(', ')}];`);
1004
- if (requiredPosCount < positionalParams.length) {
1005
- callPreludeLines.push(
1006
- ` while (__args.length > ${requiredPosCount} && __args[__args.length - 1] === undefined) {`
1007
- );
1008
- callPreludeLines.push(` __args.pop();`);
1009
- callPreludeLines.push(` }`);
1010
- }
1011
- if (needsKwargsParam && requiredPosCount < positionalParams.length) {
1012
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr('__candidate', {
1013
- keywordOnlyNames,
1014
- requiredKwOnlyNames,
1015
- hasVarKwArgs,
1016
- });
1017
- callPreludeLines.push(
1018
- ` if (__kwargs === undefined && __args.length > ${requiredPosCount}) {`
1019
- );
1020
- callPreludeLines.push(` const __candidate = __args[__args.length - 1];`);
1021
- callPreludeLines.push(` if (${looksLikeKwargs}) {`);
1022
- callPreludeLines.push(` __kwargs = __candidate as any;`);
1023
- callPreludeLines.push(` __args.pop();`);
1024
- callPreludeLines.push(` }`);
1025
- callPreludeLines.push(` }`);
1026
- }
1027
- if (varArgsParam) {
1028
- const vname = this.escapeIdentifier(varArgsParam.name);
1029
- if (needsVarArgsArray) {
1030
- const looksLikeKwargs = this.renderLooksLikeKwargsExpr(vname, {
1031
- keywordOnlyNames,
1032
- requiredKwOnlyNames,
1033
- hasVarKwArgs,
1034
- });
1035
- callPreludeLines.push(` let __varargs: unknown[] = [];`);
1036
- callPreludeLines.push(` if (${vname} !== undefined) {`);
1037
- callPreludeLines.push(` if (globalThis.Array.isArray(${vname})) {`);
1038
- callPreludeLines.push(` __varargs = ${vname};`);
1039
- callPreludeLines.push(` } else if (__kwargs === undefined && ${looksLikeKwargs}) {`);
1040
- callPreludeLines.push(` __kwargs = ${vname} as any;`);
1041
- callPreludeLines.push(` } else {`);
1042
- callPreludeLines.push(
1043
- ` throw new Error(\`__init__ expected ${varArgsParam.name} to be an array\`);`
1044
- );
1045
- callPreludeLines.push(` }`);
1046
- callPreludeLines.push(` }`);
1047
- callPreludeLines.push(` __args.push(...__varargs);`);
1048
- } else {
1049
- callPreludeLines.push(` __args.push(...${vname});`);
1050
- }
1051
- }
874
+ const callDescriptor: CallDescriptor = {
875
+ positionalParams,
876
+ varArgsParam,
877
+ needsVarArgsArray,
878
+ hasKwArgs: needsKwargsParam,
879
+ hasVarKwArgs,
880
+ keywordOnlyNames,
881
+ requiredKwOnlyNames,
882
+ positionalOnlyNames,
883
+ requiredPosCount,
884
+ indent: ' ',
885
+ errorLabel: '__init__',
886
+ };
887
+ const callPreludeLines = emitCallPrelude(callDescriptor, this.callEmitHelpers());
1052
888
  const callPrelude = callPreludeLines.length > 0 ? `${callPreludeLines.join('\n')}\n` : '';
1053
-
1054
- if (needsKwargsParam && positionalOnlyNames.length > 0) {
1055
- guardLines.push(
1056
- ` const __positionalOnly = ${JSON.stringify(positionalOnlyNames)} as const;`
1057
- );
1058
- guardLines.push(` for (const key of __positionalOnly) {`);
1059
- guardLines.push(
1060
- ` if (__kwargs && Object.prototype.hasOwnProperty.call(__kwargs, key)) {`
1061
- );
1062
- guardLines.push(
1063
- ` throw new Error(\`__init__ does not accept positional-only argument "\${key}" as a keyword argument\`);`
1064
- );
1065
- guardLines.push(` }`);
1066
- guardLines.push(` }`);
1067
- }
1068
- if (needsKwargsParam && requiredKwOnlyNames.length > 0) {
1069
- guardLines.push(
1070
- ` const __requiredKwOnly = ${JSON.stringify(requiredKwOnlyNames)} as const;`
1071
- );
1072
- guardLines.push(` const __missing: string[] = [];`);
1073
- guardLines.push(` for (const key of __requiredKwOnly) {`);
1074
- guardLines.push(
1075
- ` if (!__kwargs || !Object.prototype.hasOwnProperty.call(__kwargs, key)) {`
1076
- );
1077
- guardLines.push(` __missing.push(key);`);
1078
- guardLines.push(` }`);
1079
- guardLines.push(` }`);
1080
- guardLines.push(` if (__missing.length > 0) {`);
1081
- guardLines.push(
1082
- ` throw new Error(\`Missing required keyword-only arguments for __init__: \${__missing.join(', ')}\`);`
1083
- );
1084
- guardLines.push(` }`);
1085
- }
889
+ const guardLines = emitArgGuards(callDescriptor);
1086
890
 
1087
891
  return {
1088
892
  overloadDecl,
@@ -503,119 +503,134 @@ export class TypeMapper {
503
503
  readonly: false,
504
504
  });
505
505
 
506
- if (this.hasPreset('stdlib')) {
507
- const stdlibModule = moduleName ?? '';
508
- const allowModule =
509
- !moduleName ||
510
- stdlibModule === 'datetime' ||
511
- stdlibModule === 'decimal' ||
512
- stdlibModule === 'uuid' ||
513
- stdlibModule === 'pathlib';
514
- if (allowModule) {
515
- if (name === 'datetime' || name === 'date' || name === 'time') {
516
- return { kind: 'primitive', name: 'string' };
517
- }
518
- if (name === 'timedelta') {
519
- return { kind: 'primitive', name: 'number' };
520
- }
521
- if (name === 'Decimal' || name === 'UUID') {
522
- return { kind: 'primitive', name: 'string' };
523
- }
524
- if (
525
- name === 'Path' ||
526
- name === 'PurePath' ||
527
- name === 'PosixPath' ||
528
- name === 'WindowsPath'
529
- ) {
530
- return { kind: 'primitive', name: 'string' };
531
- }
532
- }
533
- }
534
-
535
- if (this.hasPreset('pandas')) {
536
- const allowModule = !moduleName || moduleName.startsWith('pandas');
537
- if (allowModule) {
538
- if (name === 'DataFrame') {
539
- const recordsArray: TSArrayType = { kind: 'array', elementType: recordObject };
540
- return { kind: 'union', types: [recordObject, recordsArray] };
541
- }
542
- if (name === 'Series') {
543
- const valuesArray: TSArrayType = {
544
- kind: 'array',
545
- elementType: { kind: 'primitive', name: 'unknown' },
546
- };
547
- return { kind: 'union', types: [valuesArray, recordObject] };
548
- }
549
- }
550
- }
551
-
552
- if (this.hasPreset('pydantic')) {
553
- const allowModule = !moduleName || moduleName.startsWith('pydantic');
554
- if (allowModule && name === 'BaseModel') {
555
- return recordObject;
556
- }
557
- }
506
+ const baseProps = [prop('shape', numberArray), prop('dtype', stringType, true)];
507
+ const buildSparse = (format: 'csr' | 'csc' | 'coo'): TSObjectType => ({
508
+ kind: 'object',
509
+ properties: [
510
+ prop('format', { kind: 'literal', value: format }),
511
+ ...baseProps,
512
+ prop('data', unknownArray),
513
+ ...(format === 'coo'
514
+ ? [prop('row', numberArray), prop('col', numberArray)]
515
+ : [prop('indices', numberArray), prop('indptr', numberArray)]),
516
+ ],
517
+ });
558
518
 
559
- if (this.hasPreset('scipy')) {
560
- const allowModule = !moduleName || moduleName.startsWith('scipy');
561
- if (allowModule) {
562
- const baseProps = [prop('shape', numberArray), prop('dtype', stringType, true)];
563
- const buildSparse = (format: 'csr' | 'csc' | 'coo'): TSObjectType => ({
564
- kind: 'object',
565
- properties: [
566
- prop('format', { kind: 'literal', value: format }),
567
- ...baseProps,
568
- prop('data', unknownArray),
569
- ...(format === 'coo'
570
- ? [prop('row', numberArray), prop('col', numberArray)]
571
- : [prop('indices', numberArray), prop('indptr', numberArray)]),
572
- ],
573
- });
574
- if (name === 'csr_matrix') {
575
- return buildSparse('csr');
576
- }
577
- if (name === 'csc_matrix') {
578
- return buildSparse('csc');
579
- }
580
- if (name === 'coo_matrix') {
581
- return buildSparse('coo');
582
- }
583
- if (name === 'spmatrix') {
584
- return {
585
- kind: 'union',
586
- types: [buildSparse('csr'), buildSparse('csc'), buildSparse('coo')],
587
- };
588
- }
589
- }
590
- }
519
+ // Each entry pairs a preset with the module(s) it owns and a name->type resolver.
520
+ // Entries are evaluated in order; the first enabled preset whose module is allowed
521
+ // and whose resolver returns a type wins. This preserves the prior cascading-`if`
522
+ // precedence while keeping the per-preset rules table-driven.
523
+ const presetResolvers: ReadonlyArray<{
524
+ preset: TypePreset;
525
+ allowModule: (moduleName: string | undefined) => boolean;
526
+ resolve: (name: string) => TypescriptType | undefined;
527
+ }> = [
528
+ {
529
+ preset: 'stdlib',
530
+ allowModule: m =>
531
+ !m || m === 'datetime' || m === 'decimal' || m === 'uuid' || m === 'pathlib',
532
+ resolve: n => {
533
+ if (n === 'datetime' || n === 'date' || n === 'time') {
534
+ return { kind: 'primitive', name: 'string' };
535
+ }
536
+ if (n === 'timedelta') {
537
+ return { kind: 'primitive', name: 'number' };
538
+ }
539
+ if (n === 'Decimal' || n === 'UUID') {
540
+ return { kind: 'primitive', name: 'string' };
541
+ }
542
+ if (n === 'Path' || n === 'PurePath' || n === 'PosixPath' || n === 'WindowsPath') {
543
+ return { kind: 'primitive', name: 'string' };
544
+ }
545
+ return undefined;
546
+ },
547
+ },
548
+ {
549
+ preset: 'pandas',
550
+ allowModule: m => !m || m.startsWith('pandas'),
551
+ resolve: n => {
552
+ if (n === 'DataFrame') {
553
+ const recordsArray: TSArrayType = { kind: 'array', elementType: recordObject };
554
+ return { kind: 'union', types: [recordObject, recordsArray] };
555
+ }
556
+ if (n === 'Series') {
557
+ const valuesArray: TSArrayType = {
558
+ kind: 'array',
559
+ elementType: { kind: 'primitive', name: 'unknown' },
560
+ };
561
+ return { kind: 'union', types: [valuesArray, recordObject] };
562
+ }
563
+ return undefined;
564
+ },
565
+ },
566
+ {
567
+ preset: 'pydantic',
568
+ allowModule: m => !m || m.startsWith('pydantic'),
569
+ resolve: n => (n === 'BaseModel' ? recordObject : undefined),
570
+ },
571
+ {
572
+ preset: 'scipy',
573
+ allowModule: m => !m || m.startsWith('scipy'),
574
+ resolve: n => {
575
+ if (n === 'csr_matrix') {
576
+ return buildSparse('csr');
577
+ }
578
+ if (n === 'csc_matrix') {
579
+ return buildSparse('csc');
580
+ }
581
+ if (n === 'coo_matrix') {
582
+ return buildSparse('coo');
583
+ }
584
+ if (n === 'spmatrix') {
585
+ return {
586
+ kind: 'union',
587
+ types: [buildSparse('csr'), buildSparse('csc'), buildSparse('coo')],
588
+ };
589
+ }
590
+ return undefined;
591
+ },
592
+ },
593
+ {
594
+ preset: 'torch',
595
+ allowModule: m => !m || m.startsWith('torch'),
596
+ resolve: n =>
597
+ n === 'Tensor'
598
+ ? {
599
+ kind: 'object',
600
+ properties: [
601
+ prop('data', unknownType),
602
+ prop('shape', numberArray),
603
+ prop('dtype', stringType, true),
604
+ prop('device', stringType, true),
605
+ ],
606
+ }
607
+ : undefined,
608
+ },
609
+ {
610
+ preset: 'sklearn',
611
+ allowModule: m => !m || m.startsWith('sklearn'),
612
+ resolve: n =>
613
+ n === 'BaseEstimator'
614
+ ? {
615
+ kind: 'object',
616
+ properties: [
617
+ prop('className', stringType),
618
+ prop('module', stringType),
619
+ prop('version', stringType, true),
620
+ prop('params', recordObject),
621
+ ],
622
+ }
623
+ : undefined,
624
+ },
625
+ ];
591
626
 
592
- if (this.hasPreset('torch')) {
593
- const allowModule = !moduleName || moduleName.startsWith('torch');
594
- if (allowModule && name === 'Tensor') {
595
- return {
596
- kind: 'object',
597
- properties: [
598
- prop('data', unknownType),
599
- prop('shape', numberArray),
600
- prop('dtype', stringType, true),
601
- prop('device', stringType, true),
602
- ],
603
- };
627
+ for (const entry of presetResolvers) {
628
+ if (!this.hasPreset(entry.preset) || !entry.allowModule(moduleName)) {
629
+ continue;
604
630
  }
605
- }
606
-
607
- if (this.hasPreset('sklearn')) {
608
- const allowModule = !moduleName || moduleName.startsWith('sklearn');
609
- if (allowModule && name === 'BaseEstimator') {
610
- return {
611
- kind: 'object',
612
- properties: [
613
- prop('className', stringType),
614
- prop('module', stringType),
615
- prop('version', stringType, true),
616
- prop('params', recordObject),
617
- ],
618
- };
631
+ const resolved = entry.resolve(name);
632
+ if (resolved !== undefined) {
633
+ return resolved;
619
634
  }
620
635
  }
621
636