@quonfig/cli 0.0.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/LICENSE +21 -0
- package/README.md +707 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +9 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +8 -0
- package/dist/api/get-environments.d.ts +10 -0
- package/dist/api/get-environments.js +11 -0
- package/dist/codegen/code-generators/base-generator.d.ts +11 -0
- package/dist/codegen/code-generators/base-generator.js +8 -0
- package/dist/codegen/code-generators/base-typescript-generator.d.ts +17 -0
- package/dist/codegen/code-generators/base-typescript-generator.js +35 -0
- package/dist/codegen/code-generators/node-typescript-generator.d.ts +8 -0
- package/dist/codegen/code-generators/node-typescript-generator.js +96 -0
- package/dist/codegen/code-generators/react-typescript-generator.d.ts +11 -0
- package/dist/codegen/code-generators/react-typescript-generator.js +116 -0
- package/dist/codegen/config-downloader.d.ts +7 -0
- package/dist/codegen/config-downloader.js +26 -0
- package/dist/codegen/json-schema-to-zod.d.ts +3 -0
- package/dist/codegen/json-schema-to-zod.js +170 -0
- package/dist/codegen/language-mappers/json-to-zod-mapper.d.ts +4 -0
- package/dist/codegen/language-mappers/json-to-zod-mapper.js +55 -0
- package/dist/codegen/language-mappers/zod-base-mapper.d.ts +35 -0
- package/dist/codegen/language-mappers/zod-base-mapper.js +111 -0
- package/dist/codegen/language-mappers/zod-to-string-mapper.d.ts +25 -0
- package/dist/codegen/language-mappers/zod-to-string-mapper.js +87 -0
- package/dist/codegen/language-mappers/zod-to-typescript-mapper.d.ts +35 -0
- package/dist/codegen/language-mappers/zod-to-typescript-mapper.js +133 -0
- package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.d.ts +36 -0
- package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.js +150 -0
- package/dist/codegen/mustache-extractor.d.ts +6 -0
- package/dist/codegen/mustache-extractor.js +83 -0
- package/dist/codegen/schema-evaluator.d.ts +37 -0
- package/dist/codegen/schema-evaluator.js +283 -0
- package/dist/codegen/schema-extractor.d.ts +24 -0
- package/dist/codegen/schema-extractor.js +265 -0
- package/dist/codegen/types.d.ts +44 -0
- package/dist/codegen/types.js +5 -0
- package/dist/codegen/zod-introspection.d.ts +85 -0
- package/dist/codegen/zod-introspection.js +205 -0
- package/dist/commands/create.d.ts +19 -0
- package/dist/commands/create.js +175 -0
- package/dist/commands/download.d.ts +13 -0
- package/dist/commands/download.js +63 -0
- package/dist/commands/generate-new-hex-key.d.ts +7 -0
- package/dist/commands/generate-new-hex-key.js +10 -0
- package/dist/commands/generate.d.ts +16 -0
- package/dist/commands/generate.js +220 -0
- package/dist/commands/get.d.ts +13 -0
- package/dist/commands/get.js +141 -0
- package/dist/commands/info.d.ts +21 -0
- package/dist/commands/info.js +464 -0
- package/dist/commands/interactive.d.ts +5 -0
- package/dist/commands/interactive.js +8 -0
- package/dist/commands/list.d.ts +13 -0
- package/dist/commands/list.js +61 -0
- package/dist/commands/login.d.ts +10 -0
- package/dist/commands/login.js +124 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.js +12 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +145 -0
- package/dist/commands/override.d.ts +17 -0
- package/dist/commands/override.js +170 -0
- package/dist/commands/profile.d.ts +7 -0
- package/dist/commands/profile.js +47 -0
- package/dist/commands/schema.d.ts +21 -0
- package/dist/commands/schema.js +168 -0
- package/dist/commands/serve.d.ts +13 -0
- package/dist/commands/serve.js +99 -0
- package/dist/commands/set-default.d.ts +20 -0
- package/dist/commands/set-default.js +273 -0
- package/dist/commands/verify.d.ts +12 -0
- package/dist/commands/verify.js +48 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +52 -0
- package/dist/commands/workspace.d.ts +7 -0
- package/dist/commands/workspace.js +38 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/help.d.ts +4 -0
- package/dist/help.js +10 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +112 -0
- package/dist/interactive-prompt.d.ts +2 -0
- package/dist/interactive-prompt.js +162 -0
- package/dist/quonfig.d.ts +15 -0
- package/dist/quonfig.js +75 -0
- package/dist/result.d.ts +35 -0
- package/dist/result.js +9 -0
- package/dist/services/mcp.d.ts +15 -0
- package/dist/services/mcp.js +59 -0
- package/dist/ui/get-confirmation.d.ts +10 -0
- package/dist/ui/get-confirmation.js +4 -0
- package/dist/ui/get-environment.d.ts +11 -0
- package/dist/ui/get-environment.js +42 -0
- package/dist/ui/get-key.d.ts +27 -0
- package/dist/ui/get-key.js +15 -0
- package/dist/ui/get-string.d.ts +5 -0
- package/dist/ui/get-string.js +8 -0
- package/dist/ui/get-value.d.ts +18 -0
- package/dist/ui/get-value.js +46 -0
- package/dist/util/autocomplete.d.ts +6 -0
- package/dist/util/autocomplete.js +25 -0
- package/dist/util/coerce.d.ts +7 -0
- package/dist/util/coerce.js +62 -0
- package/dist/util/color.d.ts +3 -0
- package/dist/util/color.js +4 -0
- package/dist/util/config-value-dto.d.ts +16 -0
- package/dist/util/config-value-dto.js +82 -0
- package/dist/util/context.d.ts +18 -0
- package/dist/util/context.js +10 -0
- package/dist/util/domain-urls.d.ts +3 -0
- package/dist/util/domain-urls.js +18 -0
- package/dist/util/encryption.d.ts +20 -0
- package/dist/util/encryption.js +153 -0
- package/dist/util/file-manager.d.ts +12 -0
- package/dist/util/file-manager.js +16 -0
- package/dist/util/get-client.d.ts +7 -0
- package/dist/util/get-client.js +127 -0
- package/dist/util/is-interactive.d.ts +5 -0
- package/dist/util/is-interactive.js +7 -0
- package/dist/util/json-maybe.d.ts +3 -0
- package/dist/util/json-maybe.js +14 -0
- package/dist/util/name-arg.d.ts +4 -0
- package/dist/util/name-arg.js +3 -0
- package/dist/util/oauth-client.d.ts +30 -0
- package/dist/util/oauth-client.js +99 -0
- package/dist/util/secret-flags.d.ts +13 -0
- package/dist/util/secret-flags.js +13 -0
- package/dist/util/token-storage.d.ts +25 -0
- package/dist/util/token-storage.js +90 -0
- package/dist/validations/value.d.ts +4 -0
- package/dist/validations/value.js +11 -0
- package/dist/verify/standalone.d.ts +14 -0
- package/dist/verify/standalone.js +133 -0
- package/dist/verify/validate.d.ts +43 -0
- package/dist/verify/validate.js +756 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/oclif.manifest.json +1429 -0
- package/package.json +104 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as introspect from '../zod-introspection.js';
|
|
2
|
+
export class ZodBaseMapper {
|
|
3
|
+
resolveType(type) {
|
|
4
|
+
// Check for meta description first and allow implementers to handle it
|
|
5
|
+
const metaDescription = introspect.getMetaDescription(type);
|
|
6
|
+
if (metaDescription) {
|
|
7
|
+
const result = this.withMeta(metaDescription, () => this.resolveTypeInternal(type));
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
return this.resolveTypeInternal(type);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hook for handling meta descriptions on schemas.
|
|
14
|
+
* By default, this just returns the resolved type without any modification.
|
|
15
|
+
* Subclasses can override this to add comments or other metadata handling.
|
|
16
|
+
*
|
|
17
|
+
* @param description The description from .meta({ description: "..." })
|
|
18
|
+
* @param resolveType A function that resolves the inner type
|
|
19
|
+
* @returns The type string, potentially with description metadata
|
|
20
|
+
*/
|
|
21
|
+
withMeta(description, resolveType) {
|
|
22
|
+
// Default implementation: just return the resolved type
|
|
23
|
+
return resolveType();
|
|
24
|
+
}
|
|
25
|
+
resolveTypeInternal(type) {
|
|
26
|
+
if (introspect.isAny(type)) {
|
|
27
|
+
return this.any();
|
|
28
|
+
}
|
|
29
|
+
if (introspect.isArray(type)) {
|
|
30
|
+
const element = introspect.getArrayElement(type);
|
|
31
|
+
const internalType = this.resolveType(element);
|
|
32
|
+
return this.array(internalType);
|
|
33
|
+
}
|
|
34
|
+
if (introspect.isBoolean(type)) {
|
|
35
|
+
return this.boolean();
|
|
36
|
+
}
|
|
37
|
+
if (introspect.isDefault(type)) {
|
|
38
|
+
// Unwrap default to get the inner type
|
|
39
|
+
const { innerType } = introspect.unwrapDefault(type);
|
|
40
|
+
const internalType = this.resolveType(innerType);
|
|
41
|
+
return internalType;
|
|
42
|
+
}
|
|
43
|
+
if (introspect.isEnum(type)) {
|
|
44
|
+
const values = introspect.getEnumValues(type);
|
|
45
|
+
return this.enum(values);
|
|
46
|
+
}
|
|
47
|
+
if (introspect.isLiteral(type)) {
|
|
48
|
+
const value = introspect.getLiteralValue(type);
|
|
49
|
+
return this.literal(value);
|
|
50
|
+
}
|
|
51
|
+
if (introspect.isFunction(type)) {
|
|
52
|
+
const { args, returns } = introspect.getFunctionSignature(type);
|
|
53
|
+
const argsStr = this.functionArguments(args);
|
|
54
|
+
const returnsStr = this.functionReturns(returns);
|
|
55
|
+
return this.function(argsStr, returnsStr);
|
|
56
|
+
}
|
|
57
|
+
if (introspect.isNull(type)) {
|
|
58
|
+
return this.null();
|
|
59
|
+
}
|
|
60
|
+
if (introspect.isNullable(type)) {
|
|
61
|
+
// Handle nullable by unwrapping it
|
|
62
|
+
const innerType = introspect.unwrapNullable(type);
|
|
63
|
+
return this.union([this.resolveType(innerType), this.null()]);
|
|
64
|
+
}
|
|
65
|
+
if (introspect.isNumber(type)) {
|
|
66
|
+
const isInteger = introspect.isNumberInteger(type);
|
|
67
|
+
return this.number(isInteger);
|
|
68
|
+
}
|
|
69
|
+
if (introspect.isObject(type)) {
|
|
70
|
+
const shape = introspect.getObjectShape(type);
|
|
71
|
+
const props = Object.entries(shape);
|
|
72
|
+
return this.object(props);
|
|
73
|
+
}
|
|
74
|
+
if (introspect.isOptional(type)) {
|
|
75
|
+
const innerType = introspect.unwrapOptional(type);
|
|
76
|
+
const internalType = this.resolveType(innerType);
|
|
77
|
+
return this.optional(internalType);
|
|
78
|
+
}
|
|
79
|
+
if (introspect.isRecord(type)) {
|
|
80
|
+
const { keyType, valueType } = introspect.getRecordTypes(type);
|
|
81
|
+
const keyTypeStr = this.resolveType(keyType);
|
|
82
|
+
const valueTypeStr = this.resolveType(valueType);
|
|
83
|
+
return this.record(keyTypeStr, valueTypeStr);
|
|
84
|
+
}
|
|
85
|
+
if (introspect.isString(type)) {
|
|
86
|
+
return this.string();
|
|
87
|
+
}
|
|
88
|
+
if (introspect.isTuple(type)) {
|
|
89
|
+
const items = introspect.getTupleItems(type);
|
|
90
|
+
const itemsStr = items.map((item) => this.resolveType(item));
|
|
91
|
+
return this.tuple(itemsStr);
|
|
92
|
+
}
|
|
93
|
+
if (introspect.isUndefined(type)) {
|
|
94
|
+
return this.undefined();
|
|
95
|
+
}
|
|
96
|
+
if (introspect.isUnion(type)) {
|
|
97
|
+
const options = introspect.getUnionOptions(type);
|
|
98
|
+
const optionsStr = options.map((option) => this.resolveType(option));
|
|
99
|
+
return this.union(optionsStr);
|
|
100
|
+
}
|
|
101
|
+
if (introspect.isUnknown(type)) {
|
|
102
|
+
return this.unknown();
|
|
103
|
+
}
|
|
104
|
+
if (introspect.isNever(type)) {
|
|
105
|
+
return this.never();
|
|
106
|
+
}
|
|
107
|
+
console.warn(`Unknown zod type:`, type);
|
|
108
|
+
// If the type is not recognized, default to 'any'
|
|
109
|
+
return this.any();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { $ZodFunctionArgs, $ZodFunctionOut, util } from 'zod/v4/core';
|
|
3
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
4
|
+
export declare class ZodToStringMapper extends ZodBaseMapper {
|
|
5
|
+
any(): string;
|
|
6
|
+
array(wrappedType: string): string;
|
|
7
|
+
boolean(): string;
|
|
8
|
+
enum(values: util.EnumValue[]): string;
|
|
9
|
+
function(input: string, output: string): string;
|
|
10
|
+
functionArguments(value?: $ZodFunctionArgs): string;
|
|
11
|
+
functionReturns(value: $ZodFunctionOut): string;
|
|
12
|
+
null(): string;
|
|
13
|
+
literal(value: string | number | boolean | null): string;
|
|
14
|
+
number(isInteger?: boolean): "z.number()" | "z.number().int()";
|
|
15
|
+
object(properties: [string, z.ZodTypeAny][]): string;
|
|
16
|
+
optional(wrappedType: string): string;
|
|
17
|
+
never(): string;
|
|
18
|
+
record(keyType: string, valueType: string): string;
|
|
19
|
+
renderField(type: z.ZodTypeAny, key?: string): string;
|
|
20
|
+
string(): string;
|
|
21
|
+
tuple(wrappedTypes: string[]): string;
|
|
22
|
+
undefined(): string;
|
|
23
|
+
union(wrappedTypes: string[]): string;
|
|
24
|
+
unknown(): string;
|
|
25
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
2
|
+
export class ZodToStringMapper extends ZodBaseMapper {
|
|
3
|
+
any() {
|
|
4
|
+
return 'z.any()';
|
|
5
|
+
}
|
|
6
|
+
array(wrappedType) {
|
|
7
|
+
return `z.array(${wrappedType})`;
|
|
8
|
+
}
|
|
9
|
+
boolean() {
|
|
10
|
+
return 'z.boolean()';
|
|
11
|
+
}
|
|
12
|
+
enum(values) {
|
|
13
|
+
return `z.enum([${values.map((v) => (typeof v === 'string' ? `'${v}'` : String(v))).join(',')}])`;
|
|
14
|
+
}
|
|
15
|
+
function(input, output) {
|
|
16
|
+
return `z.function({input: z.tuple([${input}]), output: ${output}})`;
|
|
17
|
+
}
|
|
18
|
+
functionArguments(value) {
|
|
19
|
+
if (!value) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
const def = value.def;
|
|
23
|
+
const items = def.items || [];
|
|
24
|
+
const args = items.map((item) => {
|
|
25
|
+
const mapper = new ZodToStringMapper();
|
|
26
|
+
return mapper.resolveType(item);
|
|
27
|
+
});
|
|
28
|
+
return args.join(', ');
|
|
29
|
+
}
|
|
30
|
+
functionReturns(value) {
|
|
31
|
+
const mapper = new ZodToStringMapper();
|
|
32
|
+
return mapper.resolveType(value);
|
|
33
|
+
}
|
|
34
|
+
null() {
|
|
35
|
+
return 'z.null()';
|
|
36
|
+
}
|
|
37
|
+
literal(value) {
|
|
38
|
+
return `z.literal(${JSON.stringify(value)})`;
|
|
39
|
+
}
|
|
40
|
+
number(isInteger = false) {
|
|
41
|
+
const base = 'z.number()';
|
|
42
|
+
if (isInteger) {
|
|
43
|
+
return `${base}.int()`;
|
|
44
|
+
}
|
|
45
|
+
return base;
|
|
46
|
+
}
|
|
47
|
+
object(properties) {
|
|
48
|
+
const props = properties
|
|
49
|
+
.map(([key, type]) => {
|
|
50
|
+
const mapper = new ZodToStringMapper();
|
|
51
|
+
return mapper.renderField(type, key);
|
|
52
|
+
})
|
|
53
|
+
.join('; ');
|
|
54
|
+
return `z.object({${props}})`;
|
|
55
|
+
}
|
|
56
|
+
optional(wrappedType) {
|
|
57
|
+
return `${wrappedType}.optional()`;
|
|
58
|
+
}
|
|
59
|
+
never() {
|
|
60
|
+
return 'z.never()';
|
|
61
|
+
}
|
|
62
|
+
record(keyType, valueType) {
|
|
63
|
+
return `z.record(${keyType}, ${valueType})`;
|
|
64
|
+
}
|
|
65
|
+
renderField(type, key) {
|
|
66
|
+
const resolved = this.resolveType(type);
|
|
67
|
+
if (key) {
|
|
68
|
+
return `${key}: ${resolved}`;
|
|
69
|
+
}
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
string() {
|
|
73
|
+
return 'z.string()';
|
|
74
|
+
}
|
|
75
|
+
tuple(wrappedTypes) {
|
|
76
|
+
return `z.tuple([${wrappedTypes.join(', ')}])`;
|
|
77
|
+
}
|
|
78
|
+
undefined() {
|
|
79
|
+
return 'z.undefined()';
|
|
80
|
+
}
|
|
81
|
+
union(wrappedTypes) {
|
|
82
|
+
return `z.union([${wrappedTypes.join(', ')}])`;
|
|
83
|
+
}
|
|
84
|
+
unknown() {
|
|
85
|
+
return 'z.unknown()';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ZodTypeSupported } from '../types.js';
|
|
3
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
4
|
+
export type ZodToTypescriptMapperTarget = 'accessor' | 'raw';
|
|
5
|
+
export declare class ZodToTypescriptMapper extends ZodBaseMapper {
|
|
6
|
+
private fieldName;
|
|
7
|
+
private metaDescription;
|
|
8
|
+
private optionalProperty;
|
|
9
|
+
private target;
|
|
10
|
+
constructor({ fieldName, target }?: {
|
|
11
|
+
fieldName?: string;
|
|
12
|
+
target?: ZodToTypescriptMapperTarget;
|
|
13
|
+
});
|
|
14
|
+
any(): string;
|
|
15
|
+
array(wrappedType: string): string;
|
|
16
|
+
boolean(): string;
|
|
17
|
+
enum(values: string[]): string;
|
|
18
|
+
function(args: string, returns: string): string;
|
|
19
|
+
functionArguments(value?: z.ZodTuple): string;
|
|
20
|
+
functionReturns(value: z.ZodTypeAny): string;
|
|
21
|
+
null(): string;
|
|
22
|
+
literal(value: string | number | boolean | null): string;
|
|
23
|
+
number(): string;
|
|
24
|
+
object(properties: [string, z.ZodTypeAny][]): string;
|
|
25
|
+
optional(wrappedType: string): string;
|
|
26
|
+
never(): string;
|
|
27
|
+
record(keyType: string, valueType: string): string;
|
|
28
|
+
renderField(type: ZodTypeSupported): string;
|
|
29
|
+
string(): string;
|
|
30
|
+
tuple(wrappedTypes: string[]): string;
|
|
31
|
+
undefined(): string;
|
|
32
|
+
union(wrappedTypes: string[]): string;
|
|
33
|
+
unknown(): string;
|
|
34
|
+
protected withMeta(description: string, resolveType: () => string): string;
|
|
35
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
2
|
+
export class ZodToTypescriptMapper extends ZodBaseMapper {
|
|
3
|
+
fieldName;
|
|
4
|
+
metaDescription = undefined;
|
|
5
|
+
optionalProperty;
|
|
6
|
+
target;
|
|
7
|
+
constructor({ fieldName, target } = {}) {
|
|
8
|
+
super();
|
|
9
|
+
this.fieldName = fieldName;
|
|
10
|
+
this.optionalProperty = false;
|
|
11
|
+
this.target = target ?? 'accessor';
|
|
12
|
+
}
|
|
13
|
+
any() {
|
|
14
|
+
return 'any';
|
|
15
|
+
}
|
|
16
|
+
array(wrappedType) {
|
|
17
|
+
return `Array<${wrappedType}>`;
|
|
18
|
+
}
|
|
19
|
+
boolean() {
|
|
20
|
+
return 'boolean';
|
|
21
|
+
}
|
|
22
|
+
enum(values) {
|
|
23
|
+
return values.map((v) => `'${v}'`).join(' | ');
|
|
24
|
+
}
|
|
25
|
+
function(args, returns) {
|
|
26
|
+
// When in raw mode, we return a string type for functions,
|
|
27
|
+
// as this is what comes back from the server directly
|
|
28
|
+
if (this.target === 'raw') {
|
|
29
|
+
return 'string | undefined';
|
|
30
|
+
}
|
|
31
|
+
return `(...params: ${args}) => ${returns}`;
|
|
32
|
+
}
|
|
33
|
+
functionArguments(value) {
|
|
34
|
+
if (!value) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
const mapper = new ZodToTypescriptMapper();
|
|
38
|
+
return mapper.resolveType(value);
|
|
39
|
+
}
|
|
40
|
+
functionReturns(value) {
|
|
41
|
+
const mapper = new ZodToTypescriptMapper();
|
|
42
|
+
return mapper.resolveType(value);
|
|
43
|
+
}
|
|
44
|
+
null() {
|
|
45
|
+
return 'null';
|
|
46
|
+
}
|
|
47
|
+
literal(value) {
|
|
48
|
+
return JSON.stringify(value);
|
|
49
|
+
}
|
|
50
|
+
number() {
|
|
51
|
+
return 'number';
|
|
52
|
+
}
|
|
53
|
+
object(properties) {
|
|
54
|
+
const props = properties
|
|
55
|
+
.map(([fieldName, type]) => {
|
|
56
|
+
const mapper = new ZodToTypescriptMapper({ fieldName, target: this.target });
|
|
57
|
+
return mapper.renderField(type);
|
|
58
|
+
})
|
|
59
|
+
.join('; ');
|
|
60
|
+
return `{ ${props} }`;
|
|
61
|
+
}
|
|
62
|
+
optional(wrappedType) {
|
|
63
|
+
// In TypeScript, we hoist the optional flag to the field definition when operating directly on a field
|
|
64
|
+
if (this.fieldName) {
|
|
65
|
+
this.optionalProperty = true;
|
|
66
|
+
return wrappedType;
|
|
67
|
+
}
|
|
68
|
+
// Fallback to a union type w/undefined for inline optional definitions
|
|
69
|
+
return this.union([wrappedType, 'undefined']);
|
|
70
|
+
}
|
|
71
|
+
never() {
|
|
72
|
+
return 'never';
|
|
73
|
+
}
|
|
74
|
+
record(keyType, valueType) {
|
|
75
|
+
return `Record<${keyType}, ${valueType}>`;
|
|
76
|
+
}
|
|
77
|
+
renderField(type) {
|
|
78
|
+
if (!this.fieldName) {
|
|
79
|
+
throw new Error('Field name must be set to render a field.');
|
|
80
|
+
}
|
|
81
|
+
// Must invoke resolveType to ensure the type is fully resolved,
|
|
82
|
+
// which always guarantees that the optional flag is set correctly.
|
|
83
|
+
const resolved = this.resolveType(type);
|
|
84
|
+
// If there's a meta description, add it as a comment before the field
|
|
85
|
+
let result = '';
|
|
86
|
+
if (this.metaDescription) {
|
|
87
|
+
// Check if the description contains newlines (multi-line JSON)
|
|
88
|
+
if (this.metaDescription.includes('\n')) {
|
|
89
|
+
// Format as a multi-line block comment
|
|
90
|
+
const lines = this.metaDescription.split('\n');
|
|
91
|
+
result += '/**\n';
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
result += ` * ${line}\n`;
|
|
94
|
+
}
|
|
95
|
+
result += ' */ ';
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Single line comment
|
|
99
|
+
result += `/** ${this.metaDescription} */ `;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
result += `"${this.fieldName}"${this.optionalProperty ? '?' : ''}: ${resolved}`;
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
string() {
|
|
106
|
+
return 'string';
|
|
107
|
+
}
|
|
108
|
+
tuple(wrappedTypes) {
|
|
109
|
+
return `[${wrappedTypes.join(', ')}]`;
|
|
110
|
+
}
|
|
111
|
+
undefined() {
|
|
112
|
+
return 'undefined';
|
|
113
|
+
}
|
|
114
|
+
union(wrappedTypes) {
|
|
115
|
+
return wrappedTypes
|
|
116
|
+
.map((t) => {
|
|
117
|
+
// If the type includes an arrow function, we need to wrap it in parentheses
|
|
118
|
+
if (t.includes('=>')) {
|
|
119
|
+
return `(${t})`;
|
|
120
|
+
}
|
|
121
|
+
return t;
|
|
122
|
+
})
|
|
123
|
+
.join(' | ');
|
|
124
|
+
}
|
|
125
|
+
unknown() {
|
|
126
|
+
return 'unknown';
|
|
127
|
+
}
|
|
128
|
+
withMeta(description, resolveType) {
|
|
129
|
+
// Store the description to be used when rendering the field
|
|
130
|
+
this.metaDescription = description;
|
|
131
|
+
return resolveType();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ZodTypeSupported } from '../types.js';
|
|
3
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
4
|
+
export declare class ZodToTypescriptReturnValueMapper extends ZodBaseMapper {
|
|
5
|
+
private fieldName;
|
|
6
|
+
private FUNCTION_ARGUMENTS_NAME;
|
|
7
|
+
private metaDescription;
|
|
8
|
+
private returnTypePropertyPath;
|
|
9
|
+
constructor({ fieldName, returnTypePropertyPath }?: {
|
|
10
|
+
fieldName?: string;
|
|
11
|
+
returnTypePropertyPath?: string[];
|
|
12
|
+
});
|
|
13
|
+
any(): string;
|
|
14
|
+
array(): string;
|
|
15
|
+
boolean(): string;
|
|
16
|
+
enum(): string;
|
|
17
|
+
function(args: string, returns: string): string;
|
|
18
|
+
functionArguments(value?: z.ZodTuple): string;
|
|
19
|
+
functionReturns(): string;
|
|
20
|
+
null(): string;
|
|
21
|
+
literal(): string;
|
|
22
|
+
number(): string;
|
|
23
|
+
object(properties: [string, z.ZodTypeAny][]): string;
|
|
24
|
+
optional(wrappedType: string): string;
|
|
25
|
+
never(): string;
|
|
26
|
+
record(): string;
|
|
27
|
+
renderField(type: ZodTypeSupported): string;
|
|
28
|
+
string(): string;
|
|
29
|
+
tuple(wrappedTypes: string[]): string;
|
|
30
|
+
undefined(): string;
|
|
31
|
+
union(wrappedTypes: string[]): string;
|
|
32
|
+
unknown(): string;
|
|
33
|
+
protected withMeta(description: string, resolveType: () => string): string;
|
|
34
|
+
private printPath;
|
|
35
|
+
private printPropertyPath;
|
|
36
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ZodBaseMapper } from './zod-base-mapper.js';
|
|
2
|
+
export class ZodToTypescriptReturnValueMapper extends ZodBaseMapper {
|
|
3
|
+
fieldName;
|
|
4
|
+
FUNCTION_ARGUMENTS_NAME = 'params';
|
|
5
|
+
metaDescription = undefined;
|
|
6
|
+
returnTypePropertyPath;
|
|
7
|
+
constructor({ fieldName, returnTypePropertyPath } = {}) {
|
|
8
|
+
super();
|
|
9
|
+
this.fieldName = fieldName;
|
|
10
|
+
this.returnTypePropertyPath = returnTypePropertyPath ?? [];
|
|
11
|
+
}
|
|
12
|
+
any() {
|
|
13
|
+
return `raw${this.printPropertyPath()}`;
|
|
14
|
+
}
|
|
15
|
+
array() {
|
|
16
|
+
// Explicity not supporting naviagtion into array items, as the number of items is not known at compile time
|
|
17
|
+
return `raw${this.printPath(this.returnTypePropertyPath)}`;
|
|
18
|
+
}
|
|
19
|
+
boolean() {
|
|
20
|
+
return `raw${this.printPropertyPath()}`;
|
|
21
|
+
}
|
|
22
|
+
enum() {
|
|
23
|
+
return `raw${this.printPropertyPath()}`;
|
|
24
|
+
}
|
|
25
|
+
function(args, returns) {
|
|
26
|
+
return `(${args}) => ${returns}`;
|
|
27
|
+
}
|
|
28
|
+
functionArguments(value) {
|
|
29
|
+
if (!value) {
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
// Everything is already typed, so return top level params object
|
|
33
|
+
return this.FUNCTION_ARGUMENTS_NAME;
|
|
34
|
+
}
|
|
35
|
+
// Mustache handles rendering of function values
|
|
36
|
+
functionReturns() {
|
|
37
|
+
return `Mustache.render(raw${this.printPropertyPath()} ?? "", ${this.FUNCTION_ARGUMENTS_NAME})`;
|
|
38
|
+
}
|
|
39
|
+
null() {
|
|
40
|
+
return `raw${this.printPropertyPath()}`;
|
|
41
|
+
}
|
|
42
|
+
literal() {
|
|
43
|
+
return `raw${this.printPropertyPath()}`;
|
|
44
|
+
}
|
|
45
|
+
number() {
|
|
46
|
+
return `raw${this.printPropertyPath()}`;
|
|
47
|
+
}
|
|
48
|
+
object(properties) {
|
|
49
|
+
const props = properties
|
|
50
|
+
.map(([fieldName, type]) => {
|
|
51
|
+
const mapper = new ZodToTypescriptReturnValueMapper({
|
|
52
|
+
fieldName,
|
|
53
|
+
returnTypePropertyPath: [...this.returnTypePropertyPath, fieldName],
|
|
54
|
+
});
|
|
55
|
+
return mapper.renderField(type);
|
|
56
|
+
})
|
|
57
|
+
.join(', ');
|
|
58
|
+
return `{ ${props} }`;
|
|
59
|
+
}
|
|
60
|
+
optional(wrappedType) {
|
|
61
|
+
// In TypeScript, we hoist the optional flag to the field definition when operating directly on a field
|
|
62
|
+
if (this.fieldName) {
|
|
63
|
+
return wrappedType;
|
|
64
|
+
}
|
|
65
|
+
// Fallback to a union type w/undefined for inline optional definitions
|
|
66
|
+
return this.union([wrappedType, 'undefined']);
|
|
67
|
+
}
|
|
68
|
+
never() {
|
|
69
|
+
return `raw${this.printPropertyPath()}`;
|
|
70
|
+
}
|
|
71
|
+
record() {
|
|
72
|
+
// Explicitly not supporting navigation into record values, as the keys are not known at compile time
|
|
73
|
+
return `raw${this.printPath(this.returnTypePropertyPath)}`;
|
|
74
|
+
}
|
|
75
|
+
renderField(type) {
|
|
76
|
+
if (!this.fieldName) {
|
|
77
|
+
throw new Error('Field name must be set in the resolution context to render a field.');
|
|
78
|
+
}
|
|
79
|
+
// Must invoke resolveType to ensure the type is fully resolved,
|
|
80
|
+
// which always guarantees that the optional flag is set correctly.
|
|
81
|
+
const resolved = this.resolveType(type);
|
|
82
|
+
// If there's a meta description, add it as a comment before the field
|
|
83
|
+
let result = '';
|
|
84
|
+
if (this.metaDescription) {
|
|
85
|
+
// Check if the description contains newlines (multi-line JSON)
|
|
86
|
+
if (this.metaDescription.includes('\n')) {
|
|
87
|
+
// Format as a multi-line block comment
|
|
88
|
+
const lines = this.metaDescription.split('\n');
|
|
89
|
+
result += '/**\n';
|
|
90
|
+
for (const line of lines) {
|
|
91
|
+
result += ` * ${line}\n`;
|
|
92
|
+
}
|
|
93
|
+
result += ' */ ';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Single line comment
|
|
97
|
+
result += `/** ${this.metaDescription} */ `;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
result += `"${this.fieldName}": ${resolved}`;
|
|
101
|
+
// Clear metaDescription to prevent it from leaking into subsequent renderField calls
|
|
102
|
+
this.metaDescription = undefined;
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
string() {
|
|
106
|
+
return `raw${this.printPropertyPath()}`;
|
|
107
|
+
}
|
|
108
|
+
tuple(wrappedTypes) {
|
|
109
|
+
const tupleNavigation = wrappedTypes.map((wt, index) => {
|
|
110
|
+
let massagedWrappedType = wt;
|
|
111
|
+
if (massagedWrappedType !== 'raw') {
|
|
112
|
+
// Remove trailing ! from the wrapped type
|
|
113
|
+
massagedWrappedType = massagedWrappedType.replace(/!$/, '');
|
|
114
|
+
}
|
|
115
|
+
return `${massagedWrappedType}?.[${index}]!`;
|
|
116
|
+
});
|
|
117
|
+
return `[${tupleNavigation.join(`, `)}]`;
|
|
118
|
+
}
|
|
119
|
+
undefined() {
|
|
120
|
+
return `raw${this.printPropertyPath()}`;
|
|
121
|
+
}
|
|
122
|
+
union(wrappedTypes) {
|
|
123
|
+
// If we have functions in the union, force the return type to be a function type
|
|
124
|
+
const functionWrappedTypes = wrappedTypes.filter((t) => t.includes('=>'));
|
|
125
|
+
if (functionWrappedTypes.length > 0) {
|
|
126
|
+
return functionWrappedTypes[0];
|
|
127
|
+
}
|
|
128
|
+
return `raw${this.printPropertyPath()}`;
|
|
129
|
+
}
|
|
130
|
+
unknown() {
|
|
131
|
+
return `raw${this.printPropertyPath()}`;
|
|
132
|
+
}
|
|
133
|
+
withMeta(description, resolveType) {
|
|
134
|
+
// Store the description to be used when rendering the field
|
|
135
|
+
this.metaDescription = description;
|
|
136
|
+
return resolveType();
|
|
137
|
+
}
|
|
138
|
+
printPath(paths) {
|
|
139
|
+
if (paths.length === 0) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
// Always safe navigate the path to ensure we don't throw an error if the property doesn't exist
|
|
143
|
+
const path = paths.reduce((acc, part) => `${acc}?.['${part}']`, '');
|
|
144
|
+
// To satisfy TypeScript's type system, tell it the value is always defined
|
|
145
|
+
return `${path}!`;
|
|
146
|
+
}
|
|
147
|
+
printPropertyPath() {
|
|
148
|
+
return this.printPath(this.returnTypePropertyPath);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare class MustacheExtractor {
|
|
3
|
+
static extractSchema(template: string, log: (category: string | unknown, message?: unknown) => void): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
4
|
+
private static generateZodSchema;
|
|
5
|
+
private static parseMustacheTemplate;
|
|
6
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Extracts a zod schema from a mustache template.
|
|
3
|
+
// eg "Hello {{name}}!" -> z.object({ name: z.string() })
|
|
4
|
+
export class MustacheExtractor {
|
|
5
|
+
static extractSchema(template, log) {
|
|
6
|
+
if (!template) {
|
|
7
|
+
return z.object({});
|
|
8
|
+
}
|
|
9
|
+
const nodes = this.parseMustacheTemplate(template, log);
|
|
10
|
+
return this.generateZodSchema(nodes);
|
|
11
|
+
}
|
|
12
|
+
static generateZodSchema(nodes) {
|
|
13
|
+
const properties = {};
|
|
14
|
+
for (const node of nodes) {
|
|
15
|
+
if (node.isSection) {
|
|
16
|
+
if (node.children.length > 0) {
|
|
17
|
+
const childSchema = this.generateZodSchema(node.children);
|
|
18
|
+
properties[node.name] = node.isInverted ? childSchema.optional() : z.array(childSchema);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
properties[node.name] = node.isInverted ? z.boolean().optional() : z.boolean();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (!properties[node.name]) {
|
|
25
|
+
properties[node.name] = z.string();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return z.object(properties);
|
|
29
|
+
}
|
|
30
|
+
static parseMustacheTemplate(template, log) {
|
|
31
|
+
const tokens = template.match(/{{[^}]+}}|[^{}]+/g) || [];
|
|
32
|
+
const root = [];
|
|
33
|
+
const stack = [root];
|
|
34
|
+
for (const token of tokens) {
|
|
35
|
+
if (!token.startsWith('{{'))
|
|
36
|
+
continue;
|
|
37
|
+
const content = token.slice(2, -2).trim();
|
|
38
|
+
// Add check for partials
|
|
39
|
+
if (content.startsWith('>')) {
|
|
40
|
+
log(`Found Mustache partial: ${content.slice(1)}`);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (content.startsWith('#')) {
|
|
44
|
+
// Section start
|
|
45
|
+
const name = content.slice(1);
|
|
46
|
+
const newSection = {
|
|
47
|
+
children: [],
|
|
48
|
+
isInverted: false,
|
|
49
|
+
isSection: true,
|
|
50
|
+
name,
|
|
51
|
+
};
|
|
52
|
+
stack.at(-1).push(newSection);
|
|
53
|
+
stack.push(newSection.children);
|
|
54
|
+
}
|
|
55
|
+
else if (content.startsWith('^')) {
|
|
56
|
+
// Inverted section
|
|
57
|
+
const name = content.slice(1);
|
|
58
|
+
const newSection = {
|
|
59
|
+
children: [],
|
|
60
|
+
isInverted: true,
|
|
61
|
+
isSection: true,
|
|
62
|
+
name,
|
|
63
|
+
};
|
|
64
|
+
stack.at(-1).push(newSection);
|
|
65
|
+
stack.push(newSection.children);
|
|
66
|
+
}
|
|
67
|
+
else if (content.startsWith('/')) {
|
|
68
|
+
// Section end
|
|
69
|
+
stack.pop();
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Regular variable
|
|
73
|
+
stack.at(-1).push({
|
|
74
|
+
children: [],
|
|
75
|
+
isInverted: false,
|
|
76
|
+
isSection: false,
|
|
77
|
+
name: content,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return root;
|
|
82
|
+
}
|
|
83
|
+
}
|