data-api-client 2.0.0 → 2.1.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/README.md CHANGED
@@ -3,16 +3,34 @@
3
3
  [![npm](https://img.shields.io/npm/v/data-api-client.svg)](https://www.npmjs.com/package/data-api-client)
4
4
  [![npm](https://img.shields.io/npm/l/data-api-client.svg)](https://www.npmjs.com/package/data-api-client)
5
5
 
6
- > **Note:** Version 2.0.0 is currently in active development. We welcome your feedback and bug reports! Please [open an issue](https://github.com/jeremydaly/data-api-client/issues) if you encounter any problems or have suggestions for improvement.
6
+ > **Note:** Version 2.1.0 introduces mysql2 and pg compatibility layers with full ORM support! We welcome your feedback and bug reports. Please [open an issue](https://github.com/jeremydaly/data-api-client/issues) if you encounter any problems or have suggestions for improvement.
7
7
  >
8
8
  > **Using v1.x?** See [README_v1.md](README_v1.md) for v1.x documentation.
9
9
 
10
- The **Data API Client** is a lightweight wrapper that simplifies working with the Amazon Aurora Serverless Data API by abstracting away the notion of field values. This abstraction annotates native JavaScript types supplied as input parameters, as well as converts annotated response data to native JavaScript types. It's basically a [DocumentClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) for the Data API. It also dramatically simplifies **transactions** and uses modern async/await patterns with AWS SDK v3.
10
+ The **Data API Client** is a lightweight wrapper that simplifies working with the Amazon Aurora Serverless Data API by abstracting away the notion of field values. This abstraction annotates native JavaScript types supplied as input parameters, as well as converts annotated response data to native JavaScript types. It's basically a [DocumentClient](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html) for the Data API. It also dramatically simplifies **transactions**, provides **automatic retry logic** for scale-to-zero clusters, and includes **compatibility layers** for mysql2 and pg with full **ORM support**.
11
11
 
12
- **Version 2.0** introduces support for the new [RDS Data API for Aurora Serverless v2 and Aurora provisioned database instances](https://aws.amazon.com/about-aws/whats-new/2024/09/amazon-aurora-mysql-rds-data-api/), enhanced [Amazon Aurora PostgreSQL-Compatible Edition](https://aws.amazon.com/about-aws/whats-new/2023/12/amazon-aurora-postgresql-rds-data-api/) support, migration to AWS SDK v3, full TypeScript implementation, and comprehensive PostgreSQL data type coverage including **automatic array handling**.
12
+ **Version 2.1** adds mysql2 and pg compatibility layers, automatic retry logic for cluster wake-ups, and verified support for Drizzle ORM and Kysely query builder.
13
+
14
+ **Version 2.0** introduced support for the new [RDS Data API for Aurora Serverless v2 and Aurora provisioned database instances](https://aws.amazon.com/about-aws/whats-new/2024/09/amazon-aurora-mysql-rds-data-api/), enhanced [Amazon Aurora PostgreSQL-Compatible Edition](https://aws.amazon.com/about-aws/whats-new/2023/12/amazon-aurora-postgresql-rds-data-api/) support, migration to AWS SDK v3, full TypeScript implementation, and comprehensive PostgreSQL data type coverage including **automatic array handling**.
13
15
 
14
16
  For more information about the Aurora Serverless Data API, you can review the [official documentation](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/data-api.html) or read [Aurora Serverless Data API: An (updated) First Look](https://www.jeremydaly.com/aurora-serverless-data-api-a-first-look/) for some more insights on performance.
15
17
 
18
+ ## What's New in v2.1
19
+
20
+ - **Automatic Retry Logic**: Built-in retry handling for Aurora Serverless scale-to-zero wake-ups
21
+ - Smart detection of `DatabaseResumingException` with optimized retry delays
22
+ - Automatic handling of connection errors with exponential backoff
23
+ - Configurable and enabled by default
24
+ - **mysql2 Compatibility Layer**: Drop-in replacement for the `mysql2/promise` library
25
+ - Full support for connection pools and transactions
26
+ - Works seamlessly with ORMs like Drizzle and Kysely
27
+ - **pg Compatibility Layer**: Drop-in replacement for the `pg` (node-postgres) library
28
+ - Promise-based and callback-based APIs
29
+ - Compatible with ORMs and query builders
30
+ - **ORM Support**: Tested and verified with popular ORMs:
31
+ - **Drizzle ORM**: Full support for both MySQL and PostgreSQL
32
+ - **Kysely**: Query builder support for both engines
33
+
16
34
  ## What's New in v2.0
17
35
 
18
36
  - **AWS SDK v3**: Migrated from AWS SDK v2 to v3 for smaller bundle sizes and better tree-shaking
@@ -186,8 +204,49 @@ Below is a table containing all of the possible configuration options for the `d
186
204
  | database | `string` | _Optional_ default database to use with queries. Can be overridden when querying. | |
187
205
  | engine | `mysql` or `pg` | The type of database engine you're connecting to (MySQL or Postgres). | `pg` |
188
206
  | hydrateColumnNames | `boolean` | When `true`, results will be returned as objects with column names as keys. If `false`, results will be returned as an array of values. | `true` |
207
+ | namedPlaceholders | `boolean` | Enable named placeholders (`:name` syntax) for mysql2 compatibility layer. When `true`, parameters use object format. Only applies to mysql2 compat layer. | `false` |
189
208
  | options | `object` | An _optional_ configuration object that is passed directly into the RDSDataClient constructor. See [AWS SDK docs](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-rds-data/classes/rdsdataclient.html) for available options. | `{}` |
190
209
  | formatOptions | `object` | Formatting options to auto parse dates and coerce native JavaScript date objects to supported date formats. Valid keys are `deserializeDate` and `treatAsLocalDate`. Both accept boolean values. | `deserializeDate: true, treatAsLocalDate: false` |
210
+ | retryOptions | `object` | Configuration for automatic retry logic. Valid keys are `enabled` (boolean), `maxRetries` (number), and `retryableErrors` (string array). | `enabled: true, maxRetries: 9` |
211
+
212
+ ### Automatic Retry Logic
213
+
214
+ Version 2.1 includes built-in retry logic to handle Aurora Serverless scale-to-zero cluster wake-ups automatically. When your cluster is paused and needs to resume, the client will automatically retry your queries with optimized delays.
215
+
216
+ **Features:**
217
+ - **Smart Error Detection**: Automatically detects `DatabaseResumingException` and connection errors
218
+ - **Strategy-Based Retries**: Different retry strategies based on error type:
219
+ - DatabaseResumingException: Up to 10 attempts with progressive delays (0s, 2s, 5s, 10s, 15s, 20s, 25s, 30s, 35s, 40s)
220
+ - Connection errors: 3 quick retries with exponential backoff (0s, 2s, 4s)
221
+ - **Enabled by Default**: Works automatically without any configuration
222
+ - **Configurable**: Customize retry behavior per your needs
223
+
224
+ **Configuration:**
225
+
226
+ ```javascript
227
+ const data = dataApiClient({
228
+ secretArn: 'arn:...',
229
+ resourceArn: 'arn:...',
230
+ database: 'myDatabase',
231
+ retryOptions: {
232
+ enabled: true, // Enable/disable retries (default: true)
233
+ maxRetries: 9, // Maximum retry attempts (default: 9 for up to 40s total)
234
+ retryableErrors: [] // Additional error patterns to retry (optional)
235
+ }
236
+ })
237
+ ```
238
+
239
+ **Disable retries** (not recommended for scale-to-zero clusters):
240
+
241
+ ```javascript
242
+ const data = dataApiClient({
243
+ secretArn: 'arn:...',
244
+ resourceArn: 'arn:...',
245
+ retryOptions: { enabled: false }
246
+ })
247
+ ```
248
+
249
+ The retry logic works seamlessly across all operations: queries, transactions, batch operations, and compatibility layer methods.
191
250
 
192
251
  ### Connection Reuse
193
252
 
@@ -455,6 +514,293 @@ const data = dataApiClient({
455
514
  })
456
515
  ```
457
516
 
517
+ ## mysql2 and pg Compatibility Layers
518
+
519
+ Version 2.1 introduces compatibility layers that allow you to use the Data API Client as a drop-in replacement for popular database libraries. This makes it easy to migrate existing applications or use ORMs without modification.
520
+
521
+ ### mysql2 Compatibility
522
+
523
+ Use the Data API Client as a replacement for `mysql2/promise`:
524
+
525
+ ```javascript
526
+ import { createMySQLConnection, createMySQLPool } from 'data-api-client/compat/mysql2'
527
+
528
+ // Create a connection
529
+ const connection = createMySQLConnection({
530
+ resourceArn: 'arn:aws:rds:us-east-1:XXXXXXXXXXXX:cluster:my-cluster',
531
+ secretArn: 'arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:mySecret',
532
+ database: 'myDatabase'
533
+ })
534
+
535
+ // Use like mysql2/promise with positional placeholders
536
+ const [rows, fields] = await connection.query('SELECT * FROM users WHERE id = ?', [123])
537
+ await connection.execute('INSERT INTO users (name, email) VALUES (?, ?)', ['Alice', 'alice@example.com'])
538
+
539
+ // Note: connection.end() is optional - it's a no-op for Data API (no connection to close)
540
+
541
+ // Create a pool for connection pooling
542
+ const pool = createMySQLPool({
543
+ resourceArn: 'arn:...',
544
+ secretArn: 'arn:...',
545
+ database: 'myDatabase'
546
+ })
547
+
548
+ // Get connection from pool
549
+ pool.getConnection((err, connection) => {
550
+ if (err) throw err
551
+ connection.query('SELECT * FROM users', (err, results) => {
552
+ connection.release() // Optional - no-op for Data API
553
+ // Handle results
554
+ })
555
+ })
556
+
557
+ // Or use promises
558
+ const connection = await pool.getConnection()
559
+ const [rows] = await connection.query('SELECT * FROM users')
560
+ connection.release() // Optional - no-op for Data API
561
+ ```
562
+
563
+ #### Named Placeholders Support
564
+
565
+ The mysql2 compatibility layer supports **named placeholders** (`:name` syntax), matching the behavior of the native mysql2 library's `namedPlaceholders` option:
566
+
567
+ ```javascript
568
+ import { createMySQLConnection, createMySQLPool } from 'data-api-client/compat/mysql2'
569
+
570
+ // Create a connection with namedPlaceholders enabled
571
+ const connection = createMySQLConnection({
572
+ resourceArn: 'arn:aws:rds:us-east-1:XXXXXXXXXXXX:cluster:my-cluster',
573
+ secretArn: 'arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:mySecret',
574
+ database: 'myDatabase',
575
+ namedPlaceholders: true // Enable named placeholders
576
+ })
577
+
578
+ // Use named placeholders with object parameters
579
+ const [users] = await connection.query(
580
+ 'SELECT * FROM users WHERE name = :name AND age > :age',
581
+ { name: 'Alice', age: 25 }
582
+ )
583
+
584
+ // INSERT with named placeholders
585
+ await connection.query(
586
+ 'INSERT INTO users (name, email, active) VALUES (:name, :email, :active)',
587
+ { name: 'Bob', email: 'bob@example.com', active: true }
588
+ )
589
+
590
+ // UPDATE with named placeholders
591
+ await connection.query(
592
+ 'UPDATE users SET age = :newAge WHERE id = :id',
593
+ { id: 123, newAge: 30 }
594
+ )
595
+
596
+ // Named placeholders work with transactions
597
+ await connection.beginTransaction()
598
+ try {
599
+ await connection.query(
600
+ 'INSERT INTO orders (user_id, total) VALUES (:userId, :total)',
601
+ { userId: 123, total: 99.99 }
602
+ )
603
+ await connection.query(
604
+ 'UPDATE users SET last_order = NOW() WHERE id = :userId',
605
+ { userId: 123 }
606
+ )
607
+ await connection.commit()
608
+ } catch (err) {
609
+ await connection.rollback()
610
+ }
611
+
612
+ // Named placeholders also work with pools
613
+ const pool = createMySQLPool({
614
+ resourceArn: 'arn:...',
615
+ secretArn: 'arn:...',
616
+ database: 'myDatabase',
617
+ namedPlaceholders: true
618
+ })
619
+
620
+ const [results] = await pool.query(
621
+ 'SELECT * FROM products WHERE category = :category AND price < :maxPrice',
622
+ { category: 'electronics', maxPrice: 500 }
623
+ )
624
+ ```
625
+
626
+ **Named Placeholders Features:**
627
+ - Use `:paramName` syntax in SQL (colon followed by identifier)
628
+ - Pass parameters as objects: `{ paramName: value }`
629
+ - Same parameter can be referenced multiple times in the query
630
+ - Works with all query types (SELECT, INSERT, UPDATE, DELETE)
631
+ - Fully compatible with transactions, pools, and callbacks
632
+ - Backward compatible: positional `?` placeholders still work when `namedPlaceholders` is disabled (default)
633
+
634
+ **Query-Level namedPlaceholders:**
635
+
636
+ You can also enable or disable named placeholders on a per-query basis, which overrides the connection-level setting:
637
+
638
+ ```javascript
639
+ // Connection WITHOUT namedPlaceholders at config level
640
+ const connection = createMySQLConnection({
641
+ resourceArn: 'arn:...',
642
+ secretArn: 'arn:...',
643
+ database: 'myDatabase'
644
+ // namedPlaceholders NOT set (defaults to false)
645
+ })
646
+
647
+ // Enable namedPlaceholders for specific queries
648
+ const [rows] = await connection.query(
649
+ {
650
+ sql: 'SELECT * FROM users WHERE username = :username AND age > :minAge',
651
+ namedPlaceholders: true // Enable for this query only
652
+ },
653
+ { username: 'john_doe', minAge: 25 }
654
+ )
655
+
656
+ // Or explicitly disable for a specific query (when connection has it enabled)
657
+ const [rows2] = await connection.query(
658
+ {
659
+ sql: 'SELECT * FROM users WHERE id = ?',
660
+ namedPlaceholders: false // Use positional placeholders for this query
661
+ },
662
+ [123]
663
+ )
664
+ ```
665
+
666
+ This allows you to:
667
+ - Use named placeholders in specific queries without enabling it globally
668
+ - Mix named and positional placeholders in different queries
669
+ - Override connection-level settings when needed
670
+
671
+ ### pg Compatibility
672
+
673
+ Use the Data API Client as a replacement for `pg` (node-postgres):
674
+
675
+ ```javascript
676
+ import { createPgClient, createPgPool } from 'data-api-client/compat/pg'
677
+
678
+ // Create a client
679
+ const client = createPgClient({
680
+ resourceArn: 'arn:aws:rds:us-east-1:XXXXXXXXXXXX:cluster:my-cluster',
681
+ secretArn: 'arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:mySecret',
682
+ database: 'myDatabase'
683
+ })
684
+
685
+ // Note: client.connect() is optional - it's a no-op for Data API (no connection needed)
686
+ await client.connect() // Optional
687
+
688
+ // Use like pg
689
+ const result = await client.query('SELECT * FROM users WHERE id = $1', [123])
690
+ console.log(result.rows)
691
+
692
+ // With callback style
693
+ client.query('SELECT * FROM users', (err, result) => {
694
+ console.log(result.rows)
695
+ })
696
+
697
+ // Note: client.end() is optional - it's a no-op for Data API (no connection to close)
698
+ await client.end() // Optional
699
+
700
+ // Create a pool
701
+ const pool = createPgPool({
702
+ resourceArn: 'arn:...',
703
+ secretArn: 'arn:...',
704
+ database: 'myDatabase'
705
+ })
706
+
707
+ const result = await pool.query('SELECT * FROM users WHERE id = $1', [123])
708
+ ```
709
+
710
+ ### Using with ORMs
711
+
712
+ The compatibility layers work seamlessly with popular ORMs:
713
+
714
+ #### Drizzle ORM
715
+
716
+ **MySQL with Drizzle:**
717
+
718
+ ```typescript
719
+ import { drizzle } from 'drizzle-orm/mysql2'
720
+ import { createMySQLPool } from 'data-api-client/compat/mysql2'
721
+
722
+ const pool = createMySQLPool({
723
+ resourceArn: 'arn:...',
724
+ secretArn: 'arn:...',
725
+ database: 'myDatabase'
726
+ })
727
+
728
+ const db = drizzle(pool as any)
729
+
730
+ // Use Drizzle normally
731
+ const users = await db.select().from(usersTable).where(eq(usersTable.id, 123))
732
+ ```
733
+
734
+ **PostgreSQL with Drizzle:**
735
+
736
+ ```typescript
737
+ import { drizzle } from 'drizzle-orm/node-postgres'
738
+ import { createPgClient } from 'data-api-client/compat/pg'
739
+
740
+ const client = createPgClient({
741
+ resourceArn: 'arn:...',
742
+ secretArn: 'arn:...',
743
+ database: 'myDatabase'
744
+ })
745
+
746
+ // Note: client.connect() is optional (no-op for Data API)
747
+ await client.connect() // Optional - can be omitted
748
+ const db = drizzle(client as any)
749
+
750
+ // Use Drizzle normally
751
+ const users = await db.select().from(usersTable).where(eq(usersTable.id, 123))
752
+ ```
753
+
754
+ #### Kysely Query Builder
755
+
756
+ **MySQL with Kysely:**
757
+
758
+ ```typescript
759
+ import { Kysely, MysqlDialect } from 'kysely'
760
+ import { createMySQLPool } from 'data-api-client/compat/mysql2'
761
+
762
+ const pool = createMySQLPool({
763
+ resourceArn: 'arn:...',
764
+ secretArn: 'arn:...',
765
+ database: 'myDatabase'
766
+ })
767
+
768
+ const db = new Kysely<Database>({
769
+ dialect: new MysqlDialect({ pool: pool as any })
770
+ })
771
+
772
+ // Use Kysely normally
773
+ const users = await db.selectFrom('users').selectAll().where('id', '=', 123).execute()
774
+ ```
775
+
776
+ **PostgreSQL with Kysely:**
777
+
778
+ ```typescript
779
+ import { Kysely, PostgresDialect } from 'kysely'
780
+ import { createPgPool } from 'data-api-client/compat/pg'
781
+
782
+ const pool = createPgPool({
783
+ resourceArn: 'arn:...',
784
+ secretArn: 'arn:...',
785
+ database: 'myDatabase'
786
+ })
787
+
788
+ const db = new Kysely<Database>({
789
+ dialect: new PostgresDialect({ pool: pool as any })
790
+ })
791
+
792
+ // Use Kysely normally
793
+ const users = await db.selectFrom('users').selectAll().where('id', '=', 123).execute()
794
+ ```
795
+
796
+ **Benefits of Compatibility Layers:**
797
+ - **Zero code changes** when migrating from mysql2 or pg
798
+ - **Full ORM support** (Drizzle, Kysely)
799
+ - **Automatic retry logic** for cluster wake-ups
800
+ - **Connection pooling simulation** (getConnection, release)
801
+ - **Both Promise and callback APIs** supported
802
+ - **No-op connection management**: `connect()`, `end()`, and `release()` are optional since the Data API is connectionless - they're included only for backward compatibility with existing code
803
+
458
804
  ## PostgreSQL Array Support
459
805
 
460
806
  One of the most powerful features in v2.0 is automatic PostgreSQL array handling. While the Data API has limitations with array _parameters_, array _results_ are fully supported and automatically converted to native JavaScript arrays.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,aAAa,EAA6B,MAAM,SAAS,CAAA;AAyB5G,eAAO,MAAM,IAAI,GAAI,QAAQ,mBAAmB,KAAG,aAkHlD,CAAA"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,aAAa,EAA6B,MAAM,SAAS,CAAA;AA0B5G,eAAO,MAAM,IAAI,GAAI,QAAQ,mBAAmB,KAAG,aAoJlD,CAAA"}
package/dist/client.js CHANGED
@@ -5,6 +5,7 @@ const client_rds_data_1 = require("@aws-sdk/client-rds-data");
5
5
  const utils_1 = require("./utils");
6
6
  const query_1 = require("./query");
7
7
  const transaction_1 = require("./transaction");
8
+ const retry_1 = require("./retry");
8
9
  const init = (params) => {
9
10
  const options = typeof params.options === 'object'
10
11
  ? params.options
@@ -31,39 +32,48 @@ const init = (params) => {
31
32
  deserializeDate: typeof params.formatOptions === 'object' && params.formatOptions.deserializeDate === false ? false : true,
32
33
  treatAsLocalDate: typeof params.formatOptions === 'object' && params.formatOptions.treatAsLocalDate ? true : false
33
34
  },
35
+ retryOptions: {
36
+ enabled: typeof params.retryOptions === 'object' && params.retryOptions.enabled === false ? false : true,
37
+ maxRetries: typeof params.retryOptions === 'object' && typeof params.retryOptions.maxRetries === 'number'
38
+ ? params.retryOptions.maxRetries
39
+ : 9,
40
+ retryableErrors: typeof params.retryOptions === 'object' && Array.isArray(params.retryOptions.retryableErrors)
41
+ ? params.retryOptions.retryableErrors
42
+ : []
43
+ },
34
44
  RDS: params.client ? params.client : new client_rds_data_1.RDSDataClient(options)
35
45
  };
36
46
  return {
37
47
  query: (...x) => query_1.query.call(undefined, config, ...x),
38
48
  transaction: (x) => (0, transaction_1.transaction)(config, x),
39
- batchExecuteStatement: async (args) => config.RDS.send(new client_rds_data_1.BatchExecuteStatementCommand({
49
+ batchExecuteStatement: async (args) => (0, retry_1.withRetry)(() => config.RDS.send(new client_rds_data_1.BatchExecuteStatementCommand({
40
50
  ...args,
41
51
  resourceArn: args.resourceArn || config.resourceArn,
42
52
  secretArn: args.secretArn || config.secretArn,
43
53
  database: args.database || config.database
44
- })),
45
- beginTransaction: async (args) => config.RDS.send(new client_rds_data_1.BeginTransactionCommand({
54
+ })), config.retryOptions),
55
+ beginTransaction: async (args) => (0, retry_1.withRetry)(() => config.RDS.send(new client_rds_data_1.BeginTransactionCommand({
46
56
  ...(args || {}),
47
57
  resourceArn: (args === null || args === void 0 ? void 0 : args.resourceArn) || config.resourceArn,
48
58
  secretArn: (args === null || args === void 0 ? void 0 : args.secretArn) || config.secretArn,
49
59
  database: (args === null || args === void 0 ? void 0 : args.database) || config.database
50
- })),
51
- commitTransaction: async (args) => config.RDS.send(new client_rds_data_1.CommitTransactionCommand({
60
+ })), config.retryOptions),
61
+ commitTransaction: async (args) => (0, retry_1.withRetry)(() => config.RDS.send(new client_rds_data_1.CommitTransactionCommand({
52
62
  ...args,
53
63
  resourceArn: args.resourceArn || config.resourceArn,
54
64
  secretArn: args.secretArn || config.secretArn
55
- })),
56
- executeStatement: async (args) => config.RDS.send(new client_rds_data_1.ExecuteStatementCommand({
65
+ })), config.retryOptions),
66
+ executeStatement: async (args) => (0, retry_1.withRetry)(() => config.RDS.send(new client_rds_data_1.ExecuteStatementCommand({
57
67
  ...args,
58
68
  resourceArn: args.resourceArn || config.resourceArn,
59
69
  secretArn: args.secretArn || config.secretArn,
60
70
  database: args.database || config.database
61
- })),
62
- rollbackTransaction: async (args) => config.RDS.send(new client_rds_data_1.RollbackTransactionCommand({
71
+ })), config.retryOptions),
72
+ rollbackTransaction: async (args) => (0, retry_1.withRetry)(() => config.RDS.send(new client_rds_data_1.RollbackTransactionCommand({
63
73
  ...args,
64
74
  resourceArn: args.resourceArn || config.resourceArn,
65
75
  secretArn: args.secretArn || config.secretArn
66
- }))
76
+ })), config.retryOptions)
67
77
  };
68
78
  };
69
79
  exports.init = init;
@@ -0,0 +1,28 @@
1
+ export interface PostgresError extends Error {
2
+ code?: string;
3
+ severity?: string;
4
+ detail?: string;
5
+ hint?: string;
6
+ position?: string;
7
+ internalPosition?: string;
8
+ internalQuery?: string;
9
+ where?: string;
10
+ schema?: string;
11
+ table?: string;
12
+ column?: string;
13
+ dataType?: string;
14
+ constraint?: string;
15
+ file?: string;
16
+ line?: string;
17
+ routine?: string;
18
+ }
19
+ export interface MySQLError extends Error {
20
+ code?: string;
21
+ errno?: number;
22
+ sqlState?: string;
23
+ sqlMessage?: string;
24
+ sql?: string;
25
+ }
26
+ export declare function mapToPostgresError(error: any): PostgresError;
27
+ export declare function mapToMySQLError(error: any): MySQLError;
28
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/compat/errors.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAc,SAAQ,KAAK;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAKD,MAAM,WAAW,UAAW,SAAQ,KAAK;IACvC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AA6BD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,GAAG,aAAa,CAgE5D;AAKD,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,UAAU,CA+GtD"}
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapToPostgresError = mapToPostgresError;
4
+ exports.mapToMySQLError = mapToMySQLError;
5
+ function extractConstraintName(message) {
6
+ const match = message.match(/constraint "([^"]+)"/);
7
+ return match ? match[1] : undefined;
8
+ }
9
+ function extractTableName(message) {
10
+ const match = message.match(/table "([^"]+)"/);
11
+ return match ? match[1] : undefined;
12
+ }
13
+ function extractColumnName(message) {
14
+ const match = message.match(/column "([^"]+)"/);
15
+ return match ? match[1] : undefined;
16
+ }
17
+ function mapToPostgresError(error) {
18
+ var _a, _b;
19
+ const pgError = new Error(error.message);
20
+ pgError.name = 'error';
21
+ pgError.severity = 'ERROR';
22
+ const message = error.message || '';
23
+ if (message.includes('duplicate key') || message.includes('already exists')) {
24
+ pgError.code = '23505';
25
+ pgError.constraint = extractConstraintName(message);
26
+ pgError.detail = (_a = message.match(/Detail: (.+)/)) === null || _a === void 0 ? void 0 : _a[1];
27
+ }
28
+ else if (message.includes('violates foreign key constraint')) {
29
+ pgError.code = '23503';
30
+ pgError.constraint = extractConstraintName(message);
31
+ pgError.table = extractTableName(message);
32
+ }
33
+ else if (message.includes('violates not-null constraint')) {
34
+ pgError.code = '23502';
35
+ pgError.column = extractColumnName(message);
36
+ pgError.table = extractTableName(message);
37
+ }
38
+ else if (message.includes('violates check constraint')) {
39
+ pgError.code = '23514';
40
+ pgError.constraint = extractConstraintName(message);
41
+ }
42
+ else if (message.includes('syntax error')) {
43
+ pgError.code = '42601';
44
+ pgError.position = (_b = message.match(/at or near "(.+?)"/)) === null || _b === void 0 ? void 0 : _b[1];
45
+ }
46
+ else if (message.includes('column') && message.includes('does not exist')) {
47
+ pgError.code = '42703';
48
+ pgError.column = extractColumnName(message);
49
+ }
50
+ else if (message.includes('relation') && message.includes('does not exist')) {
51
+ pgError.code = '42P01';
52
+ pgError.table = extractTableName(message);
53
+ }
54
+ else if (message.includes('function') && message.includes('does not exist')) {
55
+ pgError.code = '42883';
56
+ }
57
+ else if (message.includes('invalid input syntax')) {
58
+ pgError.code = '22P02';
59
+ }
60
+ else if (message.includes('division by zero')) {
61
+ pgError.code = '22012';
62
+ }
63
+ else if (message.includes('value too long')) {
64
+ pgError.code = '22001';
65
+ }
66
+ else if (message.includes('permission denied')) {
67
+ pgError.code = '42501';
68
+ }
69
+ else if (message.includes('connection') || message.includes('timeout')) {
70
+ pgError.code = '08006';
71
+ }
72
+ else {
73
+ pgError.code = 'EUNKNOWN';
74
+ }
75
+ return pgError;
76
+ }
77
+ function mapToMySQLError(error) {
78
+ const mysqlError = new Error(error.message);
79
+ mysqlError.sqlMessage = error.message || '';
80
+ const message = error.message || '';
81
+ if (message.includes('Duplicate entry') || message.includes('duplicate key')) {
82
+ mysqlError.code = 'ER_DUP_ENTRY';
83
+ mysqlError.errno = 1062;
84
+ mysqlError.sqlState = '23000';
85
+ }
86
+ else if (message.includes('foreign key constraint fails')) {
87
+ if (message.includes('Cannot delete or update a parent row')) {
88
+ mysqlError.code = 'ER_ROW_IS_REFERENCED_2';
89
+ mysqlError.errno = 1451;
90
+ }
91
+ else {
92
+ mysqlError.code = 'ER_NO_REFERENCED_ROW_2';
93
+ mysqlError.errno = 1452;
94
+ }
95
+ mysqlError.sqlState = '23000';
96
+ }
97
+ else if (message.includes('cannot be null') || message.includes('NOT NULL')) {
98
+ mysqlError.code = 'ER_BAD_NULL_ERROR';
99
+ mysqlError.errno = 1048;
100
+ mysqlError.sqlState = '23000';
101
+ }
102
+ else if (message.includes("Table") && message.includes("doesn't exist")) {
103
+ mysqlError.code = 'ER_NO_SUCH_TABLE';
104
+ mysqlError.errno = 1146;
105
+ mysqlError.sqlState = '42S02';
106
+ }
107
+ else if (message.includes('Unknown column')) {
108
+ mysqlError.code = 'ER_BAD_FIELD_ERROR';
109
+ mysqlError.errno = 1054;
110
+ mysqlError.sqlState = '42S22';
111
+ }
112
+ else if (message.includes('syntax') || message.includes('SQL syntax')) {
113
+ mysqlError.code = 'ER_PARSE_ERROR';
114
+ mysqlError.errno = 1064;
115
+ mysqlError.sqlState = '42000';
116
+ }
117
+ else if (message.includes('Data too long') || message.includes('too long')) {
118
+ mysqlError.code = 'ER_DATA_TOO_LONG';
119
+ mysqlError.errno = 1406;
120
+ mysqlError.sqlState = '22001';
121
+ }
122
+ else if (message.includes('Division by 0')) {
123
+ mysqlError.code = 'ER_DIVISION_BY_ZERO';
124
+ mysqlError.errno = 1365;
125
+ mysqlError.sqlState = '22012';
126
+ }
127
+ else if (message.includes('Access denied')) {
128
+ mysqlError.code = 'ER_ACCESS_DENIED_ERROR';
129
+ mysqlError.errno = 1045;
130
+ mysqlError.sqlState = '28000';
131
+ }
132
+ else if (message.includes('Deadlock')) {
133
+ mysqlError.code = 'ER_LOCK_DEADLOCK';
134
+ mysqlError.errno = 1213;
135
+ mysqlError.sqlState = '40001';
136
+ }
137
+ else if (message.includes('Lock wait timeout')) {
138
+ mysqlError.code = 'ER_LOCK_WAIT_TIMEOUT';
139
+ mysqlError.errno = 1205;
140
+ mysqlError.sqlState = 'HY000';
141
+ }
142
+ else if (message.includes("Can't connect")) {
143
+ mysqlError.code = 'ER_CONNECTION_ERROR';
144
+ mysqlError.errno = 2003;
145
+ mysqlError.sqlState = 'HY000';
146
+ }
147
+ else if (message.includes('server has gone away')) {
148
+ mysqlError.code = 'ER_SERVER_GONE_ERROR';
149
+ mysqlError.errno = 2006;
150
+ mysqlError.sqlState = 'HY000';
151
+ }
152
+ else if (message.includes('Lost connection')) {
153
+ mysqlError.code = 'ER_SERVER_LOST';
154
+ mysqlError.errno = 2013;
155
+ mysqlError.sqlState = 'HY000';
156
+ }
157
+ else {
158
+ mysqlError.code = 'EUNKNOWN';
159
+ mysqlError.errno = 0;
160
+ mysqlError.sqlState = 'HY000';
161
+ }
162
+ return mysqlError;
163
+ }
@@ -0,0 +1,7 @@
1
+ export { createPgClient, createPgPool } from './pg';
2
+ export type { PgCompatClient, PgCompatPool, PgQueryResult } from './pg';
3
+ export { createMySQLConnection, createMySQLPool } from './mysql2';
4
+ export type { Connection, Pool, PoolConnection, MySQL2QueryResult } from './mysql2';
5
+ export { mapToPostgresError, mapToMySQLError } from './errors';
6
+ export type { PostgresError, MySQLError } from './errors';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compat/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACnD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AAEvE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AACjE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAEnF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC9D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}