@scriptc/compiler 0.4.6 → 0.4.7

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 (652) hide show
  1. package/ambient/node-fallback/events.d.ts +6 -1
  2. package/ambient/node-fallback/fs-promises.d.ts +1 -1
  3. package/ambient/node-fallback/fs.d.ts +54 -10
  4. package/ambient/node-fallback/http.d.ts +3 -1
  5. package/ambient/node-fallback/net.d.ts +19 -0
  6. package/ambient/node-fallback/querystring.d.ts +3 -3
  7. package/ambient/scriptc-node-fallback.d.ts +58 -13
  8. package/dist/backend/executable-features.js +13 -0
  9. package/dist/backend/executable-features.js.map +1 -1
  10. package/dist/backend/immutability.d.ts +12 -1
  11. package/dist/backend/immutability.js +50 -4
  12. package/dist/backend/immutability.js.map +1 -1
  13. package/dist/backend/immutability.test.js +95 -1
  14. package/dist/backend/immutability.test.js.map +1 -1
  15. package/dist/backend/library-pack.js +2 -0
  16. package/dist/backend/library-pack.js.map +1 -1
  17. package/dist/backend/linker.d.ts +11 -0
  18. package/dist/backend/linker.js +31 -3
  19. package/dist/backend/linker.js.map +1 -1
  20. package/dist/backend/lld.d.ts +7 -2
  21. package/dist/backend/lld.js +5 -3
  22. package/dist/backend/lld.js.map +1 -1
  23. package/dist/backend/lld.test.js +10 -4
  24. package/dist/backend/lld.test.js.map +1 -1
  25. package/dist/backend/llvm/constant-callbacks.test.js +50 -1
  26. package/dist/backend/llvm/constant-callbacks.test.js.map +1 -1
  27. package/dist/backend/llvm/cpu-dispatch.js +6 -5
  28. package/dist/backend/llvm/cpu-dispatch.js.map +1 -1
  29. package/dist/backend/llvm/dyn.d.ts +53 -1
  30. package/dist/backend/llvm/dyn.js +221 -9
  31. package/dist/backend/llvm/dyn.js.map +1 -1
  32. package/dist/backend/llvm/emit-function.js +1 -1
  33. package/dist/backend/llvm/emit-function.js.map +1 -1
  34. package/dist/backend/llvm/emit-module.js +148 -50
  35. package/dist/backend/llvm/emit-module.js.map +1 -1
  36. package/dist/backend/llvm/emit-statements.js +39 -19
  37. package/dist/backend/llvm/emit-statements.js.map +1 -1
  38. package/dist/backend/llvm/emitter.d.ts +29 -2
  39. package/dist/backend/llvm/emitter.js +86 -7
  40. package/dist/backend/llvm/emitter.js.map +1 -1
  41. package/dist/backend/llvm/expr-calls.d.ts +4 -1
  42. package/dist/backend/llvm/expr-calls.js +20 -4
  43. package/dist/backend/llvm/expr-calls.js.map +1 -1
  44. package/dist/backend/llvm/expr-dynamic.js +209 -16
  45. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  46. package/dist/backend/llvm/expr-primitives.js +94 -11
  47. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  48. package/dist/backend/llvm/expr-stream-bridges.d.ts +8 -0
  49. package/dist/backend/llvm/expr-stream-bridges.js +42 -2
  50. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  51. package/dist/backend/llvm/expr-stream-callbacks.d.ts +16 -0
  52. package/dist/backend/llvm/expr-stream-callbacks.js +206 -20
  53. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  54. package/dist/backend/llvm/integer-values.d.ts +2 -0
  55. package/dist/backend/llvm/integer-values.js +6 -0
  56. package/dist/backend/llvm/integer-values.js.map +1 -1
  57. package/dist/backend/llvm/js-ic.d.ts +38 -0
  58. package/dist/backend/llvm/js-ic.js +295 -1
  59. package/dist/backend/llvm/js-ic.js.map +1 -1
  60. package/dist/backend/llvm/lib-dispatch.js +20 -0
  61. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  62. package/dist/backend/llvm/lib-filesystem.js +116 -11
  63. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  64. package/dist/backend/llvm/lib-process.js +136 -24
  65. package/dist/backend/llvm/lib-process.js.map +1 -1
  66. package/dist/backend/llvm/lib-shared.js +70 -0
  67. package/dist/backend/llvm/lib-shared.js.map +1 -1
  68. package/dist/backend/llvm/lib-stream.js +67 -0
  69. package/dist/backend/llvm/lib-stream.js.map +1 -1
  70. package/dist/backend/llvm/loop-entry-versions.d.ts +2 -2
  71. package/dist/backend/llvm/loop-entry-versions.js +43 -21
  72. package/dist/backend/llvm/loop-entry-versions.js.map +1 -1
  73. package/dist/backend/llvm/loop-state.d.ts +7 -1
  74. package/dist/backend/llvm/loop-state.js +20 -2
  75. package/dist/backend/llvm/loop-state.js.map +1 -1
  76. package/dist/backend/llvm/map-number-lookup.d.ts +3 -1
  77. package/dist/backend/llvm/map-number-lookup.js +7 -3
  78. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  79. package/dist/backend/llvm/map-number-lookup.test.js +52 -11
  80. package/dist/backend/llvm/map-number-lookup.test.js.map +1 -1
  81. package/dist/backend/llvm/receiver-brackets.test.d.ts +1 -0
  82. package/dist/backend/llvm/receiver-brackets.test.js +68 -0
  83. package/dist/backend/llvm/receiver-brackets.test.js.map +1 -0
  84. package/dist/backend/llvm/record-views.d.ts +22 -2
  85. package/dist/backend/llvm/record-views.js +139 -10
  86. package/dist/backend/llvm/record-views.js.map +1 -1
  87. package/dist/backend/llvm/record-views.test.js +76 -0
  88. package/dist/backend/llvm/record-views.test.js.map +1 -1
  89. package/dist/backend/llvm/reference-writes.js +1 -0
  90. package/dist/backend/llvm/reference-writes.js.map +1 -1
  91. package/dist/backend/llvm/shapes.d.ts +3 -0
  92. package/dist/backend/llvm/shapes.js +18 -3
  93. package/dist/backend/llvm/shapes.js.map +1 -1
  94. package/dist/backend/llvm/shapes.test.js +4 -0
  95. package/dist/backend/llvm/shapes.test.js.map +1 -1
  96. package/dist/backend/llvm/speculative-integers.d.ts +10 -1
  97. package/dist/backend/llvm/speculative-integers.js +59 -8
  98. package/dist/backend/llvm/speculative-integers.js.map +1 -1
  99. package/dist/backend/llvm/unwind-abi.d.ts +3 -3
  100. package/dist/backend/llvm/unwind-abi.js +8 -3
  101. package/dist/backend/llvm/unwind-abi.js.map +1 -1
  102. package/dist/backend/llvm/walkers.js +1 -1
  103. package/dist/backend/llvm/walkers.js.map +1 -1
  104. package/dist/backend/native/contracts.d.ts +54 -0
  105. package/dist/backend/native/executable.js +87 -5
  106. package/dist/backend/native/executable.js.map +1 -1
  107. package/dist/backend/native/library.js +1 -1
  108. package/dist/backend/native/library.js.map +1 -1
  109. package/dist/backend/native/runtime-inputs.js +5 -1
  110. package/dist/backend/native/runtime-inputs.js.map +1 -1
  111. package/dist/backend/native/vendor.d.ts +1 -1
  112. package/dist/backend/native/vendor.js +1 -1
  113. package/dist/backend/native/vendor.js.map +1 -1
  114. package/dist/backend/native-codegen.js +3 -0
  115. package/dist/backend/native-codegen.js.map +1 -1
  116. package/dist/backend/native-link-info-core.d.ts +43 -0
  117. package/dist/backend/native-link-info-core.js.map +1 -1
  118. package/dist/backend/native-link-info.js +1 -0
  119. package/dist/backend/native-link-info.js.map +1 -1
  120. package/dist/backend/native-profile.d.ts +4 -1
  121. package/dist/backend/native-profile.js +12 -1
  122. package/dist/backend/native-profile.js.map +1 -1
  123. package/dist/backend/native-profile.test.js +10 -0
  124. package/dist/backend/native-profile.test.js.map +1 -1
  125. package/dist/backend/publication.d.ts +4 -1
  126. package/dist/backend/publication.js +4 -2
  127. package/dist/backend/publication.js.map +1 -1
  128. package/dist/backend/runtime-abi.d.ts +2 -2
  129. package/dist/backend/runtime-abi.js +2 -2
  130. package/dist/backend/runtime-pack-core.d.ts +31 -7
  131. package/dist/backend/runtime-pack-core.js +21 -1
  132. package/dist/backend/runtime-pack-core.js.map +1 -1
  133. package/dist/backend/runtime-pack-native.d.ts +18 -1
  134. package/dist/backend/runtime-pack-native.js +138 -29
  135. package/dist/backend/runtime-pack-native.js.map +1 -1
  136. package/dist/backend/runtime-pack-native.test.js +166 -4
  137. package/dist/backend/runtime-pack-native.test.js.map +1 -1
  138. package/dist/backend/runtime-pack-targets.test.js +3 -1
  139. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  140. package/dist/backend/runtime-pack.d.ts +7 -0
  141. package/dist/backend/runtime-pack.js +25 -3
  142. package/dist/backend/runtime-pack.js.map +1 -1
  143. package/dist/backend/runtime-pack.test.js +3 -11
  144. package/dist/backend/runtime-pack.test.js.map +1 -1
  145. package/dist/backend/vendor-archives.d.ts +4 -1
  146. package/dist/backend/vendor-archives.js +264 -3
  147. package/dist/backend/vendor-archives.js.map +1 -1
  148. package/dist/backend/vendor-inputs.d.ts +36 -0
  149. package/dist/backend/vendor-inputs.js +42 -0
  150. package/dist/backend/vendor-inputs.js.map +1 -1
  151. package/dist/backend/vendor-inputs.test.js +28 -1
  152. package/dist/backend/vendor-inputs.test.js.map +1 -1
  153. package/dist/backend/warm-cache.js +2 -0
  154. package/dist/backend/warm-cache.js.map +1 -1
  155. package/dist/compat/fetch-profile.d.ts +7 -1
  156. package/dist/compat/fetch-profile.js +23 -10
  157. package/dist/compat/fetch-profile.js.map +1 -1
  158. package/dist/compile-types.d.ts +7 -0
  159. package/dist/contracts/check.d.ts +3 -0
  160. package/dist/contracts/check.js +18 -5
  161. package/dist/contracts/check.js.map +1 -1
  162. package/dist/coverage/surface-manifest.js +1 -1
  163. package/dist/coverage/surface-manifest.js.map +1 -1
  164. package/dist/diagnostics/codes.d.ts +6 -0
  165. package/dist/diagnostics/codes.js +6 -0
  166. package/dist/diagnostics/codes.js.map +1 -1
  167. package/dist/diagnostics/diagnostic.d.ts +50 -0
  168. package/dist/diagnostics/diagnostic.js +142 -3
  169. package/dist/diagnostics/diagnostic.js.map +1 -1
  170. package/dist/diagnostics/dynamic-tier.js +3 -1
  171. package/dist/diagnostics/dynamic-tier.js.map +1 -1
  172. package/dist/executable/executable-cache.d.ts +29 -0
  173. package/dist/executable/executable-cache.js +18 -0
  174. package/dist/executable/executable-cache.js.map +1 -1
  175. package/dist/executable/executable-cache.test.js +5 -1
  176. package/dist/executable/executable-cache.test.js.map +1 -1
  177. package/dist/executable/prepare.d.ts +13 -3
  178. package/dist/executable/prepare.js +271 -18
  179. package/dist/executable/prepare.js.map +1 -1
  180. package/dist/frontend/early-errors.d.ts +14 -0
  181. package/dist/frontend/early-errors.js +305 -0
  182. package/dist/frontend/early-errors.js.map +1 -0
  183. package/dist/frontend/early-errors.test.d.ts +1 -0
  184. package/dist/frontend/early-errors.test.js +42 -0
  185. package/dist/frontend/early-errors.test.js.map +1 -0
  186. package/dist/frontend/inferred-js-diagnostics.test.js +23 -0
  187. package/dist/frontend/inferred-js-diagnostics.test.js.map +1 -1
  188. package/dist/frontend/input-tracker.d.ts +47 -0
  189. package/dist/frontend/input-tracker.js +173 -36
  190. package/dist/frontend/input-tracker.js.map +1 -1
  191. package/dist/frontend/input-tracker.test.js +86 -2
  192. package/dist/frontend/input-tracker.test.js.map +1 -1
  193. package/dist/frontend/js-inference.d.ts +2 -0
  194. package/dist/frontend/js-inference.js +18 -0
  195. package/dist/frontend/js-inference.js.map +1 -0
  196. package/dist/frontend/lowering/binding-analysis.d.ts +2 -1
  197. package/dist/frontend/lowering/binding-analysis.js +65 -1
  198. package/dist/frontend/lowering/binding-analysis.js.map +1 -1
  199. package/dist/frontend/lowering/builtin-calls.js +9 -0
  200. package/dist/frontend/lowering/builtin-calls.js.map +1 -1
  201. package/dist/frontend/lowering/builtin-namespaces.d.ts +1 -1
  202. package/dist/frontend/lowering/builtin-namespaces.js +19 -2
  203. package/dist/frontend/lowering/builtin-namespaces.js.map +1 -1
  204. package/dist/frontend/lowering/builtins/child-process-methods.d.ts +7 -0
  205. package/dist/frontend/lowering/builtins/child-process-methods.js +50 -10
  206. package/dist/frontend/lowering/builtins/child-process-methods.js.map +1 -1
  207. package/dist/frontend/lowering/builtins/child-process.js +5 -1
  208. package/dist/frontend/lowering/builtins/child-process.js.map +1 -1
  209. package/dist/frontend/lowering/builtins/dates.d.ts +1 -1
  210. package/dist/frontend/lowering/builtins/dates.js +23 -1
  211. package/dist/frontend/lowering/builtins/dates.js.map +1 -1
  212. package/dist/frontend/lowering/builtins/environment.d.ts +55 -0
  213. package/dist/frontend/lowering/builtins/environment.js +213 -2
  214. package/dist/frontend/lowering/builtins/environment.js.map +1 -1
  215. package/dist/frontend/lowering/builtins/filesystem-calls.js +47 -16
  216. package/dist/frontend/lowering/builtins/filesystem-calls.js.map +1 -1
  217. package/dist/frontend/lowering/builtins/filesystem.d.ts +25 -7
  218. package/dist/frontend/lowering/builtins/filesystem.js +185 -16
  219. package/dist/frontend/lowering/builtins/filesystem.js.map +1 -1
  220. package/dist/frontend/lowering/builtins/module-calls.js +39 -2
  221. package/dist/frontend/lowering/builtins/module-calls.js.map +1 -1
  222. package/dist/frontend/lowering/builtins/module-resolution.d.ts +8 -0
  223. package/dist/frontend/lowering/builtins/module-resolution.js +31 -5
  224. package/dist/frontend/lowering/builtins/module-resolution.js.map +1 -1
  225. package/dist/frontend/lowering/builtins/operating-system.d.ts +3 -2
  226. package/dist/frontend/lowering/builtins/operating-system.js +5 -4
  227. package/dist/frontend/lowering/builtins/operating-system.js.map +1 -1
  228. package/dist/frontend/lowering/builtins/process.d.ts +17 -1
  229. package/dist/frontend/lowering/builtins/process.js +285 -19
  230. package/dist/frontend/lowering/builtins/process.js.map +1 -1
  231. package/dist/frontend/lowering/builtins/querystring.d.ts +8 -0
  232. package/dist/frontend/lowering/builtins/querystring.js +202 -18
  233. package/dist/frontend/lowering/builtins/querystring.js.map +1 -1
  234. package/dist/frontend/lowering/builtins/readline.js +21 -3
  235. package/dist/frontend/lowering/builtins/readline.js.map +1 -1
  236. package/dist/frontend/lowering/builtins/reflection.js +60 -1
  237. package/dist/frontend/lowering/builtins/reflection.js.map +1 -1
  238. package/dist/frontend/lowering/builtins/text-codecs.js +208 -59
  239. package/dist/frontend/lowering/builtins/text-codecs.js.map +1 -1
  240. package/dist/frontend/lowering/builtins/url.js +20 -1
  241. package/dist/frontend/lowering/builtins/url.js.map +1 -1
  242. package/dist/frontend/lowering/builtins/v8.d.ts +5 -5
  243. package/dist/frontend/lowering/builtins/v8.js +7 -5
  244. package/dist/frontend/lowering/builtins/v8.js.map +1 -1
  245. package/dist/frontend/lowering/builtins/web-streams-dyn.d.ts +17 -2
  246. package/dist/frontend/lowering/builtins/web-streams-dyn.js +97 -9
  247. package/dist/frontend/lowering/builtins/web-streams-dyn.js.map +1 -1
  248. package/dist/frontend/lowering/call-signatures.js +77 -0
  249. package/dist/frontend/lowering/call-signatures.js.map +1 -1
  250. package/dist/frontend/lowering/cjs-exports-object.d.ts +3 -0
  251. package/dist/frontend/lowering/cjs-exports-object.js +1 -1
  252. package/dist/frontend/lowering/cjs-exports-object.js.map +1 -1
  253. package/dist/frontend/lowering/cjs-inline-require.js +5 -0
  254. package/dist/frontend/lowering/cjs-inline-require.js.map +1 -1
  255. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +5 -0
  256. package/dist/frontend/lowering/class-dynamic-dispatch.js +114 -0
  257. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  258. package/dist/frontend/lowering/class-protocols.js +59 -10
  259. package/dist/frontend/lowering/class-protocols.js.map +1 -1
  260. package/dist/frontend/lowering/class-runtime-statics.js +15 -0
  261. package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
  262. package/dist/frontend/lowering/class-views.d.ts +13 -0
  263. package/dist/frontend/lowering/class-views.js +51 -0
  264. package/dist/frontend/lowering/class-views.js.map +1 -0
  265. package/dist/frontend/lowering/coercions/function-adapters.d.ts +15 -1
  266. package/dist/frontend/lowering/coercions/function-adapters.js +54 -10
  267. package/dist/frontend/lowering/coercions/function-adapters.js.map +1 -1
  268. package/dist/frontend/lowering/coercions/state.d.ts +7 -0
  269. package/dist/frontend/lowering/coercions/state.js +7 -0
  270. package/dist/frontend/lowering/coercions/state.js.map +1 -1
  271. package/dist/frontend/lowering/coercions/structural-plans.js +60 -3
  272. package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
  273. package/dist/frontend/lowering/coercions/structural.js +29 -1
  274. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  275. package/dist/frontend/lowering/containers/array-callbacks.d.ts +8 -1
  276. package/dist/frontend/lowering/containers/array-callbacks.js +132 -28
  277. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  278. package/dist/frontend/lowering/containers/array-construction.js +16 -5
  279. package/dist/frontend/lowering/containers/array-construction.js.map +1 -1
  280. package/dist/frontend/lowering/containers/array-methods.js +16 -0
  281. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  282. package/dist/frontend/lowering/containers/bytes.js +4 -0
  283. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  284. package/dist/frontend/lowering/containers/callback-arguments.d.ts +11 -0
  285. package/dist/frontend/lowering/containers/callback-arguments.js +53 -0
  286. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  287. package/dist/frontend/lowering/containers/filter-predicates.d.ts +10 -0
  288. package/dist/frontend/lowering/containers/filter-predicates.js +69 -3
  289. package/dist/frontend/lowering/containers/filter-predicates.js.map +1 -1
  290. package/dist/frontend/lowering/containers/indexed-objects.js +46 -2
  291. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  292. package/dist/frontend/lowering/expressions/array-literals.d.ts +14 -0
  293. package/dist/frontend/lowering/expressions/array-literals.js +26 -10
  294. package/dist/frontend/lowering/expressions/array-literals.js.map +1 -1
  295. package/dist/frontend/lowering/expressions/binary.js +104 -12
  296. package/dist/frontend/lowering/expressions/binary.js.map +1 -1
  297. package/dist/frontend/lowering/expressions/element-access.js +121 -4
  298. package/dist/frontend/lowering/expressions/element-access.js.map +1 -1
  299. package/dist/frontend/lowering/expressions/identifiers.js +143 -3
  300. package/dist/frontend/lowering/expressions/identifiers.js.map +1 -1
  301. package/dist/frontend/lowering/expressions/keyed-field-targets.d.ts +11 -1
  302. package/dist/frontend/lowering/expressions/keyed-field-targets.js +22 -1
  303. package/dist/frontend/lowering/expressions/keyed-field-targets.js.map +1 -1
  304. package/dist/frontend/lowering/expressions/narrowing.js +25 -1
  305. package/dist/frontend/lowering/expressions/narrowing.js.map +1 -1
  306. package/dist/frontend/lowering/expressions/object-literals.d.ts +5 -1
  307. package/dist/frontend/lowering/expressions/object-literals.js +778 -229
  308. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  309. package/dist/frontend/lowering/expressions/optional-chains.js +66 -20
  310. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  311. package/dist/frontend/lowering/expressions/property-access.d.ts +31 -0
  312. package/dist/frontend/lowering/expressions/property-access.js +796 -12
  313. package/dist/frontend/lowering/expressions/property-access.js.map +1 -1
  314. package/dist/frontend/lowering/expressions/relational.js +29 -1
  315. package/dist/frontend/lowering/expressions/relational.js.map +1 -1
  316. package/dist/frontend/lowering/expressions/typeof.js +17 -0
  317. package/dist/frontend/lowering/expressions/typeof.js.map +1 -1
  318. package/dist/frontend/lowering/expressions/updates.js +132 -17
  319. package/dist/frontend/lowering/expressions/updates.js.map +1 -1
  320. package/dist/frontend/lowering/frozen-records.d.ts +6 -0
  321. package/dist/frontend/lowering/frozen-records.js +10 -4
  322. package/dist/frontend/lowering/frozen-records.js.map +1 -1
  323. package/dist/frontend/lowering/frozen-records.test.d.ts +1 -0
  324. package/dist/frontend/lowering/frozen-records.test.js +44 -0
  325. package/dist/frontend/lowering/frozen-records.test.js.map +1 -0
  326. package/dist/frontend/lowering/function-names.d.ts +9 -0
  327. package/dist/frontend/lowering/function-names.js +95 -0
  328. package/dist/frontend/lowering/function-names.js.map +1 -0
  329. package/dist/frontend/lowering/function-returns.js +5 -3
  330. package/dist/frontend/lowering/function-returns.js.map +1 -1
  331. package/dist/frontend/lowering/generic-functions.d.ts +49 -0
  332. package/dist/frontend/lowering/generic-functions.js +365 -7
  333. package/dist/frontend/lowering/generic-functions.js.map +1 -1
  334. package/dist/frontend/lowering/global-values.d.ts +9 -0
  335. package/dist/frontend/lowering/global-values.js +44 -0
  336. package/dist/frontend/lowering/global-values.js.map +1 -1
  337. package/dist/frontend/lowering/handle-properties.js +34 -1
  338. package/dist/frontend/lowering/handle-properties.js.map +1 -1
  339. package/dist/frontend/lowering/key-order-witness.d.ts +54 -0
  340. package/dist/frontend/lowering/key-order-witness.js +546 -5
  341. package/dist/frontend/lowering/key-order-witness.js.map +1 -1
  342. package/dist/frontend/lowering/lib-boundary.js +14 -0
  343. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  344. package/dist/frontend/lowering/lower-assert.js +110 -1
  345. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  346. package/dist/frontend/lowering/lower-builtin-values.d.ts +36 -6
  347. package/dist/frontend/lowering/lower-builtin-values.js +332 -58
  348. package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
  349. package/dist/frontend/lowering/lower-calls.d.ts +22 -1
  350. package/dist/frontend/lowering/lower-calls.js +427 -48
  351. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  352. package/dist/frontend/lowering/lower-calls.test.d.ts +1 -0
  353. package/dist/frontend/lowering/lower-calls.test.js +78 -0
  354. package/dist/frontend/lowering/lower-calls.test.js.map +1 -0
  355. package/dist/frontend/lowering/lower-classes.d.ts +16 -5
  356. package/dist/frontend/lowering/lower-classes.js +168 -51
  357. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  358. package/dist/frontend/lowering/lower-event-emitter.d.ts +9 -0
  359. package/dist/frontend/lowering/lower-event-emitter.js +383 -41
  360. package/dist/frontend/lowering/lower-event-emitter.js.map +1 -1
  361. package/dist/frontend/lowering/lower-exprs.d.ts +14 -0
  362. package/dist/frontend/lowering/lower-exprs.js +25 -0
  363. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  364. package/dist/frontend/lowering/lower-functions.js +2 -0
  365. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  366. package/dist/frontend/lowering/lower-inspect.js +80 -33
  367. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  368. package/dist/frontend/lowering/lower-modules.js +87 -25
  369. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  370. package/dist/frontend/lowering/lower-node-island.d.ts +35 -4
  371. package/dist/frontend/lowering/lower-node-island.js +456 -61
  372. package/dist/frontend/lowering/lower-node-island.js.map +1 -1
  373. package/dist/frontend/lowering/lower-server.js +160 -9
  374. package/dist/frontend/lowering/lower-server.js.map +1 -1
  375. package/dist/frontend/lowering/lower-shared-modules.js +13 -1
  376. package/dist/frontend/lowering/lower-shared-modules.js.map +1 -1
  377. package/dist/frontend/lowering/lower-stdlib.d.ts +3 -2
  378. package/dist/frontend/lowering/lower-stdlib.js +32 -18
  379. package/dist/frontend/lowering/lower-stdlib.js.map +1 -1
  380. package/dist/frontend/lowering/lower-stmts.d.ts +21 -4
  381. package/dist/frontend/lowering/lower-stmts.js +580 -103
  382. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  383. package/dist/frontend/lowering/lower-stream.js +126 -8
  384. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  385. package/dist/frontend/lowering/lower-timers.js +133 -21
  386. package/dist/frontend/lowering/lower-timers.js.map +1 -1
  387. package/dist/frontend/lowering/lowerer.d.ts +139 -3
  388. package/dist/frontend/lowering/lowerer.js +579 -43
  389. package/dist/frontend/lowering/lowerer.js.map +1 -1
  390. package/dist/frontend/lowering/lowerer.test.js +1 -1
  391. package/dist/frontend/lowering/lowerer.test.js.map +1 -1
  392. package/dist/frontend/lowering/module-namespace-elements.js +15 -0
  393. package/dist/frontend/lowering/module-namespace-elements.js.map +1 -1
  394. package/dist/frontend/lowering/new-target.js +2 -2
  395. package/dist/frontend/lowering/nullish-empty-pattern.d.ts +17 -0
  396. package/dist/frontend/lowering/nullish-empty-pattern.js +118 -0
  397. package/dist/frontend/lowering/nullish-empty-pattern.js.map +1 -0
  398. package/dist/frontend/lowering/partial-assertions.d.ts +39 -0
  399. package/dist/frontend/lowering/partial-assertions.js +195 -0
  400. package/dist/frontend/lowering/partial-assertions.js.map +1 -0
  401. package/dist/frontend/lowering/primitive-wrappers.d.ts +34 -0
  402. package/dist/frontend/lowering/primitive-wrappers.js +220 -0
  403. package/dist/frontend/lowering/primitive-wrappers.js.map +1 -0
  404. package/dist/frontend/lowering/private-statics.d.ts +46 -0
  405. package/dist/frontend/lowering/private-statics.js +322 -0
  406. package/dist/frontend/lowering/private-statics.js.map +1 -0
  407. package/dist/frontend/lowering/record-sources.d.ts +12 -1
  408. package/dist/frontend/lowering/record-sources.js +60 -0
  409. package/dist/frontend/lowering/record-sources.js.map +1 -1
  410. package/dist/frontend/lowering/strict-equality.d.ts +4 -0
  411. package/dist/frontend/lowering/strict-equality.js +14 -0
  412. package/dist/frontend/lowering/strict-equality.js.map +1 -1
  413. package/dist/frontend/lowering/surfaces.d.ts +27 -0
  414. package/dist/frontend/lowering/surfaces.js +135 -62
  415. package/dist/frontend/lowering/surfaces.js.map +1 -1
  416. package/dist/frontend/lowering/unit-journal.d.ts +128 -17
  417. package/dist/frontend/lowering/unit-journal.js +310 -53
  418. package/dist/frontend/lowering/unit-journal.js.map +1 -1
  419. package/dist/frontend/lowering/unit-journal.test.js +91 -3
  420. package/dist/frontend/lowering/unit-journal.test.js.map +1 -1
  421. package/dist/frontend/lowering/unit-reuse.d.ts +92 -0
  422. package/dist/frontend/lowering/unit-reuse.js +1008 -0
  423. package/dist/frontend/lowering/unit-reuse.js.map +1 -0
  424. package/dist/frontend/lowering/width-lift.d.ts +12 -0
  425. package/dist/frontend/npm-file-import.test.d.ts +1 -0
  426. package/dist/frontend/npm-file-import.test.js +76 -0
  427. package/dist/frontend/npm-file-import.test.js.map +1 -0
  428. package/dist/frontend/npm-static.js +17 -2
  429. package/dist/frontend/npm-static.js.map +1 -1
  430. package/dist/frontend/npm.d.ts +9 -0
  431. package/dist/frontend/npm.js +82 -0
  432. package/dist/frontend/npm.js.map +1 -1
  433. package/dist/frontend/pipeline-native.js +2 -1
  434. package/dist/frontend/pipeline-native.js.map +1 -1
  435. package/dist/frontend/pipeline.d.ts +4 -0
  436. package/dist/frontend/pipeline.js +68 -7
  437. package/dist/frontend/pipeline.js.map +1 -1
  438. package/dist/frontend/program-node.d.ts +3 -0
  439. package/dist/frontend/program-node.js +13 -6
  440. package/dist/frontend/program-node.js.map +1 -1
  441. package/dist/frontend/program.d.ts +9 -0
  442. package/dist/frontend/program.js +159 -7
  443. package/dist/frontend/program.js.map +1 -1
  444. package/dist/frontend/provenance-core.js +4 -0
  445. package/dist/frontend/provenance-core.js.map +1 -1
  446. package/dist/frontend/services.d.ts +14 -0
  447. package/dist/frontend/services.js +52 -2
  448. package/dist/frontend/services.js.map +1 -1
  449. package/dist/frontend/services.test.js +61 -1
  450. package/dist/frontend/services.test.js.map +1 -1
  451. package/dist/frontend/shape-inference/analyze.d.ts +5 -0
  452. package/dist/frontend/shape-inference/analyze.js +104 -4
  453. package/dist/frontend/shape-inference/analyze.js.map +1 -1
  454. package/dist/frontend/shape-inference/domain.js +0 -1
  455. package/dist/frontend/shape-inference/domain.js.map +1 -1
  456. package/dist/frontend/shape-inference/index.js +5 -1
  457. package/dist/frontend/shape-inference/index.js.map +1 -1
  458. package/dist/frontend/shape-inference/memo.d.ts +2 -1
  459. package/dist/frontend/shape-inference/memo.js +6 -2
  460. package/dist/frontend/shape-inference/memo.js.map +1 -1
  461. package/dist/frontend/shape-inference/overlay.d.ts +1 -1
  462. package/dist/frontend/shape-inference/overlay.js +116 -2
  463. package/dist/frontend/shape-inference/overlay.js.map +1 -1
  464. package/dist/frontend/shape-inference/static-objects.js +11 -6
  465. package/dist/frontend/shape-inference/static-objects.js.map +1 -1
  466. package/dist/frontend/source-phase-imports.test.d.ts +1 -0
  467. package/dist/frontend/source-phase-imports.test.js +37 -0
  468. package/dist/frontend/source-phase-imports.test.js.map +1 -0
  469. package/dist/frontend/ts7/ast-node.d.ts +6 -1
  470. package/dist/frontend/ts7/ast-node.js +12 -0
  471. package/dist/frontend/ts7/ast-node.js.map +1 -1
  472. package/dist/frontend/ts7/ast-wire-protocols.test.js +11 -3
  473. package/dist/frontend/ts7/ast-wire-protocols.test.js.map +1 -1
  474. package/dist/frontend/ts7/ast-wire.js +3 -5
  475. package/dist/frontend/ts7/ast-wire.js.map +1 -1
  476. package/dist/frontend/ts7/checker-backend.d.ts +13 -0
  477. package/dist/frontend/ts7/checker-backend.js +84 -2
  478. package/dist/frontend/ts7/checker-backend.js.map +1 -1
  479. package/dist/frontend/ts7/checker-backend.test.js +126 -2
  480. package/dist/frontend/ts7/checker-backend.test.js.map +1 -1
  481. package/dist/frontend/ts7/program-host.d.ts +22 -1
  482. package/dist/frontend/ts7/program-host.js +49 -4
  483. package/dist/frontend/ts7/program-host.js.map +1 -1
  484. package/dist/frontend/ts7/program-host.test.js +83 -0
  485. package/dist/frontend/ts7/program-host.test.js.map +1 -1
  486. package/dist/frontend/ts7/rpc-process.d.ts +16 -1
  487. package/dist/frontend/ts7/rpc-process.js +170 -92
  488. package/dist/frontend/ts7/rpc-process.js.map +1 -1
  489. package/dist/frontend/ts7/rpc-process.test.js +49 -21
  490. package/dist/frontend/ts7/rpc-process.test.js.map +1 -1
  491. package/dist/frontend/tsc-codes.js +70 -0
  492. package/dist/frontend/tsc-codes.js.map +1 -1
  493. package/dist/frontend/type-mapper.d.ts +7 -2
  494. package/dist/frontend/type-mapper.js +148 -1
  495. package/dist/frontend/type-mapper.js.map +1 -1
  496. package/dist/index.d.ts +1 -1
  497. package/dist/index.js +152 -8
  498. package/dist/index.js.map +1 -1
  499. package/dist/ir/api.d.ts +1 -1
  500. package/dist/ir/api.js +1 -1
  501. package/dist/ir/api.js.map +1 -1
  502. package/dist/ir/build.d.ts +3 -2
  503. package/dist/ir/build.js +4 -2
  504. package/dist/ir/build.js.map +1 -1
  505. package/dist/ir/builtin-effects.js +53 -0
  506. package/dist/ir/builtin-effects.js.map +1 -1
  507. package/dist/ir/builtin-signatures.d.ts +1 -1
  508. package/dist/ir/builtin-signatures.js +211 -10
  509. package/dist/ir/builtin-signatures.js.map +1 -1
  510. package/dist/ir/codec.d.ts +82 -0
  511. package/dist/ir/codec.generated.d.ts +13 -0
  512. package/dist/ir/codec.generated.js +5274 -0
  513. package/dist/ir/codec.generated.js.map +1 -0
  514. package/dist/ir/codec.js +325 -0
  515. package/dist/ir/codec.js.map +1 -0
  516. package/dist/ir/codec.test.d.ts +1 -0
  517. package/dist/ir/codec.test.js +225 -0
  518. package/dist/ir/codec.test.js.map +1 -0
  519. package/dist/ir/fields.generated.d.ts +20 -10
  520. package/dist/ir/fields.generated.js +1031 -11
  521. package/dist/ir/fields.generated.js.map +1 -1
  522. package/dist/ir/fields.test.js +46 -2
  523. package/dist/ir/fields.test.js.map +1 -1
  524. package/dist/ir/function-references.d.ts +5 -4
  525. package/dist/ir/function-references.js +8 -4
  526. package/dist/ir/function-references.js.map +1 -1
  527. package/dist/ir/function-references.test.js +7 -0
  528. package/dist/ir/function-references.test.js.map +1 -1
  529. package/dist/ir/inline-array-sources.js +4 -1
  530. package/dist/ir/inline-array-sources.js.map +1 -1
  531. package/dist/ir/inline-array-views.d.ts +13 -0
  532. package/dist/ir/inline-array-views.js +944 -0
  533. package/dist/ir/inline-array-views.js.map +1 -0
  534. package/dist/ir/inline-arrays.d.ts +7 -0
  535. package/dist/ir/inline-arrays.js +190 -55
  536. package/dist/ir/inline-arrays.js.map +1 -1
  537. package/dist/ir/inline-arrays.test.js +140 -2
  538. package/dist/ir/inline-arrays.test.js.map +1 -1
  539. package/dist/ir/integer-ranges.js +16 -6
  540. package/dist/ir/integer-ranges.js.map +1 -1
  541. package/dist/ir/ir.d.ts +75 -44
  542. package/dist/ir/ir.js.map +1 -1
  543. package/dist/ir/js-ic.d.ts +10 -9
  544. package/dist/ir/js-ic.js +28 -20
  545. package/dist/ir/js-ic.js.map +1 -1
  546. package/dist/ir/js-ic.test.js +10 -2
  547. package/dist/ir/js-ic.test.js.map +1 -1
  548. package/dist/ir/jsval.js +374 -63
  549. package/dist/ir/jsval.js.map +1 -1
  550. package/dist/ir/jsval.test.js +96 -2
  551. package/dist/ir/jsval.test.js.map +1 -1
  552. package/dist/ir/loop-entry-ranges.js +112 -9
  553. package/dist/ir/loop-entry-ranges.js.map +1 -1
  554. package/dist/ir/loop-entry-ranges.test.js +40 -0
  555. package/dist/ir/loop-entry-ranges.test.js.map +1 -1
  556. package/dist/ir/parameter-versions.d.ts +2 -1
  557. package/dist/ir/parameter-versions.js +3 -1
  558. package/dist/ir/parameter-versions.js.map +1 -1
  559. package/dist/ir/receiver-readers.js +58 -2
  560. package/dist/ir/receiver-readers.js.map +1 -1
  561. package/dist/ir/runtime-features.d.ts +60 -1
  562. package/dist/ir/runtime-features.js +277 -2
  563. package/dist/ir/runtime-features.js.map +1 -1
  564. package/dist/ir/runtime-features.test.js +165 -1
  565. package/dist/ir/runtime-features.test.js.map +1 -1
  566. package/dist/ir/saturating-loop-jams.d.ts +8 -1
  567. package/dist/ir/saturating-loop-jams.js +46 -7
  568. package/dist/ir/saturating-loop-jams.js.map +1 -1
  569. package/dist/ir/saturating-loop-jams.test.js +39 -1
  570. package/dist/ir/saturating-loop-jams.test.js.map +1 -1
  571. package/dist/ir/self-captures.js +8 -1
  572. package/dist/ir/self-captures.js.map +1 -1
  573. package/dist/ir/self-captures.test.js +15 -0
  574. package/dist/ir/self-captures.test.js.map +1 -1
  575. package/dist/ir/serialize.d.ts +3 -2
  576. package/dist/ir/serialize.js +6 -2
  577. package/dist/ir/serialize.js.map +1 -1
  578. package/dist/ir/validate.js +82 -4
  579. package/dist/ir/validate.js.map +1 -1
  580. package/dist/ir/validate.test.js +46 -0
  581. package/dist/ir/validate.test.js.map +1 -1
  582. package/dist/library/ir-policy.js +3 -0
  583. package/dist/library/ir-policy.js.map +1 -1
  584. package/dist/native/cache.js +2 -0
  585. package/dist/native/cache.js.map +1 -1
  586. package/dist/native/cli.js +2 -0
  587. package/dist/native/cli.js.map +1 -1
  588. package/dist/native/compiler.js +8 -2
  589. package/dist/native/compiler.js.map +1 -1
  590. package/dist/native/prepare.js +32 -3
  591. package/dist/native/prepare.js.map +1 -1
  592. package/dist/native/sanitizer.js +53 -3
  593. package/dist/native/sanitizer.js.map +1 -1
  594. package/dist/native/sanitizer.test.js +88 -2
  595. package/dist/native/sanitizer.test.js.map +1 -1
  596. package/dist/native/toolchain.js +9 -2
  597. package/dist/native/toolchain.js.map +1 -1
  598. package/dist/node-islands/boundary.d.ts +25 -1
  599. package/dist/node-islands/boundary.js +28 -2
  600. package/dist/node-islands/boundary.js.map +1 -1
  601. package/dist/node-islands/detect.d.ts +18 -1
  602. package/dist/node-islands/detect.js +365 -40
  603. package/dist/node-islands/detect.js.map +1 -1
  604. package/dist/node-islands/detect.test.js +151 -4
  605. package/dist/node-islands/detect.test.js.map +1 -1
  606. package/dist/node-islands/effect-gate.js +30 -20
  607. package/dist/node-islands/effect-gate.js.map +1 -1
  608. package/dist/node-islands/effect-gate.test.js +4 -1
  609. package/dist/node-islands/effect-gate.test.js.map +1 -1
  610. package/dist/node-islands/libnode.d.ts +1 -1
  611. package/dist/node-islands/libnode.js +1 -1
  612. package/dist/node-islands/libnode.js.map +1 -1
  613. package/dist/node-islands/modules.d.ts +28 -0
  614. package/dist/node-islands/modules.js +156 -0
  615. package/dist/node-islands/modules.js.map +1 -1
  616. package/dist/node-islands/modules.test.js +105 -1
  617. package/dist/node-islands/modules.test.js.map +1 -1
  618. package/dist/node-islands/policy.js +2 -3
  619. package/dist/node-islands/policy.js.map +1 -1
  620. package/dist/node-islands/probe-recorder.d.ts +1 -0
  621. package/dist/node-islands/probe-recorder.js +596 -0
  622. package/dist/node-islands/probe-recorder.js.map +1 -0
  623. package/dist/node-islands/probe-recorder.test.d.ts +1 -0
  624. package/dist/node-islands/probe-recorder.test.js +80 -0
  625. package/dist/node-islands/probe-recorder.test.js.map +1 -0
  626. package/dist/node-islands/report.test.js +2 -0
  627. package/dist/node-islands/report.test.js.map +1 -1
  628. package/dist/node-islands/rules.js +4 -0
  629. package/dist/node-islands/rules.js.map +1 -1
  630. package/dist/node-islands/scan.d.ts +7 -1
  631. package/dist/node-islands/scan.js +51 -2
  632. package/dist/node-islands/scan.js.map +1 -1
  633. package/dist/node-islands/snapshot.d.ts +38 -1
  634. package/dist/node-islands/snapshot.js +165 -161
  635. package/dist/node-islands/snapshot.js.map +1 -1
  636. package/dist/node-islands/snapshot.test.js +63 -2
  637. package/dist/node-islands/snapshot.test.js.map +1 -1
  638. package/dist/node-islands/summary.js +8 -0
  639. package/dist/node-islands/summary.js.map +1 -1
  640. package/dist/node-islands/type-strip.d.ts +11 -0
  641. package/dist/node-islands/type-strip.js +157 -0
  642. package/dist/node-islands/type-strip.js.map +1 -0
  643. package/dist/node-islands/type-strip.test.d.ts +1 -0
  644. package/dist/node-islands/type-strip.test.js +31 -0
  645. package/dist/node-islands/type-strip.test.js.map +1 -0
  646. package/dist/project-config.d.ts +4 -0
  647. package/dist/project-config.js +19 -0
  648. package/dist/project-config.js.map +1 -1
  649. package/dist/project-config.test.js +10 -0
  650. package/dist/project-config.test.js.map +1 -1
  651. package/package.json +20 -20
  652. package/surface-manifest.json +43 -11
