@quatronic/qore 0.1.1 → 0.1.3
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 +277 -4
- package/dist/cli.js +3955 -679
- 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
|
|
|
@@ -176,6 +301,154 @@ If a repository resolves to no module on any portal you're logged in to,
|
|
|
176
301
|
the error lists the portals it tried and, when a staging repository looks
|
|
177
302
|
likely, suggests `qore login --portal staging`.
|
|
178
303
|
|
|
304
|
+
### Workspaces
|
|
305
|
+
|
|
306
|
+
`qore clone` sets up a local directory holding one or more of an app's
|
|
307
|
+
modules, each in its own repository; `qore sync` keeps that directory in
|
|
308
|
+
step with the app afterwards.
|
|
309
|
+
|
|
310
|
+
```sh
|
|
311
|
+
qore clone <app> # clones every module of <app> into ./<app-slug>
|
|
312
|
+
qore clone <app> <dir> # ...into <dir> instead
|
|
313
|
+
cd <app-slug>
|
|
314
|
+
qore status # app-level table, one row per module/environment
|
|
315
|
+
cd <module>
|
|
316
|
+
qore env pull # no trust prompt — a clone made by qore is already trusted
|
|
317
|
+
cd ..
|
|
318
|
+
qore sync # clone any module that's new, fetch the rest
|
|
319
|
+
qore sync --pull # ...and fast-forward every clean repository with an upstream
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
- **`--protocol https|ssh`** picks the clone URL (default `https`). `ssh`
|
|
323
|
+
rewrites `https://github.com/o/r` to `git@github.com:o/r.git`; your own
|
|
324
|
+
git `insteadOf` config still applies on top of either.
|
|
325
|
+
- **`--tenant <slug>`** on `qore clone` disambiguates an app slug that
|
|
326
|
+
exists in more than one tenant you have access to.
|
|
327
|
+
- **The workspace file** is `qore.workspace.json` at the directory `qore
|
|
328
|
+
clone` created, holding `{"schemaVersion": 1, "portal": "<apiOrigin>",
|
|
329
|
+
"appId": "<uuid>"}`. It never lists modules — those always come from the
|
|
330
|
+
portal, so a module added to the app later shows up on the next `qore
|
|
331
|
+
sync` with no local change needed. `qore` finds it by walking up from the
|
|
332
|
+
current directory (stopping at your home directory). The module commands
|
|
333
|
+
(`env pull`, `run`, …), `qore status` and `qore sync` pick up its portal
|
|
334
|
+
(and, for a module shared by several apps, its app) automatically when run
|
|
335
|
+
from inside the workspace — including from a module's own repository;
|
|
336
|
+
`qore clone`, `qore whoami` and `qore tenant` do not read it. An explicit
|
|
337
|
+
`--portal` flag always wins over it. `qore status` shows the app-level
|
|
338
|
+
table only when run in the directory that holds `qore.workspace.json`
|
|
339
|
+
itself (not inside one of its module repositories, which shows that
|
|
340
|
+
module).
|
|
341
|
+
- **Nothing is ever deleted, moved or overwritten.** A present module
|
|
342
|
+
directory is left exactly as it is; `sync` only fetches into it, and
|
|
343
|
+
only fast-forwards with `--pull`, and only when the tree is clean and has
|
|
344
|
+
an upstream — a dirty tree, one with no upstream, or one that would need
|
|
345
|
+
a merge is reported, never touched. `sync` runs a plain `git fetch` (no
|
|
346
|
+
`--prune`). A module's directory whose `origin` doesn't point at that
|
|
347
|
+
module's repository is reported as re-pointed, and one that isn't a git
|
|
348
|
+
repository at all is reported too — neither is repaired; move it aside
|
|
349
|
+
and run `qore sync` again. A repository directory in the workspace that
|
|
350
|
+
isn't one of the app's modules (typically a module that left the app) is
|
|
351
|
+
reported as removed, and left on disk.
|
|
352
|
+
- **A target directory that already holds something else** — someone
|
|
353
|
+
else's files, or another app's or portal's workspace — makes `qore
|
|
354
|
+
clone` fail with `QORE_WORKSPACE_CONFLICT` before writing anything,
|
|
355
|
+
including `qore.workspace.json`. Pick a different directory
|
|
356
|
+
(`qore clone <app> <dir>`).
|
|
357
|
+
- **Per-module clone failures** are diagnosed individually and don't stop
|
|
358
|
+
the rest of the run:
|
|
359
|
+
- `QORE_GITHUB_NOT_LINKED` — your GitHub account isn't linked in Qore;
|
|
360
|
+
link it at the account page, then `qore sync`.
|
|
361
|
+
- `QORE_REPO_NO_ACCESS` — you have no seat on the repository; the error
|
|
362
|
+
names the dev team(s) that build the module and their maintainers to ask.
|
|
363
|
+
- `QORE_REPO_ACCESS_PENDING` — access was granted recently, or your
|
|
364
|
+
GitHub organisation invitation isn't accepted yet; the error links the
|
|
365
|
+
invitation page.
|
|
366
|
+
- `QORE_GIT_AUTH` — git itself couldn't authenticate (no SSH key,
|
|
367
|
+
GitHub's SSH host key not accepted yet, no `gh auth login`, or SSO
|
|
368
|
+
authorisation needed for the org); the error names the fix.
|
|
369
|
+
|
|
370
|
+
`clone`/`sync` exits `0` when at least one module cloned or was already
|
|
371
|
+
present and every failure is one of the first three above
|
|
372
|
+
(`QORE_GITHUB_NOT_LINKED`, `QORE_REPO_NO_ACCESS`,
|
|
373
|
+
`QORE_REPO_ACCESS_PENDING`) — so a developer missing a seat on one module
|
|
374
|
+
of a multi-module app still gets a usable workspace for the rest.
|
|
375
|
+
`QORE_GIT_AUTH` is not an access failure in this sense: it fails the run
|
|
376
|
+
(exit `5`). Any other failure, or a run where nothing succeeded at all,
|
|
377
|
+
exits with that failure's own code. With `--json`, once the per-module
|
|
378
|
+
loop has run its results are under `data.results` (or
|
|
379
|
+
`error.details.results` on failure); an error before that (not logged in,
|
|
380
|
+
no workspace, a conflicting target directory) is an ordinary error
|
|
381
|
+
document without results.
|
|
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
|
+
**Output.** Every line of a dev server's own stdout/stderr is prefixed per
|
|
432
|
+
module and column-aligned:
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
api │ listening on 8000
|
|
436
|
+
web │ listening on 3001
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
qore's own start, ready and exit lines (`▶ api (pid 4121)`,
|
|
440
|
+
`✓ api is listening on 8000`, `✗ web exited with code 1`) are not prefixed
|
|
441
|
+
and go to stderr, so `--json`'s stdout stays pure NDJSON. A module's own
|
|
442
|
+
stdout goes to stdout and its stderr to stderr. Colour is disabled with `--json`, `--no-color` or `NO_COLOR`.
|
|
443
|
+
|
|
444
|
+
With `--json` — the only qore command that streams instead of printing one
|
|
445
|
+
document — stdout is newline-delimited JSON, one event object per line
|
|
446
|
+
(`{"type":"start"|"ready"|"line"|"exit"|"warning"|"skip"|...}`), instead of
|
|
447
|
+
the usual single `{"ok":...}` document. An `exit` event carries an optional
|
|
448
|
+
`hint` (`{"hint":"..."}`, the same early-exit hint the human renderer prints
|
|
449
|
+
as ` → ...`) when the module crashed early with a non-zero code and no
|
|
450
|
+
signal — never on a signalled or graceful exit.
|
|
451
|
+
|
|
179
452
|
### `--json`
|
|
180
453
|
|
|
181
454
|
Every command accepts `--json`, which prints exactly one JSON document on
|
|
@@ -192,8 +465,8 @@ qore whoami --json
|
|
|
192
465
|
| Code | Meaning |
|
|
193
466
|
|------|---------|
|
|
194
467
|
| 0 | Success |
|
|
195
|
-
| 1 | Internal error, `git` is not installed or not on `PATH`,
|
|
196
|
-
| 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 |
|
|
468
|
+
| 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) |
|
|
469
|
+
| 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`) |
|
|
197
470
|
| 4 | Not logged in, session/login expired, login denied, or clock skew |
|
|
198
471
|
| 5 | Access denied — lost tenant access, resource not found, no developer access |
|
|
199
472
|
| 6 | Auth or API temporarily unreachable/unavailable |
|