create-warlock 5.11.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +25 -3
  2. package/README.md +38 -5
  3. package/esm/commands/create-new-app/index.mjs +102 -9
  4. package/esm/commands/create-new-app/index.mjs.map +1 -1
  5. package/esm/commands/create-new-app/types.d.mts +8 -3
  6. package/esm/commands/create-warlock-app/index.mjs +12 -1
  7. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  8. package/esm/features/agent-kit-targets.mjs +134 -0
  9. package/esm/features/agent-kit-targets.mjs.map +1 -0
  10. package/esm/features/features-map.mjs +18 -0
  11. package/esm/features/features-map.mjs.map +1 -1
  12. package/esm/flags/definitions.mjs +89 -0
  13. package/esm/flags/definitions.mjs.map +1 -0
  14. package/esm/helpers/app.mjs +21 -0
  15. package/esm/helpers/app.mjs.map +1 -1
  16. package/esm/index.mjs +15 -23
  17. package/esm/index.mjs.map +1 -1
  18. package/esm/prompts/ask-stack.mjs +35 -0
  19. package/esm/prompts/ask-stack.mjs.map +1 -0
  20. package/esm/summary.mjs +25 -0
  21. package/esm/summary.mjs.map +1 -0
  22. package/llms-full.txt +65 -38
  23. package/llms.txt +1 -1
  24. package/package.json +2 -2
  25. package/skills/create-a-warlock-project/SKILL.md +65 -38
  26. package/templates/warlock/AGENTS.md +1 -1
  27. package/templates/warlock/skills/api-design/SKILL.md +2 -2
  28. package/templates/warlock/skills/security-baseline/SKILL.md +1 -1
  29. package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +1 -1
  30. package/templates/warlock/src/app/auth/controllers/logout.controller.ts +3 -3
  31. package/templates/warlock/src/app/auth/controllers/me.controller.ts +1 -1
  32. package/templates/warlock/src/app/auth/requests/guarded.request.ts +2 -2
  33. package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +1 -1
  34. package/templates/warlock/src/app/users/models/user/migrations/11-12-2025_23-58-03-user.migration.ts +1 -0
  35. package/templates/warlock/src/app/users/models/user/user.model.ts +12 -4
  36. package/templates/warlock/src/app/users/services/login-social.ts +4 -3
  37. package/templates/warlock/src/config/database.postgres.ts +4 -0
  38. package/templates/warlock/src/typings.d.ts +14 -8
  39. package/templates/warlock/src/web/home/components/contact-section.tsx +31 -14
  40. package/templates/warlock/src/web/home/hooks/use-contact-form.ts +18 -7
  41. package/templates/warlock/src/web/home/index.page.tsx +1 -1
  42. package/templates/warlock/src/web/root.tsx +5 -2
  43. package/templates/warlock/src/web/home/hooks/use-locale-switcher.ts +0 -35