@@ -10,15 +10,18 @@ import { NARROW_FIRST } from "../surfaces.js";
10
10
  import { recordShapeMismatchDiag, unionLiteralMismatchDiag, } from "../../../diagnostics/diagnostic.js";
11
11
  import { PoisonError, jsFuncNameOf, neverTaintedJsType } from "../lowerer.js";
12
12
  import { lowerIndexMergeHelper } from "../containers/indexed-objects.js";
13
+ import { recordMayKeepSource } from "../record-sources.js";
13
14
  import { PERF_ENTRY_MARKER, X509_PUBLIC_KEY_FIELD, isGenericCallableMemberType, jsOpenObjectType, } from "../../type-mapper.js";
14
15
  import { isSafeToRepeat } from "./evaluation-safety.js";
15
16
  import { tryLowerExpression } from "./try-lower-expression.js";
16
17
  import { classSymbolKeyOf, fenceSymbolFieldCopy, symbolFieldDisplayName, } from "../symbol-fields.js";
17
18
  import { planUnionRetag } from "../union-retag.js";
19
+ import { literalValues } from "../../literal-values.js";
18
20
  import { jsHoldsDynObject, runtimeOptionalStorageOperand } from "../lower-exprs.js";
19
21
  import { builtinGlobalValue } from "./property-access.js";
