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.
Files changed (31) hide show
  1. package/lib/internal/_stringLengthGte.d.ts +7 -3
  2. package/lib/internal/_stringLengthGte.js +9 -3
  3. package/lib/internal/_stringLengthGte.js.map +1 -1
  4. package/lib/internal/_stringLengthGte.mjs +8 -3
  5. package/lib/internal/_stringLengthGte.mjs.map +1 -1
  6. package/lib/internal/_stringLengthLte.d.ts +7 -3
  7. package/lib/internal/_stringLengthLte.js +9 -3
  8. package/lib/internal/_stringLengthLte.js.map +1 -1
  9. package/lib/internal/_stringLengthLte.mjs +8 -3
  10. package/lib/internal/_stringLengthLte.mjs.map +1 -1
  11. package/native/cmd/ttsc-typia/json_schema_recursive_array_alias_transform_test.go +79 -0
  12. package/native/core/context/ImportProgrammer.go +19 -9
  13. package/native/core/context/ImportProgrammerPattern.go +36 -0
  14. package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +4 -1
  15. package/native/core/programmers/RandomProgrammer.go +19 -14
  16. package/native/core/programmers/internal/CheckerProgrammer.go +13 -6
  17. package/native/core/programmers/iterate/check_string.go +41 -5
  18. package/native/core/programmers/iterate/check_string_pattern_test.go +91 -0
  19. package/native/core/programmers/iterate/check_template.go +4 -4
  20. package/native/core/programmers/iterate/json_schema_alias.go +22 -24
  21. package/native/core/programmers/iterate/json_schema_array.go +14 -10
  22. package/native/core/programmers/iterate/json_schema_array_names.go +113 -0
  23. package/native/core/programmers/iterate/json_schema_native.go +10 -3
  24. package/native/core/programmers/iterate/json_schema_recursive_array_identity_test.go +75 -0
  25. package/native/core/programmers/iterate/json_schema_recursive_array_ownership_test.go +112 -0
  26. package/native/core/programmers/iterate/json_schema_station.go +5 -1
  27. package/native/core/programmers/json/JsonSchemasProgrammer.go +1 -0
  28. package/native/core/programmers/random_programmer_array_schema_reference_test.go +53 -0
  29. package/package.json +3 -3
  30. package/src/internal/_stringLengthGte.ts +8 -3
  31. 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
- * Iteration stops as soon as the count is reached, so a long string is not
5
- * scanned to its end.
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 The iterator is consumed only until the count reaches the requested minimum, which decides the lower bound without scanning the whole string; a minimum of zero is true at once.
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
- * Iteration stops as soon as the count is reached, so a long string is not
8
- * scanned to its end.
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 The iterator is consumed only until the count reaches the requested minimum, which decides the lower bound without scanning the whole string; a minimum of zero is true at once.
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;;;;;;;;;;GAUG;AACI,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAW,EAAE;IACzE,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;AALW,QAAA,gBAAgB,GAAhB,gBAAgB,CAK3B"}
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
- * Iteration stops as soon as the count is reached, so a long string is not
6
- * scanned to its end.
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 The iterator is consumed only until the count reaches the requested minimum, which decides the lower bound without scanning the whole string; a minimum of zero is true at once.
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 * Iteration stops as soon as the count is reached, so a long string is not\n * scanned to its end.\n *\n * @evidence contracts/common.md#principled-implementation The iterator is consumed only until the count reaches the requested minimum, which decides the lower bound without scanning the whole string; a minimum of zero is true at once.\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 */\nexport const _stringLengthGte = (value: string, length: number): boolean => {\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":";;;;;;;;;;;;AAWA,MAAa,oBAAoB,OAAe,WAA4B;CAC1E,IAAI,QAAgB;CACpB,IAAI,UAAU,OAAO,OAAO;CAC5B,KAAK,MAAM,OAAO,OAAO,IAAI,UAAU,EAAE,OAAO,OAAO;CACvD,OAAO;AACT"}
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
- * Iteration stops as soon as the count is exceeded, so a long string is not
5
- * scanned to its end.
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 The iterator is consumed until the count exceeds the maximum, which decides the upper bound without scanning past it; a negative maximum is false at once.
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
- * Iteration stops as soon as the count is exceeded, so a long string is not
8
- * scanned to its end.
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 The iterator is consumed until the count exceeds the maximum, which decides the upper bound without scanning past it; a negative maximum is false at once.
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;;;;;;;;;;GAUG;AACI,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAW,EAAE;IACzE,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;AALW,QAAA,gBAAgB,GAAhB,gBAAgB,CAK3B"}
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
- * Iteration stops as soon as the count is exceeded, so a long string is not
6
- * scanned to its end.
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 The iterator is consumed until the count exceeds the maximum, which decides the upper bound without scanning past it; a negative maximum is false at once.
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 * Iteration stops as soon as the count is exceeded, so a long string is not\n * scanned to its end.\n *\n * @evidence contracts/common.md#principled-implementation The iterator is consumed until the count exceeds the maximum, which decides the upper bound without scanning past it; a negative maximum is false at once.\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 */\nexport const _stringLengthLte = (value: string, length: number): boolean => {\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":";;;;;;;;;;;;AAWA,MAAa,oBAAoB,OAAe,WAA4B;CAC1E,IAAI,QAAgB;CACpB,IAAI,EAAE,SAAS,SAAS,OAAO;CAC/B,KAAK,MAAM,OAAO,OAAO,IAAI,EAAE,EAAE,SAAS,SAAS,OAAO;CAC1D,OAAO;AACT"}
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 are always imported from the typia package's internal folder under prefixed aliases.
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_ map[string]*importProgrammer_asset
26
- order_ []string
27
- options_ ImportProgrammer_IOptions
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 import statements for everything requested. Files are
336
- // ordered by rank, with the internal helper files grouped by kind, and by first
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
- explore.Aliased = false
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(props.Array, components, schemaList)
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 array == nil ||
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
- schema, ok := components.Schemas[array.Type.Name]
1429
- if ok == false || len(schema) == 0 {
1430
- return schemaList
1431
- }
1432
- if oneOf, ok := schema["oneOf"].([]nativeiterate.JsonSchema); ok {
1433
- return oneOf
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 []nativeiterate.JsonSchema{schema}
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
- return checkerProgrammer_or(
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 check entry of a string atomic: a `typeof input === "string"` guard plus the atomic's type-tag conditions.
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: check_string_transpile(props.Context, tag.Validate)(props.Input),
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
- func check_string_transpile(context nativecontext.ITypiaContext, script string) func(input *shimast.Expression) *shimast.Node {
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
- return nativefactories.ExpressionFactory.Transpile(nativefactories.ExpressionFactory_TranspileProps{
119
+ transpile := nativefactories.ExpressionFactory.Transpile(nativefactories.ExpressionFactory_TranspileProps{
87
120
  Importer: importer,
88
- Script: 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
  }