@tulipes/cli 0.2.0 → 0.3.0-rc.2

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 (110) hide show
  1. package/dist/config-check.js +1 -1
  2. package/dist/env-check.d.ts +5 -3
  3. package/dist/env-check.js +26 -19
  4. package/dist/env-check.js.map +1 -1
  5. package/dist/main.js +83 -19
  6. package/dist/main.js.map +1 -1
  7. package/dist/migrate-0.12-env.d.ts +36 -0
  8. package/dist/migrate-0.12-env.js +250 -0
  9. package/dist/migrate-0.12-env.js.map +1 -0
  10. package/dist/migrate-0.12-scaffold.d.ts +1 -0
  11. package/dist/migrate-0.12-scaffold.js +197 -0
  12. package/dist/migrate-0.12-scaffold.js.map +1 -0
  13. package/dist/migrate-0.12.d.ts +47 -0
  14. package/dist/migrate-0.12.js +210 -0
  15. package/dist/migrate-0.12.js.map +1 -0
  16. package/dist/migrate.d.ts +3 -1
  17. package/dist/migrate.js +11 -5
  18. package/dist/migrate.js.map +1 -1
  19. package/dist/new-module.d.ts +7 -0
  20. package/dist/new-module.js +35 -16
  21. package/dist/new-module.js.map +1 -1
  22. package/dist/roles.d.ts +15 -0
  23. package/dist/roles.js +54 -0
  24. package/dist/roles.js.map +1 -0
  25. package/dist/routes.d.ts +7 -2
  26. package/dist/routes.js +19 -10
  27. package/dist/routes.js.map +1 -1
  28. package/dist/spec.d.ts +2 -0
  29. package/dist/spec.js +13 -11
  30. package/dist/spec.js.map +1 -1
  31. package/dist/sync.js +66 -53
  32. package/dist/sync.js.map +1 -1
  33. package/package.json +4 -4
  34. package/templates/AGENTS.md +35 -23
  35. package/templates/app/.envs/.env.development +2 -1
  36. package/templates/app/.envs/.env.production +2 -2
  37. package/templates/app/.envs/.env.staging +2 -2
  38. package/templates/app/README.md +44 -29
  39. package/templates/app/config/app.config.ts +60 -7
  40. package/templates/app/docs/agent-manifest.json +8 -681
  41. package/templates/app/modules/agent/controllers/{agent.controllers.ts → web.agent.controllers.ts} +1 -1
  42. package/templates/app/modules/agent/routes/{agent.routes.ts → web.agent.routes.ts} +4 -4
  43. package/templates/app/modules/auth/bootstrap/credentials.bootstrap.ts +20 -12
  44. package/templates/app/modules/auth/bootstrap/seed-admin.bootstrap.ts +4 -2
  45. package/templates/app/modules/auth/controllers/{auth.controllers.ts → web.auth.controllers.ts} +1 -1
  46. package/templates/app/modules/auth/helpers/passport.ts +1 -1
  47. package/templates/app/modules/auth/helpers/session-cache.ts +3 -3
  48. package/templates/app/modules/auth/helpers/tokens.ts +3 -5
  49. package/templates/app/modules/auth/module.acl.ts +31 -9
  50. package/templates/app/modules/auth/module.config.ts +5 -3
  51. package/templates/app/modules/auth/package.json +10 -10
  52. package/templates/app/modules/auth/routes/{auth.routes.ts → web.auth.routes.ts} +15 -10
  53. package/templates/app/modules/auth/tests/auth.test.ts +1 -1
  54. package/templates/app/modules/config/controllers/{config.controllers.ts → bo.config.controllers.ts} +3 -2
  55. package/templates/app/modules/config/helpers/audit.ts +1 -1
  56. package/templates/app/modules/config/helpers/describe.ts +35 -26
  57. package/templates/app/modules/config/module.acl.ts +1 -1
  58. package/templates/app/modules/config/routes/{config.routes.ts → bo.config.routes.ts} +5 -5
  59. package/templates/app/modules/config/tests/config.test.ts +14 -6
  60. package/templates/app/modules/core/helpers/cache/index.ts +8 -7
  61. package/templates/app/modules/core/helpers/lazy.ts +3 -3
  62. package/templates/app/modules/core/helpers/logger/index.ts +24 -16
  63. package/templates/app/modules/core/module.config.ts +5 -3
  64. package/templates/app/modules/core/package.json +46 -19
  65. package/templates/app/modules/core/routes/core.routes.ts +4 -4
  66. package/templates/app/modules/hello/agent/hello.agent.ts +2 -2
  67. package/templates/app/modules/hello/controllers/{hello.controllers.ts → web/hello.controllers.ts} +1 -1
  68. package/templates/app/modules/hello/controllers/{greetings.controllers.ts → web/hello.greetings.controllers.ts} +2 -2
  69. package/templates/app/modules/hello/module.acl.ts +12 -7
  70. package/templates/app/modules/hello/queues/hello.queues.ts +1 -1
  71. package/templates/app/modules/hello/routes/{greetings.routes.ts → web/hello.greetings.routes.ts} +4 -4
  72. package/templates/app/modules/hello/routes/{hello.routes.ts → web/hello.routes.ts} +14 -8
  73. package/templates/app/modules/security/helpers/rate-limit.ts +3 -2
  74. package/templates/app/modules/security/routes/security.routes.ts +4 -4
  75. package/templates/app/modules/settings/bootstrap/log-streams.bootstrap.ts +2 -2
  76. package/templates/app/modules/settings/controllers/{settings.controllers.ts → bo.settings.controllers.ts} +7 -6
  77. package/templates/app/modules/settings/helpers/log-stream.ts +6 -5
  78. package/templates/app/modules/settings/routes/{settings.routes.ts → bo.settings.routes.ts} +3 -3
  79. package/templates/app/modules/settings/tests/settings.test.ts +21 -25
  80. package/templates/app/modules/users/controllers/{users.controllers.ts → bo.users.controllers.ts} +5 -55
  81. package/templates/{overlays/no-queues/modules/users/controllers/users.controllers.ts → app/modules/users/controllers/web.users.controllers.ts} +4 -42
  82. package/templates/app/modules/users/module.acl.ts +4 -4
  83. package/templates/app/modules/users/queues/users.queues.ts +1 -1
  84. package/templates/app/modules/users/routes/bo.users.routes.ts +39 -0
  85. package/templates/app/modules/users/routes/web.users.routes.ts +45 -0
  86. package/templates/app/nginx/production.conf +1 -1
  87. package/templates/app/scripts/deploy-nginx.ts +3 -2
  88. package/templates/app/tests/support.ts +4 -3
  89. package/templates/claude/skills/tulipes-agent-tool/SKILL.md +6 -4
  90. package/templates/claude/skills/tulipes-boot-errors/SKILL.md +13 -7
  91. package/templates/claude/skills/tulipes-config/SKILL.md +6 -4
  92. package/templates/claude/skills/tulipes-endpoint/SKILL.md +34 -15
  93. package/templates/claude/skills/tulipes-env-variable/SKILL.md +62 -37
  94. package/templates/claude/skills/tulipes-i18n/SKILL.md +20 -1
  95. package/templates/claude/skills/tulipes-module/SKILL.md +10 -5
  96. package/templates/claude/skills/tulipes-permissions/SKILL.md +123 -35
  97. package/templates/claude/skills/tulipes-queue/SKILL.md +3 -2
  98. package/templates/claude/skills/tulipes-settings/SKILL.md +9 -8
  99. package/templates/claude/skills/tulipes-spec/SKILL.md +12 -2
  100. package/templates/minimal/config/app.config.ts +13 -3
  101. package/templates/minimal/modules/agent/routes/{agent.routes.ts → web.agent.routes.ts} +2 -2
  102. package/templates/minimal/modules/core/module.acl.ts +7 -1
  103. package/templates/minimal/modules/core/package.json +4 -4
  104. package/templates/minimal/modules/hello/module.acl.ts +1 -1
  105. package/templates/minimal/modules/hello/routes/{health.routes.ts → web.hello.health.routes.ts} +1 -1
  106. package/templates/minimal/modules/hello/routes/{hello.routes.ts → web.hello.routes.ts} +1 -1
  107. package/templates/overlays/no-queues/modules/hello/controllers/{greetings.controllers.ts → web/hello.greetings.controllers.ts} +1 -1
  108. package/templates/overlays/no-queues/modules/users/controllers/bo.users.controllers.ts +57 -0
  109. package/templates/versions.json +8 -8
  110. package/templates/app/modules/users/routes/users.routes.ts +0 -60