20
22
  import { holdsEvolvingArray } from "../lower-stmts.js";
21
23
  import { builtinNamespaceValue } from "../builtin-namespaces.js";
24
+ import { tsUntypedGlobalValue } from "../global-values.js";
22
25
  /** `{ a: 1, b: "x" }` → recordLit. The record type comes from the
23
26
  * contextual type when tsc has one (annotated declarations, arguments,
24
27
  * nested literals) and from the literal's own type otherwise — both intern
@@ -154,6 +157,34 @@ function pureKeyExpr(lowerer, expr) {
154
157
  return expr.templateSpans.nodes.every((s) => pureKeyExpr(lowerer, s.expression));
155
158
  return false;
156
159
  }
160
+ /** Whether expr reads no state that evaluating other code could change:
161
+ * literals, `const` bindings, and unary operators over those. */
162
+ function constantInitializer(lowerer, expr) {
163
+ if (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr))
164
+ return constantInitializer(lowerer, expr.expression);
165
+ if (ts.isAsExpression(expr) || ts.isTypeAssertion(expr))
166
+ return constantInitializer(lowerer, expr.expression);
167
+ if (ts.isStringLiteralLike(expr) || ts.isNumericLiteral(expr))
168
+ return true;
169
+ if (expr.kind === ts.SyntaxKind.TrueKeyword ||
170
+ expr.kind === ts.SyntaxKind.FalseKeyword ||
171
+ expr.kind === ts.SyntaxKind.NullKeyword)
172
+ return true;
173
+ if (ts.isPrefixUnaryExpression(expr) &&
174
+ (expr.operator === ts.SyntaxKind.MinusToken ||
175
+ expr.operator === ts.SyntaxKind.PlusToken ||
176
+ expr.operator === ts.SyntaxKind.ExclamationToken ||
177
+ expr.operator === ts.SyntaxKind.TildeToken))
178
+ return constantInitializer(lowerer, expr.operand);
179
+ if (ts.isIdentifier(expr)) {
180
+ const decls = lowerer.checker.getSymbolAtLocation(expr)?.declarations ?? [];
181
+ return (decls.length > 0 &&
182
+ decls.every((d) => ts.isVariableDeclaration(d) &&
183
+ ts.isVariableDeclarationList(d.parent) &&
184
+ (ts.getCombinedNodeFlags(d) & ts.NodeFlags.Const) !== 0));
185
+ }
186
+ return false;
187
+ }
157
188
  /** A property's field name: identifier/string-literal keys directly,
158
189
  * foldable computed keys through literalComputedKey (the syntax pass
159
190
  * fenced everything else). */
