@tulipes/cli 0.1.1 → 0.2.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 (209) hide show
  1. package/README.md +10 -7
  2. package/dist/agent.d.ts +12 -0
  3. package/dist/agent.js +29 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/app-core.d.ts +8 -6
  6. package/dist/app-core.js +23 -14
  7. package/dist/app-core.js.map +1 -1
  8. package/dist/bin.js +3 -3
  9. package/dist/bin.js.map +1 -1
  10. package/dist/config-check.d.ts +8 -0
  11. package/dist/config-check.js +43 -0
  12. package/dist/config-check.js.map +1 -0
  13. package/dist/config-import.d.ts +20 -0
  14. package/dist/config-import.js +133 -0
  15. package/dist/config-import.js.map +1 -0
  16. package/dist/dev.d.ts +4 -4
  17. package/dist/dev.js +7 -10
  18. package/dist/dev.js.map +1 -1
  19. package/dist/init.d.ts +6 -11
  20. package/dist/init.js +11 -6422
  21. package/dist/init.js.map +1 -1
  22. package/dist/inspection.d.ts +9 -8
  23. package/dist/inspection.js +10 -11
  24. package/dist/inspection.js.map +1 -1
  25. package/dist/main.js +82 -11
  26. package/dist/main.js.map +1 -1
  27. package/dist/migrate.d.ts +30 -0
  28. package/dist/migrate.js +772 -0
  29. package/dist/migrate.js.map +1 -0
  30. package/dist/new-module.js +84 -43
  31. package/dist/new-module.js.map +1 -1
  32. package/dist/routes.d.ts +3 -5
  33. package/dist/routes.js +4 -6
  34. package/dist/routes.js.map +1 -1
  35. package/dist/spec.d.ts +2 -5
  36. package/dist/spec.js +13 -6
  37. package/dist/spec.js.map +1 -1
  38. package/dist/sync.d.ts +3 -11
  39. package/dist/sync.js +81 -20
  40. package/dist/sync.js.map +1 -1
  41. package/dist/template.d.ts +58 -0
  42. package/dist/template.js +244 -0
  43. package/dist/template.js.map +1 -0
  44. package/dist/update.js +4 -4
  45. package/package.json +17 -16
  46. package/templates/AGENTS.md +68 -31
  47. package/templates/CLAUDE.md +5 -4
  48. package/templates/app/.envs/.env.development +18 -0
  49. package/templates/app/.envs/.env.production +19 -0
  50. package/templates/app/.envs/.env.staging +19 -0
  51. package/templates/app/README.md +327 -0
  52. package/templates/app/app.ts +3 -0
  53. package/templates/app/config/app.config.ts +73 -0
  54. package/templates/app/docs/agent-manifest.json +724 -0
  55. package/templates/app/ecosystem.config.cjs +60 -0
  56. package/templates/app/modules/agent/controllers/agent.controllers.ts +31 -0
  57. package/templates/app/modules/agent/i18n/en.json +5 -0
  58. package/templates/app/modules/agent/i18n/fr.json +5 -0
  59. package/templates/app/modules/agent/module.acl.ts +14 -0
  60. package/templates/app/modules/agent/package.json +21 -0
  61. package/templates/app/modules/agent/routes/agent.routes.ts +29 -0
  62. package/templates/app/modules/agent/schemas/agent.schemas.ts +9 -0
  63. package/templates/app/modules/auth/bootstrap/credentials.bootstrap.ts +26 -0
  64. package/templates/app/modules/auth/bootstrap/seed-admin.bootstrap.ts +44 -0
  65. package/templates/app/modules/auth/controllers/auth.controllers.ts +312 -0
  66. package/templates/app/modules/auth/helpers/access-check.ts +42 -0
  67. package/templates/app/modules/auth/helpers/auth.ts +48 -0
  68. package/templates/app/modules/auth/helpers/documents.ts +57 -0
  69. package/templates/app/modules/auth/helpers/passport.ts +180 -0
  70. package/templates/app/modules/auth/helpers/passwords.ts +58 -0
  71. package/templates/app/modules/auth/helpers/session-cache.ts +67 -0
  72. package/templates/app/modules/auth/helpers/tokens.ts +329 -0
  73. package/templates/app/modules/auth/i18n/en.json +19 -0
  74. package/templates/app/modules/auth/i18n/fr.json +19 -0
  75. package/templates/app/modules/auth/lib/index.ts +12 -0
  76. package/templates/app/modules/auth/models/credential.model.ts +31 -0
  77. package/templates/app/modules/auth/models/revoked-token.model.ts +20 -0
  78. package/templates/app/modules/auth/models/session.model.ts +42 -0
  79. package/templates/app/modules/auth/module.acl.ts +41 -0
  80. package/templates/app/modules/auth/module.config.ts +30 -0
  81. package/templates/app/modules/auth/package.json +61 -0
  82. package/templates/app/modules/auth/routes/auth.routes.ts +94 -0
  83. package/templates/app/modules/auth/schemas/auth.schemas.ts +37 -0
  84. package/templates/app/modules/auth/tests/auth.test.ts +1079 -0
  85. package/templates/app/modules/config/controllers/config.controllers.ts +89 -0
  86. package/templates/app/modules/config/helpers/audit.ts +41 -0
  87. package/templates/app/modules/config/helpers/describe.ts +72 -0
  88. package/templates/app/modules/config/i18n/en.json +6 -0
  89. package/templates/app/modules/config/i18n/fr.json +6 -0
  90. package/templates/app/modules/config/models/config-audit.model.ts +26 -0
  91. package/templates/app/modules/config/module.acl.ts +12 -0
  92. package/templates/app/modules/config/package.json +23 -0
  93. package/templates/app/modules/config/routes/config.routes.ts +37 -0
  94. package/templates/app/modules/config/schemas/config.schemas.ts +13 -0
  95. package/templates/app/modules/config/tests/config.test.ts +126 -0
  96. package/templates/app/modules/core/bootstrap/cache.bootstrap.ts +12 -0
  97. package/templates/app/modules/core/helpers/cache/index.ts +60 -0
  98. package/templates/app/modules/core/helpers/cache/memory-cache.ts +53 -0
  99. package/templates/app/modules/core/helpers/cache/redis-cache.ts +68 -0
  100. package/templates/app/modules/core/helpers/cache/types.ts +37 -0
  101. package/templates/app/modules/core/helpers/lazy.ts +13 -0
  102. package/templates/app/modules/core/helpers/logger/index.ts +115 -0
  103. package/templates/app/modules/core/helpers/mongoose/index.ts +27 -0
  104. package/templates/app/modules/core/helpers/mongoose/plugins/base.plugin.ts +14 -0
  105. package/templates/app/modules/core/helpers/mongoose/plugins/soft-delete.plugin.ts +124 -0
  106. package/templates/app/modules/core/helpers/mongoose/plugins/timestamps.plugin.ts +13 -0
  107. package/templates/app/modules/core/helpers/mongoose/plugins/types.ts +45 -0
  108. package/templates/app/modules/core/helpers/mongoose/register.ts +20 -0
  109. package/templates/app/modules/core/lib/index.ts +34 -0
  110. package/templates/app/modules/core/module.config.ts +24 -0
  111. package/templates/app/modules/core/package.json +80 -0
  112. package/templates/app/modules/core/routes/core.routes.ts +24 -0
  113. package/templates/app/modules/core/tests/core.test.ts +19 -0
  114. package/templates/app/modules/hello/agent/hello.agent.ts +33 -0
  115. package/templates/app/modules/hello/bootstrap/seed-greetings.bootstrap.ts +27 -0
  116. package/templates/app/modules/hello/controllers/greetings.controllers.ts +43 -0
  117. package/templates/app/modules/hello/controllers/hello.controllers.ts +50 -0
  118. package/templates/app/modules/hello/helpers/greeting.ts +8 -0
  119. package/templates/app/modules/hello/i18n/en.json +8 -0
  120. package/templates/app/modules/hello/i18n/fr.json +8 -0
  121. package/templates/app/modules/hello/models/greeting.model.ts +24 -0
  122. package/templates/app/modules/hello/module.acl.ts +16 -0
  123. package/templates/app/modules/hello/module.config.ts +14 -0
  124. package/templates/app/modules/hello/package.json +24 -0
  125. package/templates/app/modules/hello/queues/hello.queues.ts +38 -0
  126. package/templates/app/modules/hello/routes/greetings.routes.ts +25 -0
  127. package/templates/app/modules/hello/routes/hello.routes.ts +44 -0
  128. package/templates/app/modules/hello/sockets/hello.sockets.ts +21 -0
  129. package/templates/app/modules/hello/tests/hello.test.ts +19 -0
  130. package/templates/app/modules/security/bootstrap/rate-limit.bootstrap.ts +8 -0
  131. package/templates/app/modules/security/helpers/hardening.ts +65 -0
  132. package/templates/app/modules/security/helpers/proxy.ts +16 -0
  133. package/templates/app/modules/security/helpers/rate-limit.ts +150 -0
  134. package/templates/app/modules/security/module.config.ts +59 -0
  135. package/templates/app/modules/security/package.json +26 -0
  136. package/templates/app/modules/security/routes/security.routes.ts +42 -0
  137. package/templates/app/modules/security/tests/security.test.ts +174 -0
  138. package/templates/app/modules/settings/bootstrap/log-streams.bootstrap.ts +12 -0
  139. package/templates/app/modules/settings/controllers/settings.controllers.ts +58 -0
  140. package/templates/app/modules/settings/helpers/access-lease.ts +99 -0
  141. package/templates/app/modules/settings/helpers/log-stream.ts +272 -0
  142. package/templates/app/modules/settings/helpers/log-tail.ts +253 -0
  143. package/templates/app/modules/settings/i18n/en.json +3 -0
  144. package/templates/app/modules/settings/i18n/fr.json +3 -0
  145. package/templates/app/modules/settings/module.acl.ts +12 -0
  146. package/templates/app/modules/settings/package.json +21 -0
  147. package/templates/app/modules/settings/routes/settings.routes.ts +33 -0
  148. package/templates/app/modules/settings/schemas/settings.schemas.ts +8 -0
  149. package/templates/app/modules/settings/tests/settings.test.ts +409 -0
  150. package/templates/app/modules/users/controllers/users.controllers.ts +113 -0
  151. package/templates/app/modules/users/i18n/en.json +14 -0
  152. package/templates/app/modules/users/i18n/fr.json +14 -0
  153. package/templates/app/modules/users/models/user.model.ts +18 -0
  154. package/templates/app/modules/users/module.acl.ts +10 -0
  155. package/templates/app/modules/users/module.config.ts +11 -0
  156. package/templates/app/modules/users/package.json +24 -0
  157. package/templates/app/modules/users/queues/users.queues.ts +31 -0
  158. package/templates/app/modules/users/routes/users.routes.ts +60 -0
  159. package/templates/app/modules/users/schemas/users.schemas.ts +30 -0
  160. package/templates/app/modules/users/sockets/users.sockets.ts +18 -0
  161. package/templates/app/nginx/development.conf +44 -0
  162. package/templates/app/nginx/production.conf +75 -0
  163. package/templates/app/nginx/staging.conf +51 -0
  164. package/templates/app/package.json +58 -0
  165. package/templates/app/scripts/count-greetings.ts +29 -0
  166. package/templates/app/scripts/deploy-nginx.ts +143 -0
  167. package/templates/app/scripts/list-users.ts +34 -0
  168. package/templates/app/tests/app.test.ts +28 -0
  169. package/templates/app/tests/support.ts +36 -0
  170. package/templates/app/tsconfig.json +28 -0
  171. package/templates/app/vitest.config.ts +6 -0
  172. package/templates/app/worker.ts +6 -0
  173. package/templates/claude/skills/tulipes-agent-tool/SKILL.md +83 -0
  174. package/templates/claude/skills/tulipes-boot-errors/SKILL.md +11 -5
  175. package/templates/claude/skills/tulipes-config/SKILL.md +74 -0
  176. package/templates/claude/skills/tulipes-endpoint/SKILL.md +38 -25
  177. package/templates/claude/skills/tulipes-env-variable/SKILL.md +54 -26
  178. package/templates/claude/skills/tulipes-model/SKILL.md +11 -4
  179. package/templates/claude/skills/tulipes-module/SKILL.md +11 -10
  180. package/templates/claude/skills/tulipes-queue/SKILL.md +20 -6
  181. package/templates/claude/skills/tulipes-settings/SKILL.md +49 -100
  182. package/templates/claude/skills/tulipes-spec/SKILL.md +10 -12
  183. package/templates/minimal/README.md +21 -0
  184. package/templates/minimal/app.ts +3 -0
  185. package/templates/minimal/config/app.config.ts +16 -0
  186. package/templates/minimal/modules/agent/module.acl.ts +3 -0
  187. package/templates/minimal/modules/agent/package.json +14 -0
  188. package/templates/minimal/modules/agent/routes/agent.routes.ts +20 -0
  189. package/templates/minimal/modules/core/module.acl.ts +4 -0
  190. package/templates/minimal/modules/core/package.json +21 -0
  191. package/templates/minimal/modules/hello/i18n/en.json +3 -0
  192. package/templates/minimal/modules/hello/module.acl.ts +2 -0
  193. package/templates/minimal/modules/hello/package.json +11 -0
  194. package/templates/minimal/modules/hello/routes/health.routes.ts +9 -0
  195. package/templates/minimal/modules/hello/routes/hello.routes.ts +10 -0
  196. package/templates/minimal/modules/security/package.json +15 -0
  197. package/templates/minimal/modules/security/routes/security.routes.ts +8 -0
  198. package/templates/minimal/package.json +38 -0
  199. package/templates/minimal/tests/app.test.ts +52 -0
  200. package/templates/minimal/tsconfig.json +22 -0
  201. package/templates/minimal/vitest.config.ts +5 -0
  202. package/templates/overlays/no-queues/ecosystem.config.cjs +52 -0
  203. package/templates/overlays/no-queues/modules/hello/controllers/greetings.controllers.ts +41 -0
  204. package/templates/overlays/no-queues/modules/users/controllers/users.controllers.ts +107 -0
  205. package/templates/versions.json +11 -0
  206. package/dist/minimal.d.ts +0 -2
  207. package/dist/minimal.js +0 -2200
  208. package/dist/minimal.js.map +0 -1
  209. /package/templates/{browser-auth.md → app/docs/browser-auth.md} +0 -0
