@spfn/core 0.2.0-beta.34 → 0.2.0-beta.36

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.
@@ -994,6 +994,78 @@ interface TransactionalOptions {
994
994
  */
995
995
  declare function Transactional(options?: TransactionalOptions): hono_types.MiddlewareHandler<any, string, {}, Response>;
996
996
 
997
+ /**
998
+ * Transaction runner options
999
+ */
1000
+ interface RunInTransactionOptions {
1001
+ /**
1002
+ * Slow transaction warning threshold in milliseconds
1003
+ * @default 1000 (1 second)
1004
+ */
1005
+ slowThreshold?: number;
1006
+ /**
1007
+ * Enable transaction logging
1008
+ * @default true
1009
+ */
1010
+ enableLogging?: boolean;
1011
+ /**
1012
+ * Transaction timeout in milliseconds
1013
+ *
1014
+ * Sets PostgreSQL `statement_timeout` to enforce database-level timeout.
1015
+ * If transaction exceeds this duration, PostgreSQL will automatically cancel
1016
+ * the query and rollback the transaction, ensuring data consistency.
1017
+ *
1018
+ * Behavior:
1019
+ * - `timeout: 0` - Disables timeout (unlimited execution time)
1020
+ * - `timeout: null` - Uses default (30s or TRANSACTION_TIMEOUT env var)
1021
+ * - `timeout: undefined` - Uses default (30s or TRANSACTION_TIMEOUT env var)
1022
+ * - `timeout: N` - Sets timeout to N milliseconds (1 to 2147483647)
1023
+ *
1024
+ * Note: Timeout is only applied to root transactions. Nested transactions
1025
+ * (SAVEPOINTs) inherit the timeout from the outer transaction.
1026
+ *
1027
+ * @default 30000 (30 seconds) or TRANSACTION_TIMEOUT environment variable
1028
+ *
1029
+ * @example
1030
+ * ```typescript
1031
+ * // Use default timeout (30s)
1032
+ * await runInTransaction(callback);
1033
+ *
1034
+ * // Disable timeout for long-running operations
1035
+ * await runInTransaction(callback, { timeout: 0 });
1036
+ *
1037
+ * // Set custom timeout (60s)
1038
+ * await runInTransaction(callback, { timeout: 60000 });
1039
+ * ```
1040
+ */
1041
+ timeout?: number;
1042
+ /**
1043
+ * Context string for logging (e.g., 'migration:add-user', 'script:cleanup')
1044
+ * @default 'transaction'
1045
+ */
1046
+ context?: string;
1047
+ }
1048
+ /**
1049
+ * Run a callback function within a database transaction
1050
+ *
1051
+ * Automatically manages transaction lifecycle:
1052
+ * - Commits on success
1053
+ * - Rolls back on error
1054
+ * - Tracks execution time
1055
+ * - Warns about slow transactions
1056
+ * - Enforces timeout if configured
1057
+ *
1058
+ * Errors are propagated to the caller without modification.
1059
+ * Caller is responsible for error handling and conversion.
1060
+ *
1061
+ * @param callback - Function to execute within transaction
1062
+ * @param options - Transaction options
1063
+ * @returns Result of callback function
1064
+ * @throws TransactionError if database not initialized or timeout exceeded
1065
+ * @throws Any error thrown by callback function
1066
+ */
1067
+ declare function runInTransaction<T>(callback: (tx: TransactionDB) => Promise<T>, options?: RunInTransactionOptions): Promise<T>;
1068
+
997
1069
  /**
998
1070
  * PostgreSQL Error Conversion Utilities
999
1071
  *
@@ -1602,4 +1674,4 @@ declare abstract class BaseRepository<TSchema extends Record<string, unknown> =
1602
1674
  protected _count<T extends PgTable>(table: T, where?: Record<string, any> | SQL | undefined): Promise<number>;
1603
1675
  }
1604
1676
 
1605
- export { type AfterCommitCallback, BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
1677
+ export { type AfterCommitCallback, BaseRepository, type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
package/dist/db/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { drizzle } from 'drizzle-orm/postgres-js';
2
2
  import { env } from '@spfn/core/config';
3
3
  import { logger } from '@spfn/core/logger';
4
+ import net from 'net';
4
5
  import postgres from 'postgres';
5
6
  import { QueryError, ConnectionError, DeadlockError, TransactionError, ConstraintViolationError, DuplicateEntryError, DatabaseError } from '@spfn/core/errors';
6
7
  import { parseNumber, parseBoolean } from '@spfn/core/env';
@@ -10,7 +11,7 @@ import { bigserial, timestamp, bigint, uuid as uuid$1, text, jsonb, pgSchema } f
10
11
  import { AsyncLocalStorage } from 'async_hooks';
11
12
  import { createMiddleware } from 'hono/factory';
12
13
  import { randomUUID } from 'crypto';
13
- import { count as count$1, sql, eq, and } from 'drizzle-orm';
14
+ import { sql, count as count$1, eq, and } from 'drizzle-orm';
14
15
 
15
16
  // src/db/manager/factory.ts
16
17
  function parseUniqueViolation(message) {
@@ -129,6 +130,12 @@ function fromPostgresError(error) {
129
130
  }
130
131
 
131
132
  // src/db/manager/connection.ts
133
+ function getSocketFamily() {
134
+ const family = process.env.DATABASE_SOCKET_FAMILY;
135
+ if (family === "4") return 4;
136
+ if (family === "6") return 6;
137
+ return void 0;
138
+ }
132
139
  var dbLogger = logger.child("@spfn/core:database");
133
140
  var DEFAULT_CONNECT_TIMEOUT = 10;
134
141
  function delay(ms) {
@@ -189,10 +196,21 @@ async function createDatabaseConnection(connectionString, poolConfig, retryConfi
189
196
  let client;
190
197
  for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
191
198
  try {
199
+ const socketFamily = getSocketFamily();
192
200
  client = postgres(connectionString, {
193
201
  max: poolConfig.max,
194
202
  idle_timeout: poolConfig.idleTimeout,
195
- connect_timeout: DEFAULT_CONNECT_TIMEOUT
203
+ connect_timeout: DEFAULT_CONNECT_TIMEOUT,
204
+ ...socketFamily && {
205
+ socket: ({ host, port }) => new Promise((resolve, reject) => {
206
+ const socket = new net.Socket();
207
+ socket.on("error", reject);
208
+ socket.connect(
209
+ { port: port[0], host: host[0], family: socketFamily },
210
+ () => resolve(socket)
211
+ );
212
+ })
213
+ }
196
214
  });
197
215
  await client`SELECT 1 as test`;
198
216
  if (attempt > 0) {
@@ -1900,6 +1918,6 @@ var BaseRepository = class {
1900
1918
  }
1901
1919
  };
1902
1920
 
1903
- export { BaseRepository, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
1921
+ export { BaseRepository, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, enumText, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, onAfterCommit, optionalForeignKey, packageNameToSchema, publishingFields, runInTransaction, runWithTransaction, setDatabase, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
1904
1922
  //# sourceMappingURL=index.js.map
1905
1923
  //# sourceMappingURL=index.js.map