@@ -401,8 +432,11 @@ export function lowerDynObjectLiteral(lowerer, expr, boxValue, conditionalArms =
401
432
  // A PURE member read a JS file cannot lower (a namespace object
402
433
  // in an export aggregate): the slot takes a boxed fence closure —
403
434
  // the diagnostics defer to the runtime-fence ledger, the object
404
- // builds, and only USING the member stops the run.
405
- const pureMember = ts.isIdentifier(valueExpr);
435
+ // builds, and only USING the member stops the run. A TypeScript
436
+ // source keeps the compile-time refusal: the closure would answer
437
+ // typeof "function" and undefined members where Node reads the
438
+ // value.
439
+ const pureMember = ts.isIdentifier(valueExpr) && isJsSourceFile(ts.getSourceFileOfNode(prop));
406
440
  if (!(err instanceof PoisonError) || !pureMember)
407
441
  throw err;
408
442
  const fence = lowerer.deferToRuntimeFence(propDiagsBefore, prop, {
@@ -493,9 +527,7 @@ function fenceAccessorSpreadSource(lowerer, prop, srcShape) {
493
527
  /** Only definitely present later records replace earlier defaults. A
494
528
  * callback binding may store undefined despite its checker annotation. */
495
529
  function laterSpreadType(lowerer, expr) {
496
- let source = expr;
497
- while (ts.isParenthesizedExpression(source))
498
- source = source.expression;
530
+ const source = spreadSourceNode(lowerer, expr);
499
531
  if (ts.isIdentifier(source))
500
532
  return lowerer.lowerExpr(source).type;
501
533
  return lowerer.mapTypeOf(lowerer.typeOf(source));
@@ -772,7 +804,11 @@ function lowerOptionalIndexSpreadSource(lowerer, source, loc) {
772
804
  ? selected
773
805
  : { kind: "seqExpr", stmts: prefix, result: selected, type: recordType, loc };
774
806
  }
775
- export function lowerObjectLiteral(lowerer, expr, expected) {
807
+ export function lowerObjectLiteral(lowerer, expr, expected,
808
+ /** Build exactly at `expected` (one arm of a distributed union spread,
809
+ * lowerDistributedUnionSpread): the checker types the literal as the
810
+ * whole union, so its contextual type cannot choose the layout. */
811
+ exact = false) {
776
812
  // A spread of an untyped (`any`) source into a typed slot: TypeScript
777
813
  // types such a literal `any`, and the copy holds whatever own properties
778
814
  // the source has at runtime. Build it as a checked-dynamic object, then
@@ -861,7 +897,7 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
861
897
  tryLowerExpression(lowerer, property.expression)?.type.kind === "dyn"))) {
862
898
  return lowerDynObjectLiteral(lowerer, expr);
863
899
  }
864
- const unionClone = lowerUnionRecordClone(lowerer, expr);
900
+ const unionClone = exact ? null : lowerUnionRecordClone(lowerer, expr);
865
901
  if (unionClone !== null)
866
902
  return unionClone;
867
903
  // The RUNTIME-KEYED literal (JS): a computed key that doesn't fold to a
@@ -993,7 +1029,7 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
993
1029
  ts.constituentTypes(tsType).some((t) => ts.symbolNameOf(t.getSymbol()) === "PromiseLike")) {
994
1030
  tsType = lowerer.checker.getAwaitedType(tsType) ?? tsType;
995
1031
  }
996
- let mapped = lowerer.mapTypeOf(tsType);
1032
+ let mapped = exact && expected ? expected : lowerer.mapTypeOf(tsType);
997
1033
  // A checked-dynamic source has runtime keys even when later explicit
998
1034
  // fields make the literal's inferred type look like a fixed record.
999
1035
  // Keep a checked-dynamic destination in that representation so copying
@@ -1193,14 +1229,27 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1193
1229
  lowerer.unsupported("SC1090", deferredConditionalSpread, CONDITIONAL_SPREAD_FENCE);
1194
1230
  }
1195
1231
  if (!mapped || mapped.kind !== "record") {
1232
+ // Distribute against the contextual union when there is one (its arms
1233
+ // carry the discriminants), else against the literal's own union.
1234
+ const ctx = lowerer.mapTypeOf(tsType);
1235
+ const unionCtx = ctx?.kind === "union" ? ctx : mapped;
1236
+ const distributed = exact ? null : lowerDistributedUnionSpread(lowerer, expr, unionCtx);
1237
+ if (distributed)
1238
+ return distributed;
1196
1239
  // A spread merge over UNION-typed sources (`{ ...base, ...override }`
1197
1240
  // with `base`, `override` of arm-union types) has one field set per
1198
1241
  // combination of source arms, so its own type is a union of records.
1199
1242
  // No single arm of the contextual union can hold that value without
1200
1243
  // dropping the fields the other arms contributed (Node keeps them
1201
1244
  // all), and a runtime tag would be a guess about which arm the value
1202
- // "is". Refuse, naming the candidate arms.
1245
+ // "is". Build the object Node builds instead — the checked-dynamic
1246
+ // copy — and let the union's checked conversion validate it.
1203
1247
  if (mapped?.kind === "union") {
1248
+ if (!exact && unionCtx?.kind === "union") {
1249
+ const merged = lowerUnionSpreadMergeDyn(lowerer, expr, unionCtx);
1250
+ if (merged)
1251
+ return merged;
1252
+ }
1204
1253
  const members = tsType.isUnionType() ? ts.constituentTypes(tsType) : [tsType];
1205
1254
  const arms = [...new Set(members.map((m) => lowerer.checker.typeToString(m)))];
1206
1255
  lowerer.pushDiag(unionLiteralMismatchDiag(lowerer.checker.typeToString(tsType), arms, locOf(expr)));
@@ -1208,7 +1257,20 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1208
1257
  }
1209
1258
  lowerer.badType(expr, tsType);
1210
1259
  }
1211
- let type = mapped;
1260
+ // An enclosing literal that built at its OWN type (its context had no
1261
+ // layout) asks for the matching own layout of this nested literal. When
1262
+ // this literal's contextual layout cannot reach that slot, build at it:
1263
+ // the enclosing literal's coercion then decides, and the width lifts
1264
+ // (the index-signature overflow capture included) carry the whole tree
1265
+ // into the real destination. Otherwise the two layers disagree and the
1266
+ // SC2002 names the source literal's own type as the expected one.
1267
+ const layout = expected &&
1268
+ !typeEquals(mapped, expected) &&
1269
+ lowerer.widthLiftPlan(mapped, expected) === null &&
1270
+ typeEquals(lowerer.mapTypeOf(lowerer.typeOf(expr)) ?? mapped, expected)
1271
+ ? expected
1272
+ : mapped;
1273
+ let type = layout;
1212
1274
  // A fresh, plain literal can construct its fields directly for a known
1213
1275
  // destination with the same field set. This matters for inferred
1214
1276
  // conditional records: an empty array in one arm has no element layout
@@ -1219,15 +1281,31 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1219
1281
  expr.properties.nodes.every((p) => ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p))) {
1220
1282
  const own = lowerer.shapes.get(type.shapeId);
1221
1283
  const target = lowerer.shapes.get(expected.shapeId);
1222
- if (!own.tuple &&
1284
+ // The field set to compare is the LITERAL's: its checker type. The
1285
+ // contextual type a few lines up can be wider than what the literal
1286
+ // writes (an unannotated method checked against a generic slot's
1287
+ // signature: `{ content, details }` under a contextual
1288
+ // `AgentToolResult<unknown>` whose optional members the literal never
1289
+ // mentions), and the destination still names the same fields as the
1290
+ // literal, so the build can construct them directly. Only when the
1291
+ // contextual build has no conversion into the destination at all: a
1292
+ // value that converts today keeps its exact construction.
1293
+ const ownType = lowerer.mapTypeOf(lowerer.typeOf(expr));
1294
+ const ownChecked = ownType?.kind === "record" &&
1295
+ ownType.shapeId !== type.shapeId &&
1296
+ lowerer.widthLiftPlan(type, expected) === null
1297
+ ? lowerer.shapes.get(ownType.shapeId)
1298
+ : undefined;
1299
+ const fits = (from) => !from.tuple &&
1223
1300
  !target.tuple &&
1224
- !own.indexValue &&
1301
+ !from.indexValue &&
1225
1302
  !target.indexValue &&
1226
- !shapeHasAccessorSlots(own) &&
1303
+ !shapeHasAccessorSlots(from) &&
1227
1304
  !shapeHasAccessorSlots(target) &&
1228
- target.fields.every((field) => own.fields.some((candidate) => candidate.name === field.name) ||
1305
+ target.fields.every((field) => from.fields.some((candidate) => candidate.name === field.name) ||
1229
1306
  (field.type.kind === "union" && lowerer.armTag(field.type.unionId, UNDEFINED_T) >= 0)) &&
1230
- own.fields.every((field) => target.fields.some((candidate) => candidate.name === field.name))) {
1307
+ from.fields.every((field) => target.fields.some((candidate) => candidate.name === field.name));
1308
+ if (fits(own) || (ownChecked !== undefined && fits(ownChecked))) {
1231
1309
  type = { kind: "record", shapeId: expected.shapeId };
1232
1310
  }
1233
1311
  }
@@ -1357,7 +1435,13 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1357
1435
  lowerer.badType(valueNode, lowerer.typeOf(valueNode));
1358
1436
  overrides.push({ name, value });
1359
1437
  }
1360
- return { kind: "recordClone", source, overrides, type, loc };
1438
+ return lowerer.keyOrder.clone({
1439
+ kind: "recordClone",
1440
+ source,
1441
+ overrides,
1442
+ type,
1443
+ loc,
1444
+ });
1361
1445
  }
