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.
Files changed (93) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +66 -15
  3. package/esm/commands/create-new-app/get-app-path.mjs +1 -1
  4. package/esm/commands/create-new-app/get-app-path.mjs.map +1 -1
  5. package/esm/commands/create-new-app/index.mjs +20 -14
  6. package/esm/commands/create-new-app/index.mjs.map +1 -1
  7. package/esm/commands/create-new-app/types.d.mts +3 -2
  8. package/esm/commands/create-warlock-app/index.mjs +2 -1
  9. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  10. package/esm/features/agent-kit-targets.mjs +1 -1
  11. package/esm/features/agent-kit-targets.mjs.map +1 -1
  12. package/esm/features/database-drivers.mjs +1 -1
  13. package/esm/features/database-drivers.mjs.map +1 -1
  14. package/esm/features/features-map.mjs +13 -13
  15. package/esm/features/features-map.mjs.map +1 -1
  16. package/esm/flags/assert-flag-combinations.mjs +1 -1
  17. package/esm/flags/assert-flag-combinations.mjs.map +1 -1
  18. package/esm/flags/assert-package-manager.mjs +1 -1
  19. package/esm/flags/assert-package-manager.mjs.map +1 -1
  20. package/esm/flags/definitions.mjs +6 -1
  21. package/esm/flags/definitions.mjs.map +1 -1
  22. package/esm/flags/seed-from-flags.mjs +2 -1
  23. package/esm/flags/seed-from-flags.mjs.map +1 -1
  24. package/esm/helpers/app.mjs +72 -6
  25. package/esm/helpers/app.mjs.map +1 -1
  26. package/esm/helpers/exec.mjs +1 -1
  27. package/esm/helpers/exec.mjs.map +1 -1
  28. package/esm/helpers/insert-import.mjs +1 -1
  29. package/esm/helpers/insert-import.mjs.map +1 -1
  30. package/esm/helpers/package-manager.mjs +22 -2
  31. package/esm/helpers/package-manager.mjs.map +1 -1
  32. package/esm/helpers/paths.mjs +1 -1
  33. package/esm/helpers/paths.mjs.map +1 -1
  34. package/esm/helpers/pnpm-build-approvals.mjs +58 -3
  35. package/esm/helpers/pnpm-build-approvals.mjs.map +1 -1
  36. package/esm/helpers/project-builder-helpers.mjs +1 -1
  37. package/esm/helpers/project-builder-helpers.mjs.map +1 -1
  38. package/esm/helpers/tty.mjs +1 -1
  39. package/esm/helpers/tty.mjs.map +1 -1
  40. package/esm/helpers/warlock-versions.mjs +1 -1
  41. package/esm/helpers/warlock-versions.mjs.map +1 -1
  42. package/esm/index.d.mts +1 -1
  43. package/esm/index.mjs +6 -2
  44. package/esm/index.mjs.map +1 -1
  45. package/esm/prompts/ask-agent-targets.mjs +4 -2
  46. package/esm/prompts/ask-agent-targets.mjs.map +1 -1
  47. package/esm/prompts/ask-ai-features.mjs +5 -3
  48. package/esm/prompts/ask-ai-features.mjs.map +1 -1
  49. package/esm/prompts/ask-features.mjs +53 -0
  50. package/esm/prompts/ask-features.mjs.map +1 -0
  51. package/esm/prompts/ask-lint-toolchain.mjs +21 -0
  52. package/esm/prompts/ask-lint-toolchain.mjs.map +1 -0
  53. package/esm/prompts/ask-project-name.mjs +1 -1
  54. package/esm/prompts/ask-project-name.mjs.map +1 -1
  55. package/esm/prompts/ask-stack.mjs +1 -1
  56. package/esm/prompts/ask-stack.mjs.map +1 -1
  57. package/esm/prompts/fit-to-terminal.mjs +46 -0
  58. package/esm/prompts/fit-to-terminal.mjs.map +1 -0
  59. package/esm/summary.mjs +12 -2
  60. package/esm/summary.mjs.map +1 -1
  61. package/esm/ui/banner.mjs +115 -47
  62. package/esm/ui/banner.mjs.map +1 -1
  63. package/esm/ui/report.mjs +1 -1
  64. package/esm/ui/report.mjs.map +1 -1
  65. package/esm/ui/spinner.mjs +1 -1
  66. package/esm/ui/spinner.mjs.map +1 -1
  67. package/esm/ui/spinners.mjs +1 -1
  68. package/esm/ui/spinners.mjs.map +1 -1
  69. package/esm/ui/wrap-text.mjs +55 -0
  70. package/esm/ui/wrap-text.mjs.map +1 -0
  71. package/llms-full.txt +79 -29
  72. package/package.json +6 -5
  73. package/skills/create-a-warlock-project/SKILL.md +79 -29
  74. package/templates/toolchains/oxc/.oxfmtrc.json +12 -0
  75. package/templates/toolchains/oxc/.oxlintrc.json +24 -0
  76. package/templates/toolchains/oxc/.vscode/extensions.json +3 -0
  77. package/templates/toolchains/oxc/.vscode/settings.json +35 -0
  78. package/templates/warlock/AGENTS.md +22 -2
  79. package/templates/warlock/README.md +5 -3
  80. package/templates/warlock/package.json +6 -4
  81. package/templates/warlock/skills/api-design/SKILL.md +2 -2
  82. package/templates/warlock/skills/code-standards/SKILL.md +18 -4
  83. package/templates/warlock/skills/security-baseline/SKILL.md +12 -1
  84. package/templates/warlock/skills/testing-strategy/SKILL.md +17 -0
  85. package/templates/warlock/src/app/auth/routes.ts +25 -5
  86. package/templates/warlock/src/app/auth/utils/types.ts +6 -1
  87. package/templates/warlock/src/app/contact/routes.ts +1 -1
  88. package/templates/warlock/src/app/locale/routes.ts +1 -1
  89. package/templates/warlock/src/app/users/controllers/list-users.controller.ts +10 -2
  90. package/templates/warlock/src/app/users/schema/list-users.schema.ts +15 -0
  91. package/templates/warlock/src/config/tests.ts +8 -0
  92. package/templates/warlock/src/web/404.css +2 -6
  93. package/templates/warlock/src/web/root.tsx +5 -0
