avro-config-cli 1.0.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.
- package/README.md +73 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +203 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# avro-config-cli
|
|
2
|
+
|
|
3
|
+
CLI tool for working with Avro schemas
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g avro-config-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Initialize configuration
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
avro-config init
|
|
17
|
+
avro-config init --template advanced
|
|
18
|
+
avro-config init --output custom-config.json
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Validate configuration
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
avro-config validate
|
|
25
|
+
avro-config validate path/to/config.json
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### View configuration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
avro-config show
|
|
32
|
+
avro-config show --env production
|
|
33
|
+
avro-config show --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Modify configuration
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
avro-config set settings.debug true
|
|
40
|
+
avro-config set settings.logLevel \"warn\"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Compare configurations
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
avro-config diff config-dev.json config-prod.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### List templates
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
avro-config templates
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Templates
|
|
56
|
+
|
|
57
|
+
| Template | Description |
|
|
58
|
+
|----------|-------------|
|
|
59
|
+
| `minimal` | Bare minimum configuration |
|
|
60
|
+
| `standard` | Recommended defaults for most projects |
|
|
61
|
+
| `advanced` | Full-featured with security, caching, and multi-environment support |
|
|
62
|
+
|
|
63
|
+
## Why avro-config-cli?
|
|
64
|
+
|
|
65
|
+
- **Zero dependencies at runtime** — just `commander` and `chalk`
|
|
66
|
+
- **Template-based** — start with minimal, standard, or advanced presets
|
|
67
|
+
- **Validation built-in** — catch config errors before deployment
|
|
68
|
+
- **Environment-aware** — manage dev/staging/production configs in one file
|
|
69
|
+
- **Diff support** — compare configs across environments
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
var SCHEMA_FILE = "schema.avsc";
|
|
9
|
+
var AVRO_PRIMITIVES = /* @__PURE__ */ new Set([
|
|
10
|
+
"null",
|
|
11
|
+
"boolean",
|
|
12
|
+
"int",
|
|
13
|
+
"long",
|
|
14
|
+
"float",
|
|
15
|
+
"double",
|
|
16
|
+
"bytes",
|
|
17
|
+
"string"
|
|
18
|
+
]);
|
|
19
|
+
function readSchema() {
|
|
20
|
+
const path = resolve(SCHEMA_FILE);
|
|
21
|
+
if (!existsSync(path)) {
|
|
22
|
+
console.error(chalk.red(`Schema file not found. Run ${chalk.bold("avro-config init")} first.`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
26
|
+
}
|
|
27
|
+
function writeSchema(schema) {
|
|
28
|
+
writeFileSync(resolve(SCHEMA_FILE), JSON.stringify(schema, null, 2) + "\n");
|
|
29
|
+
}
|
|
30
|
+
function parseAvroType(raw) {
|
|
31
|
+
const trimmed = raw.trim();
|
|
32
|
+
if (AVRO_PRIMITIVES.has(trimmed)) {
|
|
33
|
+
return trimmed;
|
|
34
|
+
}
|
|
35
|
+
if (trimmed.startsWith("array<") && trimmed.endsWith(">")) {
|
|
36
|
+
const inner = trimmed.slice(6, -1);
|
|
37
|
+
return { type: "array", items: parseAvroType(inner) };
|
|
38
|
+
}
|
|
39
|
+
if (trimmed.startsWith("map<") && trimmed.endsWith(">")) {
|
|
40
|
+
const inner = trimmed.slice(4, -1);
|
|
41
|
+
return { type: "map", values: parseAvroType(inner) };
|
|
42
|
+
}
|
|
43
|
+
if (trimmed.startsWith("union<") && trimmed.endsWith(">")) {
|
|
44
|
+
const inner = trimmed.slice(6, -1);
|
|
45
|
+
const parts = inner.split(",").map((p) => parseAvroType(p.trim()));
|
|
46
|
+
return parts;
|
|
47
|
+
}
|
|
48
|
+
console.error(
|
|
49
|
+
chalk.red(`Unknown Avro type: ${chalk.bold(raw)}`),
|
|
50
|
+
chalk.dim(
|
|
51
|
+
"\nValid primitives: null, boolean, int, long, float, double, bytes, string"
|
|
52
|
+
),
|
|
53
|
+
chalk.dim("\nComplex syntax: array<type>, map<type>, union<type1,type2>")
|
|
54
|
+
);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
return "null";
|
|
57
|
+
}
|
|
58
|
+
function avroTypeToTypeScript(type) {
|
|
59
|
+
if (typeof type === "string") {
|
|
60
|
+
const map = {
|
|
61
|
+
null: "null",
|
|
62
|
+
boolean: "boolean",
|
|
63
|
+
int: "number",
|
|
64
|
+
long: "number",
|
|
65
|
+
float: "number",
|
|
66
|
+
double: "number",
|
|
67
|
+
bytes: "Buffer",
|
|
68
|
+
string: "string"
|
|
69
|
+
};
|
|
70
|
+
return map[type] ?? "unknown";
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(type)) {
|
|
73
|
+
return type.map(avroTypeToTypeScript).join(" | ");
|
|
74
|
+
}
|
|
75
|
+
if (type.type === "array") {
|
|
76
|
+
return `${avroTypeToTypeScript(type.items)}[]`;
|
|
77
|
+
}
|
|
78
|
+
if (type.type === "map") {
|
|
79
|
+
return `Record<string, ${avroTypeToTypeScript(type.values)}>`;
|
|
80
|
+
}
|
|
81
|
+
return "unknown";
|
|
82
|
+
}
|
|
83
|
+
function avroTypeToJsonSchema(type) {
|
|
84
|
+
if (typeof type === "string") {
|
|
85
|
+
const map = {
|
|
86
|
+
null: { type: "null" },
|
|
87
|
+
boolean: { type: "boolean" },
|
|
88
|
+
int: { type: "integer" },
|
|
89
|
+
long: { type: "integer" },
|
|
90
|
+
float: { type: "number" },
|
|
91
|
+
double: { type: "number" },
|
|
92
|
+
bytes: { type: "string", contentEncoding: "base64" },
|
|
93
|
+
string: { type: "string" }
|
|
94
|
+
};
|
|
95
|
+
return map[type] ?? {};
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(type)) {
|
|
98
|
+
const schemas = type.map(avroTypeToJsonSchema);
|
|
99
|
+
return { oneOf: schemas };
|
|
100
|
+
}
|
|
101
|
+
if (type.type === "array") {
|
|
102
|
+
return { type: "array", items: avroTypeToJsonSchema(type.items) };
|
|
103
|
+
}
|
|
104
|
+
if (type.type === "map") {
|
|
105
|
+
return {
|
|
106
|
+
type: "object",
|
|
107
|
+
additionalProperties: avroTypeToJsonSchema(type.values)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {};
|
|
111
|
+
}
|
|
112
|
+
var program = new Command();
|
|
113
|
+
program.name("avro-config").description("CLI tool for working with Apache Avro schemas").version("1.0.0");
|
|
114
|
+
program.command("init").description("Create a new Avro schema template").option("-n, --name <name>", "Record name", "MyRecord").option("--namespace <namespace>", "Namespace", "com.example").option("-d, --doc <doc>", "Documentation string", "An Avro record schema").action((opts) => {
|
|
115
|
+
if (existsSync(resolve(SCHEMA_FILE))) {
|
|
116
|
+
console.error(
|
|
117
|
+
chalk.yellow(`Schema file ${chalk.bold(SCHEMA_FILE)} already exists.`)
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const schema = {
|
|
122
|
+
type: "record",
|
|
123
|
+
name: opts.name,
|
|
124
|
+
namespace: opts.namespace,
|
|
125
|
+
doc: opts.doc,
|
|
126
|
+
fields: []
|
|
127
|
+
};
|
|
128
|
+
writeSchema(schema);
|
|
129
|
+
console.log(chalk.green(`Created ${chalk.bold(SCHEMA_FILE)}`));
|
|
130
|
+
console.log(
|
|
131
|
+
chalk.dim(` name: ${schema.name}`)
|
|
132
|
+
);
|
|
133
|
+
console.log(chalk.dim(` namespace: ${schema.namespace}`));
|
|
134
|
+
console.log(chalk.dim(` doc: ${schema.doc}`));
|
|
135
|
+
});
|
|
136
|
+
program.command("field <name> <type>").description(
|
|
137
|
+
"Add a field to the schema. Types: null, boolean, int, long, float, double, bytes, string, array<type>, map<type>, union<t1,t2>"
|
|
138
|
+
).option("-d, --doc <doc>", "Field documentation").option("--default <default>", "Default value (JSON)").action(
|
|
139
|
+
(name, type, opts) => {
|
|
140
|
+
const schema = readSchema();
|
|
141
|
+
if (schema.fields.some((f) => f.name === name)) {
|
|
142
|
+
console.error(
|
|
143
|
+
chalk.red(`Field ${chalk.bold(name)} already exists in schema.`)
|
|
144
|
+
);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
const avroType = parseAvroType(type);
|
|
148
|
+
const field = { name, type: avroType };
|
|
149
|
+
if (opts.doc) field.doc = opts.doc;
|
|
150
|
+
if (opts.default !== void 0) {
|
|
151
|
+
try {
|
|
152
|
+
field.default = JSON.parse(opts.default);
|
|
153
|
+
} catch {
|
|
154
|
+
field.default = opts.default;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
schema.fields.push(field);
|
|
158
|
+
writeSchema(schema);
|
|
159
|
+
console.log(
|
|
160
|
+
chalk.green(`Added field ${chalk.bold(name)} (${chalk.cyan(type)}) to ${SCHEMA_FILE}`)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
program.command("convert").description("Show TypeScript and JSON Schema conversions for the Avro schema").action(() => {
|
|
165
|
+
const schema = readSchema();
|
|
166
|
+
console.log(chalk.bold("\nAvro Schema"));
|
|
167
|
+
console.log(chalk.dim("\u2500".repeat(50)));
|
|
168
|
+
console.log(chalk.cyan(JSON.stringify(schema, null, 2)));
|
|
169
|
+
console.log(chalk.bold("\nTypeScript Interface"));
|
|
170
|
+
console.log(chalk.dim("\u2500".repeat(50)));
|
|
171
|
+
const tsFields = schema.fields.map((f) => {
|
|
172
|
+
const tsType = avroTypeToTypeScript(f.type);
|
|
173
|
+
const comment = f.doc ? ` /** ${f.doc} */
|
|
174
|
+
` : "";
|
|
175
|
+
return `${comment} ${f.name}: ${tsType};`;
|
|
176
|
+
}).join("\n");
|
|
177
|
+
console.log(
|
|
178
|
+
chalk.yellow(`export interface ${schema.name} {
|
|
179
|
+
${tsFields}
|
|
180
|
+
}`)
|
|
181
|
+
);
|
|
182
|
+
console.log(chalk.bold("\nJSON Schema"));
|
|
183
|
+
console.log(chalk.dim("\u2500".repeat(50)));
|
|
184
|
+
const jsonSchemaProps = {};
|
|
185
|
+
const required = [];
|
|
186
|
+
for (const field of schema.fields) {
|
|
187
|
+
jsonSchemaProps[field.name] = avroTypeToJsonSchema(field.type);
|
|
188
|
+
const isNullable = Array.isArray(field.type) && field.type.some((t) => t === "null");
|
|
189
|
+
if (!isNullable && field.default === void 0) {
|
|
190
|
+
required.push(field.name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const jsonSchema = {
|
|
194
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
195
|
+
title: schema.name,
|
|
196
|
+
description: schema.doc,
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: jsonSchemaProps,
|
|
199
|
+
...required.length > 0 ? { required } : {}
|
|
200
|
+
};
|
|
201
|
+
console.log(chalk.green(JSON.stringify(jsonSchema, null, 2)));
|
|
202
|
+
});
|
|
203
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avro-config-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for working with Avro schemas",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"avro-config": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"dev": "tsup --watch"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"chalk": "^5.3.0",
|
|
18
|
+
"commander": "^12.1.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^25.5.2",
|
|
22
|
+
"tsup": "^8.1.0",
|
|
23
|
+
"typescript": "^5.4.5"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/okirmio-create/cli-forge.git",
|
|
28
|
+
"directory": "avro-config-cli"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/okirmio-create/cli-forge/tree/main/avro-config-cli",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/okirmio-create/cli-forge/issues"
|
|
33
|
+
}
|
|
34
|
+
}
|