@spfn/core 0.1.0-alpha.64 → 0.1.0-alpha.68

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.
Files changed (37) hide show
  1. package/README.md +5 -5
  2. package/dist/{auto-loader-CdsxOceW.d.ts → auto-loader-JFaZ9gON.d.ts} +3 -2
  3. package/dist/cache/index.d.ts +211 -0
  4. package/dist/cache/index.js +992 -0
  5. package/dist/cache/index.js.map +1 -0
  6. package/dist/client/index.d.ts +2 -2
  7. package/dist/codegen/generators/index.d.ts +7 -6
  8. package/dist/codegen/generators/index.js +208 -27
  9. package/dist/codegen/generators/index.js.map +1 -1
  10. package/dist/codegen/index.d.ts +67 -118
  11. package/dist/codegen/index.js +1419 -1295
  12. package/dist/codegen/index.js.map +1 -1
  13. package/dist/database-errors-CoPrcOpq.d.ts +86 -0
  14. package/dist/db/index.d.ts +316 -9
  15. package/dist/db/index.js +6 -6
  16. package/dist/db/index.js.map +1 -1
  17. package/dist/error-handler-wjLL3v-a.d.ts +44 -0
  18. package/dist/errors/index.d.ts +119 -0
  19. package/dist/errors/index.js +160 -0
  20. package/dist/errors/index.js.map +1 -0
  21. package/dist/index-DHiAqhKv.d.ts +101 -0
  22. package/dist/index.d.ts +2 -228
  23. package/dist/index.js +274 -292
  24. package/dist/index.js.map +1 -1
  25. package/dist/middleware/index.d.ts +33 -0
  26. package/dist/middleware/index.js +890 -0
  27. package/dist/middleware/index.js.map +1 -0
  28. package/dist/route/index.d.ts +172 -7
  29. package/dist/route/index.js +209 -70
  30. package/dist/route/index.js.map +1 -1
  31. package/dist/server/index.js +267 -176
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/{types-Bd8YsFSU.d.ts → types-CAON3Mmg.d.ts} +1 -1
  34. package/package.json +19 -2
  35. package/dist/bind-CSzshBtm.d.ts +0 -17
  36. package/dist/contract-generator-CqKsfsNE.d.ts +0 -52
  37. package/dist/postgres-errors-lw1aRUFe.d.ts +0 -397
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Database Error Classes
3
+ *
4
+ * Type-safe error handling with custom error class hierarchy
5
+ * Mapped to HTTP status codes for API responses
6
+ */
7
+ /**
8
+ * Base Database Error
9
+ *
10
+ * Base class for all database-related errors
11
+ */
12
+ declare class DatabaseError<TDetails extends Record<string, unknown> = Record<string, unknown>> extends Error {
13
+ readonly statusCode: number;
14
+ readonly details?: TDetails;
15
+ readonly timestamp: Date;
16
+ constructor(message: string, statusCode?: number, details?: TDetails);
17
+ /**
18
+ * Serialize error for API response
19
+ */
20
+ toJSON(): {
21
+ name: string;
22
+ message: string;
23
+ statusCode: number;
24
+ details: TDetails | undefined;
25
+ timestamp: string;
26
+ };
27
+ }
28
+ /**
29
+ * Connection Error (503 Service Unavailable)
30
+ *
31
+ * Database connection failure, connection pool exhaustion, etc.
32
+ */
33
+ declare class ConnectionError extends DatabaseError {
34
+ constructor(message: string, details?: Record<string, any>);
35
+ }
36
+ /**
37
+ * Query Error (500 Internal Server Error)
38
+ *
39
+ * SQL query execution failure, syntax errors, etc.
40
+ */
41
+ declare class QueryError extends DatabaseError {
42
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
43
+ }
44
+ /**
45
+ * Not Found Error (404 Not Found)
46
+ *
47
+ * Requested resource does not exist
48
+ */
49
+ declare class NotFoundError extends QueryError {
50
+ constructor(resource: string, id: string | number);
51
+ }
52
+ /**
53
+ * Constraint Violation Error (400 Bad Request)
54
+ *
55
+ * Database constraint violation (NOT NULL, CHECK, FOREIGN KEY, etc.)
56
+ * This is different from HTTP ValidationError which validates request input
57
+ */
58
+ declare class ConstraintViolationError extends QueryError {
59
+ constructor(message: string, details?: Record<string, any>);
60
+ }
61
+ /**
62
+ * Transaction Error (500 Internal Server Error)
63
+ *
64
+ * Transaction start/commit/rollback failure
65
+ */
66
+ declare class TransactionError extends DatabaseError {
67
+ constructor(message: string, statusCode?: number, details?: Record<string, any>);
68
+ }
69
+ /**
70
+ * Deadlock Error (409 Conflict)
71
+ *
72
+ * Database deadlock detected
73
+ */
74
+ declare class DeadlockError extends TransactionError {
75
+ constructor(message: string, details?: Record<string, any>);
76
+ }
77
+ /**
78
+ * Duplicate Entry Error (409 Conflict)
79
+ *
80
+ * Unique constraint violation (e.g., duplicate email)
81
+ */
82
+ declare class DuplicateEntryError extends QueryError {
83
+ constructor(field: string, value: string | number);
84
+ }
85
+
86
+ export { ConnectionError as C, DatabaseError as D, NotFoundError as N, QueryError as Q, TransactionError as T, ConstraintViolationError as a, DeadlockError as b, DuplicateEntryError as c };
@@ -1,10 +1,11 @@
1
1
  import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
