@assemora/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 +202 -0
- package/README.md +272 -0
- package/bin.mjs +31 -0
- package/dist/args.d.ts +35 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +104 -0
- package/dist/args.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/agents.d.ts +50 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +169 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/artifacts.d.ts +5 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +126 -0
- package/dist/commands/artifacts.js.map +1 -0
- package/dist/commands/console.d.ts +25 -0
- package/dist/commands/console.d.ts.map +1 -0
- package/dist/commands/console.js +99 -0
- package/dist/commands/console.js.map +1 -0
- package/dist/commands/db.d.ts +48 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +585 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/index.d.ts +21 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +21 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/inspect.d.ts +8 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +280 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/make.d.ts +29 -0
- package/dist/commands/make.d.ts.map +1 -0
- package/dist/commands/make.js +393 -0
- package/dist/commands/make.js.map +1 -0
- package/dist/commands/mcp.d.ts +19 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +116 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/new.d.ts +20 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +83 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/run.d.ts +20 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +355 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/watchdog.d.ts +2 -0
- package/dist/commands/watchdog.d.ts.map +1 -0
- package/dist/commands/watchdog.js +74 -0
- package/dist/commands/watchdog.js.map +1 -0
- package/dist/config.d.ts +89 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +156 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/output.d.ts +96 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +181 -0
- package/dist/output.js.map +1 -0
- package/dist/project.d.ts +44 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +102 -0
- package/dist/project.js.map +1 -0
- package/dist/registry.d.ts +48 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +74 -0
- package/dist/registry.js.map +1 -0
- package/package.json +49 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `assemora.config.ts` — how the CLI finds the project (SPEC.md §79, ADR-0021).
|
|
3
|
+
*
|
|
4
|
+
* The config is the whole of the CLI's knowledge of an application. It hands back an
|
|
5
|
+
* application the CLI boots itself, and every path it declares is relative to the
|
|
6
|
+
* config file rather than to whatever directory the command was typed in, so
|
|
7
|
+
* `assemora db:migrate` means the same thing from `src/` as it does from the root.
|
|
8
|
+
*
|
|
9
|
+
* Node 24 strips TypeScript types natively, so a `.ts` config is imported directly:
|
|
10
|
+
* no transpiler, no dependency, and the file the developer edits is the file that
|
|
11
|
+
* runs.
|
|
12
|
+
*/
|
|
13
|
+
import { stat } from 'node:fs/promises';
|
|
14
|
+
import { dirname, join, resolve as resolvePath } from 'node:path';
|
|
15
|
+
import { pathToFileURL } from 'node:url';
|
|
16
|
+
import { AssemoraError, ConfigurationError } from '@assemora/core';
|
|
17
|
+
/**
|
|
18
|
+
* Identity, plus types.
|
|
19
|
+
*
|
|
20
|
+
* It exists so that the object is checked where it is written, which is the only
|
|
21
|
+
* place a mistake in it is cheap to fix.
|
|
22
|
+
*/
|
|
23
|
+
export const defineConfig = (config) => config;
|
|
24
|
+
/** In the order they are looked for, in each directory, on the way up. */
|
|
25
|
+
export const CONFIG_FILENAMES = ['assemora.config.ts', 'assemora.config.js'];
|
|
26
|
+
export const DEFAULT_PATHS = {
|
|
27
|
+
source: 'src',
|
|
28
|
+
migrations: 'database/migrations',
|
|
29
|
+
generated: '.assemora/generated',
|
|
30
|
+
};
|
|
31
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
32
|
+
const isFile = async (path) => {
|
|
33
|
+
try {
|
|
34
|
+
return (await stat(path)).isFile();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const findConfig = async (from) => {
|
|
41
|
+
let directory = from;
|
|
42
|
+
for (;;) {
|
|
43
|
+
for (const name of CONFIG_FILENAMES) {
|
|
44
|
+
const candidate = join(directory, name);
|
|
45
|
+
if (await isFile(candidate))
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
const parent = dirname(directory);
|
|
49
|
+
if (parent === directory)
|
|
50
|
+
return undefined;
|
|
51
|
+
directory = parent;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/** Every complaint names the file and the field, because that is where the fix goes. */
|
|
55
|
+
const invalid = (file, field, expected) => new ConfigurationError(`${file}: "${field}" ${expected}`);
|
|
56
|
+
const optionalString = (value, file, field) => {
|
|
57
|
+
if (value === undefined)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
60
|
+
throw invalid(file, field, 'must be a non-empty path');
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
};
|
|
64
|
+
const optionalRecord = (value, file, field) => {
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (!isRecord(value))
|
|
68
|
+
throw invalid(file, field, 'must be an object');
|
|
69
|
+
return value;
|
|
70
|
+
};
|
|
71
|
+
const requiredString = (value, file, field) => {
|
|
72
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
73
|
+
throw invalid(file, field, 'must be a non-empty string');
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Checks the shape of the default export.
|
|
78
|
+
*
|
|
79
|
+
* A config is ordinary TypeScript that ran a moment ago, so nothing here can be
|
|
80
|
+
* trusted to be what it claims. What survives this is cast once, deliberately, and
|
|
81
|
+
* that cast is the only place the declared type meets the imported value.
|
|
82
|
+
*/
|
|
83
|
+
const validate = (imported, file) => {
|
|
84
|
+
if (!isRecord(imported) || imported.default === undefined) {
|
|
85
|
+
throw new ConfigurationError(`${file} has no default export. It must \`export default defineConfig({ app: … })\`.`);
|
|
86
|
+
}
|
|
87
|
+
const declared = imported.default;
|
|
88
|
+
if (!isRecord(declared)) {
|
|
89
|
+
throw new ConfigurationError(`${file}: the default export must be the object defineConfig() returns`);
|
|
90
|
+
}
|
|
91
|
+
if (typeof declared.app !== 'function') {
|
|
92
|
+
throw invalid(file, 'app', "must be a function returning the application, for example: app: () => import('./src/app.ts').then((module) => module.createApp())");
|
|
93
|
+
}
|
|
94
|
+
optionalString(declared.server, file, 'server');
|
|
95
|
+
const paths = optionalRecord(declared.paths, file, 'paths');
|
|
96
|
+
if (paths !== undefined) {
|
|
97
|
+
optionalString(paths.source, file, 'paths.source');
|
|
98
|
+
optionalString(paths.migrations, file, 'paths.migrations');
|
|
99
|
+
optionalString(paths.generated, file, 'paths.generated');
|
|
100
|
+
}
|
|
101
|
+
const openapi = optionalRecord(declared.openapi, file, 'openapi');
|
|
102
|
+
if (openapi !== undefined) {
|
|
103
|
+
optionalString(openapi.out, file, 'openapi.out');
|
|
104
|
+
const info = optionalRecord(openapi.info, file, 'openapi.info');
|
|
105
|
+
if (info !== undefined) {
|
|
106
|
+
requiredString(info.title, file, 'openapi.info.title');
|
|
107
|
+
requiredString(info.version, file, 'openapi.info.version');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const sdk = optionalRecord(declared.sdk, file, 'sdk');
|
|
111
|
+
if (sdk !== undefined) {
|
|
112
|
+
optionalString(sdk.out, file, 'sdk.out');
|
|
113
|
+
optionalString(sdk.clientModule, file, 'sdk.clientModule');
|
|
114
|
+
}
|
|
115
|
+
return declared;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Finds the project's config, walking up from `cwd`, and validates it.
|
|
119
|
+
*
|
|
120
|
+
* Walking up is what lets a command be typed from anywhere inside the project, which
|
|
121
|
+
* is how every other tool a developer already has behaves.
|
|
122
|
+
*/
|
|
123
|
+
export const loadConfig = async (cwd) => {
|
|
124
|
+
const from = resolvePath(cwd);
|
|
125
|
+
const file = await findConfig(from);
|
|
126
|
+
if (file === undefined) {
|
|
127
|
+
throw new ConfigurationError(`No ${CONFIG_FILENAMES.join(' or ')} in ${from} or any directory above it. ` +
|
|
128
|
+
'Run the command inside an Assemora project, or create one with `assemora new`.');
|
|
129
|
+
}
|
|
130
|
+
let imported;
|
|
131
|
+
try {
|
|
132
|
+
imported = await import(pathToFileURL(file).href);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
// The cause carries the real stack — a syntax error points at their line, not
|
|
136
|
+
// ours — and `run --debug` walks it.
|
|
137
|
+
throw new AssemoraError('CONFIGURATION_ERROR', `${file} could not be imported: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
138
|
+
}
|
|
139
|
+
const config = validate(imported, file);
|
|
140
|
+
const root = dirname(file);
|
|
141
|
+
const resolve = (path) => resolvePath(root, path);
|
|
142
|
+
const declared = config.paths ?? {};
|
|
143
|
+
return {
|
|
144
|
+
config,
|
|
145
|
+
file,
|
|
146
|
+
root,
|
|
147
|
+
paths: {
|
|
148
|
+
source: resolve(declared.source ?? DEFAULT_PATHS.source),
|
|
149
|
+
migrations: resolve(declared.migrations ?? DEFAULT_PATHS.migrations),
|
|
150
|
+
generated: resolve(declared.generated ?? DEFAULT_PATHS.generated),
|
|
151
|
+
},
|
|
152
|
+
server: config.server === undefined ? undefined : resolve(config.server),
|
|
153
|
+
resolve,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import './commands/index.js';
|
|
2
|
+
import { type CliCommand } from './registry.js';
|
|
3
|
+
export { type AssemoraConfig, type AssemoraPaths, defineConfig, type LoadedConfig, type ResolvedPaths, } from './config.js';
|
|
4
|
+
export type RunOptions = {
|
|
5
|
+
/** Where the command runs. Defaults to `process.cwd()`. */
|
|
6
|
+
readonly cwd?: string;
|
|
7
|
+
/** What `--version` prints. Defaults to the version of this package. */
|
|
8
|
+
readonly version?: string;
|
|
9
|
+
/** The command table. Defaults to everything the groups registered. */
|
|
10
|
+
readonly commands?: readonly CliCommand[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Resolves one invocation and returns its exit code.
|
|
14
|
+
*
|
|
15
|
+
* It never calls `process.exit`: a test drives it in-process, and a command that has
|
|
16
|
+
* written to a pipe needs the process to flush before it leaves.
|
|
17
|
+
*/
|
|
18
|
+
export declare const run: (argv: readonly string[], options?: RunOptions) => Promise<number>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,qBAAqB,CAAA;AAG5B,OAAO,EAAE,KAAK,UAAU,EAAiD,MAAM,eAAe,CAAA;AAE9F,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AAEpB,MAAM,MAAM,UAAU,GAAG;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CAC1C,CAAA;AAkFD;;;;;GAKG;AACH,eAAO,MAAM,GAAG,SAAgB,SAAS,MAAM,EAAE,YAAW,UAAU,KAAQ,OAAO,CAAC,MAAM,CAyC3F,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@assemora/cli` — the front door (SPEC.md §77, ADR-0021).
|
|
3
|
+
*
|
|
4
|
+
* The CLI is one more client of the application layer. It never imports a feature
|
|
5
|
+
* package and never reaches a database on its own: it loads the project's
|
|
6
|
+
* `assemora.config.ts`, boots the application that config hands back, and asks it
|
|
7
|
+
* questions through the Schema Registry and the Query Bus — the same two doors
|
|
8
|
+
* Studio and an agent use.
|
|
9
|
+
*
|
|
10
|
+
* `run()` returns the exit code instead of taking the process down with it, so the
|
|
11
|
+
* whole CLI is drivable from a test. `bin.ts` is what exits.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { bool, parseArgs } from './args.js';
|
|
15
|
+
import './commands/index.js';
|
|
16
|
+
import { detail, fail, line } from './output.js';
|
|
17
|
+
import { shutdown } from './project.js';
|
|
18
|
+
import { commandHelpText, helpText, registeredCommands } from './registry.js';
|
|
19
|
+
export { defineConfig, } from './config.js';
|
|
20
|
+
const UNKNOWN_VERSION = '0.0.0-unknown';
|
|
21
|
+
/**
|
|
22
|
+
* Read rather than compiled in, so that a published build reports its own version
|
|
23
|
+
* and a checkout reports the one in the repository.
|
|
24
|
+
*/
|
|
25
|
+
const declaredVersion = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const manifest = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
|
|
28
|
+
const version = typeof manifest === 'object' && manifest !== null
|
|
29
|
+
? manifest.version
|
|
30
|
+
: undefined;
|
|
31
|
+
return typeof version === 'string' ? version : UNKNOWN_VERSION;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return UNKNOWN_VERSION;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const messageOf = (error) => error instanceof Error ? error.message : String(error);
|
|
38
|
+
/** The console methods that write to stdout, which is the stream the answer owns. */
|
|
39
|
+
const LOGGING = ['log', 'info', 'debug'];
|
|
40
|
+
/**
|
|
41
|
+
* Keeps stdout for the answer, for as long as this invocation lasts.
|
|
42
|
+
*
|
|
43
|
+
* `@assemora/core`'s logger writes info records with `console.log`, and every command
|
|
44
|
+
* that inspects an application boots one — so `assemora routes --json | jq` was handed
|
|
45
|
+
* a boot record before the JSON, and `assemora api:openapi --stdout > openapi.json`
|
|
46
|
+
* wrote a file that was not a document. The CLI cannot reach into the logger: the
|
|
47
|
+
* project's config builds the application, and `logger` is its option to pass. What
|
|
48
|
+
* the CLI does own is what stdout means for one invocation, and this is that: anything
|
|
49
|
+
* that is not the answer goes to stderr, which is the rule the whole of `output.ts` is
|
|
50
|
+
* written to.
|
|
51
|
+
*
|
|
52
|
+
* The REPL is unaffected — it writes to its own stream — though a `console.log` typed
|
|
53
|
+
* inside it lands on stderr, where a terminal shows it just the same.
|
|
54
|
+
*
|
|
55
|
+
* It is put back afterwards because `run()` is called in process, by `assemora new`
|
|
56
|
+
* running two commands and by every test in this package.
|
|
57
|
+
*/
|
|
58
|
+
const keepStdoutForTheAnswer = () => {
|
|
59
|
+
const original = LOGGING.map((name) => [name, console[name]]);
|
|
60
|
+
for (const [name] of original) {
|
|
61
|
+
console[name] = (...values) => {
|
|
62
|
+
console.error(...values);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return () => {
|
|
66
|
+
for (const [name, method] of original)
|
|
67
|
+
console[name] = method;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** The stack and every cause below it — what `--debug` is for. */
|
|
71
|
+
const reportStack = (error) => {
|
|
72
|
+
let current = error;
|
|
73
|
+
while (current instanceof Error) {
|
|
74
|
+
detail(current.stack ?? `${current.name}: ${current.message}`);
|
|
75
|
+
current = current.cause;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const close = async (debug) => {
|
|
79
|
+
try {
|
|
80
|
+
await shutdown();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
// Failing to close is not what the command was asked to do, so it must not
|
|
84
|
+
// change the exit code. It is still worth saying when somebody is looking.
|
|
85
|
+
if (debug)
|
|
86
|
+
detail(`The application did not shut down cleanly: ${messageOf(error)}`);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Resolves one invocation and returns its exit code.
|
|
91
|
+
*
|
|
92
|
+
* It never calls `process.exit`: a test drives it in-process, and a command that has
|
|
93
|
+
* written to a pipe needs the process to flush before it leaves.
|
|
94
|
+
*/
|
|
95
|
+
export const run = async (argv, options = {}) => {
|
|
96
|
+
const args = parseArgs(argv);
|
|
97
|
+
const commands = options.commands ?? registeredCommands();
|
|
98
|
+
const debug = bool(args, 'debug');
|
|
99
|
+
const restoreConsole = keepStdoutForTheAnswer();
|
|
100
|
+
try {
|
|
101
|
+
if (bool(args, 'version')) {
|
|
102
|
+
line(options.version ?? (await declaredVersion()));
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
// No command at all is a question, not a mistake: the answer is the list.
|
|
106
|
+
if (args.command === undefined) {
|
|
107
|
+
line(helpText(commands));
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
const command = commands.find((entry) => entry.name === args.command);
|
|
111
|
+
if (command === undefined) {
|
|
112
|
+
fail(`Unknown command "${args.command}". Run \`assemora --help\` to see what there is.`);
|
|
113
|
+
return 2;
|
|
114
|
+
}
|
|
115
|
+
if (bool(args, 'help') || bool(args, 'h')) {
|
|
116
|
+
line(commandHelpText(command));
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
return await command.handler({ args, cwd: options.cwd ?? process.cwd() });
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
fail(messageOf(error));
|
|
123
|
+
if (debug)
|
|
124
|
+
reportStack(error);
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
// Shutting down is still the application talking, so the redirect outlives it.
|
|
129
|
+
await close(debug);
|
|
130
|
+
restoreConsole();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/output.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing to a terminal (SPEC.md §77).
|
|
3
|
+
*
|
|
4
|
+
* Two rules run through everything here. Anything that is not the answer — a
|
|
5
|
+
* warning, an error, a stack — goes to stderr, so `assemora api:openapi --stdout`
|
|
6
|
+
* and `assemora routes --json` stay pipeable. And terminal formatting is for a
|
|
7
|
+
* terminal: colour needs a TTY with `NO_COLOR` unset, and column alignment needs a
|
|
8
|
+
* TTY at all, because a pipe wants the data rather than the padding.
|
|
9
|
+
*
|
|
10
|
+
* The write target is a module-level seam rather than a parameter every command
|
|
11
|
+
* would have to thread. `captureOutput()` is what a test installs in its place.
|
|
12
|
+
*/
|
|
13
|
+
/** What this module writes to. `process.stdout` and `process.stderr` satisfy it. */
|
|
14
|
+
export type OutputStream = {
|
|
15
|
+
write(text: string): unknown;
|
|
16
|
+
readonly isTTY?: boolean;
|
|
17
|
+
readonly columns?: number;
|
|
18
|
+
};
|
|
19
|
+
export type OutputTarget = {
|
|
20
|
+
readonly stdout: OutputStream;
|
|
21
|
+
readonly stderr: OutputStream;
|
|
22
|
+
};
|
|
23
|
+
/** Sends output somewhere else. Tests use `captureOutput()`; this is the raw seam. */
|
|
24
|
+
export declare const useOutput: (next: OutputTarget) => void;
|
|
25
|
+
export declare const resetOutput: () => void;
|
|
26
|
+
export type CapturedOutput = {
|
|
27
|
+
/** Everything written to stdout so far. */
|
|
28
|
+
readonly stdout: string;
|
|
29
|
+
/** Everything written to stderr so far. */
|
|
30
|
+
readonly stderr: string;
|
|
31
|
+
restore(): void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Records output instead of printing it.
|
|
35
|
+
*
|
|
36
|
+
* `tty` decides whether the captured run believes it is talking to a terminal, so a
|
|
37
|
+
* test can assert both the aligned form and the piped one; it defaults to `false`,
|
|
38
|
+
* which is the form assertions are written against.
|
|
39
|
+
*/
|
|
40
|
+
export declare const captureOutput: (options?: {
|
|
41
|
+
readonly tty?: boolean;
|
|
42
|
+
readonly width?: number;
|
|
43
|
+
}) => CapturedOutput;
|
|
44
|
+
declare const CODES: {
|
|
45
|
+
readonly bold: '\u001B[1m';
|
|
46
|
+
readonly dim: '\u001B[2m';
|
|
47
|
+
readonly red: '\u001B[31m';
|
|
48
|
+
readonly green: '\u001B[32m';
|
|
49
|
+
readonly yellow: '\u001B[33m';
|
|
50
|
+
readonly cyan: '\u001B[36m';
|
|
51
|
+
};
|
|
52
|
+
export type StyleName = keyof typeof CODES;
|
|
53
|
+
/**
|
|
54
|
+
* Colour for text a command writes to stdout itself.
|
|
55
|
+
*
|
|
56
|
+
* Each call asks at the moment it is made, so installing a capture target before it
|
|
57
|
+
* runs is enough to turn colour off — nothing is decided at import time.
|
|
58
|
+
*/
|
|
59
|
+
export declare const style: Readonly<Record<StyleName, (text: string) => string>>;
|
|
60
|
+
/** One line to stdout. This is the answer; everything else is commentary. */
|
|
61
|
+
export declare const line: (text?: string) => void;
|
|
62
|
+
/** A line that reports success. */
|
|
63
|
+
export declare const ok: (text: string) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Something the command did anyway, and the reader has to know about — a
|
|
66
|
+
* destructive column drop, a skipped file (SPEC.md §34).
|
|
67
|
+
*
|
|
68
|
+
* The `warning:` word is not decoration: it is the only part of the classification
|
|
69
|
+
* that survives colour being unavailable.
|
|
70
|
+
*/
|
|
71
|
+
export declare const warn: (text: string) => void;
|
|
72
|
+
/** Something the command did not do. */
|
|
73
|
+
export declare const fail: (text: string) => void;
|
|
74
|
+
/** Secondary context on stderr — a stack under `--debug`, a hint under an error. */
|
|
75
|
+
export declare const detail: (text: string) => void;
|
|
76
|
+
/** The `--json` half of every listing. Two-space indent, because a human reads it too. */
|
|
77
|
+
export declare const json: (value: unknown) => void;
|
|
78
|
+
export type Column<Row> = {
|
|
79
|
+
readonly header: string;
|
|
80
|
+
readonly value: (row: Row) => string;
|
|
81
|
+
readonly align?: 'left' | 'right';
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* A listing, as a grid.
|
|
85
|
+
*
|
|
86
|
+
* A long cell is truncated rather than wrapped. A wrapped cell turns a forty-row
|
|
87
|
+
* listing into a paragraph and destroys the one thing a grid is for — reading down a
|
|
88
|
+
* column — and nothing is lost by it, because every listing also answers `--json`,
|
|
89
|
+
* which is what the next command in the pipe wanted anyway (ADR-0021).
|
|
90
|
+
*
|
|
91
|
+
* No rows means no output. An empty grid says less than the sentence the command can
|
|
92
|
+
* write instead, so the command writes it.
|
|
93
|
+
*/
|
|
94
|
+
export declare const table: <Row>(rows: readonly Row[], columns: readonly Column<Row>[]) => void;
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oFAAoF;AACpF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC9B,CAAA;AAMD,sFAAsF;AACtF,eAAO,MAAM,SAAS,SAAU,YAAY,KAAG,IAE9C,CAAA;AAED,eAAO,MAAM,WAAW,QAAO,IAE9B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,2CAA2C;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,aACf;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3D,cAwBF,CAAA;AAID,QAAA,MAAM,KAAK;aACT,IAAI,EAAE,WAAW;aACjB,GAAG,EAAE,WAAW;aAChB,GAAG,EAAE,YAAY;aACjB,KAAK,EAAE,YAAY;aACnB,MAAM,EAAE,YAAY;aACpB,IAAI,EAAE,YAAY;CACV,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,KAAK,CAAA;AAY1C;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,CAOvE,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,IAAI,qBAAgB,IAEhC,CAAA;AAED,mCAAmC;AACnC,eAAO,MAAM,EAAE,SAAU,MAAM,KAAG,IAEjC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,SAAU,MAAM,KAAG,IAEnC,CAAA;AAED,wCAAwC;AACxC,eAAO,MAAM,IAAI,SAAU,MAAM,KAAG,IAEnC,CAAA;AAED,oFAAoF;AACpF,eAAO,MAAM,MAAM,SAAU,MAAM,KAAG,IAErC,CAAA;AAED,0FAA0F;AAC1F,eAAO,MAAM,IAAI,UAAW,OAAO,KAAG,IAErC,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAA;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CAClC,CAAA;AA4CD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAAI,GAAG,QAAQ,SAAS,GAAG,EAAE,WAAW,SAAS,MAAM,CAAC,GAAG,CAAC,EAAE,KAAG,IAuClF,CAAA"}
|
package/dist/output.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing to a terminal (SPEC.md §77).
|
|
3
|
+
*
|
|
4
|
+
* Two rules run through everything here. Anything that is not the answer — a
|
|
5
|
+
* warning, an error, a stack — goes to stderr, so `assemora api:openapi --stdout`
|
|
6
|
+
* and `assemora routes --json` stay pipeable. And terminal formatting is for a
|
|
7
|
+
* terminal: colour needs a TTY with `NO_COLOR` unset, and column alignment needs a
|
|
8
|
+
* TTY at all, because a pipe wants the data rather than the padding.
|
|
9
|
+
*
|
|
10
|
+
* The write target is a module-level seam rather than a parameter every command
|
|
11
|
+
* would have to thread. `captureOutput()` is what a test installs in its place.
|
|
12
|
+
*/
|
|
13
|
+
const processTarget = () => ({ stdout: process.stdout, stderr: process.stderr });
|
|
14
|
+
let target = processTarget();
|
|
15
|
+
/** Sends output somewhere else. Tests use `captureOutput()`; this is the raw seam. */
|
|
16
|
+
export const useOutput = (next) => {
|
|
17
|
+
target = next;
|
|
18
|
+
};
|
|
19
|
+
export const resetOutput = () => {
|
|
20
|
+
target = processTarget();
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Records output instead of printing it.
|
|
24
|
+
*
|
|
25
|
+
* `tty` decides whether the captured run believes it is talking to a terminal, so a
|
|
26
|
+
* test can assert both the aligned form and the piped one; it defaults to `false`,
|
|
27
|
+
* which is the form assertions are written against.
|
|
28
|
+
*/
|
|
29
|
+
export const captureOutput = (options = {}) => {
|
|
30
|
+
const previous = target;
|
|
31
|
+
const out = [];
|
|
32
|
+
const err = [];
|
|
33
|
+
const stream = (sink) => ({
|
|
34
|
+
write: (text) => sink.push(text),
|
|
35
|
+
isTTY: options.tty ?? false,
|
|
36
|
+
columns: options.width ?? 80,
|
|
37
|
+
});
|
|
38
|
+
target = { stdout: stream(out), stderr: stream(err) };
|
|
39
|
+
return {
|
|
40
|
+
get stdout() {
|
|
41
|
+
return out.join('');
|
|
42
|
+
},
|
|
43
|
+
get stderr() {
|
|
44
|
+
return err.join('');
|
|
45
|
+
},
|
|
46
|
+
restore: () => {
|
|
47
|
+
target = previous;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const RESET = '\u001b[0m';
|
|
52
|
+
const CODES = {
|
|
53
|
+
bold: '\u001b[1m',
|
|
54
|
+
dim: '\u001b[2m',
|
|
55
|
+
red: '\u001b[31m',
|
|
56
|
+
green: '\u001b[32m',
|
|
57
|
+
yellow: '\u001b[33m',
|
|
58
|
+
cyan: '\u001b[36m',
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* `NO_COLOR` set to anything non-empty turns colour off everywhere, which is the
|
|
62
|
+
* whole of https://no-color.org — the value is never inspected.
|
|
63
|
+
*/
|
|
64
|
+
const colourAllowed = (stream) => stream.isTTY === true && (process.env.NO_COLOR ?? '') === '';
|
|
65
|
+
const paint = (stream, name, text) => colourAllowed(stream) ? `${CODES[name]}${text}${RESET}` : text;
|
|
66
|
+
/**
|
|
67
|
+
* Colour for text a command writes to stdout itself.
|
|
68
|
+
*
|
|
69
|
+
* Each call asks at the moment it is made, so installing a capture target before it
|
|
70
|
+
* runs is enough to turn colour off — nothing is decided at import time.
|
|
71
|
+
*/
|
|
72
|
+
export const style = {
|
|
73
|
+
bold: (text) => paint(target.stdout, 'bold', text),
|
|
74
|
+
dim: (text) => paint(target.stdout, 'dim', text),
|
|
75
|
+
red: (text) => paint(target.stdout, 'red', text),
|
|
76
|
+
green: (text) => paint(target.stdout, 'green', text),
|
|
77
|
+
yellow: (text) => paint(target.stdout, 'yellow', text),
|
|
78
|
+
cyan: (text) => paint(target.stdout, 'cyan', text),
|
|
79
|
+
};
|
|
80
|
+
/** One line to stdout. This is the answer; everything else is commentary. */
|
|
81
|
+
export const line = (text = '') => {
|
|
82
|
+
target.stdout.write(`${text}\n`);
|
|
83
|
+
};
|
|
84
|
+
/** A line that reports success. */
|
|
85
|
+
export const ok = (text) => {
|
|
86
|
+
target.stdout.write(`${paint(target.stdout, 'green', text)}\n`);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Something the command did anyway, and the reader has to know about — a
|
|
90
|
+
* destructive column drop, a skipped file (SPEC.md §34).
|
|
91
|
+
*
|
|
92
|
+
* The `warning:` word is not decoration: it is the only part of the classification
|
|
93
|
+
* that survives colour being unavailable.
|
|
94
|
+
*/
|
|
95
|
+
export const warn = (text) => {
|
|
96
|
+
target.stderr.write(`${paint(target.stderr, 'yellow', `warning: ${text}`)}\n`);
|
|
97
|
+
};
|
|
98
|
+
/** Something the command did not do. */
|
|
99
|
+
export const fail = (text) => {
|
|
100
|
+
target.stderr.write(`${paint(target.stderr, 'red', `error: ${text}`)}\n`);
|
|
101
|
+
};
|
|
102
|
+
/** Secondary context on stderr — a stack under `--debug`, a hint under an error. */
|
|
103
|
+
export const detail = (text) => {
|
|
104
|
+
target.stderr.write(`${paint(target.stderr, 'dim', text)}\n`);
|
|
105
|
+
};
|
|
106
|
+
/** The `--json` half of every listing. Two-space indent, because a human reads it too. */
|
|
107
|
+
export const json = (value) => {
|
|
108
|
+
target.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
109
|
+
};
|
|
110
|
+
const GAP = ' ';
|
|
111
|
+
/** Below this a truncated column shows more ellipsis than content. */
|
|
112
|
+
const MIN_WIDTH = 6;
|
|
113
|
+
const cellText = (text) => text.replace(/[\r\n\t]+/g, ' ');
|
|
114
|
+
const truncate = (text, width) => text.length <= width ? text : `${text.slice(0, Math.max(width - 1, 0))}…`;
|
|
115
|
+
const pad = (text, width, align) => align === 'right' ? text.padStart(width) : text.padEnd(width);
|
|
116
|
+
/**
|
|
117
|
+
* Shrinks the widest column, one character at a time, until the row fits.
|
|
118
|
+
*
|
|
119
|
+
* Taking it off the widest column is what keeps a listing readable: an over-long
|
|
120
|
+
* description loses characters while the name and the method beside it keep all of
|
|
121
|
+
* theirs, which is the opposite of what shrinking everything proportionally does.
|
|
122
|
+
*/
|
|
123
|
+
const fit = (widths, available) => {
|
|
124
|
+
const fitted = [...widths];
|
|
125
|
+
const gaps = GAP.length * Math.max(fitted.length - 1, 0);
|
|
126
|
+
let total = fitted.reduce((sum, width) => sum + width, 0) + gaps;
|
|
127
|
+
while (total > available) {
|
|
128
|
+
let widest = 0;
|
|
129
|
+
for (let index = 1; index < fitted.length; index += 1) {
|
|
130
|
+
if ((fitted[index] ?? 0) > (fitted[widest] ?? 0))
|
|
131
|
+
widest = index;
|
|
132
|
+
}
|
|
133
|
+
if ((fitted[widest] ?? 0) <= MIN_WIDTH)
|
|
134
|
+
break;
|
|
135
|
+
fitted[widest] = (fitted[widest] ?? 0) - 1;
|
|
136
|
+
total -= 1;
|
|
137
|
+
}
|
|
138
|
+
return fitted;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* A listing, as a grid.
|
|
142
|
+
*
|
|
143
|
+
* A long cell is truncated rather than wrapped. A wrapped cell turns a forty-row
|
|
144
|
+
* listing into a paragraph and destroys the one thing a grid is for — reading down a
|
|
145
|
+
* column — and nothing is lost by it, because every listing also answers `--json`,
|
|
146
|
+
* which is what the next command in the pipe wanted anyway (ADR-0021).
|
|
147
|
+
*
|
|
148
|
+
* No rows means no output. An empty grid says less than the sentence the command can
|
|
149
|
+
* write instead, so the command writes it.
|
|
150
|
+
*/
|
|
151
|
+
export const table = (rows, columns) => {
|
|
152
|
+
if (rows.length === 0 || columns.length === 0)
|
|
153
|
+
return;
|
|
154
|
+
const header = columns.map((column) => cellText(column.header));
|
|
155
|
+
const body = rows.map((row) => columns.map((column) => cellText(column.value(row))));
|
|
156
|
+
const aligned = target.stdout.isTTY === true;
|
|
157
|
+
if (!aligned) {
|
|
158
|
+
// Piped: the separator is all the structure a reader downstream can use, and
|
|
159
|
+
// padding a column to a width nobody can see only adds trailing whitespace.
|
|
160
|
+
target.stdout.write(`${[header, ...body].map((cells) => cells.join(GAP)).join('\n')}\n`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const natural = columns.map((_, index) => Math.max(header[index]?.length ?? 0, ...body.map((cells) => cells[index]?.length ?? 0)));
|
|
164
|
+
const available = typeof target.stdout.columns === 'number' ? target.stdout.columns : 80;
|
|
165
|
+
const widths = fit(natural, available);
|
|
166
|
+
const render = (cells) => cells
|
|
167
|
+
.map((cell, index) => {
|
|
168
|
+
const width = widths[index] ?? cell.length;
|
|
169
|
+
const shown = truncate(cell, width);
|
|
170
|
+
// The last column is never padded: trailing spaces are invisible until
|
|
171
|
+
// somebody copies the line out of the terminal.
|
|
172
|
+
return index === cells.length - 1 && (columns[index]?.align ?? 'left') === 'left'
|
|
173
|
+
? shown
|
|
174
|
+
: pad(shown, width, columns[index]?.align ?? 'left');
|
|
175
|
+
})
|
|
176
|
+
.join(GAP);
|
|
177
|
+
target.stdout.write(`${paint(target.stdout, 'dim', render(header))}\n`);
|
|
178
|
+
for (const cells of body)
|
|
179
|
+
target.stdout.write(`${render(cells)}\n`);
|
|
180
|
+
};
|
|
181
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|