package/README.md CHANGED
@@ -16,21 +16,24 @@ loader it carries.
16
16
  ```sh
17
17
  npm install -g @tulipes/cli@next
18
18
  tulipes init my-api # minimal preset; --preset production for the full app
19
- cd my-api && corepack enable && yarn install && yarn dev
19
+ cd my-api && pnpm install && pnpm dev
20
20
  ```
21
21
 
22
- Generated apps also list `@tulipes/cli` in `devDependencies`, so `yarn dev`,
23
- `yarn sync`, `yarn spec` and `yarn tulipes new module <name>` work without a
22
+ Generated apps also list `@tulipes/cli` in `devDependencies`, so `pnpm dev`,
23
+ `pnpm sync`, `pnpm spec` and `pnpm tulipes new module <name>` work without a
24
24
  global install and always match the app.
25
25
 
26
26
  | Command | What it does |
27
27
  |---|---|
28
28
  | `tulipes init [dir] [--minimal \| --preset minimal \| --preset production] [--no-queues] [--no-sockets]` | scaffold an application; no services. The two opt-outs apply to the production preset and leave that provider, its files, dependencies and worker process out |
29
- | `tulipes dev [entry]` | regenerate types, then run under tsx watch |
30
- | `tulipes sync` | regenerate `types/config.d.ts` and `.env.example` |
29
+ | `tulipes dev [entry]` | regenerate types, then run the entrypoint with `node --watch` |
30
+ | `tulipes sync` | regenerate `types/tulipes.d.ts` and `.env.example` |
31
31
  | `tulipes env:check` | validate the environment without booting |