2
2
  import postgres, { Sql } from 'postgres';
3
- export { c as DrizzleConfigOptions, h as TransactionContext, n as TransactionDB, T as Transactional, j as TransactionalOptions, d as detectDialect, f as foreignKey, b as fromPostgresError, a as generateDrizzleConfigFile, g as getDrizzleConfig, e as getTransaction, i as id, o as optionalForeignKey, r as runWithTransaction, t as timestamps } from '../postgres-errors-lw1aRUFe.js';
4
- import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
5
- import { PgTable, PgColumn } from 'drizzle-orm/pg-core';
3
+ import * as drizzle_orm from 'drizzle-orm';
6
4
  import { SQL } from 'drizzle-orm';
7
- import 'hono';
5
+ import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
6
+ import { PgColumn, PgTable } from 'drizzle-orm/pg-core';
7
+ import * as hono from 'hono';
8
+ import { D as DatabaseError } from '../database-errors-CoPrcOpq.js';
8
9
 
9
10
  /**
10
11
  * Database Configuration
@@ -151,7 +152,7 @@ type DbConnectionType = 'read' | 'write';
151
152
  * }
152
153
  * ```
153
154
  */
154
- declare function getDatabase(type?: DbConnectionType): PostgresJsDatabase | undefined;
155
+ declare function getDatabase(type?: DbConnectionType): PostgresJsDatabase<Record<string, unknown>> | undefined;
155
156
  /**
156
157
  * Set global database instances (for testing or manual configuration)
157
158
  *
@@ -169,7 +170,7 @@ declare function getDatabase(type?: DbConnectionType): PostgresJsDatabase | unde
169
170
  * setDatabase(drizzle(writeClient), drizzle(readClient));
170
171
  * ```
171
172
  */
172
- declare function setDatabase(write: PostgresJsDatabase | undefined, read?: PostgresJsDatabase | undefined): void;
173
+ declare function setDatabase(write: PostgresJsDatabase<Record<string, unknown>> | undefined, read?: PostgresJsDatabase<Record<string, unknown>> | undefined): void;
173
174
  /**
174
175
  * Initialize database from environment variables
175
176
  * Automatically called by server startup
@@ -217,8 +218,8 @@ declare function setDatabase(write: PostgresJsDatabase | undefined, read?: Postg
217
218
  * ```
218
219
  */
