create-daloy 1.0.0-beta.4 → 1.0.0-beta.6

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.
@@ -1,23 +1,33 @@
1
1
  # AGENTS.md
2
2
 
3
- A [DaloyJS](https://daloyjs.dev) REST API deployed to **Vercel** on the **Node.js runtime** (Vercel's recommended runtime for standalone functions, running on Fluid Compute). **Contract-first**: routes are defined with Zod schemas and OpenAPI 3.1 is generated from them. When `docs: true` is set in `new App({...})`, three routes are auto-mounted: `GET /openapi.json`, `GET /openapi.yaml`, and `GET /docs` (Scalar UI).
3
+ A [DaloyJS](https://daloyjs.dev) REST API deployed to **Vercel** on the **Node.js runtime**. **Contract-first**: routes use validation schemas (Zod here; DaloyJS also supports Standard Schema-compatible validators) and generate OpenAPI 3.1. With `docs: true`, DaloyJS auto-mounts `GET /openapi.json`, `GET /openapi.yaml`, and `GET /docs` (Scalar UI).
4
4
 
5
5
  - Package manager: pnpm (use `pnpm` unless the project's `package.json` was rewritten for npm/yarn/bun).
6
6
  - Runtime: Vercel Node.js Functions on Fluid Compute (Web Standard `Request`/`Response`).
7
7
 
8
+ ## Agent guidance
9
+
10
+ - Treat this file as the short, durable project contract for AI coding agents.
11
+ - Use `.agents/skills/daloyjs-best-practices/SKILL.md` for the detailed DaloyJS workflow; keep this file concise and do not duplicate that skill.
12
+ - If instructions conflict, follow the user's latest prompt first, then the nearest `AGENTS.md`, then the skill.
13
+ - Change route definitions, schemas, metadata, and tests first; regenerate generated files instead of hand-editing OpenAPI output.
14
+
8
15
  ## Commands
9
16
 
10
17
  - `pnpm dev` — local Node dev server (`src/dev.ts`) on http://localhost:3000 (no `vercel dev` / login needed; serves the same app the Vercel Function runs)
11
18
  - `pnpm typecheck` — `tsc --noEmit`
12
19
  - `pnpm test` — run test suite
20
+ - `pnpm contract` — run `daloy inspect --check api/index.ts`
21
+ - `pnpm hooks:install` — enable the optional pre-push contract gate
13
22
  - `pnpm deploy` — deploy to Vercel
14
23
  - `pnpm audit` — supply-chain audit
15
24
 
16
25
  ## Project shape
17
26
 
18
- - `api/index.ts` — the single Vercel Node.js Functions entrypoint. Builds the `App`, registers routes/middleware, and exports `default toFetchHandler(app)` from `@daloyjs/core/vercel` (Node.js Functions expect a default export with a `fetch` method; Node.js is the default runtime, so no `runtime` export is needed). `vercel.json` rewrites every path (`/(.*)` `/api`) to this one function, so DaloyJS owns all routing and the app's routes are served at the site root (`/healthz`, `/docs`, …), not under `/api/*`. If you specifically need the Edge runtime, add `export const runtime = "edge"` and switch to `default toWebHandler(app)`.
19
- - `vercel.json` Vercel config. The `rewrites` rule routing all paths to `/api` is **required** for root routing; do not remove it. (`functions` sets memory / maxDuration.)
20
- - `src/dev.ts` — local Node dev server (`pnpm dev`). Imports the `app` exported from `api/index.ts` and serves it via `@daloyjs/core/node` at the root, so you get fast local iteration without `vercel dev`. Dev-only; it is not under `api/`, so Vercel never deploys it.
27
+ - `api/index.ts` — the single Vercel Node.js Functions entrypoint. Export `default toFetchHandler(app)` from `@daloyjs/core/vercel`; add `runtime = "edge"` and switch to `toWebHandler(app)` only when the user asks for Edge.
28
+ - This template is not a Next.js App Router project. Do not add `app/api` routes, `next.config.*`, or Next-specific file structure unless the user asks to convert or embed the API in a Next.js app.
29
+ - `vercel.json` — routes all paths to `/api` so DaloyJS owns root routing; do not remove this rewrite.
30
+ - `src/dev.ts` — local Node dev server (`pnpm dev`) for fast iteration without `vercel dev`. Dev-only; Vercel does not deploy it.
21
31
  - `tests/` — test files.
22
32
 
23
33
  ## Imports
@@ -28,34 +38,36 @@ This project uses TypeScript with `"allowImportingTsExtensions"`, so relative im
28
38
  import handler from "../api/index.ts";
29
39
  ```
30
40
 
31
- You import the file you see. Vercel bundles the `api/` functions at deploy time and resolves `.ts` directly, and the test runner (tsx) does too. Bare-specifier imports from packages (`@daloyjs/core`, `zod`, ) do not need an extension.
41
+ You import the file you see. Vercel and tsx resolve `.ts` directly. Bare package imports (`@daloyjs/core`, `zod`, ...) need no extension.
32
42
 
33
43
  ## Core rules
34
44
 
35
45
  1. The route definition is the contract. Method, path, request schemas, and response schemas live in one place — `app.route({...})`.
36
- 2. Validate every input with Zod. Use `.strict()` on top-level object schemas to reject unknown keys at the boundary.
46
+ 2. Validate every input with Zod or another Standard Schema-compatible validator. For Zod object schemas, use `.strict()` to reject unknown keys at the boundary.
37
47
  3. Preserve literal types in responses: `status: 200 as const`, `z.literal(...)` on discriminator fields.
38
48
  4. Throw typed errors (`NotFoundError`, `BadRequestError`, etc.) from `@daloyjs/core`.
39
49
  5. Keep `requestId()`, `secureHeaders()`, and `rateLimit()` enabled. For production traffic, back rate-limiting with Vercel KV or another shared store (the in-memory limiter resets per instance).
40
- 6. On the Node.js runtime the full Node API is available (`node:*`, `Buffer`, `fs`), but prefer Web Standards (`Request`/`Response`, `fetch`, Web Crypto) so the same app can also run on the Edge runtime or another adapter unchanged. If you opt into the Edge runtime, drop `node:` modules entirely.
50
+ 6. Prefer Web Standards (`Request`/`Response`, `fetch`, `Web Crypto`) even though Node APIs are available; Edge runtime code must avoid `node:` modules.
41
51
  7. Keep a single `api/index.ts` entry and the `vercel.json` `/(.*)` → `/api` rewrite so DaloyJS handles all routing at the site root.
42
- 8. Every new route ships with a test that covers a happy path and at least one unhappy path.
52
+ 8. Keep operation IDs stable and examples schema-valid; `pnpm contract` must pass after route, metadata, or OpenAPI-facing changes.
53
+ 9. Every new route ships with a test that covers a happy path and at least one unhappy path.
43
54
 
44
55
  ## Secure-by-default (do not let an AI strip these)
45
56
 
46
- Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): *"If you tell an AI to make something work, it might remove the very security checks that protect you."* When a guard rejects a request, **satisfy it, do not delete it.**
57
+ Per Supabase + Aikido on [secure-by-default development](https://www.aikido.dev/blog/supabase-approach-to-secure-by-default-development): _"If you tell an AI to make something work, it might remove the very security checks that protect you."_ When a guard rejects a request, **satisfy it, do not delete it.**
47
58
 
48
- - Keep `secureHeaders()`, `requestId()`, `rateLimit()` registered, and `bodyLimitBytes` / `requestTimeoutMs` set on `new App({...})`. For production, back the limiter with Vercel KV **in addition to** the in-memory limiter (which resets per instance).
49
- - Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
50
- - Every protected route attaches an auth `beforeHandle` and ships an unhappy-path test proving an unauthenticated request returns `401` (and wrong scope returns `403`) — the HTTP-boundary equivalent of Supabase's pgTAP policy tests.
59
+ - Keep `secureHeaders()`, `requestId()`, `rateLimit()`, `bodyLimitBytes`, and `requestTimeoutMs`. For production, back rate limits with Vercel KV or another shared store.
60
+ - Keep Zod `.strict()` on top-level request objects; do not switch to `.passthrough()`. For other validators, use the strict / no-extra-keys equivalent. Keep `responses[N].body` schemas tight; never widen to `z.any()` to let a privileged field escape.
61
+ - Every protected route attaches auth `beforeHandle` and tests unauthenticated `401` plus wrong-scope `403`.
51
62
  - JWT verifiers keep an explicit `algorithms` allowlist; never trust the token's `alg` header, never allow `none`, always check `exp` / `nbf`.
52
- - Credential / HMAC comparisons use a constant-time comparison (the framework's `timingSafeEqual`), never `===`. Throw typed errors from `@daloyjs/core` so problem+json redacts in prod; never return raw stack traces.
63
+ - Credential / HMAC comparisons use constant-time comparison, never `===`. Throw typed errors so problem+json redacts in prod.
53
64
  - Keep the single `api/index.ts` entry and the `vercel.json` rewrite so DaloyJS owns routing — do not split into per-path files that bypass the middleware chain, and do not remove the rewrite (the root domain would 404).
54
65
  - `.env`, `.env.local`, secrets, private keys: never commit. Use `vercel env` for production secrets.
55
66
 
56
67
  ## Process expectations
57
68
 
58
69
  - Quality gates must pass before declaring work done: `pnpm typecheck` and `pnpm test`.
70
+ - Run the contract gate (`pnpm contract`) whenever route shapes, examples, operation IDs, or OpenAPI metadata change.
59
71
  - Bug fixes include a regression test.
60
72
  - For deploys, ensure the user has run `vercel login`; do not authenticate on their behalf.
61
73
  - Never bypass safety checks without a clear reason.
@@ -2,11 +2,11 @@
2
2
  name: daloyjs-best-practices
3
3
  description: >-
4
4
  Best practices for building, testing, and hardening this DaloyJS REST API on
5
- Vercel (Node.js runtime). Use when adding or changing HTTP routes, Zod
6
- schemas, middleware, or error handling; regenerating the OpenAPI spec or the
7
- typed Hey API client; keeping the single Vercel Functions entrypoint and
8
- Web-Standard handler; or working on auth, rate limits, secrets, and the
9
- project's quality gates.
5
+ Vercel (Node.js runtime). Use when adding or changing HTTP routes,
6
+ Zod/Standard Schema validation schemas, middleware, route metadata, or error
7
+ handling; regenerating the OpenAPI spec or typed Hey API client; keeping the
8
+ single Vercel Functions entrypoint and Web-Standard handler; running contract
9
+ gates; or working on auth, rate limits, secrets, and security defaults.
10
10
  license: MIT
11
11
  ---
12
12
 
@@ -42,7 +42,8 @@ DaloyJS is a **contract-first** framework. On Vercel, additionally:
42
42
  then drop `node:` modules.
43
43
  2. **The route definition is the contract.** Method, path, request
44
44
  schemas, and response schemas live in one place (`app.route({...})`).
45
- 3. **Zod schemas validate at every boundary.**
45
+ 3. **Validation schemas protect every boundary.** This template uses Zod,
46
+ and Daloy accepts any Standard Schema-compatible library.
46
47
  4. **Preserve literal types.** Return `status: 200 as const`.
47
48
  5. **Secure by default.** `requestId()`, `secureHeaders()`, and
48
49
  `rateLimit()` are registered before route definitions. Note the
@@ -53,6 +54,9 @@ DaloyJS is a **contract-first** framework. On Vercel, additionally:
53
54
  and `vercel.json` rewrites every path (`/(.*)` → `/api`) to it, so
54
55
  DaloyJS owns all routing at the site root and generates a unified
55
56
  OpenAPI spec. Removing the rewrite makes the root domain 404.
57
+ 7. **Contract gates are part of done.** Keep `operationId` values stable,
58
+ examples schema-valid, declared error responses accurate, and the
59
+ generated OpenAPI contract in sync with the live route table.
56
60
 
57
61
  ## Project shape
58
62
 
@@ -73,11 +77,14 @@ DaloyJS is a **contract-first** framework. On Vercel, additionally:
73
77
  pnpm dev # local Node dev server (src/dev.ts) on http://localhost:3000
74
78
  pnpm typecheck # tsc --noEmit
75
79
  pnpm test # run test suite
80
+ pnpm contract # daloy inspect --check api/index.ts
76
81
  pnpm deploy # deploy to Vercel
77
82
  pnpm audit # supply-chain audit
78
83
  ```
79
84
 
80
85
  Always run `pnpm typecheck` and `pnpm test` before declaring a task done.
86
+ `pnpm test` includes the contract gate; if you need a focused contract
87
+ check, run `pnpm contract`.
81
88
 
82
89
  ## OpenAPI & docs routes
83
90
 
@@ -97,17 +104,32 @@ On Vercel the YAML serializer is pure-string (no extra deps) and adds
97
104
  <1KB to the bundle. For hand-rolled mounting, `openapiToYAML` is exported
98
105
  from `@daloyjs/core/openapi`.
99
106
 
107
+ ## AI-ready contract metadata
108
+
109
+ Daloy can expose route metadata to OpenAPI and agent tooling. Add metadata
110
+ when it helps consumers understand or safely automate the route:
111
+
112
+ - Use `summary`, `description`, and `tags` for concise human-facing docs.
113
+ - Use `meta.examples` for realistic happy-path and unhappy-path examples.
114
+ Examples must match the declared schemas; the contract gate rejects drift.
115
+ - Use `meta.extensions` for stable `x-*` fields consumed by internal tools.
116
+ - Use `deprecated` and `sunset` when changing API lifecycle. Do not remove
117
+ a route or response shape silently if generated clients may depend on it.
118
+
100
119
  ## Workflow: add a new route
101
120
 
102
121
  1. **Open `api/index.ts`.**
103
122
  2. **Design schemas first.** Use `z.object({...}).strict()` for inputs.
104
123
  3. **Call `app.route({...})`** with `method`, `path`, `operationId`,
105
124
  `tags`, `responses`, `handler` (plus `request` when accepting input).
125
+ Add `meta` examples / descriptions when the route is user-facing or
126
+ consumed by agents.
106
127
  4. **Return `{ status, body, headers? }`** with `status: 200 as const`.
107
128
  5. **Throw typed errors** (`NotFoundError`, `BadRequestError`, etc.)
108
129
  from `@daloyjs/core`.
109
130
  6. **Add a test** under `tests/` using in-process `app.request(...)`.
110
- 7. **Run the quality gates**: `pnpm typecheck && pnpm test`.
131
+ 7. **Run the contract gate**: `pnpm contract` or `pnpm test`.
132
+ 8. **Run the quality gates**: `pnpm typecheck && pnpm test`.
111
133
 
112
134
  ### Example: a typed route
113
135
 
@@ -146,6 +168,8 @@ app.route({
146
168
  - **Pagination**: standardize on `{ items, nextCursor }` cursor
147
169
  pagination.
148
170
  - **Discriminated unions**: `z.discriminatedUnion("kind", [...])`.
171
+ - Keep response examples close to the route definition and schema-valid.
172
+ The contract test intentionally fails invalid examples.
149
173
 
150
174
  ## Error handling
151
175
 
@@ -183,6 +207,8 @@ Cover **happy paths and unhappy paths** for every route: valid input,
183
207
  validation failures (400), auth failures (401/403), not-found (404),
184
208
  conflict (409), rate limiting (429). For external services, inject an
185
209
  in-memory fake during tests.
210
+ The shipped contract test should fail invalid examples, duplicate/missing
211
+ `operationId`, or missing responses.
186
212
 
187
213
  Aim for **100% line and function coverage** on the routes you add.
188
214
 
@@ -191,6 +217,10 @@ Aim for **100% line and function coverage** on the routes you add.
191
217
  - Keep `secureHeaders()`, `requestId()`, and `rateLimit()` enabled. For
192
218
  production traffic, back rate-limiting with Vercel KV or another
193
219
  shared store so limits apply across instances.
220
+ - Never make a failing test pass by deleting or weakening a security guard.
221
+ If a guard blocks a legitimate route, add the narrowest per-route
222
+ override or configuration knob and cover both the allowed and rejected
223
+ paths in tests.
194
224
  - Never log secrets — filter `authorization`, `cookie`, etc.
195
225
  - Read secrets from `process.env` (available on Node.js Functions).
196
226
  Validate via Zod at module load.
@@ -200,6 +230,9 @@ Aim for **100% line and function coverage** on the routes you add.
200
230
  - Validate redirects against an allowlist.
201
231
  - Set `bodyLimitBytes` and `requestTimeoutMs` on `new App({...})` to
202
232
  mitigate DoS.
233
+ - For outbound HTTP, prefer `fetchGuard()` or a transport layered on top
234
+ of it when URLs can be influenced by users or tenants. SSRF protections
235
+ should fail closed for private ranges and cloud metadata endpoints.
203
236
  - Serverless functions still have bundle-size and cold-start costs; be
204
237
  cautious about adding heavy dependencies. Inspect bundle size during
205
238
  deploy.
@@ -229,6 +262,8 @@ Aim for **100% line and function coverage** on the routes you add.
229
262
  Edge runtime, use `toWebHandler(app)` with `export const runtime = "edge"`.
230
263
  - Do not import `@daloyjs/core/node`, `@daloyjs/core/bun`, etc. — only
231
264
  `@daloyjs/core` and `@daloyjs/core/vercel`.
265
+ - Do not hand-edit OpenAPI paths or client types. Fix the route definition,
266
+ schema, or metadata and regenerate.
232
267
  - Node APIs (`Buffer`, `fs`, full `process`) are available on the Node.js
233
268
  runtime, but keep handlers Web-Standard where practical so the app can
234
269
  also run on the Edge runtime unchanged.
@@ -241,6 +276,8 @@ Aim for **100% line and function coverage** on the routes you add.
241
276
  - Every new feature ships with happy-path and unhappy-path tests.
242
277
  - Bug fixes include a regression test.
243
278
  - `pnpm typecheck` and `pnpm test` must pass before completion.
279
+ - When route metadata, examples, lifecycle flags, or operation IDs change,
280
+ run the contract gate and inspect the relevant generated OpenAPI diff.
244
281
  - For deploys, ensure the user is logged in via `vercel login`; do not
245
282
  authenticate on their behalf.
246
283
  - Keep `README.md`, this `SKILL.md`, and `AGENTS.md` consistent.
@@ -13,7 +13,7 @@
13
13
  "hooks:install": "git config core.hooksPath .githooks"
14
14
  },
15
15
  "dependencies": {
16
- "@daloyjs/core": "^1.0.0-beta.4",
16
+ "@daloyjs/core": "^1.0.0-beta.6",
17
17
  "zod": "^4.4.3"
18
18
  },
19
19
  "devDependencies": {