@aws/aurora-dsql-postgresjs-connector 0.1.2 → 0.2.0
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 +58 -1
- package/dist/index.cjs +278 -18
- package/dist/index.d.cts +17 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +17 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +275 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -10
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ const sql = AuroraDSQLPostgres({
|
|
|
118
118
|
});
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
### Configuration Options
|
|
122
122
|
|
|
123
123
|
| Option | Type | Required | Description |
|
|
124
124
|
|-----------------------------|----------------------------------|----------|----------------------------------------------------------|
|
|
@@ -131,6 +131,63 @@ const sql = AuroraDSQLPostgres({
|
|
|
131
131
|
|
|
132
132
|
All standard [Postgres.js options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) are also supported.
|
|
133
133
|
|
|
134
|
+
## Websocket Connector
|
|
135
|
+
The websocket connector provides an alternative connection method to Aurora DSQL using WebSockets instead of standard TCP sockets. This approach is designed to work in use cases where TCP sockets are not provided by the platform. For Node.js server applications, use the standard TCP socket connector shown above.
|
|
136
|
+
|
|
137
|
+
### Use cases
|
|
138
|
+
- Browser Compatibility - Web browsers don't support raw TCP sockets (Node.js net module). WebSockets are a common way to establish persistent database connections from browser-based applications.
|
|
139
|
+
|
|
140
|
+
- Simplified Architecture - Eliminates the need for a separate backend API layer when building prototypes or internal tools, allowing direct browser-to-database connections.
|
|
141
|
+
|
|
142
|
+
### AWS Credentials
|
|
143
|
+
- DSQL access tokens are not secrets and can be accessed by the user in the browser
|
|
144
|
+
- Access tokens should be time limited, and use a role with access permissions that are scoped to data accessible by that user
|
|
145
|
+
|
|
146
|
+
### Basic Usage
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import type { AwsCredentialIdentity } from '@aws-sdk/types';
|
|
150
|
+
import { auroraDSQLWsPostgres, AuroraDSQLWsConfig } from '@aws/aurora-dsql-postgresjs-connector';
|
|
151
|
+
|
|
152
|
+
// For testing only, DO NOT USE in a production environment
|
|
153
|
+
const simulateSecureGetCredentialsAPI = (): Promise<AwsCredentialIdentity> => {
|
|
154
|
+
// Users must retrieve the AwsCredentialIdentity through a secure API
|
|
155
|
+
// DO NOT store the IAM accessKeyId and secretAccessKey inside the JavaScript source code
|
|
156
|
+
// for more details, refer to the example in the folder "example/src/alternative/websocket"
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const config: AuroraDSQLWsConfig<{}> = {
|
|
160
|
+
host: 'your-cluster.dsql.us-east-1.on.aws',
|
|
161
|
+
database: "postgres",
|
|
162
|
+
user: "admin",
|
|
163
|
+
customCredentialsProvider: simulateSecureGetCredentialsAPI,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const sql = auroraDSQLWsPostgres(config);
|
|
167
|
+
|
|
168
|
+
const result = await sql`SELECT version()`;
|
|
169
|
+
console.log(result);
|
|
170
|
+
await sql.end();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Configuration Options
|
|
174
|
+
|
|
175
|
+
| Option | Type | Required | Description |
|
|
176
|
+
|-----------------------------|----------------------------------|----------|----------------------------------------------------------|
|
|
177
|
+
| `host` | `string` | Yes | DSQL cluster hostname or cluster ID |
|
|
178
|
+
| `database` | `string?` | No | Database name |
|
|
179
|
+
| `username` | `string?` | No | Database username (uses admin if not provided) |
|
|
180
|
+
| `region` | `string?` | No | AWS region (auto-detected from hostname if not provided) |
|
|
181
|
+
| `customCredentialsProvider` | `AwsCredentialIdentityProvider?` | No | Custom AWS credentials provider |
|
|
182
|
+
| `tokenDurationSecs` | `number?` | No | Token expiration time in seconds |
|
|
183
|
+
| `connectionCheck` | `boolean?` | No | Perform a heart beat connectivity check with`Select 1` before every query (Default: false)
|
|
184
|
+
| `connectionId` | `string?` | No | An optional connection identifier to be used in the `onReservedConnectionClose` callback
|
|
185
|
+
| `onReservedConnectionClose` | `(connectionId?: string) => void?` | No | A callback that is executed upon unexpected closure of a reserved connection, such as a heartbeat failure. The connectionId is passed to the callback when available.
|
|
186
|
+
|
|
187
|
+
Other standard [Postgres.js options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) are also supported, except for `socket`, `port`, and `ssl`, which have default values.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
134
191
|
## Authentication
|
|
135
192
|
|
|
136
193
|
The connector automatically handles DSQL authentication by generating tokens using the DSQL client token generator. If the
|
package/dist/index.cjs
CHANGED
|
@@ -27,56 +27,295 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
//#endregion
|
|
28
28
|
let postgres = require("postgres");
|
|
29
29
|
postgres = __toESM(postgres);
|
|
30
|
-
let
|
|
30
|
+
let _aws_sdk_dsql_signer = require("@aws-sdk/dsql-signer");
|
|
31
|
+
let events = require("events");
|
|
32
|
+
let async_mutex = require("async-mutex");
|
|
31
33
|
|
|
34
|
+
//#region src/postgres-web-socket.ts
|
|
35
|
+
const HEARTBEAT_TIMEOUT = 5e3;
|
|
36
|
+
const MESSAGE_CODE_SIMPLE_QUERY = "Q".charCodeAt(0);
|
|
37
|
+
const MESSAGE_CODE_SYNC = "S".charCodeAt(0);
|
|
38
|
+
const MESSAGE_CODE_RFQ = "Z".charCodeAt(0);
|
|
39
|
+
var ReadyState = /* @__PURE__ */ function(ReadyState$1) {
|
|
40
|
+
ReadyState$1["Closed"] = "closed";
|
|
41
|
+
ReadyState$1["Querying"] = "querying";
|
|
42
|
+
ReadyState$1["Open"] = "open";
|
|
43
|
+
return ReadyState$1;
|
|
44
|
+
}(ReadyState || {});
|
|
45
|
+
var PostgresWs = class extends events.EventEmitter {
|
|
46
|
+
constructor(config) {
|
|
47
|
+
super();
|
|
48
|
+
this.mutex = new async_mutex.Mutex();
|
|
49
|
+
this.mutexRelease = null;
|
|
50
|
+
this.ws = null;
|
|
51
|
+
this.connected = false;
|
|
52
|
+
this.disableHeartBeat = false;
|
|
53
|
+
this.heartBeatTimeout = null;
|
|
54
|
+
this.pendingQueries = [];
|
|
55
|
+
this.readyState = ReadyState.Closed;
|
|
56
|
+
this.port = 443;
|
|
57
|
+
this.config = config;
|
|
58
|
+
this.host = config.host || "";
|
|
59
|
+
}
|
|
60
|
+
onReadyForQuery() {
|
|
61
|
+
if (this.pendingQueries.length > 0) {
|
|
62
|
+
if (this.pendingQueries[0].numOfQueries > 0) this.pendingQueries[0].numOfQueries--;
|
|
63
|
+
if (this.pendingQueries[0].numOfQueries <= 0) {
|
|
64
|
+
this.pendingQueries.shift();
|
|
65
|
+
this.readyState = ReadyState.Open;
|
|
66
|
+
this.disableHeartBeat = false;
|
|
67
|
+
this.releaseMutex();
|
|
68
|
+
this.processQueue();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
handleHeartBeatResponse(data) {
|
|
73
|
+
if (this.pendingQueries.length === 0 || !this.pendingQueries[0].isHeartBeat) return false;
|
|
74
|
+
const messageType = String.fromCharCode(data[0]);
|
|
75
|
+
if (messageType === "D") {
|
|
76
|
+
if (new TextDecoder().decode(data.slice(5)).includes("1")) {
|
|
77
|
+
clearTimeout(this.heartBeatTimeout);
|
|
78
|
+
this.heartBeatTimeout = null;
|
|
79
|
+
}
|
|
80
|
+
} else if (messageType === "Z") this.onReadyForQuery();
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
async connect() {
|
|
84
|
+
const url = `wss://${this.config.host}:${this.port}`;
|
|
85
|
+
this.ws = new WebSocket(url);
|
|
86
|
+
this.ws.binaryType = "arraybuffer";
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
if (this.ws != null) {
|
|
89
|
+
this.ws.onopen = () => {
|
|
90
|
+
this.connected = true;
|
|
91
|
+
this.readyState = ReadyState.Open;
|
|
92
|
+
resolve(this);
|
|
93
|
+
};
|
|
94
|
+
this.ws.onmessage = (event) => {
|
|
95
|
+
const data = new Uint8Array(event.data);
|
|
96
|
+
if (this.config.connectionCheck) {
|
|
97
|
+
if (this.handleHeartBeatResponse(data)) return;
|
|
98
|
+
if (data.length > 0 && data[0] === MESSAGE_CODE_RFQ) {
|
|
99
|
+
if (data.length > 5) if (String.fromCharCode(data[5]) === "E") {
|
|
100
|
+
this.disableHeartBeat = true;
|
|
101
|
+
this.cleanUpTxErrorState();
|
|
102
|
+
} else this.onReadyForQuery();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.emit("data", Buffer.from(data));
|
|
106
|
+
};
|
|
107
|
+
this.ws.onerror = (event) => {
|
|
108
|
+
const msg = event.message || "WebSocket error";
|
|
109
|
+
const error = /* @__PURE__ */ new Error(`${msg} ${this.host}:${this.port}`);
|
|
110
|
+
this.emit("error", error);
|
|
111
|
+
reject(error);
|
|
112
|
+
};
|
|
113
|
+
this.ws.onclose = () => {
|
|
114
|
+
if (this.heartBeatTimeout) {
|
|
115
|
+
clearTimeout(this.heartBeatTimeout);
|
|
116
|
+
this.heartBeatTimeout = null;
|
|
117
|
+
}
|
|
118
|
+
this.releaseMutex();
|
|
119
|
+
this.connected = false;
|
|
120
|
+
this.readyState = ReadyState.Closed;
|
|
121
|
+
this.ws = null;
|
|
122
|
+
this.emit("close");
|
|
123
|
+
if (this.config.onReservedConnectionClose) this.config.onReservedConnectionClose(this.config.connectionId);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
createQueryBuffer(sql) {
|
|
129
|
+
const sqlBytes = new TextEncoder().encode(sql + "\0");
|
|
130
|
+
const length = 4 + sqlBytes.length;
|
|
131
|
+
const buf = new Uint8Array(1 + length);
|
|
132
|
+
const view = new DataView(buf.buffer);
|
|
133
|
+
buf[0] = MESSAGE_CODE_SIMPLE_QUERY;
|
|
134
|
+
view.setInt32(1, length, false);
|
|
135
|
+
buf.set(sqlBytes, 5);
|
|
136
|
+
return buf;
|
|
137
|
+
}
|
|
138
|
+
write(data, encodingOrCallback, callback) {
|
|
139
|
+
let callbackFn;
|
|
140
|
+
if (typeof encodingOrCallback === "function") callbackFn = encodingOrCallback;
|
|
141
|
+
else callbackFn = callback;
|
|
142
|
+
let sendData;
|
|
143
|
+
if (typeof data === "string") sendData = new TextEncoder().encode(data);
|
|
144
|
+
else if (data instanceof Buffer) sendData = new Uint8Array(data);
|
|
145
|
+
else sendData = data;
|
|
146
|
+
if (!this.ws || !this.connected) {
|
|
147
|
+
if (callbackFn) callbackFn(/* @__PURE__ */ new Error("websocket is not connected"));
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
let offset = 0;
|
|
151
|
+
let queryCount = 0;
|
|
152
|
+
let hasSync = false;
|
|
153
|
+
while (offset < sendData.length) {
|
|
154
|
+
if (sendData[offset] === MESSAGE_CODE_SIMPLE_QUERY) queryCount++;
|
|
155
|
+
if (sendData[offset] === MESSAGE_CODE_SYNC) hasSync = true;
|
|
156
|
+
if (offset + 5 <= sendData.length) {
|
|
157
|
+
const length = new DataView(sendData.buffer, sendData.byteOffset + offset + 1, 4).getInt32(0, false);
|
|
158
|
+
offset += 1 + length;
|
|
159
|
+
} else break;
|
|
160
|
+
}
|
|
161
|
+
if (!this.config.connectionCheck || queryCount == 0 && hasSync === false) try {
|
|
162
|
+
this.ws.send(sendData);
|
|
163
|
+
if (callbackFn) callbackFn();
|
|
164
|
+
return true;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
if (callbackFn) callbackFn(err);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (queryCount > 0 && this.config.connectionCheck && this.heartBeatTimeout === null && !this.disableHeartBeat) {
|
|
170
|
+
const buf = this.createQueryBuffer("select 1;");
|
|
171
|
+
this.pendingQueries.push({
|
|
172
|
+
buffer: buf,
|
|
173
|
+
numOfQueries: 1,
|
|
174
|
+
isHeartBeat: true
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
this.pendingQueries.push({
|
|
178
|
+
buffer: sendData,
|
|
179
|
+
numOfQueries: queryCount,
|
|
180
|
+
isHeartBeat: false
|
|
181
|
+
});
|
|
182
|
+
this.processQueue();
|
|
183
|
+
if (callbackFn) callbackFn();
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
releaseMutex() {
|
|
187
|
+
if (this.mutexRelease) {
|
|
188
|
+
this.mutexRelease();
|
|
189
|
+
this.mutexRelease = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
cleanUpTxErrorState() {
|
|
193
|
+
if (this.pendingQueries.length > 0) this.pendingQueries.shift();
|
|
194
|
+
while (this.pendingQueries.length > 0 && this.pendingQueries[0].isHeartBeat) this.pendingQueries.shift();
|
|
195
|
+
this.readyState = ReadyState.Open;
|
|
196
|
+
this.releaseMutex();
|
|
197
|
+
if (this.pendingQueries.length > 0) this.processQueue();
|
|
198
|
+
}
|
|
199
|
+
async processQueue() {
|
|
200
|
+
if (this.mutexRelease || this.readyState !== ReadyState.Open || this.pendingQueries.length === 0) return;
|
|
201
|
+
this.mutexRelease = await this.mutex.acquire();
|
|
202
|
+
if (this.pendingQueries.length == 0) {
|
|
203
|
+
this.releaseMutex();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const data = this.pendingQueries[0];
|
|
207
|
+
if (data.buffer.length > 0 && data.buffer[0] === MESSAGE_CODE_SIMPLE_QUERY) this.readyState = ReadyState.Querying;
|
|
208
|
+
if (data.isHeartBeat) this.heartBeatTimeout = setTimeout(() => {
|
|
209
|
+
console.log("Heart beat timed out");
|
|
210
|
+
if (this.ws) this.ws.close(1e3, "Heart beat timeout");
|
|
211
|
+
}, HEARTBEAT_TIMEOUT);
|
|
212
|
+
if (this.ws) this.ws.send(data.buffer);
|
|
213
|
+
else throw Error("Websocket is not initialized");
|
|
214
|
+
}
|
|
215
|
+
end() {
|
|
216
|
+
if (this.ws && this.readyState !== ReadyState.Closed) this.ws.close();
|
|
217
|
+
}
|
|
218
|
+
destroy() {
|
|
219
|
+
if (this.ws) this.ws.close();
|
|
220
|
+
}
|
|
221
|
+
setKeepAlive() {
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
resume() {
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
pause() {
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
cork() {
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
uncork() {
|
|
234
|
+
return this;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function createPostgresWs(config) {
|
|
238
|
+
return async () => {
|
|
239
|
+
const socket = new PostgresWs(config);
|
|
240
|
+
await socket.connect();
|
|
241
|
+
return socket;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
//#endregion
|
|
32
246
|
//#region src/client.ts
|
|
247
|
+
const version = "0.2.0";
|
|
33
248
|
const ADMIN = "admin";
|
|
34
249
|
const DEFAULT_DATABASE = "postgres";
|
|
35
250
|
const DEFAULT_EXPIRY = 30;
|
|
36
251
|
const PRE_REGION_HOST_PATTERN = ".dsql.";
|
|
37
252
|
const POST_REGION_HOST_PATTERN = ".on.aws";
|
|
38
|
-
|
|
253
|
+
const APPLICATION_NAME = `aurora-dsql-nodejs-postgresjs/${version}`;
|
|
254
|
+
function parseConnectionParams(urlOrOptions, options) {
|
|
39
255
|
let opts;
|
|
40
256
|
let host;
|
|
41
257
|
let username;
|
|
42
258
|
let database;
|
|
43
|
-
let ssl;
|
|
44
259
|
if (typeof urlOrOptions === "string") {
|
|
45
260
|
let parsedOptions = parseConnectionString(urlOrOptions);
|
|
46
261
|
host = options?.hostname || options?.host || parsedOptions.host || process.env.PGHOST;
|
|
47
262
|
username = options?.username || options?.user || parsedOptions.username || process.env.PGUSERNAME || process.env.USER || ADMIN;
|
|
48
263
|
database = options?.database || options?.db || parsedOptions.database || process.env.PGDATABASE;
|
|
49
|
-
|
|
50
|
-
|
|
264
|
+
opts = {
|
|
265
|
+
...options,
|
|
266
|
+
ssl: options?.ssl || parsedOptions.ssl
|
|
267
|
+
};
|
|
51
268
|
} else {
|
|
52
269
|
host = urlOrOptions?.hostname || urlOrOptions?.host || process.env.PGHOST;
|
|
53
270
|
username = urlOrOptions?.username || urlOrOptions?.user || process.env.PGUSERNAME || process.env.USER || ADMIN;
|
|
54
271
|
database = urlOrOptions?.database || urlOrOptions?.db || process.env.PGDATABASE;
|
|
55
|
-
ssl = urlOrOptions?.ssl;
|
|
56
272
|
opts = { ...urlOrOptions };
|
|
57
273
|
}
|
|
58
274
|
if (Array.isArray(host)) throw new Error("Multi-host configurations are not supported for Aurora DSQL");
|
|
59
|
-
|
|
60
|
-
if (isClusterID(host))
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
275
|
+
if (!opts.region) opts.region = parseRegionFromHost(host);
|
|
276
|
+
if (isClusterID(host)) host = buildHostnameFromIdAndRegion(host, opts.region);
|
|
277
|
+
if (!database) opts.database = DEFAULT_DATABASE;
|
|
278
|
+
opts.host = host;
|
|
279
|
+
opts.username = username;
|
|
280
|
+
opts.connection = {
|
|
281
|
+
...opts.connection,
|
|
282
|
+
application_name: buildApplicationName(opts.connection?.application_name)
|
|
283
|
+
};
|
|
284
|
+
return { opts };
|
|
285
|
+
}
|
|
286
|
+
function setupDsqlSigner(opts) {
|
|
64
287
|
let signerConfig = {
|
|
65
|
-
hostname: host,
|
|
66
|
-
region: opts.region
|
|
288
|
+
hostname: opts.host,
|
|
289
|
+
region: opts.region,
|
|
67
290
|
expiresIn: opts.tokenDurationSecs ?? opts.connect_timeout ?? (process.env.PGCONNECT_TIMEOUT ? parseInt(process.env.PGCONNECT_TIMEOUT) : void 0) ?? DEFAULT_EXPIRY,
|
|
68
291
|
profile: opts.profile || process.env.AWS_PROFILE || "default"
|
|
69
292
|
};
|
|
70
293
|
if (opts.customCredentialsProvider) signerConfig.credentials = opts.customCredentialsProvider;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
294
|
+
return { signerConfig };
|
|
295
|
+
}
|
|
296
|
+
function auroraDSQLPostgres(urlOrOptions, options) {
|
|
297
|
+
let { opts } = parseConnectionParams(urlOrOptions, options);
|
|
298
|
+
const { signerConfig } = setupDsqlSigner(opts);
|
|
299
|
+
let signer = new _aws_sdk_dsql_signer.DsqlSigner(signerConfig);
|
|
300
|
+
if (!opts.ssl) opts.ssl = true;
|
|
74
301
|
const postgresOpts = {
|
|
75
302
|
...opts,
|
|
76
|
-
pass: () => getToken(signer, username)
|
|
303
|
+
pass: () => getToken(signer, opts.username)
|
|
77
304
|
};
|
|
78
305
|
return typeof urlOrOptions === "string" ? (0, postgres.default)(urlOrOptions, postgresOpts) : (0, postgres.default)(postgresOpts);
|
|
79
306
|
}
|
|
307
|
+
function auroraDSQLWsPostgres(urlOrOptions, options) {
|
|
308
|
+
let { opts } = parseConnectionParams(urlOrOptions, options);
|
|
309
|
+
const { signerConfig } = setupDsqlSigner(opts);
|
|
310
|
+
if (!opts.pass) opts.pass = async () => {
|
|
311
|
+
return await getToken(new _aws_sdk_dsql_signer.DsqlSigner(signerConfig), opts.username);
|
|
312
|
+
};
|
|
313
|
+
opts.socket = createPostgresWs(opts);
|
|
314
|
+
opts.ssl = false;
|
|
315
|
+
if (opts.connectionCheck == void 0) opts.connectionCheck = false;
|
|
316
|
+
opts.port = 443;
|
|
317
|
+
return typeof urlOrOptions === "string" ? (0, postgres.default)(urlOrOptions, opts) : (0, postgres.default)(opts);
|
|
318
|
+
}
|
|
80
319
|
function parseConnectionString(url) {
|
|
81
320
|
let decodedUrl = decodeURI(url);
|
|
82
321
|
const parsed = new URL(decodedUrl);
|
|
@@ -104,6 +343,27 @@ async function getToken(signer, username) {
|
|
|
104
343
|
if (username === ADMIN) return await signer.getDbConnectAdminAuthToken();
|
|
105
344
|
else return await signer.getDbConnectAuthToken();
|
|
106
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Build the application_name with optional ORM prefix.
|
|
348
|
+
*
|
|
349
|
+
* If ormPrefix is provided and non-empty after trimming, prepends it to
|
|
350
|
+
* the connector identifier. Otherwise, returns the connector's application_name.
|
|
351
|
+
*
|
|
352
|
+
* PostgreSQL limits application_name to 64 characters. After accounting for
|
|
353
|
+
* the connector identifier and separator, 27 characters are available for
|
|
354
|
+
* the ORM name.
|
|
355
|
+
*
|
|
356
|
+
* @param ormPrefix Optional ORM name to prepend (e.g., "prisma")
|
|
357
|
+
* @returns Formatted application_name string
|
|
358
|
+
*/
|
|
359
|
+
function buildApplicationName(ormPrefix) {
|
|
360
|
+
if (ormPrefix) {
|
|
361
|
+
const trimmed = ormPrefix.trim();
|
|
362
|
+
if (trimmed) return `${trimmed}:${APPLICATION_NAME}`;
|
|
363
|
+
}
|
|
364
|
+
return APPLICATION_NAME;
|
|
365
|
+
}
|
|
107
366
|
|
|
108
367
|
//#endregion
|
|
109
|
-
exports.auroraDSQLPostgres = auroraDSQLPostgres;
|
|
368
|
+
exports.auroraDSQLPostgres = auroraDSQLPostgres;
|
|
369
|
+
exports.auroraDSQLWsPostgres = auroraDSQLWsPostgres;
|
package/dist/index.d.cts
CHANGED
|
@@ -10,12 +10,28 @@ declare function auroraDSQLPostgres<T extends Record<string, postgres.PostgresTy
|
|
|
10
10
|
serialize: (value: infer R) => any;
|
|
11
11
|
parse: (raw: any) => infer R;
|
|
12
12
|
} ? R : never }>;
|
|
13
|
+
declare function auroraDSQLWsPostgres<T extends Record<string, postgres.PostgresType> = {}>(url: string, options?: AuroraDSQLWsConfig<T>): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : { [type in keyof T]: T[type] extends {
|
|
14
|
+
serialize: (value: infer R) => any;
|
|
15
|
+
parse: (raw: any) => infer R;
|
|
16
|
+
} ? R : never }>;
|
|
17
|
+
declare function auroraDSQLWsPostgres<T extends Record<string, postgres.PostgresType> = {}>(options: AuroraDSQLWsConfig<T>): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : { [type in keyof T]: T[type] extends {
|
|
18
|
+
serialize: (value: infer R) => any;
|
|
19
|
+
parse: (raw: any) => infer R;
|
|
20
|
+
} ? R : never }>;
|
|
13
21
|
interface AuroraDSQLConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, 'password' | 'pass'> {
|
|
14
22
|
region?: string;
|
|
15
23
|
profile?: string;
|
|
16
24
|
tokenDurationSecs?: number;
|
|
17
25
|
customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
18
26
|
}
|
|
27
|
+
interface AuroraDSQLWsConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, "socket" | "ssl" | "port"> {
|
|
28
|
+
region?: string;
|
|
29
|
+
tokenDurationSecs?: number;
|
|
30
|
+
customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
31
|
+
connectionCheck?: boolean;
|
|
32
|
+
connectionId?: string;
|
|
33
|
+
onReservedConnectionClose?: (connectionId?: string) => void;
|
|
34
|
+
}
|
|
19
35
|
//#endregion
|
|
20
|
-
export { type AuroraDSQLConfig, auroraDSQLPostgres };
|
|
36
|
+
export { type AuroraDSQLConfig, type AuroraDSQLWsConfig, auroraDSQLPostgres, auroraDSQLWsPostgres };
|
|
21
37
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -10,12 +10,28 @@ declare function auroraDSQLPostgres<T extends Record<string, postgres.PostgresTy
|
|
|
10
10
|
serialize: (value: infer R) => any;
|
|
11
11
|
parse: (raw: any) => infer R;
|
|
12
12
|
} ? R : never }>;
|
|
13
|
+
declare function auroraDSQLWsPostgres<T extends Record<string, postgres.PostgresType> = {}>(url: string, options?: AuroraDSQLWsConfig<T>): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : { [type in keyof T]: T[type] extends {
|
|
14
|
+
serialize: (value: infer R) => any;
|
|
15
|
+
parse: (raw: any) => infer R;
|
|
16
|
+
} ? R : never }>;
|
|
17
|
+
declare function auroraDSQLWsPostgres<T extends Record<string, postgres.PostgresType> = {}>(options: AuroraDSQLWsConfig<T>): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : { [type in keyof T]: T[type] extends {
|
|
18
|
+
serialize: (value: infer R) => any;
|
|
19
|
+
parse: (raw: any) => infer R;
|
|
20
|
+
} ? R : never }>;
|
|
13
21
|
interface AuroraDSQLConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, 'password' | 'pass'> {
|
|
14
22
|
region?: string;
|
|
15
23
|
profile?: string;
|
|
16
24
|
tokenDurationSecs?: number;
|
|
17
25
|
customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
18
26
|
}
|
|
27
|
+
interface AuroraDSQLWsConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, "socket" | "ssl" | "port"> {
|
|
28
|
+
region?: string;
|
|
29
|
+
tokenDurationSecs?: number;
|
|
30
|
+
customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
31
|
+
connectionCheck?: boolean;
|
|
32
|
+
connectionId?: string;
|
|
33
|
+
onReservedConnectionClose?: (connectionId?: string) => void;
|
|
34
|
+
}
|
|
19
35
|
//#endregion
|
|
20
|
-
export { type AuroraDSQLConfig, auroraDSQLPostgres };
|
|
36
|
+
export { type AuroraDSQLConfig, type AuroraDSQLWsConfig, auroraDSQLPostgres, auroraDSQLWsPostgres };
|
|
21
37
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,54 +1,293 @@
|
|
|
1
1
|
import postgres from "postgres";
|
|
2
2
|
import { DsqlSigner } from "@aws-sdk/dsql-signer";
|
|
3
|
+
import { EventEmitter } from "events";
|
|
4
|
+
import { Mutex } from "async-mutex";
|
|
3
5
|
|
|
6
|
+
//#region src/postgres-web-socket.ts
|
|
7
|
+
const HEARTBEAT_TIMEOUT = 5e3;
|
|
8
|
+
const MESSAGE_CODE_SIMPLE_QUERY = "Q".charCodeAt(0);
|
|
9
|
+
const MESSAGE_CODE_SYNC = "S".charCodeAt(0);
|
|
10
|
+
const MESSAGE_CODE_RFQ = "Z".charCodeAt(0);
|
|
11
|
+
var ReadyState = /* @__PURE__ */ function(ReadyState$1) {
|
|
12
|
+
ReadyState$1["Closed"] = "closed";
|
|
13
|
+
ReadyState$1["Querying"] = "querying";
|
|
14
|
+
ReadyState$1["Open"] = "open";
|
|
15
|
+
return ReadyState$1;
|
|
16
|
+
}(ReadyState || {});
|
|
17
|
+
var PostgresWs = class extends EventEmitter {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
super();
|
|
20
|
+
this.mutex = new Mutex();
|
|
21
|
+
this.mutexRelease = null;
|
|
22
|
+
this.ws = null;
|
|
23
|
+
this.connected = false;
|
|
24
|
+
this.disableHeartBeat = false;
|
|
25
|
+
this.heartBeatTimeout = null;
|
|
26
|
+
this.pendingQueries = [];
|
|
27
|
+
this.readyState = ReadyState.Closed;
|
|
28
|
+
this.port = 443;
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.host = config.host || "";
|
|
31
|
+
}
|
|
32
|
+
onReadyForQuery() {
|
|
33
|
+
if (this.pendingQueries.length > 0) {
|
|
34
|
+
if (this.pendingQueries[0].numOfQueries > 0) this.pendingQueries[0].numOfQueries--;
|
|
35
|
+
if (this.pendingQueries[0].numOfQueries <= 0) {
|
|
36
|
+
this.pendingQueries.shift();
|
|
37
|
+
this.readyState = ReadyState.Open;
|
|
38
|
+
this.disableHeartBeat = false;
|
|
39
|
+
this.releaseMutex();
|
|
40
|
+
this.processQueue();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
handleHeartBeatResponse(data) {
|
|
45
|
+
if (this.pendingQueries.length === 0 || !this.pendingQueries[0].isHeartBeat) return false;
|
|
46
|
+
const messageType = String.fromCharCode(data[0]);
|
|
47
|
+
if (messageType === "D") {
|
|
48
|
+
if (new TextDecoder().decode(data.slice(5)).includes("1")) {
|
|
49
|
+
clearTimeout(this.heartBeatTimeout);
|
|
50
|
+
this.heartBeatTimeout = null;
|
|
51
|
+
}
|
|
52
|
+
} else if (messageType === "Z") this.onReadyForQuery();
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
async connect() {
|
|
56
|
+
const url = `wss://${this.config.host}:${this.port}`;
|
|
57
|
+
this.ws = new WebSocket(url);
|
|
58
|
+
this.ws.binaryType = "arraybuffer";
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
if (this.ws != null) {
|
|
61
|
+
this.ws.onopen = () => {
|
|
62
|
+
this.connected = true;
|
|
63
|
+
this.readyState = ReadyState.Open;
|
|
64
|
+
resolve(this);
|
|
65
|
+
};
|
|
66
|
+
this.ws.onmessage = (event) => {
|
|
67
|
+
const data = new Uint8Array(event.data);
|
|
68
|
+
if (this.config.connectionCheck) {
|
|
69
|
+
if (this.handleHeartBeatResponse(data)) return;
|
|
70
|
+
if (data.length > 0 && data[0] === MESSAGE_CODE_RFQ) {
|
|
71
|
+
if (data.length > 5) if (String.fromCharCode(data[5]) === "E") {
|
|
72
|
+
this.disableHeartBeat = true;
|
|
73
|
+
this.cleanUpTxErrorState();
|
|
74
|
+
} else this.onReadyForQuery();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.emit("data", Buffer.from(data));
|
|
78
|
+
};
|
|
79
|
+
this.ws.onerror = (event) => {
|
|
80
|
+
const msg = event.message || "WebSocket error";
|
|
81
|
+
const error = /* @__PURE__ */ new Error(`${msg} ${this.host}:${this.port}`);
|
|
82
|
+
this.emit("error", error);
|
|
83
|
+
reject(error);
|
|
84
|
+
};
|
|
85
|
+
this.ws.onclose = () => {
|
|
86
|
+
if (this.heartBeatTimeout) {
|
|
87
|
+
clearTimeout(this.heartBeatTimeout);
|
|
88
|
+
this.heartBeatTimeout = null;
|
|
89
|
+
}
|
|
90
|
+
this.releaseMutex();
|
|
91
|
+
this.connected = false;
|
|
92
|
+
this.readyState = ReadyState.Closed;
|
|
93
|
+
this.ws = null;
|
|
94
|
+
this.emit("close");
|
|
95
|
+
if (this.config.onReservedConnectionClose) this.config.onReservedConnectionClose(this.config.connectionId);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
createQueryBuffer(sql) {
|
|
101
|
+
const sqlBytes = new TextEncoder().encode(sql + "\0");
|
|
102
|
+
const length = 4 + sqlBytes.length;
|
|
103
|
+
const buf = new Uint8Array(1 + length);
|
|
104
|
+
const view = new DataView(buf.buffer);
|
|
105
|
+
buf[0] = MESSAGE_CODE_SIMPLE_QUERY;
|
|
106
|
+
view.setInt32(1, length, false);
|
|
107
|
+
buf.set(sqlBytes, 5);
|
|
108
|
+
return buf;
|
|
109
|
+
}
|
|
110
|
+
write(data, encodingOrCallback, callback) {
|
|
111
|
+
let callbackFn;
|
|
112
|
+
if (typeof encodingOrCallback === "function") callbackFn = encodingOrCallback;
|
|
113
|
+
else callbackFn = callback;
|
|
114
|
+
let sendData;
|
|
115
|
+
if (typeof data === "string") sendData = new TextEncoder().encode(data);
|
|
116
|
+
else if (data instanceof Buffer) sendData = new Uint8Array(data);
|
|
117
|
+
else sendData = data;
|
|
118
|
+
if (!this.ws || !this.connected) {
|
|
119
|
+
if (callbackFn) callbackFn(/* @__PURE__ */ new Error("websocket is not connected"));
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
let offset = 0;
|
|
123
|
+
let queryCount = 0;
|
|
124
|
+
let hasSync = false;
|
|
125
|
+
while (offset < sendData.length) {
|
|
126
|
+
if (sendData[offset] === MESSAGE_CODE_SIMPLE_QUERY) queryCount++;
|
|
127
|
+
if (sendData[offset] === MESSAGE_CODE_SYNC) hasSync = true;
|
|
128
|
+
if (offset + 5 <= sendData.length) {
|
|
129
|
+
const length = new DataView(sendData.buffer, sendData.byteOffset + offset + 1, 4).getInt32(0, false);
|
|
130
|
+
offset += 1 + length;
|
|
131
|
+
} else break;
|
|
132
|
+
}
|
|
133
|
+
if (!this.config.connectionCheck || queryCount == 0 && hasSync === false) try {
|
|
134
|
+
this.ws.send(sendData);
|
|
135
|
+
if (callbackFn) callbackFn();
|
|
136
|
+
return true;
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (callbackFn) callbackFn(err);
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
if (queryCount > 0 && this.config.connectionCheck && this.heartBeatTimeout === null && !this.disableHeartBeat) {
|
|
142
|
+
const buf = this.createQueryBuffer("select 1;");
|
|
143
|
+
this.pendingQueries.push({
|
|
144
|
+
buffer: buf,
|
|
145
|
+
numOfQueries: 1,
|
|
146
|
+
isHeartBeat: true
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
this.pendingQueries.push({
|
|
150
|
+
buffer: sendData,
|
|
151
|
+
numOfQueries: queryCount,
|
|
152
|
+
isHeartBeat: false
|
|
153
|
+
});
|
|
154
|
+
this.processQueue();
|
|
155
|
+
if (callbackFn) callbackFn();
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
releaseMutex() {
|
|
159
|
+
if (this.mutexRelease) {
|
|
160
|
+
this.mutexRelease();
|
|
161
|
+
this.mutexRelease = null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
cleanUpTxErrorState() {
|
|
165
|
+
if (this.pendingQueries.length > 0) this.pendingQueries.shift();
|
|
166
|
+
while (this.pendingQueries.length > 0 && this.pendingQueries[0].isHeartBeat) this.pendingQueries.shift();
|
|
167
|
+
this.readyState = ReadyState.Open;
|
|
168
|
+
this.releaseMutex();
|
|
169
|
+
if (this.pendingQueries.length > 0) this.processQueue();
|
|
170
|
+
}
|
|
171
|
+
async processQueue() {
|
|
172
|
+
if (this.mutexRelease || this.readyState !== ReadyState.Open || this.pendingQueries.length === 0) return;
|
|
173
|
+
this.mutexRelease = await this.mutex.acquire();
|
|
174
|
+
if (this.pendingQueries.length == 0) {
|
|
175
|
+
this.releaseMutex();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const data = this.pendingQueries[0];
|
|
179
|
+
if (data.buffer.length > 0 && data.buffer[0] === MESSAGE_CODE_SIMPLE_QUERY) this.readyState = ReadyState.Querying;
|
|
180
|
+
if (data.isHeartBeat) this.heartBeatTimeout = setTimeout(() => {
|
|
181
|
+
console.log("Heart beat timed out");
|
|
182
|
+
if (this.ws) this.ws.close(1e3, "Heart beat timeout");
|
|
183
|
+
}, HEARTBEAT_TIMEOUT);
|
|
184
|
+
if (this.ws) this.ws.send(data.buffer);
|
|
185
|
+
else throw Error("Websocket is not initialized");
|
|
186
|
+
}
|
|
187
|
+
end() {
|
|
188
|
+
if (this.ws && this.readyState !== ReadyState.Closed) this.ws.close();
|
|
189
|
+
}
|
|
190
|
+
destroy() {
|
|
191
|
+
if (this.ws) this.ws.close();
|
|
192
|
+
}
|
|
193
|
+
setKeepAlive() {
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
resume() {
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
pause() {
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
cork() {
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
uncork() {
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
function createPostgresWs(config) {
|
|
210
|
+
return async () => {
|
|
211
|
+
const socket = new PostgresWs(config);
|
|
212
|
+
await socket.connect();
|
|
213
|
+
return socket;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
4
218
|
//#region src/client.ts
|
|
219
|
+
const version = "0.2.0";
|
|
5
220
|
const ADMIN = "admin";
|
|
6
221
|
const DEFAULT_DATABASE = "postgres";
|
|
7
222
|
const DEFAULT_EXPIRY = 30;
|
|
8
223
|
const PRE_REGION_HOST_PATTERN = ".dsql.";
|
|
9
224
|
const POST_REGION_HOST_PATTERN = ".on.aws";
|
|
10
|
-
|
|
225
|
+
const APPLICATION_NAME = `aurora-dsql-nodejs-postgresjs/${version}`;
|
|
226
|
+
function parseConnectionParams(urlOrOptions, options) {
|
|
11
227
|
let opts;
|
|
12
228
|
let host;
|
|
13
229
|
let username;
|
|
14
230
|
let database;
|
|
15
|
-
let ssl;
|
|
16
231
|
if (typeof urlOrOptions === "string") {
|
|
17
232
|
let parsedOptions = parseConnectionString(urlOrOptions);
|
|
18
233
|
host = options?.hostname || options?.host || parsedOptions.host || process.env.PGHOST;
|
|
19
234
|
username = options?.username || options?.user || parsedOptions.username || process.env.PGUSERNAME || process.env.USER || ADMIN;
|
|
20
235
|
database = options?.database || options?.db || parsedOptions.database || process.env.PGDATABASE;
|
|
21
|
-
|
|
22
|
-
|
|
236
|
+
opts = {
|
|
237
|
+
...options,
|
|
238
|
+
ssl: options?.ssl || parsedOptions.ssl
|
|
239
|
+
};
|
|
23
240
|
} else {
|
|
24
241
|
host = urlOrOptions?.hostname || urlOrOptions?.host || process.env.PGHOST;
|
|
25
242
|
username = urlOrOptions?.username || urlOrOptions?.user || process.env.PGUSERNAME || process.env.USER || ADMIN;
|
|
26
243
|
database = urlOrOptions?.database || urlOrOptions?.db || process.env.PGDATABASE;
|
|
27
|
-
ssl = urlOrOptions?.ssl;
|
|
28
244
|
opts = { ...urlOrOptions };
|
|
29
245
|
}
|
|
30
246
|
if (Array.isArray(host)) throw new Error("Multi-host configurations are not supported for Aurora DSQL");
|
|
31
|
-
|
|
32
|
-
if (isClusterID(host))
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
247
|
+
if (!opts.region) opts.region = parseRegionFromHost(host);
|
|
248
|
+
if (isClusterID(host)) host = buildHostnameFromIdAndRegion(host, opts.region);
|
|
249
|
+
if (!database) opts.database = DEFAULT_DATABASE;
|
|
250
|
+
opts.host = host;
|
|
251
|
+
opts.username = username;
|
|
252
|
+
opts.connection = {
|
|
253
|
+
...opts.connection,
|
|
254
|
+
application_name: buildApplicationName(opts.connection?.application_name)
|
|
255
|
+
};
|
|
256
|
+
return { opts };
|
|
257
|
+
}
|
|
258
|
+
function setupDsqlSigner(opts) {
|
|
36
259
|
let signerConfig = {
|
|
37
|
-
hostname: host,
|
|
38
|
-
region: opts.region
|
|
260
|
+
hostname: opts.host,
|
|
261
|
+
region: opts.region,
|
|
39
262
|
expiresIn: opts.tokenDurationSecs ?? opts.connect_timeout ?? (process.env.PGCONNECT_TIMEOUT ? parseInt(process.env.PGCONNECT_TIMEOUT) : void 0) ?? DEFAULT_EXPIRY,
|
|
40
263
|
profile: opts.profile || process.env.AWS_PROFILE || "default"
|
|
41
264
|
};
|
|
42
265
|
if (opts.customCredentialsProvider) signerConfig.credentials = opts.customCredentialsProvider;
|
|
266
|
+
return { signerConfig };
|
|
267
|
+
}
|
|
268
|
+
function auroraDSQLPostgres(urlOrOptions, options) {
|
|
269
|
+
let { opts } = parseConnectionParams(urlOrOptions, options);
|
|
270
|
+
const { signerConfig } = setupDsqlSigner(opts);
|
|
43
271
|
let signer = new DsqlSigner(signerConfig);
|
|
44
|
-
if (!
|
|
45
|
-
if (!ssl) opts.ssl = true;
|
|
272
|
+
if (!opts.ssl) opts.ssl = true;
|
|
46
273
|
const postgresOpts = {
|
|
47
274
|
...opts,
|
|
48
|
-
pass: () => getToken(signer, username)
|
|
275
|
+
pass: () => getToken(signer, opts.username)
|
|
49
276
|
};
|
|
50
277
|
return typeof urlOrOptions === "string" ? postgres(urlOrOptions, postgresOpts) : postgres(postgresOpts);
|
|
51
278
|
}
|
|
279
|
+
function auroraDSQLWsPostgres(urlOrOptions, options) {
|
|
280
|
+
let { opts } = parseConnectionParams(urlOrOptions, options);
|
|
281
|
+
const { signerConfig } = setupDsqlSigner(opts);
|
|
282
|
+
if (!opts.pass) opts.pass = async () => {
|
|
283
|
+
return await getToken(new DsqlSigner(signerConfig), opts.username);
|
|
284
|
+
};
|
|
285
|
+
opts.socket = createPostgresWs(opts);
|
|
286
|
+
opts.ssl = false;
|
|
287
|
+
if (opts.connectionCheck == void 0) opts.connectionCheck = false;
|
|
288
|
+
opts.port = 443;
|
|
289
|
+
return typeof urlOrOptions === "string" ? postgres(urlOrOptions, opts) : postgres(opts);
|
|
290
|
+
}
|
|
52
291
|
function parseConnectionString(url) {
|
|
53
292
|
let decodedUrl = decodeURI(url);
|
|
54
293
|
const parsed = new URL(decodedUrl);
|
|
@@ -76,7 +315,27 @@ async function getToken(signer, username) {
|
|
|
76
315
|
if (username === ADMIN) return await signer.getDbConnectAdminAuthToken();
|
|
77
316
|
else return await signer.getDbConnectAuthToken();
|
|
78
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* Build the application_name with optional ORM prefix.
|
|
320
|
+
*
|
|
321
|
+
* If ormPrefix is provided and non-empty after trimming, prepends it to
|
|
322
|
+
* the connector identifier. Otherwise, returns the connector's application_name.
|
|
323
|
+
*
|
|
324
|
+
* PostgreSQL limits application_name to 64 characters. After accounting for
|
|
325
|
+
* the connector identifier and separator, 27 characters are available for
|
|
326
|
+
* the ORM name.
|
|
327
|
+
*
|
|
328
|
+
* @param ormPrefix Optional ORM name to prepend (e.g., "prisma")
|
|
329
|
+
* @returns Formatted application_name string
|
|
330
|
+
*/
|
|
331
|
+
function buildApplicationName(ormPrefix) {
|
|
332
|
+
if (ormPrefix) {
|
|
333
|
+
const trimmed = ormPrefix.trim();
|
|
334
|
+
if (trimmed) return `${trimmed}:${APPLICATION_NAME}`;
|
|
335
|
+
}
|
|
336
|
+
return APPLICATION_NAME;
|
|
337
|
+
}
|
|
79
338
|
|
|
80
339
|
//#endregion
|
|
81
|
-
export { auroraDSQLPostgres };
|
|
340
|
+
export { auroraDSQLPostgres, auroraDSQLWsPostgres };
|
|
82
341
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["opts: AuroraDSQLConfig<T>","host: string","username: string","database: string | undefined","ssl: object | boolean | string | undefined","signerConfig: DsqlSignerConfig","postgresOpts: postgres.Options<T>"],"sources":["../src/client.ts"],"sourcesContent":["/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport postgres, {PostgresType} from \"postgres\";\nimport {AwsCredentialIdentity, AwsCredentialIdentityProvider} from \"@aws-sdk/types\";\nimport {DsqlSigner, DsqlSignerConfig} from \"@aws-sdk/dsql-signer\";\n\nconst ADMIN = \"admin\";\nconst DEFAULT_DATABASE = \"postgres\";\nconst DEFAULT_EXPIRY = 30; // Based on default Postgres.js connect_timeout\n// String components of a DSQL hostname, <Cluster ID>.dsql.<region>.on.aws\nconst PRE_REGION_HOST_PATTERN = \".dsql.\";\nconst POST_REGION_HOST_PATTERN = \".on.aws\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function auroraDSQLPostgres<T extends Record<string, postgres.PostgresType> = {}>(\n url: string,\n options?: AuroraDSQLConfig<T>\n): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any,\n parse: (raw: any) => infer R\n } ? R : never\n}>;\n\nexport function auroraDSQLPostgres<T extends Record<string, postgres.PostgresType> = {}>(\n options: AuroraDSQLConfig<T>\n): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any,\n parse: (raw: any) => infer R\n } ? R : never\n}>;\n\nexport function auroraDSQLPostgres<T extends Record<string, postgres.PostgresType> = {}>(\n urlOrOptions: string | AuroraDSQLConfig<T>,\n options?: AuroraDSQLConfig<T>\n): postgres.Sql<Record<string, postgres.PostgresType> extends T ? {} : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any,\n parse: (raw: any) => infer R\n } ? R : never\n}> {\n/* eslint-enable @typescript-eslint/no-explicit-any */\n let opts: AuroraDSQLConfig<T>;\n let host: string;\n let username: string;\n let database: string | undefined;\n let ssl: object | boolean | string | undefined;\n if (typeof urlOrOptions === 'string') {\n // Called with (url, options)\n let parsedOptions = parseConnectionString(urlOrOptions);\n host = options?.hostname || options?.host || parsedOptions.host || process.env.PGHOST!;\n username = options?.username || options?.user || parsedOptions.username! || process.env.PGUSERNAME || process.env.USER || ADMIN;\n database = options?.database || options?.db || parsedOptions.database || process.env.PGDATABASE;\n ssl = options?.ssl || parsedOptions.ssl;\n opts = {...options!};\n } else {\n // Called with (options) only\n host = urlOrOptions?.hostname || urlOrOptions?.host || process.env.PGHOST!;\n username = urlOrOptions?.username || urlOrOptions?.user || process.env.PGUSERNAME || process.env.USER || ADMIN;\n database = urlOrOptions?.database || urlOrOptions?.db || process.env.PGDATABASE;\n ssl = urlOrOptions?.ssl;\n opts = {...urlOrOptions!};\n }\n if (Array.isArray(host)) {\n throw new Error('Multi-host configurations are not supported for Aurora DSQL');\n }\n let region = opts.region || parseRegionFromHost(host);\n if (isClusterID(host)) {\n host = buildHostnameFromIdAndRegion(host, region);\n opts.host = host;\n }\n let signerConfig: DsqlSignerConfig = {\n hostname: host,\n region: opts.region || parseRegionFromHost(host),\n expiresIn: opts.tokenDurationSecs ?? opts.connect_timeout ?? (process.env.PGCONNECT_TIMEOUT ? parseInt(process.env.PGCONNECT_TIMEOUT) : undefined) ?? DEFAULT_EXPIRY,\n profile: opts.profile || process.env.AWS_PROFILE || \"default\",\n };\n if (opts.customCredentialsProvider) {\n signerConfig.credentials = opts.customCredentialsProvider;\n }\n let signer = new DsqlSigner(signerConfig);\n if (!database) opts.database = DEFAULT_DATABASE;\n if (!ssl) opts.ssl = true;\n const postgresOpts: postgres.Options<T> = {\n ...opts,\n pass: () => getToken(signer, username),\n };\n return typeof urlOrOptions === 'string' ? postgres(urlOrOptions, postgresOpts) : postgres(postgresOpts);\n}\n\nfunction parseConnectionString(url: string): {\n database?: string;\n host?: string;\n username?: string;\n ssl?: string;\n} {\n let decodedUrl = decodeURI(url);\n const parsed = new URL(decodedUrl);\n\n // Check for multi-host\n if (parsed.hostname?.includes(',')) {\n throw new Error('Multi-host connection strings are not supported for Aurora DSQL');\n }\n\n return {\n username: parsed.username,\n host: parsed.hostname,\n database: parsed.pathname?.slice(1),\n ssl: parsed.searchParams.get(\"ssl\") || parsed.searchParams.get(\"sslmode\") || undefined,\n };\n}\n\nfunction parseRegionFromHost(host: string): string | undefined {\n if (!host) {\n throw new Error(\"Hostname is required to parse region\");\n }\n\n const match = host.match(/^(?<instance>[^.]+)\\.(?<dns>dsql(?:-[^.]+)?)\\.(?<domain>(?<region>[a-zA-Z0-9-]+)\\.on\\.aws\\.?)$/i);\n if (match?.groups) {\n return match.groups.region;\n }\n throw new Error(`Unable to parse region from hostname: ${host}`);\n}\n\nfunction isClusterID(host: string) {\n return !host.includes(\".\");\n}\n\nfunction buildHostnameFromIdAndRegion(host: string, region: string | undefined) {\n return host + PRE_REGION_HOST_PATTERN + region + POST_REGION_HOST_PATTERN;\n}\n\nasync function getToken(signer: DsqlSigner, username: string): Promise<string> {\n if (username === ADMIN) {\n return await signer.getDbConnectAdminAuthToken();\n } else {\n return await signer.getDbConnectAuthToken();\n }\n}\n\nexport interface AuroraDSQLConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, 'password' | 'pass'> {\n\n region?: string;\n\n profile?: string;\n\n tokenDurationSecs?: number;\n\n customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n\n}"],"mappings":";;;;AAQA,MAAM,QAAQ;AACd,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AAEvB,MAAM,0BAA0B;AAChC,MAAM,2BAA2B;AAsBjC,SAAgB,mBACZ,cACA,SAMD;CAEC,IAAIA;CACJ,IAAIC;CACJ,IAAIC;CACJ,IAAIC;CACJ,IAAIC;AACJ,KAAI,OAAO,iBAAiB,UAAU;EAElC,IAAI,gBAAgB,sBAAsB,aAAa;AACvD,SAAO,SAAS,YAAY,SAAS,QAAQ,cAAc,QAAQ,QAAQ,IAAI;AAC/E,aAAW,SAAS,YAAY,SAAS,QAAQ,cAAc,YAAa,QAAQ,IAAI,cAAc,QAAQ,IAAI,QAAQ;AAC1H,aAAW,SAAS,YAAY,SAAS,MAAM,cAAc,YAAY,QAAQ,IAAI;AACrF,QAAM,SAAS,OAAO,cAAc;AACpC,SAAO,EAAC,GAAG,SAAS;QACjB;AAEH,SAAO,cAAc,YAAY,cAAc,QAAQ,QAAQ,IAAI;AACnE,aAAW,cAAc,YAAY,cAAc,QAAQ,QAAQ,IAAI,cAAc,QAAQ,IAAI,QAAQ;AACzG,aAAW,cAAc,YAAY,cAAc,MAAM,QAAQ,IAAI;AACrE,QAAM,cAAc;AACpB,SAAO,EAAC,GAAG,cAAc;;AAE7B,KAAI,MAAM,QAAQ,KAAK,CACnB,OAAM,IAAI,MAAM,8DAA8D;CAElF,IAAI,SAAS,KAAK,UAAU,oBAAoB,KAAK;AACrD,KAAI,YAAY,KAAK,EAAE;AACnB,SAAO,6BAA6B,MAAM,OAAO;AACjD,OAAK,OAAO;;CAEhB,IAAIC,eAAiC;EACjC,UAAU;EACV,QAAQ,KAAK,UAAU,oBAAoB,KAAK;EAChD,WAAW,KAAK,qBAAqB,KAAK,oBAAoB,QAAQ,IAAI,oBAAoB,SAAS,QAAQ,IAAI,kBAAkB,GAAG,WAAc;EACtJ,SAAS,KAAK,WAAW,QAAQ,IAAI,eAAe;EACvD;AACD,KAAI,KAAK,0BACL,cAAa,cAAc,KAAK;CAEpC,IAAI,SAAS,IAAI,WAAW,aAAa;AACzC,KAAI,CAAC,SAAU,MAAK,WAAW;AAC/B,KAAI,CAAC,IAAK,MAAK,MAAM;CACrB,MAAMC,eAAoC;EACtC,GAAG;EACH,YAAY,SAAS,QAAQ,SAAS;EACzC;AACD,QAAO,OAAO,iBAAiB,WAAW,SAAS,cAAc,aAAa,GAAG,SAAS,aAAa;;AAG3G,SAAS,sBAAsB,KAK7B;CACE,IAAI,aAAa,UAAU,IAAI;CAC/B,MAAM,SAAS,IAAI,IAAI,WAAW;AAGlC,KAAI,OAAO,UAAU,SAAS,IAAI,CAC9B,OAAM,IAAI,MAAM,kEAAkE;AAGtF,QAAO;EACH,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,UAAU,OAAO,UAAU,MAAM,EAAE;EACnC,KAAK,OAAO,aAAa,IAAI,MAAM,IAAI,OAAO,aAAa,IAAI,UAAU,IAAI;EAChF;;AAGL,SAAS,oBAAoB,MAAkC;AAC3D,KAAI,CAAC,KACD,OAAM,IAAI,MAAM,uCAAuC;CAG3D,MAAM,QAAQ,KAAK,MAAM,kGAAkG;AAC3H,KAAI,OAAO,OACP,QAAO,MAAM,OAAO;AAExB,OAAM,IAAI,MAAM,yCAAyC,OAAO;;AAGpE,SAAS,YAAY,MAAc;AAC/B,QAAO,CAAC,KAAK,SAAS,IAAI;;AAG9B,SAAS,6BAA6B,MAAc,QAA4B;AAC5E,QAAO,OAAO,0BAA0B,SAAS;;AAGrD,eAAe,SAAS,QAAoB,UAAmC;AAC3E,KAAI,aAAa,MACb,QAAO,MAAM,OAAO,4BAA4B;KAEhD,QAAO,MAAM,OAAO,uBAAuB"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["callbackFn: ((err?: Error) => void) | undefined","sendData: Uint8Array","opts: any","host: string","username: string","database: string | undefined","signerConfig: DsqlSignerConfig","postgresOpts: postgres.Options<T>"],"sources":["../src/postgres-web-socket.ts","../src/client.ts"],"sourcesContent":["/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { EventEmitter } from \"events\";\nimport { AuroraDSQLWsConfig } from \"./client\";\nimport { Mutex } from \"async-mutex\";\n\nconst HEARTBEAT_TIMEOUT = 5_000; // 5 seconds\n\nconst MESSAGE_CODE_SIMPLE_QUERY = \"Q\".charCodeAt(0);\nconst MESSAGE_CODE_SYNC = \"S\".charCodeAt(0);\nconst MESSAGE_CODE_RFQ = \"Z\".charCodeAt(0);\n\ninterface Query {\n buffer: Buffer | Uint8Array;\n numOfQueries: number;\n isHeartBeat: boolean;\n}\n\nenum ReadyState {\n Closed = 'closed',\n Querying = 'querying',\n Open = 'open'\n}\n\nexport class PostgresWs extends EventEmitter {\n private mutex = new Mutex();\n private mutexRelease: (() => void) | null = null;\n\n private config: AuroraDSQLWsConfig<{}>;\n private ws: WebSocket | null = null;\n private connected: boolean = false;\n\n private disableHeartBeat: boolean = false;\n private heartBeatTimeout: NodeJS.Timeout | null = null;\n private pendingQueries: Query[] = [];\n\n public readyState: ReadyState = ReadyState.Closed;\n\n\n // these parameters are referenced when an Error is thrown\n private host: string;\n private port: number = 443;\n\n constructor(config: AuroraDSQLWsConfig<{}>) {\n super();\n this.config = config;\n this.host = config.host || '';\n }\n\n\n onReadyForQuery() {\n if (this.pendingQueries.length > 0) {\n if (this.pendingQueries[0].numOfQueries > 0) {\n this.pendingQueries[0].numOfQueries--;\n }\n\n // remove the pendingQueries now that we received the reply only if the querycount is at 0\n if (this.pendingQueries[0].numOfQueries <= 0) {\n this.pendingQueries.shift();\n this.readyState = ReadyState.Open;\n this.disableHeartBeat = false;\n this.releaseMutex();\n this.processQueue();\n }\n }\n }\n\n handleHeartBeatResponse(data: Uint8Array): boolean {\n if (this.pendingQueries.length === 0 || !this.pendingQueries[0].isHeartBeat) return false;\n\n const messageType = String.fromCharCode(data[0]);\n\n if (messageType === \"D\") {\n const dataStr = new TextDecoder().decode(data.slice(5));\n // verify that it is from heartbeat\n if (dataStr.includes(\"1\")) {\n clearTimeout(this.heartBeatTimeout!);\n this.heartBeatTimeout = null;\n }\n } else if (messageType === \"Z\") {\n this.onReadyForQuery();\n }\n\n return true;\n }\n\n async connect(): Promise<this> {\n const url = `wss://${this.config.host}:${this.port}`;\n this.ws = new WebSocket(url);\n this.ws.binaryType = \"arraybuffer\";\n\n return new Promise((resolve, reject) => {\n if (this.ws != null) {\n this.ws.onopen = () => {\n this.connected = true;\n this.readyState = ReadyState.Open;\n resolve(this);\n };\n\n this.ws.onmessage = (event: MessageEvent) => {\n const data = new Uint8Array(event.data);\n\n if (this.config.connectionCheck) {\n if (this.handleHeartBeatResponse(data)) return;\n\n if (data.length > 0 && data[0] === MESSAGE_CODE_RFQ) {\n\n if (data.length > 5) {\n const status = String.fromCharCode(data[5]);\n\n if (status === \"E\") {\n // temporarily disable heart beat when the connection has a transaction error\n this.disableHeartBeat = true;\n this.cleanUpTxErrorState();\n } else {\n // I (IDLE) or T (Transaction) \n this.onReadyForQuery();\n }\n }\n }\n }\n\n this.emit(\"data\", Buffer.from(data));\n };\n\n this.ws.onerror = (event: Event) => {\n const msg = (event as ErrorEvent).message || \"WebSocket error\";\n const error = new Error(`${msg} ${this.host}:${this.port}`);\n this.emit(\"error\", error);\n reject(error);\n };\n\n this.ws.onclose = () => {\n if (this.heartBeatTimeout) {\n clearTimeout(this.heartBeatTimeout);\n this.heartBeatTimeout = null;\n }\n\n this.releaseMutex();\n\n this.connected = false;\n this.readyState = ReadyState.Closed;\n this.ws = null;\n this.emit(\"close\");\n\n if (this.config.onReservedConnectionClose) {\n this.config.onReservedConnectionClose(this.config.connectionId);\n }\n };\n }\n });\n }\n\n createQueryBuffer(sql: string): Uint8Array {\n const sqlBytes = new TextEncoder().encode(sql + \"\\0\");\n const length = 4 + sqlBytes.length;\n const buf = new Uint8Array(1 + length);\n const view = new DataView(buf.buffer);\n\n buf[0] = MESSAGE_CODE_SIMPLE_QUERY;\n view.setInt32(1, length, false);\n buf.set(sqlBytes, 5);\n\n return buf;\n }\n\n // future proofing in case postgres.js starts using the write function with 3 parameters\n // currently the library uses the write function with 2 parameters\n // callback is needed for copy related operations \n write(data: Buffer | Uint8Array, callback?: (err?: Error) => void): boolean;\n write(data: string, encoding?: string, callback?: (err?: Error) => void): boolean;\n\n write(\n data: Buffer | Uint8Array | string,\n encodingOrCallback?: string | ((err?: Error) => void),\n callback?: (err?: Error) => void\n ): boolean {\n\n let callbackFn: ((err?: Error) => void) | undefined;\n\n if (typeof encodingOrCallback === 'function') {\n callbackFn = encodingOrCallback;\n } else {\n callbackFn = callback;\n }\n\n let sendData: Uint8Array;\n if (typeof data === 'string') {\n const encoder = new TextEncoder();\n sendData = encoder.encode(data); // Always UTF-8\n } else if (data instanceof Buffer) {\n sendData = new Uint8Array(data);\n } else {\n sendData = data;\n }\n\n if (!this.ws || !this.connected) {\n if (callbackFn) {\n callbackFn(new Error(\"websocket is not connected\"));\n }\n return false;\n }\n\n let offset = 0;\n\n let queryCount = 0; // simple query \n let hasSync = false; // extended query protocol \n\n while (offset < sendData.length) {\n if (sendData[offset] === MESSAGE_CODE_SIMPLE_QUERY) {\n queryCount++;\n }\n\n if (sendData[offset] === MESSAGE_CODE_SYNC) {\n hasSync = true;\n }\n\n // Read message length to skip to next message\n if (offset + 5 <= sendData.length) {\n const length = new DataView(sendData.buffer, sendData.byteOffset + offset + 1, 4).getInt32(0, false);\n offset += 1 + length;\n } else {\n break;\n }\n }\n\n // directly send message to websocket when \n // * connection check (heart beat) is disabled \n // * Z (ReadyForQuery) message is not expected to be returned\n if (!this.config.connectionCheck || (queryCount == 0 && hasSync === false)) {\n try {\n this.ws.send(sendData);\n if (callbackFn) {\n callbackFn();\n }\n return true;\n } catch (err) {\n if (callbackFn) {\n callbackFn(err as Error);\n }\n return false;\n }\n }\n\n // if the buffer contains a query, send a heart beat first\n if (\n queryCount > 0 &&\n this.config.connectionCheck &&\n this.heartBeatTimeout === null && // make sure no existing heart beat time out is overwritten\n !this.disableHeartBeat\n ) {\n const buf = this.createQueryBuffer(\"select 1;\");\n this.pendingQueries.push({ buffer: buf, numOfQueries: 1, isHeartBeat: true });\n }\n this.pendingQueries.push({ buffer: sendData, numOfQueries: queryCount, isHeartBeat: false });\n this.processQueue();\n\n if (callbackFn) {\n callbackFn();\n }\n\n return true;\n }\n\n private releaseMutex() {\n if (this.mutexRelease) {\n this.mutexRelease();\n this.mutexRelease = null;\n }\n }\n\n // cleans up the error state during a transaction error \n private cleanUpTxErrorState() {\n // throw away last query as the result set has a transaction error and no more result set should return \n if (this.pendingQueries.length > 0) {\n this.pendingQueries.shift();\n }\n\n // since heart beat cant be performed during a transaction error, discard heart beat queries which are already in pending queries\n while (this.pendingQueries.length > 0 && this.pendingQueries[0].isHeartBeat) {\n this.pendingQueries.shift();\n }\n this.readyState = ReadyState.Open;\n this.releaseMutex();\n\n if (this.pendingQueries.length > 0) {\n this.processQueue();\n }\n }\n\n private async processQueue(): Promise<void> {\n if (\n this.mutexRelease ||\n this.readyState !== ReadyState.Open ||\n this.pendingQueries.length === 0\n ) {\n return;\n }\n\n this.mutexRelease = await this.mutex.acquire();\n\n // it is possible that the pendingQueries is now empty \n if (this.pendingQueries.length == 0) {\n this.releaseMutex();\n return;\n }\n\n const data = this.pendingQueries[0];\n\n if (data.buffer.length > 0 && data.buffer[0] === MESSAGE_CODE_SIMPLE_QUERY) {\n this.readyState = ReadyState.Querying;\n }\n\n if (data.isHeartBeat) {\n this.heartBeatTimeout = setTimeout(() => {\n console.log(\"Heart beat timed out\");\n if (this.ws) {\n this.ws.close(1000, \"Heart beat timeout\");\n }\n }, HEARTBEAT_TIMEOUT);\n }\n\n if (this.ws) {\n this.ws.send(data.buffer);\n } else {\n throw Error(\"Websocket is not initialized\");\n }\n }\n\n end(): void {\n if (this.ws && this.readyState !== ReadyState.Closed) {\n this.ws.close();\n }\n }\n\n destroy(): void {\n if (this.ws) {\n this.ws.close();\n }\n }\n\n // Following functions needs to be defined as connection.js \n // in postgres.js can call these functions but they will be no-op\n setKeepAlive(): this {\n return this;\n }\n\n resume(): this {\n return this;\n }\n\n pause(): this {\n return this;\n }\n\n cork(): this {\n return this;\n }\n\n uncork(): this {\n return this;\n }\n}\n\nexport function createPostgresWs(\n config: AuroraDSQLWsConfig<{}>\n): () => Promise<PostgresWs> {\n return async () => {\n\n const socket = new PostgresWs(config);\n await socket.connect();\n return socket;\n };\n}\n","/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport postgres, { PostgresType } from \"postgres\";\nimport {\n AwsCredentialIdentity,\n AwsCredentialIdentityProvider,\n} from \"@aws-sdk/types\";\nimport { DsqlSigner, DsqlSignerConfig } from \"@aws-sdk/dsql-signer\";\nimport { createPostgresWs } from \"./postgres-web-socket\";\n\n// Version is injected at build time via tsdown\ndeclare const __VERSION__: string;\nconst version = typeof __VERSION__ !== \"undefined\" ? __VERSION__ : \"0.0.0\";\n\nconst ADMIN = \"admin\";\nconst DEFAULT_DATABASE = \"postgres\";\nconst DEFAULT_EXPIRY = 30; // Based on default Postgres.js connect_timeout\n// String components of a DSQL hostname, <Cluster ID>.dsql.<region>.on.aws\nconst PRE_REGION_HOST_PATTERN = \".dsql.\";\nconst POST_REGION_HOST_PATTERN = \".on.aws\";\nconst APPLICATION_NAME = `aurora-dsql-nodejs-postgresjs/${version}`;\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction parseConnectionParams(\n urlOrOptions: string | any,\n options?: any\n): { opts: any; } {\n let opts: any;\n let host: string;\n let username: string;\n let database: string | undefined;\n\n if (typeof urlOrOptions === \"string\") {\n let parsedOptions = parseConnectionString(urlOrOptions);\n host = options?.hostname || options?.host || parsedOptions.host || process.env.PGHOST!;\n username = options?.username || options?.user || parsedOptions.username! ||\n process.env.PGUSERNAME || process.env.USER || ADMIN;\n database = options?.database || options?.db || parsedOptions.database || process.env.PGDATABASE;\n opts = {\n ...options,\n ssl: options?.ssl || parsedOptions.ssl\n };\n } else {\n host = urlOrOptions?.hostname || urlOrOptions?.host || process.env.PGHOST!;\n username = urlOrOptions?.username || urlOrOptions?.user ||\n process.env.PGUSERNAME || process.env.USER || ADMIN;\n database = urlOrOptions?.database || urlOrOptions?.db || process.env.PGDATABASE;\n opts = { ...urlOrOptions };\n }\n\n if (Array.isArray(host)) {\n throw new Error(\"Multi-host configurations are not supported for Aurora DSQL\");\n }\n\n if (!opts.region) {\n opts.region = parseRegionFromHost(host);\n }\n\n if (isClusterID(host)) {\n host = buildHostnameFromIdAndRegion(host, opts.region);\n }\n\n if (!database) {\n opts.database = DEFAULT_DATABASE;\n }\n\n opts.host = host;\n opts.username = username;\n opts.connection = {\n ...opts.connection,\n application_name: buildApplicationName(opts.connection?.application_name),\n };\n\n return { opts };\n}\n\nfunction setupDsqlSigner(opts: any): { signerConfig: DsqlSignerConfig; } {\n\n let signerConfig: DsqlSignerConfig = {\n hostname: opts.host,\n region: opts.region,\n expiresIn: opts.tokenDurationSecs ?? opts.connect_timeout ??\n (process.env.PGCONNECT_TIMEOUT ? parseInt(process.env.PGCONNECT_TIMEOUT) : undefined) ??\n DEFAULT_EXPIRY,\n profile: opts.profile || process.env.AWS_PROFILE || \"default\",\n };\n\n if (opts.customCredentialsProvider) {\n signerConfig.credentials = opts.customCredentialsProvider;\n }\n\n return { signerConfig };\n}\n\nexport function auroraDSQLPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n url: string,\n options?: AuroraDSQLConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n>;\n\nexport function auroraDSQLPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n options: AuroraDSQLConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n>;\n\nexport function auroraDSQLPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n urlOrOptions: string | AuroraDSQLConfig<T>,\n options?: AuroraDSQLConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n> {\n\n let { opts } = parseConnectionParams(urlOrOptions, options);\n const { signerConfig } = setupDsqlSigner(opts);\n let signer = new DsqlSigner(signerConfig);\n\n if (!opts.ssl) opts.ssl = true;\n const postgresOpts: postgres.Options<T> = {\n ...opts,\n pass: () => getToken(signer, opts.username),\n };\n return typeof urlOrOptions === \"string\"\n ? postgres(urlOrOptions, postgresOpts)\n : postgres(postgresOpts);\n}\n\nexport function auroraDSQLWsPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n url: string,\n options?: AuroraDSQLWsConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n>;\n\nexport function auroraDSQLWsPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n options: AuroraDSQLWsConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n>;\n\nexport function auroraDSQLWsPostgres<\n T extends Record<string, postgres.PostgresType> = {}\n>(\n urlOrOptions: string | AuroraDSQLWsConfig<T>,\n options?: AuroraDSQLWsConfig<T>\n): postgres.Sql<\n Record<string, postgres.PostgresType> extends T\n ? {}\n : {\n [type in keyof T]: T[type] extends {\n serialize: (value: infer R) => any;\n parse: (raw: any) => infer R;\n }\n ? R\n : never;\n }\n> {\n\n let { opts } = parseConnectionParams(urlOrOptions, options);\n const { signerConfig } = setupDsqlSigner(opts);\n\n if (!opts.pass) {\n opts.pass = async () => {\n let signer = new DsqlSigner(signerConfig);\n return await getToken(signer, opts.username);\n };\n }\n\n // swap out socket to use websocket\n opts.socket = createPostgresWs(opts);\n\n // ssl must be false otherwise postgres.js will try to use the net.socket \n opts.ssl = false;\n\n if (opts.connectionCheck == undefined) {\n // disable connection check by default \n // connection check sends a 'select 1' before every query\n opts.connectionCheck = false;\n }\n\n opts.port = 443;\n\n return typeof urlOrOptions === \"string\"\n ? postgres(urlOrOptions, opts)\n : postgres(opts);\n}\n\n\nfunction parseConnectionString(url: string): {\n database?: string;\n host?: string;\n username?: string;\n ssl?: string;\n} {\n let decodedUrl = decodeURI(url);\n const parsed = new URL(decodedUrl);\n\n // Check for multi-host\n if (parsed.hostname?.includes(\",\")) {\n throw new Error(\n \"Multi-host connection strings are not supported for Aurora DSQL\"\n );\n }\n\n return {\n username: parsed.username,\n host: parsed.hostname,\n database: parsed.pathname?.slice(1),\n ssl:\n parsed.searchParams.get(\"ssl\") ||\n parsed.searchParams.get(\"sslmode\") ||\n undefined,\n };\n}\n\nfunction parseRegionFromHost(host: string): string | undefined {\n if (!host) {\n throw new Error(\"Hostname is required to parse region\");\n }\n\n const match = host.match(\n /^(?<instance>[^.]+)\\.(?<dns>dsql(?:-[^.]+)?)\\.(?<domain>(?<region>[a-zA-Z0-9-]+)\\.on\\.aws\\.?)$/i\n );\n if (match?.groups) {\n return match.groups.region;\n }\n throw new Error(`Unable to parse region from hostname: ${host}`);\n}\n\nfunction isClusterID(host: string) {\n return !host.includes(\".\");\n}\n\nfunction buildHostnameFromIdAndRegion(\n host: string,\n region: string | undefined\n) {\n return host + PRE_REGION_HOST_PATTERN + region + POST_REGION_HOST_PATTERN;\n}\n\nasync function getToken(signer: DsqlSigner, username: string): Promise<string> {\n if (username === ADMIN) {\n return await signer.getDbConnectAdminAuthToken();\n } else {\n return await signer.getDbConnectAuthToken();\n }\n}\n\n/**\n * Build the application_name with optional ORM prefix.\n *\n * If ormPrefix is provided and non-empty after trimming, prepends it to\n * the connector identifier. Otherwise, returns the connector's application_name.\n *\n * PostgreSQL limits application_name to 64 characters. After accounting for\n * the connector identifier and separator, 27 characters are available for\n * the ORM name.\n *\n * @param ormPrefix Optional ORM name to prepend (e.g., \"prisma\")\n * @returns Formatted application_name string\n */\nfunction buildApplicationName(ormPrefix?: string): string {\n if (ormPrefix) {\n const trimmed = ormPrefix.trim();\n if (trimmed) {\n return `${trimmed}:${APPLICATION_NAME}`;\n }\n }\n return APPLICATION_NAME;\n}\n\nexport interface AuroraDSQLConfig<T extends Record<string, PostgresType<T>>> extends Omit<postgres.Options<T>, 'password' | 'pass'> {\n\n region?: string;\n\n profile?: string;\n\n tokenDurationSecs?: number;\n\n customCredentialsProvider?: AwsCredentialIdentity | AwsCredentialIdentityProvider;\n\n}\nexport interface AuroraDSQLWsConfig<T extends Record<string, PostgresType<T>>>\n extends Omit<postgres.Options<T>, \"socket\" | \"ssl\" | \"port\"> {\n region?: string;\n tokenDurationSecs?: number;\n customCredentialsProvider?:\n | AwsCredentialIdentity\n | AwsCredentialIdentityProvider;\n connectionCheck?: boolean;\n connectionId?: string;\n onReservedConnectionClose?: (connectionId?: string) => void;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws/aurora-dsql-postgresjs-connector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "An AWS Aurora DSQL connector with IAM authentication for Postgres.js",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/awslabs/aurora-dsql-nodejs-connector"
|
|
8
|
+
"url": "git+https://github.com/awslabs/aurora-dsql-nodejs-connector.git"
|
|
9
9
|
},
|
|
10
10
|
"homepage": "https://github.com/awslabs/aurora-dsql-nodejs-connector",
|
|
11
|
-
"keywords": [
|
|
11
|
+
"keywords": [
|
|
12
|
+
"aws",
|
|
13
|
+
"aurora",
|
|
14
|
+
"dsql",
|
|
15
|
+
"postgres",
|
|
16
|
+
"database",
|
|
17
|
+
"iam",
|
|
18
|
+
"authentication"
|
|
19
|
+
],
|
|
12
20
|
"publishConfig": {
|
|
13
21
|
"access": "public"
|
|
14
22
|
},
|
|
@@ -31,15 +39,17 @@
|
|
|
31
39
|
"scripts": {
|
|
32
40
|
"clean": "rm -rf dist/ node_modules/",
|
|
33
41
|
"build": "npm run build:dev",
|
|
34
|
-
"build:dev": "npm run
|
|
35
|
-
"build:prod": "npm run
|
|
36
|
-
"typecheck": "tsc --noEmit",
|
|
42
|
+
"build:dev": "npm run check:types && tsdown \"src/**/*.ts\" --sourcemap --no-publint --no-attw",
|
|
43
|
+
"build:prod": "npm run check:types && tsdown",
|
|
37
44
|
"test": "npm run test:unit && npm run test:integration",
|
|
38
45
|
"test:unit": "jest --testPathIgnorePatterns=integration",
|
|
39
46
|
"test:integration": "NODE_OPTIONS='--experimental-vm-modules' jest --testPathPatterns=integration",
|
|
40
47
|
"test:watch": "jest --watch",
|
|
41
48
|
"lint": "eslint src/**/*.ts test/**/*.ts",
|
|
42
|
-
"lint:fix": "eslint src/**/*.ts test/**/*.ts --fix"
|
|
49
|
+
"lint:fix": "eslint src/**/*.ts test/**/*.ts --fix",
|
|
50
|
+
"check": "npm run check:types && npm run check:deps && npm run lint",
|
|
51
|
+
"check:types": "tsc --noEmit",
|
|
52
|
+
"check:deps": "depcheck --ignores='@types/*,ts-jest,tsx'"
|
|
43
53
|
},
|
|
44
54
|
"files": [
|
|
45
55
|
"dist/",
|
|
@@ -47,23 +57,30 @@
|
|
|
47
57
|
],
|
|
48
58
|
"dependencies": {
|
|
49
59
|
"@aws-sdk/credential-providers": "^3.901.0",
|
|
50
|
-
"@aws-sdk/dsql-signer": "^3.940.0"
|
|
60
|
+
"@aws-sdk/dsql-signer": "^3.940.0",
|
|
61
|
+
"@aws-sdk/types": "^3.901.0",
|
|
62
|
+
"async-mutex": "^0.5.0"
|
|
51
63
|
},
|
|
52
64
|
"peerDependencies": {
|
|
53
65
|
"postgres": "^3.4.7"
|
|
54
66
|
},
|
|
55
67
|
"devDependencies": {
|
|
68
|
+
"@arethetypeswrong/core": "^0.18.2",
|
|
56
69
|
"@eslint/js": "^9.37.0",
|
|
57
|
-
"@
|
|
70
|
+
"@jest/globals": "^30.2.0",
|
|
71
|
+
"@types/jest": "^30.0.0",
|
|
58
72
|
"@types/node": "^25.0.0",
|
|
59
73
|
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
60
74
|
"@typescript-eslint/parser": "^8.48.0",
|
|
75
|
+
"depcheck": "^1.4.7",
|
|
61
76
|
"eslint": "^9.39.1",
|
|
62
77
|
"eslint-plugin-header": "^3.1.1",
|
|
78
|
+
"eslint-plugin-import-x": "^4.16.1",
|
|
63
79
|
"jest": "^30.2.0",
|
|
64
80
|
"postgres": "^3.4.7",
|
|
81
|
+
"publint": "^0.3.16",
|
|
65
82
|
"ts-jest": "^29.4.5",
|
|
66
|
-
"tsdown": "^0.
|
|
83
|
+
"tsdown": "^0.18.0",
|
|
67
84
|
"typescript": "^5.9.3"
|
|
68
85
|
}
|
|
69
86
|
}
|