@grupodiariodaregiao/bunstone 0.4.5 → 0.4.7

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 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
- let command = args[0];
13
- let projectName = args[1];
24
+ const command = args[0];
14
25
 
15
- // Default to "new" command if only project name is provided
16
- if (command && command !== "new" && !projectName) {
17
- projectName = command;
18
- command = "new";
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
- // Default project name if none provided
22
- if (!projectName && command === "new") {
23
- projectName = "my-bunstone-app";
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
- const projectPath = join(process.cwd(), projectName || "");
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
- await copyDir(srcPath, destPath);
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
- if (command !== "new" || !projectName) {
47
- console.log("Usage: bunstone new <project-name>");
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
- scaffold();
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.js CHANGED
@@ -32499,6 +32499,41 @@ class AdapterError extends BunstoneError {
32499
32499
  }
32500
32500
  }
32501
32501
 
32502
+ class ImportError extends BunstoneError {
32503
+ constructor(message, code = "BNS-IMP-002", suggestion, context, cause) {
32504
+ super(message, code, suggestion, context, cause);
32505
+ }
32506
+ static typeOnlyImport(name, pkg, valueAlternatives) {
32507
+ const altSection = valueAlternatives.length > 0 ? [
32508
+ "",
32509
+ "If you were looking for a runtime value with a similar name, did you mean one of these?",
32510
+ ...valueAlternatives.map((s) => ` - ${s}`)
32511
+ ].join(`
32512
+ `) : "";
32513
+ return new ImportError(`'${name}' is a type-only export of '${pkg}' \u2014 it does not exist at runtime.`, "BNS-IMP-001", [
32514
+ `Replace the import with 'import type':`,
32515
+ ` \u2717 import { ${name} } from '${pkg}'`,
32516
+ ` \u2713 import type { ${name} } from '${pkg}'`,
32517
+ altSection
32518
+ ].filter(Boolean).join(`
32519
+ `), { name, package: pkg });
32520
+ }
32521
+ static unknownExport(name, pkg, suggestions) {
32522
+ const didYouMean = suggestions.length > 0 ? [
32523
+ "",
32524
+ "Did you mean one of these?",
32525
+ ...suggestions.map((s) => ` - ${s}`)
32526
+ ].join(`
32527
+ `) : "";
32528
+ return new ImportError(`'${name}' is not exported by '${pkg}'.`, "BNS-IMP-002", [
32529
+ "Check the spelling of the imported name.",
32530
+ "Run 'bunstone exports' to see all available exports.",
32531
+ didYouMean
32532
+ ].filter(Boolean).join(`
32533
+ `), { name, package: pkg });
32534
+ }
32535
+ }
32536
+
32502
32537
  // lib/injectable.ts
32503
32538
  var import_reflect_metadata = __toESM(require_Reflect(), 1);
32504
32539
  function Injectable() {
@@ -43269,7 +43304,156 @@ var colors = {
43269
43304
  strikethrough: "\x1B[9m"
43270
43305
  };
43271
43306
 
43307
+ // lib/utils/fuzzy-match.ts
43308
+ function editDistance(a, b3) {
43309
+ const s1 = a.toLowerCase();
43310
+ const s2 = b3.toLowerCase();
43311
+ const m = s1.length;
43312
+ const n2 = s2.length;
43313
+ let prev = Array.from({ length: n2 + 1 }, (_3, j3) => j3);
43314
+ let curr = new Array(n2 + 1).fill(0);
43315
+ for (let i = 1;i <= m; i++) {
43316
+ curr[0] = i;
43317
+ for (let j3 = 1;j3 <= n2; j3++) {
43318
+ const cost = s1[i - 1] === s2[j3 - 1] ? 0 : 1;
43319
+ curr[j3] = cost === 0 ? prev[j3 - 1] : 1 + Math.min(prev[j3], curr[j3 - 1], prev[j3 - 1]);
43320
+ }
43321
+ [prev, curr] = [curr, prev];
43322
+ }
43323
+ return prev[n2];
43324
+ }
43325
+ function closestMatches(name2, candidates, limit = 5, maxDistance = 4) {
43326
+ const results = [];
43327
+ for (const candidate of candidates) {
43328
+ const dist = editDistance(name2, candidate);
43329
+ if (dist <= maxDistance) {
43330
+ results.push({ name: candidate, dist });
43331
+ }
43332
+ }
43333
+ results.sort((a, b3) => a.dist - b3.dist);
43334
+ return results.slice(0, limit).map((r2) => r2.name);
43335
+ }
43336
+
43337
+ // lib/utils/known-exports.ts
43338
+ var TYPE_ONLY_EXPORTS = new Set([
43339
+ "RabbitMessage",
43340
+ "RabbitPublishOptions",
43341
+ "RabbitSubscribeOptions",
43342
+ "DeadLetterMessage",
43343
+ "DeadLetterDeathInfo",
43344
+ "RequeueOptions",
43345
+ "RabbitMQExchangeConfig",
43346
+ "RabbitMQQueueBinding",
43347
+ "RabbitMQQueueConfig",
43348
+ "RabbitMQReconnectConfig",
43349
+ "RabbitMQModuleOptions",
43350
+ "HttpRequest",
43351
+ "ModuleConfig",
43352
+ "Options",
43353
+ "GuardContract"
43354
+ ]);
43355
+ var VALUE_EXPORTS = [
43356
+ "CacheAdapter",
43357
+ "FormDataAdapter",
43358
+ "UploadAdapter",
43359
+ "EmailModule",
43360
+ "EmailService",
43361
+ "EmailLayout",
43362
+ "AppStartup",
43363
+ "Layout",
43364
+ "Controller",
43365
+ "Get",
43366
+ "Post",
43367
+ "Put",
43368
+ "Patch",
43369
+ "Delete",
43370
+ "Head",
43371
+ "Options",
43372
+ "RateLimit",
43373
+ "RateLimitGuard",
43374
+ "RedisStorage",
43375
+ "CommandBus",
43376
+ "CqrsModule",
43377
+ "CommandHandler",
43378
+ "EventHandler",
43379
+ "QueryHandler",
43380
+ "Saga",
43381
+ "EventBus",
43382
+ "QueryBus",
43383
+ "SqlModule",
43384
+ "SqlService",
43385
+ "BullMqModule",
43386
+ "QueueService",
43387
+ "Processor",
43388
+ "Process",
43389
+ "RabbitMQModule",
43390
+ "RabbitMQService",
43391
+ "RabbitMQDeadLetterService",
43392
+ "RabbitMQConnection",
43393
+ "RabbitConsumer",
43394
+ "RabbitSubscribe",
43395
+ "BunstoneError",
43396
+ "DependencyResolutionError",
43397
+ "ModuleInitializationError",
43398
+ "ConfigurationError",
43399
+ "CqrsError",
43400
+ "DatabaseError",
43401
+ "BullMQError",
43402
+ "RabbitMQError",
43403
+ "ScheduleError",
43404
+ "TestingError",
43405
+ "RateLimitError",
43406
+ "UploadError",
43407
+ "EmailError",
43408
+ "HttpParamError",
43409
+ "GuardError",
43410
+ "AdapterError",
43411
+ "ImportError",
43412
+ "ErrorFormatter",
43413
+ "Guard",
43414
+ "UseGuards",
43415
+ "HttpException",
43416
+ "BadRequestException",
43417
+ "UnauthorizedException",
43418
+ "ForbiddenException",
43419
+ "NotFoundException",
43420
+ "ConflictException",
43421
+ "UnprocessableEntityException",
43422
+ "InternalServerErrorException",
43423
+ "OkResponse",
43424
+ "CreatedResponse",
43425
+ "NoContentResponse",
43426
+ "HttpMethod",
43427
+ "Body",
43428
+ "Param",
43429
+ "Query",
43430
+ "Headers",
43431
+ "Req",
43432
+ "Res",
43433
+ "FormData",
43434
+ "Injectable",
43435
+ "Jwt",
43436
+ "JwtModule",
43437
+ "JwtService",
43438
+ "UseJwt",
43439
+ "Module",
43440
+ "OnModuleInit",
43441
+ "OnModuleDestroy",
43442
+ "ApiTags",
43443
+ "ApiOperation",
43444
+ "ApiResponse",
43445
+ "ApiBody",
43446
+ "Render",
43447
+ "Cron",
43448
+ "Timeout",
43449
+ "Test",
43450
+ "TestingModuleBuilder",
43451
+ "TestApp",
43452
+ "Logger"
43453
+ ];
43454
+
43272
43455
  // lib/utils/error-formatter.ts
43456
+ var BUN_EXPORT_NOT_FOUND = /Export named '(.+?)' not found in module '(.+?)'/;
43273
43457
  var BORDER = "\u2501".repeat(64);
43274
43458
  var THIN = "\u2500".repeat(64);
43275
43459
  function label(text, color) {
@@ -43282,8 +43466,26 @@ function indent(text, spaces = 4) {
43282
43466
  }
43283
43467
 
43284
43468
  class ErrorFormatter {
43469
+ static fromBunSyntaxError(error) {
43470
+ if (!(error instanceof SyntaxError))
43471
+ return null;
43472
+ const match = BUN_EXPORT_NOT_FOUND.exec(error.message);
43473
+ if (!match)
43474
+ return null;
43475
+ const name2 = match[1] ?? "";
43476
+ const modulePath = match[2] ?? "";
43477
+ const pkg = modulePath.includes("node_modules/") ? modulePath.replace(/.*node_modules\//, "").replace(/\/dist.*/, "") : modulePath;
43478
+ if (TYPE_ONLY_EXPORTS.has(name2)) {
43479
+ const valueAlternatives = closestMatches(name2, VALUE_EXPORTS);
43480
+ return ImportError.typeOnlyImport(name2, pkg, valueAlternatives);
43481
+ }
43482
+ const allNames = [...VALUE_EXPORTS, ...TYPE_ONLY_EXPORTS];
43483
+ const suggestions = closestMatches(name2, allNames);
43484
+ return ImportError.unknownExport(name2, pkg, suggestions);
43485
+ }
43285
43486
  static format(error, exit = false) {
43286
- const e2 = error;
43487
+ const upgraded = ErrorFormatter.fromBunSyntaxError(error);
43488
+ const e2 = upgraded ?? error;
43287
43489
  console.error(`
43288
43490
  ${colors.red}${BORDER}${colors.reset}`);
43289
43491
  console.error(`${colors.red}${colors.bold} \uD83D\uDCA5 Bunstone \u2014 Error Report${colors.reset}`);
@@ -117981,6 +118183,13 @@ if (document.readyState === 'loading') {
117981
118183
  return raw.content.toString();
117982
118184
  }
117983
118185
  })();
118186
+ const xDeath = raw.properties.headers?.["x-death"];
118187
+ const isDeadLetter = Array.isArray(xDeath) && xDeath.length > 0;
118188
+ const isDlqQueue = queueName.toLowerCase().includes(".dlq");
118189
+ if ((isDlqQueue || descriptor.options.queue?.toLowerCase().includes(".dlq")) && !isDeadLetter) {
118190
+ channel.ack(raw);
118191
+ return;
118192
+ }
117984
118193
  const msg = {
117985
118194
  data,
117986
118195
  raw,
@@ -117993,7 +118202,7 @@ if (document.readyState === 'loading') {
117993
118202
  } catch (err) {
117994
118203
  AppStartup.logger.error(`Unhandled error in RabbitMQ handler ${providerClass.name}.${descriptor.methodName}() on exchange "${exchange}" routingKey "${routingKey}": ${err.message}`);
117995
118204
  if (!noAck) {
117996
- channel.nack(raw, false, true);
118205
+ channel.nack(raw, false, false);
117997
118206
  }
117998
118207
  }
117999
118208
  }, { noAck });
@@ -118036,6 +118245,13 @@ if (document.readyState === 'loading') {
118036
118245
  return raw.content.toString();
118037
118246
  }
118038
118247
  })();
118248
+ const xDeath = raw.properties.headers?.["x-death"];
118249
+ const isDeadLetter = Array.isArray(xDeath) && xDeath.length > 0;
118250
+ const isDlqQueue = queue2.toLowerCase().includes(".dlq");
118251
+ if (isDlqQueue && !isDeadLetter) {
118252
+ channel.ack(raw);
118253
+ return;
118254
+ }
118039
118255
  let settled = false;
118040
118256
  const settle = (fn3) => {
118041
118257
  if (!settled) {
@@ -118065,7 +118281,7 @@ if (document.readyState === 'loading') {
118065
118281
  } catch (err) {
118066
118282
  AppStartup.logger.error(`Unhandled error in RabbitMQ handler ${providerName}.${descriptor.methodName}() on queue "${queue2}": ${err.message}`);
118067
118283
  if (!handlerNoAck && !settled) {
118068
- settle(() => channel.nack(raw, false, true));
118284
+ settle(() => channel.nack(raw, false, false));
118069
118285
  }
118070
118286
  }
118071
118287
  }
@@ -120335,6 +120551,7 @@ export {
120335
120551
  Jwt,
120336
120552
  InternalServerErrorException,
120337
120553
  Injectable,
120554
+ ImportError,
120338
120555
  HttpParamError,
120339
120556
  HttpException,
120340
120557
  Header,
@@ -196,3 +196,26 @@ export declare class GuardError extends BunstoneError {
196
196
  export declare class AdapterError extends BunstoneError {
197
197
  constructor(message: string, code?: "BNS-ADP-001" | "BNS-ADP-002", suggestion?: string, context?: Record<string, unknown>, cause?: Error);
198
198
  }
199
+ /**
200
+ * Represents an import-time error detected by the `bunstone run` CLI.
201
+ *
202
+ * Bun raises a raw `SyntaxError: Export named 'X' not found` that gives no
203
+ * guidance on the problem. This error carries structured context so the CLI
204
+ * can print a rich, actionable crash report.
205
+ *
206
+ * Codes:
207
+ * - `BNS-IMP-001` – name is a type-only export (must use `import type`)
208
+ * - `BNS-IMP-002` – name does not exist at all in the package (typo / wrong name)
209
+ */
210
+ export declare class ImportError extends BunstoneError {
211
+ constructor(message: string, code?: "BNS-IMP-001" | "BNS-IMP-002", suggestion?: string, context?: Record<string, unknown>, cause?: Error);
212
+ /**
213
+ * The user imported a type-only name as a value.
214
+ * e.g. `import { RabbitMessage }` instead of `import type { RabbitMessage }`.
215
+ */
216
+ static typeOnlyImport(name: string, pkg: string, valueAlternatives: string[]): ImportError;
217
+ /**
218
+ * The user imported a name that does not exist in the package at all.
219
+ */
220
+ static unknownExport(name: string, pkg: string, suggestions: string[]): ImportError;
221
+ }
@@ -1,3 +1,4 @@
1
+ import { ImportError } from "../errors";
1
2
  /**
2
3
  * Formats and prints a structured "Crash Report" whenever a `BunstoneError`
3
4
  * (or any unhandled error) aborts initialisation.
@@ -22,6 +23,13 @@
22
23
  * ────────────────────────────────────────────────────────────────
23
24
  */
24
25
  export declare class ErrorFormatter {
26
+ /**
27
+ * Attempts to convert a raw Bun `SyntaxError: Export named 'X' not found`
28
+ * into a structured `ImportError` with actionable hints.
29
+ *
30
+ * Returns `null` when the error does not match the pattern.
31
+ */
32
+ static fromBunSyntaxError(error: unknown): ImportError | null;
25
33
  /**
26
34
  * Prints a full crash report and (optionally) exits the process.
27
35
  *
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Lightweight fuzzy-matching utilities used by the `bunstone run` CLI command
3
+ * to produce "did you mean?" suggestions when an export name is not found.
4
+ */
5
+ /**
6
+ * Levenshtein edit distance between two strings (case-insensitive).
7
+ */
8
+ export declare function editDistance(a: string, b: string): number;
9
+ /**
10
+ * Returns up to `limit` closest matches for `name` from `candidates`,
11
+ * ordered by edit distance (closest first).
12
+ *
13
+ * Only returns candidates whose distance is ≤ `maxDistance`.
14
+ */
15
+ export declare function closestMatches(name: string, candidates: Iterable<string>, limit?: number, maxDistance?: number): string[];
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Catalogue of every public name exported from the bunstone package,
3
+ * split by whether it is a **value** (present at runtime) or a **type**
4
+ * (erased at compile-time, absent from the JS bundle).
5
+ *
6
+ * Used by the `bunstone run` CLI command to produce "did you mean?" hints
7
+ * when Bun throws `SyntaxError: Export named 'X' not found`.
8
+ */
9
+ /** Names that are exported with `export type` – they do NOT exist in the JS bundle. */
10
+ export declare const TYPE_ONLY_EXPORTS: ReadonlySet<string>;
11
+ /**
12
+ * All value names shipped in the bundle.
13
+ * Keep in sync with `index.ts`.
14
+ */
15
+ export declare const VALUE_EXPORTS: ReadonlyArray<string>;
@@ -1080,6 +1080,24 @@ if (document.readyState === 'loading') {
1080
1080
  }
1081
1081
  })();
1082
1082
 
1083
+ const xDeath = raw.properties.headers?.["x-death"];
1084
+ const isDeadLetter =
1085
+ Array.isArray(xDeath) && xDeath.length > 0;
1086
+ const isDlqQueue = queueName.toLowerCase().includes(".dlq");
1087
+
1088
+ // If the handler is on a DLQ or uses DeadLetterMessage type,
1089
+ // only process messages that have x-death headers.
1090
+ if (
1091
+ (isDlqQueue ||
1092
+ descriptor.options.queue
1093
+ ?.toLowerCase()
1094
+ .includes(".dlq")) &&
1095
+ !isDeadLetter
1096
+ ) {
1097
+ channel.ack(raw);
1098
+ return;
1099
+ }
1100
+
1083
1101
  const msg: RabbitMessage = {
1084
1102
  data,
1085
1103
  raw,
@@ -1095,7 +1113,7 @@ if (document.readyState === 'loading') {
1095
1113
  `Unhandled error in RabbitMQ handler ${providerClass.name}.${descriptor.methodName}() on exchange "${exchange}" routingKey "${routingKey}": ${err.message}`,
1096
1114
  );
1097
1115
  if (!noAck) {
1098
- channel.nack(raw, false, true);
1116
+ channel.nack(raw, false, false);
1099
1117
  }
1100
1118
  }
1101
1119
  },
@@ -1161,6 +1179,17 @@ if (document.readyState === 'loading') {
1161
1179
  }
1162
1180
  })();
1163
1181
 
1182
+ const xDeath = raw.properties.headers?.["x-death"];
1183
+ const isDeadLetter = Array.isArray(xDeath) && xDeath.length > 0;
1184
+ const isDlqQueue = queue.toLowerCase().includes(".dlq");
1185
+
1186
+ // If this is a DLQ, skip messages that don't have x-death headers
1187
+ // (i.e. someone published directly to the DLQ instead of it being a failed message)
1188
+ if (isDlqQueue && !isDeadLetter) {
1189
+ channel.ack(raw);
1190
+ return;
1191
+ }
1192
+
1164
1193
  // Settle guard: ack/nack/reject may only be called once per
1165
1194
  // delivery tag regardless of how many handlers invoke it.
1166
1195
  let settled = false;
@@ -1204,7 +1233,7 @@ if (document.readyState === 'loading') {
1204
1233
  `Unhandled error in RabbitMQ handler ${providerName}.${descriptor.methodName}() on queue "${queue}": ${err.message}`,
1205
1234
  );
1206
1235
  if (!handlerNoAck && !settled) {
1207
- settle(() => channel.nack(raw, false, true));
1236
+ settle(() => channel.nack(raw, false, false));
1208
1237
  }
1209
1238
  }
