create-warlock 5.29.0 → 5.31.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 +39 -0
- package/README.md +66 -15
- package/esm/commands/create-new-app/get-app-path.mjs +1 -1
- package/esm/commands/create-new-app/get-app-path.mjs.map +1 -1
- package/esm/commands/create-new-app/index.mjs +20 -14
- package/esm/commands/create-new-app/index.mjs.map +1 -1
- package/esm/commands/create-new-app/types.d.mts +3 -2
- package/esm/commands/create-warlock-app/index.mjs +2 -1
- package/esm/commands/create-warlock-app/index.mjs.map +1 -1
- package/esm/features/agent-kit-targets.mjs +1 -1
- package/esm/features/agent-kit-targets.mjs.map +1 -1
- package/esm/features/database-drivers.mjs +1 -1
- package/esm/features/database-drivers.mjs.map +1 -1
- package/esm/features/features-map.mjs +13 -13
- package/esm/features/features-map.mjs.map +1 -1
- package/esm/flags/assert-flag-combinations.mjs +1 -1
- package/esm/flags/assert-flag-combinations.mjs.map +1 -1
- package/esm/flags/assert-package-manager.mjs +1 -1
- package/esm/flags/assert-package-manager.mjs.map +1 -1
- package/esm/flags/definitions.mjs +6 -1
- package/esm/flags/definitions.mjs.map +1 -1
- package/esm/flags/seed-from-flags.mjs +2 -1
- package/esm/flags/seed-from-flags.mjs.map +1 -1
- package/esm/helpers/app.mjs +72 -6
- package/esm/helpers/app.mjs.map +1 -1
- package/esm/helpers/exec.mjs +1 -1
- package/esm/helpers/exec.mjs.map +1 -1
- package/esm/helpers/insert-import.mjs +1 -1
- package/esm/helpers/insert-import.mjs.map +1 -1
- package/esm/helpers/package-manager.mjs +22 -2
- package/esm/helpers/package-manager.mjs.map +1 -1
- package/esm/helpers/paths.mjs +1 -1
- package/esm/helpers/paths.mjs.map +1 -1
- package/esm/helpers/pnpm-build-approvals.mjs +58 -3
- package/esm/helpers/pnpm-build-approvals.mjs.map +1 -1
- package/esm/helpers/project-builder-helpers.mjs +1 -1
- package/esm/helpers/project-builder-helpers.mjs.map +1 -1
- package/esm/helpers/tty.mjs +1 -1
- package/esm/helpers/tty.mjs.map +1 -1
- package/esm/helpers/warlock-versions.mjs +1 -1
- package/esm/helpers/warlock-versions.mjs.map +1 -1
- package/esm/index.d.mts +1 -1
- package/esm/index.mjs +6 -2
- package/esm/index.mjs.map +1 -1
- package/esm/prompts/ask-agent-targets.mjs +4 -2
- package/esm/prompts/ask-agent-targets.mjs.map +1 -1
- package/esm/prompts/ask-ai-features.mjs +5 -3
- package/esm/prompts/ask-ai-features.mjs.map +1 -1
- package/esm/prompts/ask-features.mjs +53 -0
- package/esm/prompts/ask-features.mjs.map +1 -0
- package/esm/prompts/ask-lint-toolchain.mjs +21 -0
- package/esm/prompts/ask-lint-toolchain.mjs.map +1 -0
- package/esm/prompts/ask-project-name.mjs +1 -1
- package/esm/prompts/ask-project-name.mjs.map +1 -1
- package/esm/prompts/ask-stack.mjs +1 -1
- package/esm/prompts/ask-stack.mjs.map +1 -1
- package/esm/prompts/fit-to-terminal.mjs +46 -0
- package/esm/prompts/fit-to-terminal.mjs.map +1 -0
- package/esm/summary.mjs +12 -2
- package/esm/summary.mjs.map +1 -1
- package/esm/ui/banner.mjs +115 -47
- package/esm/ui/banner.mjs.map +1 -1
- package/esm/ui/report.mjs +1 -1
- package/esm/ui/report.mjs.map +1 -1
- package/esm/ui/spinner.mjs +1 -1
- package/esm/ui/spinner.mjs.map +1 -1
- package/esm/ui/spinners.mjs +1 -1
- package/esm/ui/spinners.mjs.map +1 -1
- package/esm/ui/wrap-text.mjs +55 -0
- package/esm/ui/wrap-text.mjs.map +1 -0
- package/llms-full.txt +79 -29
- package/package.json +6 -5
- package/skills/create-a-warlock-project/SKILL.md +79 -29
- package/templates/toolchains/oxc/.oxfmtrc.json +12 -0
- package/templates/toolchains/oxc/.oxlintrc.json +24 -0
- package/templates/toolchains/oxc/.vscode/extensions.json +3 -0
- package/templates/toolchains/oxc/.vscode/settings.json +35 -0
- package/templates/warlock/AGENTS.md +22 -2
- package/templates/warlock/README.md +5 -3
- package/templates/warlock/package.json +6 -4
- package/templates/warlock/skills/api-design/SKILL.md +2 -2
- package/templates/warlock/skills/code-standards/SKILL.md +18 -4
- package/templates/warlock/skills/security-baseline/SKILL.md +12 -1
- package/templates/warlock/skills/testing-strategy/SKILL.md +17 -0
- package/templates/warlock/src/app/auth/routes.ts +25 -5
- package/templates/warlock/src/app/auth/utils/types.ts +6 -1
- package/templates/warlock/src/app/contact/routes.ts +1 -1
- package/templates/warlock/src/app/locale/routes.ts +1 -1
- package/templates/warlock/src/app/users/controllers/list-users.controller.ts +10 -2
- package/templates/warlock/src/app/users/schema/list-users.schema.ts +15 -0
- package/templates/warlock/src/config/tests.ts +8 -0
- package/templates/warlock/src/web/404.css +2 -6
- package/templates/warlock/src/web/root.tsx +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrap-text.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/ui/wrap-text.ts"],"sourcesContent":["import { stripVTControlCharacters } from \"node:util\";\n\nconst segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\nexport function visibleWidth(text: string): number {\n let width = 0;\n for (const { segment } of segmenter.segment(stripVTControlCharacters(text))) {\n if (/^[\\p{Mark}\\p{Control}\\p{Format}]+$/u.test(segment)) continue;\n const point = segment.codePointAt(0)!;\n const wide =\n /[\\p{Extended_Pictographic}\\p{Regional_Indicator}]/u.test(segment) ||\n (point >= 0x1100 &&\n (point <= 0x115f ||\n point === 0x2329 ||\n point === 0x232a ||\n (point >= 0x2e80 && point <= 0xa4cf) ||\n (point >= 0xac00 && point <= 0xd7a3) ||\n (point >= 0xf900 && point <= 0xfaff) ||\n (point >= 0xfe10 && point <= 0xfe6f) ||\n (point >= 0xff01 && point <= 0xff60) ||\n (point >= 0xffe0 && point <= 0xffe6) ||\n (point >= 0x20000 && point <= 0x3fffd)));\n width += wide ? 2 : 1;\n }\n return width;\n}\n\nexport function wrapText(text: string, width: number): string[] {\n const lines: string[] = [];\n let line = \"\";\n for (const word of stripVTControlCharacters(text).split(/\\s+/u)) {\n if (line && visibleWidth(`${line} ${word}`) > width) {\n lines.push(line);\n line = \"\";\n }\n for (const { segment } of segmenter.segment(`${line ? \" \" : \"\"}${word}`)) {\n if (visibleWidth(line + segment) > width) {\n lines.push(line);\n line = \"\";\n }\n line += segment;\n }\n }\n if (line || !lines.length) lines.push(line);\n return lines;\n}\n\nexport type StyledSegment = { text: string; style: (text: string) => string };\n\nexport function wrapStyledText(segments: StyledSegment[], width: number): string[] {\n const text = segments.map((segment) => segment.text).join(\"\");\n let offset = 0;\n return wrapText(text, width).map((line) => {\n while (/\\s/u.test(text[offset] ?? \"\") && offset < text.length) offset++;\n const start = offset;\n offset += line.length;\n let segmentStart = 0;\n return segments\n .map((segment) => {\n const segmentEnd = segmentStart + segment.text.length;\n const from = Math.max(start, segmentStart);\n const to = Math.min(offset, segmentEnd);\n const result =\n to > from\n ? segment.style(segment.text.slice(from - segmentStart, to - segmentStart))\n : \"\";\n segmentStart = segmentEnd;\n return result;\n })\n .join(\"\");\n });\n}\n"],"mappings":";;;AAEA,MAAM,YAAY,IAAI,KAAK,UAAU,QAAW,EAAE,aAAa,WAAW,CAAC;AAE3E,SAAgB,aAAa,MAAsB;CACjD,IAAI,QAAQ;CACZ,KAAK,MAAM,EAAE,aAAa,UAAU,QAAQ,yBAAyB,IAAI,CAAC,GAAG;EAC3E,IAAI,sCAAsC,KAAK,OAAO,GAAG;EACzD,MAAM,QAAQ,QAAQ,YAAY,CAAC;EACnC,MAAM,OACJ,qDAAqD,KAAK,OAAO,KAChE,SAAS,SACP,SAAS,QACR,UAAU,QACV,UAAU,QACT,SAAS,SAAU,SAAS,SAC5B,SAAS,SAAU,SAAS,SAC5B,SAAS,SAAU,SAAS,SAC5B,SAAS,SAAU,SAAS,SAC5B,SAAS,SAAU,SAAS,SAC5B,SAAS,SAAU,SAAS,SAC5B,SAAS,UAAW,SAAS;EACpC,SAAS,OAAO,IAAI;CACtB;CACA,OAAO;AACT;AAEA,SAAgB,SAAS,MAAc,OAAyB;CAC9D,MAAM,QAAkB,CAAC;CACzB,IAAI,OAAO;CACX,KAAK,MAAM,QAAQ,yBAAyB,IAAI,CAAC,CAAC,MAAM,MAAM,GAAG;EAC/D,IAAI,QAAQ,aAAa,GAAG,KAAK,GAAG,MAAM,IAAI,OAAO;GACnD,MAAM,KAAK,IAAI;GACf,OAAO;EACT;EACA,KAAK,MAAM,EAAE,aAAa,UAAU,QAAQ,GAAG,OAAO,MAAM,KAAK,MAAM,GAAG;GACxE,IAAI,aAAa,OAAO,OAAO,IAAI,OAAO;IACxC,MAAM,KAAK,IAAI;IACf,OAAO;GACT;GACA,QAAQ;EACV;CACF;CACA,IAAI,QAAQ,CAAC,MAAM,QAAQ,MAAM,KAAK,IAAI;CAC1C,OAAO;AACT;AAIA,SAAgB,eAAe,UAA2B,OAAyB;CACjF,MAAM,OAAO,SAAS,KAAK,YAAY,QAAQ,IAAI,CAAC,CAAC,KAAK,EAAE;CAC5D,IAAI,SAAS;CACb,OAAO,SAAS,MAAM,KAAK,CAAC,CAAC,KAAK,SAAS;EACzC,OAAO,MAAM,KAAK,KAAK,WAAW,EAAE,KAAK,SAAS,KAAK,QAAQ;EAC/D,MAAM,QAAQ;EACd,UAAU,KAAK;EACf,IAAI,eAAe;EACnB,OAAO,SACJ,KAAK,YAAY;GAChB,MAAM,aAAa,eAAe,QAAQ,KAAK;GAC/C,MAAM,OAAO,KAAK,IAAI,OAAO,YAAY;GACzC,MAAM,KAAK,KAAK,IAAI,QAAQ,UAAU;GACtC,MAAM,SACJ,KAAK,OACD,QAAQ,MAAM,QAAQ,KAAK,MAAM,OAAO,cAAc,KAAK,YAAY,CAAC,IACxE;GACN,eAAe;GACf,OAAO;EACT,CAAC,CAAC,CACD,KAAK,EAAE;CACZ,CAAC;AACH"}
|
package/llms-full.txt
CHANGED
|
@@ -40,7 +40,7 @@ The first positional argument is the project name; it also becomes the directory
|
|
|
40
40
|
|
|
41
41
|
1. **Default (a TTY, no `--yes`, no `--interactive`)** — asks the project name (if not already given), API-only or full-stack web (`--stack`), then a database unless `--db` or `--no-db` already supplied it. The stack is the only choice that changes the generated app's shape.
|
|
42
42
|
2. **`--yes` (or no TTY at all — CI, a script, an agent harness)** — fully headless. Every answer comes from a flag or its default; nothing is ever prompted, and stdin is never read.
|
|
43
|
-
3. **`--interactive` (alias `--customize`)** — the full long-form wizard: project name, package manager, database driver, features, AI providers, git, JWT — one prompt after another, exactly like earlier `create-warlock` releases.
|
|
43
|
+
3. **`--interactive` (alias `--customize`)** — the full long-form wizard: project name, package manager, database driver, features, AI providers, lint/format toolchain, git, JWT — one prompt after another, exactly like earlier `create-warlock` releases.
|
|
44
44
|
|
|
45
45
|
**Without a terminal, `--yes` is not required.** When stdin is not a TTY — CI, a
|
|
46
46
|
script, a container, an agent harness — the scaffolder does not try to prompt,
|
|
@@ -54,7 +54,7 @@ that is just the project name, the one required answer with no default:
|
|
|
54
54
|
```
|
|
55
55
|
A project name is required and no terminal is available to ask for one. Pass it as
|
|
56
56
|
the first argument or --name=<name>. Non-interactive flags: --yes, --pm=<npm|yarn|pnpm>,
|
|
57
|
-
--db=<driver>|--no-db, --features=<list>, --ai=<list>, --git|--no-git, --jwt|--no-jwt.
|
|
57
|
+
--db=<driver>|--no-db, --features=<list>, --ai=<list>, --lint=<oxc|eslint>, --git|--no-git, --jwt|--no-jwt.
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## After scaffolding: the decisions summary
|
|
@@ -63,36 +63,42 @@ Because almost every choice defaults silently, `create-warlock` prints what it
|
|
|
63
63
|
decided right after scaffolding, e.g.:
|
|
64
64
|
|
|
65
65
|
```
|
|
66
|
-
Using npm · database: none · agent-kit targets: claude
|
|
66
|
+
Using npm · database: none · lint: eslint · agent-kit targets: claude
|
|
67
|
+
Features: none
|
|
67
68
|
change these — package manager: --pm, database: --db/--no-db, agents: --agents,
|
|
68
|
-
features: --features, ai: --ai, git: --git/--no-git, jwt: --jwt/--no-jwt — or
|
|
69
|
+
features: --features, ai: --ai, lint: --lint, git: --git/--no-git, jwt: --jwt/--no-jwt — or
|
|
69
70
|
re-run with --interactive for the full wizard.
|
|
70
71
|
```
|
|
71
72
|
|
|
72
73
|
## Flags
|
|
73
74
|
|
|
74
|
-
| Flag
|
|
75
|
-
|
|
|
76
|
-
| `<positional>` / `--name`
|
|
77
|
-
| `--stack`
|
|
78
|
-
| `--db`
|
|
79
|
-
| `--no-db`
|
|
80
|
-
| `--pm`
|
|
81
|
-
| `--features`
|
|
82
|
-
| `--
|
|
83
|
-
| `--
|
|
84
|
-
| `--
|
|
85
|
-
| `--
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
| Flag | Takes value | Default | Purpose |
|
|
76
|
+
| ------------------------------ | ----------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
77
|
+
| `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
|
|
78
|
+
| `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds the `web` feature by default. |
|
|
79
|
+
| `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
|
|
80
|
+
| `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
|
|
81
|
+
| `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — must be one of `npm` / `yarn` / `pnpm` / `bun`; anything else is rejected before scaffolding starts. |
|
|
82
|
+
| `--features` | yes (CSV) | none, or `["web"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
|
|
83
|
+
| `--lint` | yes | `eslint` | `oxc` (Oxlint/Oxfmt) or `eslint` (ESLint/Prettier); unknown or missing values fail before writes. |
|
|
84
|
+
| `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
|
|
85
|
+
| `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
|
|
86
|
+
| `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
|
|
87
|
+
| `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
|
|
88
|
+
| `--yes` / `-y` | no | off | Fully headless mode. |
|
|
89
|
+
| `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
|
|
90
|
+
|
|
91
|
+
With `--pm=pnpm`, the scaffold reads `pnpm --version` and records install-script
|
|
92
|
+
approvals (esbuild, and Sharp when the `image` feature is selected) where that
|
|
93
|
+
version reads them: `allowBuilds` in `pnpm-workspace.yaml` on pnpm 10.26+, 11
|
|
94
|
+
and 12; that file plus `onlyBuiltDependencies` on 10.5–10.25; and, before 10.5,
|
|
95
|
+
no workspace file (those versions reject one without `packages:`) but
|
|
96
|
+
`pnpm.onlyBuiltDependencies` in `package.json`. An explicit existing denial is
|
|
97
|
+
retained. If selected feature dependencies do
|
|
90
98
|
not install, JWT generation is skipped and reported rather than retried through
|
|
91
99
|
the package manager.
|
|
92
|
-
| `--yes` / `-y` | no | off | Fully headless mode. |
|
|
93
|
-
| `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
|
|
94
100
|
|
|
95
|
-
Value flags accept either `--db=postgres` or `--db postgres`. Unknown `--features` / `--ai` / `--agents`
|
|
101
|
+
Value flags accept either `--db=postgres` or `--db postgres`. Unknown `--pm` / `--db` / `--features` / `--ai` / `--agents` / `--lint` values fail fast before any write or install.
|
|
96
102
|
|
|
97
103
|
`--help`/`-h` and `--version`/`-v` short-circuit before anything else — no prompt, filesystem write, or network call, and `--help` wins even over a positional project name. `--version` prints the installed `create-warlock` version. `--help` lists every flag above together with its default.
|
|
98
104
|
|
|
@@ -106,18 +112,18 @@ fallback that always works — even fully offline. Any other target
|
|
|
106
112
|
`goose`, …) is validated against the list published at
|
|
107
113
|
https://mongez.js.org/agent-kit/llms.txt, fetched once and cached locally. If
|
|
108
114
|
that fetch fails for an explicitly requested non-default target, the run fails
|
|
109
|
-
visibly and names every target it
|
|
115
|
+
visibly and names every target it _could_ determine — it never silently
|
|
110
116
|
guesses.
|
|
111
117
|
|
|
112
118
|
## Valid keys
|
|
113
119
|
|
|
114
120
|
**Database drivers** (`--db`):
|
|
115
121
|
|
|
116
|
-
| Key | Default port | Status
|
|
117
|
-
| ---------- | ------------ |
|
|
118
|
-
| `mongodb` | 27017 | available
|
|
119
|
-
| `postgres` | 5432 | available
|
|
120
|
-
| `mysql` | 3306 | coming soon (disabled in the wizard)
|
|
122
|
+
| Key | Default port | Status |
|
|
123
|
+
| ---------- | ------------ | ------------------------------------------------------------------------ |
|
|
124
|
+
| `mongodb` | 27017 | available |
|
|
125
|
+
| `postgres` | 5432 | available |
|
|
126
|
+
| `mysql` | 3306 | coming soon (disabled in the wizard) |
|
|
121
127
|
| `none` | — | opt out — no driver, no driver package, `src/config/database.ts` removed |
|
|
122
128
|
|
|
123
129
|
**Features** (`--features`): `react`, `react-email`, `mail`, `ses`, `image`, `s3`, `redis`, `scheduler`, `herald`, `socket`, `test`, `access`, `web`, `tailwind`, `shadcn`, `notifications`.
|
|
@@ -187,4 +193,48 @@ Failures surface the error and halt; there is no rollback. After it finishes, `c
|
|
|
187
193
|
- The `create-module` topic of the `warlock-js-core` skill — generate a new module inside the project.
|
|
188
194
|
- The `add-connector` topic of the `warlock-js-core` skill — what `warlock add` wires when a feature pulls a connector.
|
|
189
195
|
|
|
196
|
+
The project-ready summary wraps the complete feature list to your terminal width.
|
|
197
|
+
Terminals narrower than 50 columns get a plain list; piped output uses 80 columns.
|
|
198
|
+
|
|
199
|
+
## Custom feature groups
|
|
200
|
+
|
|
201
|
+
`--customize` opens a section menu for optional features. Open a section to edit
|
|
202
|
+
its checklist; **Select all** includes the remaining features while preserving
|
|
203
|
+
items you explicitly unticked. The category cursor stays on the last edited
|
|
204
|
+
section. Section
|
|
205
|
+
counts update when returning to the menu, and **Continue** keeps the selection.
|
|
206
|
+
Flags preselect their existing feature keys. Both menus and checklists retain
|
|
207
|
+
terminal paging. The decisions overview previews up to six feature/AI keys with
|
|
208
|
+
a total count; the success screen shows the complete list.
|
|
209
|
+
|
|
210
|
+
## Choose the linter and formatter
|
|
211
|
+
|
|
212
|
+
`--lint=eslint` is the default. `--lint=oxc` (also `--lint oxc`) installs exact
|
|
213
|
+
`oxlint@1.88.0` and `oxfmt@0.73.0` versions, emits `.oxlintrc.json` and
|
|
214
|
+
`.oxfmtrc.json`, and removes the template's ESLint/Prettier configs and dependencies.
|
|
215
|
+
The custom wizard prompts for the same choice; a flag preselects it.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm create warlock@latest my-app -- --lint=oxc --yes
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Both choices provide `lint`/`format` and non-mutating `lint:check`/`format:check`
|
|
222
|
+
scripts. Oxc projects also configure VS Code format-on-save with the official
|
|
223
|
+
`oxc.oxc-vscode` formatter, use `source.fixAll.oxc` for lint fixes and recommend
|
|
224
|
+
that extension. The ESLint toolchain retains its Prettier/ESLint editor settings.
|
|
225
|
+
Oxc config enables `devServer.healthCheckers: { eslint: false, oxlint: true }` and
|
|
226
|
+
leaves TypeScript health checks enabled. Style matches the template's 100-column,
|
|
227
|
+
double-quote, semicolon and trailing-comma settings. Import sorting is on in
|
|
228
|
+
`.oxfmtrc.json`, kept to one block like the ESLint toolchain's
|
|
229
|
+
`prettier-plugin-organize-imports`:
|
|
230
|
+
|
|
231
|
+
```json
|
|
232
|
+
{ "sortImports": { "newlinesBetween": false } }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`format` (`oxfmt --write ./src`) orders built-ins, then packages, then relative
|
|
236
|
+
imports. Oxfmt does not delete unused imports, so remove them from Oxlint's
|
|
237
|
+
`no-unused-vars` diagnostics. Scripts in both toolchains call local binaries
|
|
238
|
+
(`eslint --fix ./src --max-warnings=0`, `tsc --noEmit`), never `npx`.
|
|
239
|
+
|
|
190
240
|
|
package/package.json
CHANGED
|
@@ -4,24 +4,25 @@
|
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "https://github.com/warlockjs/
|
|
7
|
+
"url": "git+https://github.com/warlockjs/framework.git",
|
|
8
|
+
"directory": "packages/create-warlock"
|
|
8
9
|
},
|
|
9
10
|
"bin": {
|
|
10
11
|
"create-warlock": "bin/create-app.js"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
|
-
"@clack/prompts": "^0.
|
|
14
|
+
"@clack/prompts": "^0.8.2",
|
|
14
15
|
"@mongez/copper": "^2.1.3",
|
|
15
|
-
"@warlock.js/fs": "5.
|
|
16
|
+
"@warlock.js/fs": "5.31.0",
|
|
16
17
|
"@mongez/reinforcements": "^4.0.2",
|
|
17
18
|
"cross-spawn": "^7.0.3",
|
|
18
19
|
"rimraf": "^6.0.1",
|
|
19
20
|
"which-pm-runs": "^1.1.0"
|
|
20
21
|
},
|
|
21
22
|
"engines": {
|
|
22
|
-
"node": ">=22.
|
|
23
|
+
"node": ">=22.15"
|
|
23
24
|
},
|
|
24
|
-
"version": "5.
|
|
25
|
+
"version": "5.31.0",
|
|
25
26
|
"type": "module",
|
|
26
27
|
"main": "./esm/index.mjs",
|
|
27
28
|
"module": "./esm/index.mjs",
|
|
@@ -32,7 +32,7 @@ The first positional argument is the project name; it also becomes the directory
|
|
|
32
32
|
|
|
33
33
|
1. **Default (a TTY, no `--yes`, no `--interactive`)** — asks the project name (if not already given), API-only or full-stack web (`--stack`), then a database unless `--db` or `--no-db` already supplied it. The stack is the only choice that changes the generated app's shape.
|
|
34
34
|
2. **`--yes` (or no TTY at all — CI, a script, an agent harness)** — fully headless. Every answer comes from a flag or its default; nothing is ever prompted, and stdin is never read.
|
|
35
|
-
3. **`--interactive` (alias `--customize`)** — the full long-form wizard: project name, package manager, database driver, features, AI providers, git, JWT — one prompt after another, exactly like earlier `create-warlock` releases.
|
|
35
|
+
3. **`--interactive` (alias `--customize`)** — the full long-form wizard: project name, package manager, database driver, features, AI providers, lint/format toolchain, git, JWT — one prompt after another, exactly like earlier `create-warlock` releases.
|
|
36
36
|
|
|
37
37
|
**Without a terminal, `--yes` is not required.** When stdin is not a TTY — CI, a
|
|
38
38
|
script, a container, an agent harness — the scaffolder does not try to prompt,
|
|
@@ -46,7 +46,7 @@ that is just the project name, the one required answer with no default:
|
|
|
46
46
|
```
|
|
47
47
|
A project name is required and no terminal is available to ask for one. Pass it as
|
|
48
48
|
the first argument or --name=<name>. Non-interactive flags: --yes, --pm=<npm|yarn|pnpm>,
|
|
49
|
-
--db=<driver>|--no-db, --features=<list>, --ai=<list>, --git|--no-git, --jwt|--no-jwt.
|
|
49
|
+
--db=<driver>|--no-db, --features=<list>, --ai=<list>, --lint=<oxc|eslint>, --git|--no-git, --jwt|--no-jwt.
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
## After scaffolding: the decisions summary
|
|
@@ -55,36 +55,42 @@ Because almost every choice defaults silently, `create-warlock` prints what it
|
|
|
55
55
|
decided right after scaffolding, e.g.:
|
|
56
56
|
|
|
57
57
|
```
|
|
58
|
-
Using npm · database: none · agent-kit targets: claude
|
|
58
|
+
Using npm · database: none · lint: eslint · agent-kit targets: claude
|
|
59
|
+
Features: none
|
|
59
60
|
change these — package manager: --pm, database: --db/--no-db, agents: --agents,
|
|
60
|
-
features: --features, ai: --ai, git: --git/--no-git, jwt: --jwt/--no-jwt — or
|
|
61
|
+
features: --features, ai: --ai, lint: --lint, git: --git/--no-git, jwt: --jwt/--no-jwt — or
|
|
61
62
|
re-run with --interactive for the full wizard.
|
|
62
63
|
```
|
|
63
64
|
|
|
64
65
|
## Flags
|
|
65
66
|
|
|
66
|
-
| Flag
|
|
67
|
-
|
|
|
68
|
-
| `<positional>` / `--name`
|
|
69
|
-
| `--stack`
|
|
70
|
-
| `--db`
|
|
71
|
-
| `--no-db`
|
|
72
|
-
| `--pm`
|
|
73
|
-
| `--features`
|
|
74
|
-
| `--
|
|
75
|
-
| `--
|
|
76
|
-
| `--
|
|
77
|
-
| `--
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
| Flag | Takes value | Default | Purpose |
|
|
68
|
+
| ------------------------------ | ----------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| `<positional>` / `--name` | yes | — (required) | Project name + target directory. |
|
|
70
|
+
| `--stack` | yes | `api` | `api` or `web` — the structural choice; `web` seeds the `web` feature by default. |
|
|
71
|
+
| `--db` | yes | `mongodb` | Database driver key (`mongodb`, `postgres`, or `none`). |
|
|
72
|
+
| `--no-db` | no | — | Opt out of a database — shorthand for `--db=none`. |
|
|
73
|
+
| `--pm` | yes | inferred (`npm_config_user_agent` / system) | Package manager — must be one of `npm` / `yarn` / `pnpm` / `bun`; anything else is rejected before scaffolding starts. |
|
|
74
|
+
| `--features` | yes (CSV) | none, or `["web"]` when `--stack=web` and `--features` is not given | Comma-separated optional feature keys. An explicit `--features` always wins over the `--stack` default. |
|
|
75
|
+
| `--lint` | yes | `eslint` | `oxc` (Oxlint/Oxfmt) or `eslint` (ESLint/Prettier); unknown or missing values fail before writes. |
|
|
76
|
+
| `--ai` | yes (CSV) | none | Comma-separated AI provider keys (auto-pulls `@warlock.js/ai`). |
|
|
77
|
+
| `--agents` | yes (CSV) | `claude` | Comma-separated `agent-kit` targets — see below. |
|
|
78
|
+
| `--git` / `--no-git` | no | off | Initialize a Git repo (`git init` + `main` branch + initial commit). |
|
|
79
|
+
| `--jwt` / `--no-jwt` | no | off | Generate JWT secrets via the project's `jwt` script. |
|
|
80
|
+
| `--yes` / `-y` | no | off | Fully headless mode. |
|
|
81
|
+
| `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
|
|
82
|
+
|
|
83
|
+
With `--pm=pnpm`, the scaffold reads `pnpm --version` and records install-script
|
|
84
|
+
approvals (esbuild, and Sharp when the `image` feature is selected) where that
|
|
85
|
+
version reads them: `allowBuilds` in `pnpm-workspace.yaml` on pnpm 10.26+, 11
|
|
86
|
+
and 12; that file plus `onlyBuiltDependencies` on 10.5–10.25; and, before 10.5,
|
|
87
|
+
no workspace file (those versions reject one without `packages:`) but
|
|
88
|
+
`pnpm.onlyBuiltDependencies` in `package.json`. An explicit existing denial is
|
|
89
|
+
retained. If selected feature dependencies do
|
|
82
90
|
not install, JWT generation is skipped and reported rather than retried through
|
|
83
91
|
the package manager.
|
|
84
|
-
| `--yes` / `-y` | no | off | Fully headless mode. |
|
|
85
|
-
| `--interactive`, `--customize` | no | off | Restore the full long-form wizard. |
|
|
86
92
|
|
|
87
|
-
Value flags accept either `--db=postgres` or `--db postgres`. Unknown `--features` / `--ai` / `--agents`
|
|
93
|
+
Value flags accept either `--db=postgres` or `--db postgres`. Unknown `--pm` / `--db` / `--features` / `--ai` / `--agents` / `--lint` values fail fast before any write or install.
|
|
88
94
|
|
|
89
95
|
`--help`/`-h` and `--version`/`-v` short-circuit before anything else — no prompt, filesystem write, or network call, and `--help` wins even over a positional project name. `--version` prints the installed `create-warlock` version. `--help` lists every flag above together with its default.
|
|
90
96
|
|
|
@@ -98,18 +104,18 @@ fallback that always works — even fully offline. Any other target
|
|
|
98
104
|
`goose`, …) is validated against the list published at
|
|
99
105
|
https://mongez.js.org/agent-kit/llms.txt, fetched once and cached locally. If
|
|
100
106
|
that fetch fails for an explicitly requested non-default target, the run fails
|
|
101
|
-
visibly and names every target it
|
|
107
|
+
visibly and names every target it _could_ determine — it never silently
|
|
102
108
|
guesses.
|
|
103
109
|
|
|
104
110
|
## Valid keys
|
|
105
111
|
|
|
106
112
|
**Database drivers** (`--db`):
|
|
107
113
|
|
|
108
|
-
| Key | Default port | Status
|
|
109
|
-
| ---------- | ------------ |
|
|
110
|
-
| `mongodb` | 27017 | available
|
|
111
|
-
| `postgres` | 5432 | available
|
|
112
|
-
| `mysql` | 3306 | coming soon (disabled in the wizard)
|
|
114
|
+
| Key | Default port | Status |
|
|
115
|
+
| ---------- | ------------ | ------------------------------------------------------------------------ |
|
|
116
|
+
| `mongodb` | 27017 | available |
|
|
117
|
+
| `postgres` | 5432 | available |
|
|
118
|
+
| `mysql` | 3306 | coming soon (disabled in the wizard) |
|
|
113
119
|
| `none` | — | opt out — no driver, no driver package, `src/config/database.ts` removed |
|
|
114
120
|
|
|
115
121
|
**Features** (`--features`): `react`, `react-email`, `mail`, `ses`, `image`, `s3`, `redis`, `scheduler`, `herald`, `socket`, `test`, `access`, `web`, `tailwind`, `shadcn`, `notifications`.
|
|
@@ -178,3 +184,47 @@ Failures surface the error and halt; there is no rollback. After it finishes, `c
|
|
|
178
184
|
- The `run-app` topic of the `warlock-js-core` skill — `warlock dev` / `build` / `start` once the project exists.
|
|
179
185
|
- The `create-module` topic of the `warlock-js-core` skill — generate a new module inside the project.
|
|
180
186
|
- The `add-connector` topic of the `warlock-js-core` skill — what `warlock add` wires when a feature pulls a connector.
|
|
187
|
+
|
|
188
|
+
The project-ready summary wraps the complete feature list to your terminal width.
|
|
189
|
+
Terminals narrower than 50 columns get a plain list; piped output uses 80 columns.
|
|
190
|
+
|
|
191
|
+
## Custom feature groups
|
|
192
|
+
|
|
193
|
+
`--customize` opens a section menu for optional features. Open a section to edit
|
|
194
|
+
its checklist; **Select all** includes the remaining features while preserving
|
|
195
|
+
items you explicitly unticked. The category cursor stays on the last edited
|
|
196
|
+
section. Section
|
|
197
|
+
counts update when returning to the menu, and **Continue** keeps the selection.
|
|
198
|
+
Flags preselect their existing feature keys. Both menus and checklists retain
|
|
199
|
+
terminal paging. The decisions overview previews up to six feature/AI keys with
|
|
200
|
+
a total count; the success screen shows the complete list.
|
|
201
|
+
|
|
202
|
+
## Choose the linter and formatter
|
|
203
|
+
|
|
204
|
+
`--lint=eslint` is the default. `--lint=oxc` (also `--lint oxc`) installs exact
|
|
205
|
+
`oxlint@1.88.0` and `oxfmt@0.73.0` versions, emits `.oxlintrc.json` and
|
|
206
|
+
`.oxfmtrc.json`, and removes the template's ESLint/Prettier configs and dependencies.
|
|
207
|
+
The custom wizard prompts for the same choice; a flag preselects it.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm create warlock@latest my-app -- --lint=oxc --yes
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Both choices provide `lint`/`format` and non-mutating `lint:check`/`format:check`
|
|
214
|
+
scripts. Oxc projects also configure VS Code format-on-save with the official
|
|
215
|
+
`oxc.oxc-vscode` formatter, use `source.fixAll.oxc` for lint fixes and recommend
|
|
216
|
+
that extension. The ESLint toolchain retains its Prettier/ESLint editor settings.
|
|
217
|
+
Oxc config enables `devServer.healthCheckers: { eslint: false, oxlint: true }` and
|
|
218
|
+
leaves TypeScript health checks enabled. Style matches the template's 100-column,
|
|
219
|
+
double-quote, semicolon and trailing-comma settings. Import sorting is on in
|
|
220
|
+
`.oxfmtrc.json`, kept to one block like the ESLint toolchain's
|
|
221
|
+
`prettier-plugin-organize-imports`:
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{ "sortImports": { "newlinesBetween": false } }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`format` (`oxfmt --write ./src`) orders built-ins, then packages, then relative
|
|
228
|
+
imports. Oxfmt does not delete unused imports, so remove them from Oxlint's
|
|
229
|
+
`no-unused-vars` diagnostics. Scripts in both toolchains call local binaries
|
|
230
|
+
(`eslint --fix ./src --max-warnings=0`, `tsc --noEmit`), never `npx`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxfmt/configuration_schema.json",
|
|
3
|
+
"semi": true,
|
|
4
|
+
"tabWidth": 2,
|
|
5
|
+
"printWidth": 100,
|
|
6
|
+
"singleQuote": false,
|
|
7
|
+
"arrowParens": "always",
|
|
8
|
+
"trailingComma": "all",
|
|
9
|
+
"endOfLine": "lf",
|
|
10
|
+
"sortImports": { "newlinesBetween": false },
|
|
11
|
+
"ignorePatterns": ["node_modules/", "dist/"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["typescript", "import", "react"],
|
|
4
|
+
"env": { "node": true, "es2021": true },
|
|
5
|
+
"categories": { "correctness": "off" },
|
|
6
|
+
"rules": {
|
|
7
|
+
"typescript/no-explicit-any": "off",
|
|
8
|
+
"no-unused-vars": [
|
|
9
|
+
"warn",
|
|
10
|
+
{
|
|
11
|
+
"vars": "all",
|
|
12
|
+
"varsIgnorePattern": "^_",
|
|
13
|
+
"args": "after-used",
|
|
14
|
+
"argsIgnorePattern": "^_"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"typescript/explicit-member-accessibility": ["error", { "accessibility": "explicit" }],
|
|
18
|
+
"typescript/consistent-type-imports": [
|
|
19
|
+
"error",
|
|
20
|
+
{ "prefer": "type-imports", "disallowTypeAnnotations": false }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"ignorePatterns": ["node_modules/", "dist/"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
3
|
+
"editor.formatOnSave": true,
|
|
4
|
+
"editor.codeActionsOnSave": {
|
|
5
|
+
"source.fixAll.oxc": "explicit",
|
|
6
|
+
"source.organizeImports": "never"
|
|
7
|
+
},
|
|
8
|
+
"[typescript]": {
|
|
9
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
10
|
+
"editor.formatOnSave": true
|
|
11
|
+
},
|
|
12
|
+
"[typescriptreact]": {
|
|
13
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
14
|
+
"editor.formatOnSave": true
|
|
15
|
+
},
|
|
16
|
+
"[javascript]": {
|
|
17
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
18
|
+
"editor.formatOnSave": true
|
|
19
|
+
},
|
|
20
|
+
"[javascriptreact]": {
|
|
21
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
22
|
+
"editor.formatOnSave": true
|
|
23
|
+
},
|
|
24
|
+
"[json]": {
|
|
25
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
26
|
+
"editor.formatOnSave": true
|
|
27
|
+
},
|
|
28
|
+
"[css]": {
|
|
29
|
+
"editor.defaultFormatter": "oxc.oxc-vscode",
|
|
30
|
+
"editor.formatOnSave": true
|
|
31
|
+
},
|
|
32
|
+
"oxc.enable.oxlint": true,
|
|
33
|
+
"oxc.enable.oxfmt": true,
|
|
34
|
+
"oxc.requireConfig": true
|
|
35
|
+
}
|
|
@@ -80,8 +80,8 @@ automatically on `postinstall`).
|
|
|
80
80
|
- `yarn start` — start the production server (`warlock start`)
|
|
81
81
|
- `yarn migrate` / `yarn migrate.fresh` / `yarn migrate.list` — run migrations
|
|
82
82
|
- `yarn seed` — run database seeders
|
|
83
|
-
- `yarn lint` / `yarn format` / `yarn tsc` — lint (
|
|
84
|
-
|
|
83
|
+
- `yarn lint` / `yarn format` / `yarn tsc` — lint and format with the selected toolchain, and type-check (`tsc --noEmit`)
|
|
84
|
+
- `npx warlock check` — verify a change: typecheck, lint, the affected tests and the framework's conventions in one report (`--changed` for only what changed); run it after every change and loop until it exits 0
|
|
85
85
|
- `yarn skills:sync` — re-sync skills from `node_modules` and regenerate the
|
|
86
86
|
derived per-agent files from this `AGENTS.md`
|
|
87
87
|
|
|
@@ -98,6 +98,13 @@ suffixes, and JSDoc on the public surface. The bar is senior-level clean code, n
|
|
|
98
98
|
|
|
99
99
|
- Respect **module-boundaries** — do not reach across module internals.
|
|
100
100
|
- Never weaken the **security-baseline** — always validate input at boundaries.
|
|
101
|
+
- `npx warlock check` enforces these rules whichever linter the project uses:
|
|
102
|
+
pass `request.validated()` (never `request.all()`) to queries and services
|
|
103
|
+
(`WL9005`), give every POST/PUT/PATCH/DELETE route auth middleware or an
|
|
104
|
+
explicit `public: true` (`WL9006`), wrap request-supplied redirect targets in
|
|
105
|
+
`safeRedirect()` (`WL9007`), keep raw emails, tokens and request bodies out of
|
|
106
|
+
logs (`WL9008`) and bound regexes that run on request input (`WL9009`). Each
|
|
107
|
+
problem carries its fix; `npx warlock explain <CODE>` prints it again.
|
|
101
108
|
- Follow **git-workflow** for commits and branches; do not commit or push unless
|
|
102
109
|
explicitly asked.
|
|
103
110
|
- When you are unsure how a package or feature works, **stop and read its skill**
|
|
@@ -122,3 +129,16 @@ Three things that trip everyone up on a fresh app:
|
|
|
122
129
|
instance does not expose its columns as direct properties — `post.title` is
|
|
123
130
|
`undefined` at runtime and a type error. Use `post.get("title")`, or
|
|
124
131
|
`post.toJSON()` in a page loader (resources are the serialization gate).
|
|
132
|
+
|
|
133
|
+
## This app (generated)
|
|
134
|
+
|
|
135
|
+
`warlock dev` rewrites everything between the two markers below from the
|
|
136
|
+
running app — modules, routes, models, installed skills and the commands to
|
|
137
|
+
verify a change. Edit outside the markers; turn it off with
|
|
138
|
+
`devServer.agentsMd: false` in `warlock.config.ts`.
|
|
139
|
+
|
|
140
|
+
<!-- warlock:generated:start -->
|
|
141
|
+
|
|
142
|
+
Run `npx warlock dev` (or `npx warlock agents-md --write`) to fill this section.
|
|
143
|
+
|
|
144
|
+
<!-- warlock:generated:end -->
|
|
@@ -7,7 +7,7 @@ Full documentation: [warlock.js.org](https://warlock.js.org).
|
|
|
7
7
|
|
|
8
8
|
## Requirements
|
|
9
9
|
|
|
10
|
-
- Node.js **22.
|
|
10
|
+
- Node.js **22.15** or newer
|
|
11
11
|
- The database you picked when scaffolding (MongoDB or PostgreSQL), running and
|
|
12
12
|
reachable with the settings in `.env` — skip this if you scaffolded with no
|
|
13
13
|
database
|
|
@@ -40,8 +40,10 @@ It restarts on file changes.
|
|
|
40
40
|
| `npm run seed` | Run the database seeders |
|
|
41
41
|
| `npm run jwt` | Generate a JWT secret into `.env` |
|
|
42
42
|
| `npm run tsc` | Type-check without building |
|
|
43
|
-
| `npm run lint` |
|
|
44
|
-
| `npm run format` |
|
|
43
|
+
| `npm run lint` | The configured linter, auto-fixing what it can |
|
|
44
|
+
| `npm run format` | The configured formatter over `src/` |
|
|
45
|
+
| `npm run lint:check` | Lint without changing files |
|
|
46
|
+
| `npm run format:check` | Check formatting without changing files |
|
|
45
47
|
| `npm run skills:sync` | Refresh the AI agent skills (see below) |
|
|
46
48
|
|
|
47
49
|
The `warlock` CLI has more than the scripts expose — run `npx warlock --help`
|
|
@@ -19,12 +19,14 @@
|
|
|
19
19
|
"gen.mig": "warlock generate.migration",
|
|
20
20
|
"gen.r": "warlock generate.repository",
|
|
21
21
|
"gen.rs": "warlock generate.resource",
|
|
22
|
-
"lint": "
|
|
23
|
-
"format": "
|
|
24
|
-
"tsc": "warlock generate.typings &&
|
|
22
|
+
"lint": "eslint --fix ./src --max-warnings=0",
|
|
23
|
+
"format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,css,md,json}\" --config ./.prettierrc.json",
|
|
24
|
+
"tsc": "warlock generate.typings && tsc --noEmit",
|
|
25
25
|
"postinstall": "agent-kit init && agent-kit sync || echo \"warning: AI agent skills were NOT synced, so coding agents will not see the @warlock.js skills. Run the skills:sync script.\"",
|
|
26
26
|
"skills:sync": "agent-kit sync",
|
|
27
|
-
"skills:watch": "agent-kit watch"
|
|
27
|
+
"skills:watch": "agent-kit watch",
|
|
28
|
+
"lint:check": "eslint ./src --max-warnings=0",
|
|
29
|
+
"format:check": "prettier --check \"./src/**/*.{js,jsx,ts,tsx,css,md,json}\" --config ./.prettierrc.json"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@mongez/reinforcements": "^4.0.2",
|
|
@@ -24,8 +24,8 @@ The list lands under the **plural name of the module's resource** — `users`, `
|
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
26
|
// ✅ list-users.controller.ts
|
|
27
|
-
export const listUsersController: RequestHandler = async (request, response) => {
|
|
28
|
-
const { data, pagination } = await listUsersService(request.
|
|
27
|
+
export const listUsersController: RequestHandler = async ({ request, response }) => {
|
|
28
|
+
const { data, pagination } = await listUsersService(request.validated());
|
|
29
29
|
|
|
30
30
|
return response.success({
|
|
31
31
|
users: data,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: code-standards
|
|
3
|
-
description: 'Project-level TypeScript code standards for apps built on Warlock.js — `interface` vs `type`, access modifiers, no `any`, no inline-duplicated unions, file-naming suffixes (`.contract.ts` / `.type.ts` / `.service.ts` / `.model.ts` / `.resource.ts` / `.spec.ts`), single-responsibility files, FP-factory + internal-class pattern, brace-every-control-block readability with blank lines between consecutive blocks, JSDoc on public surface and non-obvious logic, error classes extending framework errors, async/await + `Promise.all`, `undefined` over `null`, Vitest co-located, ESLint
|
|
3
|
+
description: 'Project-level TypeScript code standards for apps built on Warlock.js — `interface` vs `type`, access modifiers, no `any`, no inline-duplicated unions, file-naming suffixes (`.contract.ts` / `.type.ts` / `.service.ts` / `.model.ts` / `.resource.ts` / `.spec.ts`), single-responsibility files, FP-factory + internal-class pattern, brace-every-control-block readability with blank lines between consecutive blocks, JSDoc on public surface and non-obvious logic, error classes extending framework errors, async/await + `Promise.all`, `undefined` over `null`, Vitest co-located, ESLint/Prettier or Oxlint/Oxfmt. Triggers: writing/editing any `.ts` / `.tsx` file in this project; user asks "what are the code standards / style rules / conventions"; spawning a sub-agent that will write code; reviewing a diff for style issues; deciding `interface` vs `type`, when to use a class, where helpers live, how to name a file, or whether something needs JSDoc. Skip: pure-Markdown / JSON / YAML edits with no code change; runtime / behavior questions about a specific framework primitive — load the relevant `@warlock.js/<pkg>` skill instead; package-publishing or CI workflow questions.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Code Standards
|
|
@@ -551,7 +551,20 @@ A secret with no safe default (a payment key, a signing secret) should fail loud
|
|
|
551
551
|
|
|
552
552
|
## 11. Tooling
|
|
553
553
|
|
|
554
|
-
|
|
554
|
+
Use the project's selected toolchain: `eslint.config.js` + `.prettierrc.json`, or
|
|
555
|
+
`.oxlintrc.json` + `.oxfmtrc.json`. Run `npm run lint:check` and
|
|
556
|
+
`npm run format:check` for non-mutating checks. The Oxc scaffold enables the
|
|
557
|
+
Oxlint development health checker and retains TypeScript checking.
|
|
558
|
+
|
|
559
|
+
`npx warlock check` is the one verify command, in either toolchain: it
|
|
560
|
+
typechecks, runs the configured linter and the affected tests, and checks the
|
|
561
|
+
framework's conventions — `request.validated()` instead of `request.all()`,
|
|
562
|
+
guarded or `public: true` mutating routes, safe redirects, redacted logs,
|
|
563
|
+
bounded regexes on input, one controller per file, `type` (not `kind`) as a
|
|
564
|
+
union's discriminator. Each problem has a `WL` code and its fix
|
|
565
|
+
(`npx warlock explain <CODE>`).
|
|
566
|
+
|
|
567
|
+
For ESLint, consider enabling these additional rules:
|
|
555
568
|
|
|
556
569
|
- `@typescript-eslint/explicit-member-accessibility` — enforces § 1.2
|
|
557
570
|
- `@typescript-eslint/no-explicit-any` — enforces § 1.3
|
|
@@ -561,7 +574,7 @@ Ship a `.eslintrc` with at least these rules on:
|
|
|
561
574
|
- `curly` — enforces braces (§ 4.4)
|
|
562
575
|
- `no-console` — enforces § 8
|
|
563
576
|
|
|
564
|
-
Match the
|
|
577
|
+
Match the selected formatter config: `.prettierrc.json` or `.oxfmtrc.json`. Both use 100 columns, double quotes, semicolons and trailing commas. Oxfmt does not run `prettier-plugin-organize-imports`; maintain import order and remove unused imports separately, then run the linter.
|
|
565
578
|
|
|
566
579
|
---
|
|
567
580
|
|
|
@@ -583,7 +596,8 @@ Before any change ships:
|
|
|
583
596
|
- [ ] JSDoc on every public export and every multi-method class
|
|
584
597
|
- [ ] No `console.log`, `log.<level>(module, action, message, context)` signature used correctly
|
|
585
598
|
- [ ] Tests co-located, public-surface-driven
|
|
586
|
-
- [ ] `tsc --noEmit` and `
|
|
599
|
+
- [ ] `tsc --noEmit`, `npm run lint:check` and `npm run format:check` pass clean
|
|
600
|
+
- [ ] `npx warlock check` exits 0
|
|
587
601
|
|
|
588
602
|
**Non-negotiable defaults**
|
|
589
603
|
- [ ] Every untrusted input has a `@warlock.js/seal` schema at the boundary
|
|
@@ -243,7 +243,17 @@ The framework sets `Content-Type: application/json` for `response.success(...)`
|
|
|
243
243
|
|
|
244
244
|
### 5.3 Open redirects
|
|
245
245
|
|
|
246
|
-
If you implement a "redirect back to where the user came from" feature, validate the destination URL against a whitelist of internal paths. Never blindly follow a user-supplied URL.
|
|
246
|
+
If you implement a "redirect back to where the user came from" feature, validate the destination URL against a whitelist of internal paths. Never blindly follow a user-supplied URL. `safeRedirect()` from `@warlock.js/core` does it: same-origin paths and the origins you allow pass, anything else becomes the fallback.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { safeRedirect, type RequestHandler } from "@warlock.js/core";
|
|
250
|
+
|
|
251
|
+
export const afterLoginController: RequestHandler = async ({ request, response }) => {
|
|
252
|
+
return response.redirect(safeRedirect(request.input("next"), { fallback: "/dashboard" }));
|
|
253
|
+
};
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
`npx warlock check` reports a redirect target taken from request input without it as `WL9007`.
|
|
247
257
|
|
|
248
258
|
### 5.4 CORS
|
|
249
259
|
|
|
@@ -350,3 +360,4 @@ Before merging a change that touches input, identity, secrets, or persistence:
|
|
|
350
360
|
- [ ] Logger redaction config covers any new sensitive field introduced
|
|
351
361
|
- [ ] File uploads cap size, sniff MIME, server-generate the stored filename
|
|
352
362
|
- [ ] Webhooks verify signature before any other work
|
|
363
|
+
- [ ] `npx warlock check` reports no `WL9005`–`WL9009` problem (unvalidated `request.all()`, unguarded mutating route, unsafe redirect, sensitive value in a log, unbounded regex on input)
|