typia 13.1.19 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/executable/FileSystemIdentity.d.ts +23 -0
- package/lib/executable/FileSystemIdentity.js +30 -0
- package/lib/executable/FileSystemIdentity.js.map +1 -1
- package/lib/executable/FileSystemIdentity.mjs +4 -0
- package/lib/executable/FileSystemIdentity.mjs.map +1 -1
- package/lib/executable/TypiaGenerateWizard.js +15 -9
- package/lib/executable/TypiaGenerateWizard.js.map +1 -1
- package/lib/executable/TypiaGenerateWizard.mjs +11 -7
- package/lib/executable/TypiaGenerateWizard.mjs.map +1 -1
- package/lib/http.d.ts +3 -3
- package/lib/http.js.map +1 -1
- package/lib/http.mjs.map +1 -1
- package/lib/internal/_ProtobufReader.d.ts +18 -3
- package/lib/internal/_ProtobufReader.js +39 -11
- package/lib/internal/_ProtobufReader.js.map +1 -1
- package/lib/internal/_ProtobufReader.mjs +37 -10
- package/lib/internal/_ProtobufReader.mjs.map +1 -1
- package/lib/internal/_assertGuard.js +13 -3
- package/lib/internal/_assertGuard.js.map +1 -1
- package/lib/internal/_assertGuard.mjs +4 -2
- package/lib/internal/_assertGuard.mjs.map +1 -1
- package/lib/internal/_isFormatIdnHostname.js +5 -1
- package/lib/internal/_isFormatIdnHostname.js.map +1 -1
- package/lib/internal/_isFormatIdnHostname.mjs +1 -1
- package/lib/internal/_isFormatIdnHostname.mjs.map +1 -1
- package/lib/internal/_isFormatTime.js +19 -2
- package/lib/internal/_isFormatTime.js.map +1 -1
- package/lib/internal/_isFormatTime.mjs +9 -2
- package/lib/internal/_isFormatTime.mjs.map +1 -1
- package/lib/internal/_jsonStringifyArray.d.ts +1 -0
- package/lib/internal/_jsonStringifyArray.js +48 -0
- package/lib/internal/_jsonStringifyArray.js.map +1 -0
- package/lib/internal/_jsonStringifyArray.mjs +40 -0
- package/lib/internal/_jsonStringifyArray.mjs.map +1 -0
- package/lib/internal/_jsonStringifyElement.d.ts +1 -0
- package/lib/internal/_jsonStringifyElement.js +23 -0
- package/lib/internal/_jsonStringifyElement.js.map +1 -0
- package/lib/internal/_jsonStringifyElement.mjs +23 -0
- package/lib/internal/_jsonStringifyElement.mjs.map +1 -0
- package/lib/internal/_jsonStringifyProperty.d.ts +1 -0
- package/lib/internal/_jsonStringifyProperty.js +31 -0
- package/lib/internal/_jsonStringifyProperty.js.map +1 -0
- package/lib/internal/_jsonStringifyProperty.mjs +31 -0
- package/lib/internal/_jsonStringifyProperty.mjs.map +1 -0
- package/lib/internal/_llmApplicationFinalize.js +1 -1
- package/lib/internal/_llmApplicationFinalize.js.map +1 -1
- package/lib/internal/_llmApplicationFinalize.mjs +1 -1
- package/lib/internal/_llmApplicationFinalize.mjs.map +1 -1
- package/lib/internal/_randomArray.js +37 -4
- package/lib/internal/_randomArray.js.map +1 -1
- package/lib/internal/_randomArray.mjs +16 -3
- package/lib/internal/_randomArray.mjs.map +1 -1
- package/lib/internal/_randomFormatByte.js +28 -2
- package/lib/internal/_randomFormatByte.js.map +1 -1
- package/lib/internal/_randomFormatByte.mjs +22 -3
- package/lib/internal/_randomFormatByte.mjs.map +1 -1
- package/lib/internal/_randomFormatDate.d.ts +3 -6
- package/lib/internal/_randomFormatDate.js +2 -10
- package/lib/internal/_randomFormatDate.js.map +1 -1
- package/lib/internal/_randomFormatDate.mjs +2 -6
- package/lib/internal/_randomFormatDate.mjs.map +1 -1
- package/lib/internal/_randomFormatDatetime.d.ts +3 -6
- package/lib/internal/_randomFormatDatetime.js +25 -8
- package/lib/internal/_randomFormatDatetime.js.map +1 -1
- package/lib/internal/_randomFormatDatetime.mjs +17 -7
- package/lib/internal/_randomFormatDatetime.mjs.map +1 -1
- package/lib/internal/_randomFormatDuration.js +23 -15
- package/lib/internal/_randomFormatDuration.js.map +1 -1
- package/lib/internal/_randomFormatDuration.mjs +23 -15
- package/lib/internal/_randomFormatDuration.mjs.map +1 -1
- package/lib/internal/_randomFormatHostname.d.ts +2 -2
- package/lib/internal/_randomFormatHostname.js +2 -2
- package/lib/internal/_randomFormatHostname.mjs +2 -2
- package/lib/internal/_randomFormatHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.js +9 -10
- package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.mjs +3 -4
- package/lib/internal/_randomFormatIdnHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv4.js +31 -1
- package/lib/internal/_randomFormatIpv4.js.map +1 -1
- package/lib/internal/_randomFormatIpv4.mjs +23 -2
- package/lib/internal/_randomFormatIpv4.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv6.js +57 -1
- package/lib/internal/_randomFormatIpv6.js.map +1 -1
- package/lib/internal/_randomFormatIpv6.mjs +45 -2
- package/lib/internal/_randomFormatIpv6.mjs.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.js +14 -2
- package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.mjs +13 -3
- package/lib/internal/_randomFormatJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatRegex.js +15 -2
- package/lib/internal/_randomFormatRegex.js.map +1 -1
- package/lib/internal/_randomFormatRegex.mjs +14 -3
- package/lib/internal/_randomFormatRegex.mjs.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.js +19 -5
- package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs +21 -6
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatTime.js +38 -7
- package/lib/internal/_randomFormatTime.js.map +1 -1
- package/lib/internal/_randomFormatTime.mjs +40 -5
- package/lib/internal/_randomFormatTime.mjs.map +1 -1
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPattern.mjs.map +1 -1
- package/lib/internal/_randomStringLength.d.ts +41 -10
- package/lib/internal/_randomStringLength.js +49 -13
- package/lib/internal/_randomStringLength.js.map +1 -1
- package/lib/internal/_randomStringLength.mjs +48 -13
- package/lib/internal/_randomStringLength.mjs.map +1 -1
- package/lib/internal/private/__notationRename.js.map +1 -1
- package/lib/internal/private/__notationRename.mjs.map +1 -1
- package/lib/internal/private/__randomComposition.d.ts +31 -0
- package/lib/internal/private/__randomComposition.js +68 -0
- package/lib/internal/private/__randomComposition.js.map +1 -0
- package/lib/internal/private/__randomComposition.mjs +71 -0
- package/lib/internal/private/__randomComposition.mjs.map +1 -0
- package/lib/internal/private/__randomEpoch.d.ts +19 -0
- package/lib/internal/private/__randomEpoch.js +31 -0
- package/lib/internal/private/__randomEpoch.js.map +1 -0
- package/lib/internal/private/__randomEpoch.mjs +28 -0
- package/lib/internal/private/__randomEpoch.mjs.map +1 -0
- package/lib/json.d.ts +2 -2
- package/lib/json.js.map +1 -1
- package/lib/json.mjs.map +1 -1
- package/lib/module.d.ts +28 -13
- package/lib/module.js.map +1 -1
- package/lib/module.mjs.map +1 -1
- package/lib/notations.d.ts +4 -4
- package/lib/notations.js.map +1 -1
- package/lib/notations.mjs.map +1 -1
- package/lib/plain.d.ts +3 -3
- package/lib/plain.js.map +1 -1
- package/lib/plain.mjs.map +1 -1
- package/lib/protobuf.d.ts +2 -2
- package/lib/protobuf.js.map +1 -1
- package/lib/protobuf.mjs.map +1 -1
- package/native/cmd/ttsc-typia/assert_guard_factory_contract_test.go +17 -4
- package/native/cmd/ttsc-typia/build.go +3 -0
- package/native/cmd/ttsc-typia/callable_interface_function_semantics_transform_test.go +331 -0
- package/native/cmd/ttsc-typia/callable_type_literal_boundaries_transform_test.go +1184 -0
- package/native/cmd/ttsc-typia/compare_functional_union_membership_transform_test.go +434 -0
- package/native/cmd/ttsc-typia/compare_template_literal_union_transform_test.go +241 -0
- package/native/cmd/ttsc-typia/create_assert_error_factory_arity_test.go +309 -0
- package/native/cmd/ttsc-typia/default_library_spoof_native_identity_transform_test.go +216 -0
- package/native/cmd/ttsc-typia/json_schema_dynamic_key_determinism_transform_test.go +246 -0
- package/native/cmd/ttsc-typia/json_stringify_contextual_undefined_transform_test.go +556 -0
- package/native/cmd/ttsc-typia/lib_replacement_native_identity_transform_test.go +277 -0
- package/native/cmd/ttsc-typia/local_function_named_like_typia_operation_transform_test.go +185 -0
- package/native/cmd/ttsc-typia/native_named_intersection_union_transform_test.go +390 -0
- package/native/cmd/ttsc-typia/node_buffer_native_exports_is_transform_test.go +526 -0
- package/native/cmd/ttsc-typia/package_declaration_native_identity_is_transform_test.go +293 -0
- package/native/cmd/ttsc-typia/protobuf_diagnostic_code_transform_test.go +167 -0
- package/native/cmd/ttsc-typia/recursive_conditional_alias_name_transform_test.go +307 -0
- package/native/cmd/ttsc-typia/shadowed_typia_module_declaration_diagnostic_test.go +152 -0
- package/native/cmd/ttsc-typia/symbol_index_domain_transform_test.go +316 -0
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +1 -3
- package/native/cmd/ttsc-typia/transform.go +23 -0
- package/native/cmd/ttsc-typia/transform_helpers_test.go +166 -25
- package/native/cmd/ttsc-typia/user_global_native_identity_transform_test.go +484 -0
- package/native/core/factories/FormatCheatSheet.go +2 -2
- package/native/core/factories/MetadataCommentTagFactory.go +26 -11
- package/native/core/factories/ProtobufFactory.go +6 -1
- package/native/core/factories/factory_utility_coverage_test.go +896 -907
- package/native/core/factories/internal/metadata/MetadataHelper.go +190 -58
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +25 -0
- package/native/core/factories/internal/metadata/iterate_metadata_function.go +1 -1
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +103 -23
- package/native/core/factories/internal/metadata/iterate_metadata_map.go +3 -7
- package/native/core/factories/internal/metadata/iterate_metadata_native.go +32 -14
- package/native/core/factories/internal/metadata/iterate_metadata_set.go +3 -7
- package/native/core/factories/metadata_comment_tag_factory_coverage_test.go +2 -2
- package/native/core/programmers/RandomProgrammer.go +5 -2
- package/native/core/programmers/compare/CompareEqualProgrammer.go +27 -13
- package/native/core/programmers/helpers/FunctionProgrammer.go +6 -13
- package/native/core/programmers/helpers/StringifyJoinder.go +129 -50
- package/native/core/programmers/helpers/joiner_branch_coverage_test.go +116 -120
- package/native/core/programmers/iterate/iterate_check_coverage_test.go +76 -158
- package/native/core/programmers/iterate/iterate_utility_coverage_test.go +231 -270
- package/native/core/programmers/iterate/json_schema_object.go +49 -7
- package/native/core/programmers/iterate/json_schema_pattern_property_order_test.go +44 -0
- package/native/core/programmers/iterate/openapi_v3_downgrader.go +34 -1
- package/native/core/programmers/iterate/template_runtime_pattern.go +11 -0
- package/native/core/programmers/json/JsonStringifyProgrammer.go +47 -6
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +9 -1
- package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -1
- package/native/core/schemas/metadata/MetadataCollection.go +47 -14
- package/native/core/schemas/metadata/MetadataDefaultLibrary.go +81 -0
- package/native/core/schemas/metadata/MetadataObjectType.go +7 -18
- package/native/go.mod +4 -4
- package/native/transform/CallExpressionTransformer.go +167 -0
- package/native/transform/features/protobuf/ProtobufMessageTransformer.go +1 -1
- package/package.json +3 -7
- package/src/executable/FileSystemIdentity.ts +33 -0
- package/src/executable/TypiaGenerateWizard.ts +18 -12
- package/src/http.ts +18 -4
- package/src/internal/_ProtobufReader.ts +40 -11
- package/src/internal/_assertGuard.ts +13 -2
- package/src/internal/_isFormatIdnHostname.ts +5 -1
- package/src/internal/_isFormatTime.ts +19 -2
- package/src/internal/_jsonStringifyArray.ts +50 -0
- package/src/internal/_jsonStringifyElement.ts +19 -0
- package/src/internal/_jsonStringifyProperty.ts +30 -0
- package/src/internal/_llmApplicationFinalize.ts +6 -1
- package/src/internal/_randomArray.ts +42 -6
- package/src/internal/_randomFormatByte.ts +36 -4
- package/src/internal/_randomFormatDate.ts +4 -20
- package/src/internal/_randomFormatDatetime.ts +34 -20
- package/src/internal/_randomFormatDuration.ts +19 -4
- package/src/internal/_randomFormatHostname.ts +2 -2
- package/src/internal/_randomFormatIdnHostname.ts +8 -9
- package/src/internal/_randomFormatIpv4.ts +43 -3
- package/src/internal/_randomFormatIpv6.ts +67 -3
- package/src/internal/_randomFormatJsonPointer.ts +23 -3
- package/src/internal/_randomFormatRegex.ts +22 -6
- package/src/internal/_randomFormatRelativeJsonPointer.ts +23 -10
- package/src/internal/_randomFormatTime.ts +50 -12
- package/src/internal/_randomPattern.ts +4 -1
- package/src/internal/_randomStringLength.ts +61 -17
- package/src/internal/private/__notationRename.ts +1 -4
- package/src/internal/private/__randomComposition.ts +74 -0
- package/src/internal/private/__randomEpoch.ts +34 -0
- package/src/json.ts +16 -4
- package/src/module.ts +56 -22
- package/src/notations.ts +16 -4
- package/src/plain.ts +12 -3
- package/src/protobuf.ts +16 -4
- package/native/adapter/visit.go +0 -173
- package/native/adapter/visit_property_chain_edges_test.go +0 -129
- package/native/core/programmers/helpers/disable_function_programmer_declare.go +0 -10
- package/native/core/programmers/iterate/check_union_array_like.go +0 -422
- package/native/core/programmers/iterate/prune_object_properties.go +0 -105
- package/native/core/programmers/iterate/stringify_dynamic_properties.go +0 -219
- package/native/core/programmers/iterate/stringify_native.go +0 -14
- package/native/core/programmers/iterate/stringify_regular_properties.go +0 -112
- package/native/core/schemas/metadata/MetadataApplication.go +0 -41
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestCreateAssertErrorFactoryArity verifies that the declared arity of created
|
|
12
|
+
// assert functions agrees with the emitted one (#2328).
|
|
13
|
+
//
|
|
14
|
+
// Every `create*Assert*` factory emits `(input, errorFactory = <create-time
|
|
15
|
+
// factory>) => ...`, but the declarations returned `(input) => T`. A pointwise
|
|
16
|
+
// hand-off such as `rows.map(parseUser)` therefore type-checked while
|
|
17
|
+
// `Array#map` handed the element index to `errorFactory`. The declared surface
|
|
18
|
+
// has to name that parameter, without breaking the documented
|
|
19
|
+
// `AssertionGuard<T>` annotation or an ordinary one-parameter callback
|
|
20
|
+
// position.
|
|
21
|
+
//
|
|
22
|
+
// 1. Type-check a fixture that overrides `errorFactory` at call time on every
|
|
23
|
+
// member of the family, and that pins the `.map` hand-off as an error while
|
|
24
|
+
// the adjacent one-parameter controls still compile.
|
|
25
|
+
// 2. Transform that same fixture and require each member's emitted function to
|
|
26
|
+
// carry the parameter, with factories outside the family as the controls
|
|
27
|
+
// that must not.
|
|
28
|
+
// 3. Transform a runtime fixture, execute it, and require the call-time factory
|
|
29
|
+
// to build the error while a numeric second argument falls back to the
|
|
30
|
+
// ordinary type-guard error.
|
|
31
|
+
func TestCreateAssertErrorFactoryArity(t *testing.T) {
|
|
32
|
+
surface := compareEqualCoverProject(t, "create-assert-error-factory-", createAssertErrorFactorySurfaceSource)
|
|
33
|
+
ttscTypiaTestTypecheck(t, surface)
|
|
34
|
+
createAssertErrorFactoryAssertEmittedArity(t, compareEqualCoverTransform(t, surface))
|
|
35
|
+
|
|
36
|
+
project := compareEqualCoverProject(t, "create-assert-error-factory-runtime-", createAssertErrorFactoryRuntimeSource)
|
|
37
|
+
ttscTypiaTestTypecheck(t, project)
|
|
38
|
+
js := compareEqualCoverTransform(t, project)
|
|
39
|
+
if !strings.Contains(js, "(input, errorFactory)") {
|
|
40
|
+
t.Fatalf("emitted factory lost its errorFactory parameter:\n%s", js)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
node, err := exec.LookPath("node")
|
|
44
|
+
if err != nil {
|
|
45
|
+
t.Skip("node executable not found")
|
|
46
|
+
}
|
|
47
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
48
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
49
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
50
|
+
}
|
|
51
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
52
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(ttscTypiaTestRewriteCommonJS(t, js)), 0o644); err != nil {
|
|
53
|
+
t.Fatalf("write runtime module: %v", err)
|
|
54
|
+
}
|
|
55
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
56
|
+
if err := os.WriteFile(runner, []byte(createAssertErrorFactoryRuntimeRunner), 0o644); err != nil {
|
|
57
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
58
|
+
}
|
|
59
|
+
cmd := exec.Command(node, runner)
|
|
60
|
+
cmd.Dir = runtimeDir
|
|
61
|
+
if output, err := cmd.CombinedOutput(); err != nil {
|
|
62
|
+
t.Fatalf("created assert error factory runtime cases failed: %v\n%s", err, output)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// createAssertErrorFactoryAssertEmittedArity requires each member of the family
|
|
67
|
+
// to emit the parameter its declaration now names, and each control not to.
|
|
68
|
+
//
|
|
69
|
+
// The declaration and the emit are written on opposite sides of the codebase,
|
|
70
|
+
// so a declaration test alone can over-promise: dropping `Guardian.Parameter`
|
|
71
|
+
// from one programmer would leave the type claiming a parameter the function no
|
|
72
|
+
// longer takes. The emitted body of each member is what settles it.
|
|
73
|
+
func createAssertErrorFactoryAssertEmittedArity(t *testing.T, js string) {
|
|
74
|
+
t.Helper()
|
|
75
|
+
starts := make([]int, len(createAssertErrorFactoryMembers))
|
|
76
|
+
for i, name := range createAssertErrorFactoryMembers {
|
|
77
|
+
// The CommonJS prologue lists every export on one `void 0` line, so the
|
|
78
|
+
// real assignment is the last occurrence, never the first.
|
|
79
|
+
index := strings.LastIndex(js, "exports."+name+" = ")
|
|
80
|
+
if index < 0 {
|
|
81
|
+
t.Fatalf("emit has no assignment for %s:\n%s", name, js)
|
|
82
|
+
}
|
|
83
|
+
starts[i] = index
|
|
84
|
+
}
|
|
85
|
+
for i, name := range createAssertErrorFactoryMembers {
|
|
86
|
+
end := strings.LastIndex(js, "exports."+createAssertErrorFactorySentinel+" = ")
|
|
87
|
+
if i+1 < len(createAssertErrorFactoryMembers) {
|
|
88
|
+
end = starts[i+1]
|
|
89
|
+
}
|
|
90
|
+
if starts[i] >= end {
|
|
91
|
+
t.Fatalf("emitted members are out of source order at %s", name)
|
|
92
|
+
}
|
|
93
|
+
segment := js[starts[i]:end]
|
|
94
|
+
// The returned function is the public one. Every composed member also
|
|
95
|
+
// builds a private `__assert` that takes the parameter, so matching the
|
|
96
|
+
// parameter list anywhere in the segment would keep passing after the
|
|
97
|
+
// returned arrow lost it.
|
|
98
|
+
carries := strings.Contains(segment, "return (input, errorFactory)")
|
|
99
|
+
if strings.HasPrefix(name, "m") && carries == false {
|
|
100
|
+
t.Fatalf("%s lost its errorFactory parameter:\n%s", name, segment)
|
|
101
|
+
}
|
|
102
|
+
if strings.HasPrefix(name, "n") && strings.Contains(segment, "errorFactory") {
|
|
103
|
+
t.Fatalf("%s gained an errorFactory parameter it does not declare:\n%s", name, segment)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// createAssertErrorFactoryMembers lists the emitted fixture exports in source
|
|
109
|
+
// order: `m*` is the whole family whose emitted function takes `errorFactory`,
|
|
110
|
+
// `n*` are factories that must not grow one.
|
|
111
|
+
var createAssertErrorFactoryMembers = []string{
|
|
112
|
+
"m01", "m02", "m03", "m04", "m05", "m06", "m07", "m08", "m09",
|
|
113
|
+
"m10", "m11", "m12", "m13", "m14", "m15", "m16", "m17", "m18",
|
|
114
|
+
"n01", "n02", "n03",
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const createAssertErrorFactorySentinel = "zzz"
|
|
118
|
+
|
|
119
|
+
// createAssertErrorFactorySurfaceSource is type-checked and transformed, but
|
|
120
|
+
// never executed: the http, protobuf and notation members pull runtime helpers
|
|
121
|
+
// this package stubs only for the emit it runs, so the executed fixture stays
|
|
122
|
+
// on the module family.
|
|
123
|
+
const createAssertErrorFactorySurfaceSource = `import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
124
|
+
import typia, { AssertionGuard, TypeGuardError } from "typia";
|
|
125
|
+
|
|
126
|
+
interface User {
|
|
127
|
+
id: number;
|
|
128
|
+
}
|
|
129
|
+
interface StringUser {
|
|
130
|
+
id: string;
|
|
131
|
+
}
|
|
132
|
+
interface SnakeUser {
|
|
133
|
+
user_id: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
type ErrorFactory = (props: TypeGuardError.IProps) => Error;
|
|
137
|
+
type UserGuard = (
|
|
138
|
+
input: unknown,
|
|
139
|
+
errorFactory?: undefined | ErrorFactory,
|
|
140
|
+
) => asserts input is User;
|
|
141
|
+
|
|
142
|
+
type Assert<T extends true> = T;
|
|
143
|
+
type Equal<X, Y> =
|
|
144
|
+
(<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
|
|
145
|
+
? true
|
|
146
|
+
: false;
|
|
147
|
+
|
|
148
|
+
const factory: ErrorFactory = (props) =>
|
|
149
|
+
Object.assign(new Error("call-time"), { path: props.path });
|
|
150
|
+
|
|
151
|
+
const assertUser = typia.createAssert<User>();
|
|
152
|
+
const assertUserEquals = typia.createAssertEquals<User>();
|
|
153
|
+
const guardUser = typia.createAssertGuard<User>();
|
|
154
|
+
const guardUserEquals = typia.createAssertGuardEquals<User>();
|
|
155
|
+
const jsonParse = typia.json.createAssertParse<User>();
|
|
156
|
+
const jsonStringify = typia.json.createAssertStringify<User>();
|
|
157
|
+
const httpFormData = typia.http.createAssertFormData<StringUser>();
|
|
158
|
+
const httpQuery = typia.http.createAssertQuery<StringUser>();
|
|
159
|
+
const httpHeaders = typia.http.createAssertHeaders<StringUser>();
|
|
160
|
+
const notationCamel = typia.notations.createAssertCamel<SnakeUser>();
|
|
161
|
+
const notationPascal = typia.notations.createAssertPascal<SnakeUser>();
|
|
162
|
+
const notationSnake = typia.notations.createAssertSnake<SnakeUser>();
|
|
163
|
+
const notationKebab = typia.notations.createAssertKebab<SnakeUser>();
|
|
164
|
+
const plainClone = typia.plain.createAssertClone<User>();
|
|
165
|
+
const plainPrune = typia.plain.createAssertPrune<User>();
|
|
166
|
+
const plainClassify = typia.plain.createAssertClassify<User>();
|
|
167
|
+
const protobufDecode = typia.protobuf.createAssertDecode<User>();
|
|
168
|
+
const protobufEncode = typia.protobuf.createAssertEncode<User>();
|
|
169
|
+
|
|
170
|
+
// typia's own declaration of the guard factories, not a local alias: the guard
|
|
171
|
+
// family is the one place where a user annotation could hide a reverted change.
|
|
172
|
+
export type GuardCases = [
|
|
173
|
+
Assert<Equal<typeof guardUser, UserGuard>>,
|
|
174
|
+
Assert<Equal<typeof guardUserEquals, UserGuard>>,
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const annotatedGuard: UserGuard = guardUser;
|
|
178
|
+
const annotatedGuardEquals: UserGuard = guardUserEquals;
|
|
179
|
+
|
|
180
|
+
// Every call below is a compile-time assertion that the family member declares
|
|
181
|
+
// the call-time override the emit gives it.
|
|
182
|
+
export const overrides = (
|
|
183
|
+
raw: unknown,
|
|
184
|
+
text: string,
|
|
185
|
+
user: User,
|
|
186
|
+
snake: SnakeUser,
|
|
187
|
+
form: FormData,
|
|
188
|
+
headers: Record<string, string | string[] | undefined>,
|
|
189
|
+
bytes: Uint8Array,
|
|
190
|
+
): void => {
|
|
191
|
+
assertUser(raw, factory);
|
|
192
|
+
assertUserEquals(raw, factory);
|
|
193
|
+
annotatedGuard(raw, factory);
|
|
194
|
+
annotatedGuardEquals(raw, factory);
|
|
195
|
+
jsonParse(text, factory);
|
|
196
|
+
jsonStringify(user, factory);
|
|
197
|
+
httpFormData(form, factory);
|
|
198
|
+
httpQuery(text, factory);
|
|
199
|
+
httpHeaders(headers, factory);
|
|
200
|
+
notationCamel(snake, factory);
|
|
201
|
+
notationPascal(snake, factory);
|
|
202
|
+
notationSnake(snake, factory);
|
|
203
|
+
notationKebab(snake, factory);
|
|
204
|
+
plainClone(raw, factory);
|
|
205
|
+
plainPrune(raw, factory);
|
|
206
|
+
plainClassify(raw, factory);
|
|
207
|
+
protobufDecode(bytes, factory);
|
|
208
|
+
protobufEncode(user, factory);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// The narrowing contract of the guard family survives the extra parameter, and
|
|
212
|
+
// the documented one-parameter annotation still accepts the created guard.
|
|
213
|
+
export const documented: AssertionGuard<User> = typia.createAssertGuard<User>();
|
|
214
|
+
export const narrowed = (raw: unknown): number => {
|
|
215
|
+
annotatedGuard(raw);
|
|
216
|
+
return raw.id;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
declare const rows: unknown[];
|
|
220
|
+
|
|
221
|
+
// @ts-expect-error Array#map hands the element index to errorFactory.
|
|
222
|
+
export const mappedAssert = rows.map(assertUser);
|
|
223
|
+
// @ts-expect-error the guard family carries the same hazard.
|
|
224
|
+
export const mappedGuard = rows.map(guardUser);
|
|
225
|
+
|
|
226
|
+
// Adjacent one-axis controls that must keep compiling.
|
|
227
|
+
export const mappedExplicit = rows.map((row) => assertUser(row));
|
|
228
|
+
type Parser = (input: unknown) => User;
|
|
229
|
+
export const parser: Parser = assertUser;
|
|
230
|
+
declare function takesParser(parse: (input: unknown) => User): User;
|
|
231
|
+
export const parsed = takesParser(assertUser);
|
|
232
|
+
export const standard: StandardSchemaV1<User, User> = typia.createValidate<User>();
|
|
233
|
+
|
|
234
|
+
// The emitted-arity census. Order is load-bearing: the assertion slices the
|
|
235
|
+
// emit between consecutive assignments, and ` + "`zzz`" + ` closes the last one.
|
|
236
|
+
export const m01 = typia.createAssert<User>();
|
|
237
|
+
export const m02 = typia.createAssertEquals<User>();
|
|
238
|
+
export const m03 = typia.createAssertGuard<User>();
|
|
239
|
+
export const m04 = typia.createAssertGuardEquals<User>();
|
|
240
|
+
export const m05 = typia.json.createAssertParse<User>();
|
|
241
|
+
export const m06 = typia.json.createAssertStringify<User>();
|
|
242
|
+
export const m07 = typia.http.createAssertFormData<StringUser>();
|
|
243
|
+
export const m08 = typia.http.createAssertQuery<StringUser>();
|
|
244
|
+
export const m09 = typia.http.createAssertHeaders<StringUser>();
|
|
245
|
+
export const m10 = typia.notations.createAssertCamel<SnakeUser>();
|
|
246
|
+
export const m11 = typia.notations.createAssertPascal<SnakeUser>();
|
|
247
|
+
export const m12 = typia.notations.createAssertSnake<SnakeUser>();
|
|
248
|
+
export const m13 = typia.notations.createAssertKebab<SnakeUser>();
|
|
249
|
+
export const m14 = typia.plain.createAssertClone<User>();
|
|
250
|
+
export const m15 = typia.plain.createAssertPrune<User>();
|
|
251
|
+
export const m16 = typia.plain.createAssertClassify<User>();
|
|
252
|
+
export const m17 = typia.protobuf.createAssertDecode<User>();
|
|
253
|
+
export const m18 = typia.protobuf.createAssertEncode<User>();
|
|
254
|
+
export const n01 = typia.createIs<User>();
|
|
255
|
+
export const n02 = typia.json.createIsParse<User>();
|
|
256
|
+
export const n03 = typia.plain.createClone<User>();
|
|
257
|
+
export const zzz = 0;
|
|
258
|
+
`
|
|
259
|
+
|
|
260
|
+
const createAssertErrorFactoryRuntimeSource = `import typia, { TypeGuardError } from "typia";
|
|
261
|
+
|
|
262
|
+
interface User {
|
|
263
|
+
id: number;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export const assertUser = typia.createAssert<User>();
|
|
267
|
+
export const assertUserEquals = typia.createAssertEquals<User>();
|
|
268
|
+
export const withOverride = (
|
|
269
|
+
input: unknown,
|
|
270
|
+
errorFactory: (props: TypeGuardError.IProps) => Error,
|
|
271
|
+
): User => assertUser(input, errorFactory);
|
|
272
|
+
`
|
|
273
|
+
|
|
274
|
+
const createAssertErrorFactoryRuntimeRunner = `const mod = require("./main.cjs");
|
|
275
|
+
|
|
276
|
+
const expect = (label, actual, expected) => {
|
|
277
|
+
if (actual !== expected) throw new Error(label + ": expected " + expected + ", got " + actual);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const capture = (task) => {
|
|
281
|
+
try {
|
|
282
|
+
task();
|
|
283
|
+
} catch (exp) {
|
|
284
|
+
return exp;
|
|
285
|
+
}
|
|
286
|
+
throw new Error("expected the assertion to throw");
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
expect("valid input passes", 1, mod.assertUser({ id: 1 }).id);
|
|
290
|
+
|
|
291
|
+
// A pointwise hand-off fills errorFactory with the element index. Both the
|
|
292
|
+
// falsy 0 and a truthy index must reach the ordinary type-guard error.
|
|
293
|
+
for (const index of [0, 1, 2]) {
|
|
294
|
+
const error = capture(() => mod.assertUser({ id: "bad" }, index));
|
|
295
|
+
expect("index " + index + " expected", "number", error.expected);
|
|
296
|
+
expect("index " + index + " path", "$input.id", error.path);
|
|
297
|
+
expect("index " + index + " method", "typia.createAssert", error.method);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// The declared parameter is real: a callable factory still wins.
|
|
301
|
+
const custom = capture(() =>
|
|
302
|
+
mod.withOverride({ id: "bad" }, (props) => Object.assign(new Error("call-time"), props)),
|
|
303
|
+
);
|
|
304
|
+
expect("call-time factory message", "call-time", custom.message);
|
|
305
|
+
expect("call-time factory path", "$input.id", custom.path);
|
|
306
|
+
|
|
307
|
+
const equals = capture(() => mod.assertUserEquals({ id: 1, extra: true }, 4));
|
|
308
|
+
expect("equals surplus expected", "undefined", equals.expected);
|
|
309
|
+
`
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// TestDefaultLibrarySpoofNativeIdentityTransform keeps a declaration package out
|
|
13
|
+
// of the runtime-native authority even when its file is named like a TypeScript
|
|
14
|
+
// default library (#2200).
|
|
15
|
+
//
|
|
16
|
+
// Runtime-native identity admits a global whose constructor a default library
|
|
17
|
+
// provides, so how "default library" is decided is the whole boundary. `lib.d.ts`
|
|
18
|
+
// is an ordinary published file name, so a base-name test makes that authority
|
|
19
|
+
// claimable by any package a project pulls in through `typeRoots` — the forged
|
|
20
|
+
// declarations below are a full-member copy of the DOM `File` and `Blob`,
|
|
21
|
+
// including their `declare var` constructor bindings, and nothing but the
|
|
22
|
+
// containing directory distinguishes them from the real thing.
|
|
23
|
+
//
|
|
24
|
+
// 1. Publish `lib.dom.d.ts` and `lib.es5.d.ts` from a fixture `@types` package
|
|
25
|
+
// that declares global File and Blob with their constructors.
|
|
26
|
+
// 2. Transform validators for those globals with no DOM library and no Node
|
|
27
|
+
// types loaded, so the forged package is their only declaration source.
|
|
28
|
+
// 3. Require the emit to keep the forged members instead of an `instanceof`.
|
|
29
|
+
// 4. Execute the validators in Node against the forged structural values and
|
|
30
|
+
// against the real runtime instances, requiring an exact case count.
|
|
31
|
+
func TestDefaultLibrarySpoofNativeIdentityTransform(t *testing.T) {
|
|
32
|
+
project := defaultLibrarySpoofNativeIdentityProject(t)
|
|
33
|
+
js, errText, code := ttscTypiaTestCapture(func() int {
|
|
34
|
+
return runTransform([]string{
|
|
35
|
+
"--cwd", project,
|
|
36
|
+
"--tsconfig", "tsconfig.json",
|
|
37
|
+
"--file", "src/input.ts",
|
|
38
|
+
"--output", "js",
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
if code != 0 {
|
|
42
|
+
t.Fatalf("default library spoof transform failed: code=%d stderr=\n%s", code, errText)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
failures := []string{}
|
|
46
|
+
for _, native := range []string{"instanceof File", "instanceof Blob"} {
|
|
47
|
+
if strings.Contains(js, native) {
|
|
48
|
+
failures = append(failures, fmt.Sprintf("a lib.*.d.ts named package declaration was promoted to %q", native))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for _, member := range []string{"spoofBlobBrand", "spoofFileBrand"} {
|
|
52
|
+
if !strings.Contains(js, member) {
|
|
53
|
+
failures = append(failures, fmt.Sprintf("emit dropped forged declaration member %q", member))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
output, runtimeErr := defaultLibrarySpoofNativeIdentityRun(t, project, js)
|
|
58
|
+
if runtimeErr != nil {
|
|
59
|
+
failures = append(failures, fmt.Sprintf("runtime matrix failed: %v\n%s", runtimeErr, output))
|
|
60
|
+
}
|
|
61
|
+
if expected := "RAN 6 SPOOF CASES"; !strings.Contains(output, expected) {
|
|
62
|
+
failures = append(failures, fmt.Sprintf("spoof runner did not report %q; got:\n%s", expected, output))
|
|
63
|
+
}
|
|
64
|
+
if len(failures) != 0 {
|
|
65
|
+
t.Fatalf("default library spoof mismatches:\n%s\n\nemit:\n%s", strings.Join(failures, "\n"), js)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func defaultLibrarySpoofNativeIdentityProject(t *testing.T) string {
|
|
70
|
+
t.Helper()
|
|
71
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
72
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
73
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
74
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
75
|
+
}
|
|
76
|
+
dir, err := os.MkdirTemp(base, "default-library-spoof-native-identity-")
|
|
77
|
+
if err != nil {
|
|
78
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
79
|
+
}
|
|
80
|
+
t.Cleanup(func() {
|
|
81
|
+
_ = os.RemoveAll(dir)
|
|
82
|
+
})
|
|
83
|
+
for name, content := range map[string]string{
|
|
84
|
+
"tsconfig.json": defaultLibrarySpoofNativeIdentityTSConfig,
|
|
85
|
+
"node_modules/@types/spoof-lib/package.json": defaultLibrarySpoofNativeIdentityPackageJSON,
|
|
86
|
+
"node_modules/@types/spoof-lib/lib.dom.d.ts": defaultLibrarySpoofNativeIdentityDeclarations,
|
|
87
|
+
"node_modules/@types/spoof-lib/lib.es5.d.ts": defaultLibrarySpoofNativeIdentityExtraDeclarations,
|
|
88
|
+
"src/input.ts": defaultLibrarySpoofNativeIdentitySource,
|
|
89
|
+
} {
|
|
90
|
+
path := filepath.Join(dir, filepath.FromSlash(name))
|
|
91
|
+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
92
|
+
t.Fatalf("mkdir fixture path %s: %v", filepath.Dir(path), err)
|
|
93
|
+
}
|
|
94
|
+
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
|
95
|
+
t.Fatalf("write fixture file %s: %v", path, err)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return dir
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func defaultLibrarySpoofNativeIdentityRun(t *testing.T, project string, js string) (string, error) {
|
|
102
|
+
t.Helper()
|
|
103
|
+
node, err := exec.LookPath("node")
|
|
104
|
+
if err != nil {
|
|
105
|
+
t.Skip("node executable not found")
|
|
106
|
+
return "", nil
|
|
107
|
+
}
|
|
108
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
109
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
110
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
111
|
+
}
|
|
112
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
113
|
+
for name, content := range map[string]string{
|
|
114
|
+
"input.cjs": ttscTypiaTestRewriteCommonJS(t, js),
|
|
115
|
+
"run.cjs": defaultLibrarySpoofNativeIdentityRuntimeRunner,
|
|
116
|
+
} {
|
|
117
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, name), []byte(content), 0o644); err != nil {
|
|
118
|
+
t.Fatalf("write runtime file %s: %v", name, err)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
cmd := exec.Command(node, filepath.Join(runtimeDir, "run.cjs"))
|
|
122
|
+
cmd.Dir = runtimeDir
|
|
123
|
+
output, err := cmd.CombinedOutput()
|
|
124
|
+
return string(output), err
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const defaultLibrarySpoofNativeIdentityTSConfig = `{
|
|
128
|
+
"compilerOptions": {
|
|
129
|
+
"target": "ES2022",
|
|
130
|
+
"module": "commonjs",
|
|
131
|
+
"moduleResolution": "bundler",
|
|
132
|
+
"ignoreDeprecations": "6.0",
|
|
133
|
+
"lib": ["ES2022"],
|
|
134
|
+
"types": ["spoof-lib"],
|
|
135
|
+
"esModuleInterop": true,
|
|
136
|
+
"strict": true,
|
|
137
|
+
"skipLibCheck": true
|
|
138
|
+
},
|
|
139
|
+
"include": ["src"]
|
|
140
|
+
}
|
|
141
|
+
`
|
|
142
|
+
|
|
143
|
+
const defaultLibrarySpoofNativeIdentityPackageJSON = `{
|
|
144
|
+
"name": "@types/spoof-lib",
|
|
145
|
+
"version": "1.0.0",
|
|
146
|
+
"types": "lib.dom.d.ts"
|
|
147
|
+
}
|
|
148
|
+
`
|
|
149
|
+
|
|
150
|
+
const defaultLibrarySpoofNativeIdentityDeclarations = `/// <reference path="./lib.es5.d.ts" />
|
|
151
|
+
|
|
152
|
+
interface Blob {
|
|
153
|
+
readonly size: number;
|
|
154
|
+
readonly type: string;
|
|
155
|
+
spoofBlobBrand: string;
|
|
156
|
+
}
|
|
157
|
+
declare var Blob: {
|
|
158
|
+
prototype: Blob;
|
|
159
|
+
new (parts?: unknown[], options?: unknown): Blob;
|
|
160
|
+
};
|
|
161
|
+
`
|
|
162
|
+
|
|
163
|
+
const defaultLibrarySpoofNativeIdentityExtraDeclarations = `interface File extends Blob {
|
|
164
|
+
readonly lastModified: number;
|
|
165
|
+
readonly name: string;
|
|
166
|
+
readonly webkitRelativePath: string;
|
|
167
|
+
spoofFileBrand: string;
|
|
168
|
+
}
|
|
169
|
+
declare var File: {
|
|
170
|
+
prototype: File;
|
|
171
|
+
new (parts: unknown[], name: string, options?: unknown): File;
|
|
172
|
+
};
|
|
173
|
+
`
|
|
174
|
+
|
|
175
|
+
const defaultLibrarySpoofNativeIdentitySource = `import typia from "typia";
|
|
176
|
+
|
|
177
|
+
export const createdIsBlob = typia.createIs<Blob>();
|
|
178
|
+
export const createdIsFile = typia.createIs<File>();
|
|
179
|
+
`
|
|
180
|
+
|
|
181
|
+
const defaultLibrarySpoofNativeIdentityRuntimeRunner = `const validators = require("./input.cjs");
|
|
182
|
+
|
|
183
|
+
let ran = 0;
|
|
184
|
+
const failures = [];
|
|
185
|
+
const eq = (name, actual, expected) => {
|
|
186
|
+
ran += 1;
|
|
187
|
+
if (actual !== expected) {
|
|
188
|
+
failures.push(name + ": expected " + expected + " but got " + actual);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const spoofBlob = {
|
|
193
|
+
size: 1,
|
|
194
|
+
type: "text/plain",
|
|
195
|
+
spoofBlobBrand: "spoof",
|
|
196
|
+
};
|
|
197
|
+
const spoofFile = {
|
|
198
|
+
...spoofBlob,
|
|
199
|
+
lastModified: 0,
|
|
200
|
+
name: "x.txt",
|
|
201
|
+
webkitRelativePath: "",
|
|
202
|
+
spoofFileBrand: "spoof",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
eq("spoof Blob structural", validators.createdIsBlob(spoofBlob), true);
|
|
206
|
+
eq("spoof Blob rejects runtime instance", validators.createdIsBlob(new Blob(["x"])), false);
|
|
207
|
+
eq("spoof Blob missing brand", validators.createdIsBlob({ size: 1, type: "text/plain" }), false);
|
|
208
|
+
eq("spoof File structural", validators.createdIsFile(spoofFile), true);
|
|
209
|
+
eq("spoof File rejects runtime instance", validators.createdIsFile(new File(["x"], "x.txt")), false);
|
|
210
|
+
eq("spoof File missing brand", validators.createdIsFile({ ...spoofFile, spoofFileBrand: undefined }), false);
|
|
211
|
+
|
|
212
|
+
console.log("RAN " + ran + " SPOOF CASES");
|
|
213
|
+
if (failures.length !== 0) {
|
|
214
|
+
throw new Error("MISMATCHES:\n" + failures.join("\n"));
|
|
215
|
+
}
|
|
216
|
+
`
|