@outfitter/cli 0.5.2 → 0.5.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/README.md +74 -58
- package/dist/actions.js +1 -1
- package/dist/colors/index.js +1 -17
- package/dist/index.js +2 -17
- package/dist/output.js +165 -4
- package/dist/schema.js +1 -1
- package/dist/shared/@outfitter/{cli-0cjts94k.js → cli-5vtr4bdt.js} +4 -1
- package/dist/terminal/index.js +1 -19
- package/dist/types.js +0 -5
- package/package.json +35 -35
- package/dist/shared/@outfitter/cli-7wp5nj0s.js +0 -169
- package/dist/shared/@outfitter/cli-zw75pdk8.js +0 -1
package/README.md
CHANGED
|
@@ -56,11 +56,11 @@ output(errors, { stream: process.stderr });
|
|
|
56
56
|
|
|
57
57
|
**Options:**
|
|
58
58
|
|
|
59
|
-
| Option
|
|
60
|
-
|
|
61
|
-
| `mode`
|
|
62
|
-
| `stream` | `WritableStream` | `stdout` | Stream to write to
|
|
63
|
-
| `pretty` | `boolean`
|
|
59
|
+
| Option | Type | Default | Description |
|
|
60
|
+
| -------- | ---------------- | -------- | ---------------------------- |
|
|
61
|
+
| `mode` | `OutputMode` | auto | Force a specific output mode |
|
|
62
|
+
| `stream` | `WritableStream` | `stdout` | Stream to write to |
|
|
63
|
+
| `pretty` | `boolean` | `false` | Pretty-print JSON output |
|
|
64
64
|
|
|
65
65
|
**Output Modes:**
|
|
66
66
|
|
|
@@ -108,10 +108,10 @@ const ids = await collectIds(args.ids, {
|
|
|
108
108
|
|
|
109
109
|
**Options:**
|
|
110
110
|
|
|
111
|
-
| Option
|
|
112
|
-
|
|
113
|
-
| `allowFile`
|
|
114
|
-
| `allowStdin` | `boolean` | `true`
|
|
111
|
+
| Option | Type | Default | Description |
|
|
112
|
+
| ------------ | --------- | ------- | ----------------------- |
|
|
113
|
+
| `allowFile` | `boolean` | `true` | Allow `@file` expansion |
|
|
114
|
+
| `allowStdin` | `boolean` | `true` | Allow `@-` for stdin |
|
|
115
115
|
|
|
116
116
|
#### `expandFileArg(input, options?)`
|
|
117
117
|
|
|
@@ -132,11 +132,11 @@ const content = await expandFileArg(args.content, {
|
|
|
132
132
|
|
|
133
133
|
**Options:**
|
|
134
134
|
|
|
135
|
-
| Option
|
|
136
|
-
|
|
137
|
-
| `encoding` | `BufferEncoding` | `utf-8` | File encoding
|
|
138
|
-
| `maxSize`
|
|
139
|
-
| `trim`
|
|
135
|
+
| Option | Type | Default | Description |
|
|
136
|
+
| ---------- | ---------------- | ------- | -------------------------- |
|
|
137
|
+
| `encoding` | `BufferEncoding` | `utf-8` | File encoding |
|
|
138
|
+
| `maxSize` | `number` | - | Maximum file size in bytes |
|
|
139
|
+
| `trim` | `boolean` | `false` | Trim whitespace |
|
|
140
140
|
|
|
141
141
|
#### `parseGlob(pattern, options?)`
|
|
142
142
|
|
|
@@ -154,13 +154,13 @@ const files = await parseGlob("src/**/*.ts", {
|
|
|
154
154
|
|
|
155
155
|
**Options:**
|
|
156
156
|
|
|
157
|
-
| Option
|
|
158
|
-
|
|
159
|
-
| `cwd`
|
|
160
|
-
| `ignore`
|
|
161
|
-
| `onlyFiles`
|
|
162
|
-
| `onlyDirectories` | `boolean`
|
|
163
|
-
| `followSymlinks`
|
|
157
|
+
| Option | Type | Default | Description |
|
|
158
|
+
| ----------------- | ---------- | --------------- | ---------------------- |
|
|
159
|
+
| `cwd` | `string` | `process.cwd()` | Working directory |
|
|
160
|
+
| `ignore` | `string[]` | `[]` | Patterns to exclude |
|
|
161
|
+
| `onlyFiles` | `boolean` | `false` | Only match files |
|
|
162
|
+
| `onlyDirectories` | `boolean` | `false` | Only match directories |
|
|
163
|
+
| `followSymlinks` | `boolean` | `false` | Follow symbolic links |
|
|
164
164
|
|
|
165
165
|
#### `parseKeyValue(input)`
|
|
166
166
|
|
|
@@ -219,15 +219,15 @@ if (result.isOk()) {
|
|
|
219
219
|
|
|
220
220
|
**Filter Operators:**
|
|
221
221
|
|
|
222
|
-
| Prefix | Operator
|
|
223
|
-
|
|
224
|
-
| (none) | `eq`
|
|
225
|
-
| `!`
|
|
226
|
-
| `>`
|
|
227
|
-
| `<`
|
|
228
|
-
| `>=`
|
|
229
|
-
| `<=`
|
|
230
|
-
| `~`
|
|
222
|
+
| Prefix | Operator | Description |
|
|
223
|
+
| ------ | ---------- | --------------------- |
|
|
224
|
+
| (none) | `eq` | Equals (default) |
|
|
225
|
+
| `!` | `ne` | Not equals |
|
|
226
|
+
| `>` | `gt` | Greater than |
|
|
227
|
+
| `<` | `lt` | Less than |
|
|
228
|
+
| `>=` | `gte` | Greater than or equal |
|
|
229
|
+
| `<=` | `lte` | Less than or equal |
|
|
230
|
+
| `~` | `contains` | Contains substring |
|
|
231
231
|
|
|
232
232
|
#### `parseSortSpec(input)`
|
|
233
233
|
|
|
@@ -268,13 +268,13 @@ if (result.isOk()) {
|
|
|
268
268
|
|
|
269
269
|
**Options:**
|
|
270
270
|
|
|
271
|
-
| Option
|
|
272
|
-
|
|
273
|
-
| `trim`
|
|
271
|
+
| Option | Type | Default | Description |
|
|
272
|
+
| ----------- | --------- | ------- | -------------------- |
|
|
273
|
+
| `trim` | `boolean` | `false` | Trim whitespace |
|
|
274
274
|
| `lowercase` | `boolean` | `false` | Convert to lowercase |
|
|
275
|
-
| `minLength` | `number`
|
|
276
|
-
| `maxLength` | `number`
|
|
277
|
-
| `pattern`
|
|
275
|
+
| `minLength` | `number` | - | Minimum length |
|
|
276
|
+
| `maxLength` | `number` | - | Maximum length |
|
|
277
|
+
| `pattern` | `RegExp` | - | Required pattern |
|
|
278
278
|
|
|
279
279
|
#### `confirmDestructive(options)`
|
|
280
280
|
|
|
@@ -366,7 +366,12 @@ if (flags.reset) {
|
|
|
366
366
|
Composable flag presets provide typed, reusable CLI flag definitions. See the [full conventions guide](../../docs/cli/conventions.md) for the complete catalog.
|
|
367
367
|
|
|
368
368
|
```typescript
|
|
369
|
-
import {
|
|
369
|
+
import {
|
|
370
|
+
composePresets,
|
|
371
|
+
verbosePreset,
|
|
372
|
+
cwdPreset,
|
|
373
|
+
forcePreset,
|
|
374
|
+
} from "@outfitter/cli/flags";
|
|
370
375
|
import { outputModePreset } from "@outfitter/cli/query";
|
|
371
376
|
|
|
372
377
|
const preset = composePresets(verbosePreset(), cwdPreset(), forcePreset());
|
|
@@ -382,6 +387,7 @@ command("deploy")
|
|
|
382
387
|
**Available presets:** `verbosePreset`, `cwdPreset`, `dryRunPreset`, `forcePreset`, `interactionPreset`, `strictPreset`, `colorPreset`, `projectionPreset`, `paginationPreset`, `timeWindowPreset`, `executionPreset`, `outputModePreset`, `jqPreset`
|
|
383
388
|
|
|
384
389
|
**Additional modules:**
|
|
390
|
+
|
|
385
391
|
- `@outfitter/cli/verbs` — Standard verb families (`create`, `modify`, `remove`, `list`, `show`)
|
|
386
392
|
- `@outfitter/cli/query` — Output mode and jq expression presets
|
|
387
393
|
- `@outfitter/cli/completion` — Shell completion script generation
|
|
@@ -390,19 +396,20 @@ command("deploy")
|
|
|
390
396
|
|
|
391
397
|
### Environment Variables
|
|
392
398
|
|
|
393
|
-
| Variable
|
|
394
|
-
|
|
395
|
-
| `OUTFITTER_ENV`
|
|
396
|
-
| `OUTFITTER_VERBOSE` | Override verbose mode (`1` or `0`)
|
|
397
|
-
| `OUTFITTER_JSON`
|
|
398
|
-
| `OUTFITTER_JSONL`
|
|
399
|
-
| `XDG_STATE_HOME`
|
|
399
|
+
| Variable | Description | Default |
|
|
400
|
+
| ------------------- | ----------------------------------------------------------- | ----------------- |
|
|
401
|
+
| `OUTFITTER_ENV` | Environment profile (`development`, `production`, `test`) | `production` |
|
|
402
|
+
| `OUTFITTER_VERBOSE` | Override verbose mode (`1` or `0`) | - |
|
|
403
|
+
| `OUTFITTER_JSON` | Set to `1` to force JSON output | - |
|
|
404
|
+
| `OUTFITTER_JSONL` | Set to `1` to force JSONL output (takes priority over JSON) | - |
|
|
405
|
+
| `XDG_STATE_HOME` | State directory for pagination | Platform-specific |
|
|
400
406
|
|
|
401
407
|
### `resolveVerbose(verbose?)`
|
|
402
408
|
|
|
403
409
|
Resolve verbose mode from environment configuration. Use this instead of hardcoding verbosity so your CLI responds to `OUTFITTER_ENV` and `OUTFITTER_VERBOSE` automatically.
|
|
404
410
|
|
|
405
411
|
**Precedence** (highest wins):
|
|
412
|
+
|
|
406
413
|
1. `OUTFITTER_VERBOSE` environment variable (`"1"` or `"0"`)
|
|
407
414
|
2. Explicit `verbose` parameter (from `--verbose` CLI flag)
|
|
408
415
|
3. `OUTFITTER_ENV` profile defaults (`true` in development)
|
|
@@ -449,18 +456,18 @@ The bridge uses `optsWithGlobals()` so global and subcommand `--json` flags both
|
|
|
449
456
|
|
|
450
457
|
Exit codes are automatically determined from error categories:
|
|
451
458
|
|
|
452
|
-
| Category
|
|
453
|
-
|
|
454
|
-
| `validation` | 1
|
|
455
|
-
| `not_found`
|
|
456
|
-
| `conflict`
|
|
457
|
-
| `permission` | 4
|
|
458
|
-
| `timeout`
|
|
459
|
-
| `rate_limit` | 6
|
|
460
|
-
| `network`
|
|
461
|
-
| `internal`
|
|
462
|
-
| `auth`
|
|
463
|
-
| `cancelled`
|
|
459
|
+
| Category | Exit Code |
|
|
460
|
+
| ------------ | --------- |
|
|
461
|
+
| `validation` | 1 |
|
|
462
|
+
| `not_found` | 2 |
|
|
463
|
+
| `conflict` | 3 |
|
|
464
|
+
| `permission` | 4 |
|
|
465
|
+
| `timeout` | 5 |
|
|
466
|
+
| `rate_limit` | 6 |
|
|
467
|
+
| `network` | 7 |
|
|
468
|
+
| `internal` | 8 |
|
|
469
|
+
| `auth` | 9 |
|
|
470
|
+
| `cancelled` | 130 |
|
|
464
471
|
|
|
465
472
|
### Tagged Errors
|
|
466
473
|
|
|
@@ -478,9 +485,18 @@ exitWithError(error); // Exits with code 2
|
|
|
478
485
|
All types are exported for TypeScript consumers:
|
|
479
486
|
|
|
480
487
|
```typescript
|
|
481
|
-
import type {
|
|
488
|
+
import type {
|
|
489
|
+
CLIConfig,
|
|
490
|
+
CommandConfig,
|
|
491
|
+
CommandAction,
|
|
492
|
+
CommandFlags,
|
|
493
|
+
} from "@outfitter/cli/command";
|
|
482
494
|
import type { OutputMode, OutputOptions } from "@outfitter/cli/output";
|
|
483
|
-
import type {
|
|
495
|
+
import type {
|
|
496
|
+
CollectIdsOptions,
|
|
497
|
+
ExpandFileOptions,
|
|
498
|
+
ParseGlobOptions,
|
|
499
|
+
} from "@outfitter/cli/input";
|
|
484
500
|
import type { PaginationState, CursorOptions } from "@outfitter/cli/pagination";
|
|
485
501
|
```
|
|
486
502
|
|
package/dist/actions.js
CHANGED
package/dist/colors/index.js
CHANGED
|
@@ -1,17 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import"../shared/@outfitter/cli-zw75pdk8.js";
|
|
3
|
-
import {
|
|
4
|
-
ANSI,
|
|
5
|
-
applyColor,
|
|
6
|
-
createTheme,
|
|
7
|
-
createTokens,
|
|
8
|
-
resolveTokenColorEnabled
|
|
9
|
-
} from "../shared/@outfitter/cli-rk9zagkm.js";
|
|
10
|
-
import"../shared/@outfitter/cli-jbj78ac5.js";
|
|
11
|
-
export {
|
|
12
|
-
resolveTokenColorEnabled,
|
|
13
|
-
createTokens,
|
|
14
|
-
createTheme,
|
|
15
|
-
applyColor,
|
|
16
|
-
ANSI
|
|
17
|
-
};
|
|
1
|
+
export { ANSI, applyColor, createTheme, createTokens, resolveTokenColorEnabled } from "./colors.js";
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
ANSI,
|
|
5
|
-
createTheme
|
|
6
|
-
} from "./shared/@outfitter/cli-rk9zagkm.js";
|
|
7
|
-
import"./shared/@outfitter/cli-jbj78ac5.js";
|
|
8
|
-
import {
|
|
9
|
-
exitWithError,
|
|
10
|
-
output
|
|
11
|
-
} from "./shared/@outfitter/cli-7wp5nj0s.js";
|
|
12
|
-
export {
|
|
13
|
-
output,
|
|
14
|
-
exitWithError,
|
|
15
|
-
createTheme,
|
|
16
|
-
ANSI
|
|
17
|
-
};
|
|
1
|
+
export { ANSI, createTheme } from "./colors/index.js";
|
|
2
|
+
export { exitWithError, output } from "./output.js";
|
package/dist/output.js
CHANGED
|
@@ -1,9 +1,170 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/cli/src/output.ts
|
|
3
|
+
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
2
4
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
safeStringify as contractsSafeStringify,
|
|
6
|
+
exitCodeMap
|
|
7
|
+
} from "@outfitter/contracts";
|
|
8
|
+
var DEFAULT_EXIT_CODE = 1;
|
|
9
|
+
function writeWithBackpressure(stream, data) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const canContinue = stream.write(data, (error) => {
|
|
12
|
+
if (error)
|
|
13
|
+
reject(error);
|
|
14
|
+
});
|
|
15
|
+
if (canContinue) {
|
|
16
|
+
resolve();
|
|
17
|
+
} else {
|
|
18
|
+
stream.once("drain", () => resolve());
|
|
19
|
+
stream.once("error", reject);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function detectMode(options) {
|
|
24
|
+
if (options?.mode) {
|
|
25
|
+
return options.mode;
|
|
26
|
+
}
|
|
27
|
+
const envJsonl = process.env["OUTFITTER_JSONL"];
|
|
28
|
+
const envJson = process.env["OUTFITTER_JSON"];
|
|
29
|
+
if (envJsonl === "1")
|
|
30
|
+
return "jsonl";
|
|
31
|
+
if (envJson === "1")
|
|
32
|
+
return "json";
|
|
33
|
+
if (envJsonl === "0" || envJson === "0")
|
|
34
|
+
return "human";
|
|
35
|
+
return "human";
|
|
36
|
+
}
|
|
37
|
+
function isValidCategory(category) {
|
|
38
|
+
return category in exitCodeMap;
|
|
39
|
+
}
|
|
40
|
+
function safeStringify(value, pretty) {
|
|
41
|
+
const wrappedValue = value === undefined ? null : value;
|
|
42
|
+
return contractsSafeStringify(wrappedValue, pretty ? 2 : undefined);
|
|
43
|
+
}
|
|
44
|
+
function formatHuman(data) {
|
|
45
|
+
if (data === null || data === undefined) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
if (typeof data === "string") {
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
52
|
+
return String(data);
|
|
53
|
+
}
|
|
54
|
+
if (Array.isArray(data)) {
|
|
55
|
+
return data.map((item) => formatHuman(item)).join(`
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
if (typeof data === "object") {
|
|
59
|
+
const lines = [];
|
|
60
|
+
for (const [key, value] of Object.entries(data)) {
|
|
61
|
+
lines.push(`${key}: ${formatHuman(value)}`);
|
|
62
|
+
}
|
|
63
|
+
return lines.join(`
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
return String(data);
|
|
67
|
+
}
|
|
68
|
+
function getErrorProperties(error) {
|
|
69
|
+
const errorObj = error;
|
|
70
|
+
return {
|
|
71
|
+
_tag: errorObj._tag,
|
|
72
|
+
category: errorObj.category,
|
|
73
|
+
context: errorObj.context
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function getExitCode(error) {
|
|
77
|
+
const { category } = getErrorProperties(error);
|
|
78
|
+
if (category !== undefined && isValidCategory(category)) {
|
|
79
|
+
return exitCodeMap[category];
|
|
80
|
+
}
|
|
81
|
+
return DEFAULT_EXIT_CODE;
|
|
82
|
+
}
|
|
83
|
+
function serializeErrorToJson(error) {
|
|
84
|
+
const { _tag, category, context } = getErrorProperties(error);
|
|
85
|
+
const result = {
|
|
86
|
+
message: error.message
|
|
87
|
+
};
|
|
88
|
+
if (_tag !== undefined) {
|
|
89
|
+
result._tag = _tag;
|
|
90
|
+
}
|
|
91
|
+
if (category !== undefined) {
|
|
92
|
+
result.category = category;
|
|
93
|
+
}
|
|
94
|
+
if (context !== undefined) {
|
|
95
|
+
result.context = context;
|
|
96
|
+
}
|
|
97
|
+
return JSON.stringify(result);
|
|
98
|
+
}
|
|
99
|
+
function formatErrorHuman(error) {
|
|
100
|
+
const { _tag } = getErrorProperties(error);
|
|
101
|
+
if (_tag) {
|
|
102
|
+
return `${_tag}: ${error.message}`;
|
|
103
|
+
}
|
|
104
|
+
return error.message;
|
|
105
|
+
}
|
|
106
|
+
async function output(data, options) {
|
|
107
|
+
const mode = detectMode(options);
|
|
108
|
+
const stream = options?.stream ?? process.stdout;
|
|
109
|
+
let outputText;
|
|
110
|
+
switch (mode) {
|
|
111
|
+
case "json": {
|
|
112
|
+
const jsonData = data === undefined ? null : data;
|
|
113
|
+
outputText = safeStringify(jsonData, options?.pretty);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case "jsonl": {
|
|
117
|
+
if (Array.isArray(data)) {
|
|
118
|
+
if (data.length === 0) {
|
|
119
|
+
outputText = "";
|
|
120
|
+
} else {
|
|
121
|
+
outputText = data.map((item) => safeStringify(item)).join(`
|
|
122
|
+
`);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
outputText = safeStringify(data);
|
|
126
|
+
}
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
default: {
|
|
130
|
+
outputText = formatHuman(data);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (outputText) {
|
|
135
|
+
await writeWithBackpressure(stream, `${outputText}
|
|
136
|
+
`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function exitWithError(error, options) {
|
|
140
|
+
const exitCode = getExitCode(error);
|
|
141
|
+
const mode = detectMode({
|
|
142
|
+
...options,
|
|
143
|
+
stream: options?.stream ?? process.stderr
|
|
144
|
+
});
|
|
145
|
+
const isJsonMode = mode === "json" || mode === "jsonl";
|
|
146
|
+
if (isJsonMode) {
|
|
147
|
+
process.stderr.write(`${serializeErrorToJson(error)}
|
|
148
|
+
`);
|
|
149
|
+
} else {
|
|
150
|
+
process.stderr.write(`${formatErrorHuman(error)}
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
process.exit(exitCode);
|
|
154
|
+
}
|
|
155
|
+
function resolveVerbose(verbose) {
|
|
156
|
+
const envVerbose = process.env["OUTFITTER_VERBOSE"];
|
|
157
|
+
if (envVerbose === "1")
|
|
158
|
+
return true;
|
|
159
|
+
if (envVerbose === "0")
|
|
160
|
+
return false;
|
|
161
|
+
if (verbose !== undefined) {
|
|
162
|
+
return verbose;
|
|
163
|
+
}
|
|
164
|
+
const env = getEnvironment();
|
|
165
|
+
const defaults = getEnvironmentDefaults(env);
|
|
166
|
+
return defaults.verbose;
|
|
167
|
+
}
|
|
7
168
|
export {
|
|
8
169
|
resolveVerbose,
|
|
9
170
|
output,
|
package/dist/schema.js
CHANGED
|
@@ -174,6 +174,7 @@ function createSchemaCommand(source, options) {
|
|
|
174
174
|
const diffCmd = new Command("diff").description("Compare runtime schema against committed surface map").option("--output <mode>", "Output mode (human, json)", "human").option("--against <version>", "Compare runtime against a named snapshot").option("--from <version>", "Base snapshot for snapshot-to-snapshot diff").option("--to <version>", "Target snapshot for snapshot-to-snapshot diff").action(async (diffOptions) => {
|
|
175
175
|
let left;
|
|
176
176
|
let right;
|
|
177
|
+
let diffMode = "committed-to-runtime";
|
|
177
178
|
if (diffOptions.from && !diffOptions.to || !diffOptions.from && diffOptions.to) {
|
|
178
179
|
process.stderr.write(`Both --from and --to are required for snapshot-to-snapshot diff.
|
|
179
180
|
`);
|
|
@@ -181,6 +182,7 @@ function createSchemaCommand(source, options) {
|
|
|
181
182
|
return;
|
|
182
183
|
}
|
|
183
184
|
if (diffOptions.from && diffOptions.to) {
|
|
185
|
+
diffMode = "snapshot-to-snapshot";
|
|
184
186
|
const fromPath = resolveSnapshotPath(cwd, outputDir, diffOptions.from);
|
|
185
187
|
const toPath = resolveSnapshotPath(cwd, outputDir, diffOptions.to);
|
|
186
188
|
try {
|
|
@@ -210,6 +212,7 @@ function createSchemaCommand(source, options) {
|
|
|
210
212
|
return;
|
|
211
213
|
}
|
|
212
214
|
} else if (diffOptions.against) {
|
|
215
|
+
diffMode = "snapshot-to-runtime";
|
|
213
216
|
const snapshotPath = resolveSnapshotPath(cwd, outputDir, diffOptions.against);
|
|
214
217
|
try {
|
|
215
218
|
left = await readSurfaceMap(snapshotPath);
|
|
@@ -244,7 +247,7 @@ function createSchemaCommand(source, options) {
|
|
|
244
247
|
}
|
|
245
248
|
right = generateSurfaceMap(source, { generator: "runtime" });
|
|
246
249
|
}
|
|
247
|
-
const result = diffSurfaceMaps(left, right);
|
|
250
|
+
const result = diffSurfaceMaps(left, right, { mode: diffMode });
|
|
248
251
|
if (diffOptions.output === "json") {
|
|
249
252
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
250
253
|
`);
|
package/dist/terminal/index.js
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
getEnvValue,
|
|
4
|
-
getTerminalWidth,
|
|
5
|
-
hasNoColorEnv,
|
|
6
|
-
isInteractive,
|
|
7
|
-
resolveColorEnv,
|
|
8
|
-
resolveForceColorEnv,
|
|
9
|
-
supportsColor
|
|
10
|
-
} from "../shared/@outfitter/cli-jbj78ac5.js";
|
|
11
|
-
export {
|
|
12
|
-
supportsColor,
|
|
13
|
-
resolveForceColorEnv,
|
|
14
|
-
resolveColorEnv,
|
|
15
|
-
isInteractive,
|
|
16
|
-
hasNoColorEnv,
|
|
17
|
-
getTerminalWidth,
|
|
18
|
-
getEnvValue
|
|
19
|
-
};
|
|
1
|
+
export { getEnvValue, getTerminalWidth, hasNoColorEnv, isInteractive, resolveColorEnv, resolveForceColorEnv, supportsColor } from "./detection.js";
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outfitter/cli",
|
|
3
|
+
"version": "0.5.3",
|
|
3
4
|
"description": "Typed CLI runtime with terminal detection, rendering, output contracts, and input parsing",
|
|
4
|
-
"
|
|
5
|
-
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bun",
|
|
7
|
+
"cli",
|
|
8
|
+
"colors",
|
|
9
|
+
"commander",
|
|
10
|
+
"outfitter",
|
|
11
|
+
"renderers",
|
|
12
|
+
"terminal",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/outfitter-dev/outfitter.git",
|
|
19
|
+
"directory": "packages/cli"
|
|
20
|
+
},
|
|
6
21
|
"files": [
|
|
7
22
|
"dist"
|
|
8
23
|
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
9
26
|
"module": "./dist/index.js",
|
|
10
27
|
"types": "./dist/index.d.ts",
|
|
11
28
|
"exports": {
|
|
@@ -107,13 +124,15 @@
|
|
|
107
124
|
}
|
|
108
125
|
}
|
|
109
126
|
},
|
|
110
|
-
"
|
|
127
|
+
"publishConfig": {
|
|
128
|
+
"access": "public"
|
|
129
|
+
},
|
|
111
130
|
"scripts": {
|
|
112
131
|
"build": "cd ../.. && bunup --filter @outfitter/cli",
|
|
113
132
|
"test": "bun test",
|
|
114
133
|
"test:watch": "bun test --watch",
|
|
115
|
-
"lint": "
|
|
116
|
-
"lint:fix": "
|
|
134
|
+
"lint": "oxlint ./src",
|
|
135
|
+
"lint:fix": "oxlint --fix ./src",
|
|
117
136
|
"typecheck": "tsc --noEmit",
|
|
118
137
|
"clean": "rm -rf dist",
|
|
119
138
|
"prepublishOnly": "bun ../../scripts/check-publish-manifest.ts"
|
|
@@ -123,41 +142,22 @@
|
|
|
123
142
|
"better-result": "^2.5.1",
|
|
124
143
|
"commander": "^14.0.2"
|
|
125
144
|
},
|
|
145
|
+
"devDependencies": {
|
|
146
|
+
"@outfitter/config": "0.3.4",
|
|
147
|
+
"@outfitter/contracts": "0.4.2",
|
|
148
|
+
"@outfitter/schema": "0.2.3",
|
|
149
|
+
"@types/bun": "^1.3.9",
|
|
150
|
+
"@types/node": "^25.3.0",
|
|
151
|
+
"typescript": "^5.9.3"
|
|
152
|
+
},
|
|
126
153
|
"peerDependencies": {
|
|
127
154
|
"@outfitter/config": ">=0.3.0",
|
|
128
155
|
"@outfitter/contracts": ">=0.2.0",
|
|
129
156
|
"@outfitter/schema": ">=0.1.0",
|
|
130
157
|
"@outfitter/types": ">=0.2.0",
|
|
131
|
-
"zod": "
|
|
132
|
-
},
|
|
133
|
-
"devDependencies": {
|
|
134
|
-
"@outfitter/config": "0.3.3",
|
|
135
|
-
"@outfitter/contracts": "0.4.1",
|
|
136
|
-
"@outfitter/schema": "0.2.2",
|
|
137
|
-
"@types/bun": "^1.3.9",
|
|
138
|
-
"@types/node": "^25.0.10",
|
|
139
|
-
"typescript": "^5.9.3"
|
|
158
|
+
"zod": ">=4.0.0"
|
|
140
159
|
},
|
|
141
160
|
"engines": {
|
|
142
|
-
"bun": ">=1.3.
|
|
143
|
-
}
|
|
144
|
-
"publishConfig": {
|
|
145
|
-
"access": "public"
|
|
146
|
-
},
|
|
147
|
-
"repository": {
|
|
148
|
-
"type": "git",
|
|
149
|
-
"url": "https://github.com/outfitter-dev/outfitter.git",
|
|
150
|
-
"directory": "packages/cli"
|
|
151
|
-
},
|
|
152
|
-
"license": "MIT",
|
|
153
|
-
"keywords": [
|
|
154
|
-
"cli",
|
|
155
|
-
"commander",
|
|
156
|
-
"typescript",
|
|
157
|
-
"bun",
|
|
158
|
-
"outfitter",
|
|
159
|
-
"terminal",
|
|
160
|
-
"colors",
|
|
161
|
-
"renderers"
|
|
162
|
-
]
|
|
161
|
+
"bun": ">=1.3.10"
|
|
162
|
+
}
|
|
163
163
|
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
// packages/cli/src/output.ts
|
|
3
|
-
import { getEnvironment, getEnvironmentDefaults } from "@outfitter/config";
|
|
4
|
-
import {
|
|
5
|
-
safeStringify as contractsSafeStringify,
|
|
6
|
-
exitCodeMap
|
|
7
|
-
} from "@outfitter/contracts";
|
|
8
|
-
var DEFAULT_EXIT_CODE = 1;
|
|
9
|
-
function writeWithBackpressure(stream, data) {
|
|
10
|
-
return new Promise((resolve, reject) => {
|
|
11
|
-
const canContinue = stream.write(data, (error) => {
|
|
12
|
-
if (error)
|
|
13
|
-
reject(error);
|
|
14
|
-
});
|
|
15
|
-
if (canContinue) {
|
|
16
|
-
resolve();
|
|
17
|
-
} else {
|
|
18
|
-
stream.once("drain", () => resolve());
|
|
19
|
-
stream.once("error", reject);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
function detectMode(options) {
|
|
24
|
-
if (options?.mode) {
|
|
25
|
-
return options.mode;
|
|
26
|
-
}
|
|
27
|
-
const envJsonl = process.env["OUTFITTER_JSONL"];
|
|
28
|
-
const envJson = process.env["OUTFITTER_JSON"];
|
|
29
|
-
if (envJsonl === "1")
|
|
30
|
-
return "jsonl";
|
|
31
|
-
if (envJson === "1")
|
|
32
|
-
return "json";
|
|
33
|
-
if (envJsonl === "0" || envJson === "0")
|
|
34
|
-
return "human";
|
|
35
|
-
return "human";
|
|
36
|
-
}
|
|
37
|
-
function isValidCategory(category) {
|
|
38
|
-
return category in exitCodeMap;
|
|
39
|
-
}
|
|
40
|
-
function safeStringify(value, pretty) {
|
|
41
|
-
const wrappedValue = value === undefined ? null : value;
|
|
42
|
-
return contractsSafeStringify(wrappedValue, pretty ? 2 : undefined);
|
|
43
|
-
}
|
|
44
|
-
function formatHuman(data) {
|
|
45
|
-
if (data === null || data === undefined) {
|
|
46
|
-
return "";
|
|
47
|
-
}
|
|
48
|
-
if (typeof data === "string") {
|
|
49
|
-
return data;
|
|
50
|
-
}
|
|
51
|
-
if (typeof data === "number" || typeof data === "boolean") {
|
|
52
|
-
return String(data);
|
|
53
|
-
}
|
|
54
|
-
if (Array.isArray(data)) {
|
|
55
|
-
return data.map((item) => formatHuman(item)).join(`
|
|
56
|
-
`);
|
|
57
|
-
}
|
|
58
|
-
if (typeof data === "object") {
|
|
59
|
-
const lines = [];
|
|
60
|
-
for (const [key, value] of Object.entries(data)) {
|
|
61
|
-
lines.push(`${key}: ${formatHuman(value)}`);
|
|
62
|
-
}
|
|
63
|
-
return lines.join(`
|
|
64
|
-
`);
|
|
65
|
-
}
|
|
66
|
-
return String(data);
|
|
67
|
-
}
|
|
68
|
-
function getErrorProperties(error) {
|
|
69
|
-
const errorObj = error;
|
|
70
|
-
return {
|
|
71
|
-
_tag: errorObj._tag,
|
|
72
|
-
category: errorObj.category,
|
|
73
|
-
context: errorObj.context
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function getExitCode(error) {
|
|
77
|
-
const { category } = getErrorProperties(error);
|
|
78
|
-
if (category !== undefined && isValidCategory(category)) {
|
|
79
|
-
return exitCodeMap[category];
|
|
80
|
-
}
|
|
81
|
-
return DEFAULT_EXIT_CODE;
|
|
82
|
-
}
|
|
83
|
-
function serializeErrorToJson(error) {
|
|
84
|
-
const { _tag, category, context } = getErrorProperties(error);
|
|
85
|
-
const result = {
|
|
86
|
-
message: error.message
|
|
87
|
-
};
|
|
88
|
-
if (_tag !== undefined) {
|
|
89
|
-
result._tag = _tag;
|
|
90
|
-
}
|
|
91
|
-
if (category !== undefined) {
|
|
92
|
-
result.category = category;
|
|
93
|
-
}
|
|
94
|
-
if (context !== undefined) {
|
|
95
|
-
result.context = context;
|
|
96
|
-
}
|
|
97
|
-
return JSON.stringify(result);
|
|
98
|
-
}
|
|
99
|
-
function formatErrorHuman(error) {
|
|
100
|
-
const { _tag } = getErrorProperties(error);
|
|
101
|
-
if (_tag) {
|
|
102
|
-
return `${_tag}: ${error.message}`;
|
|
103
|
-
}
|
|
104
|
-
return error.message;
|
|
105
|
-
}
|
|
106
|
-
async function output(data, options) {
|
|
107
|
-
const mode = detectMode(options);
|
|
108
|
-
const stream = options?.stream ?? process.stdout;
|
|
109
|
-
let outputText;
|
|
110
|
-
switch (mode) {
|
|
111
|
-
case "json": {
|
|
112
|
-
const jsonData = data === undefined ? null : data;
|
|
113
|
-
outputText = safeStringify(jsonData, options?.pretty);
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
case "jsonl": {
|
|
117
|
-
if (Array.isArray(data)) {
|
|
118
|
-
if (data.length === 0) {
|
|
119
|
-
outputText = "";
|
|
120
|
-
} else {
|
|
121
|
-
outputText = data.map((item) => safeStringify(item)).join(`
|
|
122
|
-
`);
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
outputText = safeStringify(data);
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
default: {
|
|
130
|
-
outputText = formatHuman(data);
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (outputText) {
|
|
135
|
-
await writeWithBackpressure(stream, `${outputText}
|
|
136
|
-
`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
function exitWithError(error, options) {
|
|
140
|
-
const exitCode = getExitCode(error);
|
|
141
|
-
const mode = detectMode({
|
|
142
|
-
...options,
|
|
143
|
-
stream: options?.stream ?? process.stderr
|
|
144
|
-
});
|
|
145
|
-
const isJsonMode = mode === "json" || mode === "jsonl";
|
|
146
|
-
if (isJsonMode) {
|
|
147
|
-
process.stderr.write(`${serializeErrorToJson(error)}
|
|
148
|
-
`);
|
|
149
|
-
} else {
|
|
150
|
-
process.stderr.write(`${formatErrorHuman(error)}
|
|
151
|
-
`);
|
|
152
|
-
}
|
|
153
|
-
process.exit(exitCode);
|
|
154
|
-
}
|
|
155
|
-
function resolveVerbose(verbose) {
|
|
156
|
-
const envVerbose = process.env["OUTFITTER_VERBOSE"];
|
|
157
|
-
if (envVerbose === "1")
|
|
158
|
-
return true;
|
|
159
|
-
if (envVerbose === "0")
|
|
160
|
-
return false;
|
|
161
|
-
if (verbose !== undefined) {
|
|
162
|
-
return verbose;
|
|
163
|
-
}
|
|
164
|
-
const env = getEnvironment();
|
|
165
|
-
const defaults = getEnvironmentDefaults(env);
|
|
166
|
-
return defaults.verbose;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export { output, exitWithError, resolveVerbose };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// @bun
|