@config-bound/cli 0.1.0 → 0.2.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/package.json +29 -23
- package/scripts/generate-docs.ts +362 -0
- package/src/cli.module.ts +7 -1
- package/src/commands/generate-bind.command.ts +175 -0
- package/src/commands/generate.command.ts +13 -0
- package/src/services/bind-generator.service.ts +248 -0
- package/src/services/config-discovery.service.spec.ts +10 -10
- package/src/services/config-discovery.service.ts +26 -41
- package/src/services/config-loader.service.ts +8 -16
- package/src/services/schema-export.service.spec.ts +20 -22
- package/src/services/schema-export.service.ts +6 -11
- package/tsconfig.json +7 -2
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-format$colon$ci.log +0 -6
- package/.turbo/turbo-lint$colon$ci.log +0 -4
- package/.turbo/turbo-test.log +0 -19
- package/dist/cli.module.d.ts +0 -3
- package/dist/cli.module.d.ts.map +0 -1
- package/dist/cli.module.js +0 -29
- package/dist/cli.module.js.map +0 -1
- package/dist/commands/export.command.d.ts +0 -30
- package/dist/commands/export.command.d.ts.map +0 -1
- package/dist/commands/export.command.js +0 -226
- package/dist/commands/export.command.js.map +0 -1
- package/dist/commands/list.command.d.ts +0 -13
- package/dist/commands/list.command.d.ts.map +0 -1
- package/dist/commands/list.command.js +0 -93
- package/dist/commands/list.command.js.map +0 -1
- package/dist/main.d.ts +0 -3
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js +0 -17
- package/dist/main.js.map +0 -1
- package/dist/services/config-discovery.service.d.ts +0 -15
- package/dist/services/config-discovery.service.d.ts.map +0 -1
- package/dist/services/config-discovery.service.js +0 -191
- package/dist/services/config-discovery.service.js.map +0 -1
- package/dist/services/config-discovery.service.spec.d.ts +0 -2
- package/dist/services/config-discovery.service.spec.d.ts.map +0 -1
- package/dist/services/config-discovery.service.spec.js +0 -137
- package/dist/services/config-discovery.service.spec.js.map +0 -1
- package/dist/services/config-loader.service.d.ts +0 -13
- package/dist/services/config-loader.service.d.ts.map +0 -1
- package/dist/services/config-loader.service.js +0 -241
- package/dist/services/config-loader.service.js.map +0 -1
- package/dist/services/file-writer.service.d.ts +0 -6
- package/dist/services/file-writer.service.d.ts.map +0 -1
- package/dist/services/file-writer.service.js +0 -38
- package/dist/services/file-writer.service.js.map +0 -1
- package/dist/services/file-writer.service.spec.d.ts +0 -2
- package/dist/services/file-writer.service.spec.d.ts.map +0 -1
- package/dist/services/file-writer.service.spec.js +0 -98
- package/dist/services/file-writer.service.spec.js.map +0 -1
- package/dist/services/schema-export.service.d.ts +0 -14
- package/dist/services/schema-export.service.d.ts.map +0 -1
- package/dist/services/schema-export.service.js +0 -58
- package/dist/services/schema-export.service.js.map +0 -1
- package/dist/services/schema-export.service.spec.d.ts +0 -2
- package/dist/services/schema-export.service.spec.d.ts.map +0 -1
- package/dist/services/schema-export.service.spec.js +0 -69
- package/dist/services/schema-export.service.spec.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @config-bound/cli
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [87d2ab6]
|
|
8
|
+
- @config-bound/core@1.0.0
|
|
9
|
+
- @config-bound/schema-export@0.1.3
|
|
10
|
+
|
|
11
|
+
## 0.2.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- f2299f6: Add `configbound generate bind` command
|
|
16
|
+
|
|
17
|
+
Scaffolds the boilerplate for a new custom bind — either as an embedded TypeScript class or a publishable npm package. Handles class structure, static `create()` factory, and cache setup so you can focus on the implementation.
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [02b3369]
|
|
22
|
+
- Updated dependencies [ac54dea]
|
|
23
|
+
- Updated dependencies [0f899ee]
|
|
24
|
+
- @config-bound/config-bound@0.2.0
|
|
25
|
+
- @config-bound/schema-export@0.1.1
|
|
26
|
+
|
|
3
27
|
## 0.1.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Robert Keyser
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@config-bound/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "CLI tool for ConfigBound schema export and management",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -17,46 +17,52 @@
|
|
|
17
17
|
},
|
|
18
18
|
"main": "./dist/main.js",
|
|
19
19
|
"types": "./dist/main.d.ts",
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "tsc",
|
|
22
|
-
"clean": "rimraf dist coverage .turbo",
|
|
23
|
-
"dev": "tsx src/main.ts",
|
|
24
|
-
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
|
|
25
|
-
"test:dev": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
|
|
26
|
-
"test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
|
|
27
|
-
"lint": "eslint src/**/*.ts --fix",
|
|
28
|
-
"lint:ci": "eslint src/**/*.ts",
|
|
29
|
-
"format": "prettier --write --config ../../.config/.prettierrc --ignore-path ../../.config/.prettierignore src/**/*.ts",
|
|
30
|
-
"format:ci": "prettier --check --config ../../.config/.prettierrc --ignore-path ../../.config/.prettierignore src/**/*.ts"
|
|
31
|
-
},
|
|
32
20
|
"dependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
-
"@nestjs/common": "^10.4.15",
|
|
36
|
-
"@nestjs/core": "^10.4.15",
|
|
21
|
+
"@nestjs/common": "^11.1.18",
|
|
22
|
+
"@nestjs/core": "^11.1.18",
|
|
37
23
|
"chalk": "^4.1.2",
|
|
38
24
|
"esbuild-register": "^3.6.0",
|
|
39
25
|
"nest-commander": "^3.15.0",
|
|
40
26
|
"reflect-metadata": "^0.2.2",
|
|
41
27
|
"ts-morph": "^24.0.0",
|
|
42
|
-
"tsx": "^4.20.6"
|
|
28
|
+
"tsx": "^4.20.6",
|
|
29
|
+
"@config-bound/core": "1.0.0",
|
|
30
|
+
"@config-bound/schema-export": "0.1.3"
|
|
43
31
|
},
|
|
44
32
|
"devDependencies": {
|
|
45
|
-
"@
|
|
33
|
+
"@nestjs/testing": "^11.1.18",
|
|
46
34
|
"@types/jest": "^30.0.0",
|
|
47
|
-
"@types/node": "^24.10.
|
|
35
|
+
"@types/node": "^24.10.0",
|
|
48
36
|
"eslint": "^9.39.1",
|
|
49
37
|
"jest": "^30.2.0",
|
|
50
|
-
"
|
|
38
|
+
"markdown-table": "^3.0.4",
|
|
39
|
+
"rimraf": "^6.1.0",
|
|
51
40
|
"ts-jest": "^29.4.5",
|
|
52
|
-
"typescript": "^
|
|
41
|
+
"typescript": "^6.0.0",
|
|
42
|
+
"@config-bound/eslint-config": "0.0.0",
|
|
43
|
+
"@config-bound/typescript-config": "0.0.0"
|
|
53
44
|
},
|
|
54
45
|
"publishConfig": {
|
|
55
46
|
"access": "public",
|
|
56
47
|
"registry": "https://registry.npmjs.org"
|
|
57
48
|
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=22"
|
|
51
|
+
},
|
|
58
52
|
"repository": {
|
|
59
53
|
"type": "git",
|
|
60
54
|
"url": "https://github.com/notr-ai/ConfigBound"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc",
|
|
58
|
+
"clean": "rimraf dist coverage .turbo",
|
|
59
|
+
"dev": "tsx src/main.ts",
|
|
60
|
+
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
|
|
61
|
+
"test:dev": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
|
|
62
|
+
"test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage",
|
|
63
|
+
"lint": "eslint src/**/*.ts --fix",
|
|
64
|
+
"lint:ci": "eslint src/**/*.ts",
|
|
65
|
+
"format": "prettier --write --config ../../.config/.prettierrc --ignore-path ../../.config/.prettierignore src/**/*.ts",
|
|
66
|
+
"format:ci": "prettier --check --config ../../.config/.prettierrc --ignore-path ../../.config/.prettierignore src/**/*.ts"
|
|
61
67
|
}
|
|
62
|
-
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates CLI reference documentation for the docs site.
|
|
3
|
+
*
|
|
4
|
+
* WHAT IT DOES:
|
|
5
|
+
* Boots the Nest CLI application, extracts command metadata from
|
|
6
|
+
* registered CommandRunner instances, and generates markdown reference pages
|
|
7
|
+
* combining authored prose with derived options tables.
|
|
8
|
+
*
|
|
9
|
+
* DEPENDENCIES:
|
|
10
|
+
* - @config-bound/cli package with Nest application and command runners
|
|
11
|
+
* - apps/cli/src/cli.module.ts and command classes
|
|
12
|
+
*
|
|
13
|
+
* OUTPUT:
|
|
14
|
+
* - apps/docs/reference/cli/*.md (index, list, export, generate-bind)
|
|
15
|
+
*
|
|
16
|
+
* RUN VIA:
|
|
17
|
+
* npm run docs:cli (from repo root)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import 'reflect-metadata';
|
|
21
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
22
|
+
import { resolve } from 'path';
|
|
23
|
+
import { markdownTable } from 'markdown-table';
|
|
24
|
+
import { CommandFactory } from 'nest-commander';
|
|
25
|
+
import { CliModule } from '../src/cli.module.js';
|
|
26
|
+
import { ExportCommand } from '../src/commands/export.command.js';
|
|
27
|
+
import { ListCommand } from '../src/commands/list.command.js';
|
|
28
|
+
import { GenerateBindCommand } from '../src/commands/generate-bind.command.js';
|
|
29
|
+
|
|
30
|
+
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
interface CommanderOption {
|
|
33
|
+
flags: string;
|
|
34
|
+
description: string;
|
|
35
|
+
defaultValue: unknown;
|
|
36
|
+
hidden: boolean;
|
|
37
|
+
long: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface CommandRunner {
|
|
41
|
+
command: { options: CommanderOption[] };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A page definition. `sections` is authored prose; `optionsHeading` marks where
|
|
46
|
+
* the derived options table is injected. Pages without options omit the heading.
|
|
47
|
+
*/
|
|
48
|
+
interface PageSpec {
|
|
49
|
+
filename: string;
|
|
50
|
+
frontmatterDescription: string;
|
|
51
|
+
title: string;
|
|
52
|
+
intro: string;
|
|
53
|
+
usage: string;
|
|
54
|
+
positionalArgs?: { name: string; description: string; default?: string }[];
|
|
55
|
+
/** Heading text under which the derived options table is rendered. */
|
|
56
|
+
optionsHeading: string;
|
|
57
|
+
sections: { heading: string; body: string }[];
|
|
58
|
+
related: { text: string; link: string }[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── Rendering ─────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
const SCRIPT_NAME = 'generate-docs';
|
|
64
|
+
const GENERATED_NOTICE = `<!-- This file is generated by scripts/generate-cli-docs.mjs. Do not edit it directly. -->`;
|
|
65
|
+
|
|
66
|
+
function renderOptionsTable(options: CommanderOption[]): string {
|
|
67
|
+
const visible = options.filter(
|
|
68
|
+
(opt) => !opt.hidden && opt.long !== '--help' && opt.long !== '--version',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const rows = visible.map((opt) => [
|
|
72
|
+
`\`${opt.flags}\``,
|
|
73
|
+
opt.description,
|
|
74
|
+
opt.defaultValue !== undefined && opt.defaultValue !== null
|
|
75
|
+
? `\`${String(opt.defaultValue)}\``
|
|
76
|
+
: '—'
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
return markdownTable([
|
|
80
|
+
['Flag', 'Description', 'Default'],
|
|
81
|
+
...rows
|
|
82
|
+
]);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function renderPage(spec: PageSpec, runner: CommandRunner | null): string {
|
|
86
|
+
const parts: string[] = [
|
|
87
|
+
`---\ndescription: ${spec.frontmatterDescription}\n---`,
|
|
88
|
+
GENERATED_NOTICE,
|
|
89
|
+
`# ${spec.title}`,
|
|
90
|
+
spec.intro,
|
|
91
|
+
`## Usage\n\n\`\`\`text\n${spec.usage}\n\`\`\``,
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
if (spec.positionalArgs && spec.positionalArgs.length > 0) {
|
|
95
|
+
const rows = spec.positionalArgs.map((a) => [
|
|
96
|
+
`\`${a.name}\``,
|
|
97
|
+
a.description,
|
|
98
|
+
a.default ?? 'Required'
|
|
99
|
+
]);
|
|
100
|
+
const argsTable = markdownTable([
|
|
101
|
+
['Argument', 'Description', 'Default'],
|
|
102
|
+
...rows
|
|
103
|
+
]);
|
|
104
|
+
parts.push(`### Arguments\n\n${argsTable}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (runner) {
|
|
108
|
+
parts.push(`### ${spec.optionsHeading}\n\n${renderOptionsTable(runner.command.options)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const section of spec.sections) {
|
|
112
|
+
parts.push(`## ${section.heading}\n\n${section.body}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (spec.related.length > 0) {
|
|
116
|
+
const links = spec.related.map((r) => `- [${r.text}](${r.link})`).join('\n');
|
|
117
|
+
parts.push(`## Related\n\n${links}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return parts.join('\n\n');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ─── Page definitions ───────────────────────────────────────────────────────
|
|
124
|
+
// Authored prose lives here. Derived content (options tables) is injected at
|
|
125
|
+
// render time from the live CommandRunner instances.
|
|
126
|
+
|
|
127
|
+
const INDEX_PAGE = `---
|
|
128
|
+
description: The ConfigBound CLI provides tools to discover, inspect, and export configuration schemas without writing code.
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
${GENERATED_NOTICE}
|
|
132
|
+
|
|
133
|
+
# CLI
|
|
134
|
+
|
|
135
|
+
The \`@config-bound/cli\` package provides the \`configbound\` command — a set of tools for working with ConfigBound configurations from the terminal.
|
|
136
|
+
|
|
137
|
+
## Installation
|
|
138
|
+
|
|
139
|
+
\`\`\`bash
|
|
140
|
+
npm install --save-dev @config-bound/cli
|
|
141
|
+
\`\`\`
|
|
142
|
+
|
|
143
|
+
After installation, \`npx configbound\` runs the CLI. For global use:
|
|
144
|
+
|
|
145
|
+
\`\`\`bash
|
|
146
|
+
npm install --global @config-bound/cli
|
|
147
|
+
\`\`\`
|
|
148
|
+
|
|
149
|
+
## Commands
|
|
150
|
+
|
|
151
|
+
| Command | Description |
|
|
152
|
+
| ------- | ----------- |
|
|
153
|
+
| [\`configbound list\`](./list) | Discover and display all ConfigBound configurations in a directory tree |
|
|
154
|
+
| [\`configbound export\`](./export) | Export a configuration schema to JSON, YAML, or \`.env\` format |
|
|
155
|
+
| [\`configbound generate bind\`](./generate-bind) | Scaffold a new bind — embedded class or publishable package |
|
|
156
|
+
|
|
157
|
+
## How discovery works
|
|
158
|
+
|
|
159
|
+
\`list\` and \`export\` both use config discovery — scanning source files for ConfigBound configurations. Discovery reads the actual exports from your TypeScript files. No separate manifest or registration step is required.
|
|
160
|
+
|
|
161
|
+
\`export\` auto-selects a config when only one is found. When multiple configs exist in the same project, it prompts for selection. Pass \`--config\` and \`--name\` explicitly to skip discovery entirely.
|
|
162
|
+
|
|
163
|
+
## CLI vs. library
|
|
164
|
+
|
|
165
|
+
The CLI is a companion to the library, not a replacement for it. Schema definition, validation, and value resolution all happen in code. The CLI's job is to inspect what the library produces — listing what configs exist and exporting their schemas as artifacts.`;
|
|
166
|
+
|
|
167
|
+
const LIST_SPEC: PageSpec = {
|
|
168
|
+
filename: 'list.md',
|
|
169
|
+
frontmatterDescription:
|
|
170
|
+
'Discover and display all ConfigBound configurations in a project using configbound list.',
|
|
171
|
+
title: 'configbound list',
|
|
172
|
+
intro:
|
|
173
|
+
'`list` scans a directory tree for ConfigBound configurations and prints what it finds, grouped by file.\n\nUse it to confirm that discovery works correctly, or to get the exact file paths and export names needed for `configbound export --config ... --name ...`.',
|
|
174
|
+
usage: 'configbound list [path] [options]',
|
|
175
|
+
positionalArgs: [{ name: 'path', description: 'Directory to search', default: 'Current working directory' }],
|
|
176
|
+
optionsHeading: 'Options',
|
|
177
|
+
sections: [
|
|
178
|
+
{
|
|
179
|
+
heading: 'Examples',
|
|
180
|
+
body: `\`\`\`bash
|
|
181
|
+
# List all configs in the current project
|
|
182
|
+
configbound list
|
|
183
|
+
|
|
184
|
+
# Search a specific directory
|
|
185
|
+
configbound list ./src
|
|
186
|
+
|
|
187
|
+
# Search non-recursively
|
|
188
|
+
configbound list --recursive false
|
|
189
|
+
\`\`\``,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
heading: 'Output',
|
|
193
|
+
body: `Results are grouped by file. Each entry shows the export name, whether it is a default or named export, the config's display name if set, and the line number:
|
|
194
|
+
|
|
195
|
+
\`\`\`text
|
|
196
|
+
📄 src/config/app.config.ts
|
|
197
|
+
default (default) - My App Config :42
|
|
198
|
+
|
|
199
|
+
📄 src/config/db.config.ts
|
|
200
|
+
default (default) - Database Config :18
|
|
201
|
+
dbConfig (named) - Database Config (read-only) :67
|
|
202
|
+
\`\`\`
|
|
203
|
+
|
|
204
|
+
When a file has multiple exports, each is listed separately. Use the export name with \`--name\` when running \`export\`:
|
|
205
|
+
|
|
206
|
+
\`\`\`bash
|
|
207
|
+
configbound export --config src/config/db.config.ts --name dbConfig
|
|
208
|
+
\`\`\``,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
related: [{ text: '`configbound export`', link: './export' }],
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const EXPORT_SPEC: PageSpec = {
|
|
215
|
+
filename: 'export.md',
|
|
216
|
+
frontmatterDescription:
|
|
217
|
+
'Export a ConfigBound configuration schema to JSON, YAML, or .env format using configbound export.',
|
|
218
|
+
title: 'configbound export',
|
|
219
|
+
intro:
|
|
220
|
+
'`export` loads a ConfigBound configuration and serializes its schema to JSON, YAML, or `.env` format. The output goes to stdout by default, or to a file with `--output`.\n\nThis is the CLI surface for schema export. For the programmatic equivalent, see [Export your configuration schema](/how-to/schema-export). For why schema export matters, see [Schema as the Source of Truth](/explanation/schema-source-of-truth).',
|
|
221
|
+
usage: 'configbound export [options]',
|
|
222
|
+
optionsHeading: 'Options',
|
|
223
|
+
sections: [
|
|
224
|
+
{
|
|
225
|
+
heading: 'Discovery',
|
|
226
|
+
body: `When \`--config\` is omitted, \`export\` scans the current directory for ConfigBound configurations:
|
|
227
|
+
|
|
228
|
+
- One config found: selected automatically.
|
|
229
|
+
- Multiple configs found: a numbered menu is displayed and you choose.
|
|
230
|
+
- No configs found: the command exits and suggests using \`--config\`.
|
|
231
|
+
|
|
232
|
+
Pass \`--config\` to skip discovery entirely. When the file has more than one export, also pass \`--name\` to identify which one to export.`,
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
heading: 'Examples',
|
|
236
|
+
body: `\`\`\`bash
|
|
237
|
+
# Auto-discover and export to stdout as JSON
|
|
238
|
+
configbound export
|
|
239
|
+
|
|
240
|
+
# Export a specific file as YAML
|
|
241
|
+
configbound export --config ./src/config/app.config.ts --format yaml
|
|
242
|
+
|
|
243
|
+
# Write JSON output to a file
|
|
244
|
+
configbound export --output ./docs/schema.json
|
|
245
|
+
|
|
246
|
+
# Named export from a file with multiple configs
|
|
247
|
+
configbound export --config ./src/config/all.ts --name databaseConfig
|
|
248
|
+
|
|
249
|
+
# Generate a .env file including normally-omitted elements
|
|
250
|
+
configbound export --format env --include-omitted --output .env.example
|
|
251
|
+
\`\`\``,
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
heading: 'Omitted elements',
|
|
255
|
+
body: 'Elements defined with `omitFromSchema: true` are excluded from exported output by default. The intent is to keep sensitive or internal fields out of generated artifacts. Pass `--include-omitted` to override this — useful when generating a complete `.env.example` for onboarding or local development.',
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
related: [
|
|
259
|
+
{ text: '`configbound list`', link: './list' },
|
|
260
|
+
{ text: 'Export your configuration schema', link: '/how-to/schema-export' },
|
|
261
|
+
{ text: 'Schema as the Source of Truth', link: '/explanation/schema-source-of-truth' },
|
|
262
|
+
],
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const GENERATE_BIND_SPEC: PageSpec = {
|
|
266
|
+
filename: 'generate-bind.md',
|
|
267
|
+
frontmatterDescription: 'Scaffold a new custom bind using configbound generate bind.',
|
|
268
|
+
title: 'configbound generate bind',
|
|
269
|
+
intro:
|
|
270
|
+
'`generate bind` scaffolds the boilerplate for a new bind. It handles the repetitive parts — class structure, factory pattern, cache setup — so you can focus on the implementation.',
|
|
271
|
+
usage: 'configbound generate bind <name> [options]',
|
|
272
|
+
positionalArgs: [
|
|
273
|
+
{ name: 'name', description: 'The bind name in kebab-case (e.g. `vault`, `aws-ssm`, `1password`)', default: 'Required' },
|
|
274
|
+
],
|
|
275
|
+
optionsHeading: 'Options',
|
|
276
|
+
sections: [
|
|
277
|
+
{
|
|
278
|
+
heading: 'Generation modes',
|
|
279
|
+
body: `**\`embedded\`** generates a single TypeScript class file. The bind lives in your project and is not published. Use this when the bind is specific to one application.
|
|
280
|
+
|
|
281
|
+
**\`package\`** generates a full npm package scaffold: \`package.json\`, \`tsconfig.json\`, \`eslint.config.mjs\`, and a \`src/\` directory containing the bind class and an index. Use this when you intend to publish the bind for others to use.`,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
heading: 'Examples',
|
|
285
|
+
body: `\`\`\`bash
|
|
286
|
+
# Interactive — prompts for embedded or package
|
|
287
|
+
configbound generate bind vault
|
|
288
|
+
|
|
289
|
+
# Non-interactive embedded bind
|
|
290
|
+
configbound generate bind vault --type embedded
|
|
291
|
+
|
|
292
|
+
# Non-interactive package scaffold
|
|
293
|
+
configbound generate bind aws-ssm --type package
|
|
294
|
+
|
|
295
|
+
# Preview what would be generated without writing anything
|
|
296
|
+
configbound generate bind 1password --type package --dry-run
|
|
297
|
+
\`\`\``,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
heading: 'What gets generated',
|
|
301
|
+
body: `Both modes produce a class that extends \`Bind\` with a static async \`create()\` factory and a synchronous \`retrieve()\` method. The generated code includes inline comments explaining what to fill in.
|
|
302
|
+
|
|
303
|
+
The factory pattern pre-loads values into a cache at startup. \`retrieve()\` reads from that cache synchronously. This satisfies the \`Bind\` contract without requiring changes to the core library.`,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
heading: 'Name derivation',
|
|
307
|
+
body: 'The kebab-case name you provide is used to derive the class name. `vault` becomes `VaultBind`. `aws-ssm` becomes `AwsSsmBind`. Leading digits are replaced with words: `1password` becomes `OnepasswordBind`.',
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
related: [
|
|
311
|
+
{ text: 'Create a custom bind', link: '/how-to/custom-bind' },
|
|
312
|
+
{
|
|
313
|
+
text: '`Bind` API reference',
|
|
314
|
+
link: '/reference/api/@config-bound.core.bind.bind.Class.Bind',
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// ─── Main ──────────────────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
function writeDocFile(outDir: string, filename: string, content: string): void {
|
|
322
|
+
writeFileSync(resolve(outDir, filename), content + '\n', 'utf-8');
|
|
323
|
+
console.log(` generated: reference/cli/${filename}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function main(): Promise<void> {
|
|
327
|
+
// Optional output directory override (defaults to apps/docs/reference/cli).
|
|
328
|
+
// Used for custom builds or testing. Normal builds use the default.
|
|
329
|
+
const outDir = process.argv[2] ?? resolve(import.meta.dirname, '../../apps/docs/reference/cli');
|
|
330
|
+
|
|
331
|
+
const app = await CommandFactory.createWithoutRunning(CliModule, { logger: false });
|
|
332
|
+
|
|
333
|
+
const listRunner = app.get(ListCommand) as CommandRunner;
|
|
334
|
+
const exportRunner = app.get(ExportCommand) as CommandRunner;
|
|
335
|
+
const generateBindRunner = app.get(GenerateBindCommand) as CommandRunner;
|
|
336
|
+
|
|
337
|
+
if (!listRunner?.command?.options) {
|
|
338
|
+
throw new Error(`[${SCRIPT_NAME}] Failed to retrieve ListCommand with valid options`);
|
|
339
|
+
}
|
|
340
|
+
if (!exportRunner?.command?.options) {
|
|
341
|
+
throw new Error(`[${SCRIPT_NAME}] Failed to retrieve ExportCommand with valid options`);
|
|
342
|
+
}
|
|
343
|
+
if (!generateBindRunner?.command?.options) {
|
|
344
|
+
throw new Error(`[${SCRIPT_NAME}] Failed to retrieve GenerateBindCommand with valid options`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
348
|
+
|
|
349
|
+
console.log(`[${SCRIPT_NAME}] Generating CLI reference docs...`);
|
|
350
|
+
writeDocFile(outDir, 'index.md', INDEX_PAGE);
|
|
351
|
+
writeDocFile(outDir, LIST_SPEC.filename, renderPage(LIST_SPEC, listRunner));
|
|
352
|
+
writeDocFile(outDir, EXPORT_SPEC.filename, renderPage(EXPORT_SPEC, exportRunner));
|
|
353
|
+
writeDocFile(outDir, GENERATE_BIND_SPEC.filename, renderPage(GENERATE_BIND_SPEC, generateBindRunner));
|
|
354
|
+
console.log(`[${SCRIPT_NAME}] Done.`);
|
|
355
|
+
|
|
356
|
+
await app.close();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
main().catch((err: unknown) => {
|
|
360
|
+
console.error(`[${SCRIPT_NAME}] Unexpected error:`, err);
|
|
361
|
+
process.exit(1);
|
|
362
|
+
});
|
package/src/cli.module.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { Module } from '@nestjs/common';
|
|
2
2
|
import { ListCommand } from './commands/list.command.js';
|
|
3
3
|
import { ExportCommand } from './commands/export.command.js';
|
|
4
|
+
import { GenerateCommand } from './commands/generate.command.js';
|
|
5
|
+
import { GenerateBindCommand } from './commands/generate-bind.command.js';
|
|
4
6
|
import { ConfigDiscoveryService } from './services/config-discovery.service.js';
|
|
5
7
|
import { ConfigLoaderService } from './services/config-loader.service.js';
|
|
6
8
|
import { SchemaExportService } from './services/schema-export.service.js';
|
|
7
9
|
import { FileWriterService } from './services/file-writer.service.js';
|
|
10
|
+
import { BindGeneratorService } from './services/bind-generator.service.js';
|
|
8
11
|
|
|
9
12
|
@Module({
|
|
10
13
|
providers: [
|
|
11
14
|
ListCommand,
|
|
12
15
|
ExportCommand,
|
|
16
|
+
GenerateCommand,
|
|
17
|
+
GenerateBindCommand,
|
|
13
18
|
ConfigDiscoveryService,
|
|
14
19
|
ConfigLoaderService,
|
|
15
20
|
SchemaExportService,
|
|
16
|
-
FileWriterService
|
|
21
|
+
FileWriterService,
|
|
22
|
+
BindGeneratorService
|
|
17
23
|
]
|
|
18
24
|
})
|
|
19
25
|
export class CliModule {}
|