@zmdb/cli 1.0.0-beta.1
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 +674 -0
- package/README.md +19 -0
- package/dist/__fixtures__/http-client/src/contract.d.ts +38 -0
- package/dist/__fixtures__/http-client/src/contract.d.ts.map +1 -0
- package/dist/__fixtures__/http-client/src/contract.js +121 -0
- package/dist/__fixtures__/http-client/src/contract.js.map +1 -0
- package/dist/__fixtures__/http-client/src/models.d.ts +11 -0
- package/dist/__fixtures__/http-client/src/models.d.ts.map +1 -0
- package/dist/__fixtures__/http-client/src/models.js +2 -0
- package/dist/__fixtures__/http-client/src/models.js.map +1 -0
- package/dist/__fixtures__/http-client/src/schema.d.ts +4 -0
- package/dist/__fixtures__/http-client/src/schema.d.ts.map +1 -0
- package/dist/__fixtures__/http-client/src/schema.js +2 -0
- package/dist/__fixtures__/http-client/src/schema.js.map +1 -0
- package/dist/__fixtures__/http-client/src/unrelated.d.ts +2 -0
- package/dist/__fixtures__/http-client/src/unrelated.d.ts.map +1 -0
- package/dist/__fixtures__/http-client/src/unrelated.js +2 -0
- package/dist/__fixtures__/http-client/src/unrelated.js.map +1 -0
- package/dist/__fixtures__/http-client/zmdb.config.d.ts +16 -0
- package/dist/__fixtures__/http-client/zmdb.config.d.ts.map +1 -0
- package/dist/__fixtures__/http-client/zmdb.config.js +12 -0
- package/dist/__fixtures__/http-client/zmdb.config.js.map +1 -0
- package/dist/__fixtures__/project/src/schema.d.ts +6 -0
- package/dist/__fixtures__/project/src/schema.d.ts.map +1 -0
- package/dist/__fixtures__/project/src/schema.js +2 -0
- package/dist/__fixtures__/project/src/schema.js.map +1 -0
- package/dist/__fixtures__/project/zmdb.config.d.ts +22 -0
- package/dist/__fixtures__/project/zmdb.config.d.ts.map +1 -0
- package/dist/__fixtures__/project/zmdb.config.js +55 -0
- package/dist/__fixtures__/project/zmdb.config.js.map +1 -0
- package/dist/application-loader.d.ts +4 -0
- package/dist/application-loader.d.ts.map +1 -0
- package/dist/application-loader.js +102 -0
- package/dist/application-loader.js.map +1 -0
- package/dist/args.d.ts +23 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +353 -0
- package/dist/args.js.map +1 -0
- package/dist/atomic.d.ts +13 -0
- package/dist/atomic.d.ts.map +1 -0
- package/dist/atomic.js +28 -0
- package/dist/atomic.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +4 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/check.d.ts +5 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +7 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/client.d.ts +38 -0
- package/dist/commands/client.d.ts.map +1 -0
- package/dist/commands/client.js +347 -0
- package/dist/commands/client.js.map +1 -0
- package/dist/commands/codegen.d.ts +9 -0
- package/dist/commands/codegen.d.ts.map +1 -0
- package/dist/commands/codegen.js +56 -0
- package/dist/commands/codegen.js.map +1 -0
- package/dist/commands/embed.d.ts +8 -0
- package/dist/commands/embed.d.ts.map +1 -0
- package/dist/commands/embed.js +17 -0
- package/dist/commands/embed.js.map +1 -0
- package/dist/commands/export.d.ts +5 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +6 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/generate.d.ts +5 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +6 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/migrate.d.ts +9 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +18 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/new.d.ts +4 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +35 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pull.d.ts +5 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +14 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +6 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +11 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/studio.d.ts +31 -0
- package/dist/commands/studio.d.ts.map +1 -0
- package/dist/commands/studio.js +154 -0
- package/dist/commands/studio.js.map +1 -0
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +11 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/database.d.ts +4 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +8 -0
- package/dist/database.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +626 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle.d.ts +6 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +75 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/migration-project.d.ts +14 -0
- package/dist/migration-project.d.ts.map +1 -0
- package/dist/migration-project.js +74 -0
- package/dist/migration-project.js.map +1 -0
- package/dist/output.d.ts +27 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +60 -0
- package/dist/output.js.map +1 -0
- package/dist/repl.d.ts +48 -0
- package/dist/repl.d.ts.map +1 -0
- package/dist/repl.js +265 -0
- package/dist/repl.js.map +1 -0
- package/dist/scaffold.d.ts +28 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +414 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/studio/index.d.ts +20 -0
- package/dist/studio/index.d.ts.map +1 -0
- package/dist/studio/index.js +607 -0
- package/dist/studio/index.js.map +1 -0
- package/dist/templates/command.d.ts +3 -0
- package/dist/templates/command.d.ts.map +1 -0
- package/dist/templates/command.js +65 -0
- package/dist/templates/command.js.map +1 -0
- package/dist/templates/controller.d.ts +3 -0
- package/dist/templates/controller.d.ts.map +1 -0
- package/dist/templates/controller.js +41 -0
- package/dist/templates/controller.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +18 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/module.d.ts +3 -0
- package/dist/templates/module.d.ts.map +1 -0
- package/dist/templates/module.js +41 -0
- package/dist/templates/module.js.map +1 -0
- package/dist/templates/project.d.ts +3 -0
- package/dist/templates/project.d.ts.map +1 -0
- package/dist/templates/project.js +280 -0
- package/dist/templates/project.js.map +1 -0
- package/dist/templates/repository.d.ts +3 -0
- package/dist/templates/repository.d.ts.map +1 -0
- package/dist/templates/repository.js +66 -0
- package/dist/templates/repository.js.map +1 -0
- package/dist/templates/schema.d.ts +3 -0
- package/dist/templates/schema.d.ts.map +1 -0
- package/dist/templates/schema.js +36 -0
- package/dist/templates/schema.js.map +1 -0
- package/dist/templates/types.d.ts +25 -0
- package/dist/templates/types.d.ts.map +1 -0
- package/dist/templates/types.js +17 -0
- package/dist/templates/types.js.map +1 -0
- package/package.json +72 -0
- package/src/__fixtures__/http-client/package.json +5 -0
- package/src/__fixtures__/http-client/src/contract.ts +85 -0
- package/src/__fixtures__/http-client/src/models.ts +12 -0
- package/src/__fixtures__/http-client/src/schema.ts +3 -0
- package/src/__fixtures__/http-client/src/unrelated.ts +1 -0
- package/src/__fixtures__/http-client/tsconfig.json +17 -0
- package/src/__fixtures__/http-client/zmdb.config.ts +12 -0
- package/src/__fixtures__/project/package.json +5 -0
- package/src/__fixtures__/project/src/schema.ts +6 -0
- package/src/__fixtures__/project/tsconfig.json +24 -0
- package/src/__fixtures__/project/zmdb.config.ts +57 -0
- package/src/application-loader.ts +108 -0
- package/src/args.ts +391 -0
- package/src/atomic.ts +43 -0
- package/src/bin.ts +5 -0
- package/src/commands/check.ts +11 -0
- package/src/commands/client.ts +415 -0
- package/src/commands/codegen.ts +73 -0
- package/src/commands/embed.ts +36 -0
- package/src/commands/export.ts +10 -0
- package/src/commands/generate.ts +14 -0
- package/src/commands/migrate.ts +38 -0
- package/src/commands/new.ts +48 -0
- package/src/commands/pull.ts +32 -0
- package/src/commands/push.ts +21 -0
- package/src/commands/studio.ts +200 -0
- package/src/commands/upgrade.ts +14 -0
- package/src/database.ts +9 -0
- package/src/errors.ts +19 -0
- package/src/index.ts +710 -0
- package/src/lifecycle.ts +73 -0
- package/src/migration-project.ts +91 -0
- package/src/output.ts +77 -0
- package/src/repl.ts +328 -0
- package/src/scaffold.ts +485 -0
- package/src/studio/index.ts +674 -0
- package/src/templates/command.ts +66 -0
- package/src/templates/controller.ts +42 -0
- package/src/templates/index.ts +29 -0
- package/src/templates/module.ts +42 -0
- package/src/templates/project.ts +281 -0
- package/src/templates/repository.ts +67 -0
- package/src/templates/schema.ts +37 -0
- package/src/templates/types.ts +46 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createServer, type IncomingHttpHeaders, type ServerResponse } from 'node:http';
|
|
2
|
+
|
|
3
|
+
import { createStudioApp, type StudioApp, type StudioInput } from '../studio/index.js';
|
|
4
|
+
|
|
5
|
+
export interface StudioListenOptions {
|
|
6
|
+
readonly host: '127.0.0.1';
|
|
7
|
+
readonly port: number;
|
|
8
|
+
readonly fetch: StudioApp['fetch'];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface StudioListener extends AsyncDisposable {
|
|
12
|
+
readonly host: '127.0.0.1';
|
|
13
|
+
readonly port: number;
|
|
14
|
+
readonly closed: Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface StudioRuntime {
|
|
18
|
+
createApp(input: StudioInput): StudioApp;
|
|
19
|
+
listen(options: StudioListenOptions): Promise<StudioListener>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RunStudioOptions {
|
|
23
|
+
readonly port?: number;
|
|
24
|
+
readonly runtime?: StudioRuntime;
|
|
25
|
+
readonly stdout?: (text: string) => void;
|
|
26
|
+
readonly stderr?: (text: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DEFAULT_RUNTIME: StudioRuntime = {
|
|
30
|
+
createApp: createStudioApp,
|
|
31
|
+
listen: listenStudio,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Start the local viewer and keep it alive until its loopback listener closes.
|
|
36
|
+
*
|
|
37
|
+
* The host is not an option: the literal passed to the runtime is the security
|
|
38
|
+
* boundary, and the concrete listener below uses it without a fallback.
|
|
39
|
+
*/
|
|
40
|
+
export async function runStudio(input: StudioInput, options: RunStudioOptions = {}): Promise<number> {
|
|
41
|
+
const runtime = options.runtime ?? DEFAULT_RUNTIME;
|
|
42
|
+
const stdout = options.stdout ?? (text => process.stdout.write(text));
|
|
43
|
+
const stderr = options.stderr ?? (text => process.stderr.write(text));
|
|
44
|
+
let app: StudioApp | undefined;
|
|
45
|
+
let listener: StudioListener | undefined;
|
|
46
|
+
let failed = false;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
app = runtime.createApp(input);
|
|
50
|
+
listener = await runtime.listen({
|
|
51
|
+
host: '127.0.0.1',
|
|
52
|
+
port: options.port ?? 0,
|
|
53
|
+
fetch: request => app?.fetch(request) ?? Promise.resolve(new Response('studio is closed', { status: 503 })),
|
|
54
|
+
});
|
|
55
|
+
stdout(`http://${listener.host}:${String(listener.port)}\n`);
|
|
56
|
+
await listener.closed;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
failed = true;
|
|
59
|
+
stderr(`zmdb studio: ${messageOf(error)}\n`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (listener !== undefined) {
|
|
63
|
+
try {
|
|
64
|
+
await listener[Symbol.asyncDispose]();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
stderr(`zmdb studio: ${failed ? 'cleanup: ' : ''}${messageOf(error)}\n`);
|
|
67
|
+
failed = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (app !== undefined) {
|
|
71
|
+
try {
|
|
72
|
+
await app[Symbol.asyncDispose]();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
stderr(`zmdb studio: ${failed ? 'cleanup: ' : ''}${messageOf(error)}\n`);
|
|
75
|
+
failed = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return failed ? 1 : 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Bind the Fetch application to one Node HTTP listener on IPv4 loopback. */
|
|
82
|
+
export function listenStudio(options: StudioListenOptions): Promise<StudioListener> {
|
|
83
|
+
validatePort(options.port);
|
|
84
|
+
|
|
85
|
+
return new Promise<StudioListener>((resolveListener, rejectListener) => {
|
|
86
|
+
const server = createServer((request, response) => {
|
|
87
|
+
if (request.method !== 'GET') {
|
|
88
|
+
request.resume();
|
|
89
|
+
}
|
|
90
|
+
void serve(request.method ?? 'GET', request.url ?? '/', request.headers, response, options).catch(
|
|
91
|
+
(error: unknown) => {
|
|
92
|
+
if (!response.headersSent) {
|
|
93
|
+
response.writeHead(500, { 'content-type': 'text/plain; charset=utf-8' });
|
|
94
|
+
response.end(`zmdb studio: ${messageOf(error)}`);
|
|
95
|
+
} else {
|
|
96
|
+
response.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const onOpenError = (error: Error): void => {
|
|
103
|
+
rejectListener(error);
|
|
104
|
+
};
|
|
105
|
+
server.once('error', onOpenError);
|
|
106
|
+
server.listen(options.port, options.host, () => {
|
|
107
|
+
server.off('error', onOpenError);
|
|
108
|
+
const address = server.address();
|
|
109
|
+
if (typeof address !== 'object' || address === null) {
|
|
110
|
+
server.close();
|
|
111
|
+
rejectListener(new Error('zmdb studio did not receive a TCP listener address'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const closed = new Promise<void>((resolveClosed, rejectClosed) => {
|
|
116
|
+
server.once('close', resolveClosed);
|
|
117
|
+
server.once('error', rejectClosed);
|
|
118
|
+
});
|
|
119
|
+
const closeForSignal = (): void => {
|
|
120
|
+
server.close();
|
|
121
|
+
};
|
|
122
|
+
const removeSignalHandlers = (): void => {
|
|
123
|
+
process.off('SIGINT', closeForSignal);
|
|
124
|
+
process.off('SIGTERM', closeForSignal);
|
|
125
|
+
};
|
|
126
|
+
process.once('SIGINT', closeForSignal);
|
|
127
|
+
process.once('SIGTERM', closeForSignal);
|
|
128
|
+
void closed.then(removeSignalHandlers, removeSignalHandlers);
|
|
129
|
+
|
|
130
|
+
let disposePromise: Promise<void> | undefined;
|
|
131
|
+
resolveListener({
|
|
132
|
+
host: options.host,
|
|
133
|
+
port: address.port,
|
|
134
|
+
closed,
|
|
135
|
+
[Symbol.asyncDispose](): Promise<void> {
|
|
136
|
+
disposePromise ??= closeServer(server.listening, () => server.close(), closed, removeSignalHandlers);
|
|
137
|
+
return disposePromise;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function serve(
|
|
145
|
+
method: string,
|
|
146
|
+
path: string,
|
|
147
|
+
incomingHeaders: IncomingHttpHeaders,
|
|
148
|
+
response: ServerResponse,
|
|
149
|
+
options: StudioListenOptions,
|
|
150
|
+
): Promise<void> {
|
|
151
|
+
const url = new URL(path, `http://${options.host}:${String(options.port)}`);
|
|
152
|
+
const request = new Request(url, {
|
|
153
|
+
method,
|
|
154
|
+
headers: requestHeaders(incomingHeaders),
|
|
155
|
+
});
|
|
156
|
+
const result = await options.fetch(request);
|
|
157
|
+
const headers: Record<string, string> = {};
|
|
158
|
+
result.headers.forEach((value, name) => {
|
|
159
|
+
headers[name] = value;
|
|
160
|
+
});
|
|
161
|
+
response.writeHead(result.status, headers);
|
|
162
|
+
response.end(new Uint8Array(await result.arrayBuffer()));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function requestHeaders(input: IncomingHttpHeaders): Headers {
|
|
166
|
+
const headers = new Headers();
|
|
167
|
+
for (const [name, value] of Object.entries(input)) {
|
|
168
|
+
if (typeof value === 'string') {
|
|
169
|
+
headers.set(name, value);
|
|
170
|
+
} else if (Array.isArray(value)) {
|
|
171
|
+
for (const item of value) {
|
|
172
|
+
headers.append(name, item);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return headers;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function closeServer(
|
|
180
|
+
listening: boolean,
|
|
181
|
+
close: () => void,
|
|
182
|
+
closed: Promise<void>,
|
|
183
|
+
removeSignalHandlers: () => void,
|
|
184
|
+
): Promise<void> {
|
|
185
|
+
removeSignalHandlers();
|
|
186
|
+
if (listening) {
|
|
187
|
+
close();
|
|
188
|
+
}
|
|
189
|
+
await closed;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function validatePort(port: number): void {
|
|
193
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535) {
|
|
194
|
+
throw new RangeError(`studio port must be an integer from 0 through 65535, received ${String(port)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function messageOf(error: unknown): string {
|
|
199
|
+
return error instanceof Error ? error.message : String(error);
|
|
200
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ResolvedConfig } from '@zmdb/compiler/config';
|
|
2
|
+
import { upgradeSnapshot as upgradeProjectSnapshot, type UpgradeResult } from '@zmdb/migrations/files';
|
|
3
|
+
|
|
4
|
+
import { migrationProject, rethrowProjectError } from '../migration-project.js';
|
|
5
|
+
|
|
6
|
+
export type { UpgradeResult };
|
|
7
|
+
|
|
8
|
+
export async function upgradeSnapshot(config: ResolvedConfig): Promise<UpgradeResult> {
|
|
9
|
+
try {
|
|
10
|
+
return await upgradeProjectSnapshot(migrationProject(config));
|
|
11
|
+
} catch (error) {
|
|
12
|
+
rethrowProjectError(error);
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/database.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type DialectTarget, type Introspector } from '@zmdb/sql';
|
|
2
|
+
|
|
3
|
+
export function configuredDialect(dialect: DialectTarget): DialectTarget {
|
|
4
|
+
return dialect;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function configuredIntrospector(dialect: DialectTarget): Introspector {
|
|
8
|
+
return dialect.introspector;
|
|
9
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** A command-line contract error rather than a failed schema operation. */
|
|
2
|
+
export class CliInvocationError extends Error {
|
|
3
|
+
constructor(message: string) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'CliInvocationError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Preserve the original failure before an error raised while disposing its resources. */
|
|
10
|
+
export function errorMessage(error: unknown): string {
|
|
11
|
+
if (error instanceof SuppressedError) {
|
|
12
|
+
return `${errorMessage(error.suppressed)}\ncleanup: ${errorMessage(error.error)}`;
|
|
13
|
+
}
|
|
14
|
+
if (error instanceof AggregateError) {
|
|
15
|
+
const errors: readonly unknown[] = error.errors;
|
|
16
|
+
if (errors.length > 0) return errors.map(errorMessage).join('\ncleanup: ');
|
|
17
|
+
}
|
|
18
|
+
return error instanceof Error ? error.message : String(error);
|
|
19
|
+
}
|