219
220
  declare function initDatabase(options?: DatabaseOptions): Promise<{
220
- write?: PostgresJsDatabase;
221
- read?: PostgresJsDatabase;
221
+ write?: PostgresJsDatabase<Record<string, unknown>>;
222
+ read?: PostgresJsDatabase<Record<string, unknown>>;
222
223
  }>;
223
224
  /**
224
225
  * Close all database connections and cleanup
@@ -269,6 +270,157 @@ declare function createDatabaseConnection(connectionString: string, poolConfig:
269
270
  */
270
271
  declare function checkConnection(client: Sql): Promise<boolean>;
271
272
 
273
+ /**
274
+ * Drizzle Kit configuration generator
275
+ * Automatically generates drizzle.config.ts from environment variables
276
+ */
277
+ interface DrizzleConfigOptions {
278
+ /** Database connection URL (defaults to process.env.DATABASE_URL) */
279
+ databaseUrl?: string;
280
+ /** Schema files glob pattern or array of patterns (defaults to './src/server/entities/\*\*\/*.ts') */
281
+ schema?: string | string[];
282
+ /** Migration output directory (defaults to './src/server/drizzle') */
283
+ out?: string;
284
+ /** Database dialect (auto-detected from URL if not provided) */
285
+ dialect?: 'postgresql' | 'mysql' | 'sqlite';
286
+ /** Current working directory for discovering package schemas */
287
+ cwd?: string;
288
+ /** Disable automatic package schema discovery */
289
+ disablePackageDiscovery?: boolean;
290
+ /** Only include schemas from specific package (e.g., '@spfn/cms') */
291
+ packageFilter?: string;
292
+ }
293
+ /**
294
+ * Detect database dialect from connection URL
295
+ */
296
+ declare function detectDialect(url: string): 'postgresql' | 'mysql' | 'sqlite';
297
+ /**
298
+ * Generate Drizzle Kit configuration
299
+ *
300
+ * @param options - Configuration options
301
+ * @returns Drizzle Kit configuration object
302
+ *
303
+ * @example
304
+ * ```ts
305
+ * // Zero-config (reads from process.env.DATABASE_URL)
306
+ * const config = getDrizzleConfig();
307
+ *
308
+ * // Custom config
309
+ * const config = getDrizzleConfig({
310
+ * databaseUrl: 'postgresql://localhost/mydb',
311
+ * schema: './src/db/schema/*.ts',
312
+ * out: './migrations',
313
+ * });
314
+ * ```
315
+ */
316
+ declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
317
+ schema: string | string[];
318
+ out: string;
319
+ dialect: "postgresql" | "mysql" | "sqlite";
320
+ dbCredentials: {
321
+ url: string;
322
+ };
323
+ };
324
+ /**
325
+ * Generate drizzle.config.ts file content
326
+ *
327
+ * @param options - Configuration options
328
+ * @returns File content as string
329
+ */
330
+ declare function generateDrizzleConfigFile(options?: DrizzleConfigOptions): string;
331
+
332
+ /**
333
+ * Standard auto-incrementing primary key
334
+ *
335
+ * @returns bigserial primary key column
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * export const users = pgTable('users', {
340
+ * id: id(),
341
+ * // ...
342
+ * });
343
+ * ```
344
+ */
345
+ declare function id(): drizzle_orm.IsPrimaryKey<drizzle_orm.NotNull<drizzle_orm_pg_core.PgBigSerial53BuilderInitial<"id">>>;
346
+ /**
347
+ * Standard timestamp fields (createdAt, updatedAt)
348
+ *
349
+ * Both fields are timezone-aware, auto-set to current time on creation.
350
+ * When autoUpdate is enabled, updatedAt will be automatically updated on record updates.
351
+ *
352
+ * @param options - Optional configuration
353
+ * @param options.autoUpdate - Automatically update updatedAt on record updates (default: false)
354
+ * @returns Object with createdAt and updatedAt columns
355
+ *
356
+ * @example
357
+ * ```typescript
358
+ * // Without auto-update
359
+ * export const users = pgTable('users', {
360
+ * id: id(),
361
+ * email: text('email'),
362
+ * ...timestamps(),
363
+ * });
364
+ *
365
+ * // With auto-update
366
+ * export const posts = pgTable('posts', {
367
+ * id: id(),
368
+ * title: text('title'),
369
+ * ...timestamps({ autoUpdate: true }),
370
+ * });
371
+ * ```
372
+ */
373
+ declare function timestamps(options?: {
374
+ autoUpdate?: boolean;
375
+ }): {
376
+ createdAt: drizzle_orm.NotNull<drizzle_orm.HasDefault<drizzle_orm_pg_core.PgTimestampBuilderInitial<"created_at">>>;
377
+ updatedAt: drizzle_orm.NotNull<drizzle_orm.HasDefault<drizzle_orm_pg_core.PgTimestampBuilderInitial<"updated_at">>>;
378
+ };
379
+ /**
380
+ * Foreign key reference to another table
381
+ *
382
+ * Creates a bigserial column with cascade delete.
383
+ * Type-safe: ensures the reference points to a valid PostgreSQL column.
384
+ *
385
+ * @param name - Column name (e.g., 'author' creates 'author_id')
386
+ * @param reference - Reference to parent table column
387
+ * @param options - Optional foreign key options
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * import { users } from './users';
392
+ *
393
+ * export const posts = pgTable('posts', {
394
+ * id: id(),
395
+ * authorId: foreignKey('author', () => users.id),
396
+ * ...timestamps(),
397
+ * });
398
+ * ```
399
+ */
400
+ declare function foreignKey<T extends PgColumn>(name: string, reference: () => T, options?: {
401
+ onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
402
+ }): drizzle_orm.NotNull<drizzle_orm_pg_core.PgBigSerial53BuilderInitial<`${string}_id`>>;
403
+ /**
404
+ * Optional foreign key reference (nullable)
405
+ *
406
+ * Type-safe: ensures the reference points to a valid PostgreSQL column.
407
+ *
408
+ * @param name - Column name (e.g., 'author' creates 'author_id')
409
+ * @param reference - Reference to parent table column
410
+ * @param options - Optional foreign key options
411
+ *
412
+ * @example
413
+ * ```typescript
414
+ * export const posts = pgTable('posts', {
415
+ * id: id(),
416
+ * authorId: optionalForeignKey('author', () => users.id),
417
+ * });
418
+ * ```
419
+ */
420
+ declare function optionalForeignKey<T extends PgColumn>(name: string, reference: () => T, options?: {
421
+ onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
422
+ }): drizzle_orm_pg_core.PgBigSerial53BuilderInitial<`${string}_id`>;
423
+
272
424
  /**
273
425
  * Database Schema Helper
274
426
  *
@@ -330,6 +482,161 @@ declare function getSchemaInfo(packageName: string): {
330
482
  scope: string | null;
331
483
  };
332
484
 
485
+ /**
486
+ * AsyncLocalStorage-based Transaction Context
487
+ *
488
+ * Uses Node.js AsyncLocalStorage to propagate transactions throughout the async call chain.
489
+ *
490
+ * Features:
491
+ * - AsyncLocalStorage-based context management
492
+ * - Type-safe transaction propagation across async chains
493
+ * - Transaction ID tracking for debugging and tracing
494
+ * - Nested transaction detection and logging
495
+ * - Transaction nesting level tracking
496
+ */
497
+
498
+ /**
499
+ * Transaction database type
500
+ * Uses Record<string, unknown> to accept any schema shape
501
+ */
502
+ type TransactionDB = PostgresJsDatabase<Record<string, unknown>>;
503
+ /**
504
+ * Transaction context stored in AsyncLocalStorage
505
+ */
506
+ type TransactionContext = {
507
+ /** The actual Drizzle transaction object */
508
+ tx: TransactionDB;
509
+ /** Unique transaction ID for logging and tracing */
510
+ txId: string;
511
+ level: number;
512
+ };
513
+ /**
514
+ * Get current transaction from AsyncLocalStorage
515
+ *
516
+ * @returns Transaction if available, null otherwise
517
+ */
518
+ declare function getTransaction(): TransactionDB | null;
519
+ /**
520
+ * Run a function within a transaction context
521
+ *
522
+ * The transaction will be available to all async operations within the callback
523
+ * via getTransaction().
524
+ *
525
+ * @param tx - Drizzle transaction object
526
+ * @param txId - Unique ID for the transaction
527
+ * @param callback - Function to run within transaction context
528
+ * @returns Result of the callback
529
+ */
530
+ declare function runWithTransaction<T>(tx: TransactionDB, txId: string, // Add txId parameter
531
+ callback: () => Promise<T>): Promise<T>;
532
+
533
+ /**
534
+ * Transaction middleware options
535
+ */
536
+ interface TransactionalOptions {
537
+ /**
538
+ * Slow transaction warning threshold in milliseconds
539
+ * @default 1000 (1 second)
540
+ */
541
+ slowThreshold?: number;
542
+ /**
543
+ * Enable transaction logging
544
+ * @default true
545
+ */
546
+ enableLogging?: boolean;
547
+ /**
548
+ * Transaction timeout in milliseconds
549
+ *
550
+ * If transaction exceeds this duration, it will be aborted with TransactionError.
551
+ *
552
+ * @default 30000 (30 seconds) or TRANSACTION_TIMEOUT environment variable
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * // Default timeout (30s or TRANSACTION_TIMEOUT env var)
557
+ * Transactional()
558
+ *
559
+ * // Custom timeout for specific route (60s)
560
+ * Transactional({ timeout: 60000 })
561
+ *
562
+ * // Disable timeout
563
+ * Transactional({ timeout: 0 })
564
+ * ```
565
+ */
566
+ timeout?: number;
567
+ }
568
+ /**
569
+ * Transaction middleware for Hono routes
570
+ *
571
+ * Automatically wraps route handlers in a database transaction.
572
+ * Commits on success, rolls back on error.
573
+ *
574
+ * @example
575
+ * ```typescript
576
+ * // In your route file
577
+ * export const middlewares = [Transactional()];
578
+ *
579
+ * export async function POST(c: RouteContext) {
580
+ * // All DB operations run in a transaction
581
+ * const [user] = await db.insert(users).values(body).returning();
582
+ * await db.insert(profiles).values({ userId: user.id });
583
+ * // Auto-commits on success
584
+ * return c.json(user, 201);
585
+ * }
586
+ * ```
587
+ *
588
+ * @example
589
+ * ```typescript
590
+ * // With custom options
591
+ * export const middlewares = [
592
+ * Transactional({
593
+ * slowThreshold: 2000, // Warn if transaction takes > 2s
594
+ * enableLogging: false, // Disable logging
595
+ * timeout: 60000, // 60 second timeout for long operations
596
+ * })
597
+ * ];
598
+ * ```
599
+ *
600
+ * 🔄 Transaction behavior:
601
+ * - Success: Auto-commit
602
+ * - Error: Auto-rollback
603
+ * - Detects context.error to trigger rollback
604
+ *
605
+ * 📊 Transaction logging:
606
+ * - Auto-logs transaction start/commit/rollback
607
+ * - Measures and records execution time
608
+ * - Warns about slow transactions (default: > 1s)
609
+ */
610
+ declare function Transactional(options?: TransactionalOptions): hono.MiddlewareHandler<any, string, {}>;
611
+
612
+ /**
613
+ * PostgreSQL Error Conversion Utilities
614
+ *
615
+ * Converts PostgreSQL-specific error codes to custom error types
616
+ * @see https://www.postgresql.org/docs/current/errcodes-appendix.html
617
+ */
618
+
619
+ /**
620
+ * Convert PostgreSQL error to custom DatabaseError
621
+ *
622
+ * Maps PostgreSQL error codes to appropriate error classes with correct status codes
623
+ *
624
+ * @param error - PostgreSQL error object (from pg driver or Drizzle)
625
+ * @returns Custom DatabaseError instance
626
+ *
627
+ * @example
628
+ * ```typescript
629
+ * import { fromPostgresError } from '@spfn/core/db';
630
+ *
631
+ * try {
632
+ * await db.insert(users).values(data);
633
+ * } catch (pgError) {
634
+ * throw fromPostgresError(pgError);
635
+ * }
636
+ * ```
637
+ */
638
+ declare function fromPostgresError(error: any): DatabaseError;
639
+
333
640
  /**
334
641
  * Database Helper Functions
335
642
  *
@@ -573,4 +880,4 @@ declare function deleteMany<T extends PgTable>(table: T, where: WhereObject<Infe
573
880
  */
