typia 15.2.0 → 15.2.2
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/internal/_stringLengthGte.d.ts +7 -3
- package/lib/internal/_stringLengthGte.js +9 -3
- package/lib/internal/_stringLengthGte.js.map +1 -1
- package/lib/internal/_stringLengthGte.mjs +8 -3
- package/lib/internal/_stringLengthGte.mjs.map +1 -1
- package/lib/internal/_stringLengthLte.d.ts +7 -3
- package/lib/internal/_stringLengthLte.js +9 -3
- package/lib/internal/_stringLengthLte.js.map +1 -1
- package/lib/internal/_stringLengthLte.mjs +8 -3
- package/lib/internal/_stringLengthLte.mjs.map +1 -1
- package/native/cmd/ttsc-typia/json_schema_recursive_array_alias_transform_test.go +79 -0
- package/native/core/context/ImportProgrammer.go +19 -9
- package/native/core/context/ImportProgrammerPattern.go +36 -0
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +4 -1
- package/native/core/programmers/RandomProgrammer.go +19 -14
- package/native/core/programmers/internal/CheckerProgrammer.go +13 -6
- package/native/core/programmers/iterate/check_string.go +41 -5
- package/native/core/programmers/iterate/check_string_pattern_test.go +91 -0
- package/native/core/programmers/iterate/check_template.go +4 -4
- package/native/core/programmers/iterate/json_schema_alias.go +22 -24
- package/native/core/programmers/iterate/json_schema_array.go +14 -10
- package/native/core/programmers/iterate/json_schema_array_names.go +113 -0
- package/native/core/programmers/iterate/json_schema_native.go +10 -3
- package/native/core/programmers/iterate/json_schema_recursive_array_identity_test.go +75 -0
- package/native/core/programmers/iterate/json_schema_recursive_array_ownership_test.go +112 -0
- package/native/core/programmers/iterate/json_schema_station.go +5 -1
- package/native/core/programmers/json/JsonSchemasProgrammer.go +1 -0
- package/native/core/programmers/random_programmer_array_schema_reference_test.go +53 -0
- package/package.json +3 -3
- package/src/internal/_stringLengthGte.ts +8 -3
- package/src/internal/_stringLengthLte.ts +8 -3
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Test whether a string has at least the given number of code points.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Twice the requested minimum in UTF-16 units guarantees enough code points.
|
|
5
|
+
* Other inputs are counted only until the minimum is reached.
|
|
6
6
|
*
|
|
7
|
-
* @evidence contracts/common.md#principled-implementation
|
|
7
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies at most two UTF-16 units, so code-unit length at least twice the minimum proves the code-point bound. Otherwise iteration stops at the minimum; nonpositive minima remain true and NaN remains false.
|
|
8
8
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
9
9
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
10
10
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
11
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit lower bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
12
|
+
*
|
|
13
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
14
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
11
15
|
*/
|
|
12
16
|
export declare const _stringLengthGte: (value: string, length: number) => boolean;
|
|
@@ -4,15 +4,21 @@ exports._stringLengthGte = void 0;
|
|
|
4
4
|
/**
|
|
5
5
|
* Test whether a string has at least the given number of code points.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Twice the requested minimum in UTF-16 units guarantees enough code points.
|
|
8
|
+
* Other inputs are counted only until the minimum is reached.
|
|
9
9
|
*
|
|
10
|
-
* @evidence contracts/common.md#principled-implementation
|
|
10
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies at most two UTF-16 units, so code-unit length at least twice the minimum proves the code-point bound. Otherwise iteration stops at the minimum; nonpositive minima remain true and NaN remains false.
|
|
11
11
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
12
12
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
13
13
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
14
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit lower bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
15
|
+
*
|
|
16
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
17
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
14
18
|
*/
|
|
15
19
|
const _stringLengthGte = (value, length) => {
|
|
20
|
+
if (value.length >= length * 2)
|
|
21
|
+
return true;
|
|
16
22
|
let count = 0;
|
|
17
23
|
if (length <= count)
|
|
18
24
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_stringLengthGte.js","sources":["../../src/internal/_stringLengthGte.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"_stringLengthGte.js","sources":["../../src/internal/_stringLengthGte.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACI,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAW,EAAE;IACzE,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,KAAK,GAAW,CAAC,CAAC;IACtB,IAAI,MAAM,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,KAAK;QAAE,IAAI,MAAM,IAAI,EAAE,KAAK;YAAE,OAAO,IAAI,CAAC;IAC5D,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AANW,QAAA,gBAAgB,GAAhB,gBAAgB,CAM3B"}
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Test whether a string has at least the given number of code points.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Twice the requested minimum in UTF-16 units guarantees enough code points.
|
|
6
|
+
* Other inputs are counted only until the minimum is reached.
|
|
7
7
|
*
|
|
8
|
-
* @evidence contracts/common.md#principled-implementation
|
|
8
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies at most two UTF-16 units, so code-unit length at least twice the minimum proves the code-point bound. Otherwise iteration stops at the minimum; nonpositive minima remain true and NaN remains false.
|
|
9
9
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
10
10
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
11
11
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
12
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit lower bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
13
|
+
*
|
|
14
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
15
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
12
16
|
*/
|
|
13
17
|
const _stringLengthGte = (value, length) => {
|
|
18
|
+
if (value.length >= length * 2) return true;
|
|
14
19
|
let count = 0;
|
|
15
20
|
if (length <= count) return true;
|
|
16
21
|
for (const _ch of value) if (length <= ++count) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_stringLengthGte.mjs","names":[],"sources":["../../src/internal/_stringLengthGte.ts"],"sourcesContent":["/**\n * Test whether a string has at least the given number of code points.\n *\n *
|
|
1
|
+
{"version":3,"file":"_stringLengthGte.mjs","names":[],"sources":["../../src/internal/_stringLengthGte.ts"],"sourcesContent":["/**\n * Test whether a string has at least the given number of code points.\n *\n * Twice the requested minimum in UTF-16 units guarantees enough code points.\n * Other inputs are counted only until the minimum is reached.\n *\n * @evidence contracts/common.md#principled-implementation Each code point occupies at most two UTF-16 units, so code-unit length at least twice the minimum proves the code-point bound. Otherwise iteration stops at the minimum; nonpositive minima remain true and NaN remains false.\n * @evidence contracts/common.md#clear-and-simple-design One loop with an early return.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.\n * @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.\n * @evidence contracts/performance.md#efficient-algorithms The code-unit lower bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.\n *\n * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.\n * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.\n */\nexport const _stringLengthGte = (value: string, length: number): boolean => {\n if (value.length >= length * 2) return true;\n let count: number = 0;\n if (length <= count) return true;\n for (const _ch of value) if (length <= ++count) return true;\n return false;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,oBAAoB,OAAe,WAA4B;CAC1E,IAAI,MAAM,UAAU,SAAS,GAAG,OAAO;CACvC,IAAI,QAAgB;CACpB,IAAI,UAAU,OAAO,OAAO;CAC5B,KAAK,MAAM,OAAO,OAAO,IAAI,UAAU,EAAE,OAAO,OAAO;CACvD,OAAO;AACT"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Test whether a string has at most the given number of code points.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* UTF-16 length decides inputs whose code units already fit the bound. Other
|
|
5
|
+
* inputs are counted only until the maximum is exceeded.
|
|
6
6
|
*
|
|
7
|
-
* @evidence contracts/common.md#principled-implementation
|
|
7
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies one or two UTF-16 units, so code-unit length at most the maximum proves the code-point bound. Otherwise iteration stops once the maximum is exceeded; negative and NaN maxima remain false.
|
|
8
8
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
9
9
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
10
10
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
11
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit upper bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
12
|
+
*
|
|
13
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
14
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
11
15
|
*/
|
|
12
16
|
export declare const _stringLengthLte: (value: string, length: number) => boolean;
|
|
@@ -4,15 +4,21 @@ exports._stringLengthLte = void 0;
|
|
|
4
4
|
/**
|
|
5
5
|
* Test whether a string has at most the given number of code points.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* UTF-16 length decides inputs whose code units already fit the bound. Other
|
|
8
|
+
* inputs are counted only until the maximum is exceeded.
|
|
9
9
|
*
|
|
10
|
-
* @evidence contracts/common.md#principled-implementation
|
|
10
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies one or two UTF-16 units, so code-unit length at most the maximum proves the code-point bound. Otherwise iteration stops once the maximum is exceeded; negative and NaN maxima remain false.
|
|
11
11
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
12
12
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
13
13
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
14
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit upper bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
15
|
+
*
|
|
16
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
17
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
14
18
|
*/
|
|
15
19
|
const _stringLengthLte = (value, length) => {
|
|
20
|
+
if (value.length <= length)
|
|
21
|
+
return true;
|
|
16
22
|
let count = 0;
|
|
17
23
|
if (!(count <= length))
|
|
18
24
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_stringLengthLte.js","sources":["../../src/internal/_stringLengthLte.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"_stringLengthLte.js","sources":["../../src/internal/_stringLengthLte.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;GAcG;AACI,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAW,EAAE;IACzE,IAAI,KAAK,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,KAAK,GAAW,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,KAAK;QAAE,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;IAChE,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AANW,QAAA,gBAAgB,GAAhB,gBAAgB,CAM3B"}
|
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Test whether a string has at most the given number of code points.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* UTF-16 length decides inputs whose code units already fit the bound. Other
|
|
6
|
+
* inputs are counted only until the maximum is exceeded.
|
|
7
7
|
*
|
|
8
|
-
* @evidence contracts/common.md#principled-implementation
|
|
8
|
+
* @evidence contracts/common.md#principled-implementation Each code point occupies one or two UTF-16 units, so code-unit length at most the maximum proves the code-point bound. Otherwise iteration stops once the maximum is exceeded; negative and NaN maxima remain false.
|
|
9
9
|
* @evidence contracts/common.md#clear-and-simple-design One loop with an early return.
|
|
10
10
|
* @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.
|
|
11
11
|
* @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.
|
|
12
|
+
* @evidence contracts/performance.md#efficient-algorithms The code-unit upper bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.
|
|
13
|
+
*
|
|
14
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.
|
|
15
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.
|
|
12
16
|
*/
|
|
13
17
|
const _stringLengthLte = (value, length) => {
|
|
18
|
+
if (value.length <= length) return true;
|
|
14
19
|
let count = 0;
|
|
15
20
|
if (!(count <= length)) return false;
|
|
16
21
|
for (const _ch of value) if (!(++count <= length)) return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_stringLengthLte.mjs","names":[],"sources":["../../src/internal/_stringLengthLte.ts"],"sourcesContent":["/**\n * Test whether a string has at most the given number of code points.\n *\n *
|
|
1
|
+
{"version":3,"file":"_stringLengthLte.mjs","names":[],"sources":["../../src/internal/_stringLengthLte.ts"],"sourcesContent":["/**\n * Test whether a string has at most the given number of code points.\n *\n * UTF-16 length decides inputs whose code units already fit the bound. Other\n * inputs are counted only until the maximum is exceeded.\n *\n * @evidence contracts/common.md#principled-implementation Each code point occupies one or two UTF-16 units, so code-unit length at most the maximum proves the code-point bound. Otherwise iteration stops once the maximum is exceeded; negative and NaN maxima remain false.\n * @evidence contracts/common.md#clear-and-simple-design One loop with an early return.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The unit is code points.\n * @evidence contracts/common.md#meaningful-documentation The doc states the early return and the code point unit.\n * @evidence contracts/performance.md#efficient-algorithms The code-unit upper bound accepts in constant time without allocating an iterator; ambiguous inputs retain the bounded code-point walk and constant auxiliary space.\n *\n * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call depends on its own primitive string and bound; this predicate coordinates no repeated requests or shared computation.\n * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate retains no state or handles after returning; iteration and scalar counters are call-local.\n */\nexport const _stringLengthLte = (value: string, length: number): boolean => {\n if (value.length <= length) return true;\n let count: number = 0;\n if (!(count <= length)) return false;\n for (const _ch of value) if (!(++count <= length)) return false;\n return true;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,MAAa,oBAAoB,OAAe,WAA4B;CAC1E,IAAI,MAAM,UAAU,QAAQ,OAAO;CACnC,IAAI,QAAgB;CACpB,IAAI,EAAE,SAAS,SAAS,OAAO;CAC/B,KAAK,MAAM,OAAO,OAAO,IAAI,EAAE,EAAE,SAAS,SAAS,OAAO;CAC1D,OAAO;AACT"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestJsonSchemaRecursiveArrayAliasTransform verifies recursive arrays survive
|
|
10
|
+
// analysis and produce finite, repeatable schema literals.
|
|
11
|
+
//
|
|
12
|
+
// The alias and array metadata may share an identity; constructed metadata alone
|
|
13
|
+
// cannot establish which names the TypeScript checker actually supplies.
|
|
14
|
+
//
|
|
15
|
+
// 1. Transform recursive, constrained and nonrecursive declarations together.
|
|
16
|
+
// 2. Compare named structural bodies and distinct use-site constraints.
|
|
17
|
+
// 3. Repeat emission and require identical complete JavaScript bytes.
|
|
18
|
+
//
|
|
19
|
+
// @evidence contracts/testing.md#behavioral-verification Runs the native transform in process, decodes emitted schema literals and compares array bodies and tagged properties; complete output equality pins deterministic emission.
|
|
20
|
+
// @evidence contracts/testing.md#independent-expectations The authored recursive type requires an array whose items reference the value union. Explicit max/min tags constrain different properties and must not modify an untagged sibling.
|
|
21
|
+
// @evidence contracts/testing.md#distinguishing-cases Union-root and array-root traversal, two tagged sibling uses and an untagged sibling coexist with an ordinary array control; emitted identity and constraint placement are asserted separately.
|
|
22
|
+
// @evidence contracts/testing.md#execution-ownership pnpm test:go:native discovers this test; runTransform and AST decoding execute in process using one fixture project, without compiler or JavaScript subprocesses.
|
|
23
|
+
func TestJsonSchemaRecursiveArrayAliasTransform(t *testing.T) {
|
|
24
|
+
dir := ttscTypiaTestFixtureDirectory(t, "recursive-array-schema-")
|
|
25
|
+
if err := os.MkdirAll(filepath.Join(dir, "src"), 0o755); err != nil {
|
|
26
|
+
t.Fatal(err)
|
|
27
|
+
}
|
|
28
|
+
for name, source := range map[string]string{
|
|
29
|
+
"tsconfig.json": atomicIntersectionSchemaTSConfig,
|
|
30
|
+
"src/main.ts": `import typia, { tags } from "typia";
|
|
31
|
+
type JsonArray = JsonValue[];
|
|
32
|
+
type JsonObject = { [key: string]: JsonValue };
|
|
33
|
+
type JsonValue = string | number | boolean | null | JsonArray | JsonObject;
|
|
34
|
+
interface Constrained {
|
|
35
|
+
small: JsonArray & tags.MaxItems<1>;
|
|
36
|
+
many: JsonArray & tags.MinItems<2>;
|
|
37
|
+
plain: JsonArray;
|
|
38
|
+
}
|
|
39
|
+
export const schemas = typia.json.schemas<[JsonValue, JsonArray, Constrained, string[]]>();
|
|
40
|
+
`,
|
|
41
|
+
} {
|
|
42
|
+
if err := os.WriteFile(filepath.Join(dir, filepath.FromSlash(name)), []byte(source), 0o644); err != nil {
|
|
43
|
+
t.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var previous string
|
|
47
|
+
for i := 0; i < 3; i++ {
|
|
48
|
+
output, stderr, code := ttscTypiaTestCapture(func() int {
|
|
49
|
+
return runTransform([]string{"--cwd", dir, "--tsconfig", "tsconfig.json", "--file", "src/main.ts", "--output", "js"})
|
|
50
|
+
})
|
|
51
|
+
if code != 0 {
|
|
52
|
+
t.Fatalf("transform failed (%d): %s", code, stderr)
|
|
53
|
+
}
|
|
54
|
+
unit := ttscTypiaTestSchemaLiteral(t, output)
|
|
55
|
+
if i != 0 && output != previous {
|
|
56
|
+
t.Error("unchanged source produced different bytes")
|
|
57
|
+
}
|
|
58
|
+
previous = output
|
|
59
|
+
schemas := ttscTypiaTestSchemaPath(t, unit, "components", "schemas").(map[string]any)
|
|
60
|
+
body := schemas["JsonArray"].(map[string]any)
|
|
61
|
+
ttscTypiaTestSchemaEqual(t, body, map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/JsonValue"}})
|
|
62
|
+
properties := ttscTypiaTestSchemaPath(t, unit, "components", "schemas", "Constrained", "properties").(map[string]any)
|
|
63
|
+
for _, sample := range []struct {
|
|
64
|
+
name string
|
|
65
|
+
constraint string
|
|
66
|
+
value any
|
|
67
|
+
}{
|
|
68
|
+
{"small", "maxItems", float64(1)}, {"many", "minItems", float64(2)}, {"plain", "", nil},
|
|
69
|
+
} {
|
|
70
|
+
ref := properties[sample.name].(map[string]any)["$ref"].(string)
|
|
71
|
+
target := schemas[ref[len("#/components/schemas/"):]].(map[string]any)
|
|
72
|
+
expected := map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/JsonValue"}}
|
|
73
|
+
if sample.constraint != "" {
|
|
74
|
+
expected[sample.constraint] = sample.value
|
|
75
|
+
}
|
|
76
|
+
ttscTypiaTestSchemaEqual(t, target, expected)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -9,7 +9,7 @@ import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
// ImportProgrammer collects the imports that generated code needs and builds the
|
|
12
|
-
// references to them.
|
|
12
|
+
// references to them. It also collects lazily initialized pattern slots.
|
|
13
13
|
//
|
|
14
14
|
// In its default mode references are plain identifiers and ToStatements writes
|
|
15
15
|
// named, default and namespace imports. After SetEmitContext, every file becomes
|
|
@@ -17,14 +17,16 @@ import (
|
|
|
17
17
|
// name, so the compiler's module transform binds them. It is not safe for
|
|
18
18
|
// concurrent use.
|
|
19
19
|
//
|
|
20
|
-
// @evidence contracts/common.md#principled-implementation Requests are recorded per file with their insertion order and each file is emitted once, either as named, default and namespace imports in the legacy mode or as one namespace import with member accesses on a generated unique name in emit-context mode, so the compiler's module transform decides the final CommonJS aliasing. Internal helpers
|
|
20
|
+
// @evidence contracts/common.md#principled-implementation Requests are recorded per file with their insertion order and each file is emitted once, either as named, default and namespace imports in the legacy mode or as one namespace import with member accesses on a generated unique name in emit-context mode, so the compiler's module transform decides the final CommonJS aliasing. Internal helpers use typia's internal folder. Static pattern sources share file-local lazy slots only when the compiler can allocate collision-safe names.
|
|
21
21
|
// @evidence contracts/common.md#clear-and-simple-design One type with private asset records and helpers for module identifiers, ranks, member construction and aliases; the two emission modes share the recording code.
|
|
22
22
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts Ordering uses a rank table over internal helper names and first use, not special-casing of a consumer. It is not safe for concurrent use, which the doc states.
|
|
23
23
|
// @evidence contracts/common.md#meaningful-documentation The doc describes both modes and the concurrency limit, and the field comment explains emit mode.
|
|
24
24
|
type ImportProgrammer struct {
|
|
25
|
-
assets_
|
|
26
|
-
order_
|
|
27
|
-
options_
|
|
25
|
+
assets_ map[string]*importProgrammer_asset
|
|
26
|
+
order_ []string
|
|
27
|
+
options_ ImportProgrammer_IOptions
|
|
28
|
+
patterns_ map[string]*shimast.Node
|
|
29
|
+
patternOrder_ []string
|
|
28
30
|
// emit_ switches import emission to AST-integration mode. When set, imports
|
|
29
31
|
// become namespace imports created by emit_.Factory, and every reference is a
|
|
30
32
|
// member access on a cloned unique name so tsgo's module transform owns the
|
|
@@ -332,14 +334,15 @@ func (p *ImportProgrammer) GetInternalText(name string) string {
|
|
|
332
334
|
return alias
|
|
333
335
|
}
|
|
334
336
|
|
|
335
|
-
// ToStatements returns the
|
|
336
|
-
// ordered by rank, with
|
|
337
|
-
// use within a rank.
|
|
337
|
+
// ToStatements returns the requested imports followed by lazy pattern slots.
|
|
338
|
+
// Import files are ordered by rank, with internal helpers grouped by kind, and
|
|
339
|
+
// by first use within a rank.
|
|
338
340
|
//
|
|
339
|
-
// @evidence contracts/common.md#principled-implementation Files are stable-sorted by a rank that groups internal helpers by kind and keeps insertion order within a rank, then emitted as one namespace import with optional constants in emit mode, or as namespace, default and named imports in the legacy mode, where a type-only default gets the type keyword.
|
|
341
|
+
// @evidence contracts/common.md#principled-implementation Files are stable-sorted by a rank that groups internal helpers by kind and keeps insertion order within a rank, then emitted as one namespace import with optional constants in emit mode, or as namespace, default and named imports in the legacy mode, where a type-only default gets the type keyword. Pattern slots follow as uninitialized var declarations in first-use order.
|
|
340
342
|
// @evidence contracts/common.md#clear-and-simple-design One method with the two mode branches and the rank helper.
|
|
341
343
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts The ranking is a fixed table and the order is deterministic.
|
|
342
344
|
// @evidence contracts/common.md#meaningful-documentation The doc states the order and the two forms.
|
|
345
|
+
// @evidence contracts/performance.md#efficient-algorithms Sorting A import assets costs O(A log A); pattern declarations follow in one O(P) first-use walk. Output allocation is proportional to the declarations that must be emitted, without scanning authored input strings or allocating a runtime lookup table.
|
|
343
346
|
func (p *ImportProgrammer) ToStatements() []*shimast.Node {
|
|
344
347
|
f := NodeFactoryOf(p.emit_)
|
|
345
348
|
statements := []*shimast.Node{}
|
|
@@ -448,6 +451,13 @@ func (p *ImportProgrammer) ToStatements() []*shimast.Node {
|
|
|
448
451
|
))
|
|
449
452
|
}
|
|
450
453
|
}
|
|
454
|
+
for _, source := range p.patternOrder_ {
|
|
455
|
+
statements = append(statements, f.NewVariableStatement(nil,
|
|
456
|
+
f.NewVariableDeclarationList(f.NewNodeList([]*shimast.Node{
|
|
457
|
+
f.NewVariableDeclaration(p.patterns_[source].Clone(f), nil, nil, nil),
|
|
458
|
+
}), shimast.NodeFlagsNone),
|
|
459
|
+
))
|
|
460
|
+
}
|
|
451
461
|
return statements
|
|
452
462
|
}
|
|
453
463
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package context
|
|
2
|
+
|
|
3
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
4
|
+
|
|
5
|
+
// Pattern reuses a flag-free RegExp after its first successful construction.
|
|
6
|
+
// The constructor stays at the check site, so invalid sources still throw only
|
|
7
|
+
// when the string check is reached. File-scoped var slots also work when a
|
|
8
|
+
// circular import invokes a hoisted function before this module's body runs.
|
|
9
|
+
// Without an emit context, retain the constructor: the standalone factory has
|
|
10
|
+
// no compiler name-collision information for a file-scoped generated slot.
|
|
11
|
+
//
|
|
12
|
+
// @evidence contracts/common.md#principled-implementation A flag-free RegExp test does not advance lastIndex, so checks with the same source can share the compiled object. Nullish assignment records only successful construction and preserves lazy SyntaxError timing. Uninitialized var declarations are hoisted and do not reset an early circular-import initialization. The compiler supplies collision-safe names; without that capability, the original constructor is returned without introducing a binding.
|
|
13
|
+
// @evidence contracts/common.md#clear-and-simple-design The file's existing statement collector owns one generated slot per distinct source and returns its lazy initialization expression.
|
|
14
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Only the caller's verified static flag-free pattern expressions use this method; arbitrary custom validators remain untouched.
|
|
15
|
+
// @evidence contracts/common.md#meaningful-documentation The comment explains reuse, lazy construction and the var declaration.
|
|
16
|
+
// @evidence contracts/performance.md#efficient-algorithms Source lookup uses the file's map and each generated check performs one nullish guard instead of constructing another RegExp.
|
|
17
|
+
// @evidence contracts/performance.md#reuse-equivalent-work Standard JavaScript RegExp objects with identical source and no flags have equivalent stateless test behavior; the importer is scoped to one transformed source file and not shared between programs.
|
|
18
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Generation retains one slot per distinct authored pattern until the file transform ends; runtime retains at most one RegExp per slot for the containing module's lifetime, with no runtime-keyed cache.
|
|
19
|
+
func (p *ImportProgrammer) Pattern(source string, constructor *shimast.Node) *shimast.Node {
|
|
20
|
+
if p.emit_ == nil {
|
|
21
|
+
return constructor
|
|
22
|
+
}
|
|
23
|
+
f := NodeFactoryOf(p.emit_)
|
|
24
|
+
name := p.patterns_[source]
|
|
25
|
+
if name == nil {
|
|
26
|
+
if p.patterns_ == nil {
|
|
27
|
+
p.patterns_ = map[string]*shimast.Node{}
|
|
28
|
+
}
|
|
29
|
+
name = p.emit_.Factory.NewUniqueName("__" + p.options_.InternalPrefix + "pattern")
|
|
30
|
+
p.patterns_[source] = name
|
|
31
|
+
p.patternOrder_ = append(p.patternOrder_, source)
|
|
32
|
+
}
|
|
33
|
+
return f.NewParenthesizedExpression(f.NewBinaryExpression(nil,
|
|
34
|
+
name.Clone(f), nil, f.NewToken(shimast.KindQuestionQuestionEqualsToken), constructor,
|
|
35
|
+
))
|
|
36
|
+
}
|
|
@@ -327,7 +327,10 @@ func Iterate_metadata_intersection(props IMetadataIteratorProps) bool {
|
|
|
327
327
|
options := props.Options
|
|
328
328
|
options.Functional = false
|
|
329
329
|
explore := props.Explore
|
|
330
|
-
|
|
330
|
+
// Attach tags to the surviving structure, not to a new alias wrapper whose
|
|
331
|
+
// array/object bucket would be empty here. Nested members clear Aliased again
|
|
332
|
+
// when they descend, so recursive references still retain their aliases.
|
|
333
|
+
explore.Aliased = true
|
|
331
334
|
explore.Escaped = false
|
|
332
335
|
// Re-explore the surviving base alone (`Intersected: true` short-circuits the
|
|
333
336
|
// intersection handler at the top of this function). TypeScript flattens nested
|
|
@@ -806,7 +806,7 @@ func randomProgrammer_decode_array(props randomProgrammer_decodeArrayProps) []*s
|
|
|
806
806
|
Components: components,
|
|
807
807
|
Array: props.Array,
|
|
808
808
|
})
|
|
809
|
-
schemaList = randomProgrammer_array_schema_list(
|
|
809
|
+
schemaList = randomProgrammer_array_schema_list(components, schemaList)
|
|
810
810
|
f := nativecontext.NodeFactoryOf(props.Context.Emit)
|
|
811
811
|
output := make([]*shimast.Node, 0, len(schemaList))
|
|
812
812
|
if props.Array.Type.Recursive {
|
|
@@ -1414,25 +1414,30 @@ func randomProgrammer_schema_without_items(schema nativeiterate.JsonSchema, emit
|
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
1416
|
func randomProgrammer_array_schema_list(
|
|
1417
|
-
array *schemametadata.MetadataArray,
|
|
1418
1417
|
components *nativeiterate.OpenApi_IComponents,
|
|
1419
1418
|
schemaList []nativeiterate.JsonSchema,
|
|
1420
1419
|
) []nativeiterate.JsonSchema {
|
|
1421
|
-
if
|
|
1422
|
-
array.Type == nil ||
|
|
1423
|
-
array.Type.Recursive == false ||
|
|
1424
|
-
components == nil ||
|
|
1425
|
-
components.Schemas == nil {
|
|
1420
|
+
if components == nil || components.Schemas == nil {
|
|
1426
1421
|
return schemaList
|
|
1427
1422
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1423
|
+
output := make([]nativeiterate.JsonSchema, 0, len(schemaList))
|
|
1424
|
+
for _, schema := range schemaList {
|
|
1425
|
+
// The producer's reference identifies this use's component, which can
|
|
1426
|
+
// differ from the underlying array type when schema tags are present.
|
|
1427
|
+
ref, isReference := schema["$ref"].(string)
|
|
1428
|
+
key, isLocal := strings.CutPrefix(ref, "#/components/schemas/")
|
|
1429
|
+
target, exists := components.Schemas[key]
|
|
1430
|
+
if !isReference || !isLocal || !exists || len(target) == 0 {
|
|
1431
|
+
output = append(output, schema)
|
|
1432
|
+
continue
|
|
1433
|
+
}
|
|
1434
|
+
if oneOf, ok := target["oneOf"].([]nativeiterate.JsonSchema); ok {
|
|
1435
|
+
output = append(output, oneOf...)
|
|
1436
|
+
} else {
|
|
1437
|
+
output = append(output, target)
|
|
1438
|
+
}
|
|
1434
1439
|
}
|
|
1435
|
-
return
|
|
1440
|
+
return output
|
|
1436
1441
|
}
|
|
1437
1442
|
|
|
1438
1443
|
func randomProgrammer_internal(context nativecontext.ITypiaContext, name string) *shimast.Node {
|
|
@@ -1240,7 +1240,7 @@ func checkerProgrammer_decode_array(props checkerProgrammer_decodeArrayProps) *s
|
|
|
1240
1240
|
if props.Array.Type.Index != nil {
|
|
1241
1241
|
index = *props.Array.Type.Index
|
|
1242
1242
|
}
|
|
1243
|
-
|
|
1243
|
+
main := checkerProgrammer_or(
|
|
1244
1244
|
f.NewCallExpression(
|
|
1245
1245
|
f.NewIdentifier(props.Functor.UseLocal(fmt.Sprintf("%sa%d", props.Config.Prefix, index))),
|
|
1246
1246
|
nil,
|
|
@@ -1260,14 +1260,12 @@ func checkerProgrammer_decode_array(props checkerProgrammer_decodeArrayProps) *s
|
|
|
1260
1260
|
}),
|
|
1261
1261
|
props.Context.Emit,
|
|
1262
1262
|
)
|
|
1263
|
+
// Structural traversal is shared and visit-guarded, but this use's tags must
|
|
1264
|
+
// be checked even when that helper has already visited the same array value.
|
|
1265
|
+
return checkerProgrammer_check_array(props, main)
|
|
1263
1266
|
}
|
|
1264
1267
|
|
|
1265
1268
|
func checkerProgrammer_decode_array_inline(props checkerProgrammer_decodeArrayInlineProps) *shimast.Node {
|
|
1266
|
-
length := nativeiterate.Check_array_length(nativeiterate.Check_array_lengthProps{
|
|
1267
|
-
Context: props.Context,
|
|
1268
|
-
Array: props.Array,
|
|
1269
|
-
Input: props.Input,
|
|
1270
|
-
})
|
|
1271
1269
|
main := FeatureProgrammer.Decode_array(FeatureProgrammer_DecodeArrayProps{
|
|
1272
1270
|
Config: FeatureProgrammer_DecodeArrayConfig{
|
|
1273
1271
|
Prefix: props.Config.Prefix,
|
|
@@ -1299,6 +1297,15 @@ func checkerProgrammer_decode_array_inline(props checkerProgrammer_decodeArrayIn
|
|
|
1299
1297
|
Explore: props.Explore,
|
|
1300
1298
|
Emit: props.Context.Emit,
|
|
1301
1299
|
})
|
|
1300
|
+
return checkerProgrammer_check_array(props, main)
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
func checkerProgrammer_check_array(props checkerProgrammer_decodeArrayProps, main *shimast.Node) *shimast.Node {
|
|
1304
|
+
length := nativeiterate.Check_array_length(nativeiterate.Check_array_lengthProps{
|
|
1305
|
+
Context: props.Context,
|
|
1306
|
+
Array: props.Array,
|
|
1307
|
+
Input: props.Input,
|
|
1308
|
+
})
|
|
1302
1309
|
if length.Expression == nil && len(length.Conditions) == 0 {
|
|
1303
1310
|
return main
|
|
1304
1311
|
}
|
|
@@ -24,7 +24,7 @@ type Check_stringProps struct {
|
|
|
24
24
|
// Check_string builds the check entry of a string atomic: a `typeof input ===
|
|
25
25
|
// "string"` guard plus the atomic's type-tag conditions.
|
|
26
26
|
//
|
|
27
|
-
// @evidence contracts/common.md#principled-implementation It builds the
|
|
27
|
+
// @evidence contracts/common.md#principled-implementation It builds the string guard and type-tag conditions. A static flag-free RegExp(source).test(input) whose source agrees with pattern metadata uses the file's lazy reusable slot; other validation expressions retain their authored AST.
|
|
28
28
|
// @evidence contracts/common.md#clear-and-simple-design One exported function; the pieces that repeat live in private helpers of the same file.
|
|
29
29
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts Its inputs are its arguments and the context they carry, and it keeps no state of its own.
|
|
30
30
|
// @evidence contracts/common.md#meaningful-documentation The doc states what it builds.
|
|
@@ -50,9 +50,10 @@ func check_string_type_tags(props Check_stringProps) [][]nativehelpers.ICheckEnt
|
|
|
50
50
|
tags := check_string_filter_validate(row)
|
|
51
51
|
conditions := make([]nativehelpers.ICheckEntry_ICondition, 0, len(tags))
|
|
52
52
|
for _, tag := range tags {
|
|
53
|
+
expression := check_string_transpile(props.Context, tag)(props.Input)
|
|
53
54
|
conditions = append(conditions, nativehelpers.ICheckEntry_ICondition{
|
|
54
55
|
Expected: "string & " + tag.Name,
|
|
55
|
-
Expression:
|
|
56
|
+
Expression: expression,
|
|
56
57
|
})
|
|
57
58
|
}
|
|
58
59
|
for _, tag := range row {
|
|
@@ -68,6 +69,36 @@ func check_string_type_tags(props Check_stringProps) [][]nativehelpers.ICheckEnt
|
|
|
68
69
|
return output
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
// Recognize only the standard flag-free RegExp(source).test(input) form. A
|
|
73
|
+
// custom pattern-kind tag may supply arbitrary validation code or flags, and
|
|
74
|
+
// must retain that code and its state rather than being lowered from value alone.
|
|
75
|
+
func check_string_pattern(context nativecontext.ITypiaContext, tag nativemetadata.IMetadataTypeTag, input *shimast.Expression, expression *shimast.Node) *shimast.Node {
|
|
76
|
+
source, ok := tag.Value.(string)
|
|
77
|
+
if !ok || tag.Kind != "pattern" || context.Importer == nil || expression.Kind != shimast.KindCallExpression {
|
|
78
|
+
return expression
|
|
79
|
+
}
|
|
80
|
+
test := expression.AsCallExpression()
|
|
81
|
+
if test.Arguments == nil || len(test.Arguments.Nodes) != 1 || test.Arguments.Nodes[0] != input || test.Expression.Kind != shimast.KindPropertyAccessExpression {
|
|
82
|
+
return expression
|
|
83
|
+
}
|
|
84
|
+
member := test.Expression.AsPropertyAccessExpression()
|
|
85
|
+
if member.Name().Text() != "test" || member.Expression.Kind != shimast.KindCallExpression {
|
|
86
|
+
return expression
|
|
87
|
+
}
|
|
88
|
+
constructor := member.Expression.AsCallExpression()
|
|
89
|
+
if constructor.Expression.Kind != shimast.KindIdentifier || constructor.Expression.Text() != "RegExp" || constructor.Arguments == nil || len(constructor.Arguments.Nodes) != 1 {
|
|
90
|
+
return expression
|
|
91
|
+
}
|
|
92
|
+
argument := constructor.Arguments.Nodes[0]
|
|
93
|
+
if !shimast.IsStringLiteral(argument) || argument.Text() != source {
|
|
94
|
+
return expression
|
|
95
|
+
}
|
|
96
|
+
f := nativecontext.NodeFactoryOf(context.Emit)
|
|
97
|
+
return f.NewCallExpression(f.NewPropertyAccessExpression(
|
|
98
|
+
context.Importer.Pattern(source, member.Expression), nil, f.NewIdentifier("test"), shimast.NodeFlagsNone,
|
|
99
|
+
), nil, nil, test.Arguments, shimast.NodeFlagsNone)
|
|
100
|
+
}
|
|
101
|
+
|
|
71
102
|
func check_string_filter_validate(row []nativemetadata.IMetadataTypeTag) []nativemetadata.IMetadataTypeTag {
|
|
72
103
|
tags := []nativemetadata.IMetadataTypeTag{}
|
|
73
104
|
for _, tag := range row {
|
|
@@ -78,13 +109,18 @@ func check_string_filter_validate(row []nativemetadata.IMetadataTypeTag) []nativ
|
|
|
78
109
|
return tags
|
|
79
110
|
}
|
|
80
111
|
|
|
81
|
-
|
|
112
|
+
// The same lowering serves ordinary strings, whole-template tags and captured
|
|
113
|
+
// placeholder/backtracking checks, so every standard Pattern shares the cache.
|
|
114
|
+
func check_string_transpile(context nativecontext.ITypiaContext, tag nativemetadata.IMetadataTypeTag) func(input *shimast.Expression) *shimast.Node {
|
|
82
115
|
var importer nativefactories.ExpressionFactory_Importer
|
|
83
116
|
if v := context.Importer; v != nil {
|
|
84
117
|
importer = v
|
|
85
118
|
}
|
|
86
|
-
|
|
119
|
+
transpile := nativefactories.ExpressionFactory.Transpile(nativefactories.ExpressionFactory_TranspileProps{
|
|
87
120
|
Importer: importer,
|
|
88
|
-
Script:
|
|
121
|
+
Script: tag.Validate,
|
|
89
122
|
}, context.Emit)
|
|
123
|
+
return func(input *shimast.Expression) *shimast.Node {
|
|
124
|
+
return check_string_pattern(context, tag, input, transpile(input))
|
|
125
|
+
}
|
|
90
126
|
}
|