@prisma/ppg
Version:
Lightweight client for Prisma Postgres
1,229 lines (1,215 loc) • 38.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
var __typeError = (msg) => {
throw TypeError(msg);
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __using = (stack, value, async) => {
if (value != null) {
if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
var dispose, inner;
if (async) dispose = value[__knownSymbol("asyncDispose")];
if (dispose === void 0) {
dispose = value[__knownSymbol("dispose")];
if (async) inner = dispose;
}
if (typeof dispose !== "function") __typeError("Object not disposable");
if (inner) dispose = function() {
try {
inner.call(this);
} catch (e) {
return Promise.reject(e);
}
};
stack.push([async, dispose, value]);
} else if (async) {
stack.push([async]);
}
return value;
};
var __callDispose = (stack, error, hasError) => {
var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
};
var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
var next = (it) => {
while (it = stack.pop()) {
try {
var result = it[1] && it[1].call(it[2]);
if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
} catch (e) {
fail(e);
}
}
if (hasError) throw error;
};
return next();
};
// src/index.ts
var index_exports = {};
__export(index_exports, {
BINARY: () => BINARY,
DatabaseError: () => DatabaseError,
GenericError: () => GenericError,
HttpResponseError: () => HttpResponseError,
TEXT: () => TEXT,
ValidationError: () => ValidationError,
WebSocketError: () => WebSocketError,
boundedByteStreamParameter: () => boundedByteStreamParameter,
byteArrayParameter: () => byteArrayParameter,
client: () => client,
defaultClientConfig: () => defaultClientConfig,
prismaPostgres: () => prismaPostgres
});
module.exports = __toCommonJS(index_exports);
// src/common/types.ts
var BINARY = "binary";
var TEXT = "text";
function boundedByteStreamParameter(readableStream, format, byteLength) {
return Object.assign(readableStream, { byteLength, format });
}
function isBoundedByteStreamParameter(x) {
return x instanceof ReadableStream && "byteLength" in x && typeof x.byteLength === "number" && hasFormat(x);
}
function hasFormat(x) {
return "format" in x && (x.format === TEXT || x.format === BINARY);
}
function byteArrayParameter(array, format) {
return Object.assign(array, { format });
}
function isByteArrayParameter(x) {
return x instanceof Uint8Array && hasFormat(x);
}
function toCollectableIterator(iterator, transform) {
let collected = false;
const transformFn = transform ?? ((item) => item);
const collectableIterator = {
async next() {
if (collected) {
return { value: void 0, done: true };
}
const result = await iterator.next();
if (result.done) {
return { value: void 0, done: true };
}
return { value: transformFn(result.value), done: false };
},
async collect() {
if (collected) {
return [];
}
collected = true;
const results = [];
for await (const item of iterator) {
results.push(transformFn(item));
}
return results;
},
[Symbol.asyncIterator]() {
return collectableIterator;
},
async return(value) {
collected = true;
await iterator.return?.(value);
return { value: void 0, done: true };
},
async throw(error) {
collected = true;
await iterator.throw?.(error);
return Promise.reject(error);
}
};
return collectableIterator;
}
var GenericError = class extends Error {
constructor(msg, opts) {
super(msg, opts);
this.name = new.target.name;
}
};
var ValidationError = class extends GenericError {
constructor(msg, opts) {
super(msg, opts);
this.name = new.target.name;
}
};
var HttpResponseError = class extends GenericError {
/** HTTP status code from the failed request */
status;
constructor({ message, statusCode }, opts) {
super(message, opts);
this.name = new.target.name;
this.status = statusCode;
}
};
var WebSocketError = class extends GenericError {
/** WebSocket closure code (e.g., 1000 for normal closure) */
closureCode;
/** Human-readable closure reason */
closureReason;
constructor({ message, closureCode, closureReason }, opts) {
super(`${message}${closureStr(closureCode, closureReason)}`, opts);
this.name = new.target.name;
this.closureCode = closureCode;
this.closureReason = closureReason;
}
};
function closureStr(closureCode, closureReason) {
return !closureCode && !closureReason ? "" : ` (${closureCode} : ${closureReason})`;
}
var DatabaseError = class extends GenericError {
/** PostgreSQL error code (SQLSTATE) */
code;
/** Additional error details from PostgreSQL (severity, hint, etc.) */
details;
constructor(details, opts) {
super(details.message, opts);
this.code = details.code;
this.details = { ...details };
delete this.details.code;
delete this.details.message;
this.name = new.target.name;
}
};
// src/transport/shims.ts
async function* emptyIterableIterator() {
}
function createDeferred() {
if ("withResolvers" in Promise && typeof Promise.withResolvers === "function") {
return Promise.withResolvers();
}
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
async function createWebSocket(url, protocols) {
if (typeof WebSocket !== "undefined") {
return new WebSocket(url, protocols);
}
try {
const WS = await import("ws");
const WebSocketImpl = WS.WebSocket || WS.default;
return new WebSocketImpl(url, protocols);
} catch (error) {
throw new GenericError(
'WebSocket is not available. For Node.js < 21, please install the "ws" package: npm install ws',
{ cause: error }
);
}
}
var MAX_BUFFERED_AMOUNT = 1024 * 1024;
function wsBusyCheck(ws) {
return "bufferedAmount" in ws && typeof ws.bufferedAmount === "number" ? () => ws.bufferedAmount > MAX_BUFFERED_AMOUNT : () => false;
}
var hasBufferByteLength = typeof Buffer !== "undefined" && typeof Buffer.byteLength === "function";
function utf8ByteLength(str) {
if (hasBufferByteLength) {
return Buffer.byteLength(str, "utf8");
}
let bytes = 0;
for (let i = 0; i < str.length; i++) {
let codePoint = str.charCodeAt(i);
if (codePoint >= 55296 && codePoint <= 56319) {
const next = str.charCodeAt(i + 1);
if (next >= 56320 && next <= 57343) {
codePoint = (codePoint - 55296 << 10) + (next - 56320) + 65536;
i++;
}
}
if (codePoint <= 127) {
bytes += 1;
} else if (codePoint <= 2047) {
bytes += 2;
} else if (codePoint <= 65535) {
bytes += 3;
} else {
bytes += 4;
}
}
return bytes;
}
// src/transport/frames.ts
function isNonNull(x) {
return !!x && typeof x === "object";
}
function isQueryDescriptorFrame(frame) {
return isNonNull(frame) && ("query" in frame && typeof frame.query === "string" || "exec" in frame && typeof frame.exec === "string");
}
function isExtendedParamFrame(frame) {
return isNonNull(frame) && "type" in frame && "data" in frame && (frame.type === "text" || frame.type === "binary");
}
function isDataRowDescription(frame) {
return isNonNull(frame) && "columns" in frame && Array.isArray(frame.columns);
}
function isDataRow(frame) {
return isNonNull(frame) && "values" in frame && Array.isArray(frame.values);
}
function isCommandComplete(frame) {
return isNonNull(frame) && "complete" in frame && frame.complete === true;
}
function isErrorFrame(frame) {
return isNonNull(frame) && "error" in frame && typeof frame.error === "object" && frame.error !== null && "message" in frame.error && typeof frame.error.message === "string";
}
var EXENDED_PARAM_SIZE_THRESHOLD = 1 << 10;
async function streamToBase64(stream) {
const reader = stream.getReader();
const chunks = [];
let totalLength = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
totalLength += value.length;
}
const combined = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
combined.set(chunk, offset);
offset += chunk.length;
}
const binaryString = Array.from(combined, (byte) => String.fromCharCode(byte)).join("");
return btoa(binaryString);
}
async function requestFrames(kind, sql, rawParams) {
const queryParams = [];
const extendedFrames = [];
for (const param of rawParams) {
if (typeof param === "string") {
const byteLength = utf8ByteLength(param);
if (byteLength <= EXENDED_PARAM_SIZE_THRESHOLD) {
queryParams.push({
type: TEXT,
value: param
});
} else {
queryParams.push({
type: TEXT,
byteSize: byteLength
});
const stringStream = new ReadableStream({
start(controller) {
controller.enqueue(param);
controller.close();
}
});
const encoderStream = new TextEncoderStream();
const encodedStream = stringStream.pipeThrough(encoderStream);
const boundedStream = boundedByteStreamParameter(encodedStream, TEXT, byteLength);
extendedFrames.push({
type: TEXT,
data: boundedStream
});
}
} else if (isByteArrayParameter(param)) {
const byteLength = param.byteLength;
const format = param.format;
if (byteLength <= EXENDED_PARAM_SIZE_THRESHOLD) {
if (format === TEXT) {
const textValue = new TextDecoder().decode(param);
queryParams.push({
type: TEXT,
value: textValue
});
} else {
const binaryString = Array.from(param, (byte) => String.fromCharCode(byte)).join("");
const base64 = btoa(binaryString);
queryParams.push({
type: BINARY,
value: base64
});
}
} else {
queryParams.push({
type: format,
byteSize: byteLength
});
extendedFrames.push({
type: format,
data: param
});
}
} else if (isBoundedByteStreamParameter(param)) {
const format = param.format;
if (param.byteLength <= EXENDED_PARAM_SIZE_THRESHOLD) {
if (format === TEXT) {
const chunks = [];
const reader = param.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const combined = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
let offset = 0;
for (const chunk of chunks) {
combined.set(chunk, offset);
offset += chunk.length;
}
const textValue = new TextDecoder().decode(combined);
queryParams.push({
type: TEXT,
value: textValue
});
} else {
const base64 = await streamToBase64(param);
queryParams.push({
type: BINARY,
value: base64
});
}
} else {
queryParams.push({
type: format,
byteSize: param.byteLength
});
extendedFrames.push({
type: format,
data: param
});
}
} else if (param === null) {
queryParams.push({
type: TEXT,
value: null
});
} else {
throw new ValidationError(`unsupported raw parameter type: ${param}`);
}
}
const parameters = queryParams.length > 0 ? queryParams : void 0;
const queryDescriptor = kind === "query" ? { query: sql, parameters } : { exec: sql, parameters };
return [queryDescriptor, ...extendedFrames];
}
// src/transport/shared.ts
var FRAME_URNS = {
queryUrn: "urn:prisma:query",
queryDescriptorUrn: "urn:prisma:query:descriptor",
binaryParamUrn: "urn:prisma:query:param:binary",
textParamUrn: "urn:prisma:query:param:text",
dataRowDescriptionUrn: "urn:prisma:query:result:description",
dataRowUrn: "urn:prisma:query:result:datarow",
commandCompleteUrn: "urn:prisma:query:result:complete",
errorUrn: "urn:prisma:query:result:error"
};
var MIME_TYPES = {
applicationJson: "application/json",
applicationNdJson: "application/x-ndjson",
applicationOctetStream: "application/octet-stream",
multipartFormData: "multipart/form-data",
multipartMixed: "multipart/mixed",
textPlain: "text/plain"
};
// src/transport/multipart.ts
function createMultipartStream(frames, boundary) {
const textEncoder = new TextEncoder();
async function* multipartGenerator() {
for (const frame of frames) {
yield textEncoder.encode(`--${boundary}\r
`);
if (isQueryDescriptorFrame(frame)) {
const descriptor = frame;
yield textEncoder.encode(`Content-Disposition: form-data; name="${FRAME_URNS.queryDescriptorUrn}"\r
`);
yield textEncoder.encode(
`Content-Type: ${MIME_TYPES.applicationJson}; profile="${FRAME_URNS.queryDescriptorUrn}"\r
\r
`
);
yield textEncoder.encode(`${JSON.stringify(descriptor)}\r
`);
} else if (isExtendedParamFrame(frame)) {
const extendedParam = frame;
if (extendedParam.type === "text") {
yield textEncoder.encode(`Content-Disposition: form-data; name="${FRAME_URNS.textParamUrn}"\r
`);
yield textEncoder.encode(
`Content-Type: ${MIME_TYPES.textPlain}; charset=utf-8; profile="${FRAME_URNS.textParamUrn}"\r
\r
`
);
const param = extendedParam.data;
if (typeof param === "string") {
yield textEncoder.encode(param);
yield textEncoder.encode("\r\n");
} else if (isByteArrayParameter(param)) {
yield param;
yield textEncoder.encode("\r\n");
} else if (isBoundedByteStreamParameter(param)) {
const reader = param.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
} finally {
reader.releaseLock();
}
yield textEncoder.encode("\r\n");
} else {
throw new ValidationError(
`Unsupported text extended parameter data type. Expected string, ByteArrayParameter, or BoundedByteStream, got: ${typeof param}`
);
}
} else {
yield textEncoder.encode(`Content-Disposition: form-data; name="${FRAME_URNS.binaryParamUrn}"\r
`);
yield textEncoder.encode(
`Content-Type: ${MIME_TYPES.applicationOctetStream}; profile="${FRAME_URNS.binaryParamUrn}"\r
\r
`
);
if (typeof extendedParam.data === "string") {
yield textEncoder.encode(extendedParam.data);
yield textEncoder.encode("\r\n");
} else if (isByteArrayParameter(extendedParam.data)) {
yield extendedParam.data;
yield textEncoder.encode("\r\n");
} else if (isBoundedByteStreamParameter(extendedParam.data)) {
const reader = extendedParam.data.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield value;
}
} finally {
reader.releaseLock();
}
yield textEncoder.encode("\r\n");
} else {
throw new ValidationError(
`Unsupported binary extended parameter data type. Expected string, ByteArrayParameter, or BoundedByteStream, got: ${typeof extendedParam.data}`
);
}
}
} else {
throw new ValidationError(
`Unsupported frame type. Expected QueryDescriptorFrame or ExtendedParamFrame, got: ${JSON.stringify(frame)}`
);
}
}
yield textEncoder.encode(`--${boundary}--\r
`);
}
const iterator = multipartGenerator();
return new ReadableStream({
async pull(controller) {
try {
const { done, value } = await iterator.next();
if (done) {
controller.close();
} else {
controller.enqueue(value);
}
} catch (error) {
controller.error(error);
}
},
async cancel(reason) {
await iterator.return?.(reason);
}
});
}
// src/transport/ndjson.ts
async function parseNDJSONResponse(response) {
if (!response.body) {
throw new Error("Response body is null");
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let columns = [];
async function* rowGenerator() {
try {
while (true) {
const { done, value } = await reader.read();
if (value) {
buffer += decoder.decode(value, { stream: !done });
}
const lines = buffer.split("\n");
if (!done) {
buffer = lines.pop() || "";
} else {
buffer = "";
}
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
const frame = JSON.parse(trimmed);
if (isDataRowDescription(frame)) {
columns = frame.columns.map((col) => ({ name: col.name, oid: col.typeOid }));
} else if (isDataRow(frame)) {
yield frame.values;
} else if (isCommandComplete(frame)) {
return;
} else if (isErrorFrame(frame)) {
throw new DatabaseError(frame.error);
}
}
if (done) {
break;
}
}
} finally {
reader.releaseLock();
}
}
const generator = rowGenerator();
const firstResult = await generator.next();
async function* rowsWithFirst() {
if (!firstResult.done && firstResult.value !== void 0) {
yield firstResult.value;
}
yield* generator;
}
const rows = toCollectableIterator(rowsWithFirst());
return {
columns,
rows
};
}
// src/transport/http.ts
var HTTP_REQUEST_PATH = "/v0/statement";
function httpTransport(config) {
async function statement(kind, sql, parameters) {
const frames = await requestFrames(kind, sql, parameters);
const url = new URL(HTTP_REQUEST_PATH, config.endpoint);
if (config.database) {
url.searchParams.set("db", config.database);
}
const authHeader = `Basic ${btoa(`${config.username}:${config.password}`)}`;
const headers = {
Authorization: authHeader
};
return request({ url: url.toString(), headers, frames, keepalive: config.keepalive ?? false });
}
return { statement };
}
async function request({ headers, keepalive, frames, url }) {
const boundary = `----PPGBoundary${Date.now()}${Math.random().toString(36).substring(2)}`;
const bodyStream = createMultipartStream(frames, boundary);
const requestInit = {
method: "POST",
headers: {
...headers,
"Content-Type": `${MIME_TYPES.multipartFormData}; profile="${FRAME_URNS.queryUrn}"; boundary=${boundary}`
},
body: bodyStream,
duplex: "half",
keepalive
// node doesn't seem to support it, throws a TypeError when this is true
};
const response = await fetch(url, requestInit);
if (!response.ok) {
const responseText = await response.text();
const message = responseText || `HTTP error ${response.status}: ${response.statusText}`;
throw new HttpResponseError({ message, statusCode: response.status });
}
return parseNDJSONResponse(response);
}
// src/transport/query-queue.ts
function newRunningQuery() {
const statementDeferred = createDeferred();
const rowBuffer = [];
let completed = false;
let rowDeferred;
let columns = [];
function tryResolveRow(value) {
if (rowDeferred) {
rowDeferred.resolve(value);
rowDeferred = null;
return true;
}
return false;
}
function tryRejectRow(error2) {
if (rowDeferred) {
rowDeferred.reject(error2);
rowDeferred = null;
return true;
}
return false;
}
async function* rowGenerator() {
while (true) {
if (rowBuffer.length > 0) {
yield rowBuffer.shift();
continue;
}
if (completed) {
break;
}
rowDeferred = createDeferred();
const result = await rowDeferred.promise;
if (result.done) break;
yield result.value;
}
}
function rowDescription(descr) {
columns = descr.columns.map((col) => ({
name: col.name,
oid: col.typeOid
}));
statementDeferred.resolve({
columns,
rows: toCollectableIterator(rowGenerator())
});
}
function dataRow(row) {
if (!tryResolveRow({ value: row.values, done: false })) {
rowBuffer.push(row.values);
}
}
function error({ error: error2 }) {
const err = new DatabaseError(error2);
abort(err);
}
function complete() {
completed = true;
tryResolveRow({ value: void 0, done: true });
statementDeferred.resolve({
columns,
rows: toCollectableIterator(emptyIterableIterator())
});
}
function abort(err) {
if (!tryRejectRow(err)) {
statementDeferred.reject(err);
}
}
return {
rowDescription,
dataRow,
error,
complete,
abort,
statementPromise: statementDeferred.promise
};
}
function newQueryQueue() {
const queryQueue = [];
function enqueueNew() {
const query = newRunningQuery();
queryQueue.push(query);
return {
promise: query.statementPromise,
abort: query.abort
};
}
function processFrame(urn, payload) {
if (queryQueue.length === 0) return;
const currentQuery = queryQueue[0];
if (urn === FRAME_URNS.dataRowDescriptionUrn && isDataRowDescription(payload)) {
currentQuery.rowDescription(payload);
} else if (urn === FRAME_URNS.dataRowUrn && isDataRow(payload)) {
currentQuery.dataRow(payload);
} else if (urn === FRAME_URNS.commandCompleteUrn && isCommandComplete(payload)) {
currentQuery.complete();
queryQueue.shift();
} else if (urn === FRAME_URNS.errorUrn && isErrorFrame(payload)) {
currentQuery.error(payload);
queryQueue.shift();
} else {
const protocolError = new Error(`Protocol error: unexpected frame URN '${urn}' for current query state`);
abortAll(protocolError);
}
}
function abortAll(error) {
for (const q of queryQueue) {
q.abort(error);
}
queryQueue.length = 0;
}
function isEmpty() {
return queryQueue.length === 0;
}
return {
isEmpty,
enqueueNew,
processFrame,
abortAll
};
}
// src/transport/websocket-conn.ts
var WS_REQUEST_PATH = "/v0/session";
var WS_SUBPROTOCOL = "prisma-postgres-1.0";
var MAX_BACKOFF_MS = 100;
async function wsTransportConnection(config) {
const url = new URL(WS_REQUEST_PATH, config.endpoint);
url.protocol = url.protocol.replace("http", "ws");
if (config.database) {
url.searchParams.set("db", config.database);
}
const ws = await createWebSocket(url.toString(), WS_SUBPROTOCOL);
ws.binaryType = "arraybuffer";
const authDeferred = createDeferred();
const queryQueue = newQueryQueue();
let expectingUrn = true;
let currentUrn = null;
const authFrame = {
username: config.username,
password: config.password
};
ws.onopen = () => {
ws.send(JSON.stringify(authFrame));
authDeferred.resolve(conn);
};
ws.onmessage = (event) => {
if (typeof event.data !== "string") {
const error = new Error(`Protocol error: expected text message, received ${typeof event.data}`);
queryQueue.abortAll(error);
ws.close(1002, "Protocol error: binary messages not supported");
return;
}
if (expectingUrn) {
currentUrn = event.data;
expectingUrn = false;
return;
}
const payload = JSON.parse(event.data);
queryQueue.processFrame(currentUrn, payload);
currentUrn = null;
expectingUrn = true;
};
ws.onerror = (event) => {
const errorMsg = "message" in event ? String(event.message) : "WebSocket error";
const error = new WebSocketError({ message: errorMsg });
authDeferred.reject(error);
queryQueue.abortAll(error);
};
ws.onclose = (event) => {
authDeferred.resolve(conn);
const err = new WebSocketError({
message: "WebSocket connection closed",
closureCode: event.code,
closureReason: event.reason
});
authDeferred.reject(err);
queryQueue.abortAll(err);
};
const isBusy = wsBusyCheck(ws);
const conn = {
isReady() {
return ws.readyState === WebSocket.OPEN;
},
enqueueNewQuery() {
return queryQueue.enqueueNew();
},
async send(data) {
let backoffMs = 5;
while (isBusy()) {
await new Promise((resolve) => setTimeout(resolve, backoffMs));
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
}
ws.send(data);
},
close() {
ws.close(1e3, "Normal closure");
}
};
return authDeferred.promise;
}
// src/transport/websocket.ts
function webSocketTransport(config) {
let conn;
let sendQueue = Promise.resolve();
const transport = {
async connect() {
conn ??= await wsTransportConnection(config);
},
async statement(kind, sql, parameters) {
if (!conn?.isReady()) {
throw new WebSocketError({ message: "WebSocket is not connected" });
}
const frames = await requestFrames(kind, sql, parameters);
const enqueuedQuery = conn.enqueueNewQuery();
const activeConn = conn;
sendQueue = sendQueue.then(() => sendFrames(activeConn, frames)).catch(enqueuedQuery.abort);
await sendQueue;
return enqueuedQuery.promise;
},
close() {
conn?.close();
},
isConnected() {
return !!conn?.isReady();
},
[Symbol.dispose]() {
this.close();
}
};
return transport;
}
async function sendFrames(conn, frames) {
for (const frame of frames) {
if ("query" in frame || "exec" in frame) {
await conn.send(FRAME_URNS.queryDescriptorUrn);
await conn.send(JSON.stringify(frame));
} else {
const isTextParam = frame.type === "text";
await conn.send(isTextParam ? FRAME_URNS.textParamUrn : FRAME_URNS.binaryParamUrn);
if (typeof frame.data === "string") {
await conn.send(frame.data);
} else if (frame.data instanceof Uint8Array) {
await conn.send(frame.data);
} else if (frame.data instanceof ReadableStream) {
if (isTextParam) {
const text = await consumeStreamToString(frame.data);
await conn.send(text);
} else {
const bytes = await consumeStreamToUint8Array(frame.data);
await conn.send(bytes);
}
} else {
throw new ValidationError(`Unsupported extended parameter data type: ${typeof frame.data}`);
}
}
}
}
async function consumeStreamToUint8Array(stream) {
const chunks = [];
let totalLength = 0;
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
totalLength += value.length;
}
} finally {
reader.releaseLock();
}
const result = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
async function consumeStreamToString(stream) {
const decoder = new TextDecoder();
let result = "";
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
result += decoder.decode(value, { stream: true });
}
result += decoder.decode();
} finally {
reader.releaseLock();
}
return result;
}
// src/api/client.ts
function parseConnectionString(connectionString) {
const url = new URL(connectionString);
if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") {
throw new Error(`Invalid connection string protocol: ${url.protocol}. Expected "postgres:" or "postgresql:"`);
}
const username = url.username;
const password = url.password;
const hostname = url.hostname;
const database = url.pathname.slice(1) || void 0;
if (!username || !password) {
throw new Error("Connection string must include username and password");
}
const endpoint = `https://${hostname}`;
return {
endpoint,
username,
password,
database
};
}
function defaultClientConfig(connectionString) {
return {
connectionString,
parsers: DEFAULT_PARSERS.map((p) => ({ ...p })),
serializers: DEFAULT_SERIALIZERS.map((s) => ({ ...s }))
};
}
var passThrough = (v) => v;
var nullPassThrough = (fn) => (v) => v === null ? null : fn(v);
var DEFAULT_PARSERS = [
// Boolean
{ oid: 16, parse: (value) => value === "t" },
// Int2 (smallint)
{ oid: 21, parse: nullPassThrough((value) => Number.parseInt(value, 10)) },
// Int4 (integer)
{ oid: 23, parse: nullPassThrough((value) => Number.parseInt(value, 10)) },
// Int8 (bigint) - parse as BigInt to preserve precision
{ oid: 20, parse: nullPassThrough(BigInt) },
// Float4 (real)
{ oid: 700, parse: nullPassThrough((value) => Number.parseFloat(value)) },
// Float8 (double precision)
{ oid: 701, parse: nullPassThrough((value) => Number.parseFloat(value)) },
// Text
{ oid: 25, parse: passThrough },
// Varchar
{ oid: 1043, parse: passThrough },
// JSON
{ oid: 114, parse: nullPassThrough((value) => JSON.parse(value)) },
// JSONB
{ oid: 3802, parse: nullPassThrough((value) => JSON.parse(value)) }
];
var serializeToString = (x) => x.toString();
var DEFAULT_SERIALIZERS = [
// Date serializer
{
supports: (value) => value instanceof Date,
serialize: (value) => value.toISOString()
},
// BigInt serializer
{
supports: (value) => typeof value === "bigint",
serialize: serializeToString
},
// Boolean serializer
{
supports: (value) => typeof value === "boolean",
serialize: (value) => value ? "t" : "f"
},
// Number serializer
{
supports: (value) => typeof value === "number",
serialize: serializeToString
}
];
function client(config) {
const transportConfig = "transportConfig" in config ? config.transportConfig : (
// override the config for testing
parseConnectionString(config.connectionString.toString())
);
const parsersMap = (config.parsers || []).reduce(
(map, parser) => map.set(parser.oid, parser),
/* @__PURE__ */ new Map()
);
const parsers = [...parsersMap.values()];
const serializers = config.serializers || [];
const transport = httpTransport(transportConfig);
const statements = createStatements(transport, serializers, parsers);
async function newSession(sessionConfig) {
const sessionTransport = webSocketTransport(transportConfig);
await sessionTransport.connect();
const sessionParsersMap = sessionConfig?.parsers?.reduce((map, parser) => map.set(parser.oid, parser), new Map(parsersMap)) ?? new Map(parsersMap);
const sessionParsers = [...sessionParsersMap.values()];
const sessionSerializers = [...sessionConfig?.serializers || [], ...config.serializers || []];
const sessionStatements = createStatements(sessionTransport, sessionSerializers, sessionParsers);
const session = {
...sessionStatements,
close() {
sessionTransport.close();
},
get active() {
return sessionTransport.isConnected();
},
[Symbol.dispose]() {
this.close();
}
};
return session;
}
return {
...statements,
newSession
};
}
function createStatements(transport, serializers, parsers) {
function transformResponse(response) {
const columns = response.columns.map((col) => ({
name: col.name,
oid: col.oid
}));
const rows = toCollectableIterator(response.rows, (rawRow) => parseSessionRow(parsers, columns, rawRow));
return { columns, rows };
}
return {
async query(sql, ...params) {
const rawParams = serializeSessionParams(serializers, params);
const response = await transport.statement("query", sql, rawParams);
return transformResponse(response);
},
async exec(sql, ...params) {
const rawParams = serializeSessionParams(serializers, params);
const response = await transport.statement("exec", sql, rawParams);
const firstRow = await response.rows.next();
assertRowAffectedResult(firstRow);
return Number.parseInt(firstRow.value[0], 10);
}
};
}
function serializeSessionParams(serializers, params) {
return params.map((param) => {
if (param === null || param === void 0) {
return null;
}
const serializer = serializers.find((s) => s.supports(param));
return serializer ? serializer.serialize(param) : typeof param === "string" ? param : String(param);
});
}
function parseSessionRow(parsers, columns, rawValues) {
const values = rawValues.map((value, index) => {
const columnOid = columns[index].oid;
const parser = parsers.find((p) => p.oid === columnOid);
return parser ? parser.parse(value) : value;
});
return { values };
}
function assertRowAffectedResult(x) {
if (!x || x.done || x.value?.length !== 1 || !x.value[0] || !/^\d+$/.test(x.value[0])) {
throw new Error(`Protocol error: missing rowsAffected value in exec response: ${JSON.stringify(x)}`);
}
}
// src/api/ppg.ts
function prismaPostgres(config) {
const lowLevelClient = client(config);
const prismaStatements = createPrismaStatements(lowLevelClient);
async function transaction(callback) {
var _stack = [];
try {
const session = __using(_stack, await lowLevelClient.newSession());
const sessionStatements = createPrismaStatements(session);
try {
const beginPromise = session.exec("BEGIN");
const result = await Promise.all([beginPromise, callback(sessionStatements)]);
await session.exec("COMMIT");
return result[1];
} catch (error) {
await session.exec("ROLLBACK");
throw error;
}
} catch (_) {
var _error = _, _hasError = true;
} finally {
__callDispose(_stack, _error, _hasError);
}
}
function batchWithQueries(...statements) {
return transaction(async (stmt) => {
const results = [];
for (const statement of statements) {
if ("query" in statement) {
const rows = await stmt.query(statement.query, ...statement.parameters || []).collect();
results.push(rows);
} else {
const affected = await stmt.exec(statement.exec, ...statement.parameters || []);
results.push(affected);
}
}
return results;
});
}
function batch(...queries) {
if (queries.length === 0) {
return createBatchQueryBuilder([]);
}
return batchWithQueries(...queries);
}
function createBatchQueryBuilder(statements) {
return {
query(sql, ...params) {
return createBatchQueryBuilder([...statements, { query: sql, parameters: params }]);
},
exec(sql, ...params) {
return createBatchQueryBuilder([...statements, { exec: sql, parameters: params }]);
},
run() {
return batchWithQueries(...statements);
}
};
}
return {
...prismaStatements,
transaction,
batch
};
}
function templateToSqlParams(strings, values) {
let sql = strings[0];
for (let i = 0; i < values.length; i++) {
sql += `$${i + 1}${strings[i + 1]}`;
}
return [sql, values];
}
function rowToObject(columns, values) {
const obj = {};
for (let i = 0; i < columns.length; i++) {
obj[columns[i].name] = values[i];
}
return obj;
}
function createPrismaStatements(statements) {
async function* rowObjectsGenerator(sql, params) {
const result = await statements.query(sql, ...params);
for await (const row of result.rows) {
yield rowToObject(result.columns, row.values);
}
}
function sqlTag(strings, ...values) {
const [sql, params] = templateToSqlParams(strings, values);
return toCollectableIterator(rowObjectsGenerator(sql, params));
}
sqlTag.exec = async (strings, ...values) => {
const [sql, params] = templateToSqlParams(strings, values);
return statements.exec(sql, ...params);
};
return {
sql: sqlTag,
query(sql, ...params) {
return toCollectableIterator(rowObjectsGenerator(sql, params));
},
async exec(sql, ...params) {
return statements.exec(sql, ...params);
}
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BINARY,
DatabaseError,
GenericError,
HttpResponseError,
TEXT,
ValidationError,
WebSocketError,
boundedByteStreamParameter,
byteArrayParameter,
client,
defaultClientConfig,
prismaPostgres
});