@m2k-5f/pgtx 2.7.2 → 2.8.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 +65 -37
- package/dist/batch.d.ts +4 -1
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +14 -7
- package/dist/clauses/fragment.clause.js +1 -1
- package/dist/connection.d.ts +17 -16
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +231 -175
- package/dist/error.d.ts +6 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +26 -7
- package/dist/pool.d.ts +1 -1
- package/dist/pool.d.ts.map +1 -1
- package/dist/pool.js +5 -3
- package/dist/protocol/connection-request-writer.d.ts +107 -11
- package/dist/protocol/connection-request-writer.d.ts.map +1 -1
- package/dist/protocol/connection-request-writer.js +442 -48
- package/dist/protocol/connection-response-reader.d.ts +75 -30
- package/dist/protocol/connection-response-reader.d.ts.map +1 -1
- package/dist/protocol/connection-response-reader.js +285 -289
- package/dist/protocol/constants.d.ts +37 -30
- package/dist/protocol/constants.d.ts.map +1 -1
- package/dist/protocol/constants.js +43 -37
- package/dist/protocol/socket-authorization.d.ts.map +1 -1
- package/dist/protocol/socket-authorization.js +3 -3
- package/dist/protocol/socket-connector.d.ts +4 -4
- package/dist/protocol/socket-connector.d.ts.map +1 -1
- package/dist/protocol/socket-connector.js +2 -2
- package/dist/protocol/types.d.ts +14 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +264 -0
- package/dist/query.d.ts +34 -24
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +45 -30
- package/dist/queue.d.ts +1 -0
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +1 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/template-compiler.d.ts +5 -2
- package/dist/utils/template-compiler.d.ts.map +1 -1
- package/dist/utils/template-compiler.js +4 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +73 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
[](https://github.com/M2K-5F/pgtx/actions/workflows/tests.yaml)
|
|
4
4
|
[](https://www.npmjs.com/package/@m2k-5f/pgtx)
|
|
5
5
|
|
|
6
|
-
A PostgreSQL driver for Node.js that
|
|
7
|
-
|
|
8
|
-
It exists because `pg` leaves throughput on the table and `postgres.js`, while fast, still isn't fast enough once you actually saturate a connection pool. Pgtx is built around one idea: batch what can be batched, write to the socket once, and get out of the way.
|
|
6
|
+
A PostgreSQL driver for Node.js with an API that doesn't require a manual to use. Built for regular applications, not for people who need four different flavors of the same stream implementation or a config object with forty optional fields you'll never touch.
|
|
9
7
|
|
|
10
8
|
```bash
|
|
11
9
|
npm install @m2k-5f/pgtx
|
|
@@ -25,8 +23,9 @@ const pool = new Pool({
|
|
|
25
23
|
|
|
26
24
|
const [user] = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
|
|
27
25
|
|
|
26
|
+
// returns rows → query, doesn't → execute
|
|
28
27
|
await pool.execute`
|
|
29
|
-
INSERT INTO users ${sql.insert([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }])}
|
|
28
|
+
INSERT INTO users ${sql.insert<User>([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }])}
|
|
30
29
|
`
|
|
31
30
|
|
|
32
31
|
await pool.begin(async tx => {
|
|
@@ -43,32 +42,74 @@ Benchmarks run on GitHub Actions (Ubuntu, 2 vCPUs), reproducible, sources in thi
|
|
|
43
42
|
|
|
44
43
|
**HTTP throughput against `postgres.js` and Bun's own native `Bun.sql` driver, on Bun:**
|
|
45
44
|
|
|
46
|
-
| Connections | Pgtx | Postgres.js | Bun.sql |
|
|
47
|
-
|
|
48
|
-
| 50 | **
|
|
49
|
-
| 200 | **
|
|
50
|
-
| 500 | **
|
|
45
|
+
| Connections | Pgtx | Postgres.js | Bun.sql |
|
|
46
|
+
|---:|---:|---:|---:|
|
|
47
|
+
| 50 | **21,093 req/s** | 8,967 req/s | 10,170 req/s |
|
|
48
|
+
| 200 | **22,731 req/s** | 9,861 req/s | 11,725 req/s |
|
|
49
|
+
| 500 | **22,673 req/s** | 8,289 req/s | 11,005 req/s |
|
|
51
50
|
|
|
52
|
-
Bun.sql is Bun's own built-in driver, written in native code and generally treated as the speed baseline in that ecosystem. Pgtx stays
|
|
51
|
+
Bun.sql is Bun's own built-in driver, written in native code and generally treated as the speed baseline in that ecosystem. Pgtx stays ahead of it at every concurrency level tested — the gap doesn't come from JS-vs-native, it comes from the protocol implementation.
|
|
53
52
|
|
|
54
|
-
**How:** everything you fire concurrently against the same connection gets folded into one pipelined write — Parse/Bind/Execute for every query in the batch goes out in a single `socket.write()`, and results get demuxed as they come back, in order, without buffering rows you haven't asked for yet. Prepared statements are cached and deduplicated automatically, row descriptions are cached alongside them
|
|
53
|
+
**How:** everything you fire concurrently against the same connection gets folded into one pipelined write — Parse/Bind/Execute for every query in the batch goes out in a single `socket.write()`, and results get demuxed as they come back, in order, without buffering rows you haven't asked for yet. Prepared statements are cached and deduplicated automatically, row descriptions are cached alongside them. None of this requires you to change how you write queries.
|
|
54
|
+
|
|
55
55
|
|
|
56
56
|
## The parts worth knowing about
|
|
57
57
|
|
|
58
|
-
### Errors you can pattern-match on
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
### Error handling
|
|
60
|
+
|
|
61
|
+
Every query returns a `Future<T[], PostgresError>` instead of a bare `Promise`. `await` works as usual, while `Future` also provides typed error handling and recovery without a `try/catch` pyramid.
|
|
62
|
+
|
|
63
|
+
Queries in the same pipeline are isolated from each other. Each query gets its own `Sync`, so an error in one query does not affect other queries that were sent in the same batch.
|
|
61
64
|
|
|
62
65
|
```typescript
|
|
63
|
-
const
|
|
64
|
-
.
|
|
65
|
-
.
|
|
66
|
+
const [a, b] = await Promise.allSettled([
|
|
67
|
+
pool.execute`UPDATE accounts SET balance = balance + 100 WHERE id = ${1}`,
|
|
68
|
+
pool.execute`INSERT INTO accounts (id) VALUES (${1})` // duplicate key, fails
|
|
69
|
+
])
|
|
70
|
+
|
|
71
|
+
// a: fulfilled — the balance update is committed
|
|
72
|
+
// b: rejected — the duplicate key error only affects this query
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Errors can be matched and recovered directly on the `Future`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const users = await pool.query<User>`
|
|
79
|
+
SELECT * FROM users WHERE id = ${1}
|
|
80
|
+
`
|
|
81
|
+
.recoverIf(err => err.code === '42P01', []) // undefined_table → []
|
|
82
|
+
.recoverIf(err => err.code === '23505', []) // unique_violation → []
|
|
66
83
|
.tapErr(err => logger.error(err))
|
|
67
84
|
```
|
|
68
85
|
|
|
86
|
+
This isolation is per statement, not atomicity across multiple statements. If several queries must succeed or fail together, use `begin()` and `savepoint()`.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### PostgreSQL Type Support
|
|
90
|
+
|
|
91
|
+
The driver provides **100% support for PostgreSQL data types using the full binary protocol**. All supported types are encoded and decoded directly in PostgreSQL's binary wire format, without falling back to text-based parsing.
|
|
92
|
+
|
|
93
|
+
For a complete list of supported PostgreSQL types, their JavaScript input/output types, and string formats, see the [PostgreSQL Data Types](./DATATYPES.md) reference.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Transactions and savepoints
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
await pool.begin(async tx => {
|
|
100
|
+
await tx.execute`INSERT INTO orders (user_id) VALUES (${userId})`
|
|
101
|
+
|
|
102
|
+
await tx.savepoint('reserve_stock', async stx => {
|
|
103
|
+
await stx.execute`UPDATE stock SET count = count - 1 WHERE product_id = ${productId}`
|
|
104
|
+
if (outOfStock) throw new Error('out of stock') // only the savepoint rolls back
|
|
105
|
+
}).tapErr(console.log)
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
|
|
69
110
|
### Pipelining is automatic, not opt-in
|
|
70
111
|
|
|
71
|
-
`Bind`/`.bind` from fluent-future group independent queries into the same pipeline batch for you:
|
|
112
|
+
`Bind`/`.bind` from [fluent-future](https://www.npmjs.com/package/fluent-future) group independent queries into the same pipeline batch for you:
|
|
72
113
|
|
|
73
114
|
```typescript
|
|
74
115
|
// 5 queries, 2 round-trips
|
|
@@ -94,29 +135,16 @@ for await (const log of pool.stream<Log>`SELECT * FROM application_logs WHERE le
|
|
|
94
135
|
}
|
|
95
136
|
```
|
|
96
137
|
|
|
138
|
+
|
|
97
139
|
It's a real Web Streams object, so it drops straight into an HTTP response body:
|
|
98
140
|
|
|
99
141
|
```typescript
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return new Response(stream, { headers: { "Content-Type": "application/json" } })
|
|
104
|
-
}
|
|
142
|
+
fetch(req) {
|
|
143
|
+
const stream = pool.stream`SELECT id, email FROM giant_user_table`
|
|
144
|
+
return new Response(stream, { headers: { "Content-Type": "application/json" } })
|
|
105
145
|
}
|
|
106
146
|
```
|
|
107
147
|
|
|
108
|
-
### Transactions and savepoints
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
await pool.begin(async tx => {
|
|
112
|
-
await tx.execute`INSERT INTO orders (user_id) VALUES (${userId})`
|
|
113
|
-
|
|
114
|
-
await tx.savepoint('reserve_stock', async stx => {
|
|
115
|
-
await stx.execute`UPDATE stock SET count = count - 1 WHERE product_id = ${productId}`
|
|
116
|
-
if (outOfStock) throw new Error('out of stock') // only the savepoint rolls back
|
|
117
|
-
}).tapErr(console.log)
|
|
118
|
-
})
|
|
119
|
-
```
|
|
120
148
|
|
|
121
149
|
### LISTEN / NOTIFY without babysitting a connection
|
|
122
150
|
|
|
@@ -199,10 +227,10 @@ interface ConnectionPartialConfig {
|
|
|
199
227
|
host: string
|
|
200
228
|
port: number
|
|
201
229
|
database: string
|
|
202
|
-
logLevel?: 'error' | 'notice' | 'query' // default 'error'
|
|
230
|
+
logLevel?: 'error' | 'notice' | 'query' | "none" // default 'error'
|
|
203
231
|
int8toBigint?: boolean // default false
|
|
204
|
-
queryTimeout?: number // default 30000
|
|
205
|
-
syncShedule?: 'beforeMicrotask' | 'afterMicrotask' | 'Immediate' // default '
|
|
232
|
+
queryTimeout?: number // default 30000 (ms)
|
|
233
|
+
syncShedule?: 'beforeMicrotask' | 'afterMicrotask' | 'Immediate' // default 'Immediate'
|
|
206
234
|
ssl?: 'disable' | 'prefer' | 'require' // defaut 'prefer'
|
|
207
235
|
caPath?: string // forces `ssl` to 'require' if provided
|
|
208
236
|
}
|
package/dist/batch.d.ts
CHANGED
|
@@ -7,9 +7,12 @@ export declare class Batch {
|
|
|
7
7
|
constructor(buffer: ConnectionRequestWriter);
|
|
8
8
|
registerParse(query: PostgresQuery): void;
|
|
9
9
|
registerQuery(query: PostgresQuery): void;
|
|
10
|
+
get hasMore(): boolean;
|
|
10
11
|
end(): ConnectionRequestWriter;
|
|
11
12
|
get current(): PostgresQuery;
|
|
12
13
|
next(): void;
|
|
13
|
-
|
|
14
|
+
get shift(): PostgresQuery;
|
|
15
|
+
error(cause: PostgresError): void;
|
|
16
|
+
get residual(): PostgresQuery[];
|
|
14
17
|
}
|
|
15
18
|
//# sourceMappingURL=batch.d.ts.map
|
package/dist/batch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,qBAAa,KAAK;IACd,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,OAAO,CAAyB;gBAE5B,MAAM,EAAE,uBAAuB;IAM3C,aAAa,CAAC,KAAK,EAAE,aAAa;IAOlC,aAAa,CAAC,KAAK,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGxC,qBAAa,KAAK;IACd,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,OAAO,CAAyB;gBAE5B,MAAM,EAAE,uBAAuB;IAM3C,aAAa,CAAC,KAAK,EAAE,aAAa;IAOlC,aAAa,CAAC,KAAK,EAAE,aAAa;IASlC,IAAI,OAAO,YAEV;IAED,GAAG;IAIH,IAAI,OAAO,kBAEV;IAED,IAAI;IAIJ,IAAI,KAAK,kBAER;IAED,KAAK,CAAC,KAAK,EAAE,aAAa;IAM1B,IAAI,QAAQ,oBAEX;CACJ"}
|
package/dist/batch.js
CHANGED
|
@@ -13,10 +13,14 @@ export class Batch {
|
|
|
13
13
|
this._queryQueue.push(query);
|
|
14
14
|
this._buffer
|
|
15
15
|
.writeBind("", query.statement, query.args)
|
|
16
|
-
.writeExecute("")
|
|
16
|
+
.writeExecute("")
|
|
17
|
+
.writeSync();
|
|
18
|
+
}
|
|
19
|
+
get hasMore() {
|
|
20
|
+
return this._queryQueue.hasMore;
|
|
17
21
|
}
|
|
18
22
|
end() {
|
|
19
|
-
return this._buffer
|
|
23
|
+
return this._buffer;
|
|
20
24
|
}
|
|
21
25
|
get current() {
|
|
22
26
|
return this._queryQueue.current;
|
|
@@ -24,12 +28,15 @@ export class Batch {
|
|
|
24
28
|
next() {
|
|
25
29
|
this._queryQueue.next();
|
|
26
30
|
}
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
get shift() {
|
|
32
|
+
return this._queryQueue.shift;
|
|
33
|
+
}
|
|
34
|
+
error(cause) {
|
|
29
35
|
while (this._queryQueue.hasMore) {
|
|
30
|
-
|
|
31
|
-
this._queryQueue.shift.reject(cause);
|
|
36
|
+
this._queryQueue.shift.error(cause);
|
|
32
37
|
}
|
|
33
|
-
|
|
38
|
+
}
|
|
39
|
+
get residual() {
|
|
40
|
+
return this._queryQueue.residual;
|
|
34
41
|
}
|
|
35
42
|
}
|
package/dist/connection.d.ts
CHANGED
|
@@ -14,26 +14,28 @@ import { PostgresError } from "./error";
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class Connection {
|
|
16
16
|
private readonly config;
|
|
17
|
-
private
|
|
17
|
+
private _writer;
|
|
18
|
+
private _sheduled;
|
|
19
|
+
private _queue;
|
|
20
|
+
private _executingCounter;
|
|
18
21
|
private _closing;
|
|
19
22
|
private _closed;
|
|
20
23
|
private _reconnecting;
|
|
21
|
-
private _cachedBuffer;
|
|
22
24
|
private _socket;
|
|
23
|
-
private _batchQueue;
|
|
24
25
|
private _parsed;
|
|
25
26
|
private _parsing;
|
|
26
27
|
private _listeningCallbacks;
|
|
27
28
|
private _stmtCounter;
|
|
28
29
|
private _nextStatement;
|
|
30
|
+
private _registerShedule;
|
|
31
|
+
private _shedule;
|
|
32
|
+
private _registerQuery;
|
|
29
33
|
private constructor();
|
|
30
34
|
/**
|
|
31
35
|
* Opens a new connection and authenticates.
|
|
32
36
|
* @throws {PostgresError} if authentication fails or the connection can't be established
|
|
33
37
|
*/
|
|
34
38
|
static new(config: ConnectionPartialConfig): Future<Connection, PostgresError>;
|
|
35
|
-
private _registerBatch;
|
|
36
|
-
private _sync;
|
|
37
39
|
/**
|
|
38
40
|
* Runs a query, binding template values as `$1, $2, ...`.
|
|
39
41
|
* Parameterized queries are cached as prepared statements.
|
|
@@ -51,6 +53,15 @@ export declare class Connection {
|
|
|
51
53
|
*/
|
|
52
54
|
execute(templates: TemplateStringsArray, ...params: any[]): Future<void, PostgresError>;
|
|
53
55
|
private _performExecute;
|
|
56
|
+
/**
|
|
57
|
+
* Streams query results as a `ReadableStream`, without buffering rows in memory.
|
|
58
|
+
* Ideal for large result sets or piping straight into an HTTP response.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* for await (const row of conn.stream<User>`SELECT * FROM orders`) { ... }
|
|
62
|
+
*/
|
|
63
|
+
stream<T extends Row>(templates: TemplateStringsArray, ...params: any[]): ReadableStream<T>;
|
|
64
|
+
private _performStream;
|
|
54
65
|
/**
|
|
55
66
|
* Runs `txCallback` inside `BEGIN`/`COMMIT`, rolling back on error.
|
|
56
67
|
*
|
|
@@ -66,21 +77,11 @@ export declare class Connection {
|
|
|
66
77
|
listen(channelName: string, callback: (payload: string) => void): Future<void, PostgresError>;
|
|
67
78
|
/** Unsubscribes `callback`, issuing `UNLISTEN` once no callbacks remain. */
|
|
68
79
|
unlisten(channelName: string, callback: (payload: string) => void): Future<void, PostgresError>;
|
|
69
|
-
/**
|
|
70
|
-
* Streams query results as a `ReadableStream`, without buffering rows in memory.
|
|
71
|
-
* Ideal for large result sets or piping straight into an HTTP response.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* for await (const row of conn.stream<User>`SELECT * FROM orders`) { ... }
|
|
75
|
-
*/
|
|
76
|
-
stream<T extends Row>(templates: TemplateStringsArray, ...params: any[]): ReadableStream<T>;
|
|
77
|
-
private _performStream;
|
|
78
80
|
private _reconnect;
|
|
79
81
|
private _performReconnect;
|
|
80
82
|
private _restoreSubscriptions;
|
|
81
|
-
private
|
|
83
|
+
private get _currentQuery();
|
|
82
84
|
private _handlePacket;
|
|
83
|
-
private get _hasQueries();
|
|
84
85
|
/** Whether the connection is alive and usable. */
|
|
85
86
|
get isOpened(): boolean;
|
|
86
87
|
/** Whether the connection is closed or closing. */
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiC,uBAAuB,EAAuC,GAAG,EAAiB,MAAM,SAAS,CAAA;AACzI,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAA;AACjD,OAAO,EAAkD,aAAa,EAAE,MAAM,SAAS,CAAA;AAevF;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,iBAAiB,CAAI;IAE7B,OAAO,CAAC,QAAQ,CAAsD;IACtE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAA2C;IAEhE,OAAO,CAAC,OAAO,CAAiB;IAEhC,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAA8D;IAE9E,OAAO,CAAC,mBAAmB,CAAyD;IACpF,OAAO,CAAC,YAAY,CAAI;IAExB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,cAAc;IA6BtB,OAAO;IAYP;;;OAGG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB;IAkB1C;;;;;;OAMG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAatE,OAAO,CAAC,aAAa;IA+DrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAazD,OAAO,CAAC,eAAe;IA+DvB;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IA2BvE,OAAO,CAAC,cAAc;IAkEtB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAoB7D,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAOhD,sFAAsF;IACtF,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAe/D,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAoB/F,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,qBAAqB;IAW7B,OAAO,KAAK,aAAa,GAExB;IAGD,OAAO,CAAC,aAAa;IA6HrB,kDAAkD;IAClD,IAAI,QAAQ,YAEX;IAGD,mDAAmD;IACnD,IAAI,QAAQ,YAEX;IAGD;;OAEG;IACH,KAAK;CAwBR"}
|