@commissionsight/cli 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.
- package/LICENSE +21 -0
- package/README.md +248 -0
- package/bin/cs.mjs +2 -0
- package/dist/commands/admin.d.ts +7 -0
- package/dist/commands/admin.js +409 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.js +107 -0
- package/dist/commands/batch.d.ts +2 -0
- package/dist/commands/batch.js +68 -0
- package/dist/commands/billing.d.ts +6 -0
- package/dist/commands/billing.js +75 -0
- package/dist/commands/carrier.d.ts +6 -0
- package/dist/commands/carrier.js +111 -0
- package/dist/commands/completion.d.ts +6 -0
- package/dist/commands/completion.js +56 -0
- package/dist/commands/context.d.ts +6 -0
- package/dist/commands/context.js +73 -0
- package/dist/commands/file.d.ts +6 -0
- package/dist/commands/file.js +97 -0
- package/dist/commands/job.d.ts +2 -0
- package/dist/commands/job.js +186 -0
- package/dist/commands/member.d.ts +5 -0
- package/dist/commands/member.js +91 -0
- package/dist/commands/meta.d.ts +7 -0
- package/dist/commands/meta.js +36 -0
- package/dist/commands/rate.d.ts +5 -0
- package/dist/commands/rate.js +69 -0
- package/dist/commands/registry.d.ts +14 -0
- package/dist/commands/registry.js +56 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +168 -0
- package/dist/commands/session.d.ts +5 -0
- package/dist/commands/session.js +21 -0
- package/dist/commands/team.d.ts +5 -0
- package/dist/commands/team.js +61 -0
- package/dist/commands/upload.d.ts +85 -0
- package/dist/commands/upload.js +111 -0
- package/dist/commands/webhook.d.ts +5 -0
- package/dist/commands/webhook.js +56 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.js +65 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.js +33 -0
- package/dist/config/store.d.ts +17 -0
- package/dist/config/store.js +74 -0
- package/dist/context.d.ts +22 -0
- package/dist/context.js +100 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +70 -0
- package/dist/globals.d.ts +10 -0
- package/dist/globals.js +38 -0
- package/dist/io.d.ts +28 -0
- package/dist/io.js +28 -0
- package/dist/lib/batch.d.ts +52 -0
- package/dist/lib/batch.js +0 -0
- package/dist/lib/confirm.d.ts +2 -0
- package/dist/lib/confirm.js +23 -0
- package/dist/lib/file.d.ts +6 -0
- package/dist/lib/file.js +43 -0
- package/dist/lib/input.d.ts +2 -0
- package/dist/lib/input.js +35 -0
- package/dist/lib/paginate.d.ts +33 -0
- package/dist/lib/paginate.js +47 -0
- package/dist/lib/period.d.ts +15 -0
- package/dist/lib/period.js +43 -0
- package/dist/lib/poll.d.ts +14 -0
- package/dist/lib/poll.js +17 -0
- package/dist/lib/resolve.d.ts +30 -0
- package/dist/lib/resolve.js +81 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +17 -0
- package/dist/output/color.d.ts +26 -0
- package/dist/output/color.js +37 -0
- package/dist/output/csv.d.ts +25 -0
- package/dist/output/csv.js +119 -0
- package/dist/output/envelope.d.ts +29 -0
- package/dist/output/envelope.js +66 -0
- package/dist/output/help.d.ts +7 -0
- package/dist/output/help.js +57 -0
- package/dist/output/print.d.ts +14 -0
- package/dist/output/print.js +70 -0
- package/dist/output/schema-tree.d.ts +32 -0
- package/dist/output/schema-tree.js +33 -0
- package/dist/router.d.ts +6 -0
- package/dist/router.js +267 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +1 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +39 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +41 -0
- package/package.json +53 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic, RFC-4180 CSV writer (plan §9.6). Column order is fixed per
|
|
3
|
+
* known result type so exports are stable/diff-friendly. Arrays render
|
|
4
|
+
* pipe-joined; nested objects are flattened with dotted keys.
|
|
5
|
+
*/
|
|
6
|
+
/** Canonical column orders for the exportable result types. */
|
|
7
|
+
export declare const RESULT_ROW_COLUMNS: readonly ["memberRefId", "policyRefId", "status", "flags", "commissionAmount", "prevCommissionAmount", "commissionOwed", "comparedAgainstPeriod", "memberExternalId", "memberName", "email", "planName", "policyNumber", "premiumAmount"];
|
|
8
|
+
export declare const CHARGEBACK_COLUMNS: readonly ["memberRefId", "policyRefId", "memberExternalId", "policyNumber", "planName", "chargebackAmount", "paidOut", "originalPayout.period", "originalPayout.amount", "originalPayout.fileName", "fullyReversed"];
|
|
9
|
+
export declare const ATTRITION_COLUMNS: readonly ["period", "year", "month", "red", "memberCount", "attritionRate", "commissionAtRisk"];
|
|
10
|
+
export declare const COMPARISON_COLUMNS: readonly ["memberRefId", "status", "flags", "commissionAmount", "prevCommissionAmount", "comparedAgainstPeriod", "memberName", "memberExternalId", "policyNumber"];
|
|
11
|
+
/**
|
|
12
|
+
* Render rows to CSV text. When `columns` is omitted, the union of keys (in
|
|
13
|
+
* first-seen order) is used.
|
|
14
|
+
*/
|
|
15
|
+
export declare function toCSV(rows: Record<string, unknown>[], columns?: readonly string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* Emit CSV to a file (when `outPath` is set) or to stdout. Returns a short
|
|
18
|
+
* status object for the command to surface. Keeps stdout CSV-only in --csv mode.
|
|
19
|
+
*/
|
|
20
|
+
export declare function emitCSV(io: {
|
|
21
|
+
stdout(s: string): void;
|
|
22
|
+
}, rows: Record<string, unknown>[], columns: readonly string[] | undefined, outPath: string | undefined, writeFile: (path: string, data: string) => void): {
|
|
23
|
+
rows: number;
|
|
24
|
+
out: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic, RFC-4180 CSV writer (plan §9.6). Column order is fixed per
|
|
3
|
+
* known result type so exports are stable/diff-friendly. Arrays render
|
|
4
|
+
* pipe-joined; nested objects are flattened with dotted keys.
|
|
5
|
+
*/
|
|
6
|
+
/** Canonical column orders for the exportable result types. */
|
|
7
|
+
export const RESULT_ROW_COLUMNS = [
|
|
8
|
+
'memberRefId',
|
|
9
|
+
'policyRefId',
|
|
10
|
+
'status',
|
|
11
|
+
'flags',
|
|
12
|
+
'commissionAmount',
|
|
13
|
+
'prevCommissionAmount',
|
|
14
|
+
'commissionOwed',
|
|
15
|
+
'comparedAgainstPeriod',
|
|
16
|
+
'memberExternalId',
|
|
17
|
+
'memberName',
|
|
18
|
+
'email',
|
|
19
|
+
'planName',
|
|
20
|
+
'policyNumber',
|
|
21
|
+
'premiumAmount',
|
|
22
|
+
];
|
|
23
|
+
export const CHARGEBACK_COLUMNS = [
|
|
24
|
+
'memberRefId',
|
|
25
|
+
'policyRefId',
|
|
26
|
+
'memberExternalId',
|
|
27
|
+
'policyNumber',
|
|
28
|
+
'planName',
|
|
29
|
+
'chargebackAmount',
|
|
30
|
+
'paidOut',
|
|
31
|
+
'originalPayout.period',
|
|
32
|
+
'originalPayout.amount',
|
|
33
|
+
'originalPayout.fileName',
|
|
34
|
+
'fullyReversed',
|
|
35
|
+
];
|
|
36
|
+
export const ATTRITION_COLUMNS = [
|
|
37
|
+
'period',
|
|
38
|
+
'year',
|
|
39
|
+
'month',
|
|
40
|
+
'red',
|
|
41
|
+
'memberCount',
|
|
42
|
+
'attritionRate',
|
|
43
|
+
'commissionAtRisk',
|
|
44
|
+
];
|
|
45
|
+
export const COMPARISON_COLUMNS = [
|
|
46
|
+
'memberRefId',
|
|
47
|
+
'status',
|
|
48
|
+
'flags',
|
|
49
|
+
'commissionAmount',
|
|
50
|
+
'prevCommissionAmount',
|
|
51
|
+
'comparedAgainstPeriod',
|
|
52
|
+
'memberName',
|
|
53
|
+
'memberExternalId',
|
|
54
|
+
'policyNumber',
|
|
55
|
+
];
|
|
56
|
+
function getPath(obj, path) {
|
|
57
|
+
if (!path.includes('.'))
|
|
58
|
+
return obj[path];
|
|
59
|
+
return path.split('.').reduce((acc, key) => {
|
|
60
|
+
if (acc && typeof acc === 'object')
|
|
61
|
+
return acc[key];
|
|
62
|
+
return undefined;
|
|
63
|
+
}, obj);
|
|
64
|
+
}
|
|
65
|
+
function cell(value) {
|
|
66
|
+
if (value === null || value === undefined)
|
|
67
|
+
return '';
|
|
68
|
+
if (Array.isArray(value))
|
|
69
|
+
return value.join('|');
|
|
70
|
+
if (typeof value === 'object')
|
|
71
|
+
return JSON.stringify(value);
|
|
72
|
+
return String(value);
|
|
73
|
+
}
|
|
74
|
+
/** RFC-4180 field quoting. */
|
|
75
|
+
function quote(field) {
|
|
76
|
+
if (/[",\r\n]/.test(field)) {
|
|
77
|
+
return '"' + field.replace(/"/g, '""') + '"';
|
|
78
|
+
}
|
|
79
|
+
return field;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Render rows to CSV text. When `columns` is omitted, the union of keys (in
|
|
83
|
+
* first-seen order) is used.
|
|
84
|
+
*/
|
|
85
|
+
export function toCSV(rows, columns) {
|
|
86
|
+
const cols = columns ?? unionKeys(rows);
|
|
87
|
+
const lines = [];
|
|
88
|
+
lines.push(cols.map((c) => quote(c)).join(','));
|
|
89
|
+
for (const row of rows) {
|
|
90
|
+
lines.push(cols.map((c) => quote(cell(getPath(row, c)))).join(','));
|
|
91
|
+
}
|
|
92
|
+
return lines.join('\r\n') + '\r\n';
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Emit CSV to a file (when `outPath` is set) or to stdout. Returns a short
|
|
96
|
+
* status object for the command to surface. Keeps stdout CSV-only in --csv mode.
|
|
97
|
+
*/
|
|
98
|
+
export function emitCSV(io, rows, columns, outPath, writeFile) {
|
|
99
|
+
const csv = toCSV(rows, columns);
|
|
100
|
+
if (outPath) {
|
|
101
|
+
writeFile(outPath, csv);
|
|
102
|
+
return { rows: rows.length, out: outPath };
|
|
103
|
+
}
|
|
104
|
+
io.stdout(csv);
|
|
105
|
+
return { rows: rows.length, out: '(stdout)' };
|
|
106
|
+
}
|
|
107
|
+
function unionKeys(rows) {
|
|
108
|
+
const seen = [];
|
|
109
|
+
const set = new Set();
|
|
110
|
+
for (const r of rows) {
|
|
111
|
+
for (const k of Object.keys(r)) {
|
|
112
|
+
if (!set.has(k)) {
|
|
113
|
+
set.add(k);
|
|
114
|
+
seen.push(k);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return seen;
|
|
119
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stable JSON envelope (plan §5.1) and the error-shaping helpers.
|
|
3
|
+
* Success → { ok: true, data }. Failure → { ok: false, error }.
|
|
4
|
+
*/
|
|
5
|
+
import { exitCodeFor } from '../errors.js';
|
|
6
|
+
export interface OkEnvelope {
|
|
7
|
+
ok: true;
|
|
8
|
+
data: unknown;
|
|
9
|
+
}
|
|
10
|
+
export interface ErrEnvelope {
|
|
11
|
+
ok: false;
|
|
12
|
+
error: {
|
|
13
|
+
status: number;
|
|
14
|
+
code: string | null;
|
|
15
|
+
message: string;
|
|
16
|
+
body?: unknown;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export type Envelope = OkEnvelope | ErrEnvelope;
|
|
20
|
+
export declare function ok(data: unknown): OkEnvelope;
|
|
21
|
+
/**
|
|
22
|
+
* Derive a stable machine `code` from an RFC 9457 problem+json body: prefer the
|
|
23
|
+
* `type` URI slug, then `title`/`code`. Returns null when nothing usable.
|
|
24
|
+
*/
|
|
25
|
+
export declare function deriveCode(body: unknown): string | null;
|
|
26
|
+
/** Shape any thrown value into the error envelope. */
|
|
27
|
+
export declare function errEnvelope(err: unknown): ErrEnvelope;
|
|
28
|
+
/** Convenience: the exit code that pairs with a thrown value. */
|
|
29
|
+
export { exitCodeFor };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The stable JSON envelope (plan §5.1) and the error-shaping helpers.
|
|
3
|
+
* Success → { ok: true, data }. Failure → { ok: false, error }.
|
|
4
|
+
*/
|
|
5
|
+
import { ApiError, UsageError, exitCodeFor } from '../errors.js';
|
|
6
|
+
export function ok(data) {
|
|
7
|
+
return { ok: true, data };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Derive a stable machine `code` from an RFC 9457 problem+json body: prefer the
|
|
11
|
+
* `type` URI slug, then `title`/`code`. Returns null when nothing usable.
|
|
12
|
+
*/
|
|
13
|
+
export function deriveCode(body) {
|
|
14
|
+
if (!body || typeof body !== 'object')
|
|
15
|
+
return null;
|
|
16
|
+
const b = body;
|
|
17
|
+
if (typeof b['code'] === 'string' && b['code'])
|
|
18
|
+
return slug(b['code']);
|
|
19
|
+
if (typeof b['type'] === 'string' && b['type'] && b['type'] !== 'about:blank') {
|
|
20
|
+
const tail = b['type'].split(/[/#]/).filter(Boolean).pop();
|
|
21
|
+
if (tail)
|
|
22
|
+
return slug(tail);
|
|
23
|
+
}
|
|
24
|
+
if (typeof b['title'] === 'string' && b['title'])
|
|
25
|
+
return slug(b['title']);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function slug(s) {
|
|
29
|
+
return s
|
|
30
|
+
.trim()
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
33
|
+
.replace(/^_+|_+$/g, '');
|
|
34
|
+
}
|
|
35
|
+
/** Shape any thrown value into the error envelope. */
|
|
36
|
+
export function errEnvelope(err) {
|
|
37
|
+
if (err instanceof ApiError) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: {
|
|
41
|
+
status: err.status,
|
|
42
|
+
code: deriveCode(err.body),
|
|
43
|
+
message: err.message,
|
|
44
|
+
body: err.body,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (err instanceof UsageError) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: {
|
|
52
|
+
status: 0,
|
|
53
|
+
code: 'usage_error',
|
|
54
|
+
message: err.message,
|
|
55
|
+
...(err.candidates ? { body: { candidates: err.candidates } } : {}),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
const code = err instanceof Error && err.name && err.name !== 'Error'
|
|
61
|
+
? slug(err.name)
|
|
62
|
+
: null;
|
|
63
|
+
return { ok: false, error: { status: 0, code, message } };
|
|
64
|
+
}
|
|
65
|
+
/** Convenience: the exit code that pairs with a thrown value. */
|
|
66
|
+
export { exitCodeFor };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--help` rendering, derived from the command table and global flag specs.
|
|
3
|
+
*/
|
|
4
|
+
import type { Cmd } from '../types.js';
|
|
5
|
+
import type { IO } from '../io.js';
|
|
6
|
+
export declare function renderTopHelp(io: IO, table: Cmd[]): void;
|
|
7
|
+
export declare function renderCommandHelp(io: IO, cmd: Cmd, table: Cmd[]): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { GLOBAL_OPTIONS } from '../globals.js';
|
|
2
|
+
import { cliVersion } from '../version.js';
|
|
3
|
+
function optLine(name, spec) {
|
|
4
|
+
const short = spec.short ? `-${spec.short}, ` : ' ';
|
|
5
|
+
const val = spec.type === 'string' ? ` ${spec.placeholder ?? '<value>'}` : '';
|
|
6
|
+
const flag = `${short}--${name}${val}`;
|
|
7
|
+
const choices = spec.choices ? ` (${spec.choices.join('|')})` : '';
|
|
8
|
+
return ` ${flag.padEnd(30)}${spec.desc}${choices}`;
|
|
9
|
+
}
|
|
10
|
+
export function renderTopHelp(io, table) {
|
|
11
|
+
io.stdout(`cs ${cliVersion()} — CommissionSight CLI\n\n`);
|
|
12
|
+
io.stdout('Usage: cs <command> [subcommand] [args] [flags]\n\n');
|
|
13
|
+
// Group by top-level segment.
|
|
14
|
+
const groups = new Map();
|
|
15
|
+
for (const c of table) {
|
|
16
|
+
const key = c.path[0];
|
|
17
|
+
const arr = groups.get(key) ?? [];
|
|
18
|
+
arr.push(c);
|
|
19
|
+
groups.set(key, arr);
|
|
20
|
+
}
|
|
21
|
+
io.stdout('Commands:\n');
|
|
22
|
+
for (const key of [...groups.keys()].sort()) {
|
|
23
|
+
const cmds = groups.get(key).sort((a, b) => a.path.join(' ').localeCompare(b.path.join(' ')));
|
|
24
|
+
for (const c of cmds) {
|
|
25
|
+
io.stdout(` ${c.path.join(' ').padEnd(28)}${c.summary}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
io.stdout('\nGlobal flags:\n');
|
|
29
|
+
for (const [name, spec] of Object.entries(GLOBAL_OPTIONS)) {
|
|
30
|
+
io.stdout(optLine(name, spec) + '\n');
|
|
31
|
+
}
|
|
32
|
+
io.stdout("\nRun 'cs <command> --help' for command-specific help.\n");
|
|
33
|
+
io.stdout("Run 'cs schema --json' for the machine-readable command tree.\n");
|
|
34
|
+
}
|
|
35
|
+
export function renderCommandHelp(io, cmd, table) {
|
|
36
|
+
const usageArgs = (cmd.args ?? [])
|
|
37
|
+
.map((a) => (a.required ? `<${a.name}>` : `[${a.name}]`) + (a.variadic ? '...' : ''))
|
|
38
|
+
.join(' ');
|
|
39
|
+
io.stdout(`cs ${cmd.path.join(' ')} — ${cmd.summary}\n\n`);
|
|
40
|
+
io.stdout(`Usage: cs ${cmd.path.join(' ')} ${usageArgs} [flags]\n`);
|
|
41
|
+
// Subcommands sharing this prefix.
|
|
42
|
+
const subs = table.filter((c) => c !== cmd && cmd.path.every((seg, i) => c.path[i] === seg) && c.path.length > cmd.path.length);
|
|
43
|
+
if (subs.length) {
|
|
44
|
+
io.stdout('\nSubcommands:\n');
|
|
45
|
+
for (const s of subs.sort((a, b) => a.path.join(' ').localeCompare(b.path.join(' ')))) {
|
|
46
|
+
io.stdout(` ${s.path.join(' ').padEnd(28)}${s.summary}\n`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const opts = cmd.options ?? {};
|
|
50
|
+
if (Object.keys(opts).length) {
|
|
51
|
+
io.stdout('\nFlags:\n');
|
|
52
|
+
for (const [name, spec] of Object.entries(opts)) {
|
|
53
|
+
io.stdout(optLine(name, spec) + '\n');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
io.stdout('\nGlobal flags apply (see `cs --help`).\n');
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human renderer: a minimal table + key/value printer (plan §9.6). Zero deps.
|
|
3
|
+
* All output goes through the injected IO so tests can capture it.
|
|
4
|
+
*/
|
|
5
|
+
import type { IO } from '../io.js';
|
|
6
|
+
import { type Painter } from './color.js';
|
|
7
|
+
export interface Printer {
|
|
8
|
+
paint: Painter;
|
|
9
|
+
line(s?: string): void;
|
|
10
|
+
kv(obj: Record<string, unknown>): void;
|
|
11
|
+
table(rows: Record<string, unknown>[], columns?: string[]): void;
|
|
12
|
+
raw(s: string): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function makePrinter(io: IO, colorEnabled: boolean): Printer;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { makePainter, statusColor } from './color.js';
|
|
2
|
+
export function makePrinter(io, colorEnabled) {
|
|
3
|
+
const paint = makePainter(colorEnabled);
|
|
4
|
+
const line = (s = '') => io.stdout(s + '\n');
|
|
5
|
+
return {
|
|
6
|
+
paint,
|
|
7
|
+
line,
|
|
8
|
+
raw: (s) => io.stdout(s),
|
|
9
|
+
kv(obj) {
|
|
10
|
+
const keys = Object.keys(obj);
|
|
11
|
+
const width = keys.reduce((m, k) => Math.max(m, k.length), 0);
|
|
12
|
+
for (const k of keys) {
|
|
13
|
+
const label = paint('gray', k.padEnd(width));
|
|
14
|
+
line(`${label} ${fmtValue(obj[k], paint)}`);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
table(rows, columns) {
|
|
18
|
+
if (rows.length === 0) {
|
|
19
|
+
line(paint('dim', '(no rows)'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const cols = columns ?? unionKeys(rows);
|
|
23
|
+
const widths = cols.map((c) => Math.max(c.length, ...rows.map((r) => plain(fmtValue(r[c], paint)).length)));
|
|
24
|
+
const header = cols
|
|
25
|
+
.map((c, i) => paint('bold', c.padEnd(widths[i] ?? c.length)))
|
|
26
|
+
.join(' ');
|
|
27
|
+
line(header);
|
|
28
|
+
line(paint('dim', cols.map((_, i) => '─'.repeat(widths[i] ?? 1)).join(' ')));
|
|
29
|
+
for (const r of rows) {
|
|
30
|
+
line(cols
|
|
31
|
+
.map((c, i) => {
|
|
32
|
+
const cell = fmtValue(r[c], paint);
|
|
33
|
+
return pad(cell, widths[i] ?? 0);
|
|
34
|
+
})
|
|
35
|
+
.join(' '));
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function unionKeys(rows) {
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
for (const r of rows)
|
|
43
|
+
for (const k of Object.keys(r))
|
|
44
|
+
seen.add(k);
|
|
45
|
+
return [...seen];
|
|
46
|
+
}
|
|
47
|
+
function fmtValue(v, paint) {
|
|
48
|
+
if (v === null || v === undefined)
|
|
49
|
+
return paint('dim', '—');
|
|
50
|
+
if (typeof v === 'boolean')
|
|
51
|
+
return v ? paint('green', 'yes') : paint('gray', 'no');
|
|
52
|
+
if (typeof v === 'object')
|
|
53
|
+
return JSON.stringify(v);
|
|
54
|
+
const s = String(v);
|
|
55
|
+
// Color known status words inline.
|
|
56
|
+
if (['green', 'yellow', 'red', 'ok', 'watch', 'alert', 'completed', 'failed', 'queued', 'processing'].includes(s.toLowerCase())) {
|
|
57
|
+
return paint(statusColor(s), s);
|
|
58
|
+
}
|
|
59
|
+
return s;
|
|
60
|
+
}
|
|
61
|
+
/** Strip ANSI codes to measure visible width. */
|
|
62
|
+
function plain(s) {
|
|
63
|
+
// eslint-disable-next-line no-control-regex
|
|
64
|
+
return s.replace(/\[[0-9;]*m/g, '');
|
|
65
|
+
}
|
|
66
|
+
/** Pad accounting for invisible ANSI sequences. */
|
|
67
|
+
function pad(s, width) {
|
|
68
|
+
const visible = plain(s).length;
|
|
69
|
+
return s + ' '.repeat(Math.max(0, width - visible));
|
|
70
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the machine-readable command/flag tree for `cs schema` and drive
|
|
3
|
+
* `--help` text — all from the same command table the router dispatches on.
|
|
4
|
+
*/
|
|
5
|
+
import type { Cmd } from '../types.js';
|
|
6
|
+
export interface SchemaOption {
|
|
7
|
+
flag: string;
|
|
8
|
+
type: 'string' | 'boolean';
|
|
9
|
+
short?: string;
|
|
10
|
+
desc: string;
|
|
11
|
+
default?: string | boolean;
|
|
12
|
+
choices?: readonly string[];
|
|
13
|
+
multiple?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SchemaCommand {
|
|
16
|
+
command: string;
|
|
17
|
+
path: string[];
|
|
18
|
+
summary: string;
|
|
19
|
+
args: {
|
|
20
|
+
name: string;
|
|
21
|
+
required: boolean;
|
|
22
|
+
variadic: boolean;
|
|
23
|
+
}[];
|
|
24
|
+
options: SchemaOption[];
|
|
25
|
+
}
|
|
26
|
+
export interface SchemaDoc {
|
|
27
|
+
name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
globalOptions: SchemaOption[];
|
|
30
|
+
commands: SchemaCommand[];
|
|
31
|
+
}
|
|
32
|
+
export declare function buildSchema(table: Cmd[], version: string): SchemaDoc;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GLOBAL_OPTIONS } from '../globals.js';
|
|
2
|
+
function optionToSchema(flag, spec) {
|
|
3
|
+
return {
|
|
4
|
+
flag,
|
|
5
|
+
type: spec.type,
|
|
6
|
+
...(spec.short ? { short: spec.short } : {}),
|
|
7
|
+
desc: spec.desc,
|
|
8
|
+
...(spec.default !== undefined ? { default: spec.default } : {}),
|
|
9
|
+
...(spec.choices ? { choices: spec.choices } : {}),
|
|
10
|
+
...(spec.multiple ? { multiple: true } : {}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function buildSchema(table, version) {
|
|
14
|
+
return {
|
|
15
|
+
name: 'cs',
|
|
16
|
+
version,
|
|
17
|
+
globalOptions: Object.entries(GLOBAL_OPTIONS).map(([f, s]) => optionToSchema(f, s)),
|
|
18
|
+
commands: table
|
|
19
|
+
.slice()
|
|
20
|
+
.sort((a, b) => a.path.join(' ').localeCompare(b.path.join(' ')))
|
|
21
|
+
.map((c) => ({
|
|
22
|
+
command: c.path.join(' '),
|
|
23
|
+
path: c.path,
|
|
24
|
+
summary: c.summary,
|
|
25
|
+
args: (c.args ?? []).map((a) => ({
|
|
26
|
+
name: a.name,
|
|
27
|
+
required: Boolean(a.required),
|
|
28
|
+
variadic: Boolean(a.variadic),
|
|
29
|
+
})),
|
|
30
|
+
options: Object.entries(c.options ?? {}).map(([f, s]) => optionToSchema(f, s)),
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/router.d.ts
ADDED