zodrift 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +50 -0
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.js +52 -0
- package/dist/commands/codegen.d.ts +1 -0
- package/dist/commands/codegen.js +63 -0
- package/dist/commands/fix.d.ts +1 -0
- package/dist/commands/fix.js +113 -0
- package/dist/commands/forms.d.ts +1 -0
- package/dist/commands/forms.js +41 -0
- package/dist/commands/openapi.d.ts +1 -0
- package/dist/commands/openapi.js +49 -0
- package/dist/core/checker.d.ts +2 -0
- package/dist/core/checker.js +39 -0
- package/dist/core/compare.d.ts +2 -0
- package/dist/core/compare.js +190 -0
- package/dist/core/emit.d.ts +10 -0
- package/dist/core/emit.js +229 -0
- package/dist/core/file-discovery.d.ts +5 -0
- package/dist/core/file-discovery.js +37 -0
- package/dist/core/pairing.d.ts +6 -0
- package/dist/core/pairing.js +27 -0
- package/dist/core/ts-parser.d.ts +5 -0
- package/dist/core/ts-parser.js +239 -0
- package/dist/core/type-utils.d.ts +16 -0
- package/dist/core/type-utils.js +139 -0
- package/dist/core/types.d.ts +133 -0
- package/dist/core/types.js +1 -0
- package/dist/core/zod-parser.d.ts +5 -0
- package/dist/core/zod-parser.js +321 -0
- package/dist/reporters/index.d.ts +2 -0
- package/dist/reporters/index.js +14 -0
- package/dist/reporters/json.d.ts +2 -0
- package/dist/reporters/json.js +18 -0
- package/dist/reporters/pretty.d.ts +2 -0
- package/dist/reporters/pretty.js +35 -0
- package/dist/reporters/sarif.d.ts +2 -0
- package/dist/reporters/sarif.js +85 -0
- package/dist/utils/args.d.ts +9 -0
- package/dist/utils/args.js +55 -0
- package/package.json +44 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import { cloneTypeNode, locationFromTs, makePrimitive, stripUndefinedFromUnion, unwrapOptional, } from "./type-utils.js";
|
|
4
|
+
function isExported(node) {
|
|
5
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
6
|
+
return !!modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
7
|
+
}
|
|
8
|
+
function getLocationFromNode(sourceFile, node) {
|
|
9
|
+
const pos = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
10
|
+
return locationFromTs(path.resolve(sourceFile.fileName), pos.line, pos.character);
|
|
11
|
+
}
|
|
12
|
+
function literalFromTypeNode(node) {
|
|
13
|
+
if (node.literal.kind === ts.SyntaxKind.NullKeyword) {
|
|
14
|
+
return { kind: "literal", value: null };
|
|
15
|
+
}
|
|
16
|
+
if (ts.isStringLiteral(node.literal)) {
|
|
17
|
+
return { kind: "literal", value: node.literal.text };
|
|
18
|
+
}
|
|
19
|
+
if (ts.isNumericLiteral(node.literal)) {
|
|
20
|
+
return { kind: "literal", value: Number(node.literal.text) };
|
|
21
|
+
}
|
|
22
|
+
if (node.literal.kind === ts.SyntaxKind.TrueKeyword) {
|
|
23
|
+
return { kind: "literal", value: true };
|
|
24
|
+
}
|
|
25
|
+
if (node.literal.kind === ts.SyntaxKind.FalseKeyword) {
|
|
26
|
+
return { kind: "literal", value: false };
|
|
27
|
+
}
|
|
28
|
+
return makePrimitive("unknown");
|
|
29
|
+
}
|
|
30
|
+
function parseProperty(member, sourceFile, context, visiting) {
|
|
31
|
+
if (!ts.isPropertySignature(member) || !member.type || !member.name) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const propName = ts.isIdentifier(member.name) || ts.isStringLiteral(member.name)
|
|
35
|
+
? member.name.text
|
|
36
|
+
: ts.isNumericLiteral(member.name)
|
|
37
|
+
? member.name.text
|
|
38
|
+
: member.name.getText(sourceFile);
|
|
39
|
+
let parsedType = parseTypeNode(member.type, sourceFile, context, visiting);
|
|
40
|
+
let optional = Boolean(member.questionToken);
|
|
41
|
+
if (parsedType.kind === "union") {
|
|
42
|
+
const stripped = stripUndefinedFromUnion(parsedType);
|
|
43
|
+
optional = optional || stripped.optional;
|
|
44
|
+
parsedType = stripped.node;
|
|
45
|
+
}
|
|
46
|
+
const unwrapped = unwrapOptional(parsedType);
|
|
47
|
+
optional = optional || unwrapped.optional;
|
|
48
|
+
parsedType = unwrapped.node;
|
|
49
|
+
return {
|
|
50
|
+
name: propName,
|
|
51
|
+
optional,
|
|
52
|
+
type: parsedType,
|
|
53
|
+
location: getLocationFromNode(sourceFile, member.name),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseObjectMembers(members, sourceFile, context, visiting) {
|
|
57
|
+
const properties = {};
|
|
58
|
+
for (const member of members) {
|
|
59
|
+
const parsed = parseProperty(member, sourceFile, context, visiting);
|
|
60
|
+
if (!parsed) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
properties[parsed.name] = parsed;
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
kind: "object",
|
|
67
|
+
properties,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function parseTypeReference(node, sourceFile, context, visiting) {
|
|
71
|
+
const name = node.typeName.getText(sourceFile);
|
|
72
|
+
const args = node.typeArguments ?? [];
|
|
73
|
+
if ((name === "Array" || name === "ReadonlyArray") && args.length === 1) {
|
|
74
|
+
return {
|
|
75
|
+
kind: "array",
|
|
76
|
+
element: parseTypeNode(args[0], sourceFile, context, visiting),
|
|
77
|
+
location: getLocationFromNode(sourceFile, node),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (context.declarations.has(name)) {
|
|
81
|
+
return parseNamedDeclaration(name, sourceFile, context, visiting);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
kind: "reference",
|
|
85
|
+
name,
|
|
86
|
+
location: getLocationFromNode(sourceFile, node),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function parseTypeNode(node, sourceFile, context, visiting) {
|
|
90
|
+
switch (node.kind) {
|
|
91
|
+
case ts.SyntaxKind.StringKeyword:
|
|
92
|
+
return { kind: "primitive", name: "string", location: getLocationFromNode(sourceFile, node) };
|
|
93
|
+
case ts.SyntaxKind.NumberKeyword:
|
|
94
|
+
return { kind: "primitive", name: "number", location: getLocationFromNode(sourceFile, node) };
|
|
95
|
+
case ts.SyntaxKind.BooleanKeyword:
|
|
96
|
+
return { kind: "primitive", name: "boolean", location: getLocationFromNode(sourceFile, node) };
|
|
97
|
+
case ts.SyntaxKind.AnyKeyword:
|
|
98
|
+
return { kind: "primitive", name: "any", location: getLocationFromNode(sourceFile, node) };
|
|
99
|
+
case ts.SyntaxKind.UnknownKeyword:
|
|
100
|
+
return { kind: "primitive", name: "unknown", location: getLocationFromNode(sourceFile, node) };
|
|
101
|
+
case ts.SyntaxKind.NullKeyword:
|
|
102
|
+
return { kind: "primitive", name: "null", location: getLocationFromNode(sourceFile, node) };
|
|
103
|
+
case ts.SyntaxKind.UndefinedKeyword:
|
|
104
|
+
return { kind: "primitive", name: "undefined", location: getLocationFromNode(sourceFile, node) };
|
|
105
|
+
default:
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
if (ts.isParenthesizedTypeNode(node)) {
|
|
109
|
+
return parseTypeNode(node.type, sourceFile, context, visiting);
|
|
110
|
+
}
|
|
111
|
+
if (ts.isLiteralTypeNode(node)) {
|
|
112
|
+
const literal = literalFromTypeNode(node);
|
|
113
|
+
return {
|
|
114
|
+
...literal,
|
|
115
|
+
location: getLocationFromNode(sourceFile, node),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (ts.isArrayTypeNode(node)) {
|
|
119
|
+
return {
|
|
120
|
+
kind: "array",
|
|
121
|
+
element: parseTypeNode(node.elementType, sourceFile, context, visiting),
|
|
122
|
+
location: getLocationFromNode(sourceFile, node),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (ts.isTupleTypeNode(node)) {
|
|
126
|
+
return {
|
|
127
|
+
kind: "tuple",
|
|
128
|
+
items: node.elements.map((element) => parseTypeNode(element, sourceFile, context, visiting)),
|
|
129
|
+
location: getLocationFromNode(sourceFile, node),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (ts.isUnionTypeNode(node)) {
|
|
133
|
+
return {
|
|
134
|
+
kind: "union",
|
|
135
|
+
members: node.types.map((item) => parseTypeNode(item, sourceFile, context, visiting)),
|
|
136
|
+
location: getLocationFromNode(sourceFile, node),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (ts.isTypeLiteralNode(node)) {
|
|
140
|
+
return {
|
|
141
|
+
...parseObjectMembers(node.members, sourceFile, context, visiting),
|
|
142
|
+
location: getLocationFromNode(sourceFile, node),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
146
|
+
return parseTypeReference(node, sourceFile, context, visiting);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
kind: "primitive",
|
|
150
|
+
name: "unknown",
|
|
151
|
+
location: getLocationFromNode(sourceFile, node),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function parseNamedDeclaration(name, sourceFile, context, visiting) {
|
|
155
|
+
const cached = context.cache.get(name);
|
|
156
|
+
if (cached) {
|
|
157
|
+
return cloneTypeNode(cached);
|
|
158
|
+
}
|
|
159
|
+
if (visiting.has(name)) {
|
|
160
|
+
return {
|
|
161
|
+
kind: "reference",
|
|
162
|
+
name,
|
|
163
|
+
location: getLocationFromNode(sourceFile, sourceFile),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const declaration = context.declarations.get(name);
|
|
167
|
+
if (!declaration) {
|
|
168
|
+
return {
|
|
169
|
+
kind: "reference",
|
|
170
|
+
name,
|
|
171
|
+
location: getLocationFromNode(sourceFile, sourceFile),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
visiting.add(name);
|
|
175
|
+
let parsed;
|
|
176
|
+
if (ts.isInterfaceDeclaration(declaration.node)) {
|
|
177
|
+
parsed = {
|
|
178
|
+
...parseObjectMembers(declaration.node.members, declaration.sourceFile, context, visiting),
|
|
179
|
+
location: getLocationFromNode(declaration.sourceFile, declaration.node),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
parsed = parseTypeNode(declaration.node.type, declaration.sourceFile, context, visiting);
|
|
184
|
+
}
|
|
185
|
+
visiting.delete(name);
|
|
186
|
+
context.cache.set(name, parsed);
|
|
187
|
+
return cloneTypeNode(parsed);
|
|
188
|
+
}
|
|
189
|
+
export function parseTypeDeclarations(filePaths) {
|
|
190
|
+
const errors = [];
|
|
191
|
+
if (filePaths.length === 0) {
|
|
192
|
+
return { declarations: [], errors };
|
|
193
|
+
}
|
|
194
|
+
const uniqueFilePaths = Array.from(new Set(filePaths.map((item) => path.resolve(item))));
|
|
195
|
+
const pathSet = new Set(uniqueFilePaths);
|
|
196
|
+
const program = ts.createProgram(uniqueFilePaths, {
|
|
197
|
+
target: ts.ScriptTarget.ES2022,
|
|
198
|
+
module: ts.ModuleKind.NodeNext,
|
|
199
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
200
|
+
allowJs: false,
|
|
201
|
+
skipLibCheck: true,
|
|
202
|
+
});
|
|
203
|
+
const declarationsMap = new Map();
|
|
204
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
205
|
+
const sourcePath = path.resolve(sourceFile.fileName);
|
|
206
|
+
if (!pathSet.has(sourcePath)) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
for (const statement of sourceFile.statements) {
|
|
210
|
+
if ((ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) &&
|
|
211
|
+
isExported(statement)) {
|
|
212
|
+
declarationsMap.set(statement.name.text, {
|
|
213
|
+
node: statement,
|
|
214
|
+
sourceFile,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const context = {
|
|
220
|
+
declarations: declarationsMap,
|
|
221
|
+
cache: new Map(),
|
|
222
|
+
};
|
|
223
|
+
const declarations = [];
|
|
224
|
+
for (const [name, record] of declarationsMap.entries()) {
|
|
225
|
+
try {
|
|
226
|
+
const node = parseNamedDeclaration(name, record.sourceFile, context, new Set());
|
|
227
|
+
declarations.push({
|
|
228
|
+
name,
|
|
229
|
+
node,
|
|
230
|
+
location: getLocationFromNode(record.sourceFile, record.node),
|
|
231
|
+
sourceFilePath: path.resolve(record.sourceFile.fileName),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
errors.push(`Failed to parse type declaration ${name} in ${record.sourceFile.fileName}: ${String(error)}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { declarations, errors };
|
|
239
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ObjectProperty, PrimitiveTypeName, SourceLocation, TypeNode, UnionNode } from "./types.js";
|
|
2
|
+
export declare function normalizePath(base: string, segment: string): string;
|
|
3
|
+
export declare function locationFromTs(sourceFilePath: string, line: number, character: number): SourceLocation;
|
|
4
|
+
export declare function makePrimitive(name: PrimitiveTypeName): TypeNode;
|
|
5
|
+
export declare function unwrapOptional(node: TypeNode): {
|
|
6
|
+
node: TypeNode;
|
|
7
|
+
optional: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function stringifyType(node: TypeNode): string;
|
|
10
|
+
export declare function canonicalSignature(node: TypeNode): string;
|
|
11
|
+
export declare function stripUndefinedFromUnion(node: UnionNode): {
|
|
12
|
+
optional: boolean;
|
|
13
|
+
node: TypeNode;
|
|
14
|
+
};
|
|
15
|
+
export declare function cloneProperty(property: ObjectProperty): ObjectProperty;
|
|
16
|
+
export declare function cloneTypeNode(node: TypeNode): TypeNode;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
export function normalizePath(base, segment) {
|
|
2
|
+
return base ? `${base}.${segment}` : segment;
|
|
3
|
+
}
|
|
4
|
+
export function locationFromTs(sourceFilePath, line, character) {
|
|
5
|
+
return {
|
|
6
|
+
filePath: sourceFilePath,
|
|
7
|
+
line: line + 1,
|
|
8
|
+
column: character + 1,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function makePrimitive(name) {
|
|
12
|
+
return { kind: "primitive", name };
|
|
13
|
+
}
|
|
14
|
+
export function unwrapOptional(node) {
|
|
15
|
+
if (node.kind === "optional") {
|
|
16
|
+
return { node: node.inner, optional: true };
|
|
17
|
+
}
|
|
18
|
+
return { node, optional: false };
|
|
19
|
+
}
|
|
20
|
+
export function stringifyType(node) {
|
|
21
|
+
switch (node.kind) {
|
|
22
|
+
case "primitive":
|
|
23
|
+
return node.name;
|
|
24
|
+
case "literal":
|
|
25
|
+
if (typeof node.value === "string") {
|
|
26
|
+
return `"${node.value}"`;
|
|
27
|
+
}
|
|
28
|
+
return String(node.value);
|
|
29
|
+
case "array":
|
|
30
|
+
return `${stringifyType(node.element)}[]`;
|
|
31
|
+
case "tuple":
|
|
32
|
+
return `[${node.items.map((item) => stringifyType(item)).join(", ")}]`;
|
|
33
|
+
case "union":
|
|
34
|
+
return node.members.map((member) => stringifyType(member)).join(" | ");
|
|
35
|
+
case "object":
|
|
36
|
+
return "object";
|
|
37
|
+
case "reference":
|
|
38
|
+
return node.name;
|
|
39
|
+
case "optional":
|
|
40
|
+
return `${stringifyType(node.inner)} | undefined`;
|
|
41
|
+
default:
|
|
42
|
+
return "unknown";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function canonicalSignature(node) {
|
|
46
|
+
switch (node.kind) {
|
|
47
|
+
case "primitive":
|
|
48
|
+
return `p:${node.name}`;
|
|
49
|
+
case "literal":
|
|
50
|
+
return `l:${JSON.stringify(node.value)}`;
|
|
51
|
+
case "reference":
|
|
52
|
+
return `r:${node.name}`;
|
|
53
|
+
case "array":
|
|
54
|
+
return `a:${canonicalSignature(node.element)}`;
|
|
55
|
+
case "tuple":
|
|
56
|
+
return `t:[${node.items.map((item) => canonicalSignature(item)).join(",")}]`;
|
|
57
|
+
case "optional":
|
|
58
|
+
return `o:${canonicalSignature(node.inner)}`;
|
|
59
|
+
case "object": {
|
|
60
|
+
const sorted = Object.values(node.properties)
|
|
61
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
62
|
+
.map((property) => `${property.name}${property.optional ? "?" : ""}:${canonicalSignature(property.type)}`)
|
|
63
|
+
.join(";");
|
|
64
|
+
return `obj:{${sorted}}`;
|
|
65
|
+
}
|
|
66
|
+
case "union": {
|
|
67
|
+
const members = node.members.map((member) => canonicalSignature(member)).sort();
|
|
68
|
+
return `u:${members.join("|")}`;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
return "unknown";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function stripUndefinedFromUnion(node) {
|
|
75
|
+
const nonUndefined = node.members.filter((member) => !(member.kind === "primitive" && member.name === "undefined"));
|
|
76
|
+
if (nonUndefined.length === node.members.length) {
|
|
77
|
+
return { optional: false, node };
|
|
78
|
+
}
|
|
79
|
+
if (nonUndefined.length === 0) {
|
|
80
|
+
return { optional: true, node: makePrimitive("undefined") };
|
|
81
|
+
}
|
|
82
|
+
if (nonUndefined.length === 1) {
|
|
83
|
+
return { optional: true, node: nonUndefined[0] };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
optional: true,
|
|
87
|
+
node: {
|
|
88
|
+
kind: "union",
|
|
89
|
+
members: nonUndefined,
|
|
90
|
+
location: node.location,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export function cloneProperty(property) {
|
|
95
|
+
return {
|
|
96
|
+
...property,
|
|
97
|
+
type: cloneTypeNode(property.type),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function cloneTypeNode(node) {
|
|
101
|
+
switch (node.kind) {
|
|
102
|
+
case "primitive":
|
|
103
|
+
case "literal":
|
|
104
|
+
case "reference":
|
|
105
|
+
return { ...node };
|
|
106
|
+
case "optional":
|
|
107
|
+
return {
|
|
108
|
+
...node,
|
|
109
|
+
inner: cloneTypeNode(node.inner),
|
|
110
|
+
};
|
|
111
|
+
case "array":
|
|
112
|
+
return {
|
|
113
|
+
...node,
|
|
114
|
+
element: cloneTypeNode(node.element),
|
|
115
|
+
};
|
|
116
|
+
case "tuple":
|
|
117
|
+
return {
|
|
118
|
+
...node,
|
|
119
|
+
items: node.items.map((item) => cloneTypeNode(item)),
|
|
120
|
+
};
|
|
121
|
+
case "union":
|
|
122
|
+
return {
|
|
123
|
+
...node,
|
|
124
|
+
members: node.members.map((member) => cloneTypeNode(member)),
|
|
125
|
+
};
|
|
126
|
+
case "object": {
|
|
127
|
+
const properties = {};
|
|
128
|
+
for (const [name, property] of Object.entries(node.properties)) {
|
|
129
|
+
properties[name] = cloneProperty(property);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...node,
|
|
133
|
+
properties,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
default:
|
|
137
|
+
return node;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export type PrimitiveTypeName = "string" | "number" | "boolean" | "any" | "unknown" | "null" | "undefined";
|
|
2
|
+
export type NodeKind = "primitive" | "literal" | "object" | "array" | "tuple" | "union" | "reference" | "optional";
|
|
3
|
+
export interface SourceLocation {
|
|
4
|
+
filePath: string;
|
|
5
|
+
line: number;
|
|
6
|
+
column: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PrimitiveNode {
|
|
9
|
+
kind: "primitive";
|
|
10
|
+
name: PrimitiveTypeName;
|
|
11
|
+
location?: SourceLocation;
|
|
12
|
+
}
|
|
13
|
+
export interface LiteralNode {
|
|
14
|
+
kind: "literal";
|
|
15
|
+
value: string | number | boolean | null;
|
|
16
|
+
location?: SourceLocation;
|
|
17
|
+
}
|
|
18
|
+
export interface ObjectProperty {
|
|
19
|
+
name: string;
|
|
20
|
+
type: TypeNode;
|
|
21
|
+
optional: boolean;
|
|
22
|
+
location?: SourceLocation;
|
|
23
|
+
}
|
|
24
|
+
export interface ObjectNode {
|
|
25
|
+
kind: "object";
|
|
26
|
+
properties: Record<string, ObjectProperty>;
|
|
27
|
+
location?: SourceLocation;
|
|
28
|
+
}
|
|
29
|
+
export interface ArrayNode {
|
|
30
|
+
kind: "array";
|
|
31
|
+
element: TypeNode;
|
|
32
|
+
location?: SourceLocation;
|
|
33
|
+
}
|
|
34
|
+
export interface TupleNode {
|
|
35
|
+
kind: "tuple";
|
|
36
|
+
items: TypeNode[];
|
|
37
|
+
location?: SourceLocation;
|
|
38
|
+
}
|
|
39
|
+
export interface UnionNode {
|
|
40
|
+
kind: "union";
|
|
41
|
+
members: TypeNode[];
|
|
42
|
+
location?: SourceLocation;
|
|
43
|
+
}
|
|
44
|
+
export interface ReferenceNode {
|
|
45
|
+
kind: "reference";
|
|
46
|
+
name: string;
|
|
47
|
+
location?: SourceLocation;
|
|
48
|
+
}
|
|
49
|
+
export interface OptionalNode {
|
|
50
|
+
kind: "optional";
|
|
51
|
+
inner: TypeNode;
|
|
52
|
+
location?: SourceLocation;
|
|
53
|
+
}
|
|
54
|
+
export type TypeNode = PrimitiveNode | LiteralNode | ObjectNode | ArrayNode | TupleNode | UnionNode | ReferenceNode | OptionalNode;
|
|
55
|
+
export interface TypeDeclaration {
|
|
56
|
+
name: string;
|
|
57
|
+
node: TypeNode;
|
|
58
|
+
location: SourceLocation;
|
|
59
|
+
sourceFilePath: string;
|
|
60
|
+
}
|
|
61
|
+
export interface SchemaDeclaration {
|
|
62
|
+
name: string;
|
|
63
|
+
node: TypeNode;
|
|
64
|
+
location: SourceLocation;
|
|
65
|
+
sourceFilePath: string;
|
|
66
|
+
objectNodeText?: string;
|
|
67
|
+
}
|
|
68
|
+
export type DriftIssueKind = "missing_in_schema" | "extra_in_schema" | "optional_mismatch" | "type_mismatch";
|
|
69
|
+
export interface DriftIssue {
|
|
70
|
+
kind: DriftIssueKind;
|
|
71
|
+
pairName: string;
|
|
72
|
+
path: string;
|
|
73
|
+
message: string;
|
|
74
|
+
typeValue?: string;
|
|
75
|
+
schemaValue?: string;
|
|
76
|
+
typeLocation?: SourceLocation;
|
|
77
|
+
schemaLocation?: SourceLocation;
|
|
78
|
+
}
|
|
79
|
+
export interface DriftPairResult {
|
|
80
|
+
typeName: string;
|
|
81
|
+
schemaName: string;
|
|
82
|
+
typeDecl: TypeDeclaration;
|
|
83
|
+
schemaDecl: SchemaDeclaration;
|
|
84
|
+
issues: DriftIssue[];
|
|
85
|
+
}
|
|
86
|
+
export interface CheckResult {
|
|
87
|
+
pairs: DriftPairResult[];
|
|
88
|
+
totalIssues: number;
|
|
89
|
+
checkedPairs: number;
|
|
90
|
+
unmatchedTypes: string[];
|
|
91
|
+
unmatchedSchemas: string[];
|
|
92
|
+
errors: string[];
|
|
93
|
+
}
|
|
94
|
+
export interface Pairing {
|
|
95
|
+
typeDecl: TypeDeclaration;
|
|
96
|
+
schemaDecl: SchemaDeclaration;
|
|
97
|
+
}
|
|
98
|
+
export interface ReporterPayload {
|
|
99
|
+
result: CheckResult;
|
|
100
|
+
cwd: string;
|
|
101
|
+
}
|
|
102
|
+
export type OutputFormat = "pretty" | "json" | "sarif";
|
|
103
|
+
export interface CheckOptions {
|
|
104
|
+
cwd: string;
|
|
105
|
+
pattern: string;
|
|
106
|
+
format: OutputFormat;
|
|
107
|
+
maxIssues?: number;
|
|
108
|
+
changedOnly: boolean;
|
|
109
|
+
}
|
|
110
|
+
export interface FixOptions {
|
|
111
|
+
cwd: string;
|
|
112
|
+
pattern: string;
|
|
113
|
+
write: boolean;
|
|
114
|
+
dryRun: boolean;
|
|
115
|
+
target: "schema" | "type";
|
|
116
|
+
}
|
|
117
|
+
export interface CodegenOptions {
|
|
118
|
+
cwd: string;
|
|
119
|
+
pattern: string;
|
|
120
|
+
from: "ts" | "zod";
|
|
121
|
+
write: boolean;
|
|
122
|
+
outDir: string;
|
|
123
|
+
}
|
|
124
|
+
export interface OpenApiOptions {
|
|
125
|
+
cwd: string;
|
|
126
|
+
pattern: string;
|
|
127
|
+
outFile?: string;
|
|
128
|
+
}
|
|
129
|
+
export interface FormsOptions {
|
|
130
|
+
cwd: string;
|
|
131
|
+
pattern: string;
|
|
132
|
+
outFile?: string;
|
|
133
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|