@ant.sh/colony 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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/cjs/cli.js +281 -0
  4. package/dist/cjs/cli.js.map +7 -0
  5. package/dist/cjs/index.js +383 -0
  6. package/dist/cjs/index.js.map +7 -0
  7. package/dist/cjs/package.json +3 -0
  8. package/dist/cjs/parser.js +319 -0
  9. package/dist/cjs/parser.js.map +7 -0
  10. package/dist/cjs/providers/aws.js +115 -0
  11. package/dist/cjs/providers/aws.js.map +7 -0
  12. package/dist/cjs/providers/openbao.js +49 -0
  13. package/dist/cjs/providers/openbao.js.map +7 -0
  14. package/dist/cjs/providers/vault-base.js +98 -0
  15. package/dist/cjs/providers/vault-base.js.map +7 -0
  16. package/dist/cjs/providers/vault.js +49 -0
  17. package/dist/cjs/providers/vault.js.map +7 -0
  18. package/dist/cjs/resolver.js +247 -0
  19. package/dist/cjs/resolver.js.map +7 -0
  20. package/dist/cjs/secrets.js +238 -0
  21. package/dist/cjs/secrets.js.map +7 -0
  22. package/dist/cjs/strings.js +99 -0
  23. package/dist/cjs/strings.js.map +7 -0
  24. package/dist/cjs/util.js +74 -0
  25. package/dist/cjs/util.js.map +7 -0
  26. package/dist/esm/cli.js +281 -0
  27. package/dist/esm/cli.js.map +7 -0
  28. package/dist/esm/index.d.ts +342 -0
  29. package/dist/esm/index.js +347 -0
  30. package/dist/esm/index.js.map +7 -0
  31. package/dist/esm/package.json +3 -0
  32. package/dist/esm/parser.js +286 -0
  33. package/dist/esm/parser.js.map +7 -0
  34. package/dist/esm/providers/aws.js +82 -0
  35. package/dist/esm/providers/aws.js.map +7 -0
  36. package/dist/esm/providers/openbao.js +26 -0
  37. package/dist/esm/providers/openbao.js.map +7 -0
  38. package/dist/esm/providers/vault-base.js +75 -0
  39. package/dist/esm/providers/vault-base.js.map +7 -0
  40. package/dist/esm/providers/vault.js +26 -0
  41. package/dist/esm/providers/vault.js.map +7 -0
  42. package/dist/esm/resolver.js +224 -0
  43. package/dist/esm/resolver.js.map +7 -0
  44. package/dist/esm/secrets.js +209 -0
  45. package/dist/esm/secrets.js.map +7 -0
  46. package/dist/esm/strings.js +75 -0
  47. package/dist/esm/strings.js.map +7 -0
  48. package/dist/esm/util.js +47 -0
  49. package/dist/esm/util.js.map +7 -0
  50. package/package.json +66 -0
  51. package/src/cli.js +353 -0
  52. package/src/index.d.ts +342 -0
  53. package/src/index.js +473 -0
  54. package/src/parser.js +381 -0
  55. package/src/providers/aws.js +112 -0
  56. package/src/providers/openbao.js +32 -0
  57. package/src/providers/vault-base.js +92 -0
  58. package/src/providers/vault.js +31 -0
  59. package/src/resolver.js +286 -0
  60. package/src/secrets.js +313 -0
  61. package/src/strings.js +84 -0
  62. package/src/util.js +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 rune-config contributors
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/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # Colony
2
+
3
+ **Environment-aware config for Node.js. One file, multiple environments.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@ant.sh/colony.svg)](https://www.npmjs.com/package/@ant.sh/colony)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ```
9
+ # config/app.colony
10
+ *.database.host = "localhost";
11
+ prod.database.host = "prod-db.example.com";
12
+ ```
13
+
14
+ ```js
15
+ import { loadColony } from "@ant.sh/colony";
16
+
17
+ const config = await loadColony({
18
+ entry: "./config/app.colony",
19
+ ctx: { env: "prod" }
20
+ });
21
+
22
+ config.database.host // => "prod-db.example.com"
23
+ ```
24
+
25
+ ## Features
26
+
27
+ - **Single config file** for all environments (dev, staging, prod)
28
+ - **Wildcard matching** - `*` sets defaults, specific rules override
29
+ - **Multiple dimensions** - env, region, feature flags, anything
30
+ - **Secret management** - AWS Secrets Manager, Vault, OpenBao
31
+ - **Interpolation** - `${ENV:VAR}`, `${ctx.region}`, `${VAR:custom}`
32
+ - **CLI tools** - validate, diff, lint, print
33
+ - **TypeScript** - Full type definitions included
34
+ - **Zero runtime deps** - Only `fast-glob` and `json5`
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ npm install @ant.sh/colony
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ **1. Create `config/app.colony`**
45
+
46
+ ```
47
+ @dims env;
48
+
49
+ # Defaults
50
+ *.app.name = "MyApp";
51
+ *.database.host = "localhost";
52
+ *.database.port = 5432;
53
+
54
+ # Production overrides
55
+ prod.database.host = "prod-db.example.com";
56
+ ```
57
+
58
+ **2. Load in your app**
59
+
60
+ ```js
61
+ import { loadColony } from "@ant.sh/colony";
62
+
63
+ const config = await loadColony({
64
+ entry: "./config/app.colony",
65
+ ctx: { env: process.env.NODE_ENV || "dev" }
66
+ });
67
+
68
+ console.log(config.database.host);
69
+ // dev: "localhost"
70
+ // prod: "prod-db.example.com"
71
+ ```
72
+
73
+ ## Multiple Dimensions
74
+
75
+ ```
76
+ @dims env, region;
77
+
78
+ *.*.database.host = "localhost";
79
+ prod.*.database.host = "prod-db.example.com";
80
+ prod.eu.database.host = "prod-db-eu.example.com";
81
+ ```
82
+
83
+ ```js
84
+ const config = await loadColony({
85
+ entry: "./config/app.colony",
86
+ ctx: { env: "prod", region: "eu" }
87
+ });
88
+ // => "prod-db-eu.example.com"
89
+ ```
90
+
91
+ ## Secrets Integration
92
+
93
+ ```
94
+ *.db.password = "${AWS:myapp/db#password}";
95
+ *.api.key = "${OPENBAO:secret/data/app#api_key}";
96
+ ```
97
+
98
+ ```js
99
+ import { loadColony, AwsSecretsProvider, OpenBaoProvider } from "@ant.sh/colony";
100
+
101
+ const config = await loadColony({
102
+ entry: "./config/app.colony",
103
+ secrets: {
104
+ providers: [
105
+ new AwsSecretsProvider({ region: "us-east-1" }),
106
+ new OpenBaoProvider(),
107
+ ],
108
+ },
109
+ });
110
+ ```
111
+
112
+ **Built-in providers:** AWS Secrets Manager, HashiCorp Vault, OpenBao
113
+
114
+ ## CLI
115
+
116
+ ```bash
117
+ colony print --entry ./config/app.colony --ctx "env=prod"
118
+ colony diff --entry ./config/app.colony --ctx1 "env=dev" --ctx2 "env=prod"
119
+ colony validate --entry ./config/app.colony
120
+ colony lint --entry ./config/app.colony
121
+ ```
122
+
123
+ ## package.json Setup
124
+
125
+ ```json
126
+ {
127
+ "scripts": {
128
+ "dev": "NODE_ENV=dev node src/index.js",
129
+ "prod": "NODE_ENV=prod node src/index.js",
130
+ "start": "node src/index.js",
131
+ "config:validate": "colony validate --entry ./config/app.colony",
132
+ "config:lint": "colony lint --entry ./config/app.colony",
133
+ "config:diff": "colony diff --entry ./config/app.colony --ctx1 \"env=dev\" --ctx2 \"env=prod\""
134
+ }
135
+ }
136
+ ```
137
+
138
+ ```bash
139
+ npm run dev # Run with dev config
140
+ npm run prod # Run with prod config
141
+ ```
142
+
143
+ Your app picks up the environment automatically:
144
+
145
+ ```js
146
+ // src/index.js
147
+ import { loadColony } from "@ant.sh/colony";
148
+
149
+ const config = await loadColony({
150
+ entry: "./config/app.colony",
151
+ ctx: { env: process.env.NODE_ENV || "dev" }
152
+ });
153
+
154
+ console.log(`Running in ${process.env.NODE_ENV} mode`);
155
+ console.log(`Database: ${config.database.host}`);
156
+ ```
157
+
158
+ ## Documentation
159
+
160
+ See [DOCS.md](./DOCS.md) for complete documentation including:
161
+
162
+ - Config syntax reference
163
+ - All operators (`=`, `:=`, `+=`, `-=`, `|=`)
164
+ - Interpolation patterns
165
+ - Secret providers
166
+ - Security sandbox options
167
+ - API reference
168
+ - TypeScript types
169
+
170
+ ## License
171
+
172
+ MIT
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env node
2
+ var import_index = require("./index.js");
3
+ var import_util = require("./util.js");
4
+ function parseArgs(argv) {
5
+ const args = { _: [] };
6
+ for (let i = 2; i < argv.length; i++) {
7
+ const a = argv[i];
8
+ if (a.startsWith("--")) {
9
+ const k = a.slice(2);
10
+ const v = i + 1 < argv.length && !argv[i + 1].startsWith("--") ? argv[++i] : true;
11
+ args[k] = v;
12
+ } else {
13
+ args._.push(a);
14
+ }
15
+ }
16
+ return args;
17
+ }
18
+ function parseCtx(s) {
19
+ const out = {};
20
+ if (!s || s === true) return out;
21
+ const parts = String(s).split(/\s+/).filter(Boolean);
22
+ for (const p of parts) {
23
+ const idx = p.indexOf("=");
24
+ if (idx === -1) continue;
25
+ out[p.slice(0, idx)] = p.slice(idx + 1);
26
+ }
27
+ return out;
28
+ }
29
+ function printUsage() {
30
+ console.error(`Usage:
31
+ colony print --entry ./config/app.colony [options]
32
+ colony validate --entry ./config/app.colony
33
+ colony dry-run --entry ./config/app.colony
34
+ colony diff --entry ./config/app.colony --ctx1 "env=dev" --ctx2 "env=prod"
35
+ colony keys --entry ./config/app.colony [--ctx "..."]
36
+ colony env --entry ./config/app.colony [--ctx "..."]
37
+ colony lint --entry ./config/app.colony
38
+
39
+ Commands:
40
+ print Resolve and print the configuration
41
+ validate Check syntax of all colony files without resolving
42
+ dry-run List all files that would be included
43
+ diff Compare configs between two contexts
44
+ keys List all config keys in dot notation
45
+ env Output config as KEY=value for shell sourcing
46
+ lint Check for potential issues (unused rules, shadows, etc.)
47
+
48
+ Options for 'print':
49
+ --entry <file> Entry colony file (required)
50
+ --dims <d1,d2,...> Dimension names (comma-separated)
51
+ --ctx "k1=v1 k2=v2" Context values (space-separated key=value pairs)
52
+ --format <json|env> Output format (default: json)
53
+ --query <key.path> Extract specific value (like jq)
54
+ --explain <key.path> Show which rule set a specific key
55
+ --base-path <dir> Restrict includes to this directory (security)
56
+ --allowed-env <v1,v2> Whitelist of allowed env vars (security)
57
+ --allowed-vars <v1,v2> Whitelist of allowed custom vars (security)
58
+ --max-file-size <bytes> Maximum file size for includes (security)
59
+ --warn-skipped Warn when skipping already-visited includes
60
+ --show-warnings Show all warnings after output
61
+ --strict Exit with error if there are any warnings
62
+
63
+ Options for 'diff':
64
+ --entry <file> Entry colony file (required)
65
+ --dims <d1,d2,...> Dimension names (comma-separated)
66
+ --ctx1 "k1=v1 ..." First context
67
+ --ctx2 "k1=v1 ..." Second context
68
+ --format <json|text> Output format (default: text)
69
+ `);
70
+ }
71
+ function formatDiff(diff, format = "text") {
72
+ if (format === "json") {
73
+ return JSON.stringify(diff, null, 2);
74
+ }
75
+ const lines = [];
76
+ if (diff.added.length > 0) {
77
+ lines.push("Added:");
78
+ for (const key of diff.added) {
79
+ lines.push(` + ${key}`);
80
+ }
81
+ }
82
+ if (diff.removed.length > 0) {
83
+ if (lines.length > 0) lines.push("");
84
+ lines.push("Removed:");
85
+ for (const key of diff.removed) {
86
+ lines.push(` - ${key}`);
87
+ }
88
+ }
89
+ if (diff.changed.length > 0) {
90
+ if (lines.length > 0) lines.push("");
91
+ lines.push("Changed:");
92
+ for (const { key, from, to } of diff.changed) {
93
+ lines.push(` ~ ${key}`);
94
+ lines.push(` from: ${JSON.stringify(from)}`);
95
+ lines.push(` to: ${JSON.stringify(to)}`);
96
+ }
97
+ }
98
+ if (lines.length === 0) {
99
+ return "No differences found.";
100
+ }
101
+ return lines.join("\n");
102
+ }
103
+ function formatAsEnv(cfg, prefix = "") {
104
+ const lines = [];
105
+ function flatten(obj, currentPrefix) {
106
+ for (const [key, value] of Object.entries(obj)) {
107
+ const envKey = currentPrefix ? `${currentPrefix}_${key}`.toUpperCase().replace(/[^A-Z0-9_]/g, "_") : key.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
108
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
109
+ flatten(value, envKey);
110
+ } else {
111
+ const envValue = typeof value === "string" ? value : JSON.stringify(value);
112
+ const escaped = envValue.replace(/'/g, "'\\''");
113
+ lines.push(`${envKey}='${escaped}'`);
114
+ }
115
+ }
116
+ }
117
+ flatten(cfg, prefix);
118
+ return lines.sort().join("\n");
119
+ }
120
+ (async () => {
121
+ try {
122
+ const args = parseArgs(process.argv);
123
+ const cmd = args._[0];
124
+ if (!cmd || cmd === "help" || args.help) {
125
+ printUsage();
126
+ process.exit(cmd === "help" || args.help ? 0 : 1);
127
+ }
128
+ const entry = args.entry;
129
+ if (!entry) {
130
+ console.error("Error: Missing --entry\n");
131
+ printUsage();
132
+ process.exit(1);
133
+ }
134
+ if (cmd === "validate") {
135
+ const result = await (0, import_index.validateColony)(entry);
136
+ if (result.valid) {
137
+ console.log(`\u2713 All ${result.files.length} file(s) valid:`);
138
+ for (const f of result.files) {
139
+ console.log(` ${f}`);
140
+ }
141
+ process.exit(0);
142
+ } else {
143
+ console.error(`\u2717 Validation failed with ${result.errors.length} error(s):`);
144
+ for (const e of result.errors) {
145
+ console.error(`
146
+ ${e.file}:`);
147
+ console.error(` ${e.error}`);
148
+ }
149
+ process.exit(1);
150
+ }
151
+ }
152
+ if (cmd === "dry-run") {
153
+ const files = await (0, import_index.dryRunIncludes)(entry);
154
+ console.log(`Files that would be included (${files.length}):`);
155
+ for (const f of files) {
156
+ console.log(` ${f}`);
157
+ }
158
+ process.exit(0);
159
+ }
160
+ if (cmd === "diff") {
161
+ if (!args.ctx1 || !args.ctx2) {
162
+ console.error("Error: diff command requires both --ctx1 and --ctx2\n");
163
+ printUsage();
164
+ process.exit(1);
165
+ }
166
+ const dims2 = typeof args.dims === "string" ? args.dims.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
167
+ const ctx1 = parseCtx(args.ctx1);
168
+ const ctx2 = parseCtx(args.ctx2);
169
+ const result = await (0, import_index.diffColony)({ entry, dims: dims2, ctx1, ctx2 });
170
+ const format = args.format || "text";
171
+ console.log(formatDiff(result.diff, format));
172
+ const hasDiffs = result.diff.added.length > 0 || result.diff.removed.length > 0 || result.diff.changed.length > 0;
173
+ process.exit(hasDiffs ? 1 : 0);
174
+ }
175
+ if (cmd === "lint") {
176
+ const dims2 = typeof args.dims === "string" ? args.dims.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
177
+ const result = await (0, import_index.lintColony)({ entry, dims: dims2 });
178
+ if (result.issues.length === 0) {
179
+ console.log("\u2713 No issues found");
180
+ process.exit(0);
181
+ }
182
+ console.error(`Found ${result.issues.length} issue(s):
183
+ `);
184
+ for (const issue of result.issues) {
185
+ const icon = issue.severity === "error" ? "\u2717" : "\u26A0";
186
+ console.error(`${icon} [${issue.type}] ${issue.message}`);
187
+ if (issue.file) {
188
+ console.error(` at ${issue.file}:${issue.line || 0}`);
189
+ }
190
+ }
191
+ process.exit(result.issues.some((i) => i.severity === "error") ? 1 : 0);
192
+ }
193
+ if (cmd === "keys") {
194
+ const dims2 = typeof args.dims === "string" ? args.dims.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
195
+ const ctx2 = parseCtx(args.ctx);
196
+ const cfg2 = await (0, import_index.loadColony)({ entry, dims: dims2, ctx: ctx2 });
197
+ const keys = cfg2.keys();
198
+ for (const key of keys) {
199
+ console.log(key);
200
+ }
201
+ process.exit(0);
202
+ }
203
+ if (cmd === "env") {
204
+ const dims2 = typeof args.dims === "string" ? args.dims.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
205
+ const ctx2 = parseCtx(args.ctx);
206
+ const cfg2 = await (0, import_index.loadColony)({ entry, dims: dims2, ctx: ctx2 });
207
+ console.log(formatAsEnv(cfg2.toJSON(), args.prefix || ""));
208
+ process.exit(0);
209
+ }
210
+ if (cmd !== "print") {
211
+ console.error(`Error: Unknown command "${cmd}"
212
+ `);
213
+ printUsage();
214
+ process.exit(1);
215
+ }
216
+ const dims = typeof args.dims === "string" ? args.dims.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
217
+ const ctx = parseCtx(args.ctx);
218
+ const sandbox = {};
219
+ if (args["base-path"]) {
220
+ sandbox.basePath = args["base-path"];
221
+ }
222
+ if (args["allowed-env"]) {
223
+ sandbox.allowedEnvVars = args["allowed-env"].split(",").map((s) => s.trim()).filter(Boolean);
224
+ }
225
+ if (args["allowed-vars"]) {
226
+ sandbox.allowedVars = args["allowed-vars"].split(",").map((s) => s.trim()).filter(Boolean);
227
+ }
228
+ if (args["max-file-size"]) {
229
+ sandbox.maxFileSize = parseInt(args["max-file-size"], 10);
230
+ }
231
+ const warnOnSkippedIncludes = !!args["warn-skipped"];
232
+ const cfg = await (0, import_index.loadColony)({ entry, dims, ctx, sandbox, warnOnSkippedIncludes });
233
+ if (args.query) {
234
+ const value = (0, import_util.getByPath)(cfg, args.query);
235
+ if (value === void 0) {
236
+ console.error(`Key not found: ${args.query}`);
237
+ process.exit(1);
238
+ }
239
+ if (typeof value === "object") {
240
+ console.log(JSON.stringify(value, null, 2));
241
+ } else {
242
+ console.log(value);
243
+ }
244
+ } else {
245
+ const format = args.format || "json";
246
+ if (format === "env") {
247
+ console.log(formatAsEnv(cfg.toJSON(), args.prefix || ""));
248
+ } else {
249
+ console.log(JSON.stringify(cfg, null, 2));
250
+ }
251
+ }
252
+ if (args.strict && cfg._warnings?.length > 0) {
253
+ console.error(`
254
+ \u2717 Strict mode: ${cfg._warnings.length} warning(s) found:`);
255
+ for (const w of cfg._warnings) {
256
+ console.error(` [${w.type}] ${w.message}`);
257
+ }
258
+ process.exit(1);
259
+ }
260
+ if (args["show-warnings"] && cfg._warnings?.length > 0) {
261
+ console.error(`
262
+ Warnings (${cfg._warnings.length}):`);
263
+ for (const w of cfg._warnings) {
264
+ console.error(` [${w.type}] ${w.message}`);
265
+ }
266
+ }
267
+ if (typeof args.explain === "string") {
268
+ const info = cfg.explain(args.explain);
269
+ console.error(`
270
+ Explain ${args.explain}:`);
271
+ console.error(info ? JSON.stringify(info, null, 2) : "(no matching rule / unset)");
272
+ }
273
+ } catch (err) {
274
+ console.error(`Error: ${err.message}`);
275
+ if (process.env.DEBUG) {
276
+ console.error(err.stack);
277
+ }
278
+ process.exit(1);
279
+ }
280
+ })();
281
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cli.js"],
4
+ "sourcesContent": ["#!/usr/bin/env node\nimport { loadColony, validateColony, dryRunIncludes, diffColony, lintColony } from \"./index.js\";\nimport { getByPath } from \"./util.js\";\n\nfunction parseArgs(argv) {\n const args = { _: [] };\n for (let i = 2; i < argv.length; i++) {\n const a = argv[i];\n if (a.startsWith(\"--\")) {\n const k = a.slice(2);\n const v = (i + 1 < argv.length && !argv[i + 1].startsWith(\"--\")) ? argv[++i] : true;\n args[k] = v;\n } else {\n args._.push(a);\n }\n }\n return args;\n}\n\nfunction parseCtx(s) {\n const out = {};\n if (!s || s === true) return out;\n const parts = String(s).split(/\\s+/).filter(Boolean);\n for (const p of parts) {\n const idx = p.indexOf(\"=\");\n if (idx === -1) continue;\n out[p.slice(0, idx)] = p.slice(idx + 1);\n }\n return out;\n}\n\nfunction printUsage() {\n console.error(`Usage:\n colony print --entry ./config/app.colony [options]\n colony validate --entry ./config/app.colony\n colony dry-run --entry ./config/app.colony\n colony diff --entry ./config/app.colony --ctx1 \"env=dev\" --ctx2 \"env=prod\"\n colony keys --entry ./config/app.colony [--ctx \"...\"]\n colony env --entry ./config/app.colony [--ctx \"...\"]\n colony lint --entry ./config/app.colony\n\nCommands:\n print Resolve and print the configuration\n validate Check syntax of all colony files without resolving\n dry-run List all files that would be included\n diff Compare configs between two contexts\n keys List all config keys in dot notation\n env Output config as KEY=value for shell sourcing\n lint Check for potential issues (unused rules, shadows, etc.)\n\nOptions for 'print':\n --entry <file> Entry colony file (required)\n --dims <d1,d2,...> Dimension names (comma-separated)\n --ctx \"k1=v1 k2=v2\" Context values (space-separated key=value pairs)\n --format <json|env> Output format (default: json)\n --query <key.path> Extract specific value (like jq)\n --explain <key.path> Show which rule set a specific key\n --base-path <dir> Restrict includes to this directory (security)\n --allowed-env <v1,v2> Whitelist of allowed env vars (security)\n --allowed-vars <v1,v2> Whitelist of allowed custom vars (security)\n --max-file-size <bytes> Maximum file size for includes (security)\n --warn-skipped Warn when skipping already-visited includes\n --show-warnings Show all warnings after output\n --strict Exit with error if there are any warnings\n\nOptions for 'diff':\n --entry <file> Entry colony file (required)\n --dims <d1,d2,...> Dimension names (comma-separated)\n --ctx1 \"k1=v1 ...\" First context\n --ctx2 \"k1=v1 ...\" Second context\n --format <json|text> Output format (default: text)\n`);\n}\n\nfunction formatDiff(diff, format = \"text\") {\n if (format === \"json\") {\n return JSON.stringify(diff, null, 2);\n }\n\n const lines = [];\n\n if (diff.added.length > 0) {\n lines.push(\"Added:\");\n for (const key of diff.added) {\n lines.push(` + ${key}`);\n }\n }\n\n if (diff.removed.length > 0) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"Removed:\");\n for (const key of diff.removed) {\n lines.push(` - ${key}`);\n }\n }\n\n if (diff.changed.length > 0) {\n if (lines.length > 0) lines.push(\"\");\n lines.push(\"Changed:\");\n for (const { key, from, to } of diff.changed) {\n lines.push(` ~ ${key}`);\n lines.push(` from: ${JSON.stringify(from)}`);\n lines.push(` to: ${JSON.stringify(to)}`);\n }\n }\n\n if (lines.length === 0) {\n return \"No differences found.\";\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format config as KEY=value for shell sourcing\n */\nfunction formatAsEnv(cfg, prefix = \"\") {\n const lines = [];\n\n function flatten(obj, currentPrefix) {\n for (const [key, value] of Object.entries(obj)) {\n const envKey = currentPrefix\n ? `${currentPrefix}_${key}`.toUpperCase().replace(/[^A-Z0-9_]/g, \"_\")\n : key.toUpperCase().replace(/[^A-Z0-9_]/g, \"_\");\n\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n flatten(value, envKey);\n } else {\n const envValue = typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n // Escape single quotes for shell\n const escaped = envValue.replace(/'/g, \"'\\\\''\");\n lines.push(`${envKey}='${escaped}'`);\n }\n }\n }\n\n flatten(cfg, prefix);\n return lines.sort().join(\"\\n\");\n}\n\n(async () => {\n try {\n const args = parseArgs(process.argv);\n const cmd = args._[0];\n\n if (!cmd || cmd === \"help\" || args.help) {\n printUsage();\n process.exit(cmd === \"help\" || args.help ? 0 : 1);\n }\n\n const entry = args.entry;\n if (!entry) {\n console.error(\"Error: Missing --entry\\n\");\n printUsage();\n process.exit(1);\n }\n\n // Handle validate command\n if (cmd === \"validate\") {\n const result = await validateColony(entry);\n if (result.valid) {\n console.log(`\u2713 All ${result.files.length} file(s) valid:`);\n for (const f of result.files) {\n console.log(` ${f}`);\n }\n process.exit(0);\n } else {\n console.error(`\u2717 Validation failed with ${result.errors.length} error(s):`);\n for (const e of result.errors) {\n console.error(`\\n ${e.file}:`);\n console.error(` ${e.error}`);\n }\n process.exit(1);\n }\n }\n\n // Handle dry-run command\n if (cmd === \"dry-run\") {\n const files = await dryRunIncludes(entry);\n console.log(`Files that would be included (${files.length}):`);\n for (const f of files) {\n console.log(` ${f}`);\n }\n process.exit(0);\n }\n\n // Handle diff command\n if (cmd === \"diff\") {\n if (!args.ctx1 || !args.ctx2) {\n console.error(\"Error: diff command requires both --ctx1 and --ctx2\\n\");\n printUsage();\n process.exit(1);\n }\n\n const dims = typeof args.dims === \"string\"\n ? args.dims.split(\",\").map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n const ctx1 = parseCtx(args.ctx1);\n const ctx2 = parseCtx(args.ctx2);\n\n const result = await diffColony({ entry, dims, ctx1, ctx2 });\n\n const format = args.format || \"text\";\n console.log(formatDiff(result.diff, format));\n\n // Exit with code 1 if there are differences (useful for CI)\n const hasDiffs = result.diff.added.length > 0 ||\n result.diff.removed.length > 0 ||\n result.diff.changed.length > 0;\n process.exit(hasDiffs ? 1 : 0);\n }\n\n // Handle lint command\n if (cmd === \"lint\") {\n const dims = typeof args.dims === \"string\"\n ? args.dims.split(\",\").map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n const result = await lintColony({ entry, dims });\n\n if (result.issues.length === 0) {\n console.log(\"\u2713 No issues found\");\n process.exit(0);\n }\n\n console.error(`Found ${result.issues.length} issue(s):\\n`);\n for (const issue of result.issues) {\n const icon = issue.severity === \"error\" ? \"\u2717\" : \"\u26A0\";\n console.error(`${icon} [${issue.type}] ${issue.message}`);\n if (issue.file) {\n console.error(` at ${issue.file}:${issue.line || 0}`);\n }\n }\n process.exit(result.issues.some((i) => i.severity === \"error\") ? 1 : 0);\n }\n\n // Handle keys command\n if (cmd === \"keys\") {\n const dims = typeof args.dims === \"string\"\n ? args.dims.split(\",\").map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n const ctx = parseCtx(args.ctx);\n const cfg = await loadColony({ entry, dims, ctx });\n\n const keys = cfg.keys();\n for (const key of keys) {\n console.log(key);\n }\n process.exit(0);\n }\n\n // Handle env command\n if (cmd === \"env\") {\n const dims = typeof args.dims === \"string\"\n ? args.dims.split(\",\").map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n const ctx = parseCtx(args.ctx);\n const cfg = await loadColony({ entry, dims, ctx });\n\n console.log(formatAsEnv(cfg.toJSON(), args.prefix || \"\"));\n process.exit(0);\n }\n\n // Handle print command\n if (cmd !== \"print\") {\n console.error(`Error: Unknown command \"${cmd}\"\\n`);\n printUsage();\n process.exit(1);\n }\n\n const dims = typeof args.dims === \"string\"\n ? args.dims.split(\",\").map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n const ctx = parseCtx(args.ctx);\n\n // Build sandbox options\n const sandbox = {};\n if (args[\"base-path\"]) {\n sandbox.basePath = args[\"base-path\"];\n }\n if (args[\"allowed-env\"]) {\n sandbox.allowedEnvVars = args[\"allowed-env\"].split(\",\").map((s) => s.trim()).filter(Boolean);\n }\n if (args[\"allowed-vars\"]) {\n sandbox.allowedVars = args[\"allowed-vars\"].split(\",\").map((s) => s.trim()).filter(Boolean);\n }\n if (args[\"max-file-size\"]) {\n sandbox.maxFileSize = parseInt(args[\"max-file-size\"], 10);\n }\n\n const warnOnSkippedIncludes = !!args[\"warn-skipped\"];\n\n const cfg = await loadColony({ entry, dims, ctx, sandbox, warnOnSkippedIncludes });\n\n // Handle --query option\n if (args.query) {\n const value = getByPath(cfg, args.query);\n if (value === undefined) {\n console.error(`Key not found: ${args.query}`);\n process.exit(1);\n }\n if (typeof value === \"object\") {\n console.log(JSON.stringify(value, null, 2));\n } else {\n console.log(value);\n }\n } else {\n // Format output\n const format = args.format || \"json\";\n if (format === \"env\") {\n console.log(formatAsEnv(cfg.toJSON(), args.prefix || \"\"));\n } else {\n console.log(JSON.stringify(cfg, null, 2));\n }\n }\n\n // Handle --strict flag\n if (args.strict && cfg._warnings?.length > 0) {\n console.error(`\\n\u2717 Strict mode: ${cfg._warnings.length} warning(s) found:`);\n for (const w of cfg._warnings) {\n console.error(` [${w.type}] ${w.message}`);\n }\n process.exit(1);\n }\n\n // Show warnings if requested\n if (args[\"show-warnings\"] && cfg._warnings?.length > 0) {\n console.error(`\\nWarnings (${cfg._warnings.length}):`);\n for (const w of cfg._warnings) {\n console.error(` [${w.type}] ${w.message}`);\n }\n }\n\n if (typeof args.explain === \"string\") {\n const info = cfg.explain(args.explain);\n console.error(`\\nExplain ${args.explain}:`);\n console.error(info ? JSON.stringify(info, null, 2) : \"(no matching rule / unset)\");\n }\n\n } catch (err) {\n console.error(`Error: ${err.message}`);\n if (process.env.DEBUG) {\n console.error(err.stack);\n }\n process.exit(1);\n }\n})();\n"],
5
+ "mappings": "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",
6
+ "names": ["dims", "ctx", "cfg"]
7
+ }