1362
1446
  leadingSpreadLowered = source;
1363
1447
  }
@@ -1591,6 +1675,16 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1591
1675
  const droppedNames = new Set();
1592
1676
  // The order this literal creates its keys in (key-order-witness.ts).
1593
1677
  const orderSegments = [];
1678
+ // The literal's contributors when a spread drops source keys the target
1679
+ // has no slot for (key-order-witness.ts keepWidthSpreads). Conditional
1680
+ // spreads leave the literal to the static copy.
1681
+ const widthSegments = [];
1682
+ let widthDrops = false;
1683
+ let widthBlocked = false;
1684
+ // A checked-dynamic spread source's own keys are a run-time fact: even
1685
+ // when the checker's shape fits the target, the copied object can hold
1686
+ // more (slice 3).
1687
+ let widthDynSource = false;
1594
1688
  for (const prop of expr.properties.nodes) {
1595
1689
  // A later contributor can mutate an earlier source or overwrite a
1596
1690
  // value with effects. Snapshot pending fields at their source position,
@@ -1604,6 +1698,7 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1604
1698
  if (ts.isSpreadAssignment(prop)) {
1605
1699
  const cs = conditionalSpreadOf(prop.expression) ?? andSpreadOf(lowerer, prop.expression);
1606
1700
  if (cs && cs !== "unsupported") {
1701
+ widthBlocked = true;
1607
1702
  // `...(c ? { k: v } : {})` — one conditional field per carried
1608
1703
  // property at the spread's own position: cond evaluates exactly
1609
1704
  // once (into a hidden local when several fields test it), each
@@ -1676,15 +1771,25 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1676
1771
  // earlier ones (JS last-write-wins). Evaluate the source exactly
1677
1772
  // once, including empty sources and those whose fields are all
1678
1773
  // overridden. Field snapshots above preserve the copy order.
1679
- let srcNode = prop.expression;
1680
- while (ts.isParenthesizedExpression(srcNode))
1681
- srcNode = srcNode.expression;
1774
+ const srcNode = spreadSourceNode(lowerer, prop.expression);
1682
1775
  const condRecord = conditionalRecordSpreadOf(lowerer, prop.expression);
1683
- const srcLowered = capture(condRecord !== null
1776
+ let srcLowered = capture(condRecord !== null
1684
1777
  ? lowerConditionalRecordSpread(lowerer, condRecord, locOf(prop))
1685
1778
  : prop === expr.properties.nodes[0] && leadingSpreadLowered !== null
1686
1779
  ? leadingSpreadLowered
1687
- : lowerer.lowerExpr(srcNode));
1780
+ : (lowerer.spreadArmOverrides.get(srcNode) ?? lowerer.lowerExpr(srcNode)));
1781
+ {
1782
+ const narrowed = checkedSpreadSource(lowerer, srcNode, srcLowered);
1783
+ if (narrowed !== null)
1784
+ srcLowered = capture(narrowed);
1785
+ }
1786
+ if (condRecord !== null || srcLowered.kind !== "varRef")
1787
+ widthBlocked = true;
1788
+ else {
1789
+ widthSegments.push({ kind: "spread", value: srcLowered });
1790
+ if (srcLowered.type.kind === "dyn")
1791
+ widthDynSource = true;
1792
+ }
1688
1793
  // Array callbacks can receive a record-or-undefined ABI even when
1689
1794
  // TypeScript describes a required record. Select the copy strategy
1690
1795
  // from that stored representation so absent sources copy nothing.
@@ -1704,12 +1809,10 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1704
1809
  const def = lowerer.unions.get(srcType.unionId);
1705
1810
  const recArms = def?.arms.filter((a) => a.kind === "record") ?? [];
1706
1811
  if (!def ||
1707
- recArms.length !== 1 ||
1812
+ recArms.length === 0 ||
1708
1813
  !def.arms.every((a) => a.kind === "record" || isUnitType(a))) {
1709
1814
  lowerer.unsupported("SC1090", prop, `object spread of '${lowerer.fmt(srcType)}' sources (only known record shapes spread — ${NARROW_FIRST})`);
1710
1815
  }
1711
- const recArm = recArms[0];
1712
- const recTag = def.arms.indexOf(recArm);
1713
1816
  // A checked-dynamic VALUE under the union-mapped checker type
1714
1817
  // (a JS dyn-holding binding): the present/absent desugar tests
1715
1818
  // union tags a dyn box does not carry — fence honestly instead
@@ -1718,230 +1821,240 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
1718
1821
  if (probedSrc?.type.kind === "dyn") {
1719
1822
  lowerer.unsupported("SC1090", prop, `object spread of a checked-dynamic '${lowerer.fmt(srcType)}' source (${NARROW_FIRST})`);
1720
1823
  }
1721
- const srcShape = lowerer.shapes.get(recArm.shapeId);
1722
- if (!srcShape)
1723
- throw new InternalCompilerError(`lowerer bug: spread of unknown shape ${recArm.shapeId}`);
1724
- fenceAccessorSpreadSource(lowerer, prop, srcShape);
1725
- if (srcShape.indexValue || shape.indexValue) {
1726
- const fallback = dynSpreadLiteral(lowerer, expr, type);
1727
- if (fallback)
1728
- return fallback;
1729
- lowerer.unsupported("SC1090", prop, "object spread involving index-signature shapes (overflow keys are runtime state — copy the fields you need explicitly)");
1730
- }
1731
- const laterNames = new Set();
1732
- for (const later of expr.properties.nodes.slice(expr.properties.nodes.indexOf(prop) + 1)) {
1733
- if (ts.isSpreadAssignment(later)) {
1734
- if (conditionalSpreadOf(later.expression) || andSpreadOf(lowerer, later.expression))
1824
+ // Several record arms: each arm contributes its own fields behind its
1825
+ // tag test, wrapping the earlier contributor, so a key that arm lacks
1826
+ // keeps the earlier value (JS copies only the arm's own keys).
1827
+ for (const recArmCandidate of recArms) {
1828
+ const recArm = recArmCandidate;
1829
+ const recTag = def.arms.indexOf(recArm);
1830
+ const srcShape = lowerer.shapes.get(recArm.shapeId);
1831
+ if (!srcShape)
1832
+ throw new InternalCompilerError(`lowerer bug: spread of unknown shape ${recArm.shapeId}`);
1833
+ fenceAccessorSpreadSource(lowerer, prop, srcShape);
1834
+ if (srcShape.indexValue || shape.indexValue) {
1835
+ const fallback = dynSpreadLiteral(lowerer, expr, type);
1836
+ if (fallback)
1837
+ return fallback;
1838
+ lowerer.unsupported("SC1090", prop, "object spread involving index-signature shapes (overflow keys are runtime state — copy the fields you need explicitly)");
1839
+ }
1840
+ const laterNames = new Set();
1841
+ for (const later of expr.properties.nodes.slice(expr.properties.nodes.indexOf(prop) + 1)) {
1842
+ if (ts.isSpreadAssignment(later)) {
1843
+ if (conditionalSpreadOf(later.expression) || andSpreadOf(lowerer, later.expression))
1844
+ continue;
1845
+ const lt = laterSpreadType(lowerer, later.expression);
1846
+ if (lt?.kind === "record") {
1847
+ // A field the later source may lack defines nothing.
1848
+ for (const lf of lowerer.shapes.get(lt.shapeId)?.fields ?? [])
1849
+ if (!canLackField(lf.type))
1850
+ laterNames.add(lf.name);
1851
+ }
1735
1852
  continue;
1736
- const lt = laterSpreadType(lowerer, later.expression);
1737
- if (lt?.kind === "record") {
1738
- // A field the later source may lack defines nothing.
1739
- for (const lf of lowerer.shapes.get(lt.shapeId)?.fields ?? [])
1740
- if (!canLackField(lf.type))
1741
- laterNames.add(lf.name);
1742
1853
  }
1743
- continue;
1744
- }
1745
- if (later.name &&
1746
- (ts.isIdentifier(later.name) ||
1747
- ts.isStringLiteral(later.name) ||
1748
- ts.isNumericLiteral(later.name) ||
1749
- (ts.isComputedPropertyName(later.name) &&
1750
- literalComputedKey(lowerer, later.name) !== null))) {
1751
- laterNames.add(propNameText(lowerer, later.name));
1752
- }
1753
- }
1754
- const srcRef = () => srcLowered ?? lowerer.lowerExpr(srcNode);
1755
- const srcNarrowed = () => ({
1756
- kind: "unionNarrow",
1757
- unionId: srcType.unionId,
1758
- tag: recTag,
1759
- value: srcRef(),
1760
- type: recArm,
1761
- loc: locOf(prop),
1762
- });
1763
- // The source PROVEN present: the checked extraction `x!` lowers to
1764
- // (narrowedArmHelper + checkedNarrowCall). A required target field
1765
- // with no earlier contributor has no absent representation, so
1766
- // `{ ...maybeRecord }` into it must find the record: the unit arm
1767
- // throws the catchable TypeError instead of leaving the slot unset
1768
- // (divergence 38's lying-assertion stance — the checker typed the
1769
- // spread's fields optional, so only a cast or an unchecked indexed
1770
- // read reaches this shape).
1771
- const srcPresent = () => {
1772
- const helper = lowerer.narrowedArmHelper(srcType.unionId, recArm, locOf(prop));
1773
- return helper
1774
- ? lowerer.checkedNarrowCall(helper, srcRef(), recArm, locOf(prop))
1775
- : srcNarrowed();
1776
- };
1777
- orderSegments.push({ kind: "spread", shapeId: recArm.shapeId, maybeAbsent: true });
1778
- for (const f of srcShape.fields) {
1779
- if (laterNames.has(f.name))
1780
- continue;
1781
- const targetType = fieldTypes.get(f.name);
1782
- // No slot on the target shape: the copy DROPS the field
1783
- // (divergence 36's stance, same as the plain-record spread).
1784
- if (!targetType)
1785
- continue;
1786
- if (conditionalNames.has(f.name)) {
1787
- const conditionalDyn = conditionalFallback();
1788
- if (conditionalDyn)
1789
- return conditionalDyn;
1790
- lowerer.unsupported("SC1090", prop, `spread of '${f.name}' over an earlier conditional spread (the desugar keeps one entry per name — restructure so each name has one contributor)`);
1854
+ if (later.name &&
1855
+ (ts.isIdentifier(later.name) ||
1856
+ ts.isStringLiteral(later.name) ||
1857
+ ts.isNumericLiteral(later.name) ||
1858
+ (ts.isComputedPropertyName(later.name) &&
1859
+ literalComputedKey(lowerer, later.name) !== null))) {
1860
+ laterNames.add(propNameText(lowerer, later.name));
1861
+ }
1791
1862
  }
1792
- const fRead = (present = false) => ({
1793
- kind: "recordGet",
1794
- obj: present ? srcPresent() : srcNarrowed(),
1795
- shapeId: recArm.shapeId,
1796
- field: f.name,
1797
- type: f.type,
1798
- loc: locOf(prop),
1799
- });
1800
- let cond = {
1801
- kind: "unionIsTag",
1863
+ const srcRef = () => srcLowered ?? lowerer.lowerExpr(srcNode);
1864
+ const srcNarrowed = () => ({
1865
+ kind: "unionNarrow",
1802
1866
  unionId: srcType.unionId,
1803
1867
  tag: recTag,
1804
- negated: false,
1805
1868
  value: srcRef(),
1806
- type: BOOL,
1869
+ type: recArm,
1807
1870
  loc: locOf(prop),
1871
+ });
1872
+ // The source PROVEN present: the checked extraction `x!` lowers to
1873
+ // (narrowedArmHelper + checkedNarrowCall). A required target field
1874
+ // with no earlier contributor has no absent representation, so
1875
+ // `{ ...maybeRecord }` into it must find the record: the unit arm
1876
+ // throws the catchable TypeError instead of leaving the slot unset
1877
+ // (divergence 38's lying-assertion stance — the checker typed the
1878
+ // spread's fields optional, so only a cast or an unchecked indexed
1879
+ // read reaches this shape).
1880
+ const srcPresent = () => {
1881
+ const helper = lowerer.narrowedArmHelper(srcType.unionId, recArm, locOf(prop));
1882
+ return helper
1883
+ ? lowerer.checkedNarrowCall(helper, srcRef(), recArm, locOf(prop))
1884
+ : srcNarrowed();
1808
1885
  };
1809
- let thenVal;
1810
- // A source field that can be absent copies only when present.
1811
- const fieldPresent = () => ({
1812
- kind: "logical",
1813
- op: "&&",
1814
- left: cond,
1815
- right: {
1816
- kind: "recordHas",
1817
- obj: {
1818
- kind: "unionNarrow",
1819
- unionId: srcType.unionId,
1820
- tag: recTag,
1821
- value: srcRef(),
1822
- type: recArm,
1823
- loc: locOf(prop),
1824
- },
1886
+ orderSegments.push({ kind: "spread", shapeId: recArm.shapeId, maybeAbsent: true });
1887
+ for (const f of srcShape.fields) {
1888
+ if (laterNames.has(f.name))
1889
+ continue;
1890
+ const targetType = fieldTypes.get(f.name);
1891
+ // No slot on the target shape: the static copy skips the field,
1892
+ // and the literal keeps it through a kept source view when the
1893
+ // program can observe it (keepWidthSpreads).
1894
+ if (!targetType) {
1895
+ widthDrops = true;
1896
+ continue;
1897
+ }
1898
+ if (conditionalNames.has(f.name)) {
1899
+ const conditionalDyn = conditionalFallback();
1900
+ if (conditionalDyn)
1901
+ return conditionalDyn;
1902
+ lowerer.unsupported("SC1090", prop, `spread of '${f.name}' over an earlier conditional spread (the desugar keeps one entry per name — restructure so each name has one contributor)`);
1903
+ }
1904
+ const fRead = (present = false) => ({
1905
+ kind: "recordGet",
1906
+ obj: present ? srcPresent() : srcNarrowed(),
1825
1907
  shapeId: recArm.shapeId,
1826
1908
  field: f.name,
1909
+ type: f.type,
1910
+ loc: locOf(prop),
1911
+ });
1912
+ let cond = {
1913
+ kind: "unionIsTag",
1914
+ unionId: srcType.unionId,
1915
+ tag: recTag,
1916
+ negated: false,
1917
+ value: srcRef(),
1827
1918
  type: BOOL,
1828
1919
  loc: locOf(prop),
1829
- },
1830
- type: BOOL,
1831
- loc: locOf(prop),
1832
- });
1833
- const canBeAbsent = f.type.kind === "union" && lowerer.armTag(f.type.unionId, UNDEFINED_T) >= 0;
1834
- if (typeEquals(f.type, targetType)) {
1835
- thenVal = fRead();
1836
- if (canBeAbsent)
1837
- cond = fieldPresent();
1838
- }
1839
- else if (f.type.kind === "union" &&
1840
- lowerer.armTag(f.type.unionId, UNDEFINED_T) >= 0 &&
1841
- typeEquals(lowerer.stripUndefinedArm(f.type), targetType)) {
1842
- // Optional source field into a required target slot: present
1843
- // means the source holds the record AND the field its value
1844
- // arm — the spread-override completion, union-source form.
1845
- const ftUndef = lowerer.armTag(f.type.unionId, UNDEFINED_T);
1846
- cond = {
1920
+ };
1921
+ let thenVal;
1922
+ // A source field that can be absent copies only when present.
1923
+ const fieldPresent = () => ({
1847
1924
  kind: "logical",
1848
1925
  op: "&&",
1849
1926
  left: cond,
1850
1927
  right: {
1851
- kind: "unionIsTag",
1852
- unionId: f.type.unionId,
1853
- tag: ftUndef,
1854
- negated: true,
1855
- value: fRead(),
1928
+ kind: "recordHas",
1929
+ obj: {
1930
+ kind: "unionNarrow",
1931
+ unionId: srcType.unionId,
1932
+ tag: recTag,
1933
+ value: srcRef(),
1934
+ type: recArm,
1935
+ loc: locOf(prop),
1936
+ },
1937
+ shapeId: recArm.shapeId,
1938
+ field: f.name,
1856
1939
  type: BOOL,
1857
1940
  loc: locOf(prop),
1858
1941
  },
1859
1942
  type: BOOL,
1860
1943
  loc: locOf(prop),
1861
- };
1862
- const ftDef = lowerer.unions.get(f.type.unionId);
1863
- if (targetType.kind === "union") {
1864
- const retag = lowerer.unionRetagHelper(f.type.unionId, targetType.unionId, locOf(prop));
1865
- if (!retag) {
1866
- lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' cannot re-tag into '${lowerer.fmt(targetType)}' behind the present-test`));
1867
- throw new PoisonError();
1868
- }
1869
- thenVal = {
1870
- kind: "call",
1871
- callee: retag,
1872
- args: [fRead()],
1873
- type: targetType,
1874
- loc: locOf(prop),
1875
- };
1944
+ });
1945
+ const canBeAbsent = f.type.kind === "union" && lowerer.armTag(f.type.unionId, UNDEFINED_T) >= 0;
1946
+ if (typeEquals(f.type, targetType)) {
1947
+ thenVal = fRead();
1948
+ if (canBeAbsent)
1949
+ cond = fieldPresent();
1876
1950
  }
1877
- else if (ftDef &&
1878
- ftDef.arms.length === 2 &&
1879
- lowerer.armTag(f.type.unionId, targetType) >= 0) {
1880
- thenVal = {
1881
- kind: "unionNarrow",
1882
- unionId: f.type.unionId,
1883
- tag: lowerer.armTag(f.type.unionId, targetType),
1884
- value: fRead(),
1885
- type: targetType,
1951
+ else if (f.type.kind === "union" &&
1952
+ lowerer.armTag(f.type.unionId, UNDEFINED_T) >= 0 &&
1953
+ typeEquals(lowerer.stripUndefinedArm(f.type), targetType)) {
1954
+ // Optional source field into a required target slot: present
1955
+ // means the source holds the record AND the field its value
1956
+ // arm — the spread-override completion, union-source form.
1957
+ const ftUndef = lowerer.armTag(f.type.unionId, UNDEFINED_T);
1958
+ cond = {
1959
+ kind: "logical",
1960
+ op: "&&",
1961
+ left: cond,
1962
+ right: {
1963
+ kind: "unionIsTag",
1964
+ unionId: f.type.unionId,
1965
+ tag: ftUndef,
1966
+ negated: true,
1967
+ value: fRead(),
1968
+ type: BOOL,
1969
+ loc: locOf(prop),
1970
+ },
1971
+ type: BOOL,
1886
1972
  loc: locOf(prop),
1887
1973
  };
1974
+ const ftDef = lowerer.unions.get(f.type.unionId);
1975
+ if (targetType.kind === "union") {
1976
+ const retag = lowerer.unionRetagHelper(f.type.unionId, targetType.unionId, locOf(prop));
1977
+ if (!retag) {
1978
+ lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' cannot re-tag into '${lowerer.fmt(targetType)}' behind the present-test`));
1979
+ throw new PoisonError();
1980
+ }
1981
+ thenVal = {
1982
+ kind: "call",
1983
+ callee: retag,
1984
+ args: [fRead()],
1985
+ type: targetType,
1986
+ loc: locOf(prop),
1987
+ };
1988
+ }
1989
+ else if (ftDef &&
1990
+ ftDef.arms.length === 2 &&
1991
+ lowerer.armTag(f.type.unionId, targetType) >= 0) {
1992
+ thenVal = {
1993
+ kind: "unionNarrow",
1994
+ unionId: f.type.unionId,
1995
+ tag: lowerer.armTag(f.type.unionId, targetType),
1996
+ value: fRead(),
1997
+ type: targetType,
1998
+ loc: locOf(prop),
1999
+ };
2000
+ }
2001
+ else {
2002
+ lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' cannot narrow into '${lowerer.fmt(targetType)}' behind the present-test`));
2003
+ throw new PoisonError();
2004
+ }
1888
2005
  }
1889
2006
  else {
1890
- lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' cannot narrow into '${lowerer.fmt(targetType)}' behind the present-test`));
1891
- throw new PoisonError();
1892
- }
1893
- }
1894
- else {
1895
- // The width-lift fallback (arm wrap, re-tag, nested
1896
- // reshape) — the same per-field rule the slot coercion
1897
- // applies. Runs AFTER the optional-completion branch: a
1898
- // present-test has its own semantics a re-tag's stranded
1899
- // undefined trap must not shadow.
1900
- const lift = lowerer.widthLiftPlan(f.type, targetType);
1901
- if (!lift) {
1902
- lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' does not lift into '${lowerer.fmt(targetType)}'`));
1903
- throw new PoisonError();
1904
- }
1905
- thenVal = lowerer.applyWidthLift(lift, fRead(), targetType, locOf(prop));
1906
- if (canBeAbsent)
1907
- cond = fieldPresent();
1908
- }
1909
- const at = fields.findIndex((x) => x.name === f.name);
1910
- const elseVal = at >= 0 ? fields[at].value : lowerer.absentFieldValue(targetType, locOf(prop));
1911
- if (!elseVal) {
1912
- if (canBeAbsent) {
1913
- // The FIELD itself can be missing from a present source:
1914
- // no extraction makes it exist.
1915
- lowerer.unsupported("SC1090", prop, `object spread of '${lowerer.fmt(srcType)}' sources where '${f.name}' has no earlier contributor (the absent arm leaves the required field unset — spread defaults first: { ...defaults, ...overrides })`);
1916
- }
1917
- // No earlier contributor and the target slot cannot be absent:
1918
- // the source must be here. Copy through the checked extraction
1919
- // (the absent unit arm throws) — the same read the width lift
1920
- // below wraps, so a lift still applies.
1921
- let strict = fRead(true);
1922
- if (!typeEquals(f.type, targetType)) {
2007
+ // The width-lift fallback (arm wrap, re-tag, nested
2008
+ // reshape) — the same per-field rule the slot coercion
2009
+ // applies. Runs AFTER the optional-completion branch: a
2010
+ // present-test has its own semantics a re-tag's stranded
2011
+ // undefined trap must not shadow.
1923
2012
  const lift = lowerer.widthLiftPlan(f.type, targetType);
1924
2013
  if (!lift) {
1925
2014
  lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' does not lift into '${lowerer.fmt(targetType)}'`));
1926
2015
  throw new PoisonError();
1927
2016
  }
1928
- strict = lowerer.applyWidthLift(lift, strict, targetType, locOf(prop));
2017
+ thenVal = lowerer.applyWidthLift(lift, fRead(), targetType, locOf(prop));
2018
+ if (canBeAbsent)
2019
+ cond = fieldPresent();
1929
2020
  }
1930
- fields.push({ name: f.name, value: strict });
1931
- continue;
2021
+ const at = fields.findIndex((x) => x.name === f.name);
2022
+ const elseVal = at >= 0 ? fields[at].value : lowerer.absentFieldValue(targetType, locOf(prop));
2023
+ if (!elseVal) {
2024
+ if (canBeAbsent) {
2025
+ // The FIELD itself can be missing from a present source:
2026
+ // no extraction makes it exist.
2027
+ lowerer.unsupported("SC1090", prop, `object spread of '${lowerer.fmt(srcType)}' sources where '${f.name}' has no earlier contributor (the absent arm leaves the required field unset — spread defaults first: { ...defaults, ...overrides })`);
2028
+ }
2029
+ // No earlier contributor and the target slot cannot be absent:
2030
+ // the source must be here. Copy through the checked extraction
2031
+ // (the absent unit arm throws) — the same read the width lift
2032
+ // below wraps, so a lift still applies.
2033
+ let strict = fRead(true);
2034
+ if (!typeEquals(f.type, targetType)) {
2035
+ const lift = lowerer.widthLiftPlan(f.type, targetType);
2036
+ if (!lift) {
2037
+ lowerer.pushDiag(recordShapeMismatchDiag(lowerer.fmt(type), lowerer.fmt(recArm), locOf(prop), `spread field '${f.name}': '${lowerer.fmt(f.type)}' does not lift into '${lowerer.fmt(targetType)}'`));
2038
+ throw new PoisonError();
2039
+ }
2040
+ strict = lowerer.applyWidthLift(lift, strict, targetType, locOf(prop));
2041
+ }
2042
+ fields.push({ name: f.name, value: strict });
2043
+ continue;
2044
+ }
2045
+ const merged = {
2046
+ kind: "ternary",
2047
+ cond,
2048
+ then: thenVal,
2049
+ else_: elseVal,
2050
+ type: targetType,
2051
+ loc: locOf(prop),
2052
+ };
2053
+ if (at >= 0)
2054
+ fields[at] = { name: f.name, value: merged };
2055
+ else
2056
+ fields.push({ name: f.name, value: merged });
1932
2057
  }
1933
- const merged = {
1934
- kind: "ternary",
1935
- cond,
1936
- then: thenVal,
1937
- else_: elseVal,
1938
- type: targetType,
1939
- loc: locOf(prop),
1940
- };
1941
- if (at >= 0)
1942
- fields[at] = { name: f.name, value: merged };
1943
- else
1944
- fields.push({ name: f.name, value: merged });
1945
2058
  }
1946
2059
  continue;
1947
2060
  }
@@ -2006,13 +2119,15 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
2006
2119
  if (laterNames.has(f.name))
2007
2120
  continue;
2008
2121
  let targetType = fieldTypes.get(f.name);
2009
- // A source field with NO slot on the target shape: the copy
2010
- // DROPS it — a spread of a wider record into a narrower literal
2011
- // is width subtyping in spread clothing, divergence 36's stance
2012
- // (Node's object would keep the key); the read is pure, so
2013
- // skipping evaluates nothing.
2014
- if (!targetType)
2122
+ // A source field with NO slot on the target shape: the static
2123
+ // copy skips it (the read is pure, so skipping evaluates nothing),
2124
+ // and the literal keeps it through a kept source view when the
2125
+ // program can observe the target (keepWidthSpreads).
2126
+ if (!targetType) {
2127
+ if (srcLowered.type.kind !== "dyn")
2128
+ widthDrops = true;
2015
2129
  continue;
2130
+ }
2016
2131
  // A runtime-optional source field (an unchecked indexed read the
2017
2132
  // checker calls present) keeps its undefined arm in the copy, as a
2018
2133
  // property initialized from that read would.
@@ -2251,7 +2366,8 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
2251
2366
  lowerer.diags.slice(propDiagsBefore).some((d) => d.code === "SC9001")) {
2252
2367
  throw err;
2253
2368
  }
2254
- lowerer.runtimeFences.push(...lowerer.diags.splice(propDiagsBefore));
2369
+ for (const d of lowerer.diags.splice(propDiagsBefore))
2370
+ lowerer.runtimeFences.push(d);
2255
2371
  droppedNames.add(name);
2256
2372
  continue;
2257
2373
  }
@@ -2297,6 +2413,7 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
2297
2413
  fields.splice(at, 1);
2298
2414
  fields.push({ name, value });
2299
2415
  orderSegments.push({ kind: "key", name, definite: true });
2416
+ widthSegments.push({ kind: "key", name });
2300
2417
  }
2301
2418
  // Optional fields may be omitted: the field holds its absent state.
2302
2419
  // tsc rejects omission of required fields before lowering.
@@ -2323,10 +2440,46 @@ export function lowerObjectLiteral(lowerer, expr, expected) {
2323
2440
  }
2324
2441
  }
2325
2442
  const result = lowerer.keyOrder.literal({ kind: "recordLit", fields, type, loc }, orderSegments);
2443
+ if (!widthBlocked &&
2444
+ spreadStmts.length > 0 &&
2445
+ type.kind === "record" &&
2446
+ recordMayKeepSource(lowerer, type))
2447
+ lowerer.keyOrder.width(result, widthSegments, widthDrops || widthDynSource);
2326
2448
  return spreadStmts.length === 0
2327
2449
  ? result
2328
2450
  : { kind: "seqExpr", stmts: spreadStmts, result, type, loc };
2329
2451
  }
2452
+ /** A spread source whose stored union holds several record arms while the
2453
+ * checker narrowed this site to fewer (`if (b?.type === "tool") ({ ...b })`
2454
+ * on an element read, or a type predicate's arm): extract the narrowed
2455
+ * value through the CHECKED narrowing, so every arm the checker excludes,
2456
+ * including a hidden absence arm, throws the catchable TypeError instead of
2457
+ * being copied. Null when the stored union has at most one record arm (the
2458
+ * present-test desugar owns that case), when the site type is not a
2459
+ * narrowing of the stored union, or in JavaScript files. */
2460
+ function checkedSpreadSource(lowerer, node, stored) {
2461
+ if (stored.type.kind !== "union")
2462
+ return null;
2463
+ if (isJsSourceFile(ts.getSourceFileOfNode(node)))
2464
+ return null;
2465
+ const def = lowerer.unions.get(stored.type.unionId);
2466
+ if (!def || def.arms.filter((arm) => arm.kind === "record").length < 2)
2467
+ return null;
2468
+ const site = lowerer.mapTypeOf(lowerer.typeOf(node));
2469
+ if (!site || typeEquals(site, stored.type))
2470
+ return null;
2471
+ const loc = stored.loc;
2472
+ if (site.kind === "record") {
2473
+ if (lowerer.armTag(stored.type.unionId, site) < 0)
2474
+ return null;
2475
+ const helper = lowerer.narrowedArmHelper(stored.type.unionId, site, loc);
2476
+ return helper ? lowerer.checkedNarrowCall(helper, stored, site, loc) : null;
2477
+ }
2478
+ if (site.kind !== "union")
2479
+ return null;
2480
+ const helper = lowerer.narrowedRetagHelper(node, stored.type.unionId, site.unionId, loc);
2481
+ return helper ? { kind: "call", callee: helper, args: [stored], type: site, loc } : null;
2482
+ }
2330
2483
  /** The field type is an array, or a union with exactly one array arm. */
2331
2484
  function singleArrayArm(lowerer, type) {
2332
2485
  if (type.kind === "array")
@@ -2342,6 +2495,269 @@ function isEmptyArrayLiteral(node) {
2342
2495
  inner = inner.expression;
2343
2496
  return ts.isArrayLiteralExpression(inner) && inner.elements.nodes.length === 0;
2344
2497
  }
2498
+ /** `{ ...u, k: v }` where the spread source `u` is a union of records (and
2499
+ * unit arms): TypeScript types the literal as the union of one spread per
2500
+ * source arm, so it is built that way. The source evaluates once into a
2501
+ * hidden local; a tag dispatch builds the running arm's literal with that
2502
+ * arm as the source, at the destination arm chosen for it:
2503
+ * 1. the arms the discriminant's last writer selects (an explicit
2504
+ * property, or the source arm when it carries the field);
2505
+ * 2. among those, the source arm itself (`{ ...command, id }`);
2506
+ * 3. else the one arm whose keys are exactly the keys the literal writes.
2507
+ * Every key a contributor may write must be a field of the destination:
2508
+ * the record path's width copy would drop the others, and Node keeps them.
2509
+ * So a genuine merge (`{ ...http, ...patch }` where `patch` may carry a
2510
+ * stdio-only key) stays refused. A stored source the checker narrowed
2511
+ * converts through the checked re-tag first, so an excluded arm (a hidden
2512
+ * absence arm included) throws the catchable TypeError. Null when the
2513
+ * literal is not this shape, before anything is lowered except the
2514
+ * source; the caller keeps its refusal. */
2515
+ function lowerDistributedUnionSpread(lowerer, expr, mapped) {
2516
+ if (isJsSourceFile(ts.getSourceFileOfNode(expr)))
2517
+ return null;
2518
+ if (mapped?.kind !== "union")
2519
+ return null;
2520
+ const target = lowerer.unions.get(mapped.unionId);
2521
+ if (!target)
2522
+ return null;
2523
+ const props = expr.properties.nodes;
2524
+ // Exactly one distributed source: a plain spread whose checker type maps
2525
+ // to a union of records and unit arms. Several would be a product.
2526
+ let spreadIndex = -1;
2527
+ let srcNode = null;
2528
+ for (let i = 0; i < props.length; i++) {
2529
+ const prop = props[i];
2530
+ if (!ts.isSpreadAssignment(prop))
2531
+ continue;
2532
+ if (conditionalSpreadOf(prop.expression) !== null ||
2533
+ andSpreadOf(lowerer, prop.expression) !== null ||
2534
+ conditionalRecordSpreadOf(lowerer, prop.expression) !== null)
2535
+ continue;
2536
+ let node = prop.expression;
2537
+ while (ts.isParenthesizedExpression(node))
2538
+ node = node.expression;
2539
+ if (lowerer.mapTypeOf(lowerer.typeOf(node))?.kind !== "union")
2540
+ continue;
2541
+ if (srcNode !== null)
2542
+ return null;
2543
+ spreadIndex = i;
2544
+ srcNode = node;
2545
+ }
2546
+ if (srcNode === null)
2547
+ return null;
2548
+ const site = srcNode;
2549
+ // The source evaluates before the dispatch, so everything written before
2550
+ // it must be free of effects. When the source itself may have effects, an
2551
+ // earlier property may also not read a value they could change: those
2552
+ // properties must be constants.
2553
+ const sourcePure = pureKeyExpr(lowerer, site);
2554
+ for (const prop of props.slice(0, spreadIndex)) {
2555
+ if (sourcePure && ts.isShorthandPropertyAssignment(prop))
2556
+ continue;
2557
+ if (ts.isPropertyAssignment(prop) &&
2558
+ (sourcePure
2559
+ ? pureKeyExpr(lowerer, prop.initializer)
2560
+ : constantInitializer(lowerer, prop.initializer)))
2561
+ continue;
2562
+ return null;
2563
+ }
2564
+ const siteType = lowerer.mapTypeOf(lowerer.typeOf(site));
2565
+ if (siteType?.kind !== "union")
2566
+ return null;
2567
+ const siteDef = lowerer.unions.get(siteType.unionId);
2568
+ if (!siteDef || !siteDef.arms.every((arm) => arm.kind === "record" || isUnitType(arm)))
2569
+ return null;
2570
+ const discField = target.discriminant?.field;
2571
+ // The discriminant values the literal's LAST writer of that field gives
2572
+ // this source arm; null when unknown.
2573
+ const discriminantOf = (tag, arm) => {
2574
+ if (discField === undefined)
2575
+ return null;
2576
+ for (let i = props.length - 1; i >= 0; i--) {
2577
+ const prop = props[i];
2578
+ if (i === spreadIndex) {
2579
+ if (arm.kind !== "record")
2580
+ continue;
2581
+ if (!lowerer.shapes.get(arm.shapeId)?.fields.some((f) => f.name === discField))
2582
+ continue;
2583
+ const own = siteDef.discriminant;
2584
+ return own?.field === discField
2585
+ ? (own.cases.find((c) => c.tag === tag)?.values ?? null)
2586
+ : null;
2587
+ }
2588
+ if (ts.isSpreadAssignment(prop))
2589
+ return null;
2590
+ if ((ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) &&
2591
+ propNameText(lowerer, prop.name) === discField)
2592
+ return ts.isPropertyAssignment(prop)
2593
+ ? literalValues(lowerer.typeOf(prop.initializer))
2594
+ : literalValues(lowerer.typeOf(prop.name));
2595
+ }
2596
+ return null;
2597
+ };
2598
+ // Every key some contributor may write for this source arm; null when a
2599
+ // contributor has no fixed key set (an index signature, a non-record).
2600
+ const writtenKeys = (arm) => {
2601
+ const names = new Set();
2602
+ for (let i = 0; i < props.length; i++) {
2603
+ const prop = props[i];
2604
+ if (ts.isSpreadAssignment(prop)) {
2605
+ let node = prop.expression;
2606
+ while (ts.isParenthesizedExpression(node))
2607
+ node = node.expression;
2608
+ const t = i === spreadIndex ? arm : lowerer.mapTypeOf(lowerer.typeOf(node));
2609
+ if (t !== null && isUnitType(t))
2610
+ continue;
2611
+ if (t?.kind !== "record")
2612
+ return null;
2613
+ const shape = lowerer.shapes.get(t.shapeId);
2614
+ if (!shape || shape.indexValue || shape.tuple)
2615
+ return null;
2616
+ for (const f of shape.fields)
2617
+ names.add(f.name);
2618
+ }
2619
+ else if (ts.isPropertyAssignment(prop) || ts.isShorthandPropertyAssignment(prop)) {
2620
+ names.add(propNameText(lowerer, prop.name));
2621
+ }
2622
+ else {
2623
+ return null;
2624
+ }
2625
+ }
2626
+ return names;
2627
+ };
2628
+ const destinationOf = (tag, arm) => {
2629
+ const keys = writtenKeys(arm);
2630
+ if (keys === null)
2631
+ return null;
2632
+ let candidates = target.arms
2633
+ .map((a, t) => ({ a, t }))
2634
+ .filter((c) => c.a.kind === "record")
2635
+ .filter((c) => {
2636
+ const fields = lowerer.shapes.get(c.a.shapeId).fields;
2637
+ return [...keys].every((k) => fields.some((f) => f.name === k));
2638
+ });
2639
+ const values = discriminantOf(tag, arm);
2640
+ if (values && values.length > 0 && target.discriminant) {
2641
+ const cases = target.discriminant.cases;
2642
+ candidates = candidates.filter((c) => (cases.find((k) => k.tag === c.t)?.values ?? []).some((v) => values.includes(v)));
2643
+ }
2644
+ if (candidates.length === 1)
2645
+ return candidates[0].a;
2646
+ if (arm.kind === "record") {
2647
+ const same = candidates.find((c) => c.a.shapeId === arm.shapeId);
2648
+ if (same)
2649
+ return same.a;
2650
+ }
2651
+ const exactKeys = candidates.filter((c) => lowerer.shapes.get(c.a.shapeId).fields.length === keys.size);
2652
+ return exactKeys.length === 1 ? exactKeys[0].a : null;
2653
+ };
2654
+ // Plan every arm before lowering anything.
2655
+ const plan = [];
2656
+ for (let tag = 0; tag < siteDef.arms.length; tag++) {
2657
+ const arm = siteDef.arms[tag];
2658
+ const destination = destinationOf(tag, arm);
2659
+ if (destination === null)
2660
+ return null;
2661
+ plan.push({ tag, arm, destination });
2662
+ }
2663
+ const loc = locOf(expr);
2664
+ let source = lowerer.lowerExpr(site);
2665
+ if (source.type.kind === "union" && !typeEquals(source.type, siteType)) {
2666
+ const helper = lowerer.narrowedRetagHelper(site, source.type.unionId, siteType.unionId, source.loc);
2667
+ if (helper)
2668
+ source = { kind: "call", callee: helper, args: [source], type: siteType, loc: source.loc };
2669
+ }
2670
+ if (!typeEquals(source.type, siteType))
2671
+ return null;
2672
+ const saved = lowerer.declareHiddenLocal("%spreadArms", siteType);
2673
+ const ref = varRef(saved.id, siteType, loc);
2674
+ let result = null;
2675
+ for (let i = plan.length - 1; i >= 0; i--) {
2676
+ const { tag, arm, destination } = plan[i];
2677
+ const armValue = arm.kind === "record"
2678
+ ? { kind: "unionNarrow", value: ref, unionId: siteType.unionId, tag, type: arm, loc }
2679
+ : {
2680
+ kind: "recordLit",
2681
+ fields: [],
2682
+ type: { kind: "record", shapeId: lowerer.shapes.intern([]) },
2683
+ loc,
2684
+ };
2685
+ lowerer.spreadArmOverrides.set(site, armValue);
2686
+ let built;
2687
+ try {
2688
+ built = lowerObjectLiteral(lowerer, expr, destination, true);
2689
+ }
2690
+ finally {
2691
+ lowerer.spreadArmOverrides.delete(site);
2692
+ }
2693
+ const wrapped = lowerer.coerceToExpected(built, mapped);
2694
+ if (!typeEquals(wrapped.type, mapped))
2695
+ return null;
2696
+ result =
2697
+ result === null
2698
+ ? wrapped
2699
+ : {
2700
+ kind: "ternary",
2701
+ cond: {
2702
+ kind: "unionIsTag",
2703
+ value: ref,
2704
+ unionId: siteType.unionId,
2705
+ tag,
2706
+ negated: false,
2707
+ type: BOOL,
2708
+ loc,
2709
+ },
2710
+ then: wrapped,
2711
+ else_: result,
2712
+ type: mapped,
2713
+ loc,
2714
+ };
2715
+ }
2716
+ if (result === null)
2717
+ return null;
2718
+ return {
2719
+ kind: "seqExpr",
2720
+ stmts: [{ kind: "varDecl", localId: saved.id, init: source, loc }],
2721
+ result,
2722
+ type: mapped,
2723
+ loc,
2724
+ };
2725
+ }
2726
+ /** `{ ...a, ...b }` over union-typed sources that lowerDistributedUnionSpread
2727
+ * declined: no single destination arm holds every key the contributors may
2728
+ * write (`provider-composer`'s two all-optional compat unions, an aggregate
2729
+ * config merged from a http|stdio union plus a patch naming both). Node
2730
+ * builds ONE plain object: each source's own enumerable keys copy in source
2731
+ * order (later writers win), then the explicit fields write in their own
2732
+ * positions. Build exactly that object — the checked-dynamic literal — and
2733
+ * validate it into the declared union at the slot. The value keeps its
2734
+ * runtime keys (reflection, JSON, and inspection see what Node sees); a
2735
+ * value the union cannot hold throws the catchable TypeError at the
2736
+ * conversion instead of being reinterpreted as one arm.
2737
+ *
2738
+ * Null when the shape is not the SC2003 one (no union-typed spread source),
2739
+ * when the build must not demote (inside a capture buffer, or under
2740
+ * `--tier=static-only`, which keeps today's refusal), or when the union has
2741
+ * no checked extraction. The caller then keeps its refusal. */
2742
+ function lowerUnionSpreadMergeDyn(lowerer, expr, target) {
2743
+ if (isJsSourceFile(ts.getSourceFileOfNode(expr)))
2744
+ return null;
2745
+ const spreadsUnion = expr.properties.nodes.some((prop) => {
2746
+ if (!ts.isSpreadAssignment(prop))
2747
+ return false;
2748
+ let node = prop.expression;
2749
+ while (ts.isParenthesizedExpression(node))
2750
+ node = node.expression;
2751
+ return lowerer.mapTypeOf(lowerer.typeOf(node))?.kind === "union";
2752
+ });
2753
+ if (!spreadsUnion)
2754
+ return null;
2755
+ if (lowerer.tierMode !== "auto" || lowerer.diagSink !== null)
2756
+ return null;
2757
+ if (!canDynCheckTo(target, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
2758
+ return null;
2759
+ return lowerer.coerceToExpected(lowerDynObjectLiteral(lowerer, expr), target);
2760
+ }
2345
2761
  /** A union of plain records can preserve its existing variant while
2346
2762
  * replacing shared fields. Dispatch before constructing the copy: only
2347
2763
  * the selected arm evaluates overrides, and recordClone snapshots fields
@@ -2446,6 +2862,15 @@ function lowerUnionRecordClone(lowerer, expr) {
2446
2862
  };
2447
2863
  }
2448
2864
  }
2865
+ // Any other source (a capture, a runtime-optional binding, a member
2866
+ // read) converts through the same checked re-tag: the arms the site's
2867
+ // checker type excludes, a hidden absence arm included, throw the
2868
+ // catchable TypeError instead of being cloned.
2869
+ if (!typeEquals(source.type, sourceType) && source.type.kind === "union") {
2870
+ const helper = lowerer.narrowedRetagHelper(spread.expression, source.type.unionId, sourceType.unionId, source.loc);
2871
+ if (helper)
2872
+ source = { kind: "call", callee: helper, args: [source], type: sourceType, loc: source.loc };
2873
+ }
2449
2874
  if (!typeEquals(source.type, sourceType)) {
2450
2875
  lowerer.unsupported("SC1090", spread, "a union record clone whose stored source still includes absence");
2451
2876
  }
@@ -2632,15 +3057,50 @@ function lowerConditionalRecordSpread(lowerer, spread, loc) {
2632
3057
  loc,
2633
3058
  };
2634
3059
  }
3060
+ /** A spread's source expression with parentheses stripped, seeing through
3061
+ * an empty-object default: `...(x ?? {})` and `...(x || {})` spread exactly
3062
+ * what `...x` spreads (a nullish or other falsy value is a primitive with
3063
+ * no own keys, and x evaluates once either way). Only where `x` is a plain
3064
+ * record or a record-or-unit union, which the field copy and the
3065
+ * optional-spread desugar handle; index-signature and other sources keep
3066
+ * their paths. */
3067
+ function spreadSourceNode(lowerer, expr) {
3068
+ let e = expr;
3069
+ while (ts.isParenthesizedExpression(e))
3070
+ e = e.expression;
3071
+ if (!ts.isBinaryExpression(e) ||
3072
+ (e.operatorToken.kind !== ts.SyntaxKind.QuestionQuestionToken &&
3073
+ e.operatorToken.kind !== ts.SyntaxKind.BarBarToken))
3074
+ return e;
3075
+ let right = e.right;
3076
+ while (ts.isParenthesizedExpression(right))
3077
+ right = right.expression;
3078
+ if (!ts.isObjectLiteralExpression(right) || right.properties.nodes.length !== 0)
3079
+ return e;
3080
+ let left = e.left;
3081
+ while (ts.isParenthesizedExpression(left))
3082
+ left = left.expression;
3083
+ const plainRecord = (t) => t?.kind === "record" && lowerer.shapes.get(t.shapeId)?.indexValue === undefined;
3084
+ const source = lowerer.mapTypeOf(lowerer.typeOf(left));
3085
+ if (plainRecord(source ?? undefined))
3086
+ return left;
3087
+ if (source?.kind === "union") {
3088
+ const arms = lowerer.unions.get(source.unionId)?.arms ?? [];
3089
+ const records = arms.filter((arm) => arm.kind === "record");
3090
+ if (records.length === 1 &&
3091
+ plainRecord(records[0]) &&
3092
+ arms.every((arm) => arm.kind === "record" || isUnitType(arm)))
3093
+ return left;
3094
+ }
3095
+ return e;
3096
+ }
2635
3097
  /** A spread source the record desugar cannot enumerate: an untyped
2636
3098
  * (`unknown`) value, or one whose shape carries an index signature
2637
3099
  * (`Record<string, unknown>`, `{ [key: string]: T }`) so its keys are
2638
3100
  * runtime state. Node copies own enumerable keys in insertion order, which
2639
3101
  * the checked-dynamic object reproduces. */
2640
3102
  function dynSpreadSource(lowerer, node) {
2641
- let e = node;
2642
- while (ts.isParenthesizedExpression(e))
2643
- e = e.expression;
3103
+ const e = spreadSourceNode(lowerer, node);
2644
3104
  if ((lowerer.typeOf(e).flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0)
2645
3105
  return true;
2646
3106
  const indexed = (t) => {
@@ -2770,6 +3230,28 @@ export function conditionalSpreadOf(expr) {
2770
3230
  }
2771
3231
  return { cond: e.condition, props, whenTrue: trueCarries };
2772
3232
  }
3233
+ /** The record arms of a union spread source of the declared-shape merge,
3234
+ * or null unless every arm is a non-tuple record or the null/undefined
3235
+ * unit and at least one arm is a record. Unit arms contribute nothing:
3236
+ * CopyDataProperties skips nullish sources. */
3237
+ function declaredMergeUnionArms(lowerer, unionId) {
3238
+ const def = lowerer.unions.get(unionId);
3239
+ if (!def)
3240
+ return null;
3241
+ const arms = [];
3242
+ for (let tag = 0; tag < def.arms.length; tag++) {
3243
+ const arm = def.arms[tag];
3244
+ if (arm.kind === "undefinedT" || arm.kind === "nullT")
3245
+ continue;
3246
+ if (arm.kind !== "record")
3247
+ return null;
3248
+ const armShape = lowerer.shapes.get(arm.shapeId);
3249
+ if (!armShape || armShape.tuple)
3250
+ return null;
3251
+ arms.push({ tag, shapeId: arm.shapeId, shape: armShape });
3252
+ }
3253
+ return arms.length > 0 ? arms : null;
3254
+ }
2773
3255
  /** `{ ...idx, ...idx2 }` typed a DECLARED shape (no index signature) — the
2774
3256
  * defaults-merge idiom over runtime-keyed sources (`{ ...fromEntries(a),
2775
3257
  * ...fromEntries(b) }` typed AppConfig). Lowers to ONE interned helper
@@ -2785,7 +3267,12 @@ export function conditionalSpreadOf(expr) {
2785
3267
  * untyped copy would store the lie) — and a key naming NO declared field
2786
3268
  * is DROPPED (the shape cannot represent it; Node keeps it invisibly —
2787
3269
  * divergence 68). Later contributors overwrite earlier ones
2788
- * (last-write-wins). Fixed, hybrid, and pure index records may contribute. */
3270
+ * (last-write-wins). Fixed, hybrid, and pure index records may contribute,
3271
+ * and so may a union of those records with the null/undefined unit: each
3272
+ * record arm copies through its own shape's helper behind its tag test,
3273
+ * a unit arm copies nothing (CopyDataProperties skips nullish sources),
3274
+ * and the union is held in a hidden local, so it still evaluates once, in
3275
+ * order. */
2789
3276
  function lowerDeclaredSpreadMerge(lowerer, expr, type, shape, loc) {
2790
3277
  const srcs = [];
2791
3278
  for (const prop of expr.properties.nodes) {
@@ -2795,6 +3282,15 @@ function lowerDeclaredSpreadMerge(lowerer, expr, type, shape, loc) {
2795
3282
  srcs.push({ value, shapeId: "dyn" });
2796
3283
  continue;
2797
3284
  }
3285
+ if (value.type.kind === "union") {
3286
+ const arms = declaredMergeUnionArms(lowerer, value.type.unionId);
3287
+ if (arms) {
3288
+ for (const arm of arms)
3289
+ fenceAccessorSpreadSource(lowerer, prop, arm.shape);
3290
+ srcs.push({ value, shapeId: "union", arms });
3291
+ continue;
3292
+ }
3293
+ }
2798
3294
  const srcShape = value.type.kind === "record" ? lowerer.shapes.get(value.type.shapeId) : undefined;
2799
3295
  if (value.type.kind !== "record" || !srcShape || srcShape.tuple) {
2800
3296
  lowerer.unsupported("SC1090", prop, `object spread of '${lowerer.fmt(value.type)}' into '${lowerer.fmt(type)}' (only records spread into a declared shape)`);
@@ -2932,12 +3428,11 @@ function lowerDeclaredSpreadMerge(lowerer, expr, type, shape, loc) {
2932
3428
  loc,
2933
3429
  },
2934
3430
  ];
2935
- for (const source of srcs) {
3431
+ const mergeHelper = (source, sourceType) => {
2936
3432
  const key = `declmerge-source:${type.shapeId}:${source.shapeId}`;
2937
3433
  let helper = lowerer.valueHelpers.get(key);
2938
3434
  if (!helper) {
2939
3435
  helper = keyedHelperName(lowerer, "%rec.declmerge", key);
2940
- const sourceType = source.value.type;
2941
3436
  const destination = varRef("out.0", type, loc);
2942
3437
  const receiver = varRef("source.0", sourceType, loc);
2943
3438
  const locals = [
@@ -3112,6 +3607,59 @@ function lowerDeclaredSpreadMerge(lowerer, expr, type, shape, loc) {
3112
3607
  // leave an entry that a later literal could reuse without a body.
3113
3608
  lowerer.valueHelpers.set(key, helper);
3114
3609
  }
3610
+ return helper;
3611
+ };
3612
+ for (const source of srcs) {
3613
+ if (source.arms && source.value.type.kind === "union") {
3614
+ // Evaluate the union once, then copy the running arm's own keys.
3615
+ const unionType = source.value.type;
3616
+ const held = lowerer.declareHiddenLocal("%declMergeSource", unionType);
3617
+ const heldRef = varRef(held.id, unionType, loc);
3618
+ stmts.push({ kind: "varDecl", localId: held.id, init: source.value, loc });
3619
+ for (const arm of source.arms) {
3620
+ const armType = { kind: "record", shapeId: arm.shapeId };
3621
+ const helper = mergeHelper({ value: heldRef, shapeId: arm.shapeId, shape: arm.shape }, armType);
3622
+ stmts.push({
3623
+ kind: "if",
3624
+ cond: {
3625
+ kind: "unionIsTag",
3626
+ unionId: unionType.unionId,
3627
+ tag: arm.tag,
3628
+ negated: false,
3629
+ value: heldRef,
3630
+ type: BOOL,
3631
+ loc,
3632
+ },
3633
+ then: [
3634
+ {
3635
+ kind: "exprStmt",
3636
+ expr: {
3637
+ kind: "call",
3638
+ callee: helper,
3639
+ args: [
3640
+ outRef,
3641
+ {
3642
+ kind: "unionNarrow",
3643
+ unionId: unionType.unionId,
3644
+ tag: arm.tag,
3645
+ value: heldRef,
3646
+ type: armType,
3647
+ loc,
3648
+ },
3649
+ ],
3650
+ type,
3651
+ loc,
3652
+ },
3653
+ loc,
3654
+ },
3655
+ ],
3656
+ else_: null,
3657
+ loc,
3658
+ });
3659
+ }
3660
+ continue;
3661
+ }
3662
+ const helper = mergeHelper(source, source.value.type);
3115
3663
  stmts.push({
3116
3664
  kind: "exprStmt",
3117
3665
  expr: { kind: "call", callee: helper, args: [outRef, source.value], type, loc },
@@ -3134,7 +3682,8 @@ function shorthandBuiltinValue(lowerer, prop) {
3134
3682
  const js = isJsSourceFile(ts.getSourceFileOfNode(prop));
3135
3683
  const name = ts.symbolName(symbol);
3136
3684
  return ((js ? builtinNamespaceValue(lowerer, name, locOf(prop.name)) : null) ??
3137
- builtinGlobalValue(name, locOf(prop.name), js));
3685
+ builtinGlobalValue(name, locOf(prop.name), js) ??
3686
+ tsUntypedGlobalValue(lowerer, prop.name, name, locOf(prop.name)));
3138
3687
  }
3139
3688
  export function lowerShorthandValue(lowerer, prop) {
3140
3689
  // 7 types the shorthand's name as PropertyName; it is always an