32
- | `tulipes routes [--offline \| --runtime]` | list declared endpoints |
33
- | `tulipes spec [--offline \| --runtime] [--openapi file] [--postman file] [--url base]` | generate documents (needs `@tulipes/spec` in the app) |
32
+ | `tulipes routes` | list declared endpoints, through inspection |
33
+ | `tulipes spec [--openapi file] [--postman file] [--url base]` | generate documents through inspection (needs `@tulipes/spec` in the app) |
34
+ | `tulipes agent manifest [--out file] [--role role]` | every tool the app exposes to agents, as JSON, through inspection |
35
+ | `tulipes migrate 0.11` | rewrite an app generated on 0.10 to the 0.11 layout and contracts; every judgement it cannot make is a `TODO 0.11:` |
36
+ | `tulipes config:import [--env APP_ENV] [--from file] [--dry-run]` | after a migration, carry the env file's values for variables that became settings into the store |
34
37
  | `tulipes new module <name> [--model\|--no-model] [--queues\|--no-queues] [--sockets\|--no-sockets]` | scaffold a module for the providers the app declares |
35
38
  | `tulipes update` | upgrade the global CLI and, inside an app, its core and CLI ranges |
36
39
  | `tulipes -v` | print the CLI version, its bundled core, and the app's core when they differ |
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `tulipes agent manifest` — every tool the app exposes to agents, from
3
+ * the same inspection boot as `tulipes routes`: route tools from the RAIs
4
+ * with method, path and input schema, actions from `agent/*.agent.ts`.
5
+ * No service, secret or `.env` is needed. `--role` narrows it to what one
6
+ * role may call, which is what the HTTP transport serves that role.
7
+ */
8
+ export interface AgentManifestOptions {
9
+ out?: string;
10
+ role?: string;
11
+ }
12
+ export declare function runAgentManifest(rootDir: string, options: AgentManifestOptions): Promise<void>;
package/dist/agent.js ADDED
@@ -0,0 +1,29 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { inspectApp } from "./inspection.js";
4
+ export async function runAgentManifest(rootDir, options) {
5
+ const handle = await inspectApp(rootDir);
6
+ try {
7
+ const registry = handle.ctx.agent;
8
+ const manifest = registry.manifest(options.role);
9
+ const json = `${JSON.stringify(manifest, null, 2)}\n`;
10
+ if (options.out) {
11
+ const path = resolve(options.out);
12
+ mkdirSync(dirname(path), { recursive: true });
13
+ writeFileSync(path, json);
14
+ console.log(`wrote ${options.out}: ${manifest.tools.length} tool(s) across ${manifest.modules.length} module(s)`);
15
+ }
16
+ else {
17
+ process.stdout.write(json);
18
+ }
19
+ if (registry.warnings.length > 0) {
20
+ console.error(`\n${registry.warnings.length} schema(s) could not be described:`);
21
+ for (const warning of registry.warnings)
22
+ console.error(` ! ${warning}`);
23
+ }
24
+ }
25
+ finally {
26
+ await handle.shutdown();
27
+ }
28
+ }
29
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAc7C,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAe,EAAE,OAA6B;IACnF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAM,CAAC;QACnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;QAEtD,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,KAAK,CAAC,MAAM,mBAAmB,QAAQ,CAAC,OAAO,CAAC,MAAM,YAAY,CAAC,CAAC;QACpH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,MAAM,oCAAoC,CAAC,CAAC;YACjF,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ;gBAAE,OAAO,CAAC,KAAK,CAAC,OAAO,OAAO,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC"}
@@ -23,11 +23,13 @@ export declare function importFromApp<T>(rootDir: string, specifier: string): Pr
23
23
  */
