@warlock.js/core 4.12.0 → 4.13.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/CHANGELOG.md +116 -0
- package/esm/cli/cli-commands.manager.mjs.map +1 -1
- package/esm/cli/commands/add.command.mjs +1 -1
- package/esm/cli/commands/add.command.mjs.map +1 -1
- package/esm/cli/commands/build.command.mjs +1 -1
- package/esm/cli/commands/build.command.mjs.map +1 -1
- package/esm/cli/commands/create-database.command.mjs +1 -1
- package/esm/cli/commands/create-database.command.mjs.map +1 -1
- package/esm/cli/commands/dev-server.command.mjs +1 -1
- package/esm/cli/commands/dev-server.command.mjs.map +1 -1
- package/esm/cli/commands/doctor/doctor-command.action.mjs.map +1 -1
- package/esm/cli/commands/doctor.command.mjs +1 -1
- package/esm/cli/commands/doctor.command.mjs.map +1 -1
- package/esm/cli/commands/drop-tables.command.mjs +1 -1
- package/esm/cli/commands/drop-tables.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generate.command.mjs +1 -1
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs +1 -1
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -1
- package/esm/cli/commands/generate/utils/path-resolver.mjs +1 -1
- package/esm/cli/commands/generate/utils/writer.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs +1 -1
- package/esm/cli/commands/migrate.command.mjs.map +1 -1
- package/esm/cli/commands/routes/routes-command.action.mjs.map +1 -1
- package/esm/cli/commands/routes.command.mjs +1 -1
- package/esm/cli/commands/routes.command.mjs.map +1 -1
- package/esm/cli/commands/seed.command.mjs +1 -1
- package/esm/cli/commands/seed.command.mjs.map +1 -1
- package/esm/cli/commands/start-production.command.mjs +1 -1
- package/esm/cli/commands/start-production.command.mjs.map +1 -1
- package/esm/cli/commands/storage-put.action.mjs.map +1 -1
- package/esm/cli/commands/storage-put.command.mjs +1 -1
- package/esm/cli/commands/storage-put.command.mjs.map +1 -1
- package/esm/cli/commands/typings-generator.command.mjs +2 -2
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -1
- package/esm/cli/commands/update.command.mjs +1 -1
- package/esm/cli/commands/update.command.mjs.map +1 -1
- package/esm/cli/commands-loader.mjs +1 -1
- package/esm/cli/commands-loader.mjs.map +1 -1
- package/esm/{cli → commands}/cli-command.d.mts +1 -1
- package/esm/commands/cli-command.d.mts.map +1 -0
- package/esm/{cli → commands}/cli-command.mjs +1 -1
- package/esm/commands/cli-command.mjs.map +1 -0
- package/esm/{cli → commands}/types.d.mts +1 -1
- package/esm/commands/types.d.mts.map +1 -0
- package/esm/config/load-config-files.mjs +1 -1
- package/esm/config/load-config-files.mjs.map +1 -1
- package/esm/connectors/base-connector.mjs +1 -1
- package/esm/connectors/base-connector.mjs.map +1 -1
- package/esm/connectors/connectors-manager.d.mts.map +1 -1
- package/esm/connectors/connectors-manager.mjs +5 -3
- package/esm/connectors/connectors-manager.mjs.map +1 -1
- package/esm/connectors/http-connector.d.mts.map +1 -1
- package/esm/connectors/http-connector.mjs +0 -2
- package/esm/connectors/http-connector.mjs.map +1 -1
- package/esm/database/create-database-action.mjs.map +1 -1
- package/esm/database/drop-tables-action.mjs.map +1 -1
- package/esm/database/migrate-action.mjs +1 -1
- package/esm/database/migrate-action.mjs.map +1 -1
- package/esm/database/seed-command-action.mjs +1 -1
- package/esm/database/seed-command-action.mjs.map +1 -1
- package/esm/dev-server/dev-logger.mjs +1 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +2 -2
- package/esm/dev-server/file-event-handler.mjs +1 -1
- package/esm/dev-server/file-event-handler.mjs.map +1 -1
- package/esm/dev-server/file-manager.mjs +1 -1
- package/esm/dev-server/file-manager.mjs.map +1 -1
- package/esm/dev-server/file-operations.mjs +1 -1
- package/esm/dev-server/file-operations.mjs.map +1 -1
- package/esm/dev-server/files-orchestrator.mjs +3 -18
- package/esm/dev-server/files-orchestrator.mjs.map +1 -1
- package/esm/dev-server/files-watcher.mjs +1 -1
- package/esm/dev-server/files-watcher.mjs.map +1 -1
- package/esm/dev-server/health-checker/file-health-checker.contract.d.mts +1 -1
- package/esm/dev-server/layer-executor.mjs +1 -1
- package/esm/dev-server/loader/register-loader.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs +1 -1
- package/esm/dev-server/package-json-manager.mjs.map +1 -1
- package/esm/dev-server/parse-imports.mjs +2 -2
- package/esm/dev-server/parse-imports.mjs.map +1 -1
- package/esm/dev-server/tsconfig-manager.mjs +1 -1
- package/esm/dev-server/tsconfig-manager.mjs.map +1 -1
- package/esm/dev-server/type-generator.mjs +1 -1
- package/esm/dev-server/type-generator.mjs.map +1 -1
- package/esm/dev-server/utils.mjs +1 -1
- package/esm/dev-server/utils.mjs.map +1 -1
- package/esm/generations/add-command.action.mjs +3 -3
- package/esm/generations/add-command.action.mjs.map +1 -1
- package/esm/http/build-cors-options.mjs +32 -0
- package/esm/http/build-cors-options.mjs.map +1 -0
- package/esm/http/middleware/max-body-size.middleware.d.mts +13 -5
- package/esm/http/middleware/max-body-size.middleware.d.mts.map +1 -1
- package/esm/http/middleware/max-body-size.middleware.mjs +13 -5
- package/esm/http/middleware/max-body-size.middleware.mjs.map +1 -1
- package/esm/http/plugins.d.mts.map +1 -1
- package/esm/http/plugins.mjs +2 -9
- package/esm/http/plugins.mjs.map +1 -1
- package/esm/http/server.d.mts.map +1 -1
- package/esm/http/server.mjs +3 -8
- package/esm/http/server.mjs.map +1 -1
- package/esm/index.d.mts +9 -17
- package/esm/index.mjs +4 -15
- package/esm/production/production-builder.mjs +51 -3
- package/esm/production/production-builder.mjs.map +1 -1
- package/esm/production/resolve-build-config.mjs +1 -1
- package/esm/production/resolve-build-config.mjs.map +1 -1
- package/esm/router/router.d.mts +9 -0
- package/esm/router/router.d.mts.map +1 -1
- package/esm/router/router.mjs +58 -5
- package/esm/router/router.mjs.map +1 -1
- package/esm/router/types.d.mts +24 -1
- package/esm/router/types.d.mts.map +1 -1
- package/esm/tests/index.d.mts +2 -2
- package/esm/tests/index.mjs +1 -2
- package/esm/tests/start-http-development-server.d.mts.map +1 -1
- package/esm/tests/start-http-development-server.mjs +38 -15
- package/esm/tests/start-http-development-server.mjs.map +1 -1
- package/esm/tests/test-helpers.d.mts.map +1 -1
- package/esm/tests/test-helpers.mjs +34 -13
- package/esm/tests/test-helpers.mjs.map +1 -1
- package/esm/tests/vitest-setup.d.mts +1 -1
- package/esm/tests/vitest-setup.d.mts.map +1 -1
- package/esm/tests/vitest-setup.mjs +3 -3
- package/esm/tests/vitest-setup.mjs.map +1 -1
- package/esm/utils/framework-vesion.mjs +1 -1
- package/esm/utils/index.d.mts +1 -0
- package/esm/utils/index.mjs +1 -0
- package/esm/utils/normalized-path.d.mts +55 -0
- package/esm/utils/normalized-path.d.mts.map +1 -0
- package/esm/{dev-server/path.mjs → utils/normalized-path.mjs} +15 -2
- package/esm/utils/normalized-path.mjs.map +1 -0
- package/esm/vite/index.d.mts +2 -0
- package/esm/vite/index.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.d.mts +1 -1
- package/esm/vite/lower-stage3-decorators.mjs +1 -1
- package/esm/vite/lower-stage3-decorators.mjs.map +1 -1
- package/esm/warlock-config/index.mjs +1 -1
- package/esm/warlock-config/types.d.mts +34 -1
- package/esm/warlock-config/types.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.d.mts.map +1 -1
- package/esm/warlock-config/warlock-config.manager.mjs +1 -2
- package/esm/warlock-config/warlock-config.manager.mjs.map +1 -1
- package/llms-full.txt +78 -23
- package/llms.txt +3 -3
- package/package.json +24 -12
- package/skills/lower-stage3-decorators/SKILL.md +4 -2
- package/skills/run-app/SKILL.md +6 -0
- package/skills/test-http/SKILL.md +29 -10
- package/skills/test-service/SKILL.md +12 -8
- package/skills/use-middleware/SKILL.md +5 -1
- package/esm/cli/cli-command.d.mts.map +0 -1
- package/esm/cli/cli-command.mjs.map +0 -1
- package/esm/cli/types.d.mts.map +0 -1
- package/esm/dev-server/files-orchestrator.d.mts +0 -89
- package/esm/dev-server/files-orchestrator.d.mts.map +0 -1
- package/esm/dev-server/files-watcher.d.mts +0 -69
- package/esm/dev-server/files-watcher.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts +0 -55
- package/esm/dev-server/health-checker/checkers/base-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts +0 -44
- package/esm/dev-server/health-checker/checkers/eslint-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts +0 -60
- package/esm/dev-server/health-checker/checkers/typescript-health-checker.d.mts.map +0 -1
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts +0 -119
- package/esm/dev-server/health-checker/files-healthcare.manager.d.mts.map +0 -1
- package/esm/dev-server/health-checker/index.mjs +0 -7
- package/esm/dev-server/module-loader.d.mts +0 -63
- package/esm/dev-server/module-loader.d.mts.map +0 -1
- package/esm/dev-server/path.mjs.map +0 -1
- package/esm/tests/test-server-port-channel.d.mts +0 -27
- package/esm/tests/test-server-port-channel.d.mts.map +0 -1
- /package/esm/{cli → commands}/index.d.mts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,122 @@ All notable changes to `@warlock.js/core` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
> ⚠ **Versioning: `@warlock.js/*` does not follow SemVer strictly — breaking changes may ship in a minor.** This is a deliberate decision, not an oversight: the framework is pre-adoption and the cost of a major per behaviour fix currently outweighs the benefit. **Pin an exact version or a tilde range (`~4.13.0`) if you need to opt into changes rather than receive them.** Every breaking change is marked **BREAKING** in its entry and summarised in an *Upgrading* section at the top of the release. **This policy will change once the framework has consumers beyond its author.**
|
|
8
|
+
|
|
9
|
+
## 4.13.0
|
|
10
|
+
|
|
11
|
+
### ⚠ Upgrading from 4.12.0 — read this first
|
|
12
|
+
|
|
13
|
+
**Four breaking changes. Every one of them fails visibly, and every one is fixed by a single line or a single config key.** Three are security defaults that were wrong; the fourth is an import path.
|
|
14
|
+
|
|
15
|
+
| What breaks | How you'll see it | The fix |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| **`http.cors` now actually applies** — it never had any effect in any release through 4.12.0 | requests from origins you never allow-listed start being rejected | check `http.cors` before upgrading; it now means what it says |
|
|
18
|
+
| **`http.bodyLimit` defaults to Fastify's 1 MB**, not 200 GB | large uploads that used to be accepted answer `413` | set `http.bodyLimit` explicitly if you need more |
|
|
19
|
+
| **`http.trustProxy` defaults to `false`** | `request.ip` becomes the socket address instead of `X-Forwarded-For` | set `http.trustProxy: true` **only if** you are genuinely behind a proxy that strips the header |
|
|
20
|
+
| **The package entry no longer re-exports the CLI, dev server, test helpers or Vite integration** | build fails with `has no exported member` | test helpers move to `@warlock.js/core/tests`, `lowerStage3Decorators` to `@warlock.js/core/vite` — **one line per import.** The CLI and dev-server internals are **not** public and have no replacement specifier |
|
|
21
|
+
|
|
22
|
+
**If your app configures none of the three HTTP keys, the first three changes make it strictly safer with no action from you.** The `trustProxy` default in particular meant per-IP rate limiting was bypassable by anyone sending their own `X-Forwarded-For`.
|
|
23
|
+
|
|
24
|
+
Details for each are in the entries below.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`build.singleBundle` — one file you can run with `node dist/app.js`.** The default build keeps dependencies as real `import` specifiers resolved from `node_modules`, which is right when you deploy the folder. Producing a single self-contained file previously meant knowing to set `packages: "bundle"` **and** `splitting: false`, and it still did not work
|
|
29
|
+
|
|
30
|
+
It sets both as **defaults you can override** — an explicit `splitting: true` in your config still wins. Phase ordering is unaffected: it comes from the generated entry using dynamic `await import(...)`, which the bundler defers to the call site whether or not splitting is on. A comment in the builder claiming ordering "requires `splitting: true`" was wrong and has been corrected
|
|
31
|
+
|
|
32
|
+
⚠ Native `.node` addons cannot be inlined by any bundler and are still emitted alongside the file, so "single bundle" means one JavaScript file plus any native addons — the flag does not promise otherwise
|
|
33
|
+
|
|
34
|
+
**Verified end to end on a real application**, not in isolation: the build emits exactly one file, the interop prelude is present, and the result boots and serves a request. This only became possible once dev-only tooling left the production module graph — see the entry below, which is what made `singleBundle` build a real application rather than only a synthetic one
|
|
35
|
+
|
|
36
|
+
- **`@warlock.js/core/tests` and `@warlock.js/core/vite` are real subpaths**, with their own build entries and `exports` keys — the first version in which those helpers are addressable at all. `/tests` carries the 13 documented test helpers; `/vite` carries `lowerStage3Decorators`
|
|
37
|
+
|
|
38
|
+
**The test server's port channel — `publishTestServerPort`, `withdrawTestServerPort`, `TEST_SERVER_PORT_ENV_KEY` — is deliberately NOT part of that surface.** It is how `startHttpTestServer` hands the resolved port to the worker processes that later call `testGet`; it is plumbing, and nothing outside the framework needs it. **Decided before the subpath shipped rather than after, because removing an export once people depend on it is a breaking change**
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **`setupTest()` no longer crashes in a project that has no `src/config/tests.ts`.** `config.get("tests")` resolves an absent key to `null`, and the result was dereferenced — so the very path `warlock add test` generates threw `Cannot read properties of null (reading 'connectors')` before running a single test. **`setupTest()` with no arguments at all** threw one step earlier still, on a destructured parameter with no default
|
|
43
|
+
|
|
44
|
+
**`connectors: false` now starts no connectors, as its type and the skill have always said.** The selection read `testConfig.connectors || connectors`, so a configured `false` was discarded, and every non-array value then took the "start all but http" branch — **`false` started everything except http, from config and from the caller alike.** It is now `??` with an explicit `false` check
|
|
45
|
+
|
|
46
|
+
⚠ **Config still beats the parameter:** if `tests.connectors` is set, `setupTest({ connectors })` cannot override it. That is the existing contract and this release does not change it — one line of `skills/test-service` claimed otherwise and has been corrected. **A per-call override is a contract decision for a later release**
|
|
47
|
+
|
|
48
|
+
- **The request helpers send falsy JSON bodies.** `testPost`, `testPut` and `testPatch` used `body ? JSON.stringify(body) : undefined`, so **`false`, `0`, `""` and `null` — all legal JSON documents — were sent as no body at all.** Only an omitted argument now means "no body"
|
|
49
|
+
|
|
50
|
+
**Headers are accepted in every shape `RequestInit` allows.** They were merged by object spread, which is correct only for a plain record — a `Headers` instance or a `[name, value]` tuple list was silently turned into an object with numeric keys and the header was lost with no error. They are now normalised through `new Headers(...)`
|
|
51
|
+
|
|
52
|
+
**`Content-Type: application/json` is set only when the helper serialized the body**, and never over a content type the caller set. It was previously forced onto every request, including `FormData` — replacing the multipart boundary the runtime generates and producing a request no server can parse
|
|
53
|
+
|
|
54
|
+
- **Shutdown survives a throwing log channel.** A connector whose `shutdown()` failed was reported through `log.error(...)` **from inside the catch block** — and `Logger.log()` hands each entry to `channel.log()` with no isolation, so a channel that throws synchronously (a misconfigured transport, an unserialisable payload) made that report reject. The rejection escaped `shutdown()` entirely, and the consequences went well past a missing log line: **`log.flush()` never ran, so every buffered entry from the whole run was lost; the remaining connectors were never torn down; and `process.exit(0)` — the line `gracefulShutdown` runs once `shutdown()` resolves — was never reached, leaving the process alive on the handles those connectors still held**
|
|
55
|
+
|
|
56
|
+
⚠ **This hardens the shutdown path, not the logger.** `Logger.log()` still aborts its fan-out on the first throwing channel, so the other channels never receive that entry, and an *asynchronously* rejecting channel is not covered at all — `channel.log()` is never awaited. **Logger-wide isolation is a separate fix in a later release**
|
|
57
|
+
|
|
58
|
+
- **A test server that fails to start no longer leaves half of itself running.** `startHttpTestServer()` publishes the resolved port before the late connector phase and sets `isServerRunning` only on its last line, so a failure in between left **live early-phase connectors and a published port pointing at a server that never came up** — while `stopHttpTestServer()` in `globalTeardown` reported *"No server to stop"* and walked away from them. Startup now unwinds what it started, always withdraws the port and resets its state. ⚠ **The error you get back is unchanged — it always was.** Startup had no `catch` at all, so the original failure already propagated correctly; what was missing was the cleanup, and the new `catch` exists only to run it. A failure *during* that cleanup is reported and never substituted for the cause, which is the one propagation guarantee the wrapper had to be careful not to break
|
|
59
|
+
|
|
60
|
+
**`stopHttpTestServer()` withdraws the port and resets state in a `finally`.** They previously ran after the `await`, so a shutdown that threw left the published port behind and the next run in the same process inherited it
|
|
61
|
+
|
|
62
|
+
- **`startHttpTestServer({ port: 0 })` is refused with an instruction instead of half-working.** `0` is the OS's "pick a free one" idiom, and the test server cannot honour it: the preflight would bind some unrelated ephemeral port and pass without proving anything, and nothing publishable exists afterwards — `HttpConnector.start()` records the port it **asked for**, not the one Fastify bound. Accepting it silently meant `getTestServerUrl()` resolved `0` through its own config fallback and **every worker request went to `http://host:0`**, where nothing listens. The error names the fix: pass an explicit port, or set `http.port`
|
|
63
|
+
|
|
64
|
+
- ⚠ **BREAKING — the package entry no longer re-exports the CLI, the dev server, the test helpers or the Vite integration.** Five `export *` lines are gone from `@warlock.js/core`'s root: `./cli`, `./dev-server/files-orchestrator`, `./dev-server/health-checker`, `./tests` and `./vite`
|
|
65
|
+
|
|
66
|
+
**Why it had to change:** those five put dev-only tooling into the **static module graph of every application that imports the framework** — 39 files, reaching ESLint and, through it, ESLint's optional `jiti` import. It cost nothing while the builder kept `packages: "external"`, because esbuild never walked into the framework. **Anything that bundles walks it, and the build fails.** That is why `singleBundle` could not build a single real application
|
|
67
|
+
|
|
68
|
+
⚠ **Making those imports lazy does not help and should not be attempted.** esbuild resolves `import()` at build time; a dynamic import defers *evaluation*, not resolution. Measured: `await import("jiti")` in an otherwise empty file still fails with `Could not resolve "jiti"`. **Only unreachability from the entry removes a module from the graph**
|
|
69
|
+
|
|
70
|
+
**The two public halves are now reachable from a subpath — and in 4.12.0 and earlier they were reachable from nowhere at all.** `./tests` and `./vite` are **real build entries with their own emitted files and `exports` entries**, not reachable from the root barrel, which is the entire point:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// before // after
|
|
74
|
+
import { setupTest } from "@warlock.js/core"; import { setupTest } from "@warlock.js/core/tests";
|
|
75
|
+
import { startHttpTestServer } from "@warlock.js/core";
|
|
76
|
+
import { startHttpTestServer } from "@warlock.js/core/tests";
|
|
77
|
+
import { lowerStage3Decorators } from "@warlock.js/core";
|
|
78
|
+
import { lowerStage3Decorators } from "@warlock.js/core/vite";
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**The CLI and dev-server internals are different — they were never a public API and have no replacement specifier.** If you were importing from those, you were reaching into framework internals; open an issue describing what you needed.
|
|
82
|
+
|
|
83
|
+
⚠ **If you were importing these symbols in 4.12.0, they were already not in the published package.** `esm/tests/` and `esm/vite/` did not exist in the 4.12.0 artifact, and none of those symbols appear in `esm/index.d.mts` — **the root barrel re-exported source the build never emitted.** So this is the release that makes them installable, not the one that removed them.
|
|
84
|
+
|
|
85
|
+
`Path` is unaffected — it moved to a genuine utility module and remains exported
|
|
86
|
+
|
|
87
|
+
**Removing the five lines was necessary but not sufficient.** Three production modules — `connectors/http-connector`, `connectors/connectors-manager` and `warlock-config/warlock-config.manager` — imported the dev server's console formatter directly. In `http-connector` the dev-console call was simply **deleted**: the same error was already routed through `log.fatal` on the following line. `connectors-manager` now logs a connector's shutdown failure through `@warlock.js/logger`, **awaited and then flushed** — `process.exit(0)` follows immediately, so an un-awaited log is a log that never happens. `warlock-config.manager`'s *"`warlock.config.ts` is missing"* warning writes **straight to the console instead**: it runs during CLI bootstrap, before the logger has a single channel configured, so routing it through the logger would drop it in every application
|
|
88
|
+
|
|
89
|
+
`tests/unit/meta/production-entry-graph.test.ts` enforces this from now on. It keys on **our own directory names** rather than a denylist of third-party packages, because a denylist rots the moment a dependency changes its imports and can only catch names someone thought of
|
|
90
|
+
|
|
91
|
+
- ⚠ **BREAKING — the CORS allow-list in `http.cors` now actually applies.** The framework's defaults were spread **after** your configuration, so `{ origin: "*", methods: "*" }` overwrote whatever you set. **`http.cors` has never had any effect**, in any release up to 4.12.0 — an app that configured an allow-list still answered every origin. Your configuration now wins
|
|
92
|
+
|
|
93
|
+
**This is breaking in the direction you want, but it is breaking:** an app that has been relying on the accidental `origin: "*"` while believing it was restricted will now restrict. Check your `http.cors` before upgrading
|
|
94
|
+
|
|
95
|
+
Worth knowing why the old behaviour was also broken for the legitimate case: `Access-Control-Allow-Origin: *` together with `Access-Control-Allow-Credentials: true` is a pair **browsers reject**, so a cross-origin `fetch(…, { credentials: "include" })` failed outright. The default was simultaneously too open for unauthenticated reads and unusable for authenticated ones
|
|
96
|
+
|
|
97
|
+
- ⚠ **BREAKING — `http.bodyLimit` no longer defaults to 200 GB.** An app that configures nothing now gets **Fastify's own 1 MB limit**. The previous default did not merely allow large bodies, it **replaced a protection Fastify provides**: an unauthenticated endpoint accepted a 5 MB body and ran application logic on it where bare Fastify would have answered `413`
|
|
98
|
+
|
|
99
|
+
**If you accept uploads, set `http.bodyLimit` before upgrading** — otherwise those requests start failing with `413`. The failure is immediate and one config key fixes it
|
|
100
|
+
|
|
101
|
+
The old comment recommended `middleware.maxBodySize()` for per-route caps. **That middleware cannot do this job** and its documentation has been corrected: framework middleware runs inside the route handler, after `@fastify/multipart` has already parsed the body, so it can report `Content-Length` but cannot refuse the read. For a real per-route cap use `serverOptions.bodyLimit`, which Fastify enforces while reading
|
|
102
|
+
|
|
103
|
+
- ⚠ **BREAKING — `http.trustProxy` now defaults to `false`.** `request.ip` was derived from the client-supplied `X-Forwarded-For` header by default, and `@fastify/rate-limit` keys its buckets on `request.ip` — so **a client sending a different `X-Forwarded-For` on each request got a fresh rate-limit bucket every time.** Any deployment not behind a proxy that strips the header had bypassable rate limiting, and the same applied to per-IP lockouts and audit logs
|
|
104
|
+
|
|
105
|
+
**If your app is behind a proxy, set `http.trustProxy = true`.** Until you do, `request.ip` reports the proxy's address rather than the client's — a visible, diagnosable change, unlike the previous default, which could not be detected from inside the application
|
|
106
|
+
|
|
107
|
+
- **Per-route `serverOptions` are no longer discarded by the dev server.** `scanDevServer` registers wildcard routes and dispatches per request, so it had no per-route registration slot and dropped `serverOptions` entirely. A route declaring `serverOptions.onRequest` — the documented way to run **before body parsing** — worked in production and **silently never ran in dev**, which is the only mode most teams run. `route.rateLimit` was dropped the same way, since it rides in the same options object
|
|
108
|
+
|
|
109
|
+
Matching now happens in Fastify's own `onRequest` phase and the matched route's hooks run there, so `onRequest`, `preValidation` and `preHandler` are forwarded with their pre-parse ordering intact
|
|
110
|
+
|
|
111
|
+
⚠ **Two options still cannot be forwarded in dev, and `serverOptions`' documentation now says so rather than implying otherwise:** `bodyLimit`, because Fastify reads it at registration time and no hook can bound a body already being parsed; and `preParsing`, because it must return the payload stream. For a pre-parse guarantee that holds in both modes, register a server-level hook via `router.beforeScanning` — it must be idempotent per request, since a connector restart builds a new server and runs the callback again
|
|
112
|
+
|
|
113
|
+
- **The dev server no longer rebuilds its entire route registry on every request.** A comment claimed the registry was initialised "once" and pointed at a `rebuildRouteRegistry` function that **does not exist**; the code sat inside the per-request handler, re-registering every route on every hit — and `router.any()` routes expand into seven registrations each. It is now built once and rebuilt when the route table changes
|
|
114
|
+
|
|
115
|
+
- **The dev dispatcher logs through the framework logger instead of `console.log(error)`,** with the request method and url attached
|
|
116
|
+
|
|
117
|
+
- **A bundled production build no longer succeeds and then dies at startup.** Setting `packages: "bundle"` produced a clean build whose process failed immediately with `Error: Dynamic require of "node:assert" is not supported`. Warlock's output is an ES module; bundled CommonJS dependencies call `require(...)` and read `__dirname` to locate their own assets, and neither exists in an ES module, so the bundler substituted a stub that throws. The only fix available to an application author was to hand-write an esbuild `banner` recreating `require` via `createRequire(import.meta.url)` — esbuild internals no app should need to know
|
|
118
|
+
|
|
119
|
+
The interop prelude is now injected automatically for **any** ESM build (`build.esmShim`, default `true`), not only under `singleBundle` — so a hand-written `packages: "bundle"` works too. It defines `__filename` and `__dirname` as well as `require`: a dependency resolving an asset path with a missing `__dirname` gets `undefined` and fails later, further from the cause
|
|
120
|
+
|
|
121
|
+
**An existing hand-written `banner` is preserved.** esbuild's `banner` is an object and every merge on the way to it replaces rather than merges, so previously whichever was applied second silently deleted the other. The shim is prepended to your banner instead
|
|
122
|
+
|
|
7
123
|
## 4.12.0
|
|
8
124
|
|
|
9
125
|
### Added
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-commands.manager.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { bootstrap } from \"../bootstrap\";\r\nimport { loadConfigFiles } from \"../config/load-config-files\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { manifestManager } from \"../manifest/manifest-manager\";\r\nimport { appPath } from \"../utils\";\r\nimport { loadEnvironmentFiles } from \"../utils/load-environment\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { CLICommand } from \"./cli-command\";\r\nimport {\r\n displayBootError,\r\n displayCommandError,\r\n displayCommandHelp,\r\n displayCommandNotFound,\r\n displayCommandSuccess,\r\n displayExecutingCommand,\r\n displayHelp,\r\n displayMissingCommand,\r\n displayMissingOptions,\r\n displayWarlockVersionInTerminal,\r\n type HelpCommandInfo,\r\n isMatchingCommandName,\r\n} from \"./cli-commands.utils\";\r\nimport { cliCommandsLoader } from \"./commands-loader\";\r\nimport { frameworkCommands } from \"./framework-cli-commands\";\r\nimport { CliOptionValueError, ParsedCliArgs, parseCliArgs } from \"./parse-cli-args\";\r\nimport { findSimilar } from \"./string-similarity\";\r\nimport { CommandActionData, ResolvedCLICommandOption } from \"./types\";\r\n\r\nexport class CLICommandsManager {\r\n /**\r\n * List of commands\r\n */\r\n public commands: CLICommand[] = [];\r\n\r\n /**\r\n * Commands map (name -> command)\r\n */\r\n public commandsMap: Map<string, CLICommand> = new Map();\r\n\r\n /**\r\n * Alias map (alias -> command name)\r\n */\r\n public aliasMap: Map<string, string> = new Map();\r\n\r\n /**\r\n * Register the given commands\r\n */\r\n public register(...commands: CLICommand[]): this {\r\n this.commands.push(...commands);\r\n\r\n commands.forEach((command) => {\r\n const commandKey = command.name.split(\" \")[0];\r\n\r\n manifestManager.addCommandToList(command.name, {\r\n relativePath: command.commandRelativePath,\r\n source: command.commandSource || \"project\",\r\n description: command.commandDescription,\r\n alias: command.commandAlias,\r\n options: command.commandOptions.length > 0 ? command.commandOptions : undefined,\r\n });\r\n\r\n this.commandsMap.set(commandKey, command);\r\n\r\n // Register alias if exists\r\n if (command.commandAlias) {\r\n this.aliasMap.set(command.commandAlias, commandKey);\r\n }\r\n });\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Get command by name or alias\r\n */\r\n public getCommand(name: string): CLICommand | undefined {\r\n // First try direct lookup\r\n let command = this.commandsMap.get(name);\r\n if (command) return command;\r\n\r\n // Try alias lookup\r\n const realName = this.aliasMap.get(name);\r\n if (realName) {\r\n return this.commandsMap.get(realName);\r\n }\r\n\r\n return;\r\n }\r\n\r\n /**\r\n * Get all command names and aliases\r\n * Used for fuzzy matching suggestions\r\n */\r\n public getAllCommandNames(): string[] {\r\n const names: string[] = [];\r\n\r\n // Add all command names\r\n for (const name of this.commandsMap.keys()) {\r\n names.push(name);\r\n }\r\n\r\n // Add all aliases\r\n for (const alias of this.aliasMap.keys()) {\r\n names.push(alias);\r\n }\r\n\r\n // Also include cached commands from manifest\r\n const manifestCommands = manifestManager.commandsJson?.commands || {};\r\n for (const name of Object.keys(manifestCommands)) {\r\n const baseName = name.split(\" \")[0];\r\n if (!names.includes(baseName)) {\r\n names.push(baseName);\r\n }\r\n const alias = manifestCommands[name].alias;\r\n if (alias && !names.includes(alias)) {\r\n names.push(alias);\r\n }\r\n }\r\n\r\n return names;\r\n }\r\n\r\n /**\r\n * Start the cli manager\r\n */\r\n public async start() {\r\n // Schema-less first pass: it exists to discover the command name and the\r\n // command-independent flags (--help, --version, --no-cache). Values here are\r\n // raw strings; the typed pass below is the one whose options reach a command.\r\n const { name, options } = parseCliArgs(process.argv);\r\n\r\n if (options.noCache) {\r\n manifestManager.clearCommandsCache();\r\n // remove the commands.json file as wel\r\n await manifestManager.removeCommandsFile();\r\n }\r\n\r\n if (options.version || options.v) {\r\n await displayWarlockVersionInTerminal();\r\n process.exit(0);\r\n }\r\n\r\n // Try to load from manifest first (fastest path)\r\n await manifestManager.loadCommands();\r\n\r\n // Register framework commands first (needed for help)\r\n this.register(\r\n ...frameworkCommands.map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"framework\";\r\n }\r\n return command;\r\n }),\r\n );\r\n\r\n const isHelpCommand = options.help || options.h;\r\n\r\n // Handle global help (no command)\r\n if (!name && isHelpCommand) {\r\n await this.showGlobalHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Handle warm cache\r\n if (options.warmCache) {\r\n await this.warmCache();\r\n process.exit(0);\r\n }\r\n\r\n if (!name) {\r\n displayMissingCommand();\r\n process.exit(1);\r\n }\r\n\r\n const command = await this.lazyGetCommand(name);\r\n\r\n if (manifestManager.hasChanges) {\r\n await manifestManager.saveCommands();\r\n }\r\n\r\n if (!command) {\r\n // Find similar commands for suggestions\r\n const allCommandNames = this.getAllCommandNames();\r\n const suggestions = findSimilar(name, allCommandNames).map((s) => s.value);\r\n\r\n displayCommandNotFound(name, suggestions);\r\n process.exit(1);\r\n }\r\n\r\n // Handle command-specific help\r\n if (isHelpCommand) {\r\n displayCommandHelp({\r\n name: command.name,\r\n alias: command.commandAlias,\r\n description: command.commandDescription,\r\n options: command.commandOptions,\r\n });\r\n process.exit(0);\r\n }\r\n\r\n // The first parse above had no command to consult, so every value in it is\r\n // a raw string. Now that the command is resolved, re-read argv against its\r\n // declared options so a `type: \"boolean\"` option arrives as a boolean.\r\n const resolved = this.resolveCommandArgs(command);\r\n\r\n await this.execute(command, {\r\n options: resolved.options,\r\n args: resolved.args,\r\n });\r\n }\r\n\r\n /**\r\n * Re-parse argv against the resolved command's declared options.\r\n *\r\n * parseCliArgs runs twice on purpose. The first run produces the command\r\n * name, and there is no schema to consult yet; the second one, here, can be\r\n * type-aware. Re-reading argv (rather than post-processing the first result)\r\n * is what makes `--rollback users.ts` recoverable: by the time the first pass\r\n * returns, the swallowed positional is indistinguishable from a value.\r\n */\r\n protected resolveCommandArgs(command: CLICommand, argv: string[] = process.argv): ParsedCliArgs {\r\n try {\r\n return parseCliArgs(argv, command.commandOptions);\r\n } catch (error) {\r\n if (error instanceof CliOptionValueError) {\r\n console.log();\r\n console.log(` ${colors.redBright(\"Error:\")} ${error.message}`);\r\n console.log();\r\n process.exit(1);\r\n }\r\n\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Show global help with all commands\r\n * Uses manifest if available for fast display\r\n */\r\n protected async showGlobalHelp() {\r\n if (\r\n manifestManager.isCommandLoaded &&\r\n Object.keys(manifestManager.commandsJson?.commands || {}).length > 0\r\n ) {\r\n // Use manifest directly - no need to load command files\r\n const helpCommands: HelpCommandInfo[] = Object.entries(\r\n manifestManager.commandsJson?.commands || {},\r\n ).map(([name, cmd]) => ({\r\n name,\r\n alias: cmd.alias,\r\n description: cmd.description,\r\n source: cmd.source,\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n return;\r\n }\r\n\r\n // Fallback: No manifest, build from registered commands\r\n // This happens on first run before warm-cache\r\n await this.loadPluginsCommands();\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n // Build help info from registered commands\r\n const helpCommands: HelpCommandInfo[] = this.commands.map((cmd) => ({\r\n name: cmd.name,\r\n alias: cmd.commandAlias,\r\n description: cmd.commandDescription,\r\n source: cmd.commandSource || \"project\",\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n }\r\n\r\n /**\r\n * Warm cache - scan all project commands and save to manifest\r\n */\r\n protected async warmCache() {\r\n console.log();\r\n console.log(` ${colors.cyan(\"›\")} Scanning project commands...`);\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n console.log(\r\n ` ${colors.green(\"✔\")} Cached ${colors.bold(String(projectCommands.length))} project commands`,\r\n );\r\n console.log();\r\n }\r\n\r\n /**\r\n * Load plugins commands\r\n */\r\n protected async loadPluginsCommands() {\r\n await warlockConfigManager.load();\r\n\r\n if (warlockConfigManager.isLoaded) {\r\n this.register(\r\n ...(warlockConfigManager.get(\"cli\")?.commands || []).map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"plugin\";\r\n }\r\n return command;\r\n }),\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Try to find the command based on the given command name or alias\r\n */\r\n protected async lazyGetCommand(name: string) {\r\n // first step, try to find it directly through current commands\r\n let command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // second step, try to find it through warlock config commands\r\n await this.loadPluginsCommands();\r\n\r\n command = this.getCommand(name);\r\n\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // third step, try to find it through project commands\r\n await this.loadProjectCommands(name);\r\n\r\n command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Load project commands\r\n */\r\n protected async loadProjectCommands(name: string) {\r\n // first get the commands.json contents as an object\r\n const jsonCommandsFile = await manifestManager.loadCommands();\r\n\r\n if (jsonCommandsFile) {\r\n // Check by name or alias\r\n for (const fullCommandName in jsonCommandsFile.commands) {\r\n const cmdMeta = jsonCommandsFile.commands[fullCommandName];\r\n\r\n // Match by name or alias\r\n if (isMatchingCommandName(fullCommandName, name) || cmdMeta.alias === name) {\r\n const commandPath = cmdMeta.relativePath;\r\n if (commandPath) {\r\n const executedCommand = await cliCommandsLoader.load(commandPath);\r\n\r\n if (executedCommand) {\r\n executedCommand.$relativePath(commandPath);\r\n this.register(executedCommand);\r\n return;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const command = await cliCommandsLoader.locate(name);\r\n\r\n if (command) {\r\n this.register(command);\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * Validate required options\r\n */\r\n protected validateOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): ResolvedCLICommandOption[] {\r\n const missing: ResolvedCLICommandOption[] = [];\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.required) {\r\n // Check if option is provided by name or alias\r\n const hasOption =\r\n options[opt.name] !== undefined || (opt.alias && options[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n missing.push(opt);\r\n }\r\n }\r\n });\r\n\r\n return missing;\r\n }\r\n\r\n /**\r\n * Apply default values to options\r\n */\r\n protected applyDefaultOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): Record<string, string | boolean | number> {\r\n const result = { ...options };\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.defaultValue !== undefined) {\r\n const hasOption =\r\n result[opt.name] !== undefined || (opt.alias && result[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n result[opt.name] = opt.defaultValue;\r\n }\r\n }\r\n\r\n // `!== undefined`, not truthiness: `-r=false` now resolves to the boolean\r\n // `false`, and a truthiness test would drop it before it ever reached the\r\n // canonical name — leaving `rollback` undefined instead of explicitly off.\r\n if (\r\n opt.alias !== undefined &&\r\n result[opt.alias] !== undefined &&\r\n result[opt.name] === undefined\r\n ) {\r\n result[opt.name] = result[opt.alias];\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute the given command\r\n */\r\n public async execute(command: CLICommand, data: CommandActionData) {\r\n const startTime = Date.now();\r\n\r\n // Validate required options\r\n const missingOptions = this.validateOptions(command, data.options);\r\n if (missingOptions.length > 0) {\r\n displayMissingOptions(missingOptions);\r\n process.exit(1);\r\n }\r\n\r\n // Apply default values\r\n data.options = this.applyDefaultOptions(command, data.options);\r\n\r\n displayExecutingCommand(command.name);\r\n\r\n if (command.commandPreAction) {\r\n await command.commandPreAction(data);\r\n }\r\n\r\n // load preloaders\r\n if (command.commandPreload) {\r\n try {\r\n await this.loadPreloaders(command);\r\n } catch (error) {\r\n // A preload failure (a bad import in a config file, a connector that\r\n // throws on boot, a missing module export) happens BEFORE the command's\r\n // action runs — there is no run loop to recover into, so it is ALWAYS\r\n // fatal, persistent command (dev/start) or not. Surface the real cause\r\n // and the offending file, then exit. This MUST stay outside the\r\n // command-execute try/catch below: letting a preload rejection escape\r\n // turned `warlock dev` into a silent hang — the unhandled rejection slid\r\n // past while the already-started loader worker thread kept the process\r\n // alive, freezing just after the banner with no error printed.\r\n displayBootError(command.name, error as Error);\r\n process.exit(1);\r\n }\r\n }\r\n\r\n try {\r\n await command.execute(data);\r\n\r\n if (!command.isPersistent) {\r\n displayCommandSuccess(command.name, Date.now() - startTime);\r\n\r\n process.exit(0);\r\n }\r\n } catch (error) {\r\n displayCommandError(command.name, error as Error);\r\n // Persistent commands (dev/start) own their startup-failure exits;\r\n // by the time an error reaches here they've already entered their\r\n // run loop, so it's a runtime error we log but don't crash on —\r\n // HMR or process supervisors can recover. Non-persistent\r\n // one-shot commands always exit on failure.\r\n if (!command.isPersistent) {\r\n process.exit(1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Load preloaders\r\n */\r\n protected async loadPreloaders(command: CLICommand) {\r\n const preloaders = command.commandPreload || {};\r\n\r\n if (preloaders.runtimeStrategy) {\r\n Application.setRuntimeStrategy(preloaders.runtimeStrategy);\r\n }\r\n\r\n if (preloaders.environemnt) {\r\n Application.setEnvironment(preloaders.environemnt);\r\n }\r\n\r\n // Env BEFORE config, unconditionally, for every command.\r\n //\r\n // `warlock.config.ts` is a module whose body runs on import, and projects\r\n // legitimately call `env(\"BUILD_OUT\", \"dist\")` in it. Loading config first\r\n // evaluated that body against an empty env store, so every such call\r\n // silently returned its default — under `build` and `start`, which never\r\n // loaded env at all, and under `dev` too, because config was loaded first\r\n // regardless. The ordering is load-bearing in the other direction as well:\r\n // `loadEnv()` reads `process.env.NODE_ENV` at call time to choose\r\n // `.env.<NODE_ENV>` over `.env`, so it must follow `setEnvironment` above.\r\n await loadEnvironmentFiles();\r\n\r\n await warlockConfigManager.load();\r\n\r\n if (preloaders.config || preloaders.bootstrap || preloaders.prestart) {\r\n await filesOrchestrator.init();\r\n }\r\n\r\n // `preloaders.env` is gone: env is loaded above for every command, so the\r\n // branch that used to serve it could only re-parse the same files and\r\n // re-override `process.env` a second time.\r\n if (preloaders.bootstrap) {\r\n await bootstrap();\r\n\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/bootstrap.ts\");\r\n }\r\n }\r\n\r\n // Load configuration files\r\n if (preloaders.config) {\r\n await loadConfigFiles(preloaders.config);\r\n }\r\n\r\n if (preloaders.prestart) {\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/prestart.ts\");\r\n }\r\n }\r\n\r\n // Initialize connectors.\r\n // `connectors: true` only starts the early phase here — late-phase\r\n // connectors (http, socket) start after the command's action loads\r\n // app code. Explicit lists bypass phase splitting (caller knows\r\n // exactly which connectors they want).\r\n if (preloaders.connectors) {\r\n if (preloaders.connectors === true) {\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\r\n } else {\r\n await connectorsManager.start(preloaders.connectors);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-commands.manager.mjs","names":[],"sources":["../../../../../../../core/src/cli/cli-commands.manager.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { fileExistsAsync } from \"@warlock.js/fs\";\r\nimport { Application } from \"../application\";\r\nimport { bootstrap } from \"../bootstrap\";\r\nimport { loadConfigFiles } from \"../config/load-config-files\";\r\nimport { connectorsManager } from \"../connectors/connectors-manager\";\r\nimport { ConnectorLifecyclePhase } from \"../connectors/types\";\r\nimport { filesOrchestrator } from \"../dev-server/files-orchestrator\";\r\nimport { manifestManager } from \"../manifest/manifest-manager\";\r\nimport { appPath } from \"../utils\";\r\nimport { loadEnvironmentFiles } from \"../utils/load-environment\";\r\nimport { warlockConfigManager } from \"../warlock-config/warlock-config.manager\";\r\nimport { CLICommand } from \"../commands/cli-command\";\r\nimport {\r\n displayBootError,\r\n displayCommandError,\r\n displayCommandHelp,\r\n displayCommandNotFound,\r\n displayCommandSuccess,\r\n displayExecutingCommand,\r\n displayHelp,\r\n displayMissingCommand,\r\n displayMissingOptions,\r\n displayWarlockVersionInTerminal,\r\n type HelpCommandInfo,\r\n isMatchingCommandName,\r\n} from \"./cli-commands.utils\";\r\nimport { cliCommandsLoader } from \"./commands-loader\";\r\nimport { frameworkCommands } from \"./framework-cli-commands\";\r\nimport { CliOptionValueError, ParsedCliArgs, parseCliArgs } from \"./parse-cli-args\";\r\nimport { findSimilar } from \"./string-similarity\";\r\nimport { CommandActionData, ResolvedCLICommandOption } from \"../commands/types\";\r\n\r\nexport class CLICommandsManager {\r\n /**\r\n * List of commands\r\n */\r\n public commands: CLICommand[] = [];\r\n\r\n /**\r\n * Commands map (name -> command)\r\n */\r\n public commandsMap: Map<string, CLICommand> = new Map();\r\n\r\n /**\r\n * Alias map (alias -> command name)\r\n */\r\n public aliasMap: Map<string, string> = new Map();\r\n\r\n /**\r\n * Register the given commands\r\n */\r\n public register(...commands: CLICommand[]): this {\r\n this.commands.push(...commands);\r\n\r\n commands.forEach((command) => {\r\n const commandKey = command.name.split(\" \")[0];\r\n\r\n manifestManager.addCommandToList(command.name, {\r\n relativePath: command.commandRelativePath,\r\n source: command.commandSource || \"project\",\r\n description: command.commandDescription,\r\n alias: command.commandAlias,\r\n options: command.commandOptions.length > 0 ? command.commandOptions : undefined,\r\n });\r\n\r\n this.commandsMap.set(commandKey, command);\r\n\r\n // Register alias if exists\r\n if (command.commandAlias) {\r\n this.aliasMap.set(command.commandAlias, commandKey);\r\n }\r\n });\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Get command by name or alias\r\n */\r\n public getCommand(name: string): CLICommand | undefined {\r\n // First try direct lookup\r\n let command = this.commandsMap.get(name);\r\n if (command) return command;\r\n\r\n // Try alias lookup\r\n const realName = this.aliasMap.get(name);\r\n if (realName) {\r\n return this.commandsMap.get(realName);\r\n }\r\n\r\n return;\r\n }\r\n\r\n /**\r\n * Get all command names and aliases\r\n * Used for fuzzy matching suggestions\r\n */\r\n public getAllCommandNames(): string[] {\r\n const names: string[] = [];\r\n\r\n // Add all command names\r\n for (const name of this.commandsMap.keys()) {\r\n names.push(name);\r\n }\r\n\r\n // Add all aliases\r\n for (const alias of this.aliasMap.keys()) {\r\n names.push(alias);\r\n }\r\n\r\n // Also include cached commands from manifest\r\n const manifestCommands = manifestManager.commandsJson?.commands || {};\r\n for (const name of Object.keys(manifestCommands)) {\r\n const baseName = name.split(\" \")[0];\r\n if (!names.includes(baseName)) {\r\n names.push(baseName);\r\n }\r\n const alias = manifestCommands[name].alias;\r\n if (alias && !names.includes(alias)) {\r\n names.push(alias);\r\n }\r\n }\r\n\r\n return names;\r\n }\r\n\r\n /**\r\n * Start the cli manager\r\n */\r\n public async start() {\r\n // Schema-less first pass: it exists to discover the command name and the\r\n // command-independent flags (--help, --version, --no-cache). Values here are\r\n // raw strings; the typed pass below is the one whose options reach a command.\r\n const { name, options } = parseCliArgs(process.argv);\r\n\r\n if (options.noCache) {\r\n manifestManager.clearCommandsCache();\r\n // remove the commands.json file as wel\r\n await manifestManager.removeCommandsFile();\r\n }\r\n\r\n if (options.version || options.v) {\r\n await displayWarlockVersionInTerminal();\r\n process.exit(0);\r\n }\r\n\r\n // Try to load from manifest first (fastest path)\r\n await manifestManager.loadCommands();\r\n\r\n // Register framework commands first (needed for help)\r\n this.register(\r\n ...frameworkCommands.map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"framework\";\r\n }\r\n return command;\r\n }),\r\n );\r\n\r\n const isHelpCommand = options.help || options.h;\r\n\r\n // Handle global help (no command)\r\n if (!name && isHelpCommand) {\r\n await this.showGlobalHelp();\r\n process.exit(0);\r\n }\r\n\r\n // Handle warm cache\r\n if (options.warmCache) {\r\n await this.warmCache();\r\n process.exit(0);\r\n }\r\n\r\n if (!name) {\r\n displayMissingCommand();\r\n process.exit(1);\r\n }\r\n\r\n const command = await this.lazyGetCommand(name);\r\n\r\n if (manifestManager.hasChanges) {\r\n await manifestManager.saveCommands();\r\n }\r\n\r\n if (!command) {\r\n // Find similar commands for suggestions\r\n const allCommandNames = this.getAllCommandNames();\r\n const suggestions = findSimilar(name, allCommandNames).map((s) => s.value);\r\n\r\n displayCommandNotFound(name, suggestions);\r\n process.exit(1);\r\n }\r\n\r\n // Handle command-specific help\r\n if (isHelpCommand) {\r\n displayCommandHelp({\r\n name: command.name,\r\n alias: command.commandAlias,\r\n description: command.commandDescription,\r\n options: command.commandOptions,\r\n });\r\n process.exit(0);\r\n }\r\n\r\n // The first parse above had no command to consult, so every value in it is\r\n // a raw string. Now that the command is resolved, re-read argv against its\r\n // declared options so a `type: \"boolean\"` option arrives as a boolean.\r\n const resolved = this.resolveCommandArgs(command);\r\n\r\n await this.execute(command, {\r\n options: resolved.options,\r\n args: resolved.args,\r\n });\r\n }\r\n\r\n /**\r\n * Re-parse argv against the resolved command's declared options.\r\n *\r\n * parseCliArgs runs twice on purpose. The first run produces the command\r\n * name, and there is no schema to consult yet; the second one, here, can be\r\n * type-aware. Re-reading argv (rather than post-processing the first result)\r\n * is what makes `--rollback users.ts` recoverable: by the time the first pass\r\n * returns, the swallowed positional is indistinguishable from a value.\r\n */\r\n protected resolveCommandArgs(command: CLICommand, argv: string[] = process.argv): ParsedCliArgs {\r\n try {\r\n return parseCliArgs(argv, command.commandOptions);\r\n } catch (error) {\r\n if (error instanceof CliOptionValueError) {\r\n console.log();\r\n console.log(` ${colors.redBright(\"Error:\")} ${error.message}`);\r\n console.log();\r\n process.exit(1);\r\n }\r\n\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Show global help with all commands\r\n * Uses manifest if available for fast display\r\n */\r\n protected async showGlobalHelp() {\r\n if (\r\n manifestManager.isCommandLoaded &&\r\n Object.keys(manifestManager.commandsJson?.commands || {}).length > 0\r\n ) {\r\n // Use manifest directly - no need to load command files\r\n const helpCommands: HelpCommandInfo[] = Object.entries(\r\n manifestManager.commandsJson?.commands || {},\r\n ).map(([name, cmd]) => ({\r\n name,\r\n alias: cmd.alias,\r\n description: cmd.description,\r\n source: cmd.source,\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n return;\r\n }\r\n\r\n // Fallback: No manifest, build from registered commands\r\n // This happens on first run before warm-cache\r\n await this.loadPluginsCommands();\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n // Build help info from registered commands\r\n const helpCommands: HelpCommandInfo[] = this.commands.map((cmd) => ({\r\n name: cmd.name,\r\n alias: cmd.commandAlias,\r\n description: cmd.commandDescription,\r\n source: cmd.commandSource || \"project\",\r\n }));\r\n\r\n await displayHelp(helpCommands);\r\n }\r\n\r\n /**\r\n * Warm cache - scan all project commands and save to manifest\r\n */\r\n protected async warmCache() {\r\n console.log();\r\n console.log(` ${colors.cyan(\"›\")} Scanning project commands...`);\r\n\r\n const projectCommands = await cliCommandsLoader.scanAll();\r\n\r\n this.register(...projectCommands);\r\n\r\n await manifestManager.saveCommands();\r\n\r\n console.log(\r\n ` ${colors.green(\"✔\")} Cached ${colors.bold(String(projectCommands.length))} project commands`,\r\n );\r\n console.log();\r\n }\r\n\r\n /**\r\n * Load plugins commands\r\n */\r\n protected async loadPluginsCommands() {\r\n await warlockConfigManager.load();\r\n\r\n if (warlockConfigManager.isLoaded) {\r\n this.register(\r\n ...(warlockConfigManager.get(\"cli\")?.commands || []).map((command) => {\r\n if (!command.commandSource) {\r\n command.commandSource = \"plugin\";\r\n }\r\n return command;\r\n }),\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Try to find the command based on the given command name or alias\r\n */\r\n protected async lazyGetCommand(name: string) {\r\n // first step, try to find it directly through current commands\r\n let command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // second step, try to find it through warlock config commands\r\n await this.loadPluginsCommands();\r\n\r\n command = this.getCommand(name);\r\n\r\n if (command) {\r\n return command;\r\n }\r\n\r\n // third step, try to find it through project commands\r\n await this.loadProjectCommands(name);\r\n\r\n command = this.getCommand(name);\r\n if (command) {\r\n return command;\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Load project commands\r\n */\r\n protected async loadProjectCommands(name: string) {\r\n // first get the commands.json contents as an object\r\n const jsonCommandsFile = await manifestManager.loadCommands();\r\n\r\n if (jsonCommandsFile) {\r\n // Check by name or alias\r\n for (const fullCommandName in jsonCommandsFile.commands) {\r\n const cmdMeta = jsonCommandsFile.commands[fullCommandName];\r\n\r\n // Match by name or alias\r\n if (isMatchingCommandName(fullCommandName, name) || cmdMeta.alias === name) {\r\n const commandPath = cmdMeta.relativePath;\r\n if (commandPath) {\r\n const executedCommand = await cliCommandsLoader.load(commandPath);\r\n\r\n if (executedCommand) {\r\n executedCommand.$relativePath(commandPath);\r\n this.register(executedCommand);\r\n return;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const command = await cliCommandsLoader.locate(name);\r\n\r\n if (command) {\r\n this.register(command);\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * Validate required options\r\n */\r\n protected validateOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): ResolvedCLICommandOption[] {\r\n const missing: ResolvedCLICommandOption[] = [];\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.required) {\r\n // Check if option is provided by name or alias\r\n const hasOption =\r\n options[opt.name] !== undefined || (opt.alias && options[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n missing.push(opt);\r\n }\r\n }\r\n });\r\n\r\n return missing;\r\n }\r\n\r\n /**\r\n * Apply default values to options\r\n */\r\n protected applyDefaultOptions(\r\n command: CLICommand,\r\n options: Record<string, string | boolean | number>,\r\n ): Record<string, string | boolean | number> {\r\n const result = { ...options };\r\n\r\n command.commandOptions.forEach((opt) => {\r\n if (opt.defaultValue !== undefined) {\r\n const hasOption =\r\n result[opt.name] !== undefined || (opt.alias && result[opt.alias] !== undefined);\r\n if (!hasOption) {\r\n result[opt.name] = opt.defaultValue;\r\n }\r\n }\r\n\r\n // `!== undefined`, not truthiness: `-r=false` now resolves to the boolean\r\n // `false`, and a truthiness test would drop it before it ever reached the\r\n // canonical name — leaving `rollback` undefined instead of explicitly off.\r\n if (\r\n opt.alias !== undefined &&\r\n result[opt.alias] !== undefined &&\r\n result[opt.name] === undefined\r\n ) {\r\n result[opt.name] = result[opt.alias];\r\n }\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute the given command\r\n */\r\n public async execute(command: CLICommand, data: CommandActionData) {\r\n const startTime = Date.now();\r\n\r\n // Validate required options\r\n const missingOptions = this.validateOptions(command, data.options);\r\n if (missingOptions.length > 0) {\r\n displayMissingOptions(missingOptions);\r\n process.exit(1);\r\n }\r\n\r\n // Apply default values\r\n data.options = this.applyDefaultOptions(command, data.options);\r\n\r\n displayExecutingCommand(command.name);\r\n\r\n if (command.commandPreAction) {\r\n await command.commandPreAction(data);\r\n }\r\n\r\n // load preloaders\r\n if (command.commandPreload) {\r\n try {\r\n await this.loadPreloaders(command);\r\n } catch (error) {\r\n // A preload failure (a bad import in a config file, a connector that\r\n // throws on boot, a missing module export) happens BEFORE the command's\r\n // action runs — there is no run loop to recover into, so it is ALWAYS\r\n // fatal, persistent command (dev/start) or not. Surface the real cause\r\n // and the offending file, then exit. This MUST stay outside the\r\n // command-execute try/catch below: letting a preload rejection escape\r\n // turned `warlock dev` into a silent hang — the unhandled rejection slid\r\n // past while the already-started loader worker thread kept the process\r\n // alive, freezing just after the banner with no error printed.\r\n displayBootError(command.name, error as Error);\r\n process.exit(1);\r\n }\r\n }\r\n\r\n try {\r\n await command.execute(data);\r\n\r\n if (!command.isPersistent) {\r\n displayCommandSuccess(command.name, Date.now() - startTime);\r\n\r\n process.exit(0);\r\n }\r\n } catch (error) {\r\n displayCommandError(command.name, error as Error);\r\n // Persistent commands (dev/start) own their startup-failure exits;\r\n // by the time an error reaches here they've already entered their\r\n // run loop, so it's a runtime error we log but don't crash on —\r\n // HMR or process supervisors can recover. Non-persistent\r\n // one-shot commands always exit on failure.\r\n if (!command.isPersistent) {\r\n process.exit(1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Load preloaders\r\n */\r\n protected async loadPreloaders(command: CLICommand) {\r\n const preloaders = command.commandPreload || {};\r\n\r\n if (preloaders.runtimeStrategy) {\r\n Application.setRuntimeStrategy(preloaders.runtimeStrategy);\r\n }\r\n\r\n if (preloaders.environemnt) {\r\n Application.setEnvironment(preloaders.environemnt);\r\n }\r\n\r\n // Env BEFORE config, unconditionally, for every command.\r\n //\r\n // `warlock.config.ts` is a module whose body runs on import, and projects\r\n // legitimately call `env(\"BUILD_OUT\", \"dist\")` in it. Loading config first\r\n // evaluated that body against an empty env store, so every such call\r\n // silently returned its default — under `build` and `start`, which never\r\n // loaded env at all, and under `dev` too, because config was loaded first\r\n // regardless. The ordering is load-bearing in the other direction as well:\r\n // `loadEnv()` reads `process.env.NODE_ENV` at call time to choose\r\n // `.env.<NODE_ENV>` over `.env`, so it must follow `setEnvironment` above.\r\n await loadEnvironmentFiles();\r\n\r\n await warlockConfigManager.load();\r\n\r\n if (preloaders.config || preloaders.bootstrap || preloaders.prestart) {\r\n await filesOrchestrator.init();\r\n }\r\n\r\n // `preloaders.env` is gone: env is loaded above for every command, so the\r\n // branch that used to serve it could only re-parse the same files and\r\n // re-override `process.env` a second time.\r\n if (preloaders.bootstrap) {\r\n await bootstrap();\r\n\r\n if (await fileExistsAsync(appPath(\"bootstrap.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/bootstrap.ts\");\r\n }\r\n }\r\n\r\n // Load configuration files\r\n if (preloaders.config) {\r\n await loadConfigFiles(preloaders.config);\r\n }\r\n\r\n if (preloaders.prestart) {\r\n if (await fileExistsAsync(appPath(\"prestart.ts\"))) {\r\n await filesOrchestrator.load(\"src/app/prestart.ts\");\r\n }\r\n }\r\n\r\n // Initialize connectors.\r\n // `connectors: true` only starts the early phase here — late-phase\r\n // connectors (http, socket) start after the command's action loads\r\n // app code. Explicit lists bypass phase splitting (caller knows\r\n // exactly which connectors they want).\r\n if (preloaders.connectors) {\r\n if (preloaders.connectors === true) {\r\n await connectorsManager.startPhase(ConnectorLifecyclePhase.Early);\r\n } else {\r\n await connectorsManager.start(preloaders.connectors);\r\n }\r\n }\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,qBAAb,MAAgC;;kBAIE,CAAC;qCAKa,IAAI,IAAI;kCAKf,IAAI,IAAI;;;;;CAK/C,AAAO,SAAS,GAAG,UAA8B;EAC/C,KAAK,SAAS,KAAK,GAAG,QAAQ;EAE9B,SAAS,SAAS,YAAY;GAC5B,MAAM,aAAa,QAAQ,KAAK,MAAM,GAAG,CAAC,CAAC;GAE3C,gBAAgB,iBAAiB,QAAQ,MAAM;IAC7C,cAAc,QAAQ;IACtB,QAAQ,QAAQ,iBAAiB;IACjC,aAAa,QAAQ;IACrB,OAAO,QAAQ;IACf,SAAS,QAAQ,eAAe,SAAS,IAAI,QAAQ,iBAAiB;GACxE,CAAC;GAED,KAAK,YAAY,IAAI,YAAY,OAAO;GAGxC,IAAI,QAAQ,cACV,KAAK,SAAS,IAAI,QAAQ,cAAc,UAAU;EAEtD,CAAC;EAED,OAAO;CACT;;;;CAKA,AAAO,WAAW,MAAsC;EAEtD,IAAI,UAAU,KAAK,YAAY,IAAI,IAAI;EACvC,IAAI,SAAS,OAAO;EAGpB,MAAM,WAAW,KAAK,SAAS,IAAI,IAAI;EACvC,IAAI,UACF,OAAO,KAAK,YAAY,IAAI,QAAQ;CAIxC;;;;;CAMA,AAAO,qBAA+B;EACpC,MAAM,QAAkB,CAAC;EAGzB,KAAK,MAAM,QAAQ,KAAK,YAAY,KAAK,GACvC,MAAM,KAAK,IAAI;EAIjB,KAAK,MAAM,SAAS,KAAK,SAAS,KAAK,GACrC,MAAM,KAAK,KAAK;EAIlB,MAAM,mBAAmB,gBAAgB,cAAc,YAAY,CAAC;EACpE,KAAK,MAAM,QAAQ,OAAO,KAAK,gBAAgB,GAAG;GAChD,MAAM,WAAW,KAAK,MAAM,GAAG,CAAC,CAAC;GACjC,IAAI,CAAC,MAAM,SAAS,QAAQ,GAC1B,MAAM,KAAK,QAAQ;GAErB,MAAM,QAAQ,iBAAiB,KAAK,CAAC;GACrC,IAAI,SAAS,CAAC,MAAM,SAAS,KAAK,GAChC,MAAM,KAAK,KAAK;EAEpB;EAEA,OAAO;CACT;;;;CAKA,MAAa,QAAQ;EAInB,MAAM,EAAE,MAAM,YAAY,aAAa,QAAQ,IAAI;EAEnD,IAAI,QAAQ,SAAS;GACnB,gBAAgB,mBAAmB;GAEnC,MAAM,gBAAgB,mBAAmB;EAC3C;EAEA,IAAI,QAAQ,WAAW,QAAQ,GAAG;GAChC,MAAM,gCAAgC;GACtC,QAAQ,KAAK,CAAC;EAChB;EAGA,MAAM,gBAAgB,aAAa;EAGnC,KAAK,SACH,GAAG,kBAAkB,KAAK,YAAY;GACpC,IAAI,CAAC,QAAQ,eACX,QAAQ,gBAAgB;GAE1B,OAAO;EACT,CAAC,CACH;EAEA,MAAM,gBAAgB,QAAQ,QAAQ,QAAQ;EAG9C,IAAI,CAAC,QAAQ,eAAe;GAC1B,MAAM,KAAK,eAAe;GAC1B,QAAQ,KAAK,CAAC;EAChB;EAGA,IAAI,QAAQ,WAAW;GACrB,MAAM,KAAK,UAAU;GACrB,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,CAAC,MAAM;GACT,sBAAsB;GACtB,QAAQ,KAAK,CAAC;EAChB;EAEA,MAAM,UAAU,MAAM,KAAK,eAAe,IAAI;EAE9C,IAAI,gBAAgB,YAClB,MAAM,gBAAgB,aAAa;EAGrC,IAAI,CAAC,SAAS;GAKZ,uBAAuB,MAFH,YAAY,MADR,KAAK,mBACuB,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,KAE7B,CAAC;GACxC,QAAQ,KAAK,CAAC;EAChB;EAGA,IAAI,eAAe;GACjB,mBAAmB;IACjB,MAAM,QAAQ;IACd,OAAO,QAAQ;IACf,aAAa,QAAQ;IACrB,SAAS,QAAQ;GACnB,CAAC;GACD,QAAQ,KAAK,CAAC;EAChB;EAKA,MAAM,WAAW,KAAK,mBAAmB,OAAO;EAEhD,MAAM,KAAK,QAAQ,SAAS;GAC1B,SAAS,SAAS;GAClB,MAAM,SAAS;EACjB,CAAC;CACH;;;;;;;;;;CAWA,AAAU,mBAAmB,SAAqB,OAAiB,QAAQ,MAAqB;EAC9F,IAAI;GACF,OAAO,aAAa,MAAM,QAAQ,cAAc;EAClD,SAAS,OAAO;GACd,IAAI,iBAAiB,qBAAqB;IACxC,QAAQ,IAAI;IACZ,QAAQ,IAAI,KAAK,OAAO,UAAU,QAAQ,EAAE,GAAG,MAAM,SAAS;IAC9D,QAAQ,IAAI;IACZ,QAAQ,KAAK,CAAC;GAChB;GAEA,MAAM;EACR;CACF;;;;;CAMA,MAAgB,iBAAiB;EAC/B,IACE,gBAAgB,mBAChB,OAAO,KAAK,gBAAgB,cAAc,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,GACnE;GAWA,MAAM,YATkC,OAAO,QAC7C,gBAAgB,cAAc,YAAY,CAAC,CAC7C,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU;IACtB;IACA,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,QAAQ,IAAI;GACd,EAE6B,CAAC;GAC9B;EACF;EAIA,MAAM,KAAK,oBAAoB;EAE/B,MAAM,kBAAkB,MAAM,kBAAkB,QAAQ;EAExD,KAAK,SAAS,GAAG,eAAe;EAEhC,MAAM,gBAAgB,aAAa;EAUnC,MAAM,YAPkC,KAAK,SAAS,KAAK,SAAS;GAClE,MAAM,IAAI;GACV,OAAO,IAAI;GACX,aAAa,IAAI;GACjB,QAAQ,IAAI,iBAAiB;EAC/B,EAE6B,CAAC;CAChC;;;;CAKA,MAAgB,YAAY;EAC1B,QAAQ,IAAI;EACZ,QAAQ,IAAI,KAAK,OAAO,KAAK,KAAK,EAAE,8BAA8B;EAElE,MAAM,kBAAkB,MAAM,kBAAkB,QAAQ;EAExD,KAAK,SAAS,GAAG,eAAe;EAEhC,MAAM,gBAAgB,aAAa;EAEnC,QAAQ,IACN,KAAK,OAAO,MAAM,KAAK,EAAE,UAAU,OAAO,KAAK,OAAO,gBAAgB,MAAM,CAAC,EAAE,kBACjF;EACA,QAAQ,IAAI;CACd;;;;CAKA,MAAgB,sBAAsB;EACpC,MAAM,qBAAqB,KAAK;EAEhC,IAAI,qBAAqB,UACvB,KAAK,SACH,IAAI,qBAAqB,IAAI,KAAK,CAAC,EAAE,YAAY,CAAC,EAAC,CAAE,KAAK,YAAY;GACpE,IAAI,CAAC,QAAQ,eACX,QAAQ,gBAAgB;GAE1B,OAAO;EACT,CAAC,CACH;CAEJ;;;;CAKA,MAAgB,eAAe,MAAc;EAE3C,IAAI,UAAU,KAAK,WAAW,IAAI;EAClC,IAAI,SACF,OAAO;EAIT,MAAM,KAAK,oBAAoB;EAE/B,UAAU,KAAK,WAAW,IAAI;EAE9B,IAAI,SACF,OAAO;EAIT,MAAM,KAAK,oBAAoB,IAAI;EAEnC,UAAU,KAAK,WAAW,IAAI;EAC9B,IAAI,SACF,OAAO;EAGT,OAAO;CACT;;;;CAKA,MAAgB,oBAAoB,MAAc;EAEhD,MAAM,mBAAmB,MAAM,gBAAgB,aAAa;EAE5D,IAAI,kBAEF,KAAK,MAAM,mBAAmB,iBAAiB,UAAU;GACvD,MAAM,UAAU,iBAAiB,SAAS;GAG1C,IAAI,sBAAsB,iBAAiB,IAAI,KAAK,QAAQ,UAAU,MAAM;IAC1E,MAAM,cAAc,QAAQ;IAC5B,IAAI,aAAa;KACf,MAAM,kBAAkB,MAAM,kBAAkB,KAAK,WAAW;KAEhE,IAAI,iBAAiB;MACnB,gBAAgB,cAAc,WAAW;MACzC,KAAK,SAAS,eAAe;MAC7B;KACF;IACF;GACF;EACF;EAGF,MAAM,UAAU,MAAM,kBAAkB,OAAO,IAAI;EAEnD,IAAI,SAAS;GACX,KAAK,SAAS,OAAO;GACrB;EACF;CACF;;;;CAKA,AAAU,gBACR,SACA,SAC4B;EAC5B,MAAM,UAAsC,CAAC;EAE7C,QAAQ,eAAe,SAAS,QAAQ;GACtC,IAAI,IAAI,UAIN;QAAI,EADF,QAAQ,IAAI,UAAU,UAAc,IAAI,SAAS,QAAQ,IAAI,WAAW,SAExE,QAAQ,KAAK,GAAG;GAClB;EAEJ,CAAC;EAED,OAAO;CACT;;;;CAKA,AAAU,oBACR,SACA,SAC2C;EAC3C,MAAM,SAAS,EAAE,GAAG,QAAQ;EAE5B,QAAQ,eAAe,SAAS,QAAQ;GACtC,IAAI,IAAI,iBAAiB,QAGvB;QAAI,EADF,OAAO,IAAI,UAAU,UAAc,IAAI,SAAS,OAAO,IAAI,WAAW,SAEtE,OAAO,IAAI,QAAQ,IAAI;GACzB;GAMF,IACE,IAAI,UAAU,UACd,OAAO,IAAI,WAAW,UACtB,OAAO,IAAI,UAAU,QAErB,OAAO,IAAI,QAAQ,OAAO,IAAI;EAElC,CAAC;EAED,OAAO;CACT;;;;CAKA,MAAa,QAAQ,SAAqB,MAAyB;EACjE,MAAM,YAAY,KAAK,IAAI;EAG3B,MAAM,iBAAiB,KAAK,gBAAgB,SAAS,KAAK,OAAO;EACjE,IAAI,eAAe,SAAS,GAAG;GAC7B,sBAAsB,cAAc;GACpC,QAAQ,KAAK,CAAC;EAChB;EAGA,KAAK,UAAU,KAAK,oBAAoB,SAAS,KAAK,OAAO;EAE7D,wBAAwB,QAAQ,IAAI;EAEpC,IAAI,QAAQ,kBACV,MAAM,QAAQ,iBAAiB,IAAI;EAIrC,IAAI,QAAQ,gBACV,IAAI;GACF,MAAM,KAAK,eAAe,OAAO;EACnC,SAAS,OAAO;GAUd,iBAAiB,QAAQ,MAAM,KAAc;GAC7C,QAAQ,KAAK,CAAC;EAChB;EAGF,IAAI;GACF,MAAM,QAAQ,QAAQ,IAAI;GAE1B,IAAI,CAAC,QAAQ,cAAc;IACzB,sBAAsB,QAAQ,MAAM,KAAK,IAAI,IAAI,SAAS;IAE1D,QAAQ,KAAK,CAAC;GAChB;EACF,SAAS,OAAO;GACd,oBAAoB,QAAQ,MAAM,KAAc;GAMhD,IAAI,CAAC,QAAQ,cACX,QAAQ,KAAK,CAAC;EAElB;CACF;;;;CAKA,MAAgB,eAAe,SAAqB;EAClD,MAAM,aAAa,QAAQ,kBAAkB,CAAC;EAE9C,IAAI,WAAW,iBACb,YAAY,mBAAmB,WAAW,eAAe;EAG3D,IAAI,WAAW,aACb,YAAY,eAAe,WAAW,WAAW;EAanD,MAAM,qBAAqB;EAE3B,MAAM,qBAAqB,KAAK;EAEhC,IAAI,WAAW,UAAU,WAAW,aAAa,WAAW,UAC1D,MAAM,kBAAkB,KAAK;EAM/B,IAAI,WAAW,WAAW;GACxB,MAAM,UAAU;GAEhB,IAAI,MAAM,gBAAgB,QAAQ,cAAc,CAAC,GAC/C,MAAM,kBAAkB,KAAK,sBAAsB;EAEvD;EAGA,IAAI,WAAW,QACb,MAAM,gBAAgB,WAAW,MAAM;EAGzC,IAAI,WAAW,UACb;OAAI,MAAM,gBAAgB,QAAQ,aAAa,CAAC,GAC9C,MAAM,kBAAkB,KAAK,qBAAqB;EACpD;EAQF,IAAI,WAAW,YACb,IAAI,WAAW,eAAe,MAC5B,MAAM,kBAAkB,kBAAwC;OAEhE,MAAM,kBAAkB,MAAM,WAAW,UAAU;CAGzD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/add.command.ts"],"sourcesContent":["import { addCommandAction } from \"../../generations/add-command.action\";\r\nimport { command } from \"
|
|
1
|
+
{"version":3,"file":"add.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/add.command.ts"],"sourcesContent":["import { addCommandAction } from \"../../generations/add-command.action\";\r\nimport { command } from \"../../commands/cli-command\";\r\n\r\nexport const addCommand = command({\r\n name: \"add <features...>\",\r\n description: \"Add new feature(s) to the project\",\r\n action: addCommandAction,\r\n options: [\r\n {\r\n text: \"--package-manager -pm\",\r\n description: \"Package manager to use, if not passed, it will be detected automatically\",\r\n },\r\n {\r\n text: \"--list, -l\",\r\n description: \"List available features\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--no-install\",\r\n description:\r\n \"Record dependencies in package.json without installing them — still ejects configs, adds scripts, and runs setup hooks. Used by scaffolders that run a single install afterwards.\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;CACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/build.command.ts"],"sourcesContent":["import { buildAppProduction } from \"../../production/build-app-production\";\nimport { command } from \"
|
|
1
|
+
{"version":3,"file":"build.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/build.command.ts"],"sourcesContent":["import { buildAppProduction } from \"../../production/build-app-production\";\r\nimport { command } from \"../../commands/cli-command\";\r\n\r\nexport const buildCommand = command({\r\n name: \"build\",\r\n description: \"Build the project for production\",\r\n action: buildAppProduction,\r\n preload: {\r\n warlockConfig: true,\r\n },\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,eAAe,QAAQ;CAClC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS,EACP,eAAe,KACjB;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-database.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/create-database.command.ts"],"sourcesContent":["import { createDatabaseAction } from \"../../database/create-database-action\";\nimport { command } from \"
|
|
1
|
+
{"version":3,"file":"create-database.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/create-database.command.ts"],"sourcesContent":["import { createDatabaseAction } from \"../../database/create-database-action\";\r\nimport { command } from \"../../commands/cli-command\";\r\n\r\nexport const createDatabaseCommand = command({\r\n name: \"create-database <name>\",\r\n alias: \"cdb\",\r\n description: \"Create a new database\",\r\n action: createDatabaseAction,\r\n preload: {\r\n config: [\"database\", \"log\"],\r\n env: true,\r\n connectors: [\"database\"],\r\n },\r\n options: [\r\n {\r\n text: \"--connection, -c\",\r\n description: \"Database connection name\",\r\n defaultValue: \"default\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP,QAAQ,CAAC,YAAY,KAAK;EAC1B,KAAK;EACL,YAAY,CAAC,UAAU;CACzB;CACA,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,cAAc;CAChB,CACF;AACF,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { command } from "
|
|
1
|
+
import { command } from "../../commands/cli-command.mjs";
|
|
2
2
|
import { displayStartupBanner } from "../cli-commands.utils.mjs";
|
|
3
3
|
import { isDevWorker, superviseDevServer } from "../../dev-server/supervisor.mjs";
|
|
4
4
|
import { checkForFrameworkUpdate } from "../../dev-server/check-for-updates.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-server.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/dev-server.command.ts"],"sourcesContent":["import { checkForFrameworkUpdate } from \"../../dev-server/check-for-updates\";\r\nimport { startDevelopmentServer } from \"../../dev-server/start-development-server\";\r\nimport { isDevWorker, superviseDevServer } from \"../../dev-server/supervisor\";\r\nimport { command } from \"
|
|
1
|
+
{"version":3,"file":"dev-server.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/dev-server.command.ts"],"sourcesContent":["import { checkForFrameworkUpdate } from \"../../dev-server/check-for-updates\";\r\nimport { startDevelopmentServer } from \"../../dev-server/start-development-server\";\r\nimport { isDevWorker, superviseDevServer } from \"../../dev-server/supervisor\";\r\nimport { command } from \"../../commands/cli-command\";\r\nimport { displayStartupBanner } from \"../cli-commands.utils\";\r\n\r\nexport const devServerCommand = command({\r\n name: \"dev\",\r\n description: \"Start development server (HMR, type-gen, health checks)\",\r\n persistent: true,\r\n preload: {\r\n runtimeStrategy: \"development\",\r\n config: true, // load all config\r\n bootstrap: true,\r\n prestart: true, // load prestart file (if exists)\r\n // Only the Early lifecycle phase starts here; the Late phase\r\n // (http, socket) starts after app modules load — see\r\n // development-server.ts STEP 8.5.\r\n connectors: true,\r\n },\r\n preAction: async () => {\r\n // Two roles share this command. The first `warlock dev` becomes a thin\r\n // supervisor that owns the terminal and (re)spawns the real server; the\r\n // process it spawns carries WARLOCK_DEV_WORKER and falls through to the\r\n // server itself. See supervisor.ts for why the split exists.\r\n //\r\n // This branch has to sit in `preAction`, which runs BEFORE the command's\r\n // preloaders: the supervisor must never load config or start connectors,\r\n // or it would hold a second database connection open for the whole\r\n // session. `superviseDevServer()` never resolves, so nothing below it —\r\n // preload included — runs in the supervisor.\r\n if (!isDevWorker()) {\r\n return superviseDevServer();\r\n }\r\n\r\n await displayStartupBanner({ environment: \"development\" });\r\n },\r\n action: async (data) => {\r\n const devServer = await startDevelopmentServer({\r\n fresh: Boolean(data.options.fresh),\r\n // Pass `false` only when the CLI flag is explicitly set; `undefined`\r\n // lets `warlock.config.ts > devServer.*` defaults apply.\r\n generateTypings: data.options.skipTypings ? false : undefined,\r\n healthCheckers: data.options.skipHealth ? false : undefined,\r\n });\r\n\r\n // Fire-and-forget: once the server is ready, surface a one-line notice if\r\n // a newer @warlock.js/core has been published, and arm the `u` shortcut\r\n // that updates + restarts. Fully self-guarded — it never blocks startup\r\n // nor breaks dev if the registry is unreachable.\r\n void checkForFrameworkUpdate(devServer);\r\n },\r\n options: [\r\n {\r\n text: \"--fresh, -f\",\r\n description: \"Delete .warlock/manifest.json before start (force full re-parse from disk)\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--skip-typings, -st\",\r\n description: \"Skip background type generation for this run\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--skip-health, -sh\",\r\n description: \"Skip file health checkers for this run\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;;;;AAMA,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,aAAa;CACb,YAAY;CACZ,SAAS;EACP,iBAAiB;EACjB,QAAQ;EACR,WAAW;EACX,UAAU;EAIV,YAAY;CACd;CACA,WAAW,YAAY;EAWrB,IAAI,CAAC,YAAY,GACf,OAAO,mBAAmB;EAG5B,MAAM,qBAAqB,EAAE,aAAa,cAAc,CAAC;CAC3D;CACA,QAAQ,OAAO,SAAS;EAatB,AAAK,wBAAwB,MAZL,uBAAuB;GAC7C,OAAO,QAAQ,KAAK,QAAQ,KAAK;GAGjC,iBAAiB,KAAK,QAAQ,cAAc,QAAQ;GACpD,gBAAgB,KAAK,QAAQ,aAAa,QAAQ;EACpD,CAAC,CAMqC;CACxC;CACA,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-command.action.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/doctor/doctor-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"
|
|
1
|
+
{"version":3,"file":"doctor-command.action.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/doctor/doctor-command.action.ts"],"sourcesContent":["import type { CommandActionData } from \"../../../commands/types\";\r\nimport { defaultDoctorChecks } from \"./checks\";\r\nimport { printReport } from \"./format-report\";\r\nimport { runChecks } from \"./run-checks\";\r\n\r\n/**\r\n * Action behind `warlock doctor`. Runs the default read-only diagnostic\r\n * checks, prints the grouped pass/warn/fail report, and — when any check\r\n * failed — exits the process with a non-zero code so the command signals\r\n * failure to scripts/CI.\r\n *\r\n * A failing check is REPORTED (a line in the report + a non-zero exit), never\r\n * thrown: the runner converts any thrown check into a `fail` result first.\r\n *\r\n * On success this simply returns; the CLI manager then prints its own success\r\n * banner and exits zero.\r\n *\r\n * @param _data Parsed CLI args (unused — doctor takes no positional args).\r\n */\r\nexport async function doctorCommandAction(_data: CommandActionData): Promise<void> {\r\n const report = await runChecks(defaultDoctorChecks);\r\n\r\n printReport(report);\r\n\r\n if (report.hasFailures) {\r\n process.exit(report.exitCode);\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,eAAsB,oBAAoB,OAAyC;CACjF,MAAM,SAAS,MAAM,UAAU,mBAAmB;CAElD,YAAY,MAAM;CAElB,IAAI,OAAO,aACT,QAAQ,KAAK,OAAO,QAAQ;AAEhC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/doctor.command.ts"],"sourcesContent":["import { command } from \"
|
|
1
|
+
{"version":3,"file":"doctor.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/doctor.command.ts"],"sourcesContent":["import { command } from \"../../commands/cli-command\";\r\nimport { doctorCommandAction } from \"./doctor/doctor-command.action\";\r\n\r\n/**\r\n * `warlock doctor` — run a set of READ-ONLY diagnostic checks (routes, config,\r\n * connectors, optional peers, health endpoints, release hygiene) and print a\r\n * grouped pass/warn/fail report. Exits non-zero if any check fails.\r\n *\r\n * Preload plan: load every config file and bootstrap app code so routes and\r\n * connectors are registered for introspection, but DELIBERATELY start no\r\n * connectors — doctor must not open database/cache/socket connections. The\r\n * connectors check therefore reports the registered set and which (if any) are\r\n * already active without forcing them up.\r\n */\r\nexport const doctorCommand = command({\r\n name: \"doctor\",\r\n description:\r\n \"Run read-only health checks (routes, config, connectors, optional peers, health endpoints, release hygiene) and print a pass/warn/fail report\",\r\n action: doctorCommandAction,\r\n preload: {\r\n config: true,\r\n env: true,\r\n bootstrap: true,\r\n },\r\n});\r\n"],"mappings":";;;;;;;;;;;;;;;AAcA,MAAa,gBAAgB,QAAQ;CACnC,MAAM;CACN,aACE;CACF,QAAQ;CACR,SAAS;EACP,QAAQ;EACR,KAAK;EACL,WAAW;CACb;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop-tables.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/drop-tables.command.ts"],"sourcesContent":["import { dropTablesAction } from \"../../database/drop-tables-action\";\nimport { command } from \"
|
|
1
|
+
{"version":3,"file":"drop-tables.command.mjs","names":[],"sources":["../../../../../../../../core/src/cli/commands/drop-tables.command.ts"],"sourcesContent":["import { dropTablesAction } from \"../../database/drop-tables-action\";\r\nimport { command } from \"../../commands/cli-command\";\r\n\r\nexport const dropTablesCommand = command({\r\n name: \"drop.tables\",\r\n description: \"Drop all tables in the database\",\r\n action: dropTablesAction,\r\n preload: {\r\n config: [\"database\", \"log\"],\r\n env: true,\r\n connectors: [\"database\", \"logger\"],\r\n },\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Drop tables without confirmation\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,oBAAoB,QAAQ;CACvC,MAAM;CACN,aAAa;CACb,QAAQ;CACR,SAAS;EACP,QAAQ,CAAC,YAAY,KAAK;EAC1B,KAAK;EACL,YAAY,CAAC,YAAY,QAAQ;CACnC;CACA,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { command } from "
|
|
1
|
+
import { command } from "../../../commands/cli-command.mjs";
|
|
2
2
|
import { generateController } from "./generators/controller.generator.mjs";
|
|
3
3
|
import { generateMigration } from "./generators/migration.generator.mjs";
|
|
4
4
|
import { generateModel } from "./generators/model.generator.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.command.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/generate/generate.command.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { command } from \"../../cli-command\";\r\nimport type { CommandActionData } from \"../../types\";\r\nimport { generateController } from \"./generators/controller.generator\";\r\nimport { generateMigration } from \"./generators/migration.generator\";\r\nimport { generateModel } from \"./generators/model.generator\";\r\nimport { generateModule } from \"./generators/module.generator\";\r\nimport { generateRepository } from \"./generators/repository.generator\";\r\nimport { generateResource } from \"./generators/resource.generator\";\r\nimport { generateService } from \"./generators/service.generator\";\r\n\r\nconst generators: Record<string, (data: CommandActionData) => Promise<void>> = {\r\n module: generateModule,\r\n controller: generateController,\r\n service: generateService,\r\n model: generateModel,\r\n migration: generateMigration,\r\n repository: generateRepository,\r\n resource: generateResource,\r\n};\r\n\r\nasync function generateCommandAction(data: CommandActionData) {\r\n const commandName = data.args[0];\r\n\r\n if (!commandName) {\r\n console.log(colors.red(\"Error: Generator name is required\"));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n console.log(colors.yellow(\"\\nUsage: warlock generate <generator> <name> [options]\"));\r\n process.exit(1);\r\n }\r\n\r\n const generator = generators[commandName];\r\n\r\n if (!generator) {\r\n console.log(colors.red(`Error: Unknown generator \"${commandName}\"`));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n process.exit(1);\r\n }\r\n\r\n // Remove the command name from args for the generator\r\n const generatorData: CommandActionData = {\r\n ...data,\r\n args: data.args.slice(1),\r\n };\r\n\r\n await generator(generatorData);\r\n}\r\n\r\n// Master generate command\r\nexport const generateCommand = command({\r\n name: \"generate <generator> [args...]\",\r\n alias: \"g\", // Allows 'warlock g module ...'\r\n description: \"Scaffold a new component\",\r\n action: generateCommandAction,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\n// Individual commands\r\nexport const generateModuleCommand = command({\r\n name: \"generate.module <name>\",\r\n alias: \"gen.m\",\r\n description: \"Generate a new module\",\r\n action: generateModule,\r\n options: [\r\n {\r\n text: \"--minimal, -m\",\r\n description:\r\n \"Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateControllerCommand = command({\r\n name: \"generate.controller <module>/<name>\",\r\n alias: \"gen.c\",\r\n description: \"Generate a new controller\",\r\n action: generateController,\r\n options: [\r\n {\r\n text: \"--with-validation, -v\",\r\n description: \"Generate a validation schema and bind it to the controller\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateServiceCommand = command({\r\n name: \"generate.service <module>/<name>\",\r\n alias: \"gen.s\",\r\n description: \"Generate a new service\",\r\n action: generateService,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateModelCommand = command({\r\n name: \"generate.model <module>/<name>\",\r\n alias: \"gen.md\",\r\n description: \"Generate a new model with migration\",\r\n action: generateModel,\r\n options: [\r\n {\r\n text: \"--with-resource, -rs\",\r\n description: \"Generate resource\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--table <name>\",\r\n description: \"Specify table name\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps in migration (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateRepositoryCommand = command({\r\n name: \"generate.repository <module>/<name>\",\r\n alias: \"gen.r\",\r\n description: \"Generate a new repository\",\r\n action: generateRepository,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateResourceCommand = command({\r\n name: \"generate.resource <module>/<name>\",\r\n alias: \"gen.rs\",\r\n description: \"Generate a new resource\",\r\n action: generateResource,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateMigrationCommand = command({\r\n name: \"generate.migration <model-path>\",\r\n alias: \"gen.mig\",\r\n description: \"Generate a new migration for a model\",\r\n action: generateMigration,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--add <columns>\",\r\n description: \"Columns to add (DSL: name:type:modifier,...)\",\r\n },\r\n {\r\n text: \"--drop <columns>\",\r\n description: \"Columns to drop (comma-separated names)\",\r\n },\r\n {\r\n text: \"--rename <columns>\",\r\n description: \"Columns to rename (DSL: old:new,...)\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,aAAyE;CAC7E,QAAQ;CACR,YAAY;CACZ,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,UAAU;AACZ;AAEA,eAAe,sBAAsB,MAAyB;CAC5D,MAAM,cAAc,KAAK,KAAK;CAE9B,IAAI,CAAC,aAAa;EAChB,QAAQ,IAAI,OAAO,IAAI,mCAAmC,CAAC;EAC3D,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,IAAI,OAAO,OAAO,wDAAwD,CAAC;EACnF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,YAAY,WAAW;CAE7B,IAAI,CAAC,WAAW;EACd,QAAQ,IAAI,OAAO,IAAI,6BAA6B,YAAY,EAAE,CAAC;EACnE,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,KAAK,CAAC;CAChB;CAQA,MAAM,UAAU;EAJd,GAAG;EACH,MAAM,KAAK,KAAK,MAAM,CAAC;CAGG,CAAC;AAC/B;AAGA,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAGD,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,uBAAuB,QAAQ;CAC1C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,0BAA0B,QAAQ;CAC7C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,2BAA2B,QAAQ;CAC9C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"generate.command.mjs","names":[],"sources":["../../../../../../../../../core/src/cli/commands/generate/generate.command.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport { command } from \"../../../commands/cli-command\";\r\nimport type { CommandActionData } from \"../../../commands/types\";\r\nimport { generateController } from \"./generators/controller.generator\";\r\nimport { generateMigration } from \"./generators/migration.generator\";\r\nimport { generateModel } from \"./generators/model.generator\";\r\nimport { generateModule } from \"./generators/module.generator\";\r\nimport { generateRepository } from \"./generators/repository.generator\";\r\nimport { generateResource } from \"./generators/resource.generator\";\r\nimport { generateService } from \"./generators/service.generator\";\r\n\r\nconst generators: Record<string, (data: CommandActionData) => Promise<void>> = {\r\n module: generateModule,\r\n controller: generateController,\r\n service: generateService,\r\n model: generateModel,\r\n migration: generateMigration,\r\n repository: generateRepository,\r\n resource: generateResource,\r\n};\r\n\r\nasync function generateCommandAction(data: CommandActionData) {\r\n const commandName = data.args[0];\r\n\r\n if (!commandName) {\r\n console.log(colors.red(\"Error: Generator name is required\"));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n console.log(colors.yellow(\"\\nUsage: warlock generate <generator> <name> [options]\"));\r\n process.exit(1);\r\n }\r\n\r\n const generator = generators[commandName];\r\n\r\n if (!generator) {\r\n console.log(colors.red(`Error: Unknown generator \"${commandName}\"`));\r\n console.log(colors.yellow(\"\\nAvailable generators:\"));\r\n Object.keys(generators).forEach((cmd) => {\r\n console.log(colors.cyan(` - ${cmd}`));\r\n });\r\n process.exit(1);\r\n }\r\n\r\n // Remove the command name from args for the generator\r\n const generatorData: CommandActionData = {\r\n ...data,\r\n args: data.args.slice(1),\r\n };\r\n\r\n await generator(generatorData);\r\n}\r\n\r\n// Master generate command\r\nexport const generateCommand = command({\r\n name: \"generate <generator> [args...]\",\r\n alias: \"g\", // Allows 'warlock g module ...'\r\n description: \"Scaffold a new component\",\r\n action: generateCommandAction,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\n// Individual commands\r\nexport const generateModuleCommand = command({\r\n name: \"generate.module <name>\",\r\n alias: \"gen.m\",\r\n description: \"Generate a new module\",\r\n action: generateModule,\r\n options: [\r\n {\r\n text: \"--minimal, -m\",\r\n description:\r\n \"Generate a minimal skeleton (routes, main, locales, empty subfolders) instead of the full CRUD scaffold\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateControllerCommand = command({\r\n name: \"generate.controller <module>/<name>\",\r\n alias: \"gen.c\",\r\n description: \"Generate a new controller\",\r\n action: generateController,\r\n options: [\r\n {\r\n text: \"--with-validation, -v\",\r\n description: \"Generate a validation schema and bind it to the controller\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateServiceCommand = command({\r\n name: \"generate.service <module>/<name>\",\r\n alias: \"gen.s\",\r\n description: \"Generate a new service\",\r\n action: generateService,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateModelCommand = command({\r\n name: \"generate.model <module>/<name>\",\r\n alias: \"gen.md\",\r\n description: \"Generate a new model with migration\",\r\n action: generateModel,\r\n options: [\r\n {\r\n text: \"--with-resource, -rs\",\r\n description: \"Generate resource\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--table <name>\",\r\n description: \"Specify table name\",\r\n },\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps in migration (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateRepositoryCommand = command({\r\n name: \"generate.repository <module>/<name>\",\r\n alias: \"gen.r\",\r\n description: \"Generate a new repository\",\r\n action: generateRepository,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateResourceCommand = command({\r\n name: \"generate.resource <module>/<name>\",\r\n alias: \"gen.rs\",\r\n description: \"Generate a new resource\",\r\n action: generateResource,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n\r\nexport const generateMigrationCommand = command({\r\n name: \"generate.migration <model-path>\",\r\n alias: \"gen.mig\",\r\n description: \"Generate a new migration for a model\",\r\n action: generateMigration,\r\n options: [\r\n {\r\n text: \"--force, -f\",\r\n description: \"Overwrite existing files\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--dry-run\",\r\n description: \"Preview the files that would be generated without writing anything\",\r\n type: \"boolean\",\r\n },\r\n {\r\n text: \"--add <columns>\",\r\n description: \"Columns to add (DSL: name:type:modifier,...)\",\r\n },\r\n {\r\n text: \"--drop <columns>\",\r\n description: \"Columns to drop (comma-separated names)\",\r\n },\r\n {\r\n text: \"--rename <columns>\",\r\n description: \"Columns to rename (DSL: old:new,...)\",\r\n },\r\n {\r\n text: \"--timestamps [bool]\",\r\n description: \"Include timestamps (default: true)\",\r\n type: \"boolean\",\r\n },\r\n ],\r\n});\r\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,aAAyE;CAC7E,QAAQ;CACR,YAAY;CACZ,SAAS;CACT,OAAO;CACP,WAAW;CACX,YAAY;CACZ,UAAU;AACZ;AAEA,eAAe,sBAAsB,MAAyB;CAC5D,MAAM,cAAc,KAAK,KAAK;CAE9B,IAAI,CAAC,aAAa;EAChB,QAAQ,IAAI,OAAO,IAAI,mCAAmC,CAAC;EAC3D,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,IAAI,OAAO,OAAO,wDAAwD,CAAC;EACnF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,YAAY,WAAW;CAE7B,IAAI,CAAC,WAAW;EACd,QAAQ,IAAI,OAAO,IAAI,6BAA6B,YAAY,EAAE,CAAC;EACnE,QAAQ,IAAI,OAAO,OAAO,yBAAyB,CAAC;EACpD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvC,QAAQ,IAAI,OAAO,KAAK,OAAO,KAAK,CAAC;EACvC,CAAC;EACD,QAAQ,KAAK,CAAC;CAChB;CAQA,MAAM,UAAU;EAJd,GAAG;EACH,MAAM,KAAK,KAAK,MAAM,CAAC;CAGG,CAAC;AAC/B;AAGA,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAGD,MAAa,wBAAwB,QAAQ;CAC3C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aACE;GACF,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,yBAAyB,QAAQ;CAC5C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,uBAAuB,QAAQ;CAC1C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,4BAA4B,QAAQ;CAC/C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,0BAA0B,QAAQ;CAC7C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS,CACP;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,GACA;EACE,MAAM;EACN,aAAa;EACb,MAAM;CACR,CACF;AACF,CAAC;AAED,MAAa,2BAA2B,QAAQ;CAC9C,MAAM;CACN,OAAO;CACP,aAAa;CACb,QAAQ;CACR,SAAS;EACP;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;GACb,MAAM;EACR;CACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/controller.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport type { CommandActionData } from \"
|
|
1
|
+
{"version":3,"file":"controller.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/controller.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport type { CommandActionData } from \"../../../../commands/types\";\r\nimport { controllerStub, schemaStub } from \"../templates/stubs\";\r\nimport { parseModulePath, parseName } from \"../utils/name-parser\";\r\nimport {\r\n componentExists,\r\n ensureComponentDirectory,\r\n moduleExists,\r\n resolveComponentPath,\r\n} from \"../utils/path-resolver\";\r\nimport { putFileAsync, setDryRun } from \"../utils/writer\";\r\n\r\nexport async function generateController(data: CommandActionData): Promise<void> {\r\n const input = data.args[0];\r\n\r\n if (!input) {\r\n console.log(colors.red(\"Error: Controller name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock create.controller <module>/<name>\"));\r\n console.log(colors.yellow(\"Example: warlock create.controller users/create-user\"));\r\n process.exit(1);\r\n }\r\n\r\n const { module, name: componentName } = parseModulePath(input);\r\n\r\n if (!module) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock create.controller <module>/<name>\"));\r\n process.exit(1);\r\n }\r\n\r\n // Check if module exists\r\n if (!(await moduleExists(module))) {\r\n console.log(colors.red(`Error: Module \"${module}\" does not exist`));\r\n console.log(colors.yellow(`Run: warlock create.module ${module}`));\r\n process.exit(1);\r\n }\r\n\r\n const name = parseName(componentName);\r\n const withValidation = data.options.withValidation || data.options.v;\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n // Check if controller already exists\r\n const controllerPath = resolveComponentPath(module, \"controllers\", `${name.kebab}.controller`);\r\n if ((await componentExists(module, \"controllers\", `${name.kebab}.controller`)) && !force) {\r\n console.log(colors.red(`Error: Controller \"${name.kebab}.controller.ts\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n // Ensure directories exist\r\n await ensureComponentDirectory(module, \"controllers\");\r\n\r\n // Generate controller\r\n const controllerContent = controllerStub(name, { withValidation: !!withValidation });\r\n await putFileAsync(controllerPath, controllerContent);\r\n\r\n // Generate the validation schema alongside the controller if requested.\r\n // The controller imports the schema's exported type + value directly —\r\n // there is no separate `requests/` alias file.\r\n if (withValidation) {\r\n await ensureComponentDirectory(module, \"schema\");\r\n\r\n const schemaPath = resolveComponentPath(module, \"schema\", `${name.kebab}.schema`);\r\n const schemaContent = schemaStub(name);\r\n\r\n await putFileAsync(schemaPath, schemaContent);\r\n }\r\n\r\n console.log(colors.cyan(`\\n✨ Controller \"${name.camel}\" generated successfully!`));\r\n}\r\n"],"mappings":";;;;;;;AAYA,eAAsB,mBAAmB,MAAwC;CAC/E,MAAM,QAAQ,KAAK,KAAK;CAExB,IAAI,CAAC,OAAO;EACV,QAAQ,IAAI,OAAO,IAAI,oCAAoC,CAAC;EAC5D,QAAQ,IAAI,OAAO,OAAO,kDAAkD,CAAC;EAC7E,QAAQ,IAAI,OAAO,OAAO,sDAAsD,CAAC;EACjF,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,EAAE,QAAQ,MAAM,kBAAkB,gBAAgB,KAAK;CAE7D,IAAI,CAAC,QAAQ;EACX,QAAQ,IAAI,OAAO,IAAI,gCAAgC,CAAC;EACxD,QAAQ,IAAI,OAAO,OAAO,kDAAkD,CAAC;EAC7E,QAAQ,KAAK,CAAC;CAChB;CAGA,IAAI,CAAE,MAAM,aAAa,MAAM,GAAI;EACjC,QAAQ,IAAI,OAAO,IAAI,kBAAkB,OAAO,iBAAiB,CAAC;EAClE,QAAQ,IAAI,OAAO,OAAO,8BAA8B,QAAQ,CAAC;EACjE,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,OAAO,UAAU,aAAa;CACpC,MAAM,iBAAiB,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;CACnE,MAAM,QAAQ,KAAK,QAAQ,SAAS,KAAK,QAAQ;CACjD,UAAU,QAAQ,KAAK,QAAQ,MAAM,CAAC;CAGtC,MAAM,iBAAiB,qBAAqB,QAAQ,eAAe,GAAG,KAAK,MAAM,YAAY;CAC7F,IAAK,MAAM,gBAAgB,QAAQ,eAAe,GAAG,KAAK,MAAM,YAAY,KAAM,CAAC,OAAO;EACxF,QAAQ,IAAI,OAAO,IAAI,sBAAsB,KAAK,MAAM,+BAA+B,CAAC;EACxF,QAAQ,IAAI,OAAO,OAAO,0BAA0B,CAAC;EACrD,QAAQ,KAAK,CAAC;CAChB;CAGA,MAAM,yBAAyB,QAAQ,aAAa;CAIpD,MAAM,aAAa,gBADO,eAAe,MAAM,EAAE,gBAAgB,CAAC,CAAC,eAAe,CAC/B,CAAC;CAKpD,IAAI,gBAAgB;EAClB,MAAM,yBAAyB,QAAQ,QAAQ;EAK/C,MAAM,aAHa,qBAAqB,QAAQ,UAAU,GAAG,KAAK,MAAM,QAG5C,GAFN,WAAW,IAEU,CAAC;CAC9C;CAEA,QAAQ,IAAI,OAAO,KAAK,qBAAqB,KAAK,MAAM,0BAA0B,CAAC;AACrF"}
|
|
@@ -4,8 +4,8 @@ import { migrationAlterStub, migrationStub } from "../templates/stubs.mjs";
|
|
|
4
4
|
import { parseName } from "../utils/name-parser.mjs";
|
|
5
5
|
import { ensureDirectoryAsync, putFileAsync, setDryRun } from "../utils/writer.mjs";
|
|
6
6
|
import { parseColumnDsl } from "./column-dsl-parser.mjs";
|
|
7
|
-
import { colors } from "@mongez/copper";
|
|
8
7
|
import path from "node:path";
|
|
8
|
+
import { colors } from "@mongez/copper";
|
|
9
9
|
|
|
10
10
|
//#region ../core/src/cli/commands/generate/generators/migration.generator.ts
|
|
11
11
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/migration.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"
|
|
1
|
+
{"version":3,"file":"migration.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/migration.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport { appPath } from \"../../../../utils\";\r\nimport type { CommandActionData } from \"../../../../commands/types\";\r\nimport { migrationAlterStub, migrationStub } from \"../templates/stubs\";\r\nimport { parseColumnDsl } from \"./column-dsl-parser\";\r\nimport { parseName } from \"../utils/name-parser\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\n\r\n/**\r\n * Generate a migration file for a model\r\n */\r\n/**\r\n * Create a migration file for a model\r\n */\r\nexport async function createMigrationFile(moduleName: string, entityName: string, options: any = {}) {\r\n const entity = parseName(entityName);\r\n\r\n // Generate timestamp: MM-DD-YYYY_HH-MM-SS\r\n const now = new Date();\r\n const timestamp = `${String(now.getMonth() + 1).padStart(2, \"0\")}-${String(now.getDate()).padStart(2, \"0\")}-${now.getFullYear()}_${String(now.getHours()).padStart(2, \"0\")}-${String(now.getMinutes()).padStart(2, \"0\")}-${String(now.getSeconds()).padStart(2, \"0\")}`;\r\n\r\n const migrationFileName = `${timestamp}-${entity.kebab}.migration.ts`;\r\n const migrationsPath = path.join(appPath(), moduleName, \"models\", entity.kebab, \"migrations\");\r\n\r\n // Ensure migrations directory exists\r\n await ensureDirectoryAsync(migrationsPath);\r\n\r\n const addParams = options.add as string;\r\n const dropParams = options.drop as string;\r\n const renameParams = options.rename as string;\r\n const timestamps = options.timestamps !== \"false\" && options.timestamps !== false;\r\n\r\n let migrationContent = \"\";\r\n\r\n if (addParams || dropParams || renameParams) {\r\n // Generate alter stub\r\n const parsedAdd = parseColumnDsl(addParams || \"\");\r\n const helpersSet = new Set<string>();\r\n \r\n const addLines = parsedAdd.map((col) => {\r\n helpersSet.add(col.helper);\r\n return ` ${col.name}: ${col.helper}()${col.modifiers.join(\"\")},`;\r\n });\r\n\r\n let formattedDrop: string | undefined = undefined;\r\n if (dropParams) {\r\n formattedDrop = JSON.stringify(dropParams.split(\",\").map((s) => s.trim()));\r\n }\r\n\r\n let formattedRename: string | undefined = undefined;\r\n if (renameParams) {\r\n const obj: Record<string, string> = {};\r\n renameParams.split(\",\").map((s) => s.trim()).forEach((p) => {\r\n const [oldN, newN] = p.split(\":\").map((s) => s.trim());\r\n if (oldN && newN) obj[oldN] = newN;\r\n });\r\n formattedRename = JSON.stringify(obj, null, 2).replace(/\\n/g, '\\n ');\r\n }\r\n\r\n migrationContent = migrationAlterStub(entity, {\r\n add: addLines.length > 0 ? addLines.join(\"\\n\") : undefined,\r\n drop: formattedDrop,\r\n rename: formattedRename,\r\n imports: Array.from(helpersSet),\r\n });\r\n } else {\r\n // Generate create stub\r\n migrationContent = migrationStub(entity, {\r\n timestamps,\r\n });\r\n }\r\n\r\n // Create migration file\r\n const migrationFilePath = path.join(migrationsPath, migrationFileName);\r\n await putFileAsync(migrationFilePath, migrationContent);\r\n\r\n return migrationFilePath;\r\n}\r\n\r\n/**\r\n * Generate a migration file for a model\r\n */\r\nexport async function generateMigration(data: CommandActionData) {\r\n const modelPath = data.args[0] as string;\r\n\r\n if (!modelPath) {\r\n console.log(colors.red(\"Error: Model path is required\"));\r\n console.log(colors.gray(\"Usage: warlock gen.migration <model-path>\"));\r\n console.log(colors.gray(\"Example: warlock gen.migration products/product\"));\r\n return;\r\n }\r\n\r\n // Parse model path (e.g., \"products/product\")\r\n const [moduleName, entityName] = modelPath.split(\"/\");\r\n\r\n if (!moduleName || !entityName) {\r\n console.log(colors.red(\"Error: Invalid model path format. Expected: <module>/<entity>\"));\r\n console.log(colors.gray(\"Example: warlock gen.migration products/product\"));\r\n return;\r\n }\r\n\r\n setDryRun(Boolean(data.options.dryRun));\r\n\r\n const migrationFilePath = await createMigrationFile(moduleName, entityName, data.options);\r\n\r\n console.log(\r\n colors.cyan(`\\n✨ Migration file created at: ${path.relative(appPath(), migrationFilePath)}`),\r\n );\r\n}\r\n"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { migrationStub, modelStub } from "../templates/stubs.mjs";
|
|
|
2
2
|
import { parseModulePath, singularName } from "../utils/name-parser.mjs";
|
|
3
3
|
import { ensureDirectoryAsync, putFileAsync, setDryRun } from "../utils/writer.mjs";
|
|
4
4
|
import { componentExists, moduleExists, resolveModulePath } from "../utils/path-resolver.mjs";
|
|
5
|
-
import { colors } from "@mongez/copper";
|
|
6
5
|
import path from "node:path";
|
|
6
|
+
import { colors } from "@mongez/copper";
|
|
7
7
|
|
|
8
8
|
//#region ../core/src/cli/commands/generate/generators/model.generator.ts
|
|
9
9
|
async function generateModel(data) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/model.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport type { CommandActionData } from \"
|
|
1
|
+
{"version":3,"file":"model.generator.mjs","names":[],"sources":["../../../../../../../../../../core/src/cli/commands/generate/generators/model.generator.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\r\nimport path from \"node:path\";\r\nimport type { CommandActionData } from \"../../../../commands/types\";\r\nimport { migrationStub, modelStub } from \"../templates/stubs\";\r\nimport { parseModulePath, singularName } from \"../utils/name-parser\";\r\nimport { componentExists, moduleExists, resolveModulePath } from \"../utils/path-resolver\";\r\nimport { ensureDirectoryAsync, putFileAsync, setDryRun } from \"../utils/writer\";\r\n\r\nexport async function generateModel(data: CommandActionData): Promise<void> {\r\n const input = data.args[0];\r\n\r\n if (!input) {\r\n console.log(colors.red(\"Error: Model name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock create.model <module>/<name>\"));\r\n console.log(colors.yellow(\"Example: warlock create.model users/user\"));\r\n process.exit(1);\r\n }\r\n\r\n const { module, name: componentName } = parseModulePath(input);\r\n\r\n if (!module) {\r\n console.log(colors.red(\"Error: Module name is required\"));\r\n console.log(colors.yellow(\"Usage: warlock create.model <module>/<name>\"));\r\n process.exit(1);\r\n }\r\n\r\n // Check if module exists\r\n if (!(await moduleExists(module))) {\r\n console.log(colors.red(`Error: Module \"${module}\" does not exist`));\r\n console.log(colors.yellow(`Run: warlock create.module ${module}`));\r\n process.exit(1);\r\n }\r\n\r\n const name = singularName(componentName);\r\n const force = data.options.force || data.options.f;\r\n setDryRun(Boolean(data.options.dryRun));\r\n const withResource = data.options.withResource || data.options.rs;\r\n const tableName = (data.options.table as string) || name.plural.snake;\r\n\r\n // Check if model already exists\r\n const modelDir = path.join(resolveModulePath(module), \"models\", name.kebab);\r\n const modelPath = path.join(modelDir, `${name.kebab}.model.ts`);\r\n\r\n if ((await componentExists(module, `models/${name.kebab}`, `${name.kebab}.model`)) && !force) {\r\n console.log(colors.red(`Error: Model \"${name.kebab}\" already exists`));\r\n console.log(colors.yellow(\"Use --force to overwrite\"));\r\n process.exit(1);\r\n }\r\n\r\n // Ensure directories exist\r\n await ensureDirectoryAsync(modelDir);\r\n await ensureDirectoryAsync(path.join(modelDir, \"migrations\"));\r\n\r\n // Generate model\r\n const modelContent = modelStub(name, { tableName, withResource: !!withResource });\r\n await putFileAsync(modelPath, modelContent);\r\n\r\n // Generate index.ts\r\n const indexContent = `export * from \"./${name.kebab}.model\";\r\n`;\r\n await putFileAsync(path.join(modelDir, \"index.ts\"), indexContent);\r\n\r\n // Generate migration\r\n const timestamp = new Date().toISOString().replace(/[-:T]/g, \"_\").split(\".\")[0];\r\n const migrationPath = path.join(\r\n modelDir,\r\n \"migrations\",\r\n `${timestamp}_${name.kebab}.migration.ts`,\r\n );\r\n\r\n const migrationContent = migrationStub(name, {\r\n timestamps: data.options.timestamps !== \"false\" && data.options.timestamps !== false,\r\n });\r\n\r\n await putFileAsync(migrationPath, migrationContent);\r\n\r\n console.log(colors.cyan(`\\n✨ Model \"${name.pascal}\" generated successfully!`));\r\n console.log(colors.gray(`\\nNext steps:`));\r\n console.log(colors.gray(` 1. Update model schema in ${name.kebab}.model.ts`));\r\n console.log(\r\n colors.gray(` 2. Update migration in migrations/${timestamp}_${name.kebab}.migration.ts`),\r\n );\r\n console.log(colors.gray(` 3. Run migration: warlock migrate`));\r\n}\r\n"],"mappings":";;;;;;;;AAQA,eAAsB,cAAc,MAAwC;CAC1E,MAAM,QAAQ,KAAK,KAAK;CAExB,IAAI,CAAC,OAAO;EACV,QAAQ,IAAI,OAAO,IAAI,+BAA+B,CAAC;EACvD,QAAQ,IAAI,OAAO,OAAO,6CAA6C,CAAC;EACxE,QAAQ,IAAI,OAAO,OAAO,0CAA0C,CAAC;EACrE,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,EAAE,QAAQ,MAAM,kBAAkB,gBAAgB,KAAK;CAE7D,IAAI,CAAC,QAAQ;EACX,QAAQ,IAAI,OAAO,IAAI,gCAAgC,CAAC;EACxD,QAAQ,IAAI,OAAO,OAAO,6CAA6C,CAAC;EACxE,QAAQ,KAAK,CAAC;CAChB;CAGA,IAAI,CAAE,MAAM,aAAa,MAAM,GAAI;EACjC,QAAQ,IAAI,OAAO,IAAI,kBAAkB,OAAO,iBAAiB,CAAC;EAClE,QAAQ,IAAI,OAAO,OAAO,8BAA8B,QAAQ,CAAC;EACjE,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,OAAO,aAAa,aAAa;CACvC,MAAM,QAAQ,KAAK,QAAQ,SAAS,KAAK,QAAQ;CACjD,UAAU,QAAQ,KAAK,QAAQ,MAAM,CAAC;CACtC,MAAM,eAAe,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CAC/D,MAAM,YAAa,KAAK,QAAQ,SAAoB,KAAK,OAAO;CAGhE,MAAM,WAAW,KAAK,KAAK,kBAAkB,MAAM,GAAG,UAAU,KAAK,KAAK;CAC1E,MAAM,YAAY,KAAK,KAAK,UAAU,GAAG,KAAK,MAAM,UAAU;CAE9D,IAAK,MAAM,gBAAgB,QAAQ,UAAU,KAAK,SAAS,GAAG,KAAK,MAAM,OAAO,KAAM,CAAC,OAAO;EAC5F,QAAQ,IAAI,OAAO,IAAI,iBAAiB,KAAK,MAAM,iBAAiB,CAAC;EACrE,QAAQ,IAAI,OAAO,OAAO,0BAA0B,CAAC;EACrD,QAAQ,KAAK,CAAC;CAChB;CAGA,MAAM,qBAAqB,QAAQ;CACnC,MAAM,qBAAqB,KAAK,KAAK,UAAU,YAAY,CAAC;CAI5D,MAAM,aAAa,WADE,UAAU,MAAM;EAAE;EAAW,cAAc,CAAC,CAAC;CAAa,CACtC,CAAC;CAG1C,MAAM,eAAe,oBAAoB,KAAK,MAAM;;CAEpD,MAAM,aAAa,KAAK,KAAK,UAAU,UAAU,GAAG,YAAY;CAGhE,MAAM,6BAAY,IAAI,KAAK,EAAC,CAAC,YAAY,CAAC,CAAC,QAAQ,UAAU,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;CAW7E,MAAM,aAVgB,KAAK,KACzB,UACA,cACA,GAAG,UAAU,GAAG,KAAK,MAAM,cAOE,GAJN,cAAc,MAAM,EAC3C,YAAY,KAAK,QAAQ,eAAe,WAAW,KAAK,QAAQ,eAAe,MACjF,CAEiD,CAAC;CAElD,QAAQ,IAAI,OAAO,KAAK,gBAAgB,KAAK,OAAO,0BAA0B,CAAC;CAC/E,QAAQ,IAAI,OAAO,KAAK,eAAe,CAAC;CACxC,QAAQ,IAAI,OAAO,KAAK,+BAA+B,KAAK,MAAM,UAAU,CAAC;CAC7E,QAAQ,IACN,OAAO,KAAK,uCAAuC,UAAU,GAAG,KAAK,MAAM,cAAc,CAC3F;CACA,QAAQ,IAAI,OAAO,KAAK,qCAAqC,CAAC;AAChE"}
|