@@ -170,6 +170,23 @@ vi.advanceTimersByTime(1000);
170
170
 
171
171
  A test that sleeps is a test that's slow and flaky. Stub the clock.
172
172
 
173
+ Code that reads time through `clock.now()` / `clock.timestamp()` from `@warlock.js/core` gets a frozen clock in tests already (`clockFake.travel(ms)` moves it), with no fake timers involved.
174
+
175
+ ### 4.5 Mail, queue, cache, storage — fakes are already on
176
+
177
+ In the test environment `setupTest()` installs fakes for mail, queue, cache, storage, clock and ids. Assert on them instead of mocking the drivers:
178
+
179
+ ```typescript
180
+ import { mailFake, queueFake } from "@warlock.js/core/tests";
181
+
182
+ const order = { id: 42 }; // what the code under test placed
183
+
184
+ mailFake.assertSent({ to: "ada@example.test" });
185
+ queueFake.assertPushed("invoices.send", { orderId: order.id });
186
+ ```
187
+
188
+ Fixtures come from `defineFactory()` (deterministic, no faker). Opt out of a fake in `src/config/tests.ts` (`fakes: { cache: false }`). `warlock test --changed` runs only the tests a change affects. See the `test-fakes` and `model-factories` skills of `@warlock.js/core`.
189
+
173
190
  ---
174
191
 
175
192
  ## 5. AAA structure
@@ -1,3 +1,8 @@
1
+ import {
2
+ loginThrottleMiddleware,
3
+ tokenConsumeThrottleMiddleware,
4
+ tokenIssueThrottleMiddleware,
5
+ } from "@warlock.js/auth";
1
6
  import { router } from "@warlock.js/core";
2
7
  import { guarded } from "app/shared/utils/router";
3
8
  import { forgotPasswordController } from "./controllers/forgot-password.controller";
@@ -8,12 +13,27 @@ import { meController } from "./controllers/me.controller";
8
13
  import { refreshTokenController } from "./controllers/refresh-token.controller";
9
14
  import { resetPasswordController } from "./controllers/reset-password.controller";
10
15
 
