typenative 0.1.3 → 0.3.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 (505) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/workflows/ci.yml +39 -2
  3. package/.github/workflows/npm-publish-github-packages.yml +7 -2
  4. package/CHANGELOG.md +293 -0
  5. package/CONTRIBUTION.md +8 -6
  6. package/README.md +15 -11
  7. package/bin/cli.js +7 -1
  8. package/bin/go/bridge.js +919 -0
  9. package/bin/go/convert.js +238 -0
  10. package/bin/go/metadata.js +211 -0
  11. package/bin/go/overrides.js +68 -0
  12. package/bin/go/stdlib.generated.js +175 -0
  13. package/bin/index.js +337 -239
  14. package/bin/main.js +9 -2
  15. package/bin/node/assert.js +344 -0
  16. package/bin/node/async_hooks.js +273 -0
  17. package/bin/node/buffer.js +765 -0
  18. package/bin/node/child_process.js +600 -0
  19. package/bin/node/cluster.js +1002 -0
  20. package/bin/node/console.js +121 -0
  21. package/bin/node/core.js +111 -0
  22. package/bin/node/crypto.js +414 -0
  23. package/bin/node/crypto_keys.js +2685 -0
  24. package/bin/node/dgram.js +388 -0
  25. package/bin/node/diagnostics_channel.js +534 -0
  26. package/bin/node/dns.js +635 -0
  27. package/bin/node/events.js +743 -0
  28. package/bin/node/fetch.js +737 -0
  29. package/bin/node/fs.js +998 -0
  30. package/bin/node/http.js +1500 -0
  31. package/bin/node/http2.js +688 -0
  32. package/bin/node/net.js +1143 -0
  33. package/bin/node/os.js +257 -0
  34. package/bin/node/path.js +758 -0
  35. package/bin/node/process.js +316 -0
  36. package/bin/node/punycode.js +195 -0
  37. package/bin/node/querystring.js +143 -0
  38. package/bin/node/readline.js +299 -0
  39. package/bin/node/registry.js +161 -0
  40. package/bin/node/sqlite.js +1142 -0
  41. package/bin/node/stream.js +1221 -0
  42. package/bin/node/stream_web.js +2259 -0
  43. package/bin/node/string_decoder.js +101 -0
  44. package/bin/node/system_extra.js +714 -0
  45. package/bin/node/test.js +1068 -0
  46. package/bin/node/timers.js +263 -0
  47. package/bin/node/tls.js +719 -0
  48. package/bin/node/url.js +861 -0
  49. package/bin/node/util.js +1311 -0
  50. package/bin/node/util_extra.js +599 -0
  51. package/bin/node/worker_threads.js +745 -0
  52. package/bin/node/zlib.js +263 -0
  53. package/bin/parse-native.js +10 -0
  54. package/bin/parse-node.js +10 -0
  55. package/bin/transpiler.js +2824 -588
  56. package/bin/typecheck-node.js +78 -0
  57. package/bin/version.js +2 -0
  58. package/conformance.md +708 -241
  59. package/govendor/github.com/ncruces/go-sqlite3/LICENSE +21 -0
  60. package/govendor/github.com/ncruces/go-sqlite3/README.md +121 -0
  61. package/govendor/github.com/ncruces/go-sqlite3/backup.go +132 -0
  62. package/govendor/github.com/ncruces/go-sqlite3/blob.go +266 -0
  63. package/govendor/github.com/ncruces/go-sqlite3/config.go +397 -0
  64. package/govendor/github.com/ncruces/go-sqlite3/conn.go +540 -0
  65. package/govendor/github.com/ncruces/go-sqlite3/conn_127.go +93 -0
  66. package/govendor/github.com/ncruces/go-sqlite3/const.go +436 -0
  67. package/govendor/github.com/ncruces/go-sqlite3/context.go +222 -0
  68. package/govendor/github.com/ncruces/go-sqlite3/error.go +182 -0
  69. package/govendor/github.com/ncruces/go-sqlite3/ext.go +153 -0
  70. package/govendor/github.com/ncruces/go-sqlite3/func.go +309 -0
  71. package/govendor/github.com/ncruces/go-sqlite3/go.mod +3 -0
  72. package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk.go +29 -0
  73. package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk_other.go +13 -0
  74. package/govendor/github.com/ncruces/go-sqlite3/internal/dotlk/dotlk_unix.go +57 -0
  75. package/govendor/github.com/ncruces/go-sqlite3/internal/errutil/error.go +41 -0
  76. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/alloc.go +128 -0
  77. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/const.go +122 -0
  78. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/error.go +77 -0
  79. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem.go +116 -0
  80. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_other.go +34 -0
  81. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_unix.go +87 -0
  82. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mem_windows.go +149 -0
  83. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mmap_unix.go +80 -0
  84. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/mmap_windows.go +129 -0
  85. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/sys_windows.go +60 -0
  86. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap.go +82 -0
  87. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap_sys.go +113 -0
  88. package/govendor/github.com/ncruces/go-sqlite3/internal/sqlite3_wrap/wrap_vfs.go +181 -0
  89. package/govendor/github.com/ncruces/go-sqlite3/internal/testenv/env.go +55 -0
  90. package/govendor/github.com/ncruces/go-sqlite3/internal/util/json.go +54 -0
  91. package/govendor/github.com/ncruces/go-sqlite3/internal/util/json_v2.go +54 -0
  92. package/govendor/github.com/ncruces/go-sqlite3/internal/util/math.go +29 -0
  93. package/govendor/github.com/ncruces/go-sqlite3/internal/util/page.go +5 -0
  94. package/govendor/github.com/ncruces/go-sqlite3/internal/util/parse.go +142 -0
  95. package/govendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go +3 -0
  96. package/govendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go +10 -0
  97. package/govendor/github.com/ncruces/go-sqlite3/json.go +100 -0
  98. package/govendor/github.com/ncruces/go-sqlite3/json_v2.go +116 -0
  99. package/govendor/github.com/ncruces/go-sqlite3/pointer.go +13 -0
  100. package/govendor/github.com/ncruces/go-sqlite3/quote.go +140 -0
  101. package/govendor/github.com/ncruces/go-sqlite3/stmt.go +692 -0
  102. package/govendor/github.com/ncruces/go-sqlite3/time.go +360 -0
  103. package/govendor/github.com/ncruces/go-sqlite3/txn.go +374 -0
  104. package/govendor/github.com/ncruces/go-sqlite3/value.go +212 -0
  105. package/govendor/github.com/ncruces/go-sqlite3/vfs/README.md +133 -0
  106. package/govendor/github.com/ncruces/go-sqlite3/vfs/api.go +239 -0
  107. package/govendor/github.com/ncruces/go-sqlite3/vfs/cksm.go +143 -0
  108. package/govendor/github.com/ncruces/go-sqlite3/vfs/const.go +256 -0
  109. package/govendor/github.com/ncruces/go-sqlite3/vfs/file.go +260 -0
  110. package/govendor/github.com/ncruces/go-sqlite3/vfs/filename.go +187 -0
  111. package/govendor/github.com/ncruces/go-sqlite3/vfs/lock.go +128 -0
  112. package/govendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go +23 -0
  113. package/govendor/github.com/ncruces/go-sqlite3/vfs/map.go +63 -0
  114. package/govendor/github.com/ncruces/go-sqlite3/vfs/map_other.go +13 -0
  115. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go +110 -0
  116. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go +131 -0
  117. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go +140 -0
  118. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go +56 -0
  119. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go +117 -0
  120. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go +59 -0
  121. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std.go +50 -0
  122. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go +19 -0
  123. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go +9 -0
  124. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_rw.go +13 -0
  125. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go +9 -0
  126. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_std_temp.go +16 -0
  127. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go +108 -0
  128. package/govendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go +180 -0
  129. package/govendor/github.com/ncruces/go-sqlite3/vfs/registry.go +48 -0
  130. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm.go +26 -0
  131. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go +258 -0
  132. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go +90 -0
  133. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go +168 -0
  134. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_memlk.go +52 -0
  135. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go +185 -0
  136. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go +21 -0
  137. package/govendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go +124 -0
  138. package/govendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +455 -0
  139. package/govendor/github.com/ncruces/go-sqlite3/vtab.go +677 -0
  140. package/govendor/github.com/ncruces/go-sqlite3/wrap.go +48 -0
  141. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/LICENSE +18 -0
  142. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/README.md +11 -0
  143. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/go.mod +3 -0
  144. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/libc.go +215 -0
  145. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/sqlite3.dat +0 -0
  146. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/sqlite3.go +157903 -0
  147. package/govendor/github.com/ncruces/go-sqlite3-wasm/v6/tools.sh +38 -0
  148. package/govendor/github.com/ncruces/julianday/LICENSE +21 -0
  149. package/govendor/github.com/ncruces/julianday/README.md +9 -0
  150. package/govendor/github.com/ncruces/julianday/go.mod +3 -0
  151. package/govendor/github.com/ncruces/julianday/julianday.go +124 -0
  152. package/govendor/golang.org/x/sys/LICENSE +27 -0
  153. package/govendor/golang.org/x/sys/PATENTS +22 -0
  154. package/govendor/golang.org/x/sys/go.mod +3 -0
  155. package/govendor/golang.org/x/sys/unix/README.md +184 -0
  156. package/govendor/golang.org/x/sys/unix/affinity_linux.go +189 -0
  157. package/govendor/golang.org/x/sys/unix/aliases.go +13 -0
  158. package/govendor/golang.org/x/sys/unix/asm_aix_ppc64.s +17 -0
  159. package/govendor/golang.org/x/sys/unix/asm_bsd_386.s +27 -0
  160. package/govendor/golang.org/x/sys/unix/asm_bsd_amd64.s +27 -0
  161. package/govendor/golang.org/x/sys/unix/asm_bsd_arm.s +27 -0
  162. package/govendor/golang.org/x/sys/unix/asm_bsd_arm64.s +27 -0
  163. package/govendor/golang.org/x/sys/unix/asm_bsd_ppc64.s +29 -0
  164. package/govendor/golang.org/x/sys/unix/asm_bsd_riscv64.s +27 -0
  165. package/govendor/golang.org/x/sys/unix/asm_linux_386.s +65 -0
  166. package/govendor/golang.org/x/sys/unix/asm_linux_amd64.s +57 -0
  167. package/govendor/golang.org/x/sys/unix/asm_linux_arm.s +56 -0
  168. package/govendor/golang.org/x/sys/unix/asm_linux_arm64.s +50 -0
  169. package/govendor/golang.org/x/sys/unix/asm_linux_loong64.s +51 -0
  170. package/govendor/golang.org/x/sys/unix/asm_linux_mips64x.s +54 -0
  171. package/govendor/golang.org/x/sys/unix/asm_linux_mipsx.s +52 -0
  172. package/govendor/golang.org/x/sys/unix/asm_linux_ppc64x.s +42 -0
  173. package/govendor/golang.org/x/sys/unix/asm_linux_riscv64.s +47 -0
  174. package/govendor/golang.org/x/sys/unix/asm_linux_s390x.s +54 -0
  175. package/govendor/golang.org/x/sys/unix/asm_openbsd_mips64.s +29 -0
  176. package/govendor/golang.org/x/sys/unix/asm_solaris_amd64.s +17 -0
  177. package/govendor/golang.org/x/sys/unix/asm_zos_s390x.s +382 -0
  178. package/govendor/golang.org/x/sys/unix/auxv.go +36 -0
  179. package/govendor/golang.org/x/sys/unix/auxv_unsupported.go +13 -0
  180. package/govendor/golang.org/x/sys/unix/bluetooth_linux.go +36 -0
  181. package/govendor/golang.org/x/sys/unix/bpxsvc_zos.go +657 -0
  182. package/govendor/golang.org/x/sys/unix/bpxsvc_zos.s +192 -0
  183. package/govendor/golang.org/x/sys/unix/cap_freebsd.go +195 -0
  184. package/govendor/golang.org/x/sys/unix/constants.go +13 -0
  185. package/govendor/golang.org/x/sys/unix/dev_aix_ppc.go +26 -0
  186. package/govendor/golang.org/x/sys/unix/dev_aix_ppc64.go +28 -0
  187. package/govendor/golang.org/x/sys/unix/dev_darwin.go +24 -0
  188. package/govendor/golang.org/x/sys/unix/dev_dragonfly.go +30 -0
  189. package/govendor/golang.org/x/sys/unix/dev_freebsd.go +30 -0
  190. package/govendor/golang.org/x/sys/unix/dev_linux.go +42 -0
  191. package/govendor/golang.org/x/sys/unix/dev_netbsd.go +29 -0
  192. package/govendor/golang.org/x/sys/unix/dev_openbsd.go +29 -0
  193. package/govendor/golang.org/x/sys/unix/dev_zos.go +28 -0
  194. package/govendor/golang.org/x/sys/unix/dirent.go +102 -0
  195. package/govendor/golang.org/x/sys/unix/endian_big.go +9 -0
  196. package/govendor/golang.org/x/sys/unix/endian_little.go +9 -0
  197. package/govendor/golang.org/x/sys/unix/env_unix.go +31 -0
  198. package/govendor/golang.org/x/sys/unix/fcntl.go +36 -0
  199. package/govendor/golang.org/x/sys/unix/fcntl_darwin.go +24 -0
  200. package/govendor/golang.org/x/sys/unix/fcntl_linux_32bit.go +13 -0
  201. package/govendor/golang.org/x/sys/unix/fdset.go +27 -0
  202. package/govendor/golang.org/x/sys/unix/gccgo.go +59 -0
  203. package/govendor/golang.org/x/sys/unix/gccgo_c.c +44 -0
  204. package/govendor/golang.org/x/sys/unix/gccgo_linux_amd64.go +20 -0
  205. package/govendor/golang.org/x/sys/unix/ifreq_linux.go +145 -0
  206. package/govendor/golang.org/x/sys/unix/ioctl_linux.go +341 -0
  207. package/govendor/golang.org/x/sys/unix/ioctl_signed.go +74 -0
  208. package/govendor/golang.org/x/sys/unix/ioctl_unsigned.go +74 -0
  209. package/govendor/golang.org/x/sys/unix/ioctl_zos.go +71 -0
  210. package/govendor/golang.org/x/sys/unix/mkall.sh +250 -0
  211. package/govendor/golang.org/x/sys/unix/mkerrors.sh +807 -0
  212. package/govendor/golang.org/x/sys/unix/mmap_nomremap.go +13 -0
  213. package/govendor/golang.org/x/sys/unix/mremap.go +57 -0
  214. package/govendor/golang.org/x/sys/unix/pagesize_unix.go +15 -0
  215. package/govendor/golang.org/x/sys/unix/pledge_openbsd.go +111 -0
  216. package/govendor/golang.org/x/sys/unix/ptrace_darwin.go +11 -0
  217. package/govendor/golang.org/x/sys/unix/ptrace_ios.go +11 -0
  218. package/govendor/golang.org/x/sys/unix/race.go +30 -0
  219. package/govendor/golang.org/x/sys/unix/race0.go +25 -0
  220. package/govendor/golang.org/x/sys/unix/readdirent_getdents.go +12 -0
  221. package/govendor/golang.org/x/sys/unix/readdirent_getdirentries.go +19 -0
  222. package/govendor/golang.org/x/sys/unix/readv_unix.go +103 -0
  223. package/govendor/golang.org/x/sys/unix/sockcmsg_dragonfly.go +16 -0
  224. package/govendor/golang.org/x/sys/unix/sockcmsg_linux.go +85 -0
  225. package/govendor/golang.org/x/sys/unix/sockcmsg_unix.go +106 -0
  226. package/govendor/golang.org/x/sys/unix/sockcmsg_unix_other.go +46 -0
  227. package/govendor/golang.org/x/sys/unix/sockcmsg_zos.go +58 -0
  228. package/govendor/golang.org/x/sys/unix/symaddr_zos_s390x.s +75 -0
  229. package/govendor/golang.org/x/sys/unix/syscall.go +86 -0
  230. package/govendor/golang.org/x/sys/unix/syscall_aix.go +582 -0
  231. package/govendor/golang.org/x/sys/unix/syscall_aix_ppc.go +52 -0
  232. package/govendor/golang.org/x/sys/unix/syscall_aix_ppc64.go +83 -0
  233. package/govendor/golang.org/x/sys/unix/syscall_bsd.go +609 -0
  234. package/govendor/golang.org/x/sys/unix/syscall_darwin.go +711 -0
  235. package/govendor/golang.org/x/sys/unix/syscall_darwin_amd64.go +50 -0
  236. package/govendor/golang.org/x/sys/unix/syscall_darwin_arm64.go +50 -0
  237. package/govendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go +26 -0
  238. package/govendor/golang.org/x/sys/unix/syscall_dragonfly.go +359 -0
  239. package/govendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go +56 -0
  240. package/govendor/golang.org/x/sys/unix/syscall_freebsd.go +455 -0
  241. package/govendor/golang.org/x/sys/unix/syscall_freebsd_386.go +64 -0
  242. package/govendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go +64 -0
  243. package/govendor/golang.org/x/sys/unix/syscall_freebsd_arm.go +60 -0
  244. package/govendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go +60 -0
  245. package/govendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go +60 -0
  246. package/govendor/golang.org/x/sys/unix/syscall_hurd.go +30 -0
  247. package/govendor/golang.org/x/sys/unix/syscall_hurd_386.go +28 -0
  248. package/govendor/golang.org/x/sys/unix/syscall_illumos.go +78 -0
  249. package/govendor/golang.org/x/sys/unix/syscall_linux.go +2574 -0
  250. package/govendor/golang.org/x/sys/unix/syscall_linux_386.go +313 -0
  251. package/govendor/golang.org/x/sys/unix/syscall_linux_alarm.go +12 -0
  252. package/govendor/golang.org/x/sys/unix/syscall_linux_amd64.go +144 -0
  253. package/govendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go +12 -0
  254. package/govendor/golang.org/x/sys/unix/syscall_linux_arm.go +218 -0
  255. package/govendor/golang.org/x/sys/unix/syscall_linux_arm64.go +188 -0
  256. package/govendor/golang.org/x/sys/unix/syscall_linux_gc.go +14 -0
  257. package/govendor/golang.org/x/sys/unix/syscall_linux_gc_386.go +16 -0
  258. package/govendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go +13 -0
  259. package/govendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go +30 -0
  260. package/govendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go +20 -0
  261. package/govendor/golang.org/x/sys/unix/syscall_linux_loong64.go +220 -0
  262. package/govendor/golang.org/x/sys/unix/syscall_linux_mips64x.go +187 -0
  263. package/govendor/golang.org/x/sys/unix/syscall_linux_mipsx.go +173 -0
  264. package/govendor/golang.org/x/sys/unix/syscall_linux_ppc.go +203 -0
  265. package/govendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go +114 -0
  266. package/govendor/golang.org/x/sys/unix/syscall_linux_riscv64.go +193 -0
  267. package/govendor/golang.org/x/sys/unix/syscall_linux_s390x.go +295 -0
  268. package/govendor/golang.org/x/sys/unix/syscall_linux_sparc64.go +111 -0
  269. package/govendor/golang.org/x/sys/unix/syscall_netbsd.go +388 -0
  270. package/govendor/golang.org/x/sys/unix/syscall_netbsd_386.go +37 -0
  271. package/govendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go +37 -0
  272. package/govendor/golang.org/x/sys/unix/syscall_netbsd_arm.go +37 -0
  273. package/govendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go +37 -0
  274. package/govendor/golang.org/x/sys/unix/syscall_openbsd.go +346 -0
  275. package/govendor/golang.org/x/sys/unix/syscall_openbsd_386.go +41 -0
  276. package/govendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go +41 -0
  277. package/govendor/golang.org/x/sys/unix/syscall_openbsd_arm.go +41 -0
  278. package/govendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go +41 -0
  279. package/govendor/golang.org/x/sys/unix/syscall_openbsd_libc.go +26 -0
  280. package/govendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go +39 -0
  281. package/govendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go +41 -0
  282. package/govendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go +41 -0
  283. package/govendor/golang.org/x/sys/unix/syscall_solaris.go +1183 -0
  284. package/govendor/golang.org/x/sys/unix/syscall_solaris_amd64.go +27 -0
  285. package/govendor/golang.org/x/sys/unix/syscall_unix.go +619 -0
  286. package/govendor/golang.org/x/sys/unix/syscall_unix_gc.go +14 -0
  287. package/govendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go +22 -0
  288. package/govendor/golang.org/x/sys/unix/syscall_zos_s390x.go +3213 -0
  289. package/govendor/golang.org/x/sys/unix/sysvshm_linux.go +20 -0
  290. package/govendor/golang.org/x/sys/unix/sysvshm_unix.go +51 -0
  291. package/govendor/golang.org/x/sys/unix/sysvshm_unix_other.go +13 -0
  292. package/govendor/golang.org/x/sys/unix/timestruct.go +76 -0
  293. package/govendor/golang.org/x/sys/unix/unveil_openbsd.go +51 -0
  294. package/govendor/golang.org/x/sys/unix/vgetrandom_linux.go +13 -0
  295. package/govendor/golang.org/x/sys/unix/vgetrandom_unsupported.go +11 -0
  296. package/govendor/golang.org/x/sys/unix/xattr_bsd.go +280 -0
  297. package/govendor/golang.org/x/sys/unix/zerrors_aix_ppc.go +1384 -0
  298. package/govendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go +1385 -0
  299. package/govendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +1922 -0
  300. package/govendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +1922 -0
  301. package/govendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go +1737 -0
  302. package/govendor/golang.org/x/sys/unix/zerrors_freebsd_386.go +2042 -0
  303. package/govendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go +2039 -0
  304. package/govendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +2033 -0
  305. package/govendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go +2033 -0
  306. package/govendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go +2147 -0
  307. package/govendor/golang.org/x/sys/unix/zerrors_linux.go +4209 -0
  308. package/govendor/golang.org/x/sys/unix/zerrors_linux_386.go +882 -0
  309. package/govendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +882 -0
  310. package/govendor/golang.org/x/sys/unix/zerrors_linux_arm.go +887 -0
  311. package/govendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +884 -0
  312. package/govendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +874 -0
  313. package/govendor/golang.org/x/sys/unix/zerrors_linux_mips.go +888 -0
  314. package/govendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +888 -0
  315. package/govendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +888 -0
  316. package/govendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +888 -0
  317. package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +940 -0
  318. package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +944 -0
  319. package/govendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +944 -0
  320. package/govendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +884 -0
  321. package/govendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +943 -0
  322. package/govendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +992 -0
  323. package/govendor/golang.org/x/sys/unix/zerrors_netbsd_386.go +1779 -0
  324. package/govendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go +1769 -0
  325. package/govendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go +1758 -0
  326. package/govendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go +1769 -0
  327. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_386.go +1905 -0
  328. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go +1905 -0
  329. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go +1905 -0
  330. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go +1905 -0
  331. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go +1905 -0
  332. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go +1904 -0
  333. package/govendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go +1903 -0
  334. package/govendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go +1556 -0
  335. package/govendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +990 -0
  336. package/govendor/golang.org/x/sys/unix/zptrace_armnn_linux.go +40 -0
  337. package/govendor/golang.org/x/sys/unix/zptrace_linux_arm64.go +17 -0
  338. package/govendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go +49 -0
  339. package/govendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go +49 -0
  340. package/govendor/golang.org/x/sys/unix/zptrace_x86_linux.go +79 -0
  341. package/govendor/golang.org/x/sys/unix/zsymaddr_zos_s390x.s +364 -0
  342. package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go +1461 -0
  343. package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go +1420 -0
  344. package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go +1188 -0
  345. package/govendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go +1069 -0
  346. package/govendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +2728 -0
  347. package/govendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +799 -0
  348. package/govendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +2728 -0
  349. package/govendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +799 -0
  350. package/govendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go +1666 -0
  351. package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go +1886 -0
  352. package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go +1886 -0
  353. package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go +1886 -0
  354. package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go +1886 -0
  355. package/govendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go +1886 -0
  356. package/govendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go +101 -0
  357. package/govendor/golang.org/x/sys/unix/zsyscall_linux.go +2267 -0
  358. package/govendor/golang.org/x/sys/unix/zsyscall_linux_386.go +469 -0
  359. package/govendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go +636 -0
  360. package/govendor/golang.org/x/sys/unix/zsyscall_linux_arm.go +584 -0
  361. package/govendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go +535 -0
  362. package/govendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go +469 -0
  363. package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips.go +636 -0
  364. package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go +630 -0
  365. package/govendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go +619 -0
  366. package/govendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go +636 -0
  367. package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go +641 -0
  368. package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go +687 -0
  369. package/govendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go +687 -0
  370. package/govendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go +531 -0
  371. package/govendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go +478 -0
  372. package/govendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go +631 -0
  373. package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go +1848 -0
  374. package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go +1848 -0
  375. package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go +1848 -0
  376. package/govendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go +1848 -0
  377. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +2407 -0
  378. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +719 -0
  379. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +2407 -0
  380. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +719 -0
  381. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +2407 -0
  382. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +719 -0
  383. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +2407 -0
  384. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +719 -0
  385. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +2407 -0
  386. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +719 -0
  387. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +2407 -0
  388. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +862 -0
  389. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +2407 -0
  390. package/govendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +719 -0
  391. package/govendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +2217 -0
  392. package/govendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go +3458 -0
  393. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go +280 -0
  394. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go +280 -0
  395. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go +280 -0
  396. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go +280 -0
  397. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go +280 -0
  398. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go +280 -0
  399. package/govendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go +281 -0
  400. package/govendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go +439 -0
  401. package/govendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go +437 -0
  402. package/govendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go +316 -0
  403. package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go +393 -0
  404. package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go +393 -0
  405. package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go +393 -0
  406. package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go +393 -0
  407. package/govendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go +393 -0
  408. package/govendor/golang.org/x/sys/unix/zsysnum_linux_386.go +470 -0
  409. package/govendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +394 -0
  410. package/govendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +434 -0
  411. package/govendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +337 -0
  412. package/govendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +334 -0
  413. package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +454 -0
  414. package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +384 -0
  415. package/govendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +384 -0
  416. package/govendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +454 -0
  417. package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +461 -0
  418. package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +433 -0
  419. package/govendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +433 -0
  420. package/govendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +338 -0
  421. package/govendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +399 -0
  422. package/govendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +413 -0
  423. package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go +274 -0
  424. package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go +274 -0
  425. package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go +274 -0
  426. package/govendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go +274 -0
  427. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go +219 -0
  428. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go +219 -0
  429. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go +219 -0
  430. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go +218 -0
  431. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go +221 -0
  432. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go +217 -0
  433. package/govendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go +218 -0
  434. package/govendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go +2852 -0
  435. package/govendor/golang.org/x/sys/unix/ztypes_aix_ppc.go +353 -0
  436. package/govendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go +357 -0
  437. package/govendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +878 -0
  438. package/govendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +878 -0
  439. package/govendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go +473 -0
  440. package/govendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +651 -0
  441. package/govendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +656 -0
  442. package/govendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +642 -0
  443. package/govendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +636 -0
  444. package/govendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +638 -0
  445. package/govendor/golang.org/x/sys/unix/ztypes_linux.go +6547 -0
  446. package/govendor/golang.org/x/sys/unix/ztypes_linux_386.go +747 -0
  447. package/govendor/golang.org/x/sys/unix/ztypes_linux_amd64.go +761 -0
  448. package/govendor/golang.org/x/sys/unix/ztypes_linux_arm.go +741 -0
  449. package/govendor/golang.org/x/sys/unix/ztypes_linux_arm64.go +740 -0
  450. package/govendor/golang.org/x/sys/unix/ztypes_linux_loong64.go +741 -0
  451. package/govendor/golang.org/x/sys/unix/ztypes_linux_mips.go +746 -0
  452. package/govendor/golang.org/x/sys/unix/ztypes_linux_mips64.go +743 -0
  453. package/govendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go +743 -0
  454. package/govendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go +746 -0
  455. package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc.go +754 -0
  456. package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go +749 -0
  457. package/govendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go +749 -0
  458. package/govendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +828 -0
  459. package/govendor/golang.org/x/sys/unix/ztypes_linux_s390x.go +763 -0
  460. package/govendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go +744 -0
  461. package/govendor/golang.org/x/sys/unix/ztypes_netbsd_386.go +585 -0
  462. package/govendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go +593 -0
  463. package/govendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go +590 -0
  464. package/govendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go +593 -0
  465. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_386.go +568 -0
  466. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go +568 -0
  467. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go +575 -0
  468. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go +568 -0
  469. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go +568 -0
  470. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go +570 -0
  471. package/govendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go +570 -0
  472. package/govendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go +516 -0
  473. package/govendor/golang.org/x/sys/unix/ztypes_zos_s390x.go +552 -0
  474. package/govendor/golang.org/x/sys/windows/aliases.go +13 -0
  475. package/govendor/golang.org/x/sys/windows/dll_windows.go +380 -0
  476. package/govendor/golang.org/x/sys/windows/env_windows.go +57 -0
  477. package/govendor/golang.org/x/sys/windows/eventlog.go +20 -0
  478. package/govendor/golang.org/x/sys/windows/exec_windows.go +248 -0
  479. package/govendor/golang.org/x/sys/windows/memory_windows.go +48 -0
  480. package/govendor/golang.org/x/sys/windows/mkerrors.bash +70 -0
  481. package/govendor/golang.org/x/sys/windows/mkknownfolderids.bash +27 -0
  482. package/govendor/golang.org/x/sys/windows/mksyscall.go +9 -0
  483. package/govendor/golang.org/x/sys/windows/race.go +30 -0
  484. package/govendor/golang.org/x/sys/windows/race0.go +25 -0
  485. package/govendor/golang.org/x/sys/windows/security_windows.go +1537 -0
  486. package/govendor/golang.org/x/sys/windows/service.go +257 -0
  487. package/govendor/golang.org/x/sys/windows/setupapi_windows.go +1425 -0
  488. package/govendor/golang.org/x/sys/windows/str.go +22 -0
  489. package/govendor/golang.org/x/sys/windows/syscall.go +104 -0
  490. package/govendor/golang.org/x/sys/windows/syscall_windows.go +1952 -0
  491. package/govendor/golang.org/x/sys/windows/types_windows.go +4058 -0
  492. package/govendor/golang.org/x/sys/windows/types_windows_386.go +35 -0
  493. package/govendor/golang.org/x/sys/windows/types_windows_amd64.go +34 -0
  494. package/govendor/golang.org/x/sys/windows/types_windows_arm.go +35 -0
  495. package/govendor/golang.org/x/sys/windows/types_windows_arm64.go +34 -0
  496. package/govendor/golang.org/x/sys/windows/zerrors_windows.go +9468 -0
  497. package/govendor/golang.org/x/sys/windows/zknownfolderids_windows.go +149 -0
  498. package/govendor/golang.org/x/sys/windows/zsyscall_windows.go +4828 -0
  499. package/govendor/manifest.txt +7 -0
  500. package/package.json +7 -1
  501. package/tsparser/check.go +157 -0
  502. package/tsparser/main.go +30 -1
  503. package/types/typenative-go.d.ts +15159 -5
  504. package/types/typenative-npm.d.ts +2640 -34
  505. package/types/typenative.d.ts +651 -8
package/bin/transpiler.js CHANGED
@@ -1,3 +1,46 @@
1
+ import { createNodeRegistry } from './node/registry.js';
2
+ import { registerPath } from './node/path.js';
3
+ import { registerFs } from './node/fs.js';
4
+ import { registerUrl } from './node/url.js';
5
+ import { registerOs } from './node/os.js';
6
+ import { registerChildProcess } from './node/child_process.js';
7
+ import { registerReadline } from './node/readline.js';
8
+ import { registerCore, registerConstants } from './node/core.js';
9
+ import { registerBuffer } from './node/buffer.js';
10
+ import { registerProcess } from './node/process.js';
11
+ import { registerUtil } from './node/util.js';
12
+ import { registerUtilExtra } from './node/util_extra.js';
13
+ import { registerConsole } from './node/console.js';
14
+ import { registerAssert } from './node/assert.js';
15
+ import { registerPunycode } from './node/punycode.js';
16
+ import { registerQuerystring } from './node/querystring.js';
17
+ import { registerStringDecoder } from './node/string_decoder.js';
18
+ import { registerCrypto } from './node/crypto.js';
19
+ import { registerCryptoKeys } from './node/crypto_keys.js';
20
+ import { registerZlib } from './node/zlib.js';
21
+ import { registerTimers } from './node/timers.js';
22
+ import { registerEvents } from './node/events.js';
23
+ import { registerStream } from './node/stream.js';
24
+ import { registerDiagnosticsChannel } from './node/diagnostics_channel.js';
25
+ import { registerNet } from './node/net.js';
26
+ import { registerDns } from './node/dns.js';
27
+ import { registerHttp } from './node/http.js';
28
+ import { registerTls } from './node/tls.js';
29
+ import { registerDgram } from './node/dgram.js';
30
+ import { registerSqlite } from './node/sqlite.js';
31
+ import { registerFetch } from './node/fetch.js';
32
+ import { registerStreamWeb } from './node/stream_web.js';
33
+ import { registerAsyncHooks } from './node/async_hooks.js';
34
+ import { registerTest } from './node/test.js';
35
+ import { registerWorkerThreads } from './node/worker_threads.js';
36
+ import { registerCluster } from './node/cluster.js';
37
+ import { registerHttp2 } from './node/http2.js';
38
+ import { registerSystemExtra } from './node/system_extra.js';
39
+ import { goStdlib } from './go/stdlib.generated.js';
40
+ import { goPackage, goTypeInfo as goMetaTypeInfo, hasGoPackage, isGoNumeric as isGoNumericType } from './go/metadata.js';
41
+ import { bindConvertServices, initConvertPaths, isGoBasicKind, isGoNumericKind, toGo as toGoType, fromGo as fromGoType } from './go/convert.js';
42
+ import { goPathAliases } from './go/overrides.js';
43
+ import { bindBridgeServices, resetGoBridge, initKnownPaths, goLocalType, ensureGoTypePackages, registerGoBuiltin, registerGoImport, goQualifier, goPathOf, goMemberOf, visitGoCall, visitGoMethod, visitGoNew, inferGoType, goFieldType, isGoRefCall, goTupleTypes, goCallbackResultType, ensureGoQualifier } from './go/bridge.js';
1
44
  // Shim over the removed `typescript` import: predicates and kind names operate
