@mauroandre/velojs 0.0.36 → 0.0.38

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 (32) hide show
  1. package/README.md +20 -10
  2. package/agent/AGENTS.md +143 -0
  3. package/agent/skills/velojs-actions/SKILL.md +192 -0
  4. package/agent/skills/velojs-components/SKILL.md +99 -0
  5. package/agent/skills/velojs-endpoints/SKILL.md +187 -0
  6. package/agent/skills/velojs-event-streams/SKILL.md +705 -0
  7. package/agent/skills/velojs-getting-started/SKILL.md +191 -0
  8. package/agent/skills/velojs-hooks/SKILL.md +103 -0
  9. package/agent/skills/velojs-link-component/SKILL.md +67 -0
  10. package/agent/skills/velojs-loaders/SKILL.md +134 -0
  11. package/agent/skills/velojs-middlewares/SKILL.md +143 -0
  12. package/agent/skills/velojs-production-deploy/SKILL.md +96 -0
  13. package/agent/skills/velojs-routes/SKILL.md +209 -0
  14. package/agent/skills/velojs-scripts-component/SKILL.md +62 -0
  15. package/agent/skills/velojs-server-api/SKILL.md +155 -0
  16. package/agent/skills/velojs-sockets/SKILL.md +303 -0
  17. package/agent/skills/velojs-static-generation/SKILL.md +148 -0
  18. package/agent/skills/velojs-testing/SKILL.md +472 -0
  19. package/agent/skills/velojs-type-reference/SKILL.md +142 -0
  20. package/agent/skills/velojs-vite-plugin/SKILL.md +71 -0
  21. package/dist/client.js +58 -47
  22. package/dist/components.js +18 -16
  23. package/dist/hooks.d.ts +3 -2
  24. package/dist/hooks.js +107 -110
  25. package/dist/loader-store.d.ts +49 -0
  26. package/dist/loader-store.js +103 -0
  27. package/dist/server.js +11 -11
  28. package/dist/testing/createTestApp.d.ts +1 -1
  29. package/dist/testing/index.d.ts +1 -1
  30. package/dist/testing/types.d.ts +7 -3
  31. package/dist/vite.js +276 -277
  32. package/package.json +6 -2
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: velojs-endpoints
3
+ description: "Declarative HTTP endpoints in the route tree (`{ method, handler }`) for REST/JSON APIs, webhooks, downloads, health checks — anything returning a `Response` instead of a page. Use when building an API route or receiving a webhook."
4
+ ---
5
+
6
+ # Endpoints
7
+
8
+ Endpoints are **declarative HTTP routes** you add to `routes.tsx` alongside your pages. Use them for things a page wouldn't make sense for: webhooks, verification links clicked from email, redirects, health checks, OAuth callbacks.
9
+
10
+ They are registered on the server only — never on the client (wouter). The Vite plugin strips them from the client bundle so server-only handler imports never ship to the browser.
11
+
12
+ ## When to use an endpoint (and when not to)
13
+
14
+ - **Use an endpoint** for: webhooks (GitHub, Stripe, banks), email verification links, OAuth redirect callbacks, health checks, short redirects, and any URL not meant to be rendered by a page.
15
+ - **Use a page** (`module: SomePage`) when the URL is visited by a human and the server returns HTML.
16
+ - **Use an action** (`action_*` inside a page module) when logic belongs to a page — form submits, mutations triggered by UI.
17
+
18
+ Rule of thumb: if the URL is part of the UI flow, it's a page or action. If it's an HTTP endpoint hit by something that isn't your frontend, it's an endpoint.
19
+
20
+ ## Declaring an endpoint
21
+
22
+ Endpoints live in `routes.tsx` as objects with `path`, `method`, and `handler`:
23
+
24
+ ```tsx
25
+ import type { AppRoutes } from "@mauroandre/velojs";
26
+ import * as Home from "./pages/Home.js";
27
+ import { githubWebhook } from "./webhooks/github.handler.js";
28
+
29
+ export default [
30
+ { path: "/", module: Home },
31
+ { path: "/api/github/webhook", method: "POST", handler: githubWebhook },
32
+ ] satisfies AppRoutes;
33
+ ```
34
+
35
+ That's it. The endpoint is now registered on the server.
36
+
37
+ ## The handler signature
38
+
39
+ ```ts
40
+ import type { EndpointHandler } from "@mauroandre/velojs";
41
+
42
+ export const githubWebhook: EndpointHandler = async ({ c, params, query }) => {
43
+ const body = await c.req.json();
44
+ // ...your logic...
45
+ return c.json({ ok: true });
46
+ };
47
+ ```
48
+
49
+ `EndpointHandlerArgs`:
50
+
51
+ - `c` — the Hono `Context`. Use `c.req.json()` / `c.req.formData()` / `c.req.text()` / `c.req.header(...)` to read the request. Use `c.json(...)`, `c.text(...)`, `c.redirect(...)`, `c.html(...)` to respond.
52
+ - `params` — URL params (`/api/verify/:token` → `params.token`).
53
+ - `query` — query-string values.
54
+
55
+ Handlers must return a `Response` (or a `Promise<Response>`). Hono helpers like `c.json`, `c.redirect`, `c.text` return `Response` directly. Unlike `action_*`, endpoints do **not** auto-serialize return values — HTTP endpoints need full control over status, headers, and body format (a GitHub webhook, for example, needs the raw body to verify the HMAC signature).
56
+
57
+ ## Supported HTTP methods
58
+
59
+ `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. One method per entry. If you need both GET and POST on the same path, declare two entries.
60
+
61
+ ## Inline vs module-only handlers
62
+
63
+ Both forms work. The Vite plugin strips either one from the client bundle.
64
+
65
+ ```tsx
66
+ // OK inline — handler is trivial and doesn't import anything server-only
67
+ { path: "/health", method: "GET", handler: ({ c }) => c.text("ok") },
68
+
69
+ // PREFER module — handler imports server-only libraries
70
+ import { githubWebhook } from "./webhooks/github.handler.js";
71
+ { path: "/api/github/webhook", method: "POST", handler: githubWebhook },
72
+ ```
73
+
74
+ **Recommendation:** when the handler imports anything server-only (`node:crypto`, DB client, SDK that doesn't bundle for the browser), put it in a separate module file. The plugin strips references on the client, so the handler module is tree-shaken away — but putting it in its own file makes the intent obvious to other readers and avoids edge cases where the client bundle tries to resolve server-only imports.
75
+
76
+ ## Grouping and middleware inheritance
77
+
78
+ Endpoints inherit middlewares from parent nodes, just like pages:
79
+
80
+ ```tsx
81
+ import { rateLimitMiddleware } from "./middlewares/rate-limit.js";
82
+ import { verifyGithubSignature } from "./middlewares/github-sig.js";
83
+ import { githubWebhook } from "./webhooks/github.handler.js";
84
+ import { pixWebhook } from "./webhooks/pix.handler.js";
85
+
86
+ export default [
87
+ {
88
+ path: "/api",
89
+ middlewares: [rateLimitMiddleware],
90
+ children: [
91
+ {
92
+ path: "/github/webhook",
93
+ method: "POST",
94
+ middlewares: [verifyGithubSignature],
95
+ handler: githubWebhook,
96
+ },
97
+ {
98
+ path: "/pix",
99
+ method: "POST",
100
+ handler: pixWebhook,
101
+ },
102
+ ],
103
+ },
104
+ ];
105
+ ```
106
+
107
+ - `/api/github/webhook` runs `[rateLimitMiddleware, verifyGithubSignature]` before the handler.
108
+ - `/api/pix` runs `[rateLimitMiddleware]`.
109
+
110
+ A node can be a **pure group** — only `path`, `children`, and `middlewares`, no `module` and no `handler`. The children inherit the path prefix and middlewares.
111
+
112
+ ## Redirects from email links
113
+
114
+ A common use case:
115
+
116
+ ```tsx
117
+ import { verifyEmail } from "./auth/verify.handler.js";
118
+
119
+ // routes.tsx
120
+ { path: "/verify/:token", method: "GET", handler: verifyEmail }
121
+ ```
122
+
123
+ ```ts
124
+ // auth/verify.handler.ts
125
+ import type { EndpointHandler } from "@mauroandre/velojs";
126
+ import { consumeVerifyToken } from "../services/auth.js";
127
+
128
+ export const verifyEmail: EndpointHandler = async ({ c, params }) => {
129
+ const ok = await consumeVerifyToken(params.token);
130
+ if (!ok) return c.redirect("/login?verified=0", 302);
131
+ return c.redirect("/stacks?verified=1", 302);
132
+ };
133
+ ```
134
+
135
+ Because the endpoint lives in `routes.tsx`, it's also visible to the testing toolkit — you can test the whole flow end-to-end (see below).
136
+
137
+ ## Path conflicts with pages
138
+
139
+ An endpoint and a page on the same path with the **same method** (both `GET`) conflict. VeloJS registers pages first, endpoints second, and a page handler answers without deferring — so the **page wins and the endpoint is unreachable**. You'll see a loud warning at startup telling you to rename one.
140
+
141
+ The page has to win: a page `GET` also serves `?_data=1`, the JSON every client-side navigation fetches. An endpoint taking the path over would silently break that page's navigation.
142
+
143
+ Different methods on the same path (page `GET` + endpoint `POST`) don't conflict. They're both registered and each is routed according to the request method.
144
+
145
+ ## Validation warnings
146
+
147
+ The server logs a warning and skips the entry when you declare an invalid combination:
148
+
149
+ | Problem | Warning |
150
+ |---|---|
151
+ | `handler` without `method` | `"handler" set without "method"; endpoint skipped` |
152
+ | `method` without `handler` | `"method" set without "handler"; endpoint skipped` |
153
+ | Both `module` and `handler` on the same node | `both "module" and "handler" set; endpoint ignored, page kept` |
154
+ | `isRoot: true` on an endpoint | `"isRoot" has no effect on endpoint` |
155
+
156
+ None of these throw — the server still boots. This keeps dev fast; you just see the problem in the console.
157
+
158
+ ## Testing endpoints
159
+
160
+ Because endpoints live in `routes.tsx`, `createTestApp` picks them up automatically. No bootstrap required for simple webhooks:
161
+
162
+ ```ts
163
+ import { createTestApp } from "@mauroandre/velojs/testing";
164
+ import routes from "../app/routes.js";
165
+
166
+ const app = await createTestApp({ routes });
167
+
168
+ const res = await app.post("/api/github/webhook", {
169
+ headers: { "x-hub-signature-256": signature },
170
+ body: { action: "push", ref: "refs/heads/main" },
171
+ });
172
+ expect(res.status).toBe(200);
173
+
174
+ const verify = await app.get("/verify/abc123");
175
+ expect(verify.status).toBe(302);
176
+ expect(verify.headers.location).toBe("/stacks?verified=1");
177
+ ```
178
+
179
+ See [17-testing.md](/docs/testing) for the full testing API.
180
+
181
+ ## Related
182
+
183
+ - [Routes](/docs/routes) — page routing and layouts
184
+ - [Loaders](/docs/loaders) — page data fetching
185
+ - [Actions](/docs/actions) — mutations from pages
186
+ - [Middlewares](/docs/middlewares) — authentication, logging, rate limiting
187
+ - [Testing](/docs/testing) — the backend testing toolkit