pqb 0.66.8 → 0.67.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/dist/bun.d.ts +27 -0
- package/dist/bun.js +241 -0
- package/dist/bun.js.map +1 -0
- package/dist/bun.mjs +238 -0
- package/dist/bun.mjs.map +1 -0
- package/dist/index.d.ts +248 -211
- package/dist/index.js +8967 -8905
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8964 -8906
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +24 -0
- package/dist/internal.mjs +2 -2
- package/dist/node-postgres.d.ts +3 -2
- package/dist/node-postgres.js +18 -11
- package/dist/node-postgres.js.map +1 -1
- package/dist/node-postgres.mjs +19 -13
- package/dist/node-postgres.mjs.map +1 -1
- package/dist/postgres-js.js +31 -16
- package/dist/postgres-js.js.map +1 -1
- package/dist/postgres-js.mjs +31 -16
- package/dist/postgres-js.mjs.map +1 -1
- package/package.json +13 -1
package/dist/bun.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AdapterConfigBase, AdapterSchemaConfigOptions, ColumnSchemaConfig, DbOptions, DbResult, DefaultColumnTypes, DefaultSchemaConfig, DriverAdapter, QuerySchema } from "pqb/internal";
|
|
2
|
+
declare const bunSchemaConfig: (() => DefaultSchemaConfig) & AdapterSchemaConfigOptions;
|
|
3
|
+
interface CreateBunDbOptions<SchemaConfig extends ColumnSchemaConfig, ColumnTypes> extends BunAdapterOptions, DbOptions<SchemaConfig, ColumnTypes> {}
|
|
4
|
+
declare const createDb: <SchemaConfig extends ColumnSchemaConfig = DefaultSchemaConfig, ColumnTypes = DefaultColumnTypes<SchemaConfig>>({
|
|
5
|
+
log,
|
|
6
|
+
...options
|
|
7
|
+
}: CreateBunDbOptions<SchemaConfig, ColumnTypes>) => DbResult<ColumnTypes>;
|
|
8
|
+
interface BunOptions {
|
|
9
|
+
hostname?: string;
|
|
10
|
+
port?: number;
|
|
11
|
+
database?: string;
|
|
12
|
+
username?: string;
|
|
13
|
+
password?: string | (() => string | Promise<string>);
|
|
14
|
+
max?: number;
|
|
15
|
+
idleTimeout?: number;
|
|
16
|
+
maxLifetime?: number;
|
|
17
|
+
connectionTimeout?: number;
|
|
18
|
+
prepare?: boolean;
|
|
19
|
+
tls?: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface BunAdapterOptions extends AdapterConfigBase, BunOptions {
|
|
22
|
+
schema?: QuerySchema;
|
|
23
|
+
searchPath?: string;
|
|
24
|
+
ssl?: unknown;
|
|
25
|
+
}
|
|
26
|
+
declare const BunAdapter: DriverAdapter;
|
|
27
|
+
export { BunAdapter, BunAdapterOptions, BunOptions, CreateBunDbOptions, bunSchemaConfig, createDb };
|
package/dist/bun.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let pqb_internal = require("pqb/internal");
|
|
3
|
+
let pqb = require("pqb");
|
|
4
|
+
const intervalRegExp = /^\s*(?:([+-]?\d+)\s+years?)?\s*(?:([+-]?\d+)\s+mons?)?\s*(?:([+-]?\d+)\s+days?)?\s*(?:([+-])?(\d+):(\d\d):(\d\d(?:\.\d{1,6})?))?\s*$/;
|
|
5
|
+
const parseInterval = (str) => {
|
|
6
|
+
const [, years, months, days, timeSign, hours, minutes, seconds] = intervalRegExp.exec(str) || [];
|
|
7
|
+
const timeMultiplier = timeSign === "-" ? -1 : 1;
|
|
8
|
+
const secondsFloat = Number(seconds) || 0;
|
|
9
|
+
const wholeSeconds = Math.floor(secondsFloat);
|
|
10
|
+
return {
|
|
11
|
+
years: years ? Number(years) : 0,
|
|
12
|
+
months: months ? Number(months) : 0,
|
|
13
|
+
days: days ? Number(days) : 0,
|
|
14
|
+
hours: hours ? timeMultiplier * Number(hours) : 0,
|
|
15
|
+
minutes: minutes ? timeMultiplier * Number(minutes) : 0,
|
|
16
|
+
seconds: timeMultiplier * wholeSeconds,
|
|
17
|
+
milliseconds: Math.round(timeMultiplier * (secondsFloat - wholeSeconds) * 1e6) / 1e3
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const schemaConfig = {
|
|
21
|
+
arrayEncode: (input) => {
|
|
22
|
+
return getBun().sql.array(input);
|
|
23
|
+
},
|
|
24
|
+
intervalParse: parseInterval,
|
|
25
|
+
jsonEncodedByDriver: true,
|
|
26
|
+
dateParsedByDriver: true
|
|
27
|
+
};
|
|
28
|
+
const bunSchemaConfig = Object.assign(() => (0, pqb_internal.defaultSchemaConfig)(schemaConfig), schemaConfig);
|
|
29
|
+
const createDb = ({ log, ...options }) => {
|
|
30
|
+
return (0, pqb.createDbWithAdapter)({
|
|
31
|
+
schemaConfig: bunSchemaConfig,
|
|
32
|
+
...options,
|
|
33
|
+
log,
|
|
34
|
+
adapter: new pqb_internal.AdapterClass({
|
|
35
|
+
driverAdapter: BunAdapter,
|
|
36
|
+
config: options
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const getBun = () => {
|
|
41
|
+
const Bun = globalThis.Bun;
|
|
42
|
+
if (!Bun?.SQL) throw new Error("Bun.SQL is only available when running in Bun");
|
|
43
|
+
return Bun;
|
|
44
|
+
};
|
|
45
|
+
const getBunPostgresError = () => {
|
|
46
|
+
return globalThis.Bun?.SQL.PostgresError ?? Error;
|
|
47
|
+
};
|
|
48
|
+
const queryClient = (client, text, values, arraysMode) => {
|
|
49
|
+
const runQuery = () => {
|
|
50
|
+
const query = client.unsafe(text, values);
|
|
51
|
+
const resultPromise = arraysMode ? query.values() : query;
|
|
52
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
53
|
+
return normalizeResult(result, arraysMode);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const { __lock } = client;
|
|
57
|
+
if (__lock) {
|
|
58
|
+
let resolve;
|
|
59
|
+
client.__lock = new Promise((res) => {
|
|
60
|
+
resolve = res;
|
|
61
|
+
});
|
|
62
|
+
return __lock.then(() => {
|
|
63
|
+
const promise = runQuery();
|
|
64
|
+
promise.then(resolve, resolve);
|
|
65
|
+
return promise;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const promise = runQuery();
|
|
69
|
+
client.__lock = promise.catch(pqb_internal.noop);
|
|
70
|
+
return promise;
|
|
71
|
+
};
|
|
72
|
+
const borrowBunClient = (pool) => {
|
|
73
|
+
return pool.reserve();
|
|
74
|
+
};
|
|
75
|
+
const releaseBunClient = (client) => {
|
|
76
|
+
client.release();
|
|
77
|
+
};
|
|
78
|
+
const BunAdapter = {
|
|
79
|
+
noFieldsForArrays: true,
|
|
80
|
+
manualPool: true,
|
|
81
|
+
schemaConfig,
|
|
82
|
+
errorClass: getBunPostgresError(),
|
|
83
|
+
errorFields: {
|
|
84
|
+
message: "message",
|
|
85
|
+
severity: "severity",
|
|
86
|
+
code: "errno",
|
|
87
|
+
detail: "detail",
|
|
88
|
+
schema: "schema",
|
|
89
|
+
table: "table",
|
|
90
|
+
column: "column",
|
|
91
|
+
dataType: "dataType",
|
|
92
|
+
constraint: "constraint",
|
|
93
|
+
hint: "hint",
|
|
94
|
+
position: "position",
|
|
95
|
+
internalPosition: "internalPosition",
|
|
96
|
+
internalQuery: "internalQuery",
|
|
97
|
+
where: "where",
|
|
98
|
+
file: "file",
|
|
99
|
+
line: "line",
|
|
100
|
+
routine: "routine"
|
|
101
|
+
},
|
|
102
|
+
configure(config) {
|
|
103
|
+
return new (getBun()).SQL(makeOptions(config));
|
|
104
|
+
},
|
|
105
|
+
queryClient,
|
|
106
|
+
borrow(pool) {
|
|
107
|
+
return borrowBunClient(pool);
|
|
108
|
+
},
|
|
109
|
+
release(client) {
|
|
110
|
+
releaseBunClient(client);
|
|
111
|
+
},
|
|
112
|
+
async begin(pool, cb, options) {
|
|
113
|
+
const client = await borrowBunClient(pool);
|
|
114
|
+
try {
|
|
115
|
+
await queryClient(client, options ? "BEGIN " + options : "BEGIN");
|
|
116
|
+
let result;
|
|
117
|
+
try {
|
|
118
|
+
result = await cb(client);
|
|
119
|
+
} catch (err) {
|
|
120
|
+
await queryClient(client, "ROLLBACK");
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
await queryClient(client, "COMMIT");
|
|
124
|
+
return result;
|
|
125
|
+
} finally {
|
|
126
|
+
releaseBunClient(client);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
async savepoint(client, _setClient, name, cb) {
|
|
130
|
+
const safeName = (0, pqb_internal.quoteIdentifier)(name);
|
|
131
|
+
try {
|
|
132
|
+
await queryClient(client, `SAVEPOINT ${safeName}`);
|
|
133
|
+
const res = await cb();
|
|
134
|
+
await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);
|
|
135
|
+
return res;
|
|
136
|
+
} catch (err) {
|
|
137
|
+
await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);
|
|
138
|
+
throw err;
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
async hackySavepoint(client, _setClient, state, text, values, arraysMode) {
|
|
142
|
+
const safeName = (0, pqb_internal.quoteIdentifier)(state.name);
|
|
143
|
+
let resolve;
|
|
144
|
+
let reject;
|
|
145
|
+
const promise = new Promise((res, rej) => {
|
|
146
|
+
resolve = res;
|
|
147
|
+
reject = rej;
|
|
148
|
+
});
|
|
149
|
+
let resultResolve;
|
|
150
|
+
let resultReject;
|
|
151
|
+
const resultPromise = new Promise((res, rej) => {
|
|
152
|
+
resultResolve = res;
|
|
153
|
+
resultReject = rej;
|
|
154
|
+
});
|
|
155
|
+
const savepointPromise = (async () => {
|
|
156
|
+
try {
|
|
157
|
+
await queryClient(client, `SAVEPOINT ${safeName}`);
|
|
158
|
+
try {
|
|
159
|
+
const res = await queryClient(client, text, values, arraysMode);
|
|
160
|
+
resultResolve?.(res);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
resultReject?.(err);
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
const result = await promise;
|
|
166
|
+
await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);
|
|
167
|
+
return result;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
state.activeSavepoint = {
|
|
174
|
+
async release() {
|
|
175
|
+
resolve?.();
|
|
176
|
+
await savepointPromise;
|
|
177
|
+
},
|
|
178
|
+
async rollback(err) {
|
|
179
|
+
reject?.(err);
|
|
180
|
+
await savepointPromise.catch(pqb_internal.noop);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return resultPromise;
|
|
184
|
+
},
|
|
185
|
+
close(pool) {
|
|
186
|
+
return pool.close();
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const makeOptions = (config) => {
|
|
190
|
+
const { databaseURL, host, user, password, port, database, max, idleTimeout, maxLifetime, connectionTimeout, prepare, tls, ssl, setConfig, searchPath } = config;
|
|
191
|
+
return {
|
|
192
|
+
url: databaseURL && makeUrl(databaseURL, setConfig?.search_path ?? searchPath),
|
|
193
|
+
hostname: host,
|
|
194
|
+
username: user,
|
|
195
|
+
password,
|
|
196
|
+
port,
|
|
197
|
+
database,
|
|
198
|
+
max,
|
|
199
|
+
idleTimeout,
|
|
200
|
+
maxLifetime,
|
|
201
|
+
connectionTimeout,
|
|
202
|
+
prepare,
|
|
203
|
+
tls,
|
|
204
|
+
ssl
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
const makeUrl = (databaseURL, searchPath) => {
|
|
208
|
+
if (!searchPath) return databaseURL;
|
|
209
|
+
const url = new URL(databaseURL);
|
|
210
|
+
url.searchParams.set("options", `-c search_path=${searchPath}`);
|
|
211
|
+
return url.toString();
|
|
212
|
+
};
|
|
213
|
+
const normalizeResult = (result, arraysMode) => {
|
|
214
|
+
if (!arraysMode && isMultiResult(result)) return result.map((item) => new BunQueryResult(item, arraysMode));
|
|
215
|
+
return new BunQueryResult(result, arraysMode);
|
|
216
|
+
};
|
|
217
|
+
var BunQueryResult = class {
|
|
218
|
+
constructor(result, isArray) {
|
|
219
|
+
this.result = result;
|
|
220
|
+
this.isArray = isArray;
|
|
221
|
+
this.rowCount = result.count ?? result.affectedRows ?? result.length;
|
|
222
|
+
this.rows = Array.from(result);
|
|
223
|
+
}
|
|
224
|
+
get fields() {
|
|
225
|
+
if (this.isArray) throw new Error("Bun does not support fields on array result");
|
|
226
|
+
return this._fields ??= getFields(this.result);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const isMultiResult = (result) => {
|
|
230
|
+
return Array.isArray(result[0]);
|
|
231
|
+
};
|
|
232
|
+
const getFields = (rows) => {
|
|
233
|
+
const first = rows[0];
|
|
234
|
+
if (!first || Array.isArray(first) || typeof first !== "object") return [];
|
|
235
|
+
return Object.keys(first).map((name) => ({ name }));
|
|
236
|
+
};
|
|
237
|
+
exports.BunAdapter = BunAdapter;
|
|
238
|
+
exports.bunSchemaConfig = bunSchemaConfig;
|
|
239
|
+
exports.createDb = createDb;
|
|
240
|
+
|
|
241
|
+
//# sourceMappingURL=bun.js.map
|
package/dist/bun.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun.js","names":["AdapterClass","noop"],"sources":["../src/adapters/driver-adapter-shared.ts","../src/adapters/bun.ts"],"sourcesContent":["export interface PostgresInterval {\n years: number;\n months: number;\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n milliseconds: number;\n}\n\nconst intervalRegExp =\n /^\\s*(?:([+-]?\\d+)\\s+years?)?\\s*(?:([+-]?\\d+)\\s+mons?)?\\s*(?:([+-]?\\d+)\\s+days?)?\\s*(?:([+-])?(\\d+):(\\d\\d):(\\d\\d(?:\\.\\d{1,6})?))?\\s*$/;\n\nexport const parseInterval = (str: string): PostgresInterval => {\n const [, years, months, days, timeSign, hours, minutes, seconds] =\n intervalRegExp.exec(str) || [];\n const timeMultiplier = timeSign === '-' ? -1 : 1;\n const secondsFloat = Number(seconds) || 0;\n const wholeSeconds = Math.floor(secondsFloat);\n\n return {\n years: years ? Number(years) : 0,\n months: months ? Number(months) : 0,\n days: days ? Number(days) : 0,\n hours: hours ? timeMultiplier * Number(hours) : 0,\n minutes: minutes ? timeMultiplier * Number(minutes) : 0,\n seconds: timeMultiplier * wholeSeconds,\n milliseconds:\n Math.round(timeMultiplier * (secondsFloat - wholeSeconds) * 1000000) /\n 1000,\n };\n};\n","import {\n AdapterClass,\n AdapterConfigBase,\n ColumnSchemaConfig,\n DbOptions,\n DbResult,\n DefaultColumnTypes,\n defaultSchemaConfig,\n DefaultSchemaConfig,\n DriverAdapter,\n noop,\n QueryResult,\n QueryResultRow,\n QuerySchema,\n quoteIdentifier,\n RecordUnknown,\n AdapterSchemaConfigOptions,\n} from 'pqb/internal';\nimport { createDbWithAdapter } from 'pqb';\nimport { HackySavepointState } from './adapter';\nimport { parseInterval } from './driver-adapter-shared';\n\nconst schemaConfig: AdapterSchemaConfigOptions = {\n arrayEncode: (input) => {\n return getBun().sql.array(input);\n },\n intervalParse: parseInterval,\n jsonEncodedByDriver: true,\n dateParsedByDriver: true,\n};\n\nexport const bunSchemaConfig = Object.assign(\n () => defaultSchemaConfig(schemaConfig),\n schemaConfig,\n);\n\nexport interface CreateBunDbOptions<\n SchemaConfig extends ColumnSchemaConfig,\n ColumnTypes,\n>\n extends BunAdapterOptions, DbOptions<SchemaConfig, ColumnTypes> {}\n\nexport const createDb = <\n SchemaConfig extends ColumnSchemaConfig = DefaultSchemaConfig,\n ColumnTypes = DefaultColumnTypes<SchemaConfig>,\n>({\n log,\n ...options\n}: CreateBunDbOptions<SchemaConfig, ColumnTypes>): DbResult<ColumnTypes> => {\n return createDbWithAdapter({\n schemaConfig: bunSchemaConfig as unknown as () => SchemaConfig,\n ...options,\n log,\n adapter: new AdapterClass({\n driverAdapter: BunAdapter,\n config: options,\n }),\n });\n};\n\nexport interface BunOptions {\n hostname?: string;\n port?: number;\n database?: string;\n username?: string;\n password?: string | (() => string | Promise<string>);\n max?: number;\n idleTimeout?: number;\n maxLifetime?: number;\n connectionTimeout?: number;\n prepare?: boolean;\n tls?: unknown;\n}\n\nexport interface BunAdapterOptions extends AdapterConfigBase, BunOptions {\n schema?: QuerySchema;\n searchPath?: string;\n ssl?: unknown;\n}\n\ninterface Bun {\n SQL: BunSqlConstructor;\n sql: {\n array(input: unknown): unknown;\n };\n}\n\ninterface BunSqlConstructor {\n new (options?: BunSqlOptions | string): BunSql;\n PostgresError: ErrorClass;\n}\n\ninterface BunSqlOptions extends BunOptions {\n url?: string;\n ssl?: unknown;\n}\n\ninterface BunSql {\n unsafe<Row extends QueryResultRow = QueryResultRow>(\n text: string,\n values?: unknown[],\n ): BunSqlQuery<Row>;\n reserve(): Promise<BunReservedSql>;\n close(options?: { timeout?: number }): Promise<void>;\n}\n\ninterface BunReservedSql extends BunSql {\n release(): void;\n}\n\ninterface BunSqlQuery<\n Row extends QueryResultRow = QueryResultRow,\n> extends PromiseLike<BunSqlResult<Row>> {\n values(): Promise<BunSqlResult<unknown[]>>;\n}\n\ninterface BunSqlResult<Row = unknown> extends Array<Row> {\n count?: number;\n affectedRows?: number;\n}\n\n// oxlint-disable-next-line typescript/no-explicit-any\ntype ErrorClass = new (...args: any[]) => Error;\n\nconst getBun = (): Bun => {\n const Bun = (globalThis as unknown as { Bun?: Bun }).Bun;\n if (!Bun?.SQL) {\n throw new Error('Bun.SQL is only available when running in Bun');\n }\n\n return Bun;\n};\n\nconst getBunPostgresError = (): ErrorClass => {\n return (\n (globalThis as unknown as { Bun?: { SQL: BunSqlConstructor } }).Bun?.SQL\n .PostgresError ?? (Error as ErrorClass)\n );\n};\n\nconst queryClient = <T = QueryResultRow>(\n client: BunSql,\n text: string,\n values?: unknown[],\n arraysMode?: boolean,\n): Promise<QueryResult<T>> => {\n const runQuery = () => {\n const query = client.unsafe(text, values);\n const resultPromise: PromiseLike<BunSqlResult> = arraysMode\n ? query.values()\n : query;\n\n return Promise.resolve(resultPromise).then((result) => {\n return normalizeResult(result, arraysMode) as QueryResult<T>;\n });\n };\n\n // Keep a single transactional connection ordered when savepoints are active.\n const { __lock } = client as unknown as { __lock?: Promise<unknown> };\n if (__lock) {\n let resolve: (() => void) | undefined;\n (client as unknown as RecordUnknown).__lock = new Promise<void>((res) => {\n resolve = res;\n });\n\n return __lock.then(() => {\n const promise = runQuery();\n promise.then(resolve, resolve);\n return promise;\n });\n }\n\n const promise = runQuery();\n\n (client as unknown as { __lock?: Promise<unknown> }).__lock =\n promise.catch(noop);\n\n return promise;\n};\n\nconst borrowBunClient = (pool: BunSql): Promise<BunReservedSql> => {\n return pool.reserve();\n};\n\nconst releaseBunClient = (client: BunReservedSql): void => {\n client.release();\n};\n\nexport const BunAdapter: DriverAdapter = {\n noFieldsForArrays: true,\n manualPool: true,\n schemaConfig,\n errorClass: getBunPostgresError(),\n errorFields: {\n message: 'message',\n severity: 'severity',\n code: 'errno',\n detail: 'detail',\n schema: 'schema',\n table: 'table',\n column: 'column',\n dataType: 'dataType',\n constraint: 'constraint',\n hint: 'hint',\n position: 'position',\n internalPosition: 'internalPosition',\n internalQuery: 'internalQuery',\n where: 'where',\n file: 'file',\n line: 'line',\n routine: 'routine',\n },\n\n configure(config: BunAdapterOptions): BunSql {\n return new (getBun().SQL)(makeOptions(config));\n },\n\n queryClient,\n\n borrow(pool: BunSql): Promise<BunReservedSql> {\n return borrowBunClient(pool);\n },\n\n release(client: BunReservedSql): void {\n releaseBunClient(client);\n },\n\n async begin<DriverClient, Result>(\n pool: BunSql,\n cb: (adapter: DriverClient) => Promise<Result>,\n options?: string,\n ): Promise<Result> {\n const client = await borrowBunClient(pool);\n\n try {\n await queryClient(client, options ? 'BEGIN ' + options : 'BEGIN');\n\n let result;\n try {\n result = await cb(client as DriverClient);\n } catch (err) {\n await queryClient(client, 'ROLLBACK');\n throw err;\n }\n\n await queryClient(client, 'COMMIT');\n return result as Result;\n } finally {\n releaseBunClient(client);\n }\n },\n\n async savepoint<T>(\n client: BunSql,\n // Bun doesn't need to switch the client in a savepoint.\n _setClient: (client: BunSql) => void,\n name: string,\n cb: () => Promise<T>,\n ): Promise<T> {\n const safeName = quoteIdentifier(name);\n try {\n await queryClient(client, `SAVEPOINT ${safeName}`);\n const res = await cb();\n await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);\n return res;\n } catch (err) {\n await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);\n throw err;\n }\n },\n\n async hackySavepoint<T extends QueryResultRow>(\n client: BunSql,\n // Bun doesn't need to switch the client in a savepoint.\n _setClient: (client: BunSql) => void,\n state: HackySavepointState,\n text: string,\n values?: unknown[],\n arraysMode?: boolean,\n ): Promise<QueryResult<T>> {\n const safeName = quoteIdentifier(state.name);\n\n let resolve: (() => void) | undefined;\n let reject: ((err: unknown) => void) | undefined;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n let resultResolve: ((res: QueryResult<T>) => void) | undefined;\n let resultReject: ((err: unknown) => void) | undefined;\n const resultPromise = new Promise<QueryResult<T>>((res, rej) => {\n resultResolve = res;\n resultReject = rej;\n });\n\n const savepointPromise = (async () => {\n try {\n await queryClient(client, `SAVEPOINT ${safeName}`);\n\n try {\n const res = await queryClient<T>(client, text, values, arraysMode);\n resultResolve?.(res as QueryResult<T>);\n } catch (err) {\n resultReject?.(err);\n throw err;\n }\n\n const result = await promise;\n await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (err) {\n await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);\n throw err;\n }\n })();\n\n state.activeSavepoint = {\n async release() {\n resolve?.();\n await savepointPromise;\n },\n async rollback(err) {\n reject?.(err);\n await savepointPromise.catch(noop);\n },\n };\n\n return resultPromise;\n },\n\n close(pool: BunSql): Promise<void> {\n return pool.close();\n },\n};\n\nconst makeOptions = (config: BunAdapterOptions): BunSqlOptions => {\n const {\n databaseURL,\n host,\n user,\n password,\n port,\n database,\n max,\n idleTimeout,\n maxLifetime,\n connectionTimeout,\n prepare,\n tls,\n ssl,\n setConfig,\n searchPath,\n } = config;\n\n return {\n url:\n databaseURL && makeUrl(databaseURL, setConfig?.search_path ?? searchPath),\n hostname: host,\n username: user,\n password,\n port,\n database,\n max,\n idleTimeout,\n maxLifetime,\n connectionTimeout,\n prepare,\n tls,\n ssl,\n };\n};\n\nconst makeUrl = (databaseURL: string, searchPath?: string): string => {\n if (!searchPath) return databaseURL;\n\n const url = new URL(databaseURL);\n url.searchParams.set('options', `-c search_path=${searchPath}`);\n return url.toString();\n};\n\nconst normalizeResult = <T>(\n result: BunSqlResult<T>,\n arraysMode?: boolean,\n): QueryResult<T> | QueryResult<T>[] => {\n if (!arraysMode && isMultiResult(result)) {\n return result.map((item) => new BunQueryResult(item, arraysMode));\n }\n\n return new BunQueryResult(result, arraysMode);\n};\n\nclass BunQueryResult<T> implements QueryResult<T> {\n public rowCount: number;\n public rows: T[];\n private _fields?: QueryResult<T>['fields'];\n\n constructor(\n private result: BunSqlResult<T>,\n private isArray?: boolean,\n ) {\n this.rowCount = result.count ?? result.affectedRows ?? result.length;\n\n // creating new Array because BunSqlResult is not a real array - can't do spread on it\n this.rows = Array.from(result) as T[];\n }\n\n get fields(): QueryResult<T>['fields'] {\n if (this.isArray) {\n throw new Error('Bun does not support fields on array result');\n }\n\n return (this._fields ??= getFields(this.result));\n }\n}\n\nconst isMultiResult = <T>(\n result: BunSqlResult<T>,\n): result is BunSqlResult<T>[] & BunSqlResult<T> => {\n return Array.isArray(result[0]);\n};\n\nconst getFields = <T>(rows: T[]): { name: string }[] => {\n const first = rows[0];\n if (!first || Array.isArray(first) || typeof first !== 'object') return [];\n\n return Object.keys(first).map((name) => ({ name }));\n};\n"],"mappings":"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"}
|
package/dist/bun.mjs
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { AdapterClass, defaultSchemaConfig, noop, quoteIdentifier } from "pqb/internal";
|
|
2
|
+
import { createDbWithAdapter } from "pqb";
|
|
3
|
+
const intervalRegExp = /^\s*(?:([+-]?\d+)\s+years?)?\s*(?:([+-]?\d+)\s+mons?)?\s*(?:([+-]?\d+)\s+days?)?\s*(?:([+-])?(\d+):(\d\d):(\d\d(?:\.\d{1,6})?))?\s*$/;
|
|
4
|
+
const parseInterval = (str) => {
|
|
5
|
+
const [, years, months, days, timeSign, hours, minutes, seconds] = intervalRegExp.exec(str) || [];
|
|
6
|
+
const timeMultiplier = timeSign === "-" ? -1 : 1;
|
|
7
|
+
const secondsFloat = Number(seconds) || 0;
|
|
8
|
+
const wholeSeconds = Math.floor(secondsFloat);
|
|
9
|
+
return {
|
|
10
|
+
years: years ? Number(years) : 0,
|
|
11
|
+
months: months ? Number(months) : 0,
|
|
12
|
+
days: days ? Number(days) : 0,
|
|
13
|
+
hours: hours ? timeMultiplier * Number(hours) : 0,
|
|
14
|
+
minutes: minutes ? timeMultiplier * Number(minutes) : 0,
|
|
15
|
+
seconds: timeMultiplier * wholeSeconds,
|
|
16
|
+
milliseconds: Math.round(timeMultiplier * (secondsFloat - wholeSeconds) * 1e6) / 1e3
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
const schemaConfig = {
|
|
20
|
+
arrayEncode: (input) => {
|
|
21
|
+
return getBun().sql.array(input);
|
|
22
|
+
},
|
|
23
|
+
intervalParse: parseInterval,
|
|
24
|
+
jsonEncodedByDriver: true,
|
|
25
|
+
dateParsedByDriver: true
|
|
26
|
+
};
|
|
27
|
+
const bunSchemaConfig = Object.assign(() => defaultSchemaConfig(schemaConfig), schemaConfig);
|
|
28
|
+
const createDb = ({ log, ...options }) => {
|
|
29
|
+
return createDbWithAdapter({
|
|
30
|
+
schemaConfig: bunSchemaConfig,
|
|
31
|
+
...options,
|
|
32
|
+
log,
|
|
33
|
+
adapter: new AdapterClass({
|
|
34
|
+
driverAdapter: BunAdapter,
|
|
35
|
+
config: options
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const getBun = () => {
|
|
40
|
+
const Bun = globalThis.Bun;
|
|
41
|
+
if (!Bun?.SQL) throw new Error("Bun.SQL is only available when running in Bun");
|
|
42
|
+
return Bun;
|
|
43
|
+
};
|
|
44
|
+
const getBunPostgresError = () => {
|
|
45
|
+
return globalThis.Bun?.SQL.PostgresError ?? Error;
|
|
46
|
+
};
|
|
47
|
+
const queryClient = (client, text, values, arraysMode) => {
|
|
48
|
+
const runQuery = () => {
|
|
49
|
+
const query = client.unsafe(text, values);
|
|
50
|
+
const resultPromise = arraysMode ? query.values() : query;
|
|
51
|
+
return Promise.resolve(resultPromise).then((result) => {
|
|
52
|
+
return normalizeResult(result, arraysMode);
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
const { __lock } = client;
|
|
56
|
+
if (__lock) {
|
|
57
|
+
let resolve;
|
|
58
|
+
client.__lock = new Promise((res) => {
|
|
59
|
+
resolve = res;
|
|
60
|
+
});
|
|
61
|
+
return __lock.then(() => {
|
|
62
|
+
const promise = runQuery();
|
|
63
|
+
promise.then(resolve, resolve);
|
|
64
|
+
return promise;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const promise = runQuery();
|
|
68
|
+
client.__lock = promise.catch(noop);
|
|
69
|
+
return promise;
|
|
70
|
+
};
|
|
71
|
+
const borrowBunClient = (pool) => {
|
|
72
|
+
return pool.reserve();
|
|
73
|
+
};
|
|
74
|
+
const releaseBunClient = (client) => {
|
|
75
|
+
client.release();
|
|
76
|
+
};
|
|
77
|
+
const BunAdapter = {
|
|
78
|
+
noFieldsForArrays: true,
|
|
79
|
+
manualPool: true,
|
|
80
|
+
schemaConfig,
|
|
81
|
+
errorClass: getBunPostgresError(),
|
|
82
|
+
errorFields: {
|
|
83
|
+
message: "message",
|
|
84
|
+
severity: "severity",
|
|
85
|
+
code: "errno",
|
|
86
|
+
detail: "detail",
|
|
87
|
+
schema: "schema",
|
|
88
|
+
table: "table",
|
|
89
|
+
column: "column",
|
|
90
|
+
dataType: "dataType",
|
|
91
|
+
constraint: "constraint",
|
|
92
|
+
hint: "hint",
|
|
93
|
+
position: "position",
|
|
94
|
+
internalPosition: "internalPosition",
|
|
95
|
+
internalQuery: "internalQuery",
|
|
96
|
+
where: "where",
|
|
97
|
+
file: "file",
|
|
98
|
+
line: "line",
|
|
99
|
+
routine: "routine"
|
|
100
|
+
},
|
|
101
|
+
configure(config) {
|
|
102
|
+
return new (getBun()).SQL(makeOptions(config));
|
|
103
|
+
},
|
|
104
|
+
queryClient,
|
|
105
|
+
borrow(pool) {
|
|
106
|
+
return borrowBunClient(pool);
|
|
107
|
+
},
|
|
108
|
+
release(client) {
|
|
109
|
+
releaseBunClient(client);
|
|
110
|
+
},
|
|
111
|
+
async begin(pool, cb, options) {
|
|
112
|
+
const client = await borrowBunClient(pool);
|
|
113
|
+
try {
|
|
114
|
+
await queryClient(client, options ? "BEGIN " + options : "BEGIN");
|
|
115
|
+
let result;
|
|
116
|
+
try {
|
|
117
|
+
result = await cb(client);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
await queryClient(client, "ROLLBACK");
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
await queryClient(client, "COMMIT");
|
|
123
|
+
return result;
|
|
124
|
+
} finally {
|
|
125
|
+
releaseBunClient(client);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
async savepoint(client, _setClient, name, cb) {
|
|
129
|
+
const safeName = quoteIdentifier(name);
|
|
130
|
+
try {
|
|
131
|
+
await queryClient(client, `SAVEPOINT ${safeName}`);
|
|
132
|
+
const res = await cb();
|
|
133
|
+
await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);
|
|
134
|
+
return res;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
async hackySavepoint(client, _setClient, state, text, values, arraysMode) {
|
|
141
|
+
const safeName = quoteIdentifier(state.name);
|
|
142
|
+
let resolve;
|
|
143
|
+
let reject;
|
|
144
|
+
const promise = new Promise((res, rej) => {
|
|
145
|
+
resolve = res;
|
|
146
|
+
reject = rej;
|
|
147
|
+
});
|
|
148
|
+
let resultResolve;
|
|
149
|
+
let resultReject;
|
|
150
|
+
const resultPromise = new Promise((res, rej) => {
|
|
151
|
+
resultResolve = res;
|
|
152
|
+
resultReject = rej;
|
|
153
|
+
});
|
|
154
|
+
const savepointPromise = (async () => {
|
|
155
|
+
try {
|
|
156
|
+
await queryClient(client, `SAVEPOINT ${safeName}`);
|
|
157
|
+
try {
|
|
158
|
+
const res = await queryClient(client, text, values, arraysMode);
|
|
159
|
+
resultResolve?.(res);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
resultReject?.(err);
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
const result = await promise;
|
|
165
|
+
await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);
|
|
166
|
+
return result;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
})();
|
|
172
|
+
state.activeSavepoint = {
|
|
173
|
+
async release() {
|
|
174
|
+
resolve?.();
|
|
175
|
+
await savepointPromise;
|
|
176
|
+
},
|
|
177
|
+
async rollback(err) {
|
|
178
|
+
reject?.(err);
|
|
179
|
+
await savepointPromise.catch(noop);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return resultPromise;
|
|
183
|
+
},
|
|
184
|
+
close(pool) {
|
|
185
|
+
return pool.close();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const makeOptions = (config) => {
|
|
189
|
+
const { databaseURL, host, user, password, port, database, max, idleTimeout, maxLifetime, connectionTimeout, prepare, tls, ssl, setConfig, searchPath } = config;
|
|
190
|
+
return {
|
|
191
|
+
url: databaseURL && makeUrl(databaseURL, setConfig?.search_path ?? searchPath),
|
|
192
|
+
hostname: host,
|
|
193
|
+
username: user,
|
|
194
|
+
password,
|
|
195
|
+
port,
|
|
196
|
+
database,
|
|
197
|
+
max,
|
|
198
|
+
idleTimeout,
|
|
199
|
+
maxLifetime,
|
|
200
|
+
connectionTimeout,
|
|
201
|
+
prepare,
|
|
202
|
+
tls,
|
|
203
|
+
ssl
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
const makeUrl = (databaseURL, searchPath) => {
|
|
207
|
+
if (!searchPath) return databaseURL;
|
|
208
|
+
const url = new URL(databaseURL);
|
|
209
|
+
url.searchParams.set("options", `-c search_path=${searchPath}`);
|
|
210
|
+
return url.toString();
|
|
211
|
+
};
|
|
212
|
+
const normalizeResult = (result, arraysMode) => {
|
|
213
|
+
if (!arraysMode && isMultiResult(result)) return result.map((item) => new BunQueryResult(item, arraysMode));
|
|
214
|
+
return new BunQueryResult(result, arraysMode);
|
|
215
|
+
};
|
|
216
|
+
var BunQueryResult = class {
|
|
217
|
+
constructor(result, isArray) {
|
|
218
|
+
this.result = result;
|
|
219
|
+
this.isArray = isArray;
|
|
220
|
+
this.rowCount = result.count ?? result.affectedRows ?? result.length;
|
|
221
|
+
this.rows = Array.from(result);
|
|
222
|
+
}
|
|
223
|
+
get fields() {
|
|
224
|
+
if (this.isArray) throw new Error("Bun does not support fields on array result");
|
|
225
|
+
return this._fields ??= getFields(this.result);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const isMultiResult = (result) => {
|
|
229
|
+
return Array.isArray(result[0]);
|
|
230
|
+
};
|
|
231
|
+
const getFields = (rows) => {
|
|
232
|
+
const first = rows[0];
|
|
233
|
+
if (!first || Array.isArray(first) || typeof first !== "object") return [];
|
|
234
|
+
return Object.keys(first).map((name) => ({ name }));
|
|
235
|
+
};
|
|
236
|
+
export { BunAdapter, bunSchemaConfig, createDb };
|
|
237
|
+
|
|
238
|
+
//# sourceMappingURL=bun.mjs.map
|
package/dist/bun.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun.mjs","names":[],"sources":["../src/adapters/driver-adapter-shared.ts","../src/adapters/bun.ts"],"sourcesContent":["export interface PostgresInterval {\n years: number;\n months: number;\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n milliseconds: number;\n}\n\nconst intervalRegExp =\n /^\\s*(?:([+-]?\\d+)\\s+years?)?\\s*(?:([+-]?\\d+)\\s+mons?)?\\s*(?:([+-]?\\d+)\\s+days?)?\\s*(?:([+-])?(\\d+):(\\d\\d):(\\d\\d(?:\\.\\d{1,6})?))?\\s*$/;\n\nexport const parseInterval = (str: string): PostgresInterval => {\n const [, years, months, days, timeSign, hours, minutes, seconds] =\n intervalRegExp.exec(str) || [];\n const timeMultiplier = timeSign === '-' ? -1 : 1;\n const secondsFloat = Number(seconds) || 0;\n const wholeSeconds = Math.floor(secondsFloat);\n\n return {\n years: years ? Number(years) : 0,\n months: months ? Number(months) : 0,\n days: days ? Number(days) : 0,\n hours: hours ? timeMultiplier * Number(hours) : 0,\n minutes: minutes ? timeMultiplier * Number(minutes) : 0,\n seconds: timeMultiplier * wholeSeconds,\n milliseconds:\n Math.round(timeMultiplier * (secondsFloat - wholeSeconds) * 1000000) /\n 1000,\n };\n};\n","import {\n AdapterClass,\n AdapterConfigBase,\n ColumnSchemaConfig,\n DbOptions,\n DbResult,\n DefaultColumnTypes,\n defaultSchemaConfig,\n DefaultSchemaConfig,\n DriverAdapter,\n noop,\n QueryResult,\n QueryResultRow,\n QuerySchema,\n quoteIdentifier,\n RecordUnknown,\n AdapterSchemaConfigOptions,\n} from 'pqb/internal';\nimport { createDbWithAdapter } from 'pqb';\nimport { HackySavepointState } from './adapter';\nimport { parseInterval } from './driver-adapter-shared';\n\nconst schemaConfig: AdapterSchemaConfigOptions = {\n arrayEncode: (input) => {\n return getBun().sql.array(input);\n },\n intervalParse: parseInterval,\n jsonEncodedByDriver: true,\n dateParsedByDriver: true,\n};\n\nexport const bunSchemaConfig = Object.assign(\n () => defaultSchemaConfig(schemaConfig),\n schemaConfig,\n);\n\nexport interface CreateBunDbOptions<\n SchemaConfig extends ColumnSchemaConfig,\n ColumnTypes,\n>\n extends BunAdapterOptions, DbOptions<SchemaConfig, ColumnTypes> {}\n\nexport const createDb = <\n SchemaConfig extends ColumnSchemaConfig = DefaultSchemaConfig,\n ColumnTypes = DefaultColumnTypes<SchemaConfig>,\n>({\n log,\n ...options\n}: CreateBunDbOptions<SchemaConfig, ColumnTypes>): DbResult<ColumnTypes> => {\n return createDbWithAdapter({\n schemaConfig: bunSchemaConfig as unknown as () => SchemaConfig,\n ...options,\n log,\n adapter: new AdapterClass({\n driverAdapter: BunAdapter,\n config: options,\n }),\n });\n};\n\nexport interface BunOptions {\n hostname?: string;\n port?: number;\n database?: string;\n username?: string;\n password?: string | (() => string | Promise<string>);\n max?: number;\n idleTimeout?: number;\n maxLifetime?: number;\n connectionTimeout?: number;\n prepare?: boolean;\n tls?: unknown;\n}\n\nexport interface BunAdapterOptions extends AdapterConfigBase, BunOptions {\n schema?: QuerySchema;\n searchPath?: string;\n ssl?: unknown;\n}\n\ninterface Bun {\n SQL: BunSqlConstructor;\n sql: {\n array(input: unknown): unknown;\n };\n}\n\ninterface BunSqlConstructor {\n new (options?: BunSqlOptions | string): BunSql;\n PostgresError: ErrorClass;\n}\n\ninterface BunSqlOptions extends BunOptions {\n url?: string;\n ssl?: unknown;\n}\n\ninterface BunSql {\n unsafe<Row extends QueryResultRow = QueryResultRow>(\n text: string,\n values?: unknown[],\n ): BunSqlQuery<Row>;\n reserve(): Promise<BunReservedSql>;\n close(options?: { timeout?: number }): Promise<void>;\n}\n\ninterface BunReservedSql extends BunSql {\n release(): void;\n}\n\ninterface BunSqlQuery<\n Row extends QueryResultRow = QueryResultRow,\n> extends PromiseLike<BunSqlResult<Row>> {\n values(): Promise<BunSqlResult<unknown[]>>;\n}\n\ninterface BunSqlResult<Row = unknown> extends Array<Row> {\n count?: number;\n affectedRows?: number;\n}\n\n// oxlint-disable-next-line typescript/no-explicit-any\ntype ErrorClass = new (...args: any[]) => Error;\n\nconst getBun = (): Bun => {\n const Bun = (globalThis as unknown as { Bun?: Bun }).Bun;\n if (!Bun?.SQL) {\n throw new Error('Bun.SQL is only available when running in Bun');\n }\n\n return Bun;\n};\n\nconst getBunPostgresError = (): ErrorClass => {\n return (\n (globalThis as unknown as { Bun?: { SQL: BunSqlConstructor } }).Bun?.SQL\n .PostgresError ?? (Error as ErrorClass)\n );\n};\n\nconst queryClient = <T = QueryResultRow>(\n client: BunSql,\n text: string,\n values?: unknown[],\n arraysMode?: boolean,\n): Promise<QueryResult<T>> => {\n const runQuery = () => {\n const query = client.unsafe(text, values);\n const resultPromise: PromiseLike<BunSqlResult> = arraysMode\n ? query.values()\n : query;\n\n return Promise.resolve(resultPromise).then((result) => {\n return normalizeResult(result, arraysMode) as QueryResult<T>;\n });\n };\n\n // Keep a single transactional connection ordered when savepoints are active.\n const { __lock } = client as unknown as { __lock?: Promise<unknown> };\n if (__lock) {\n let resolve: (() => void) | undefined;\n (client as unknown as RecordUnknown).__lock = new Promise<void>((res) => {\n resolve = res;\n });\n\n return __lock.then(() => {\n const promise = runQuery();\n promise.then(resolve, resolve);\n return promise;\n });\n }\n\n const promise = runQuery();\n\n (client as unknown as { __lock?: Promise<unknown> }).__lock =\n promise.catch(noop);\n\n return promise;\n};\n\nconst borrowBunClient = (pool: BunSql): Promise<BunReservedSql> => {\n return pool.reserve();\n};\n\nconst releaseBunClient = (client: BunReservedSql): void => {\n client.release();\n};\n\nexport const BunAdapter: DriverAdapter = {\n noFieldsForArrays: true,\n manualPool: true,\n schemaConfig,\n errorClass: getBunPostgresError(),\n errorFields: {\n message: 'message',\n severity: 'severity',\n code: 'errno',\n detail: 'detail',\n schema: 'schema',\n table: 'table',\n column: 'column',\n dataType: 'dataType',\n constraint: 'constraint',\n hint: 'hint',\n position: 'position',\n internalPosition: 'internalPosition',\n internalQuery: 'internalQuery',\n where: 'where',\n file: 'file',\n line: 'line',\n routine: 'routine',\n },\n\n configure(config: BunAdapterOptions): BunSql {\n return new (getBun().SQL)(makeOptions(config));\n },\n\n queryClient,\n\n borrow(pool: BunSql): Promise<BunReservedSql> {\n return borrowBunClient(pool);\n },\n\n release(client: BunReservedSql): void {\n releaseBunClient(client);\n },\n\n async begin<DriverClient, Result>(\n pool: BunSql,\n cb: (adapter: DriverClient) => Promise<Result>,\n options?: string,\n ): Promise<Result> {\n const client = await borrowBunClient(pool);\n\n try {\n await queryClient(client, options ? 'BEGIN ' + options : 'BEGIN');\n\n let result;\n try {\n result = await cb(client as DriverClient);\n } catch (err) {\n await queryClient(client, 'ROLLBACK');\n throw err;\n }\n\n await queryClient(client, 'COMMIT');\n return result as Result;\n } finally {\n releaseBunClient(client);\n }\n },\n\n async savepoint<T>(\n client: BunSql,\n // Bun doesn't need to switch the client in a savepoint.\n _setClient: (client: BunSql) => void,\n name: string,\n cb: () => Promise<T>,\n ): Promise<T> {\n const safeName = quoteIdentifier(name);\n try {\n await queryClient(client, `SAVEPOINT ${safeName}`);\n const res = await cb();\n await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);\n return res;\n } catch (err) {\n await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);\n throw err;\n }\n },\n\n async hackySavepoint<T extends QueryResultRow>(\n client: BunSql,\n // Bun doesn't need to switch the client in a savepoint.\n _setClient: (client: BunSql) => void,\n state: HackySavepointState,\n text: string,\n values?: unknown[],\n arraysMode?: boolean,\n ): Promise<QueryResult<T>> {\n const safeName = quoteIdentifier(state.name);\n\n let resolve: (() => void) | undefined;\n let reject: ((err: unknown) => void) | undefined;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n let resultResolve: ((res: QueryResult<T>) => void) | undefined;\n let resultReject: ((err: unknown) => void) | undefined;\n const resultPromise = new Promise<QueryResult<T>>((res, rej) => {\n resultResolve = res;\n resultReject = rej;\n });\n\n const savepointPromise = (async () => {\n try {\n await queryClient(client, `SAVEPOINT ${safeName}`);\n\n try {\n const res = await queryClient<T>(client, text, values, arraysMode);\n resultResolve?.(res as QueryResult<T>);\n } catch (err) {\n resultReject?.(err);\n throw err;\n }\n\n const result = await promise;\n await queryClient(client, `RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (err) {\n await queryClient(client, `ROLLBACK TO SAVEPOINT ${safeName}`);\n throw err;\n }\n })();\n\n state.activeSavepoint = {\n async release() {\n resolve?.();\n await savepointPromise;\n },\n async rollback(err) {\n reject?.(err);\n await savepointPromise.catch(noop);\n },\n };\n\n return resultPromise;\n },\n\n close(pool: BunSql): Promise<void> {\n return pool.close();\n },\n};\n\nconst makeOptions = (config: BunAdapterOptions): BunSqlOptions => {\n const {\n databaseURL,\n host,\n user,\n password,\n port,\n database,\n max,\n idleTimeout,\n maxLifetime,\n connectionTimeout,\n prepare,\n tls,\n ssl,\n setConfig,\n searchPath,\n } = config;\n\n return {\n url:\n databaseURL && makeUrl(databaseURL, setConfig?.search_path ?? searchPath),\n hostname: host,\n username: user,\n password,\n port,\n database,\n max,\n idleTimeout,\n maxLifetime,\n connectionTimeout,\n prepare,\n tls,\n ssl,\n };\n};\n\nconst makeUrl = (databaseURL: string, searchPath?: string): string => {\n if (!searchPath) return databaseURL;\n\n const url = new URL(databaseURL);\n url.searchParams.set('options', `-c search_path=${searchPath}`);\n return url.toString();\n};\n\nconst normalizeResult = <T>(\n result: BunSqlResult<T>,\n arraysMode?: boolean,\n): QueryResult<T> | QueryResult<T>[] => {\n if (!arraysMode && isMultiResult(result)) {\n return result.map((item) => new BunQueryResult(item, arraysMode));\n }\n\n return new BunQueryResult(result, arraysMode);\n};\n\nclass BunQueryResult<T> implements QueryResult<T> {\n public rowCount: number;\n public rows: T[];\n private _fields?: QueryResult<T>['fields'];\n\n constructor(\n private result: BunSqlResult<T>,\n private isArray?: boolean,\n ) {\n this.rowCount = result.count ?? result.affectedRows ?? result.length;\n\n // creating new Array because BunSqlResult is not a real array - can't do spread on it\n this.rows = Array.from(result) as T[];\n }\n\n get fields(): QueryResult<T>['fields'] {\n if (this.isArray) {\n throw new Error('Bun does not support fields on array result');\n }\n\n return (this._fields ??= getFields(this.result));\n }\n}\n\nconst isMultiResult = <T>(\n result: BunSqlResult<T>,\n): result is BunSqlResult<T>[] & BunSqlResult<T> => {\n return Array.isArray(result[0]);\n};\n\nconst getFields = <T>(rows: T[]): { name: string }[] => {\n const first = rows[0];\n if (!first || Array.isArray(first) || typeof first !== 'object') return [];\n\n return Object.keys(first).map((name) => ({ name }));\n};\n"],"mappings":"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"}
|