@tamyla/clodo-framework 4.6.1 → 4.6.3
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 +16 -0
- package/dist/cli/commands/config-schema.js +19 -7
- package/dist/cli/commands/create.js +9 -2
- package/dist/cli/commands/deploy.js +9 -2
- package/dist/cli/commands/doctor.js +20 -2
- package/dist/cli/commands/secrets.js +20 -7
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/edge-proxy/src/index.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [4.6.3](https://github.com/tamylaa/clodo-framework/compare/v4.6.2...v4.6.3) (2026-02-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **cli:** ensure doctor loads ValidationHandler at runtime and sync FRAMEWORK_VERSION ([3d86378](https://github.com/tamylaa/clodo-framework/commit/3d8637807c6fb618a4441f8d36bfef87c887a93a))
|
|
7
|
+
* **cli:** lazy-load ValidationHandler in doctor command action ([99994ed](https://github.com/tamylaa/clodo-framework/commit/99994ed270babc49424aa3b37fc94943d4247ab3))
|
|
8
|
+
* **cli:** runtime-load src-only handlers (ValidationHandler, ConfigSchemaValidator, SecretsManager) so commands register from both src and dist ([50c0120](https://github.com/tamylaa/clodo-framework/commit/50c012081f5582411de739f1053a568c01cf83fd))
|
|
9
|
+
|
|
10
|
+
## [4.6.2](https://github.com/tamylaa/clodo-framework/compare/v4.6.1...v4.6.2) (2026-02-18)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **cli:** make Doctor command load ValidationHandler at runtime (works from src & dist); test: add regression test for doctor in dist CLI ([fd7437d](https://github.com/tamylaa/clodo-framework/commit/fd7437d1a3df227ff8a35089d90f2ef82d99714a))
|
|
16
|
+
|
|
1
17
|
## [4.6.1](https://github.com/tamylaa/clodo-framework/compare/v4.6.0...v4.6.1) (2026-02-18)
|
|
2
18
|
|
|
3
19
|
|
|
@@ -10,13 +10,23 @@
|
|
|
10
10
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { readFileSync } from 'fs';
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
// Load ConfigSchemaValidator lazily so this module can be imported both
|
|
15
|
+
// from source (cli/) and from the compiled distribution (dist/cli/).
|
|
16
|
+
async function loadConfigSchemaValidator() {
|
|
17
|
+
try {
|
|
18
|
+
return (await import('../../src/validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return (await import('../../validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
14
23
|
export function registerConfigSchemaCommand(program) {
|
|
15
24
|
const cmd = program.command('config-schema').description('Inspect and validate configuration file schemas');
|
|
16
25
|
|
|
17
26
|
// ─── show ──────────────────────────────────────────────────────────────
|
|
18
|
-
cmd.command('show <type>').description('Show the schema definition for a config type (create, deploy, validate, update)').option('--json', 'Output as JSON').action((type, options) => {
|
|
19
|
-
const
|
|
27
|
+
cmd.command('show <type>').description('Show the schema definition for a config type (create, deploy, validate, update)').option('--json', 'Output as JSON').action(async (type, options) => {
|
|
28
|
+
const ValidatorClass = await loadConfigSchemaValidator();
|
|
29
|
+
const validator = new ValidatorClass();
|
|
20
30
|
const definition = validator.getSchemaDefinition(type);
|
|
21
31
|
if (!definition) {
|
|
22
32
|
console.error(chalk.red(`Unknown config type: '${type}'`));
|
|
@@ -52,8 +62,9 @@ export function registerConfigSchemaCommand(program) {
|
|
|
52
62
|
});
|
|
53
63
|
|
|
54
64
|
// ─── validate ──────────────────────────────────────────────────────────
|
|
55
|
-
cmd.command('validate <file>').description('Validate a config file against its schema').option('--type <type>', 'Config type (auto-detected if not specified)').option('--strict', 'Exit with error code on validation failures').option('--json', 'Output as JSON').action((file, options) => {
|
|
56
|
-
const
|
|
65
|
+
cmd.command('validate <file>').description('Validate a config file against its schema').option('--type <type>', 'Config type (auto-detected if not specified)').option('--strict', 'Exit with error code on validation failures').option('--json', 'Output as JSON').action(async (file, options) => {
|
|
66
|
+
const ValidatorClass = await loadConfigSchemaValidator();
|
|
67
|
+
const validator = new ValidatorClass();
|
|
57
68
|
|
|
58
69
|
// Determine command type
|
|
59
70
|
let commandType = options.type;
|
|
@@ -119,8 +130,9 @@ export function registerConfigSchemaCommand(program) {
|
|
|
119
130
|
});
|
|
120
131
|
|
|
121
132
|
// ─── types ─────────────────────────────────────────────────────────────
|
|
122
|
-
cmd.command('types').description('List all available config types').option('--json', 'Output as JSON').action(options => {
|
|
123
|
-
const
|
|
133
|
+
cmd.command('types').description('List all available config types').option('--json', 'Output as JSON').action(async options => {
|
|
134
|
+
const ValidatorClass = await loadConfigSchemaValidator();
|
|
135
|
+
const validator = new ValidatorClass();
|
|
124
136
|
const types = validator.getRegisteredTypes();
|
|
125
137
|
if (options.json) {
|
|
126
138
|
const details = {};
|
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { Clodo, ConfigLoader } from '@tamyla/clodo-framework';
|
|
10
10
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
11
|
-
|
|
11
|
+
async function loadConfigSchemaValidator() {
|
|
12
|
+
try {
|
|
13
|
+
return (await import('../../src/validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
14
|
+
} catch (err) {
|
|
15
|
+
return (await import('../../validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
12
18
|
export function registerCreateCommand(program) {
|
|
13
19
|
const command = program.command('create').description('Create a new Clodo service with conversational setup').option('-n, --non-interactive', 'Run in non-interactive mode with all required parameters').option('--service-name <name>', 'Service name (required in non-interactive mode)').option('--service-type <type>', 'Service type: data-service, auth-service, content-service, api-gateway, generic', 'generic').option('--domain-name <domain>', 'Domain name (required in non-interactive mode)').option('--cloudflare-token <token>', 'Cloudflare API token (required in non-interactive mode)').option('--cloudflare-account-id <id>', 'Cloudflare account ID (required in non-interactive mode)').option('--cloudflare-zone-id <id>', 'Cloudflare zone ID (required in non-interactive mode)').option('--environment <env>', 'Target environment: development, staging, production', 'development').option('--output-path <path>', 'Output directory for generated service', '.').option('--template-path <path>', 'Path to service templates', './templates').option('--middleware-strategy <strategy>', 'Middleware generation strategy: contract|legacy', 'contract').option('--force', 'Skip confirmation prompts').option('--validate', 'Validate service after creation');
|
|
14
20
|
|
|
@@ -27,7 +33,8 @@ export function registerCreateCommand(program) {
|
|
|
27
33
|
if (options.configFile) {
|
|
28
34
|
configFileData = configLoader.loadSafe(options.configFile, {});
|
|
29
35
|
// Validate against create schema
|
|
30
|
-
const
|
|
36
|
+
const ValidatorClass = await loadConfigSchemaValidator();
|
|
37
|
+
const schemaValidator = new ValidatorClass({
|
|
31
38
|
verbose: options.verbose
|
|
32
39
|
});
|
|
33
40
|
const validation = schemaValidator.validateConfig(configFileData, 'create');
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { Clodo, ConfigLoader, InteractiveDeploymentCoordinator, OutputFormatter } from '@tamyla/clodo-framework';
|
|
3
3
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
4
|
-
|
|
4
|
+
async function loadConfigSchemaValidator() {
|
|
5
|
+
try {
|
|
6
|
+
return (await import('../../src/validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
7
|
+
} catch (err) {
|
|
8
|
+
return (await import('../../validation/ConfigSchemaValidator.js')).ConfigSchemaValidator;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
5
11
|
export function registerDeployCommand(program) {
|
|
6
12
|
const command = program.command('deploy').description('Deploy a Clodo service with interactive configuration and validation')
|
|
7
13
|
// Cloudflare-specific options
|
|
@@ -31,7 +37,8 @@ export function registerDeployCommand(program) {
|
|
|
31
37
|
if (options.configFile) {
|
|
32
38
|
configFileData = configLoader.loadSafe(options.configFile, {});
|
|
33
39
|
// Validate against deploy schema
|
|
34
|
-
const
|
|
40
|
+
const ValidatorClass = await loadConfigSchemaValidator();
|
|
41
|
+
const schemaValidator = new ValidatorClass({
|
|
35
42
|
verbose: options.verbose
|
|
36
43
|
});
|
|
37
44
|
const validation = schemaValidator.validateConfig(configFileData, 'deploy');
|
|
@@ -3,12 +3,30 @@
|
|
|
3
3
|
* Provides preflight diagnostic checks for Clodo services
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ValidationHandler } from '../../src/service-management/handlers/ValidationHandler.js';
|
|
7
6
|
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
// ValidationHandler is loaded at runtime so the same command module works
|
|
9
|
+
// both when running from source (imports from `src/...`) and from the
|
|
10
|
+
// compiled distribution (imports from `dist/...`). We attempt the source
|
|
11
|
+
// import first, then fall back to the dist path.
|
|
12
|
+
async function loadValidationHandler() {
|
|
13
|
+
try {
|
|
14
|
+
return (await import('../../src/service-management/handlers/ValidationHandler.js')).ValidationHandler;
|
|
15
|
+
} catch (errSource) {
|
|
16
|
+
// running from dist/ (compiled package)
|
|
17
|
+
try {
|
|
18
|
+
return (await import('../../service-management/handlers/ValidationHandler.js')).ValidationHandler;
|
|
19
|
+
} catch (errDist) {
|
|
20
|
+
// last-resort: try explicit dist path (useful in some CI/dev layouts)
|
|
21
|
+
return (await import('../../dist/service-management/handlers/ValidationHandler.js')).ValidationHandler;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
8
25
|
export function registerDoctorCommand(program) {
|
|
9
26
|
program.command('doctor').description('Run preflight diagnostic checks for service health and deployment readiness').option('--json', 'Output results in JSON format for machine consumption').option('--fix', 'Attempt to automatically fix detected issues').option('--strict', 'Treat warnings as errors (non-zero exit code)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
10
27
|
try {
|
|
11
|
-
const
|
|
28
|
+
const ValidationHandlerClass = await loadValidationHandler();
|
|
29
|
+
const handler = new ValidationHandlerClass({
|
|
12
30
|
strict: options.strict
|
|
13
31
|
});
|
|
14
32
|
const results = await handler.runDoctor({
|
|
@@ -8,8 +8,16 @@
|
|
|
8
8
|
* clodo-service secrets baseline Show/update the secrets baseline
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { SecretsManager } from '../../src/security/SecretsManager.js';
|
|
12
11
|
import chalk from 'chalk';
|
|
12
|
+
|
|
13
|
+
// Lazy-load SecretsManager so this command module imports successfully from dist/
|
|
14
|
+
async function loadSecretsManager() {
|
|
15
|
+
try {
|
|
16
|
+
return (await import('../../src/security/SecretsManager.js')).SecretsManager;
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return (await import('../../security/SecretsManager.js')).SecretsManager;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
13
21
|
export function registerSecretsCommand(program) {
|
|
14
22
|
const secrets = program.command('secrets').description('Secret scanning and baseline management for leak prevention');
|
|
15
23
|
|
|
@@ -17,7 +25,8 @@ export function registerSecretsCommand(program) {
|
|
|
17
25
|
|
|
18
26
|
secrets.command('scan').description('Scan for potential secrets in the service directory').option('--json', 'Output results in JSON format').option('--include-tests', 'Include test/example/placeholder values in results').option('--service-path <path>', 'Path to service directory (defaults to current directory)').option('--severity <level>', 'Minimum severity to report: critical, high, medium', 'medium').action(async options => {
|
|
19
27
|
try {
|
|
20
|
-
const
|
|
28
|
+
const SecretsManagerClass = await loadSecretsManager();
|
|
29
|
+
const mgr = new SecretsManagerClass({
|
|
21
30
|
includeTests: options.includeTests
|
|
22
31
|
});
|
|
23
32
|
const servicePath = options.servicePath || process.cwd();
|
|
@@ -50,7 +59,8 @@ export function registerSecretsCommand(program) {
|
|
|
50
59
|
|
|
51
60
|
secrets.command('validate').description('Validate secrets against the baseline — fails if new secrets are found').option('--json', 'Output results in JSON format').option('--strict', 'Exit with non-zero code on any findings (even baselined)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
52
61
|
try {
|
|
53
|
-
const
|
|
62
|
+
const SecretsManagerClass = await loadSecretsManager();
|
|
63
|
+
const mgr = new SecretsManagerClass();
|
|
54
64
|
const servicePath = options.servicePath || process.cwd();
|
|
55
65
|
const result = await mgr.validate(servicePath);
|
|
56
66
|
if (options.json) {
|
|
@@ -77,7 +87,8 @@ export function registerSecretsCommand(program) {
|
|
|
77
87
|
|
|
78
88
|
baseline.command('show').description('Display current baseline entries').option('--json', 'Output results in JSON format').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
79
89
|
try {
|
|
80
|
-
const
|
|
90
|
+
const SecretsManagerClass = await loadSecretsManager();
|
|
91
|
+
const mgr = new SecretsManagerClass();
|
|
81
92
|
const servicePath = options.servicePath || process.cwd();
|
|
82
93
|
const entries = await mgr.baselineShow(servicePath);
|
|
83
94
|
if (options.json) {
|
|
@@ -95,7 +106,8 @@ export function registerSecretsCommand(program) {
|
|
|
95
106
|
|
|
96
107
|
baseline.command('update').description('Update baseline with current scan findings').option('--json', 'Output results in JSON format').option('--add-all', 'Add all new findings to the baseline').option('--prune', 'Remove stale entries no longer detected').option('--reason <reason>', 'Reason for updating (audit trail)').option('--service-path <path>', 'Path to service directory (defaults to current directory)').action(async options => {
|
|
97
108
|
try {
|
|
98
|
-
const
|
|
109
|
+
const SecretsManagerClass = await loadSecretsManager();
|
|
110
|
+
const mgr = new SecretsManagerClass();
|
|
99
111
|
const servicePath = options.servicePath || process.cwd();
|
|
100
112
|
const result = await mgr.baselineUpdate(servicePath, {
|
|
101
113
|
addAll: options.addAll,
|
|
@@ -115,8 +127,9 @@ export function registerSecretsCommand(program) {
|
|
|
115
127
|
|
|
116
128
|
// ─── secrets patterns ──────────────────────────────────────
|
|
117
129
|
|
|
118
|
-
secrets.command('patterns').description('List configured detection patterns and their severity').option('--json', 'Output results in JSON format').action(options => {
|
|
119
|
-
const
|
|
130
|
+
secrets.command('patterns').description('List configured detection patterns and their severity').option('--json', 'Output results in JSON format').action(async options => {
|
|
131
|
+
const SecretsManagerClass = await loadSecretsManager();
|
|
132
|
+
const mgr = new SecretsManagerClass();
|
|
120
133
|
const patterns = mgr.getPatterns();
|
|
121
134
|
if (options.json) {
|
|
122
135
|
console.log(JSON.stringify(patterns, null, 2));
|
package/dist/index.js
CHANGED
|
@@ -77,7 +77,7 @@ export { classifyError, getRecoverySuggestions } from './lib/shared/error-handli
|
|
|
77
77
|
export { FrameworkInfo } from './version/FrameworkInfo.js';
|
|
78
78
|
export { TemplateRuntime } from './utils/TemplateRuntime.js';
|
|
79
79
|
export { HealthChecker } from './monitoring/HealthChecker.js';
|
|
80
|
-
export const FRAMEWORK_VERSION = '4.
|
|
80
|
+
export const FRAMEWORK_VERSION = '4.6.1';
|
|
81
81
|
export const FRAMEWORK_NAME = 'Clodo Framework';
|
|
82
82
|
|
|
83
83
|
// ─── Compatibility Constants (for consistent wrangler.toml generation) ──
|
package/package.json
CHANGED
|
@@ -37,13 +37,13 @@ const middleware = composeMiddleware(
|
|
|
37
37
|
const routeMap = {
|
|
38
38
|
// Rewrite /api/* to upstream service
|
|
39
39
|
'/api/': {
|
|
40
|
-
upstream: 'https://
|
|
40
|
+
upstream: 'https://upstream.local',
|
|
41
41
|
stripPrefix: '/api',
|
|
42
42
|
headers: { 'X-Forwarded-By': 'clodo-proxy' }
|
|
43
43
|
},
|
|
44
44
|
// Serve static assets from R2/another origin
|
|
45
45
|
'/assets/': {
|
|
46
|
-
upstream: 'https://cdn.
|
|
46
|
+
upstream: 'https://cdn.local',
|
|
47
47
|
stripPrefix: '',
|
|
48
48
|
cache: { ttl: 86400 } // Cache for 24h
|
|
49
49
|
}
|