typia 15.1.0 → 15.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/lib/TypeGuardError.js.map +1 -1
  2. package/lib/basic.js.map +1 -1
  3. package/lib/compare.js.map +1 -1
  4. package/lib/functional.js.map +1 -1
  5. package/lib/http.js.map +1 -1
  6. package/lib/index.js.map +1 -1
  7. package/lib/internal/_IProtobufWriter.js.map +1 -1
  8. package/lib/internal/_ProtobufReader.js.map +1 -1
  9. package/lib/internal/_ProtobufSizer.js.map +1 -1
  10. package/lib/internal/_ProtobufWriter.js.map +1 -1
  11. package/lib/internal/_accessExpressionAsString.js.map +1 -1
  12. package/lib/internal/_assertGuard.js.map +1 -1
  13. package/lib/internal/_coerceLlmArguments.js.map +1 -1
  14. package/lib/internal/_createLlmEvaluation.js.map +1 -1
  15. package/lib/internal/_createStandardSchema.js.map +1 -1
  16. package/lib/internal/_decimal.js.map +1 -1
  17. package/lib/internal/_functionalTypeGuardErrorFactory.js.map +1 -1
  18. package/lib/internal/_httpFormDataReadArray.js.map +1 -1
  19. package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
  20. package/lib/internal/_httpFormDataReadBlob.js.map +1 -1
  21. package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
  22. package/lib/internal/_httpFormDataReadFile.js.map +1 -1
  23. package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
  24. package/lib/internal/_httpFormDataReadString.js.map +1 -1
  25. package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
  26. package/lib/internal/_httpHeaderReadBoolean.js.map +1 -1
  27. package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
  28. package/lib/internal/_httpParameterReadBigint.js.map +1 -1
  29. package/lib/internal/_httpParameterReadBoolean.js.map +1 -1
  30. package/lib/internal/_httpParameterReadNumber.js.map +1 -1
  31. package/lib/internal/_httpParameterReadString.js.map +1 -1
  32. package/lib/internal/_httpQueryParseURLSearchParams.js.map +1 -1
  33. package/lib/internal/_httpQueryReadArray.js.map +1 -1
  34. package/lib/internal/_httpQueryReadBigint.js.map +1 -1
  35. package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
  36. package/lib/internal/_httpQueryReadNumber.js.map +1 -1
  37. package/lib/internal/_httpQueryReadString.js.map +1 -1
  38. package/lib/internal/_isBetween.js.map +1 -1
  39. package/lib/internal/_isBigintString.js.map +1 -1
  40. package/lib/internal/_isFormatByte.js.map +1 -1
  41. package/lib/internal/_isFormatDate.js.map +1 -1
  42. package/lib/internal/_isFormatDateTime.js.map +1 -1
  43. package/lib/internal/_isFormatDuration.js.map +1 -1
  44. package/lib/internal/_isFormatEmail.js.map +1 -1
  45. package/lib/internal/_isFormatHostname.js.map +1 -1
  46. package/lib/internal/_isFormatIdnEmail.js.map +1 -1
  47. package/lib/internal/_isFormatIdnHostname.js.map +1 -1
  48. package/lib/internal/_isFormatIpv4.js.map +1 -1
  49. package/lib/internal/_isFormatIpv6.js.map +1 -1
  50. package/lib/internal/_isFormatIri.js.map +1 -1
  51. package/lib/internal/_isFormatIriReference.js.map +1 -1
  52. package/lib/internal/_isFormatJsonPointer.js.map +1 -1
  53. package/lib/internal/_isFormatPassword.js.map +1 -1
  54. package/lib/internal/_isFormatRegex.js.map +1 -1
  55. package/lib/internal/_isFormatRelativeJsonPointer.js.map +1 -1
  56. package/lib/internal/_isFormatTime.js.map +1 -1
  57. package/lib/internal/_isFormatUri.js.map +1 -1
  58. package/lib/internal/_isFormatUriReference.js.map +1 -1
  59. package/lib/internal/_isFormatUriTemplate.js.map +1 -1
  60. package/lib/internal/_isFormatUrl.js.map +1 -1
  61. package/lib/internal/_isFormatUuid.js.map +1 -1
  62. package/lib/internal/_isMultipleOf.js.map +1 -1
  63. package/lib/internal/_isTypeFloat.js.map +1 -1
  64. package/lib/internal/_isTypeInt16.js.map +1 -1
  65. package/lib/internal/_isTypeInt32.js.map +1 -1
  66. package/lib/internal/_isTypeInt64.js.map +1 -1
  67. package/lib/internal/_isTypeInt64Bigint.js.map +1 -1
  68. package/lib/internal/_isTypeInt8.js.map +1 -1
  69. package/lib/internal/_isTypeUint16.js.map +1 -1
  70. package/lib/internal/_isTypeUint32.js.map +1 -1
  71. package/lib/internal/_isTypeUint64.js.map +1 -1
  72. package/lib/internal/_isTypeUint64Bigint.js.map +1 -1
  73. package/lib/internal/_isTypeUint8.js.map +1 -1
  74. package/lib/internal/_isUniqueItems.js.map +1 -1
  75. package/lib/internal/_jsonStringifyArray.js.map +1 -1
  76. package/lib/internal/_jsonStringifyElement.js.map +1 -1
  77. package/lib/internal/_jsonStringifyNumber.js.map +1 -1
  78. package/lib/internal/_jsonStringifyProperty.js.map +1 -1
  79. package/lib/internal/_jsonStringifyRest.js.map +1 -1
  80. package/lib/internal/_jsonStringifyString.js.map +1 -1
  81. package/lib/internal/_jsonStringifyTail.js.map +1 -1
  82. package/lib/internal/_llmApplicationFinalize.js +8 -1
  83. package/lib/internal/_llmApplicationFinalize.js.map +1 -1
  84. package/lib/internal/_notationAny.js.map +1 -1
  85. package/lib/internal/_notationAssign.js.map +1 -1
  86. package/lib/internal/_notationCamel.js.map +1 -1
  87. package/lib/internal/_notationKebab.js.map +1 -1
  88. package/lib/internal/_notationKeyCollision.js.map +1 -1
  89. package/lib/internal/_notationPascal.js.map +1 -1
  90. package/lib/internal/_notationSnake.js.map +1 -1
  91. package/lib/internal/_parseLlmArguments.js.map +1 -1
  92. package/lib/internal/_plainCloneAny.js.map +1 -1
  93. package/lib/internal/_randomArray.js.map +1 -1
  94. package/lib/internal/_randomBigint.js.map +1 -1
  95. package/lib/internal/_randomBoolean.js.map +1 -1
  96. package/lib/internal/_randomFormatByte.js.map +1 -1
  97. package/lib/internal/_randomFormatDate.js.map +1 -1
  98. package/lib/internal/_randomFormatDatetime.js.map +1 -1
  99. package/lib/internal/_randomFormatDuration.js.map +1 -1
  100. package/lib/internal/_randomFormatEmail.js.map +1 -1
  101. package/lib/internal/_randomFormatHostname.js.map +1 -1
  102. package/lib/internal/_randomFormatIdnEmail.js.map +1 -1
  103. package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
  104. package/lib/internal/_randomFormatIpv4.js.map +1 -1
  105. package/lib/internal/_randomFormatIpv6.js.map +1 -1
  106. package/lib/internal/_randomFormatIri.js.map +1 -1
  107. package/lib/internal/_randomFormatIriReference.js.map +1 -1
  108. package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
  109. package/lib/internal/_randomFormatPassword.js.map +1 -1
  110. package/lib/internal/_randomFormatRegex.js.map +1 -1
  111. package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
  112. package/lib/internal/_randomFormatTime.js.map +1 -1
  113. package/lib/internal/_randomFormatUri.js.map +1 -1
  114. package/lib/internal/_randomFormatUriReference.js.map +1 -1
  115. package/lib/internal/_randomFormatUriTemplate.js.map +1 -1
  116. package/lib/internal/_randomFormatUrl.js.map +1 -1
  117. package/lib/internal/_randomFormatUuid.js.map +1 -1
  118. package/lib/internal/_randomInteger.js.map +1 -1
  119. package/lib/internal/_randomMultiple.js.map +1 -1
  120. package/lib/internal/_randomNumber.js.map +1 -1
  121. package/lib/internal/_randomPattern.js.map +1 -1
  122. package/lib/internal/_randomPick.js.map +1 -1
  123. package/lib/internal/_randomString.js.map +1 -1
  124. package/lib/internal/_randomStringLength.js.map +1 -1
  125. package/lib/internal/_stringLength.js.map +1 -1
  126. package/lib/internal/_stringLengthGte.js.map +1 -1
  127. package/lib/internal/_stringLengthLte.js.map +1 -1
  128. package/lib/internal/_throwTypeGuardError.js.map +1 -1
  129. package/lib/internal/_validateReport.js.map +1 -1
  130. package/lib/internal/private/__notationRename.js.map +1 -1
  131. package/lib/internal/private/__randomComposition.js.map +1 -1
  132. package/lib/internal/private/__randomEpoch.js.map +1 -1
  133. package/lib/json.js.map +1 -1
  134. package/lib/llm.js.map +1 -1
  135. package/lib/module.js.map +1 -1
  136. package/lib/notations.js.map +1 -1
  137. package/lib/plain.js.map +1 -1
  138. package/lib/protobuf.js.map +1 -1
  139. package/lib/re-exports.js.map +1 -1
  140. package/lib/reflect.js.map +1 -1
  141. package/lib/transform.d.ts +5 -2
  142. package/lib/transform.js +6 -2
  143. package/lib/transform.js.map +1 -1
  144. package/lib/transform.mjs +7 -3
  145. package/lib/transform.mjs.map +1 -1
  146. package/lib/transformers/NoTransformConfigurationError.js.map +1 -1
  147. package/native/cmd/ttsc-typia/build.go +3 -1
  148. package/native/cmd/ttsc-typia/content_mapper_transform_ts71_test.go +102 -0
  149. package/native/cmd/ttsc-typia/diagnostics.go +1 -1
  150. package/native/cmd/ttsc-typia/distinct_programs_concurrent_emit_test.go +167 -0
  151. package/native/cmd/ttsc-typia/exact_optional_family_emission_contract_test.go +2 -1
  152. package/native/cmd/ttsc-typia/project_dependencies_heritage_forms_transform_test.go +84 -0
  153. package/native/cmd/ttsc-typia/schema_literal_helpers_test.go +2 -1
  154. package/native/cmd/ttsc-typia/transform.go +7 -6
  155. package/native/core/context/EmitFactory.go +20 -29
  156. package/native/core/context/ImportProgrammer.go +5 -7
  157. package/native/core/context/NodeFactory.go +25 -0
  158. package/native/core/context/node_factory_ownership_test.go +45 -0
  159. package/native/core/factories/ExpressionFactory.go +13 -14
  160. package/native/core/factories/IdentifierFactory.go +3 -5
  161. package/native/core/factories/LiteralFactory.go +6 -8
  162. package/native/core/factories/NumericRangeFactory.go +8 -10
  163. package/native/core/factories/StatementFactory.go +5 -7
  164. package/native/core/factories/TemplateFactory.go +1 -3
  165. package/native/core/factories/TypeFactory.go +7 -9
  166. package/native/core/factories/ValueFactory.go +5 -7
  167. package/native/core/factories/factory_utility_coverage_test.go +21 -20
  168. package/native/core/factories/internal/metadata/MetadataHelper.go +13 -13
  169. package/native/core/factories/internal/metadata/comment_tags_coverage_test.go +7 -16
  170. package/native/core/factories/internal/metadata/emplace_metadata_object.go +36 -27
  171. package/native/core/factories/internal/metadata/iterate_metadata_alias.go +2 -2
  172. package/native/core/factories/internal/metadata/iterate_metadata_constant.go +4 -4
  173. package/native/core/factories/internal/metadata/iterate_metadata_function.go +2 -2
  174. package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +11 -11
  175. package/native/core/factories/internal/metadata/js_doc_link_text_test.go +6 -16
  176. package/native/core/factories/internal/metadata/js_doc_parameter_name_reads_qualified_name_test.go +2 -1
  177. package/native/core/factories/internal/metadata/js_doc_source_helpers_test.go +44 -0
  178. package/native/core/factories/internal/metadata/js_doc_type_tag_blank_separation_test.go +16 -22
  179. package/native/core/factories/numeric_range_factory_enforces_64bit_bounds_test.go +2 -1
  180. package/native/core/programmers/AssertProgrammer.go +16 -18
  181. package/native/core/programmers/IsProgrammer.go +10 -12
  182. package/native/core/programmers/RandomProgrammer.go +29 -31
  183. package/native/core/programmers/ValidateProgrammer.go +20 -22
  184. package/native/core/programmers/compare/CompareEqualProgrammer.go +1 -3
  185. package/native/core/programmers/compare/CompareLessProgrammer.go +1 -3
  186. package/native/core/programmers/functional/FunctionalAssertFunctionProgrammer.go +3 -5
  187. package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +2 -2
  188. package/native/core/programmers/functional/FunctionalAssertReturnProgrammer.go +2 -2
  189. package/native/core/programmers/functional/FunctionalIsFunctionProgrammer.go +2 -2
  190. package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +8 -10
  191. package/native/core/programmers/functional/FunctionalIsReturnProgrammer.go +2 -2
  192. package/native/core/programmers/functional/FunctionalValidateFunctionProgrammer.go +4 -6
  193. package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +2 -2
  194. package/native/core/programmers/functional/FunctionalValidateReturnProgrammer.go +2 -2
  195. package/native/core/programmers/helpers/ClassifyJoiner.go +1 -3
  196. package/native/core/programmers/helpers/CloneJoiner.go +8 -14
  197. package/native/core/programmers/helpers/FunctionProgrammer.go +3 -5
  198. package/native/core/programmers/helpers/NotationJoiner.go +6 -12
  199. package/native/core/programmers/helpers/PruneJoiner.go +10 -12
  200. package/native/core/programmers/helpers/RandomJoiner.go +5 -7
  201. package/native/core/programmers/helpers/StringifyJoinder.go +15 -17
  202. package/native/core/programmers/helpers/UnionExplorer.go +24 -26
  203. package/native/core/programmers/http/HttpAssertFormDataProgrammer.go +2 -4
  204. package/native/core/programmers/http/HttpAssertHeadersProgrammer.go +1 -1
  205. package/native/core/programmers/http/HttpAssertQueryProgrammer.go +1 -1
  206. package/native/core/programmers/http/HttpFormDataProgrammer.go +9 -11
  207. package/native/core/programmers/http/HttpHeadersProgrammer.go +7 -9
  208. package/native/core/programmers/http/HttpIsFormDataProgrammer.go +2 -4
  209. package/native/core/programmers/http/HttpIsHeadersProgrammer.go +1 -1
  210. package/native/core/programmers/http/HttpIsQueryProgrammer.go +1 -1
  211. package/native/core/programmers/http/HttpParameterProgrammer.go +2 -4
  212. package/native/core/programmers/http/HttpQueryProgrammer.go +8 -10
  213. package/native/core/programmers/http/HttpValidateFormDataProgrammer.go +2 -4
  214. package/native/core/programmers/http/HttpValidateHeadersProgrammer.go +1 -1
  215. package/native/core/programmers/http/HttpValidateQueryProgrammer.go +1 -1
  216. package/native/core/programmers/internal/CheckerProgrammer.go +26 -28
  217. package/native/core/programmers/internal/FeatureProgrammer.go +32 -34
  218. package/native/core/programmers/iterate/check_array_length.go +1 -1
  219. package/native/core/programmers/iterate/check_bigint.go +2 -4
  220. package/native/core/programmers/iterate/check_dynamic_key.go +5 -7
  221. package/native/core/programmers/iterate/check_dynamic_properties.go +43 -57
  222. package/native/core/programmers/iterate/check_everything.go +3 -5
  223. package/native/core/programmers/iterate/check_exclude.go +2 -4
  224. package/native/core/programmers/iterate/check_native.go +2 -4
  225. package/native/core/programmers/iterate/check_number.go +2 -4
  226. package/native/core/programmers/iterate/check_object.go +3 -5
  227. package/native/core/programmers/iterate/check_object_type_tags.go +1 -1
  228. package/native/core/programmers/iterate/check_string.go +2 -4
  229. package/native/core/programmers/iterate/check_template.go +7 -9
  230. package/native/core/programmers/iterate/decode_union_object.go +2 -4
  231. package/native/core/programmers/iterate/feature_object_entries.go +2 -4
  232. package/native/core/programmers/json/JsonAssertParseProgrammer.go +2 -2
  233. package/native/core/programmers/json/JsonAssertStringifyProgrammer.go +2 -4
  234. package/native/core/programmers/json/JsonIsParseProgrammer.go +4 -6
  235. package/native/core/programmers/json/JsonIsStringifyProgrammer.go +2 -4
  236. package/native/core/programmers/json/JsonStringifyProgrammer.go +37 -39
  237. package/native/core/programmers/json/JsonValidateParseProgrammer.go +2 -2
  238. package/native/core/programmers/json/JsonValidateStringifyProgrammer.go +2 -4
  239. package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -3
  240. package/native/core/programmers/llm/LlmCoerceProgrammer.go +2 -4
  241. package/native/core/programmers/llm/LlmControllerProgrammer.go +1 -3
  242. package/native/core/programmers/llm/LlmEvaluationProgrammer.go +1 -3
  243. package/native/core/programmers/llm/LlmParametersProgrammer.go +1 -3
  244. package/native/core/programmers/llm/LlmParseProgrammer.go +3 -5
  245. package/native/core/programmers/llm/LlmSchemaProgrammer.go +5 -7
  246. package/native/core/programmers/llm/LlmStructuredOutputProgrammer.go +2 -4
  247. package/native/core/programmers/llm/llm_schema_programmer_coverage_test.go +1 -1
  248. package/native/core/programmers/notations/NotationAssertGeneralProgrammer.go +1 -3
  249. package/native/core/programmers/notations/NotationGeneralProgrammer.go +26 -28
  250. package/native/core/programmers/notations/NotationIsGeneralProgrammer.go +1 -3
  251. package/native/core/programmers/notations/NotationValidateGeneralProgrammer.go +1 -3
  252. package/native/core/programmers/plain/PlainAssertClassifyProgrammer.go +2 -4
  253. package/native/core/programmers/plain/PlainAssertCloneProgrammer.go +2 -4
  254. package/native/core/programmers/plain/PlainAssertPruneProgrammer.go +2 -4
  255. package/native/core/programmers/plain/PlainClassifyProgrammer.go +44 -46
  256. package/native/core/programmers/plain/PlainCloneProgrammer.go +30 -32
  257. package/native/core/programmers/plain/PlainIsCloneProgrammer.go +2 -4
  258. package/native/core/programmers/plain/PlainIsPruneProgrammer.go +2 -4
  259. package/native/core/programmers/plain/PlainPruneProgrammer.go +26 -28
  260. package/native/core/programmers/plain/PlainValidateClassifyProgrammer.go +2 -4
  261. package/native/core/programmers/plain/PlainValidateCloneProgrammer.go +2 -4
  262. package/native/core/programmers/plain/PlainValidatePruneProgrammer.go +2 -4
  263. package/native/core/programmers/protobuf/ProtobufAssertDecodeProgrammer.go +2 -4
  264. package/native/core/programmers/protobuf/ProtobufAssertEncodeProgrammer.go +2 -4
  265. package/native/core/programmers/protobuf/ProtobufDecodeProgrammer.go +24 -25
  266. package/native/core/programmers/protobuf/ProtobufEncodeProgrammer.go +23 -25
  267. package/native/core/programmers/protobuf/ProtobufIsDecodeProgrammer.go +2 -4
  268. package/native/core/programmers/protobuf/ProtobufIsEncodeProgrammer.go +2 -4
  269. package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -3
  270. package/native/core/programmers/protobuf/ProtobufValidateDecodeProgrammer.go +2 -4
  271. package/native/core/programmers/protobuf/ProtobufValidateEncodeProgrammer.go +3 -5
  272. package/native/core/programmers/protobuf/protobuf_programmer_helper_coverage_test.go +1 -1
  273. package/native/core/programmers/reflect/ReflectLiteralsProgrammer.go +1 -3
  274. package/native/core/schemas/metadata/MetadataCollection.go +2 -2
  275. package/native/core/schemas/metadata/MetadataDefaultLibrary.go +1 -1
  276. package/native/core/schemas/metadata/MetadataDependency.go +13 -13
  277. package/native/core/utils/PromiseTypeFactory.go +3 -3
  278. package/native/go.mod +3 -4
  279. package/native/go.work +1 -1
  280. package/native/transform/CallExpressionTransformer.go +4 -4
  281. package/native/transform/FileTransformer.go +2 -4
  282. package/native/transform/features/RandomTransformer.go +1 -3
  283. package/native/transform/features/compare/CompareEqualTransformer.go +1 -1
  284. package/native/transform/features/compare/CompareLessTransformer.go +1 -1
  285. package/native/transform/features/functional/FunctionalGenericTransformer.go +2 -2
  286. package/native/transform/features/llm/LlmCoerceTransformer.go +1 -1
  287. package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +1 -1
  288. package/native/transform/features/llm/LlmEvaluationTransformer.go +1 -1
  289. package/native/transform/features/llm/LlmParseTransformer.go +1 -1
  290. package/native/transform/features/llm/LlmSchemaTransformer.go +1 -3
  291. package/native/transform/features/reflect/ReflectNameTransformer.go +1 -3
  292. package/native/transform/file_transformer_coverage_test.go +2 -1
  293. package/native/transform/internal/GenericTransformer.go +1 -3
  294. package/package.json +6 -6
  295. package/src/transform.ts +6 -2
