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.
- package/CHANGELOG.md +25 -3
- package/README.md +38 -5
- package/esm/commands/create-new-app/index.mjs +102 -9
- package/esm/commands/create-new-app/index.mjs.map +1 -1
- package/esm/commands/create-new-app/types.d.mts +8 -3
- package/esm/commands/create-warlock-app/index.mjs +12 -1
- package/esm/commands/create-warlock-app/index.mjs.map +1 -1
- package/esm/features/agent-kit-targets.mjs +134 -0
- package/esm/features/agent-kit-targets.mjs.map +1 -0
- package/esm/features/features-map.mjs +18 -0
- package/esm/features/features-map.mjs.map +1 -1
- package/esm/flags/definitions.mjs +89 -0
- package/esm/flags/definitions.mjs.map +1 -0
- package/esm/helpers/app.mjs +21 -0
- package/esm/helpers/app.mjs.map +1 -1
- package/esm/index.mjs +15 -23
- package/esm/index.mjs.map +1 -1
- package/esm/prompts/ask-stack.mjs +35 -0
- package/esm/prompts/ask-stack.mjs.map +1 -0
- package/esm/summary.mjs +25 -0
- package/esm/summary.mjs.map +1 -0
- package/llms-full.txt +65 -38
- package/llms.txt +1 -1
- package/package.json +2 -2
- package/skills/create-a-warlock-project/SKILL.md +65 -38
- package/templates/warlock/AGENTS.md +1 -1
- package/templates/warlock/skills/api-design/SKILL.md +2 -2
- package/templates/warlock/skills/security-baseline/SKILL.md +1 -1
- package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +1 -1
- package/templates/warlock/src/app/auth/controllers/logout.controller.ts +3 -3
- package/templates/warlock/src/app/auth/controllers/me.controller.ts +1 -1
- package/templates/warlock/src/app/auth/requests/guarded.request.ts +2 -2
- package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +1 -1
- 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 +12 -4
- package/templates/warlock/src/app/users/services/login-social.ts +4 -3
- package/templates/warlock/src/config/database.postgres.ts +4 -0
- package/templates/warlock/src/typings.d.ts +14 -8
- package/templates/warlock/src/web/home/components/contact-section.tsx +31 -14
- package/templates/warlock/src/web/home/hooks/use-contact-form.ts +18 -7
- package/templates/warlock/src/web/home/index.page.tsx +1 -1
- package/templates/warlock/src/web/root.tsx +5 -2
- 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
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
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:
|
|
65
|
-
*
|
|
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
|
|
69
|
-
* to the app's `User` model AND drops the
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
export function ContactSection({ locale }: ContactSectionProps) {
|
|
8
|
+
export function ContactSection() {
|
|
12
9
|
const { status: contactStatus, submitContact } = useContactForm();
|
|
13
|
-
const
|
|
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={
|
|
66
|
-
onClick={
|
|
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
|
|
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 (
|
|
35
|
-
if (
|
|
36
|
-
setFormErrors(
|
|
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:
|
|
52
|
+
message: body.message || "The request was not accepted.",
|
|
42
53
|
});
|
|
43
54
|
return;
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
setStatus({ state: "success", 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
|
|
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=
|
|
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
|
-
}
|