574
881
  declare function count<T extends PgTable>(table: T, where?: WhereObject<InferSelectModel<T>> | SQL | undefined): Promise<number>;
575
882
 
576
- export { type DatabaseClients, type PoolConfig, type RetryConfig, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createFunctionSchema, createMany, deleteMany, deleteOne, findMany, findOne, getDatabase, getDatabaseInfo, getSchemaInfo, initDatabase, packageNameToSchema, setDatabase, updateMany, updateOne, upsert };
883
+ export { type DatabaseClients, type DrizzleConfigOptions, type PoolConfig, type RetryConfig, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, checkConnection, closeDatabase, count, create, createDatabaseConnection, createDatabaseFromEnv, createFunctionSchema, createMany, deleteMany, deleteOne, detectDialect, findMany, findOne, foreignKey, fromPostgresError, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, id, initDatabase, optionalForeignKey, packageNameToSchema, runWithTransaction, setDatabase, timestamps, updateMany, updateOne, upsert };
package/dist/db/index.js CHANGED
@@ -1009,10 +1009,10 @@ var QueryError = class extends DatabaseError {
1009
1009
  this.name = "QueryError";
1010
1010
  }
1011
1011
  };
1012
- var ValidationError = class extends QueryError {
1012
+ var ConstraintViolationError = class extends QueryError {
1013
1013
  constructor(message, details) {
1014
1014
  super(message, 400, details);
1015
- this.name = "ValidationError";
1015
+ this.name = "ConstraintViolationError";
1016
1016
  }
1017
1017
  };
1018
1018
  var TransactionError = class extends DatabaseError {
@@ -1077,11 +1077,11 @@ function fromPostgresError(error) {
1077
1077
  case "23000":
1078
1078
  // integrity_constraint_violation
1079
1079
  case "23001":
1080
- return new ValidationError(message, { code, constraint: "integrity" });
1080
+ return new ConstraintViolationError(message, { code, constraint: "integrity" });
1081
1081
  case "23502":
1082
- return new ValidationError(message, { code, constraint: "not_null" });
1082
+ return new ConstraintViolationError(message, { code, constraint: "not_null" });
1083
1083
  case "23503":
1084
- return new ValidationError(message, { code, constraint: "foreign_key" });
1084
+ return new ConstraintViolationError(message, { code, constraint: "foreign_key" });
1085
1085
  case "23505":
1086
1086
  const parsed = parseUniqueViolation(message);
1087
1087
  if (parsed) {
@@ -1089,7 +1089,7 @@ function fromPostgresError(error) {
1089
1089
  }
1090
1090
  return new DuplicateEntryError("field", "value");
1091
1091
  case "23514":
1092
- return new ValidationError(message, { code, constraint: "check" });
1092
+ return new ConstraintViolationError(message, { code, constraint: "check" });
1093
1093
  // Class 40 — Transaction Rollback
1094
1094
  case "40000":
1095
1095
  // transaction_rollback