@@ -0,0 +1,167 @@
1
+ package main
2
+
3
+ import (
4
+ "crypto/sha256"
5
+ "fmt"
6
+ "os"
7
+ "path/filepath"
8
+ "regexp"
9
+ "strings"
10
+ "sync"
11
+ "testing"
12
+
13
+ shimast "github.com/microsoft/typescript-go/shim/ast"
14
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
15
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
16
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
17
+ "github.com/samchon/ttsc/packages/ttsc/driver"
18
+ "github.com/samchon/typia/packages/typia/native/adapter"
19
+ nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
20
+ metadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
21
+ "github.com/samchon/typia/packages/typia/native/transform"
22
+ )
23
+
24
+ // TestDistinctProgramsConcurrentEmit verifies independent Programs retain their
25
+ // own generated nodes during overlapping emits across programmer families.
26
+ //
27
+ // NodeFactory arenas are mutable. A helper dropping its emit context used to
28
+ // reach a package-global arena even when each worker owned its own Program.
29
+ // The serial control proves real validators were emitted before concurrency;
30
+ // the race-enabled canonical command additionally detects shared arena access.
31
+ //
32
+ // 1. Load two isolated Programs over the same type graph and separate markers.
33
+ // 2. Emit both serially and require generated assert/is helpers and own markers.
34
+ // 3. Emit sixteen times per Program concurrently and require identical bytes.
35
+ //
36
+ // @evidence contracts/testing.md#behavioral-verification Calls the real transform through EmitWithPluginTransformer with each file's EmitContext. Every output must contain both generated validator helpers and its authored marker; repeated parallel outputs must equal the serial output, and the race detector rejects shared mutable arenas.
37
+ // @evidence contracts/testing.md#independent-expectations The authored sources require string, number and array checks and distinct marker literals. The serial comparison only proves repeatability, not semantic correctness; the generated helper checks prevent untransformed stubs from passing, while existing TypeScript execution suites own runtime verdicts.
38
+ // @evidence contracts/testing.md#distinguishing-cases Serial controls precede overlapping workers, each owning one Program. Nested arrays, optional objects, unions and validator, JSON, HTTP, protobuf, notation, plain, random and functional producers exercise shared helper paths; foreign marker output and nondeterministic bytes fail independently of race detection.
39
+ // @evidence contracts/testing.md#execution-ownership Go's native unit population loads in-process Programs using shared immutable declarations and separate writable fixture roots. It builds no host binary and launches no JavaScript runtime; Program.Close and fixture cleanup release every test-owned resource.
40
+ func TestDistinctProgramsConcurrentEmit(t *testing.T) {
41
+ programs := make([]*driver.Program, 2)
42
+ for index := range programs {
43
+ root := ttscTypiaTestFixtureDirectory(t, "concurrent-emit-")
44
+ source := distinctProgramsConcurrentEmitSource + fmt.Sprintf("\nexport const owner = %q;\n", fmt.Sprintf("program-%d", index))
45
+ for name, text := range map[string]string{
46
+ "index.ts": source,
47
+ "tsconfig.json": `{"compilerOptions":{"target":"es2022","module":"nodenext","moduleResolution":"nodenext","strict":true,"skipLibCheck":true,"esModuleInterop":true,"outDir":"out"},"files":["index.ts"]}`,
48
+ } {
49
+ if err := os.WriteFile(filepath.Join(root, name), []byte(text), 0o644); err != nil {
50
+ t.Fatal(err)
51
+ }
52
+ }
53
+ program, diagnostics, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{ForceEmit: true})
54
+ if err != nil || driver.CountErrors(diagnostics) != 0 {
55
+ t.Fatalf("load: %v; %+v", err, diagnostics)
56
+ }
57
+ t.Cleanup(func() {
58
+ if err := program.Close(); err != nil {
59
+ t.Error(err)
60
+ }
61
+ })
62
+ if diagnostics := program.Diagnostics(); driver.CountErrors(diagnostics) != 0 {
63
+ t.Fatalf("typecheck: %+v", diagnostics)
64
+ }
65
+ programs[index] = program
66
+ }
67
+ emit := func(index int) (string, error) {
68
+ program := programs[index]
69
+ metadata.MetadataDefaultLibrary_register(program.Checker, func(source *shimast.SourceFile) bool { return source != nil && program.TSProgram.IsLibFile(source) })
70
+ defer metadata.MetadataDefaultLibrary_release(program.Checker)
71
+ options := (adapter.PluginOptions{}).TransformOptions()
72
+ var mu sync.Mutex
73
+ var transformed []*nativecontext.ITypiaDiagnostic
74
+ var javascript string
75
+ extras := nativecontext.ITypiaContext_Extras{Shared: &sync.Map{}, AddDiagnostic: func(d *nativecontext.ITypiaDiagnostic) int {
76
+ mu.Lock()
77
+ defer mu.Unlock()
78
+ transformed = append(transformed, d)
79
+ return len(transformed)
80
+ }}
81
+ diagnostics, err := program.EmitWithPluginTransformer(func(ec *shimprinter.EmitContext, source *shimast.SourceFile) *shimast.SourceFile {
82
+ return transform.Transform(program, &options, extras, ec)(source)
83
+ }, shimcompiler.WriteFile(func(name shimtspath.RootedFilePath, text string, _ *shimcompiler.WriteFileData) error {
84
+ mu.Lock()
85
+ defer mu.Unlock()
86
+ if filepath.Base(string(name)) == "index.js" {
87
+ javascript = text
88
+ }
89
+ return nil
90
+ }))
91
+ if err != nil || driver.CountErrors(diagnostics) != 0 || len(transformed) != 0 {
92
+ return "", fmt.Errorf("emit: %v; %+v; %+v", err, diagnostics, transformed)
93
+ }
94
+ for _, required := range []string{"const _ao0", "const _io0", "Array.isArray", fmt.Sprintf("program-%d", index)} {
95
+ if !strings.Contains(javascript, required) {
96
+ return "", fmt.Errorf("missing %q in generated output:\n%s", required, javascript)
97
+ }
98
+ }
99
+ if !regexp.MustCompile(`(?:"string"\s*===\s*typeof input\.name\b|typeof input\.name\s*===\s*"string")`).MatchString(javascript) {
100
+ return "", fmt.Errorf("missing name string guard:\n%s", javascript)
101
+ }
102
+ if strings.Contains(javascript, fmt.Sprintf("program-%d", 1-index)) {
103
+ return "", fmt.Errorf("output carries foreign Program marker")
104
+ }
105
+ return javascript, nil
106
+ }
107
+ controls := make([]string, len(programs))
108
+ for index := range programs {
109
+ var err error
110
+ controls[index], err = emit(index)
111
+ if err != nil {
112
+ t.Fatalf("serial: %v", err)
113
+ }
114
+ t.Logf("serial output %d SHA256: %x", index, sha256.Sum256([]byte(controls[index])))
115
+ }
116
+ start := make(chan struct{})
117
+ failures := make(chan error, len(programs))
118
+ var workers sync.WaitGroup
119
+ for index := range programs {
120
+ workers.Add(1)
121
+ go func() {
122
+ defer workers.Done()
123
+ <-start
124
+ for range 16 {
125
+ output, err := emit(index)
126
+ if err != nil {
127
+ failures <- err
128
+ return
129
+ }
130
+ if output != controls[index] {
131
+ failures <- fmt.Errorf("Program %d changed emitted bytes", index)
132
+ return
133
+ }
134
+ }
135
+ }()
136
+ }
137
+ close(start)
138
+ workers.Wait()
139
+ close(failures)
140
+ for err := range failures {
141
+ t.Error(err)
142
+ }
143
+ }
144
+
145
+ const distinctProgramsConcurrentEmitSource = `import typia from "typia";
146
+ interface RecordType {
147
+ name: string;
148
+ values: { key: string; value: number; enabled: boolean }[];
149
+ optional?: { nested: { label: string; count: number } };
150
+ union: { type: "a"; text: string } | { type: "b"; count: number };
151
+ }
152
+ export const assertRecord = typia.createAssert<RecordType>();
153
+ export const isRecord = typia.createIs<RecordType>();
154
+ export const validateRecord = typia.createValidate<RecordType>();
155
+ export const stringify = typia.json.createAssertStringify<RecordType>();
156
+ export const parse = typia.json.createAssertParse<RecordType>();
157
+ export const query = typia.http.createAssertQuery<{ name: string; values: number[] }>();
158
+ export const headers = typia.http.createAssertHeaders<{ name: string }>();
159
+ export const encode = typia.protobuf.createAssertEncode<RecordType>();
160
+ export const decode = typia.protobuf.createAssertDecode<RecordType>();
161
+ export const decodeMap = typia.protobuf.createDecode<{ values: Map<string, number> }>();
162
+ export const decodeRecord = typia.protobuf.createDecode<{ values: Record<string, number> }>();
163
+ export const camel = typia.notations.createCamel<RecordType>();
164
+ export const clone = typia.plain.createAssertClone<RecordType>();
165
+ export const random = typia.createRandom<RecordType>();
166
+ export const functional = typia.functional.assertFunction((input: RecordType): RecordType => input);
167
+ `
@@ -4,6 +4,7 @@ import (
4
4
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
5
  shimcore "github.com/microsoft/typescript-go/shim/core"
6
6
  shimparser "github.com/microsoft/typescript-go/shim/parser"
7
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
7
8
  "os"
8
9
  "path/filepath"
9
10
  "strings"
@@ -52,7 +53,7 @@ export const randomIncluded=()=>typia.random<IExactOptionalClone>({boolean:()=>t
52
53
  if code != 0 {
53
54
  t.Fatalf("exact optional transform failed %d: %s", code, stderr)
54
55
  }
55
- file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: filepath.ToSlash(filepath.Join(project, "emitted.js"))}, output, shimcore.ScriptKindJS)
56
+ file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(filepath.Join(project, "emitted.js")))}, output, shimcore.ScriptKindJS)
56
57
  bindings := map[string]*shimast.Node{}