1210
1239
  }
@@ -679,3 +679,92 @@ export class AdapterError extends BunstoneError {
679
679
  super(message, code, suggestion, context, cause);
680
680
  }
681
681
  }
682
+
683
+ // ─── Import ───────────────────────────────────────────────────────────────────
684
+
685
+ /**
686
+ * Represents an import-time error detected by the `bunstone run` CLI.
687
+ *
688
+ * Bun raises a raw `SyntaxError: Export named 'X' not found` that gives no
689
+ * guidance on the problem. This error carries structured context so the CLI
690
+ * can print a rich, actionable crash report.
691
+ *
692
+ * Codes:
693
+ * - `BNS-IMP-001` – name is a type-only export (must use `import type`)
694
+ * - `BNS-IMP-002` – name does not exist at all in the package (typo / wrong name)
695
+ */
696
+ export class ImportError extends BunstoneError {
697
+ constructor(
698
+ message: string,
699
+ code: "BNS-IMP-001" | "BNS-IMP-002" = "BNS-IMP-002",
700
+ suggestion?: string,
701
+ context?: Record<string, unknown>,
702
+ cause?: Error,
703
+ ) {
704
+ super(message, code, suggestion, context, cause);
705
+ }
706
+
707
+ /**
708
+ * The user imported a type-only name as a value.
709
+ * e.g. `import { RabbitMessage }` instead of `import type { RabbitMessage }`.
710
+ */
711
+ static typeOnlyImport(
712
+ name: string,
713
+ pkg: string,
714
+ valueAlternatives: string[],
715
+ ): ImportError {
716
+ const altSection =
717
+ valueAlternatives.length > 0
718
+ ? [
719
+ "",
720
+ "If you were looking for a runtime value with a similar name, did you mean one of these?",
721
+ ...valueAlternatives.map((s) => ` - ${s}`),
722
+ ].join("\n ")
723
+ : "";
724
+
725
+ return new ImportError(
726
+ `'${name}' is a type-only export of '${pkg}' — it does not exist at runtime.`,
727
+ "BNS-IMP-001",
728
+ [
729
+ `Replace the import with 'import type':`,
730
+ ` ✗ import { ${name} } from '${pkg}'`,
731
+ ` ✓ import type { ${name} } from '${pkg}'`,
732
+ altSection,
733
+ ]
734
+ .filter(Boolean)
735
+ .join("\n "),
736
+ { name, package: pkg },
737
+ );
738
+ }
739
+
740
+ /**
741
+ * The user imported a name that does not exist in the package at all.
742
+ */
743
+ static unknownExport(
744
+ name: string,
745
+ pkg: string,
746
+ suggestions: string[],
747
+ ): ImportError {
748
+ const didYouMean =
749
+ suggestions.length > 0
750
+ ? [
751
+ "",
752
+ "Did you mean one of these?",
753
+ ...suggestions.map((s) => ` - ${s}`),
754
+ ].join("\n ")
755
+ : "";
756
+
757
+ return new ImportError(
758
+ `'${name}' is not exported by '${pkg}'.`,
759
+ "BNS-IMP-002",
760
+ [
761
+ "Check the spelling of the imported name.",
762
+ "Run 'bunstone exports' to see all available exports.",
763
+ didYouMean,
764
+ ]
765
+ .filter(Boolean)
766
+ .join("\n "),
767
+ { name, package: pkg },
768
+ );
769
+ }
770
+ }
@@ -1,5 +1,10 @@
1
- import { BunstoneError } from "../errors";
1
+ import { BunstoneError, ImportError } from "../errors";
2
2
  import { colors } from "./colors";
