@quatronic/qore 0.1.2 → 0.1.4
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/README.md +218 -4
- package/dist/cli.js +3095 -179
- package/dist/cli.js.map +1 -1
- package/package.json +3 -1
- package/schema/qore.schema.json +10 -1
package/README.md
CHANGED
|
@@ -140,20 +140,145 @@ qore run -- pnpm dev
|
|
|
140
140
|
no worse than a `.env.local` on disk, but worth knowing before running an
|
|
141
141
|
untrusted command this way.
|
|
142
142
|
|
|
143
|
+
#### `qore dev`
|
|
144
|
+
|
|
145
|
+
Starts this module's dev server, from a trusted repository:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
qore dev
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- **Context + trust**, like `qore run`/`qore env pull` — the same
|
|
152
|
+
`--module`/`--app` resolution, the same trust-on-first-use prompt
|
|
153
|
+
(`--yes` to skip it non-interactively).
|
|
154
|
+
- **Install**, if needed: the package manager comes from package.json's
|
|
155
|
+
`"packageManager": "<pnpm|npm|yarn>@<version>"` field, or (absent that)
|
|
156
|
+
whichever lockfile is present. `node_modules` missing, or older than the
|
|
157
|
+
lockfile, triggers an install; `--no-install` skips this step entirely.
|
|
158
|
+
- **Env merge**: this module's local secrets are pulled and merged into its
|
|
159
|
+
env file on disk (same target and conflict rules as `qore env pull`) —
|
|
160
|
+
unlike `qore run`, which only injects them into the child's environment.
|
|
161
|
+
- **`db:up`**, if the repository declares one: run automatically (unless
|
|
162
|
+
`--no-services`), then `qore dev` waits for `DB_PORT` to accept
|
|
163
|
+
connections before continuing — bounded by `--wait-timeout <s>` (default
|
|
164
|
+
`60`), which also bounds a workspace's provider-port wait (below). A
|
|
165
|
+
service that needs Docker and finds it missing or not running fails
|
|
166
|
+
fast, naming which.
|
|
167
|
+
- **Port check**: `PORT` from the merged env must be free; qore never
|
|
168
|
+
picks, shifts or suggests a port (see "Ports never move" below).
|
|
169
|
+
- **Runs `dev` exactly as `qore run` would** — same package manager, same
|
|
170
|
+
child-process semantics, same exit-code passthrough. The script name is
|
|
171
|
+
`"dev"` by default, or `dev.script` in `.qore.json` (below).
|
|
172
|
+
- `qore dev` in a module repository has no `--json`: the dev server's own
|
|
173
|
+
output passes straight through untouched.
|
|
174
|
+
|
|
175
|
+
A legacy backend `dev` script (from older templates, which need a
|
|
176
|
+
build and a `.env` first) fails fast with `QORE_DEV_UNSUPPORTED`, naming
|
|
177
|
+
the old script and the fix — the same code a repository with no `dev`
|
|
178
|
+
script at all, or an unsupported package manager, gets.
|
|
179
|
+
|
|
180
|
+
**Offline start**: if the portal is unreachable, `qore dev`
|
|
181
|
+
still starts — but only when the repository is already trusted **and** its
|
|
182
|
+
env file already exists on disk (from a previous `qore dev`/`env pull`).
|
|
183
|
+
It starts from that file as last written, with a warning that values may
|
|
184
|
+
be stale. This is the one place `qore` starts without confirming secrets
|
|
185
|
+
are current; every other command still requires the portal. An untrusted
|
|
186
|
+
repository, or one with no env file yet, surfaces the original
|
|
187
|
+
"portal unreachable" error instead.
|
|
188
|
+
|
|
189
|
+
**In a workspace root** (see "Workspaces" below), `qore dev` starts every
|
|
190
|
+
present module of the app, in dependency order, from one process — unless
|
|
191
|
+
`--module <slug>` is passed, which takes the module flow above instead
|
|
192
|
+
(run it from inside that module's own repository, not the workspace root
|
|
193
|
+
itself, or it fails with "Not inside a module repository"). `--app` is
|
|
194
|
+
refused in a workspace root: an app is already implied by
|
|
195
|
+
`qore.workspace.json`.
|
|
196
|
+
|
|
197
|
+
**Ports never move.** If `PORT` (or, for a workspace, a `db:up`'s
|
|
198
|
+
`DB_PORT`) is already taken, `qore dev` fails with `QORE_PORT_IN_USE`
|
|
199
|
+
naming the port and, when it can determine one, the process already
|
|
200
|
+
holding it — it never picks a different port, shifts one, or suggests an
|
|
201
|
+
alternative. Ports are part of the local contract between modules
|
|
202
|
+
(`CONN_*_URL` values point at fixed ports), so moving one silently would
|
|
203
|
+
break every other module that depends on it.
|
|
204
|
+
|
|
205
|
+
#### What qore diagnoses
|
|
206
|
+
|
|
207
|
+
Beyond the checks above, `qore dev` recognises common failure shapes and adds
|
|
208
|
+
a hint rather than leaving a bare error (spec §14.2):
|
|
209
|
+
|
|
210
|
+
- **Docker not running**: in a TTY, waits up to 120 s for it (`Ctrl+C` to
|
|
211
|
+
stop) instead of failing immediately; non-interactive or `--json` fails at
|
|
212
|
+
once.
|
|
213
|
+
- **A leftover `qore dev` holds the port**: in a TTY, offers to stop it
|
|
214
|
+
(`Stop the previous qore dev (pid N)?`); outside a TTY it's just
|
|
215
|
+
`QORE_PORT_IN_USE`.
|
|
216
|
+
- **The dev server exits within seconds of starting**: its tail output is
|
|
217
|
+
classified into a hint — a missing dependency (`Cannot find module`), an
|
|
218
|
+
invalid/missing env var (a `ZodError`'s keys, never values), a port taken
|
|
219
|
+
after the free-port check (`EADDRINUSE`), or a Linux file-watcher limit —
|
|
220
|
+
printed after the exit, never rewriting the server's own output. An
|
|
221
|
+
unrecognised failure falls back to the tool's last lines plus the command
|
|
222
|
+
that ran, never a bare "failed".
|
|
223
|
+
- **The portal is unreachable behind a corporate proxy**: the connect error
|
|
224
|
+
names `HTTPS_PROXY`/`NODE_EXTRA_CA_CERTS` as things to check.
|
|
225
|
+
|
|
226
|
+
`QORE_DEV_EXITED` is not a thrown code: when none of this applies, `qore dev`
|
|
227
|
+
just passes the child process's own exit code through.
|
|
228
|
+
|
|
229
|
+
#### `qore doctor`
|
|
230
|
+
|
|
231
|
+
Runs the same checks `qore` makes before other commands, on demand, without
|
|
232
|
+
doing anything else — the first thing to run when something's wrong, and
|
|
233
|
+
`--json` makes it attachable to a support ticket:
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
qore doctor
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
- Prints one line per check: `✓` ok, `!` warning, `✗` failing.
|
|
240
|
+
- Checks qore's own version, Node, git, GitHub SSH, Docker + compose, every
|
|
241
|
+
logged-in portal's session, clock skew, and — inside a workspace or module
|
|
242
|
+
repository — each module's port, access and database.
|
|
243
|
+
- **Never fetches the local env and never starts anything.** Ports are read
|
|
244
|
+
from the env file already on disk; access comes from the ordinary
|
|
245
|
+
`GET /api/cli/v1/me` and `.../modules` routes.
|
|
246
|
+
- Exit code `0` only when every check passes; a `!` warning never fails it,
|
|
247
|
+
only a `✗` does.
|
|
248
|
+
- No login profile at all is a warning, not a failure.
|
|
249
|
+
- `--json` shape: success is `{"ok":true,"data":{"results":[...],"context":{...}}}`;
|
|
250
|
+
on failure it's `{"ok":false,"error":{...,"details":{"results":[...],"context":{...}}}}`
|
|
251
|
+
— the full report is always in `details`, even though the top-level error
|
|
252
|
+
names only the first failing check. A check that throws still lets the
|
|
253
|
+
rest run; it just becomes a `fail` with that error attached.
|
|
254
|
+
|
|
143
255
|
#### `.qore.json`
|
|
144
256
|
|
|
145
257
|
An optional file at the repository root that configures `qore env pull`
|
|
146
|
-
for that repository. Schema:
|
|
258
|
+
and `qore dev` for that repository. Schema:
|
|
147
259
|
[`schema/qore.schema.json`](./schema/qore.schema.json).
|
|
148
260
|
|
|
149
261
|
```json
|
|
150
262
|
{
|
|
151
263
|
"$schema": "https://unpkg.com/@quatronic/qore/schema/qore.schema.json",
|
|
152
264
|
"envFile": ".env.local",
|
|
153
|
-
"localSecrets": ["MY_LOCAL_OVERRIDE"]
|
|
265
|
+
"localSecrets": ["MY_LOCAL_OVERRIDE"],
|
|
266
|
+
"dev": {
|
|
267
|
+
"install": true,
|
|
268
|
+
"services": ["db:up"],
|
|
269
|
+
"script": "dev"
|
|
270
|
+
}
|
|
154
271
|
}
|
|
155
272
|
```
|
|
156
273
|
|
|
274
|
+
- **`dev.install`** — run the package manager's install when needed
|
|
275
|
+
(default `true`).
|
|
276
|
+
- **`dev.services`** — package.json scripts to run before the dev server,
|
|
277
|
+
in order (default: `["db:up"]` if that script exists, else none — each
|
|
278
|
+
name must be a real script in package.json).
|
|
279
|
+
- **`dev.script`** — the package.json script `qore dev` runs (default
|
|
280
|
+
`"dev"`).
|
|
281
|
+
|
|
157
282
|
It never holds a portal URL, tenant, app or module id — those are always
|
|
158
283
|
resolved from the git remote or `--module`/`--app`, never committed.
|
|
159
284
|
|
|
@@ -255,6 +380,95 @@ qore sync --pull # ...and fast-forward every clean repository with
|
|
|
255
380
|
no workspace, a conflicting target directory) is an ordinary error
|
|
256
381
|
document without results.
|
|
257
382
|
|
|
383
|
+
#### `qore dev` (workspace root)
|
|
384
|
+
|
|
385
|
+
Run from the directory holding `qore.workspace.json` (not inside one of its
|
|
386
|
+
module repositories — that takes the single-module flow above), starts
|
|
387
|
+
every present module of the app as one supervised run:
|
|
388
|
+
|
|
389
|
+
```sh
|
|
390
|
+
qore dev # every present, writable module, in dependency order
|
|
391
|
+
qore dev --only api,web # just these (comma-separated)
|
|
392
|
+
qore dev --skip docs # all but these
|
|
393
|
+
qore dev --kill-others # the first module to exit stops the rest
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
- Only **present** modules run (cloned into the workspace); one not cloned
|
|
397
|
+
yet is skipped with "not cloned — run `qore sync`". A module without
|
|
398
|
+
write access is skipped too (local secrets need write) naming who to
|
|
399
|
+
ask; its dependents still start, with a warning that the dependency
|
|
400
|
+
they point at (`CONN_*_URL`) won't be running — qore never falls back to
|
|
401
|
+
staging. `--only`/`--skip` select by slug; a skip is never fatal by
|
|
402
|
+
itself, only when it leaves nothing to start.
|
|
403
|
+
- **Every module's install, package-manager check and port check run
|
|
404
|
+
first, for every module, before anything starts.** Problems are
|
|
405
|
+
collected per module and all reported together — one module's install
|
|
406
|
+
failure never hides another's, and nothing starts if any of them failed.
|
|
407
|
+
Installs run with bounded parallelism (4 at a time).
|
|
408
|
+
- **Start order follows `dependsOn`**: providers before their consumers.
|
|
409
|
+
A consumer waits (up to `--wait-timeout`, default `60` s) for each
|
|
410
|
+
dependency's `PORT` to accept connections before its own `dev` script
|
|
411
|
+
runs; a dependency that doesn't come up in time (or that exited) logs a
|
|
412
|
+
warning and the consumer starts anyway. Modules that depend on each
|
|
413
|
+
other (a cycle) start together, without waiting, and the cycle is
|
|
414
|
+
reported once. A backend under `node --watch` that crashes waits for a
|
|
415
|
+
file change instead of exiting, so no exit line appears and its
|
|
416
|
+
consumers wait up to `--wait-timeout`.
|
|
417
|
+
- **`--kill-others`**: the first module to exit on its own (success or
|
|
418
|
+
failure) stops every other module; the others' own exit is then reported
|
|
419
|
+
quietly, not as a failure. Without it, `qore dev` keeps every surviving
|
|
420
|
+
module running until you stop it yourself.
|
|
421
|
+
- **Ctrl+C / SIGTERM/SIGHUP** stops every running module in reverse start
|
|
422
|
+
order, `SIGKILL` after a 10 s grace if one doesn't exit on its own; a
|
|
423
|
+
second Ctrl+C escalates to `SIGKILL` immediately. A `db:up` service a
|
|
424
|
+
module started is left running — `qore dev` only stops the dev servers
|
|
425
|
+
it launched directly. During the checks, Ctrl+C / SIGTERM / SIGHUP
|
|
426
|
+
stops a running install or `db:up` before `qore dev` exits.
|
|
427
|
+
- **Offline start** (same rule as module-level): only when every
|
|
428
|
+
module that would start is already trusted and has an env file on disk;
|
|
429
|
+
one exception fails the whole run with the original error.
|
|
430
|
+
|
|
431
|
+
**Feedback.** Before anything starts, a module not yet cloned is offered as
|
|
432
|
+
`Clone <module>, <module> now with qore sync? [Y/n]` (a TTY only, never
|
|
433
|
+
with `--json`/`--no-input`; `--yes` does not accept it). A module sync
|
|
434
|
+
cannot clone (a conflicting directory, no repository yet, an unsupported
|
|
435
|
+
repository URL, GitHub not linked, no repository access) gets its own
|
|
436
|
+
warning naming the real next step, and no prompt. While preflight runs, one progress line per
|
|
437
|
+
module per step (package manager, install, env, ports) is printed as it
|
|
438
|
+
completes. Once every module with a port is either listening or has been
|
|
439
|
+
reported as not running, a one-line summary follows: `✔ All modules
|
|
440
|
+
running:` (or `✔ Running:` / `! Not running:` when some were skipped) lists
|
|
441
|
+
each module with its URL or its reason. A module that crashes while others
|
|
442
|
+
keep running gets a restart hint
|
|
443
|
+
(`Restart it on its own: qore dev --only <module>`, in another terminal;
|
|
444
|
+
the others keep running); on shutdown, a final
|
|
445
|
+
line lists what stopped cleanly and what crashed, with how long it ran.
|
|
446
|
+
When the failure is about the developer's machine rather than the app
|
|
447
|
+
(Docker, Node version, a port busy with something other than a previous
|
|
448
|
+
`qore dev`, a package manager), the error is followed by a pointer to
|
|
449
|
+
`qore doctor` for a full check.
|
|
450
|
+
|
|
451
|
+
**Output.** Every line of a dev server's own stdout/stderr is prefixed per
|
|
452
|
+
module and column-aligned:
|
|
453
|
+
|
|
454
|
+
```
|
|
455
|
+
api │ listening on 8000
|
|
456
|
+
web │ listening on 3001
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
qore's own start, ready and exit lines (`▶ api (pid 4121)`,
|
|
460
|
+
`✓ api is listening on 8000`, `✗ web exited with code 1`) are not prefixed
|
|
461
|
+
and go to stderr, so `--json`'s stdout stays pure NDJSON. A module's own
|
|
462
|
+
stdout goes to stdout and its stderr to stderr. Colour is disabled with `--json`, `--no-color` or `NO_COLOR`.
|
|
463
|
+
|
|
464
|
+
With `--json` — the only qore command that streams instead of printing one
|
|
465
|
+
document — stdout is newline-delimited JSON, one event object per line
|
|
466
|
+
(`{"type":"start"|"ready"|"line"|"exit"|"warning"|"skip"|...}`), instead of
|
|
467
|
+
the usual single `{"ok":...}` document. An `exit` event carries an optional
|
|
468
|
+
`hint` (`{"hint":"..."}`, the same early-exit hint the human renderer prints
|
|
469
|
+
as ` → ...`) when the module crashed early with a non-zero code and no
|
|
470
|
+
signal — never on a signalled or graceful exit.
|
|
471
|
+
|
|
258
472
|
### `--json`
|
|
259
473
|
|
|
260
474
|
Every command accepts `--json`, which prints exactly one JSON document on
|
|
@@ -271,8 +485,8 @@ qore whoami --json
|
|
|
271
485
|
| Code | Meaning |
|
|
272
486
|
|------|---------|
|
|
273
487
|
| 0 | Success |
|
|
274
|
-
| 1 | Internal error, `git` is not installed or not on `PATH`,
|
|
275
|
-
| 2 | Missing or invalid context (e.g. a bad `--portal` value, or no module selected), or the module is part of several apps and no `--app` was given |
|
|
488
|
+
| 1 | Internal error, `git` is not installed or not on `PATH`, the OS keychain is unavailable/access was denied, or qore itself needs a newer Node (`QORE_NODE_VERSION`, checked by `qore doctor`'s own `node` check against qore's own `process.version`); `qore dev`: its package manager isn't installed (`QORE_PACKAGE_MANAGER`), its install failed (`QORE_INSTALL_FAILED`), Docker is missing (`QORE_DOCKER_MISSING`), not running (`QORE_DOCKER_NOT_RUNNING`) or its compose v2 plugin is missing (`QORE_DOCKER_COMPOSE_MISSING`), a `db:up`/service failed, never became healthy (`QORE_SERVICE_UNHEALTHY`) or its data is incompatible (`QORE_SERVICE_DATA_INCOMPATIBLE`), or the repository's own `engines.node` doesn't match the `node` on `PATH` (`QORE_NODE_VERSION`, a different check from qore's own — `qore doctor`'s `node:<module>` check, run only in a trusted repository) |
|
|
489
|
+
| 2 | Missing or invalid context (e.g. a bad `--portal` value, or no module selected), or the module is part of several apps and no `--app` was given; `qore dev`: a port is already in use (`QORE_PORT_IN_USE`) or the repository's dev setup isn't supported (`QORE_DEV_UNSUPPORTED`) |
|
|
276
490
|
| 4 | Not logged in, session/login expired, login denied, or clock skew |
|
|
277
491
|
| 5 | Access denied — lost tenant access, resource not found, no developer access |
|
|
278
492
|
| 6 | Auth or API temporarily unreachable/unavailable |
|