@config-bound/schema-export 0.1.0

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.
@@ -0,0 +1,188 @@
1
+ import {
2
+ formatAsJSON,
3
+ formatAsYAML,
4
+ formatAsEnvExample
5
+ } from './schemaFormatters.js';
6
+ import { ExportedSchema } from './schemaExporter.js';
7
+
8
+ describe('Schema Formatters', () => {
9
+ const mockSchema: ExportedSchema = {
10
+ name: 'testApp',
11
+ sections: [
12
+ {
13
+ name: 'app',
14
+ description: 'Application settings',
15
+ elements: [
16
+ {
17
+ name: 'port',
18
+ description: 'Server port',
19
+ type: 'number',
20
+ default: 3000,
21
+ example: 8080,
22
+ required: false,
23
+ sensitive: false,
24
+ joiValidation: {
25
+ type: 'number',
26
+ rules: [
27
+ { name: 'min', args: { limit: 1 } },
28
+ { name: 'max', args: { limit: 65535 } }
29
+ ]
30
+ }
31
+ },
32
+ {
33
+ name: 'apiKey',
34
+ description: 'API key',
35
+ type: 'string',
36
+ default: undefined,
37
+ example: 'sk_test_123',
38
+ required: true,
39
+ sensitive: true,
40
+ joiValidation: { type: 'string', flags: { presence: 'required' } }
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ name: 'database',
46
+ description: 'Database configuration',
47
+ elements: [
48
+ {
49
+ name: 'host',
50
+ description: 'Database host',
51
+ type: 'string',
52
+ default: 'localhost',
53
+ example: undefined,
54
+ required: false,
55
+ sensitive: false,
56
+ joiValidation: { type: 'string' }
57
+ }
58
+ ]
59
+ }
60
+ ]
61
+ };
62
+
63
+ describe('formatAsJSON', () => {
64
+ it('should format schema as pretty JSON', () => {
65
+ const result = formatAsJSON(mockSchema, true);
66
+
67
+ expect(result).toContain('"name": "testApp"');
68
+ expect(result).toContain('"port"');
69
+ expect(result).toContain('"database"');
70
+ expect(JSON.parse(result)).toEqual(mockSchema);
71
+ });
72
+
73
+ it('should format schema as compact JSON', () => {
74
+ const result = formatAsJSON(mockSchema, false);
75
+
76
+ expect(result).not.toContain('\n');
77
+ expect(JSON.parse(result)).toEqual(mockSchema);
78
+ });
79
+ });
80
+
81
+ describe('formatAsYAML', () => {
82
+ it('should format schema as YAML', () => {
83
+ const result = formatAsYAML(mockSchema);
84
+
85
+ expect(result).toContain('name: testApp');
86
+ expect(result).toContain('- name: port');
87
+ expect(result).toContain('- name: database');
88
+ expect(result).toBeTruthy();
89
+ });
90
+ });
91
+
92
+ describe('formatAsEnvExample', () => {
93
+ it('should format schema as .env.example without prefix', () => {
94
+ const result = formatAsEnvExample(mockSchema);
95
+
96
+ expect(result).toContain('# testApp Configuration');
97
+ expect(result).toContain('# Generated from schema export');
98
+ expect(result).toContain('APP_PORT=');
99
+ expect(result).toContain('APP_APIKEY=');
100
+ expect(result).toContain('DATABASE_HOST=');
101
+ expect(result).toContain('# Server port');
102
+ expect(result).toContain('# API key');
103
+ expect(result).toContain('# Database host');
104
+ });
105
+
106
+ it('should format schema as .env.example with prefix', () => {
107
+ const result = formatAsEnvExample(mockSchema, 'MYAPP');
108
+
109
+ expect(result).toContain('MYAPP_APP_PORT=');
110
+ expect(result).toContain('MYAPP_APP_APIKEY=');
111
+ expect(result).toContain('MYAPP_DATABASE_HOST=');
112
+ });
113
+
114
+ it('should include example values when available', () => {
115
+ const result = formatAsEnvExample(mockSchema);
116
+
117
+ expect(result).toContain('APP_PORT=8080');
118
+ });
119
+
120
+ it('should include default values when example is not available', () => {
121
+ const result = formatAsEnvExample(mockSchema);
122
+
123
+ expect(result).toContain('DATABASE_HOST=localhost');
124
+ });
125
+
126
+ it('should use placeholder for sensitive values', () => {
127
+ const result = formatAsEnvExample(mockSchema);
128
+
129
+ expect(result).toContain('APP_APIKEY=your-example-value');
130
+ });
131
+
132
+ it('should mark required fields', () => {
133
+ const result = formatAsEnvExample(mockSchema);
134
+
135
+ expect(result).toContain('# API key (required)');
136
+ });
137
+
138
+ it('should handle sections without descriptions', () => {
139
+ const schemaWithoutDesc: ExportedSchema = {
140
+ name: 'test',
141
+ sections: [
142
+ {
143
+ name: 'app',
144
+ elements: [
145
+ {
146
+ name: 'port',
147
+ type: 'number',
148
+ default: 3000,
149
+ required: false,
150
+ sensitive: false,
151
+ joiValidation: {}
152
+ }
153
+ ]
154
+ }
155
+ ]
156
+ };
157
+
158
+ const result = formatAsEnvExample(schemaWithoutDesc);
159
+ expect(result).toContain('APP_PORT=3000');
160
+ });
161
+
162
+ it('should handle elements without descriptions', () => {
163
+ const schemaWithoutElementDesc: ExportedSchema = {
164
+ name: 'test',
165
+ sections: [
166
+ {
167
+ name: 'app',
168
+ description: 'App config',
169
+ elements: [
170
+ {
171
+ name: 'port',
172
+ type: 'number',
173
+ default: 3000,
174
+ required: false,
175
+ sensitive: false,
176
+ joiValidation: {}
177
+ }
178
+ ]
179
+ }
180
+ ]
181
+ };
182
+
183
+ const result = formatAsEnvExample(schemaWithoutElementDesc);
184
+ expect(result).toContain('# App config');
185
+ expect(result).toContain('APP_PORT=3000');
186
+ });
187
+ });
188
+ });
@@ -0,0 +1,124 @@
1
+ import yaml from 'js-yaml';
2
+ import { ExportedSchema, ExportedElement } from './schemaExporter.js';
3
+
4
+ /**
5
+ * Formats the schema as JSON string
6
+ * @param schema - The schema to format
7
+ * @param pretty - Whether to pretty print the JSON
8
+ * @returns The configuration schema as a JSON string
9
+ */
10
+ export function formatAsJSON(
11
+ schema: ExportedSchema,
12
+ pretty: boolean = true
13
+ ): string {
14
+ return JSON.stringify(schema, null, pretty ? 2 : 0);
15
+ }
16
+
17
+ /**
18
+ * Formats the schema as YAML string
19
+ * @param schema - The schema to format
20
+ * @returns The configuration schema as a YAML string
21
+ */
22
+ export function formatAsYAML(schema: ExportedSchema): string {
23
+ return yaml.dump(schema, {
24
+ indent: 2,
25
+ lineWidth: 80,
26
+ noRefs: true
27
+ });
28
+ }
29
+
30
+ /**
31
+ * Generates an environment variable name from section and element names
32
+ * @param sectionName - The name of the section
33
+ * @param elementName - The name of the element
34
+ * @param prefix - Optional prefix for the environment variable name
35
+ * @returns The environment variable name
36
+ */
37
+ function generateEnvVarName(
38
+ sectionName: string,
39
+ elementName: string,
40
+ prefix?: string
41
+ ): string {
42
+ let name = `${sectionName}_${elementName}`.replace(/\./g, '_');
43
+ if (prefix) {
44
+ name = `${prefix}_${name}`;
45
+ }
46
+ return name.toUpperCase();
47
+ }
48
+
49
+ /**
50
+ * Formats a value for display in .env.example file
51
+ * @param element - The element to format
52
+ * @returns The formatted value
53
+ */
54
+ function formatEnvValue(element: ExportedElement): string {
55
+ if (element.sensitive) {
56
+ if (element.example !== undefined) {
57
+ return 'your-example-value';
58
+ }
59
+ return 'your-secret-value';
60
+ }
61
+
62
+ if (element.example !== undefined) {
63
+ return String(element.example);
64
+ }
65
+
66
+ if (element.default !== undefined) {
67
+ return String(element.default);
68
+ }
69
+
70
+ switch (element.type) {
71
+ case 'string':
72
+ return 'your-value';
73
+ case 'number':
74
+ return '0';
75
+ case 'boolean':
76
+ return 'true';
77
+ case 'array':
78
+ return 'value1,value2';
79
+ default:
80
+ return 'your-value';
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Formats the schema as a .env.example file
86
+ * @param schema - The exported schema
87
+ * @param prefix - Optional prefix for environment variable names (e.g., 'MYAPP')
88
+ * @returns The .env.example file content as a string
89
+ */
90
+ export function formatAsEnvExample(
91
+ schema: ExportedSchema,
92
+ prefix?: string
93
+ ): string {
94
+ const lines: string[] = [];
95
+
96
+ lines.push(`# ${schema.name} Configuration`);
97
+ lines.push('# Generated from schema export');
98
+ lines.push('');
99
+
100
+ for (const section of schema.sections) {
101
+ if (section.description) {
102
+ lines.push(`# ${section.description}`);
103
+ }
104
+
105
+ for (const element of section.elements) {
106
+ const envVarName = generateEnvVarName(section.name, element.name, prefix);
107
+ const value = formatEnvValue(element);
108
+ const isRequired = element.required && element.default === undefined;
109
+
110
+ if (element.description) {
111
+ lines.push(
112
+ `# ${element.description}${isRequired ? ' (required)' : ''}`
113
+ );
114
+ } else if (isRequired) {
115
+ lines.push('# Required');
116
+ }
117
+
118
+ lines.push(`${envVarName}=${value}`);
119
+ lines.push('');
120
+ }
121
+ }
122
+
123
+ return lines.join('\n');
124
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "@config-bound/typescript-config/package.tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "ES2024",
5
+ "lib": ["ES2024"],
6
+ "module": "NodeNext",
7
+ "moduleResolution": "NodeNext",
8
+ "outDir": "./dist",
9
+ "rootDir": "./src"
10
+ },
11
+ "include": ["src/**/*", "src/**/*.spec.ts"]
12
+ }