@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What node is given to run the server.
|
|
3
|
+
*
|
|
4
|
+
* Everything after `--` is node's, and node reads its own options before the script
|
|
5
|
+
* path: `assemora dev -- --inspect` runs `node --watch --inspect src/server.ts`.
|
|
6
|
+
* Arguments meant for the server itself have a better home — a server already reads
|
|
7
|
+
* its port and its database URL from the environment, and the environment is
|
|
8
|
+
* inherited whole.
|
|
9
|
+
*
|
|
10
|
+
* The watchdog is preloaded with the pid of the process supervising the server, which
|
|
11
|
+
* is what it watches. `--watch` hands node's own flags on to the process it restarts,
|
|
12
|
+
* so the server has it under `dev` too, one process below.
|
|
13
|
+
*/
|
|
14
|
+
export declare const serverArgv: (options: {
|
|
15
|
+
readonly watch: boolean;
|
|
16
|
+
readonly passthrough: readonly string[];
|
|
17
|
+
readonly entry: string;
|
|
18
|
+
readonly supervisor: number;
|
|
19
|
+
}) => string[];
|
|
20
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AA+KA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,YAAa;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B,KAAG,MAAM,EAMT,CAAA"}
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dev`, `start` and `build` — running the project (SPEC.md §77).
|
|
3
|
+
*
|
|
4
|
+
* The three commands are one idea: the CLI spawns a process and lives exactly as
|
|
5
|
+
* long as it does. Node 24 executes TypeScript directly, so `assemora dev` is
|
|
6
|
+
* `node --watch src/server.ts` and nothing more. There is no transpiler here and
|
|
7
|
+
* there must not be one — it would put a build step back between the file a
|
|
8
|
+
* developer edits and the file that runs, which is the step Node removed.
|
|
9
|
+
*
|
|
10
|
+
* `build` is the exception that proves it: the only thing this CLI compiles is the
|
|
11
|
+
* project's types, and it does that with the project's own TypeScript. A project that
|
|
12
|
+
* bundles something of its own declares a `build` script, and that script is the last
|
|
13
|
+
* of `build`'s three steps rather than a replacement for the other two.
|
|
14
|
+
*/
|
|
15
|
+
import { spawn } from 'node:child_process';
|
|
16
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
17
|
+
import { createRequire } from 'node:module';
|
|
18
|
+
import { constants } from 'node:os';
|
|
19
|
+
import { dirname, join } from 'node:path';
|
|
20
|
+
import { ConfigurationError } from '@assemora/core';
|
|
21
|
+
import { bool } from '../args.js';
|
|
22
|
+
import { loadConfig } from '../config.js';
|
|
23
|
+
import { fail, line, ok } from '../output.js';
|
|
24
|
+
import { commandNamed, defineCommand, register } from '../registry.js';
|
|
25
|
+
const isFile = async (path) => {
|
|
26
|
+
try {
|
|
27
|
+
return (await stat(path)).isFile();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
34
|
+
/** What the shell reports for a process a signal killed, and what Ctrl-C leaves behind. */
|
|
35
|
+
const codeForSignal = (signal) => 128 + (constants.signals[signal] ?? 0);
|
|
36
|
+
/**
|
|
37
|
+
* The signals a terminal sends when it wants what is running to stop.
|
|
38
|
+
*
|
|
39
|
+
* SIGHUP is here because the child runs in a process group of its own: closing the
|
|
40
|
+
* terminal signals its foreground group, which is now the CLI alone, so a hangup the
|
|
41
|
+
* CLI does not pass on is a `dev` server left running with no terminal to stop it from.
|
|
42
|
+
*/
|
|
43
|
+
const FORWARDED = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
|
44
|
+
/**
|
|
45
|
+
* Whether a signal can address a whole process group.
|
|
46
|
+
*
|
|
47
|
+
* POSIX has one; Windows has nothing equivalent — the shape of the answer there is
|
|
48
|
+
* `taskkill /T`, a second process spawned to end the first, which is not something to
|
|
49
|
+
* write untested. On Windows the child alone is signalled, exactly as before.
|
|
50
|
+
*/
|
|
51
|
+
const GROUPS = process.platform !== 'win32';
|
|
52
|
+
/**
|
|
53
|
+
* Stops the child, and everything the child started with it.
|
|
54
|
+
*
|
|
55
|
+
* A negative pid addresses the process group, which is the whole reason the child is
|
|
56
|
+
* spawned detached. `assemora dev` runs `node --watch`, and the watcher is a wrapper:
|
|
57
|
+
* the server holding the port is *its* child. Signalling the wrapper alone — and
|
|
58
|
+
* SIGKILL in particular, which it cannot pass on — left the server running with init
|
|
59
|
+
* for a parent and the port still held, which is exactly what the docstring below
|
|
60
|
+
* promises cannot happen.
|
|
61
|
+
*/
|
|
62
|
+
const stop = (child, signal) => {
|
|
63
|
+
const { pid } = child;
|
|
64
|
+
if (pid === undefined)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
if (GROUPS)
|
|
68
|
+
process.kill(-pid, signal);
|
|
69
|
+
else
|
|
70
|
+
child.kill(signal);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// ESRCH is the group having already gone, which is the outcome being asked for;
|
|
74
|
+
// anything else is a group that could not be signalled, and the child alone is
|
|
75
|
+
// better than nothing. Neither may throw: this runs inside a signal listener,
|
|
76
|
+
// where an exception ends the process with the child still running.
|
|
77
|
+
child.kill(signal);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Runs a child process to completion and answers with its exit code.
|
|
82
|
+
*
|
|
83
|
+
* Three things here are the whole point. Listening for the terminal's signals
|
|
84
|
+
* suppresses Node's default of dying on the spot, so Ctrl-C asks the child to stop and
|
|
85
|
+
* the CLI stays alive until it has — a `dev` server never outlives the command that
|
|
86
|
+
* started it, and never keeps the port. A second signal escalates to SIGKILL, because
|
|
87
|
+
* a child that ignores SIGTERM would otherwise make Ctrl-C look broken. And every
|
|
88
|
+
* signal goes to the child's process group rather than to the child, because what is
|
|
89
|
+
* spawned is often a wrapper — `node --watch`, a package manager — and the process
|
|
90
|
+
* that holds the port is one below it.
|
|
91
|
+
*
|
|
92
|
+
* `detached` is what creates that group. It also takes the child out of the terminal's
|
|
93
|
+
* foreground group, so the terminal's own Ctrl-C no longer reaches it directly and the
|
|
94
|
+
* forwarding below is the only path — which is why SIGHUP is forwarded too. And it
|
|
95
|
+
* means a CLI killed outright, with nothing forwarded, leaves the group untouched:
|
|
96
|
+
* `watchdog.ts` is preloaded into the server for exactly that case.
|
|
97
|
+
*/
|
|
98
|
+
const spawnProcess = (executable, argv, options) => new Promise((resolve, reject) => {
|
|
99
|
+
const child = spawn(executable, [...argv], {
|
|
100
|
+
cwd: options.cwd,
|
|
101
|
+
stdio: 'inherit',
|
|
102
|
+
detached: GROUPS,
|
|
103
|
+
});
|
|
104
|
+
let asked = false;
|
|
105
|
+
const forward = (signal) => {
|
|
106
|
+
stop(child, asked ? 'SIGKILL' : signal);
|
|
107
|
+
asked = true;
|
|
108
|
+
};
|
|
109
|
+
const listeners = FORWARDED.map((signal) => {
|
|
110
|
+
const listener = () => forward(signal);
|
|
111
|
+
process.on(signal, listener);
|
|
112
|
+
return [signal, listener];
|
|
113
|
+
});
|
|
114
|
+
// A CLI that ran two commands in one process would otherwise keep signalling a
|
|
115
|
+
// child that had already exited, and the listeners alone would stop Node leaving.
|
|
116
|
+
const release = () => {
|
|
117
|
+
for (const [signal, listener] of listeners)
|
|
118
|
+
process.off(signal, listener);
|
|
119
|
+
};
|
|
120
|
+
child.once('error', (error) => {
|
|
121
|
+
release();
|
|
122
|
+
reject(error);
|
|
123
|
+
});
|
|
124
|
+
child.once('exit', (code, signal) => {
|
|
125
|
+
release();
|
|
126
|
+
resolve(code ?? (signal === null ? 1 : codeForSignal(signal)));
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
const serverEntry = async (loaded) => {
|
|
130
|
+
if (loaded.server === undefined) {
|
|
131
|
+
throw new ConfigurationError(`${loaded.file} declares no "server", so there is nothing to run. ` +
|
|
132
|
+
"Add `server: 'src/server.ts'` — the file that starts the application.");
|
|
133
|
+
}
|
|
134
|
+
if (!(await isFile(loaded.server))) {
|
|
135
|
+
throw new ConfigurationError(`${loaded.file} names ${loaded.server} as the server to run, and there is no such file.`);
|
|
136
|
+
}
|
|
137
|
+
return loaded.server;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* The preload that takes the server down when the CLI dies with no chance to say so.
|
|
141
|
+
*
|
|
142
|
+
* Under Vitest this module runs from its source, and the file beside it is the source
|
|
143
|
+
* too; Node executes it as it executes the project's own `server.ts`.
|
|
144
|
+
*/
|
|
145
|
+
const WATCHDOG = new URL(import.meta.url.endsWith('.ts') ? './watchdog.ts' : './watchdog.js', import.meta.url);
|
|
146
|
+
/**
|
|
147
|
+
* What node is given to run the server.
|
|
148
|
+
*
|
|
149
|
+
* Everything after `--` is node's, and node reads its own options before the script
|
|
150
|
+
* path: `assemora dev -- --inspect` runs `node --watch --inspect src/server.ts`.
|
|
151
|
+
* Arguments meant for the server itself have a better home — a server already reads
|
|
152
|
+
* its port and its database URL from the environment, and the environment is
|
|
153
|
+
* inherited whole.
|
|
154
|
+
*
|
|
155
|
+
* The watchdog is preloaded with the pid of the process supervising the server, which
|
|
156
|
+
* is what it watches. `--watch` hands node's own flags on to the process it restarts,
|
|
157
|
+
* so the server has it under `dev` too, one process below.
|
|
158
|
+
*/
|
|
159
|
+
export const serverArgv = (options) => [
|
|
160
|
+
...(options.watch ? ['--watch'] : []),
|
|
161
|
+
'--import',
|
|
162
|
+
`${WATCHDOG.href}?parent=${options.supervisor}`,
|
|
163
|
+
...options.passthrough,
|
|
164
|
+
options.entry,
|
|
165
|
+
];
|
|
166
|
+
/**
|
|
167
|
+
* `dev` and `start`, which differ by one flag.
|
|
168
|
+
*
|
|
169
|
+
* The child is `process.execPath` rather than the word `node`, so the server runs
|
|
170
|
+
* under the same Node the CLI is running under, whatever PATH happens to say.
|
|
171
|
+
*/
|
|
172
|
+
const runServer = async (cwd, args, watch) => {
|
|
173
|
+
const loaded = await loadConfig(cwd);
|
|
174
|
+
const entry = await serverEntry(loaded);
|
|
175
|
+
return spawnProcess(process.execPath, serverArgv({ watch, passthrough: args.passthrough, entry, supervisor: process.pid }), { cwd: loaded.root });
|
|
176
|
+
};
|
|
177
|
+
const manifestAt = async (root) => {
|
|
178
|
+
try {
|
|
179
|
+
const parsed = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
180
|
+
return isRecord(parsed) ? parsed : {};
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// The only question being asked of the manifest is whether it declares a build
|
|
184
|
+
// script. A project that has no readable one has not declared anything.
|
|
185
|
+
return {};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const scriptNamed = (manifest, name) => {
|
|
189
|
+
const scripts = manifest.scripts;
|
|
190
|
+
if (!isRecord(scripts))
|
|
191
|
+
return undefined;
|
|
192
|
+
const script = scripts[name];
|
|
193
|
+
return typeof script === 'string' && script.trim() !== '' ? script : undefined;
|
|
194
|
+
};
|
|
195
|
+
const LOCKFILES = [
|
|
196
|
+
{ file: 'pnpm-lock.yaml', executable: 'pnpm' },
|
|
197
|
+
{ file: 'yarn.lock', executable: 'yarn' },
|
|
198
|
+
{ file: 'bun.lockb', executable: 'bun' },
|
|
199
|
+
{ file: 'package-lock.json', executable: 'npm' },
|
|
200
|
+
];
|
|
201
|
+
/**
|
|
202
|
+
* Which package manager runs the project's own `build` script.
|
|
203
|
+
*
|
|
204
|
+
* `packageManager` is asked first because it is the field a project uses to say so;
|
|
205
|
+
* the lockfile answers when it is absent, and npm is what is left when a project has
|
|
206
|
+
* neither. Getting this wrong is cheap and loud — the wrong manager fails to start
|
|
207
|
+
* — where guessing npm unconditionally would run a script against the wrong
|
|
208
|
+
* `node_modules`.
|
|
209
|
+
*/
|
|
210
|
+
const packageManagerFor = async (root, manifest) => {
|
|
211
|
+
const declared = manifest.packageManager;
|
|
212
|
+
if (typeof declared === 'string') {
|
|
213
|
+
const name = declared.split('@')[0];
|
|
214
|
+
if (name !== undefined && name !== '')
|
|
215
|
+
return name;
|
|
216
|
+
}
|
|
217
|
+
for (const { file, executable } of LOCKFILES) {
|
|
218
|
+
if (await isFile(join(root, file)))
|
|
219
|
+
return executable;
|
|
220
|
+
}
|
|
221
|
+
return 'npm';
|
|
222
|
+
};
|
|
223
|
+
/** On Windows a package manager is a `.cmd` shim, and spawn does not find it otherwise. */
|
|
224
|
+
const executableName = (name) => process.platform === 'win32' ? `${name}.cmd` : name;
|
|
225
|
+
/**
|
|
226
|
+
* The project's own TypeScript, run as a script by the Node already running.
|
|
227
|
+
*
|
|
228
|
+
* Resolving `typescript/package.json` from the project root is what finds it: every
|
|
229
|
+
* version of the package exports that file, where `typescript/bin/tsc` is not
|
|
230
|
+
* reachable through the package's own `exports`. Spawning it through
|
|
231
|
+
* `process.execPath` avoids depending on `node_modules/.bin` being populated, which
|
|
232
|
+
* a pnpm store layout or a hoisting setting is free to change.
|
|
233
|
+
*/
|
|
234
|
+
const compilerIn = (root) => {
|
|
235
|
+
try {
|
|
236
|
+
const resolve = createRequire(join(root, 'package.json'));
|
|
237
|
+
return join(dirname(resolve.resolve('typescript/package.json')), 'bin', 'tsc');
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const typecheck = async (root) => {
|
|
244
|
+
const tsconfig = join(root, 'tsconfig.json');
|
|
245
|
+
if (!(await isFile(tsconfig))) {
|
|
246
|
+
throw new ConfigurationError(`There is no tsconfig.json in ${root}, so the project cannot be typechecked. ` +
|
|
247
|
+
'Add one, or run `assemora build --no-typecheck`.');
|
|
248
|
+
}
|
|
249
|
+
const compiler = compilerIn(root);
|
|
250
|
+
if (compiler === undefined) {
|
|
251
|
+
throw new ConfigurationError(`TypeScript is not installed in ${root}, so there is nothing to typecheck with. ` +
|
|
252
|
+
'Install it as a dependency of the project, or run `assemora build --no-typecheck`.');
|
|
253
|
+
}
|
|
254
|
+
return spawnProcess(process.execPath, [compiler, '--project', tsconfig, '--noEmit'], {
|
|
255
|
+
cwd: root,
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
const NO_ARGUMENTS = {
|
|
259
|
+
command: undefined,
|
|
260
|
+
positionals: [],
|
|
261
|
+
flags: {},
|
|
262
|
+
passthrough: [],
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Regenerates one artifact by running the command that owns it.
|
|
266
|
+
*
|
|
267
|
+
* `build` generates nothing itself. `api:openapi` and `sdk:generate` already know
|
|
268
|
+
* how, and reaching them through the command table is what stops a build writing a
|
|
269
|
+
* document that differs from the one the command writes — the reason the table
|
|
270
|
+
* exists at all.
|
|
271
|
+
*/
|
|
272
|
+
const regenerate = async (name, cwd) => {
|
|
273
|
+
const command = commandNamed(name);
|
|
274
|
+
if (command === undefined) {
|
|
275
|
+
// Every group registers from `commands/index.ts`, so a missing one is a mistake
|
|
276
|
+
// in the CLI rather than anything the project can fix. It still has to stop the
|
|
277
|
+
// build: a green build that quietly generated nothing is how a stale document
|
|
278
|
+
// reaches a deployment.
|
|
279
|
+
throw new ConfigurationError(`The config declares an artifact for "${name}", and this CLI has no such command.`);
|
|
280
|
+
}
|
|
281
|
+
line(`Running ${name}`);
|
|
282
|
+
return command.handler({ args: NO_ARGUMENTS, cwd });
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Everything that must be current before the project is deployed.
|
|
286
|
+
*
|
|
287
|
+
* Three steps, in the only order that makes sense. Typecheck first, because generating
|
|
288
|
+
* an OpenAPI document from an application that does not compile is worse than
|
|
289
|
+
* generating nothing. Then regenerate exactly what the config declares. Then the
|
|
290
|
+
* project's own `build` script, if it declares one — last, because the generated SDK is
|
|
291
|
+
* an input to whatever bundles it, and a bundle built before it is a bundle built
|
|
292
|
+
* against the previous client.
|
|
293
|
+
*
|
|
294
|
+
* The project's script used to be an alternative to the other two rather than a step
|
|
295
|
+
* after them, and the scaffolded project declares one — so `assemora build` there did
|
|
296
|
+
* neither of the things this command exists to do, and `--no-typecheck` was a flag with
|
|
297
|
+
* nowhere to take effect.
|
|
298
|
+
*
|
|
299
|
+
* Any failed step answers `1`. A child's own code is not passed through, because `2`
|
|
300
|
+
* already means the invocation was wrong and a compiler's `2` does not mean that.
|
|
301
|
+
*/
|
|
302
|
+
const build = async ({ args, cwd }) => {
|
|
303
|
+
const loaded = await loadConfig(cwd);
|
|
304
|
+
const manifest = await manifestAt(loaded.root);
|
|
305
|
+
const own = scriptNamed(manifest, 'build');
|
|
306
|
+
if (!bool(args, 'no-typecheck')) {
|
|
307
|
+
line('Typechecking');
|
|
308
|
+
if ((await typecheck(loaded.root)) !== 0) {
|
|
309
|
+
fail('The typecheck failed, so nothing was regenerated and nothing was built.');
|
|
310
|
+
return 1;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const declared = [
|
|
314
|
+
...(loaded.config.openapi === undefined ? [] : ['api:openapi']),
|
|
315
|
+
...(loaded.config.sdk === undefined ? [] : ['sdk:generate']),
|
|
316
|
+
];
|
|
317
|
+
if (declared.length === 0)
|
|
318
|
+
line('The config declares no artifacts to regenerate.');
|
|
319
|
+
for (const name of declared) {
|
|
320
|
+
if ((await regenerate(name, loaded.root)) !== 0) {
|
|
321
|
+
fail(`${name} failed, so the build is not complete.`);
|
|
322
|
+
return 1;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (own !== undefined) {
|
|
326
|
+
const manager = await packageManagerFor(loaded.root, manifest);
|
|
327
|
+
line(`Running the project's own build script (${manager} run build): ${own}`);
|
|
328
|
+
if ((await spawnProcess(executableName(manager), ['run', 'build'], { cwd: loaded.root })) !== 0) {
|
|
329
|
+
fail("The project's own build script failed, so the build is not complete.");
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
ok('Build complete.');
|
|
334
|
+
return 0;
|
|
335
|
+
};
|
|
336
|
+
register(defineCommand({
|
|
337
|
+
name: 'dev',
|
|
338
|
+
group: 'run',
|
|
339
|
+
summary: 'run the server and restart it when a file changes',
|
|
340
|
+
usage: 'assemora dev [-- <node options>]',
|
|
341
|
+
handler: ({ args, cwd }) => runServer(cwd, args, true),
|
|
342
|
+
}), defineCommand({
|
|
343
|
+
name: 'build',
|
|
344
|
+
group: 'run',
|
|
345
|
+
summary: "typecheck, regenerate what the config declares, run the project's build",
|
|
346
|
+
usage: 'assemora build [--no-typecheck]',
|
|
347
|
+
handler: build,
|
|
348
|
+
}), defineCommand({
|
|
349
|
+
name: 'start',
|
|
350
|
+
group: 'run',
|
|
351
|
+
summary: 'run the server',
|
|
352
|
+
usage: 'assemora start [-- <node options>]',
|
|
353
|
+
handler: ({ args, cwd }) => runServer(cwd, args, false),
|
|
354
|
+
}));
|
|
355
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../../src/commands/watchdog.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What keeps a server from outliving the CLI that started it.
|
|
3
|
+
*
|
|
4
|
+
* `assemora dev` and `assemora start` preload this file into the server with
|
|
5
|
+
* `--import`, naming the CLI's pid in the URL. It polls that pid, and when it is gone
|
|
6
|
+
* it stops the process group it is in — the server, and under `--watch` the wrapper
|
|
7
|
+
* above it. A signal the CLI forwards is the ordinary way down; this is for the CLI
|
|
8
|
+
* dying with no chance to forward anything, which is a `kill -9`, a crash, or a tool
|
|
9
|
+
* that kills the process group of the shell it started. The child runs detached in a
|
|
10
|
+
* group of its own, so that last one reaches the CLI and nothing below it — and after
|
|
11
|
+
* an afternoon of it, nineteen servers were listening with init for a parent (#27).
|
|
12
|
+
*
|
|
13
|
+
* The pid comes from the URL rather than the environment because the environment is
|
|
14
|
+
* inherited whole by the server and by whatever the server starts, and a variable in
|
|
15
|
+
* it is a fact the CLI would be leaking into an application. The import specifier is
|
|
16
|
+
* the CLI's own, read by nobody else.
|
|
17
|
+
*
|
|
18
|
+
* It watches the CLI and not its parent, because under `--watch` its parent is Node's
|
|
19
|
+
* wrapper, which outlives the CLI: Node runs a preload in the script's process and not
|
|
20
|
+
* in the wrapper's, so the wrapper cannot watch anything for itself. It is instead
|
|
21
|
+
* taken down with the group — signalling pid 0 addresses the caller's own group, and
|
|
22
|
+
* the wrapper answers a SIGTERM by stopping its child and leaving.
|
|
23
|
+
*
|
|
24
|
+
* Nothing here may keep the event loop alive or throw: a server that has finished its
|
|
25
|
+
* work has to be able to exit, and a preload that fails takes the server with it.
|
|
26
|
+
*/
|
|
27
|
+
const EVERY = 500;
|
|
28
|
+
const GRACE = 5_000;
|
|
29
|
+
const supervisor = Number(new URL(import.meta.url).searchParams.get('parent'));
|
|
30
|
+
/**
|
|
31
|
+
* Whether a pid names nothing any more.
|
|
32
|
+
*
|
|
33
|
+
* `ESRCH` is that. `EPERM` is a live process belonging to somebody else, which after a
|
|
34
|
+
* pid is reused is "not the CLI" too — but it cannot be told apart from the CLI
|
|
35
|
+
* running under another user, so it counts as alive and the server stays.
|
|
36
|
+
*/
|
|
37
|
+
const gone = (pid) => {
|
|
38
|
+
try {
|
|
39
|
+
process.kill(pid, 0);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
return error.code === 'ESRCH';
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Signals this process and everything grouped with it.
|
|
48
|
+
*
|
|
49
|
+
* Windows has no process groups; there the process signals itself, and a `--watch`
|
|
50
|
+
* wrapper stays waiting for a file change, holding nothing. That is the same limit
|
|
51
|
+
* `run.ts` states for the signals it forwards.
|
|
52
|
+
*/
|
|
53
|
+
const everyone = (signal) => {
|
|
54
|
+
try {
|
|
55
|
+
process.kill(process.platform === 'win32' ? process.pid : 0, signal);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// The group has already gone, which is the outcome being asked for.
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
if (Number.isInteger(supervisor) && supervisor > 0) {
|
|
62
|
+
const timer = setInterval(() => {
|
|
63
|
+
if (!gone(supervisor))
|
|
64
|
+
return;
|
|
65
|
+
clearInterval(timer);
|
|
66
|
+
everyone('SIGTERM');
|
|
67
|
+
// A server draining gracefully is the reason for the grace; a server that ignores
|
|
68
|
+
// SIGTERM is the reason for what follows it.
|
|
69
|
+
setTimeout(() => everyone('SIGKILL'), GRACE).unref();
|
|
70
|
+
}, EVERY);
|
|
71
|
+
timer.unref();
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=watchdog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchdog.js","sourceRoot":"","sources":["../../src/commands/watchdog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,KAAK,GAAG,GAAG,CAAA;AACjB,MAAM,KAAK,GAAG,KAAK,CAAA;AAEnB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,GAAW,EAAW,EAAE;IACpC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QAEpB,OAAO,KAAK,CAAA;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,OAAO,CAAA;IAC1D,CAAC;AACH,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,CAAC,MAAsB,EAAQ,EAAE;IAChD,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;AACH,CAAC,CAAA;AAED,IAAI,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,OAAM;QAE7B,aAAa,CAAC,KAAK,CAAC,CAAA;QACpB,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEnB,kFAAkF;QAClF,6CAA6C;QAC7C,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,CAAA;IACtD,CAAC,EAAE,KAAK,CAAC,CAAA;IAET,KAAK,CAAC,KAAK,EAAE,CAAA;AACf,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type Application } from '@assemora/core';
|
|
2
|
+
import type { OpenApiInfo } from '@assemora/openapi';
|
|
3
|
+
import type { GenerateOptions } from '@assemora/sdk';
|
|
4
|
+
export type AssemoraPaths = {
|
|
5
|
+
/** Where `make:*` writes. Defaults to `src`. */
|
|
6
|
+
readonly source?: string;
|
|
7
|
+
/** Where `db:generate` writes and `db:migrate` reads. Defaults to `database/migrations`. */
|
|
8
|
+
readonly migrations?: string;
|
|
9
|
+
/** Machine-owned output, never edited by hand. Defaults to `.assemora/generated`. */
|
|
10
|
+
readonly generated?: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* What a config's `app()` may hand back.
|
|
14
|
+
*
|
|
15
|
+
* The application itself, or the thing `assemora()` built around one. Both are accepted
|
|
16
|
+
* because the second is the better answer and used to be impossible to give: `app: () =>
|
|
17
|
+
* createApp().app` was the line every starter carried, and that `.app` threw away the
|
|
18
|
+
* half of the application that knows about MCP, the server and the frontend — which is
|
|
19
|
+
* how `assemora mcp` came to have nothing to serve.
|
|
20
|
+
*
|
|
21
|
+
* Structural rather than imported: `@assemora/cli` may not depend on the umbrella, and
|
|
22
|
+
* naming the one member it reads is the whole of what it needs (ADR-0021).
|
|
23
|
+
*/
|
|
24
|
+
export type ConfiguredApplication = Application | {
|
|
25
|
+
readonly app: Application;
|
|
26
|
+
};
|
|
27
|
+
export type AssemoraConfig = {
|
|
28
|
+
/**
|
|
29
|
+
* How the CLI gets the application.
|
|
30
|
+
*
|
|
31
|
+
* What comes back has *not* been booted: the CLI boots it once, so two commands in
|
|
32
|
+
* one process — and `console`, which is many — share a single application and a
|
|
33
|
+
* single database pool.
|
|
34
|
+
*/
|
|
35
|
+
readonly app: () => ConfiguredApplication | Promise<ConfiguredApplication>;
|
|
36
|
+
/** What `dev` and `start` run, relative to the config. */
|
|
37
|
+
readonly server?: string;
|
|
38
|
+
readonly paths?: AssemoraPaths;
|
|
39
|
+
readonly openapi?: {
|
|
40
|
+
/** Where `api:openapi` writes when `--out` is not given. */
|
|
41
|
+
readonly out?: string;
|
|
42
|
+
readonly info?: OpenApiInfo;
|
|
43
|
+
};
|
|
44
|
+
readonly sdk?: GenerateOptions & {
|
|
45
|
+
/** Where `sdk:generate` writes when `--out` is not given. */
|
|
46
|
+
readonly out?: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Identity, plus types.
|
|
51
|
+
*
|
|
52
|
+
* It exists so that the object is checked where it is written, which is the only
|
|
53
|
+
* place a mistake in it is cheap to fix.
|
|
54
|
+
*/
|
|
55
|
+
export declare const defineConfig: (config: AssemoraConfig) => AssemoraConfig;
|
|
56
|
+
/** In the order they are looked for, in each directory, on the way up. */
|
|
57
|
+
export declare const CONFIG_FILENAMES: readonly ['assemora.config.ts', 'assemora.config.js'];
|
|
58
|
+
export declare const DEFAULT_PATHS: {
|
|
59
|
+
readonly source: 'src';
|
|
60
|
+
readonly migrations: 'database/migrations';
|
|
61
|
+
readonly generated: '.assemora/generated';
|
|
62
|
+
};
|
|
63
|
+
/** `paths`, defaulted and made absolute. */
|
|
64
|
+
export type ResolvedPaths = {
|
|
65
|
+
readonly source: string;
|
|
66
|
+
readonly migrations: string;
|
|
67
|
+
readonly generated: string;
|
|
68
|
+
};
|
|
69
|
+
export type LoadedConfig = {
|
|
70
|
+
/** The default export, exactly as it was written. */
|
|
71
|
+
readonly config: AssemoraConfig;
|
|
72
|
+
/** Absolute path of the config file that was found. */
|
|
73
|
+
readonly file: string;
|
|
74
|
+
/** The directory holding it. Every relative path in the config is relative to this. */
|
|
75
|
+
readonly root: string;
|
|
76
|
+
readonly paths: ResolvedPaths;
|
|
77
|
+
/** `server`, made absolute, or undefined when the config declares none. */
|
|
78
|
+
readonly server: string | undefined;
|
|
79
|
+
/** Makes any other path the config carries absolute. An absolute one is left alone. */
|
|
80
|
+
resolve(path: string): string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Finds the project's config, walking up from `cwd`, and validates it.
|
|
84
|
+
*
|
|
85
|
+
* Walking up is what lets a command be typed from anywhere inside the project, which
|
|
86
|
+
* is how every other tool a developer already has behaves.
|
|
87
|
+
*/
|
|
88
|
+
export declare const loadConfig: (cwd: string) => Promise<LoadedConfig>;
|
|
89
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,WAAW,EAAqC,MAAM,gBAAgB,CAAA;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,MAAM,MAAM,aAAa,GAAG;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;CAAE,CAAA;AAE/E,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAA;IAC1E,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE;QACjB,4DAA4D;QAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;QACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAA;KAC5B,CAAA;IACD,QAAQ,CAAC,GAAG,CAAC,EAAE,eAAe,GAAG;QAC/B,6DAA6D;QAC7D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,WAAY,cAAc,KAAG,cAAwB,CAAA;AAE9E,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,YAAI,oBAAoB,EAAE,oBAAoB,CAAU,CAAA;AAErF,eAAO,MAAM,aAAa;qBAChB,KAAK;yBACD,qBAAqB;wBACtB,qBAAqB;CACU,CAAA;AAE5C,4CAA4C;AAC5C,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,uFAAuF;IACvF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAC9B,CAAA;AAsHD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,QAAe,MAAM,KAAG,OAAO,CAAC,YAAY,CA0ClE,CAAA"}
|