@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.
- package/LICENSE +21 -0
- package/README.md +46 -0
- package/dist/app-core.d.ts +33 -0
- package/dist/app-core.js +58 -0
- package/dist/app-core.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/dev.d.ts +8 -0
- package/dist/dev.js +22 -0
- package/dist/dev.js.map +1 -0
- package/dist/env-check.d.ts +6 -0
- package/dist/env-check.js +28 -0
- package/dist/env-check.js.map +1 -0
- package/dist/init.d.ts +25 -0
- package/dist/init.js +6416 -0
- package/dist/init.js.map +1 -0
- package/dist/inspection.d.ts +8 -0
- package/dist/inspection.js +15 -0
- package/dist/inspection.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +166 -0
- package/dist/main.js.map +1 -0
- package/dist/minimal.d.ts +2 -0
- package/dist/minimal.js +2200 -0
- package/dist/minimal.js.map +1 -0
- package/dist/new-module.d.ts +19 -0
- package/dist/new-module.js +348 -0
- package/dist/new-module.js.map +1 -0
- package/dist/package-info.d.ts +18 -0
- package/dist/package-info.js +53 -0
- package/dist/package-info.js.map +1 -0
- package/dist/routes.d.ts +8 -0
- package/dist/routes.js +54 -0
- package/dist/routes.js.map +1 -0
- package/dist/spec.d.ts +19 -0
- package/dist/spec.js +101 -0
- package/dist/spec.js.map +1 -0
- package/dist/sync.d.ts +12 -0
- package/dist/sync.js +117 -0
- package/dist/sync.js.map +1 -0
- package/dist/update.d.ts +20 -0
- package/dist/update.js +240 -0
- package/dist/update.js.map +1 -0
- package/package.json +59 -0
- package/templates/CLAUDE.md +120 -0
- package/templates/browser-auth.md +120 -0
- package/templates/claude/skills/tulipes-boot-errors/SKILL.md +74 -0
- package/templates/claude/skills/tulipes-endpoint/SKILL.md +132 -0
- package/templates/claude/skills/tulipes-env-variable/SKILL.md +78 -0
- package/templates/claude/skills/tulipes-i18n/SKILL.md +98 -0
- package/templates/claude/skills/tulipes-model/SKILL.md +107 -0
- package/templates/claude/skills/tulipes-module/SKILL.md +67 -0
- package/templates/claude/skills/tulipes-permissions/SKILL.md +105 -0
- package/templates/claude/skills/tulipes-queue/SKILL.md +68 -0
- package/templates/claude/skills/tulipes-response/SKILL.md +118 -0
- package/templates/claude/skills/tulipes-settings/SKILL.md +111 -0
- package/templates/claude/skills/tulipes-socket/SKILL.md +60 -0
- package/templates/claude/skills/tulipes-spec/SKILL.md +101 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"minimal.js","sourceRoot":"","sources":["../src/minimal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tulipes new module <name>` — scaffold every contract a module can hold,
|
|
3
|
+
* so the shape of a module is obvious from the folder rather than from
|
|
4
|
+
* memory. Delete what this feature does not need; an absent file is always
|
|
5
|
+
* valid, and deleting is faster than remembering the filename convention.
|
|
6
|
+
*
|
|
7
|
+
* The package scope is borrowed from an existing sibling so the app keeps
|
|
8
|
+
* one naming convention without configuring it anywhere.
|
|
9
|
+
*
|
|
10
|
+
* Model and bootstrap files are emitted only when the app has selected a
|
|
11
|
+
* models provider (`tulipes.providers.models` in its package.json): in a
|
|
12
|
+
* provider-free app they would make the module require a database the app
|
|
13
|
+
* refuses to boot without. `--model` / `--no-model` override the detection.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runNewModule(rootDir: string, name: string, options?: {
|
|
16
|
+
models?: boolean;
|
|
17
|
+
queues?: boolean;
|
|
18
|
+
sockets?: boolean;
|
|
19
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { BootReport } from "@tulipes/core/errors";
|
|
4
|
+
import { readProviderDeclarations } from "@tulipes/core/boot";
|
|
5
|
+
import { coreVersion } from "./package-info.js";
|
|
6
|
+
/**
|
|
7
|
+
* `tulipes new module <name>` — scaffold every contract a module can hold,
|
|
8
|
+
* so the shape of a module is obvious from the folder rather than from
|
|
9
|
+
* memory. Delete what this feature does not need; an absent file is always
|
|
10
|
+
* valid, and deleting is faster than remembering the filename convention.
|
|
11
|
+
*
|
|
12
|
+
* The package scope is borrowed from an existing sibling so the app keeps
|
|
13
|
+
* one naming convention without configuring it anywhere.
|
|
14
|
+
*
|
|
15
|
+
* Model and bootstrap files are emitted only when the app has selected a
|
|
16
|
+
* models provider (`tulipes.providers.models` in its package.json): in a
|
|
17
|
+
* provider-free app they would make the module require a database the app
|
|
18
|
+
* refuses to boot without. `--model` / `--no-model` override the detection.
|
|
19
|
+
*/
|
|
20
|
+
export async function runNewModule(rootDir, name, options = {}) {
|
|
21
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
22
|
+
console.error(`module name "${name}" is invalid — lowercase kebab-case required`);
|
|
23
|
+
process.exitCode = 2;
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const modulesDir = join(rootDir, "modules");
|
|
27
|
+
const dir = join(modulesDir, name);
|
|
28
|
+
if (existsSync(dir)) {
|
|
29
|
+
console.error(`modules/${name} already exists`);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const scope = detectScope(modulesDir) ?? "@app";
|
|
34
|
+
const locales = detectLocales(modulesDir);
|
|
35
|
+
const declared = readProviderDeclarations(rootDir, new BootReport());
|
|
36
|
+
const models = options.models ?? declared.models !== undefined;
|
|
37
|
+
const queues = options.queues ?? declared.queues !== undefined;
|
|
38
|
+
const sockets = options.sockets ?? declared.sockets !== undefined;
|
|
39
|
+
for (const [relPath, content] of renderModule(name, scope, locales, models, queues, sockets)) {
|
|
40
|
+
const filePath = join(dir, relPath);
|
|
41
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
42
|
+
writeFileSync(filePath, content);
|
|
43
|
+
console.log(` create modules/${name}/${relPath}`);
|
|
44
|
+
}
|
|
45
|
+
console.log([
|
|
46
|
+
"",
|
|
47
|
+
`scaffolded modules/${name} (${scope}/${name})`,
|
|
48
|
+
"",
|
|
49
|
+
"next:",
|
|
50
|
+
" yarn install # the module is a workspace package",
|
|
51
|
+
" yarn sync # regenerate config types and .env.example",
|
|
52
|
+
"",
|
|
53
|
+
`Delete unused files; keep route files wrapped in defineRoutes for offline inspection.`,
|
|
54
|
+
...(models
|
|
55
|
+
? declared.models
|
|
56
|
+
? []
|
|
57
|
+
: ["", "--model was given but package.json declares no models provider: add",
|
|
58
|
+
' "tulipes": { "providers": { "models": "@tulipes/mongoose" } } and install it, or the boot will refuse this module.']
|
|
59
|
+
: ["", "No models/ or bootstrap/ files: package.json declares no models provider.",
|
|
60
|
+
"To add a database, declare \"tulipes\": { \"providers\": { \"models\": \"@tulipes/mongoose\" } }, install it,",
|
|
61
|
+
`then \`tulipes new module ${name} --model\` in a scratch app to copy the model template.`]),
|
|
62
|
+
...(queues
|
|
63
|
+
? declared.queues
|
|
64
|
+
? []
|
|
65
|
+
: ["", "--queues was given but package.json declares no queues provider: add",
|
|
66
|
+
' "tulipes": { "providers": { "queues": "@tulipes/bullmq" } } and install it, or the boot will refuse this module.']
|
|
67
|
+
: ["", "No queues/ file: package.json declares no queues provider.",
|
|
68
|
+
"To add background work, declare \"tulipes\": { \"providers\": { \"queues\": \"@tulipes/bullmq\" } } and install it."]),
|
|
69
|
+
...(sockets
|
|
70
|
+
? declared.sockets
|
|
71
|
+
? []
|
|
72
|
+
: ["", "--sockets was given but package.json declares no sockets provider: add",
|
|
73
|
+
' "tulipes": { "providers": { "sockets": "@tulipes/socket.io" } } and install it, or the boot will refuse this module.']
|
|
74
|
+
: ["", "No sockets/ file: package.json declares no sockets provider.",
|
|
75
|
+
"To add realtime namespaces, declare \"tulipes\": { \"providers\": { \"sockets\": \"@tulipes/socket.io\" } } and install it."]),
|
|
76
|
+
].join("\n"));
|
|
77
|
+
}
|
|
78
|
+
const json = (value) => JSON.stringify(value, null, 2) + "\n";
|
|
79
|
+
function renderModule(name, scope, locales, models, queues, sockets) {
|
|
80
|
+
const fn = camel(name);
|
|
81
|
+
const Model = pascal(name);
|
|
82
|
+
const files = [
|
|
83
|
+
// One file per locale the app already ships. The module IS the i18n
|
|
84
|
+
// namespace, so `req.t("title")` in this module's endpoints resolves
|
|
85
|
+
// here and nowhere else. Only the default locale has to be complete —
|
|
86
|
+
// gaps in the others fall back to it, and are listed in
|
|
87
|
+
// `<locale>.missing.json` on the next boot.
|
|
88
|
+
...locales.map((locale) => [
|
|
89
|
+
`i18n/${locale}.json`,
|
|
90
|
+
json({ title: `${name} module` }),
|
|
91
|
+
]),
|
|
92
|
+
["package.json", json({
|
|
93
|
+
name: `${scope}/${name}`,
|
|
94
|
+
version: "0.0.0",
|
|
95
|
+
private: true,
|
|
96
|
+
type: "module",
|
|
97
|
+
// dependsOn "core" because the helpers below import from it. The npm
|
|
98
|
+
// dependency makes it resolve; dependsOn makes the ordering explicit.
|
|
99
|
+
tulipes: { offline: true, tier: "app", priority: 100, dependsOn: ["core"] },
|
|
100
|
+
dependencies: {
|
|
101
|
+
"@tulipes/core": `^${coreVersion()}`,
|
|
102
|
+
[`${scope}/core`]: "workspace:*",
|
|
103
|
+
express: "^5",
|
|
104
|
+
...(models && { "@tulipes/mongoose": "^0.1.0-rc.3", mongoose: "^8" }),
|
|
105
|
+
...(queues && { "@tulipes/bullmq": "^0.1.0-rc.2" }),
|
|
106
|
+
...(sockets && { "@tulipes/socket.io": "^0.1.0-rc.2" }),
|
|
107
|
+
},
|
|
108
|
+
})],
|
|
109
|
+
// Empty on purpose: add an entry the moment this module needs a value
|
|
110
|
+
// that differs per deployment, then run `yarn sync`.
|
|
111
|
+
["meta.variables.json", json({ variables: [] })],
|
|
112
|
+
["module.config.ts", [
|
|
113
|
+
`import type { ConfigCtx, Ctx } from "@tulipes/core/boot";`,
|
|
114
|
+
``,
|
|
115
|
+
`/**`,
|
|
116
|
+
` * Runs in boot phase 6. Whatever this returns is stored at`,
|
|
117
|
+
` * \`config.${name}\` and typed by \`yarn sync\`, so other modules read it`,
|
|
118
|
+
` * without importing anything from here.`,
|
|
119
|
+
` */`,
|
|
120
|
+
`export default function ${fn}Config(_ctx: ConfigCtx) {`,
|
|
121
|
+
` return {};`,
|
|
122
|
+
`}`,
|
|
123
|
+
``,
|
|
124
|
+
`/** After the process is fully up. Delete if unused. */`,
|
|
125
|
+
`export async function onReady(_ctx: Ctx): Promise<void> {}`,
|
|
126
|
+
``,
|
|
127
|
+
`/** Graceful shutdown, in REVERSE load order. Delete if unused. */`,
|
|
128
|
+
`export async function onShutdown(_ctx: Ctx): Promise<void> {}`,
|
|
129
|
+
``,
|
|
130
|
+
].join("\n")],
|
|
131
|
+
["module.acl.ts", [
|
|
132
|
+
`import type { AclBuilder } from "@tulipes/core/acl";`,
|
|
133
|
+
``,
|
|
134
|
+
`/**`,
|
|
135
|
+
` * Roles are global and defined once by the sys core module; a feature`,
|
|
136
|
+
` * module only attaches grants, on resources namespaced by its own`,
|
|
137
|
+
` * name ("${name}:read", "${name}:write").`,
|
|
138
|
+
` *`,
|
|
139
|
+
` * The grant below is commented out because granting to a role this`,
|
|
140
|
+
` * app does not define is a boot crash — uncomment it once you have`,
|
|
141
|
+
` * checked which roles exist in modules/core/module.acl.ts.`,
|
|
142
|
+
` */`,
|
|
143
|
+
`export default function ${fn}Acl(_acl: AclBuilder): void {`,
|
|
144
|
+
` // _acl.allow("user", "${name}:read"); // the id declared in routes/`,
|
|
145
|
+
`}`,
|
|
146
|
+
``,
|
|
147
|
+
].join("\n")],
|
|
148
|
+
[`helpers/${name}.helpers.ts`, [
|
|
149
|
+
`/**`,
|
|
150
|
+
` * Pure logic: no Express, no database, no framework — trivial to unit`,
|
|
151
|
+
` * test. The framework never scans this folder; import from it`,
|
|
152
|
+
` * directly. Anything a second module needs belongs in the core`,
|
|
153
|
+
` * module instead, imported as ${scope}/core.`,
|
|
154
|
+
` */`,
|
|
155
|
+
`export function describe${Model}(): string {`,
|
|
156
|
+
` return "${name}";`,
|
|
157
|
+
`}`,
|
|
158
|
+
``,
|
|
159
|
+
].join("\n")],
|
|
160
|
+
[`controllers/${name}.controllers.ts`, [
|
|
161
|
+
`import type { Request, RequestHandler, Response } from "express";`,
|
|
162
|
+
`import type { Ctx } from "@tulipes/core/boot";`,
|
|
163
|
+
``,
|
|
164
|
+
`import { describe${Model} } from "../helpers/${name}.helpers.js";`,
|
|
165
|
+
``,
|
|
166
|
+
`/**`,
|
|
167
|
+
` * One class per controller, holding the context. Each method returns a`,
|
|
168
|
+
` * handler rather than being one, so the routes file stays a table of`,
|
|
169
|
+
` * contents and private helpers sit beside the handlers that use them.`,
|
|
170
|
+
` *`,
|
|
171
|
+
` * Taking ctx in the constructor is what makes it testable: construct`,
|
|
172
|
+
` * one with a stub context and call the method.`,
|
|
173
|
+
` */`,
|
|
174
|
+
`export class ${Model}Controller {`,
|
|
175
|
+
` constructor(private readonly ctx: Ctx) {}`,
|
|
176
|
+
``,
|
|
177
|
+
` index(): RequestHandler {`,
|
|
178
|
+
` return (req: Request, res: Response): void => {`,
|
|
179
|
+
` // res.respond wraps this in the app's envelope; req.t is already`,
|
|
180
|
+
` // bound to this module's i18n namespace. rai() binds both, which`,
|
|
181
|
+
` // is one more thing a declared route buys you.`,
|
|
182
|
+
` res.respond({`,
|
|
183
|
+
` data: {`,
|
|
184
|
+
` module: describe${Model}(),`,
|
|
185
|
+
` title: req.t("title"),`,
|
|
186
|
+
` app: this.ctx.config.app?.name,`,
|
|
187
|
+
` },`,
|
|
188
|
+
` });`,
|
|
189
|
+
` };`,
|
|
190
|
+
` }`,
|
|
191
|
+
`}`,
|
|
192
|
+
``,
|
|
193
|
+
].join("\n")],
|
|
194
|
+
[`routes/${name}.routes.ts`, [
|
|
195
|
+
`import { Router } from "express";`,
|
|
196
|
+
`import { defineRoutes } from "@tulipes/core/http";`,
|
|
197
|
+
``,
|
|
198
|
+
`export default defineRoutes(({ rai, runtime }) => {`,
|
|
199
|
+
` const router = Router();`,
|
|
200
|
+
` const controller = runtime(async ctx => {`,
|
|
201
|
+
` const { ${Model}Controller } = await import("../controllers/${name}.controllers.js");`,
|
|
202
|
+
` return new ${Model}Controller(ctx);`,
|
|
203
|
+
` });`,
|
|
204
|
+
` router.get("/${name}", rai({ id: "${name}:read", name: "List ${name}" }),`,
|
|
205
|
+
` controller(value => value.index()));`,
|
|
206
|
+
` return router;`,
|
|
207
|
+
`});`,
|
|
208
|
+
``,
|
|
209
|
+
].join("\n")],
|
|
210
|
+
[`models/${name}.model.ts`, [
|
|
211
|
+
`import { Schema } from "mongoose";`,
|
|
212
|
+
`import type { ModelDef } from "@tulipes/mongoose";`,
|
|
213
|
+
``,
|
|
214
|
+
`import { baseSchemaOptions } from "${scope}/core";`,
|
|
215
|
+
``,
|
|
216
|
+
`/**`,
|
|
217
|
+
` * A declaration, never a registration: the framework compiles this on`,
|
|
218
|
+
` * its own connection and puts it in the model store. Read it anywhere`,
|
|
219
|
+
` * with models!.get("${Model}"). Never call mongoose.model() yourself.`,
|
|
220
|
+
` */`,
|
|
221
|
+
`const ${fn}Schema = new Schema(`,
|
|
222
|
+
` {`,
|
|
223
|
+
` name: { type: String, required: true, trim: true },`,
|
|
224
|
+
` },`,
|
|
225
|
+
` baseSchemaOptions,`,
|
|
226
|
+
`);`,
|
|
227
|
+
``,
|
|
228
|
+
`export default { name: "${Model}", schema: ${fn}Schema } satisfies ModelDef;`,
|
|
229
|
+
``,
|
|
230
|
+
].join("\n")],
|
|
231
|
+
[`bootstrap/${name}.bootstrap.ts`, [
|
|
232
|
+
`import type { DatabaseCtx } from "@tulipes/core/boot";`,
|
|
233
|
+
``,
|
|
234
|
+
`/**`,
|
|
235
|
+
` * Runs after models are registered and before the process serves —`,
|
|
236
|
+
` * indexes, seed rows, defaults.`,
|
|
237
|
+
` *`,
|
|
238
|
+
` * MUST be idempotent: it runs on every boot of every process. Upsert,`,
|
|
239
|
+
` * never blind-insert.`,
|
|
240
|
+
` */`,
|
|
241
|
+
`export default async function ${fn}Bootstrap(_ctx: DatabaseCtx): Promise<void> {}`,
|
|
242
|
+
``,
|
|
243
|
+
].join("\n")],
|
|
244
|
+
[`queues/${name}.queues.ts`, [
|
|
245
|
+
`import type { Ctx } from "@tulipes/core/boot";`,
|
|
246
|
+
`import type { QueueRegistry } from "@tulipes/bullmq";`,
|
|
247
|
+
``,
|
|
248
|
+
`/**`,
|
|
249
|
+
` * ONE file describes both sides. The backend process registers the`,
|
|
250
|
+
` * queue so routes can produce into it; \`yarn worker\` turns the`,
|
|
251
|
+
` * processor into a live consumer.`,
|
|
252
|
+
` *`,
|
|
253
|
+
` * Produce from anywhere with ctx.queues:`,
|
|
254
|
+
` * await ctx.queues!.add("${name}.example", "job", { id });`,
|
|
255
|
+
` *`,
|
|
256
|
+
` * Queue names use "." — BullMQ reserves ":" as its redis separator.`,
|
|
257
|
+
` */`,
|
|
258
|
+
`export default function ${fn}Queues(_ctx: Ctx, queues: QueueRegistry): void {`,
|
|
259
|
+
` queues.define("${name}.example");`,
|
|
260
|
+
``,
|
|
261
|
+
` queues.process("${name}.example", async (job) => {`,
|
|
262
|
+
` // Jobs retry, so keep this safe to run twice. Pass ids in`,
|
|
263
|
+
` // job.data and re-read state here.`,
|
|
264
|
+
` return { handled: job.name };`,
|
|
265
|
+
` });`,
|
|
266
|
+
`}`,
|
|
267
|
+
``,
|
|
268
|
+
].join("\n")],
|
|
269
|
+
[`sockets/${name}.sockets.ts`, [
|
|
270
|
+
`import type { Ctx } from "@tulipes/core/boot";`,
|
|
271
|
+
`import type { SocketRegistry } from "@tulipes/socket.io";`,
|
|
272
|
+
``,
|
|
273
|
+
`/**`,
|
|
274
|
+
` * Backend mode only. A namespace is claimed by exactly one module;`,
|
|
275
|
+
` * emit from anywhere with ctx.sockets!.of("/${name}").`,
|
|
276
|
+
` */`,
|
|
277
|
+
`export default function ${fn}Sockets(_ctx: Ctx, sockets: SocketRegistry): void {`,
|
|
278
|
+
` sockets.namespace("/${name}", (nsp) => {`,
|
|
279
|
+
` nsp.on("connection", (socket) => {`,
|
|
280
|
+
` socket.emit("ready", { module: "${name}" });`,
|
|
281
|
+
` });`,
|
|
282
|
+
` });`,
|
|
283
|
+
`}`,
|
|
284
|
+
``,
|
|
285
|
+
].join("\n")],
|
|
286
|
+
];
|
|
287
|
+
return files.filter(([path]) => (models || (!path.startsWith("models/") && !path.startsWith("bootstrap/"))) &&
|
|
288
|
+
(queues || !path.startsWith("queues/")) &&
|
|
289
|
+
(sockets || !path.startsWith("sockets/")));
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* The locales a sibling module already ships — the same trick as the
|
|
293
|
+
* package scope: the app's own conventions decide, with nothing to
|
|
294
|
+
* configure. `en` alone when nothing is translated yet.
|
|
295
|
+
*/
|
|
296
|
+
function detectLocales(modulesDir) {
|
|
297
|
+
const found = new Set();
|
|
298
|
+
let entries;
|
|
299
|
+
try {
|
|
300
|
+
entries = readdirSync(modulesDir);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return ["en"];
|
|
304
|
+
}
|
|
305
|
+
for (const entry of entries) {
|
|
306
|
+
let files;
|
|
307
|
+
try {
|
|
308
|
+
files = readdirSync(join(modulesDir, entry, "i18n"));
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
continue; // that module has no translations
|
|
312
|
+
}
|
|
313
|
+
for (const file of files) {
|
|
314
|
+
if (file.endsWith(".json") && !file.endsWith(".missing.json")) {
|
|
315
|
+
found.add(file.replace(/\.json$/, ""));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return found.size > 0 ? [...found].sort() : ["en"];
|
|
320
|
+
}
|
|
321
|
+
function detectScope(modulesDir) {
|
|
322
|
+
let entries;
|
|
323
|
+
try {
|
|
324
|
+
entries = readdirSync(modulesDir);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
for (const entry of entries) {
|
|
330
|
+
try {
|
|
331
|
+
const pkg = JSON.parse(readFileSync(join(modulesDir, entry, "package.json"), "utf8"));
|
|
332
|
+
if (pkg.name?.startsWith("@"))
|
|
333
|
+
return pkg.name.split("/")[0];
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
continue; // not a module folder
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
function camel(kebab) {
|
|
342
|
+
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
343
|
+
}
|
|
344
|
+
function pascal(kebab) {
|
|
345
|
+
const c = camel(kebab);
|
|
346
|
+
return c.charAt(0).toUpperCase() + c.slice(1);
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=new-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new-module.js","sourceRoot":"","sources":["../src/new-module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This CLI's own package root (templates live there). Resolved relative to
|
|
3
|
+
* this file — src/ and dist/ both sit one level below it.
|
|
4
|
+
*/
|
|
5
|
+
export declare function packageRoot(): string;
|
|
6
|
+
/** Version of this CLI. */
|
|
7
|
+
export declare function cliVersion(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Version of the @tulipes/core this CLI bundles and scaffolds for. The
|
|
10
|
+
* exports map hides core's package.json, so walk up from its entrypoint.
|
|
11
|
+
*/
|
|
12
|
+
export declare function coreVersion(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Version of the core installed in an app, which is a different install
|
|
15
|
+
* from the global CLI and can therefore be a different version. Undefined
|
|
16
|
+
* when the directory is not an app, or its dependencies aren't installed.
|
|
17
|
+
*/
|
|
18
|
+
export declare function appCoreVersion(rootDir: string): string | undefined;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/**
|
|
6
|
+
* This CLI's own package root (templates live there). Resolved relative to
|
|
7
|
+
* this file — src/ and dist/ both sit one level below it.
|
|
8
|
+
*/
|
|
9
|
+
export function packageRoot() {
|
|
10
|
+
return join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
}
|
|
12
|
+
/** Version of this CLI. */
|
|
13
|
+
export function cliVersion() {
|
|
14
|
+
return versionAt(packageRoot()) ?? "unknown";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Version of the @tulipes/core this CLI bundles and scaffolds for. The
|
|
18
|
+
* exports map hides core's package.json, so walk up from its entrypoint.
|
|
19
|
+
*/
|
|
20
|
+
export function coreVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const entry = createRequire(join(packageRoot(), "package.json")).resolve("@tulipes/core");
|
|
23
|
+
let dir = dirname(entry);
|
|
24
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
25
|
+
const version = versionAt(dir);
|
|
26
|
+
if (version && readFileSync(join(dir, "package.json"), "utf8").includes('"name": "@tulipes/core"'))
|
|
27
|
+
return version;
|
|
28
|
+
dir = dirname(dir);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// fall through
|
|
33
|
+
}
|
|
34
|
+
return "unknown";
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Version of the core installed in an app, which is a different install
|
|
38
|
+
* from the global CLI and can therefore be a different version. Undefined
|
|
39
|
+
* when the directory is not an app, or its dependencies aren't installed.
|
|
40
|
+
*/
|
|
41
|
+
export function appCoreVersion(rootDir) {
|
|
42
|
+
return versionAt(join(rootDir, "node_modules", "@tulipes", "core"));
|
|
43
|
+
}
|
|
44
|
+
function versionAt(packageDir) {
|
|
45
|
+
try {
|
|
46
|
+
const pkg = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
|
47
|
+
return typeof pkg.version === "string" ? pkg.version : undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=package-info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-info.js","sourceRoot":"","sources":["../src/package-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,UAAU;IACxB,OAAO,SAAS,CAAC,WAAW,EAAE,CAAC,IAAI,SAAS,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC1F,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QACzB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;gBAAE,OAAO,OAAO,CAAC;YACnH,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,SAAS,SAAS,CAAC,UAAkB;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE5E,CAAC;QACF,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tulipes routes` — every endpoint the app declares, with the roles that
|
|
3
|
+
* can reach it.
|
|
4
|
+
*
|
|
5
|
+
* Reads shared declarations offline, or uses backend inspection for legacy
|
|
6
|
+
* applications. Both paths use the same route scan and ACL metadata.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runRoutes(rootDir: string, offline?: boolean): Promise<void>;
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { inspectApp } from "./inspection.js";
|
|
2
|
+
/**
|
|
3
|
+
* `tulipes routes` — every endpoint the app declares, with the roles that
|
|
4
|
+
* can reach it.
|
|
5
|
+
*
|
|
6
|
+
* Reads shared declarations offline, or uses backend inspection for legacy
|
|
7
|
+
* applications. Both paths use the same route scan and ACL metadata.
|
|
8
|
+
*/
|
|
9
|
+
export async function runRoutes(rootDir, offline = false) {
|
|
10
|
+
const handle = await inspectApp(rootDir, offline);
|
|
11
|
+
try {
|
|
12
|
+
const records = [...handle.ctx.routes.all()].sort((a, b) => a.info.id.localeCompare(b.info.id));
|
|
13
|
+
if (records.length === 0) {
|
|
14
|
+
console.log("no routes declared");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const grants = handle.ctx.acl.describe();
|
|
18
|
+
const rolesFor = (id) => {
|
|
19
|
+
if (handle.ctx.routes.checker)
|
|
20
|
+
return "dynamic (custom access checker)";
|
|
21
|
+
const roles = grants
|
|
22
|
+
.filter(({ resource }) => resource === id || resource === "*" || resource === `${id.split(":")[0]}:*`)
|
|
23
|
+
.map(({ role }) => role);
|
|
24
|
+
return [...new Set(roles)].sort().join(", ") || "— nobody";
|
|
25
|
+
};
|
|
26
|
+
const width = (pick) => Math.max(...records.map((r) => pick(r).length));
|
|
27
|
+
const w = {
|
|
28
|
+
method: width((r) => r.method),
|
|
29
|
+
id: width((r) => r.info.id),
|
|
30
|
+
path: width((r) => r.path),
|
|
31
|
+
module: width((r) => r.module),
|
|
32
|
+
};
|
|
33
|
+
console.log(`${records.length} endpoint(s)\n`);
|
|
34
|
+
for (const record of records) {
|
|
35
|
+
console.log([
|
|
36
|
+
record.method.padEnd(w.method),
|
|
37
|
+
record.info.id.padEnd(w.id),
|
|
38
|
+
record.path.padEnd(w.path),
|
|
39
|
+
record.module.padEnd(w.module),
|
|
40
|
+
rolesFor(record.info.id),
|
|
41
|
+
].join(" "));
|
|
42
|
+
if (record.info.name || record.info.description) {
|
|
43
|
+
const label = [record.info.name, record.info.description]
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.join(" — ");
|
|
46
|
+
console.log(" ".repeat(w.method + 2) + label);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await handle.shutdown();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe,EAAE,OAAO,GAAG,KAAK;IAC9D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACzD,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACnC,CAAC;QAEF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,GAAI,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAU,EAAE;YACtC,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO,iCAAiC,CAAC;YACxE,MAAM,KAAK,GAAG,MAAM;iBACjB,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;iBACrG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC;QAC7D,CAAC,CAAC;QAEF,MAAM,KAAK,GAAG,CAAC,IAA6C,EAAU,EAAE,CACtE,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;QAElD,MAAM,CAAC,GAAG;YACR,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;YAC9B,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;SAC/B,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,gBAAgB,CAAC,CAAC;QAC/C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CACT;gBACE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC1B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;gBAC9B,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;aACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;YACF,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChD,MAAM,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;qBACtD,MAAM,CAAC,OAAO,CAAC;qBACf,IAAI,CAAC,KAAK,CAAC,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC"}
|
package/dist/spec.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `tulipes spec` — an OpenAPI document and/or a Postman collection, built
|
|
3
|
+
* from the app's declared routes.
|
|
4
|
+
*
|
|
5
|
+
* Shares the routes command's inspection path: offline declarations when
|
|
6
|
+
* requested, or backend inspection without listening for legacy factories.
|
|
7
|
+
*
|
|
8
|
+
* The generator lives in `@tulipes/spec`, imported lazily: core carries no
|
|
9
|
+
* dependency on it, and an app that never generates a spec never installs
|
|
10
|
+
* it.
|
|
11
|
+
*/
|
|
12
|
+
export interface SpecOptions {
|
|
13
|
+
offline?: boolean;
|
|
14
|
+
openapi?: string;
|
|
15
|
+
postman?: string;
|
|
16
|
+
/** Base URL the documents advertise. */
|
|
17
|
+
url?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function runSpec(rootDir: string, options: SpecOptions): Promise<void>;
|