3
+ import { closestMatches } from "./fuzzy-match";
4
+ import { TYPE_ONLY_EXPORTS, VALUE_EXPORTS } from "./known-exports";
5
+
6
+ /** Regexp that matches Bun's "Export named 'X' not found in module 'Y'." message. */
7
+ const BUN_EXPORT_NOT_FOUND = /Export named '(.+?)' not found in module '(.+?)'/;
3
8
 
4
9
  const BORDER = "━".repeat(64);
5
10
  const THIN = "─".repeat(64);
@@ -39,6 +44,36 @@ function indent(text: string, spaces = 4): string {
39
44
  * ────────────────────────────────────────────────────────────────
40
45
  */
41
46
  export class ErrorFormatter {
47
+ /**
48
+ * Attempts to convert a raw Bun `SyntaxError: Export named 'X' not found`
49
+ * into a structured `ImportError` with actionable hints.
50
+ *
51
+ * Returns `null` when the error does not match the pattern.
52
+ */
53
+ static fromBunSyntaxError(error: unknown): ImportError | null {
54
+ if (!(error instanceof SyntaxError)) return null;
55
+
56
+ const match = BUN_EXPORT_NOT_FOUND.exec(error.message);
57
+ if (!match) return null;
58
+
59
+ const name = match[1] ?? "";
60
+ const modulePath = match[2] ?? "";
61
+ const pkg = modulePath.includes("node_modules/")
62
+ ? modulePath.replace(/.*node_modules\//, "").replace(/\/dist.*/, "")
63
+ : modulePath;
64
+
65
+ if (TYPE_ONLY_EXPORTS.has(name)) {
66
+ // Name exists but is type-only
67
+ const valueAlternatives = closestMatches(name, VALUE_EXPORTS);
68
+ return ImportError.typeOnlyImport(name, pkg, valueAlternatives);
69
+ }
70
+
71
+ // Name does not exist at all – fuzzy match against all known names
72
+ const allNames = [...VALUE_EXPORTS, ...TYPE_ONLY_EXPORTS];
73
+ const suggestions = closestMatches(name, allNames);
74
+ return ImportError.unknownExport(name, pkg, suggestions);
75
+ }
76
+
42
77
  /**
43
78
  * Prints a full crash report and (optionally) exits the process.
44
79
  *
@@ -46,7 +81,9 @@ export class ErrorFormatter {
46
81
  * @param exit When `true` (default), calls `process.exit(1)` after printing.
47
82
  */
48
83
  static format(error: unknown, exit = false): void {
49
- const e = error as any;
84
+ // Upgrade raw Bun import errors before formatting
85
+ const upgraded = ErrorFormatter.fromBunSyntaxError(error);
86
+ const e = (upgraded ?? error) as any;
50
87
 
51
88
  console.error(`\n${colors.red}${BORDER}${colors.reset}`);
52
89
  console.error(
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Lightweight fuzzy-matching utilities used by the `bunstone run` CLI command
3
+ * to produce "did you mean?" suggestions when an export name is not found.
4
+ */
5
+
6
+ /**
7
+ * Levenshtein edit distance between two strings (case-insensitive).
8
+ */
9
+ export function editDistance(a: string, b: string): number {
10
+ const s1 = a.toLowerCase();
11
+ const s2 = b.toLowerCase();
12
+ const m = s1.length;
13
+ const n = s2.length;
14
+
15
+ // Two-row rolling DP – avoids 2D-array index safety issues.
16
+ let prev: number[] = Array.from({ length: n + 1 }, (_, j) => j);
17
+ let curr: number[] = new Array<number>(n + 1).fill(0);
18
+
19
+ for (let i = 1; i <= m; i++) {
20
+ curr[0] = i;
21
+ for (let j = 1; j <= n; j++) {
22
+ const cost = s1[i - 1] === s2[j - 1] ? 0 : 1;
23
+ curr[j] =
24
+ cost === 0
25
+ ? (prev[j - 1] as number)
26
+ : 1 +
27
+ Math.min(
28
+ prev[j] as number,
29
+ curr[j - 1] as number,
30
+ prev[j - 1] as number,
31
+ );
32
+ }
33
+ [prev, curr] = [curr, prev];
34
+ }
35
+
36
+ return prev[n] as number;
37
+ }
38
+
39
+ /**
40
+ * Returns up to `limit` closest matches for `name` from `candidates`,
41
+ * ordered by edit distance (closest first).
42
+ *
43
+ * Only returns candidates whose distance is ≤ `maxDistance`.
44
+ */
45
+ export function closestMatches(
46
+ name: string,
47
+ candidates: Iterable<string>,
48
+ limit = 5,
49
+ maxDistance = 4,
50
+ ): string[] {
51
+ const results: { name: string; dist: number }[] = [];
52
+
53
+ for (const candidate of candidates) {
54
+ const dist = editDistance(name, candidate);
55
+ if (dist <= maxDistance) {
56
+ results.push({ name: candidate, dist });
57
+ }
58
+ }
59
+
60
+ results.sort((a, b) => a.dist - b.dist);
61
+ return results.slice(0, limit).map((r) => r.name);
62
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Catalogue of every public name exported from the bunstone package,
3
+ * split by whether it is a **value** (present at runtime) or a **type**
4
+ * (erased at compile-time, absent from the JS bundle).
5
+ *
6
+ * Used by the `bunstone run` CLI command to produce "did you mean?" hints
7
+ * when Bun throws `SyntaxError: Export named 'X' not found`.
8
+ */
9
+
10
+ // ── Type-only exports (erased at runtime) ─────────────────────────────────
11
+
12
+ /** Names that are exported with `export type` – they do NOT exist in the JS bundle. */
13
+ export const TYPE_ONLY_EXPORTS: ReadonlySet<string> = new Set([
14
+ // rabbitmq-message.interface
15
+ "RabbitMessage",
16
+ "RabbitPublishOptions",
17
+ "RabbitSubscribeOptions",
18
+ "DeadLetterMessage",
19
+ "DeadLetterDeathInfo",
20
+ "RequeueOptions",
21
+ // rabbitmq-options.interface
22
+ "RabbitMQExchangeConfig",
23
+ "RabbitMQQueueBinding",
24
+ "RabbitMQQueueConfig",
25
+ "RabbitMQReconnectConfig",
26
+ "RabbitMQModuleOptions",
27
+ // general types
28
+ "HttpRequest",
29
+ "ModuleConfig",
30
+ "Options",
31
+ "GuardContract",
32
+ ]);
33
+
34
+ // ── Value exports (present at runtime) ────────────────────────────────────
35
+
36
+ /**
37
+ * All value names shipped in the bundle.
38
+ * Keep in sync with `index.ts`.
39
+ */
40
+ export const VALUE_EXPORTS: ReadonlyArray<string> = [
41
+ // adapters
42
+ "CacheAdapter",
43
+ "FormDataAdapter",
44
+ "UploadAdapter",
45
+ // email
46
+ "EmailModule",
47
+ "EmailService",
48
+ "EmailLayout",
49
+ // app
50
+ "AppStartup",
51
+ // components
52
+ "Layout",
53
+ // controller
54
+ "Controller",
55
+ "Get",
56
+ "Post",
57
+ "Put",
58
+ "Patch",
59
+ "Delete",
60
+ "Head",
61
+ "Options",
62
+ // rate limit
63
+ "RateLimit",
64
+ "RateLimitGuard",
65
+ "RedisStorage",
66
+ // cqrs
67
+ "CommandBus",
68
+ "CqrsModule",
69
+ "CommandHandler",
70
+ "EventHandler",
71
+ "QueryHandler",
72
+ "Saga",
73
+ "EventBus",
74
+ "QueryBus",
75
+ // database
76
+ "SqlModule",
77
+ "SqlService",
78
+ // bullmq
79
+ "BullMqModule",
80
+ "QueueService",
81
+ "Processor",
82
+ "Process",
83
+ // rabbitmq
84
+ "RabbitMQModule",
85
+ "RabbitMQService",
86
+ "RabbitMQDeadLetterService",
87
+ "RabbitMQConnection",
88
+ "RabbitConsumer",
89
+ "RabbitSubscribe",
90
+ // errors
91
+ "BunstoneError",
92
+ "DependencyResolutionError",
93
+ "ModuleInitializationError",
94
+ "ConfigurationError",
95
+ "CqrsError",
96
+ "DatabaseError",
97
+ "BullMQError",
98
+ "RabbitMQError",
99
+ "ScheduleError",
100
+ "TestingError",
101
+ "RateLimitError",
102
+ "UploadError",
103
+ "EmailError",
104
+ "HttpParamError",
105
+ "GuardError",
106
+ "AdapterError",
107
+ "ImportError",
108
+ "ErrorFormatter",
109
+ // guard
110
+ "Guard",
111
+ "UseGuards",
112
+ // http-exceptions
113
+ "HttpException",
114
+ "BadRequestException",
115
+ "UnauthorizedException",
116
+ "ForbiddenException",
117
+ "NotFoundException",
118
+ "ConflictException",
119
+ "UnprocessableEntityException",
120
+ "InternalServerErrorException",
121
+ "OkResponse",
122
+ "CreatedResponse",
123
+ "NoContentResponse",
124
+ // http-methods
125
+ "HttpMethod",
126
+ // http-params
127
+ "Body",
128
+ "Param",
129
+ "Query",
130
+ "Headers",
131
+ "Req",
132
+ "Res",
133
+ "FormData",
134
+ // injectable
135
+ "Injectable",
136
+ // jwt
137
+ "Jwt",
138
+ "JwtModule",
139
+ "JwtService",
140
+ "UseJwt",
141
+ // module
142
+ "Module",
143
+ // on-module
144
+ "OnModuleInit",
145
+ "OnModuleDestroy",
146
+ // openapi
147
+ "ApiTags",
148
+ "ApiOperation",
149
+ "ApiResponse",
150
+ "ApiBody",
151
+ // render
152
+ "Render",
153
+ // schedule
154
+ "Cron",
155
+ "Timeout",
156
+ // testing
157
+ "Test",
158
+ "TestingModuleBuilder",
159
+ "TestApp",
160
+ // logger
161
+ "Logger",
162
+ ];
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "types": "./dist/*.d.ts"
14
14
  }
15
15
  },
16
- "version": "0.4.5",
16
+ "version": "0.4.7",
17
17
  "homepage": "https://bunstone.diario.one/",
18
18
  "repository": {
19
19
  "url": "https://github.com/diariodaregiao/bunstone.git",