@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.
Files changed (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +707 -0
  3. package/bin/dev.cmd +3 -0
  4. package/bin/dev.js +9 -0
  5. package/bin/run.cmd +3 -0
  6. package/bin/run.js +8 -0
  7. package/dist/api/get-environments.d.ts +10 -0
  8. package/dist/api/get-environments.js +11 -0
  9. package/dist/codegen/code-generators/base-generator.d.ts +11 -0
  10. package/dist/codegen/code-generators/base-generator.js +8 -0
  11. package/dist/codegen/code-generators/base-typescript-generator.d.ts +17 -0
  12. package/dist/codegen/code-generators/base-typescript-generator.js +35 -0
  13. package/dist/codegen/code-generators/node-typescript-generator.d.ts +8 -0
  14. package/dist/codegen/code-generators/node-typescript-generator.js +96 -0
  15. package/dist/codegen/code-generators/react-typescript-generator.d.ts +11 -0
  16. package/dist/codegen/code-generators/react-typescript-generator.js +116 -0
  17. package/dist/codegen/config-downloader.d.ts +7 -0
  18. package/dist/codegen/config-downloader.js +26 -0
  19. package/dist/codegen/json-schema-to-zod.d.ts +3 -0
  20. package/dist/codegen/json-schema-to-zod.js +170 -0
  21. package/dist/codegen/language-mappers/json-to-zod-mapper.d.ts +4 -0
  22. package/dist/codegen/language-mappers/json-to-zod-mapper.js +55 -0
  23. package/dist/codegen/language-mappers/zod-base-mapper.d.ts +35 -0
  24. package/dist/codegen/language-mappers/zod-base-mapper.js +111 -0
  25. package/dist/codegen/language-mappers/zod-to-string-mapper.d.ts +25 -0
  26. package/dist/codegen/language-mappers/zod-to-string-mapper.js +87 -0
  27. package/dist/codegen/language-mappers/zod-to-typescript-mapper.d.ts +35 -0
  28. package/dist/codegen/language-mappers/zod-to-typescript-mapper.js +133 -0
  29. package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.d.ts +36 -0
  30. package/dist/codegen/language-mappers/zod-to-typescript-return-value-mapper.js +150 -0
  31. package/dist/codegen/mustache-extractor.d.ts +6 -0
  32. package/dist/codegen/mustache-extractor.js +83 -0
  33. package/dist/codegen/schema-evaluator.d.ts +37 -0
  34. package/dist/codegen/schema-evaluator.js +283 -0
  35. package/dist/codegen/schema-extractor.d.ts +24 -0
  36. package/dist/codegen/schema-extractor.js +265 -0
  37. package/dist/codegen/types.d.ts +44 -0
  38. package/dist/codegen/types.js +5 -0
  39. package/dist/codegen/zod-introspection.d.ts +85 -0
  40. package/dist/codegen/zod-introspection.js +205 -0
  41. package/dist/commands/create.d.ts +19 -0
  42. package/dist/commands/create.js +175 -0
  43. package/dist/commands/download.d.ts +13 -0
  44. package/dist/commands/download.js +63 -0
  45. package/dist/commands/generate-new-hex-key.d.ts +7 -0
  46. package/dist/commands/generate-new-hex-key.js +10 -0
  47. package/dist/commands/generate.d.ts +16 -0
  48. package/dist/commands/generate.js +220 -0
  49. package/dist/commands/get.d.ts +13 -0
  50. package/dist/commands/get.js +141 -0
  51. package/dist/commands/info.d.ts +21 -0
  52. package/dist/commands/info.js +464 -0
  53. package/dist/commands/interactive.d.ts +5 -0
  54. package/dist/commands/interactive.js +8 -0
  55. package/dist/commands/list.d.ts +13 -0
  56. package/dist/commands/list.js +61 -0
  57. package/dist/commands/login.d.ts +10 -0
  58. package/dist/commands/login.js +124 -0
  59. package/dist/commands/logout.d.ts +7 -0
  60. package/dist/commands/logout.js +12 -0
  61. package/dist/commands/mcp.d.ts +13 -0
  62. package/dist/commands/mcp.js +145 -0
  63. package/dist/commands/override.d.ts +17 -0
  64. package/dist/commands/override.js +170 -0
  65. package/dist/commands/profile.d.ts +7 -0
  66. package/dist/commands/profile.js +47 -0
  67. package/dist/commands/schema.d.ts +21 -0
  68. package/dist/commands/schema.js +168 -0
  69. package/dist/commands/serve.d.ts +13 -0
  70. package/dist/commands/serve.js +99 -0
  71. package/dist/commands/set-default.d.ts +20 -0
  72. package/dist/commands/set-default.js +273 -0
  73. package/dist/commands/verify.d.ts +12 -0
  74. package/dist/commands/verify.js +48 -0
  75. package/dist/commands/whoami.d.ts +7 -0
  76. package/dist/commands/whoami.js +52 -0
  77. package/dist/commands/workspace.d.ts +7 -0
  78. package/dist/commands/workspace.js +38 -0
  79. package/dist/constants.d.ts +2 -0
  80. package/dist/constants.js +2 -0
  81. package/dist/help.d.ts +4 -0
  82. package/dist/help.js +10 -0
  83. package/dist/index.d.ts +40 -0
  84. package/dist/index.js +112 -0
  85. package/dist/interactive-prompt.d.ts +2 -0
  86. package/dist/interactive-prompt.js +162 -0
  87. package/dist/quonfig.d.ts +15 -0
  88. package/dist/quonfig.js +75 -0
  89. package/dist/result.d.ts +35 -0
  90. package/dist/result.js +9 -0
  91. package/dist/services/mcp.d.ts +15 -0
  92. package/dist/services/mcp.js +59 -0
  93. package/dist/ui/get-confirmation.d.ts +10 -0
  94. package/dist/ui/get-confirmation.js +4 -0
  95. package/dist/ui/get-environment.d.ts +11 -0
  96. package/dist/ui/get-environment.js +42 -0
  97. package/dist/ui/get-key.d.ts +27 -0
  98. package/dist/ui/get-key.js +15 -0
  99. package/dist/ui/get-string.d.ts +5 -0
  100. package/dist/ui/get-string.js +8 -0
  101. package/dist/ui/get-value.d.ts +18 -0
  102. package/dist/ui/get-value.js +46 -0
  103. package/dist/util/autocomplete.d.ts +6 -0
  104. package/dist/util/autocomplete.js +25 -0
  105. package/dist/util/coerce.d.ts +7 -0
  106. package/dist/util/coerce.js +62 -0
  107. package/dist/util/color.d.ts +3 -0
  108. package/dist/util/color.js +4 -0
  109. package/dist/util/config-value-dto.d.ts +16 -0
  110. package/dist/util/config-value-dto.js +82 -0
  111. package/dist/util/context.d.ts +18 -0
  112. package/dist/util/context.js +10 -0
  113. package/dist/util/domain-urls.d.ts +3 -0
  114. package/dist/util/domain-urls.js +18 -0
  115. package/dist/util/encryption.d.ts +20 -0
  116. package/dist/util/encryption.js +153 -0
  117. package/dist/util/file-manager.d.ts +12 -0
  118. package/dist/util/file-manager.js +16 -0
  119. package/dist/util/get-client.d.ts +7 -0
  120. package/dist/util/get-client.js +127 -0
  121. package/dist/util/is-interactive.d.ts +5 -0
  122. package/dist/util/is-interactive.js +7 -0
  123. package/dist/util/json-maybe.d.ts +3 -0
  124. package/dist/util/json-maybe.js +14 -0
  125. package/dist/util/name-arg.d.ts +4 -0
  126. package/dist/util/name-arg.js +3 -0
  127. package/dist/util/oauth-client.d.ts +30 -0
  128. package/dist/util/oauth-client.js +99 -0
  129. package/dist/util/secret-flags.d.ts +13 -0
  130. package/dist/util/secret-flags.js +13 -0
  131. package/dist/util/token-storage.d.ts +25 -0
  132. package/dist/util/token-storage.js +90 -0
  133. package/dist/validations/value.d.ts +4 -0
  134. package/dist/validations/value.js +11 -0
  135. package/dist/verify/standalone.d.ts +14 -0
  136. package/dist/verify/standalone.js +133 -0
  137. package/dist/verify/validate.d.ts +43 -0
  138. package/dist/verify/validate.js +756 -0
  139. package/dist/version.d.ts +2 -0
  140. package/dist/version.js +2 -0
  141. package/oclif.manifest.json +1429 -0
  142. 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
+ }