typia 13.1.0 → 13.1.19
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.
- package/README.md +3 -0
- package/lib/_virtual/_rolldown/runtime.mjs +1 -5
- package/lib/executable/FileSystemIdentity.d.ts +21 -0
- package/lib/executable/FileSystemIdentity.js +183 -0
- package/lib/executable/FileSystemIdentity.js.map +1 -0
- package/lib/executable/FileSystemIdentity.mjs +129 -0
- package/lib/executable/FileSystemIdentity.mjs.map +1 -0
- package/lib/executable/TypiaGenerateWizard.js +264 -104
- package/lib/executable/TypiaGenerateWizard.js.map +1 -1
- package/lib/executable/TypiaGenerateWizard.mjs +233 -93
- package/lib/executable/TypiaGenerateWizard.mjs.map +1 -1
- package/lib/functional.d.ts +15 -12
- package/lib/functional.js.map +1 -1
- package/lib/functional.mjs.map +1 -1
- package/lib/internal/_ProtobufReader.d.ts +17 -0
- package/lib/internal/_ProtobufReader.js +117 -45
- package/lib/internal/_ProtobufReader.js.map +1 -1
- package/lib/internal/_ProtobufReader.mjs +108 -39
- package/lib/internal/_ProtobufReader.mjs.map +1 -1
- package/lib/internal/_createStandardSchema.js +18 -1
- package/lib/internal/_createStandardSchema.js.map +1 -1
- package/lib/internal/_createStandardSchema.mjs +12 -1
- package/lib/internal/_createStandardSchema.mjs.map +1 -1
- package/lib/internal/_decimal.d.ts +14 -0
- package/lib/internal/_decimal.js +61 -0
- package/lib/internal/_decimal.js.map +1 -0
- package/lib/internal/_decimal.mjs +49 -0
- package/lib/internal/_decimal.mjs.map +1 -0
- package/lib/internal/_httpQueryParseURLSearchParams.js +23 -2
- package/lib/internal/_httpQueryParseURLSearchParams.js.map +1 -1
- package/lib/internal/_httpQueryParseURLSearchParams.mjs +20 -2
- package/lib/internal/_httpQueryParseURLSearchParams.mjs.map +1 -1
- package/lib/internal/_isFormatByte.js +2 -5
- package/lib/internal/_isFormatByte.js.map +1 -1
- package/lib/internal/_isFormatByte.mjs +2 -5
- package/lib/internal/_isFormatByte.mjs.map +1 -1
- package/lib/internal/_isFormatDate.js +15 -2
- package/lib/internal/_isFormatDate.js.map +1 -1
- package/lib/internal/_isFormatDate.mjs +22 -2
- package/lib/internal/_isFormatDate.mjs.map +1 -1
- package/lib/internal/_isFormatDateTime.js +21 -2
- package/lib/internal/_isFormatDateTime.js.map +1 -1
- package/lib/internal/_isFormatDateTime.mjs +13 -2
- package/lib/internal/_isFormatDateTime.mjs.map +1 -1
- package/lib/internal/_isFormatIri.js +3 -2
- package/lib/internal/_isFormatIri.js.map +1 -1
- package/lib/internal/_isFormatIri.mjs +3 -2
- package/lib/internal/_isFormatIri.mjs.map +1 -1
- package/lib/internal/_isFormatIriReference.js +10 -2
- package/lib/internal/_isFormatIriReference.js.map +1 -1
- package/lib/internal/_isFormatIriReference.mjs +9 -2
- package/lib/internal/_isFormatIriReference.mjs.map +1 -1
- package/lib/internal/_isFormatUriReference.js +1 -1
- package/lib/internal/_isFormatUriReference.js.map +1 -1
- package/lib/internal/_isFormatUriReference.mjs +1 -1
- package/lib/internal/_isFormatUriReference.mjs.map +1 -1
- package/lib/internal/_isMultipleOf.d.ts +1 -0
- package/lib/internal/_isMultipleOf.js +13 -0
- package/lib/internal/_isMultipleOf.js.map +1 -0
- package/lib/internal/_isMultipleOf.mjs +12 -0
- package/lib/internal/_isMultipleOf.mjs.map +1 -0
- package/lib/internal/_isTypeFloat.js +1 -1
- package/lib/internal/_isTypeFloat.js.map +1 -1
- package/lib/internal/_isTypeFloat.mjs +1 -1
- package/lib/internal/_isTypeFloat.mjs.map +1 -1
- package/lib/internal/_isTypeInt64.js +1 -0
- package/lib/internal/_isTypeInt64.js.map +1 -1
- package/lib/internal/_isTypeInt64.mjs.map +1 -1
- package/lib/internal/_isTypeInt64Bigint.d.ts +1 -0
- package/lib/internal/_isTypeInt64Bigint.js +15 -0
- package/lib/internal/_isTypeInt64Bigint.js.map +1 -0
- package/lib/internal/_isTypeInt64Bigint.mjs +8 -0
- package/lib/internal/_isTypeInt64Bigint.mjs.map +1 -0
- package/lib/internal/_isTypeUint64.js +1 -0
- package/lib/internal/_isTypeUint64.js.map +1 -1
- package/lib/internal/_isTypeUint64.mjs.map +1 -1
- package/lib/internal/_isTypeUint64Bigint.d.ts +1 -0
- package/lib/internal/_isTypeUint64Bigint.js +10 -0
- package/lib/internal/_isTypeUint64Bigint.js.map +1 -0
- package/lib/internal/_isTypeUint64Bigint.mjs +8 -0
- package/lib/internal/_isTypeUint64Bigint.mjs.map +1 -0
- package/lib/internal/_isUniqueItems.js +96 -109
- package/lib/internal/_isUniqueItems.js.map +1 -1
- package/lib/internal/_isUniqueItems.mjs +62 -50
- package/lib/internal/_isUniqueItems.mjs.map +1 -1
- package/lib/internal/_notationAny.js +8 -1
- package/lib/internal/_notationAny.js.map +1 -1
- package/lib/internal/_notationAny.mjs +7 -1
- package/lib/internal/_notationAny.mjs.map +1 -1
- package/lib/internal/_notationAssign.d.ts +1 -0
- package/lib/internal/_notationAssign.js +18 -0
- package/lib/internal/_notationAssign.js.map +1 -0
- package/lib/internal/_notationAssign.mjs +17 -0
- package/lib/internal/_notationAssign.mjs.map +1 -0
- package/lib/internal/_notationCamel.js +30 -9
- package/lib/internal/_notationCamel.js.map +1 -1
- package/lib/internal/_notationCamel.mjs +13 -5
- package/lib/internal/_notationCamel.mjs.map +1 -1
- package/lib/internal/_notationKebab.d.ts +1 -0
- package/lib/internal/_notationKebab.js +15 -0
- package/lib/internal/_notationKebab.js.map +1 -0
- package/lib/internal/_notationKebab.mjs +15 -0
- package/lib/internal/_notationKebab.mjs.map +1 -0
- package/lib/internal/_notationKeyCollision.d.ts +1 -0
- package/lib/internal/_notationKeyCollision.js +8 -0
- package/lib/internal/_notationKeyCollision.js.map +1 -0
- package/lib/internal/_notationKeyCollision.mjs +8 -0
- package/lib/internal/_notationKeyCollision.mjs.map +1 -0
- package/lib/internal/_notationPascal.js +20 -5
- package/lib/internal/_notationPascal.js.map +1 -1
- package/lib/internal/_notationPascal.mjs +10 -5
- package/lib/internal/_notationPascal.mjs.map +1 -1
- package/lib/internal/_notationSnake.js +25 -22
- package/lib/internal/_notationSnake.js.map +1 -1
- package/lib/internal/_notationSnake.mjs +11 -11
- package/lib/internal/_notationSnake.mjs.map +1 -1
- package/lib/internal/_plainCloneAny.js +174 -38
- package/lib/internal/_plainCloneAny.js.map +1 -1
- package/lib/internal/_plainCloneAny.mjs +126 -22
- package/lib/internal/_plainCloneAny.mjs.map +1 -1
- package/lib/internal/_randomArray.d.ts +1 -1
- package/lib/internal/_randomArray.js +11 -4
- package/lib/internal/_randomArray.js.map +1 -1
- package/lib/internal/_randomArray.mjs +9 -3
- package/lib/internal/_randomArray.mjs.map +1 -1
- package/lib/internal/_randomFormatByte.d.ts +2 -1
- package/lib/internal/_randomFormatByte.js +2 -1
- package/lib/internal/_randomFormatByte.js.map +1 -1
- package/lib/internal/_randomFormatByte.mjs +2 -1
- package/lib/internal/_randomFormatByte.mjs.map +1 -1
- package/lib/internal/_randomFormatDate.d.ts +2 -0
- package/lib/internal/_randomFormatDate.js +3 -2
- package/lib/internal/_randomFormatDate.js.map +1 -1
- package/lib/internal/_randomFormatDate.mjs +3 -2
- package/lib/internal/_randomFormatDate.mjs.map +1 -1
- package/lib/internal/_randomFormatDatetime.d.ts +2 -0
- package/lib/internal/_randomFormatDatetime.js +3 -2
- package/lib/internal/_randomFormatDatetime.js.map +1 -1
- package/lib/internal/_randomFormatDatetime.mjs +3 -2
- package/lib/internal/_randomFormatDatetime.mjs.map +1 -1
- package/lib/internal/_randomFormatDuration.d.ts +2 -1
- package/lib/internal/_randomFormatDuration.js +3 -2
- package/lib/internal/_randomFormatDuration.js.map +1 -1
- package/lib/internal/_randomFormatDuration.mjs +3 -2
- package/lib/internal/_randomFormatDuration.mjs.map +1 -1
- package/lib/internal/_randomFormatEmail.d.ts +2 -1
- package/lib/internal/_randomFormatEmail.js +8 -1
- package/lib/internal/_randomFormatEmail.js.map +1 -1
- package/lib/internal/_randomFormatEmail.mjs +5 -1
- package/lib/internal/_randomFormatEmail.mjs.map +1 -1
- package/lib/internal/_randomFormatHostname.d.ts +8 -1
- package/lib/internal/_randomFormatHostname.js +45 -2
- package/lib/internal/_randomFormatHostname.js.map +1 -1
- package/lib/internal/_randomFormatHostname.mjs +38 -2
- package/lib/internal/_randomFormatHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatIdnEmail.d.ts +2 -1
- package/lib/internal/_randomFormatIdnEmail.js +16 -2
- package/lib/internal/_randomFormatIdnEmail.js.map +1 -1
- package/lib/internal/_randomFormatIdnEmail.mjs +11 -2
- package/lib/internal/_randomFormatIdnEmail.mjs.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.d.ts +2 -1
- package/lib/internal/_randomFormatIdnHostname.js +26 -1
- package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.mjs +25 -2
- package/lib/internal/_randomFormatIdnHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv4.d.ts +2 -1
- package/lib/internal/_randomFormatIpv4.js +2 -1
- package/lib/internal/_randomFormatIpv4.js.map +1 -1
- package/lib/internal/_randomFormatIpv4.mjs +2 -1
- package/lib/internal/_randomFormatIpv4.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv6.d.ts +2 -1
- package/lib/internal/_randomFormatIpv6.js +2 -1
- package/lib/internal/_randomFormatIpv6.js.map +1 -1
- package/lib/internal/_randomFormatIpv6.mjs +2 -1
- package/lib/internal/_randomFormatIpv6.mjs.map +1 -1
- package/lib/internal/_randomFormatIri.d.ts +2 -1
- package/lib/internal/_randomFormatIri.js +1 -1
- package/lib/internal/_randomFormatIri.js.map +1 -1
- package/lib/internal/_randomFormatIri.mjs +1 -1
- package/lib/internal/_randomFormatIri.mjs.map +1 -1
- package/lib/internal/_randomFormatIriReference.d.ts +2 -1
- package/lib/internal/_randomFormatIriReference.js +1 -1
- package/lib/internal/_randomFormatIriReference.js.map +1 -1
- package/lib/internal/_randomFormatIriReference.mjs +1 -1
- package/lib/internal/_randomFormatIriReference.mjs.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.d.ts +2 -1
- package/lib/internal/_randomFormatJsonPointer.js +8 -1
- package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.mjs +5 -1
- package/lib/internal/_randomFormatJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatPassword.d.ts +2 -1
- package/lib/internal/_randomFormatPassword.js +7 -5
- package/lib/internal/_randomFormatPassword.js.map +1 -1
- package/lib/internal/_randomFormatPassword.mjs +5 -1
- package/lib/internal/_randomFormatPassword.mjs.map +1 -1
- package/lib/internal/_randomFormatRegex.d.ts +2 -1
- package/lib/internal/_randomFormatRegex.js +2 -1
- package/lib/internal/_randomFormatRegex.js.map +1 -1
- package/lib/internal/_randomFormatRegex.mjs +2 -1
- package/lib/internal/_randomFormatRegex.mjs.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.d.ts +2 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.js +3 -2
- package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs +3 -2
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatTime.d.ts +2 -1
- package/lib/internal/_randomFormatTime.js +3 -2
- package/lib/internal/_randomFormatTime.js.map +1 -1
- package/lib/internal/_randomFormatTime.mjs +3 -2
- package/lib/internal/_randomFormatTime.mjs.map +1 -1
- package/lib/internal/_randomFormatUri.d.ts +2 -1
- package/lib/internal/_randomFormatUri.js +1 -1
- package/lib/internal/_randomFormatUri.js.map +1 -1
- package/lib/internal/_randomFormatUri.mjs +1 -1
- package/lib/internal/_randomFormatUri.mjs.map +1 -1
- package/lib/internal/_randomFormatUriReference.d.ts +2 -1
- package/lib/internal/_randomFormatUriReference.js +1 -1
- package/lib/internal/_randomFormatUriReference.js.map +1 -1
- package/lib/internal/_randomFormatUriReference.mjs +1 -1
- package/lib/internal/_randomFormatUriReference.mjs.map +1 -1
- package/lib/internal/_randomFormatUriTemplate.d.ts +2 -1
- package/lib/internal/_randomFormatUriTemplate.js +1 -1
- package/lib/internal/_randomFormatUriTemplate.js.map +1 -1
- package/lib/internal/_randomFormatUriTemplate.mjs +1 -1
- package/lib/internal/_randomFormatUriTemplate.mjs.map +1 -1
- package/lib/internal/_randomFormatUrl.d.ts +2 -1
- package/lib/internal/_randomFormatUrl.js +21 -1
- package/lib/internal/_randomFormatUrl.js.map +1 -1
- package/lib/internal/_randomFormatUrl.mjs +18 -1
- package/lib/internal/_randomFormatUrl.mjs.map +1 -1
- package/lib/internal/_randomFormatUuid.d.ts +2 -1
- package/lib/internal/_randomFormatUuid.js +3 -2
- package/lib/internal/_randomFormatUuid.js.map +1 -1
- package/lib/internal/_randomFormatUuid.mjs +3 -2
- package/lib/internal/_randomFormatUuid.mjs.map +1 -1
- package/lib/internal/_randomInteger.js +59 -28
- package/lib/internal/_randomInteger.js.map +1 -1
- package/lib/internal/_randomInteger.mjs +56 -16
- package/lib/internal/_randomInteger.mjs.map +1 -1
- package/lib/internal/_randomMultiple.d.ts +8 -0
- package/lib/internal/_randomMultiple.js +168 -0
- package/lib/internal/_randomMultiple.js.map +1 -0
- package/lib/internal/_randomMultiple.mjs +158 -0
- package/lib/internal/_randomMultiple.mjs.map +1 -0
- package/lib/internal/_randomNumber.js +42 -42
- package/lib/internal/_randomNumber.js.map +1 -1
- package/lib/internal/_randomNumber.mjs +41 -27
- package/lib/internal/_randomNumber.mjs.map +1 -1
- package/lib/internal/_randomPattern.d.ts +2 -1
- package/lib/internal/_randomPattern.js +19 -4
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPattern.mjs +10 -4
- package/lib/internal/_randomPattern.mjs.map +1 -1
- package/lib/internal/_randomStringLength.d.ts +33 -0
- package/lib/internal/_randomStringLength.js +48 -0
- package/lib/internal/_randomStringLength.js.map +1 -0
- package/lib/internal/_randomStringLength.mjs +44 -0
- package/lib/internal/_randomStringLength.mjs.map +1 -0
- package/lib/internal/_stringLength.d.ts +1 -0
- package/lib/internal/_stringLength.js +11 -0
- package/lib/internal/_stringLength.js.map +1 -0
- package/lib/internal/_stringLength.mjs +10 -0
- package/lib/internal/_stringLength.mjs.map +1 -0
- package/lib/internal/_validateReport.js +4 -1
- package/lib/internal/_validateReport.js.map +1 -1
- package/lib/internal/_validateReport.mjs +2 -1
- package/lib/internal/_validateReport.mjs.map +1 -1
- package/lib/internal/private/__notationRename.d.ts +14 -0
- package/lib/internal/private/__notationRename.js +25 -0
- package/lib/internal/private/__notationRename.js.map +1 -0
- package/lib/internal/private/__notationRename.mjs +24 -0
- package/lib/internal/private/__notationRename.mjs.map +1 -0
- package/lib/json.d.ts +2 -2
- package/lib/json.mjs.map +1 -1
- package/lib/module.d.ts +2 -2
- package/lib/module.js.map +1 -1
- package/lib/module.mjs.map +1 -1
- package/lib/notations.d.ts +20 -4
- package/lib/notations.js.map +1 -1
- package/lib/notations.mjs.map +1 -1
- package/native/adapter/plugin_options.go +117 -0
- package/native/adapter/plugin_options_payload_matrix_test.go +181 -0
- package/native/cmd/ttsc-typia/any_array_type_tags_transform_test.go +63 -9
- package/native/cmd/ttsc-typia/assert_guard_factory_contract_test.go +152 -0
- package/native/cmd/ttsc-typia/build.go +81 -64
- package/native/cmd/ttsc-typia/build_no_emit_preserves_analysis_options_test.go +67 -0
- package/native/cmd/ttsc-typia/build_no_emit_transform_diagnostics_test.go +138 -0
- package/native/cmd/ttsc-typia/build_plugin_options_come_from_payload_test.go +48 -0
- package/native/cmd/ttsc-typia/build_transactional_emit_success_test.go +81 -0
- package/native/cmd/ttsc-typia/build_transform_failure_atomicity_test.go +138 -0
- package/native/cmd/ttsc-typia/command_routes_and_diagnostics_coverage_test.go +3 -1
- package/native/cmd/ttsc-typia/compare_less_nan_transform_test.go +98 -0
- package/native/cmd/ttsc-typia/compare_native_object_overmatch_transform_test.go +161 -0
- package/native/cmd/ttsc-typia/compare_nondiscriminable_union_transform_test.go +343 -0
- package/native/cmd/ttsc-typia/compare_union_discrimination_transform_test.go +289 -0
- package/native/cmd/ttsc-typia/escaped_property_key_path_transform_test.go +193 -0
- package/native/cmd/ttsc-typia/finite_option_number_leaf_transform_test.go +240 -0
- package/native/cmd/ttsc-typia/functional_promised_type_transform_test.go +220 -0
- package/native/cmd/ttsc-typia/functional_receiver_transform_test.go +232 -0
- package/native/cmd/ttsc-typia/is_object_function_member_transform_test.go +205 -0
- package/native/cmd/ttsc-typia/json_application_alias_function_validation_transform_test.go +151 -0
- package/native/cmd/ttsc-typia/json_is_stringify_finite_number_transform_test.go +198 -0
- package/native/cmd/ttsc-typia/json_stringify_special_key_transform_test.go +238 -0
- package/native/cmd/ttsc-typia/main_wasm.go +21 -2
- package/native/cmd/ttsc-typia/native_collection_identity_is_transform_test.go +234 -0
- package/native/cmd/ttsc-typia/native_generic_name_guard_is_transform_test.go +194 -0
- package/native/cmd/ttsc-typia/native_name_collision_is_transform_test.go +204 -0
- package/native/cmd/ttsc-typia/non_trailing_rest_tuple_transform_test.go +23 -11
- package/native/cmd/ttsc-typia/notation_dynamic_keys_transform_test.go +286 -0
- package/native/cmd/ttsc-typia/notation_kebab_case_transform_test.go +1 -1
- package/native/cmd/ttsc-typia/notation_literal_collision_transform_test.go +164 -0
- package/native/cmd/ttsc-typia/notation_pascal_case_transform_test.go +230 -0
- package/native/cmd/ttsc-typia/notation_unicode_key_transform_test.go +309 -0
- package/native/cmd/ttsc-typia/pattern_line_terminator_escape_transform_test.go +224 -0
- package/native/cmd/ttsc-typia/plain_prune_union_object_overmatch_transform_test.go +219 -0
- package/native/cmd/ttsc-typia/plugin_options_payload_helpers_test.go +181 -0
- package/native/cmd/ttsc-typia/private_field_filter_is_transform_test.go +278 -0
- package/native/cmd/ttsc-typia/project_catalog_transform_coverage_test.go +73 -27
- package/native/cmd/ttsc-typia/project_dependencies_barrel_reexport_transform_test.go +120 -0
- package/native/cmd/ttsc-typia/project_dependencies_collapsed_alias_transform_test.go +134 -0
- package/native/cmd/ttsc-typia/project_dependencies_custom_lib_name_transform_test.go +135 -0
- package/native/cmd/ttsc-typia/project_dependencies_default_barrel_transform_test.go +114 -0
- package/native/cmd/ttsc-typia/project_dependencies_empty_heritage_transform_test.go +100 -0
- package/native/cmd/ttsc-typia/project_dependencies_envelope_transform_test.go +148 -0
- package/native/cmd/ttsc-typia/project_dependencies_index_signature_alias_transform_test.go +139 -0
- package/native/cmd/ttsc-typia/project_dependencies_local_reexport_transform_test.go +122 -0
- package/native/cmd/ttsc-typia/project_dependencies_signature_alias_transform_test.go +125 -0
- package/native/cmd/ttsc-typia/project_dependencies_star_barrel_transform_test.go +112 -0
- package/native/cmd/ttsc-typia/project_reference_graph_envelope_transform_test.go +190 -0
- package/native/cmd/ttsc-typia/protobuf_is_encode_finite_number_transform_test.go +98 -0
- package/native/cmd/ttsc-typia/protobuf_sequence_field_numbers_transform_test.go +52 -22
- package/native/cmd/ttsc-typia/symbol_key_member_filter_transform_test.go +279 -0
- package/native/cmd/ttsc-typia/template_interpolation_type_tags_transform_test.go +86 -23
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -1
- package/native/cmd/ttsc-typia/transform.go +228 -39
- package/native/cmd/ttsc-typia/{transform_declaration_source_produces_no_js_test.go → transform_declaration_source_produces_no_javascript_test.go} +10 -2
- package/native/cmd/ttsc-typia/transform_fixtures_emit_coverage_test.go +21 -4
- package/native/cmd/ttsc-typia/transform_helpers_test.go +159 -1
- package/native/cmd/ttsc-typia/transform_plugin_options_come_from_payload_test.go +43 -0
- package/native/cmd/ttsc-typia/transform_single_file_failure_atomicity_test.go +114 -0
- package/native/cmd/ttsc-typia/transform_single_file_failure_writes_no_stdout_test.go +43 -0
- package/native/cmd/ttsc-typia/transform_single_file_js_distinguishes_same_basename_sources_test.go +81 -0
- package/native/cmd/ttsc-typia/transform_single_file_js_empty_emit_reports_no_output_test.go +100 -0
- package/native/cmd/ttsc-typia/transform_single_file_js_locates_output_across_layouts_test.go +160 -0
- package/native/cmd/ttsc-typia/transform_single_file_success_publishes_artifact_test.go +54 -0
- package/native/cmd/ttsc-typia/transform_wasm_host_argv_reports_diagnostics_test.go +39 -0
- package/native/cmd/ttsc-typia/tuple_optional_compare_clone_transform_test.go +227 -0
- package/native/core/factories/FormatCheatSheet.go +40 -7
- package/native/core/factories/IdentifierFactory.go +131 -4
- package/native/core/factories/MetadataCommentTagFactory.go +83 -40
- package/native/core/factories/MetadataTypeTagFactory.go +13 -1
- package/native/core/factories/NumericRangeFactory.go +60 -17
- package/native/core/factories/ProtobufFactory.go +7 -4
- package/native/core/factories/factory_utility_coverage_test.go +8 -2
- package/native/core/factories/format_alias_resolves_to_cheat_sheet_test.go +57 -0
- package/native/core/factories/format_alias_table_targets_the_cheat_sheet_test.go +44 -0
- package/native/core/factories/identifier_factory_postfix_escapes_every_key_test.go +147 -0
- package/native/core/factories/identifier_factory_postfix_survives_every_code_point_test.go +86 -0
- package/native/core/factories/internal/metadata/MetadataHelper.go +134 -7
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +116 -8
- package/native/core/factories/internal/metadata/iterate_metadata.go +6 -0
- package/native/core/factories/internal/metadata/iterate_metadata_function.go +4 -11
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +8 -6
- package/native/core/factories/internal/metadata/iterate_metadata_map.go +9 -0
- package/native/core/factories/internal/metadata/iterate_metadata_native.go +27 -2
- package/native/core/factories/internal/metadata/iterate_metadata_set.go +9 -0
- package/native/core/factories/internal/metadata/js_doc_link_text_test.go +69 -0
- package/native/core/factories/internal/metadata/type_arguments_precondition_test.go +70 -0
- package/native/core/factories/metadata_comment_tag_factory_coverage_test.go +39 -0
- package/native/core/factories/metadata_comment_tag_factory_exclusivity_matches_interface_test.go +84 -0
- package/native/core/factories/metadata_type_tag_factory_accepts_legal_tag_combinations_test.go +60 -0
- package/native/core/factories/metadata_type_tag_factory_declarations_test.go +86 -0
- package/native/core/factories/metadata_type_tag_factory_keeps_bool_form_exclusivity_test.go +38 -0
- package/native/core/factories/metadata_type_tag_factory_rejects_cross_kind_exclusive_tags_test.go +65 -0
- package/native/core/factories/metadata_type_tag_factory_rejects_duplicated_array_form_tags_test.go +41 -0
- package/native/core/factories/numeric_range_factory_enforces_64bit_bounds_test.go +69 -0
- package/native/core/factories/protobuf_recovery_ownership_test.go +64 -0
- package/native/core/programmers/IsProgrammer.go +15 -1
- package/native/core/programmers/RandomProgrammer.go +51 -14
- package/native/core/programmers/compare/CompareEqualProgrammer.go +393 -5
- package/native/core/programmers/compare/CompareLessProgrammer.go +4 -2
- package/native/core/programmers/functional/FunctionalAssertFunctionProgrammer.go +4 -6
- package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +5 -13
- package/native/core/programmers/functional/FunctionalAssertReturnProgrammer.go +6 -14
- package/native/core/programmers/functional/FunctionalIsFunctionProgrammer.go +4 -6
- package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +49 -14
- package/native/core/programmers/functional/FunctionalIsReturnProgrammer.go +5 -13
- package/native/core/programmers/functional/FunctionalValidateFunctionProgrammer.go +4 -6
- package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +5 -13
- package/native/core/programmers/functional/FunctionalValidateReturnProgrammer.go +5 -13
- package/native/core/programmers/functional/internal/FunctionalGeneralProgrammer.go +4 -17
- package/native/core/programmers/helpers/CloneJoiner.go +32 -0
- package/native/core/programmers/helpers/NotationJoiner.go +86 -28
- package/native/core/programmers/helpers/StringifyJoinder.go +32 -1
- package/native/core/programmers/helpers/UnionExplorer.go +203 -61
- package/native/core/programmers/helpers/joiner_branch_coverage_test.go +5 -3
- package/native/core/programmers/helpers/union_explorer_coverage_test.go +1 -1
- package/native/core/programmers/internal/CheckerProgrammer.go +100 -48
- package/native/core/programmers/iterate/check_template.go +344 -32
- package/native/core/programmers/iterate/json_schema_discriminator.go +36 -10
- package/native/core/programmers/iterate/json_schema_discriminator_declaration_syntax_test.go +205 -0
- package/native/core/programmers/iterate/openapi_v3_downgrader.go +641 -0
- package/native/core/programmers/iterate/openapi_v3_downgrader_boolean_default_test.go +95 -0
- package/native/core/programmers/iterate/openapi_v3_downgrader_test.go +123 -0
- package/native/core/programmers/iterate/template_interpolation_tags_test.go +40 -8
- package/native/core/programmers/iterate/template_runtime_pattern.go +52 -7
- package/native/core/programmers/json/JsonApplicationProgrammer.go +45 -11
- package/native/core/programmers/json/JsonSchemasProgrammer.go +12 -2
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +12 -10
- package/native/core/programmers/llm/LlmSchemaProgrammer.go +23 -6
- package/native/core/programmers/llm/llm_reference_codec_test.go +24 -0
- package/native/core/programmers/notations/NotationGeneralProgrammer.go +183 -67
- package/native/core/programmers/notations/notation_general_helpers_test.go +1 -3
- package/native/core/programmers/notations/notation_general_programmer_pascal_multiword_test.go +72 -0
- package/native/core/programmers/notations/notation_general_programmer_underscore_boundary_test.go +68 -0
- package/native/core/programmers/plain/PlainClassifyProgrammer.go +23 -15
- package/native/core/programmers/plain/PlainCloneProgrammer.go +20 -7
- package/native/core/programmers/plain/PlainPruneProgrammer.go +19 -5
- package/native/core/programmers/plain/plain_tuple_seed_recovery_ownership_test.go +25 -0
- package/native/core/programmers/protobuf/ProtobufDecodeProgrammer.go +37 -28
- package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +40 -21
- package/native/core/programmers/protobuf/protobuf_programmer_helper_coverage_test.go +26 -0
- package/native/core/programmers/random_programmer_length_argument_test.go +53 -0
- package/native/core/schemas/metadata/MetadataCollection.go +270 -20
- package/native/core/schemas/metadata/MetadataDependency.go +388 -0
- package/native/core/schemas/metadata/MetadataObjectType.go +17 -4
- package/native/core/schemas/metadata/metadata_collection_generic_argument_dot_test.go +122 -0
- package/native/core/schemas/metadata/metadata_collection_name_allocation_test.go +147 -0
- package/native/core/schemas/metadata/metadata_collection_openapi_name_cascade_test.go +55 -0
- package/native/core/schemas/metadata/metadata_collection_openapi_name_test.go +68 -0
- package/native/core/utils/PatternUtil.go +13 -0
- package/native/core/utils/PromiseTypeFactory.go +59 -0
- package/native/core/utils/ProtobufNameEncoder.go +8 -0
- package/native/core/utils/promise_type_factory_semantics_test.go +118 -0
- package/native/go.mod +9 -1
- package/native/transform/CallExpressionTransformer.go +12 -0
- package/native/transform/features/json/JsonApplicationTransformer.go +1 -1
- package/native/transform/features/json/JsonSchemaTransformer.go +1 -1
- package/native/transform/features/json/JsonSchemasTransformer.go +5 -4
- package/package.json +16 -4
- package/src/executable/FileSystemIdentity.ts +189 -0
- package/src/executable/TypiaGenerateWizard.ts +377 -110
- package/src/functional.ts +30 -60
- package/src/internal/_ProtobufReader.ts +129 -43
- package/src/internal/_createStandardSchema.ts +15 -1
- package/src/internal/_decimal.ts +69 -0
- package/src/internal/_httpQueryParseURLSearchParams.ts +24 -2
- package/src/internal/_isFormatByte.ts +2 -5
- package/src/internal/_isFormatDate.ts +15 -2
- package/src/internal/_isFormatDateTime.ts +28 -2
- package/src/internal/_isFormatIri.ts +5 -2
- package/src/internal/_isFormatIriReference.ts +11 -3
- package/src/internal/_isFormatUriReference.ts +1 -1
- package/src/internal/_isMultipleOf.ts +8 -0
- package/src/internal/_isTypeFloat.ts +1 -1
- package/src/internal/_isTypeInt64.ts +1 -0
- package/src/internal/_isTypeInt64Bigint.ts +12 -0
- package/src/internal/_isTypeUint64.ts +1 -0
- package/src/internal/_isTypeUint64Bigint.ts +7 -0
- package/src/internal/_isUniqueItems.ts +134 -132
- package/src/internal/_notationAny.ts +9 -4
- package/src/internal/_notationAssign.ts +20 -0
- package/src/internal/_notationCamel.ts +31 -10
- package/src/internal/_notationKebab.ts +11 -0
- package/src/internal/_notationKeyCollision.ts +9 -0
- package/src/internal/_notationPascal.ts +19 -6
- package/src/internal/_notationSnake.ts +24 -21
- package/src/internal/_plainCloneAny.ts +208 -33
- package/src/internal/_randomArray.ts +13 -4
- package/src/internal/_randomFormatByte.ts +4 -1
- package/src/internal/_randomFormatDate.ts +17 -12
- package/src/internal/_randomFormatDatetime.ts +15 -10
- package/src/internal/_randomFormatDuration.ts +16 -14
- package/src/internal/_randomFormatEmail.ts +8 -2
- package/src/internal/_randomFormatHostname.ts +48 -1
- package/src/internal/_randomFormatIdnEmail.ts +18 -2
- package/src/internal/_randomFormatIdnHostname.ts +34 -2
- package/src/internal/_randomFormatIpv4.ts +3 -2
- package/src/internal/_randomFormatIpv6.ts +3 -2
- package/src/internal/_randomFormatIri.ts +3 -1
- package/src/internal/_randomFormatIriReference.ts +3 -1
- package/src/internal/_randomFormatJsonPointer.ts +8 -2
- package/src/internal/_randomFormatPassword.ts +11 -6
- package/src/internal/_randomFormatRegex.ts +4 -1
- package/src/internal/_randomFormatRelativeJsonPointer.ts +13 -6
- package/src/internal/_randomFormatTime.ts +13 -10
- package/src/internal/_randomFormatUri.ts +3 -1
- package/src/internal/_randomFormatUriReference.ts +3 -1
- package/src/internal/_randomFormatUriTemplate.ts +3 -1
- package/src/internal/_randomFormatUrl.ts +25 -2
- package/src/internal/_randomFormatUuid.ts +10 -6
- package/src/internal/_randomInteger.ts +87 -40
- package/src/internal/_randomMultiple.ts +245 -0
- package/src/internal/_randomNumber.ts +72 -55
- package/src/internal/_randomPattern.ts +26 -4
- package/src/internal/_randomStringLength.ts +72 -0
- package/src/internal/_stringLength.ts +5 -0
- package/src/internal/_validateReport.ts +5 -1
- package/src/internal/private/__notationRename.ts +24 -0
- package/src/json.ts +2 -2
- package/src/module.ts +4 -6
- package/src/notations.ts +20 -4
- package/lib/internal/private/__notationCapitalize.d.ts +0 -1
- package/lib/internal/private/__notationCapitalize.js +0 -6
- package/lib/internal/private/__notationCapitalize.js.map +0 -1
- package/lib/internal/private/__notationCapitalize.mjs +0 -6
- package/lib/internal/private/__notationCapitalize.mjs.map +0 -1
- package/lib/internal/private/__notationUnsnake.d.ts +0 -4
- package/lib/internal/private/__notationUnsnake.js +0 -26
- package/lib/internal/private/__notationUnsnake.js.map +0 -1
- package/lib/internal/private/__notationUnsnake.mjs +0 -15
- package/lib/internal/private/__notationUnsnake.mjs.map +0 -1
- package/native/cmd/ttsc-typia/plugin_options_missing_config_test.go +0 -26
- package/native/cmd/ttsc-typia/plugin_options_undefined_false_test.go +0 -36
- package/src/internal/private/__notationCapitalize.ts +0 -2
- package/src/internal/private/__notationUnsnake.ts +0 -24
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestNotationDynamicKeysTransform verifies dynamic key rewriting and collision rejection.
|
|
11
|
+
//
|
|
12
|
+
// Index signatures use the dynamic object-entry branch rather than statically
|
|
13
|
+
// emitted property assignments. That branch must apply the selected rename to
|
|
14
|
+
// every runtime key and reject two source keys that normalize to one destination
|
|
15
|
+
// before either value is silently lost.
|
|
16
|
+
//
|
|
17
|
+
// 1. Transform direct and factory forms of plain/assert/is/validate operations
|
|
18
|
+
// for camel, Pascal, snake, and kebab notation.
|
|
19
|
+
// 2. Require valid dynamic and nested keys to use each selected convention.
|
|
20
|
+
// 3. Require every operation form to throw on a dynamic destination collision.
|
|
21
|
+
func TestNotationDynamicKeysTransform(t *testing.T) {
|
|
22
|
+
project := notationDynamicKeysProject(t)
|
|
23
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
24
|
+
return runTransform([]string{
|
|
25
|
+
"--cwd", project,
|
|
26
|
+
"--tsconfig", "tsconfig.json",
|
|
27
|
+
"--file", "src/main.ts",
|
|
28
|
+
"--output", "js",
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
if code != 0 {
|
|
32
|
+
t.Fatalf("dynamic notation transform failed: code=%d stderr=\n%s", code, errText)
|
|
33
|
+
}
|
|
34
|
+
notationDynamicKeysRunRuntimeCases(t, project, out)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func notationDynamicKeysProject(t *testing.T) string {
|
|
38
|
+
t.Helper()
|
|
39
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
40
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
41
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
42
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
43
|
+
}
|
|
44
|
+
dir, err := os.MkdirTemp(base, "notation-dynamic-")
|
|
45
|
+
if err != nil {
|
|
46
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
47
|
+
}
|
|
48
|
+
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
|
49
|
+
src := filepath.Join(dir, "src")
|
|
50
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
51
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
52
|
+
}
|
|
53
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(notationDynamicKeysTSConfig), 0o644); err != nil {
|
|
54
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
55
|
+
}
|
|
56
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(notationDynamicKeysSource), 0o644); err != nil {
|
|
57
|
+
t.Fatalf("write source: %v", err)
|
|
58
|
+
}
|
|
59
|
+
return dir
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func notationDynamicKeysRunRuntimeCases(t *testing.T, project string, js string) {
|
|
63
|
+
t.Helper()
|
|
64
|
+
node, err := exec.LookPath("node")
|
|
65
|
+
if err != nil {
|
|
66
|
+
t.Skip("node executable not found")
|
|
67
|
+
}
|
|
68
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
69
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
70
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
71
|
+
}
|
|
72
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
73
|
+
runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
|
|
74
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
75
|
+
t.Fatalf("write runtime module: %v", err)
|
|
76
|
+
}
|
|
77
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
78
|
+
if err := os.WriteFile(runner, []byte(notationDynamicKeysRuntimeRunner), 0o644); err != nil {
|
|
79
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
80
|
+
}
|
|
81
|
+
cmd := exec.Command(node, runner)
|
|
82
|
+
cmd.Dir = runtimeDir
|
|
83
|
+
output, err := cmd.CombinedOutput()
|
|
84
|
+
if err != nil {
|
|
85
|
+
t.Fatalf("dynamic notation runtime cases failed: %v\n%s", err, output)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const notationDynamicKeysTSConfig = `{
|
|
90
|
+
"compilerOptions": {
|
|
91
|
+
"target": "ES2022",
|
|
92
|
+
"module": "commonjs",
|
|
93
|
+
"moduleResolution": "bundler",
|
|
94
|
+
"ignoreDeprecations": "6.0",
|
|
95
|
+
"types": ["*"],
|
|
96
|
+
"esModuleInterop": true,
|
|
97
|
+
"strict": true,
|
|
98
|
+
"skipLibCheck": true
|
|
99
|
+
},
|
|
100
|
+
"include": ["src"]
|
|
101
|
+
}
|
|
102
|
+
`
|
|
103
|
+
|
|
104
|
+
const notationDynamicKeysSource = `import typia from "typia";
|
|
105
|
+
|
|
106
|
+
type UnderscoreRecord = Record<string, { inner_key: string }>;
|
|
107
|
+
type CamelRecord = Record<string, { innerValue: string }>;
|
|
108
|
+
type NumericRecord = Record<number, { innerValue: string }>;
|
|
109
|
+
type TemplateRecord = Record<` + "`item_${string}`" + `, { inner_key: string }>;
|
|
110
|
+
interface SnakeLiteral { "__proto__": { innerValue: string }; }
|
|
111
|
+
interface CamelMixed {
|
|
112
|
+
user_id: { inner_key: string };
|
|
113
|
+
[key: string]: { inner_key: string };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const snakeLiteral = (input: SnakeLiteral) =>
|
|
117
|
+
typia.notations.snake<SnakeLiteral>(input);
|
|
118
|
+
export const mixedCamel = (input: CamelMixed) =>
|
|
119
|
+
typia.notations.camel<CamelMixed>(input);
|
|
120
|
+
export const numericSnake = (input: NumericRecord) =>
|
|
121
|
+
typia.notations.snake<NumericRecord>(input);
|
|
122
|
+
export const templateCamel = (input: TemplateRecord) =>
|
|
123
|
+
typia.notations.camel<TemplateRecord>(input);
|
|
124
|
+
|
|
125
|
+
export const camel = {
|
|
126
|
+
direct: (input: UnderscoreRecord) => typia.notations.camel<UnderscoreRecord>(input),
|
|
127
|
+
create: typia.notations.createCamel<UnderscoreRecord>(),
|
|
128
|
+
assert: (input: unknown) => typia.notations.assertCamel<UnderscoreRecord>(input),
|
|
129
|
+
createAssert: typia.notations.createAssertCamel<UnderscoreRecord>(),
|
|
130
|
+
is: (input: unknown) => typia.notations.isCamel<UnderscoreRecord>(input),
|
|
131
|
+
createIs: typia.notations.createIsCamel<UnderscoreRecord>(),
|
|
132
|
+
validate: (input: unknown) => typia.notations.validateCamel<UnderscoreRecord>(input),
|
|
133
|
+
createValidate: typia.notations.createValidateCamel<UnderscoreRecord>(),
|
|
134
|
+
};
|
|
135
|
+
export const pascal = {
|
|
136
|
+
direct: (input: UnderscoreRecord) => typia.notations.pascal<UnderscoreRecord>(input),
|
|
137
|
+
create: typia.notations.createPascal<UnderscoreRecord>(),
|
|
138
|
+
assert: (input: unknown) => typia.notations.assertPascal<UnderscoreRecord>(input),
|
|
139
|
+
createAssert: typia.notations.createAssertPascal<UnderscoreRecord>(),
|
|
140
|
+
is: (input: unknown) => typia.notations.isPascal<UnderscoreRecord>(input),
|
|
141
|
+
createIs: typia.notations.createIsPascal<UnderscoreRecord>(),
|
|
142
|
+
validate: (input: unknown) => typia.notations.validatePascal<UnderscoreRecord>(input),
|
|
143
|
+
createValidate: typia.notations.createValidatePascal<UnderscoreRecord>(),
|
|
144
|
+
};
|
|
145
|
+
export const snake = {
|
|
146
|
+
direct: (input: CamelRecord) => typia.notations.snake<CamelRecord>(input),
|
|
147
|
+
create: typia.notations.createSnake<CamelRecord>(),
|
|
148
|
+
assert: (input: unknown) => typia.notations.assertSnake<CamelRecord>(input),
|
|
149
|
+
createAssert: typia.notations.createAssertSnake<CamelRecord>(),
|
|
150
|
+
is: (input: unknown) => typia.notations.isSnake<CamelRecord>(input),
|
|
151
|
+
createIs: typia.notations.createIsSnake<CamelRecord>(),
|
|
152
|
+
validate: (input: unknown) => typia.notations.validateSnake<CamelRecord>(input),
|
|
153
|
+
createValidate: typia.notations.createValidateSnake<CamelRecord>(),
|
|
154
|
+
};
|
|
155
|
+
export const kebab = {
|
|
156
|
+
direct: (input: CamelRecord) => typia.notations.kebab<CamelRecord>(input),
|
|
157
|
+
create: typia.notations.createKebab<CamelRecord>(),
|
|
158
|
+
assert: (input: unknown) => typia.notations.assertKebab<CamelRecord>(input),
|
|
159
|
+
createAssert: typia.notations.createAssertKebab<CamelRecord>(),
|
|
160
|
+
is: (input: unknown) => typia.notations.isKebab<CamelRecord>(input),
|
|
161
|
+
createIs: typia.notations.createIsKebab<CamelRecord>(),
|
|
162
|
+
validate: (input: unknown) => typia.notations.validateKebab<CamelRecord>(input),
|
|
163
|
+
createValidate: typia.notations.createValidateKebab<CamelRecord>(),
|
|
164
|
+
};
|
|
165
|
+
`
|
|
166
|
+
|
|
167
|
+
const notationDynamicKeysRuntimeRunner = `const mod = require("./main.cjs");
|
|
168
|
+
|
|
169
|
+
const literalMagic = mod.snakeLiteral(
|
|
170
|
+
Object.fromEntries([["__proto__", { innerValue: "literal" }]]),
|
|
171
|
+
);
|
|
172
|
+
if (
|
|
173
|
+
Object.getPrototypeOf(literalMagic) !== Object.prototype ||
|
|
174
|
+
!Object.hasOwn(literalMagic, "__proto__") ||
|
|
175
|
+
!Object.hasOwn(literalMagic.__proto__, "inner_value")
|
|
176
|
+
) {
|
|
177
|
+
throw new Error("snake literal __proto__ was not emitted as an own data property");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let mixedCollision = null;
|
|
181
|
+
try {
|
|
182
|
+
mod.mixedCamel({
|
|
183
|
+
user_id: { inner_key: "literal" },
|
|
184
|
+
userId: { inner_key: "dynamic" },
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
mixedCollision = error;
|
|
188
|
+
}
|
|
189
|
+
if (mixedCollision === null) {
|
|
190
|
+
throw new Error("camel mixed literal/dynamic destination collision was accepted");
|
|
191
|
+
}
|
|
192
|
+
for (const key of ["user_id", "userId", "userId"]) {
|
|
193
|
+
if (!String(mixedCollision.message).includes(JSON.stringify(key))) {
|
|
194
|
+
throw new Error("camel mixed collision omitted " + key + ": " + mixedCollision.message);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const numeric = mod.numericSnake({ 7: { innerValue: "numeric" } });
|
|
199
|
+
if (!Object.hasOwn(numeric, "7") || !Object.hasOwn(numeric[7], "inner_value")) {
|
|
200
|
+
throw new Error("snake numeric index key disagreed with its mapped return type");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const template = mod.templateCamel({ item_user_id: { inner_key: "template" } });
|
|
204
|
+
if (!Object.hasOwn(template, "itemUserId") || !Object.hasOwn(template.itemUserId, "innerKey")) {
|
|
205
|
+
throw new Error("camel template-literal index key was not converted");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const families = [
|
|
209
|
+
{
|
|
210
|
+
name: "camel",
|
|
211
|
+
api: mod.camel,
|
|
212
|
+
valid: { user_id: { inner_key: "u" }, account_name: { inner_key: "a" } },
|
|
213
|
+
expected: [["userId", "innerKey"], ["accountName", "innerKey"]],
|
|
214
|
+
collision: { user_id: { inner_key: "a" }, userId: { inner_key: "b" } },
|
|
215
|
+
collisionKeys: ["user_id", "userId", "userId"],
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "pascal",
|
|
219
|
+
api: mod.pascal,
|
|
220
|
+
valid: { user_id: { inner_key: "u" }, account_name: { inner_key: "a" } },
|
|
221
|
+
expected: [["UserId", "InnerKey"], ["AccountName", "InnerKey"]],
|
|
222
|
+
collision: { user_id: { inner_key: "a" }, UserId: { inner_key: "b" } },
|
|
223
|
+
collisionKeys: ["user_id", "UserId", "UserId"],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "snake",
|
|
227
|
+
api: mod.snake,
|
|
228
|
+
valid: Object.fromEntries([
|
|
229
|
+
["userId", { innerValue: "u" }],
|
|
230
|
+
["accountName", { innerValue: "a" }],
|
|
231
|
+
["__proto__", { innerValue: "p" }],
|
|
232
|
+
]),
|
|
233
|
+
expected: [["user_id", "inner_value"], ["account_name", "inner_value"], ["__proto__", "inner_value"]],
|
|
234
|
+
collision: { userId: { innerValue: "a" }, user_id: { innerValue: "b" } },
|
|
235
|
+
collisionKeys: ["userId", "user_id", "user_id"],
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "kebab",
|
|
239
|
+
api: mod.kebab,
|
|
240
|
+
valid: { userId: { innerValue: "u" }, account_name: { innerValue: "a" } },
|
|
241
|
+
expected: [["user-id", "inner-value"], ["account-name", "inner-value"]],
|
|
242
|
+
collision: { userId: { innerValue: "a" }, user_id: { innerValue: "b" } },
|
|
243
|
+
collisionKeys: ["userId", "user_id", "user-id"],
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
const variants = ["direct", "create", "assert", "createAssert", "is", "createIs", "validate", "createValidate"];
|
|
247
|
+
|
|
248
|
+
for (const family of families) {
|
|
249
|
+
for (const variant of variants) {
|
|
250
|
+
const raw = family.api[variant](family.valid);
|
|
251
|
+
const converted = variant === "validate" || variant === "createValidate"
|
|
252
|
+
? raw.success === true ? raw.data : null
|
|
253
|
+
: raw;
|
|
254
|
+
if (converted === null) {
|
|
255
|
+
throw new Error(family.name + "." + variant + " rejected valid dynamic input");
|
|
256
|
+
}
|
|
257
|
+
if (Object.keys(converted).length !== family.expected.length) {
|
|
258
|
+
throw new Error(family.name + "." + variant + " emitted unexpected keys: " + JSON.stringify(converted));
|
|
259
|
+
}
|
|
260
|
+
for (const [outer, inner] of family.expected) {
|
|
261
|
+
if (
|
|
262
|
+
!Object.hasOwn(converted, outer) ||
|
|
263
|
+
Object.keys(converted[outer]).length !== 1 ||
|
|
264
|
+
!Object.hasOwn(converted[outer], inner)
|
|
265
|
+
) {
|
|
266
|
+
throw new Error(family.name + "." + variant + " kept an unconverted key: " + JSON.stringify(converted));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
let collision = null;
|
|
271
|
+
try {
|
|
272
|
+
family.api[variant](family.collision);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
collision = error;
|
|
275
|
+
}
|
|
276
|
+
if (collision === null) {
|
|
277
|
+
throw new Error(family.name + "." + variant + " silently accepted a destination collision");
|
|
278
|
+
}
|
|
279
|
+
for (const key of family.collisionKeys) {
|
|
280
|
+
if (!String(collision.message).includes(JSON.stringify(key))) {
|
|
281
|
+
throw new Error(family.name + "." + variant + " collision omitted " + key + ": " + collision.message);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
`
|
|
@@ -36,7 +36,7 @@ func TestNotationKebabCaseTransform(t *testing.T) {
|
|
|
36
36
|
if code != 0 {
|
|
37
37
|
t.Fatalf("kebab notation transform failed: code=%d stderr=\n%s", code, errText)
|
|
38
38
|
}
|
|
39
|
-
for _, key := range []string{`"user-id"`, `"user-name"`, `"_private-value"`, `xmlparser
|
|
39
|
+
for _, key := range []string{`"user-id"`, `"user-name"`, `"_private-value"`, `"xmlparser"`, `"inner-value"`} {
|
|
40
40
|
if !strings.Contains(out, key) {
|
|
41
41
|
t.Fatalf("emitted converter should contain kebab key %s:\n%s", key, out)
|
|
42
42
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestNotationLiteralCollisionTransform verifies static destination collisions are rejected.
|
|
11
|
+
//
|
|
12
|
+
// Literal property metadata exposes every source key during code emission. A
|
|
13
|
+
// converter must reject two distinct literals that normalize to one result
|
|
14
|
+
// instead of emitting duplicate assignments whose last value silently wins.
|
|
15
|
+
//
|
|
16
|
+
// 1. Transform direct and factory plain/assert/is/validate calls for all cases.
|
|
17
|
+
// 2. Give each naming family two literal keys with one normalized destination.
|
|
18
|
+
// 3. Execute every operation and require an error naming both source keys and destination.
|
|
19
|
+
func TestNotationLiteralCollisionTransform(t *testing.T) {
|
|
20
|
+
project := notationLiteralCollisionProject(t)
|
|
21
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
22
|
+
return runTransform([]string{
|
|
23
|
+
"--cwd", project,
|
|
24
|
+
"--tsconfig", "tsconfig.json",
|
|
25
|
+
"--file", "src/main.ts",
|
|
26
|
+
"--output", "js",
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
if code != 0 {
|
|
30
|
+
t.Fatalf("literal collision transform failed: code=%d stderr=\n%s", code, errText)
|
|
31
|
+
}
|
|
32
|
+
notationLiteralCollisionRunRuntimeCases(t, project, out)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func notationLiteralCollisionProject(t *testing.T) string {
|
|
36
|
+
t.Helper()
|
|
37
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
38
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
39
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
40
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
41
|
+
}
|
|
42
|
+
dir, err := os.MkdirTemp(base, "notation-literal-collision-")
|
|
43
|
+
if err != nil {
|
|
44
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
45
|
+
}
|
|
46
|
+
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
|
47
|
+
src := filepath.Join(dir, "src")
|
|
48
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
49
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
50
|
+
}
|
|
51
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(notationDynamicKeysTSConfig), 0o644); err != nil {
|
|
52
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
53
|
+
}
|
|
54
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(notationLiteralCollisionSource), 0o644); err != nil {
|
|
55
|
+
t.Fatalf("write source: %v", err)
|
|
56
|
+
}
|
|
57
|
+
return dir
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func notationLiteralCollisionRunRuntimeCases(t *testing.T, project string, js string) {
|
|
61
|
+
t.Helper()
|
|
62
|
+
node, err := exec.LookPath("node")
|
|
63
|
+
if err != nil {
|
|
64
|
+
t.Skip("node executable not found")
|
|
65
|
+
}
|
|
66
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
67
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
68
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
69
|
+
}
|
|
70
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
71
|
+
runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
|
|
72
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
73
|
+
t.Fatalf("write runtime module: %v", err)
|
|
74
|
+
}
|
|
75
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
76
|
+
if err := os.WriteFile(runner, []byte(notationLiteralCollisionRuntimeRunner), 0o644); err != nil {
|
|
77
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
78
|
+
}
|
|
79
|
+
cmd := exec.Command(node, runner)
|
|
80
|
+
cmd.Dir = runtimeDir
|
|
81
|
+
output, err := cmd.CombinedOutput()
|
|
82
|
+
if err != nil {
|
|
83
|
+
t.Fatalf("literal collision runtime cases failed: %v\n%s", err, output)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const notationLiteralCollisionSource = `import typia from "typia";
|
|
88
|
+
|
|
89
|
+
interface CamelCollision { user_id: string; userId: string; }
|
|
90
|
+
interface PascalCollision { user_id: string; UserId: string; }
|
|
91
|
+
interface SnakeCollision { userId: string; user_id: string; }
|
|
92
|
+
interface KebabCollision { userId: string; user_id: string; }
|
|
93
|
+
|
|
94
|
+
export const camel = {
|
|
95
|
+
direct: (input: CamelCollision) => typia.notations.camel<CamelCollision>(input),
|
|
96
|
+
create: typia.notations.createCamel<CamelCollision>(),
|
|
97
|
+
assert: (input: unknown) => typia.notations.assertCamel<CamelCollision>(input),
|
|
98
|
+
createAssert: typia.notations.createAssertCamel<CamelCollision>(),
|
|
99
|
+
is: (input: unknown) => typia.notations.isCamel<CamelCollision>(input),
|
|
100
|
+
createIs: typia.notations.createIsCamel<CamelCollision>(),
|
|
101
|
+
validate: (input: unknown) => typia.notations.validateCamel<CamelCollision>(input),
|
|
102
|
+
createValidate: typia.notations.createValidateCamel<CamelCollision>(),
|
|
103
|
+
};
|
|
104
|
+
export const pascal = {
|
|
105
|
+
direct: (input: PascalCollision) => typia.notations.pascal<PascalCollision>(input),
|
|
106
|
+
create: typia.notations.createPascal<PascalCollision>(),
|
|
107
|
+
assert: (input: unknown) => typia.notations.assertPascal<PascalCollision>(input),
|
|
108
|
+
createAssert: typia.notations.createAssertPascal<PascalCollision>(),
|
|
109
|
+
is: (input: unknown) => typia.notations.isPascal<PascalCollision>(input),
|
|
110
|
+
createIs: typia.notations.createIsPascal<PascalCollision>(),
|
|
111
|
+
validate: (input: unknown) => typia.notations.validatePascal<PascalCollision>(input),
|
|
112
|
+
createValidate: typia.notations.createValidatePascal<PascalCollision>(),
|
|
113
|
+
};
|
|
114
|
+
export const snake = {
|
|
115
|
+
direct: (input: SnakeCollision) => typia.notations.snake<SnakeCollision>(input),
|
|
116
|
+
create: typia.notations.createSnake<SnakeCollision>(),
|
|
117
|
+
assert: (input: unknown) => typia.notations.assertSnake<SnakeCollision>(input),
|
|
118
|
+
createAssert: typia.notations.createAssertSnake<SnakeCollision>(),
|
|
119
|
+
is: (input: unknown) => typia.notations.isSnake<SnakeCollision>(input),
|
|
120
|
+
createIs: typia.notations.createIsSnake<SnakeCollision>(),
|
|
121
|
+
validate: (input: unknown) => typia.notations.validateSnake<SnakeCollision>(input),
|
|
122
|
+
createValidate: typia.notations.createValidateSnake<SnakeCollision>(),
|
|
123
|
+
};
|
|
124
|
+
export const kebab = {
|
|
125
|
+
direct: (input: KebabCollision) => typia.notations.kebab<KebabCollision>(input),
|
|
126
|
+
create: typia.notations.createKebab<KebabCollision>(),
|
|
127
|
+
assert: (input: unknown) => typia.notations.assertKebab<KebabCollision>(input),
|
|
128
|
+
createAssert: typia.notations.createAssertKebab<KebabCollision>(),
|
|
129
|
+
is: (input: unknown) => typia.notations.isKebab<KebabCollision>(input),
|
|
130
|
+
createIs: typia.notations.createIsKebab<KebabCollision>(),
|
|
131
|
+
validate: (input: unknown) => typia.notations.validateKebab<KebabCollision>(input),
|
|
132
|
+
createValidate: typia.notations.createValidateKebab<KebabCollision>(),
|
|
133
|
+
};
|
|
134
|
+
`
|
|
135
|
+
|
|
136
|
+
const notationLiteralCollisionRuntimeRunner = `const mod = require("./main.cjs");
|
|
137
|
+
|
|
138
|
+
const families = [
|
|
139
|
+
{ name: "camel", api: mod.camel, input: { user_id: "a", userId: "b" }, expected: ["user_id", "userId", "userId"] },
|
|
140
|
+
{ name: "pascal", api: mod.pascal, input: { user_id: "a", UserId: "b" }, expected: ["user_id", "UserId", "UserId"] },
|
|
141
|
+
{ name: "snake", api: mod.snake, input: { userId: "a", user_id: "b" }, expected: ["userId", "user_id", "user_id"] },
|
|
142
|
+
{ name: "kebab", api: mod.kebab, input: { userId: "a", user_id: "b" }, expected: ["userId", "user_id", "user-id"] },
|
|
143
|
+
];
|
|
144
|
+
const variants = ["direct", "create", "assert", "createAssert", "is", "createIs", "validate", "createValidate"];
|
|
145
|
+
|
|
146
|
+
for (const family of families) {
|
|
147
|
+
for (const variant of variants) {
|
|
148
|
+
let collision = null;
|
|
149
|
+
try {
|
|
150
|
+
family.api[variant](family.input);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
collision = error;
|
|
153
|
+
}
|
|
154
|
+
if (collision === null) {
|
|
155
|
+
throw new Error(family.name + "." + variant + " silently accepted a literal destination collision");
|
|
156
|
+
}
|
|
157
|
+
for (const key of family.expected) {
|
|
158
|
+
if (!String(collision.message).includes(JSON.stringify(key))) {
|
|
159
|
+
throw new Error(family.name + "." + variant + " collision omitted " + key + ": " + collision.message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
`
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestNotationPascalCaseTransform verifies PascalCase notation conversion.
|
|
12
|
+
//
|
|
13
|
+
// Issue #2183: `notations.pascal` capitalized each multi-word segment without
|
|
14
|
+
// lowercasing its tail, so an all-caps or mixed-case word kept its inner
|
|
15
|
+
// uppercase characters and `MAX_COUNT` became `MAXCOUNT`. Both the statically
|
|
16
|
+
// emitted converter (Go, one property assignment per known key) and the dynamic
|
|
17
|
+
// converter (runtime `_notationPascal`, one call per index-signature key) must
|
|
18
|
+
// agree with the declared `PascalCase<T>` type — the type-level assertions live
|
|
19
|
+
// in `tests/test-interface/src/typings/test_pascal_case.ts` — or the result type
|
|
20
|
+
// lies about the produced keys and `.MaxCount` reads back `undefined`.
|
|
21
|
+
//
|
|
22
|
+
// 1. Transform a static fixture covering all-caps, mixed-case, leading
|
|
23
|
+
// underscore, single-word (routed through `plain`, tail preserved), and
|
|
24
|
+
// nested keys; require the emitted converter to reference the PascalCase
|
|
25
|
+
// keys and never the un-lowercased `MAXCOUNT` form.
|
|
26
|
+
// 2. Execute the statically emitted converter and assert its runtime keys.
|
|
27
|
+
// 3. Execute the dynamic `Record` converter (runtime `_notationPascal`) over
|
|
28
|
+
// the same all-caps witnesses and assert it produces the identical keys, so
|
|
29
|
+
// the compile-time emit and the runtime helper are proven byte-equal.
|
|
30
|
+
func TestNotationPascalCaseTransform(t *testing.T) {
|
|
31
|
+
project := notationPascalCaseProject(t)
|
|
32
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
33
|
+
return runTransform([]string{
|
|
34
|
+
"--cwd", project,
|
|
35
|
+
"--tsconfig", "tsconfig.json",
|
|
36
|
+
"--file", "src/main.ts",
|
|
37
|
+
"--output", "js",
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
if code != 0 {
|
|
41
|
+
t.Fatalf("pascal notation transform failed: code=%d stderr=\n%s", code, errText)
|
|
42
|
+
}
|
|
43
|
+
for _, key := range []string{"MaxCount", "HttpPort", "UserId", "FoobarBaz", "InnerValue"} {
|
|
44
|
+
if !strings.Contains(out, key) {
|
|
45
|
+
t.Fatalf("emitted converter should contain PascalCase key %q:\n%s", key, out)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for _, bug := range []string{"MAXCOUNT", "HTTPPORT", "FooBarBaz", "INNERVALUE"} {
|
|
49
|
+
if strings.Contains(out, bug) {
|
|
50
|
+
t.Fatalf("emitted converter must not keep the un-lowercased key %q:\n%s", bug, out)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
notationPascalCaseRunRuntimeCases(t, project, out)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func notationPascalCaseProject(t *testing.T) string {
|
|
57
|
+
t.Helper()
|
|
58
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
59
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
60
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
61
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
62
|
+
}
|
|
63
|
+
dir, err := os.MkdirTemp(base, "notation-pascal-")
|
|
64
|
+
if err != nil {
|
|
65
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
66
|
+
}
|
|
67
|
+
t.Cleanup(func() {
|
|
68
|
+
_ = os.RemoveAll(dir)
|
|
69
|
+
})
|
|
70
|
+
src := filepath.Join(dir, "src")
|
|
71
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
72
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
73
|
+
}
|
|
74
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(notationPascalCaseTSConfig), 0o644); err != nil {
|
|
75
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
76
|
+
}
|
|
77
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(notationPascalCaseSource), 0o644); err != nil {
|
|
78
|
+
t.Fatalf("write source: %v", err)
|
|
79
|
+
}
|
|
80
|
+
return dir
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func notationPascalCaseRunRuntimeCases(t *testing.T, project string, js string) {
|
|
84
|
+
t.Helper()
|
|
85
|
+
node, err := exec.LookPath("node")
|
|
86
|
+
if err != nil {
|
|
87
|
+
t.Skip("node executable not found")
|
|
88
|
+
}
|
|
89
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
90
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
91
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
92
|
+
}
|
|
93
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
94
|
+
runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
|
|
95
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
96
|
+
t.Fatalf("write runtime module: %v", err)
|
|
97
|
+
}
|
|
98
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
99
|
+
if err := os.WriteFile(runner, []byte(notationPascalCaseRuntimeRunner), 0o644); err != nil {
|
|
100
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
101
|
+
}
|
|
102
|
+
cmd := exec.Command(node, runner)
|
|
103
|
+
cmd.Dir = runtimeDir
|
|
104
|
+
output, err := cmd.CombinedOutput()
|
|
105
|
+
if err != nil {
|
|
106
|
+
t.Fatalf("pascal notation runtime cases failed: %v\n%s", err, output)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const notationPascalCaseTSConfig = `{
|
|
111
|
+
"compilerOptions": {
|
|
112
|
+
"target": "ES2022",
|
|
113
|
+
"module": "commonjs",
|
|
114
|
+
"moduleResolution": "bundler",
|
|
115
|
+
"ignoreDeprecations": "6.0",
|
|
116
|
+
"types": ["*"],
|
|
117
|
+
"esModuleInterop": true,
|
|
118
|
+
"strict": true,
|
|
119
|
+
"skipLibCheck": true
|
|
120
|
+
},
|
|
121
|
+
"include": ["src"]
|
|
122
|
+
}
|
|
123
|
+
`
|
|
124
|
+
|
|
125
|
+
const notationPascalCaseSource = `import typia from "typia";
|
|
126
|
+
|
|
127
|
+
interface SourceRecord {
|
|
128
|
+
MAX_COUNT: number;
|
|
129
|
+
HTTP_PORT: number;
|
|
130
|
+
USER_ID: number;
|
|
131
|
+
fooBar_baz: number;
|
|
132
|
+
_MAX_COUNT: number;
|
|
133
|
+
userID: number;
|
|
134
|
+
ID: number;
|
|
135
|
+
nested: { INNER_VALUE: number };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type DynamicRecord = Record<string, { DEEP_VALUE: number }>;
|
|
139
|
+
|
|
140
|
+
export const toPascal = typia.notations.createPascal<SourceRecord>();
|
|
141
|
+
export const isPascal = typia.notations.createIsPascal<SourceRecord>();
|
|
142
|
+
export const assertPascal = typia.notations.createAssertPascal<SourceRecord>();
|
|
143
|
+
export const validatePascal =
|
|
144
|
+
typia.notations.createValidatePascal<SourceRecord>();
|
|
145
|
+
export const toPascalDynamic = typia.notations.createPascal<DynamicRecord>();
|
|
146
|
+
`
|
|
147
|
+
|
|
148
|
+
const notationPascalCaseRuntimeRunner = `const mod = require("./main.cjs");
|
|
149
|
+
|
|
150
|
+
const valid = {
|
|
151
|
+
MAX_COUNT: 1,
|
|
152
|
+
HTTP_PORT: 2,
|
|
153
|
+
USER_ID: 3,
|
|
154
|
+
fooBar_baz: 4,
|
|
155
|
+
_MAX_COUNT: 5,
|
|
156
|
+
userID: 6,
|
|
157
|
+
ID: 7,
|
|
158
|
+
nested: { INNER_VALUE: 8 },
|
|
159
|
+
};
|
|
160
|
+
// Byte-for-byte the key set of PascalCase<SourceRecord>: multi-word tails
|
|
161
|
+
// lowercase (MAX_COUNT -> MaxCount), single words keep their tail through the
|
|
162
|
+
// plain path (userID -> UserID, ID -> ID).
|
|
163
|
+
const expectedKeys = [
|
|
164
|
+
"MaxCount", "HttpPort", "UserId", "FoobarBaz", "_MaxCount", "UserID", "ID", "Nested",
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
const converted = mod.toPascal(valid);
|
|
168
|
+
const keys = Object.keys(converted).sort();
|
|
169
|
+
if (JSON.stringify(keys) !== JSON.stringify([...expectedKeys].sort())) {
|
|
170
|
+
throw new Error("pascal conversion produced unexpected keys: " + JSON.stringify(keys));
|
|
171
|
+
}
|
|
172
|
+
if (converted.MaxCount !== 1 || converted.HttpPort !== 2 || converted.UserId !== 3) {
|
|
173
|
+
throw new Error("pascal conversion lost multi-word values: " + JSON.stringify(converted));
|
|
174
|
+
}
|
|
175
|
+
if (converted.FoobarBaz !== 4 || converted._MaxCount !== 5) {
|
|
176
|
+
throw new Error("mixed-case/leading-underscore keys converted incorrectly: " + JSON.stringify(converted));
|
|
177
|
+
}
|
|
178
|
+
if (converted.UserID !== 6 || converted.ID !== 7) {
|
|
179
|
+
throw new Error("single-word keys must keep their tail: " + JSON.stringify(converted));
|
|
180
|
+
}
|
|
181
|
+
if (JSON.stringify(Object.keys(converted.Nested)) !== JSON.stringify(["InnerValue"])) {
|
|
182
|
+
throw new Error("nested keys should recurse into PascalCase: " + JSON.stringify(converted.Nested));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (mod.isPascal(valid) === null) {
|
|
186
|
+
throw new Error("isPascal should accept the valid input");
|
|
187
|
+
}
|
|
188
|
+
if (mod.isPascal({ ...valid, MAX_COUNT: "x" }) !== null) {
|
|
189
|
+
throw new Error("isPascal should reject an invalid property type");
|
|
190
|
+
}
|
|
191
|
+
if (mod.assertPascal(valid).MaxCount !== 1) {
|
|
192
|
+
throw new Error("assertPascal should return the converted object");
|
|
193
|
+
}
|
|
194
|
+
let thrown = null;
|
|
195
|
+
try {
|
|
196
|
+
mod.assertPascal({ ...valid, nested: { INNER_VALUE: "x" } });
|
|
197
|
+
} catch (error) {
|
|
198
|
+
thrown = error;
|
|
199
|
+
}
|
|
200
|
+
if (thrown === null) {
|
|
201
|
+
throw new Error("assertPascal should throw on invalid nested input");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const success = mod.validatePascal(valid);
|
|
205
|
+
if (success.success !== true || success.data.HttpPort !== 2) {
|
|
206
|
+
throw new Error("validatePascal should succeed with converted data: " + JSON.stringify(success));
|
|
207
|
+
}
|
|
208
|
+
const failure = mod.validatePascal({ ...valid, ID: "x" });
|
|
209
|
+
if (failure.success !== false || failure.errors.length === 0) {
|
|
210
|
+
throw new Error("validatePascal should collect errors for invalid input: " + JSON.stringify(failure));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Dynamic index-signature keys route through the runtime _notationPascal helper
|
|
214
|
+
// rather than statically emitted assignments. Feeding the same all-caps
|
|
215
|
+
// witnesses proves the runtime helper and the compile-time emit agree.
|
|
216
|
+
const dynamic = mod.toPascalDynamic({
|
|
217
|
+
MAX_COUNT: { DEEP_VALUE: 1 },
|
|
218
|
+
HTTP_PORT: { DEEP_VALUE: 2 },
|
|
219
|
+
});
|
|
220
|
+
const dynamicKeys = Object.keys(dynamic).sort();
|
|
221
|
+
if (JSON.stringify(dynamicKeys) !== JSON.stringify(["HttpPort", "MaxCount"])) {
|
|
222
|
+
throw new Error("dynamic pascal keys disagreed with the static emit: " + JSON.stringify(dynamicKeys));
|
|
223
|
+
}
|
|
224
|
+
if (
|
|
225
|
+
!Object.hasOwn(dynamic.MaxCount, "DeepValue") ||
|
|
226
|
+
!Object.hasOwn(dynamic.HttpPort, "DeepValue")
|
|
227
|
+
) {
|
|
228
|
+
throw new Error("dynamic pascal nested keys were not lowercased: " + JSON.stringify(dynamic));
|
|
229
|
+
}
|
|
230
|
+
`
|