@grupodiariodaregiao/bunstone 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +476 -24
- package/dist/index.d.ts +1 -0
- package/dist/index.js +553 -80
- package/dist/lib/errors/index.d.ts +204 -10
- package/dist/lib/utils/error-formatter.d.ts +37 -2
- package/dist/lib/utils/fuzzy-match.d.ts +15 -0
- package/dist/lib/utils/known-exports.d.ts +15 -0
- package/lib/adapters/cache-adapter.ts +3 -0
- package/lib/adapters/upload-adapter.ts +2 -1
- package/lib/app-startup.ts +6 -7
- package/lib/bullmq/queue.service.ts +2 -4
- package/lib/cqrs/command-bus.ts +1 -4
- package/lib/cqrs/query-bus.ts +1 -4
- package/lib/database/sql-module.ts +1 -4
- package/lib/email/email-module.ts +2 -3
- package/lib/errors/index.ts +727 -18
- package/lib/guard.ts +1 -0
- package/lib/http-params.ts +5 -2
- package/lib/jwt/jwt.service.ts +1 -0
- package/lib/jwt.ts +1 -0
- package/lib/module.ts +2 -3
- package/lib/rabbitmq/rabbitmq-connection.ts +2 -3
- package/lib/ratelimit/storage/redis.storage.ts +3 -2
- package/lib/schedule/cron/cron.ts +2 -1
- package/lib/schedule/timeout/timeout.ts +2 -1
- package/lib/testing/testing-module.ts +2 -1
- package/lib/utils/dependency-injection.ts +2 -10
- package/lib/utils/error-formatter.ts +148 -32
- package/lib/utils/fuzzy-match.ts +62 -0
- package/lib/utils/known-exports.ts +162 -0
- package/package.json +1 -1
package/bin/cli.ts
CHANGED
|
@@ -8,22 +8,464 @@ import {
|
|
|
8
8
|
} from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
+
// ── Tiny ANSI helpers (no external dep) ──────────────────────────────────────
|
|
12
|
+
const R = "\x1b[0m";
|
|
13
|
+
const bold = (s: string) => `\x1b[1m${s}${R}`;
|
|
14
|
+
const red = (s: string) => `\x1b[31m${s}${R}`;
|
|
15
|
+
const yellow = (s: string) => `\x1b[33m${s}${R}`;
|
|
16
|
+
const green = (s: string) => `\x1b[32m${s}${R}`;
|
|
17
|
+
const cyan = (s: string) => `\x1b[36m${s}${R}`;
|
|
18
|
+
const gray = (s: string) => `\x1b[90m${s}${R}`;
|
|
19
|
+
const BORDER = "━".repeat(64);
|
|
20
|
+
const THIN = "─".repeat(64);
|
|
21
|
+
|
|
22
|
+
// ── Arg parsing ───────────────────────────────────────────────────────────────
|
|
11
23
|
const args = Bun.argv.slice(2);
|
|
12
|
-
|
|
13
|
-
let projectName = args[1];
|
|
24
|
+
const command = args[0];
|
|
14
25
|
|
|
15
|
-
//
|
|
16
|
-
if (command
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
// ── Command dispatch ──────────────────────────────────────────────────────────
|
|
27
|
+
if (command === "run") {
|
|
28
|
+
await runCommand(args.slice(1));
|
|
29
|
+
} else if (command === "exports") {
|
|
30
|
+
await exportsCommand();
|
|
31
|
+
} else if (command === "new" || (command && !args[1])) {
|
|
32
|
+
await scaffold(command === "new" ? args[1] : command);
|
|
33
|
+
} else {
|
|
34
|
+
printHelp();
|
|
35
|
+
process.exit(1);
|
|
19
36
|
}
|
|
20
37
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
// bunstone run [bun-flags...] <entrypoint>
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Regexp that matches Bun's raw import error line.
|
|
44
|
+
* e.g. SyntaxError: Export named 'RabbitMessage' not found in module '…/bunstone/dist/index.js'.
|
|
45
|
+
*/
|
|
46
|
+
const BUN_EXPORT_RE = /Export named '(.+?)' not found in module '(.+?)'/;
|
|
47
|
+
|
|
48
|
+
async function runCommand(runArgs: string[]) {
|
|
49
|
+
if (runArgs.length === 0) {
|
|
50
|
+
console.error(red("✖ Usage: bunstone run [bun-flags...] <entrypoint.ts>"));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Separate bun flags (start with -) from the entrypoint
|
|
55
|
+
const bunFlags: string[] = [];
|
|
56
|
+
let entrypoint = "";
|
|
57
|
+
for (const arg of runArgs) {
|
|
58
|
+
if (arg.startsWith("-") && !entrypoint) bunFlags.push(arg);
|
|
59
|
+
else entrypoint = arg;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!entrypoint) {
|
|
63
|
+
console.error(red("✖ No entrypoint file specified."));
|
|
64
|
+
console.error(gray(" Usage: bunstone run [--watch] src/main.ts"));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const cmd = ["bun", ...bunFlags, entrypoint];
|
|
69
|
+
|
|
70
|
+
const proc = Bun.spawn(cmd, {
|
|
71
|
+
stdin: "inherit",
|
|
72
|
+
stdout: "inherit",
|
|
73
|
+
stderr: "pipe",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Accumulate stderr so we can inspect it for the import error pattern
|
|
77
|
+
const stderrChunks: Uint8Array[] = [];
|
|
78
|
+
const stderrReader = proc.stderr.getReader();
|
|
79
|
+
|
|
80
|
+
(async () => {
|
|
81
|
+
const decoder = new TextDecoder();
|
|
82
|
+
while (true) {
|
|
83
|
+
const { done, value } = await stderrReader.read();
|
|
84
|
+
if (done) break;
|
|
85
|
+
stderrChunks.push(value);
|
|
86
|
+
|
|
87
|
+
// Write to real stderr immediately so the developer still sees everything
|
|
88
|
+
process.stderr.write(decoder.decode(value));
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
|
|
92
|
+
const exitCode = await proc.exited;
|
|
93
|
+
|
|
94
|
+
if (exitCode !== 0) {
|
|
95
|
+
const decoder = new TextDecoder();
|
|
96
|
+
const fullStderr = stderrChunks.map((c) => decoder.decode(c)).join("");
|
|
97
|
+
|
|
98
|
+
const match = BUN_EXPORT_RE.exec(fullStderr);
|
|
99
|
+
if (match) {
|
|
100
|
+
const [, name, modulePath] = match;
|
|
101
|
+
printImportError(name, modulePath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
process.exit(exitCode);
|
|
24
106
|
}
|
|
25
107
|
|
|
26
|
-
|
|
108
|
+
function printImportError(name: string, modulePath: string) {
|
|
109
|
+
// Lazy-load the utils so the CLI does not depend on the built dist
|
|
110
|
+
const pkg = modulePath.includes("node_modules/")
|
|
111
|
+
? modulePath.replace(/.*node_modules\//, "").replace(/\/dist.*/, "")
|
|
112
|
+
: modulePath;
|
|
113
|
+
|
|
114
|
+
// These are inlined here (not imported from dist) so the CLI works even
|
|
115
|
+
// before the user runs `bun build`.
|
|
116
|
+
const TYPE_ONLY: ReadonlySet<string> = new Set([
|
|
117
|
+
"RabbitMessage",
|
|
118
|
+
"RabbitPublishOptions",
|
|
119
|
+
"RabbitSubscribeOptions",
|
|
120
|
+
"DeadLetterMessage",
|
|
121
|
+
"DeadLetterDeathInfo",
|
|
122
|
+
"RequeueOptions",
|
|
123
|
+
"RabbitMQExchangeConfig",
|
|
124
|
+
"RabbitMQQueueBinding",
|
|
125
|
+
"RabbitMQQueueConfig",
|
|
126
|
+
"RabbitMQReconnectConfig",
|
|
127
|
+
"RabbitMQModuleOptions",
|
|
128
|
+
"HttpRequest",
|
|
129
|
+
"ModuleConfig",
|
|
130
|
+
"Options",
|
|
131
|
+
"GuardContract",
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
const ALL_VALUES = [
|
|
135
|
+
"CacheAdapter",
|
|
136
|
+
"FormDataAdapter",
|
|
137
|
+
"UploadAdapter",
|
|
138
|
+
"EmailModule",
|
|
139
|
+
"EmailService",
|
|
140
|
+
"EmailLayout",
|
|
141
|
+
"AppStartup",
|
|
142
|
+
"Layout",
|
|
143
|
+
"Controller",
|
|
144
|
+
"Get",
|
|
145
|
+
"Post",
|
|
146
|
+
"Put",
|
|
147
|
+
"Patch",
|
|
148
|
+
"Delete",
|
|
149
|
+
"Head",
|
|
150
|
+
"RateLimit",
|
|
151
|
+
"RateLimitGuard",
|
|
152
|
+
"RedisStorage",
|
|
153
|
+
"CommandBus",
|
|
154
|
+
"CqrsModule",
|
|
155
|
+
"CommandHandler",
|
|
156
|
+
"EventHandler",
|
|
157
|
+
"QueryHandler",
|
|
158
|
+
"Saga",
|
|
159
|
+
"EventBus",
|
|
160
|
+
"QueryBus",
|
|
161
|
+
"SqlModule",
|
|
162
|
+
"SqlService",
|
|
163
|
+
"BullMqModule",
|
|
164
|
+
"QueueService",
|
|
165
|
+
"Processor",
|
|
166
|
+
"Process",
|
|
167
|
+
"RabbitMQModule",
|
|
168
|
+
"RabbitMQService",
|
|
169
|
+
"RabbitMQDeadLetterService",
|
|
170
|
+
"RabbitMQConnection",
|
|
171
|
+
"RabbitConsumer",
|
|
172
|
+
"RabbitSubscribe",
|
|
173
|
+
"BunstoneError",
|
|
174
|
+
"DependencyResolutionError",
|
|
175
|
+
"ModuleInitializationError",
|
|
176
|
+
"ConfigurationError",
|
|
177
|
+
"CqrsError",
|
|
178
|
+
"DatabaseError",
|
|
179
|
+
"BullMQError",
|
|
180
|
+
"RabbitMQError",
|
|
181
|
+
"ScheduleError",
|
|
182
|
+
"TestingError",
|
|
183
|
+
"RateLimitError",
|
|
184
|
+
"UploadError",
|
|
185
|
+
"EmailError",
|
|
186
|
+
"HttpParamError",
|
|
187
|
+
"GuardError",
|
|
188
|
+
"AdapterError",
|
|
189
|
+
"ImportError",
|
|
190
|
+
"ErrorFormatter",
|
|
191
|
+
"Guard",
|
|
192
|
+
"UseGuards",
|
|
193
|
+
"HttpException",
|
|
194
|
+
"BadRequestException",
|
|
195
|
+
"UnauthorizedException",
|
|
196
|
+
"ForbiddenException",
|
|
197
|
+
"NotFoundException",
|
|
198
|
+
"ConflictException",
|
|
199
|
+
"UnprocessableEntityException",
|
|
200
|
+
"InternalServerErrorException",
|
|
201
|
+
"OkResponse",
|
|
202
|
+
"CreatedResponse",
|
|
203
|
+
"NoContentResponse",
|
|
204
|
+
"HttpMethod",
|
|
205
|
+
"Body",
|
|
206
|
+
"Param",
|
|
207
|
+
"Query",
|
|
208
|
+
"Headers",
|
|
209
|
+
"Req",
|
|
210
|
+
"Res",
|
|
211
|
+
"FormData",
|
|
212
|
+
"Injectable",
|
|
213
|
+
"Jwt",
|
|
214
|
+
"JwtModule",
|
|
215
|
+
"JwtService",
|
|
216
|
+
"UseJwt",
|
|
217
|
+
"Module",
|
|
218
|
+
"OnModuleInit",
|
|
219
|
+
"OnModuleDestroy",
|
|
220
|
+
"ApiTags",
|
|
221
|
+
"ApiOperation",
|
|
222
|
+
"ApiResponse",
|
|
223
|
+
"ApiBody",
|
|
224
|
+
"Render",
|
|
225
|
+
"Cron",
|
|
226
|
+
"Timeout",
|
|
227
|
+
"Test",
|
|
228
|
+
"TestingModuleBuilder",
|
|
229
|
+
"TestApp",
|
|
230
|
+
"Logger",
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
const isTypeOnly = TYPE_ONLY.has(name);
|
|
234
|
+
const suggestions = closestMatchesCli(
|
|
235
|
+
name,
|
|
236
|
+
isTypeOnly ? ALL_VALUES : [...ALL_VALUES, ...TYPE_ONLY],
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const code = isTypeOnly ? "BNS-IMP-001" : "BNS-IMP-002";
|
|
240
|
+
|
|
241
|
+
console.error(`\n${red(BORDER)}`);
|
|
242
|
+
console.error(red(bold(" 💥 Bunstone — Import Error")));
|
|
243
|
+
console.error(`${red(BORDER)}\n`);
|
|
244
|
+
|
|
245
|
+
console.error(
|
|
246
|
+
` ${yellow(bold("Code :"))} ${bold(code)} ${gray(`(ImportError)`)}`,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
if (isTypeOnly) {
|
|
250
|
+
console.error(
|
|
251
|
+
` ${yellow(bold("Message :"))} '${name}' is a ${bold("type-only")} export of '${pkg}' — it does not exist at runtime.`,
|
|
252
|
+
);
|
|
253
|
+
console.error(`\n ${green(bold("💡 How to fix:"))}`);
|
|
254
|
+
console.error(
|
|
255
|
+
green(
|
|
256
|
+
[
|
|
257
|
+
` Replace the import with 'import type':`,
|
|
258
|
+
``,
|
|
259
|
+
` ${red("✗")} import { ${name} } from '${pkg}'`,
|
|
260
|
+
` ${green("✓")} import type { ${name} } from '${pkg}'`,
|
|
261
|
+
].join("\n"),
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
if (suggestions.length > 0) {
|
|
265
|
+
console.error(
|
|
266
|
+
`\n ${cyan("If you were looking for a runtime value with a similar name:")}`,
|
|
267
|
+
);
|
|
268
|
+
for (const s of suggestions) {
|
|
269
|
+
console.error(` ${gray("→")} ${s}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
console.error(
|
|
274
|
+
` ${yellow(bold("Message :"))} '${name}' is not exported by '${pkg}'.`,
|
|
275
|
+
);
|
|
276
|
+
console.error(`\n ${green(bold("💡 How to fix:"))}`);
|
|
277
|
+
console.error(green(` Check the spelling of the imported name.`));
|
|
278
|
+
console.error(
|
|
279
|
+
green(
|
|
280
|
+
` Run ${bold("bunstone exports")} to see all available exports.`,
|
|
281
|
+
),
|
|
282
|
+
);
|
|
283
|
+
if (suggestions.length > 0) {
|
|
284
|
+
console.error(`\n ${cyan("Did you mean one of these?")}`);
|
|
285
|
+
for (const s of suggestions) {
|
|
286
|
+
console.error(` ${gray("→")} ${s}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
console.error(`\n${red(THIN)}`);
|
|
292
|
+
console.error(red(bold(" ✖ Fix the import above and restart.")));
|
|
293
|
+
console.error(`${red(BORDER)}\n`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Minimal inline Levenshtein for the CLI (avoids importing from dist). */
|
|
297
|
+
function closestMatchesCli(
|
|
298
|
+
name: string,
|
|
299
|
+
candidates: Iterable<string>,
|
|
300
|
+
limit = 5,
|
|
301
|
+
maxDist = 4,
|
|
302
|
+
): string[] {
|
|
303
|
+
function dist(a: string, b: string) {
|
|
304
|
+
const s1 = a.toLowerCase();
|
|
305
|
+
const s2 = b.toLowerCase();
|
|
306
|
+
const [m, n] = [s1.length, s2.length];
|
|
307
|
+
const dp = Array.from({ length: m + 1 }, (_, i) =>
|
|
308
|
+
Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)),
|
|
309
|
+
);
|
|
310
|
+
for (let i = 1; i <= m; i++)
|
|
311
|
+
for (let j = 1; j <= n; j++)
|
|
312
|
+
dp[i][j] =
|
|
313
|
+
s1[i - 1] === s2[j - 1]
|
|
314
|
+
? dp[i - 1][j - 1]
|
|
315
|
+
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
316
|
+
return dp[m][n];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const results: { name: string; d: number }[] = [];
|
|
320
|
+
for (const c of candidates) {
|
|
321
|
+
const d = dist(name, c);
|
|
322
|
+
if (d <= maxDist) results.push({ name: c, d });
|
|
323
|
+
}
|
|
324
|
+
return results
|
|
325
|
+
.sort((a, b) => a.d - b.d)
|
|
326
|
+
.slice(0, limit)
|
|
327
|
+
.map((r) => r.name);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
331
|
+
// bunstone exports
|
|
332
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
async function exportsCommand() {
|
|
335
|
+
const VALUE_EXPORTS = [
|
|
336
|
+
"CacheAdapter",
|
|
337
|
+
"FormDataAdapter",
|
|
338
|
+
"UploadAdapter",
|
|
339
|
+
"EmailModule",
|
|
340
|
+
"EmailService",
|
|
341
|
+
"EmailLayout",
|
|
342
|
+
"AppStartup",
|
|
343
|
+
"Layout",
|
|
344
|
+
"Controller",
|
|
345
|
+
"Get",
|
|
346
|
+
"Post",
|
|
347
|
+
"Put",
|
|
348
|
+
"Patch",
|
|
349
|
+
"Delete",
|
|
350
|
+
"Head",
|
|
351
|
+
"RateLimit",
|
|
352
|
+
"RateLimitGuard",
|
|
353
|
+
"RedisStorage",
|
|
354
|
+
"CommandBus",
|
|
355
|
+
"CqrsModule",
|
|
356
|
+
"CommandHandler",
|
|
357
|
+
"EventHandler",
|
|
358
|
+
"QueryHandler",
|
|
359
|
+
"Saga",
|
|
360
|
+
"EventBus",
|
|
361
|
+
"QueryBus",
|
|
362
|
+
"SqlModule",
|
|
363
|
+
"SqlService",
|
|
364
|
+
"BullMqModule",
|
|
365
|
+
"QueueService",
|
|
366
|
+
"Processor",
|
|
367
|
+
"Process",
|
|
368
|
+
"RabbitMQModule",
|
|
369
|
+
"RabbitMQService",
|
|
370
|
+
"RabbitMQDeadLetterService",
|
|
371
|
+
"RabbitMQConnection",
|
|
372
|
+
"RabbitConsumer",
|
|
373
|
+
"RabbitSubscribe",
|
|
374
|
+
"BunstoneError",
|
|
375
|
+
"DependencyResolutionError",
|
|
376
|
+
"ModuleInitializationError",
|
|
377
|
+
"ConfigurationError",
|
|
378
|
+
"CqrsError",
|
|
379
|
+
"DatabaseError",
|
|
380
|
+
"BullMQError",
|
|
381
|
+
"RabbitMQError",
|
|
382
|
+
"ScheduleError",
|
|
383
|
+
"TestingError",
|
|
384
|
+
"RateLimitError",
|
|
385
|
+
"UploadError",
|
|
386
|
+
"EmailError",
|
|
387
|
+
"HttpParamError",
|
|
388
|
+
"GuardError",
|
|
389
|
+
"AdapterError",
|
|
390
|
+
"ImportError",
|
|
391
|
+
"ErrorFormatter",
|
|
392
|
+
"Guard",
|
|
393
|
+
"UseGuards",
|
|
394
|
+
"HttpException",
|
|
395
|
+
"BadRequestException",
|
|
396
|
+
"UnauthorizedException",
|
|
397
|
+
"ForbiddenException",
|
|
398
|
+
"NotFoundException",
|
|
399
|
+
"ConflictException",
|
|
400
|
+
"UnprocessableEntityException",
|
|
401
|
+
"InternalServerErrorException",
|
|
402
|
+
"OkResponse",
|
|
403
|
+
"CreatedResponse",
|
|
404
|
+
"NoContentResponse",
|
|
405
|
+
"HttpMethod",
|
|
406
|
+
"Body",
|
|
407
|
+
"Param",
|
|
408
|
+
"Query",
|
|
409
|
+
"Headers",
|
|
410
|
+
"Req",
|
|
411
|
+
"Res",
|
|
412
|
+
"FormData",
|
|
413
|
+
"Injectable",
|
|
414
|
+
"Jwt",
|
|
415
|
+
"JwtModule",
|
|
416
|
+
"JwtService",
|
|
417
|
+
"UseJwt",
|
|
418
|
+
"Module",
|
|
419
|
+
"OnModuleInit",
|
|
420
|
+
"OnModuleDestroy",
|
|
421
|
+
"ApiTags",
|
|
422
|
+
"ApiOperation",
|
|
423
|
+
"ApiResponse",
|
|
424
|
+
"ApiBody",
|
|
425
|
+
"Render",
|
|
426
|
+
"Cron",
|
|
427
|
+
"Timeout",
|
|
428
|
+
"Test",
|
|
429
|
+
"TestingModuleBuilder",
|
|
430
|
+
"TestApp",
|
|
431
|
+
"Logger",
|
|
432
|
+
];
|
|
433
|
+
const TYPE_ONLY_EXPORTS = [
|
|
434
|
+
"RabbitMessage",
|
|
435
|
+
"RabbitPublishOptions",
|
|
436
|
+
"RabbitSubscribeOptions",
|
|
437
|
+
"DeadLetterMessage",
|
|
438
|
+
"DeadLetterDeathInfo",
|
|
439
|
+
"RequeueOptions",
|
|
440
|
+
"RabbitMQExchangeConfig",
|
|
441
|
+
"RabbitMQQueueBinding",
|
|
442
|
+
"RabbitMQQueueConfig",
|
|
443
|
+
"RabbitMQReconnectConfig",
|
|
444
|
+
"RabbitMQModuleOptions",
|
|
445
|
+
"HttpRequest",
|
|
446
|
+
"ModuleConfig",
|
|
447
|
+
"Options",
|
|
448
|
+
"GuardContract",
|
|
449
|
+
];
|
|
450
|
+
|
|
451
|
+
console.log(`\n${bold("@grupodiariodaregiao/bunstone")} — Public Exports\n`);
|
|
452
|
+
console.log(cyan(bold(" Value exports ")) + gray("(import { ... })"));
|
|
453
|
+
for (const name of VALUE_EXPORTS.sort())
|
|
454
|
+
console.log(` ${gray("·")} ${name}`);
|
|
455
|
+
|
|
456
|
+
console.log();
|
|
457
|
+
console.log(
|
|
458
|
+
yellow(bold(" Type-only exports ")) + gray("(import type { ... })"),
|
|
459
|
+
);
|
|
460
|
+
for (const name of TYPE_ONLY_EXPORTS.sort())
|
|
461
|
+
console.log(` ${gray("·")} ${name}`);
|
|
462
|
+
console.log();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
466
|
+
// bunstone new <project-name>
|
|
467
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
468
|
+
|
|
27
469
|
const starterPath = join(import.meta.dir, "..", "starter");
|
|
28
470
|
|
|
29
471
|
async function copyDir(src: string, dest: string) {
|
|
@@ -34,28 +476,20 @@ async function copyDir(src: string, dest: string) {
|
|
|
34
476
|
const srcPath = join(src, entry.name);
|
|
35
477
|
const destPath = join(dest, entry.name);
|
|
36
478
|
|
|
37
|
-
if (entry.isDirectory())
|
|
38
|
-
|
|
39
|
-
} else {
|
|
40
|
-
await copyFile(srcPath, destPath);
|
|
41
|
-
}
|
|
479
|
+
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
480
|
+
else await copyFile(srcPath, destPath);
|
|
42
481
|
}
|
|
43
482
|
}
|
|
44
483
|
|
|
45
|
-
async function scaffold() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(" or: bunstone <project-name>");
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
484
|
+
async function scaffold(projectName_?: string) {
|
|
485
|
+
const projectName = projectName_ ?? "my-bunstone-app";
|
|
486
|
+
const projectPath = join(process.cwd(), projectName);
|
|
51
487
|
|
|
52
488
|
console.log(`🚀 Scaffolding new Bunstone project in ${projectPath}...`);
|
|
53
489
|
|
|
54
490
|
try {
|
|
55
|
-
// Copy the entire starter directory
|
|
56
491
|
await copyDir(starterPath, projectPath);
|
|
57
492
|
|
|
58
|
-
// Update package.json name
|
|
59
493
|
const pkgPath = join(projectPath, "package.json");
|
|
60
494
|
const pkgContent = await readFile(pkgPath, "utf-8");
|
|
61
495
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -82,4 +516,22 @@ async function scaffold() {
|
|
|
82
516
|
}
|
|
83
517
|
}
|
|
84
518
|
|
|
85
|
-
|
|
519
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
520
|
+
// Help
|
|
521
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
522
|
+
|
|
523
|
+
function printHelp() {
|
|
524
|
+
console.log(`
|
|
525
|
+
${bold("bunstone")} — CLI for the Bunstone framework
|
|
526
|
+
|
|
527
|
+
${cyan("Usage:")}
|
|
528
|
+
bunstone new <project-name> Scaffold a new project
|
|
529
|
+
bunstone run [bun-flags] <entry> Run your app with enhanced error messages
|
|
530
|
+
bunstone exports List all public exports
|
|
531
|
+
|
|
532
|
+
${cyan("Examples:")}
|
|
533
|
+
bunstone new my-api
|
|
534
|
+
bunstone run src/main.ts
|
|
535
|
+
bunstone run --watch src/main.ts
|
|
536
|
+
`);
|
|
537
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export * from "./lib/rabbitmq/decorators/subscribe.decorator";
|
|
|
46
46
|
export type * from "./lib/rabbitmq/interfaces/rabbitmq-options.interface";
|
|
47
47
|
export type * from "./lib/rabbitmq/interfaces/rabbitmq-message.interface";
|
|
48
48
|
export * from "./lib/errors";
|
|
49
|
+
export { ErrorFormatter } from "./lib/utils/error-formatter";
|
|
49
50
|
export * from "./lib/guard";
|
|
50
51
|
export * from "./lib/http-exceptions";
|
|
51
52
|
export * from "./lib/http-methods";
|