11
- // Auth routes
16
+ // Auth routes. Sign-in and password recovery are open by design (`public: true`)
17
+ // and throttled; the rest need a signed-in user.
12
18
  router.prefix("/auth", () => {
13
- router.post("/login", loginController);
14
- router.post("/refresh-token", refreshTokenController);
15
- router.post("/forgot-password", forgotPasswordController);
16
- router.post("/reset-password", resetPasswordController);
19
+ // Failed sign-ins lock the account and the IP after 5 tries in 15 minutes.
20
+ router.post("/login", loginController, {
21
+ public: true,
22
+ middleware: [loginThrottleMiddleware()],
23
+ });
24
+ router.post("/refresh-token", refreshTokenController, {
25
+ public: true,
26
+ rateLimit: { max: 30, timeWindow: 60_000 },
27
+ });
28
+ // Reset mails: 3 per hour per email and per IP. Reset attempts: 10 failures per 15 minutes.
29
+ router.post("/forgot-password", forgotPasswordController, {
30
+ public: true,
31
+ middleware: [tokenIssueThrottleMiddleware()],
32
+ });
33
+ router.post("/reset-password", resetPasswordController, {
34
+ public: true,
35
+ middleware: [tokenConsumeThrottleMiddleware()],
36
+ });
17
37
  guarded(() => {
18
38
  router.post("/logout", logoutController);
19
39
  router.post("/logout-all", logoutAllController);
@@ -7,4 +7,9 @@ export type OTPChannel = "email" | "sms" | "whatsapp" | "push";
7
7
  * Common OTP types
8
8
  */
9
9
  export type OTPType =
10
- "password-reset" | "email-verification" | "phone-verification" | "two-factor" | string;
10
+ | "password-reset"
11
+ | "email-verification"
12
+ | "phone-verification"
13
+ | "two-factor"
14
+ // Applications may also define their own OTP purposes.
15
+ | string;
@@ -1,4 +1,4 @@
1
1
  import { router } from "@warlock.js/core";
2
2
  import { contactController } from "./controllers/contact.controller";
3
3
 
4
- router.post("/api/contact", contactController);
4
+ router.post("/api/contact", contactController, { public: true });
@@ -1,4 +1,4 @@
1
1
  import { router } from "@warlock.js/core";
2
2
  import { localeController } from "./controllers/locale.controller";
3
3
 
4
- router.post("/api/locale", localeController);
4
+ router.post("/api/locale", localeController, { public: true });
@@ -1,8 +1,12 @@
1
1
  import { type GuardedRequestHandler } from "app/auth/requests/guarded.request";
2
2
  import { usersRepository } from "../repositories/users.repository";
3
+ import { type ListUsersSchema, listUsersSchema } from "../schema/list-users.schema";
3
4
 
4
- export const listUsersController: GuardedRequestHandler = async ({ request, response }) => {
5
- const users = await usersRepository.listCached(request.all());
5
+ export const listUsersController: GuardedRequestHandler<ListUsersSchema> = async ({
6
+ request,
7
+ response,
8
+ }) => {
9
+ const users = await usersRepository.listCached(request.validated());
6
10
 
7
11
  return response.success({
8
12
  users,
@@ -10,3 +14,7 @@ export const listUsersController: GuardedRequestHandler = async ({ request, resp
10
14
  };
11
15
 
12
16
  listUsersController.description = "List Users Controller";
17
+
18
+ listUsersController.validation = {
19
+ schema: listUsersSchema,
20
+ };
@@ -0,0 +1,15 @@
1
+ import { type Infer, v } from "@warlock.js/seal";
2
+
3
+ /**
4
+ * Query parameters the user list accepts. Unknown keys are stripped, so a
5
+ * smuggled operator (`?email[$ne]=`) never reaches the repository filters.
6
+ */
7
+ export const listUsersSchema = v.object({
8
+ id: v.string().optional(),
9
+ name: v.string().max(100).optional(),
10
+ email: v.string().max(255).optional(),
11
+ page: v.numeric().min(1).optional(),
12
+ limit: v.numeric().min(1).max(100).optional(),
13
+ });
14
+
15
+ export type ListUsersSchema = Infer<typeof listUsersSchema>;
@@ -1,5 +1,13 @@
1
1
  const testsConfigurations = {
2
+ // In tests, mail, queue, cache, storage, clock and ids are faked by default:
3
+ // nothing is delivered, pushed to Redis or uploaded, and assertions such as
4
+ // mailFake.assertSent(...) work (see @warlock.js/core/tests).
2
5
  //
6
+ // Opt out of every fake, keeping the real drivers:
7
+ // fakes: false,
8
+ //
9
+ // ...or of one, e.g. keep the configured cache driver:
10
+ // fakes: { cache: false },
3
11
  };
4
12
 
5
13
  export default testsConfigurations;
@@ -14,12 +14,8 @@
14
14
  padding-inline: clamp(1.5rem, 7vw, 8rem);
15
15
  background: #fafafa;
16
16
  color: #18181b;
17
- font-family:
18
- system-ui,
19
- -apple-system,
20
- BlinkMacSystemFont,
21
- "Segoe UI",
22
- sans-serif;
17
+ /* prettier-ignore */
18
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
23
19
  }
24
20
 
25
21
  .not-found__content {
@@ -15,6 +15,11 @@ export default function App({ children }: AppProps) {
15
15
  return (
16
16
  <html lang={locale} dir={direction}>
17
17
  <head>
18
+ {/*
19
+ <Head /> also renders the viewport tag (width=device-width,
20
+ initial-scale=1). Change it with `metadata.viewport`,
21
+ or set it to false to leave it out; do not add a second one here.
22
+ */}
18
23
  {/*
19
24
  Placement only. The framework injects the page's `metadata`, the
20
25
  stylesheet and preload tags for this route, and the canonical links