57
58
  assertImports := map[string]bool{}
58
59
  var walk func(*shimast.Node) bool
@@ -0,0 +1,84 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesHeritageFormsTransform verifies both type and value
11
+ // heritage retain empty bases and phantom type arguments in dependency reports.
12
+ //
13
+ // TypeScript 7.1 represents interface extends and class implements with type
14
+ // references, while class extends keeps an expression with type arguments.
15
+ // Empty bases and unused generic arguments cannot be recovered from properties.
16
+ //
17
+ // 1. Declare aliased and qualified empty generic interfaces, classes and contracts.
18
+ // 2. Transform validators for interface, class, implements and class-expression forms.
19
+ // 3. Require all written heritage and argument files, excluding an unused sibling.
20
+ //
21
+ // @evidence contracts/testing.md#behavioral-verification Calls the real project transform and checks main.ts dependencies include each empty interface, base class, implemented contract and phantom argument file. A valid plain class without heritage is transformed in the same project and an unconsumed sibling must remain absent.
22
+ // @evidence contracts/testing.md#independent-expectations Editing any authored empty base or phantom type argument can change the consumed type, so their written references belong to the dependency contract even before they contribute a property. The unrelated interface is never referenced and must not become a dependency.
23
+ // @evidence contracts/testing.md#distinguishing-cases Interface extends, class extends, class implements and class-expression extends/implements combine named aliases, qualified namespace names and generic arguments. The plain-class and unused-file controls distinguish absent heritage from over-reporting; the existing empty-heritage test owns the minimal interface witness.
24
+ // @evidence contracts/testing.md#execution-ownership The canonical native and 7.1 Go populations discover this in-process transform unit. It shares immutable declaration inputs through the existing fixture helper and starts no compiler or JavaScript subprocess.
25
+ func TestProjectDependenciesHeritageFormsTransform(t *testing.T) {
26
+ project := ttscTypiaTestFixtureDirectory(t, "heritage-forms-")
27
+ if err := os.MkdirAll(filepath.Join(project, "src"), 0o755); err != nil {
28
+ t.Fatal(err)
29
+ }
30
+ files := map[string]string{
31
+ "tsconfig.json": projectDependenciesEnvelopeTSConfig,
32
+ "src/interface-base.ts": `export namespace Types { export interface Empty<T> {} }`,
33
+ "src/class-base.ts": `export class Parent<T> {}`,
34
+ "src/contract.ts": `export interface Contract<T> {}`,
35
+ "src/tag.ts": `export interface Tag { id: string }`,
36
+ "src/unrelated.ts": `export interface Unused { unused: string }`,
37
+ "src/models.ts": `import { Types } from "./interface-base";
38
+ import { Parent as Base } from "./class-base";
39
+ import { Contract as ContractAlias } from "./contract";
40
+ import { Tag } from "./tag";
41
+ export interface ViaInterface extends Types.Empty<Tag> { id: string }
42
+ export class ViaClass extends Base<Tag> { id!: string }
43
+ export class ViaImplements implements ContractAlias<Tag> { id!: string }
44
+ export const Bound = class extends Base<Tag> implements ContractAlias<Tag> { id!: string };
45
+ export class Plain { id!: string }
46
+ `,
47
+ "src/main.ts": `import typia from "typia";
48
+ import { ViaInterface, ViaClass, ViaImplements, Bound, Plain } from "./models";
49
+ export const i = typia.createIs<ViaInterface>();
50
+ export const c = typia.createIs<ViaClass>();
51
+ export const m = (input: unknown) => typia.validate<ViaImplements>(input);
52
+ export const e = typia.createIs<InstanceType<typeof Bound>>();
53
+ export const p = typia.createIs<Plain>();
54
+ `,
55
+ }
56
+ for name, text := range files {
57
+ if err := os.WriteFile(filepath.Join(project, name), []byte(text), 0o644); err != nil {
58
+ t.Fatal(err)
59
+ }
60
+ }
61
+ ttscTypiaTestTypecheck(t, project)
62
+ output, errors, code := ttscTypiaTestCapture(func() int { return runTransform([]string{"--cwd", project, "--output", "ts"}) })
63
+ if code != 0 {
64
+ t.Fatalf("heritage transform: %d\n%s", code, errors)
65
+ }
66
+ var envelope struct {
67
+ Dependencies map[string][]string `json:"dependencies"`
68
+ }
69
+ if err := json.Unmarshal([]byte(output), &envelope); err != nil {
70
+ t.Fatal(err)
71
+ }
72
+ found := map[string]bool{}
73
+ for _, name := range envelope.Dependencies["src/main.ts"] {
74
+ found[name] = true
75
+ }
76
+ for _, name := range []string{"models", "interface-base", "class-base", "contract", "tag"} {
77
+ if !found["src/"+name+".ts"] {
78
+ t.Errorf("missing %s heritage dependency: %v", name, found)
79
+ }
80
+ }
81
+ if found["src/unrelated.ts"] {
82
+ t.Errorf("unconsumed heritage was reported: %v", found)
83
+ }
84
+ }
@@ -9,13 +9,14 @@ import (
9
9
  shimast "github.com/microsoft/typescript-go/shim/ast"
10
10
  shimcore "github.com/microsoft/typescript-go/shim/core"
11
11
  shimparser "github.com/microsoft/typescript-go/shim/parser"
12
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
12
13
  )
