@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,37 @@
1
+ import { ZodTypeSupported } from './types.js';
2
+ /**
3
+ * Options for the secure schema validator
4
+ */
5
+ export interface SecureSchemaValidatorOptions {
6
+ /**
7
+ * Whether to perform AST validation
8
+ */
9
+ astValidation?: boolean;
10
+ /**
11
+ * Maximum allowed AST nodes to prevent DoS attacks
12
+ */
13
+ maxAstNodes?: number;
14
+ }
15
+ /**
16
+ * Validates a JavaScript expression AST for potentially unsafe operations
17
+ *
18
+ * @param ast - The AST to validate
19
+ * @param parentMap - Map of nodes to their parent nodes
20
+ * @returns An object with validation result and optional error message
21
+ */
22
+ export declare function validateAst(ast: any, parentMap: WeakMap<any, any>): {
23
+ error?: string;
24
+ isValid: boolean;
25
+ };
26
+ /**
27
+ * Securely evaluates a Zod schema string using AST validation
28
+ *
29
+ * @param schemaString - The schema string to evaluate
30
+ * @param options - Options for secure evaluation
31
+ * @returns The evaluated schema or an error
32
+ */
33
+ export declare function secureEvaluateSchema(schemaString: string, options?: SecureSchemaValidatorOptions): {
34
+ error?: string;
35
+ schema?: ZodTypeSupported;
36
+ success: boolean;
37
+ };
@@ -0,0 +1,283 @@
1
+ // TODO: We should resolve these any references to ensure generation is fully type-safe
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import * as acorn from 'acorn';
4
+ import * as walk from 'acorn-walk';
5
+ import { z } from 'zod';
6
+ /**
7
+ * Default options for the secure schema validator
8
+ */
9
+ const DEFAULT_OPTIONS = {
10
+ astValidation: true,
11
+ maxAstNodes: 500,
12
+ };
13
+ /**
14
+ * Validates a JavaScript expression AST for potentially unsafe operations
15
+ *
16
+ * @param ast - The AST to validate
17
+ * @param parentMap - Map of nodes to their parent nodes
18
+ * @returns An object with validation result and optional error message
19
+ */
20
+ export function validateAst(ast, parentMap) {
21
+ let isValid = true;
22
+ let error;
23
+ // Track what we've seen for detailed error messages
24
+ const issues = [];
25
+ // Count nodes to prevent DoS attacks with extremely large schemas
26
+ let nodeCount = 0;
27
+ // Helper function to check if an arrow function is used in a valid Zod method context
28
+ const isValidZodMethodArg = (node) => {
29
+ // Check if this node is an argument to a method call
30
+ const parent = parentMap.get(node);
31
+ if (parent && parent.type === 'CallExpression' && parent.callee && parent.callee.type === 'MemberExpression') {
32
+ // Check if method is on an object that's part of a valid Zod chain
33
+ const memberExpr = parent.callee;
34
+ // Allow methods like refine, transform, superRefine, etc.
35
+ const allowedMethods = ['refine', 'transform', 'superRefine', 'pipe', 'preprocess'];
36
+ if (memberExpr.property && memberExpr.property.name && allowedMethods.includes(memberExpr.property.name)) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ };
42
+ // Count all nodes separately to track complexity
43
+ walk.full(ast, () => {
44
+ nodeCount++;
45
+ });
46
+ // Custom AST walker to check for unsafe patterns
47
+ walk.simple(ast, {
48
+ ArrowFunctionExpression(node) {
49
+ // Allow arrow functions only in valid Zod method contexts
50
+ if (!isValidZodMethodArg(node)) {
51
+ issues.push(`Forbidden standalone arrow function`);
52
+ isValid = false;
53
+ }
54
+ },
55
+ // Block async/await
56
+ AwaitExpression() {
57
+ issues.push('Await expressions are not allowed');
58
+ isValid = false;
59
+ },
60
+ // Restrict function calls
61
+ CallExpression(node) {
62
+ if (node.callee.type === 'MemberExpression') {
63
+ const obj = node.callee.object;
64
+ if (obj.type === 'Identifier' && obj.name !== 'z') {
65
+ issues.push(`Only calls to z.* methods are allowed, found: ${obj.name}`);
66
+ isValid = false;
67
+ }
68
+ }
69
+ else if (node.callee.type === 'Identifier' && node.callee.name === 'require') {
70
+ issues.push(`Forbidden function call: require`);
71
+ isValid = false;
72
+ }
73
+ else if (node.callee.type === 'Identifier' && !['z'].includes(node.callee.name)) {
74
+ issues.push(`Forbidden function call: ${node.callee.name}`);
75
+ isValid = false;
76
+ }
77
+ },
78
+ // Block class declarations
79
+ ClassDeclaration() {
80
+ issues.push(`Forbidden node type: ClassDeclaration`);
81
+ isValid = false;
82
+ },
83
+ ClassExpression() {
84
+ issues.push(`Forbidden node type: ClassExpression`);
85
+ isValid = false;
86
+ },
87
+ ExportAllDeclaration() {
88
+ issues.push(`Forbidden operation: ExportAllDeclaration`);
89
+ isValid = false;
90
+ },
91
+ ExportDefaultDeclaration() {
92
+ issues.push(`Forbidden operation: ExportDefaultDeclaration`);
93
+ isValid = false;
94
+ },
95
+ ExportNamedDeclaration() {
96
+ issues.push(`Forbidden operation: ExportNamedDeclaration`);
97
+ isValid = false;
98
+ },
99
+ // Block function declarations
100
+ FunctionDeclaration() {
101
+ issues.push(`Forbidden node type: FunctionDeclaration`);
102
+ isValid = false;
103
+ },
104
+ FunctionExpression() {
105
+ issues.push(`Forbidden node type: FunctionExpression`);
106
+ isValid = false;
107
+ },
108
+ // Block access to global objects and require
109
+ Identifier(node) {
110
+ const parent = parentMap.get(node);
111
+ const forbiddenGlobals = [
112
+ 'window',
113
+ 'global',
114
+ 'process',
115
+ 'console',
116
+ 'document',
117
+ 'navigator',
118
+ 'localStorage',
119
+ 'fetch',
120
+ 'XMLHttpRequest',
121
+ 'WebSocket',
122
+ 'eval',
123
+ 'require',
124
+ ];
125
+ if (forbiddenGlobals.includes(node.name) && !(parent?.type === 'MemberExpression' && parent.property === node)) {
126
+ issues.push(`Forbidden global object: ${node.name}`);
127
+ isValid = false;
128
+ }
129
+ },
130
+ // Block imports, exports
131
+ ImportDeclaration() {
132
+ issues.push(`Forbidden operation: ImportDeclaration`);
133
+ isValid = false;
134
+ },
135
+ // Block dangerous property access
136
+ MemberExpression(node) {
137
+ if (node.property.type === 'Identifier') {
138
+ const dangerousProps = [
139
+ 'constructor',
140
+ 'prototype',
141
+ '__proto__',
142
+ '__defineGetter__',
143
+ '__defineSetter__',
144
+ '__lookupGetter__',
145
+ '__lookupSetter__',
146
+ ];
147
+ if (dangerousProps.includes(node.property.name)) {
148
+ issues.push(`Forbidden property access: ${node.property.name}`);
149
+ isValid = false;
150
+ }
151
+ }
152
+ },
153
+ });
154
+ if (nodeCount > DEFAULT_OPTIONS.maxAstNodes) {
155
+ isValid = false;
156
+ issues.push(`Schema exceeds maximum allowed complexity (${nodeCount} nodes > ${DEFAULT_OPTIONS.maxAstNodes} limit)`);
157
+ }
158
+ if (!isValid) {
159
+ error = `Schema contains potentially unsafe operations: ${issues.join(', ')}`;
160
+ }
161
+ return { error, isValid };
162
+ }
163
+ /**
164
+ * Removes .describe() calls from the AST by filtering them out
165
+ * This is more robust than regex as it properly handles nested structures
166
+ *
167
+ * @param ast - The AST to process
168
+ * @param schemaString - The original schema string
169
+ * @returns The schema string with .describe() calls removed
170
+ */
171
+ function removeDescribeCalls(ast, schemaString) {
172
+ const nodesToRemove = [];
173
+ // Walk the AST and find all .describe() calls
174
+ walk.simple(ast, {
175
+ CallExpression(node) {
176
+ if (node.callee.type === 'MemberExpression' &&
177
+ node.callee.property.type === 'Identifier' &&
178
+ node.callee.property.name === 'describe') {
179
+ // Mark the entire .describe(...) call for removal
180
+ // We want to remove from the dot before 'describe' to the closing paren
181
+ const start = node.callee.property.start - 1; // Include the dot
182
+ const end = node.end;
183
+ nodesToRemove.push({ start, end });
184
+ }
185
+ },
186
+ });
187
+ // Sort in reverse order so we can remove from the end without affecting earlier positions
188
+ nodesToRemove.sort((a, b) => b.start - a.start);
189
+ let result = schemaString;
190
+ for (const { start, end } of nodesToRemove) {
191
+ result = result.slice(0, Math.max(0, start)) + result.slice(Math.max(0, end));
192
+ }
193
+ return result;
194
+ }
195
+ /**
196
+ * Securely evaluates a Zod schema string using AST validation
197
+ *
198
+ * @param schemaString - The schema string to evaluate
199
+ * @param options - Options for secure evaluation
200
+ * @returns The evaluated schema or an error
201
+ */
202
+ export function secureEvaluateSchema(schemaString, options = {}) {
203
+ const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
204
+ const trimmedSchema = schemaString.trim();
205
+ try {
206
+ // Phase 1: AST Validation (Static Analysis)
207
+ if (mergedOptions.astValidation) {
208
+ // Parse the schema string into an AST
209
+ const ast = acorn.parse(trimmedSchema, {
210
+ allowAwaitOutsideFunction: false,
211
+ ecmaVersion: 2020,
212
+ sourceType: 'script',
213
+ });
214
+ // Create a map to track parent-child relationships
215
+ const parentMap = new WeakMap();
216
+ // Function to build parent-child relationships
217
+ function buildParentChildRelationships(node, parent = null) {
218
+ if (parent) {
219
+ parentMap.set(node, parent);
220
+ }
221
+ // Recursively process all properties that might contain child nodes
222
+ for (const key in node) {
223
+ if (Object.hasOwn(node, key)) {
224
+ const child = node[key];
225
+ if (child && typeof child === 'object') {
226
+ if (Array.isArray(child)) {
227
+ for (const item of child) {
228
+ if (item && typeof item === 'object') {
229
+ buildParentChildRelationships(item, node);
230
+ }
231
+ }
232
+ }
233
+ else {
234
+ buildParentChildRelationships(child, node);
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ // Build the parent-child relationships
241
+ buildParentChildRelationships(ast);
242
+ // Validate the AST for security concerns
243
+ const { error, isValid } = validateAst(ast, parentMap);
244
+ if (!isValid) {
245
+ return { error, success: false };
246
+ }
247
+ // Remove .describe() calls after validation
248
+ const filteredSchema = removeDescribeCalls(ast, trimmedSchema);
249
+ // Phase 2: Execute with Function constructor
250
+ // We're deliberately avoiding VM modules due to security concerns
251
+ // The AST validation provides our primary security layer
252
+ // eslint-disable-next-line no-new-func
253
+ const constructSchema = new Function('z', `return ${filteredSchema}`);
254
+ const schema = constructSchema(z);
255
+ // Phase 3: Validate Result
256
+ if (!schema || typeof schema !== 'object' || !('_def' in schema)) {
257
+ return {
258
+ error: 'The provided string did not evaluate to a valid Zod schema',
259
+ success: false,
260
+ };
261
+ }
262
+ return { schema, success: true };
263
+ }
264
+ // If AST validation is disabled, just execute directly (not recommended)
265
+ // eslint-disable-next-line no-new-func
266
+ const constructSchema = new Function('z', `return ${trimmedSchema}`);
267
+ const schema = constructSchema(z);
268
+ // Phase 3: Validate Result
269
+ if (!schema || typeof schema !== 'object' || !('_def' in schema)) {
270
+ return {
271
+ error: 'The provided string did not evaluate to a valid Zod schema',
272
+ success: false,
273
+ };
274
+ }
275
+ return { schema, success: true };
276
+ }
277
+ catch (error) {
278
+ return {
279
+ error: error instanceof Error ? `Evaluation error: ${error.message}` : 'Unknown evaluation error',
280
+ success: false,
281
+ };
282
+ }
283
+ }
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ import { type Config, type ConfigFile } from './types.js';
3
+ export type DurationTypeMap = () => z.ZodTypeAny;
4
+ export declare class SchemaExtractor {
5
+ private log;
6
+ constructor(log: (category: string | unknown, message?: unknown) => void);
7
+ execute({ config, configFile, durationTypeMap, }: {
8
+ config: Config;
9
+ configFile: ConfigFile;
10
+ durationTypeMap?: DurationTypeMap;
11
+ }): z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
12
+ private createZodFunctionFromMustacheStrings;
13
+ private getAllJsonValues;
14
+ /**
15
+ * Get all string values at a specific location in the config
16
+ * @param config The configuration to extract strings from
17
+ * @param location Array of keys representing the path to look for strings. Empty array for direct string values.
18
+ * @returns Array of strings found at the specified location
19
+ */
20
+ private getAllStringsAtLocation;
21
+ private inferFromConfig;
22
+ private replaceStringsWithMustache;
23
+ private resolveUserSchema;
24
+ }
@@ -0,0 +1,265 @@
1
+ import { z } from 'zod';
2
+ import { jsonSchemaToZod, isLegacySchemaWrapper } from './json-schema-to-zod.js';
3
+ import { JsonToZodMapper } from './language-mappers/json-to-zod-mapper.js';
4
+ import { ZodToStringMapper } from './language-mappers/zod-to-string-mapper.js';
5
+ import { MustacheExtractor } from './mustache-extractor.js';
6
+ import { secureEvaluateSchema } from './schema-evaluator.js';
7
+ export class SchemaExtractor {
8
+ log;
9
+ constructor(log) {
10
+ this.log = log;
11
+ }
12
+ execute({ config, configFile, durationTypeMap, }) {
13
+ const userDefinedSchema = this.resolveUserSchema(config, configFile);
14
+ const schemaWithoutMustache = userDefinedSchema ?? this.inferFromConfig(config, durationTypeMap);
15
+ return this.replaceStringsWithMustache(schemaWithoutMustache, config);
16
+ }
17
+ createZodFunctionFromMustacheStrings(strings) {
18
+ if (strings.length === 0) {
19
+ return z.string();
20
+ }
21
+ if (strings.length > 1) {
22
+ // De-dup exact schemas
23
+ const schemas = {};
24
+ strings.forEach((str) => {
25
+ const schema = MustacheExtractor.extractSchema(str, this.log);
26
+ const stringSchema = new ZodToStringMapper().renderField(schema);
27
+ schemas[stringSchema] = schema;
28
+ });
29
+ const schemaResults = Object.values(schemas);
30
+ // If the schema is empty (no properties), just return basic string
31
+ const noSchemasPresent = schemaResults.every((s) => {
32
+ if (s instanceof z.ZodObject) {
33
+ return Object.keys(s.shape).length === 0;
34
+ }
35
+ return false;
36
+ });
37
+ if (noSchemasPresent) {
38
+ return z.string();
39
+ }
40
+ // Return an explicit function if we have only one schema defined
41
+ if (schemaResults.length === 1) {
42
+ return z.function({ input: z.tuple([schemaResults[0]]), output: z.string() });
43
+ }
44
+ // Return a function with a union of schemas if we have multiple defined
45
+ return z.function({
46
+ input: z.tuple([z.union(schemaResults)]),
47
+ output: z.string(),
48
+ });
49
+ }
50
+ const schema = MustacheExtractor.extractSchema(strings[0], this.log);
51
+ // If the schema is empty (no properties), just return basic string
52
+ if (schema instanceof z.ZodObject && Object.keys(schema.shape).length === 0) {
53
+ return z.string();
54
+ }
55
+ return z.function({ input: z.tuple([schema]), output: z.string() });
56
+ }
57
+ getAllJsonValues(config) {
58
+ return config.rows.flatMap((row) => row.values.flatMap((valueObj) => {
59
+ if (config.valueType === 'JSON') {
60
+ // Try to parse JSON from json field
61
+ if (valueObj.value.json?.json) {
62
+ try {
63
+ return [JSON.parse(valueObj.value.json.json)];
64
+ }
65
+ catch (error) {
66
+ console.warn(`Failed to parse JSON for ${config.key}:`, error);
67
+ }
68
+ }
69
+ // Try to parse JSON from string field
70
+ else if (valueObj.value.string) {
71
+ try {
72
+ return [JSON.parse(valueObj.value.string)];
73
+ }
74
+ catch (error) {
75
+ console.warn(`Failed to parse JSON string for ${config.key}:`, error);
76
+ }
77
+ }
78
+ }
79
+ return [];
80
+ }));
81
+ }
82
+ /**
83
+ * Get all string values at a specific location in the config
84
+ * @param config The configuration to extract strings from
85
+ * @param location Array of keys representing the path to look for strings. Empty array for direct string values.
86
+ * @returns Array of strings found at the specified location
87
+ */
88
+ getAllStringsAtLocation(config, location) {
89
+ if (location.length === 0) {
90
+ // For empty location, just get direct string values
91
+ return config.rows.flatMap((row) => row.values.flatMap((valueObj) => (valueObj.value.string ? [valueObj.value.string] : [])));
92
+ }
93
+ // For JSON values, we need to traverse the object
94
+ return config.rows.flatMap((row) => row.values.flatMap((valueObj) => {
95
+ let jsonContent = null;
96
+ // Try to parse JSON from either json field or string field
97
+ if (valueObj.value.json?.json) {
98
+ try {
99
+ jsonContent = JSON.parse(valueObj.value.json.json);
100
+ }
101
+ catch (error) {
102
+ console.warn(`Failed to parse JSON for ${config.key}:`, error);
103
+ return [];
104
+ }
105
+ }
106
+ else if (valueObj.value.string && config.valueType === 'JSON') {
107
+ try {
108
+ jsonContent = JSON.parse(valueObj.value.string);
109
+ }
110
+ catch (error) {
111
+ console.warn(`Failed to parse JSON string for ${config.key}:`, error);
112
+ return [];
113
+ }
114
+ }
115
+ if (!jsonContent)
116
+ return [];
117
+ // Traverse the JSON object to the specified location
118
+ let current = jsonContent;
119
+ for (const key of location) {
120
+ if (current && typeof current === 'object' && key in current) {
121
+ current = current[key];
122
+ }
123
+ else {
124
+ return [];
125
+ }
126
+ }
127
+ // If we found a string at the location, return it
128
+ return typeof current === 'string' ? [current] : [];
129
+ }));
130
+ }
131
+ inferFromConfig(config, durationTypeMap) {
132
+ const { key, valueType } = config;
133
+ switch (valueType) {
134
+ case 'STRING': {
135
+ return z.string();
136
+ }
137
+ case 'BOOL': {
138
+ return z.boolean();
139
+ }
140
+ case 'INT': {
141
+ return z.number().int();
142
+ }
143
+ case 'DOUBLE': {
144
+ return z.number();
145
+ }
146
+ case 'STRING_LIST': {
147
+ return z.array(z.string());
148
+ }
149
+ case 'DURATION': {
150
+ return durationTypeMap?.() ?? z.number();
151
+ }
152
+ case 'JSON': {
153
+ const jsonValues = this.getAllJsonValues(config);
154
+ this.log('JSON values:', JSON.stringify(jsonValues, null, 2));
155
+ if (jsonValues.length > 0) {
156
+ try {
157
+ // De-dup exact schemas
158
+ const schemas = {};
159
+ jsonValues.forEach((json) => {
160
+ const schema = new JsonToZodMapper().resolve(json);
161
+ const stringSchema = new ZodToStringMapper().renderField(schema);
162
+ schemas[stringSchema] = schema;
163
+ });
164
+ const schemaResults = Object.values(schemas);
165
+ // Return a single schema explicitly if we have only one defined
166
+ if (schemaResults.length === 1) {
167
+ return schemaResults[0];
168
+ }
169
+ // Return a union of schemas if we have multiple defined
170
+ return z.union(schemaResults);
171
+ }
172
+ catch (error) {
173
+ console.warn(`Error inferring JSON schema for ${key}:`, error);
174
+ }
175
+ }
176
+ return z.union([z.array(z.any()), z.record(z.string(), z.any())]);
177
+ }
178
+ case 'LOG_LEVEL': {
179
+ return z.enum(['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR']);
180
+ }
181
+ default: {
182
+ return z.any();
183
+ }
184
+ }
185
+ }
186
+ replaceStringsWithMustache(schema, config, schemaLocation = []) {
187
+ const typeName = schema.def.type;
188
+ // Handle enums explicitly
189
+ if (typeName === 'enum') {
190
+ return schema;
191
+ }
192
+ // Check for both direct string and optional string
193
+ if (schema instanceof z.ZodString ||
194
+ (schema instanceof z.ZodOptional && schema.def.innerType instanceof z.ZodString)) {
195
+ const stringsAtLocation = this.getAllStringsAtLocation(config, schemaLocation);
196
+ if (schema instanceof z.ZodOptional) {
197
+ return this.createZodFunctionFromMustacheStrings(stringsAtLocation).optional();
198
+ }
199
+ return this.createZodFunctionFromMustacheStrings(stringsAtLocation);
200
+ }
201
+ if (schema instanceof z.ZodObject) {
202
+ const newShape = {};
203
+ for (const [key, value] of Object.entries(schema.shape)) {
204
+ newShape[key] = this.replaceStringsWithMustache(value, config, [...schemaLocation, key]);
205
+ }
206
+ return z.object(newShape);
207
+ }
208
+ // NOTE: no support for Mustache in Arrays or Unions
209
+ // For all other types, just return as is
210
+ return schema;
211
+ }
212
+ // @ts-expect-error OK with not explicitly returning undefined
213
+ resolveUserSchema(config, configFile) {
214
+ const { schemaKey } = config;
215
+ if (!schemaKey) {
216
+ return;
217
+ }
218
+ const schemaFile = configFile.schemas?.find((schema) => {
219
+ const schemaPath = schema.path.replace(/\\/g, '/');
220
+ const derivedKey = schemaPath.split('/').at(-1)?.replace(/\.json$/, '');
221
+ return derivedKey === schemaKey;
222
+ });
223
+ if (schemaFile) {
224
+ const schema = schemaFile.schema;
225
+ if (isLegacySchemaWrapper(schema)) {
226
+ const legacySchema = schema;
227
+ const defaultSection = legacySchema.default;
228
+ const rules = Array.isArray(defaultSection?.rules) ? defaultSection.rules : [];
229
+ const firstRule = rules[0];
230
+ const value = firstRule?.value;
231
+ const legacySchemaValue = value?.schema;
232
+ const schemaStr = legacySchemaValue?.schema;
233
+ if (typeof schemaStr === 'string') {
234
+ const result = secureEvaluateSchema(schemaStr);
235
+ if (result.success && result.schema) {
236
+ this.log(`Successfully parsed legacy schema wrapper: ${schemaKey}`);
237
+ return result.schema;
238
+ }
239
+ console.warn(`Failed to parse legacy schema wrapper ${schemaKey}: ${result.error}`);
240
+ }
241
+ }
242
+ else {
243
+ this.log(`Successfully parsed JSON Schema document: ${schemaKey}`);
244
+ return jsonSchemaToZod(schema);
245
+ }
246
+ }
247
+ const legacySchemaConfig = configFile.configs.find((c) => c.key === schemaKey && c.configType === 'SCHEMA');
248
+ if (!legacySchemaConfig) {
249
+ return;
250
+ }
251
+ for (const row of legacySchemaConfig.rows) {
252
+ for (const valueObj of row.values) {
253
+ if (valueObj.value.schema?.schema) {
254
+ const schemaStr = valueObj.value.schema.schema;
255
+ const result = secureEvaluateSchema(schemaStr);
256
+ if (result.success && result.schema) {
257
+ this.log(`Successfully parsed schema from schema config: ${legacySchemaConfig.key}`);
258
+ return result.schema;
259
+ }
260
+ console.warn(`Failed to parse schema from schema config ${legacySchemaConfig.key}: ${result.error}`);
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { $ZodFunctionArgs, $ZodFunctionOut, util } from 'zod/v4/core';
3
+ export declare enum SupportedLanguage {
4
+ Node = "node-ts",
5
+ React = "react-ts"
6
+ }
7
+ export interface ConfigValue {
8
+ value: {
9
+ bool?: boolean;
10
+ int?: number;
11
+ json?: {
12
+ json: string;
13
+ };
14
+ logLevel?: string;
15
+ schema?: {
16
+ schema: string;
17
+ schemaType: string;
18
+ };
19
+ string?: string;
20
+ };
21
+ }
22
+ export interface SchemaFile {
23
+ path: string;
24
+ schema: Record<string, unknown>;
25
+ }
26
+ export interface ConfigRow {
27
+ values: ConfigValue[];
28
+ }
29
+ export interface Config {
30
+ configType: 'CONFIG' | 'FEATURE_FLAG' | 'SCHEMA';
31
+ key: string;
32
+ rows: ConfigRow[];
33
+ schemaKey?: string;
34
+ sendToClientSdk?: boolean;
35
+ valueType: 'BOOL' | 'DOUBLE' | 'DURATION' | 'INT' | 'JSON' | 'LOG_LEVEL' | 'STRING' | 'STRING_LIST';
36
+ }
37
+ export interface ConfigFile {
38
+ configs: Config[];
39
+ schemas?: SchemaFile[];
40
+ }
41
+ /**
42
+ * Supported Zod schema types for codegen
43
+ */
44
+ export type ZodTypeSupported = z.ZodAny | z.ZodArray<z.ZodTypeAny> | z.ZodBoolean | z.ZodDefault<z.ZodTypeAny> | z.ZodEnum<util.EnumLike> | z.ZodFunction<$ZodFunctionArgs, $ZodFunctionOut> | z.ZodNull | z.ZodLiteral<string | number | boolean | null> | z.ZodNumber | z.ZodObject<z.ZodRawShape> | z.ZodOptional<z.ZodTypeAny> | z.ZodRecord | z.ZodNever | z.ZodString | z.ZodTuple | z.ZodUndefined | z.ZodUnion<[z.ZodTypeAny, ...z.ZodTypeAny[]]> | z.ZodUnknown | z.ZodNullable<z.ZodTypeAny> | z.ZodTypeAny;
@@ -0,0 +1,5 @@
1
+ export var SupportedLanguage;
2
+ (function (SupportedLanguage) {
3
+ SupportedLanguage["Node"] = "node-ts";
4
+ SupportedLanguage["React"] = "react-ts";
5
+ })(SupportedLanguage || (SupportedLanguage = {}));