33/**
44 * This script generates argument definitions and updates:
55 * - server.json arrays
6- * - TODO: README.md configuration table
6+ * - README.md configuration table
77 *
88 * It uses the Zod schema and OPTIONS defined in src/common/config.ts
99 */
1010
1111import { readFileSync , writeFileSync } from "fs" ;
1212import { join , dirname } from "path" ;
1313import { fileURLToPath } from "url" ;
14- import { OPTIONS , UserConfigSchema } from "../src/common/config.js" ;
15- import type { ZodObject , ZodRawShape } from "zod" ;
14+ import { OPTIONS , UserConfigSchema , defaultUserConfig , configRegistry } from "../src/common/config.js" ;
15+ import assert from "assert" ;
16+ import { execSync } from "child_process" ;
1617
1718const __filename = fileURLToPath ( import . meta. url ) ;
1819const __dirname = dirname ( __filename ) ;
@@ -21,14 +22,12 @@ function camelCaseToSnakeCase(str: string): string {
2122 return str . replace ( / [ A - Z ] / g, ( letter ) => `_${ letter } ` ) . toUpperCase ( ) ;
2223}
2324
24- // List of configuration keys that contain sensitive/secret information
25+ // List of mongosh OPTIONS that contain sensitive/secret information
2526// These should be redacted in logs and marked as secret in environment variable definitions
26- const SECRET_CONFIG_KEYS = new Set ( [
27+ const SECRET_OPTIONS_KEYS = new Set ( [
2728 "connectionString" ,
2829 "username" ,
2930 "password" ,
30- "apiClientId" ,
31- "apiClientSecret" ,
3231 "tlsCAFile" ,
3332 "tlsCertificateKeyFile" ,
3433 "tlsCertificateKeyFilePassword" ,
@@ -37,56 +36,74 @@ const SECRET_CONFIG_KEYS = new Set([
3736 "sslPEMKeyFile" ,
3837 "sslPEMKeyPassword" ,
3938 "sslCRLFile" ,
40- "voyageApiKey" ,
4139] ) ;
4240
43- interface EnvironmentVariable {
41+ interface ArgumentInfo {
4442 name : string ;
4543 description : string ;
4644 isRequired : boolean ;
4745 format : string ;
4846 isSecret : boolean ;
4947 configKey : string ;
5048 defaultValue ?: unknown ;
49+ defaultValueDescription ?: string ;
5150}
5251
5352interface ConfigMetadata {
5453 description : string ;
5554 defaultValue ?: unknown ;
55+ defaultValueDescription ?: string ;
56+ isSecret ?: boolean ;
5657}
5758
5859function extractZodDescriptions ( ) : Record < string , ConfigMetadata > {
5960 const result : Record < string , ConfigMetadata > = { } ;
6061
6162 // Get the shape of the Zod schema
62- const shape = ( UserConfigSchema as ZodObject < ZodRawShape > ) . shape ;
63+ const shape = UserConfigSchema . shape ;
6364
6465 for ( const [ key , fieldSchema ] of Object . entries ( shape ) ) {
6566 const schema = fieldSchema ;
6667 // Extract description from Zod schema
67- const description = schema . description || `Configuration option: ${ key } ` ;
68+ let description = schema . description || `Configuration option: ${ key } ` ;
69+
70+ if ( "innerType" in schema . def ) {
71+ if ( schema . def . innerType . def . type === "array" ) {
72+ assert (
73+ description . startsWith ( "An array of" ) ,
74+ `Field description for field "${ key } " with array type does not start with 'An array of'`
75+ ) ;
76+ description = description . replace ( "An array of" , "Comma separated values of" ) ;
77+ }
78+ }
6879
6980 // Extract default value if present
7081 let defaultValue : unknown = undefined ;
71- if ( schema . _def && "defaultValue" in schema . _def ) {
72- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
73- defaultValue = schema . _def . defaultValue ( ) as unknown ;
82+ let defaultValueDescription : string | undefined = undefined ;
83+ let isSecret : boolean | undefined = undefined ;
84+ if ( schema . def && "defaultValue" in schema . def ) {
85+ defaultValue = schema . def . defaultValue ;
86+ }
87+ // Get metadata from custom registry
88+ const registryMeta = configRegistry . get ( schema ) ;
89+ if ( registryMeta ) {
90+ defaultValueDescription = registryMeta . defaultValueDescription ;
91+ isSecret = registryMeta . isSecret ;
7492 }
7593
7694 result [ key ] = {
7795 description,
7896 defaultValue,
97+ defaultValueDescription,
98+ isSecret,
7999 } ;
80100 }
81101
82102 return result ;
83103}
84104
85- function generateEnvironmentVariables (
86- options : typeof OPTIONS ,
87- zodMetadata : Record < string , ConfigMetadata >
88- ) : EnvironmentVariable [ ] {
89- const envVars : EnvironmentVariable [ ] = [ ] ;
105+ function getArgumentInfo ( options : typeof OPTIONS , zodMetadata : Record < string , ConfigMetadata > ) : ArgumentInfo [ ] {
106+ const argumentInfos : ArgumentInfo [ ] = [ ] ;
90107 const processedKeys = new Set < string > ( ) ;
91108
92109 // Helper to add env var
@@ -107,14 +124,15 @@ function generateEnvironmentVariables(
107124 format = "string" ; // Arrays are passed as comma-separated strings
108125 }
109126
110- envVars . push ( {
127+ argumentInfos . push ( {
111128 name : envVarName ,
112129 description : metadata . description ,
113130 isRequired : false ,
114131 format : format ,
115- isSecret : SECRET_CONFIG_KEYS . has ( key ) ,
132+ isSecret : metadata . isSecret ?? SECRET_OPTIONS_KEYS . has ( key ) ,
116133 configKey : key ,
117134 defaultValue : metadata . defaultValue ,
135+ defaultValueDescription : metadata . defaultValueDescription ,
118136 } ) ;
119137 } ;
120138
@@ -139,10 +157,10 @@ function generateEnvironmentVariables(
139157 }
140158
141159 // Sort by name for consistent output
142- return envVars . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
160+ return argumentInfos . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
143161}
144162
145- function generatePackageArguments ( envVars : EnvironmentVariable [ ] ) : unknown [ ] {
163+ function generatePackageArguments ( envVars : ArgumentInfo [ ] ) : unknown [ ] {
146164 const packageArguments : unknown [ ] = [ ] ;
147165
148166 // Generate positional arguments from the same config options (only documented ones)
@@ -168,7 +186,7 @@ function generatePackageArguments(envVars: EnvironmentVariable[]): unknown[] {
168186 return packageArguments ;
169187}
170188
171- function updateServerJsonEnvVars ( envVars : EnvironmentVariable [ ] ) : void {
189+ function updateServerJsonEnvVars ( envVars : ArgumentInfo [ ] ) : void {
172190 const serverJsonPath = join ( __dirname , ".." , "server.json" ) ;
173191 const packageJsonPath = join ( __dirname , ".." , "package.json" ) ;
174192
@@ -179,7 +197,7 @@ function updateServerJsonEnvVars(envVars: EnvironmentVariable[]): void {
179197 packages : {
180198 registryType ?: string ;
181199 identifier : string ;
182- environmentVariables : EnvironmentVariable [ ] ;
200+ environmentVariables : ArgumentInfo [ ] ;
183201 packageArguments ?: unknown [ ] ;
184202 version ?: string ;
185203 } [ ] ;
@@ -207,7 +225,7 @@ function updateServerJsonEnvVars(envVars: EnvironmentVariable[]): void {
207225 // Update environmentVariables, packageArguments, and version for all packages
208226 if ( serverJson . packages && Array . isArray ( serverJson . packages ) ) {
209227 for ( const pkg of serverJson . packages ) {
210- pkg . environmentVariables = envVarsArray as EnvironmentVariable [ ] ;
228+ pkg . environmentVariables = envVarsArray as ArgumentInfo [ ] ;
211229 pkg . packageArguments = packageArguments ;
212230
213231 // For OCI packages, update the version tag in the identifier and not a version field
@@ -224,11 +242,72 @@ function updateServerJsonEnvVars(envVars: EnvironmentVariable[]): void {
224242 console . log ( `✓ Updated server.json (version ${ version } )` ) ;
225243}
226244
245+ function generateReadmeConfigTable ( argumentInfos : ArgumentInfo [ ] ) : string {
246+ const rows = [
247+ "| CLI Option | Environment Variable | Default | Description |" ,
248+ "| -------------------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |" ,
249+ ] ;
250+
251+ // Filter to only include options that are in the Zod schema (documented options)
252+ const documentedVars = argumentInfos . filter ( ( v ) => ! v . description . startsWith ( "Configuration option:" ) ) ;
253+
254+ for ( const argumentInfo of documentedVars ) {
255+ const cliOption = `\`${ argumentInfo . configKey } \`` ;
256+ const envVarName = `\`${ argumentInfo . name } \`` ;
257+
258+ // Get default value from Zod schema or fallback to defaultUserConfig
259+ const config = defaultUserConfig as unknown as Record < string , unknown > ;
260+ const defaultValue = argumentInfo . defaultValue ?? config [ argumentInfo . configKey ] ;
261+
262+ let defaultValueString = argumentInfo . defaultValueDescription ?? "`<not set>`" ;
263+ if ( ! argumentInfo . defaultValueDescription && defaultValue !== undefined && defaultValue !== null ) {
264+ if ( Array . isArray ( defaultValue ) ) {
265+ defaultValueString = `\`"${ defaultValue . join ( "," ) } "\`` ;
266+ } else if ( typeof defaultValue === "number" ) {
267+ defaultValueString = `\`${ defaultValue } \`` ;
268+ } else if ( typeof defaultValue === "boolean" ) {
269+ defaultValueString = `\`${ defaultValue } \`` ;
270+ } else if ( typeof defaultValue === "string" ) {
271+ defaultValueString = `\`"${ defaultValue } "\`` ;
272+ } else {
273+ throw new Error ( `Unsupported default value type: ${ typeof defaultValue } ` ) ;
274+ }
275+ }
276+
277+ const desc = argumentInfo . description . replace ( / \| / g, "\\|" ) ; // Escape pipes in description
278+ rows . push (
279+ `| ${ cliOption . padEnd ( 38 ) } | ${ envVarName . padEnd ( 51 ) } | ${ defaultValueString . padEnd ( 75 ) } | ${ desc . padEnd ( 199 ) } |`
280+ ) ;
281+ }
282+
283+ return rows . join ( "\n" ) ;
284+ }
285+
286+ function updateReadmeConfigTable ( envVars : ArgumentInfo [ ] ) : void {
287+ const readmePath = join ( __dirname , ".." , "README.md" ) ;
288+ let content = readFileSync ( readmePath , "utf-8" ) ;
289+
290+ const newTable = generateReadmeConfigTable ( envVars ) ;
291+
292+ // Find and replace the configuration options table
293+ const tableRegex = / # # # C o n f i g u r a t i o n O p t i o n s \n \n \| C L I O p t i o n [ \s \S ] * ?\n \n # # # # / ;
294+ const replacement = `### Configuration Options\n\n${ newTable } \n\n####` ;
295+
296+ content = content . replace ( tableRegex , replacement ) ;
297+
298+ writeFileSync ( readmePath , content , "utf-8" ) ;
299+ console . log ( "✓ Updated README.md configuration table" ) ;
300+
301+ // Run prettier on the README.md file
302+ execSync ( "npx prettier --write README.md" , { cwd : join ( __dirname , ".." ) } ) ;
303+ }
304+
227305function main ( ) : void {
228306 const zodMetadata = extractZodDescriptions ( ) ;
229307
230- const envVars = generateEnvironmentVariables ( OPTIONS , zodMetadata ) ;
231- updateServerJsonEnvVars ( envVars ) ;
308+ const argumentInfo = getArgumentInfo ( OPTIONS , zodMetadata ) ;
309+ updateServerJsonEnvVars ( argumentInfo ) ;
310+ updateReadmeConfigTable ( argumentInfo ) ;
232311}
233312
234313main ( ) ;
0 commit comments