24
24
  export declare function appBoot(rootDir: string): Promise<BootFn>;
25
25
  /**
26
- * Registers the TypeScript loader before any app file is imported.
27
- *
28
- * The published CLI runs as plain node while an app's modules are
29
- * TypeScript, so without this every dynamic import of a `.ts` file fails
30
- * to resolve. The hook is process-wide, so it also covers imports the
31
- * app's own core makes afterwards. Harmless when the app is compiled.
26
+ * An app's modules are TypeScript and the CLI imports them as they are:
27
+ * Node strips the types natively (every supported Node does), so there is
28
+ * no loader to register. What this checks is that the node running the
29
+ * CLI is one of those — a node too old to strip types would fail later,
30
+ * on the first module file, with a syntax error that says nothing about
31
+ * the cause.
32
32
  */
33
+ export declare function requireNativeTypeScript(): void;
34
+ /** @deprecated Since 0.11 nothing is registered; kept for callers written against 0.10. */
33
35
  export declare function registerTypeScript(): Promise<void>;
package/dist/app-core.js CHANGED
@@ -1,3 +1,11 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
1
9
  import { createRequire } from "node:module";
2
10
  import { join } from "node:path";
3
11
  import { pathToFileURL } from "node:url";
@@ -16,7 +24,7 @@ export async function importFromApp(rootDir, specifier) {
16
24
  // whatever directory the CLI itself was installed into.
17
25
  const requireFromApp = createRequire(join(rootDir, "package.json"));
18
26
  const entry = requireFromApp.resolve(specifier);
19
- return (await import(pathToFileURL(entry).href));
27
+ return (await import(__rewriteRelativeImportExtension(pathToFileURL(entry).href)));
20
28
  }