2
45
  // on the normalized JSON AST ({kind: 'Xxx', ...} plain objects) produced by
3
46
  // src/parse-node.ts (Node) or the tsparser Go tool (self-hosted).
@@ -242,10 +285,16 @@ const SyntaxKind = {
242
285
  // Local replacement for nanoid's customAlphabet — must stay transpilable by
243
286
  // TypeNative itself (no dependencies), only using mapped String methods.
244
287
  const GO_SAFE_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
288
+ // Ids come from a Park–Miller generator reset for every transpilation, so the
289
+ // same source always gives the same Go (reproducible builds; the self-hosted
290
+ // compiler's output can be compared with the Node CLI's). The products stay
291
+ // below 2^46, exact in a float64 in both runtimes.
292
+ let goSafeIdSeed = 1;
245
293
  function goSafeId() {
246
294
  let id = '';
247
295
  for (let i = 0; i < 8; i++) {
248
- id += GO_SAFE_ALPHABET.charAt(Math.floor(Math.random() * GO_SAFE_ALPHABET.length));
296
+ goSafeIdSeed = (goSafeIdSeed * 16807) % 2147483647;
297
+ id += GO_SAFE_ALPHABET.charAt(goSafeIdSeed % GO_SAFE_ALPHABET.length);
249
298
  }
250
299
  return id;
251
300
  }
@@ -260,6 +309,8 @@ const mainPackageVariables = [];
260
309
  // True while emitting a separate Go file, whose top-level statements sit at package scope
261
310
  let emittingModuleFile = false;
262
311
  const importedPackages = new Set();
312
+ // Side-effect `import 'go:time/tzdata'` packages, imported as `_`
313
+ const goSideEffectImports = new Set();
263
314
  let outsideNodes = [];
264
315
  const classNames = new Set();
265
316
  let promiseResolveName = '';
@@ -305,7 +356,7 @@ const referenceArrays = new Set();
305
356
  function setVarGoType(name, goType) {
306
357
  if (!name)
307
358
  return;
308
- if (goType && goType.startsWith('[]') && goType !== '[]interface{}') {
359
+ if (goType && goType.startsWith('[]') && goType !== '[]interface{}' && goType !== '[]byte') {
309
360
  variableGoTypes.set(name, `*${goType}`);
310
361
  referenceArrays.add(name);
311
362
  return;
@@ -353,6 +404,13 @@ const classStaticProps = new Set();
353
404
  let fileResolver = null;
354
405
  // Directory of the file currently being processed (null = main entry file)
355
406
  let currentFileDir = null;
407
+ // Name of the file currently being parsed, as diagnostics and line directives show it
408
+ let currentFileName = '';
409
+ // Names of the parsed source files, indexed by the fileIndex stamped on each
410
+ // SourceFile node (line markers and diagnostics refer to them)
411
+ const sourceFileNames = [];
412
+ // Errors found while transpiling (unsupported syntax, unresolved imports)
413
+ const diagnostics = [];
356
414
  // Tracks already-included files by a stable key to prevent duplicates/cycles
357
415
  const includedLocalImports = new Set();
358
416
  // Collects Go source files generated from local TS imports (filename → content)
@@ -409,6 +467,8 @@ function parseSource(code) {
409
467
  lowerWeakCollections(sourceFile);
410
468
  lowerGlobalThis(sourceFile);
411
469
  linkParents(sourceFile, undefined);
470
+ sourceFile.fileIndex = sourceFileNames.length;
471
+ sourceFileNames.push(currentFileName);
412
472
  for (const stmt of sourceFile.statements ?? []) {
413
473
  if (!stmt.name || !isIdentifier(stmt.name))
414
474
  continue;
@@ -427,6 +487,115 @@ function parseSource(code) {
427
487
  }
428
488
  return sourceFile;
429
489
  }
490
+ // Index (into sourceFileNames) of the file a node was parsed from, or -1
491
+ function sourceFileIndexOf(node) {
492
+ let current = node;
493
+ while (current && current.kind !== 'SourceFile')
494
+ current = current.parent;
495
+ return current && typeof current.fileIndex === 'number' ? current.fileIndex : -1;
496
+ }
497
+ // Line markers: generated statements and declarations start with
498
+ // /*tn@<file>:<line>:<column>*/, which applyLineDirectives turns into Go
499
+ // /*line file.ts:line:column*/ directives once the code is complete, so Go
500
+ // compile errors and stack traces point at the TypeScript source. The marker
501
+ // carries no file name until then: names like strings.ts would otherwise look
502
+ // like uses of a Go package to the import pruning.
503
+ // A line holding only this sentinel becomes //line <go file>:<next line>,
504
+ // which returns the following generated code (helpers) to its real Go position
505
+ const LINE_RESET = '/*tn@' + 'reset*/';
506
+ function lineMarker(node) {
507
+ if (!node?.line)
508
+ return '';
509
+ const fileIndex = sourceFileIndexOf(node);
510
+ if (fileIndex < 0)
511
+ return '';
512
+ return `/*tn@${fileIndex}:${node.line}:${node.column}*/`;
513
+ }
514
+ // Prefixes generated code with its node's line marker (after any leading
515
+ // whitespace, so the position applies to the code's first token)
516
+ function withLineMarker(node, code) {
517
+ if (!code.trim())
518
+ return code;
519
+ const marker = lineMarker(node);
520
+ if (!marker)
521
+ return code;
522
+ const indent = code.length - code.trimStart().length;
523
+ return code.slice(0, indent) + marker + code.slice(indent);
524
+ }
525
+ // Replaces line markers with Go line directives and resets with //line
526
+ // directives back to the generated file (goFile, as Go should report it).
527
+ // One statement often spans several Go lines (assert → if + panic): every
528
+ // unmarked line after a marker repeats its directive, so those lines report
529
+ // the statement's line instead of counting on from it.
530
+ function applyLineDirectives(code, goFile) {
531
+ const lines = code.split('\n');
532
+ let pin = '';
533
+ // Inside a multi-line raw string or block comment, where nothing may be inserted
534
+ let state = '';
535
+ for (let index = 0; index < lines.length; index++) {
536
+ const text = lines[index];
537
+ if (text === LINE_RESET) {
538
+ lines[index] = `//line ${goFile}:${index + 2}`;
539
+ pin = '';
540
+ continue;
541
+ }
542
+ const startState = state;
543
+ if (text.includes('`') || text.includes('/*') || text.includes('*/'))
544
+ state = goLineEndState(text, state);
545
+ if (text.includes('/*tn@')) {
546
+ // (regex literals inline: a /g regex held in a variable compiles to a
547
+ // replace-first in the self-hosted compiler). Later lines repeat the
548
+ // line's last directive; text like "/*tn@" in a string matches nothing
549
+ lines[index] = text.replace(/\/\*tn@(\d+):(\d+):(\d+)\*\//g, (_match, file, line, column) => {
550
+ pin = `/*line ${sourceFileNames[Number(file)]}:${line}:${column}*/`;
551
+ return pin;
552
+ });
553
+ }
554
+ else if (pin && startState === '' && text.trim()) {
555
+ const indent = text.length - text.trimStart().length;
556
+ lines[index] = text.slice(0, indent) + pin + text.slice(indent);
557
+ }
558
+ }
559
+ return lines.join('\n');
560
+ }
561
+ // The multi-line context after a Go line that starts in state: '' (code),
562
+ // '`' (inside a raw string) or '*' (inside a block comment)
563
+ function goLineEndState(line, state) {
564
+ let rest = line;
565
+ if (state !== '') {
566
+ const close = state === '`' ? '`' : '*/';
567
+ const end = rest.indexOf(close);
568
+ if (end < 0)
569
+ return state;
570
+ rest = rest.slice(end + close.length);
571
+ }
572
+ // Go literals and comments that end on this line, matched leftmost-first
573
+ // like Go's scanner (a quote inside a raw string is not a string): what's
574
+ // left after them is an unclosed raw string or comment
575
+ rest = rest.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`[^`]*`|\/\*.*?\*\/|\/\/.*/g, ' ');
576
+ const rawString = rest.indexOf('`');
577
+ const comment = rest.indexOf('/*');
578
+ if (rawString < 0 && comment < 0)
579
+ return '';
580
+ if (comment < 0 || (rawString >= 0 && rawString < comment))
581
+ return '`';
582
+ return '*';
583
+ }
584
+ // Records a transpile error at the node (or its nearest positioned ancestor)
585
+ function reportError(node, message) {
586
+ let positioned = node;
587
+ while (positioned && !positioned.line)
588
+ positioned = positioned.parent;
589
+ const fileIndex = sourceFileIndexOf(node);
590
+ const line = positioned ? positioned.line : 0;
591
+ const column = positioned ? positioned.column : 0;
592
+ diagnostics.push({
593
+ file: fileIndex >= 0 ? sourceFileNames[fileIndex] : currentFileName,
594
+ line: line,
595
+ column: column,
596
+ message: message
597
+ });
598
+ }
430
599
  // `this: T` parameters only type `this`; they are not real parameters
431
600
  function stripThisParameters(node) {
432
601
  if (Array.isArray(node.parameters)) {
@@ -789,23 +958,40 @@ const usedHelpers = new Set();
789
958
  // per-file import lists can exclude them (helpers are emitted once, in the
790
959
  // main file, which carries these imports instead).
791
960
  const helperProvidedPackages = new Set();
961
+ // GOOS of the current transpilation (see platformHelper)
962
+ let targetGoos = 'linux';
792
963
  // Whether the file being parsed is .tsx
793
964
  let currentFileIsTsx = false;
794
965
  export function transpileToNative(code, options) {
795
966
  fileResolver = options?.readFile ?? null;
796
967
  parseFunction = options?.parse ?? defaultParseFunction;
797
968
  currentFileIsTsx = !!options?.tsx;
969
+ currentFileName = options?.fileName ?? (currentFileIsTsx ? 'main.tsx' : 'main.ts');
970
+ const goFile = options?.goFile ?? 'code.go';
971
+ targetGoos = options?.goos ?? 'linux';
972
+ sourceFileNames.length = 0;
973
+ diagnostics.length = 0;
974
+ goSafeIdSeed = 1;
975
+ consoleGroupsUsed = code.includes('console.group');
798
976
  declaredFunctions.clear();
799
977
  declaredInterfaces.clear();
800
978
  declaredTypeAliases.clear();
801
979
  variableTypeNodes.clear();
980
+ goRefVariables.clear();
802
981
  narrowedVariables.clear();
803
- nodeCallResultTypes.clear();
982
+ bindNodeRegistryServices();
983
+ nodeLocalNames.clear();
984
+ goPackageLocalNames.clear();
985
+ resetGoBridge();
986
+ initKnownPaths();
987
+ initConvertPaths();
988
+ nodeParentClassKeys.clear();
804
989
  asyncFunctionNames.clear();
805
990
  generatorFunctionNames.clear();
806
991
  currentFileDir = null;
807
992
  const sourceFile = parseSource(code);
808
993
  importedPackages.clear();
994
+ goSideEffectImports.clear();
809
995
  outsideNodes = [];
810
996
  classNames.clear();
811
997
  promiseResolveName = '';
@@ -828,6 +1014,7 @@ export function transpileToNative(code, options) {
828
1014
  classGetterMethods.clear();
829
1015
  classSetterMethods.clear();
830
1016
  classIteratorMethods.clear();
1017
+ seedNodeClassTables();
831
1018
  declaredClasses.clear();
832
1019
  classParentNames.clear();
833
1020
  referenceArrays.clear();
@@ -835,29 +1022,58 @@ export function transpileToNative(code, options) {
835
1022
  helperProvidedPackages.clear();
836
1023
  mainPackageVariables.length = 0;
837
1024
  const transpiledCode = visit(sourceFile, { addFunctionOutside: true });
838
- const transpiledCodeOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
1025
+ const transpiledCodeOutside = outsideNodes.map((n) => withLineMarker(n, visit(n, { isOutside: true }))).join('\n');
839
1026
  // Keep the process alive until detached async work and timers settle
840
1027
  const drains = [];
841
- if (usedHelpers.has('asyncWait'))
842
- drains.push('__tnWaitGroup.Wait()');
843
- if (usedHelpers.has('timerWait'))
844
- drains.unshift('TnRunMicrotasks()');
845
- if (usedHelpers.has('timerWait'))
846
- drains.push('__tnTimers.Wait()');
847
- const drainCode = drains.length > 0 ? `\n\t${drains.join('\n\t')}` : '';
1028
+ // The event loop runs until no timers, async work or open handles remain
1029
+ if (usedHelpers.has('eventLoop'))
1030
+ drains.push('TnRunLoop()');
1031
+ // process.exitCode
1032
+ if (usedHelpers.has('nodeProcess'))
1033
+ drains.push('TnProcessExit()');
1034
+ const drainCode = drains.length > 0 ? `\n${LINE_RESET}\n\t${drains.join('\n\t')}` : '';
1035
+ // Go bridge types are qualified by path internally (net/http.Cookie)
1036
+ const mainCode = localizeGoPaths(transpiledCode);
1037
+ const outsideCode = localizeGoPaths(transpiledCodeOutside);
1038
+ const packageVariables = mainPackageVariables.map((v) => localizeGoPaths(v));
1039
+ // Go bridge packages pulled in for a type that never got emitted
1040
+ // (a method's result type, a param type needing no conversion) would be
1041
+ // unused imports, which Go rejects.
1042
+ const emitted = stripGoStrings(`${mainCode}\n${outsideCode}\n${packageVariables.join('\n')}`);
1043
+ const helpersCode = emitGoHelpers();
1044
+ const mainImports = [...importedPackages].filter((pkg) => {
1045
+ if (!hasGoPackage(pkg))
1046
+ return true;
1047
+ const pkgName = goPathAliases[pkg] ?? pkg.split('/').pop();
1048
+ return referencesQualifier(emitted, pkgName) || referencesQualifier(helpersCode, pkgName);
1049
+ });
1050
+ for (const pkg of goSideEffectImports) {
1051
+ if (!mainImports.includes(pkg))
1052
+ mainImports.push(pkg);
1053
+ }
1054
+ const importLine = (pkg) => goSideEffectImports.has(pkg) && !importedPackages.has(pkg)
1055
+ ? `import _ "${pkg}"`
1056
+ : goImportLine(pkg);
1057
+ // process.on('uncaughtException'): a throw in the top-level code is handed to it too
1058
+ const body = usedHelpers.has('nodeUncaught') ? `func() {\n\tdefer TnMainRecover()\n\t${mainCode.trim()}\n\t}()` : mainCode.trim();
848
1059
  const main = `package main
849
1060
 
850
- ${[...importedPackages].sort().map((pkg) => goImportLine(pkg)).join('\n')}
851
-
852
- ${mainPackageVariables.join('\n')}
1061
+ ${mainImports.sort().map((pkg) => importLine(pkg)).join('\n')}
853
1062
 
1063
+ ${packageVariables.join('\n')}
1064
+ ${LINE_RESET}
854
1065
  func main() {
855
- ${transpiledCode.trim()}${drainCode}
1066
+ ${body}${drainCode}
856
1067
  }
857
1068
 
858
- ${transpiledCodeOutside.trim()}
859
- ${emitGoHelpers()}`.trimEnd();
860
- return { main, files: localImportFiles };
1069
+ ${outsideCode.trim()}
1070
+ ${LINE_RESET}
1071
+ ${helpersCode}`.trimEnd();
1072
+ return {
1073
+ main: applyLineDirectives(main, goFile),
1074
+ files: localImportFiles,
1075
+ diagnostics: diagnostics.slice()
1076
+ };
861
1077
  }
862
1078
  export // Property map of an interface, including inherited properties (flattened —
863
1079
  function collectInterfaceProperties(iface, into, seen = new Set()) {
@@ -896,7 +1112,7 @@ function visit(node, options = {}) {
896
1112
  }
897
1113
  if (isSourceFile(node)) {
898
1114
  return (node.statements ?? [])
899
- .map((n) => visit(n, { addFunctionOutside: true }))
1115
+ .map((n) => withLineMarker(n, visit(n, { addFunctionOutside: true })))
900
1116
  .filter((n) => !!n)
901
1117
  .join(options.inline ? '' : '\n\t');
902
1118
  }
@@ -919,10 +1135,25 @@ function visit(node, options = {}) {
919
1135
  }
920
1136
  if (node.text === 'undefined')
921
1137
  return 'nil';
1138
+ // resolve / reject passed as values (setTimeout(resolve, ms)): closures over the channel
1139
+ const isCallee = isCallExpression(node.parent) && node.parent.expression === node;
1140
+ if (!isCallee && promiseResolveName && node.text === promiseResolveName) {
1141
+ useHelper('promise');
1142
+ return 'TnResolver(ch)';
1143
+ }
1144
+ if (!isCallee && promiseRejectName && node.text === promiseRejectName) {
1145
+ useHelper('promise');
1146
+ return 'TnRejecter(ch)';
1147
+ }
922
1148
  if (node.text === 'NaN' || node.text === 'Infinity') {
923
1149
  importedPackages.add('math');
924
1150
  return node.text === 'NaN' ? 'math.NaN()' : 'math.Inf(1)';
925
1151
  }
1152
+ if (nodeLocalNames.size > 0 || nodeRegistry.globals[node.text]) {
1153
+ const nodeValue = visitNodeValue(node);
1154
+ if (nodeValue !== undefined)
1155
+ return nodeValue;
1156
+ }
926
1157
  const goAlias = importAliases.get(node.text);
927
1158
  if (goAlias)
928
1159
  return goAlias;
@@ -1049,6 +1280,11 @@ function visit(node, options = {}) {
1049
1280
  if (isStringLiteral(node.argumentExpression)) {
1050
1281
  return `${visit(node.expression)}[${visit(node.argumentExpression)}]`;
1051
1282
  }
1283
+ // Buffer / Uint8Array elements read as numbers
1284
+ if (targetType === '[]byte' && !isAssignmentTarget(node)) {
1285
+ useHelper('dynamic');
1286
+ return `float64(TnAt(${visit(node.expression)}, ${visit(node.argumentExpression)}))`;
1287
+ }
1052
1288
  // Reading past the end of an array gives undefined in JS: the zero value here
1053
1289
  if (targetType?.startsWith('[]') && !isAssignmentTarget(node)) {
1054
1290
  useHelper('dynamic');
@@ -1057,9 +1293,28 @@ function visit(node, options = {}) {
1057
1293
  return `${visit(node.expression)}[int(${visit(node.argumentExpression)})]`;
1058
1294
  }
1059
1295
  else if (isPropertyAccessExpression(node)) {
1296
+ if (isMockAccess(node)) {
1297
+ nodeRegistry.helper('nodeTest');
1298
+ return `TnMockOf(${visit(node.expression)})`;
1299
+ }
1300
+ if (isProcessEnv(node.expression) && !isCallee(node) && !isAssignmentTarget(node)) {
1301
+ useHelper('getenv');
1302
+ return `TnGetenv("${node.name.text}")`;
1303
+ }
1304
+ if (!isCallee(node) && !isAssignmentTarget(node)) {
1305
+ const nodeValue = visitNodeValue(node);
1306
+ if (nodeValue !== undefined)
1307
+ return nodeValue;
1308
+ }
1060
1309
  const regexProperty = visitRegexProperty(node);
1061
1310
  if (regexProperty)
1062
1311
  return regexProperty;
1312
+ // Go package member or field on a Go value.
1313
+ if (!isCallee(node) || isCallee(node)) {
1314
+ const goProp = visitGoProperty(node);
1315
+ if (goProp !== undefined)
1316
+ return goProp;
1317
+ }
1063
1318
  // import.meta.url — the file URL of the running program
1064
1319
  if (isNewTargetNode(node.expression) && node.name.text === 'name')
1065
1320
  return '__newTarget';
@@ -1137,20 +1392,34 @@ function visit(node, options = {}) {
1137
1392
  return getAcessString(leftSide, rightSide, objectType);
1138
1393
  }
1139
1394
  else if (isVariableDeclaration(node)) {
1395
+ const nodeAlias = visitNodeAliasDeclaration(node);
1396
+ if (nodeAlias !== undefined)
1397
+ return nodeAlias;
1140
1398
  // Object destructuring: const { x, y } = obj
1141
1399
  if (isObjectBindingPattern(node.name) && node.initializer) {
1142
- const initExpr = visit(node.initializer);
1400
+ let initExpr = visit(node.initializer);
1143
1401
  const initType = inferExpressionType(node.initializer);
1144
1402
  const parts = [];
1403
+ // Evaluate the initializer once (const { a, b } = new X())
1404
+ if ((node.name.elements ?? []).length > 1 && !isIdentifier(node.initializer)) {
1405
+ const source = getTempName('src');
1406
+ parts.push(`${source} := ${initExpr}`);
1407
+ initExpr = source;
1408
+ }
1145
1409
  for (const el of node.name.elements ?? []) {
1146
1410
  const localName = visit(el.name);
1147
1411
  const rawProp = el.propertyName ?? el.name;
1148
1412
  const propName = isIdentifier(rawProp) ? rawProp.text : visit(rawProp);
1413
+ const structName = initType ? initType.replace(/^\*/, '') : '';
1414
+ // Struct literal fields, or the properties of a class (Node classes included)
1149
1415
  const fieldType = initType
1150
- ? getStructFieldGoType(initType.replace(/^\*/, ''), propName)
1416
+ ? (getStructFieldGoType(structName, propName) ?? classPropertyTypes.get(structName)?.get(propName))
1151
1417
  : undefined;
1152
1418
  const defaultVal = el.initializer;
1153
- if (defaultVal) {
1419
+ if (!defaultVal && classGetterMethods.has(`${structName}.${propName}`)) {
1420
+ parts.push(`${localName} := (${initExpr}).Get_${goFieldName(propName)}()`);
1421
+ }
1422
+ else if (defaultVal) {
1154
1423
  // Missing property (nil) takes the default
1155
1424
  const defType = inferExpressionType(defaultVal) ?? fieldType ?? 'interface{}';
1156
1425
  const tmp = getTempName('destructure');
@@ -1172,8 +1441,22 @@ function visit(node, options = {}) {
1172
1441
  : visit(node.initializer);
1173
1442
  const tmpVar = getTempName('arr');
1174
1443
  const elemType = initGoType?.startsWith('[]') ? initGoType.slice(2) : undefined;
1444
+ // Multi-value Go calls: each element asserts back to its own type
1445
+ const tupleTypes = goTupleTypes(unwrapParentheses(node.initializer));
1175
1446
  const parts = [`${tmpVar} := ${initExpr}`];
1176
1447
  node.name.elements.forEach((el, idx) => {
1448
+ const tupleType = tupleTypes?.[idx];
1449
+ if (tupleType && !isOmittedExpression(el) && isIdentifier(el.name) && !el.initializer) {
1450
+ const localName = visit(el.name);
1451
+ if (localName === '_' || localName.startsWith('_')) {
1452
+ parts.push(`_ = ${tmpVar}`);
1453
+ return;
1454
+ }
1455
+ ensureGoTypePackages(tupleType);
1456
+ parts.push(`${localName} := ${tmpVar}[${idx}].(${goLocalType(tupleType)})`);
1457
+ registerLocalVariable(localName, tupleType);
1458
+ return;
1459
+ }
1177
1460
  if (isOmittedExpression(el))
1178
1461
  return;
1179
1462
  const bindEl = el;
@@ -1231,6 +1514,10 @@ function visit(node, options = {}) {
1231
1514
  variableTypeNodes.set(node.name.text, node.type);
1232
1515
  else
1233
1516
  variableTypeNodes.delete(node.name.text);
1517
+ if (node.initializer && isGoRefCall(node.initializer))
1518
+ goRefVariables.add(node.name.text);
1519
+ else
1520
+ goRefVariables.delete(node.name.text);
1234
1521
  if (node.type && !isInferredAny) {
1235
1522
  variableGoTypes.set(node.name.text, getType(node.type));
1236
1523
  }
@@ -1309,7 +1596,8 @@ function visit(node, options = {}) {
1309
1596
  const packageType = type === ':' ? variableGoTypes.get(node.name.text) : type;
1310
1597
  if (isMainTopLevel && packageType && packageType !== 'nil' && isIdentifier(node.name)) {
1311
1598
  const name = getSafeName(node.name.text);
1312
- mainPackageVariables.push(`var ${name} ${packageType}`);
1599
+ ensureGoTypePackages(packageType);
1600
+ mainPackageVariables.push(`var ${name} ${goLocalType(packageType)}`);
1313
1601
  return initializer ? `${name} ${initializer}` : '';
1314
1602
  }
1315
1603
  const declName = isIdentifier(node.name) ? getSafeName(node.name.text) : visit(node.name);
@@ -1318,9 +1606,14 @@ function visit(node, options = {}) {
1318
1606
  if (type === ':' &&
1319
1607
  isIdentifier(node.name) &&
1320
1608
  containsNode(node.initializer, (n) => isIdentifier(n) && n.text === node.name.text && !isFieldNameIdentifier(n))) {
1321
- return `var ${declName} ${inferExpressionType(node.initializer)}\n\t\t${declName} ${initializer}`;
1609
+ const initType = inferExpressionType(node.initializer) ?? '';
1610
+ ensureGoTypePackages(initType);
1611
+ return `var ${declName} ${goLocalType(initType)}\n\t\t${declName} ${initializer}`;
1322
1612
  }
1323
- return `${type === ':' ? '' : 'var '}${declName} ${type}${type === ':' ? '' : ' '}${initializer}`;
1613
+ const emitType = type === ':' ? type : goLocalType(type);
1614
+ if (type !== ':')
1615
+ ensureGoTypePackages(type);
1616
+ return `${type === ':' ? '' : 'var '}${declName} ${emitType}${type === ':' ? '' : ' '}${initializer}`;
1324
1617
  }
1325
1618
  else if (isCallExpression(node)) {
1326
1619
  if (hasQuestionDot(node) ||
@@ -1333,6 +1626,26 @@ function visit(node, options = {}) {
1333
1626
  return visitNullablePrimitiveOptionalCall(node, visit(node.expression.expression), receiverType);
1334
1627
  }
1335
1628
  }
1629
+ const nodeCall = visitNodeCall(node);
1630
+ if (nodeCall !== undefined)
1631
+ return nodeCall;
1632
+ const goCall = visitGoCall(node);
1633
+ if (goCall !== undefined)
1634
+ return goCall;
1635
+ // Method call on a Go-typed receiver (pkg.Type value).
1636
+ if (isPropertyAccessExpression(node.expression)) {
1637
+ const recvType = inferExpressionType(node.expression.expression);
1638
+ if (recvType && recvType.startsWith('chan ')) {
1639
+ const chanCall = visitGoChanCall(node, visit(node.expression.expression));
1640
+ if (chanCall !== undefined)
1641
+ return chanCall;
1642
+ }
1643
+ if (recvType && goMetaTypeInfo(recvType)) {
1644
+ const goMethod = visitGoMethod(node, visit(node.expression.expression), recvType);
1645
+ if (goMethod !== undefined)
1646
+ return goMethod;
1647
+ }
1648
+ }
1336
1649
  const recordCall = visitRecordFunctionCall(node);
1337
1650
  if (recordCall)
1338
1651
  return recordCall;
@@ -1387,7 +1700,12 @@ function visit(node, options = {}) {
1387
1700
  const recv = visit(node.expression.expression);
1388
1701
  const cbs = (node.arguments ?? []).map((a) => visit(a));
1389
1702
  const cbExpr = cbs[0] ?? 'func(interface{}) {}';
1390
- return `__tnWaitGroup.Add(1)\n\t\tgo func() {\n\t\t\tdefer __tnWaitGroup.Done()\n\t\t\t__v := <-(${recv})\n\t\t\t(${cbExpr})(__v)\n\t\t}()`;
1703
+ useHelper('eventLoop');
1704
+ // A callback without parameters (then(() => …)) takes no value
1705
+ const takesValue = !(cb && (isArrowFunction(cb) || isFunctionExpression(cb)) && (cb.parameters ?? []).length === 0);
1706
+ const receive = takesValue ? `__v := <-(${recv})` : `<-(${recv})`;
1707
+ const invoke = takesValue ? `(${cbExpr})(__v)` : `(${cbExpr})()`;
1708
+ return `__tnWaitGroup.Add(1)\n\t\tgo func(__tnCtx TnAsyncContext) {\n\t\t\t${receive}\n\t\t\tTnLoopPost(func() {\n\t\t\t\tdefer __tnWaitGroup.Done()\n\t\t\t\tTnAsyncRun(__tnCtx, func() { ${invoke} })\n\t\t\t})\n\t\t}(TnAsyncSnapshot())`;
1391
1709
  }
1392
1710
  }
1393
1711
  // Array.from(arrayLike, mapFn) — array-likes are { length } structs
@@ -1453,6 +1771,19 @@ function visit(node, options = {}) {
1453
1771
  useHelper('dynamic');
1454
1772
  return `float64(^TnInt32(${visit(node.operand)}))`;
1455
1773
  }
1774
+ // -10n / -x on bigints (*big.Int)
1775
+ if (node.operator === 'MinusToken' && node.operand.kind === 'BigIntLiteral') {
1776
+ useHelper('bigint');
1777
+ return `TnBigInt("-${String(node.operand.text).replace(/n$/, '')}")`;
1778
+ }
1779
+ if (node.operator === 'MinusToken' && inferExpressionType(node.operand) === '*tnbig.Int') {
1780
+ return `new(tnbig.Int).Neg(${visit(node.operand)})`;
1781
+ }
1782
+ // -0: Go constant arithmetic has no negative zero
1783
+ if (node.operator === 'MinusToken' && isNumericLiteral(node.operand) && Number(node.operand.text) === 0) {
1784
+ importedPackages.add('math');
1785
+ return 'math.Copysign(0, -1)';
1786
+ }
1456
1787
  return `${getOperatorText(node.operator)}${visit(node.operand)}`;
1457
1788
  }
1458
1789
  else if (isPostfixUnaryExpression(node)) {
@@ -1480,11 +1811,22 @@ function visit(node, options = {}) {
1480
1811
  if (node.operatorToken.kind === 'QuestionQuestionToken') {
1481
1812
  return visitNullishCoalescingExpression(node);
1482
1813
  }
1814
+ if (node.operatorToken.kind === 'InstanceOfKeyword' &&
1815
+ isIdentifier(node.right) &&
1816
+ (node.right.text === 'Error' || BUILTIN_ERROR_NAMES.has(node.right.text)) &&
1817
+ !classNames.has(node.right.text)) {
1818
+ useHelper('error');
1819
+ return `TnIsErrorOf(${visit(node.left)}, ${toGoStringLiteral(node.right.text)})`;
1820
+ }
1483
1821
  if (node.operatorToken.kind === 'InstanceOfKeyword' && isIdentifier(node.right)) {
1484
1822
  // x instanceof C: the runtime type is C's pointer type
1485
1823
  return `func() bool { _, ok := interface{}(${visit(node.left)}).(*${getSafeName(node.right.text)}); return ok }()`;
1486
1824
  }
1487
1825
  if (node.operatorToken.kind === 'InKeyword') {
1826
+ if (isProcessEnv(node.right)) {
1827
+ useHelper('nodeProcess');
1828
+ return `TnHasEnv(${visit(node.left)})`;
1829
+ }
1488
1830
  if (isDynamicValue(node.right)) {
1489
1831
  useHelper('dynamic');
1490
1832
  return `TnHas(${visit(node.right)}, ${visit(node.left)})`;
@@ -1527,8 +1869,22 @@ function visit(node, options = {}) {
1527
1869
  const bigRight = inferExpressionType(node.right);
1528
1870
  if (bigLeft === '*tnbig.Int' || bigRight === '*tnbig.Int') {
1529
1871
  useHelper('bigint');
1530
- const bl = visit(node.left);
1531
- const br = visit(node.right);
1872
+ let bl = visit(node.left);
1873
+ let br = visit(node.right);
1874
+ // number | bigint values (node:sqlite's changes): === is true only for an
1875
+ // equal bigint; other operators convert the dynamic side
1876
+ const dynamicSide = bigLeft === 'interface{}' ? bl : bigRight === 'interface{}' ? br : '';
1877
+ if (dynamicSide) {
1878
+ const bigSide = dynamicSide === bl ? br : bl;
1879
+ if (op === '==')
1880
+ return `TnBigSame(${dynamicSide}, ${bigSide})`;
1881
+ if (op === '!=')
1882
+ return `!TnBigSame(${dynamicSide}, ${bigSide})`;
1883
+ if (dynamicSide === bl)
1884
+ bl = `TnToBigInt(${bl})`;
1885
+ else
1886
+ br = `TnToBigInt(${br})`;
1887
+ }
1532
1888
  if (op === '==')
1533
1889
  return `${bl}.Cmp(${br}) == 0`;
1534
1890
  if (op === '!=')
@@ -1558,6 +1914,19 @@ function visit(node, options = {}) {
1558
1914
  importedPackages.add('math');
1559
1915
  return `math.Pow(${visit(node.left)}, ${visit(node.right)})`;
1560
1916
  }
1917
+ // Named Go numerics (time.Duration): coerce the float64 side so Go's
1918
+ // operator type-checks (time.Second * 2 → time.Second * time.Duration(2)).
1919
+ if (['<', '>', '<=', '>=', '-', '*', '/', '+', '+=', '-=', '*=', '/='].includes(op)) {
1920
+ const numericCoercion = visitGoNumericArithmetic(node, op);
1921
+ if (numericCoercion)
1922
+ return numericCoercion;
1923
+ }
1924
+ // x.Kind() === reflect.String: the method result reads as a number
1925
+ if (op === '==' || op === '!=') {
1926
+ const numericEquality = visitGoNumericArithmetic(node, op);
1927
+ if (numericEquality)
1928
+ return numericEquality;
1929
+ }
1561
1930
  if (op === '**=') {
1562
1931
  importedPackages.add('math');
1563
1932
  const target = visit(node.left);
@@ -1620,6 +1989,17 @@ function visit(node, options = {}) {
1620
1989
  const other = isNewTargetNode(node.left) ? node.right : node.left;
1621
1990
  return `__newTarget ${op} ${isIdentifier(other) ? toGoStringLiteral(other.text) : '""'}`;
1622
1991
  }
1992
+ const nodeAssignment = visitNodeAssignment(node);
1993
+ if (nodeAssignment !== undefined)
1994
+ return nodeAssignment;
1995
+ const goFieldAssignment = visitGoFieldAssignment(node, op);
1996
+ if (goFieldAssignment !== undefined)
1997
+ return goFieldAssignment;
1998
+ // buf[i] = v on a Buffer / Uint8Array: bytes wrap modulo 256
1999
+ if (op === '=' && isElementAccessExpression(node.left) && inferExpressionType(node.left.expression) === '[]byte') {
2000
+ useHelper('buffer');
2001
+ return `${visit(node.left.expression)}[int(${visit(node.left.argumentExpression)})] = TnByte(${visit(node.right)})`;
2002
+ }
1623
2003
  // obj[key] = v on any
1624
2004
  if (op === '=' && isElementAccessExpression(node.left) && isDynamicValue(node.left.expression)) {
1625
2005
  useHelper('dynamic');
@@ -1699,11 +2079,10 @@ function visit(node, options = {}) {
1699
2079
  return '';
1700
2080
  }
1701
2081
  else if (isAwaitExpression(node)) {
1702
- if (inferExpressionType(node.expression)?.startsWith('chan ')) {
1703
- useHelper('promise');
1704
- return `TnAwait(${visit(node.expression)})`;
1705
- }
1706
- return `<-${visit(node.expression)}`;
2082
+ // Promises are channels: TnAwait keeps the event loop running while it waits
2083
+ // (a bare receive would block it, e.g. in async callbacks)
2084
+ useHelper('promise');
2085
+ return `TnAwait(${visit(node.expression)})`;
1707
2086
  }
1708
2087
  else if (isVariableDeclarationList(node)) {
1709
2088
  return ((node.declarations ?? []).map((n) => visit(n)).join(options.inline ? ';' : ';\n\t') +
@@ -1718,7 +2097,7 @@ function visit(node, options = {}) {
1718
2097
  !emittingModuleFile) {
1719
2098
  useHelper('asyncWait');
1720
2099
  useHelper('promise');
1721
- return `__tnWaitGroup.Add(1)\n\t\tgo func() {\n\t\t\tdefer __tnWaitGroup.Done()\n\t\t\tTnAwait(${getSafeName(exprStmt.expression.text)}())\n\t\t}();\n\t`;
2100
+ return `__tnWaitGroup.Add(1)\n\t\tTnDetach(${visit(exprStmt)});\n\t`;
1722
2101
  }
1723
2102
  return visit(node.expression) + (options.inline ? '' : ';\n\t');
1724
2103
  }
@@ -1748,10 +2127,17 @@ function visit(node, options = {}) {
1748
2127
  iterNode.arguments.length > 0) {
1749
2128
  iterNode = iterNode.arguments[0];
1750
2129
  }
1751
- const iterExpr = isDynamicValue(iterNode)
2130
+ let iterExpr = isDynamicValue(iterNode)
1752
2131
  ? toGoValueOfType(iterNode, '[]interface{}')
1753
2132
  : visit(iterNode, { inline: true });
1754
- const iterType = inferExpressionType(iterNode);
2133
+ let iterType = inferExpressionType(iterNode);
2134
+ // Node class instances iterate through their iterator method (URLSearchParams → entries())
2135
+ const nodeIterator = iterType ? nodeRegistry.iterators[iterType] : undefined;
2136
+ if (iterType && nodeIterator) {
2137
+ const cls = nodeRegistry.classes[nodeRegistry.classByGoType[iterType]];
2138
+ iterExpr = `${iterExpr}.${nodeIterator}()`;
2139
+ iterType = cls?.methods[nodeIterator] ?? iterType;
2140
+ }
1755
2141
  if (iterType && iterType.startsWith('*TnMap[')) {
1756
2142
  const valueType = extractMapValueType(iterType);
1757
2143
  const isSet = valueType === 'struct{}';
@@ -1789,13 +2175,13 @@ function visit(node, options = {}) {
1789
2175
  const thenTerm = node.elseStatement ? '' : ';';
1790
2176
  const thenCode = withNarrowing(getNarrowedNames(node.expression, true), () => isBlock(node.thenStatement)
1791
2177
  ? visit(node.thenStatement, { inline: !!node.elseStatement })
1792
- : `{\n${visit(node.thenStatement)}\n}${thenTerm}`);
2178
+ : `{\n${withLineMarker(node.thenStatement, visit(node.thenStatement))}\n}${thenTerm}`);
1793
2179
  const condition = `if ${toGoCondition(node.expression)} ${thenCode}`;
1794
2180
  if (node.elseStatement) {
1795
2181
  // else-if chains stay chained; other single statements get a block
1796
2182
  const elseCode = withNarrowing(getNarrowedNames(node.expression, false), () => isBlock(node.elseStatement) || isIfStatement(node.elseStatement)
1797
2183
  ? visit(node.elseStatement)
1798
- : `{\n${visit(node.elseStatement)}\n};\n\t`);
2184
+ : `{\n${withLineMarker(node.elseStatement, visit(node.elseStatement))}\n};\n\t`);
1799
2185
  return `${condition} else ${elseCode}`;
1800
2186
  }
1801
2187
  return condition;
@@ -1861,7 +2247,13 @@ function visit(node, options = {}) {
1861
2247
  const enclosingFn = getEnclosingFunction(node);
1862
2248
  const isAsync = enclosingFn?.modifiers?.some((m) => m.kind === 'AsyncKeyword');
1863
2249
  const returnGoType = enclosingFn && !isAsync ? inferFunctionBodyReturnType(enclosingFn) : undefined;
1864
- const value = node.expression ? toGoValueOfType(node.expression, returnGoType) : '';
2250
+ // TS closures passed as Go callbacks return the Go result type
2251
+ const goCallbackResult = enclosingFn ? goCallbackResultType(enclosingFn) : undefined;
2252
+ const value = !node.expression
2253
+ ? ''
2254
+ : goCallbackResult !== undefined
2255
+ ? toGoType(visit(node.expression), inferExpressionType(node.expression) ?? '', goCallbackResult)
2256
+ : toGoValueOfType(node.expression, returnGoType);
1865
2257
  const terminator = options.inline ? '' : ';\n\t';
1866
2258
  if (tryReturn && enclosingFn === tryReturn.fn)
1867
2259
  return tryAwareReturn(value) + terminator;
@@ -1918,7 +2310,11 @@ function visit(node, options = {}) {
1918
2310
  if (!isBlock(node.body)) {
1919
2311
  // void-style calls (console.*, assert) emit statements, not values
1920
2312
  if (isVoidStatementCall(node.body) || !inferredRetType) {
1921
- return `func(${parameterInfo.signature})${returnType} { ${visit(node.body)}; }`;
2313
+ // `(c) => (body += c)`: the parenthesized expression is a statement
2314
+ let statement = node.body;
2315
+ while (statement.kind === 'ParenthesizedExpression')
2316
+ statement = statement.expression;
2317
+ return `func(${parameterInfo.signature})${returnType} { ${visit(statement)}; }`;
1922
2318
  }
1923
2319
  return `func(${parameterInfo.signature})${returnType} { return ${visit(node.body)}; }`;
1924
2320
  }
@@ -2090,7 +2486,7 @@ function visit(node, options = {}) {
2090
2486
  let parentClassName = null;
2091
2487
  for (const clause of node.heritageClauses ?? []) {
2092
2488
  if (clause.token === 'ExtendsKeyword') {
2093
- parentClassName = visit(clause.types[0].expression);
2489
+ parentClassName = nodeParentGoName(clause.types[0].expression) || visit(clause.types[0].expression);
2094
2490
  }
2095
2491
  }
2096
2492
  if (parentClassName && parentClassName !== 'Error') {
@@ -2126,13 +2522,14 @@ function visit(node, options = {}) {
2126
2522
  if ((node.heritageClauses ?? [])) {
2127
2523
  for (const clause of (node.heritageClauses ?? [])) {
2128
2524
  if (clause.token === 'ExtendsKeyword') {
2129
- parentClass = visit(clause.types[0].expression);
2525
+ parentClass = nodeParentGoName(clause.types[0].expression) || visit(clause.types[0].expression);
2130
2526
  }
2131
2527
  }
2132
2528
  }
2133
2529
  const extendsError = parentClass === 'Error';
2134
2530
  if (extendsError)
2135
2531
  useHelper('error');
2532
+ applyNodeHookTypes(parentClass, node);
2136
2533
  if (parentClass && !extendsError)
2137
2534
  classParentNames.set(name, parentClass);
2138
2535
  const isAbstractClass = (node.modifiers ?? []).some((m) => m.kind === 'AbstractKeyword');
@@ -2184,7 +2581,7 @@ function visit(node, options = {}) {
2184
2581
  const parentInit = parentClass && !extendsError && !superCalledInCtor
2185
2582
  ? `self.${goFieldName(parentClass)} = *${parentConstructorCall(parentClass, [])}\n\t\t`
2186
2583
  : '';
2187
- const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${ctorParameterInfo.prefixBlockContent}${parentInit}${fieldInitializers(node)}${parameterAssignments}${bodyStatements}return self;\n\t}`;
2584
+ const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${nodeSelfLink(parentClass)}${ctorParameterInfo.prefixBlockContent}${parentInit}${fieldInitializers(node)}${parameterAssignments}${bodyStatements}${nodeSelfLink(parentClass, node)}return self;\n\t}`;
2188
2585
  result += emitConstructor(name, typeParams, typeParamNames, ctorParameterInfo.signature, ctorBody);
2189
2586
  }
2190
2587
  else if (extendsError) {
@@ -2193,7 +2590,7 @@ function visit(node, options = {}) {
2193
2590
  }
2194
2591
  else {
2195
2592
  const parentInit = parentClass && !extendsError ? `self.${goFieldName(parentClass)} = *${parentConstructorCall(parentClass, [])}\n\t\t` : '';
2196
- const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${parentInit}${fieldInitializers(node)}return self\n\t}`;
2593
+ const ctorBody = `{\n\t\tself := &${name}${typeParamNames}{}\n\t\t${parentInit}${fieldInitializers(node)}${nodeSelfLink(parentClass, node)}return self\n\t}`;
2197
2594
  result += emitConstructor(name, typeParams, typeParamNames, '', ctorBody);
2198
2595
  }
2199
2596
  // Inherited concrete methods are copied onto the subclass: Go embedding
@@ -2252,7 +2649,7 @@ function visit(node, options = {}) {
2252
2649
  }
2253
2650
  const methodParameterInfo = getFunctionParametersInfo(member.parameters ?? []);
2254
2651
  const returnType = member.type ? ` ${getType(member.type)}` : '';
2255
- copiedMethods.push(`func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n`);
2652
+ copiedMethods.push(`${lineMarker(member)}func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n`);
2256
2653
  }
2257
2654
  ancestorIndex--;
2258
2655
  }
@@ -2283,7 +2680,7 @@ function visit(node, options = {}) {
2283
2680
  const genBody = visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent });
2284
2681
  asyncChannelVar = prevChannel;
2285
2682
  asyncFunctionNode = prevFn;
2286
- result += `func (self *${name}${typeParamNames}) ${genName}(${methodParameterInfo.signature}) chan ${yieldType} {\n\t__ch := make(chan ${yieldType})\n\tgo func() {\n\t\tdefer close(__ch)\n\t\t${genBody.trim()}\n\t}()\n\treturn __ch\n}\n\n`;
2683
+ result += `${lineMarker(member)}func (self *${name}${typeParamNames}) ${genName}(${methodParameterInfo.signature}) chan ${yieldType} {\n\t__ch := make(chan ${yieldType})\n\tgo func() {\n\t\tdefer close(__ch)\n\t\t${genBody.trim()}\n\t}()\n\treturn __ch\n}\n\n`;
2287
2684
  }
2288
2685
  continue;
2289
2686
  }
@@ -2301,23 +2698,23 @@ function visit(node, options = {}) {
2301
2698
  }
2302
2699
  else if (isStatic) {
2303
2700
  // Static methods become package-level functions named ClassName_methodName
2304
- result += `func ${name}_${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
2701
+ result += `${lineMarker(member)}func ${name}_${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
2305
2702
  }
2306
2703
  else {
2307
- result += `func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
2704
+ result += `${lineMarker(member)}func (self *${name}${typeParamNames}) ${methodName}(${methodParameterInfo.signature})${returnType} ${visit(member.body, { prefixBlockContent: methodParameterInfo.prefixBlockContent })}\n\n`;
2308
2705
  }
2309
2706
  }
2310
2707
  else if (isGetAccessor(member)) {
2311
2708
  // Getter: get prop() { ... } → func (self *T) Get_prop() RetType { ... }
2312
2709
  const getterName = visit(member.name);
2313
2710
  const returnType = member.type ? ` ${getType(member.type)}` : ' interface{}';
2314
- result += `func (self *${name}${typeParamNames}) Get_${getterName}()${returnType} ${visit(member.body)}\n\n`;
2711
+ result += `${lineMarker(member)}func (self *${name}${typeParamNames}) Get_${getterName}()${returnType} ${visit(member.body)}\n\n`;
2315
2712
  }
2316
2713
  else if (isSetAccessor(member)) {
2317
2714
  // Setter: set prop(val) { ... } → func (self *T) Set_prop(val ValType) { ... }
2318
2715
  const setterName = visit(member.name);
2319
2716
  const parameterInfo = getFunctionParametersInfo(member.parameters ?? []);
2320
- result += `func (self *${name}${typeParamNames}) Set_${setterName}(${parameterInfo.signature}) ${visit(member.body, { prefixBlockContent: parameterInfo.prefixBlockContent })}\n\n`;
2717
+ result += `${lineMarker(member)}func (self *${name}${typeParamNames}) Set_${setterName}(${parameterInfo.signature}) ${visit(member.body, { prefixBlockContent: parameterInfo.prefixBlockContent })}\n\n`;
2321
2718
  }
2322
2719
  }
2323
2720
  result += copiedMethods.join('\n');
@@ -2342,6 +2739,16 @@ function visit(node, options = {}) {
2342
2739
  return result.trim();
2343
2740
  }
2344
2741
  else if (isNewExpression(node)) {
2742
+ const nodeNew = visitNodeNew(node);
2743
+ if (nodeNew !== undefined)
2744
+ return nodeNew;
2745
+ const goTarget = goNewTarget(node);
2746
+ if (goTarget) {
2747
+ importedPackages.add(goTarget.path);
2748
+ const goNew = visitGoNew(node, goTarget.path, goTarget.typeName);
2749
+ if (goNew !== undefined)
2750
+ return goNew;
2751
+ }
2345
2752
  const className = visit(node.expression);
2346
2753
  if (className === 'Promise') {
2347
2754
  return visitNewPromise(node);
@@ -2365,17 +2772,25 @@ function visit(node, options = {}) {
2365
2772
  if (className === 'Set') {
2366
2773
  return visitNewSet(node);
2367
2774
  }
2775
+ // new TypeError(message) & co.: a TnError with that name
2776
+ if (BUILTIN_ERROR_NAMES.has(className) && !classNames.has(className)) {
2777
+ useHelper('error');
2778
+ const msg = (node.arguments ?? [])[0] ? visit(node.arguments[0]) : '""';
2779
+ return withErrorOptions(`TnNodeError(${toGoStringLiteral(className)}, "", ${msg})`, (node.arguments ?? [])[1]);
2780
+ }
2368
2781
  // new Error(message) → a TnError value with a .message
2369
2782
  if (className === 'Error') {
2370
2783
  useHelper('error');
2371
2784
  const msg = (node.arguments ?? [])[0] ? visit(node.arguments[0]) : '""';
2372
- return `TnNewError(${msg})`;
2785
+ return withErrorOptions(`TnNewError(${msg})`, (node.arguments ?? [])[1]);
2373
2786
  }
2374
2787
  // new Date() / new Date(ms) → Go time
2375
2788
  if (className === 'Date') {
2376
2789
  importedPackages.add('time');
2377
- if ((node.arguments ?? []).length === 0)
2378
- return 'time.Now()';
2790
+ if ((node.arguments ?? []).length === 0) {
2791
+ useHelper('clock');
2792
+ return 'TnTimeNow()';
2793
+ }
2379
2794
  return `time.UnixMilli(int64(${visit(node.arguments[0])})).UTC()`;
2380
2795
  }
2381
2796
  // new Array<number>(3) → make([]float64, 3)
@@ -2482,10 +2897,16 @@ function visit(node, options = {}) {
2482
2897
  else if (isExportDeclaration(node) || isExportAssignment(node)) {
2483
2898
  return '';
2484
2899
  }
2900
+ else if (node.kind === 'EmptyStatement' || node.kind === 'DebuggerStatement') {
2901
+ // `;` and `debugger` do nothing in a native binary
2902
+ return '';
2903
+ }
2485
2904
  const syntaxKind = node.kind;
2486
2905
  if (!['VariableStatement', 'EndOfFileToken'].includes(syntaxKind)) {
2487
- const snippet = String(node.text ?? node.kind).substring(0, 60).replace(/\n/g, ' ');
2488
- console.warn(`[TypeNative] Unsupported syntax: ${syntaxKind} — "${snippet}"`);
2906
+ // The Go output would be incomplete: an error, not a guess (its children
2907
+ // are not visited, which would only report follow-on errors)
2908
+ reportError(node, `Unsupported syntax: ${syntaxKind}`);
2909
+ return '';
2489
2910
  }
2490
2911
  for (const child of childNodes(node)) {
2491
2912
  code += visit(child);
@@ -2509,6 +2930,8 @@ function toGoValueOfType(expr, goType) {
2509
2930
  }
2510
2931
  // Array reference slots (*[]T): literals take an address, aliases pass through
2511
2932
  if (goType && goType.startsWith('*[]')) {
2933
+ // Go bridge element types are qualified (*[]io/fs.DirEntry)
2934
+ goType = goLocalType(goType);
2512
2935
  if (isIdentifier(expr) && referenceArrays.has(expr.text)) {
2513
2936
  return getSafeName(expr.text);
2514
2937
  }
@@ -2519,6 +2942,14 @@ function toGoValueOfType(expr, goType) {
2519
2942
  ? `&${code}`
2520
2943
  : `func() ${goType} { __v := ${code}; return &__v }()`;
2521
2944
  }
2945
+ // (x as string[]) on an any value, or (rows as Row[]) on an array of any
2946
+ // values (node:sqlite's all()): convert, then box
2947
+ const castSource = isAsExpression(expr) ? expr.expression : expr;
2948
+ const dynamicElements = goType !== '*[]interface{}' && inferExpressionType(castSource) === '[]interface{}';
2949
+ if (isDynamicValue(castSource) || dynamicElements) {
2950
+ useHelper('dynamic');
2951
+ return `func() ${goType} { __v := TnAs[${goType.slice(1)}](${visit(castSource)}); return &__v }()`;
2952
+ }
2522
2953
  return `func() ${goType} { __v := ${visit(expr)}; return &__v }()`;
2523
2954
  }
2524
2955
  const code = visit(expr);
@@ -2592,10 +3023,13 @@ function isProcessEnv(expr) {
2592
3023
  while (isParenthesizedExpression(expr) || isAsExpression(expr) || isNonNullExpression(expr)) {
2593
3024
  expr = expr.expression;
2594
3025
  }
2595
- return (isPropertyAccessExpression(expr) &&
3026
+ if (isPropertyAccessExpression(expr) &&
2596
3027
  isIdentifier(expr.expression) &&
2597
3028
  expr.expression.text === 'process' &&
2598
- expr.name.text === 'env');
3029
+ expr.name.text === 'env') {
3030
+ return true;
3031
+ }
3032
+ return resolveNodeKey(expr) === 'process.env';
2599
3033
  }
2600
3034
  // Whether an expression is (part of) an optional chain: a?.b, a?.b(), a?.b().c
2601
3035
  function isOptionalChain(expr) {
@@ -2715,10 +3149,10 @@ function visitBlockStatements(statements) {
2715
3149
  for (let index = 0; index < statements.length; index++) {
2716
3150
  const statement = statements[index];
2717
3151
  if (isUsingDeclaration(statement)) {
2718
- parts.push(visitUsingDeclaration(statement, statements.slice(index + 1)));
3152
+ parts.push(withLineMarker(statement, visitUsingDeclaration(statement, statements.slice(index + 1))));
2719
3153
  break;
2720
3154
  }
2721
- parts.push(visit(statement));
3155
+ parts.push(withLineMarker(statement, visit(statement)));
2722
3156
  if (isIfStatement(statement) && !statement.elseStatement && alwaysExits(statement.thenStatement)) {
2723
3157
  for (const name of getNarrowedNames(statement.expression, false)) {
2724
3158
  if (!narrowedVariables.has(name)) {
@@ -2765,18 +3199,41 @@ function visitUsingDeclaration(statement, rest) {
2765
3199
  return head + visitBlockStatements(rest);
2766
3200
  return `func() {\n\t\t${head}${visitBlockStatements(rest)}}()\n\t`;
2767
3201
  }
3202
+ // Binary arithmetic with a bridged named numeric: coerce the float64 side.
3203
+ function visitGoNumericArithmetic(node, op) {
3204
+ const leftType = inferExpressionType(node.left);
3205
+ const rightType = inferExpressionType(node.right);
3206
+ const leftNumeric = leftType && isGoNumericType(leftType) ? leftType : undefined;
3207
+ const rightNumeric = rightType && isGoNumericType(rightType) ? rightType : undefined;
3208
+ if (!leftNumeric && !rightNumeric)
3209
+ return undefined;
3210
+ if (leftNumeric && rightNumeric)
3211
+ return undefined;
3212
+ const numericType = leftNumeric ?? rightNumeric;
3213
+ if (!numericType)
3214
+ return undefined;
3215
+ const left = leftNumeric ? visit(node.left) : `${numericType}(${visit(node.left)})`;
3216
+ const right = rightNumeric ? visit(node.right) : `${numericType}(${visit(node.right)})`;
3217
+ if (['+=', '-=', '*=', '/='].includes(op)) {
3218
+ const target = visit(node.left);
3219
+ const baseOp = op.slice(0, -1);
3220
+ return `${target} = ${left} ${baseOp} ${right}`;
3221
+ }
3222
+ return `${left} ${op} ${right}`;
3223
+ }
2768
3224
  function visitDynamicArithmetic(node, op) {
2769
3225
  const leftDynamic = isDynamicValue(node.left);
2770
3226
  const rightDynamic = isDynamicValue(node.right);
2771
3227
  if (!leftDynamic && !rightDynamic)
2772
3228
  return undefined;
2773
- const isConcat = op === '+' && [node.left, node.right].some((side) => inferExpressionType(side) === 'string');
3229
+ const isConcat = (op === '+' || op === '+=') && [node.left, node.right].some((side) => inferExpressionType(side) === 'string');
2774
3230
  const operand = (side, dynamic) => {
2775
3231
  if (!dynamic)
2776
3232
  return visit(side);
2777
3233
  if (isConcat) {
2778
- importedPackages.add('fmt');
2779
- return `fmt.Sprint(${visit(side)})`;
3234
+ // String(x) as JS renders it (Buffers decode, numbers in JS notation)
3235
+ useHelper('dynamic');
3236
+ return `TnJSString(${visit(side)})`;
2780
3237
  }
2781
3238
  useHelper('dynamic');
2782
3239
  return `TnAs[float64](${visit(side)})`;
@@ -2886,6 +3343,10 @@ function getStructFieldGoType(structType, field) {
2886
3343
  function jsStringOf(code, goType) {
2887
3344
  if (goType === 'string')
2888
3345
  return code;
3346
+ if (goType === '[]byte') {
3347
+ useHelper('buffer');
3348
+ return `TnBufferToString(${code}, "")`;
3349
+ }
2889
3350
  if (goType === 'float64') {
2890
3351
  useHelper('dynamic');
2891
3352
  return `TnNumStr(${code})`;
@@ -2894,10 +3355,27 @@ function jsStringOf(code, goType) {
2894
3355
  importedPackages.add('strconv');
2895
3356
  return `strconv.FormatBool(${code})`;
2896
3357
  }
3358
+ if (goType === 'interface{}') {
3359
+ useHelper('dynamic');
3360
+ useHelper('error');
3361
+ return `TnJSString(${code})`;
3362
+ }
3363
+ // Node classes with a toString() (MIMEType, X509Certificate, …)
3364
+ const nodeKey = goType ? nodeRegistry.classByGoType[goType] : undefined;
3365
+ if (nodeKey && 'toString' in nodeRegistry.classes[nodeKey].methods)
3366
+ return `${code}.toString()`;
2897
3367
  importedPackages.add('fmt');
2898
3368
  return `fmt.Sprintf("%v", ${code})`;
2899
3369
  }
2900
3370
  // JS bitwise operators work on 32-bit integers (ToInt32 / ToUint32)
3371
+ // Go-typed numeric operands (typed constants like x509.KeyUsageCertSign,
3372
+ // int fields) read as float64 so the TnInt32 helpers accept them
3373
+ function asFloatOperand(node, code) {
3374
+ const goType = inferExpressionType(node);
3375
+ if (goType && goType !== 'float64' && isGoNumericKind(goType))
3376
+ return `float64(${code})`;
3377
+ return code;
3378
+ }
2901
3379
  function visitBitwise(node, op) {
2902
3380
  const kind = node.operatorToken.kind;
2903
3381
  const base = {
@@ -2918,8 +3396,8 @@ function visitBitwise(node, op) {
2918
3396
  if (!bitOp)
2919
3397
  return undefined;
2920
3398
  useHelper('dynamic');
2921
- const left = visit(node.left);
2922
- const right = visit(node.right);
3399
+ const left = asFloatOperand(node.left, visit(node.left));
3400
+ const right = asFloatOperand(node.right, visit(node.right));
2923
3401
  let value;
2924
3402
  if (bitOp === '>>>')
2925
3403
  value = `float64(TnUint32(${left}) >> (TnUint32(${right}) & 31))`;
@@ -2938,6 +3416,10 @@ function visitDelete(node) {
2938
3416
  const key = isPropertyAccessExpression(target)
2939
3417
  ? toGoStringLiteral(target.name.text)
2940
3418
  : visit(target.argumentExpression);
3419
+ if (isProcessEnv(object)) {
3420
+ importedPackages.add('os');
3421
+ return `func() bool { os.Unsetenv(${key}); return true }()`;
3422
+ }
2941
3423
  const objectType = inferExpressionType(object);
2942
3424
  if (objectType?.startsWith('*TnMap['))
2943
3425
  return `${visit(object)}.Delete(${key})`;
@@ -2990,7 +3472,60 @@ function isNewTargetNode(node) {
2990
3472
  return node?.kind === 'MetaProperty' && node.name?.text === 'target';
2991
3473
  }
2992
3474
  // The parent part of a subclass constructor: new.target-aware chains pass the target on
3475
+ // Subclass instances of Node classes with overridable methods register
3476
+ // themselves on the embedded struct (`self.TnTransform.tnSelf = self`)
3477
+ function nodeSelfLink(parentClass, classNode) {
3478
+ if (!parentClass || !nodeParentClassKeys.has(parentClass))
3479
+ return '';
3480
+ const key = nodeParentClassKeys.get(parentClass) ?? '';
3481
+ const cls = nodeRegistry.classes[key];
3482
+ if (!cls || !('tnSelf' in cls.props))
3483
+ return '';
3484
+ let code = `self.${parentClass}.tnSelf = self\n\t\t`;
3485
+ // Overridden hook methods (_transform …) are registered on the embedded struct
3486
+ if (classNode && nodeRegistry.hookPrefixes[key]) {
3487
+ for (const member of classNode.members ?? []) {
3488
+ if (!isMethodDeclaration(member) || !member.name || !isIdentifier(member.name))
3489
+ continue;
3490
+ const name = member.name.text;
3491
+ if (name in nodeRegistry.hookParams)
3492
+ code += `self.${parentClass}.setHook(${toGoStringLiteral(name)}, self.${name})\n\t\t`;
3493
+ }
3494
+ }
3495
+ return code;
3496
+ }
3497
+ // Hook methods of subclasses of Node classes take the hooks' parameter types
3498
+ function applyNodeHookTypes(parentClass, classNode) {
3499
+ if (!parentClass || !nodeParentClassKeys.has(parentClass))
3500
+ return;
3501
+ if (!nodeRegistry.hookPrefixes[nodeParentClassKeys.get(parentClass) ?? ''])
3502
+ return;
3503
+ for (const member of classNode.members ?? []) {
3504
+ if (!isMethodDeclaration(member) || !member.name || !isIdentifier(member.name))
3505
+ continue;
3506
+ const name = member.name.text;
3507
+ if (name in nodeRegistry.hookParams)
3508
+ member.tnParamTypes = nodeRegistry.hookParams[name];
3509
+ }
3510
+ }
3511
+ // class X extends EventEmitter: the parent is a Node helper struct
3512
+ const nodeParentClassKeys = new Map();
3513
+ function nodeParentGoName(expr) {
3514
+ const key = resolveNodeKey(expr);
3515
+ const cls = key ? nodeRegistry.classes[key] : undefined;
3516
+ if (!cls || !cls.goType.startsWith('*') || !nodeRegistry.constructors[key])
3517
+ return '';
3518
+ const goName = cls.goType.slice(1);
3519
+ nodeParentClassKeys.set(goName, key);
3520
+ return goName;
3521
+ }
2993
3522
  function parentConstructorCall(parentClass, args) {
3523
+ if (nodeParentClassKeys.has(parentClass)) {
3524
+ const key = nodeParentClassKeys.get(parentClass) ?? '';
3525
+ const call = { args, nodes: [], types: args.map(() => ''), recv: '' };
3526
+ const ctor = nodeRegistry.constructors[key](call);
3527
+ return ctor;
3528
+ }
2994
3529
  if (!usesNewTarget(parentClass))
2995
3530
  return `New${parentClass}(${args.join(', ')})`;
2996
3531
  return `new${parentClass}As(${['__newTarget', ...args].join(', ')})`;
@@ -3139,6 +3674,9 @@ function toFieldGoType(goType) {
3139
3674
  return !goType || goType === 'nil' ? 'interface{}' : goType;
3140
3675
  }
3141
3676
  function isDictionaryLiteral(node) {
3677
+ // Keys that are not Go identifiers ('Content-Type') can't be struct fields
3678
+ if (hasNonIdentifierKeys(node))
3679
+ return true;
3142
3680
  const declaration = node.parent;
3143
3681
  if (!isVariableDeclaration(declaration) || !isIdentifier(declaration.name))
3144
3682
  return false;
@@ -3153,6 +3691,24 @@ function isDictionaryLiteral(node) {
3153
3691
  n.expression.text === name &&
3154
3692
  !isStringLiteral(n.argumentExpression));
3155
3693
  }
3694
+ function hasNonIdentifierKeys(node) {
3695
+ if (!isObjectLiteralExpression(node))
3696
+ return false;
3697
+ let sawKey = false;
3698
+ for (const p of node.properties ?? []) {
3699
+ if (!isPropertyAssignment(p)) {
3700
+ if (p.kind === 'SpreadAssignment' || isMethodDeclaration(p) || isGetAccessor(p) || isSetAccessor(p))
3701
+ return false;
3702
+ continue;
3703
+ }
3704
+ // Go identifiers have no `$` ({ $id: 1 }, node:sqlite's named parameters)
3705
+ if (isStringLiteral(p.name) && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(p.name.text))
3706
+ sawKey = true;
3707
+ if (isIdentifier(p.name) && p.name.text.includes('$'))
3708
+ sawKey = true;
3709
+ }
3710
+ return sawKey;
3711
+ }
3156
3712
  function containsNode(root, predicate) {
3157
3713
  for (const child of childNodes(root)) {
3158
3714
  if (predicate(child) || containsNode(child, predicate))
@@ -3253,6 +3809,9 @@ function getAsExpressionType(node) {
3253
3809
  return inferExpressionType(node.expression) ?? 'interface{}';
3254
3810
  }
3255
3811
  const target = getType(node.type);
3812
+ // (x as File): dynamic values holding Node class instances assert to them
3813
+ if (nodeRegistry.classByGoType[target])
3814
+ return target;
3256
3815
  if (isDynamicValue(node.expression) && (isStructGoType(target) || target.startsWith('*'))) {
3257
3816
  return 'interface{}';
3258
3817
  }
@@ -3554,6 +4113,8 @@ function getMemberTypeNode(typeNode, name) {
3554
4113
  // (TypeScript's "contextual type"), derived syntactically from declarations
3555
4114
  // Declared type nodes of variables/parameters in scope (latest declaration wins)
3556
4115
  const variableTypeNodes = new Map();
4116
+ // Variables initialized by go:builtin ref<T>() (their `.value` dereferences)
4117
+ const goRefVariables = new Set();
3557
4118
  // Declared type (as a type node) of an assignable expression, when known
3558
4119
  function getExpressionTypeNode(expr) {
3559
4120
  if (isIdentifier(expr))
@@ -3890,16 +4451,43 @@ function inferFunctionBodyReturnType(node) {
3890
4451
  if (contextualFn?.type)
3891
4452
  return getType(contextualFn.type);
3892
4453
  if (isArrowFunction(node) && !isBlock(node.body)) {
3893
- return inferExpressionType(node.body);
4454
+ const bodyType = inferExpressionType(node.body);
4455
+ // `() => undefined`: nil is not a Go type
4456
+ return bodyType === 'nil' ? 'interface{}' : bodyType;
3894
4457
  }
3895
4458
  if (node.body && isBlock(node.body)) {
3896
- for (const stmt of node.body.statements) {
4459
+ let returnsNil = false;
4460
+ for (const stmt of node.body.statements ?? []) {
3897
4461
  if (isReturnStatement(stmt) && stmt.expression) {
3898
4462
  const exprType = inferExpressionType(stmt.expression);
3899
- if (exprType)
4463
+ if (exprType === 'nil')
4464
+ returnsNil = true;
4465
+ else if (exprType)
3900
4466
  return exprType;
3901
4467
  }
3902
4468
  }
4469
+ if (returnsNil)
4470
+ return 'interface{}';
4471
+ }
4472
+ return undefined;
4473
+ }
4474
+ function getEnclosingParameterType(expr) {
4475
+ const primitives = ['NumberKeyword', 'StringKeyword', 'BooleanKeyword', 'BigIntKeyword'];
4476
+ for (let node = expr.parent; node; node = node.parent) {
4477
+ if (!isArrowFunction(node) && !isFunctionExpression(node) && node.kind !== 'FunctionDeclaration' && !isMethodDeclaration(node)) {
4478
+ continue;
4479
+ }
4480
+ const param = (node.parameters ?? []).find((p) => isIdentifier(p.name) && p.name.text === expr.text);
4481
+ if (!param)
4482
+ continue;
4483
+ if (!param.type || param.questionToken || param.initializer)
4484
+ return undefined;
4485
+ // ...values: number[] reads as []float64 in the body
4486
+ if (param.dotDotDotToken) {
4487
+ const elem = param.type.kind === 'ArrayType' ? param.type.elementType : undefined;
4488
+ return elem && primitives.includes(elem.kind) ? `[]${getType(elem)}` : undefined;
4489
+ }
4490
+ return primitives.includes(param.type.kind) ? getParameterGoType(param) : undefined;
3903
4491
  }
3904
4492
  return undefined;
3905
4493
  }
@@ -3916,7 +4504,15 @@ function inferArrowFunctionGoType(node) {
3916
4504
  const retType = inferFunctionBodyReturnType(node);
3917
4505
  return `func(${params})${retType ? ` ${retType}` : ''}`;
3918
4506
  }
4507
+ // fn.mock on a function value: the call record of a node:test mock (functions have no fields in Go)
4508
+ function isMockAccess(node) {
4509
+ if (!isPropertyAccessExpression(node) || node.name.text !== 'mock' || !nodeRegistry.modules['test'])
4510
+ return false;
4511
+ return !!inferExpressionType(node.expression)?.startsWith('func(');
4512
+ }
3919
4513
  function inferExpressionType(expr) {
4514
+ if (isMockAccess(expr))
4515
+ return '*TnMockFunctionContext';
3920
4516
  if (isAwaitExpression(expr)) {
3921
4517
  // await unwraps the promise channel
3922
4518
  const inner = inferExpressionType(expr.expression);
@@ -3935,6 +4531,12 @@ function inferExpressionType(expr) {
3935
4531
  return getAsExpressionType(expr);
3936
4532
  if (isTypeAssertionExpression(expr))
3937
4533
  return getType(expr.type);
4534
+ const nodeType = inferNodeExpressionType(expr);
4535
+ if (nodeType !== undefined)
4536
+ return nodeType === '' ? undefined : nodeType;
4537
+ const goType = inferGoExpressionType(expr);
4538
+ if (goType !== undefined)
4539
+ return goType;
3938
4540
  if (isStringLiteral(expr) ||
3939
4541
  isNoSubstitutionTemplateLiteral(expr) ||
3940
4542
  isTemplateExpression(expr)) {
@@ -3954,6 +4556,14 @@ function inferExpressionType(expr) {
3954
4556
  return 'TnJsxNode';
3955
4557
  if (expr.kind === 'TaggedTemplateExpression' && isStringRawTag(expr.tag))
3956
4558
  return 'string';
4559
+ // sql`…` with a Node class method as the tag (node:sqlite's tag store)
4560
+ if (expr.kind === 'TaggedTemplateExpression' && isPropertyAccessExpression(expr.tag)) {
4561
+ const recvType = inferExpressionType(expr.tag.expression);
4562
+ const cls = recvType ? nodeRegistry.classes[nodeRegistry.classByGoType[recvType]] : undefined;
4563
+ const result = cls?.methods[expr.tag.name.text];
4564
+ if (result)
4565
+ return result;
4566
+ }
3957
4567
  if (isRegularExpressionLiteral(expr)) {
3958
4568
  const lastSlash = expr.text.lastIndexOf('/');
3959
4569
  if (needsStatefulRegex(expr.text.substring(1, lastSlash), expr.text.substring(lastSlash + 1))) {
@@ -3971,6 +4581,12 @@ function inferExpressionType(expr) {
3971
4581
  if (isIdentifier(expr) && expr.text === 'undefined')
3972
4582
  return 'nil';
3973
4583
  if (isIdentifier(expr)) {
4584
+ // A primitive-typed parameter of an enclosing function: its annotation,
4585
+ // not a same-named variable registered earlier (inferring the return type
4586
+ // of an arrow before it is visited)
4587
+ const enclosingParamType = getEnclosingParameterType(expr);
4588
+ if (enclosingParamType)
4589
+ return enclosingParamType;
3974
4590
  let varType = variableGoTypes.get(expr.text);
3975
4591
  // Array references are read as the slice they point to
3976
4592
  if (varType?.startsWith('*[]'))
@@ -3986,7 +4602,7 @@ function inferExpressionType(expr) {
3986
4602
  const ctorName = expr.expression.text;
3987
4603
  if (classNames.has(ctorName))
3988
4604
  return `*${ctorName}`;
3989
- if (ctorName === 'Error')
4605
+ if (ctorName === 'Error' || (BUILTIN_ERROR_NAMES.has(ctorName) && !classNames.has(ctorName)))
3990
4606
  return '*TnError';
3991
4607
  if (ctorName === 'Promise' && (expr.typeArguments ?? []).length > 0) {
3992
4608
  return `chan ${getType(expr.typeArguments[0]) || 'struct{}'}`;
@@ -4076,13 +4692,10 @@ function inferExpressionType(expr) {
4076
4692
  if (fn)
4077
4693
  return `chan ${getGeneratorYieldType(fn)}`;
4078
4694
  }
4079
- const nodeResultType = callee ? nodeCallResultTypes.get(callee) : undefined;
4080
4695
  if (callee && BUILTIN_CALL_TYPES[callee])
4081
4696
  return BUILTIN_CALL_TYPES[callee];
4082
4697
  if (callee?.startsWith('Math.'))
4083
4698
  return 'float64';
4084
- if (nodeResultType)
4085
- return nodeResultType;
4086
4699
  }
4087
4700
  // Object.keys(map) / Object.values(map)
4088
4701
  if (isCallExpression(expr) &&
@@ -4181,6 +4794,15 @@ function inferExpressionType(expr) {
4181
4794
  const leftType = inferExpressionType(expr.expression);
4182
4795
  if (expr.name.text === 'length')
4183
4796
  return 'float64';
4797
+ // record.key reads the Record's value type
4798
+ if (leftType?.startsWith('*TnMap[') && expr.name.text !== 'size' && !isCallee(expr)) {
4799
+ const objectType = resolveExpressionType(expr.expression);
4800
+ if (objectType === 'Record' || objectType === 'Map' || objectType === undefined) {
4801
+ const valueType = extractMapValueType(leftType);
4802
+ if (valueType !== 'struct{}')
4803
+ return valueType;
4804
+ }
4805
+ }
4184
4806
  const resolvedLeftType = leftType?.replace(/^\*/, '').replace(/\[.*\]$/, '');
4185
4807
  const resolvedPropertyType = resolvedLeftType
4186
4808
  ? (classPropertyTypes.get(resolvedLeftType)?.get(expr.name.text) ??
@@ -4292,6 +4914,10 @@ function inferExpressionType(expr) {
4292
4914
  return ownerType;
4293
4915
  if (methodName === 'reduceRight' && expr.arguments?.[1])
4294
4916
  return inferExpressionType(expr.arguments[1]);
4917
+ // the accumulator: the initial value's type, else the element type
4918
+ if (methodName === 'reduce') {
4919
+ return (expr.arguments?.[1] ? inferExpressionType(expr.arguments[1]) : undefined) ?? elementType;
4920
+ }
4295
4921
  if (methodName === 'flat' && elementType.startsWith('[]'))
4296
4922
  return elementType;
4297
4923
  if (methodName === 'flat' || methodName === 'flatMap')
@@ -4562,7 +5188,7 @@ const BUILTIN_FUNCTION_TYPES = {
4562
5188
  parseInt: 'float64',
4563
5189
  Number: 'float64',
4564
5190
  String: 'string',
4565
- setTimeout: '*time.Timer',
5191
+ setTimeout: '*TnTimeout',
4566
5192
  setInterval: '*TnInterval'
4567
5193
  };
4568
5194
  const STRING_METHOD_RETURN_TYPES = {
@@ -4919,12 +5545,18 @@ function getMapLookup(expr) {
4919
5545
  return undefined;
4920
5546
  }
4921
5547
  function isNilableGoType(goType) {
4922
- return (goType.startsWith('*') ||
5548
+ if (goType.startsWith('*') ||
4923
5549
  goType.startsWith('[]') ||
4924
5550
  goType.startsWith('map[') ||
4925
5551
  goType.startsWith('func') ||
4926
5552
  goType.startsWith('chan ') ||
4927
- goType === 'interface{}');
5553
+ goType === 'interface{}' ||
5554
+ goType === 'error') {
5555
+ return true;
5556
+ }
5557
+ // Go bridge named types over nil-able kinds (net.IP, io.Reader, http.Header)
5558
+ const kind = goMetaTypeInfo(goType)?.kind;
5559
+ return kind === 'interface' || kind === 'slice' || kind === 'map' || kind === 'func' || kind === 'chan';
4928
5560
  }
4929
5561
  function visitOptionalPropertyAccess(node) {
4930
5562
  const baseExpr = visit(node.expression);
@@ -4944,7 +5576,10 @@ function visitOptionalPropertyAccess(node) {
4944
5576
  'interface{}';
4945
5577
  const nullableType = makeNullableType(propertyType);
4946
5578
  const tmp = getTempName('opt');
4947
- const propertyAccess = `${tmp}.${visit(node.name)}`;
5579
+ // Class getters (also Node classes' accessors such as err.syscall) are calls
5580
+ const propertyAccess = classGetterMethods.has(`${className}.${node.name.text}`)
5581
+ ? `${tmp}.Get_${visit(node.name)}()`
5582
+ : `${tmp}.${visit(node.name)}`;
4948
5583
  if (nullableType.startsWith('*') && nullableType.slice(1) === propertyType) {
4949
5584
  const valueTemp = getTempName('optv');
4950
5585
  return `func() ${nullableType} { ${tmp} := ${baseExpr}; if ${tmp} == nil { var __zero ${nullableType}; return __zero }; ${valueTemp} := ${propertyAccess}; return &${valueTemp} }()`;
@@ -5051,10 +5686,18 @@ function inferArrayCallbackReturnType(callback, elementType, fallbackType) {
5051
5686
  const explicitType = getType(callback.type);
5052
5687
  return explicitType || fallbackType;
5053
5688
  }
5054
- if (isBlock(callback.body)) {
5055
- return inferFunctionBodyReturnType(callback) ?? fallbackType;
5056
- }
5057
- const inferred = inferExpressionType(callback.body);
5689
+ // The element parameter has the element type while the body is inferred
5690
+ const param = (callback.parameters ?? [])[0];
5691
+ const paramName = param && isIdentifier(param.name) && !param.type ? param.name.text : '';
5692
+ const hadParam = paramName ? variableGoTypes.has(paramName) : false;
5693
+ const savedParam = paramName ? variableGoTypes.get(paramName) : undefined;
5694
+ if (paramName)
5695
+ variableGoTypes.set(paramName, elementType);
5696
+ const inferred = isBlock(callback.body) ? inferFunctionBodyReturnType(callback) : inferExpressionType(callback.body);
5697
+ if (paramName && hadParam && savedParam !== undefined)
5698
+ variableGoTypes.set(paramName, savedParam);
5699
+ else if (paramName)
5700
+ variableGoTypes.delete(paramName);
5058
5701
  return inferred ?? fallbackType;
5059
5702
  }
5060
5703
  if (isIdentifier(callback)) {
@@ -5493,7 +6136,10 @@ function getType(typeNode, getArrayType = false) {
5493
6136
  const params = (typeNode.parameters ?? [])
5494
6137
  .map((p) => {
5495
6138
  const paramType = p.type ? getType(p.type) : 'interface{}';
5496
- return p.dotDotDotToken && paramType.startsWith('[]') ? `...${paramType.slice(2)}` : paramType;
6139
+ if (p.dotDotDotToken && paramType.startsWith('[]'))
6140
+ return `...${paramType.slice(2)}`;
6141
+ // optional parameters are nullable (as in declarations and at call sites)
6142
+ return p.questionToken ? makeNullableType(paramType) : paramType;
5497
6143
  })
5498
6144
  .join(', ');
5499
6145
  const ret = typeNode.type ? ` ${getType(typeNode.type)}` : '';
@@ -5508,6 +6154,11 @@ function getType(typeNode, getArrayType = false) {
5508
6154
  useHelper('symbol');
5509
6155
  return '*TnSymbol';
5510
6156
  }
6157
+ if (isTypeReferenceNode(typeNode)) {
6158
+ const nodeClassKey = nodeClassKeyOfTypeName(typeNode.typeName);
6159
+ if (nodeClassKey)
6160
+ return nodeRegistry.classes[nodeClassKey].goType;
6161
+ }
5511
6162
  if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName)) {
5512
6163
  const name = typeNode.typeName.text;
5513
6164
  if (enumNames.has(name)) {
@@ -5529,12 +6180,28 @@ function getType(typeNode, getArrayType = false) {
5529
6180
  if (name === 'RegExp') {
5530
6181
  return '*regexp.Regexp';
5531
6182
  }
5532
- if (name === 'Error') {
6183
+ // go:builtin GoError values are Go errors.
6184
+ if (name === 'GoError') {
6185
+ return 'error';
6186
+ }
6187
+ if (name === 'GoChan' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
6188
+ const inner = getType(typeNode.typeArguments[0]);
6189
+ return `chan ${inner || 'interface{}'}`;
6190
+ }
6191
+ if (name === 'GoRef' && typeNode.typeArguments && typeNode.typeArguments.length > 0) {
6192
+ const inner = getType(typeNode.typeArguments[0]);
6193
+ return `*${inner || 'interface{}'}`;
6194
+ }
6195
+ if (name === 'Error' || (BUILTIN_ERROR_NAMES.has(name) && !classNames.has(name))) {
5533
6196
  return '*TnError';
5534
6197
  }
5535
6198
  if (name === 'RegExpExecArray' || name === 'RegExpMatchArray') {
5536
6199
  return '[]string';
5537
6200
  }
6201
+ // Uint8Array / Buffer / ArrayBuffer annotations are Go []byte.
6202
+ if (name === 'Uint8Array' || name === 'Buffer' || name === 'ArrayBuffer') {
6203
+ return '[]byte';
6204
+ }
5538
6205
  if (name === 'TemplateStringsArray' || name === 'ReadonlyArray') {
5539
6206
  return name === 'TemplateStringsArray' ? '[]string' : getType(typeNode.typeArguments?.[0]);
5540
6207
  }
@@ -5608,6 +6275,11 @@ function getTypeCategory(typeNode) {
5608
6275
  if (nonNullTypes.length === 1)
5609
6276
  return getTypeCategory(nonNullTypes[0]);
5610
6277
  }
6278
+ if (isTypeReferenceNode(typeNode)) {
6279
+ const nodeClassKey = nodeClassKeyOfTypeName(typeNode.typeName);
6280
+ if (nodeClassKey)
6281
+ return nodeRegistry.classes[nodeClassKey].category;
6282
+ }
5611
6283
  if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName)) {
5612
6284
  const name = typeNode.typeName.text;
5613
6285
  if (name === 'Map')
@@ -5649,16 +6321,25 @@ function goTypeCategory(goType) {
5649
6321
  return undefined;
5650
6322
  if (goType.startsWith('*TnMap['))
5651
6323
  return extractMapValueType(goType) === 'struct{}' ? 'Set' : 'Map';
6324
+ const nodeClassKey = nodeRegistry.classByGoType[goType];
6325
+ if (nodeClassKey)
6326
+ return nodeRegistry.classes[nodeClassKey].category;
5652
6327
  if (goType.startsWith('*[]'))
5653
6328
  goType = goType.slice(1);
5654
- if (goType.startsWith('[]'))
6329
+ if (goType.startsWith('[]')) {
6330
+ if (goType === '[]byte')
6331
+ return 'Buffer';
5655
6332
  return 'array';
6333
+ }
5656
6334
  if (goType === 'string')
5657
6335
  return 'string';
5658
6336
  if (goType === '*regexp.Regexp' || goType === '*TnRegex')
5659
6337
  return 'RegExp';
5660
6338
  if (goType === 'time.Time')
5661
6339
  return 'Date';
6340
+ // Bridged Go values: methods pass through as Go methods.
6341
+ if (goMetaTypeInfo(goType))
6342
+ return 'class';
5662
6343
  return undefined;
5663
6344
  }
5664
6345
  function isNilLiteral(node) {
@@ -5677,6 +6358,10 @@ function getAcessString(leftSide, rightSide, objectType) {
5677
6358
  if (rightSide === 'length' && objectType !== 'class') {
5678
6359
  return `float64(len(${leftSide}))`;
5679
6360
  }
6361
+ // Buffers / Uint8Arrays / ArrayBuffers are []byte
6362
+ if (rightSide === 'byteLength' && objectType === 'Buffer') {
6363
+ return `float64(len(${leftSide}))`;
6364
+ }
5680
6365
  if (rightSide === 'size' && (objectType === 'Map' || objectType === 'Set')) {
5681
6366
  return `float64(${leftSide}.Len())`;
5682
6367
  }
@@ -5739,18 +6424,61 @@ const callHandlers = {
5739
6424
  const message = args.length > 1 ? args[1] : '"Assertion failed"';
5740
6425
  return `if !(${args[0]}) {\n\t\tpanic(${message})\n\t}`;
5741
6426
  },
5742
- 'console.log': (_caller, args) => {
5743
- importedPackages.add('fmt');
5744
- return `fmt.Println(${args.join(', ')})`;
6427
+ 'console.log': (_caller, args) => consoleOut('Stdout', args),
6428
+ 'console.info': (_caller, args) => consoleOut('Stdout', args),
6429
+ 'console.debug': (_caller, args) => consoleOut('Stdout', args),
6430
+ 'console.trace': (_caller, args) => consoleOut('Stderr', args, 'Trace:'),
6431
+ 'console.dir': (_caller, args) => {
6432
+ useHelper('nodeUtil');
6433
+ return consoleOut('Stdout', [`TnInspect(${args.join(', ')})`], '', true);
6434
+ },
6435
+ 'console.assert': (_caller, args) => {
6436
+ const condition = args[0] ?? 'false';
6437
+ currentCallArgTypes = currentCallArgTypes.slice(1);
6438
+ const message = consoleOut('Stderr', args.slice(1), args.length > 1 ? 'Assertion failed:' : 'Assertion failed');
6439
+ return `if !(${condition}) {\n\t\t${message}\n\t}`;
6440
+ },
6441
+ 'console.table': (_caller, args) => {
6442
+ useHelper('nodeUtil');
6443
+ return consoleOut('Stdout', [`TnConsoleTableString(${args.join(', ')})`], '', true);
6444
+ },
6445
+ 'console.count': (_caller, args) => {
6446
+ useHelper('console');
6447
+ return `TnConsoleCount(${args.join(', ')})`;
6448
+ },
6449
+ 'console.countReset': (_caller, args) => {
6450
+ useHelper('console');
6451
+ return `TnConsoleCountReset(${args.join(', ')})`;
6452
+ },
6453
+ 'console.group': (_caller, args) => {
6454
+ useHelper('console');
6455
+ return `TnConsoleGroup(${consoleArgs(args).join(', ')})`;
6456
+ },
6457
+ 'console.groupCollapsed': (_caller, args) => {
6458
+ useHelper('console');
6459
+ return `TnConsoleGroup(${consoleArgs(args).join(', ')})`;
6460
+ },
6461
+ 'console.groupEnd': () => {
6462
+ useHelper('console');
6463
+ return 'TnConsoleGroupEnd()';
5745
6464
  },
5746
6465
  'console.time': (_caller, args) => {
5747
- importedPackages.add('time');
5748
- return `${getTimerName(args[0])} := time.Now()`;
6466
+ useHelper('console');
6467
+ return `TnConsoleTime(${args.slice(0, 1).join(', ')})`;
5749
6468
  },
5750
6469
  'console.timeEnd': (_caller, args) => {
5751
- importedPackages.add('time');
6470
+ useHelper('console');
6471
+ return `TnConsoleTimeEnd(${args.slice(0, 1).join(', ')})`;
6472
+ },
6473
+ 'console.timeLog': (_caller, args) => {
6474
+ useHelper('console');
6475
+ const label = args[0] ?? '"default"';
6476
+ currentCallArgTypes = currentCallArgTypes.slice(1);
6477
+ const extra = args.length > 1 ? consoleArgs(args.slice(1)) : [];
5752
6478
  importedPackages.add('fmt');
5753
- return `fmt.Println("Elapsed time:", time.Since(${getTimerName(args[0])}))`;
6479
+ importedPackages.add('strings');
6480
+ const text = extra.length > 0 ? `strings.TrimSuffix(fmt.Sprintln(${extra.join(', ')}), "\\n")` : '""';
6481
+ return `TnConsoleTimeLog(${label}, ${text})`;
5754
6482
  },
5755
6483
  'Math.hypot': (_caller, args) => mathCall('Hypot', args),
5756
6484
  'Math.cbrt': (_caller, args) => mathCall('Cbrt', args),
@@ -5925,18 +6653,11 @@ const callHandlers = {
5925
6653
  },
5926
6654
  'Date.now': () => {
5927
6655
  importedPackages.add('time');
5928
- return 'float64(time.Now().UnixMilli())';
5929
- },
5930
- 'console.error': (_caller, args) => {
5931
- importedPackages.add('fmt');
5932
- importedPackages.add('os');
5933
- return `fmt.Fprintln(os.Stderr, ${args.join(', ')})`;
5934
- },
5935
- 'console.warn': (_caller, args) => {
5936
- importedPackages.add('fmt');
5937
- importedPackages.add('os');
5938
- return `fmt.Fprintln(os.Stderr, ${args.join(', ')})`;
6656
+ useHelper('clock');
6657
+ return 'float64(TnTimeNow().UnixMilli())';
5939
6658
  },
6659
+ 'console.error': (_caller, args) => consoleOut('Stderr', args),
6660
+ 'console.warn': (_caller, args) => consoleOut('Stderr', args),
5940
6661
  'Array.isArray': (_caller, args) => {
5941
6662
  importedPackages.add('reflect');
5942
6663
  return `(reflect.ValueOf(${args[0]}).Kind() == reflect.Slice)`;
@@ -5970,6 +6691,59 @@ const callHandlers = {
5970
6691
  },
5971
6692
  'Boolean': (_caller, args) => `(${truthinessCheck(args[0] ?? 'false', currentCallArgTypes[0] ?? 'interface{}')})`,
5972
6693
  };
6694
+ // Built-in Error subclasses (TnErrors carrying their name)
6695
+ const BUILTIN_ERROR_NAMES = new Set(['TypeError', 'RangeError', 'SyntaxError', 'ReferenceError', 'EvalError', 'URIError']);
6696
+ // Whether the program uses console.group (then every console line is
6697
+ // written through the indentation-aware helper)
6698
+ let consoleGroupsUsed = false;
6699
+ // One console line to stdout / stderr, with an optional leading word
6700
+ function consoleOut(stream, args, prefix = '', formatted = false) {
6701
+ let parts = formatted ? args : consoleArgs(args);
6702
+ if (prefix)
6703
+ parts = [toGoStringLiteral(prefix), ...parts];
6704
+ importedPackages.add('fmt');
6705
+ if (consoleGroupsUsed) {
6706
+ useHelper('console');
6707
+ importedPackages.add('os');
6708
+ importedPackages.add('strings');
6709
+ return `TnConsoleWrite(os.${stream}, strings.TrimSuffix(fmt.Sprintln(${parts.join(', ')}), "\\n"))`;
6710
+ }
6711
+ if (stream === 'Stdout')
6712
+ return `fmt.Println(${parts.join(', ')})`;
6713
+ importedPackages.add('os');
6714
+ return `fmt.Fprintln(os.Stderr, ${parts.join(', ')})`;
6715
+ }
6716
+ // console.* arguments, printed like Node: strings as-is, numbers in JS
6717
+ // notation, everything else through util.inspect (and a format string first
6718
+ // argument through util.format)
6719
+ // The inferred type of call argument i ('' when unknown); a plain lookup keeps
6720
+ // the self-hosted build from dereferencing a missing (nil) entry
6721
+ function callArgType(i) {
6722
+ if (i >= currentCallArgTypes.length)
6723
+ return '';
6724
+ const t = currentCallArgTypes[i];
6725
+ return t ? t : '';
6726
+ }
6727
+ function consoleArgs(args) {
6728
+ const types = args.map((_arg, i) => callArgType(i));
6729
+ const isFormatString = args.length > 1 && types[0] === 'string' && args[0].includes('%');
6730
+ const needsInspect = args.some((_arg, i) => !['string', 'float64', 'bool', '[]byte'].includes(types[i]));
6731
+ if (isFormatString || needsInspect) {
6732
+ useHelper('nodeUtil');
6733
+ return [`TnUtilFormat(${args.join(', ')})`];
6734
+ }
6735
+ return args.map((arg, i) => {
6736
+ if (types[i] === 'float64') {
6737
+ useHelper('dynamic');
6738
+ return `TnNumStr(${arg})`;
6739
+ }
6740
+ if (types[i] === '[]byte') {
6741
+ useHelper('buffer');
6742
+ return `TnBufferInspect(${arg})`;
6743
+ }
6744
+ return arg;
6745
+ });
6746
+ }
5973
6747
  const stringMethodHandlers = {
5974
6748
  split: (obj, args) => {
5975
6749
  if (args[0]?.startsWith('regexp.MustCompile('))
@@ -6003,7 +6777,10 @@ const stringMethodHandlers = {
6003
6777
  },
6004
6778
  indexOf: (obj, args) => {
6005
6779
  importedPackages.add('strings');
6006
- return `float64(strings.Index(${obj}, ${args[0]}))`;
6780
+ if (args.length < 2)
6781
+ return `float64(strings.Index(${obj}, ${args[0]}))`;
6782
+ // indexOf(x, from): search from the clamped position, index into the whole string
6783
+ return `func() float64 { __s := ${obj}; __f := int(${args[1]}); if __f < 0 { __f = 0 }; if __f > len(__s) { __f = len(__s) }; __i := strings.Index(__s[__f:], ${args[0]}); if __i < 0 { return -1 }; return float64(__i + __f) }()`;
6007
6784
  },
6008
6785
  charCodeAt: (obj, args) => `float64(${obj}[int(${args[0]})])`,
6009
6786
  localeCompare: (obj, args) => {
@@ -6258,6 +7035,8 @@ function toGoRegexp(arg) {
6258
7035
  // A JS slice index: negative values count from the end
6259
7036
  // A JS slice index: negative values count from the end; out-of-range values clamp
6260
7037
  function sliceIndex(obj, index) {
7038
+ if (index === undefined || index === '')
7039
+ return '0';
6261
7040
  if (index.startsWith('-'))
6262
7041
  return `max(0, int(float64(len(${obj})) + ${index}))`;
6263
7042
  return `min(int(${index}), len(${obj}))`;
@@ -6296,9 +7075,21 @@ const setMethodHandlers = {
6296
7075
  isSupersetOf: (obj, args) => `${args[0]}.IsSubsetOf(${obj})`,
6297
7076
  isDisjointFrom: (obj, args) => `(${obj}.Intersection(${args[0]}).Len() == 0)`
6298
7077
  };
7078
+ // A table entry only when the table itself has it (not Object.prototype's valueOf …)
7079
+ function ownMethodHandler(table, key) {
7080
+ return Object.hasOwn(table, key) ? table[key] : undefined;
7081
+ }
6299
7082
  function getDynamicCallHandler(caller, objectType) {
6300
7083
  if (promiseResolveName && caller === promiseResolveName) {
6301
- return (_caller, args) => `ch <- ${args[0]}`;
7084
+ // The first resolution wins; later ones are ignored (as in JS)
7085
+ return (_caller, args) => {
7086
+ // resolve() without a value: the channel type's zero value
7087
+ if (args.length === 0 || args[0] === 'nil') {
7088
+ useHelper('promise');
7089
+ return 'TnResolveZero(ch)';
7090
+ }
7091
+ return `select { case ch <- ${args[0]}: default: }`;
7092
+ };
6302
7093
  }
6303
7094
  if (promiseRejectName && caller === promiseRejectName) {
6304
7095
  return (_caller, args) => {
@@ -6314,10 +7105,17 @@ function getDynamicCallHandler(caller, objectType) {
6314
7105
  return (c, args) => {
6315
7106
  const obj = c.substring(0, dotIndex);
6316
7107
  if (args.length > 0) {
7108
+ // bigint.toString(radix)
7109
+ if (currentReceiverGoType === '*tnbig.Int')
7110
+ return `${obj}.Text(int(${args[0]}))`;
6317
7111
  importedPackages.add('strconv');
6318
7112
  return `strconv.FormatInt(int64(${obj}), int(${args[0]}))`;
6319
7113
  }
6320
- return jsStringOf(obj, objectType === 'number' || objectType === 'float64' ? 'float64' : undefined);
7114
+ return jsStringOf(obj, objectType === 'number' || objectType === 'float64'
7115
+ ? 'float64'
7116
+ : currentReceiverGoType === 'interface{}'
7117
+ ? 'interface{}'
7118
+ : undefined);
6321
7119
  };
6322
7120
  }
6323
7121
  if (methodName === 'toPrecision') {
@@ -6338,29 +7136,29 @@ function getDynamicCallHandler(caller, objectType) {
6338
7136
  return null;
6339
7137
  let handler;
6340
7138
  if (objectType === 'string') {
6341
- handler = stringMethodHandlers[methodName];
7139
+ handler = ownMethodHandler(stringMethodHandlers, methodName);
6342
7140
  }
6343
7141
  else if (objectType === 'array') {
6344
- handler = arrayMethodHandlers[methodName];
7142
+ handler = ownMethodHandler(arrayMethodHandlers, methodName);
6345
7143
  }
6346
7144
  else if (objectType === 'RegExp') {
6347
- handler = regexpMethodHandlers[methodName];
7145
+ handler = ownMethodHandler(regexpMethodHandlers, methodName);
6348
7146
  }
6349
7147
  else if (objectType === 'Date') {
6350
- handler = dateMethodHandlers[methodName];
7148
+ handler = ownMethodHandler(dateMethodHandlers, methodName);
6351
7149
  }
6352
7150
  else if (objectType === 'Map') {
6353
- handler = mapMethodHandlers[methodName];
7151
+ handler = ownMethodHandler(mapMethodHandlers, methodName);
6354
7152
  }
6355
7153
  else if (objectType === 'Set') {
6356
- handler = setMethodHandlers[methodName];
7154
+ handler = ownMethodHandler(setMethodHandlers, methodName);
6357
7155
  }
6358
7156
  else {
6359
7157
  // Unknown type: try both maps for backward compatibility
6360
7158
  handler =
6361
- stringMethodHandlers[methodName] ??
6362
- arrayMethodHandlers[methodName] ??
6363
- regexpMethodHandlers[methodName];
7159
+ ownMethodHandler(stringMethodHandlers, methodName) ??
7160
+ ownMethodHandler(arrayMethodHandlers, methodName) ??
7161
+ ownMethodHandler(regexpMethodHandlers, methodName);
6364
7162
  }
6365
7163
  if (handler) {
6366
7164
  return (c, args) => {
@@ -6373,7 +7171,10 @@ function getDynamicCallHandler(caller, objectType) {
6373
7171
  }
6374
7172
  function getCallString(caller, args, typeArgs = '', objectType) {
6375
7173
  // Parentheses from casts like `(mod as any).fn(...)` are stripped for lookup
6376
- const handler = callHandlers[caller] ?? callHandlers[caller.replace(/[()]/g, '')] ??
7174
+ // (own entries only: a plain-object lookup would also find Object.prototype members)
7175
+ const bare = caller.replace(/[()]/g, '');
7176
+ const handler = (Object.hasOwn(callHandlers, caller) ? callHandlers[caller] : undefined) ??
7177
+ (Object.hasOwn(callHandlers, bare) ? callHandlers[bare] : undefined) ??
6377
7178
  getDynamicCallHandler(caller, objectType);
6378
7179
  if (handler) {
6379
7180
  return handler(caller, args, typeArgs);
@@ -6474,11 +7275,15 @@ function visitDynamicMethodCall(node) {
6474
7275
  includes: 'TnIncludes',
6475
7276
  indexOf: 'TnIndexOf',
6476
7277
  slice: 'TnSlice',
6477
- concat: 'TnConcat'
7278
+ concat: 'TnConcat',
7279
+ toString: 'TnJSString'
6478
7280
  };
6479
7281
  const helper = dynamicHelpers[method];
6480
- if (!helper)
7282
+ // (a plain-object lookup also finds Object.prototype members)
7283
+ if (!helper || typeof helper !== 'string')
6481
7284
  return undefined;
7285
+ if (method === 'toString')
7286
+ useHelper('error');
6482
7287
  useHelper('dynamic');
6483
7288
  const args = (node.arguments ?? []).map((a) => visit(a));
6484
7289
  return `${helper}(${[visit(node.expression.expression), ...args].join(', ')})`;
@@ -6901,6 +7706,13 @@ function getTypeArguments(typeArguments) {
6901
7706
  return `[${args.join(', ')}]`;
6902
7707
  }
6903
7708
  function getParameterGoType(param) {
7709
+ // Node callbacks / hooks: `any`-typed parameters take Node's types too
7710
+ const nodeHookTypes = param.parent?.tnParamTypes;
7711
+ if (nodeHookTypes && param.type?.kind === 'AnyKeyword' && !param.dotDotDotToken) {
7712
+ const hookType = nodeHookTypes[(param.parent.parameters ?? []).indexOf(param)];
7713
+ if (hookType)
7714
+ return hookType;
7715
+ }
6904
7716
  // Rest parameter: ...args — use variadic syntax
6905
7717
  if (param.dotDotDotToken) {
6906
7718
  if (param.type) {
@@ -6919,7 +7731,7 @@ function getParameterGoType(param) {
6919
7731
  // Array parameters are references (mutations are visible to the caller) —
6920
7732
  // except unbound type parameters (Go generics), template-tag arrays, and
6921
7733
  // dynamic []interface{} (runtime helper arrays)
6922
- if (explicitType.startsWith('[]') && explicitType !== '[]interface{}' && !param.dotDotDotToken) {
7734
+ if (explicitType.startsWith('[]') && explicitType !== '[]interface{}' && explicitType !== '[]byte' && !param.dotDotDotToken) {
6923
7735
  const isTaggedTemplateArray = isTypeReferenceNode(param.type) &&
6924
7736
  isIdentifier(param.type.typeName) &&
6925
7737
  param.type.typeName.text === 'TemplateStringsArray';
@@ -6943,9 +7755,19 @@ function getParameterGoType(param) {
6943
7755
  const contextualFn = param.parent ? getContextualFunctionType(param.parent) : undefined;
6944
7756
  if (contextualFn) {
6945
7757
  const index = (param.parent.parameters ?? []).indexOf(param);
6946
- const contextualParamType = contextualFn.parameters?.[index]?.type;
6947
- if (contextualParamType)
6948
- return getType(contextualParamType);
7758
+ const contextualParam = contextualFn.parameters?.[index];
7759
+ const contextualParamType = contextualParam?.type;
7760
+ if (contextualParamType) {
7761
+ const contextualGoType = getType(contextualParamType);
7762
+ return contextualParam.questionToken ? makeNullableType(contextualGoType) : contextualGoType;
7763
+ }
7764
+ }
7765
+ // Callbacks of Node APIs: parameter types from the registry
7766
+ const nodeParamTypes = param.parent?.tnParamTypes;
7767
+ if (nodeParamTypes) {
7768
+ const nodeParamType = nodeParamTypes[(param.parent.parameters ?? []).indexOf(param)];
7769
+ if (nodeParamType)
7770
+ return nodeParamType;
6949
7771
  }
6950
7772
  return 'interface{}';
6951
7773
  }
@@ -7153,8 +7975,27 @@ function getFunctionParametersInfo(parameters) {
7153
7975
  prefixBlockContent: prefixBlockContent + destructuring
7154
7976
  };
7155
7977
  }
7978
+ // Go packages the generated code may import: a user identifier with one of
7979
+ // these names would shadow the package, so it is renamed (unless the name is
7980
+ // itself a `go:` package import)
7981
+ const goPackageIdentifiers = new Set([
7982
+ 'aes', 'atomic', 'base64', 'binary', 'bufio', 'bytes', 'cipher', 'crc32', 'errors', 'exec', 'filepath',
7983
+ 'flate', 'fmt', 'fs', 'gzip', 'hash', 'hex', 'hkdf', 'hmac', 'html', 'io', 'iter', 'json', 'math', 'md5',
7984
+ 'net', 'os', 'pbkdf2', 'rand', 'reflect', 'regexp', 'runtime', 'sha1', 'sha256', 'sha3', 'sha512', 'signal',
7985
+ 'slices', 'sort', 'strconv', 'strings', 'subtle', 'sync', 'syscall', 'time', 'unicode', 'unsafe', 'user',
7986
+ 'utf16', 'utf8', 'zlib', 'asn1', 'bits', 'context', 'ecdh', 'ecdsa', 'ed25519', 'elliptic', 'encoding', 'http',
7987
+ 'log', 'mime', 'multipart', 'netip', 'pem', 'pkix', 'rsa', 'textproto', 'tls', 'x509',
7988
+ // Aliased imports (math/big, crypto, net/url, crypto/rand)
7989
+ 'tnbig', 'tncrypto', 'tnurl', 'cryptorand',
7990
+ // Go builtins: a package-level `var copy` would shadow copy() in the helpers
7991
+ 'append', 'cap', 'clear', 'close', 'complex', 'copy', 'imag', 'len', 'make', 'max', 'min', 'panic',
7992
+ 'print', 'println', 'real', 'recover', 'any', 'byte', 'error', 'float64', 'int', 'int64', 'rune',
7993
+ 'uint', 'uint8', 'uint32', 'nil', 'iota'
7994
+ ]);
7995
+ // Local names of `go:` package imports (`import * as strings from 'go:strings'`)
7996
+ const goPackageLocalNames = new Set();
7156
7997
  function getSafeName(name) {
7157
- if (!dangerousNames.has(name)) {
7998
+ if (!dangerousNames.has(name) && !(goPackageIdentifiers.has(name) && !goPackageLocalNames.has(name))) {
7158
7999
  return name;
7159
8000
  }
7160
8001
  if (!renamedFunctions.has(name)) {
@@ -7212,6 +8053,11 @@ function emitChannelFunction(node, isAsync) {
7212
8053
  const body = visit(node.body, { prefixBlockContent: parameterInfo.prefixBlockContent });
7213
8054
  asyncChannelVar = prevChannel;
7214
8055
  asyncFunctionNode = prevFn;
8056
+ if (isAsync) {
8057
+ // The body runs synchronously until its first await, holding the JS baton
8058
+ useHelper('eventLoop');
8059
+ return (`func ${name}${typeParams}(${parameterInfo.signature}) chan ${chanType} {\n\t${channelVar} := make(chan ${chanType}, 1)\n\tTnStartAsync(func() {\n\t\tdefer close(${channelVar})${rejectOnPanic}\n\t\t${body.trim()}\n\t})\n\treturn ${channelVar}\n}`);
8060
+ }
7215
8061
  return (`func ${name}${typeParams}(${parameterInfo.signature}) chan ${chanType} {\n\t${channelVar} := make(chan ${chanType})\n\tgo func() {\n\t\tdefer close(${channelVar})${rejectOnPanic}\n\t\t${body.trim()}\n\t}()\n\treturn ${channelVar}\n}`);
7216
8062
  }
7217
8063
  // The type a generator yields: from its first yield expression's operand
@@ -7231,6 +8077,7 @@ function visitPromiseReturn(node, options) {
7231
8077
  if (!callback || (!isArrowFunction(callback) && !isFunctionExpression(callback))) {
7232
8078
  return `return ${visit(node)}` + (options.inline ? '' : ';\n\t');
7233
8079
  }
8080
+ useHelper('promise');
7234
8081
  const channelType = getPromiseChannelType(node);
7235
8082
  const resolveParam = callback.parameters[0];
7236
8083
  const resolveName = resolveParam ? visit(resolveParam.name) : '';
@@ -7242,7 +8089,7 @@ function visitPromiseReturn(node, options) {
7242
8089
  const body = isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
7243
8090
  promiseResolveName = prevResolveName;
7244
8091
  promiseRejectName = prevRejectName;
7245
- return (`ch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch` +
8092
+ return (`ch := make(chan ${channelType}, 1)\n\t\tfunc() {\n\t\t\tdefer TnRejectExecutor(ch)\n\t\t\tfunc() ${body.trimEnd()}()\n\t\t}()\n\t\treturn ch` +
7246
8093
  (options.inline ? '' : ';\n\t'));
7247
8094
  }
7248
8095
  function visitNewPromise(node) {
@@ -7261,7 +8108,10 @@ function visitNewPromise(node) {
7261
8108
  const body = isBlock(callback.body) ? visit(callback.body) : `{ ${visit(callback.body)} }`;
7262
8109
  promiseResolveName = prevResolveName;
7263
8110
  promiseRejectName = prevRejectName;
7264
- return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType})\n\t\tgo func() ${body.trimEnd()}()\n\t\treturn ch;\n\t}()`;
8111
+ // Buffered: resolving never blocks (a loop task may resolve a promise its own goroutine awaits)
8112
+ // The executor runs synchronously, as in JS (a throw rejects the promise)
8113
+ useHelper('promise');
8114
+ return `func() chan ${channelType} {\n\t\tch := make(chan ${channelType}, 1)\n\t\tfunc() {\n\t\t\tdefer TnRejectExecutor(ch)\n\t\t\tfunc() ${body.trimEnd()}()\n\t\t}()\n\t\treturn ch;\n\t}()`;
7265
8115
  }
7266
8116
  function extractMapValueType(mapType) {
7267
8117
  const args = splitMapTypeArgs(mapType);
@@ -7350,11 +8200,13 @@ function normalizeCjsToEsm(code) {
7350
8200
  code = code.replace(/(?:module\.exports|exports)\.(\w+)\s*=\s*function\s*\w*\s*\(/g, 'export function $1(');
7351
8201
  return code;
7352
8202
  }
7353
- function includeLocalImport(code, dir, goFileName, tsx = false) {
8203
+ function includeLocalImport(code, dir, goFileName, tsx = false, fileName = '') {
7354
8204
  const prevDir = currentFileDir;
7355
8205
  const prevTsx = currentFileIsTsx;
8206
+ const prevFileName = currentFileName;
7356
8207
  currentFileDir = dir;
7357
8208
  currentFileIsTsx = tsx;
8209
+ currentFileName = fileName;
7358
8210
  // Normalize CommonJS modules to ES module syntax before parsing
7359
8211
  if (!code.includes('export ') && (code.includes('module.exports') || code.includes('exports.'))) {
7360
8212
  code = normalizeCjsToEsm(code);
@@ -7367,6 +8219,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
7367
8219
  }
7368
8220
  currentFileDir = prevDir;
7369
8221
  currentFileIsTsx = prevTsx;
8222
+ currentFileName = prevFileName;
7370
8223
  return;
7371
8224
  }
7372
8225
  // Separate-file mode (local TS imports): transpile to its own Go file.
@@ -7374,23 +8227,30 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
7374
8227
  // `package main`, so helpers are emitted once in the main output file.
7375
8228
  const savedOutsideNodes = outsideNodes;
7376
8229
  const savedPackages = [...importedPackages];
8230
+ // Helper packages registered by the including file so far must survive this
8231
+ // file's own tracking (the main file imports them for the helpers it carries)
8232
+ const savedHelperPackages = [...helperProvidedPackages];
7377
8233
  outsideNodes = [];
7378
8234
  importedPackages.clear();
7379
8235
  helperProvidedPackages.clear();
7380
8236
  const sf = parseSource(code);
7381
- const inlineLines = [];
8237
+ const isDeclarationLine = (line) => /^(var|const|type|func|import)\b/.test(line.trim());
8238
+ const declarationLines = [];
8239
+ const statementLines = [];
7382
8240
  const prevEmittingModuleFile = emittingModuleFile;
7383
8241
  emittingModuleFile = true;
7384
8242
  for (const stmt of sf.statements) {
7385
8243
  const result = visit(stmt, { addFunctionOutside: true });
7386
- if (result.trim())
7387
- inlineLines.push(result);
8244
+ if (!result.trim())
8245
+ continue;
8246
+ const line = withLineMarker(stmt, localizeGoPaths(result));
8247
+ if (isDeclarationLine(result))
8248
+ declarationLines.push(line);
8249
+ else
8250
+ statementLines.push(line);
7388
8251
  }
7389
8252
  emittingModuleFile = prevEmittingModuleFile;
7390
- const fileOutside = outsideNodes.map((n) => visit(n, { isOutside: true })).join('\n');
7391
- const isDeclarationLine = (line) => /^(var|const|type|func|import)\b/.test(line.trim());
7392
- const declarationLines = inlineLines.filter(isDeclarationLine);
7393
- const statementLines = inlineLines.filter((line) => !isDeclarationLine(line));
8253
+ const fileOutside = localizeGoPaths(outsideNodes.map((n) => withLineMarker(n, visit(n, { isOutside: true }))).join('\n'));
7394
8254
  const fileInline = [
7395
8255
  ...declarationLines,
7396
8256
  ...(statementLines.length > 0 ? [`func init() {\n\t${statementLines.join('\n\t')}\n}`] : [])
@@ -7405,7 +8265,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
7405
8265
  .filter((pkg) => {
7406
8266
  if (!helperProvidedPackages.has(pkg))
7407
8267
  return true;
7408
- const pkgName = goImportAliases[pkg] ?? pkg.split('/').pop();
8268
+ const pkgName = goPathAliases[pkg] ?? pkg.split('/').pop();
7409
8269
  return stripGoStrings(fileCode).includes(`${pkgName}.`);
7410
8270
  })
7411
8271
  .sort()
@@ -7418,7 +8278,7 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
7418
8278
  parts.push(fileInline.trim());
7419
8279
  if (fileOutside.trim())
7420
8280
  parts.push(fileOutside.trim());
7421
- localImportFiles.set(goFileName, parts.join('\n\n'));
8281
+ localImportFiles.set(goFileName, applyLineDirectives(parts.join('\n\n'), goFileName));
7422
8282
  // Restore main-file state; helper-provided packages carry over so the main
7423
8283
  // file (which carries the helper sources) imports them.
7424
8284
  outsideNodes = savedOutsideNodes;
@@ -7426,29 +8286,74 @@ function includeLocalImport(code, dir, goFileName, tsx = false) {
7426
8286
  for (const p of savedPackages)
7427
8287
  importedPackages.add(p);
7428
8288
  currentFileIsTsx = prevTsx;
8289
+ for (const p of savedHelperPackages)
8290
+ helperProvidedPackages.add(p);
7429
8291
  for (const p of helperProvidedPackages)
7430
8292
  importedPackages.add(p);
7431
8293
  currentFileDir = prevDir;
8294
+ currentFileName = prevFileName;
7432
8295
  }
7433
8296
  // Go source without its string literals (for detecting real package use)
7434
8297
  function stripGoStrings(code) {
7435
8298
  return code.replace(/"(?:[^"\\\n]|\\.)*"|`[^`]*`/g, '""');
7436
8299
  }
7437
- function registerGoPackageAliases(node, goPkg) {
7438
- const pkgName = goPkg.split('/').pop();
8300
+ // go:builtin is virtual: ref<T> → new(T), chan<T> → make(chan T),
8301
+ // GoError → *TnError. No Go import; local names map to inline emitters.
8302
+ function registerGoBuiltinAliases(node) {
7439
8303
  if (!node.importClause)
7440
8304
  return;
7441
8305
  const clause = node.importClause;
8306
+ if (clause.name)
8307
+ importAliases.set(clause.name.text, 'go:builtin');
8308
+ const bindings = clause.namedBindings;
8309
+ if (!bindings)
8310
+ return;
8311
+ if (isNamespaceImport(bindings)) {
8312
+ importAliases.set(bindings.name.text, 'go:builtin');
8313
+ return;
8314
+ }
8315
+ if (isNamedImports(bindings)) {
8316
+ for (const el of bindings.elements ?? []) {
8317
+ if (el.isTypeOnly)
8318
+ continue;
8319
+ const localName = el.name.text;
8320
+ const importedName = el.propertyName?.text ?? localName;
8321
+ importAliases.set(localName, `go:builtin.${importedName}`);
8322
+ registerGoBuiltin(localName, importedName);
8323
+ }
8324
+ }
8325
+ }
8326
+ function registerGoPackageAliases(node, goPkg) {
8327
+ // Real package name from metadata (math/rand/v2 → rand); fall back to the
8328
+ // last path segment for packages without metadata.
8329
+ const info = goPackage(goPkg);
8330
+ const pkgName = info ? info.name : goPkg.split('/').pop();
8331
+ // Collision alias (crypto/rand vs math/rand): the emitted qualifier.
8332
+ const qualifier = goPathAliases[goPkg] ?? pkgName;
8333
+ goPackageLocalNames.add(pkgName);
8334
+ goPackageLocalNames.add(qualifier);
8335
+ const named = [];
8336
+ let defaultName = null;
8337
+ let namespaceName = null;
8338
+ if (!node.importClause) {
8339
+ registerGoImport(goPkg, qualifier, null, null, []);
8340
+ return;
8341
+ }
8342
+ const clause = node.importClause;
7442
8343
  // Default import: `import fmt from 'go:fmt'` or `import myFmt from 'go:fmt'`
7443
- if (clause.name && clause.name.text !== pkgName) {
7444
- importAliases.set(clause.name.text, pkgName);
8344
+ if (clause.name) {
8345
+ defaultName = clause.name.text;
8346
+ if (clause.name.text !== pkgName && clause.name.text !== qualifier) {
8347
+ importAliases.set(clause.name.text, qualifier);
8348
+ }
7445
8349
  }
7446
8350
  if (clause.namedBindings) {
7447
8351
  if (isNamespaceImport(clause.namedBindings)) {
7448
8352
  // `import * as myFmt from 'go:fmt'`
7449
8353
  const localName = clause.namedBindings.name.text;
7450
- if (localName !== pkgName) {
7451
- importAliases.set(localName, pkgName);
8354
+ namespaceName = localName;
8355
+ if (localName !== pkgName && localName !== qualifier) {
8356
+ importAliases.set(localName, qualifier);
7452
8357
  }
7453
8358
  }
7454
8359
  else if (isNamedImports(clause.namedBindings)) {
@@ -7458,10 +8363,12 @@ function registerGoPackageAliases(node, goPkg) {
7458
8363
  continue;
7459
8364
  const localName = el.name.text;
7460
8365
  const importedName = el.propertyName?.text ?? localName;
7461
- importAliases.set(localName, `${pkgName}.${importedName}`);
8366
+ importAliases.set(localName, `${qualifier}.${importedName}`);
8367
+ named.push({ imported: importedName, local: localName });
7462
8368
  }
7463
8369
  }
7464
8370
  }
8371
+ registerGoImport(goPkg, qualifier, defaultName, namespaceName, named);
7465
8372
  }
7466
8373
  function getImportLocalName(node) {
7467
8374
  const clause = node.importClause;
@@ -7489,7 +8396,10 @@ function needPkg(pkg) {
7489
8396
  const helperRequires = {
7490
8397
  objectAssign: ['orderedMap'],
7491
8398
  promiseAll: ['orderedMap', 'asyncWait', 'promise'],
7492
- promise: ['error'],
8399
+ promise: ['error', 'eventLoop', 'asyncWait'],
8400
+ goMust: ['error'],
8401
+ bigint: ['error'],
8402
+ goBlocking: ['error', 'eventLoop'],
7493
8403
  decorators: [],
7494
8404
  jsx: [],
7495
8405
  objectIntegrity: [],
@@ -7499,9 +8409,21 @@ const helperRequires = {
7499
8409
  fancyRegex: ['orderedMap'],
7500
8410
  dynamic: ['error'],
7501
8411
  moduleUrl: ['pathToFileURL'],
7502
- timerWait: [],
7503
- asyncWait: []
8412
+ timerWait: ['eventLoop'],
8413
+ clock: [],
8414
+ asyncWait: ['eventLoop'],
8415
+ eventLoop: []
7504
8416
  };
8417
+ // Helpers with OS-specific variants (Windows APIs that would not compile
8418
+ // elsewhere) are required as '<id>@os': the target OS's variant
8419
+ // ('<id>@windows') when there is one, else '<id>@other'
8420
+ function platformHelper(id) {
8421
+ if (!id.endsWith('@os'))
8422
+ return id;
8423
+ const base = id.slice(0, id.length - 3);
8424
+ const specific = `${base}@${targetGoos}`;
8425
+ return goHelpers[specific] !== undefined || nodeRegistry.goHelpers[specific] !== undefined ? specific : `${base}@other`;
8426
+ }
7505
8427
  function useHelper(id) {
7506
8428
  if (usedHelpers.has(id))
7507
8429
  return;
@@ -7510,159 +8432,85 @@ function useHelper(id) {
7510
8432
  if (id === 'namedGroups') {
7511
8433
  interfacePropertyTypes.set('TnMatch', new Map([['groups', '*TnMap[string, string]']]));
7512
8434
  }
7513
- for (const dep of helperRequires[id] ?? [])
7514
- useHelper(dep);
7515
- for (const pkg of helperPackages[id] ?? []) {
8435
+ for (const dep of helperRequires[id] ?? nodeRegistry.helperRequires[id] ?? [])
8436
+ useHelper(platformHelper(dep));
8437
+ for (const pkg of helperPackages[id] ?? nodeRegistry.helperPackages[id] ?? []) {
7516
8438
  importedPackages.add(pkg);
7517
8439
  helperProvidedPackages.add(pkg);
7518
8440
  }
7519
8441
  }
7520
- const nodeModuleMappings = {
7521
- path: {
7522
- functions: {
7523
- join: (args) => {
7524
- needPkg('path/filepath');
7525
- return `filepath.Join(${args.join(', ')})`;
7526
- },
7527
- dirname: (args) => {
7528
- needPkg('path/filepath');
7529
- return `filepath.Dir(${args[0]})`;
7530
- },
7531
- basename: (args) => {
7532
- needPkg('path/filepath');
7533
- if (args[1]) {
7534
- needPkg('strings');
7535
- return `strings.TrimSuffix(filepath.Base(${args[0]}), ${args[1]})`;
7536
- }
7537
- return `filepath.Base(${args[0]})`;
7538
- },
7539
- extname: (args) => {
7540
- needPkg('path/filepath');
7541
- return `filepath.Ext(${args[0]})`;
7542
- },
7543
- resolve: (args) => {
7544
- needPkg('path/filepath');
7545
- return `func() string { p, _ := filepath.Abs(filepath.Join(${args.join(', ')})); return p }()`;
7546
- }
7547
- }
7548
- },
7549
- fs: {
7550
- functions: {
7551
- readFileSync: (args) => {
7552
- useHelper('readFile');
7553
- return `TnReadFile(${args[0]})`;
7554
- },
7555
- writeFileSync: (args) => {
7556
- useHelper('writeFile');
7557
- return `TnWriteFile(${args[0]}, ${args[1]})`;
7558
- },
7559
- appendFileSync: (args) => {
7560
- useHelper('appendFile');
7561
- return `TnAppendFile(${args[0]}, ${args[1]})`;
7562
- },
7563
- existsSync: (args) => {
7564
- needPkg('os');
7565
- return `func() bool { _, err := os.Stat(${args[0]}); return !os.IsNotExist(err) }()`;
7566
- },
7567
- mkdirSync: (args) => {
7568
- useHelper('mkdirAll');
7569
- return `TnMkdirAll(${args[0]})`;
7570
- },
7571
- readdirSync: (args) => {
7572
- useHelper('readDir');
7573
- return `TnReadDir(${args[0]})`;
7574
- },
7575
- copyFileSync: (args) => {
7576
- useHelper('copyFile');
7577
- return `TnCopyFile(${args[0]}, ${args[1]})`;
7578
- },
7579
- rmSync: (args) => {
7580
- useHelper('removeAll');
7581
- return `TnRemoveAll(${args[0]})`;
7582
- },
7583
- unlinkSync: (args) => {
7584
- useHelper('removeAll');
7585
- return `TnRemoveAll(${args[0]})`;
7586
- }
7587
- }
7588
- },
7589
- url: {
7590
- functions: {
7591
- fileURLToPath: (args) => {
7592
- useHelper('fileURLToPath');
7593
- return `TnFileURLToPath(${args[0]})`;
7594
- },
7595
- pathToFileURL: (args) => {
7596
- useHelper('pathToFileURL');
7597
- return `TnPathToFileURL(${args[0]})`;
7598
- }
7599
- }
7600
- },
7601
- os: {
7602
- functions: {
7603
- platform: () => {
7604
- useHelper('osPlatform');
7605
- return 'TnOsPlatform()';
7606
- },
7607
- homedir: () => {
7608
- needPkg('os');
7609
- return `func() string { h, _ := os.UserHomeDir(); return h }()`;
7610
- },
7611
- tmpdir: () => {
7612
- needPkg('os');
7613
- return 'os.TempDir()';
7614
- }
7615
- }
7616
- },
7617
- child_process: {
7618
- functions: {
7619
- // exec(command) — run through the shell, returns { stdout, stderr, status }
7620
- exec: (args) => {
7621
- useHelper('exec');
7622
- return `TnExecShell(${args[0]})`;
7623
- },
7624
- // execSync(command) — run through the shell, returns stdout (panics on failure)
7625
- execSync: (args) => {
7626
- useHelper('exec');
7627
- useHelper('execSync');
7628
- return `TnExecShellSync(${args[0]})`;
7629
- },
7630
- // spawnSync(file, args, options) — matches Node's sync API.
7631
- // options with `inherit` → run with the parent's stdio (returns exit code);
7632
- // otherwise output is captured. Fields match Node: stdout/stderr/status.
7633
- spawnSync: (args) => {
7634
- useHelper('exec');
7635
- useHelper('runInherit');
7636
- // Untyped array literals visit as `[] {…}` — give them the []string
7637
- // type the variadic helper needs (`[]string{…}...` is valid Go spread)
7638
- const arrArg = args[1] ?? '[]string{}';
7639
- const typedArg = arrArg.startsWith('[]') && !arrArg.startsWith('[]string')
7640
- ? arrArg.replace(/^\[\]\S* \{/, '[]string{')
7641
- : arrArg;
7642
- if (args[2]?.includes('inherit')) {
7643
- return `TnRunInherit(${args[0]}, ${typedArg}...)`;
7644
- }
7645
- if (args[2]?.includes('input')) {
7646
- // {input: expr, ...} → run with expr as stdin
7647
- useHelper('execInput');
7648
- const m = /input:\s*([^,}]+)/.exec(args[2]);
7649
- const inputExpr = m ? m[1] : '""';
7650
- return `TnExecInput(${args[0]}, ${inputExpr}, ${typedArg}...)`;
7651
- }
7652
- return `TnExec(${args[0]}, ${typedArg}...)`;
7653
- }
7654
- }
7655
- },
7656
- readline: {
7657
- functions: {
7658
- // question(prompt) — print prompt and read one line from stdin (synchronous)
7659
- question: (args) => {
7660
- useHelper('question');
7661
- return `TnQuestion(${args[0] ?? '""'})`;
7662
- }
7663
- }
7664
- }
7665
- };
8442
+ // Node.js standard library (src/node/*): functions, values and classes the
8443
+ // transpiler resolves imports and Node globals against
8444
+ // (services are bound in transpileToNative: a Go package-level initializer
8445
+ // referring to visit() would be an initialization cycle)
8446
+ const nodeRegistry = createNodeRegistry();
8447
+ function bindNodeRegistryServices() {
8448
+ nodeRegistry.pkg = needPkg;
8449
+ nodeRegistry.helper = useHelper;
8450
+ nodeRegistry.temp = getTempName;
8451
+ nodeRegistry.typeOf = (node) => inferExpressionType(node) ?? '';
8452
+ nodeRegistry.visit = (node) => visit(node);
8453
+ nodeRegistry.categoryOf = (node) => resolveExpressionType(node) ?? '';
8454
+ bindConvertServices({
8455
+ pkg: needPkg,
8456
+ helper: useHelper,
8457
+ typeOf: (node) => inferExpressionType(node) ?? '',
8458
+ localType: goLocalType
8459
+ });
8460
+ bindBridgeServices({
8461
+ pkg: needPkg,
8462
+ helper: useHelper,
8463
+ temp: getTempName,
8464
+ typeOf: (node) => inferExpressionType(node) ?? '',
8465
+ visit: (node) => visit(node),
8466
+ categoryOf: (node) => resolveExpressionType(node) ?? '',
8467
+ goTypeOf: (node) => getType(node),
8468
+ isIdentifier: (node) => isIdentifier(node),
8469
+ isPropertyAccess: (node) => isPropertyAccessExpression(node),
8470
+ isArrowFunction: (node) => isArrowFunction(node),
8471
+ isFunctionExpression: (node) => isFunctionExpression(node),
8472
+ isObjectLiteral: (node) => isObjectLiteralExpression(node),
8473
+ declareLocal: (name, goType) => registerLocalVariable(name, goType)
8474
+ });
8475
+ }
8476
+ registerCore(nodeRegistry);
8477
+ registerBuffer(nodeRegistry);
8478
+ registerPath(nodeRegistry);
8479
+ registerFs(nodeRegistry);
8480
+ registerUrl(nodeRegistry);
8481
+ registerOs(nodeRegistry);
8482
+ registerProcess(nodeRegistry);
8483
+ registerUtil(nodeRegistry);
8484
+ registerUtilExtra(nodeRegistry);
8485
+ registerConsole(nodeRegistry);
8486
+ registerAssert(nodeRegistry);
8487
+ registerPunycode(nodeRegistry);
8488
+ registerQuerystring(nodeRegistry);
8489
+ registerStringDecoder(nodeRegistry);
8490
+ registerCrypto(nodeRegistry);
8491
+ registerCryptoKeys(nodeRegistry);
8492
+ registerZlib(nodeRegistry);
8493
+ registerTimers(nodeRegistry);
8494
+ registerEvents(nodeRegistry);
8495
+ registerStream(nodeRegistry);
8496
+ registerDiagnosticsChannel(nodeRegistry);
8497
+ registerConstants(nodeRegistry);
8498
+ registerChildProcess(nodeRegistry);
8499
+ registerReadline(nodeRegistry);
8500
+ registerNet(nodeRegistry);
8501
+ registerDns(nodeRegistry);
8502
+ registerTls(nodeRegistry);
8503
+ registerHttp(nodeRegistry);
8504
+ registerDgram(nodeRegistry);
8505
+ registerSqlite(nodeRegistry);
8506
+ registerFetch(nodeRegistry);
8507
+ registerStreamWeb(nodeRegistry);
8508
+ registerAsyncHooks(nodeRegistry);
8509
+ registerTest(nodeRegistry);
8510
+ registerWorkerThreads(nodeRegistry);
8511
+ registerCluster(nodeRegistry);
8512
+ registerHttp2(nodeRegistry);
8513
+ registerSystemExtra(nodeRegistry);
7666
8514
  // Go packages required by each helper, registered when the helper is used.
7667
8515
  const helperPackages = {
7668
8516
  regexReplaceFirst: ['regexp'],
@@ -7672,38 +8520,31 @@ const helperPackages = {
7672
8520
  typeOf: ['reflect'],
7673
8521
  dynamic: ['math', 'strings', 'fmt', 'sort', 'reflect', 'strconv', 'regexp', 'unsafe'],
7674
8522
  regexReplaceFunc: ['regexp'],
7675
- readFile: ['os'],
7676
- writeFile: ['os'],
7677
- appendFile: ['os'],
7678
- mkdirAll: ['os'],
7679
- readDir: ['os'],
7680
- copyFile: ['os', 'io', 'path/filepath'],
7681
- removeAll: ['os'],
7682
- fileURLToPath: ['net/url', 'path/filepath', 'strings'],
7683
- pathToFileURL: ['net/url', 'path/filepath'],
8523
+ pathToFileURL: ['net/url', 'path/filepath', 'strings'],
7684
8524
  osPlatform: ['runtime'],
7685
8525
  error: [],
7686
- bigint: ['math/big'],
8526
+ bigint: ['math/big', 'math'],
7687
8527
  symbol: [],
7688
8528
  fancyRegex: ['regexp', 'strings', 'strconv', 'fmt'],
7689
8529
  namedGroups: ['regexp'],
7690
8530
  moduleUrl: ['os'],
7691
- asyncWait: ['sync'],
7692
- timerWait: ['sync', 'time'],
8531
+ asyncWait: [],
8532
+ timerWait: ['sync', 'sync/atomic', 'time'],
8533
+ clock: ['time'],
8534
+ eventLoop: ['sync', 'sync/atomic', 'time'],
7693
8535
  uri: ['strings', 'fmt', 'net/url'],
7694
8536
  promiseAll: [],
7695
8537
  promise: ['sync'],
8538
+ goMust: [],
8539
+ goNums: [],
8540
+ goStrings: [],
8541
+ goBlocking: [],
7696
8542
  decorators: [],
7697
8543
  jsx: ['strings', 'html', 'reflect', 'strconv', 'fmt'],
7698
8544
  objectIntegrity: ['sync', 'reflect'],
7699
8545
  structuredClone: ['reflect', 'unsafe'],
7700
8546
  globalThis: [],
7701
- exec: ['os/exec', 'bytes', 'runtime'],
7702
- execSync: ['os/exec', 'bytes', 'runtime'],
7703
- runInherit: ['os/exec', 'os'],
7704
- question: ['bufio', 'fmt', 'os', 'strings'],
7705
8547
  getenv: ['os'],
7706
- execInput: ['os/exec', 'bytes', 'strings']
7707
8548
  };
7708
8549
  // Go helper sources emitted (once) into the main output file when used.
7709
8550
  // Field names are intentionally lowercase: every generated file is `package main`,
@@ -7723,10 +8564,27 @@ func TnGet(obj interface{}, key string) interface{} {
7723
8564
  if key == "length" {
7724
8565
  return float64(len(o))
7725
8566
  }
8567
+ case interface{ TnProp(string) (interface{}, bool) }:
8568
+ if v, ok := o.TnProp(key); ok || key != "length" {
8569
+ return v
8570
+ }
7726
8571
  case *TnError:
7727
8572
  if key == "message" {
7728
8573
  return o.message
7729
8574
  }
8575
+ if key == "code" {
8576
+ if o.code == "" {
8577
+ return o.prop("code")
8578
+ }
8579
+ return o.code
8580
+ }
8581
+ if key == "name" {
8582
+ return o.Get_name()
8583
+ }
8584
+ if key == "stack" {
8585
+ return o.Get_stack()
8586
+ }
8587
+ return o.prop(key)
7730
8588
  }
7731
8589
  rv := reflect.ValueOf(obj)
7732
8590
  if rv.Kind() == reflect.Pointer && !rv.IsNil() && rv.Elem().Kind() == reflect.Struct {
@@ -7743,6 +8601,27 @@ func TnGet(obj interface{}, key string) interface{} {
7743
8601
  return nil
7744
8602
  }
7745
8603
 
8604
+ // new Error(message, { cause })
8605
+ func TnErrorWithOptions(e *TnError, options interface{}) *TnError {
8606
+ if options == nil {
8607
+ return e
8608
+ }
8609
+ if e.props == nil {
8610
+ e.props = map[string]interface{}{}
8611
+ }
8612
+ if m, ok := options.(map[string]interface{}); ok {
8613
+ if cause, present := m["cause"]; present {
8614
+ e.props["cause"] = cause
8615
+ }
8616
+ return e
8617
+ }
8618
+ rv := reflect.ValueOf(options)
8619
+ if rv.Kind() == reflect.Pointer && !rv.IsNil() && rv.Elem().Kind() == reflect.Struct && rv.Elem().FieldByName("cause").IsValid() {
8620
+ e.props["cause"] = TnGet(options, "cause")
8621
+ }
8622
+ return e
8623
+ }
8624
+
7746
8625
  func TnSet(obj interface{}, key string, value interface{}) {
7747
8626
  if o, ok := obj.(map[string]interface{}); ok {
7748
8627
  o[key] = value
@@ -7781,6 +8660,15 @@ func TnIndex(obj interface{}, key interface{}) interface{} {
7781
8660
  if i, ok := key.(float64); ok && i >= 0 && int(i) < len(o) {
7782
8661
  return string(o[int(i)])
7783
8662
  }
8663
+ case []byte:
8664
+ if i, ok := key.(float64); ok && i >= 0 && int(i) < len(o) {
8665
+ return float64(o[int(i)])
8666
+ }
8667
+ case interface{ TnProp(string) (interface{}, bool) }:
8668
+ if k, ok := key.(string); ok {
8669
+ v, _ := o.TnProp(k)
8670
+ return v
8671
+ }
7784
8672
  }
7785
8673
  return nil
7786
8674
  }
@@ -7831,14 +8719,52 @@ func TnLength(v interface{}) float64 {
7831
8719
  }
7832
8720
 
7833
8721
  // A value as JS prints it in a template literal (nil pointers are "undefined")
7834
- func TnFormat(v interface{}) string {
7835
- rv := reflect.ValueOf(v)
7836
- if !rv.IsValid() || (rv.Kind() == reflect.Pointer && rv.IsNil()) {
8722
+ // String(v) of a dynamic value, as JS renders it
8723
+ func TnJSString(v interface{}) string {
8724
+ switch x := v.(type) {
8725
+ case nil:
7837
8726
  return "undefined"
7838
- }
7839
- if rv.Kind() == reflect.Pointer {
7840
- return fmt.Sprintf("%v", rv.Elem().Interface())
7841
- }
8727
+ case string:
8728
+ return x
8729
+ case float64:
8730
+ return TnNumStr(x)
8731
+ case bool:
8732
+ if x {
8733
+ return "true"
8734
+ }
8735
+ return "false"
8736
+ case []byte:
8737
+ return strings.ToValidUTF8(string(x), "�")
8738
+ case []interface{}:
8739
+ parts := make([]string, len(x))
8740
+ for i, e := range x {
8741
+ if e != nil {
8742
+ parts[i] = TnJSString(e)
8743
+ }
8744
+ }
8745
+ return strings.Join(parts, ",")
8746
+ case []string:
8747
+ return strings.Join(x, ",")
8748
+ case map[string]interface{}:
8749
+ return "[object Object]"
8750
+ case *TnError:
8751
+ return x.Get_name() + ": " + x.message
8752
+ case fmt.Stringer:
8753
+ return x.String()
8754
+ case interface{ toString() string }:
8755
+ return x.toString()
8756
+ }
8757
+ return fmt.Sprintf("%v", v)
8758
+ }
8759
+
8760
+ func TnFormat(v interface{}) string {
8761
+ rv := reflect.ValueOf(v)
8762
+ if !rv.IsValid() || (rv.Kind() == reflect.Pointer && rv.IsNil()) {
8763
+ return "undefined"
8764
+ }
8765
+ if rv.Kind() == reflect.Pointer {
8766
+ return fmt.Sprintf("%v", rv.Elem().Interface())
8767
+ }
7842
8768
  return fmt.Sprintf("%v", v)
7843
8769
  }
7844
8770
 
@@ -8006,6 +8932,9 @@ func TnHas(obj interface{}, key interface{}) bool {
8006
8932
  }
8007
8933
 
8008
8934
  func TnKeys(obj interface{}) []string {
8935
+ if record, ok := obj.(interface{ Keys() []string }); ok {
8936
+ return append([]string{}, record.Keys()...)
8937
+ }
8009
8938
  o, _ := obj.(map[string]interface{})
8010
8939
  keys := make([]string, 0, len(o))
8011
8940
  for k := range o {
@@ -8093,6 +9022,10 @@ func TnAsPtr[T any](v interface{}) *T {
8093
9022
  if t, ok := v.(T); ok {
8094
9023
  return &t
8095
9024
  }
9025
+ var zero T
9026
+ if converted, ok := TnConvertDynamic(v, reflect.TypeOf(&zero)); ok {
9027
+ return converted.Interface().(*T)
9028
+ }
8096
9029
  return nil
8097
9030
  }
8098
9031
 
@@ -8100,10 +9033,93 @@ func TnAs[T any](v interface{}) T {
8100
9033
  if p, ok := v.(*T); ok && p != nil {
8101
9034
  return *p
8102
9035
  }
8103
- t, _ := v.(T)
9036
+ t, ok := v.(T)
9037
+ if !ok && v != nil {
9038
+ if converted, cok := TnConvertDynamic(v, reflect.TypeOf((*T)(nil)).Elem()); cok {
9039
+ return converted.Interface().(T)
9040
+ }
9041
+ }
8104
9042
  return t
8105
9043
  }
8106
9044
 
9045
+ // Converts a dynamic value (decoded JSON: maps, []interface{}, float64 …) to a
9046
+ // Go type: structs (and pointers to them) field by field, slices element by
9047
+ // element, numbers between kinds
9048
+ func TnConvertDynamic(v interface{}, t reflect.Type) (reflect.Value, bool) {
9049
+ if v == nil {
9050
+ return reflect.Zero(t), true
9051
+ }
9052
+ rv := reflect.ValueOf(v)
9053
+ if rv.Type().AssignableTo(t) {
9054
+ return rv, true
9055
+ }
9056
+ switch t.Kind() {
9057
+ case reflect.Interface:
9058
+ if rv.Type().Implements(t) {
9059
+ return rv, true
9060
+ }
9061
+ case reflect.Pointer:
9062
+ if rv.Kind() == reflect.Pointer && rv.IsNil() {
9063
+ return reflect.Zero(t), true
9064
+ }
9065
+ inner, ok := TnConvertDynamic(v, t.Elem())
9066
+ if !ok {
9067
+ return reflect.Value{}, false
9068
+ }
9069
+ p := reflect.New(t.Elem())
9070
+ p.Elem().Set(inner)
9071
+ return p, true
9072
+ case reflect.Struct:
9073
+ m, ok := v.(map[string]interface{})
9074
+ if props, isRecord := v.(interface{ Entries() [][]interface{} }); !ok && isRecord {
9075
+ // A string-keyed TnMap (a Record, a node:sqlite row)
9076
+ m, ok = map[string]interface{}{}, true
9077
+ for _, e := range props.Entries() {
9078
+ if k, isString := e[0].(string); isString {
9079
+ m[k] = e[1]
9080
+ }
9081
+ }
9082
+ }
9083
+ if !ok {
9084
+ return reflect.Value{}, false
9085
+ }
9086
+ out := reflect.New(t).Elem()
9087
+ for i := 0; i < t.NumField(); i++ {
9088
+ field := t.Field(i)
9089
+ name := strings.TrimSuffix(field.Name, "_")
9090
+ value, present := m[name]
9091
+ if !present {
9092
+ continue
9093
+ }
9094
+ converted, ok := TnConvertDynamic(value, field.Type)
9095
+ if !ok {
9096
+ continue
9097
+ }
9098
+ f := out.Field(i)
9099
+ reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(converted)
9100
+ }
9101
+ return out, true
9102
+ case reflect.Slice:
9103
+ if rv.Kind() != reflect.Slice {
9104
+ return reflect.Value{}, false
9105
+ }
9106
+ out := reflect.MakeSlice(t, rv.Len(), rv.Len())
9107
+ for i := 0; i < rv.Len(); i++ {
9108
+ converted, ok := TnConvertDynamic(rv.Index(i).Interface(), t.Elem())
9109
+ if !ok {
9110
+ return reflect.Value{}, false
9111
+ }
9112
+ out.Index(i).Set(converted)
9113
+ }
9114
+ return out, true
9115
+ case reflect.Float64, reflect.Float32, reflect.Int, reflect.Int64, reflect.Int32:
9116
+ if rv.CanConvert(t) && rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Float64 {
9117
+ return rv.Convert(t), true
9118
+ }
9119
+ }
9120
+ return reflect.Value{}, false
9121
+ }
9122
+
8107
9123
  func TnTruthy(v interface{}) bool {
8108
9124
  switch t := v.(type) {
8109
9125
  case nil:
@@ -8243,6 +9259,31 @@ func tnJSONWrite(buf *bytes.Buffer, rv reflect.Value, indent string, depth int)
8243
9259
  }
8244
9260
  buf.WriteByte('}')
8245
9261
  case reflect.Struct:
9262
+ // Records (TnMap) keep their key order; reached through a pointer, the
9263
+ // struct value needs Addr() for pointer-receiver methods
9264
+ if rv.CanAddr() {
9265
+ if m, ok := rv.Addr().Interface().(interface{ Entries() [][]interface{} }); ok {
9266
+ buf.WriteByte('{')
9267
+ for i, e := range m.Entries() {
9268
+ if i > 0 {
9269
+ buf.WriteByte(',')
9270
+ }
9271
+ tnJSONNewline(buf, indent, depth+1)
9272
+ kd, _ := tnJSONMarshal(fmt.Sprint(e[0]))
9273
+ buf.Write(kd)
9274
+ buf.WriteByte(':')
9275
+ if indent != "" {
9276
+ buf.WriteByte(' ')
9277
+ }
9278
+ tnJSONWrite(buf, reflect.ValueOf(e[1]), indent, depth+1)
9279
+ }
9280
+ if len(m.Entries()) > 0 {
9281
+ tnJSONNewline(buf, indent, depth)
9282
+ }
9283
+ buf.WriteByte('}')
9284
+ return
9285
+ }
9286
+ }
8246
9287
  if m, ok := rv.Interface().(json.Marshaler); ok {
8247
9288
  data, err := m.MarshalJSON()
8248
9289
  if err == nil {
@@ -8323,6 +9364,17 @@ func (m *TnMap[K, V]) Get(k K) V {
8323
9364
  return m.values[k]
8324
9365
  }
8325
9366
 
9367
+ // A string key's value when the map is held in a dynamic value (a Record or a
9368
+ // node:sqlite row read as \`row.name\`): TnGet finds it through this method
9369
+ func (m *TnMap[K, V]) TnProp(key string) (interface{}, bool) {
9370
+ k, ok := any(key).(K)
9371
+ if !ok || m == nil {
9372
+ return nil, false
9373
+ }
9374
+ v, present := m.values[k]
9375
+ return v, present
9376
+ }
9377
+
8326
9378
  func (m *TnMap[K, V]) Lookup(k K) (V, bool) {
8327
9379
  if m == nil {
8328
9380
  var zero V
@@ -8447,6 +9499,40 @@ func (m *TnMap[K, V]) All() iter.Seq2[K, V] {
8447
9499
  }
8448
9500
  }
8449
9501
 
9502
+ // JSON objects decode in document order (json.Unmarshal into a ref)
9503
+ func (m *TnMap[K, V]) UnmarshalJSON(data []byte) error {
9504
+ dec := json.NewDecoder(bytes.NewReader(data))
9505
+ tok, err := dec.Token()
9506
+ if err != nil {
9507
+ return err
9508
+ }
9509
+ if delim, ok := tok.(json.Delim); !ok || delim != '{' {
9510
+ return fmt.Errorf("json: cannot unmarshal %v into an object", tok)
9511
+ }
9512
+ m.keys = nil
9513
+ m.values = map[K]V{}
9514
+ for dec.More() {
9515
+ tok, err := dec.Token()
9516
+ if err != nil {
9517
+ return err
9518
+ }
9519
+ name, _ := tok.(string)
9520
+ var key K
9521
+ if p, ok := any(&key).(*string); ok {
9522
+ *p = name
9523
+ } else if err := json.Unmarshal([]byte(name), &key); err != nil {
9524
+ return err
9525
+ }
9526
+ var value V
9527
+ if err := dec.Decode(&value); err != nil {
9528
+ return err
9529
+ }
9530
+ m.Set(key, value)
9531
+ }
9532
+ _, err = dec.Token()
9533
+ return err
9534
+ }
9535
+
8450
9536
  // JSON objects keep insertion order
8451
9537
  func (m *TnMap[K, V]) MarshalJSON() ([]byte, error) {
8452
9538
  var buf bytes.Buffer
@@ -9424,81 +10510,17 @@ func TnGroup(groups []string, i int) string {
9424
10510
  return groups[i]
9425
10511
  }
9426
10512
  return ""
9427
- }`,
9428
- readFile: `func TnReadFile(path string) string {
9429
- data, err := os.ReadFile(path)
9430
- if err != nil {
9431
- panic(err)
9432
- }
9433
- return string(data)
9434
- }`,
9435
- writeFile: `func TnWriteFile(path string, content string) {
9436
- if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
9437
- panic(err)
9438
- }
9439
- }`,
9440
- appendFile: `func TnAppendFile(path string, content string) {
9441
- f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
9442
- if err != nil {
9443
- panic(err)
9444
- }
9445
- defer f.Close()
9446
- if _, err := f.WriteString(content); err != nil {
9447
- panic(err)
9448
- }
9449
- }`,
9450
- mkdirAll: `func TnMkdirAll(path string) {
9451
- if err := os.MkdirAll(path, 0o755); err != nil {
9452
- panic(err)
9453
- }
9454
- }`,
9455
- readDir: `func TnReadDir(path string) []string {
9456
- entries, err := os.ReadDir(path)
9457
- if err != nil {
9458
- panic(err)
9459
- }
9460
- names := make([]string, 0, len(entries))
9461
- for _, e := range entries {
9462
- names = append(names, e.Name())
9463
- }
9464
- return names
9465
- }`,
9466
- copyFile: `func TnCopyFile(src string, dst string) {
9467
- in, err := os.Open(src)
9468
- if err != nil {
9469
- panic(err)
9470
- }
9471
- defer in.Close()
9472
- if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
9473
- panic(err)
9474
- }
9475
- out, err := os.Create(dst)
9476
- if err != nil {
9477
- panic(err)
9478
- }
9479
- defer out.Close()
9480
- if _, err := io.Copy(out, in); err != nil {
9481
- panic(err)
9482
- }
9483
- }`,
9484
- removeAll: `func TnRemoveAll(path string) {
9485
- if err := os.RemoveAll(path); err != nil {
9486
- panic(err)
9487
- }
9488
- }`,
9489
- fileURLToPath: `func TnFileURLToPath(u string) string {
9490
- trimmed := strings.TrimPrefix(u, "file://")
9491
- if unescaped, err := tnurl.PathUnescape(trimmed); err == nil {
9492
- trimmed = unescaped
9493
- }
9494
- return filepath.FromSlash(trimmed)
9495
10513
  }`,
9496
10514
  pathToFileURL: `func TnPathToFileURL(path string) string {
9497
10515
  abs, err := filepath.Abs(path)
9498
10516
  if err != nil {
9499
10517
  panic(err)
9500
10518
  }
9501
- return "file://" + tnurl.PathEscape(filepath.ToSlash(abs))
10519
+ slashed := filepath.ToSlash(abs)
10520
+ if !strings.HasPrefix(slashed, "/") {
10521
+ slashed = "/" + slashed
10522
+ }
10523
+ return "file://" + (&tnurl.URL{Path: slashed}).EscapedPath()
9502
10524
  }`,
9503
10525
  moduleUrl: `// import.meta.url: the file URL of the running program
9504
10526
  func TnModuleURL() string {
@@ -9517,22 +10539,223 @@ func TnModuleURL() string {
9517
10539
  }
9518
10540
  return runtime.GOOS
9519
10541
  }`,
9520
- error: `// TnError: JS Error values (thrown and caught) with a message
10542
+ error: `// TnError: JS Error values (thrown and caught) with a message; Node
10543
+ // errors also carry a code ('ENOENT', 'ERR_ASSERTION') and extra properties
9521
10544
  type TnError struct {
9522
10545
  message string
10546
+ code string
10547
+ props map[string]interface{}
10548
+ goErr error
9523
10549
  }
9524
10550
 
9525
10551
  func (e *TnError) Error() string { return e.message }
9526
10552
 
9527
10553
  func TnNewError(message string) *TnError {
9528
10554
  return &TnError{message: message}
10555
+ }
10556
+
10557
+
10558
+ // x instanceof Error / TypeError / …
10559
+ func TnIsErrorOf(v interface{}, name string) bool {
10560
+ if name == "Error" {
10561
+ _, ok := v.(error)
10562
+ return ok
10563
+ }
10564
+ e, ok := v.(*TnError)
10565
+ return ok && e.Get_name() == name
10566
+ }
10567
+
10568
+ // A Node error: name ('TypeError', 'AssertionError'), code and message
10569
+ func TnNodeError(name string, code string, message string) *TnError {
10570
+ return &TnError{message: message, code: code, props: map[string]interface{}{"name": name}}
10571
+ }
10572
+
10573
+ func (e *TnError) prop(key string) interface{} {
10574
+ if e.props == nil {
10575
+ return nil
10576
+ }
10577
+ return e.props[key]
10578
+ }
10579
+
10580
+ func (e *TnError) Get_name() string {
10581
+ if name, ok := e.prop("name").(string); ok {
10582
+ return name
10583
+ }
10584
+ return "Error"
10585
+ }
10586
+
10587
+ func (e *TnError) Get_stack() string { return e.Get_name() + ": " + e.message }
10588
+
10589
+ func (e *TnError) Get_syscall() string {
10590
+ s, _ := e.prop("syscall").(string)
10591
+ return s
10592
+ }
10593
+
10594
+ func (e *TnError) Get_path() string {
10595
+ s, _ := e.prop("path").(string)
10596
+ return s
10597
+ }
10598
+
10599
+ func (e *TnError) Get_errno() float64 {
10600
+ n, _ := e.prop("errno").(float64)
10601
+ return n
10602
+ }
10603
+
10604
+ func (e *TnError) Get_cause() interface{} { return e.prop("cause") }
10605
+
10606
+ func (e *TnError) Get_hostname() string {
10607
+ s, _ := e.prop("hostname").(string)
10608
+ return s
10609
+ }
10610
+
10611
+ func (e *TnError) Get_address() string {
10612
+ s, _ := e.prop("address").(string)
10613
+ return s
10614
+ }
10615
+
10616
+ func (e *TnError) Get_port() float64 {
10617
+ n, _ := e.prop("port").(float64)
10618
+ return n
10619
+ }
10620
+
10621
+ // AssertionError fields
10622
+ func (e *TnError) Get_actual() interface{} { return e.prop("actual") }
10623
+
10624
+ func (e *TnError) Get_expected() interface{} { return e.prop("expected") }
10625
+
10626
+ func (e *TnError) Get_operator() string {
10627
+ s, _ := e.prop("operator").(string)
10628
+ return s
10629
+ }
10630
+
10631
+ func (e *TnError) Get_generatedMessage() bool {
10632
+ b, _ := e.prop("generatedMessage").(bool)
10633
+ return b
10634
+ }`,
10635
+ asyncWait: `// Pending async work (promise callbacks, detached async calls) keeps the
10636
+ // event loop alive until it settles
10637
+ var __tnWaitGroup tnLoopGroup`,
10638
+ bigint: `// Base from the literal's prefix (0x / 0o / 0b), decimal otherwise
10639
+ func TnBigInt(s string) *tnbig.Int {
10640
+ v, ok := new(tnbig.Int).SetString(s, 0)
10641
+ if !ok {
10642
+ v, _ = new(tnbig.Int).SetString(s, 10)
10643
+ }
10644
+ return v
10645
+ }
10646
+
10647
+ // x === 1n where x is a dynamic (number | bigint) value
10648
+ func TnBigSame(v interface{}, b *tnbig.Int) bool {
10649
+ x, ok := v.(*tnbig.Int)
10650
+ return ok && x != nil && b != nil && x.Cmp(b) == 0
10651
+ }
10652
+
10653
+ // A dynamic value as a bigint operand: a bigint, or an integral number
10654
+ func TnToBigInt(v interface{}) *tnbig.Int {
10655
+ switch x := v.(type) {
10656
+ case *tnbig.Int:
10657
+ if x != nil {
10658
+ return x
10659
+ }
10660
+ case float64:
10661
+ if x == math.Trunc(x) && !math.IsInf(x, 0) {
10662
+ b, _ := new(tnbig.Float).SetFloat64(x).Int(nil)
10663
+ return b
10664
+ }
10665
+ }
10666
+ panic(TnNodeError("TypeError", "", "Cannot mix BigInt and other types, use explicit conversions"))
9529
10667
  }`,
9530
- asyncWait: `// Pending async work (promise callbacks, detached async calls) is drained
9531
- // before the process exits
9532
- var __tnWaitGroup sync.WaitGroup`,
9533
- bigint: `func TnBigInt(s string) *tnbig.Int {
9534
- v, _ := new(tnbig.Int).SetString(s, 10)
10668
+ goMust: `// Go bridge: (T, error) results throw a *TnError carrying the Go error
10669
+ func tnGoError(err error) *TnError {
10670
+ return &TnError{message: err.Error(), goErr: err}
10671
+ }
10672
+
10673
+ func tnGoMust[T any](v T, err error) T {
10674
+ if err != nil {
10675
+ panic(tnGoError(err))
10676
+ }
9535
10677
  return v
10678
+ }
10679
+
10680
+ func tnGoMust2[A any, B any](a A, b B, err error) (A, B) {
10681
+ if err != nil {
10682
+ panic(tnGoError(err))
10683
+ }
10684
+ return a, b
10685
+ }
10686
+
10687
+ func tnGoCheck(err error) {
10688
+ if err != nil {
10689
+ panic(tnGoError(err))
10690
+ }
10691
+ }`,
10692
+ goNums: `// Go bridge: numeric slice conversion ([]int ↔ []float64)
10693
+ type tnGoNumber interface {
10694
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 |
10695
+ ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
10696
+ ~float32 | ~float64
10697
+ }
10698
+
10699
+ func tnGoNums[T tnGoNumber](s []float64) []T {
10700
+ out := make([]T, len(s))
10701
+ for i, v := range s {
10702
+ out[i] = T(v)
10703
+ }
10704
+ return out
10705
+ }
10706
+
10707
+ func tnGoFromNums[T tnGoNumber](s []T) []float64 {
10708
+ out := make([]float64, len(s))
10709
+ for i, v := range s {
10710
+ out[i] = float64(v)
10711
+ }
10712
+ return out
10713
+ }`,
10714
+ goStrings: `// Go bridge: first rune of a string (unicode funcs take rune)
10715
+ func tnGoRune(s string) rune {
10716
+ for _, r := range s {
10717
+ return r
10718
+ }
10719
+ return 0
10720
+ }`,
10721
+ goBlocking: `// Go bridge: blocking calls run on a goroutine while the baton holder
10722
+ // pumps pending loop tasks, so timers and handlers keep running
10723
+ func tnGoBlocking(fn func() interface{}) interface{} {
10724
+ done := make(chan interface{}, 1)
10725
+ go func() {
10726
+ defer func() {
10727
+ if r := recover(); r != nil {
10728
+ done <- r
10729
+ return
10730
+ }
10731
+ }()
10732
+ done <- fn()
10733
+ }()
10734
+ for {
10735
+ select {
10736
+ case v := <-done:
10737
+ if err, ok := v.(*TnError); ok {
10738
+ panic(err)
10739
+ }
10740
+ return v
10741
+ default:
10742
+ }
10743
+ TnRunMicrotasks()
10744
+ if TnLoopRunPending() {
10745
+ continue
10746
+ }
10747
+ tnRelease()
10748
+ select {
10749
+ case v := <-done:
10750
+ tnAcquire()
10751
+ if err, ok := v.(*TnError); ok {
10752
+ panic(err)
10753
+ }
10754
+ return v
10755
+ case <-tnLoopSignal:
10756
+ tnAcquire()
10757
+ }
10758
+ }
9536
10759
  }`,
9537
10760
  symbol: `// TnSymbol: JS symbol values (unique by identity)
9538
10761
  type TnSymbol struct {
@@ -9542,57 +10765,422 @@ type TnSymbol struct {
9542
10765
  func TnNewSymbol(description string) *TnSymbol {
9543
10766
  return &TnSymbol{description: description}
9544
10767
  }`,
9545
- timerWait: `// Pending setTimeout timers are waited on before the process exits
9546
- var __tnTimers sync.WaitGroup
10768
+ timerWait: `// Timers: callbacks run on the event loop; pending (ref'ed) timers keep the process alive
10769
+ var __tnTimers tnLoopGroup
9547
10770
 
9548
- func TnSetTimeout(ms float64, fn func()) *time.Timer {
10771
+ // A setTimeout / setImmediate handle (Node's Timeout: ref / unref / hasRef / refresh / close)
10772
+ type TnTimeout struct {
10773
+ timer *time.Timer
10774
+ ms float64
10775
+ fn func()
10776
+ refd bool
10777
+ active bool
10778
+ gen int
10779
+ }
10780
+
10781
+ // node:test mock timers: when set (and the API is mocked) it takes over a timer;
10782
+ // run calls the callback and reports whether the timer repeats
10783
+ var tnMockTimerHook func(kind string, ms float64, run func() bool) bool
10784
+
10785
+ func TnSetTimeout(ms float64, fn func()) *TnTimeout { return tnSetTimeout("setTimeout", ms, fn) }
10786
+
10787
+ func TnSetImmediate(fn func()) *TnTimeout { return tnSetTimeout("setImmediate", 0, fn) }
10788
+
10789
+ func tnSetTimeout(kind string, ms float64, fn func()) *TnTimeout {
10790
+ t := &TnTimeout{ms: ms, fn: TnAsyncWrap(fn), active: true}
10791
+ if tnMockTimerHook != nil && tnMockTimerHook(kind, ms, func() bool {
10792
+ if t.active {
10793
+ t.active = false
10794
+ t.fn()
10795
+ }
10796
+ return false
10797
+ }) {
10798
+ return t
10799
+ }
9549
10800
  __tnTimers.Add(1)
9550
- return time.AfterFunc(time.Duration(ms*float64(time.Millisecond)), func() {
9551
- defer __tnTimers.Done()
9552
- fn()
9553
- TnRunMicrotasks()
10801
+ t.refd = true
10802
+ t.arm()
10803
+ return t
10804
+ }
10805
+
10806
+ func (t *TnTimeout) arm() {
10807
+ t.gen++
10808
+ gen := t.gen
10809
+ t.timer = time.AfterFunc(time.Duration(t.ms*float64(time.Millisecond)), func() {
10810
+ TnLoopPost(func() {
10811
+ if !t.active || gen != t.gen {
10812
+ return
10813
+ }
10814
+ t.active = false
10815
+ if t.refd {
10816
+ t.refd = false
10817
+ __tnTimers.Done()
10818
+ }
10819
+ t.fn()
10820
+ })
9554
10821
  })
9555
10822
  }
9556
10823
 
9557
- // setInterval: fn every ms until cleared (the process lives until then)
10824
+ func (t *TnTimeout) ref() *TnTimeout {
10825
+ if !t.refd && t.active {
10826
+ t.refd = true
10827
+ __tnTimers.Add(1)
10828
+ }
10829
+ return t
10830
+ }
10831
+
10832
+ func (t *TnTimeout) unref() *TnTimeout {
10833
+ if t.refd {
10834
+ t.refd = false
10835
+ __tnTimers.Done()
10836
+ }
10837
+ return t
10838
+ }
10839
+
10840
+ func (t *TnTimeout) hasRef() bool { return t.refd }
10841
+
10842
+ // refresh(): restarts the countdown (a fired timeout runs again)
10843
+ func (t *TnTimeout) refresh() *TnTimeout {
10844
+ if t.timer == nil && t.active && !t.refd {
10845
+ // A mocked timer: node:test's clock owns it
10846
+ return t
10847
+ }
10848
+ if t.timer != nil {
10849
+ t.timer.Stop()
10850
+ }
10851
+ if !t.active {
10852
+ t.active = true
10853
+ if !t.refd {
10854
+ t.refd = true
10855
+ __tnTimers.Add(1)
10856
+ }
10857
+ }
10858
+ t.arm()
10859
+ return t
10860
+ }
10861
+
10862
+ func (t *TnTimeout) close() *TnTimeout {
10863
+ TnClearTimeout(t)
10864
+ return t
10865
+ }
10866
+
10867
+ // setInterval: fn every ms until cleared (the process lives until then, unless unref'ed)
9558
10868
  type TnInterval struct {
9559
- stop chan struct{}
9560
- once sync.Once
10869
+ stop chan struct{}
10870
+ once sync.Once
10871
+ stopped int32
10872
+ refd bool
10873
+ ms float64
10874
+ fn func()
9561
10875
  }
9562
10876
 
9563
10877
  func TnSetInterval(ms float64, fn func()) *TnInterval {
10878
+ t := &TnInterval{stop: make(chan struct{}), ms: ms, fn: TnAsyncWrap(fn)}
10879
+ if tnMockTimerHook != nil && tnMockTimerHook("setInterval", ms, func() bool {
10880
+ if atomic.LoadInt32(&t.stopped) != 0 {
10881
+ return false
10882
+ }
10883
+ t.fn()
10884
+ return atomic.LoadInt32(&t.stopped) == 0
10885
+ }) {
10886
+ return t
10887
+ }
9564
10888
  __tnTimers.Add(1)
9565
- t := &TnInterval{stop: make(chan struct{})}
10889
+ t.refd = true
10890
+ t.start(t.stop)
10891
+ return t
10892
+ }
10893
+
10894
+ func (t *TnInterval) start(stop chan struct{}) {
9566
10895
  go func() {
9567
- ticker := time.NewTicker(max(time.Duration(ms*float64(time.Millisecond)), time.Millisecond))
10896
+ ticker := time.NewTicker(max(time.Duration(t.ms*float64(time.Millisecond)), time.Millisecond))
9568
10897
  defer ticker.Stop()
9569
10898
  for {
9570
10899
  select {
9571
- case <-t.stop:
10900
+ case <-stop:
9572
10901
  return
9573
10902
  case <-ticker.C:
9574
- fn()
9575
- TnRunMicrotasks()
10903
+ TnLoopPost(func() {
10904
+ if atomic.LoadInt32(&t.stopped) == 0 {
10905
+ t.fn()
10906
+ }
10907
+ })
9576
10908
  }
9577
10909
  }
9578
10910
  }()
9579
- return t
9580
10911
  }
9581
10912
 
9582
10913
  func TnClearInterval(t *TnInterval) {
9583
10914
  if t != nil {
9584
10915
  t.once.Do(func() {
10916
+ atomic.StoreInt32(&t.stopped, 1)
9585
10917
  close(t.stop)
9586
- __tnTimers.Done()
10918
+ if t.refd {
10919
+ t.refd = false
10920
+ __tnTimers.Done()
10921
+ }
9587
10922
  })
9588
10923
  }
9589
10924
  }
9590
10925
 
9591
- // queueMicrotask: run after the current synchronous code (end of main or of a timer callback)
9592
- var tnMicrotasks []func()
9593
- var tnMicrotasksMu sync.Mutex
10926
+ func (t *TnInterval) ref() *TnInterval {
10927
+ if !t.refd && atomic.LoadInt32(&t.stopped) == 0 {
10928
+ t.refd = true
10929
+ __tnTimers.Add(1)
10930
+ }
10931
+ return t
10932
+ }
9594
10933
 
9595
- func TnQueueMicrotask(fn func()) {
10934
+ func (t *TnInterval) unref() *TnInterval {
10935
+ if t.refd {
10936
+ t.refd = false
10937
+ __tnTimers.Done()
10938
+ }
10939
+ return t
10940
+ }
10941
+
10942
+ func (t *TnInterval) hasRef() bool { return t.refd }
10943
+
10944
+ func (t *TnInterval) refresh() *TnInterval {
10945
+ if atomic.LoadInt32(&t.stopped) == 0 {
10946
+ close(t.stop)
10947
+ t.stop = make(chan struct{})
10948
+ t.start(t.stop)
10949
+ }
10950
+ return t
10951
+ }
10952
+
10953
+ func (t *TnInterval) close() *TnInterval {
10954
+ TnClearInterval(t)
10955
+ return t
10956
+ }
10957
+
10958
+ func TnClearTimeout(t *TnTimeout) {
10959
+ if t != nil && t.active {
10960
+ t.active = false
10961
+ if t.timer != nil {
10962
+ t.timer.Stop()
10963
+ }
10964
+ if t.refd {
10965
+ t.refd = false
10966
+ __tnTimers.Done()
10967
+ }
10968
+ }
10969
+ }`,
10970
+ clock: `// The current time; node:test mock timers can replace it (Date.now / new Date())
10971
+ var tnClockHook func() time.Time
10972
+
10973
+ func TnTimeNow() time.Time {
10974
+ if tnClockHook != nil {
10975
+ return tnClockHook()
10976
+ }
10977
+ return time.Now()
10978
+ }`,
10979
+ eventLoop: `// The event loop. JS runs one thing at a time: whoever holds the
10980
+ // baton (main, a loop task, a running async function body) runs JS code; the
10981
+ // others wait. An await runs pending loop tasks, then hands the baton over
10982
+ // while it waits. Callbacks from timers, I/O and other goroutines are posted
10983
+ // as tasks. Refs (pending timers, async work, open handles) keep it alive.
10984
+ var tnLoopMu sync.Mutex
10985
+ var tnLoopTasks []func()
10986
+ var tnLoopSignal = make(chan struct{}, 1)
10987
+ var tnLoopRefs int64
10988
+
10989
+ // The baton: free when it sits in the channel (main holds it at start).
10990
+ // A call of an async function lends it to the body until its first await.
10991
+ var tnBaton = make(chan struct{}, 1)
10992
+ var tnLendStack []chan struct{}
10993
+
10994
+ // Async context (AsyncLocalStorage): the baton holder's current stores. Callbacks
10995
+ // scheduled by JS code capture it (TnAsyncWrap) and run with it restored; an await
10996
+ // resumes with the context it started with. Other loop tasks run without stores.
10997
+ type TnAsyncContext map[interface{}]interface{}
10998
+
10999
+ var tnAsyncCtx TnAsyncContext
11000
+
11001
+ func TnAsyncSnapshot() TnAsyncContext { return tnAsyncCtx }
11002
+
11003
+ // Runs fn with ctx as the current context, restoring the previous one afterwards
11004
+ func TnAsyncRun(ctx TnAsyncContext, fn func()) {
11005
+ saved := tnAsyncCtx
11006
+ tnAsyncCtx = ctx
11007
+ defer func() { tnAsyncCtx = saved }()
11008
+ fn()
11009
+ }
11010
+
11011
+ // fn bound to the current context (no-op when there is none to keep)
11012
+ func TnAsyncWrap(fn func()) func() {
11013
+ ctx := tnAsyncCtx
11014
+ if ctx == nil {
11015
+ return fn
11016
+ }
11017
+ return func() { TnAsyncRun(ctx, fn) }
11018
+ }
11019
+
11020
+ func tnAcquire() { <-tnBaton }
11021
+
11022
+ func tnRelease() {
11023
+ if n := len(tnLendStack); n > 0 {
11024
+ lender := tnLendStack[n-1]
11025
+ tnLendStack = tnLendStack[:n-1]
11026
+ close(lender)
11027
+ return
11028
+ }
11029
+ tnBaton <- struct{}{}
11030
+ }
11031
+
11032
+ // Runs an async function body: synchronously until its first await (or the
11033
+ // end), then the caller continues while the body waits
11034
+ func TnStartAsync(body func()) {
11035
+ yielded := make(chan struct{})
11036
+ tnLendStack = append(tnLendStack, yielded)
11037
+ ctx := tnAsyncCtx
11038
+ go func() {
11039
+ defer tnRelease()
11040
+ tnAsyncCtx = ctx
11041
+ body()
11042
+ }()
11043
+ <-yielded
11044
+ tnAsyncCtx = ctx
11045
+ }
11046
+
11047
+ func tnLoopNotify() {
11048
+ select {
11049
+ case tnLoopSignal <- struct{}{}:
11050
+ default:
11051
+ }
11052
+ }
11053
+
11054
+ func TnLoopRef() { atomic.AddInt64(&tnLoopRefs, 1) }
11055
+
11056
+ func TnLoopUnref() {
11057
+ atomic.AddInt64(&tnLoopRefs, -1)
11058
+ tnLoopNotify()
11059
+ }
11060
+
11061
+ func TnLoopPost(fn func()) {
11062
+ tnLoopMu.Lock()
11063
+ tnLoopTasks = append(tnLoopTasks, fn)
11064
+ tnLoopMu.Unlock()
11065
+ tnLoopNotify()
11066
+ }
11067
+
11068
+ // Runs a callback on the loop from a goroutine that does not hold the baton,
11069
+ // and waits for it
11070
+ func TnLoopCall(fn func()) {
11071
+ done := make(chan struct{})
11072
+ TnLoopPost(func() {
11073
+ defer close(done)
11074
+ fn()
11075
+ })
11076
+ <-done
11077
+ }
11078
+
11079
+ // (baton holder) Runs the queued tasks, each followed by its microtasks
11080
+ func TnLoopRunPending() bool {
11081
+ ran := false
11082
+ for {
11083
+ tnLoopMu.Lock()
11084
+ if len(tnLoopTasks) == 0 {
11085
+ tnLoopMu.Unlock()
11086
+ return ran
11087
+ }
11088
+ fn := tnLoopTasks[0]
11089
+ tnLoopTasks = tnLoopTasks[1:]
11090
+ tnLoopMu.Unlock()
11091
+ ran = true
11092
+ tnRunTask(fn)
11093
+ TnRunMicrotasks()
11094
+ }
11095
+ }
11096
+
11097
+ // Set by process.on('uncaughtException'): handles a throw escaping a loop task
11098
+ // (true when handled); 'beforeExit' runs when the loop drains (true when it emitted)
11099
+ var tnUncaughtHook func(err interface{}) bool
11100
+ var tnBeforeExitHook func() bool
11101
+
11102
+ func tnRunTask(fn func()) {
11103
+ if tnUncaughtHook != nil {
11104
+ defer func() {
11105
+ if r := recover(); r != nil {
11106
+ if !tnUncaughtHook(r) {
11107
+ panic(r)
11108
+ }
11109
+ }
11110
+ }()
11111
+ }
11112
+ TnAsyncRun(nil, fn)
11113
+ }
11114
+
11115
+ func tnLoopIdle() bool {
11116
+ tnLoopMu.Lock()
11117
+ defer tnLoopMu.Unlock()
11118
+ return len(tnLoopTasks) == 0 && atomic.LoadInt64(&tnLoopRefs) <= 0
11119
+ }
11120
+
11121
+ // The end of main: run until nothing is pending
11122
+ func TnRunLoop() {
11123
+ for {
11124
+ TnRunMicrotasks()
11125
+ TnLoopRunPending()
11126
+ if tnLoopIdle() {
11127
+ if tnBeforeExitHook == nil || !tnBeforeExitHook() {
11128
+ return
11129
+ }
11130
+ TnRunMicrotasks()
11131
+ TnLoopRunPending()
11132
+ if tnLoopIdle() {
11133
+ return
11134
+ }
11135
+ continue
11136
+ }
11137
+ tnRelease()
11138
+ select {
11139
+ case <-tnLoopSignal:
11140
+ case <-time.After(10 * time.Millisecond):
11141
+ }
11142
+ tnAcquire()
11143
+ }
11144
+ }
11145
+
11146
+ // (baton holder) Receives from a channel, running loop tasks while waiting
11147
+ func TnLoopReceive[T any](ch chan T) (T, bool) {
11148
+ ctx := tnAsyncCtx
11149
+ defer func() { tnAsyncCtx = ctx }()
11150
+ for {
11151
+ select {
11152
+ case v, ok := <-ch:
11153
+ return v, ok
11154
+ default:
11155
+ }
11156
+ TnRunMicrotasks()
11157
+ if TnLoopRunPending() {
11158
+ continue
11159
+ }
11160
+ tnRelease()
11161
+ select {
11162
+ case v, ok := <-ch:
11163
+ tnAcquire()
11164
+ return v, ok
11165
+ case <-tnLoopSignal:
11166
+ tnAcquire()
11167
+ }
11168
+ }
11169
+ }
11170
+
11171
+ // A wait group that keeps the loop alive (detached async calls, .then callbacks)
11172
+ type tnLoopGroup struct{}
11173
+
11174
+ func (tnLoopGroup) Add(n int) { atomic.AddInt64(&tnLoopRefs, int64(n)) }
11175
+ func (tnLoopGroup) Done() { TnLoopUnref() }
11176
+ func (tnLoopGroup) Wait() { TnRunLoop() }
11177
+
11178
+ // queueMicrotask: run after the current synchronous code (end of main or of a loop task)
11179
+ var tnMicrotasks []func()
11180
+ var tnMicrotasksMu sync.Mutex
11181
+
11182
+ func TnQueueMicrotask(fn func()) {
11183
+ fn = TnAsyncWrap(fn)
9596
11184
  tnMicrotasksMu.Lock()
9597
11185
  tnMicrotasks = append(tnMicrotasks, fn)
9598
11186
  tnMicrotasksMu.Unlock()
@@ -9608,13 +11196,7 @@ func TnRunMicrotasks() {
9608
11196
  fn := tnMicrotasks[0]
9609
11197
  tnMicrotasks = tnMicrotasks[1:]
9610
11198
  tnMicrotasksMu.Unlock()
9611
- fn()
9612
- }
9613
- }
9614
-
9615
- func TnClearTimeout(t *time.Timer) {
9616
- if t != nil && t.Stop() {
9617
- __tnTimers.Done()
11199
+ TnAsyncRun(nil, fn)
9618
11200
  }
9619
11201
  }`,
9620
11202
  uri: `// encodeURIComponent: escapes everything except A-Z a-z 0-9 - _ . ! ~ * ' ( )
@@ -9851,11 +11433,76 @@ func TnDecorateClass(result interface{}) {
9851
11433
  promise: `// Promises are channels; a rejected promise is a closed channel with a reason
9852
11434
  var tnRejections sync.Map
9853
11435
 
11436
+ // A throw inside a Promise executor rejects the promise
11437
+ func TnRejectExecutor[T any](ch chan T) {
11438
+ if r := recover(); r != nil {
11439
+ if _, rejected := tnRejections.Load(ch); !rejected && len(ch) == 0 {
11440
+ TnReject(ch, r)
11441
+ }
11442
+ }
11443
+ }
11444
+
11445
+ func TnResolveZero[T any](ch chan T) {
11446
+ var zero T
11447
+ select {
11448
+ case ch <- zero:
11449
+ default:
11450
+ }
11451
+ }
11452
+
11453
+ // resolve / reject of a Promise executor used as values
11454
+ func TnResolver[T any](ch chan T) func(...interface{}) {
11455
+ return func(args ...interface{}) {
11456
+ var v T
11457
+ if len(args) > 0 {
11458
+ if x, ok := args[0].(T); ok {
11459
+ v = x
11460
+ }
11461
+ }
11462
+ select {
11463
+ case ch <- v:
11464
+ default:
11465
+ }
11466
+ }
11467
+ }
11468
+
11469
+ func TnRejecter[T any](ch chan T) func(...interface{}) {
11470
+ return func(args ...interface{}) {
11471
+ var reason interface{}
11472
+ if len(args) > 0 {
11473
+ reason = args[0]
11474
+ }
11475
+ if _, rejected := tnRejections.Load(ch); rejected || len(ch) > 0 {
11476
+ return
11477
+ }
11478
+ TnReject(ch, reason)
11479
+ }
11480
+ }
11481
+
9854
11482
  func TnReject[T any](ch chan T, reason interface{}) {
9855
11483
  tnRejections.Store(ch, reason)
9856
11484
  close(ch)
9857
11485
  }
9858
11486
 
11487
+ // A detached call (\`main();\`): the process waits for it; a rejection is fatal
11488
+ // unless process.on('unhandledRejection') takes it
11489
+ var tnUnhandledHook func(reason interface{}) bool
11490
+
11491
+ func TnDetach[T any](ch chan T) {
11492
+ go func() {
11493
+ defer __tnWaitGroup.Done()
11494
+ _, reason, rejected := tnSettle(ch)
11495
+ if rejected {
11496
+ TnLoopCall(func() {
11497
+ if tnUnhandledHook != nil && tnUnhandledHook(reason) {
11498
+ return
11499
+ }
11500
+ panic(reason)
11501
+ })
11502
+ }
11503
+ }()
11504
+ }
11505
+
9859
11506
  // Deferred in async functions: a throw rejects the function's promise
9860
11507
  func TnRejectOnPanic[T any](ch chan T) {
9861
11508
  if r := recover(); r != nil {
@@ -9863,9 +11510,7 @@ func TnRejectOnPanic[T any](ch chan T) {
9863
11510
  }
9864
11511
  }
9865
11512
 
9866
- // Waits for a promise: its value, or its rejection reason
9867
- func tnSettle[T any](ch chan T) (T, interface{}, bool) {
9868
- v, ok := <-ch
11513
+ func tnSettled[T any](ch chan T, v T, ok bool) (T, interface{}, bool) {
9869
11514
  if !ok {
9870
11515
  if reason, rejected := tnRejections.Load(ch); rejected {
9871
11516
  return v, reason, true
@@ -9874,9 +11519,16 @@ func tnSettle[T any](ch chan T) (T, interface{}, bool) {
9874
11519
  return v, nil, false
9875
11520
  }
9876
11521
 
9877
- // await: the value, or the rejection rethrown
11522
+ // Waits for a promise (helper goroutines, without the baton): its value, or its rejection reason
11523
+ func tnSettle[T any](ch chan T) (T, interface{}, bool) {
11524
+ v, ok := <-ch
11525
+ return tnSettled(ch, v, ok)
11526
+ }
11527
+
11528
+ // await (baton holder): the value, or the rejection rethrown; loop tasks run meanwhile
9878
11529
  func TnAwait[T any](ch chan T) T {
9879
- v, reason, rejected := tnSettle(ch)
11530
+ received, ok := TnLoopReceive(ch)
11531
+ v, reason, rejected := tnSettled(ch, received, ok)
9880
11532
  if rejected {
9881
11533
  panic(reason)
9882
11534
  }
@@ -9891,11 +11543,12 @@ func TnPromiseReject[T any](reason interface{}) chan T {
9891
11543
 
9892
11544
  func TnPromiseCatch[T any, E any](p chan T, onRejected func(E)) chan T {
9893
11545
  out := make(chan T, 1)
11546
+ ctx := TnAsyncSnapshot()
9894
11547
  go func() {
9895
11548
  v, reason, rejected := tnSettle(p)
9896
11549
  if rejected {
9897
11550
  e, _ := reason.(E)
9898
- onRejected(e)
11551
+ TnLoopCall(func() { TnAsyncRun(ctx, func() { onRejected(e) }) })
9899
11552
  }
9900
11553
  out <- v
9901
11554
  close(out)
@@ -9905,11 +11558,12 @@ func TnPromiseCatch[T any, E any](p chan T, onRejected func(E)) chan T {
9905
11558
 
9906
11559
  func TnPromiseCatchValue[T any, E any](p chan T, onRejected func(E) T) chan T {
9907
11560
  out := make(chan T, 1)
11561
+ ctx := TnAsyncSnapshot()
9908
11562
  go func() {
9909
11563
  v, reason, rejected := tnSettle(p)
9910
11564
  if rejected {
9911
11565
  e, _ := reason.(E)
9912
- v = onRejected(e)
11566
+ TnLoopCall(func() { TnAsyncRun(ctx, func() { v = onRejected(e) }) })
9913
11567
  }
9914
11568
  out <- v
9915
11569
  close(out)
@@ -9919,9 +11573,10 @@ func TnPromiseCatchValue[T any, E any](p chan T, onRejected func(E) T) chan T {
9919
11573
 
9920
11574
  func TnPromiseFinally[T any](p chan T, onFinally func()) chan T {
9921
11575
  out := make(chan T, 1)
11576
+ onFinally = TnAsyncWrap(onFinally)
9922
11577
  go func() {
9923
11578
  v, reason, rejected := tnSettle(p)
9924
- onFinally()
11579
+ TnLoopCall(onFinally)
9925
11580
  if rejected {
9926
11581
  TnReject(out, reason)
9927
11582
  return
@@ -10022,170 +11677,585 @@ func TnPromiseAll[T any](chs []chan T) chan []T {
10022
11677
  close(out)
10023
11678
  }()
10024
11679
  return out
10025
- }`,
10026
- exec: `type tnExecResult struct {
10027
- stdout string
10028
- stderr string
10029
- status float64
10030
- }
10031
-
10032
- func TnExec(name string, args ...string) tnExecResult {
10033
- cmd := exec.Command(name, args...)
10034
- var stdout, stderr bytes.Buffer
10035
- cmd.Stdout = &stdout
10036
- cmd.Stderr = &stderr
10037
- err := cmd.Run()
10038
- status := 0.0
10039
- if err != nil {
10040
- if exitErr, ok := err.(*exec.ExitError); ok {
10041
- status = float64(exitErr.ExitCode())
10042
- } else {
10043
- panic(err)
10044
- }
10045
- }
10046
- return tnExecResult{stdout: stdout.String(), stderr: stderr.String(), status: status}
10047
- }
10048
-
10049
- func TnExecShell(command string) tnExecResult {
10050
- if runtime.GOOS == "windows" {
10051
- return TnExec("cmd", "/C", command)
10052
- }
10053
- return TnExec("sh", "-c", command)
10054
- }`,
10055
- execSync: `func TnExecShellSync(command string) string {
10056
- result := TnExecShell(command)
10057
- if result.status != 0 {
10058
- panic(result.stderr)
10059
- }
10060
- return result.stdout
10061
11680
  }`,
10062
11681
  getenv: `func TnGetenv(name string) string {
10063
11682
  return os.Getenv(name)
10064
11683
  }`,
10065
- execInput: `func TnExecInput(name string, input string, args ...string) tnExecResult {
10066
- cmd := exec.Command(name, args...)
10067
- cmd.Stdin = strings.NewReader(input)
10068
- var stdout, stderr bytes.Buffer
10069
- cmd.Stdout = &stdout
10070
- cmd.Stderr = &stderr
10071
- err := cmd.Run()
10072
- status := 0.0
10073
- if err != nil {
10074
- if exitErr, ok := err.(*exec.ExitError); ok {
10075
- status = float64(exitErr.ExitCode())
10076
- } else {
10077
- panic(err)
10078
- }
10079
- }
10080
- return tnExecResult{stdout: stdout.String(), stderr: stderr.String(), status: status}
10081
- }`,
10082
- runInherit: `func TnRunInherit(name string, args ...string) tnExecResult {
10083
- cmd := exec.Command(name, args...)
10084
- cmd.Stdin = os.Stdin
10085
- cmd.Stdout = os.Stdout
10086
- cmd.Stderr = os.Stderr
10087
- err := cmd.Run()
10088
- if err != nil {
10089
- if exitErr, ok := err.(*exec.ExitError); ok {
10090
- return tnExecResult{status: float64(exitErr.ExitCode())}
10091
- }
10092
- panic(err)
10093
- }
10094
- return tnExecResult{}
10095
- }`,
10096
- question: `var tnStdinReader = bufio.NewReader(os.Stdin)
10097
-
10098
- func TnQuestion(prompt string) string {
10099
- if prompt != "" {
10100
- fmt.Print(prompt)
10101
- }
10102
- // Reuse one buffered reader: a fresh reader per call would discard
10103
- // buffered stdin and hit EOF on the second question.
10104
- line, err := tnStdinReader.ReadString('\\n')
10105
- if err != nil && line == "" {
10106
- panic(err)
10107
- }
10108
- return strings.TrimRight(line, "\\r\\n")
10109
- }`
10110
11684
  };
10111
11685
  // Returns the Go source for all helpers used in this transpilation, or '' if none.
10112
11686
  // Emitted only into the main output file: every generated file shares `package main`,
10113
11687
  // so helpers are visible across files and must not be duplicated.
10114
11688
  function emitGoHelpers() {
10115
- return [...usedHelpers]
11689
+ const sources = [...usedHelpers]
10116
11690
  .sort()
10117
- .map((id) => goHelpers[id])
10118
- .filter(Boolean)
10119
- .join('\n\n');
10120
- }
10121
- // Result types of the mapped Node.js stdlib functions ('' = no result)
10122
- const NODE_FUNCTION_TYPES = {
10123
- path: {
10124
- join: 'string',
10125
- resolve: 'string',
10126
- dirname: 'string',
10127
- basename: 'string',
10128
- extname: 'string',
10129
- relative: 'string',
10130
- normalize: 'string'
10131
- },
10132
- fs: {
10133
- readFileSync: 'string',
10134
- existsSync: 'bool',
10135
- readdirSync: '[]string',
10136
- writeFileSync: '',
10137
- appendFileSync: '',
10138
- mkdirSync: '',
10139
- copyFileSync: '',
10140
- rmSync: '',
10141
- unlinkSync: ''
10142
- },
10143
- os: { platform: 'string', homedir: 'string', tmpdir: 'string' },
10144
- url: { fileURLToPath: 'string', pathToFileURL: 'string' },
10145
- child_process: { execSync: 'string' },
10146
- readline: { question: 'string' }
10147
- };
10148
- // Result types of imported Node.js functions by their local callee text
10149
- // (`join`, `path.join`)
10150
- const nodeCallResultTypes = new Map();
10151
- // Mapping from Node.js stdlib module names to Go setup functions.
10152
- // Each entry adds the required Go imports and registers call handlers for the local identifier.
11691
+ .map((id) => goHelpers[id] ?? nodeRegistry.goHelpers[id])
11692
+ .filter(Boolean);
11693
+ if (usedHelpers.has('nodeUtil')) {
11694
+ const names = [...classNames].sort().map((name) => `${toGoStringLiteral(getSafeName(name))}: true`);
11695
+ sources.push(`// Classes util.inspect prints as \`Name { … }\`\nvar tnInspectClassNames = map[string]bool{${names.join(', ')}}`);
11696
+ }
11697
+ return sources.join('\n\n');
11698
+ }
11699
+ /// Local names bound by Node.js imports → registry keys ('join' → 'path.join',
11700
+ // 'fs' → 'fs', 'fsp' → 'fs/promises', 'EventEmitter' → 'events.EventEmitter')
11701
+ const nodeLocalNames = new Map();
11702
+ // The registry name of a Node module specifier ('node:fs', 'fs', 'node:sys' → 'util'),
11703
+ // or '' when it is not a (supported) builtin
11704
+ function nodeModuleName(spec) {
11705
+ const prefixed = spec.startsWith('node:');
11706
+ let name = prefixed ? spec.slice(5) : spec;
11707
+ if (nodeRegistry.aliases[name])
11708
+ name = nodeRegistry.aliases[name];
11709
+ if (nodeRegistry.modules[name])
11710
+ return name;
11711
+ return '';
11712
+ }
11713
+ // Binds the import's local names to registry keys
10153
11714
  function setupNodeModuleImport(node, nodeModule) {
10154
- const mapping = nodeModuleMappings[nodeModule];
10155
- if (!mapping)
10156
- return;
10157
11715
  const clause = node.importClause;
10158
11716
  if (!clause)
10159
11717
  return;
10160
- if (clause.namedBindings && isNamedImports(clause.namedBindings)) {
10161
- // Named imports: `import { join, dirname } from 'node:path'`
10162
- // Map each local name directly to its Go qualified function via importAliases,
10163
- // so bare calls like `join(...)` resolve to `filepath.Join(...)`.
10164
- for (const el of clause.namedBindings.elements) {
10165
- if (el.isTypeOnly)
10166
- continue;
10167
- const localName = el.name.text;
10168
- const importedName = el.propertyName?.text ?? localName;
10169
- const resultType = NODE_FUNCTION_TYPES[nodeModule]?.[importedName];
10170
- if (resultType !== undefined)
10171
- nodeCallResultTypes.set(localName, resultType);
10172
- const fn = mapping.functions[importedName];
10173
- if (fn) {
10174
- // Register a call handler keyed on the local name
10175
- callHandlers[localName] = (_caller, args) => fn(args);
11718
+ const defaultKey = nodeRegistry.namespaces[`${nodeModule}.default`] || nodeModule;
11719
+ if (clause.name)
11720
+ nodeLocalNames.set(clause.name.text, defaultKey);
11721
+ const bindings = clause.namedBindings;
11722
+ if (!bindings)
11723
+ return;
11724
+ if (isNamespaceImport(bindings)) {
11725
+ nodeLocalNames.set(bindings.name.text, nodeModule);
11726
+ return;
11727
+ }
11728
+ for (const el of bindings.elements ?? []) {
11729
+ const importedName = el.propertyName?.text ?? el.name.text;
11730
+ if (importedName === 'default') {
11731
+ nodeLocalNames.set(el.name.text, defaultKey);
11732
+ continue;
11733
+ }
11734
+ const key = `${nodeModule}.${importedName}`;
11735
+ nodeLocalNames.set(el.name.text, nodeRegistry.namespaces[key] || key);
11736
+ }
11737
+ }
11738
+ // A Node global (Buffer, URL, process…) not shadowed by a user declaration
11739
+ function nodeGlobalKey(name) {
11740
+ const key = nodeRegistry.globals[name];
11741
+ if (!key || typeof key !== 'string')
11742
+ return '';
11743
+ if (variableGoTypes.has(name) || classNames.has(name) || declaredFunctions.has(name) || importAliases.has(name))
11744
+ return '';
11745
+ return key;
11746
+ }
11747
+ // Registry key of a Node binding expression (`join`, `fs.promises.readFile`,
11748
+ // `Buffer.from`, `(childProcess as any).spawnSync`), or '' when it is not one
11749
+ function resolveNodeKey(expr) {
11750
+ if (!expr)
11751
+ return '';
11752
+ if (isIdentifier(expr)) {
11753
+ if (nodeLocalNames.has(expr.text))
11754
+ return nodeLocalNames.get(expr.text) ?? '';
11755
+ return nodeGlobalKey(expr.text);
11756
+ }
11757
+ if (isParenthesizedExpression(expr) || isAsExpression(expr) || isNonNullExpression(expr)) {
11758
+ return resolveNodeKey(expr.expression);
11759
+ }
11760
+ // util.promisify(fs.readFile) → fs/promises.readFile
11761
+ if (isCallExpression(expr) && resolveNodeKey(expr.expression) === 'util.promisify') {
11762
+ return nodePromisifiedKey((expr.arguments ?? [])[0]);
11763
+ }
11764
+ if (isPropertyAccessExpression(expr) && !expr.questionDotToken) {
11765
+ const base = resolveNodeKey(expr.expression);
11766
+ if (!base)
11767
+ return '';
11768
+ const key = `${base}.${expr.name.text}`;
11769
+ return nodeRegistry.namespaces[key] || key;
11770
+ }
11771
+ return '';
11772
+ }
11773
+ // The promise variant of a Node function, '' when it has none
11774
+ function nodePromisifiedKey(fnExpr) {
11775
+ let key = resolveNodeKey(fnExpr);
11776
+ if (!key && isIdentifier(fnExpr) && (fnExpr.text === 'setTimeout' || fnExpr.text === 'setImmediate')) {
11777
+ key = `timers.${fnExpr.text}`;
11778
+ }
11779
+ if (!key)
11780
+ return '';
11781
+ if (nodeRegistry.functions[`${key}.__promisify__`])
11782
+ return `${key}.__promisify__`;
11783
+ const dot = key.lastIndexOf('.');
11784
+ const mod = key.slice(0, dot);
11785
+ const name = key.slice(dot + 1);
11786
+ const promisesKey = `${mod}/promises.${name}`;
11787
+ if (nodeRegistry.functions[promisesKey])
11788
+ return promisesKey;
11789
+ return '';
11790
+ }
11791
+ // The Node class of a Go type ('*TnEventEmitter', '[]byte'), '' when none
11792
+ function nodeClassKeyOfType(goType) {
11793
+ if (!goType)
11794
+ return '';
11795
+ return nodeRegistry.classByGoType[goType] ?? '';
11796
+ }
11797
+ // A user class extending a Node class (class X extends Transform): the Node
11798
+ // class key, for methods the user classes in the chain do not declare
11799
+ function nodeAncestorClassKey(goType, methodName) {
11800
+ if (!goType || !goType.startsWith('*'))
11801
+ return '';
11802
+ let name = goType.slice(1).replace(/\[.*\]$/, '');
11803
+ for (let depth = 0; depth < 20 && classParentNames.has(name); depth++) {
11804
+ if (declaredClassHasMethod(name, methodName))
11805
+ return '';
11806
+ const parent = classParentNames.get(name) ?? '';
11807
+ if (nodeParentClassKeys.has(parent))
11808
+ return nodeParentClassKeys.get(parent) ?? '';
11809
+ name = parent;
11810
+ }
11811
+ return '';
11812
+ }
11813
+ function declaredClassHasMethod(className, methodName) {
11814
+ const decl = declaredClasses.get(className);
11815
+ for (const member of decl?.members ?? []) {
11816
+ if (member.name && isIdentifier(member.name) && member.name.text === methodName)
11817
+ return true;
11818
+ }
11819
+ return false;
11820
+ }
11821
+ // The Node class key a type name refers to (imported or global), '' when none
11822
+ function nodeClassKeyOfName(name) {
11823
+ const key = nodeLocalNames.has(name) ? (nodeLocalNames.get(name) ?? '') : nodeGlobalKey(name);
11824
+ return key && nodeRegistry.classes[key] ? key : '';
11825
+ }
11826
+ // new Error(message, { cause }): the options set error.cause
11827
+ function withErrorOptions(errorExpr, options) {
11828
+ if (!options)
11829
+ return errorExpr;
11830
+ useHelper('dynamic');
11831
+ return `TnErrorWithOptions(${errorExpr}, ${visit(options)})`;
11832
+ }
11833
+ // The Node class key of a (qualified) type reference: `Stats`, `fs.Stats`, `NodeJS.ErrnoException`
11834
+ function nodeClassKeyOfTypeName(typeName) {
11835
+ if (isIdentifier(typeName))
11836
+ return nodeClassKeyOfName(typeName.text);
11837
+ if (typeName?.kind === 'QualifiedName') {
11838
+ const base = nodeQualifiedTypeBase(typeName.left);
11839
+ if (!base)
11840
+ return '';
11841
+ const key = `${base}.${typeName.right.text}`;
11842
+ return nodeRegistry.classes[key] ? key : '';
11843
+ }
11844
+ return '';
11845
+ }
11846
+ function nodeQualifiedTypeBase(left) {
11847
+ if (isIdentifier(left))
11848
+ return nodeLocalNames.has(left.text) ? (nodeLocalNames.get(left.text) ?? '') : nodeGlobalKey(left.text);
11849
+ if (left?.kind === 'QualifiedName') {
11850
+ const base = nodeQualifiedTypeBase(left.left);
11851
+ return base ? `${base}.${left.right.text}` : '';
11852
+ }
11853
+ return '';
11854
+ }
11855
+ // Option-object methods of Node constructors: their parameters get Node's types
11856
+ function applyNodeOptionTypes(key, options) {
11857
+ const typer = nodeRegistry.optionTypers[key];
11858
+ if (!typer || !isObjectLiteralExpression(options))
11859
+ return;
11860
+ const types = typer(options);
11861
+ for (const prop of options.properties ?? []) {
11862
+ if (!prop.name || !isIdentifier(prop.name))
11863
+ continue;
11864
+ const name = prop.name.text;
11865
+ if (!(name in types))
11866
+ continue;
11867
+ if (prop.kind === 'MethodDeclaration')
11868
+ prop.tnParamTypes = types[name];
11869
+ else if (isPropertyAssignment(prop) && (isArrowFunction(prop.initializer) || isFunctionExpression(prop.initializer))) {
11870
+ prop.initializer.tnParamTypes = types[name];
11871
+ }
11872
+ }
11873
+ }
11874
+ // Applies the registry's callback parameter types to the callback argument
11875
+ function applyNodeParamTypes(key, args) {
11876
+ const typer = nodeRegistry.paramTypers[key];
11877
+ if (!typer)
11878
+ return;
11879
+ for (let i = args.length - 1; i >= 0; i--) {
11880
+ if (isArrowFunction(args[i]) || isFunctionExpression(args[i])) {
11881
+ args[i].tnParamTypes = typer(args);
11882
+ return;
11883
+ }
11884
+ }
11885
+ }
11886
+ function makeNodeCall(node, recv) {
11887
+ const nodes = node.arguments ?? [];
11888
+ const hasSpread = nodes.some((a) => isSpreadElement(a));
11889
+ const args = hasSpread ? visitSpreadCallArguments(node) : nodes.map((a) => visit(a));
11890
+ const types = nodes.map((a) => inferExpressionType(a) ?? '');
11891
+ return { args, nodes, types, recv };
11892
+ }
11893
+ // Calls of Node APIs: imported functions, statics (Buffer.from) and methods of
11894
+ // Node class instances
11895
+ function visitNodeCall(node) {
11896
+ const callee = node.expression;
11897
+ const key = resolveNodeKey(callee);
11898
+ if (key) {
11899
+ const emit = nodeRegistry.functions[key];
11900
+ if (emit) {
11901
+ applyNodeParamTypes(key, node.arguments ?? []);
11902
+ return emit(makeNodeCall(node, ''));
11903
+ }
11904
+ // node:console re-exports the global console
11905
+ if (key.startsWith('console.') && callHandlers[key]) {
11906
+ const call = makeNodeCall(node, '');
11907
+ currentCallArgTypes = call.types.map((t) => t || undefined);
11908
+ const consoleCall = callHandlers[key](key, call.args, '');
11909
+ return consoleCall;
11910
+ }
11911
+ return undefined;
11912
+ }
11913
+ if (!isPropertyAccessExpression(callee) || callee.questionDotToken)
11914
+ return undefined;
11915
+ const receiverType = inferExpressionType(callee.expression);
11916
+ const classKey = nodeClassKeyOfType(receiverType) || nodeAncestorClassKey(receiverType, callee.name.text);
11917
+ if (!classKey)
11918
+ return undefined;
11919
+ const cls = nodeRegistry.classes[classKey];
11920
+ const methodKey = `${cls.key}#${callee.name.text}`;
11921
+ const emit = nodeRegistry.methods[methodKey];
11922
+ if (!emit && cls.category !== 'class')
11923
+ return undefined;
11924
+ applyNodeParamTypes(methodKey, node.arguments ?? []);
11925
+ const call = makeNodeCall(node, visit(callee.expression));
11926
+ if (emit)
11927
+ return emit(call);
11928
+ return `${call.recv}.${callee.name.text}(${call.args.join(', ')})`;
11929
+ }
11930
+ // Whether a registry key names a module or namespace object (path, fs/promises, process.stdout)
11931
+ function isNodeNamespaceKey(key) {
11932
+ return !!nodeRegistry.modules[key] || !!nodeRegistry.objects[key];
11933
+ }
11934
+ // `const win = path.win32` / `const { join, sep } = path` bind local names to
11935
+ // registry keys instead of declaring Go variables
11936
+ function visitNodeAliasDeclaration(node) {
11937
+ if (!node.initializer)
11938
+ return undefined;
11939
+ const key = resolveNodeKey(node.initializer);
11940
+ // const readFileAsync = promisify(fs.readFile): an alias of the promise variant
11941
+ if (key && isIdentifier(node.name) && isCallExpression(unwrapParentheses(node.initializer)) && nodeRegistry.functions[key]) {
11942
+ nodeLocalNames.set(node.name.text, key);
11943
+ return '';
11944
+ }
11945
+ if (!key || !isNodeNamespaceKey(key))
11946
+ return undefined;
11947
+ if (isIdentifier(node.name)) {
11948
+ nodeLocalNames.set(node.name.text, key);
11949
+ return '';
11950
+ }
11951
+ if (isObjectBindingPattern(node.name)) {
11952
+ for (const el of node.name.elements ?? []) {
11953
+ if (el.dotDotDotToken || el.initializer || !isIdentifier(el.name))
11954
+ return undefined;
11955
+ }
11956
+ for (const el of node.name.elements ?? []) {
11957
+ const member = (el.propertyName ?? el.name).text;
11958
+ const memberKey = `${key}.${member}`;
11959
+ nodeLocalNames.set(el.name.text, nodeRegistry.namespaces[memberKey] || memberKey);
11960
+ }
11961
+ return '';
11962
+ }
11963
+ return undefined;
11964
+ }
11965
+ // Reads of Node values (`path.sep`, `os.EOL`, an imported `sep`)
11966
+ function visitNodeValue(expr) {
11967
+ const key = resolveNodeKey(expr);
11968
+ if (!key)
11969
+ return undefined;
11970
+ if (key === 'process.env' && (isPropertyAccessExpression(expr.parent) || isElementAccessExpression(expr.parent)) && expr.parent.expression === expr) {
11971
+ return undefined;
11972
+ }
11973
+ const emit = nodeRegistry.values[key];
11974
+ if (!emit)
11975
+ return undefined;
11976
+ const value = emit(emptyNodeCall());
11977
+ return value;
11978
+ }
11979
+ function emptyNodeCall() {
11980
+ return { args: [], nodes: [], types: [], recv: '' };
11981
+ }
11982
+ // Writes of Node values (`process.exitCode = 1`): registered as 'key='
11983
+ function visitNodeAssignment(node) {
11984
+ if (node.operatorToken.kind !== 'EqualsToken')
11985
+ return undefined;
11986
+ // process.env.X = v / process.env[k] = v
11987
+ if ((isPropertyAccessExpression(node.left) || isElementAccessExpression(node.left)) && isProcessEnv(node.left.expression)) {
11988
+ importedPackages.add('os');
11989
+ const name = isPropertyAccessExpression(node.left) ? toGoStringLiteral(node.left.name.text) : visit(node.left.argumentExpression);
11990
+ return `os.Setenv(${name}, ${jsStringOf(visit(node.right), inferExpressionType(node.right))})`;
11991
+ }
11992
+ const key = resolveNodeKey(node.left);
11993
+ if (!key)
11994
+ return undefined;
11995
+ const emit = nodeRegistry.functions[`${key}=`];
11996
+ if (!emit)
11997
+ return undefined;
11998
+ const call = { args: [visit(node.right)], nodes: [node.right], types: [inferExpressionType(node.right) ?? ''], recv: '' };
11999
+ const assignment = emit(call);
12000
+ return assignment;
12001
+ }
12002
+ // Writes of Go struct fields convert to the field type: h.Size = 5 →
12003
+ // h.Size = int64(5); h.Size += 1 → h.Size = int64(float64(h.Size) + 1)
12004
+ function visitGoFieldAssignment(node, op) {
12005
+ if (!isPropertyAccessExpression(node.left))
12006
+ return undefined;
12007
+ const recvType = inferExpressionType(node.left.expression);
12008
+ if (!recvType)
12009
+ return undefined;
12010
+ const field = goFieldType(recvType, node.left.name.text);
12011
+ if (!field)
12012
+ return undefined;
12013
+ const target = `${visit(node.left.expression)}.${node.left.name.text}`;
12014
+ ensureGoTypePackages(field);
12015
+ if (op === '=') {
12016
+ return `${target} = ${toGoType(visit(node.right), inferExpressionType(node.right) ?? '', field)}`;
12017
+ }
12018
+ if (['+=', '-=', '*=', '/='].includes(op) && isGoNumericKind(field)) {
12019
+ const value = `${fromGoType(target, field)} ${op[0]} (${visit(node.right)})`;
12020
+ return `${target} = ${toGoType(value, 'float64', field)}`;
12021
+ }
12022
+ return undefined;
12023
+ }
12024
+ // new X(…) of a Node class
12025
+ function visitNodeNew(node) {
12026
+ const key = resolveNodeKey(node.expression);
12027
+ if (!key)
12028
+ return undefined;
12029
+ const emit = nodeRegistry.constructors[key];
12030
+ if (!emit)
12031
+ return undefined;
12032
+ applyNodeParamTypes(`${key}()`, node.arguments ?? []);
12033
+ applyNodeOptionTypes(key, (node.arguments ?? [])[0]);
12034
+ return emit(makeNodeCall(node, ''));
12035
+ }
12036
+ // The call a typer inspects: argument nodes and their Go types
12037
+ function nodeTypeCall(nodes) {
12038
+ return { args: [], nodes, types: nodes.map((a) => inferExpressionType(a) ?? ''), recv: '' };
12039
+ }
12040
+ // Go types of Node API expressions; undefined when the expression is not one
12041
+ // Property read on a Go package or Go value: consts, vars, fields.
12042
+ function visitGoProperty(node) {
12043
+ const base = node.expression;
12044
+ // Package member: strings.ToUpper (as a value), math.Pi, os.Stdout.
12045
+ const path = isIdentifier(base) ? goPathOf(base.text) : undefined;
12046
+ if (path) {
12047
+ const info = goPackage(path);
12048
+ if (!info)
12049
+ return undefined;
12050
+ const member = node.name.text;
12051
+ const qualifier = goQualifier(path);
12052
+ if (info.funcs[member])
12053
+ return `${qualifier}.${member}`;
12054
+ if (info.consts[member]) {
12055
+ const kind = info.consts[member].kind;
12056
+ // Untyped consts arrive as TS numbers; typed consts (time.Second)
12057
+ // keep their Go type so arithmetic stays well-typed.
12058
+ if (kind === 'typed')
12059
+ return `${qualifier}.${member}`;
12060
+ if (kind === 'string')
12061
+ return `${qualifier}.${member}`;
12062
+ if (kind === 'bool')
12063
+ return `${qualifier}.${member}`;
12064
+ return `float64(${qualifier}.${member})`;
12065
+ }
12066
+ if (info.vars[member]) {
12067
+ return `${qualifier}.${member}`;
12068
+ }
12069
+ if (info.types[member])
12070
+ return `${qualifier}.${member}`;
12071
+ return undefined;
12072
+ }
12073
+ // Field on a Go-typed value.
12074
+ if (isCallee(node) || isAssignmentTarget(node))
12075
+ return undefined;
12076
+ const recvType = inferExpressionType(base);
12077
+ if (!recvType)
12078
+ return undefined;
12079
+ const field = goFieldType(recvType, node.name.text);
12080
+ if (field) {
12081
+ const recv = visit(base);
12082
+ return fromGoType(`${recv}.${node.name.text}`, field);
12083
+ }
12084
+ // GoRef<T>.value dereferences the pointer cell (structs with a real
12085
+ // `value` field take the branch above). Only when the receiver is
12086
+ // declared as GoRef<...> or initialized by ref<T>(), or points to a
12087
+ // basic type (flag.String's *string has no fields to read).
12088
+ if (node.name.text === 'value' && recvType.startsWith('*') && isIdentifier(base)) {
12089
+ const base2 = recvType.slice(1);
12090
+ const isBasic = base2 === 'string' || base2 === 'float64' || base2 === 'bool' || isGoBasicKind(base2);
12091
+ if (isBasic || goRefVariables.has(base.text)) {
12092
+ return `(*${visit(base)})`;
12093
+ }
12094
+ const typeNode = variableTypeNodes.get(base.text);
12095
+ if (typeNode &&
12096
+ isTypeReferenceNode(typeNode) &&
12097
+ isIdentifier(typeNode.typeName) &&
12098
+ typeNode.typeName.text === 'GoRef') {
12099
+ return `(*${visit(base)})`;
12100
+ }
12101
+ }
12102
+ return undefined;
12103
+ }
12104
+ // Channel method call: ch.send(v) → ch <- v, ch.recv() → <-ch,
12105
+ // ch.close() → close(ch).
12106
+ function visitGoChanCall(node, recv) {
12107
+ if (!isPropertyAccessExpression(node.expression))
12108
+ return undefined;
12109
+ const method = node.expression.name.text;
12110
+ const args = node.arguments ?? [];
12111
+ if (method === 'send' && args.length === 1) {
12112
+ return `${recv} <- ${visit(args[0])}`;
12113
+ }
12114
+ if (method === 'recv' && args.length === 0) {
12115
+ return `<-${recv}`;
12116
+ }
12117
+ if (method === 'close' && args.length === 0) {
12118
+ return `close(${recv})`;
12119
+ }
12120
+ return undefined;
12121
+ }
12122
+ // Package + type of a `new pkg.T(...)` expression, null when none.
12123
+ function goNewTarget(node) {
12124
+ const ctor = node.expression;
12125
+ if (!ctor)
12126
+ return null;
12127
+ if (isIdentifier(ctor)) {
12128
+ const qualified = goMemberOf(ctor.text);
12129
+ if (!qualified)
12130
+ return null;
12131
+ const dot = qualified.indexOf('.');
12132
+ if (dot < 0)
12133
+ return null;
12134
+ const qualifier = qualified.slice(0, dot);
12135
+ const typeName = qualified.slice(dot + 1);
12136
+ for (const path of Object.keys(goStdlib)) {
12137
+ if (goQualifier(path) === qualifier && goPackage(path)?.types[typeName]) {
12138
+ return { path, typeName, qualifier };
10176
12139
  }
10177
12140
  }
12141
+ return null;
10178
12142
  }
10179
- else {
10180
- // Default import (`import path from 'node:path'`) or
10181
- // namespace import (`import * as path from 'node:path'`)
10182
- const localName = getImportLocalName(node) ?? nodeModule;
10183
- for (const [funcName, resultType] of Object.entries(NODE_FUNCTION_TYPES[nodeModule] ?? {})) {
10184
- nodeCallResultTypes.set(`${localName}.${funcName}`, resultType);
12143
+ if (isPropertyAccessExpression(ctor) && isIdentifier(ctor.expression)) {
12144
+ const path = goPathOf(ctor.expression.text);
12145
+ if (!path)
12146
+ return null;
12147
+ const typeName = ctor.name.text;
12148
+ if (!goPackage(path)?.types[typeName])
12149
+ return null;
12150
+ return { path, typeName, qualifier: goQualifier(path) };
12151
+ }
12152
+ return null;
12153
+ }
12154
+ function inferGoExpressionType(expr) {
12155
+ // ref<T>().value reads as the cell's element type.
12156
+ if (isPropertyAccessExpression(expr) &&
12157
+ expr.name.text === 'value' &&
12158
+ isIdentifier(expr.expression) &&
12159
+ goRefVariables.has(expr.expression.text)) {
12160
+ const refType = variableGoTypes.get(expr.expression.text);
12161
+ if (refType && refType.startsWith('*'))
12162
+ return refType.slice(1);
12163
+ }
12164
+ if (isCallExpression(expr) || isIdentifier(expr) || isPropertyAccessExpression(expr)) {
12165
+ return inferGoType(expr);
12166
+ }
12167
+ if (isNewExpression(expr)) {
12168
+ const target = goNewTarget(expr);
12169
+ if (target) {
12170
+ const info = goPackage(target.path);
12171
+ const type = info?.types[target.typeName];
12172
+ if (type) {
12173
+ for (const m of type.methods) {
12174
+ if (m.ptr)
12175
+ return `*${target.path}.${target.typeName}`;
12176
+ }
12177
+ return `${target.path}.${target.typeName}`;
12178
+ }
10185
12179
  }
10186
- for (const [funcName, fn] of Object.entries(mapping.functions)) {
10187
- callHandlers[`${localName}.${funcName}`] = (_caller, args) => fn(args);
12180
+ return undefined;
12181
+ }
12182
+ return undefined;
12183
+ }
12184
+ function inferNodeExpressionType(expr) {
12185
+ if (isCallExpression(expr)) {
12186
+ const callee = expr.expression;
12187
+ const key = resolveNodeKey(callee);
12188
+ const nodes = expr.arguments ?? [];
12189
+ if (key) {
12190
+ const typer = nodeRegistry.typers[key];
12191
+ if (typer)
12192
+ return typer(nodeTypeCall(nodes));
12193
+ if (key in nodeRegistry.results)
12194
+ return nodeRegistry.results[key];
12195
+ return undefined;
12196
+ }
12197
+ if (isPropertyAccessExpression(callee) && !callee.questionDotToken) {
12198
+ const receiverType = inferExpressionType(callee.expression);
12199
+ const classKey = nodeClassKeyOfType(receiverType) || nodeAncestorClassKey(receiverType, callee.name.text);
12200
+ if (!classKey)
12201
+ return undefined;
12202
+ const methodKey = `${nodeRegistry.classes[classKey].key}#${callee.name.text}`;
12203
+ const typer = nodeRegistry.typers[methodKey];
12204
+ if (typer)
12205
+ return typer(nodeTypeCall(nodes));
12206
+ if (methodKey in nodeRegistry.results)
12207
+ return nodeRegistry.results[methodKey];
12208
+ const cls = nodeRegistry.classes[classKey];
12209
+ const methodName = callee.name.text;
12210
+ if (methodName in cls.methods)
12211
+ return cls.methods[methodName];
12212
+ }
12213
+ return undefined;
12214
+ }
12215
+ if (isNewExpression(expr)) {
12216
+ const key = resolveNodeKey(expr.expression);
12217
+ if (key && nodeRegistry.constructors[key]) {
12218
+ const typer = nodeRegistry.typers[`${key}()`];
12219
+ const nodes = expr.arguments ?? [];
12220
+ if (typer)
12221
+ return typer(nodeTypeCall(nodes));
12222
+ return nodeRegistry.classes[key]?.goType;
10188
12223
  }
12224
+ return undefined;
12225
+ }
12226
+ if (isIdentifier(expr) || isPropertyAccessExpression(expr)) {
12227
+ const key = resolveNodeKey(expr);
12228
+ if (key && key in nodeRegistry.valueTypes)
12229
+ return nodeRegistry.valueTypes[key];
12230
+ return undefined;
12231
+ }
12232
+ if (isElementAccessExpression(expr) && inferExpressionType(expr.expression) === '[]byte')
12233
+ return 'float64';
12234
+ return undefined;
12235
+ }
12236
+ // Node classes are pseudo-classes: their methods, fields and accessors feed the
12237
+ // class tables, so dispatch and inference treat them like user classes
12238
+ function seedNodeClassTables() {
12239
+ for (const key of Object.keys(nodeRegistry.classes)) {
12240
+ const cls = nodeRegistry.classes[key];
12241
+ if (!cls.goType.startsWith('*'))
12242
+ continue;
12243
+ const goName = cls.goType.slice(1);
12244
+ // Classes sharing a Go type (ErrnoException / AssertionError on *TnError) merge their tables
12245
+ const methods = classMethodReturnTypes.get(goName) ?? new Map();
12246
+ for (const name of Object.keys(cls.methods))
12247
+ methods.set(name, cls.methods[name]);
12248
+ classMethodReturnTypes.set(goName, methods);
12249
+ const props = classPropertyTypes.get(goName) ?? new Map();
12250
+ for (const name of Object.keys(cls.props))
12251
+ props.set(name, cls.props[name]);
12252
+ for (const name of Object.keys(cls.getters)) {
12253
+ props.set(name, cls.getters[name]);
12254
+ classGetterMethods.add(`${goName}.${name}`);
12255
+ }
12256
+ for (const name of Object.keys(cls.setters))
12257
+ classSetterMethods.add(`${goName}.${name}`);
12258
+ classPropertyTypes.set(goName, props);
10189
12259
  }
10190
12260
  }
10191
12261
  function visitImportDeclaration(node) {
@@ -10199,9 +12269,14 @@ function visitImportDeclaration(node) {
10199
12269
  if (fileResolver && !includedLocalImports.has(key)) {
10200
12270
  includedLocalImports.add(key);
10201
12271
  const resolved = fileResolver(moduleSpec, currentFileDir);
10202
- if (resolved) {
12272
+ // The same file reached through different specifiers (./registry.js from
12273
+ // a sibling, ./node/registry.js from the parent) is included once
12274
+ const stem = (moduleSpec.split('/').pop() ?? moduleSpec).replace(/\.(js|ts|tsx|mjs)$/, '');
12275
+ const fileKey = resolved ? `file::${resolved.dir}::${stem}` : '';
12276
+ if (resolved && !includedLocalImports.has(fileKey)) {
12277
+ includedLocalImports.add(fileKey);
10203
12278
  const goFileName = specifierToGoFileName(moduleSpec);
10204
- includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx);
12279
+ includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx, resolved.path ?? moduleSpec);
10205
12280
  }
10206
12281
  }
10207
12282
  // Renamed bindings refer to the original declaration (one Go package)
@@ -10220,22 +12295,37 @@ function visitImportDeclaration(node) {
10220
12295
  }
10221
12296
  return '';
10222
12297
  }
12298
+ // Node.js standard library (`node:path`, or a bare builtin name like `fs`):
12299
+ // local names resolve against the registry; type-only imports bind types
12300
+ const nodeModule = nodeModuleName(moduleSpec);
12301
+ if (nodeModule) {
12302
+ setupNodeModuleImport(node, nodeModule);
12303
+ return '';
12304
+ }
10223
12305
  // Type-only imports contribute nothing to runtime
10224
12306
  if (node.importClause?.isTypeOnly)
10225
12307
  return '';
12308
+ if (moduleSpec.startsWith('node:')) {
12309
+ reportError(node, `Unsupported Node.js module: ${moduleSpec} (see conformance.md#node-builtins-node)`);
12310
+ return '';
12311
+ }
10226
12312
  // Go standard library: `import { Println } from 'go:fmt'` → import "fmt"
10227
12313
  if (moduleSpec.startsWith('go:')) {
10228
12314
  const goPkg = moduleSpec.slice(3);
12315
+ // go:builtin is virtual (ref/chan/GoError): no Go import, just aliases.
12316
+ if (goPkg === 'builtin') {
12317
+ registerGoBuiltinAliases(node);
12318
+ return '';
12319
+ }
12320
+ // Side-effect import (`import 'go:time/tzdata'`): blank Go import
12321
+ if (!node.importClause) {
12322
+ goSideEffectImports.add(goPkg);
12323
+ return '';
12324
+ }
10229
12325
  importedPackages.add(goPkg);
10230
12326
  registerGoPackageAliases(node, goPkg);
10231
12327
  return '';
10232
12328
  }
10233
- // Node.js standard library: `import path from 'node:path'` → mapped Go packages
10234
- if (moduleSpec.startsWith('node:')) {
10235
- const nodeModule = moduleSpec.slice(5);
10236
- setupNodeModuleImport(node, nodeModule);
10237
- return '';
10238
- }
10239
12329
  // npm package (bare specifier) → transpile to its own Go file
10240
12330
  const npmKey = `npm::${moduleSpec}`;
10241
12331
  if (fileResolver && !includedLocalImports.has(npmKey)) {
@@ -10243,7 +12333,7 @@ function visitImportDeclaration(node) {
10243
12333
  const resolved = fileResolver(moduleSpec, currentFileDir);
10244
12334
  if (resolved) {
10245
12335
  const goFileName = specifierToGoFileName(moduleSpec);
10246
- includeLocalImport(resolved.content, resolved.dir, goFileName);
12336
+ includeLocalImport(resolved.content, resolved.dir, goFileName, !!resolved.tsx, resolved.path ?? moduleSpec);
10247
12337
  }
10248
12338
  }
10249
12339
  // Register default/namespace import name as a "stripped" namespace
@@ -10260,14 +12350,39 @@ function visitImportDeclaration(node) {
10260
12350
  return '';
10261
12351
  }
10262
12352
  // Go packages imported under a fixed alias to avoid collisions with user
10263
- // variables (a variable named `big` would shadow the package name)
10264
- const goImportAliases = {
10265
- 'math/big': 'tnbig',
10266
- 'net/url': 'tnurl',
10267
- 'golang.org/x/text/unicode/norm': 'tnnorm'
10268
- };
12353
+ // variables (a variable named `big` would shadow the package name).
12354
+ // Table lives in src/go/overrides.ts (shared with the bridge qualifier).
12355
+ // Go bridge types are fully qualified internally (net/http.Cookie,
12356
+ // *[]io/fs.DirEntry); emitted code spells them with the import qualifier.
12357
+ // Rewrites path-qualified names outside string literals and comments, and
12358
+ // imports their packages.
12359
+ function localizeGoPaths(code) {
12360
+ if (code.indexOf('/') < 0)
12361
+ return code;
12362
+ // String/rune literals and line comments match first and pass through
12363
+ return code.replace(/"(?:[^"\\\n]|\\.)*"|`[^`]*`|'(?:[^'\\\n]|\\.)*'|\/\/[^\n]*|\b([a-z][a-z0-9_]*(?:\/[a-z0-9_]+)+)\./g, (match, path) => {
12364
+ if (!path || !hasGoPackage(path))
12365
+ return match;
12366
+ ensureGoQualifier(path);
12367
+ importedPackages.add(path);
12368
+ return `${goQualifier(path)}.`;
12369
+ });
12370
+ }
12371
+ // Whether Go code uses `name.` as a package qualifier (not the tail of a
12372
+ // longer identifier or selector: `ratio.` does not use `io`)
12373
+ function referencesQualifier(code, name) {
12374
+ const needle = `${name}.`;
12375
+ let at = code.indexOf(needle);
12376
+ while (at >= 0) {
12377
+ const prev = at > 0 ? code[at - 1] : ' ';
12378
+ if (!/[A-Za-z0-9_.]/.test(prev))
12379
+ return true;
12380
+ at = code.indexOf(needle, at + 1);
12381
+ }
12382
+ return false;
12383
+ }
10269
12384
  function goImportLine(pkg) {
10270
- const alias = goImportAliases[pkg];
12385
+ const alias = goPathAliases[pkg];
10271
12386
  return alias ? `import ${alias} "${pkg}"` : `import "${pkg}"`;
10272
12387
  }
10273
12388
  // Records the type of a variable introduced by a loop or binding
@@ -10286,9 +12401,89 @@ function registerLocalVariable(name, goType) {
10286
12401
  variableGoTypes.delete(name);
10287
12402
  }
10288
12403
  // for...of over arrays and strings (a string yields one-character strings)
12404
+ // Whether a Go type is an iter.Seq / Seq2 (range-over-func).
12405
+ function isGoIterSeq(goType) {
12406
+ const base = goType.replace(/^\*/, '');
12407
+ return base.startsWith('iter.Seq') || base.startsWith('import(\'go:iter\').Seq');
12408
+ }
12409
+ // Element type of an iter.Seq[V] (Seq2 yields its key type in for…of).
12410
+ function goIterElem(goType) {
12411
+ const start = goType.indexOf('[');
12412
+ const end = goType.lastIndexOf(']');
12413
+ if (start < 0 || end < 0)
12414
+ return 'interface{}';
12415
+ const inner = goType.slice(start + 1, end);
12416
+ let depth = 0;
12417
+ for (let i = 0; i < inner.length; i++) {
12418
+ if (inner[i] === '[' || inner[i] === '(')
12419
+ depth++;
12420
+ else if (inner[i] === ']' || inner[i] === ')')
12421
+ depth--;
12422
+ else if (inner[i] === ',' && depth === 0)
12423
+ return inner.slice(0, i).trim() || 'interface{}';
12424
+ }
12425
+ return inner.trim() || 'interface{}';
12426
+ }
12427
+ // Type args of an iter.Seq[V] / Seq2[K, V].
12428
+ function goIterElems(goType) {
12429
+ const start = goType.indexOf('[');
12430
+ const end = goType.lastIndexOf(']');
12431
+ if (start < 0 || end < 0)
12432
+ return [];
12433
+ const inner = goType.slice(start + 1, end);
12434
+ const out = [];
12435
+ let depth = 0;
12436
+ let from = 0;
12437
+ for (let i = 0; i < inner.length; i++) {
12438
+ if (inner[i] === '[' || inner[i] === '(')
12439
+ depth++;
12440
+ else if (inner[i] === ']' || inner[i] === ')')
12441
+ depth--;
12442
+ else if (inner[i] === ',' && depth === 0) {
12443
+ out.push(inner.slice(from, i).trim());
12444
+ from = i + 1;
12445
+ }
12446
+ }
12447
+ out.push(inner.slice(from).trim());
12448
+ return out;
12449
+ }
10289
12450
  function visitForOfSequence(node, iterExpr, iterType) {
10290
12451
  if (iterType === 'interface{}')
10291
12452
  iterType = '[]interface{}';
12453
+ // Go iter.Seq[V] / Seq2[K, V]: range the func directly.
12454
+ if (iterType && isGoIterSeq(iterType)) {
12455
+ const declaration = isVariableDeclarationList(node.initializer)
12456
+ ? node.initializer.declarations[0]
12457
+ : undefined;
12458
+ // Go ints arrive under a temp and re-bind as TS numbers
12459
+ let prefix = '';
12460
+ const bindIterVar = (name, goType) => {
12461
+ if (!name || isOmittedExpression(name) || !isIdentifier(name))
12462
+ return '_';
12463
+ const local = visit(name);
12464
+ if (goType !== 'float64' && isGoNumericKind(goType)) {
12465
+ const raw = getTempName('it');
12466
+ prefix += `${local} := float64(${raw}); _ = ${local};\n\t\t`;
12467
+ registerLocalVariable(name.text, 'float64');
12468
+ return raw;
12469
+ }
12470
+ registerLocalVariable(name.text, goType);
12471
+ return local;
12472
+ };
12473
+ // Seq2 destructures as [key, value]
12474
+ if (declaration && isArrayBindingPattern(declaration.name) && goIterElems(iterType).length === 2) {
12475
+ const elems = goIterElems(iterType);
12476
+ const bound = declaration.name.elements.map((el, i) => bindIterVar(isOmittedExpression(el) ? undefined : el.name, elems[i] ?? 'interface{}'));
12477
+ const key = bound[0] ?? '_';
12478
+ const value = bound[1] ?? '_';
12479
+ return `for ${key}, ${value} := range ${iterExpr}${visitLoopBody(node.statement, prefix)}`;
12480
+ }
12481
+ if (declaration && isIdentifier(declaration.name)) {
12482
+ const loopVar = bindIterVar(declaration.name, goIterElem(iterType));
12483
+ return `for ${loopVar} := range ${iterExpr}${visitLoopBody(node.statement, prefix)}`;
12484
+ }
12485
+ return `for _ = range ${iterExpr}${visitLoopBody(node.statement)}`;
12486
+ }
10292
12487
  // An iterable class instance: range its Symbol_iterator channel
10293
12488
  if (iterType?.startsWith('*')) {
10294
12489
  const iterClass = iterType.slice(1).replace(/\[.*\]$/, '');
@@ -10309,10 +12504,49 @@ function visitForOfSequence(node, iterExpr, iterType) {
10309
12504
  const declaration = isVariableDeclarationList(node.initializer)
10310
12505
  ? node.initializer.declarations[0]
10311
12506
  : undefined;
12507
+ // for await: receive through the event loop (the values may come from loop
12508
+ // tasks — stream data, events — that must run while this loop waits)
12509
+ if (node.awaitModifier) {
12510
+ useHelper('eventLoop');
12511
+ const chanVar = getTempName('chan');
12512
+ const valueVar = declaration && isIdentifier(declaration.name) ? visit(declaration.name) : getTempName('item');
12513
+ if (declaration && isIdentifier(declaration.name))
12514
+ registerLocalVariable(declaration.name.text, elemType);
12515
+ // A rejected iterator (a stream error) throws out of the loop
12516
+ useHelper('promise');
12517
+ let awaitPrefix = `${valueVar}, __ok := TnLoopReceive(${chanVar})\n\t\tif !__ok {\n\t\t\tif __reason, __rejected := tnRejections.Load(${chanVar}); __rejected {\n\t\t\t\tpanic(__reason)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\t_ = ${valueVar}\n\t\t`;
12518
+ if (declaration && isArrayBindingPattern(declaration.name)) {
12519
+ const memberType = elemType.startsWith('[]') ? elemType.slice(2) : undefined;
12520
+ useHelper('dynamic');
12521
+ (declaration.name.elements ?? []).forEach((el, index) => {
12522
+ if (isOmittedExpression(el))
12523
+ return;
12524
+ const name = visit(el.name);
12525
+ registerLocalVariable(name, memberType);
12526
+ awaitPrefix += `${name} := TnAt(${valueVar}, ${index})\n\t\t_ = ${name}\n\t\t`;
12527
+ });
12528
+ }
12529
+ return `${chanVar} := ${iterExpr}\n\t\tfor ${visitLoopBody(node.statement, awaitPrefix)}`;
12530
+ }
10312
12531
  if (declaration && isIdentifier(declaration.name)) {
10313
12532
  registerLocalVariable(declaration.name.text, elemType);
10314
12533
  return `for ${visit(declaration.name)} := range ${iterExpr}${visitLoopBody(node.statement)}`;
10315
12534
  }
12535
+ // for await (const [a, b] of channel) → a := item[0]; b := item[1]
12536
+ if (declaration && isArrayBindingPattern(declaration.name)) {
12537
+ const chanItem = getTempName('item');
12538
+ const memberType = elemType.startsWith('[]') ? elemType.slice(2) : undefined;
12539
+ let chanPrefix = '';
12540
+ (declaration.name.elements ?? []).forEach((el, index) => {
12541
+ if (isOmittedExpression(el))
12542
+ return;
12543
+ const name = visit(el.name);
12544
+ registerLocalVariable(name, memberType);
12545
+ chanPrefix += `${name} := TnAt(${chanItem}, ${index})\n\t\t_ = ${name}\n\t\t`;
12546
+ });
12547
+ useHelper('dynamic');
12548
+ return `for ${chanItem} := range ${iterExpr}${visitLoopBody(node.statement, chanPrefix)}`;
12549
+ }
10316
12550
  return `for _ = range ${iterExpr}${visitLoopBody(node.statement)}`;
10317
12551
  }
10318
12552
  const elementType = iterType === 'string' ? 'string' : iterType?.startsWith('[]') ? iterType.slice(2) : undefined;
@@ -10371,6 +12605,8 @@ function visitReturningTryStatement(node, fn, options) {
10371
12605
  if (node.catchClause) {
10372
12606
  const varDecl = node.catchClause.variableDeclaration;
10373
12607
  const catchVar = varDecl ? visit(varDecl.name) : '_r';
12608
+ if (varDecl && isIdentifier(varDecl.name))
12609
+ registerLocalVariable(varDecl.name.text, 'interface{}');
10374
12610
  const catchBody = blockCode(node.catchClause.block, true);
10375
12611
  deferreds.push(`defer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\t${catchVar} := r\n\t\t\t\t_ = ${catchVar}\n\t\t\t\t${catchBody}\t\t\t}\n\t\t\t}()`);
10376
12612
  }