typia 9.7.0 → 9.7.1
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/programmers/json/JsonStringifyProgrammer.js +2 -2
- package/lib/programmers/json/JsonStringifyProgrammer.js.map +1 -1
- package/lib/programmers/json/JsonStringifyProgrammer.mjs +2 -2
- package/lib/tags/Constant.d.mts +32 -0
- package/lib/tags/Constant.d.ts +32 -0
- package/lib/tags/ContentMediaType.d.mts +17 -0
- package/lib/tags/ContentMediaType.d.ts +17 -0
- package/lib/tags/Default.d.mts +30 -0
- package/lib/tags/Default.d.ts +30 -0
- package/lib/tags/Example.d.mts +32 -0
- package/lib/tags/Example.d.ts +32 -0
- package/lib/tags/Examples.d.mts +40 -0
- package/lib/tags/Examples.d.ts +40 -0
- package/lib/tags/ExclusiveMaximum.d.mts +18 -0
- package/lib/tags/ExclusiveMaximum.d.ts +18 -0
- package/lib/tags/ExclusiveMinimum.d.mts +18 -0
- package/lib/tags/ExclusiveMinimum.d.ts +18 -0
- package/lib/tags/Format.d.mts +24 -0
- package/lib/tags/Format.d.ts +24 -0
- package/lib/tags/JsonSchemaPlugin.d.mts +26 -0
- package/lib/tags/JsonSchemaPlugin.d.ts +26 -0
- package/lib/tags/MaxItems.d.mts +19 -0
- package/lib/tags/MaxItems.d.ts +19 -0
- package/lib/tags/MaxLength.d.mts +12 -0
- package/lib/tags/MaxLength.d.ts +12 -0
- package/lib/tags/Maximum.d.mts +18 -0
- package/lib/tags/Maximum.d.ts +18 -0
- package/lib/tags/MinItems.d.mts +19 -0
- package/lib/tags/MinItems.d.ts +19 -0
- package/lib/tags/MinLength.d.mts +12 -0
- package/lib/tags/MinLength.d.ts +12 -0
- package/lib/tags/Minimum.d.mts +18 -0
- package/lib/tags/Minimum.d.ts +18 -0
- package/lib/tags/MultipleOf.d.mts +18 -0
- package/lib/tags/MultipleOf.d.ts +18 -0
- package/lib/tags/Pattern.d.mts +15 -0
- package/lib/tags/Pattern.d.ts +15 -0
- package/lib/tags/Sequence.d.mts +25 -0
- package/lib/tags/Sequence.d.ts +25 -0
- package/lib/tags/TagBase.d.mts +27 -0
- package/lib/tags/TagBase.d.ts +27 -0
- package/lib/tags/Type.d.mts +26 -0
- package/lib/tags/Type.d.ts +26 -0
- package/lib/tags/UniqueItems.d.mts +19 -0
- package/lib/tags/UniqueItems.d.ts +19 -0
- package/lib/transformers/ImportTransformer.js +119 -1
- package/lib/transformers/ImportTransformer.js.map +1 -1
- package/lib/transformers/ImportTransformer.mjs +119 -1
- package/package.json +1 -1
- package/src/IRandomGenerator.ts +13 -5
- package/src/programmers/json/JsonStringifyProgrammer.ts +2 -2
- package/src/tags/Constant.ts +32 -0
- package/src/tags/ContentMediaType.ts +17 -0
- package/src/tags/Default.ts +30 -0
- package/src/tags/Example.ts +32 -0
- package/src/tags/Examples.ts +40 -0
- package/src/tags/ExclusiveMaximum.ts +18 -0
- package/src/tags/ExclusiveMinimum.ts +18 -0
- package/src/tags/Format.ts +24 -0
- package/src/tags/JsonSchemaPlugin.ts +26 -0
- package/src/tags/MaxItems.ts +19 -0
- package/src/tags/MaxLength.ts +12 -0
- package/src/tags/Maximum.ts +18 -0
- package/src/tags/MinItems.ts +19 -0
- package/src/tags/MinLength.ts +12 -0
- package/src/tags/Minimum.ts +18 -0
- package/src/tags/MultipleOf.ts +18 -0
- package/src/tags/Pattern.ts +15 -0
- package/src/tags/Sequence.ts +25 -0
- package/src/tags/TagBase.ts +27 -0
- package/src/tags/Type.ts +26 -0
- package/src/tags/UniqueItems.ts +19 -0
- package/src/transformers/ImportTransformer.ts +178 -1
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
|
+
/**
|
|
3
|
+
* Unique items validation tag for arrays.
|
|
4
|
+
*
|
|
5
|
+
* Enforces that all items in an array are unique, preventing duplicate values.
|
|
6
|
+
* Uniqueness is determined using strict equality (===) for primitives and
|
|
7
|
+
* deep structural comparison for objects and arrays. This means two objects
|
|
8
|
+
* with the same properties and values are considered duplicates.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Ensure all IDs are unique
|
|
12
|
+
* type UserIds = number[] & UniqueItems;
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Prevent duplicate email addresses
|
|
16
|
+
* type EmailList = string[] & UniqueItems;
|
|
17
|
+
*
|
|
18
|
+
* @template Value - Boolean flag to enable/disable uniqueness validation (defaults to true)
|
|
19
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
20
|
+
*/
|
|
2
21
|
export type UniqueItems<Value extends boolean = true> = TagBase<{
|
|
3
22
|
target: "array";
|
|
4
23
|
kind: "uniqueItems";
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
|
+
/**
|
|
3
|
+
* Unique items validation tag for arrays.
|
|
4
|
+
*
|
|
5
|
+
* Enforces that all items in an array are unique, preventing duplicate values.
|
|
6
|
+
* Uniqueness is determined using strict equality (===) for primitives and
|
|
7
|
+
* deep structural comparison for objects and arrays. This means two objects
|
|
8
|
+
* with the same properties and values are considered duplicates.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Ensure all IDs are unique
|
|
12
|
+
* type UserIds = number[] & UniqueItems;
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Prevent duplicate email addresses
|
|
16
|
+
* type EmailList = string[] & UniqueItems;
|
|
17
|
+
*
|
|
18
|
+
* @template Value - Boolean flag to enable/disable uniqueness validation (defaults to true)
|
|
19
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
20
|
+
*/
|
|
2
21
|
export type UniqueItems<Value extends boolean = true> = TagBase<{
|
|
3
22
|
target: "array";
|
|
4
23
|
kind: "uniqueItems";
|
|
@@ -19,12 +19,16 @@ var ImportTransformer;
|
|
|
19
19
|
return props.file;
|
|
20
20
|
const from = get_directory_path(path_1.default.resolve(props.file.getSourceFile().fileName));
|
|
21
21
|
const to = from.replace(props.top, props.to);
|
|
22
|
-
|
|
22
|
+
// First pass: transform relative imports
|
|
23
|
+
let transformedFile = typescript_1.default.visitEachChild(props.file, (node) => transform_node({
|
|
23
24
|
top: props.top,
|
|
24
25
|
from,
|
|
25
26
|
to,
|
|
26
27
|
node,
|
|
27
28
|
}), props.context);
|
|
29
|
+
// Second pass: remove unused typia imports
|
|
30
|
+
transformedFile = removeUnusedTypiaImports(transformedFile);
|
|
31
|
+
return transformedFile;
|
|
28
32
|
};
|
|
29
33
|
const transform_node = (props) => {
|
|
30
34
|
if (!typescript_1.default.isImportDeclaration(props.node) ||
|
|
@@ -51,4 +55,118 @@ const get_directory_path = (file) => {
|
|
|
51
55
|
split.pop();
|
|
52
56
|
return path_1.default.resolve(split.join(path_1.default.sep));
|
|
53
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Remove unused typia imports that are only used in transformable calls
|
|
60
|
+
*/
|
|
61
|
+
const removeUnusedTypiaImports = (file) => {
|
|
62
|
+
const imports = new Map();
|
|
63
|
+
for (const stmt of file.statements) {
|
|
64
|
+
if (typescript_1.default.isImportDeclaration(stmt) === false ||
|
|
65
|
+
typescript_1.default.isStringLiteral(stmt.moduleSpecifier) === false ||
|
|
66
|
+
stmt.moduleSpecifier.text !== "typia" ||
|
|
67
|
+
stmt.importClause === undefined)
|
|
68
|
+
continue;
|
|
69
|
+
// Track default import (import typia from 'typia')
|
|
70
|
+
if (stmt.importClause.name)
|
|
71
|
+
imports.set(stmt.importClause.name.text, {
|
|
72
|
+
declaration: stmt,
|
|
73
|
+
default: true,
|
|
74
|
+
});
|
|
75
|
+
// Track named imports (import { tags } from 'typia') - keep these
|
|
76
|
+
if (stmt.importClause.namedBindings &&
|
|
77
|
+
typescript_1.default.isNamedImports(stmt.importClause.namedBindings))
|
|
78
|
+
for (const element of stmt.importClause.namedBindings.elements)
|
|
79
|
+
imports.set(element.name.text, {
|
|
80
|
+
declaration: stmt,
|
|
81
|
+
default: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (imports.size === 0)
|
|
85
|
+
return file; // No typia imports to check
|
|
86
|
+
// Find usage of typia identifiers that are NOT transformable calls
|
|
87
|
+
const nonTransformableUsage = new Set();
|
|
88
|
+
const checkUsage = (node) => {
|
|
89
|
+
if (typescript_1.default.isIdentifier(node) && imports.has(node.text)) {
|
|
90
|
+
const identifier = node.text;
|
|
91
|
+
// Check if this identifier is being used as part of a property access
|
|
92
|
+
if (node.parent &&
|
|
93
|
+
typescript_1.default.isPropertyAccessExpression(node.parent) &&
|
|
94
|
+
node.parent.expression === node) {
|
|
95
|
+
// This is typia.something - check if it's a transformable call pattern
|
|
96
|
+
if (!isLikelyTransformableCall(node.parent))
|
|
97
|
+
nonTransformableUsage.add(identifier);
|
|
98
|
+
}
|
|
99
|
+
else
|
|
100
|
+
// Direct usage of the typia identifier (not as property access)
|
|
101
|
+
// This is definitely non-transformable usage
|
|
102
|
+
nonTransformableUsage.add(identifier);
|
|
103
|
+
}
|
|
104
|
+
typescript_1.default.forEachChild(node, checkUsage);
|
|
105
|
+
};
|
|
106
|
+
// Check all statements except import declarations
|
|
107
|
+
for (const stmt of file.statements)
|
|
108
|
+
if (!typescript_1.default.isImportDeclaration(stmt) ||
|
|
109
|
+
!typescript_1.default.isStringLiteral(stmt.moduleSpecifier) ||
|
|
110
|
+
stmt.moduleSpecifier.text !== "typia")
|
|
111
|
+
checkUsage(stmt);
|
|
112
|
+
// Update import statements
|
|
113
|
+
const newStatements = file.statements
|
|
114
|
+
.map((stmt) => {
|
|
115
|
+
if (typescript_1.default.isImportDeclaration(stmt) &&
|
|
116
|
+
typescript_1.default.isStringLiteral(stmt.moduleSpecifier) &&
|
|
117
|
+
stmt.moduleSpecifier.text === "typia" &&
|
|
118
|
+
stmt.importClause) {
|
|
119
|
+
const newImportClause = filterTypiaImportClause(stmt.importClause, nonTransformableUsage);
|
|
120
|
+
if (newImportClause)
|
|
121
|
+
return typescript_1.default.factory.createImportDeclaration(stmt.modifiers, newImportClause, stmt.moduleSpecifier, stmt.attributes);
|
|
122
|
+
return null; // Skip adding the import if all imports are unused
|
|
123
|
+
}
|
|
124
|
+
return stmt;
|
|
125
|
+
})
|
|
126
|
+
.filter((stmt) => stmt !== null);
|
|
127
|
+
return typescript_1.default.factory.updateSourceFile(file, newStatements, file.isDeclarationFile, file.referencedFiles, file.typeReferenceDirectives, file.hasNoDefaultLib, file.libReferenceDirectives);
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Check if a property access expression looks like a transformable typia call
|
|
131
|
+
* This uses heuristics to detect patterns like typia.xxx(), typia.namespace.xxx()
|
|
132
|
+
*/
|
|
133
|
+
const isLikelyTransformableCall = (node) => {
|
|
134
|
+
// Check if this is eventually part of a call expression
|
|
135
|
+
let current = node;
|
|
136
|
+
// Walk up the chain to find if this leads to a call expression
|
|
137
|
+
// Handle patterns like: typia.xxx(), typia.namespace.xxx()
|
|
138
|
+
while (typescript_1.default.isPropertyAccessExpression(current)) {
|
|
139
|
+
current = current.parent;
|
|
140
|
+
}
|
|
141
|
+
// If the final result is a call expression, this is likely transformable
|
|
142
|
+
if (typescript_1.default.isCallExpression(current) &&
|
|
143
|
+
(current.expression === node ||
|
|
144
|
+
(typescript_1.default.isPropertyAccessExpression(current.expression) &&
|
|
145
|
+
isTypiaPropertyChain(current.expression)))) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Check if a property access expression is part of a typia.xxx.yyy chain
|
|
152
|
+
*/
|
|
153
|
+
const isTypiaPropertyChain = (node) => {
|
|
154
|
+
let current = node;
|
|
155
|
+
while (typescript_1.default.isPropertyAccessExpression(current)) {
|
|
156
|
+
current = current.expression;
|
|
157
|
+
}
|
|
158
|
+
return typescript_1.default.isIdentifier(current) && current.text === "typia";
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Filter import clause to remove unused default imports
|
|
162
|
+
*/
|
|
163
|
+
const filterTypiaImportClause = (importClause, usedImports) => {
|
|
164
|
+
const hasDefaultImport = importClause.name && usedImports.has(importClause.name.text);
|
|
165
|
+
const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
|
|
166
|
+
// Return undefined if no imports are used
|
|
167
|
+
if (!hasDefaultImport && !namedBindings) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
return typescript_1.default.factory.createImportClause(importClause.isTypeOnly, hasDefaultImport ? importClause.name : undefined, namedBindings);
|
|
171
|
+
};
|
|
54
172
|
//# sourceMappingURL=ImportTransformer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImportTransformer.js","sourceRoot":"","sources":["../../src/transformers/ImportTransformer.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AACxB,4DAA4B;AAE5B,IAAiB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ImportTransformer.js","sourceRoot":"","sources":["../../src/transformers/ImportTransformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,12 +14,16 @@ var ImportTransformer;
|
|
|
14
14
|
return props.file;
|
|
15
15
|
const from = get_directory_path(path.resolve(props.file.getSourceFile().fileName));
|
|
16
16
|
const to = from.replace(props.top, props.to);
|
|
17
|
-
|
|
17
|
+
// First pass: transform relative imports
|
|
18
|
+
let transformedFile = ts.visitEachChild(props.file, (node) => transform_node({
|
|
18
19
|
top: props.top,
|
|
19
20
|
from,
|
|
20
21
|
to,
|
|
21
22
|
node,
|
|
22
23
|
}), props.context);
|
|
24
|
+
// Second pass: remove unused typia imports
|
|
25
|
+
transformedFile = removeUnusedTypiaImports(transformedFile);
|
|
26
|
+
return transformedFile;
|
|
23
27
|
};
|
|
24
28
|
const transform_node = (props) => {
|
|
25
29
|
if (!ts.isImportDeclaration(props.node) ||
|
|
@@ -46,6 +50,120 @@ const get_directory_path = (file) => {
|
|
|
46
50
|
split.pop();
|
|
47
51
|
return path.resolve(split.join(path.sep));
|
|
48
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Remove unused typia imports that are only used in transformable calls
|
|
55
|
+
*/
|
|
56
|
+
const removeUnusedTypiaImports = (file) => {
|
|
57
|
+
const imports = new Map();
|
|
58
|
+
for (const stmt of file.statements) {
|
|
59
|
+
if (ts.isImportDeclaration(stmt) === false ||
|
|
60
|
+
ts.isStringLiteral(stmt.moduleSpecifier) === false ||
|
|
61
|
+
stmt.moduleSpecifier.text !== "typia" ||
|
|
62
|
+
stmt.importClause === undefined)
|
|
63
|
+
continue;
|
|
64
|
+
// Track default import (import typia from 'typia')
|
|
65
|
+
if (stmt.importClause.name)
|
|
66
|
+
imports.set(stmt.importClause.name.text, {
|
|
67
|
+
declaration: stmt,
|
|
68
|
+
default: true,
|
|
69
|
+
});
|
|
70
|
+
// Track named imports (import { tags } from 'typia') - keep these
|
|
71
|
+
if (stmt.importClause.namedBindings &&
|
|
72
|
+
ts.isNamedImports(stmt.importClause.namedBindings))
|
|
73
|
+
for (const element of stmt.importClause.namedBindings.elements)
|
|
74
|
+
imports.set(element.name.text, {
|
|
75
|
+
declaration: stmt,
|
|
76
|
+
default: false,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (imports.size === 0)
|
|
80
|
+
return file; // No typia imports to check
|
|
81
|
+
// Find usage of typia identifiers that are NOT transformable calls
|
|
82
|
+
const nonTransformableUsage = new Set();
|
|
83
|
+
const checkUsage = (node) => {
|
|
84
|
+
if (ts.isIdentifier(node) && imports.has(node.text)) {
|
|
85
|
+
const identifier = node.text;
|
|
86
|
+
// Check if this identifier is being used as part of a property access
|
|
87
|
+
if (node.parent &&
|
|
88
|
+
ts.isPropertyAccessExpression(node.parent) &&
|
|
89
|
+
node.parent.expression === node) {
|
|
90
|
+
// This is typia.something - check if it's a transformable call pattern
|
|
91
|
+
if (!isLikelyTransformableCall(node.parent))
|
|
92
|
+
nonTransformableUsage.add(identifier);
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
// Direct usage of the typia identifier (not as property access)
|
|
96
|
+
// This is definitely non-transformable usage
|
|
97
|
+
nonTransformableUsage.add(identifier);
|
|
98
|
+
}
|
|
99
|
+
ts.forEachChild(node, checkUsage);
|
|
100
|
+
};
|
|
101
|
+
// Check all statements except import declarations
|
|
102
|
+
for (const stmt of file.statements)
|
|
103
|
+
if (!ts.isImportDeclaration(stmt) ||
|
|
104
|
+
!ts.isStringLiteral(stmt.moduleSpecifier) ||
|
|
105
|
+
stmt.moduleSpecifier.text !== "typia")
|
|
106
|
+
checkUsage(stmt);
|
|
107
|
+
// Update import statements
|
|
108
|
+
const newStatements = file.statements
|
|
109
|
+
.map((stmt) => {
|
|
110
|
+
if (ts.isImportDeclaration(stmt) &&
|
|
111
|
+
ts.isStringLiteral(stmt.moduleSpecifier) &&
|
|
112
|
+
stmt.moduleSpecifier.text === "typia" &&
|
|
113
|
+
stmt.importClause) {
|
|
114
|
+
const newImportClause = filterTypiaImportClause(stmt.importClause, nonTransformableUsage);
|
|
115
|
+
if (newImportClause)
|
|
116
|
+
return ts.factory.createImportDeclaration(stmt.modifiers, newImportClause, stmt.moduleSpecifier, stmt.attributes);
|
|
117
|
+
return null; // Skip adding the import if all imports are unused
|
|
118
|
+
}
|
|
119
|
+
return stmt;
|
|
120
|
+
})
|
|
121
|
+
.filter((stmt) => stmt !== null);
|
|
122
|
+
return ts.factory.updateSourceFile(file, newStatements, file.isDeclarationFile, file.referencedFiles, file.typeReferenceDirectives, file.hasNoDefaultLib, file.libReferenceDirectives);
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Check if a property access expression looks like a transformable typia call
|
|
126
|
+
* This uses heuristics to detect patterns like typia.xxx(), typia.namespace.xxx()
|
|
127
|
+
*/
|
|
128
|
+
const isLikelyTransformableCall = (node) => {
|
|
129
|
+
// Check if this is eventually part of a call expression
|
|
130
|
+
let current = node;
|
|
131
|
+
// Walk up the chain to find if this leads to a call expression
|
|
132
|
+
// Handle patterns like: typia.xxx(), typia.namespace.xxx()
|
|
133
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
134
|
+
current = current.parent;
|
|
135
|
+
}
|
|
136
|
+
// If the final result is a call expression, this is likely transformable
|
|
137
|
+
if (ts.isCallExpression(current) &&
|
|
138
|
+
(current.expression === node ||
|
|
139
|
+
(ts.isPropertyAccessExpression(current.expression) &&
|
|
140
|
+
isTypiaPropertyChain(current.expression)))) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Check if a property access expression is part of a typia.xxx.yyy chain
|
|
147
|
+
*/
|
|
148
|
+
const isTypiaPropertyChain = (node) => {
|
|
149
|
+
let current = node;
|
|
150
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
151
|
+
current = current.expression;
|
|
152
|
+
}
|
|
153
|
+
return ts.isIdentifier(current) && current.text === "typia";
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Filter import clause to remove unused default imports
|
|
157
|
+
*/
|
|
158
|
+
const filterTypiaImportClause = (importClause, usedImports) => {
|
|
159
|
+
const hasDefaultImport = importClause.name && usedImports.has(importClause.name.text);
|
|
160
|
+
const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
|
|
161
|
+
// Return undefined if no imports are used
|
|
162
|
+
if (!hasDefaultImport && !namedBindings) {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
return ts.factory.createImportClause(importClause.isTypeOnly, hasDefaultImport ? importClause.name : undefined, namedBindings);
|
|
166
|
+
};
|
|
49
167
|
|
|
50
168
|
export { ImportTransformer };
|
|
51
169
|
//# sourceMappingURL=ImportTransformer.mjs.map
|
package/package.json
CHANGED
package/src/IRandomGenerator.ts
CHANGED
|
@@ -288,25 +288,33 @@ export namespace IRandomGenerator {
|
|
|
288
288
|
* Custom string generator that can handle special string formats
|
|
289
289
|
* based on schema properties.
|
|
290
290
|
*/
|
|
291
|
-
string?: (
|
|
291
|
+
string?: (
|
|
292
|
+
schema: OpenApi.IJsonSchema.IString & Record<string, any>,
|
|
293
|
+
) => string;
|
|
292
294
|
|
|
293
295
|
/**
|
|
294
296
|
* Custom number generator that can handle special number constraints
|
|
295
297
|
* based on schema properties.
|
|
296
298
|
*/
|
|
297
|
-
number?: (
|
|
299
|
+
number?: (
|
|
300
|
+
schema: OpenApi.IJsonSchema.INumber & Record<string, any>,
|
|
301
|
+
) => number;
|
|
298
302
|
|
|
299
303
|
/**
|
|
300
304
|
* Custom integer generator that can handle special integer constraints
|
|
301
305
|
* based on schema properties.
|
|
302
306
|
*/
|
|
303
|
-
integer?: (
|
|
307
|
+
integer?: (
|
|
308
|
+
schema: OpenApi.IJsonSchema.IInteger & Record<string, any>,
|
|
309
|
+
) => number;
|
|
304
310
|
|
|
305
311
|
/**
|
|
306
312
|
* Custom bigint generator that can handle special bigint constraints
|
|
307
313
|
* based on schema properties.
|
|
308
314
|
*/
|
|
309
|
-
bigint?: (
|
|
315
|
+
bigint?: (
|
|
316
|
+
schema: OpenApi.IJsonSchema.IInteger & Record<string, any>,
|
|
317
|
+
) => bigint;
|
|
310
318
|
|
|
311
319
|
/**
|
|
312
320
|
* Custom boolean generator that can handle special boolean constraints
|
|
@@ -321,7 +329,7 @@ export namespace IRandomGenerator {
|
|
|
321
329
|
array?: <T>(
|
|
322
330
|
schema: Omit<OpenApi.IJsonSchema.IArray, "items"> & {
|
|
323
331
|
element: (index: number, count: number) => T;
|
|
324
|
-
} & Record<string, any
|
|
332
|
+
} & Record<string, any>,
|
|
325
333
|
) => T[];
|
|
326
334
|
}
|
|
327
335
|
}
|
|
@@ -263,7 +263,7 @@ export namespace JsonStringifyProgrammer {
|
|
|
263
263
|
if (props.metadata.templates.length)
|
|
264
264
|
if (AtomicPredicator.template(props.metadata)) {
|
|
265
265
|
const partial = Metadata.initialize();
|
|
266
|
-
partial.atomics.push(
|
|
266
|
+
(partial.atomics.push(
|
|
267
267
|
MetadataAtomic.create({ type: "string", tags: [] }),
|
|
268
268
|
),
|
|
269
269
|
unions.push({
|
|
@@ -278,7 +278,7 @@ export namespace JsonStringifyProgrammer {
|
|
|
278
278
|
...props,
|
|
279
279
|
type: "string",
|
|
280
280
|
}),
|
|
281
|
-
});
|
|
281
|
+
}));
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
// CONSTANTS
|
package/src/tags/Constant.ts
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Constant tag that adds title and description metadata to literal types.
|
|
5
|
+
*
|
|
6
|
+
* This tag enhances literal values with human-readable documentation without
|
|
7
|
+
* changing their runtime type. It's particularly useful for enum-like values
|
|
8
|
+
* where you want to provide context about what each value represents.
|
|
9
|
+
*
|
|
10
|
+
* The metadata appears in generated JSON Schema, making API documentation
|
|
11
|
+
* more descriptive and helping developers understand the meaning of specific
|
|
12
|
+
* constant values.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* type Status =
|
|
17
|
+
* | Constant<"active", { title: "Active", description: "User is currently active" }>
|
|
18
|
+
* | Constant<"inactive", { title: "Inactive", description: "User account is disabled" }>
|
|
19
|
+
* | Constant<"pending", { title: "Pending", description: "Awaiting verification" }>;
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* interface Config {
|
|
25
|
+
* debugLevel: Constant<0, { title: "Off", description: "No debug output" }>
|
|
26
|
+
* | Constant<1, { title: "Basic", description: "Basic logging" }>
|
|
27
|
+
* | Constant<2, { title: "Verbose", description: "Detailed debug info" }>;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @template Value The literal value (boolean, number, string, or bigint)
|
|
32
|
+
* @template Content Metadata with optional title and description
|
|
33
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
34
|
+
*/
|
|
3
35
|
export type Constant<
|
|
4
36
|
Value extends boolean | number | string | bigint,
|
|
5
37
|
Content extends {
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* String content media type constraint tag.
|
|
5
|
+
*
|
|
6
|
+
* Specifies the MIME type of string content for proper interpretation.
|
|
7
|
+
* This tag serves as metadata to indicate how the string content should be
|
|
8
|
+
* interpreted, but does not perform validation on the content itself.
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* type JsonData = string & ContentMediaType<"application/json">; // JSON string
|
|
12
|
+
* type Base64Image = string & ContentMediaType<"image/png">; // Base64 PNG
|
|
13
|
+
* type XmlContent = string & ContentMediaType<"application/xml">; // XML data
|
|
14
|
+
*
|
|
15
|
+
* Common MIME types: application/json, text/html, image/jpeg, image/png,
|
|
16
|
+
* application/pdf, text/plain, application/xml, and many more.
|
|
17
|
+
*
|
|
18
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
19
|
+
*/
|
|
3
20
|
export type ContentMediaType<Value extends string> = TagBase<{
|
|
4
21
|
target: "string";
|
|
5
22
|
kind: "contentMediaType";
|
package/src/tags/Default.ts
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Default value tag that specifies a default value for properties.
|
|
5
|
+
*
|
|
6
|
+
* When a property is not provided during validation, this tag instructs
|
|
7
|
+
* typia to use the specified default value. This is useful for optional
|
|
8
|
+
* properties that should have a specific fallback value.
|
|
9
|
+
*
|
|
10
|
+
* Supported types: boolean, bigint, number, and string primitives.
|
|
11
|
+
* Note: Default values are applied during validation, not at compile time.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* interface User {
|
|
16
|
+
* name: string;
|
|
17
|
+
* active: boolean & Default<true>; // defaults to true if not provided
|
|
18
|
+
* retries: number & Default<3>; // defaults to 3 if not provided
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* interface Config {
|
|
25
|
+
* timeout: bigint & Default<5000n>; // defaults to 5000n
|
|
26
|
+
* prefix: string & Default<"user_">; // defaults to "user_"
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @template Value The default value (boolean, bigint, number, or string)
|
|
31
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
32
|
+
*/
|
|
3
33
|
export type Default<Value extends boolean | bigint | number | string> =
|
|
4
34
|
TagBase<{
|
|
5
35
|
target: Value extends boolean
|
package/src/tags/Example.ts
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Example value tag that provides a single example value for documentation.
|
|
5
|
+
*
|
|
6
|
+
* This tag adds an example value to your JSON Schema, which is useful for
|
|
7
|
+
* API documentation, client code generation, and helping developers understand
|
|
8
|
+
* expected data formats. The example doesn't affect runtime validation.
|
|
9
|
+
*
|
|
10
|
+
* Use Example for a single representative value. For multiple named examples,
|
|
11
|
+
* use the Examples tag instead.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* interface Product {
|
|
16
|
+
* name: string & Example<"iPhone 15 Pro">;
|
|
17
|
+
* price: number & Example<999.99>;
|
|
18
|
+
* inStock: boolean & Example<true>;
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* interface User {
|
|
25
|
+
* profile: {
|
|
26
|
+
* bio: string;
|
|
27
|
+
* tags: string[];
|
|
28
|
+
* } & Example<{ bio: "Software engineer", tags: ["typescript", "react"] }>;
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @template Value The example value (primitives, objects, arrays, or null)
|
|
33
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
34
|
+
*/
|
|
3
35
|
export type Example<
|
|
4
36
|
Value extends
|
|
5
37
|
| boolean
|
package/src/tags/Examples.ts
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Multiple examples tag that provides named example values for documentation.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the Example tag which provides a single example, Examples allows you
|
|
7
|
+
* to provide multiple labeled examples. This is particularly useful when you
|
|
8
|
+
* want to show different scenarios or use cases for the same type.
|
|
9
|
+
*
|
|
10
|
+
* The examples are added to JSON Schema as an object where keys are descriptive
|
|
11
|
+
* names and values are the example data. This helps in API documentation and
|
|
12
|
+
* test case generation.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* interface PaymentRequest {
|
|
17
|
+
* amount: number & Examples<{
|
|
18
|
+
* small: 10.50,
|
|
19
|
+
* medium: 99.99,
|
|
20
|
+
* large: 1000.00
|
|
21
|
+
* }>;
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* interface UserStatus {
|
|
28
|
+
* state: string & Examples<{
|
|
29
|
+
* active: "active",
|
|
30
|
+
* inactive: "inactive",
|
|
31
|
+
* suspended: "suspended"
|
|
32
|
+
* }>;
|
|
33
|
+
* profile: object & Examples<{
|
|
34
|
+
* basic: { name: "John", age: 25 },
|
|
35
|
+
* detailed: { name: "Jane", age: 30, bio: "Developer", verified: true }
|
|
36
|
+
* }>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @template Value A record object mapping example names to example values
|
|
41
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
42
|
+
*/
|
|
3
43
|
export type Examples<
|
|
4
44
|
Value extends Record<
|
|
5
45
|
string,
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Exclusive maximum value constraint tag.
|
|
5
|
+
*
|
|
6
|
+
* Enforces that a numeric value must be strictly less than the specified maximum (not equal).
|
|
7
|
+
* This constraint validates that the input value satisfies: input < maximum.
|
|
8
|
+
*
|
|
9
|
+
* Example usage:
|
|
10
|
+
* ```typescript
|
|
11
|
+
* type Discount = number & tags.ExclusiveMaximum<100>; // Must be < 100
|
|
12
|
+
* type ByteValue = bigint & tags.ExclusiveMaximum<256n>; // Must be < 256
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Note: This tag is mutually exclusive with Maximum. You cannot apply both
|
|
16
|
+
* ExclusiveMaximum and Maximum constraints to the same property.
|
|
17
|
+
*
|
|
18
|
+
* @template Value - The exclusive maximum value constraint (number or bigint literal)
|
|
19
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
20
|
+
*/
|
|
3
21
|
export type ExclusiveMaximum<Value extends number | bigint> = TagBase<{
|
|
4
22
|
target: Value extends bigint ? "bigint" : "number";
|
|
5
23
|
kind: "exclusiveMaximum";
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { TagBase } from "./TagBase";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Exclusive minimum value constraint tag.
|
|
5
|
+
*
|
|
6
|
+
* Enforces that a numeric value must be strictly greater than the specified minimum (not equal).
|
|
7
|
+
* This constraint validates that the input value satisfies: input > minimum.
|
|
8
|
+
*
|
|
9
|
+
* Example usage:
|
|
10
|
+
* ```typescript
|
|
11
|
+
* type PositiveNumber = number & tags.ExclusiveMinimum<0>; // Must be > 0
|
|
12
|
+
* type LargeCount = bigint & tags.ExclusiveMinimum<999n>; // Must be > 999
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Note: This tag is mutually exclusive with Minimum. You cannot apply both
|
|
16
|
+
* ExclusiveMinimum and Minimum constraints to the same property.
|
|
17
|
+
*
|
|
18
|
+
* @template Value - The exclusive minimum value constraint (number or bigint literal)
|
|
19
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
20
|
+
*/
|
|
3
21
|
export type ExclusiveMinimum<Value extends number | bigint> = TagBase<{
|
|
4
22
|
target: Value extends bigint ? "bigint" : "number";
|
|
5
23
|
kind: "exclusiveMinimum";
|