21
29
  catch {
22
30
  return undefined;
@@ -39,20 +47,21 @@ export async function appBoot(rootDir) {
39
47
  return module?.boot ?? bundledBoot;
40
48
  }
41
49
  /**
42
- * Registers the TypeScript loader before any app file is imported.
43
- *
44
- * The published CLI runs as plain node while an app's modules are
45
- * TypeScript, so without this every dynamic import of a `.ts` file fails
46
- * to resolve. The hook is process-wide, so it also covers imports the
47
- * app's own core makes afterwards. Harmless when the app is compiled.
50
+ * An app's modules are TypeScript and the CLI imports them as they are:
51
+ * Node strips the types natively (every supported Node does), so there is
52
+ * no loader to register. What this checks is that the node running the
53
+ * CLI is one of those — a node too old to strip types would fail later,
54
+ * on the first module file, with a syntax error that says nothing about
55
+ * the cause.
48
56
  */
49
- export async function registerTypeScript() {
50
- try {
51
- const tsx = (await import("tsx/esm/api"));
52
- tsx.register?.();
53
- }
54
- catch {
55
- // No tsx available: a compiled app still boots fine without it.
57
+ export function requireNativeTypeScript() {
58
+ const features = process.features;
59
+ if (!features.typescript) {
60
+ throw new Error(`Node ${process.versions.node} cannot run TypeScript natively — Tulipes apps need Node 24 (or 22.18+)`);
56
61
  }
57
62
  }
63
+ /** @deprecated Since 0.11 nothing is registered; kept for callers written against 0.10. */
64
+ export async function registerTypeScript() {
65
+ requireNativeTypeScript();
66
+ }
58
67
  //# sourceMappingURL=app-core.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-core.js","sourceRoot":"","sources":["../src/app-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKzD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,OAAe,EAAE,SAAiB;IACvE,IAAI,CAAC;QACH,sEAAsE;QACtE,wDAAwD;QACxD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAM,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAe;IAC3C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAoB,OAAO,EAAE,oBAAoB,CAAC,CAAC;IACrF,OAAO,MAAM,EAAE,IAAI,IAAI,WAAW,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC,CAA8B,CAAC;QACvE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"app-core.js","sourceRoot":"","sources":["../src/app-core.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,IAAI,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAKzD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAI,OAAe,EAAE,SAAiB;IACvE,IAAI,CAAC;QACH,sEAAsE;QACtE,wDAAwD;QACxD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChD,OAAO,CAAC,MAAM,MAAM,kCAAC,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAC,CAAM,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,OAAe;IAC3C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAoB,OAAO,EAAE,oBAAoB,CAAC,CAAC;IACrF,OAAO,MAAM,EAAE,IAAI,IAAI,WAAW,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAA2C,CAAC;IACrE,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,QAAQ,OAAO,CAAC,QAAQ,CAAC,IAAI,yEAAyE,CAAC,CAAC;IAC1H,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,uBAAuB,EAAE,CAAC;AAC5B,CAAC"}
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- // Published bin entry — compiled to dist/bin.js. The dev workspace uses
3
- // bin/tulipes.js (a tsx shim over the same main) instead; publishConfig.bin
4
- // swaps this one in at pack time.
2
+ // Published bin entry — compiled to dist/bin.js. The dev workspace runs
3
+ // this source through node's native type stripping instead; publishConfig.bin
4
+ // swaps the compiled one in at pack time.
5
5
  import { main } from "./main.js";
6
6
  await main(process.argv.slice(2));
7
7
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,wEAAwE;AACxE,4EAA4E;AAC5E,kCAAkC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,wEAAwE;AACxE,8EAA8E;AAC9E,0CAA0C;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `tulipes config:check` — boot in script mode, which loads every settings
3
+ * namespace from the store the app declares (or core's file store) and
4
+ * refuses invalid stored values the way a serving process would; then
5
+ * print what each module runs with and exit. A deploy preflight for the
6
+ * settings store, the way `env:check` is one for the environment.
7
+ */
8
+ export declare function runConfigCheck(rootDir: string): Promise<void>;
@@ -0,0 +1,43 @@
1
+ import { TulipesBootError } from "@tulipes/core/errors";
2
+ import { appBoot, requireNativeTypeScript } from "./app-core.js";
3
+ /**
4
+ * `tulipes config:check` — boot in script mode, which loads every settings
5
+ * namespace from the store the app declares (or core's file store) and
6
+ * refuses invalid stored values the way a serving process would; then
7
+ * print what each module runs with and exit. A deploy preflight for the
8
+ * settings store, the way `env:check` is one for the environment.
9
+ */
10
+ export async function runConfigCheck(rootDir) {
11
+ requireNativeTypeScript();
12
+ const boot = await appBoot(rootDir);
13
+ let handle;
14
+ try {
15
+ handle = await boot({ rootDir, mode: "script", handleSignals: false });
16
+ }
17
+ catch (error) {
18
+ if (error instanceof TulipesBootError) {
19
+ console.error(error.message);
20
+ process.exitCode = 1;
21
+ return;
22
+ }
23
+ throw error;
24
+ }
25
+ try {
26
+ const { settings } = handle.ctx;
27
+ const namespaces = settings.namespaces();
28
+ console.log(`APP_ENV=${handle.env.appEnv} — ${namespaces.length} settings namespace(s) in ${settings.status().label}:`);
29
+ for (const namespace of namespaces) {
30
+ const description = settings.describe(namespace);
31
+ const { version, updated_at, updated_by } = description.stored;
32
+ console.log(` ✔ ${namespace} v${version} ${updated_at} by ${updated_by.label ?? updated_by.id}`);
33
+ for (const [field, value] of Object.entries(description.values)) {
34
+ const pending = description.pendingRestart.includes(field) ? " (restart pending)" : "";
35
+ console.log(` ${field} = ${JSON.stringify(value)}${pending}`);
36
+ }
37
+ }
38
+ }
39
+ finally {
40
+ await handle.shutdown();
41
+ }
42
+ }
43
+ //# sourceMappingURL=config-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-check.js","sourceRoot":"","sources":["../src/config-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,uBAAuB,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC;QAChC,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,UAAU,CAAC,MAAM,6BAA6B,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;QACxH,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,OAAO,SAAS,MAAM,OAAO,KAAK,UAAU,QAAQ,UAAU,CAAC,KAAK,IAAI,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;YACrG,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChE,MAAM,OAAO,GAAG,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxF,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `tulipes config:import` — after `tulipes migrate 0.11`, carry the values a
3
+ * deployment kept in `.envs/.env.<APP_ENV>` for variables that became
4
+ * settings into the settings store, so the upgraded app boots with what
5
+ * it ran with. The mapping is `config/migration-0.11.json`, written by the
6
+ * migration; the values are validated by each module's schema before they
7
+ * are written, and written with the store's own versioning — through the
8
+ * file store the app uses by default, or the Mongo store it declares.
9
+ */
10
+ export interface ConfigImportOptions {
11
+ /** APP_ENV to read and write for; default the process's APP_ENV or "development". */
12
+ env?: string;
13
+ /** An env file to read instead of `.envs/.env.<env>`. */
14
+ from?: string;
15
+ /** Print what would be written and write nothing. */
16
+ dryRun?: boolean;
17
+ }
18
+ export declare function runConfigImport(rootDir: string, options?: ConfigImportOptions): Promise<void>;
19
+ /** `KEY=value` lines, quotes and inline comments removed — what dotenv reads, without dotenv. */
20
+ export declare function parseEnv(text: string): Record<string, string>;
@@ -0,0 +1,133 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { join, resolve } from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ import { importFromApp } from "./app-core.js";
13
+ export async function runConfigImport(rootDir, options = {}) {
14
+ const appEnv = options.env ?? process.env.APP_ENV ?? "development";
15
+ const mappingFile = join(rootDir, "config", "migration-0.11.json");
16
+ if (!existsSync(mappingFile)) {
17
+ console.error("config/migration-0.11.json not found — run `tulipes migrate 0.11` first; it records which variables became settings");
18
+ process.exitCode = 2;
19
+ return;
20
+ }
21
+ const mapping = JSON.parse(readFileSync(mappingFile, "utf8"));
22
+ const envFile = options.from ? resolve(options.from) : join(rootDir, ".envs", `.env.${appEnv}`);
23
+ const values = existsSync(envFile) ? parseEnv(readFileSync(envFile, "utf8")) : {};
24
+ const read = (name) => process.env[name] ?? values[name];
25
+ if (!existsSync(envFile))
26
+ console.warn(`${envFile} does not exist; only process.env is read`);
27
+ const core = await importFromApp(rootDir, "@tulipes/core/config");
28
+ if (!core) {
29
+ console.error("@tulipes/core is not installed in this app — pnpm install first");
30
+ process.exitCode = 2;
31
+ return;
32
+ }
33
+ const store = await openStore(rootDir, appEnv, read, core);
34
+ if (!store)
35
+ return;
36
+ console.log(`store: ${store.status().label}`);
37
+ let written = 0;
38
+ try {
39
+ for (const [namespace, fields] of Object.entries(mapping.settings)) {
40
+ const configFile = join(rootDir, "modules", namespace, "module.config.ts");
41
+ const exported = (await import(__rewriteRelativeImportExtension(pathToFileURL(configFile).href)));
42
+ if (!exported.default || !core.isConfigDeclaration(exported.default)) {
43
+ console.error(` ${namespace}: module.config.ts does not export defineConfig({ … }); skipped`);
44
+ continue;
45
+ }
46
+ const patch = {};
47
+ const sources = [];
48
+ for (const [setting, { variable, type }] of Object.entries(fields)) {
49
+ const raw = read(variable);
50
+ if (raw === undefined)
51
+ continue;
52
+ patch[setting] = coerce(raw, type);
53
+ sources.push(`${setting} ← ${variable}`);
54
+ }
55
+ if (sources.length === 0) {
56
+ console.log(` ${namespace}: nothing set for ${Object.values(fields).map((field) => field.variable).join(", ")}; the schema defaults stay`);
57
+ continue;
58
+ }
59
+ const stored = await store.load(namespace);
60
+ const candidate = { ...stored?.values, ...patch };
61
+ const parsed = exported.default.schema.safeParse(candidate);
62
+ if (!parsed.success) {
63
+ console.error(` ${namespace}: refused by the schema — ${parsed.error?.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`);
64
+ process.exitCode = 1;
65
+ continue;
66
+ }
67
+ console.log(` ${namespace}: ${sources.join(", ")}${options.dryRun ? " (dry run)" : ""}`);
68
+ if (options.dryRun)
69
+ continue;
70
+ await store.save(namespace, parsed.data, stored?.version ?? 0, { id: "config:import", label: `from ${envFile}` });
71
+ written++;
72
+ }
73
+ }
74
+ finally {
75
+ await store.dispose?.();
76
+ }
77
+ console.log(`${written} namespace(s) written for APP_ENV=${appEnv}` + (written ? "; the variables can leave the env file now" : ""));
78
+ }
79
+ async function openStore(rootDir, appEnv, read, core) {
80
+ const pkg = JSON.parse(readFileSync(join(rootDir, "package.json"), "utf8"));
81
+ const declared = pkg.tulipes?.providers?.settings;
82
+ if (!declared)
83
+ return new core.FileSettingsStore(rootDir, appEnv);
84
+ if (!declared.startsWith("@tulipes/mongoose")) {
85
+ console.error(`the app declares settings provider "${declared}", which this command cannot write to — set the values through the console once the app runs`);
86
+ process.exitCode = 2;
87
+ return undefined;
88
+ }
89
+ const uri = read("MONGO_URI");
90
+ if (!uri) {
91
+ console.error("MONGO_URI is not set in the env file or the process; the Mongo settings store needs it");
92
+ process.exitCode = 2;
93
+ return undefined;
94
+ }
95
+ const mongoose = await importFromApp(rootDir, "mongoose");
96
+ const provider = await importFromApp(rootDir, "@tulipes/mongoose");
97
+ if (!mongoose || !provider) {
98
+ console.error("mongoose and @tulipes/mongoose must be installed in the app");
99
+ process.exitCode = 2;
100
+ return undefined;
101
+ }
102
+ const connection = mongoose.createConnection(uri, { serverSelectionTimeoutMS: 5_000 });
103
+ await connection.asPromise();
104
+ const store = new provider.MongoSettingsStore(connection);
105
+ const dispose = store.dispose?.bind(store);
106
+ store.dispose = async () => { await dispose?.(); await connection.close(); };
107
+ return store;
108
+ }
109
+ function coerce(raw, type) {
110
+ switch (type) {
111
+ case "number": return Number(raw);
112
+ case "boolean": return /^(1|true|yes|on)$/i.test(raw.trim());
113
+ default: return raw;
114
+ }
115
+ }
116
+ /** `KEY=value` lines, quotes and inline comments removed — what dotenv reads, without dotenv. */
117
+ export function parseEnv(text) {
118
+ const values = {};
119
+ for (const line of text.split("\n")) {
120
+ const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
121
+ if (!match)
122
+ continue;
123
+ let value = match[2].trim();
124
+ const quoted = /^(["'])((?:(?!\1).)*)\1/.exec(value);
125
+ if (quoted)
126
+ value = quoted[2];
127
+ else
128
+ value = value.replace(/\s+#.*$/, "");
129
+ values[match[1]] = value;
130
+ }
131
+ return values;
132
+ }
133
+ //# sourceMappingURL=config-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-import.js","sourceRoot":"","sources":["../src/config-import.ts"],"names":[],"mappings":"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"}
package/dist/dev.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * `tulipes dev [entry]` — sync first (generated types must never be stale
3
- * when the dev server starts), then hand off to tsx in watch mode.
4
- *
5
- * tsx is resolved from core's own dependencies rather than trusting PATH,
6
- * so the command works in any workspace layout.
3
+ * when the dev server starts), then run the entrypoint with node itself in
4
+ * watch mode. Node strips the types natively, so there is no loader to
5
+ * resolve and nothing to transpile: what runs in development is what PM2
6
+ * runs in production.
7
7
  */
8
8
  export declare function runDev(rootDir: string, entry: string): Promise<void>;
package/dist/dev.js CHANGED
@@ -1,20 +1,17 @@
1
1
  import { spawn } from "node:child_process";
2
- import { createRequire } from "node:module";
3
- import { dirname, join } from "node:path";
4
2
  import { runSync } from "./sync.js";
3
+ import { requireNativeTypeScript } from "./app-core.js";
5
4
  /**
6
5
  * `tulipes dev [entry]` — sync first (generated types must never be stale
7
- * when the dev server starts), then hand off to tsx in watch mode.
8
- *
9
- * tsx is resolved from core's own dependencies rather than trusting PATH,
10
- * so the command works in any workspace layout.
6
+ * when the dev server starts), then run the entrypoint with node itself in
7
+ * watch mode. Node strips the types natively, so there is no loader to
8
+ * resolve and nothing to transpile: what runs in development is what PM2
9
+ * runs in production.
11
10
  */
12
11
  export async function runDev(rootDir, entry) {
12
+ requireNativeTypeScript();
13
13
  await runSync(rootDir);
14
- const require = createRequire(import.meta.url);
15
- const tsxPackage = require.resolve("tsx/package.json");
16
- const tsxCli = join(dirname(tsxPackage), "dist", "cli.mjs");
17
- const child = spawn(process.execPath, [tsxCli, "watch", "--clear-screen=false", entry], { cwd: rootDir, stdio: "inherit" });
14
+ const child = spawn(process.execPath, ["--watch", "--watch-preserve-output", entry], { cwd: rootDir, stdio: "inherit" });
18
15
  child.on("exit", (code) => {
19
16
  process.exitCode = code ?? 0;
20
17
  });
package/dist/dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAe,EAAE,KAAa;IACzD,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvB,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAE5D,MAAM,KAAK,GAAG,KAAK,CACjB,OAAO,CAAC,QAAQ,EAChB,CAAC,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAAE,KAAK,CAAC,EAChD,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CACnC,CAAC;IACF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAe,EAAE,KAAa;IACzD,uBAAuB,EAAE,CAAC;IAC1B,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvB,MAAM,KAAK,GAAG,KAAK,CACjB,OAAO,CAAC,QAAQ,EAChB,CAAC,SAAS,EAAE,yBAAyB,EAAE,KAAK,CAAC,EAC7C,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CACnC,CAAC;IACF,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QACxB,OAAO,CAAC,QAAQ,GAAG,IAAI,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/init.d.ts CHANGED
@@ -1,15 +1,10 @@
1
1
  /**
2
- * `tulipes init [dir]` — scaffold a complete application.
3
- *
4
- * Three modules ship: core (sys 0 — env contract, roles, request ids),
5
- * security (sys 10 — helmet, cors, logging, body parsing) and hello, a
6
- * worked example of EVERY module contract: config with lifecycle hooks,
7
- * ACL grants, routes, a controller, a helper, a model, a bootstrap task,
8
- * a queue with its processor, and a socket namespace.
9
- *
10
- * The generated app therefore needs mongo and redis running — models and
11
- * queues cannot work without them, and the boot refuses rather than
12
- * degrading quietly.
2
+ * `tulipes init [dir]` — scaffold a complete application from a template
3
+ * tree. The production preset is the reference application the framework
4
+ * is developed against, copied verbatim and transformed only in the ways
5
+ * `template.ts` lists; the minimal preset is a three-module API with no
6
+ * infrastructure. Neither is rendered from strings, so what this command
7
+ * writes and what the repository tests are the same files.
13
8
  */
14
9
  export type InitPreset = "minimal" | "production";
15
10
  /**