13
14
 
14
15
  // ttscTypiaTestSchemaLiteral decodes the emitted schema object's AST without
15
16
  // executing JavaScript. It rejects expressions rather than evaluating them.
16
17
  func ttscTypiaTestSchemaLiteral(t *testing.T, output string) map[string]any {
17
18
  t.Helper()
18
- file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: filepath.ToSlash(filepath.Join(t.TempDir(), "emitted.ts"))}, output, shimcore.ScriptKindTS)
19
+ file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(filepath.Join(t.TempDir(), "emitted.ts")))}, output, shimcore.ScriptKindTS)
19
20
  var found map[string]any
20
21
  var walk func(*shimast.Node) bool
21
22
  walk = func(node *shimast.Node) bool {
@@ -14,6 +14,7 @@ import (
14
14
  shimast "github.com/microsoft/typescript-go/shim/ast"
15
15
  shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
16
16
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
17
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
17
18
  "github.com/samchon/ttsc/packages/ttsc/driver"
18
19
  typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
19
20
  nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
@@ -250,7 +251,7 @@ func runTransformProject(
250
251
  if sf.IsDeclarationFile {
251
252
  continue
252
253
  }
253
- key := driver.TransformOutputKey(cwd, sf.FileName())
254
+ key := driver.TransformOutputKey(cwd, string(sf.FileName()))
254
255
  if filepath.IsAbs(key) || key == ".." || strings.HasPrefix(key, "../") {
255
256
  continue
256
257
  }
@@ -332,13 +333,13 @@ func transformSingleToJavaScript(
332
333
  // itself. Nothing the emit writes is then this file's artifact, so guard the
333
334
  // comparison rather than let an empty expectation match anything.
334
335
  expected := ""
335
- if paths := shimcompiler.GetOutputPathsFor(target, prog.TSProgram.Options(), prog.TSProgram, false); paths != nil {
336
- expected = filepath.ToSlash(paths.JsFilePath())
336
+ if paths := shimcompiler.OutputPathsFor(target, prog.TSProgram.Options(), prog.TSProgram); paths != nil {
337
+ expected = filepath.ToSlash(string(paths.JsFilePath()))
337
338
  }
338
339
  var captured string
339
340
  found := false
340
- writeFile := shimcompiler.WriteFile(func(fileName, text string, _ *shimcompiler.WriteFileData) error {
341
- if expected != "" && filepath.ToSlash(fileName) == expected {
341
+ writeFile := shimcompiler.WriteFile(func(fileName shimtspath.RootedFilePath, text string, _ *shimcompiler.WriteFileData) error {
342
+ if expected != "" && filepath.ToSlash(string(fileName)) == expected {
342
343
  captured = text
343
344
  found = true
344
345
  }
@@ -349,7 +350,7 @@ func transformSingleToJavaScript(
349
350
  return "", 3
350
351
  }
351
352
  if !found {
352
- fmt.Fprintf(stderr, "ttsc-typia transform: no output produced for %s\n", target.FileName())
353
+ fmt.Fprintf(stderr, "ttsc-typia transform: no output produced for %s\n", string(target.FileName()))
353
354
  return "", 3
354
355
  }
355
356
  return captured, 0
@@ -5,24 +5,18 @@ import (
5
5
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
6
6
  )
7
7
 
8
- // EmitFactory resolves the node factory a node-creating helper should use.
8
+ // EmitFactory selects a live emit context's factory or the caller's explicit
9
+ // standalone fallback. Returning the same instance preserves its compiler or
10
+ // caller-installed hooks. A nil fallback remains nil when no context is supplied.
9
11
  //
10
- // When an emit EmitContext is present (emit stage / AST-integration mode) its
11
- // Factory is returned so every created node carries original-node tracking and
12
- // emit metadata (parent links, comments, source maps, const-enum constants,
13
- // namespace alias) that the tsgo emit pipeline relies on. When ec is nil (legacy
14
- // text emit, analysis-only or test paths) the caller's standalone fallback
15
- // factory is returned unchanged.
12
+ // The caller owns the fallback and must confine its mutable arena to its own
13
+ // construction lifetime. This selector retains no factory; callers that need a
14
+ // fresh standalone arena can use NodeFactoryOf instead.
16
15
  //
17
- // This is the single-resolver form of ImportProgrammer's `if p.emit_ != nil`
18
- // branch: the embedded ast.NodeFactory inside printer.NodeFactory carries the
19
- // emit hooks, so &ec.Factory.NodeFactory produces metadata-bearing nodes while
20
- // printing identically to the fallback (token text/structure unchanged).
21
- //
22
- // @evidence contracts/common.md#principled-implementation A live emit context returns its embedded node factory, whose hooks give created nodes original-node links and emit metadata, and otherwise the caller's standalone factory is returned unchanged; both print the same tokens.
23
- // @evidence contracts/common.md#clear-and-simple-design One conditional that replaces the same branch repeated in every node-creating helper.
24
- // @evidence contracts/common.md#prohibited-implementation-shortcuts The fallback is the supported analysis and test path and not a workaround for a missing context.
25
- // @evidence contracts/common.md#meaningful-documentation The doc states both outcomes and why the emit factory is preferred.
16
+ // @evidence contracts/common.md#principled-implementation A live context returns its embedded factory to retain compiler emit hooks. Otherwise the exact supplied fallback, including nil, is returned so caller ownership and custom hooks remain intact; the selector allocates and retains no arena.
17
+ // @evidence contracts/common.md#clear-and-simple-design One conditional implements the explicit-fallback selection contract used by native consumers, separately from NodeFactoryOf's fresh-allocation contract.
18
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts Selection uses the public compiler factory boundary without foreign mutation, shared state or serialization. The fallback is caller-owned; typia's own context-free construction uses fresh factories through NodeFactoryOf.
19
+ // @evidence contracts/common.md#meaningful-documentation The comment states selection priority, nil behavior, hook preservation and the caller's arena ownership responsibility.
26
20
  func EmitFactory(ec *shimprinter.EmitContext, fallback *shimast.NodeFactory) *shimast.NodeFactory {
27
21
  if ec != nil {
28
22
  return &ec.Factory.NodeFactory
@@ -30,20 +24,17 @@ func EmitFactory(ec *shimprinter.EmitContext, fallback *shimast.NodeFactory) *sh
30
24
  return fallback
31
25
  }
32
26
 
33
- // EmitFactoryOf is the variadic form for node-creating free helpers and shared
34
- // static factories that receive the emit context through an optional `emit
35
- // ...*EmitContext` parameter. Passing no context (or a nil one) selects the
36
- // standalone fallback, which is the legacy / analysis-only / test path; passing
37
- // a live context routes creation through ec.Factory. This lets call sites thread
38
- // the real context in progressively while the build stays green at every step.
27
+ // EmitFactoryOf selects the first optional context through EmitFactory.
28
+ // An omitted or nil first context selects the caller's fallback even when a
29
+ // later context is live; no factory is allocated or retained by the selector.
39
30
  //
40
- // @evidence contracts/common.md#principled-implementation The variadic form selects the first emit context when it is present and non-nil and otherwise the fallback, so helpers can receive the context as an optional trailing argument.
41
- // @evidence contracts/common.md#clear-and-simple-design One conditional over a slice, used by free helpers that cannot hold a receiver.
42
- // @evidence contracts/common.md#prohibited-implementation-shortcuts It does not mutate its inputs.
43
- // @evidence contracts/common.md#meaningful-documentation The doc explains the optional parameter and the progressive threading of the context.
31
+ // @evidence contracts/common.md#principled-implementation Only the first optional context participates, preserving the existing variadic consumer contract. EmitFactory owns the same live-context versus caller-fallback decision for both entry forms.
32
+ // @evidence contracts/common.md#clear-and-simple-design A slice-presence check adapts optional arguments to the scalar selector without duplicating its factory selection.
33
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts The adapter changes neither the caller's factory nor its context and stores no shared mutable arena.
34
+ // @evidence contracts/common.md#meaningful-documentation The comment explains omitted, nil-first and later-context behavior and makes the absence of allocation and retained state explicit.
44
35
  func EmitFactoryOf(fallback *shimast.NodeFactory, emit ...*shimprinter.EmitContext) *shimast.NodeFactory {
45
- if len(emit) != 0 && emit[0] != nil {
46
- return &emit[0].Factory.NodeFactory
36
+ if len(emit) == 0 {
37
+ return EmitFactory(nil, fallback)
47
38
  }
48
- return fallback
39
+ return EmitFactory(emit[0], fallback)
49
40
  }
@@ -130,8 +130,6 @@ type importProgrammer_asset struct {
130
130
  order []string
131
131
  }
132
132
 
133
- var importProgrammer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
134
-
135
133
  // NewImportProgrammer creates an importer with the first option record, or the
136
134
  // zero options.
137
135
  //
@@ -213,7 +211,7 @@ func (p *ImportProgrammer) Default(props ImportProgrammer_IDefault) *shimast.Nod
213
211
  if p.emit_ != nil {
214
212
  return p.member(asset, "default")
215
213
  }
216
- return EmitFactory(p.emit_, importProgrammer_factory).NewIdentifier(asset.Default.Name)
214
+ return NodeFactoryOf(p.emit_).NewIdentifier(asset.Default.Name)
217
215
  }
218
216
 
219
217
  // Instance records a named import and returns its reference. The alias, or the
@@ -236,7 +234,7 @@ func (p *ImportProgrammer) Instance(props ImportProgrammer_IInstance) *shimast.N
236
234
  if p.emit_ != nil {
237
235
  return p.member(asset, props.Name)
238
236
  }
239
- return EmitFactory(p.emit_, importProgrammer_factory).NewIdentifier(alias)
237
+ return NodeFactoryOf(p.emit_).NewIdentifier(alias)
240
238
  }
241
239
 
242
240
  // Namespace records a namespace import of the file and returns its reference. The
@@ -255,7 +253,7 @@ func (p *ImportProgrammer) Namespace(props ImportProgrammer_INamespace) *shimast
255
253
  if p.emit_ != nil {
256
254
  return p.namespaceName(asset)
257
255
  }
258
- return EmitFactory(p.emit_, importProgrammer_factory).NewIdentifier(asset.namespace.Name)
256
+ return NodeFactoryOf(p.emit_).NewIdentifier(asset.namespace.Name)
259
257
  }
260
258
 
261
259
  // Type builds an import type node `import("file").Qualifier<Arguments>`. It
@@ -266,7 +264,7 @@ func (p *ImportProgrammer) Namespace(props ImportProgrammer_INamespace) *shimast
266
264
  // @evidence contracts/common.md#prohibited-implementation-shortcuts An unsupported qualifier type produces a node without a qualifier and does not panic.
267
265
  // @evidence contracts/common.md#meaningful-documentation The doc states that nothing is registered.
268
266
  func (p *ImportProgrammer) Type(props ImportProgrammer_TypeProps) *shimast.Node {
269
- f := EmitFactory(p.emit_, importProgrammer_factory)
267
+ f := NodeFactoryOf(p.emit_)
270
268
  var qualifier *shimast.EntityName
271
269
  switch name := props.Name.(type) {
272
270
  case string:
@@ -343,7 +341,7 @@ func (p *ImportProgrammer) GetInternalText(name string) string {
343
341
  // @evidence contracts/common.md#prohibited-implementation-shortcuts The ranking is a fixed table and the order is deterministic.
344
342
  // @evidence contracts/common.md#meaningful-documentation The doc states the order and the two forms.
345
343
  func (p *ImportProgrammer) ToStatements() []*shimast.Node {
346
- f := EmitFactory(p.emit_, importProgrammer_factory)
344
+ f := NodeFactoryOf(p.emit_)
347
345
  statements := []*shimast.Node{}
348
346
  order := append([]string{}, p.order_...)
349
347
  indices := map[string]int{}
@@ -0,0 +1,25 @@
1
+ package context
2
+
3
+ import (
4
+ shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
6
+ )
7
+
8
+ // NodeFactoryOf returns the first emit context's factory, or a fresh standalone
9
+ // factory when that context is absent or nil. Standalone callers own the factory
10
+ // and its nodes; no mutable arena is shared between independent calls.
11
+ //
12
+ // An emit factory retains the compiler's original-node and parent metadata.
13
+ // Helpers should forward their context throughout a transform. A standalone
14
+ // factory has no emit hooks and serves analysis and context-free construction.
15
+ //
16
+ // @evidence contracts/common.md#principled-implementation A live first context returns its embedded node factory, whose hooks maintain emit provenance. Otherwise the supported ast constructor supplies a new arena owned by this caller, so independent Programs cannot race through a fallback factory.
17
+ // @evidence contracts/common.md#clear-and-simple-design One resolver owns both optional-context selection and standalone allocation; node-building helpers retain no package-global factory.
18
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts The resolver uses the compiler's public factory boundary without modifying foreign state or serializing independent Programs. Missing contexts receive independent factories rather than a shared compensation.
19
+ // @evidence contracts/common.md#meaningful-documentation The comment distinguishes per-emit provenance from standalone construction and states ownership and nil behavior.
20
+ func NodeFactoryOf(emit ...*shimprinter.EmitContext) *shimast.NodeFactory {
21
+ if len(emit) != 0 && emit[0] != nil {
22
+ return &emit[0].Factory.NodeFactory
23
+ }
24
+ return shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
25
+ }
@@ -0,0 +1,45 @@
1
+ package context
2
+
3
+ import (
4
+ "testing"
5
+
6
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
7
+ )
8
+
9
+ // TestNodeFactoryOwnership verifies standalone arenas are independent while a
10
+ // live first emit context preserves its compiler factory identity.
11
+ //
12
+ // A shared no-context fallback races even when all live contexts are distinct.
13
+ // Allocating separately also prevents a later standalone caller from changing
14
+ // nodes retained by an earlier caller.
15
+ //
16
+ // 1. Select factories with omitted and explicit nil contexts and compare identity.
17
+ // 2. Select a live context twice and require its embedded factory both times.
18
+ // 3. Allocate distinct identifiers and require the earlier identifier to survive.
19
+ //
20
+ // @evidence contracts/testing.md#behavioral-verification Calls NodeFactoryOf with absent, nil and live contexts, asserts independent fallback pointer identities and exact live factory identity, then checks identifier text survives allocations in the other factories.
21
+ // @evidence contracts/testing.md#independent-expectations The ownership contract requires no arena sharing across independent standalone requests and requires live compiler factory identity to retain emit hooks. Authored identifier strings supply the content oracle.
22
+ // @evidence contracts/testing.md#distinguishing-cases Omitted, explicit nil, repeated live and nil-first-with-live-second contexts distinguish first-argument selection and fallback independence; a different context must select a different live factory.
23
+ // @evidence contracts/testing.md#execution-ownership Go's native unit population discovers this same-package resolver test through test:go:native and test:go:7.1. It uses real in-memory compiler factories and starts no process or installed consumer.
24
+ func TestNodeFactoryOwnership(t *testing.T) {
25
+ first, second := NodeFactoryOf(), NodeFactoryOf(nil)
26
+ live := shimprinter.NewEmitContext()
27
+ third := NodeFactoryOf(nil, live)
28
+ if first == second || first == third || second == third {
29
+ t.Fatal("standalone callers share a mutable arena")
30
+ }
31
+ if NodeFactoryOf(live) != &live.Factory.NodeFactory || NodeFactoryOf(live) != NodeFactoryOf(live) {
32
+ t.Fatal("live emit factory identity was lost")
33
+ }
34
+ if NodeFactoryOf(live) == NodeFactoryOf(shimprinter.NewEmitContext()) {
35
+ t.Fatal("independent emit contexts share a factory")
36
+ }
37
+ original := first.NewIdentifier("first")
38
+ for range 1024 {
39
+ second.NewIdentifier("second")
40
+ third.NewIdentifier("third")
41
+ }
42
+ if original.Text() != "first" {
43
+ t.Fatalf("standalone allocation corrupted retained text: %s", original.Text())
44
+ }
45
+ }
@@ -3,6 +3,7 @@ package factories
3
3
  import (
4
4
  "crypto/rand"
5
5
  "fmt"
6
+ shimtspath "github.com/microsoft/typescript-go/shim/tspath"
6
7
  "math"
7
8
  "os"
8
9
  "path/filepath"
@@ -153,10 +154,8 @@ type ExpressionFactory_GetEscapedTextProps struct {
153
154
  Input *shimast.Expression
154
155
  }
155
156
 
156
- var expressionFactory_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
157
-
158
157
  func (expressionFactoryNamespace) Number(value any, emit ...*shimprinter.EmitContext) *shimast.Node {
159
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
158
+ f := nativecontext.NodeFactoryOf(emit...)
160
159
  text, numeric := expressionFactory_number_text(value)
161
160
  // Go spells the non-finite floats `NaN`, `+Inf`, and `-Inf`. Only the first
162
161
  // happens to be JavaScript; `+Inf` evaluates an undeclared `Inf` and throws a
@@ -193,7 +192,7 @@ func (expressionFactoryNamespace) Number(value any, emit ...*shimprinter.EmitCon
193
192
  // `BigInt` parses the digits of a string directly, so the string form is exact
194
193
  // at any magnitude.
195
194
  func (expressionFactoryNamespace) Bigint(value any, emit ...*shimprinter.EmitContext) *shimast.Node {
196
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
195
+ f := nativecontext.NodeFactoryOf(emit...)
197
196
  return f.NewCallExpression(
198
197
  f.NewIdentifier("BigInt"),
199
198
  nil,
@@ -206,7 +205,7 @@ func (expressionFactoryNamespace) Bigint(value any, emit ...*shimprinter.EmitCon
206
205
  }
207
206
 
208
207
  func (expressionFactoryNamespace) IsRequired(input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
209
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
208
+ f := nativecontext.NodeFactoryOf(emit...)
210
209
  return f.NewBinaryExpression(
211
210
  nil,
212
211
  f.NewIdentifier("undefined"),
@@ -217,7 +216,7 @@ func (expressionFactoryNamespace) IsRequired(input *shimast.Expression, emit ...
217
216
  }
218
217
 
219
218
  func (expressionFactoryNamespace) IsArray(input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
220
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
219
+ f := nativecontext.NodeFactoryOf(emit...)
221
220
  return f.NewCallExpression(
222
221
  f.NewIdentifier("Array.isArray"),
223
222
  nil,
@@ -228,7 +227,7 @@ func (expressionFactoryNamespace) IsArray(input *shimast.Expression, emit ...*sh
228
227
  }
229
228
 
230
229
  func (expressionFactoryNamespace) IsObject(props ExpressionFactory_IsObjectProps, emit ...*shimprinter.EmitContext) *shimast.Node {
231
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
230
+ f := nativecontext.NodeFactoryOf(emit...)
232
231
  conditions := []*shimast.Node{
233
232
  f.NewBinaryExpression(
234
233
  nil,
@@ -273,7 +272,7 @@ func (expressionFactoryNamespace) IsObject(props ExpressionFactory_IsObjectProps
273
272
  }
274
273
 
275
274
  func (expressionFactoryNamespace) IsInstanceOf(t string, input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
276
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
275
+ f := nativecontext.NodeFactoryOf(emit...)
277
276
  return f.NewBinaryExpression(
278
277
  nil,
279
278
  input,
@@ -284,7 +283,7 @@ func (expressionFactoryNamespace) IsInstanceOf(t string, input *shimast.Expressi
284
283
  }
285
284
 
286
285
  func (expressionFactoryNamespace) Coalesce(x *shimast.Expression, y *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
287
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
286
+ f := nativecontext.NodeFactoryOf(emit...)
288
287
  return f.NewBinaryExpression(
289
288
  nil,
290
289
  x,
@@ -301,7 +300,7 @@ func (expressionFactoryNamespace) Coalesce(x *shimast.Expression, y *shimast.Exp
301
300
  // removes that workaround. The node is structurally identical to the patched-up
302
301
  // form, so emitted text is unchanged.
303
302
  func (expressionFactoryNamespace) Conditional(condition *shimast.Expression, whenTrue *shimast.Expression, whenFalse *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
304
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
303
+ f := nativecontext.NodeFactoryOf(emit...)
305
304
  return f.NewConditionalExpression(
306
305
  condition,
307
306
  f.NewToken(shimast.KindQuestionToken),
@@ -312,7 +311,7 @@ func (expressionFactoryNamespace) Conditional(condition *shimast.Expression, whe
312
311
  }
313
312
 
314
313
  func (expressionFactoryNamespace) Currying(props ExpressionFactory_CurryingProps, emit ...*shimprinter.EmitContext) *shimast.Node {
315
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
314
+ f := nativecontext.NodeFactoryOf(emit...)
316
315
  if len(props.Arguments) == 0 {
317
316
  return f.NewCallExpression(props.Function, nil, nil, nil, shimast.NodeFlagsNone)
318
317
  }
@@ -342,7 +341,7 @@ func (expressionFactoryNamespace) SelfCall(ec *shimprinter.EmitContext, body *sh
342
341
  if body != nil && body.Kind == shimast.KindCallExpression {
343
342
  return body
344
343
  }
345
- f := nativecontext.EmitFactory(ec, expressionFactory_factory)
344
+ f := nativecontext.NodeFactoryOf(ec)
346
345
  var output *shimast.TypeNode
347
346
  if len(typeNode) != 0 {
348
347
  output = typeNode[0]
@@ -376,13 +375,13 @@ func (expressionFactoryNamespace) GetEscapedText(props ExpressionFactory_GetEsca
376
375
  }
377
376
 
378
377
  func (expressionFactoryNamespace) Transpile(props ExpressionFactory_TranspileProps, emit ...*shimprinter.EmitContext) func(input *shimast.Expression) *shimast.Node {
379
- f := nativecontext.EmitFactoryOf(expressionFactory_factory, emit...)
378
+ f := nativecontext.NodeFactoryOf(emit...)
380
379
  fileName := filepath.Join(os.TempDir(), expressionFactory_random_format_uuid()+".ts")
381
380
  if absolute, err := filepath.Abs(fileName); err == nil {
382
381
  fileName = absolute
383
382
  }
384
383
  file := shimparser.ParseSourceFile(
385
- shimast.SourceFileParseOptions{FileName: filepath.ToSlash(fileName)},
384
+ shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(fileName))},
386
385
  props.Script,
387
386
  shimcore.ScriptKindTS,
388
387
  )