package/dist/spec.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../src/spec.ts"],"names":[],"mappings":"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"}
package/dist/sync.js CHANGED
@@ -1,22 +1,21 @@
1
1
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
  import { BootReport } from "@tulipes/core/errors";
4
- import { discoverModuleMeta } from "@tulipes/core/env";
5
- import { discoverModules } from "@tulipes/core/modules";
6
- import { sortModules } from "@tulipes/core/modules";
7
- import { exploreModules } from "@tulipes/core/modules";
4
+ import { discoverModules, exploreModules, sortModules } from "@tulipes/core/modules";
8
5
  /**
9
6
  * `tulipes sync` — everything derivable from the declarations, derived:
10
7
  *
11
- * types/tulipes.d.ts KnownVariables, GlobalConfig, KnownModels and
12
- * KnownJobs augmentations. A config namespace is typed
13
- * as the OUTPUT of the schema its module.config.ts
14
- * declares, a model as its file's default export and a
15
- * queue's job data as its file's `jobs` schema, all via
16
- * `typeof import(...)` — the compiler does the
17
- * inference, codegen never executes module code. The
18
- * one thing read from source is each model's name,
19
- * which keys the interface and so must be a literal.
8
+ * types/tulipes.d.ts EnvValues, GlobalConfig, KnownModels and KnownJobs
9
+ * augmentations. A config namespace is typed as the
10
+ * OUTPUT of the schema its module.config.ts declares,
11
+ * intersected with the read-only environment values
12
+ * its package.json declares; a model as its file's
13
+ * default export and a queue's job data as its file's
14
+ * `jobs` schema, all via `typeof import(...)` — the
15
+ * compiler does the inference, codegen never executes
16
+ * module code. The one thing read from source is each
17
+ * model's name, which keys the interface and so must
18
+ * be a literal.
20
19
  * .env.example every variable, grouped by module and `group`.
21
20
  *
22
21
  * Both files are committed and marked generated.
@@ -28,18 +27,14 @@ export const TYPES_FILE = "tulipes.d.ts";
28
27
  export async function runSync(rootDir) {
29
28
  const report = new BootReport();
30
29
  const modulesDir = join(rootDir, "modules");
31
- const metas = discoverModuleMeta(modulesDir, report);
30
+ // The manifests carry the environment declarations; load order keeps
31
+ // the output stable.
32
32
  const order = sortModules(discoverModules(modulesDir, report), report);
33
33
  report.throwIfAny("sync");
34
- const metaByModule = new Map(metas.map((meta) => [meta.module, meta]));
35
- // Meta discovery walks folders; align it to load order for stable output.
36
- const orderedMetas = order
37
- .map((module) => metaByModule.get(module.name))
38
- .filter((meta) => meta !== undefined);
39
34
  const typesDir = join(rootDir, "types");
40
35
  mkdirSync(typesDir, { recursive: true });
41
36
  const dtsPath = join(typesDir, TYPES_FILE);
42
- writeFileSync(dtsPath, renderDts(rootDir, orderedMetas, order));
37
+ writeFileSync(dtsPath, renderDts(rootDir, order));
43
38
  console.log(`wrote ${relative(rootDir, dtsPath)}`);
44
39
  const legacyPath = join(typesDir, LEGACY_DTS);
45
40
  if (existsSync(legacyPath) && readFileSync(legacyPath, "utf8").startsWith(GENERATED_MARKER)) {
@@ -47,29 +42,39 @@ export async function runSync(rootDir) {
47
42
  console.log(`removed ${relative(rootDir, legacyPath)} (now ${TYPES_FILE})`);
48
43
  }
49
44
  const envExamplePath = join(rootDir, ".env.example");
50
- writeFileSync(envExamplePath, renderEnvExample(orderedMetas));
45
+ writeFileSync(envExamplePath, renderEnvExample(order));
51
46
  console.log(`wrote ${relative(rootDir, envExamplePath)}`);
52
47
  }
53
- function renderDts(rootDir, metas, order) {
54
- const variableLines = metas.flatMap((meta) => meta.variables.flatMap((spec) => [
55
- ` /** [${meta.module}] ${spec.description} */`,
56
- ` ${spec.name}: ${tsTypeOf(spec)};`,
57
- ]));
48
+ function renderDts(rootDir, order) {
49
+ // `env.<module>.<name>` in config/app.config.ts: one block per module that
50
+ // declares anything, each value read-only, optional when nothing
51
+ // guarantees it (no default, not required).
52
+ const envLines = order
53
+ .filter((module) => Object.keys(module.env).length > 0)
54
+ .flatMap((module) => [
55
+ ` ${JSON.stringify(module.name)}: {`,
56
+ ...envFieldLines(module, " "),
57
+ ` };`,
58
+ ]);
58
59
  const explored = exploreModules(order);
59
60
  // `.ts` specifiers: the app imports its own files that way (Node strips
60
61
  // the types natively) and tsconfig allows importing them.
61
62
  const specifierOf = (file) => "./" + relative(join(rootDir, "types"), file);
62
- // Only modules with a declaration get a typed namespace; the rest fall
63
- // back to GlobalConfig's index signature.
63
+ // A module's namespace is its settings' output, its environment values,
64
+ // or both; a module with neither falls back to GlobalConfig's index
65
+ // signature. Optional on purpose: a namespace only exists once phase 6
66
+ // has loaded it, and an empty config seed must stay assignable.
64
67
  const configLines = explored
65
- .filter((files) => files.configFile)
66
- .flatMap((files) => [
67
- // Optional on purpose: a namespace only exists once phase 6 has loaded
68
- // it, and an empty config seed must stay assignable.
69
- ` ${JSON.stringify(files.module.name)}?: import("zod/v4").output<`,
70
- ` (typeof import("${specifierOf(files.configFile)}"))["default"]["schema"]`,
71
- ` >;`,
72
- ]);
68
+ .filter((files) => files.configFile || Object.keys(files.module.env).length > 0)
69
+ .flatMap((files) => {
70
+ const parts = [];
71
+ if (files.configFile) {
72
+ parts.push(`import("zod/v4").output<(typeof import("${specifierOf(files.configFile)}"))["default"]["schema"]>`);
73
+ }
74
+ if (Object.keys(files.module.env).length > 0)
75
+ parts.push(`EnvValues[${JSON.stringify(files.module.name)}]`);
76
+ return [` ${JSON.stringify(files.module.name)}?: ${parts.join(" & ")};`];
77
+ });
73
78
  // A model's name keys the interface, so it is read from the file — the
74
79
  // one place codegen looks at source rather than types. A name that is not
75
80
  // a literal is reported and the model stays reachable through the
@@ -109,13 +114,10 @@ function renderDts(rootDir, metas, order) {
109
114
  return [
110
115
  `${GENERATED_MARKER} — do not edit.`,
111
116
  "",
112
- 'declare module "@tulipes/core/env" {',
113
- " interface KnownVariables {",
114
- ...variableLines,
115
- " }",
116
- "}",
117
- "",
118
117
  'declare module "@tulipes/core/config" {',
118
+ " interface EnvValues {",
119
+ ...envLines,
120
+ " }",
119
121
  " interface GlobalConfig {",
120
122
  ...configLines,
121
123
  " }",
@@ -135,6 +137,16 @@ export function modelNameOf(file) {
135
137
  const scope = DEFAULT_EXPORT_OBJECT.exec(source)?.[1] ?? source;
136
138
  return NAME_LITERAL.exec(scope)?.[2] ?? NAME_LITERAL.exec(source)?.[2];
137
139
  }
140
+ /** `/** doc *\/ readonly name: type;` for each of a module's declared values. */
141
+ function envFieldLines(module, indent) {
142
+ return Object.entries(module.env).flatMap(([name, spec]) => {
143
+ const guaranteed = spec.required === true || spec.default !== undefined;
144
+ return [
145
+ `${indent}/** ${spec.key} — ${spec.description} */`,
146
+ `${indent}readonly ${name}: ${tsTypeOf(spec)}${guaranteed ? "" : " | undefined"};`,
147
+ ];
148
+ });
149
+ }
138
150
  function tsTypeOf(spec) {
139
151
  switch (spec.type) {
140
152
  case "number":
@@ -147,29 +159,30 @@ function tsTypeOf(spec) {
147
159
  return "string";
148
160
  }
149
161
  }
150
- function renderEnvExample(metas) {
162
+ function renderEnvExample(order) {
151
163
  const lines = [
152
164
  "# AUTO-GENERATED by `tulipes sync` from every module's package.json tulipes.env.",
153
165
  "# Copy into .envs/.env.<mode> and fill in the blanks.",
154
166
  ];
155
- for (const meta of metas) {
156
- if (meta.variables.length === 0)
167
+ for (const module of order) {
168
+ const variables = Object.entries(module.env);
169
+ if (variables.length === 0)
157
170
  continue;
158
- lines.push("", `# ━━━ ${meta.module} ━━━`);
159
- // Stable grouping: order of first appearance, ungrouped vars last.
171
+ lines.push("", `# ━━━ ${module.name} ━━━`);
172
+ // Stable grouping: order of first appearance, ungrouped variables last.
160
173
  const groups = new Map();
161
- for (const spec of meta.variables) {
162
- const key = spec.group ?? "";
163
- (groups.get(key) ?? groups.set(key, []).get(key)).push(spec);
174
+ for (const entry of variables) {
175
+ const group = entry[1].group ?? "";
176
+ (groups.get(group) ?? groups.set(group, []).get(group)).push(entry);
164
177
  }
165
178
  for (const [group, specs] of [...groups.entries()].sort(([a], [b]) => Number(a === "") - Number(b === ""))) {
166
179
  if (group)
167
180
  lines.push("", `# ── ${group} ──`);
168
- for (const spec of specs) {
181
+ for (const [name, spec] of specs) {
169
182
  const constraint = spec.type === "enum" ? `enum: ${spec.enum.join(" | ")}` : spec.type;
170
183
  const requirement = spec.required ? ", REQUIRED" : "";
171
- lines.push(`# ${spec.description} (${constraint}${requirement})`);
172
- lines.push(`${spec.name}=${spec.default ?? ""}`);
184
+ lines.push(`# ${spec.description} (${constraint}${requirement}) → config.${module.name}.${name}`);
185
+ lines.push(`${spec.key}=${spec.default ?? ""}`);
173
186
  }
174
187
  }
175
188
  }
package/dist/sync.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tulipes/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0-rc.2",
4
4
  "description": "The tulipes command line: scaffold, sync, inspect, describe and run Tulipes apps",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,12 +19,12 @@
19
19
  "access": "public"
20
20
  },
21
21
  "dependencies": {
22
- "@tulipes/core": "^0.11.0"
22
+ "@tulipes/core": "^0.12.0-rc.2"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^24.2.0",
26
26
  "typescript": "^7.0.2",
27
- "@tulipes/spec": "0.3.2"
27
+ "@tulipes/spec": "0.4.0-rc.2"
28
28
  },
29
29
  "repository": {
30
30
  "type": "git",
@@ -46,7 +46,7 @@
46
46
  "url": "https://github.com/kemora13conf/bp-backend-express/issues"
47
47
  },
48
48
  "peerDependencies": {
49
- "@tulipes/spec": "^0.3.2"
49
+ "@tulipes/spec": "^0.4.0-rc.1"
50
50
  },
51
51
  "peerDependenciesMeta": {
52
52
  "@tulipes/spec": {
@@ -27,10 +27,12 @@ require dedicated TEST_MONGO_URI and TEST_REDIS_URL services.
27
27
  ## Non-negotiable rules
28
28
 
29
29
  1. **Never read `process.env` directly, and keep the environment small.**
30
- A value is an environment variable only if boot needs it before the
30
+ A value is an environment value only if boot needs it before the
31
31
  database answers (connection strings, the port, log destination) or it
32
- is a secret: declared under `tulipes.env` in the module's `package.json`, read with
33
- `ctx.Environment.get("NAME")`. Everything else is a *setting*: declared
32
+ is a secret: declared under `tulipes.env` in the module's `package.json`
33
+ as `"name": { "key": "VARIABLE", … }`, read as
34
+ `ctx.config.<module>.<name>` — read-only, invisible to listings, beside
35
+ the module's settings. Everything else is a *setting*: declared
34
36
  with a zod schema in that module's `module.config.ts`, stored in the
35
37
  settings store (the `settings` collection here; a JSON file under
36
38
  `config/` in an app without the Mongo store), edited through the
@@ -52,9 +54,10 @@ require dedicated TEST_MONGO_URI and TEST_REDIS_URL services.
52
54
  5. **Run `pnpm sync` after touching a module's `tulipes.env`, a
53
55
  settings schema in `module.config.ts`, a model file or a queue file's
54
56
  `jobs` export.** It regenerates `types/tulipes.d.ts` and `.env.example`;
55
- stale generated types are a lie. With it, `ctx.models.get("User")` is
56
- typed from the model's schema and `queues.add("users.welcome", …)` from
57
- the queue's job schema, so a wrong name or payload does not compile. `module.config.ts`
57
+ stale generated types are a lie. With it, `ctx.config.core.port` is a
58
+ `number`, `ctx.models.get("User")` is typed from the model's schema and
59
+ `queues.add("users.welcome", …)` from the queue's job schema, so a wrong
60
+ name or payload does not compile. `module.config.ts`
58
61
  has exactly one export, `defineConfig({ schema })`; initialization and
59
62
  cleanup belong to `bootstrap/*.bootstrap.ts`, which may return the
60
63
  function that closes what it opened, and long-lived work ends on
@@ -64,11 +67,14 @@ require dedicated TEST_MONGO_URI and TEST_REDIS_URL services.
64
67
  `throw new HttpError(404, "notFound")`, where the message is an i18n
65
68
  key and the code derives from it. Never hand-roll an error shape.
66
69
  → skill `tulipes-response`
67
- 7. **Every route declares a RAI**: put `rai({ id: "x:read" })` first in
68
- each endpoint handler chain, including routes on a nested router.
69
- The id is also the ACL permission, and core refuses to boot if a route
70
- is missing one. Public is `acl.allow("guest", "x:read")`, never a
71
- missing check. → skill `tulipes-endpoint`
70
+ 7. **Every route declares a RAI**: put `rai({ id: "users:bo:list" })` first
71
+ in each endpoint handler chain, including routes on a nested router.
72
+ The id is always `module:scope:resource[:extra]` — `users:mobile:profile`,
73
+ `users:bo:detail:delete` — where the scope is the surface it serves
74
+ (`web`, `bo`, …). It is also the ACL permission, and core refuses to boot
75
+ if a route is missing one or the id has another shape. Public is
76
+ `acl.allow("guest", "users:web:list")`, never a missing check.
77
+ → skills `tulipes-endpoint`, `tulipes-permissions`
72
78
  8. **Validate input with a zod schema on the RAI** (`body`, `query`,
73
79
  `params`, from `zod/v4`) rather than hand-rolled `if` checks. The same
74
80
  declaration generates the OpenAPI and Postman documents, so they cannot
@@ -85,7 +91,7 @@ require dedicated TEST_MONGO_URI and TEST_REDIS_URL services.
85
91
  the auth module's `Credential` model, which is `select: false`.
86
92
  12. **When auth is installed, a new feature module's namespace goes in BOTH the `admin` and
87
93
  `sysadmin` grants** in `modules/auth/module.acl.ts`. Neither role holds
88
- `*` — a wildcard would reach `config:write` and `settings:*` and make
94
+ `*` — a wildcard would reach `config:bo:write` and `settings:*` and make
89
95
  the sysadmin boundary a label rather than a rule. → skills
90
96
  `tulipes-config`, `tulipes-settings`
91
97
  13. **Cross-module imports use the package name** (`@app/users`,
@@ -106,19 +112,23 @@ require dedicated TEST_MONGO_URI and TEST_REDIS_URL services.
106
112
  never reach into another module's files from a test.
107
113
 
108
114
  For endpoint changes, read `tulipes-endpoint`: native routers live in
109
- `routes/*.routes.ts`, plain `(ctx) => Router` functions: build the router from
110
- the context, return it for the configured prefix, and read models, settings
111
- and the environment inside handlers, never while building — `tulipes routes`
112
- and `tulipes spec` build every router with nothing acquired. Global
113
- middleware that needs the environment is built on its first request with
114
- `lazy()` from `@app/core`.
115
+ `routes/`, named scope-first — `routes/web.users.routes.ts`,
116
+ `routes/bo.users.routes.ts`, and a folder per scope when one scope has
117
+ several files (`routes/web/hello.greetings.routes.ts`); controllers mirror
118
+ them (`controllers/bo.users.controllers.ts` exports `BoUsersController`).
119
+ They are plain `(ctx) => Router` functions: build the router from
120
+ the context, return it for the configured prefix, and read models and
121
+ config inside handlers, never while building — `tulipes routes` and
122
+ `tulipes spec` build every router with nothing acquired, and an environment
123
+ value read while building throws. Global middleware that needs one is built
124
+ on its first request with `lazy()` from `@app/core`.
115
125
 
116
126
  ## Where things go
117
127
 
118
128
  | Decision | Location |
119
129
  |---|---|
120
- | Connection string, secret, or anything boot needs before the database answers | `tulipes.env` in that module's `package.json` |
121
- | Policy shared by ≥2 modules and the same everywhere (pagination, locales) | `config/app.config.ts` |
130
+ | Connection string, secret, or anything boot needs before the database answers | `tulipes.env` in that module's `package.json`, read at `ctx.config.<module>.<name>` |
131
+ | Policy shared by ≥2 modules and the same everywhere (pagination, locales); wiring the environment to what core and the providers read (`server.port`, `mongoose.*`, `bullmq.redisUrl`) | `config/app.config.ts` |
122
132
  | A value an operator may change at runtime (limits, modes, toggles, lifetimes) | a field in that module's `module.config.ts` schema, read at `ctx.config.<module>` |
123
133
  | Opening a client, seeding rows, ensuring indexes, and closing them again | that module's `bootstrap/*.bootstrap.ts`, returning the cleanup |
124
134
  | A string a user will read | that module's `i18n/<locale>.json`, reached with `req.t` |
@@ -128,7 +138,7 @@ middleware that needs the environment is built on its first request with
128
138
  | Helper used by ≥2 modules | the sys core module, exported from its `lib/index.ts` and imported as `@app/core` |
129
139
  | Helper used by one module | that module's `helpers/` |
130
140
  | One-off task (backfill, export, admin chore) | `scripts/`, booted with `mode: "script"` |
131
- | Anything you would `console.log` | `moduleLogger(ctx.Environment, "<module>", ctx.mode)` from `@app/core` |
141
+ | Anything you would `console.log` | `moduleLogger(ctx, "<module>")` from `@app/core` |
132
142
 
133
143
  ## Boot order (why load order matters)
134
144
 
@@ -145,11 +155,13 @@ luck.
145
155
  | `pnpm dev` | run the backend process (regenerates types first) |
146
156
  | `pnpm worker` | run the queue-consumer process |
147
157
  | `pnpm sync` | regenerate `types/tulipes.d.ts` + `.env.example` |
158
+ | `pnpm tulipes routes [--scope s]` | every endpoint with the roles that reach it, offline |
159
+ | `pnpm tulipes roles [--role r]` | every role with its description, extras and grants, offline |
148
160
  | `pnpm tulipes agent manifest [--role r]` | print every tool the app exposes to agents, offline |
149
161
  | `pnpm test` | every `tests/` and `modules/*/tests/` file; each boots the app once through `@tulipes/testing` |
150
- | `pnpm check` | validate the environment without booting |
162
+ | `pnpm check` | resolve every environment value and show where it lands, without booting |
151
163
  | `pnpm typecheck` | typecheck the whole app |
152
- | `pnpm tulipes new module <name>` | scaffold a module |
164
+ | `pnpm tulipes new module <name> [--scope s]` | scaffold a module (its first endpoint in scope `s`, default `web`) |
153
165
  | `tulipes update` | upgrade the global cli and the framework in every manifest |
154
166
  | `pnpm script scripts/x.ts` | run a one-off script against the real app |
155
167
 
@@ -1,7 +1,8 @@
1
1
  # Development. NOT committed — .gitignore excludes .envs/, because a
2
2
  # file holding working credentials does not belong in a repository.
3
3
  # The committed contract is .env.example at the root: copy it here and
4
- # fill it in. Real deployments inject variables through the container
4
+ # fill it in. Each variable lands on one module's config — `pnpm check`
5
+ # shows where. Real deployments inject variables through the container
5
6
  # environment, which always wins over this file.
6
7
 
7
8
  PORT=3000
@@ -5,8 +5,8 @@
5
5
  # The blanks below are intentional. Real deployments inject MONGO_URI,
6
6
  # REDIS_URL and any secret through the container environment, which
7
7
  # always wins over this file. Booting without them fails closed with a
8
- # report naming every missing variable — run `pnpm check` to see it
9
- # without starting the app.
8
+ # report naming every missing variable — run `pnpm check` to see it,
9
+ # and where each value lands, without starting the app.
10
10
 
11
11
  PORT=3000
12
12
  LOG_LEVEL=info
@@ -5,8 +5,8 @@
5
5
  # The blanks below are intentional. Real deployments inject MONGO_URI,
6
6
  # REDIS_URL and any secret through the container environment, which
7
7
  # always wins over this file. Booting without them fails closed with a
8
- # report naming every missing variable — run `pnpm check` to see it
9
- # without starting the app.
8
+ # report naming every missing variable — run `pnpm check` to see it,
9
+ # and where each value lands, without starting the app.
10
10
 
11
11
  PORT=3000
12
12
  LOG_LEVEL=info
@@ -10,9 +10,10 @@ pnpm install
10
10
  pnpm dev # boot the backend process → http://localhost:3000/api/v1/hello
11
11
  ```
12
12
 
13
- Needs mongo and redis running — see `MONGO_URI` and `REDIS_URL` in
14
- `.envs/.env.development`. Run `pnpm worker` in a second terminal to
15
- consume queued jobs.
13
+ Needs mongo and redis running — see `MONGO_URI` (or the `MONGO_HOST`,
14
+ `MONGO_USERNAME`, `MONGO_PASSWORD` and `MONGO_DBNAME` parts) and
15
+ `REDIS_URL` in `.envs/.env.development`. Run `pnpm worker` in a second
16
+ terminal to consume queued jobs.
16
17
 
17
18
  `.envs/` is git-ignored: those files hold working credentials. On a
18
19
  fresh clone there is nothing in it, so copy `.env.example` — which is
@@ -23,7 +24,7 @@ declared variables — and fill it in:
23
24
  pnpm sync # refresh .env.example and types
24
25
  mkdir -p .envs # git does not track empty dirs
25
26
  cp .env.example .envs/.env.development
26
- pnpm check # validate it without booting
27
+ pnpm check # resolve it without booting, and see where each value lands
27
28
  ```
28
29
 
29
30
  ## Verify
@@ -33,7 +34,8 @@ services: `TEST_MONGO_URI=mongodb://127.0.0.1:27017/tulipes_tests TEST_REDIS_URL
33
34
  Use test-only data: the suite creates users and sessions. `pnpm spec` writes
34
35
  OpenAPI/Postman documents offline, without services or runtime secrets.
35
36
  Change the shared API prefix in `config/routes.ts`. Runtime middleware stays
36
- with endpoints in `routes/*.routes.ts`, plain functions of the context.
37
+ with endpoints in `routes/`, plain functions of the context, named
38
+ scope-first (`routes/web.users.routes.ts`, `routes/bo.users.routes.ts`).
37
39
  Staging/production boot rejects development credentials and equal JWT keys.
38
40
  Auth, settings and deployment examples are application policy you own.
39
41
 
@@ -44,11 +46,11 @@ before writing your own:
44
46
 
45
47
  | File | Contract |
46
48
  |---|---|
47
- | `package.json` `tulipes.env` | the module's environment contract |
49
+ | `package.json` `tulipes.env` | the module's environment values, read at `ctx.config.hello.<name>` (hello declares none) |
48
50
  | `module.config.ts` | `defineConfig({ schema })`: this module's settings |
49
- | `module.acl.ts` | permission grants on `hello:*` |
50
- | `routes/*.routes.ts` | native routers, shared middleware and RAI schemas |
51
- | `controllers/` | the work; constructed from the context while routes build |
51
+ | `module.acl.ts` | permission grants on `hello:web:*` ids, with prefix wildcards |
52
+ | `routes/web/*.routes.ts` | native routers, shared middleware and RAI schemas; one folder per scope |
53
+ | `controllers/web/` | the work; constructed from the context while routes build |
52
54
  | `helpers/` | pure logic, no framework |
53
55
  | `i18n/<locale>.json` | this module's strings — the module is the namespace |
54
56
  | `sockets/*.sockets.ts` | a claimed Socket.IO namespace | <!-- feature:sockets -->
@@ -64,7 +66,9 @@ before writing your own:
64
66
  | `pnpm worker` | run the queue-consumer process (needs a queue first) | <!-- feature:queues -->
65
67
  | `pnpm sync` | regenerate `types/tulipes.d.ts` + `.env.example` |
66
68
  | `pnpm check` | validate env against every module contract — CI gate |
67
- | `pnpm tulipes new module <name>` | scaffold a module |
69
+ | `pnpm tulipes new module <name>` | scaffold a module (`--scope bo` for the back office) |
70
+ | `pnpm tulipes routes` | every endpoint and the roles that reach it (`--scope bo` for one surface) |
71
+ | `pnpm tulipes roles` | every role, its description and the grants it holds |
68
72
  | `pnpm tulipes update` | upgrade the framework everywhere it is declared |
69
73
 
70
74
  ## Authentication
@@ -75,21 +79,29 @@ becomes the **guest** caller, which is what makes a public endpoint
75
79
  an ordinary ACL grant instead of a second mechanism:
76
80
 
77
81
  ```ts
78
- acl.allow("guest", "auth:login"); // that is why login is reachable
82
+ acl.allow("guest", "auth:web:login"); // that is why login is reachable
79
83
  ```
80
84
 
81
85
  Guarding is the RAI itself — its `id` IS the permission, so there is
82
- no second call to forget. 401 means "sign in"; 403 means "signed in,
83
- still not allowed":
86
+ no second call to forget. An id is always `module:scope:resource[:extra]`:
87
+ `users:web:list`, `users:bo:list:deleted`. 401 means "sign in"; 403 means
88
+ "signed in, still not allowed":
84
89
 
85
90
  ```ts
86
91
  router.get(
87
92
  `${base}/invoices`,
88
- rai({ id: "invoices:read", name: "List invoices" }),
93
+ rai({ id: "billing:web:invoices", name: "List invoices" }),
89
94
  billing.list(),
90
95
  );
91
96
  ```
92
97
 
98
+ Roles are defined once in `modules/auth/module.acl.ts`, each with a
99
+ description and any extra the app reads back (`level` here):
100
+
101
+ ```ts
102
+ acl.defineRole("admin", { description: "Runs the back office", level: 2 });
103
+ ```
104
+
93
105
  A route that declares no RAI would be reachable by guest, so core
94
106
  scans the router stack after mounting and refuses to boot when it
95
107
  finds one. Run `pnpm tulipes routes` to see every endpoint with the
@@ -139,8 +151,9 @@ TTL indexes.
139
151
  ## The console
140
152
 
141
153
  `modules/config` is the operator's console: `GET /api/v1/config`
142
- describes every module — manifest, declared variables (names, types and
143
- sources; a secret's value never), settings with their schema, live values
154
+ describes every module — manifest, environment values (config name,
155
+ variable, type and source; a secret's value never), settings with their
156
+ schema, live values
144
157
  and stored version, endpoints with the roles that reach them — and
145
158
  `PATCH /api/v1/config/:module` changes a module's settings: validated by
146
159
  its schema, refused on a stale version, audited (names, never values)
@@ -149,7 +162,7 @@ writing needs `sysadmin`. Settings live in MongoDB (`settings` and
149
162
  `settings_history`) because the root `package.json` declares
150
163
  `@tulipes/mongoose/settings`; the request pipeline — proxy trust, CORS,
151
164
  HSTS, the body limit, the rate limits — is configured from
152
- `config.security` this way, so none of it is an environment variable.
165
+ `config.security` this way, so none of it is an environment value.
153
166
 
154
167
  ## Logging
155
168
 
@@ -158,7 +171,7 @@ The logger lives in the core module and is shared by every other one:
158
171
  ```ts
159
172
  import { moduleLogger } from "@app/core";
160
173
 
161
- const log = moduleLogger(ctx.Environment, "billing", ctx.mode);
174
+ const log = moduleLogger(ctx, "billing");
162
175
  log.info({ invoiceId }, "invoice sent");
163
176
  ```
164
177
 
@@ -173,9 +186,10 @@ Files carry the process mode — `app-backend.log`, `app-worker.log` —
173
186
  because backend and worker run at the same time and two processes
174
187
  rotating one file will race.
175
188
 
176
- The destinations are environment, because the logger exists before the
177
- settings store answers: `LOG_LEVEL`, `LOG_CONSOLE`, `LOG_FILE` and
178
- `LOG_DIR`, declared in `modules/core/package.json`. The file names and
189
+ The destinations are environment values, because the logger exists before
190
+ the settings store answers: `LOG_LEVEL`, `LOG_CONSOLE`, `LOG_FILE` and
191
+ `LOG_DIR`, declared in `modules/core/package.json` and read as
192
+ `config.core.logLevel`, `logConsole`, `logFile` and `logDir`. The file names and
179
193
  the rotation (daily or 20m, whichever trips first; fourteen rotated
180
194
  files kept) never needed to differ per deployment, so they are the
181
195
  `LOG_FILES` constants in `modules/core/helpers/logger/`. A `sysadmin`
@@ -268,8 +282,8 @@ redis hold the event loop open, so a script that forgets
268
282
  `nginx/{development,staging,production}.conf` are templates with
269
283
  `{{DOMAIN}}` and `{{PORT}}` placeholders. The deploy script renders
270
284
  the one matching `APP_ENV`, using that same environment's
271
- the `core.publicDomain` setting and `PORT`, so template, domain and port can never
272
- disagree:
285
+ `core.publicDomain` setting and the port `config/app.config.ts` wired from
286
+ `PORT`, so template, domain and port can never disagree:
273
287
 
274
288
  ```sh
275
289
  APP_ENV=production pnpm script scripts/deploy-nginx.ts --dry-run
@@ -309,12 +323,13 @@ PM2 remembers the environment a process was started with:
309
323
 
310
324
  `.envs/` holds one file per mode and is **git-ignored** — those files
311
325
  carry working credentials. What is committed is `.env.example`,
312
- generated by `pnpm sync`, naming every variable any module declares:
313
- a clone copies it into `.envs/.env.<mode>` and fills it in. Servers
314
- usually skip the file entirely and inject variables through the
315
- container environment, which always wins over it. `pnpm check`
316
- validates the current environment without booting, so a deploy can
317
- fail on configuration before it fails on traffic.
326
+ generated by `pnpm sync`, naming every variable any module declares
327
+ and the `config.<module>.<name>` it lands on: a clone copies it into
328
+ `.envs/.env.<mode>` and fills it in. Servers usually skip the file
329
+ entirely and inject variables through the container environment, which
330
+ always wins over it. `pnpm check` resolves the current environment
331
+ without booting, so a deploy can fail on configuration before it fails
332
+ on traffic.
318
333
 
319
334
  PM2 is a devDependency so the scripts work straight after install;
320
335
  servers usually install it globally instead (`npm i -g pm2`), in