@@ -53,20 +53,26 @@ declare module "@warlock.js/core" {
53
53
  * ```
54
54
  */
55
55
  interface RequestLocals {}
56
+ }
56
57
 
58
+ declare module "@warlock.js/auth" {
57
59
  /**
58
- * The authenticated user — `request.user`.
60
+ * The authenticated user — `request.locals.user`.
59
61
  *
60
- * The auth middleware assigns the resolved user model here. Declaring its
61
- * shape gives unguarded handlers (ones that may or may not have a user) a
62
- * real type instead of `{}`.
62
+ * `@warlock.js/auth` already augments core's `RequestLocals` with an
63
+ * optional `user?: RequestUser` key; this augments `RequestUser` itself
64
+ * (owned by `@warlock.js/auth`, not core — core removed its own `request.user`
65
+ * and the `RequestUser` it used to declare). The auth middleware assigns the
66
+ * resolved user model here. Declaring its shape gives unguarded handlers
67
+ * (ones that may or may not have a user) a real type instead of `{}`.
63
68
  *
64
- * This does NOT make `request.user` non-optional: the framework declares it
65
- * as `user?: RequestUser`, and augmentation can add members but cannot remove
69
+ * This does NOT make `request.locals.user` non-optional: it stays
70
+ * `user?: RequestUser`, and augmentation can add members but cannot remove
66
71
  * the `?`. For routes that are actually behind the auth guard, type the
67
72
  * handler as `GuardedRequestHandler` (see
68
- * `src/app/auth/requests/guarded.request.ts`) — that narrows `request.user`
69
- * to the app's `User` model AND drops the `| undefined`.
73
+ * `src/app/auth/requests/guarded.request.ts`) — that narrows
74
+ * `request.locals.user` to the app's `User` model AND drops the
75
+ * `| undefined`.
70
76
  *
71
77
  * @example
72
78
  * ```ts
@@ -1,23 +1,40 @@
1
1
  import { Form } from "@mongez/react-form";
2
- import { useTrans } from "@warlock.js/web";
2
+ import { changeLocaleCode, useLocale, useTextDirection, useTrans } from "@warlock.js/web";
3
+ import { useState } from "react";
3
4
  import { contactSchema } from "../../../shared/contact.schema";
4
- import type { LocaleCode } from "../../../shared/locales";
5
5
  import { useContactForm } from "../hooks/use-contact-form";
6
- import { useLocaleSwitcher } from "../hooks/use-locale-switcher";
7
6
  import { ContactField, ContactSubmitButton } from "./contact-form-controls";
8
7
 
9
- type ContactSectionProps = { locale: LocaleCode };
10
-
11
- export function ContactSection({ locale }: ContactSectionProps) {
8
+ export function ContactSection() {
12
9
  const { status: contactStatus, submitContact } = useContactForm();
13
- const { error: localeError, isSwitching, switchLocale } = useLocaleSwitcher();
10
+ const locale = useLocale();
11
+ const direction = useTextDirection();
14
12
  const translate = useTrans();
13
+ const [isSwitchingLocale, setIsSwitchingLocale] = useState(false);
14
+ const [localeError, setLocaleError] = useState("");
15
+
16
+ /**
17
+ * Switch locale via the framework's `changeLocaleCode` — no reload, no
18
+ * manual cookie write. The control is disabled while the request is in
19
+ * flight, and a rejection leaves the current locale in place while
20
+ * surfacing an error instead of throwing.
21
+ */
22
+ async function toggleLocale() {
23
+ const nextLocale = locale === "en" ? "ar" : "en";
24
+
25
+ setIsSwitchingLocale(true);
26
+ setLocaleError("");
27
+
28
+ try {
29
+ await changeLocaleCode(nextLocale);
30
+ } catch (error) {
31
+ setLocaleError(error instanceof Error ? error.message : "Could not update the locale.");
32
+ } finally {
33
+ setIsSwitchingLocale(false);
34
+ }
35
+ }
15
36
  return (
16
- <section
17
- className="warlock-section warlock-contact"
18
- id="contact"
19
- dir={locale === "ar" ? "rtl" : "ltr"}
20
- >
37
+ <section className="warlock-section warlock-contact" id="contact" dir={direction}>
21
38
  <div className="warlock-contact-copy">
22
39
  <p className="warlock-overline">{translate("contact.overline")}</p>
23
40
  <h2>{translate("contact.title")}</h2>
@@ -62,8 +79,8 @@ export function ContactSection({ locale }: ContactSectionProps) {
62
79
  <button
63
80
  className="warlock-locale-toggle"
64
81
  type="button"
65
- disabled={isSwitching}
66
- onClick={() => switchLocale(locale === "en" ? "ar" : "en")}
82
+ disabled={isSwitchingLocale}
83
+ onClick={toggleLocale}
67
84
  >
68
85
  {translate("contact.toggle")}
69
86
  </button>
@@ -1,4 +1,3 @@
1
- import { http } from "@mongez/http";
2
1
  import { type FormSubmitOptions, type InferFormValues } from "@mongez/react-form";
3
2
  import { useState } from "react";
4
3
  import type { contactSchema } from "../../../shared/contact.schema";
@@ -14,6 +13,11 @@ type ContactResponse = {
14
13
  };
15
14
  };
16
15
 
16
+ type ContactErrorBody = {
17
+ message?: string;
18
+ errors?: unknown;
19
+ };
20
+
17
21
  type ContactStatus = {
18
22
  state: "idle" | "submitting" | "success" | "error";
19
23
  message: string;
@@ -29,21 +33,28 @@ export function useContactForm() {
29
33
  setStatus({ state: "submitting", message: "Sending to the backend…" });
30
34
 
31
35
  try {
32
- const result = await http.post<ContactResponse>("/api/contact", values as ContactValues);
36
+ const response = await fetch("/api/contact", {
37
+ method: "POST",
38
+ headers: { "Content-Type": "application/json" },
39
+ body: JSON.stringify(values as ContactValues),
40
+ });
41
+
42
+ const body = (await response.json().catch(() => ({}))) as ContactErrorBody &
43
+ Partial<ContactResponse>;
33
44
 
34
- if (result.error) {
35
- if (result.error.isValidationError) {
36
- setFormErrors(result.error, form);
45
+ if (!response.ok) {
46
+ if (response.status === 422) {
47
+ setFormErrors({ body }, form);
37
48
  }
38
49
 
39
50
  setStatus({
40
51
  state: "error",
41
- message: result.error.message || "The request was not accepted.",
52
+ message: body.message || "The request was not accepted.",
42
53
  });
43
54
  return;
44
55
  }
45
56
 
46
- setStatus({ state: "success", message: result.data?.message });
57
+ setStatus({ state: "success", message: body.message ?? "" });
47
58
  form.reset();
48
59
  } catch (error) {
49
60
  setStatus({
@@ -37,7 +37,7 @@ export default function HomePage({ data }: HomePageProps) {
37
37
  <HomeHeader />
38
38
  <HeroSection statusMessage={data.statusMessage} />
39
39
  <ContentSections capabilities={data.capabilities} packages={data.packages} />
40
- <ContactSection locale={data.locale} />
40
+ <ContactSection />
41
41
  <HomeFooter />
42
42
  </main>
43
43
  );
@@ -1,5 +1,5 @@
1
1
  import type { AppProps } from "@warlock.js/web";
2
- import { Head, Scripts } from "@warlock.js/web";
2
+ import { Head, Scripts, useLocale, useTextDirection } from "@warlock.js/web";
3
3
  import "./app.css";
4
4
 
5
5
  /**
@@ -9,8 +9,11 @@ import "./app.css";
9
9
  * again in the browser during hydration, where neither exists.
10
10
  */
11
11
  export default function App({ children }: AppProps) {
12
+ const locale = useLocale();
13
+ const direction = useTextDirection();
14
+
12
15
  return (
13
- <html lang="en">
16
+ <html lang={locale} dir={direction}>
14
17
  <head>
15
18
  {/*
16
19
  Placement only. The framework injects the page's `metadata`, the
@@ -1,35 +0,0 @@
1
- import { http } from "@mongez/http";
2
- import { useState } from "react";
3
- import { type LocaleCode } from "../../../shared/locales";
4
-
5
- type LocalePreferenceResponse = {
6
- locale: LocaleCode;
7
- };
8
-
9
- export function useLocaleSwitcher() {
10
- const [isSwitching, setIsSwitching] = useState(false);
11
- const [error, setError] = useState("");
12
-
13
- async function switchLocale(locale: LocaleCode): Promise<void> {
14
- setIsSwitching(true);
15
- setError("");
16
-
17
- try {
18
- const result = await http.post<LocalePreferenceResponse>("/api/locale", { locale });
19
-
20
- if (result.error) {
21
- setError(result.error.message || "Could not update the locale.");
22
- setIsSwitching(false);
23
- return;
24
- }
25
-
26
- window.location.hash = "contact";
27
- window.location.reload();
28
- } catch (caughtError) {
29
- setError(caughtError instanceof Error ? caughtError.message : "Could not update the locale.");
30
- setIsSwitching(false);
31
- }
32
- }
33
-
34
- return { error, isSwitching, switchLocale };
35
- }