@tulipes/cli 0.1.0-rc.1

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -0
  3. package/dist/app-core.d.ts +33 -0
  4. package/dist/app-core.js +58 -0
  5. package/dist/app-core.js.map +1 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +7 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/dev.d.ts +8 -0
  10. package/dist/dev.js +22 -0
  11. package/dist/dev.js.map +1 -0
  12. package/dist/env-check.d.ts +6 -0
  13. package/dist/env-check.js +28 -0
  14. package/dist/env-check.js.map +1 -0
  15. package/dist/init.d.ts +25 -0
  16. package/dist/init.js +6416 -0
  17. package/dist/init.js.map +1 -0
  18. package/dist/inspection.d.ts +8 -0
  19. package/dist/inspection.js +15 -0
  20. package/dist/inspection.js.map +1 -0
  21. package/dist/main.d.ts +1 -0
  22. package/dist/main.js +166 -0
  23. package/dist/main.js.map +1 -0
  24. package/dist/minimal.d.ts +2 -0
  25. package/dist/minimal.js +2200 -0
  26. package/dist/minimal.js.map +1 -0
  27. package/dist/new-module.d.ts +19 -0
  28. package/dist/new-module.js +348 -0
  29. package/dist/new-module.js.map +1 -0
  30. package/dist/package-info.d.ts +18 -0
  31. package/dist/package-info.js +53 -0
  32. package/dist/package-info.js.map +1 -0
  33. package/dist/routes.d.ts +8 -0
  34. package/dist/routes.js +54 -0
  35. package/dist/routes.js.map +1 -0
  36. package/dist/spec.d.ts +19 -0
  37. package/dist/spec.js +101 -0
  38. package/dist/spec.js.map +1 -0
  39. package/dist/sync.d.ts +12 -0
  40. package/dist/sync.js +117 -0
  41. package/dist/sync.js.map +1 -0
  42. package/dist/update.d.ts +20 -0
  43. package/dist/update.js +240 -0
  44. package/dist/update.js.map +1 -0
  45. package/package.json +59 -0
  46. package/templates/CLAUDE.md +120 -0
  47. package/templates/browser-auth.md +120 -0
  48. package/templates/claude/skills/tulipes-boot-errors/SKILL.md +74 -0
  49. package/templates/claude/skills/tulipes-endpoint/SKILL.md +132 -0
  50. package/templates/claude/skills/tulipes-env-variable/SKILL.md +78 -0
  51. package/templates/claude/skills/tulipes-i18n/SKILL.md +98 -0
  52. package/templates/claude/skills/tulipes-model/SKILL.md +107 -0
  53. package/templates/claude/skills/tulipes-module/SKILL.md +67 -0
  54. package/templates/claude/skills/tulipes-permissions/SKILL.md +105 -0
  55. package/templates/claude/skills/tulipes-queue/SKILL.md +68 -0
  56. package/templates/claude/skills/tulipes-response/SKILL.md +118 -0
  57. package/templates/claude/skills/tulipes-settings/SKILL.md +111 -0
  58. package/templates/claude/skills/tulipes-socket/SKILL.md +60 -0
  59. package/templates/claude/skills/tulipes-spec/SKILL.md +101 -0
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: tulipes-spec
3
+ description: Use when validating request input in a Tulipes app, adding a zod schema to a route, generating OpenAPI or a Postman collection, or grouping endpoints in the generated documents. Covers rai() schemas, the folder property and `tulipes spec`.
4
+ ---
5
+
6
+ # Schemas, validation and generated specs
7
+
8
+ A route's schemas are declared once, in its `rai()` call, and used twice: the
9
+ framework validates the request against them, and `tulipes spec` describes
10
+ them. That is the whole point — documentation cannot claim a shape the
11
+ endpoint would reject.
12
+
13
+ ```ts
14
+ import { z } from "zod/v4"; // v4, not the classic import
15
+
16
+ router.post("/users", rai({
17
+ id: "users:create",
18
+ name: "Create a user",
19
+ body: z.object({ email: z.email("emailInvalid") }),
20
+ returns: z.object({ id: z.string(), email: z.string() }),
21
+ }), users(value => value.create())); // users is a runtime() binder
22
+
23
+ ```
24
+
25
+ `params`, `query` and `body` are validated. `returns` never is — a handler
26
+ is trusted with its own output — it exists so the document can describe the
27
+ response.
28
+
29
+ **Use `zod/v4`.** `z.toJSONSchema` is what the generator calls, and it only
30
+ understands v4 schemas. The classic `from "zod"` import still validates but
31
+ produces no shape in the document, and `tulipes spec` will say so.
32
+
33
+ Keep schemas in `modules/<name>/schemas.ts` when a route file starts to
34
+ sprawl; both the example's users and auth modules do.
35
+
36
+ ## What validation gives you
37
+
38
+ Failures become the envelope's `errors[]`, with no handler code:
39
+
40
+ ```jsonc
41
+ { "success": false, "data": null,
42
+ "errors": [{ "field": "email", "message": "Must be a valid email address",
43
+ "code": "INVALID_FORMAT" }],
44
+ "meta": {} }
45
+ ```
46
+
47
+ - **The zod message doubles as an i18n key.** `z.email("emailInvalid")`
48
+ resolves in this module's namespace, so a rejected field speaks the
49
+ caller's language. A plain sentence passes through unchanged.
50
+ - `code` comes from zod's issue code, uppercased — `INVALID_FORMAT`,
51
+ `TOO_SMALL`. Clients branch on it; it is never translated.
52
+ - All three segments are checked before answering, so a request wrong in
53
+ the query *and* the body says so once.
54
+ - Coerced values are written back: `z.coerce.number()` means `req.query.page`
55
+ is a number by the time the handler reads it.
56
+ - **Validation runs after the access check.** A caller who may not reach a
57
+ route never learns which of its fields are wrong.
58
+
59
+ ## Grouping: `folder`
60
+
61
+ `folder` decides the Postman folder and the OpenAPI tag. It defaults to the
62
+ declaring module, so grouping is right without saying anything — set it to
63
+ split one module's routes apart, or to gather several modules under one
64
+ heading:
65
+
66
+ ```ts
67
+ rai({ id: "auth:sessions", name: "List sessions", folder: "sessions" })
68
+ ```
69
+
70
+ ## Generating
71
+
72
+ ```sh
73
+ yarn spec # tulipes spec --offline --openapi docs/openapi.json --postman docs/collection.json
74
+ ```
75
+
76
+ Offline generation builds native router factories wrapped in defineRoutes().
77
+ Every module opts in with tulipes.offline: true; no extra declaration file is
78
+ required. Keep schema imports pure and acquire services/controllers through
79
+ runtime(). Use --runtime for factories requiring live configuration.
80
+ --url sets the advertised base URL. Offline defaults to http://localhost:3000
81
+ and package.json title/version; runtime uses PORT/PUBLIC_DOMAIN and config.app.
82
+ Install custom access checkers inside defineRoutes using routes.setAccessChecker;
83
+ offline tooling sees the checker and makes no static role/public-access claim.
84
+
85
+ The generator lives in `@tulipes/spec`, a dev dependency. Core does not
86
+ depend on it, so an app that never generates a spec never installs it.
87
+
88
+ The Postman collection carries what an OpenAPI import cannot: one folder
89
+ per `folder`, `{{baseUrl}}` and `{{accessToken}}` variables, `noauth` on
90
+ public routes, and a script on the login request that captures the token
91
+ pair — sign in once and the rest of the collection is authenticated.
92
+
93
+ ## Rules
94
+
95
+ - **Commit the generated documents.** They are artifacts a reviewer can
96
+ diff; a spec that only exists on someone's laptop is not documentation.
97
+ - Regenerate after changing any route, and check the diff — an unexpected
98
+ change there usually means an unexpected change to the API.
99
+ - A schema is not a substitute for authorization. `rai({ id })` gates the
100
+ route; the schema only shapes the input.
101
+ - Never hand-edit the generated files.