@grupodiariodaregiao/bunstone 0.7.0 → 0.7.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/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ export * from "./lib/cqrs/interfaces/event.interface";
33
33
  export * from "./lib/cqrs/interfaces/query.interface";
34
34
  export * from "./lib/cqrs/query-bus";
35
35
  export { SqlModule, SqlService } from "./lib/database/sql-module";
36
+ export type { SqlConnectionOptions, SqlPoolOptions, SqlRegisterOptions, } from "./lib/database/sql-module";
36
37
  export { BullMqModule } from "./lib/bullmq/bullmq-module";
37
38
  export { QueueService } from "./lib/bullmq/queue.service";
38
39
  export * from "./lib/bullmq/decorators/processor.decorator";
package/dist/index.js CHANGED
@@ -13473,7 +13473,7 @@ var require_tracestate_impl = __commonJS((exports) => {
13473
13473
  const value = listMember.slice(i + 1, part.length);
13474
13474
  if ((0, tracestate_validators_1.validateKey)(key) && (0, tracestate_validators_1.validateValue)(value)) {
13475
13475
  agg.set(key, value);
13476
- } else {}
13476
+ }
13477
13477
  }
13478
13478
  return agg;
13479
13479
  }, new Map);
@@ -103227,7 +103227,7 @@ function finalize(ctx, schema) {
103227
103227
  result.$schema = "http://json-schema.org/draft-07/schema#";
103228
103228
  } else if (ctx.target === "draft-04") {
103229
103229
  result.$schema = "http://json-schema.org/draft-04/schema#";
103230
- } else if (ctx.target === "openapi-3.0") {} else {}
103230
+ } else if (ctx.target === "openapi-3.0") {}
103231
103231
  if (ctx.external?.uri) {
103232
103232
  const id = ctx.external.registry.get(schema)?.id;
103233
103233
  if (!id)
@@ -103475,7 +103475,7 @@ var literalProcessor = (schema, ctx, json, _params) => {
103475
103475
  if (val === undefined) {
103476
103476
  if (ctx.unrepresentable === "throw") {
103477
103477
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
103478
- } else {}
103478
+ }
103479
103479
  } else if (typeof val === "bigint") {
103480
103480
  if (ctx.unrepresentable === "throw") {
103481
103481
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -109939,8 +109939,6 @@ if (!nativeAccelerationDisabled) {
109939
109939
  try {
109940
109940
  if (true)
109941
109941
  extractor = require_msgpackr_extract();
109942
- else
109943
- ;
109944
109942
  if (extractor)
109945
109943
  setExtractor(extractor.extractStrings);
109946
109944
  } catch (error48) {}
@@ -123877,7 +123875,7 @@ class AppStartup {
123877
123875
  for (const timeout of timeouts) {
123878
123876
  AppStartup.logger.log(`Scheduling timeout for method: ${timeout.methodName} with delay: ${timeout.delay}ms`);
123879
123877
  setTimeout(() => {
123880
- provider[timeout.methodName]();
123878
+ AppStartup.invokeScheduledHandler(provider, timeout.methodName, "timeout");
123881
123879
  }, timeout.delay);
123882
123880
  }
123883
123881
  }
@@ -123894,11 +123892,24 @@ class AppStartup {
123894
123892
  for (const cron of crons) {
123895
123893
  AppStartup.logger.log(`Scheduling cron for method: ${cron.methodName}`);
123896
123894
  import_node_cron.default.schedule(cron.expression, () => {
123897
- provider[cron.methodName]();
123895
+ AppStartup.invokeScheduledHandler(provider, cron.methodName, "cron");
123898
123896
  });
123899
123897
  }
123900
123898
  }
123901
123899
  }
123900
+ static async invokeScheduledHandler(provider, methodName, kind) {
123901
+ const handler = provider[methodName];
123902
+ if (typeof handler !== "function") {
123903
+ AppStartup.logger.error(`${kind === "cron" ? "Cron" : "Timeout"} job "${methodName}" is not a method on the provider`);
123904
+ return;
123905
+ }
123906
+ try {
123907
+ const result = handler.call(provider);
123908
+ await Promise.resolve(result);
123909
+ } catch (err) {
123910
+ AppStartup.logger.error(`${kind === "cron" ? "Cron" : "Timeout"} job "${methodName}" failed`, err);
123911
+ }
123912
+ }
123902
123913
  static registerBullMqWorkers(module) {
123903
123914
  const providersBullMq = Reflect.getMetadata("dip:bullmq", module);
123904
123915
  if (!providersBullMq) {
@@ -124420,6 +124431,10 @@ CqrsModule = __legacyDecorateClassTS([
124420
124431
  // lib/database/sql-module.ts
124421
124432
  var import_api5 = __toESM(require_src(), 1);
124422
124433
  var {SQL } = globalThis.Bun;
124434
+ var CONNECTION_CLOSED_CODES = new Set([
124435
+ "ERR_MYSQL_CONNECTION_CLOSED",
124436
+ "ERR_POSTGRES_CONNECTION_CLOSED"
124437
+ ]);
124423
124438
  function detectProvider(url2) {
124424
124439
  if (url2.startsWith("mysql://") || url2.startsWith("mysql2://")) {
124425
124440
  return "mysql";
@@ -124439,9 +124454,47 @@ function buildConnectionConfig(provider, timezone) {
124439
124454
  }
124440
124455
  return;
124441
124456
  }
124457
+ function pickPoolOptions(source) {
124458
+ if (!source) {
124459
+ return;
124460
+ }
124461
+ const pool = {};
124462
+ if (source.max !== undefined)
124463
+ pool.max = source.max;
124464
+ if (source.idleTimeout !== undefined)
124465
+ pool.idleTimeout = source.idleTimeout;
124466
+ if (source.maxLifetime !== undefined)
124467
+ pool.maxLifetime = source.maxLifetime;
124468
+ if (source.connectionTimeout !== undefined) {
124469
+ pool.connectionTimeout = source.connectionTimeout;
124470
+ }
124471
+ return Object.keys(pool).length > 0 ? pool : undefined;
124472
+ }
124473
+ function isConnectionClosedError(err) {
124474
+ return typeof err === "object" && err !== null && "code" in err && CONNECTION_CLOSED_CODES.has(String(err.code));
124475
+ }
124476
+ async function withConnectionRetry(fn3) {
124477
+ try {
124478
+ return await fn3();
124479
+ } catch (err) {
124480
+ if (!isConnectionClosedError(err)) {
124481
+ throw err;
124482
+ }
124483
+ }
124484
+ return await fn3();
124485
+ }
124442
124486
 
124443
124487
  class SqlService {
124444
124488
  async query(query, params) {
124489
+ return withConnectionRetry(() => this.executeQuery(query, params));
124490
+ }
124491
+ async transaction(queries) {
124492
+ return withConnectionRetry(() => this.executeTransaction(queries));
124493
+ }
124494
+ async bulkInsert(table, values) {
124495
+ return withConnectionRetry(() => this.executeBulkInsert(table, values));
124496
+ }
124497
+ async executeQuery(query, params) {
124445
124498
  const sql = this.getSqlInstance();
124446
124499
  const tracer = import_api5.trace.getTracer("bunstone.db");
124447
124500
  const operation = detectOperation(query);
@@ -124468,7 +124521,7 @@ class SqlService {
124468
124521
  }
124469
124522
  });
124470
124523
  }
124471
- async transaction(queries) {
124524
+ async executeTransaction(queries) {
124472
124525
  const sql = this.getSqlInstance();
124473
124526
  const tracer = import_api5.trace.getTracer("bunstone.db");
124474
124527
  const span = tracer.startSpan("db.transaction", {
@@ -124497,7 +124550,7 @@ class SqlService {
124497
124550
  }
124498
124551
  });
124499
124552
  }
124500
- async bulkInsert(table, values) {
124553
+ async executeBulkInsert(table, values) {
124501
124554
  const sql = this.getSqlInstance();
124502
124555
  const tracer = import_api5.trace.getTracer("bunstone.db");
124503
124556
  const span = tracer.startSpan("db.INSERT", {
@@ -124538,14 +124591,28 @@ SqlService = __legacyDecorateClassTS([
124538
124591
  class SqlModule {
124539
124592
  static sqlInstance;
124540
124593
  static provider;
124541
- static register(connection2, timezone) {
124594
+ static register(connection2, second) {
124542
124595
  const url2 = typeof connection2 === "string" ? connection2 : `${connection2.provider}://${connection2.username}:${connection2.password}@${connection2.host}:${connection2.port}/${connection2.database}`;
124543
124596
  const provider = typeof connection2 === "string" ? detectProvider(connection2) : connection2.provider;
124544
124597
  SqlModule.provider = provider;
124545
- const tz = typeof connection2 === "string" ? timezone ?? "UTC" : connection2.timezone ?? "UTC";
124546
- const connectionConfig = buildConnectionConfig(provider, tz);
124598
+ let timezone = "UTC";
124599
+ let poolOptions;
124600
+ if (typeof connection2 === "string") {
124601
+ if (typeof second === "string") {
124602
+ timezone = second;
124603
+ } else if (second) {
124604
+ timezone = second.timezone ?? "UTC";
124605
+ poolOptions = pickPoolOptions(second);
124606
+ }
124607
+ } else {
124608
+ timezone = connection2.timezone ?? "UTC";
124609
+ poolOptions = pickPoolOptions(connection2);
124610
+ }
124611
+ const connectionConfig = buildConnectionConfig(provider, timezone);
124612
+ const pool = poolOptions ?? {};
124547
124613
  SqlModule.sqlInstance = new SQL({
124548
124614
  url: url2,
124615
+ ...pool,
124549
124616
  ...connectionConfig && { connection: connectionConfig }
124550
124617
  });
124551
124618
  return SqlModule;
@@ -86,6 +86,7 @@ export declare class AppStartup {
86
86
  private static buildEffectiveRateLimit;
87
87
  private static registerTimeouts;
88
88
  private static registerCronJobs;
89
+ private static invokeScheduledHandler;
89
90
  private static registerBullMqWorkers;
90
91
  /**
91
92
  * Sets up RabbitMQ consumers for every provider that has `@RabbitSubscribe`
@@ -1,5 +1,21 @@
1
1
  import { SQL } from "bun";
2
- type ConnectionOptions = {
2
+ export type SqlPoolOptions = {
3
+ /** Maximum connections in the pool. Bun default: 10 */
4
+ max?: number;
5
+ /**
6
+ * Seconds before closing idle pool connections. Bun default: 0 (no limit).
7
+ * For MariaDB/MySQL, set below the server `wait_timeout` (often 28800 = 8h).
8
+ */
9
+ idleTimeout?: number;
10
+ /**
11
+ * Max connection lifetime in seconds. Bun default: 0 (no limit).
12
+ * Forces periodic reconnection before the server drops stale sockets.
13
+ */
14
+ maxLifetime?: number;
15
+ /** Seconds to wait when opening a new connection. Bun default: 30 */
16
+ connectionTimeout?: number;
17
+ };
18
+ export type SqlConnectionOptions = SqlPoolOptions & {
3
19
  host: string;
4
20
  port: number;
5
21
  username: string;
@@ -13,6 +29,9 @@ type ConnectionOptions = {
13
29
  */
14
30
  timezone?: string;
15
31
  };
32
+ export type SqlRegisterOptions = SqlPoolOptions & {
33
+ timezone?: string;
34
+ };
16
35
  type Provider = "postgresql" | "mysql" | "sqlite";
17
36
  export declare class SqlService {
18
37
  query<T = any>(query: string, params?: any[]): Promise<T[]>;
@@ -21,13 +40,17 @@ export declare class SqlService {
21
40
  params?: any[];
22
41
  }>): Promise<void>;
23
42
  bulkInsert<T = any>(table: string, values: T[]): Promise<void>;
43
+ private executeQuery;
44
+ private executeTransaction;
45
+ private executeBulkInsert;
24
46
  private getSqlInstance;
25
47
  }
26
48
  export declare class SqlModule {
27
49
  private static sqlInstance;
28
50
  private static provider;
29
- static register(connection: ConnectionOptions): typeof SqlModule;
51
+ static register(connection: SqlConnectionOptions): typeof SqlModule;
30
52
  static register(connection: string, timezone?: string): typeof SqlModule;
53
+ static register(connection: string, options: SqlRegisterOptions): typeof SqlModule;
31
54
  static getSqlInstance(): SQL;
32
55
  static getProvider(): Provider | undefined;
33
56
  }
@@ -906,7 +906,11 @@ export class AppStartup {
906
906
  `Scheduling timeout for method: ${timeout.methodName} with delay: ${timeout.delay}ms`,
907
907
  );
908
908
  setTimeout(() => {
909
- provider[timeout.methodName]();
909
+ void AppStartup.invokeScheduledHandler(
910
+ provider,
911
+ timeout.methodName,
912
+ "timeout",
913
+ );
910
914
  }, timeout.delay);
911
915
  }
912
916
  }
@@ -935,12 +939,40 @@ export class AppStartup {
935
939
  for (const cron of crons) {
936
940
  AppStartup.logger.log(`Scheduling cron for method: ${cron.methodName}`);
937
941
  scheduler.schedule(cron.expression, () => {
938
- provider[cron.methodName]();
942
+ void AppStartup.invokeScheduledHandler(
943
+ provider,
944
+ cron.methodName,
945
+ "cron",
946
+ );
939
947
  });
940
948
  }
941
949
  }
942
950
  }
943
951
 
952
+ private static async invokeScheduledHandler(
953
+ provider: Record<string, unknown>,
954
+ methodName: string,
955
+ kind: "cron" | "timeout",
956
+ ): Promise<void> {
957
+ const handler = provider[methodName];
958
+ if (typeof handler !== "function") {
959
+ AppStartup.logger.error(
960
+ `${kind === "cron" ? "Cron" : "Timeout"} job "${methodName}" is not a method on the provider`,
961
+ );
962
+ return;
963
+ }
964
+
965
+ try {
966
+ const result = handler.call(provider);
967
+ await Promise.resolve(result);
968
+ } catch (err) {
969
+ AppStartup.logger.error(
970
+ `${kind === "cron" ? "Cron" : "Timeout"} job "${methodName}" failed`,
971
+ err,
972
+ );
973
+ }
974
+ }
975
+
944
976
  private static registerBullMqWorkers(module: any) {
945
977
  const providersBullMq: Map<any, { name?: string; methodName: string }[]> =
946
978
  Reflect.getMetadata("dip:bullmq", module);
@@ -9,7 +9,24 @@ import { DatabaseError } from "../errors";
9
9
  import { Injectable } from "../injectable";
10
10
  import { Module } from "../module";
11
11
 
12
- type ConnectionOptions = {
12
+ export type SqlPoolOptions = {
13
+ /** Maximum connections in the pool. Bun default: 10 */
14
+ max?: number;
15
+ /**
16
+ * Seconds before closing idle pool connections. Bun default: 0 (no limit).
17
+ * For MariaDB/MySQL, set below the server `wait_timeout` (often 28800 = 8h).
18
+ */
19
+ idleTimeout?: number;
20
+ /**
21
+ * Max connection lifetime in seconds. Bun default: 0 (no limit).
22
+ * Forces periodic reconnection before the server drops stale sockets.
23
+ */
24
+ maxLifetime?: number;
25
+ /** Seconds to wait when opening a new connection. Bun default: 30 */
26
+ connectionTimeout?: number;
27
+ };
28
+
29
+ export type SqlConnectionOptions = SqlPoolOptions & {
13
30
  host: string;
14
31
  port: number;
15
32
  username: string;
@@ -24,8 +41,17 @@ type ConnectionOptions = {
24
41
  timezone?: string;
25
42
  };
26
43
 
44
+ export type SqlRegisterOptions = SqlPoolOptions & {
45
+ timezone?: string;
46
+ };
47
+
27
48
  type Provider = "postgresql" | "mysql" | "sqlite";
28
49
 
50
+ const CONNECTION_CLOSED_CODES = new Set([
51
+ "ERR_MYSQL_CONNECTION_CLOSED",
52
+ "ERR_POSTGRES_CONNECTION_CLOSED",
53
+ ]);
54
+
29
55
  function detectProvider(url: string): Provider {
30
56
  if (url.startsWith("mysql://") || url.startsWith("mysql2://")) {
31
57
  return "mysql";
@@ -57,9 +83,65 @@ function buildConnectionConfig(
57
83
  return undefined;
58
84
  }
59
85
 
86
+ function pickPoolOptions(
87
+ source: SqlPoolOptions | undefined,
88
+ ): SqlPoolOptions | undefined {
89
+ if (!source) {
90
+ return undefined;
91
+ }
92
+
93
+ const pool: SqlPoolOptions = {};
94
+ if (source.max !== undefined) pool.max = source.max;
95
+ if (source.idleTimeout !== undefined) pool.idleTimeout = source.idleTimeout;
96
+ if (source.maxLifetime !== undefined) pool.maxLifetime = source.maxLifetime;
97
+ if (source.connectionTimeout !== undefined) {
98
+ pool.connectionTimeout = source.connectionTimeout;
99
+ }
100
+
101
+ return Object.keys(pool).length > 0 ? pool : undefined;
102
+ }
103
+
104
+ function isConnectionClosedError(err: unknown): boolean {
105
+ return (
106
+ typeof err === "object" &&
107
+ err !== null &&
108
+ "code" in err &&
109
+ CONNECTION_CLOSED_CODES.has(String((err as { code: unknown }).code))
110
+ );
111
+ }
112
+
113
+ async function withConnectionRetry<T>(fn: () => Promise<T>): Promise<T> {
114
+ try {
115
+ return await fn();
116
+ } catch (err) {
117
+ if (!isConnectionClosedError(err)) {
118
+ throw err;
119
+ }
120
+ }
121
+
122
+ return await fn();
123
+ }
124
+
60
125
  @Injectable()
61
126
  export class SqlService {
62
127
  async query<T = any>(query: string, params?: any[]): Promise<T[]> {
128
+ return withConnectionRetry(() => this.executeQuery<T>(query, params));
129
+ }
130
+
131
+ async transaction(
132
+ queries: Array<{ query: string; params?: any[] }>,
133
+ ): Promise<void> {
134
+ return withConnectionRetry(() => this.executeTransaction(queries));
135
+ }
136
+
137
+ async bulkInsert<T = any>(table: string, values: T[]): Promise<void> {
138
+ return withConnectionRetry(() => this.executeBulkInsert(table, values));
139
+ }
140
+
141
+ private async executeQuery<T>(
142
+ query: string,
143
+ params?: any[],
144
+ ): Promise<T[]> {
63
145
  const sql = this.getSqlInstance();
64
146
  const tracer = trace.getTracer("bunstone.db");
65
147
  const operation = detectOperation(query);
@@ -91,7 +173,7 @@ export class SqlService {
91
173
  );
92
174
  }
93
175
 
94
- async transaction(
176
+ private async executeTransaction(
95
177
  queries: Array<{ query: string; params?: any[] }>,
96
178
  ): Promise<void> {
97
179
  const sql = this.getSqlInstance();
@@ -127,7 +209,7 @@ export class SqlService {
127
209
  );
128
210
  }
129
211
 
130
- async bulkInsert<T = any>(table: string, values: T[]): Promise<void> {
212
+ private async executeBulkInsert<T>(table: string, values: T[]): Promise<void> {
131
213
  const sql = this.getSqlInstance();
132
214
  const tracer = trace.getTracer("bunstone.db");
133
215
  const span = tracer.startSpan("db.INSERT", {
@@ -175,9 +257,16 @@ export class SqlModule {
175
257
  private static sqlInstance: SQL;
176
258
  private static provider: Provider | undefined;
177
259
 
178
- static register(connection: ConnectionOptions): typeof SqlModule;
260
+ static register(connection: SqlConnectionOptions): typeof SqlModule;
179
261
  static register(connection: string, timezone?: string): typeof SqlModule;
180
- static register(connection: string | ConnectionOptions, timezone?: string) {
262
+ static register(
263
+ connection: string,
264
+ options: SqlRegisterOptions,
265
+ ): typeof SqlModule;
266
+ static register(
267
+ connection: string | SqlConnectionOptions,
268
+ second?: string | SqlRegisterOptions,
269
+ ) {
181
270
  const url =
182
271
  typeof connection === "string"
183
272
  ? connection
@@ -190,15 +279,27 @@ export class SqlModule {
190
279
 
191
280
  SqlModule.provider = provider;
192
281
 
193
- const tz =
194
- typeof connection === "string"
195
- ? (timezone ?? "UTC")
196
- : (connection.timezone ?? "UTC");
282
+ let timezone = "UTC";
283
+ let poolOptions: SqlPoolOptions | undefined;
284
+
285
+ if (typeof connection === "string") {
286
+ if (typeof second === "string") {
287
+ timezone = second;
288
+ } else if (second) {
289
+ timezone = second.timezone ?? "UTC";
290
+ poolOptions = pickPoolOptions(second);
291
+ }
292
+ } else {
293
+ timezone = connection.timezone ?? "UTC";
294
+ poolOptions = pickPoolOptions(connection);
295
+ }
197
296
 
198
- const connectionConfig = buildConnectionConfig(provider, tz);
297
+ const connectionConfig = buildConnectionConfig(provider, timezone);
298
+ const pool = poolOptions ?? {};
199
299
 
200
300
  SqlModule.sqlInstance = new SQL({
201
301
  url,
302
+ ...pool,
202
303
  ...(connectionConfig && { connection: connectionConfig }),
203
304
  });
204
305
 
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "types": "./dist/*.d.ts"
14
14
  }
15
15
  },
16
- "version": "0.7.0",
16
+ "version": "0.7.1",
17
17
  "homepage": "https://bunstone.diario.one/",
18
18
  "repository": {
19
19
  "url": "https://github.com/diariodaregiao/bunstone.git",