@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
package/bin/run.cmd
ADDED
package/bin/run.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { APICommand } from '../index.js';
|
|
2
|
+
export interface Environment {
|
|
3
|
+
active?: boolean;
|
|
4
|
+
deletedAt?: number | null;
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
protected?: boolean;
|
|
8
|
+
type?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const getEnvironments: (command: APICommand) => Promise<Environment[]>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const getEnvironments = async (command) => {
|
|
2
|
+
const request = await command.apiClient.post('/api/v1/environments/list', { workspaceId: command.workspaceId });
|
|
3
|
+
if (!request.ok) {
|
|
4
|
+
const errorMsg = request.error?.error || `Failed to fetch environments: ${request.status}`;
|
|
5
|
+
return command.err(errorMsg, { serverError: request.error });
|
|
6
|
+
}
|
|
7
|
+
// oRPC returns the array directly (not wrapped in { environments: [] })
|
|
8
|
+
const environments = (Array.isArray(request.json) ? request.json : request.json.environments);
|
|
9
|
+
// Filter out deleted environments and sort by name
|
|
10
|
+
return environments.filter((env) => !env.deletedAt).sort((a, b) => a.name.localeCompare(b.name));
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ConfigFile } from '../types.js';
|
|
2
|
+
export interface BaseGeneratorArgs {
|
|
3
|
+
configFile: ConfigFile;
|
|
4
|
+
log: (category: string | unknown, message?: unknown) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare abstract class BaseGenerator {
|
|
7
|
+
protected configFile: ConfigFile;
|
|
8
|
+
protected log: (category: string | unknown, message?: unknown) => void;
|
|
9
|
+
constructor({ configFile, log }: BaseGeneratorArgs);
|
|
10
|
+
abstract generate(): string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseGenerator, BaseGeneratorArgs } from './base-generator.js';
|
|
3
|
+
export declare abstract class BaseTypescriptGenerator extends BaseGenerator {
|
|
4
|
+
protected MUSTACHE_IMPORT: string;
|
|
5
|
+
private schemaExtractor;
|
|
6
|
+
constructor({ configFile, log }: BaseGeneratorArgs);
|
|
7
|
+
protected configurations(): {
|
|
8
|
+
configType: "CONFIG" | "FEATURE_FLAG" | "SCHEMA";
|
|
9
|
+
hasFunction: boolean;
|
|
10
|
+
key: string;
|
|
11
|
+
schema: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
12
|
+
sendToClientSdk: boolean;
|
|
13
|
+
}[];
|
|
14
|
+
protected durationTypeMap(): z.ZodTypeAny;
|
|
15
|
+
abstract declarationGenerate(): string;
|
|
16
|
+
abstract generate(): string;
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ZodToTypescriptMapper } from '../language-mappers/zod-to-typescript-mapper.js';
|
|
3
|
+
import { SchemaExtractor } from '../schema-extractor.js';
|
|
4
|
+
import { BaseGenerator } from './base-generator.js';
|
|
5
|
+
export class BaseTypescriptGenerator extends BaseGenerator {
|
|
6
|
+
MUSTACHE_IMPORT = "import Mustache from 'mustache'";
|
|
7
|
+
schemaExtractor;
|
|
8
|
+
constructor({ configFile, log }) {
|
|
9
|
+
super({ configFile, log });
|
|
10
|
+
this.schemaExtractor = new SchemaExtractor(log);
|
|
11
|
+
}
|
|
12
|
+
configurations() {
|
|
13
|
+
return this.configFile.configs
|
|
14
|
+
.filter((config) => config.configType === 'FEATURE_FLAG' || config.configType === 'CONFIG')
|
|
15
|
+
.filter((config) => config.rows.length > 0)
|
|
16
|
+
.sort((a, b) => a.key.localeCompare(b.key))
|
|
17
|
+
.map((config) => {
|
|
18
|
+
const schema = this.schemaExtractor.execute({
|
|
19
|
+
config,
|
|
20
|
+
configFile: this.configFile,
|
|
21
|
+
durationTypeMap: this.durationTypeMap,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
configType: config.configType,
|
|
25
|
+
hasFunction: schema && new ZodToTypescriptMapper().resolveType(schema).includes('=>'),
|
|
26
|
+
key: config.key,
|
|
27
|
+
schema,
|
|
28
|
+
sendToClientSdk: config.sendToClientSdk ?? false,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
durationTypeMap() {
|
|
33
|
+
return z.number();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseTypescriptGenerator } from './base-typescript-generator.js';
|
|
2
|
+
export declare class NodeTypeScriptGenerator extends BaseTypescriptGenerator {
|
|
3
|
+
declarationGenerate(): string;
|
|
4
|
+
generate(): string;
|
|
5
|
+
private additionalDependencies;
|
|
6
|
+
private generateAccessorMethods;
|
|
7
|
+
private generateSchemaTypes;
|
|
8
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { stripIndent } from 'common-tags';
|
|
2
|
+
import camelCase from 'lodash.camelcase';
|
|
3
|
+
import { ZodToTypescriptMapper } from '../language-mappers/zod-to-typescript-mapper.js';
|
|
4
|
+
import { ZodToTypescriptReturnValueMapper } from '../language-mappers/zod-to-typescript-return-value-mapper.js';
|
|
5
|
+
import { BaseTypescriptGenerator } from './base-typescript-generator.js';
|
|
6
|
+
export class NodeTypeScriptGenerator extends BaseTypescriptGenerator {
|
|
7
|
+
declarationGenerate() {
|
|
8
|
+
return (stripIndent `
|
|
9
|
+
/* eslint-disable */
|
|
10
|
+
// AUTOGENERATED by quonfig-cli's 'gen' command
|
|
11
|
+
// prettier-ignore
|
|
12
|
+
import "@quonfig/node";
|
|
13
|
+
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
declare module "@quonfig/node" {
|
|
16
|
+
export interface NodeServerConfigurationRaw {
|
|
17
|
+
${this.generateSchemaTypes('raw').join('\n ') || '// No types generated'}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NodeServerConfigurationAccessor {
|
|
21
|
+
${this.generateSchemaTypes().join('\n ') || '// No types generated'}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
` + '\n' // For adding newline to avoid prettier issues
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
generate() {
|
|
28
|
+
return (stripIndent `
|
|
29
|
+
/* eslint-disable */
|
|
30
|
+
// AUTOGENERATED by quonfig-cli's 'gen' command
|
|
31
|
+
// prettier-ignore
|
|
32
|
+
import { Quonfig, Contexts, TypedNodeServerConfigurationRaw, TypedNodeServerConfigurationAccessor } from '@quonfig/node'
|
|
33
|
+
// prettier-ignore
|
|
34
|
+
${this.additionalDependencies().join('\n// prettier-ignore\n') || '// No additional dependencies required'}
|
|
35
|
+
|
|
36
|
+
// prettier-ignore
|
|
37
|
+
type ContextObj = Record<string, Record<string, unknown>>
|
|
38
|
+
|
|
39
|
+
// prettier-ignore
|
|
40
|
+
export class QuonfigTypesafeNode {
|
|
41
|
+
constructor(public quonfig: Quonfig) { }
|
|
42
|
+
|
|
43
|
+
get<K extends keyof TypedNodeServerConfigurationRaw>(key: K, contexts?: Contexts | ContextObj): TypedNodeServerConfigurationRaw[K] {
|
|
44
|
+
return this.quonfig.get(key, contexts) as TypedNodeServerConfigurationRaw[K]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
${this.generateAccessorMethods().join('\n\n ') || '// No methods generated'}
|
|
48
|
+
}
|
|
49
|
+
` + '\n' // For adding newline to avoid prettier issues
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
additionalDependencies() {
|
|
53
|
+
const dependencies = [];
|
|
54
|
+
const hasFunctions = this.configurations().some((c) => c.hasFunction);
|
|
55
|
+
if (hasFunctions) {
|
|
56
|
+
dependencies.push(this.MUSTACHE_IMPORT);
|
|
57
|
+
}
|
|
58
|
+
return dependencies;
|
|
59
|
+
}
|
|
60
|
+
generateAccessorMethods() {
|
|
61
|
+
const uniqueMethods = {};
|
|
62
|
+
const schemaTypes = this.configurations().map((config) => {
|
|
63
|
+
let methodName = camelCase(config.key);
|
|
64
|
+
// If the method name starts with a digit, prefix it with an underscore to ensure method name is valid
|
|
65
|
+
if (/^\d/.test(methodName)) {
|
|
66
|
+
methodName = `_${methodName}`;
|
|
67
|
+
}
|
|
68
|
+
if (uniqueMethods[methodName]) {
|
|
69
|
+
throw new Error(`Method '${methodName}' is already registered. Quonfig key ${config.key} conflicts with '${uniqueMethods[methodName]}'!`);
|
|
70
|
+
}
|
|
71
|
+
uniqueMethods[methodName] = config.key;
|
|
72
|
+
if (config.hasFunction) {
|
|
73
|
+
const returnValue = new ZodToTypescriptReturnValueMapper().resolveType(config.schema);
|
|
74
|
+
return stripIndent `
|
|
75
|
+
${methodName}(contexts?: Contexts | ContextObj): TypedNodeServerConfigurationAccessor['${config.key}'] {
|
|
76
|
+
const raw = this.get('${config.key}', contexts)
|
|
77
|
+
return ${returnValue}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
return stripIndent `
|
|
82
|
+
${methodName}(contexts?: Contexts | ContextObj): TypedNodeServerConfigurationAccessor['${config.key}'] {
|
|
83
|
+
return this.get('${config.key}', contexts)
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
});
|
|
87
|
+
return schemaTypes;
|
|
88
|
+
}
|
|
89
|
+
generateSchemaTypes(target = 'accessor') {
|
|
90
|
+
const schemaTypes = this.configurations().flatMap((config) => {
|
|
91
|
+
const mapper = new ZodToTypescriptMapper({ fieldName: config.key, target });
|
|
92
|
+
return mapper.renderField(config.schema);
|
|
93
|
+
});
|
|
94
|
+
return schemaTypes;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseTypescriptGenerator } from './base-typescript-generator.js';
|
|
3
|
+
export declare class ReactTypeScriptGenerator extends BaseTypescriptGenerator {
|
|
4
|
+
declarationGenerate(): string;
|
|
5
|
+
protected durationTypeMap(): z.ZodTypeAny;
|
|
6
|
+
generate(): string;
|
|
7
|
+
private additionalDependencies;
|
|
8
|
+
private filteredConfigurations;
|
|
9
|
+
private generateAccessorMethods;
|
|
10
|
+
private generateSchemaTypes;
|
|
11
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { stripIndent } from 'common-tags';
|
|
2
|
+
import camelCase from 'lodash.camelcase';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ZodToTypescriptMapper } from '../language-mappers/zod-to-typescript-mapper.js';
|
|
5
|
+
import { ZodToTypescriptReturnValueMapper } from '../language-mappers/zod-to-typescript-return-value-mapper.js';
|
|
6
|
+
import { BaseTypescriptGenerator } from './base-typescript-generator.js';
|
|
7
|
+
export class ReactTypeScriptGenerator extends BaseTypescriptGenerator {
|
|
8
|
+
declarationGenerate() {
|
|
9
|
+
return (stripIndent `
|
|
10
|
+
/* eslint-disable */
|
|
11
|
+
// AUTOGENERATED by quonfig-cli's 'gen' command
|
|
12
|
+
// prettier-ignore
|
|
13
|
+
import "@quonfig/javascript";
|
|
14
|
+
// prettier-ignore
|
|
15
|
+
import "@quonfig/react";
|
|
16
|
+
|
|
17
|
+
// prettier-ignore
|
|
18
|
+
declare module "@quonfig/javascript" {
|
|
19
|
+
export interface FrontEndConfigurationRaw {
|
|
20
|
+
${this.generateSchemaTypes('raw').join('\n ') || '// No types generated'}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Force TypedFrontEndConfigurationRaw to always use our merged interface
|
|
24
|
+
export type TypedFrontEndConfigurationRaw = FrontEndConfigurationRaw;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
declare module "@quonfig/react" {
|
|
29
|
+
export interface FrontEndConfigurationAccessor {
|
|
30
|
+
${this.generateSchemaTypes().join('\n ') || '// No types generated'}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Force TypedFrontEndConfigurationRaw to always use our merged interface
|
|
34
|
+
export type TypedFrontEndConfigurationAccessor = FrontEndConfigurationAccessor;
|
|
35
|
+
}
|
|
36
|
+
` + '\n' // For adding newline to avoid prettier issues
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
durationTypeMap() {
|
|
40
|
+
return z.object({ ms: z.number(), seconds: z.number() });
|
|
41
|
+
}
|
|
42
|
+
generate() {
|
|
43
|
+
return (stripIndent `
|
|
44
|
+
/* eslint-disable */
|
|
45
|
+
// AUTOGENERATED by quonfig-cli's 'gen' command
|
|
46
|
+
// prettier-ignore
|
|
47
|
+
import { Quonfig, TypedFrontEndConfigurationRaw } from "@quonfig/javascript"
|
|
48
|
+
// prettier-ignore
|
|
49
|
+
import { createQuonfigHook, TypedFrontEndConfigurationAccessor } from "@quonfig/react"
|
|
50
|
+
// prettier-ignore
|
|
51
|
+
${this.additionalDependencies().join('\n// prettier-ignore\n') || '// No additional dependencies required'}
|
|
52
|
+
|
|
53
|
+
// prettier-ignore
|
|
54
|
+
export class QuonfigTypesafeReact {
|
|
55
|
+
constructor(public quonfig: Quonfig) { }
|
|
56
|
+
|
|
57
|
+
get<K extends keyof TypedFrontEndConfigurationRaw>(key: K): TypedFrontEndConfigurationRaw[K] {
|
|
58
|
+
return this.quonfig.get(key) as TypedFrontEndConfigurationRaw[K]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
${this.generateAccessorMethods().join('\n\n ') || '// No methods generated'}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// prettier-ignore
|
|
65
|
+
export const useQuonfig = createQuonfigHook(QuonfigTypesafeReact)
|
|
66
|
+
` + '\n' // For adding newline to avoid prettier issues
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
additionalDependencies() {
|
|
70
|
+
const dependencies = [];
|
|
71
|
+
const hasFunctions = this.filteredConfigurations().some((c) => c.hasFunction);
|
|
72
|
+
if (hasFunctions) {
|
|
73
|
+
dependencies.push(this.MUSTACHE_IMPORT);
|
|
74
|
+
}
|
|
75
|
+
return dependencies;
|
|
76
|
+
}
|
|
77
|
+
filteredConfigurations() {
|
|
78
|
+
return this.configurations().filter((config) => config.configType === 'FEATURE_FLAG' || config.sendToClientSdk === true);
|
|
79
|
+
}
|
|
80
|
+
generateAccessorMethods() {
|
|
81
|
+
const uniqueMethods = {};
|
|
82
|
+
const schemaTypes = this.filteredConfigurations().map((config) => {
|
|
83
|
+
let methodName = camelCase(config.key);
|
|
84
|
+
// If the method name starts with a digit, prefix it with an underscore to ensure method name is valid
|
|
85
|
+
if (/^\d/.test(methodName)) {
|
|
86
|
+
methodName = `_${methodName}`;
|
|
87
|
+
}
|
|
88
|
+
if (uniqueMethods[methodName]) {
|
|
89
|
+
throw new Error(`Method '${methodName}' is already registered. Quonfig key ${config.key} conflicts with '${uniqueMethods[methodName]}'!`);
|
|
90
|
+
}
|
|
91
|
+
uniqueMethods[methodName] = config.key;
|
|
92
|
+
if (config.hasFunction) {
|
|
93
|
+
const returnValue = new ZodToTypescriptReturnValueMapper().resolveType(config.schema);
|
|
94
|
+
return stripIndent `
|
|
95
|
+
${methodName}(): TypedFrontEndConfigurationAccessor['${config.key}'] {
|
|
96
|
+
const raw = this.get('${config.key}')
|
|
97
|
+
return ${returnValue}
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
}
|
|
101
|
+
return stripIndent `
|
|
102
|
+
get ${methodName}(): TypedFrontEndConfigurationAccessor['${config.key}'] {
|
|
103
|
+
return this.get('${config.key}')
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
});
|
|
107
|
+
return schemaTypes;
|
|
108
|
+
}
|
|
109
|
+
generateSchemaTypes(target = 'accessor') {
|
|
110
|
+
const schemaTypes = this.filteredConfigurations().map((config) => {
|
|
111
|
+
const mapper = new ZodToTypescriptMapper({ fieldName: config.key, target });
|
|
112
|
+
return mapper.renderField(config.schema);
|
|
113
|
+
});
|
|
114
|
+
return schemaTypes;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class ConfigDownloader {
|
|
2
|
+
command;
|
|
3
|
+
constructor(command) {
|
|
4
|
+
this.command = command;
|
|
5
|
+
}
|
|
6
|
+
async downloadConfig() {
|
|
7
|
+
try {
|
|
8
|
+
const response = (await this.command.apiClient.get('/all-config-types/v1/download-all-envs'));
|
|
9
|
+
if (!response.ok) {
|
|
10
|
+
throw new Error(`Failed to download config: ${response.status}`);
|
|
11
|
+
}
|
|
12
|
+
// Parse the response data - configs are nested in response.data.json
|
|
13
|
+
const configData = response.json;
|
|
14
|
+
// Print out each config key
|
|
15
|
+
this.command.verboseLog('\nFound configurations:', configData.configs?.length || 0);
|
|
16
|
+
if (!configData.configs) {
|
|
17
|
+
throw new Error('Invalid response format');
|
|
18
|
+
}
|
|
19
|
+
return configData;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error('Error downloading config:', error);
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
function toMeta(schema) {
|
|
6
|
+
const meta = {};
|
|
7
|
+
if (typeof schema.title === 'string') {
|
|
8
|
+
meta.title = schema.title;
|
|
9
|
+
}
|
|
10
|
+
if (typeof schema.description === 'string') {
|
|
11
|
+
meta.description = schema.description;
|
|
12
|
+
}
|
|
13
|
+
return Object.keys(meta).length > 0 ? meta : undefined;
|
|
14
|
+
}
|
|
15
|
+
function applyMeta(schema, meta) {
|
|
16
|
+
return meta ? schema.meta(meta) : schema;
|
|
17
|
+
}
|
|
18
|
+
function literalFromEnum(values) {
|
|
19
|
+
if (values.length === 0) {
|
|
20
|
+
return z.never();
|
|
21
|
+
}
|
|
22
|
+
if (values.every((value) => typeof value === 'string')) {
|
|
23
|
+
return z.enum(values);
|
|
24
|
+
}
|
|
25
|
+
if (values.length === 1) {
|
|
26
|
+
return z.literal(values[0]);
|
|
27
|
+
}
|
|
28
|
+
const literals = values.map((value) => z.literal(value));
|
|
29
|
+
return z.union(literals);
|
|
30
|
+
}
|
|
31
|
+
function schemaFromTypeArray(schema, types) {
|
|
32
|
+
const nonNullTypes = types.filter((item) => item !== 'null');
|
|
33
|
+
const includesNull = nonNullTypes.length !== types.length;
|
|
34
|
+
if (nonNullTypes.length === 0 && includesNull) {
|
|
35
|
+
return z.null();
|
|
36
|
+
}
|
|
37
|
+
if (nonNullTypes.length === 1) {
|
|
38
|
+
const resolved = schemaToZod({ ...schema, type: nonNullTypes[0] });
|
|
39
|
+
return includesNull ? resolved.nullable() : resolved;
|
|
40
|
+
}
|
|
41
|
+
const resolved = z.union(nonNullTypes.map((type) => schemaToZod({ ...schema, type })));
|
|
42
|
+
return includesNull ? resolved.nullable() : resolved;
|
|
43
|
+
}
|
|
44
|
+
function schemaFromObject(schema) {
|
|
45
|
+
const properties = isObject(schema.properties) ? schema.properties : {};
|
|
46
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required.filter((item) => typeof item === 'string') : []);
|
|
47
|
+
const shape = {};
|
|
48
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
49
|
+
const propertySchema = schemaToZod(value);
|
|
50
|
+
shape[key] = required.has(key) ? propertySchema : propertySchema.optional();
|
|
51
|
+
}
|
|
52
|
+
const base = z.object(shape);
|
|
53
|
+
if (schema.additionalProperties === false) {
|
|
54
|
+
return base.strict();
|
|
55
|
+
}
|
|
56
|
+
if (isObject(schema.additionalProperties) && Object.keys(properties).length === 0) {
|
|
57
|
+
return z.record(z.string(), schemaToZod(schema.additionalProperties));
|
|
58
|
+
}
|
|
59
|
+
return base;
|
|
60
|
+
}
|
|
61
|
+
function schemaToZod(schema) {
|
|
62
|
+
if (!isObject(schema)) {
|
|
63
|
+
return z.any();
|
|
64
|
+
}
|
|
65
|
+
const meta = toMeta(schema);
|
|
66
|
+
let result;
|
|
67
|
+
if (Array.isArray(schema.enum)) {
|
|
68
|
+
result = literalFromEnum(schema.enum);
|
|
69
|
+
}
|
|
70
|
+
else if (schema.const !== undefined) {
|
|
71
|
+
result = z.literal(schema.const);
|
|
72
|
+
}
|
|
73
|
+
else if (Array.isArray(schema.type)) {
|
|
74
|
+
result = schemaFromTypeArray(schema, schema.type);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
switch (schema.type) {
|
|
78
|
+
case 'string': {
|
|
79
|
+
result = z.string();
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case 'number': {
|
|
83
|
+
result = z.number();
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'integer': {
|
|
87
|
+
result = z.number().int();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case 'boolean': {
|
|
91
|
+
result = z.boolean();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'null': {
|
|
95
|
+
result = z.null();
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case 'array': {
|
|
99
|
+
if (Array.isArray(schema.prefixItems)) {
|
|
100
|
+
const items = schema.prefixItems.map((item) => schemaToZod(item));
|
|
101
|
+
result = z.tuple(items);
|
|
102
|
+
}
|
|
103
|
+
else if (schema.items !== undefined) {
|
|
104
|
+
result = z.array(schemaToZod(schema.items));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
result = z.array(z.any());
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case 'object':
|
|
112
|
+
case undefined: {
|
|
113
|
+
if (schema.properties || schema.additionalProperties !== undefined || schema.required) {
|
|
114
|
+
result = schemaFromObject(schema);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
result = z.object({});
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
default: {
|
|
122
|
+
if (Array.isArray(schema.oneOf)) {
|
|
123
|
+
const options = schema.oneOf.map((item) => schemaToZod(item));
|
|
124
|
+
result = options.length === 1 ? options[0] : z.union(options);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(schema.anyOf)) {
|
|
128
|
+
const options = schema.anyOf.map((item) => schemaToZod(item));
|
|
129
|
+
result = options.length === 1 ? options[0] : z.union(options);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (Array.isArray(schema.allOf) && schema.allOf.length === 1) {
|
|
133
|
+
result = schemaToZod(schema.allOf[0]);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (schema.not === false) {
|
|
137
|
+
result = z.never();
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
result = z.any();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (schema.default !== undefined) {
|
|
145
|
+
result = result.default(schema.default);
|
|
146
|
+
}
|
|
147
|
+
result = applyMeta(result, meta);
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
export function isLegacySchemaWrapper(schema) {
|
|
151
|
+
if (!isObject(schema)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const defaultSection = schema.default;
|
|
155
|
+
if (!isObject(defaultSection) || !Array.isArray(defaultSection.rules)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
const firstRule = defaultSection.rules[0];
|
|
159
|
+
if (!isObject(firstRule)) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const value = firstRule.value;
|
|
163
|
+
if (!isObject(value) || !isObject(value.schema)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return typeof value.schema.schema === 'string';
|
|
167
|
+
}
|
|
168
|
+
export function jsonSchemaToZod(schema) {
|
|
169
|
+
return schemaToZod(schema);
|
|
170
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export class JsonToZodMapper {
|
|
3
|
+
resolve(data) {
|
|
4
|
+
if (Array.isArray(data)) {
|
|
5
|
+
if (data.length > 0) {
|
|
6
|
+
// Check if all elements in the array have the same type
|
|
7
|
+
const firstItem = data[0];
|
|
8
|
+
const isHomogeneous = data.every((item) => {
|
|
9
|
+
const itemsMatch = typeof item === typeof firstItem;
|
|
10
|
+
// Special handling for objects and arrays
|
|
11
|
+
if (typeof firstItem === 'object') {
|
|
12
|
+
if (Array.isArray(item)) {
|
|
13
|
+
return Array.isArray(firstItem);
|
|
14
|
+
}
|
|
15
|
+
return !Array.isArray(firstItem);
|
|
16
|
+
}
|
|
17
|
+
return itemsMatch;
|
|
18
|
+
});
|
|
19
|
+
// For homogeneous arrays, use the first element's type
|
|
20
|
+
if (isHomogeneous) {
|
|
21
|
+
return z.array(this.resolve(data[0]));
|
|
22
|
+
}
|
|
23
|
+
// Explicitly do not handle mixed-type arrays
|
|
24
|
+
// They could be tuples or heterogeneous arrays
|
|
25
|
+
// Instead, we return an array of unknowns
|
|
26
|
+
}
|
|
27
|
+
return z.array(z.unknown());
|
|
28
|
+
}
|
|
29
|
+
if (typeof data === 'object' && data !== null) {
|
|
30
|
+
const shape = {};
|
|
31
|
+
const dataRecord = data;
|
|
32
|
+
for (const key in dataRecord) {
|
|
33
|
+
if (Object.hasOwn(dataRecord, key)) {
|
|
34
|
+
shape[key] = this.resolve(dataRecord[key]);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return z.object(shape);
|
|
38
|
+
}
|
|
39
|
+
if (typeof data === 'string') {
|
|
40
|
+
return z.string();
|
|
41
|
+
}
|
|
42
|
+
if (typeof data === 'number') {
|
|
43
|
+
return z.number();
|
|
44
|
+
}
|
|
45
|
+
if (typeof data === 'boolean') {
|
|
46
|
+
return z.boolean();
|
|
47
|
+
}
|
|
48
|
+
if (data === null) {
|
|
49
|
+
return z.null();
|
|
50
|
+
}
|
|
51
|
+
console.warn(`Unknown json type:`, data);
|
|
52
|
+
// If the type is not recognized, default to 'any'
|
|
53
|
+
return z.any();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { $ZodFunctionArgs, $ZodFunctionOut, $ZodType, util } from 'zod/v4/core';
|
|
3
|
+
export declare abstract class ZodBaseMapper {
|
|
4
|
+
resolveType(type: $ZodType): string;
|
|
5
|
+
/**
|
|
6
|
+
* Hook for handling meta descriptions on schemas.
|
|
7
|
+
* By default, this just returns the resolved type without any modification.
|
|
8
|
+
* Subclasses can override this to add comments or other metadata handling.
|
|
9
|
+
*
|
|
10
|
+
* @param description The description from .meta({ description: "..." })
|
|
11
|
+
* @param resolveType A function that resolves the inner type
|
|
12
|
+
* @returns The type string, potentially with description metadata
|
|
13
|
+
*/
|
|
14
|
+
protected withMeta(description: string, resolveType: () => string): string;
|
|
15
|
+
private resolveTypeInternal;
|
|
16
|
+
protected abstract any(): string;
|
|
17
|
+
protected abstract array(wrappedType: string): string;
|
|
18
|
+
protected abstract boolean(): string;
|
|
19
|
+
protected abstract enum(values: util.EnumValue[]): string;
|
|
20
|
+
protected abstract function(args: string, returns: string): string;
|
|
21
|
+
protected abstract functionArguments(value?: $ZodFunctionArgs): string;
|
|
22
|
+
protected abstract functionReturns(value: $ZodFunctionOut): string;
|
|
23
|
+
protected abstract null(): string;
|
|
24
|
+
protected abstract literal(value: string | number | boolean | null): string;
|
|
25
|
+
protected abstract number(isInteger: boolean): string;
|
|
26
|
+
protected abstract object(properties: [string, z.ZodTypeAny][]): string;
|
|
27
|
+
protected abstract optional(wrappedType: string): string;
|
|
28
|
+
protected abstract never(): string;
|
|
29
|
+
protected abstract record(keyType: string, valueType: string): string;
|
|
30
|
+
protected abstract string(): string;
|
|
31
|
+
protected abstract tuple(wrappedTypes: string[]): string;
|
|
32
|
+
protected abstract undefined(): string;
|
|
33
|
+
protected abstract union(wrappedTypes: string[]): string;
|
|
34
|
+
protected abstract unknown(): string;
|
|
35
|
+
}
|