@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
package/src/args.ts
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { parseArgs, type ParseArgsOptionDescriptor, type ParseArgsOptionsConfig } from 'node:util';
|
|
3
|
+
|
|
4
|
+
interface OptionDefinition extends ParseArgsOptionDescriptor {
|
|
5
|
+
readonly valueName?: string;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type OptionDefinitions = Readonly<Record<string, OptionDefinition>>;
|
|
10
|
+
|
|
11
|
+
interface CommandDefinition {
|
|
12
|
+
readonly usage: string;
|
|
13
|
+
readonly summary: string;
|
|
14
|
+
readonly options: OptionDefinitions;
|
|
15
|
+
readonly maximumPositionals: number;
|
|
16
|
+
readonly hidden?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const COMMON_OPTIONS: OptionDefinitions = {
|
|
20
|
+
config: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
valueName: 'path',
|
|
23
|
+
description: 'Use this config file instead of discovery.',
|
|
24
|
+
},
|
|
25
|
+
project: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
valueName: 'tsconfig',
|
|
28
|
+
description: "Override the config's TypeScript project.",
|
|
29
|
+
},
|
|
30
|
+
json: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: 'Write one machine-readable result document to stdout.',
|
|
33
|
+
},
|
|
34
|
+
yes: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Answer permitted prompts without reading stdin.',
|
|
37
|
+
},
|
|
38
|
+
force: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
description: 'Permit destructive database operations where supported.',
|
|
41
|
+
},
|
|
42
|
+
help: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
short: 'h',
|
|
45
|
+
description: 'Show help for this command.',
|
|
46
|
+
},
|
|
47
|
+
version: {
|
|
48
|
+
type: 'boolean',
|
|
49
|
+
description: 'Print the installed zmdb version.',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const DATABASE_COMMANDS: Readonly<Record<string, CommandDefinition>> = {
|
|
54
|
+
generate: {
|
|
55
|
+
usage: 'zmdb generate [--name <slug>]',
|
|
56
|
+
summary: 'Create a migration and update the stored snapshot.',
|
|
57
|
+
options: {
|
|
58
|
+
name: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
valueName: 'slug',
|
|
61
|
+
description: 'Name the migration; otherwise derive a name from the plan.',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
maximumPositionals: 0,
|
|
65
|
+
},
|
|
66
|
+
embed: {
|
|
67
|
+
usage: 'zmdb embed [--out <file>] [--with-down]',
|
|
68
|
+
summary: 'Compile SQLite migrations into a bundle-resident TypeScript module.',
|
|
69
|
+
options: {
|
|
70
|
+
out: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
valueName: 'file',
|
|
73
|
+
description: 'Write this module instead of migrations/embedded.ts.',
|
|
74
|
+
},
|
|
75
|
+
'with-down': {
|
|
76
|
+
type: 'boolean',
|
|
77
|
+
description: 'Include down sections for development tooling.',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
maximumPositionals: 0,
|
|
81
|
+
},
|
|
82
|
+
migrate: {
|
|
83
|
+
usage: 'zmdb migrate',
|
|
84
|
+
summary: 'Apply pending migrations.',
|
|
85
|
+
options: {},
|
|
86
|
+
maximumPositionals: 0,
|
|
87
|
+
},
|
|
88
|
+
rollback: {
|
|
89
|
+
usage: 'zmdb rollback [--to <version>]',
|
|
90
|
+
summary: 'Revert the latest migration or roll back to a version.',
|
|
91
|
+
options: {
|
|
92
|
+
to: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
valueName: 'version',
|
|
95
|
+
description: 'Revert down to, but not including, this version.',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
maximumPositionals: 0,
|
|
99
|
+
},
|
|
100
|
+
status: {
|
|
101
|
+
usage: 'zmdb status',
|
|
102
|
+
summary: 'List migration versions and their applied state.',
|
|
103
|
+
options: {},
|
|
104
|
+
maximumPositionals: 0,
|
|
105
|
+
},
|
|
106
|
+
push: {
|
|
107
|
+
usage: 'zmdb push --force --yes',
|
|
108
|
+
summary: 'Apply declaration changes directly to a development database.',
|
|
109
|
+
options: {},
|
|
110
|
+
maximumPositionals: 0,
|
|
111
|
+
},
|
|
112
|
+
check: {
|
|
113
|
+
usage: 'zmdb check',
|
|
114
|
+
summary: 'Report schema, migration and snapshot findings.',
|
|
115
|
+
options: {},
|
|
116
|
+
maximumPositionals: 0,
|
|
117
|
+
},
|
|
118
|
+
upgrade: {
|
|
119
|
+
usage: 'zmdb upgrade',
|
|
120
|
+
summary: 'Upgrade the stored snapshot format without touching a database.',
|
|
121
|
+
options: {},
|
|
122
|
+
maximumPositionals: 0,
|
|
123
|
+
},
|
|
124
|
+
export: {
|
|
125
|
+
usage: 'zmdb export',
|
|
126
|
+
summary: 'Print the declaration set as dialect DDL.',
|
|
127
|
+
options: {},
|
|
128
|
+
maximumPositionals: 0,
|
|
129
|
+
},
|
|
130
|
+
pull: {
|
|
131
|
+
usage: 'zmdb pull [--dry-run] [--check]',
|
|
132
|
+
summary: 'Write declarations from a live database catalogue.',
|
|
133
|
+
options: {
|
|
134
|
+
'dry-run': {
|
|
135
|
+
type: 'boolean',
|
|
136
|
+
description: 'Print every generated declaration and write nothing.',
|
|
137
|
+
},
|
|
138
|
+
check: {
|
|
139
|
+
type: 'boolean',
|
|
140
|
+
description: 'Exit non-zero when the generated declarations are not current.',
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
maximumPositionals: 0,
|
|
144
|
+
},
|
|
145
|
+
up: {
|
|
146
|
+
usage: 'zmdb up',
|
|
147
|
+
summary: 'Refused: use migrate for the database or upgrade for a snapshot.',
|
|
148
|
+
options: {},
|
|
149
|
+
maximumPositionals: 0,
|
|
150
|
+
hidden: true,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const APPLICATION_COMMANDS: Readonly<Record<string, CommandDefinition>> = {
|
|
155
|
+
client: {
|
|
156
|
+
usage: 'zmdb client generate [--check] [--watch]',
|
|
157
|
+
summary: 'Generate OpenAPI and a typed HTTP client from configured contracts.',
|
|
158
|
+
options: {
|
|
159
|
+
check: {
|
|
160
|
+
type: 'boolean',
|
|
161
|
+
description: 'Write nothing and fail when either generated artifact is stale.',
|
|
162
|
+
},
|
|
163
|
+
watch: {
|
|
164
|
+
type: 'boolean',
|
|
165
|
+
description: 'Regenerate after an input in the compiled contract dependency set changes.',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
maximumPositionals: 1,
|
|
169
|
+
},
|
|
170
|
+
new: {
|
|
171
|
+
usage: 'zmdb new <project|schema|controller|module|repository|command> <name> [--package <pkg>]',
|
|
172
|
+
summary: 'Create a formatter-clean project or application component.',
|
|
173
|
+
options: {
|
|
174
|
+
package: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
valueName: 'pkg',
|
|
177
|
+
description: 'Target this named package when invoked from a workspace.',
|
|
178
|
+
},
|
|
179
|
+
'dry-run': {
|
|
180
|
+
type: 'boolean',
|
|
181
|
+
description: 'Print every generated file and write nothing.',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
maximumPositionals: 2,
|
|
185
|
+
},
|
|
186
|
+
modules: {
|
|
187
|
+
usage:
|
|
188
|
+
'zmdb modules [path#export] [--format tree|dot] [--providers]\n' +
|
|
189
|
+
' [--module <name>] [--token <description>] [--depth <n>]',
|
|
190
|
+
summary: 'Describe application declarations without constructing providers.',
|
|
191
|
+
options: {
|
|
192
|
+
format: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
valueName: 'tree|dot',
|
|
195
|
+
description: 'Choose the human-readable graph format.',
|
|
196
|
+
},
|
|
197
|
+
providers: {
|
|
198
|
+
type: 'boolean',
|
|
199
|
+
description: 'Include provider nodes.',
|
|
200
|
+
},
|
|
201
|
+
module: {
|
|
202
|
+
type: 'string',
|
|
203
|
+
valueName: 'name',
|
|
204
|
+
description: 'Limit output to one module.',
|
|
205
|
+
},
|
|
206
|
+
token: {
|
|
207
|
+
type: 'string',
|
|
208
|
+
valueName: 'description',
|
|
209
|
+
description: 'Limit output to providers matching a token description.',
|
|
210
|
+
},
|
|
211
|
+
depth: {
|
|
212
|
+
type: 'string',
|
|
213
|
+
valueName: 'n',
|
|
214
|
+
description: 'Limit traversal to a non-negative depth.',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
maximumPositionals: 1,
|
|
218
|
+
},
|
|
219
|
+
repl: {
|
|
220
|
+
usage: 'zmdb repl [path#export] [--no-history]',
|
|
221
|
+
summary: 'Boot an application into a local interactive session.',
|
|
222
|
+
options: {
|
|
223
|
+
history: {
|
|
224
|
+
type: 'boolean',
|
|
225
|
+
default: true,
|
|
226
|
+
description: 'Use private REPL history; pass --no-history to disable it.',
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
maximumPositionals: 1,
|
|
230
|
+
},
|
|
231
|
+
studio: {
|
|
232
|
+
usage: 'zmdb studio [--port <port>]',
|
|
233
|
+
summary: 'Browse configured tables through a read-only loopback server.',
|
|
234
|
+
options: {
|
|
235
|
+
port: {
|
|
236
|
+
type: 'string',
|
|
237
|
+
valueName: 'port',
|
|
238
|
+
description: 'Bind this loopback port instead of selecting an ephemeral one.',
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
maximumPositionals: 0,
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const COMMANDS: Readonly<Record<string, CommandDefinition>> = {
|
|
246
|
+
codegen: {
|
|
247
|
+
usage: 'zmdb codegen [--project <tsconfig>] [--check] [--watch]',
|
|
248
|
+
summary: 'Generate validator artifacts from a TypeScript project.',
|
|
249
|
+
options: {
|
|
250
|
+
check: { type: 'boolean', description: 'Write nothing and fail when generated artifacts are stale.' },
|
|
251
|
+
watch: { type: 'boolean', description: 'Regenerate on source changes using one compiler session.' },
|
|
252
|
+
},
|
|
253
|
+
maximumPositionals: 0,
|
|
254
|
+
},
|
|
255
|
+
...DATABASE_COMMANDS,
|
|
256
|
+
...APPLICATION_COMMANDS,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export interface ParsedCommand {
|
|
260
|
+
readonly command: string;
|
|
261
|
+
readonly config: string;
|
|
262
|
+
readonly project?: string;
|
|
263
|
+
readonly json: boolean;
|
|
264
|
+
readonly help: boolean;
|
|
265
|
+
readonly version: boolean;
|
|
266
|
+
readonly positionals: readonly string[];
|
|
267
|
+
readonly values: Readonly<Record<string, unknown>>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export type ParseCommandResult =
|
|
271
|
+
| { readonly parsed: ParsedCommand }
|
|
272
|
+
| { readonly error: string; readonly command: string; readonly config: string; readonly json: boolean };
|
|
273
|
+
|
|
274
|
+
export function isCommand(command: string): boolean {
|
|
275
|
+
return Object.hasOwn(COMMANDS, command);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function parseCommand(command: string, argv: readonly string[], cwd: string): ParseCommandResult {
|
|
279
|
+
const definition = COMMANDS[command];
|
|
280
|
+
if (definition === undefined) {
|
|
281
|
+
return {
|
|
282
|
+
error: `unknown command "${command}"`,
|
|
283
|
+
command,
|
|
284
|
+
config: attemptedConfig(argv, cwd),
|
|
285
|
+
json: argv.includes('--json'),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const { values, positionals } = parseArgs({
|
|
291
|
+
args: argv,
|
|
292
|
+
options: parseOptions({ ...COMMON_OPTIONS, ...definition.options }),
|
|
293
|
+
strict: true,
|
|
294
|
+
allowPositionals: true,
|
|
295
|
+
allowNegative: true,
|
|
296
|
+
});
|
|
297
|
+
if (positionals.length > definition.maximumPositionals) {
|
|
298
|
+
return {
|
|
299
|
+
error: `unexpected positional argument "${positionals[definition.maximumPositionals] ?? ''}"`,
|
|
300
|
+
command,
|
|
301
|
+
config: configValue(values, cwd),
|
|
302
|
+
json: values.json === true,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
parsed: {
|
|
307
|
+
command,
|
|
308
|
+
config: configValue(values, cwd),
|
|
309
|
+
...(typeof values.project === 'string' ? { project: values.project } : {}),
|
|
310
|
+
json: values.json === true,
|
|
311
|
+
help: values.help === true,
|
|
312
|
+
version: values.version === true,
|
|
313
|
+
positionals,
|
|
314
|
+
values,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
} catch (error) {
|
|
318
|
+
return {
|
|
319
|
+
error: parseError(error),
|
|
320
|
+
command,
|
|
321
|
+
config: attemptedConfig(argv, cwd),
|
|
322
|
+
json: argv.includes('--json'),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function globalHelp(): string {
|
|
328
|
+
const commandRows = Object.entries(COMMANDS)
|
|
329
|
+
.filter(([, definition]) => definition.hidden !== true)
|
|
330
|
+
.map(([name, definition]) => ` ${name.padEnd(10)} ${definition.summary}`);
|
|
331
|
+
return [
|
|
332
|
+
'zmdb — schema and application developer tools.',
|
|
333
|
+
'',
|
|
334
|
+
'Usage:',
|
|
335
|
+
' zmdb <command> [options]',
|
|
336
|
+
'',
|
|
337
|
+
'Commands:',
|
|
338
|
+
...commandRows,
|
|
339
|
+
'',
|
|
340
|
+
'Run `zmdb <command> --help` for command-specific options.',
|
|
341
|
+
'',
|
|
342
|
+
].join('\n');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function commandHelp(command: string): string {
|
|
346
|
+
const definition = COMMANDS[command];
|
|
347
|
+
if (definition === undefined) return globalHelp();
|
|
348
|
+
const options = { ...COMMON_OPTIONS, ...definition.options };
|
|
349
|
+
const optionRows = Object.entries(options).map(([name, option]) => {
|
|
350
|
+
const longName = name === 'history' ? '--no-history' : `--${name}`;
|
|
351
|
+
const short = option.short === undefined ? '' : `-${option.short}, `;
|
|
352
|
+
const value = option.valueName === undefined ? '' : ` <${option.valueName}>`;
|
|
353
|
+
return ` ${`${short}${longName}${value}`.padEnd(30)} ${option.description}`;
|
|
354
|
+
});
|
|
355
|
+
return [definition.summary, '', 'Usage:', ` ${definition.usage}`, '', 'Options:', ...optionRows, ''].join('\n');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function parseOptions(definitions: OptionDefinitions): ParseArgsOptionsConfig {
|
|
359
|
+
const options: ParseArgsOptionsConfig = {};
|
|
360
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
361
|
+
options[name] = {
|
|
362
|
+
type: definition.type,
|
|
363
|
+
...(definition.short === undefined ? {} : { short: definition.short }),
|
|
364
|
+
...(definition.default === undefined ? {} : { default: definition.default }),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
return options;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function configValue(values: Readonly<Record<string, unknown>>, cwd: string): string {
|
|
371
|
+
return resolve(cwd, typeof values.config === 'string' ? values.config : 'zmdb.config.ts');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function attemptedConfig(argv: readonly string[], cwd: string): string {
|
|
375
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
376
|
+
const argument = argv[index];
|
|
377
|
+
if (argument === '--config') {
|
|
378
|
+
const value = argv[index + 1];
|
|
379
|
+
if (value !== undefined) return resolve(cwd, value);
|
|
380
|
+
}
|
|
381
|
+
if (argument?.startsWith('--config=')) return resolve(cwd, argument.slice('--config='.length));
|
|
382
|
+
}
|
|
383
|
+
return resolve(cwd, 'zmdb.config.ts');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function parseError(error: unknown): string {
|
|
387
|
+
if (!(error instanceof Error)) return String(error);
|
|
388
|
+
const message = error.message.replace(/^Option usage error:\s*/i, '');
|
|
389
|
+
const unknown = /^Unknown option '([^']+)'/.exec(message);
|
|
390
|
+
return unknown === null ? message : `unknown option "${unknown[1] ?? ''}"`;
|
|
391
|
+
}
|
package/src/atomic.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mkdir, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export interface AtomicWriteOperations {
|
|
5
|
+
readonly write: (path: string, text: string) => Promise<void>;
|
|
6
|
+
readonly rename: (from: string, to: string) => Promise<void>;
|
|
7
|
+
readonly remove: (path: string) => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const FILE_OPERATIONS: AtomicWriteOperations = {
|
|
11
|
+
write: (path, text) => writeFile(path, text, { flag: 'wx' }),
|
|
12
|
+
rename,
|
|
13
|
+
remove: path => rm(path, { force: true }),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let temporaryFileSequence = 0;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Replace one text file with a complete sibling written first.
|
|
20
|
+
*
|
|
21
|
+
* The temporary file lives in the destination directory, so `rename` is the
|
|
22
|
+
* atomic filesystem operation rather than a cross-device copy.
|
|
23
|
+
*/
|
|
24
|
+
export async function writeTextAtomically(
|
|
25
|
+
path: string,
|
|
26
|
+
text: string,
|
|
27
|
+
operations: AtomicWriteOperations = FILE_OPERATIONS,
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
await mkdir(dirname(path), { recursive: true });
|
|
30
|
+
temporaryFileSequence += 1;
|
|
31
|
+
const temporary = join(
|
|
32
|
+
dirname(path),
|
|
33
|
+
`.${basename(path)}.${String(process.pid)}.${String(temporaryFileSequence)}.tmp`,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await operations.write(temporary, text);
|
|
38
|
+
await operations.rename(temporary, path);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
await operations.remove(temporary);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ResolvedConfig } from '@zmdb/compiler/config';
|
|
2
|
+
import { checkProject as checkMigrationProject, type CheckResult } from '@zmdb/migrations/files';
|
|
3
|
+
|
|
4
|
+
import { configuredMigrationDriver, reflectedMigrationProject } from '../migration-project.js';
|
|
5
|
+
|
|
6
|
+
export type { CheckFinding, CheckFindingKind, CheckResult } from '@zmdb/migrations/files';
|
|
7
|
+
|
|
8
|
+
export async function checkProject(config: ResolvedConfig): Promise<CheckResult> {
|
|
9
|
+
const driver = config.driver === undefined ? undefined : await configuredMigrationDriver(config);
|
|
10
|
+
return checkMigrationProject(reflectedMigrationProject(config, driver));
|
|
11
|
+
}
|