@outfitter/cli 0.5.3 → 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 +105 -2
- package/dist/actions.d.ts +5 -2
- package/dist/actions.js +2 -2
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +8 -1
- package/dist/command.d.ts +3 -43
- package/dist/command.js +241 -13
- package/dist/envelope.d.ts +5 -0
- package/dist/envelope.js +160 -0
- package/dist/flags.d.ts +5 -189
- package/dist/flags.js +5 -1
- package/dist/hints.d.ts +34 -0
- package/dist/hints.js +26 -0
- package/dist/index.d.ts +3 -2
- package/dist/input.d.ts +3 -124
- package/dist/input.js +14 -359
- package/dist/internal/envelope-helpers.d.ts +4 -0
- package/dist/internal/envelope-helpers.js +24 -0
- package/dist/internal/envelope-types.d.ts +3 -0
- package/dist/internal/envelope-types.js +1 -0
- package/dist/internal/flag-builders.d.ts +3 -0
- package/dist/internal/flag-builders.js +155 -0
- package/dist/internal/flag-types.d.ts +3 -0
- package/dist/internal/flag-types.js +13 -0
- package/dist/internal/hint-action-graph.d.ts +5 -0
- package/dist/internal/hint-action-graph.js +11 -0
- package/dist/internal/hint-command-tree.d.ts +5 -0
- package/dist/internal/hint-command-tree.js +9 -0
- package/dist/internal/hint-error-recovery.d.ts +2 -0
- package/dist/internal/hint-error-recovery.js +7 -0
- package/dist/internal/hint-types.d.ts +4 -0
- package/dist/internal/hint-types.js +1 -0
- package/dist/internal/input-helpers.d.ts +18 -0
- package/dist/internal/input-helpers.js +11 -0
- package/dist/internal/input-normalization.d.ts +3 -0
- package/dist/internal/input-normalization.js +9 -0
- package/dist/internal/input-parsers.d.ts +3 -0
- package/dist/internal/input-parsers.js +19 -0
- package/dist/internal/input-security.d.ts +22 -0
- package/dist/internal/input-security.js +11 -0
- package/dist/internal/output-formatting.d.ts +3 -0
- package/dist/internal/output-formatting.js +21 -0
- package/dist/internal/presets.d.ts +3 -0
- package/dist/{shared/@outfitter/cli-pdb7znbq.js → internal/presets.js} +49 -223
- package/dist/internal/schema-commands.d.ts +3 -0
- package/dist/{shared/@outfitter/cli-5vtr4bdt.js → internal/schema-commands.js} +8 -99
- package/dist/internal/schema-formatting.d.ts +2 -0
- package/dist/internal/schema-formatting.js +7 -0
- package/dist/internal/schema-types.d.ts +2 -0
- package/dist/internal/schema-types.js +1 -0
- package/dist/output.d.ts +4 -3
- package/dist/output.js +13 -166
- package/dist/pagination.d.ts +1 -1
- package/dist/query.d.ts +84 -2
- package/dist/query.js +8 -45
- package/dist/schema-input.d.ts +80 -0
- package/dist/schema-input.js +15 -0
- package/dist/schema.d.ts +4 -1
- package/dist/schema.js +1 -1
- package/dist/shared/@outfitter/cli-10wxfc78.d.ts +45 -0
- package/dist/shared/@outfitter/cli-16wg5mka.d.ts +71 -0
- package/dist/shared/@outfitter/cli-1q5redaj.js +267 -0
- package/dist/shared/@outfitter/cli-2dfxs239.js +98 -0
- package/dist/shared/@outfitter/cli-30mt7c5w.d.ts +112 -0
- package/dist/shared/@outfitter/cli-3jta1h1h.js +134 -0
- package/dist/shared/@outfitter/cli-4h85mpth.js +76 -0
- package/dist/shared/@outfitter/cli-6shkwxdc.js +28 -0
- package/dist/shared/@outfitter/cli-89335n9a.js +16 -0
- package/dist/shared/@outfitter/cli-8999qjdd.js +3 -0
- package/dist/shared/@outfitter/cli-8cfxdady.js +60 -0
- package/dist/shared/@outfitter/cli-bcajqy33.d.ts +25 -0
- package/dist/shared/@outfitter/cli-c09332vm.d.ts +39 -0
- package/dist/shared/@outfitter/cli-cgha038c.d.ts +3 -0
- package/dist/shared/@outfitter/{cli-zahqsaby.js → cli-d40m2x1d.js} +19 -3
- package/dist/shared/@outfitter/cli-dg0cz7rw.js +127 -0
- package/dist/shared/@outfitter/cli-dv8kk4jw.d.ts +24 -0
- package/dist/shared/@outfitter/cli-g43887b7.js +20 -0
- package/dist/shared/@outfitter/cli-gqtkhgw4.js +52 -0
- package/dist/shared/@outfitter/cli-h4ejpmjs.d.ts +104 -0
- package/dist/shared/@outfitter/cli-htzez8v2.js +70 -0
- package/dist/shared/@outfitter/cli-hvg2m5gf.js +79 -0
- package/dist/shared/@outfitter/cli-n54zs151.d.ts +78 -0
- package/dist/shared/@outfitter/cli-nbpgw7z7.d.ts +15 -0
- package/dist/shared/@outfitter/cli-nkt399zf.d.ts +94 -0
- package/dist/shared/@outfitter/cli-pmd04gtv.d.ts +60 -0
- package/dist/shared/@outfitter/{cli-xy3gs50c.d.ts → cli-q6csxmeh.d.ts} +19 -12
- package/dist/shared/@outfitter/cli-qcskd96y.d.ts +11 -0
- package/dist/shared/@outfitter/cli-ry7btmy4.js +118 -0
- package/dist/shared/@outfitter/cli-sy99pjyj.js +32 -0
- package/dist/shared/@outfitter/cli-tm2fzngs.d.ts +23 -0
- package/dist/shared/@outfitter/cli-vvvhjwks.js +106 -0
- package/dist/shared/@outfitter/cli-wjv7g1aq.d.ts +16 -0
- package/dist/shared/@outfitter/{cli-98aa9104.d.ts → cli-x6qr7bnd.d.ts} +338 -16
- package/dist/shared/@outfitter/cli-xde45xcc.d.ts +53 -0
- package/dist/shared/@outfitter/cli-xw8ys1je.d.ts +123 -0
- package/dist/shared/@outfitter/cli-yfewnyc2.d.ts +43 -0
- package/dist/shared/@outfitter/cli-zkzj0q4q.js +99 -0
- package/dist/shared/@outfitter/cli-zv3ah6f0.js +3 -0
- package/dist/streaming.d.ts +47 -0
- package/dist/streaming.js +13 -0
- package/dist/truncation.d.ts +104 -0
- package/dist/truncation.js +111 -0
- package/dist/types.d.ts +2 -2
- package/dist/verbs.d.ts +1 -1
- package/package.json +55 -25
- package/dist/shared/@outfitter/cli-n1k0d23k.d.ts +0 -33
package/dist/input.js
CHANGED
|
@@ -1,366 +1,21 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
expandFileArg,
|
|
4
|
+
parseFilter,
|
|
5
|
+
parseGlob,
|
|
6
|
+
parseKeyValue,
|
|
7
|
+
parseRange,
|
|
8
|
+
parseSortSpec
|
|
9
|
+
} from "./shared/@outfitter/cli-1q5redaj.js";
|
|
10
|
+
import {
|
|
11
|
+
collectIds
|
|
12
|
+
} from "./shared/@outfitter/cli-gqtkhgw4.js";
|
|
13
|
+
import"./shared/@outfitter/cli-sy99pjyj.js";
|
|
14
|
+
import"./shared/@outfitter/cli-6shkwxdc.js";
|
|
15
|
+
|
|
2
16
|
// packages/cli/src/input.ts
|
|
3
|
-
import path from "path";
|
|
4
17
|
import { ValidationError } from "@outfitter/contracts";
|
|
5
18
|
import { Err, Ok } from "better-result";
|
|
6
|
-
function isSecurePath(filePath, allowAbsolute = false) {
|
|
7
|
-
if (filePath.includes("\x00")) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
if (filePath.includes("..")) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
const normalized = path.normalize(filePath);
|
|
14
|
-
if (!allowAbsolute && path.isAbsolute(normalized)) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
function isSecureGlobPattern(pattern) {
|
|
20
|
-
if (pattern.startsWith("..")) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
if (pattern.includes("/../")) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
function isWithinWorkspace(resolvedPath, workspaceRoot) {
|
|
29
|
-
const normalizedPath = path.normalize(resolvedPath);
|
|
30
|
-
const normalizedRoot = path.normalize(workspaceRoot);
|
|
31
|
-
return normalizedPath === normalizedRoot || normalizedPath.startsWith(normalizedRoot + path.sep);
|
|
32
|
-
}
|
|
33
|
-
async function readStdin() {
|
|
34
|
-
const chunks = [];
|
|
35
|
-
for await (const chunk of process.stdin) {
|
|
36
|
-
if (typeof chunk === "string") {
|
|
37
|
-
chunks.push(chunk);
|
|
38
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
39
|
-
chunks.push(chunk.toString("utf-8"));
|
|
40
|
-
} else if (chunk instanceof Uint8Array) {
|
|
41
|
-
chunks.push(Buffer.from(chunk).toString("utf-8"));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return chunks.join("");
|
|
45
|
-
}
|
|
46
|
-
function splitIds(input) {
|
|
47
|
-
return input.split(",").flatMap((part) => part.trim().split(/\s+/)).map((id) => id.trim()).filter(Boolean);
|
|
48
|
-
}
|
|
49
|
-
async function isDirectory(path2) {
|
|
50
|
-
try {
|
|
51
|
-
const result = await Bun.$`test -d ${path2}`.quiet();
|
|
52
|
-
return result.exitCode === 0;
|
|
53
|
-
} catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async function isFile(path2) {
|
|
58
|
-
try {
|
|
59
|
-
const result = await Bun.$`test -f ${path2}`.quiet();
|
|
60
|
-
return result.exitCode === 0;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
async function collectIds(input, options) {
|
|
66
|
-
const { allowFile = true, allowStdin = true } = options ?? {};
|
|
67
|
-
const ids = [];
|
|
68
|
-
const inputs = Array.isArray(input) ? input : [input];
|
|
69
|
-
for (const item of inputs) {
|
|
70
|
-
if (!item)
|
|
71
|
-
continue;
|
|
72
|
-
if (item.startsWith("@")) {
|
|
73
|
-
const filePath = item.slice(1);
|
|
74
|
-
if (filePath === "-") {
|
|
75
|
-
if (!allowStdin) {
|
|
76
|
-
throw new Error("Reading from stdin is not allowed");
|
|
77
|
-
}
|
|
78
|
-
const stdinContent = await readStdin();
|
|
79
|
-
const stdinIds = stdinContent.split(`
|
|
80
|
-
`).map((line) => line.trim()).filter(Boolean);
|
|
81
|
-
ids.push(...stdinIds);
|
|
82
|
-
} else {
|
|
83
|
-
if (!allowFile) {
|
|
84
|
-
throw new Error("File references are not allowed");
|
|
85
|
-
}
|
|
86
|
-
if (!isSecurePath(filePath, true)) {
|
|
87
|
-
throw new Error(`Security error: path traversal not allowed: ${filePath}`);
|
|
88
|
-
}
|
|
89
|
-
const file = Bun.file(filePath);
|
|
90
|
-
const exists = await file.exists();
|
|
91
|
-
if (!exists) {
|
|
92
|
-
throw new Error(`File not found: ${filePath}`);
|
|
93
|
-
}
|
|
94
|
-
const content = await file.text();
|
|
95
|
-
const fileIds = content.split(`
|
|
96
|
-
`).map((line) => line.trim()).filter(Boolean);
|
|
97
|
-
ids.push(...fileIds);
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
ids.push(...splitIds(item));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return [...new Set(ids)];
|
|
104
|
-
}
|
|
105
|
-
async function expandFileArg(input, options) {
|
|
106
|
-
const {
|
|
107
|
-
encoding: _encoding = "utf-8",
|
|
108
|
-
maxSize,
|
|
109
|
-
trim = false
|
|
110
|
-
} = options ?? {};
|
|
111
|
-
if (!input.startsWith("@")) {
|
|
112
|
-
return input;
|
|
113
|
-
}
|
|
114
|
-
const filePath = input.slice(1);
|
|
115
|
-
if (filePath === "-") {
|
|
116
|
-
let content2 = await readStdin();
|
|
117
|
-
if (trim) {
|
|
118
|
-
content2 = content2.trim();
|
|
119
|
-
}
|
|
120
|
-
return content2;
|
|
121
|
-
}
|
|
122
|
-
if (!isSecurePath(filePath, true)) {
|
|
123
|
-
throw new Error(`Security error: path traversal not allowed: ${filePath}`);
|
|
124
|
-
}
|
|
125
|
-
const file = Bun.file(filePath);
|
|
126
|
-
const exists = await file.exists();
|
|
127
|
-
if (!exists) {
|
|
128
|
-
throw new Error(`File not found: ${filePath}`);
|
|
129
|
-
}
|
|
130
|
-
if (maxSize !== undefined) {
|
|
131
|
-
const size = file.size;
|
|
132
|
-
if (size > maxSize) {
|
|
133
|
-
throw new Error(`File exceeds maximum size of ${maxSize} bytes`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
let content = await file.text();
|
|
137
|
-
if (trim) {
|
|
138
|
-
content = content.trim();
|
|
139
|
-
}
|
|
140
|
-
return content;
|
|
141
|
-
}
|
|
142
|
-
async function parseGlob(pattern, options) {
|
|
143
|
-
const {
|
|
144
|
-
cwd = process.cwd(),
|
|
145
|
-
ignore = [],
|
|
146
|
-
onlyFiles = false,
|
|
147
|
-
onlyDirectories = false,
|
|
148
|
-
followSymlinks = false
|
|
149
|
-
} = options ?? {};
|
|
150
|
-
if (!isSecureGlobPattern(pattern)) {
|
|
151
|
-
throw new Error(`Security error: glob pattern may escape workspace: ${pattern}`);
|
|
152
|
-
}
|
|
153
|
-
const resolvedCwd = path.resolve(cwd);
|
|
154
|
-
const glob = new Bun.Glob(pattern);
|
|
155
|
-
const matches = [];
|
|
156
|
-
const scanOptions = {
|
|
157
|
-
cwd,
|
|
158
|
-
followSymlinks,
|
|
159
|
-
onlyFiles: onlyFiles === true
|
|
160
|
-
};
|
|
161
|
-
for await (const match of glob.scan(scanOptions)) {
|
|
162
|
-
const fullPath = path.resolve(cwd, match);
|
|
163
|
-
if (!isWithinWorkspace(fullPath, resolvedCwd)) {
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
let shouldIgnore = false;
|
|
167
|
-
for (const ignorePattern of ignore) {
|
|
168
|
-
const ignoreGlob = new Bun.Glob(ignorePattern);
|
|
169
|
-
if (ignoreGlob.match(match)) {
|
|
170
|
-
shouldIgnore = true;
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (shouldIgnore)
|
|
175
|
-
continue;
|
|
176
|
-
if (onlyDirectories) {
|
|
177
|
-
const isDir = await isDirectory(fullPath);
|
|
178
|
-
if (!isDir)
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (onlyFiles) {
|
|
182
|
-
const isF = await isFile(fullPath);
|
|
183
|
-
if (!isF)
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
matches.push(match);
|
|
187
|
-
}
|
|
188
|
-
return matches;
|
|
189
|
-
}
|
|
190
|
-
function parseKeyValue(input) {
|
|
191
|
-
const pairs = [];
|
|
192
|
-
const inputs = Array.isArray(input) ? input : [input];
|
|
193
|
-
for (const item of inputs) {
|
|
194
|
-
if (!item)
|
|
195
|
-
continue;
|
|
196
|
-
const parts = item.split(",");
|
|
197
|
-
for (const part of parts) {
|
|
198
|
-
const trimmed = part.trim();
|
|
199
|
-
if (!trimmed)
|
|
200
|
-
continue;
|
|
201
|
-
const eqIndex = trimmed.indexOf("=");
|
|
202
|
-
if (eqIndex === -1) {
|
|
203
|
-
return new Err(new ValidationError({
|
|
204
|
-
message: `Missing '=' in key-value pair: ${trimmed}`
|
|
205
|
-
}));
|
|
206
|
-
}
|
|
207
|
-
const key = trimmed.slice(0, eqIndex).trim();
|
|
208
|
-
const value = trimmed.slice(eqIndex + 1);
|
|
209
|
-
if (!key) {
|
|
210
|
-
return new Err(new ValidationError({ message: "Empty key in key-value pair" }));
|
|
211
|
-
}
|
|
212
|
-
pairs.push({ key, value });
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return new Ok(pairs);
|
|
216
|
-
}
|
|
217
|
-
function parseRange(input, type) {
|
|
218
|
-
const trimmed = input.trim();
|
|
219
|
-
if (type === "date") {
|
|
220
|
-
const parts = trimmed.split("..");
|
|
221
|
-
if (parts.length === 1) {
|
|
222
|
-
const dateStr = parts[0];
|
|
223
|
-
if (dateStr === undefined) {
|
|
224
|
-
return new Err(new ValidationError({ message: "Empty date input" }));
|
|
225
|
-
}
|
|
226
|
-
const date = new Date(dateStr.trim());
|
|
227
|
-
if (Number.isNaN(date.getTime())) {
|
|
228
|
-
return new Err(new ValidationError({ message: `Invalid date format: ${dateStr}` }));
|
|
229
|
-
}
|
|
230
|
-
return new Ok({ type: "date", start: date, end: date });
|
|
231
|
-
}
|
|
232
|
-
if (parts.length === 2) {
|
|
233
|
-
const startStr = parts[0];
|
|
234
|
-
const endStr = parts[1];
|
|
235
|
-
if (startStr === undefined || endStr === undefined) {
|
|
236
|
-
return new Err(new ValidationError({ message: "Invalid date range format" }));
|
|
237
|
-
}
|
|
238
|
-
const start = new Date(startStr.trim());
|
|
239
|
-
const end = new Date(endStr.trim());
|
|
240
|
-
if (Number.isNaN(start.getTime())) {
|
|
241
|
-
return new Err(new ValidationError({ message: `Invalid date format: ${startStr}` }));
|
|
242
|
-
}
|
|
243
|
-
if (Number.isNaN(end.getTime())) {
|
|
244
|
-
return new Err(new ValidationError({ message: `Invalid date format: ${endStr}` }));
|
|
245
|
-
}
|
|
246
|
-
if (start.getTime() > end.getTime()) {
|
|
247
|
-
return new Err(new ValidationError({
|
|
248
|
-
message: "Start date must be before or equal to end date"
|
|
249
|
-
}));
|
|
250
|
-
}
|
|
251
|
-
return new Ok({ type: "date", start, end });
|
|
252
|
-
}
|
|
253
|
-
return new Err(new ValidationError({ message: `Invalid date range format: ${input}` }));
|
|
254
|
-
}
|
|
255
|
-
const singleNum = Number(trimmed);
|
|
256
|
-
if (!(Number.isNaN(singleNum) || trimmed.includes("-", trimmed.startsWith("-") ? 1 : 0))) {
|
|
257
|
-
return new Ok({ type: "number", min: singleNum, max: singleNum });
|
|
258
|
-
}
|
|
259
|
-
let separatorIndex = -1;
|
|
260
|
-
for (let i = 1;i < trimmed.length; i++) {
|
|
261
|
-
const char = trimmed[i];
|
|
262
|
-
const prevChar = trimmed[i - 1];
|
|
263
|
-
if (char === "-" && prevChar !== undefined && /[\d\s]/.test(prevChar)) {
|
|
264
|
-
separatorIndex = i;
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (separatorIndex === -1) {
|
|
269
|
-
return new Err(new ValidationError({ message: `Invalid numeric range format: ${input}` }));
|
|
270
|
-
}
|
|
271
|
-
const minStr = trimmed.slice(0, separatorIndex).trim();
|
|
272
|
-
const maxStr = trimmed.slice(separatorIndex + 1).trim();
|
|
273
|
-
const min = Number(minStr);
|
|
274
|
-
const max = Number(maxStr);
|
|
275
|
-
if (Number.isNaN(min)) {
|
|
276
|
-
return new Err(new ValidationError({ message: `Invalid number: ${minStr}` }));
|
|
277
|
-
}
|
|
278
|
-
if (Number.isNaN(max)) {
|
|
279
|
-
return new Err(new ValidationError({ message: `Invalid number: ${maxStr}` }));
|
|
280
|
-
}
|
|
281
|
-
if (min > max) {
|
|
282
|
-
return new Err(new ValidationError({ message: "Min must be less than or equal to max" }));
|
|
283
|
-
}
|
|
284
|
-
return new Ok({ type: "number", min, max });
|
|
285
|
-
}
|
|
286
|
-
function parseFilter(input) {
|
|
287
|
-
const trimmed = input.trim();
|
|
288
|
-
if (!trimmed) {
|
|
289
|
-
return new Ok([]);
|
|
290
|
-
}
|
|
291
|
-
const filters = [];
|
|
292
|
-
const parts = trimmed.split(",");
|
|
293
|
-
for (const part of parts) {
|
|
294
|
-
let partTrimmed = part.trim();
|
|
295
|
-
if (!partTrimmed)
|
|
296
|
-
continue;
|
|
297
|
-
let isNegated = false;
|
|
298
|
-
if (partTrimmed.startsWith("!")) {
|
|
299
|
-
isNegated = true;
|
|
300
|
-
partTrimmed = partTrimmed.slice(1).trim();
|
|
301
|
-
}
|
|
302
|
-
const colonIndex = partTrimmed.indexOf(":");
|
|
303
|
-
if (colonIndex === -1) {
|
|
304
|
-
return new Err(new ValidationError({
|
|
305
|
-
message: `Missing ':' in filter expression: ${part.trim()}`
|
|
306
|
-
}));
|
|
307
|
-
}
|
|
308
|
-
const field = partTrimmed.slice(0, colonIndex).trim();
|
|
309
|
-
let value = partTrimmed.slice(colonIndex + 1).trim();
|
|
310
|
-
let operator;
|
|
311
|
-
if (isNegated) {
|
|
312
|
-
operator = "ne";
|
|
313
|
-
} else if (value.startsWith(">=")) {
|
|
314
|
-
operator = "gte";
|
|
315
|
-
value = value.slice(2).trim();
|
|
316
|
-
} else if (value.startsWith("<=")) {
|
|
317
|
-
operator = "lte";
|
|
318
|
-
value = value.slice(2).trim();
|
|
319
|
-
} else if (value.startsWith(">")) {
|
|
320
|
-
operator = "gt";
|
|
321
|
-
value = value.slice(1).trim();
|
|
322
|
-
} else if (value.startsWith("<")) {
|
|
323
|
-
operator = "lt";
|
|
324
|
-
value = value.slice(1).trim();
|
|
325
|
-
} else if (value.startsWith("~")) {
|
|
326
|
-
operator = "contains";
|
|
327
|
-
value = value.slice(1).trim();
|
|
328
|
-
}
|
|
329
|
-
const filter = { field, value };
|
|
330
|
-
if (operator) {
|
|
331
|
-
filter.operator = operator;
|
|
332
|
-
}
|
|
333
|
-
filters.push(filter);
|
|
334
|
-
}
|
|
335
|
-
return new Ok(filters);
|
|
336
|
-
}
|
|
337
|
-
function parseSortSpec(input) {
|
|
338
|
-
const trimmed = input.trim();
|
|
339
|
-
if (!trimmed) {
|
|
340
|
-
return new Ok([]);
|
|
341
|
-
}
|
|
342
|
-
const criteria = [];
|
|
343
|
-
const parts = trimmed.split(",");
|
|
344
|
-
for (const part of parts) {
|
|
345
|
-
const partTrimmed = part.trim();
|
|
346
|
-
if (!partTrimmed)
|
|
347
|
-
continue;
|
|
348
|
-
const colonIndex = partTrimmed.indexOf(":");
|
|
349
|
-
if (colonIndex === -1) {
|
|
350
|
-
criteria.push({ field: partTrimmed, direction: "asc" });
|
|
351
|
-
} else {
|
|
352
|
-
const field = partTrimmed.slice(0, colonIndex).trim();
|
|
353
|
-
const direction = partTrimmed.slice(colonIndex + 1).trim().toLowerCase();
|
|
354
|
-
if (direction !== "asc" && direction !== "desc") {
|
|
355
|
-
return new Err(new ValidationError({
|
|
356
|
-
message: `Invalid sort direction: ${direction}. Must be 'asc' or 'desc'.`
|
|
357
|
-
}));
|
|
358
|
-
}
|
|
359
|
-
criteria.push({ field, direction });
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return new Ok(criteria);
|
|
363
|
-
}
|
|
364
19
|
function normalizeId(input, options) {
|
|
365
20
|
const {
|
|
366
21
|
trim = false,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { buildDryRunHint, createErrorEnvelope, createSuccessEnvelope, extractCategory, extractMessage, extractRetryAfterSeconds, formatEnvelopeHuman, getExitCode, safeCallHintFn } from "../shared/@outfitter/cli-nkt399zf.js";
|
|
2
|
+
import "../shared/@outfitter/cli-30mt7c5w.js";
|
|
3
|
+
import "../shared/@outfitter/cli-x6qr7bnd.js";
|
|
4
|
+
export { safeCallHintFn, getExitCode, formatEnvelopeHuman, extractRetryAfterSeconds, extractMessage, extractCategory, createSuccessEnvelope, createErrorEnvelope, buildDryRunHint };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
buildDryRunHint,
|
|
4
|
+
createErrorEnvelope,
|
|
5
|
+
createSuccessEnvelope,
|
|
6
|
+
extractCategory,
|
|
7
|
+
extractMessage,
|
|
8
|
+
extractRetryAfterSeconds,
|
|
9
|
+
formatEnvelopeHuman,
|
|
10
|
+
getExitCode,
|
|
11
|
+
safeCallHintFn
|
|
12
|
+
} from "../shared/@outfitter/cli-ry7btmy4.js";
|
|
13
|
+
import"../shared/@outfitter/cli-dg0cz7rw.js";
|
|
14
|
+
export {
|
|
15
|
+
safeCallHintFn,
|
|
16
|
+
getExitCode,
|
|
17
|
+
formatEnvelopeHuman,
|
|
18
|
+
extractRetryAfterSeconds,
|
|
19
|
+
extractMessage,
|
|
20
|
+
extractCategory,
|
|
21
|
+
createSuccessEnvelope,
|
|
22
|
+
createErrorEnvelope,
|
|
23
|
+
buildDryRunHint
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
createPreset
|
|
4
|
+
} from "../shared/@outfitter/cli-8cfxdady.js";
|
|
5
|
+
|
|
6
|
+
// packages/cli/src/internal/flag-builders.ts
|
|
7
|
+
function resolveSourceKeys(key, sources) {
|
|
8
|
+
return sources && sources.length > 0 ? sources : [key];
|
|
9
|
+
}
|
|
10
|
+
function normalizeStringListInput(value, separator) {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
const items2 = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
|
|
13
|
+
return items2.length > 0 ? items2 : undefined;
|
|
14
|
+
}
|
|
15
|
+
if (typeof value !== "string")
|
|
16
|
+
return;
|
|
17
|
+
const items = value.split(separator).map((item) => item.trim()).filter(Boolean);
|
|
18
|
+
return items.length > 0 ? items : undefined;
|
|
19
|
+
}
|
|
20
|
+
function booleanFlagPreset(config) {
|
|
21
|
+
const sources = resolveSourceKeys(config.key, config.sources);
|
|
22
|
+
const defaultValue = config.defaultValue ?? false;
|
|
23
|
+
const isNegatedFlag = config.flags.includes("--no-");
|
|
24
|
+
const optionDefault = isNegatedFlag && config.defaultValue === undefined ? {} : { defaultValue };
|
|
25
|
+
return createPreset({
|
|
26
|
+
id: config.id,
|
|
27
|
+
options: [
|
|
28
|
+
{
|
|
29
|
+
flags: config.flags,
|
|
30
|
+
description: config.description,
|
|
31
|
+
...optionDefault,
|
|
32
|
+
...config.required === true ? { required: true } : {}
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
resolve: (flags) => {
|
|
36
|
+
for (const source of sources) {
|
|
37
|
+
const value = flags[source];
|
|
38
|
+
if (typeof value === "boolean") {
|
|
39
|
+
return { [config.key]: value };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (config.negatedSources) {
|
|
43
|
+
for (const source of config.negatedSources) {
|
|
44
|
+
const value = flags[source];
|
|
45
|
+
if (typeof value === "boolean") {
|
|
46
|
+
return { [config.key]: !value };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { [config.key]: defaultValue };
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function enumFlagPreset(config) {
|
|
55
|
+
const sources = resolveSourceKeys(config.key, config.sources);
|
|
56
|
+
const allowed = new Set(config.values);
|
|
57
|
+
return createPreset({
|
|
58
|
+
id: config.id,
|
|
59
|
+
options: [
|
|
60
|
+
{
|
|
61
|
+
flags: config.flags,
|
|
62
|
+
description: config.description,
|
|
63
|
+
...config.required === true ? { required: true } : {}
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
resolve: (flags) => {
|
|
67
|
+
for (const source of sources) {
|
|
68
|
+
const value = flags[source];
|
|
69
|
+
if (typeof value === "string" && allowed.has(value)) {
|
|
70
|
+
return { [config.key]: value };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
[config.key]: config.defaultValue
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
function numberFlagPreset(config) {
|
|
80
|
+
const sources = resolveSourceKeys(config.key, config.sources);
|
|
81
|
+
return createPreset({
|
|
82
|
+
id: config.id,
|
|
83
|
+
options: [
|
|
84
|
+
{
|
|
85
|
+
flags: config.flags,
|
|
86
|
+
description: config.description,
|
|
87
|
+
...config.required === true ? { required: true } : {}
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
resolve: (flags) => {
|
|
91
|
+
let parsed;
|
|
92
|
+
for (const source of sources) {
|
|
93
|
+
const value = flags[source];
|
|
94
|
+
if (value === "" || value == null || typeof value === "boolean") {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const numeric = Number(value);
|
|
98
|
+
if (Number.isFinite(numeric)) {
|
|
99
|
+
parsed = config.integer === false ? numeric : Math.floor(numeric);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (parsed === undefined) {
|
|
104
|
+
parsed = config.defaultValue;
|
|
105
|
+
}
|
|
106
|
+
if (typeof config.min === "number" && Number.isFinite(config.min)) {
|
|
107
|
+
parsed = Math.max(parsed, config.min);
|
|
108
|
+
}
|
|
109
|
+
if (typeof config.max === "number" && Number.isFinite(config.max)) {
|
|
110
|
+
parsed = Math.min(parsed, config.max);
|
|
111
|
+
}
|
|
112
|
+
return { [config.key]: parsed };
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function stringListFlagPreset(config) {
|
|
117
|
+
const sources = resolveSourceKeys(config.key, config.sources);
|
|
118
|
+
const separator = config.separator ?? ",";
|
|
119
|
+
const fallback = config.defaultValue === undefined ? undefined : [...config.defaultValue];
|
|
120
|
+
return createPreset({
|
|
121
|
+
id: config.id,
|
|
122
|
+
options: [
|
|
123
|
+
{
|
|
124
|
+
flags: config.flags,
|
|
125
|
+
description: config.description,
|
|
126
|
+
...config.required === true ? { required: true } : {}
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
resolve: (flags) => {
|
|
130
|
+
let resolved;
|
|
131
|
+
for (const source of sources) {
|
|
132
|
+
const parsed = normalizeStringListInput(flags[source], separator);
|
|
133
|
+
if (parsed !== undefined) {
|
|
134
|
+
resolved = parsed;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (resolved === undefined) {
|
|
139
|
+
resolved = fallback === undefined ? undefined : [...fallback];
|
|
140
|
+
}
|
|
141
|
+
if (resolved && config.dedupe) {
|
|
142
|
+
resolved = [...new Set(resolved)];
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
[config.key]: resolved
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
stringListFlagPreset,
|
|
152
|
+
numberFlagPreset,
|
|
153
|
+
enumFlagPreset,
|
|
154
|
+
booleanFlagPreset
|
|
155
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { buildActionGraph, graphErrorHints, graphSuccessHints } from "../shared/@outfitter/cli-pmd04gtv.js";
|
|
2
|
+
import "../shared/@outfitter/cli-16wg5mka.js";
|
|
3
|
+
import "../shared/@outfitter/cli-xde45xcc.js";
|
|
4
|
+
import "../shared/@outfitter/cli-x6qr7bnd.js";
|
|
5
|
+
export { graphSuccessHints, graphErrorHints, buildActionGraph };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { buildCommandTree, commandTreeHints } from "../shared/@outfitter/cli-yfewnyc2.js";
|
|
2
|
+
import "../shared/@outfitter/cli-16wg5mka.js";
|
|
3
|
+
import "../shared/@outfitter/cli-xde45xcc.js";
|
|
4
|
+
import "../shared/@outfitter/cli-x6qr7bnd.js";
|
|
5
|
+
export { commandTreeHints, buildCommandTree };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ActionGraph, ActionGraphEdge, CommandTree, CommandTreeNode, CommandTreeOption } from "../shared/@outfitter/cli-16wg5mka.js";
|
|
2
|
+
import "../shared/@outfitter/cli-xde45xcc.js";
|
|
3
|
+
import "../shared/@outfitter/cli-x6qr7bnd.js";
|
|
4
|
+
export { CommandTreeOption, CommandTreeNode, CommandTree, ActionGraphEdge, ActionGraph };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic I/O and validation helpers for CLI input.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Reads stdin content using async iteration.
|
|
8
|
+
*/
|
|
9
|
+
declare function readStdin(): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Splits a string by comma and/or space, trimming each part.
|
|
12
|
+
*/
|
|
13
|
+
declare function splitIds(input: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Checks if a path is a directory using Bun shell.
|
|
16
|
+
*/
|
|
17
|
+
declare function isDirectory(dirPath: string): Promise<boolean>;
|
|
18
|
+
export { splitIds, readStdin, isDirectory };
|