@prisma-next/postgres 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -0
- package/dist/runtime.d.mts +58 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +137 -0
- package/dist/runtime.mjs.map +1 -0
- package/package.json +64 -0
- package/src/exports/runtime.ts +9 -0
- package/src/runtime/binding.ts +115 -0
- package/src/runtime/postgres.ts +186 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @prisma-next/postgres
|
|
2
|
+
|
|
3
|
+
One-liner lazy Postgres client for Prisma Next runtime composition.
|
|
4
|
+
|
|
5
|
+
## Package Classification
|
|
6
|
+
|
|
7
|
+
- **Domain**: targets
|
|
8
|
+
- **Layer**: clients
|
|
9
|
+
- **Plane**: runtime
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
`@prisma-next/postgres/runtime` exposes a single `postgres(...)` helper that composes the SQL execution stack for Postgres and returns static query roots immediately:
|
|
14
|
+
|
|
15
|
+
- `db.sql`
|
|
16
|
+
- `db.schema`
|
|
17
|
+
- `db.context`
|
|
18
|
+
- `db.stack`
|
|
19
|
+
|
|
20
|
+
Runtime and connection resources are deferred until `await db.runtime()` is called. The getter returns `Promise<Runtime>`.
|
|
21
|
+
|
|
22
|
+
When URL binding is used, pool timeouts are configurable via `poolOptions`:
|
|
23
|
+
|
|
24
|
+
- `poolOptions.connectionTimeoutMillis` (default `20_000`)
|
|
25
|
+
- `poolOptions.idleTimeoutMillis` (default `30_000`)
|
|
26
|
+
|
|
27
|
+
## Responsibilities
|
|
28
|
+
|
|
29
|
+
- Build a static Postgres execution stack from target, adapter, and driver descriptors
|
|
30
|
+
- Build static query roots from the execution context
|
|
31
|
+
- Normalize runtime binding input (`binding`, `url`, `pg`)
|
|
32
|
+
- Lazily instantiate stack and driver on first `await db.runtime()` call
|
|
33
|
+
- Connect driver with resolved binding before creating runtime
|
|
34
|
+
- Memoize runtime so repeated `await db.runtime()` calls return one instance
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
|
|
38
|
+
- `@prisma-next/sql-runtime` for stack/context/runtime primitives
|
|
39
|
+
- `@prisma-next/core-execution-plane` for stack instantiation
|
|
40
|
+
- `@prisma-next/target-postgres` for target descriptor
|
|
41
|
+
- `@prisma-next/adapter-postgres` for adapter descriptor
|
|
42
|
+
- `@prisma-next/driver-postgres` for driver descriptor
|
|
43
|
+
- `@prisma-next/sql-lane` for `sql(...)`
|
|
44
|
+
- `@prisma-next/sql-relational-core` for `schema(...)`
|
|
45
|
+
- `@prisma-next/sql-contract` for `validateContract(...)` and contract types
|
|
46
|
+
- `pg` for binding validation when using `pg` (Pool or Client) input
|
|
47
|
+
|
|
48
|
+
## Architecture
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
flowchart TD
|
|
52
|
+
App[App Code] --> Client[postgres(...)]
|
|
53
|
+
Client --> Static[Static roots: sql schema context stack]
|
|
54
|
+
Client --> Lazy[runtime()]
|
|
55
|
+
|
|
56
|
+
Lazy --> Instantiate[instantiateExecutionStack]
|
|
57
|
+
Lazy --> Bind[Resolve binding: url or pg]
|
|
58
|
+
Bind --> Assert[Assert stackInstance.driver]
|
|
59
|
+
Assert --> Connect[driver.connect binding]
|
|
60
|
+
Connect --> Runtime[createRuntime]
|
|
61
|
+
|
|
62
|
+
Runtime --> Target[@prisma-next/target-postgres]
|
|
63
|
+
Runtime --> Adapter[@prisma-next/adapter-postgres]
|
|
64
|
+
Runtime --> Driver[@prisma-next/driver-postgres]
|
|
65
|
+
Runtime --> SqlRuntime[@prisma-next/sql-runtime]
|
|
66
|
+
Runtime --> ExecPlane[@prisma-next/core-execution-plane]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Related Docs
|
|
70
|
+
|
|
71
|
+
- Spec: `agent-os/specs/2026-02-10-postgres-one-liner-lazy-client/spec.md`
|
|
72
|
+
- Architecture: `docs/Architecture Overview.md`
|
|
73
|
+
- Subsystem: `docs/architecture docs/subsystems/4. Runtime & Plugin Framework.md`
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PostgresBinding, PostgresBinding as PostgresBinding$1, PostgresDriverCreateOptions } from "@prisma-next/driver-postgres/runtime";
|
|
2
|
+
import { SelectBuilder } from "@prisma-next/sql-lane";
|
|
3
|
+
import { SchemaHandle } from "@prisma-next/sql-relational-core/schema";
|
|
4
|
+
import { ExecutionContext, Plugin, Runtime, RuntimeVerifyOptions, SqlExecutionStackWithDriver, SqlRuntimeExtensionDescriptor } from "@prisma-next/sql-runtime";
|
|
5
|
+
import { Client, Pool } from "pg";
|
|
6
|
+
import { ExtractCodecTypes, ExtractOperationTypes, SqlContract, SqlStorage } from "@prisma-next/sql-contract/types";
|
|
7
|
+
import { OperationTypeSignature, OperationTypes } from "@prisma-next/sql-relational-core/types";
|
|
8
|
+
|
|
9
|
+
//#region src/runtime/binding.d.ts
|
|
10
|
+
type PostgresBindingInput = {
|
|
11
|
+
readonly binding: PostgresBinding$1;
|
|
12
|
+
readonly url?: never;
|
|
13
|
+
readonly pg?: never;
|
|
14
|
+
} | {
|
|
15
|
+
readonly url: string;
|
|
16
|
+
readonly binding?: never;
|
|
17
|
+
readonly pg?: never;
|
|
18
|
+
} | {
|
|
19
|
+
readonly pg: Pool | Client;
|
|
20
|
+
readonly binding?: never;
|
|
21
|
+
readonly url?: never;
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/runtime/postgres.d.ts
|
|
25
|
+
type NormalizeOperationTypes<T> = { [TypeId in keyof T]: { [Method in keyof T[TypeId]]: T[TypeId][Method] extends OperationTypeSignature ? T[TypeId][Method] : OperationTypeSignature } };
|
|
26
|
+
type ToSchemaOperationTypes<T> = T extends OperationTypes ? T : NormalizeOperationTypes<T>;
|
|
27
|
+
type PostgresTargetId = 'postgres';
|
|
28
|
+
interface PostgresClient<TContract extends SqlContract<SqlStorage>> {
|
|
29
|
+
readonly sql: SelectBuilder<TContract, unknown, ExtractCodecTypes<TContract>, ExtractOperationTypes<TContract>>;
|
|
30
|
+
readonly schema: SchemaHandle<TContract, ExtractCodecTypes<TContract>, ToSchemaOperationTypes<ExtractOperationTypes<TContract>>>;
|
|
31
|
+
readonly context: ExecutionContext<TContract>;
|
|
32
|
+
readonly stack: SqlExecutionStackWithDriver<PostgresTargetId>;
|
|
33
|
+
runtime(): Promise<Runtime>;
|
|
34
|
+
}
|
|
35
|
+
interface PostgresOptionsBase<TContract extends SqlContract<SqlStorage>> {
|
|
36
|
+
readonly extensions?: readonly SqlRuntimeExtensionDescriptor<PostgresTargetId>[];
|
|
37
|
+
readonly plugins?: readonly Plugin<TContract>[];
|
|
38
|
+
readonly verify?: RuntimeVerifyOptions;
|
|
39
|
+
readonly cursor?: PostgresDriverCreateOptions['cursor'];
|
|
40
|
+
}
|
|
41
|
+
type PostgresOptionsWithContract<TContract extends SqlContract<SqlStorage>> = PostgresBindingInput & PostgresOptionsBase<TContract> & {
|
|
42
|
+
readonly contract: TContract;
|
|
43
|
+
readonly contractJson?: never;
|
|
44
|
+
};
|
|
45
|
+
type PostgresOptionsWithContractJson<TContract extends SqlContract<SqlStorage>> = PostgresBindingInput & PostgresOptionsBase<TContract> & {
|
|
46
|
+
readonly contractJson: unknown;
|
|
47
|
+
readonly contract?: never;
|
|
48
|
+
};
|
|
49
|
+
type PostgresOptions<TContract extends SqlContract<SqlStorage>> = PostgresOptionsWithContract<TContract> | PostgresOptionsWithContractJson<TContract>;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a lazy Postgres client from either `contractJson` or a TypeScript-authored `contract`.
|
|
52
|
+
* Static query surfaces are available immediately, while `runtime()` instantiates the driver/pool on first call.
|
|
53
|
+
*/
|
|
54
|
+
declare function postgres<TContract extends SqlContract<SqlStorage>>(options: PostgresOptionsWithContract<TContract>): PostgresClient<TContract>;
|
|
55
|
+
declare function postgres<TContract extends SqlContract<SqlStorage>>(options: PostgresOptionsWithContractJson<TContract>): PostgresClient<TContract>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { type PostgresBinding, type PostgresClient, type PostgresOptions, type PostgresOptionsBase, type PostgresOptionsWithContract, type PostgresOptionsWithContractJson, postgres as default };
|
|
58
|
+
//# sourceMappingURL=runtime.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/runtime/binding.ts","../src/runtime/postgres.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAIY,oBAAA;oBAEY;;;;;;EAFZ,SAAA,EAAA,CAAA,EAAA,KAAA;CAEY,GAAA;EAUL,SAAA,EAAA,EAAA,IAAA,GAAO,MAAP;EAAO,SAAA,OAAA,CAAA,EAAA,KAAA;EAAM,SAAA,GAAA,CAAA,EAAA,KAAA;;;;KCoB3B,gDACc,iBDjCP,MCkCS,CDlCT,CCkCW,MDlCS,CAAA,GCkCC,CDlCD,CCkCG,MDlCH,CAAA,CCkCW,MDlCX,CAAA,SCkC2B,sBDlC3B,GCmCxB,CDnCwB,CCmCtB,MDnCsB,CAAA,CCmCd,MDnCc,CAAA,GCoCxB,sBDpCwB,EAER,EAUL;KC4Bd,sBD5BqB,CAAA,CAAA,CAAA,GC4BO,CD5BP,SC4BiB,cD5BjB,GC4BkC,CD5BlC,GC4BsC,uBD5BtC,CC4B8D,CD5B9D,CAAA;AAAM,KC8BpB,gBAAA,GD9BoB,UAAA;UCgCf,iCAAiC,YAAY;gBAC9C,cACZ,oBAEA,kBAAkB,YAClB,sBAAsB;mBAEP,aACf,WACA,kBAAkB,YAClB,uBAAuB,sBAAsB;EAtB5C,SAAA,OAAA,EAwBe,gBAxBQ,CAwBS,SAxBT,CAAA;EACT,SAAA,KAAA,EAwBD,2BAxBC,CAwB2B,gBAxB3B,CAAA;EACE,OAAA,EAAA,EAwBR,OAxBQ,CAwBA,OAxBA,CAAA;;AAAY,UA2BhB,mBA3BgB,CAAA,kBA2BsB,WA3BtB,CA2BkC,UA3BlC,CAAA,CAAA,CAAA;EAAE,SAAA,UAAA,CAAA,EAAA,SA4BF,6BA5BE,CA4B4B,gBA5B5B,CAAA,EAAA;EAAQ,SAAA,OAAA,CAAA,EAAA,SA6Bb,MA7Ba,CA6BN,SA7BM,CAAA,EAAA;EAAgB,SAAA,MAAA,CAAA,EA8BvC,oBA9BuC;EACnD,SAAA,MAAA,CAAA,EA8BY,2BA9BZ,CAAA,QAAA,CAAA;;AAAU,KAiCN,2BAjCM,CAAA,kBAiCwC,WAjCxC,CAiCoD,UAjCpD,CAAA,CAAA,GAkChB,oBAlCgB,GAmCd,mBAnCc,CAmCM,SAnCN,CAAA,GAAA;EACV,SAAA,QAAA,EAmCiB,SAnCjB;EAAsB,SAAA,YAAA,CAAA,EAAA,KAAA;AAAA,CAAA;AAIG,KAmCrB,+BAnCqB,CAAA,kBAmC6B,WAnC7B,CAmCyC,UAnCzC,CAAA,CAAA,GAoC/B,oBApC+B,GAqC7B,mBArC6B,CAqCT,SArCS,CAAA,GAAA;EAAU,SAAA,YAAA,EAAA,OAAA;EAAiB,SAAA,QAAA,CAAA,EAAA,KAAA;CAA4B;AAAxB,KA0CpD,eA1CoD,CAAA,kBA0ClB,WA1CkB,CA0CN,UA1CM,CAAA,CAAA,GA2C5D,2BA3C4D,CA2ChC,SA3CgC,CAAA,GA4C5D,+BA5C4D,CA4C5B,SA5C4B,CAAA;;AAEhE;AAEA;;AAAkD,iBA2D1B,QA3D0B,CAAA,kBA2DC,WA3DD,CA2Da,UA3Db,CAAA,CAAA,CAAA,OAAA,EA4DvC,2BA5DuC,CA4DX,SA5DW,CAAA,CAAA,EA6D/C,cA7D+C,CA6DhC,SA7DgC,CAAA;AAE9C,iBA4DoB,QA5DpB,CAAA,kBA4D+C,WA5D/C,CA4D2D,UA5D3D,CAAA,CAAA,CAAA,OAAA,EA6DO,+BA7DP,CA6DuC,SA7DvC,CAAA,CAAA,EA8DD,cA9DC,CA8Dc,SA9Dd,CAAA"}
|
package/dist/runtime.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import postgresAdapter from "@prisma-next/adapter-postgres/runtime";
|
|
2
|
+
import { instantiateExecutionStack } from "@prisma-next/core-execution-plane/stack";
|
|
3
|
+
import postgresDriver from "@prisma-next/driver-postgres/runtime";
|
|
4
|
+
import { validateContract } from "@prisma-next/sql-contract/validate";
|
|
5
|
+
import { sql } from "@prisma-next/sql-lane";
|
|
6
|
+
import { schema } from "@prisma-next/sql-relational-core/schema";
|
|
7
|
+
import { createExecutionContext, createRuntime, createSqlExecutionStack } from "@prisma-next/sql-runtime";
|
|
8
|
+
import postgresTarget from "@prisma-next/target-postgres/runtime";
|
|
9
|
+
import { ifDefined } from "@prisma-next/utils/defined";
|
|
10
|
+
import { Client, Pool } from "pg";
|
|
11
|
+
|
|
12
|
+
//#region src/runtime/binding.ts
|
|
13
|
+
function bindingError(message, details) {
|
|
14
|
+
const error = new Error(message);
|
|
15
|
+
Object.defineProperty(error, "name", {
|
|
16
|
+
value: "RuntimeError",
|
|
17
|
+
configurable: true
|
|
18
|
+
});
|
|
19
|
+
return Object.assign(error, {
|
|
20
|
+
code: "DRIVER.BINDING_INVALID",
|
|
21
|
+
category: "RUNTIME",
|
|
22
|
+
severity: "error",
|
|
23
|
+
message,
|
|
24
|
+
details
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function validatePostgresUrl(url) {
|
|
28
|
+
const trimmed = url.trim();
|
|
29
|
+
if (trimmed.length === 0) throw bindingError("Postgres URL must be a non-empty string", {
|
|
30
|
+
field: "url",
|
|
31
|
+
reason: "empty"
|
|
32
|
+
});
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = new URL(trimmed);
|
|
36
|
+
} catch {
|
|
37
|
+
throw bindingError("Postgres URL must be a valid URL", {
|
|
38
|
+
field: "url",
|
|
39
|
+
reason: "invalid-url"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:") throw bindingError("Postgres URL must use postgres:// or postgresql://", {
|
|
43
|
+
field: "url",
|
|
44
|
+
reason: "invalid-protocol",
|
|
45
|
+
protocol: parsed.protocol
|
|
46
|
+
});
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
function resolvePostgresBinding(options) {
|
|
50
|
+
const providedCount = Number(options.binding !== void 0) + Number(options.url !== void 0) + Number(options.pg !== void 0);
|
|
51
|
+
if (providedCount !== 1) throw bindingError("Provide one binding input: binding, url, or pg", { providedCount });
|
|
52
|
+
if (options.binding !== void 0) return options.binding;
|
|
53
|
+
if (options.url !== void 0) return {
|
|
54
|
+
kind: "url",
|
|
55
|
+
url: validatePostgresUrl(options.url)
|
|
56
|
+
};
|
|
57
|
+
const pgBinding = options.pg;
|
|
58
|
+
if (pgBinding === void 0) throw bindingError("Invariant violation: expected pg binding after validation", { reason: "missing-pg-binding" });
|
|
59
|
+
if (pgBinding instanceof Pool) return {
|
|
60
|
+
kind: "pgPool",
|
|
61
|
+
pool: pgBinding
|
|
62
|
+
};
|
|
63
|
+
if (pgBinding instanceof Client) return {
|
|
64
|
+
kind: "pgClient",
|
|
65
|
+
client: pgBinding
|
|
66
|
+
};
|
|
67
|
+
throw bindingError("Unable to determine pg binding type from pg input; use binding with explicit kind", { reason: "unknown-pg-instance" });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/runtime/postgres.ts
|
|
72
|
+
function hasContractJson(options) {
|
|
73
|
+
return "contractJson" in options;
|
|
74
|
+
}
|
|
75
|
+
function resolveContract(options) {
|
|
76
|
+
return validateContract(hasContractJson(options) ? options.contractJson : options.contract);
|
|
77
|
+
}
|
|
78
|
+
function postgres(options) {
|
|
79
|
+
const contract = resolveContract(options);
|
|
80
|
+
const binding = resolvePostgresBinding(options);
|
|
81
|
+
const stack = createSqlExecutionStack({
|
|
82
|
+
target: postgresTarget,
|
|
83
|
+
adapter: postgresAdapter,
|
|
84
|
+
driver: options.cursor === void 0 ? postgresDriver : {
|
|
85
|
+
...postgresDriver,
|
|
86
|
+
create() {
|
|
87
|
+
return postgresDriver.create({ cursor: options.cursor });
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
extensionPacks: options.extensions ?? []
|
|
91
|
+
});
|
|
92
|
+
const context = createExecutionContext({
|
|
93
|
+
contract,
|
|
94
|
+
stack
|
|
95
|
+
});
|
|
96
|
+
const schema$1 = schema(context);
|
|
97
|
+
const sql$1 = sql({ context });
|
|
98
|
+
let runtimePromise;
|
|
99
|
+
return {
|
|
100
|
+
sql: sql$1,
|
|
101
|
+
schema: schema$1,
|
|
102
|
+
context,
|
|
103
|
+
stack,
|
|
104
|
+
async runtime() {
|
|
105
|
+
if (runtimePromise) return runtimePromise;
|
|
106
|
+
runtimePromise = (async () => {
|
|
107
|
+
const stackInstance = instantiateExecutionStack(stack);
|
|
108
|
+
const driver = stackInstance.driver;
|
|
109
|
+
if (driver === void 0) throw new Error("Relational runtime requires a driver descriptor on the execution stack");
|
|
110
|
+
try {
|
|
111
|
+
await driver.connect(binding);
|
|
112
|
+
return createRuntime({
|
|
113
|
+
stackInstance,
|
|
114
|
+
context,
|
|
115
|
+
driver,
|
|
116
|
+
verify: options.verify ?? {
|
|
117
|
+
mode: "onFirstUse",
|
|
118
|
+
requireMarker: false
|
|
119
|
+
},
|
|
120
|
+
...ifDefined("plugins", options.plugins)
|
|
121
|
+
});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
await driver.close().catch(() => void 0);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
127
|
+
runtimePromise.catch(() => {
|
|
128
|
+
runtimePromise = void 0;
|
|
129
|
+
});
|
|
130
|
+
return runtimePromise;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//#endregion
|
|
136
|
+
export { postgres as default };
|
|
137
|
+
//# sourceMappingURL=runtime.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":["parsed: URL","PgPool","PgClient","schema: PostgresClient<TContract>['schema']","schemaBuilder","sql","sqlBuilder","runtimePromise: Promise<Runtime> | undefined"],"sources":["../src/runtime/binding.ts","../src/runtime/postgres.ts"],"sourcesContent":["import type { PostgresBinding } from '@prisma-next/driver-postgres/runtime';\nimport type { Client, Pool } from 'pg';\nimport { Client as PgClient, Pool as PgPool } from 'pg';\n\nexport type PostgresBindingInput =\n | {\n readonly binding: PostgresBinding;\n readonly url?: never;\n readonly pg?: never;\n }\n | {\n readonly url: string;\n readonly binding?: never;\n readonly pg?: never;\n }\n | {\n readonly pg: Pool | Client;\n readonly binding?: never;\n readonly url?: never;\n };\n\ninterface PostgresBindingError extends Error {\n readonly code: 'DRIVER.BINDING_INVALID';\n readonly category: 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nfunction bindingError(message: string, details?: Record<string, unknown>): PostgresBindingError {\n const error = new Error(message) as PostgresBindingError;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n return Object.assign(error, {\n code: 'DRIVER.BINDING_INVALID' as const,\n category: 'RUNTIME' as const,\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction validatePostgresUrl(url: string): string {\n const trimmed = url.trim();\n if (trimmed.length === 0) {\n throw bindingError('Postgres URL must be a non-empty string', {\n field: 'url',\n reason: 'empty',\n });\n }\n\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw bindingError('Postgres URL must be a valid URL', {\n field: 'url',\n reason: 'invalid-url',\n });\n }\n\n if (parsed.protocol !== 'postgres:' && parsed.protocol !== 'postgresql:') {\n throw bindingError('Postgres URL must use postgres:// or postgresql://', {\n field: 'url',\n reason: 'invalid-protocol',\n protocol: parsed.protocol,\n });\n }\n\n return trimmed;\n}\n\nexport function resolvePostgresBinding(options: PostgresBindingInput): PostgresBinding {\n const providedCount =\n Number(options.binding !== undefined) +\n Number(options.url !== undefined) +\n Number(options.pg !== undefined);\n\n if (providedCount !== 1) {\n throw bindingError('Provide one binding input: binding, url, or pg', {\n providedCount,\n });\n }\n\n if (options.binding !== undefined) {\n return options.binding;\n }\n\n if (options.url !== undefined) {\n return { kind: 'url', url: validatePostgresUrl(options.url) };\n }\n\n const pgBinding = options.pg;\n if (pgBinding === undefined) {\n throw bindingError('Invariant violation: expected pg binding after validation', {\n reason: 'missing-pg-binding',\n });\n }\n\n if (pgBinding instanceof PgPool) {\n return { kind: 'pgPool', pool: pgBinding };\n }\n\n if (pgBinding instanceof PgClient) {\n return { kind: 'pgClient', client: pgBinding };\n }\n\n throw bindingError(\n 'Unable to determine pg binding type from pg input; use binding with explicit kind',\n {\n reason: 'unknown-pg-instance',\n },\n );\n}\n","import postgresAdapter from '@prisma-next/adapter-postgres/runtime';\nimport { instantiateExecutionStack } from '@prisma-next/core-execution-plane/stack';\nimport type { PostgresDriverCreateOptions } from '@prisma-next/driver-postgres/runtime';\nimport postgresDriver from '@prisma-next/driver-postgres/runtime';\nimport type {\n ExtractCodecTypes,\n ExtractOperationTypes,\n SqlContract,\n SqlStorage,\n} from '@prisma-next/sql-contract/types';\nimport { validateContract } from '@prisma-next/sql-contract/validate';\nimport type { SelectBuilder } from '@prisma-next/sql-lane';\nimport { sql as sqlBuilder } from '@prisma-next/sql-lane';\nimport type { SchemaHandle } from '@prisma-next/sql-relational-core/schema';\nimport { schema as schemaBuilder } from '@prisma-next/sql-relational-core/schema';\nimport type {\n OperationTypeSignature,\n OperationTypes,\n} from '@prisma-next/sql-relational-core/types';\nimport type {\n ExecutionContext,\n Plugin,\n Runtime,\n RuntimeVerifyOptions,\n SqlExecutionStackWithDriver,\n SqlRuntimeExtensionDescriptor,\n} from '@prisma-next/sql-runtime';\nimport {\n createExecutionContext,\n createRuntime,\n createSqlExecutionStack,\n} from '@prisma-next/sql-runtime';\nimport postgresTarget from '@prisma-next/target-postgres/runtime';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { type PostgresBindingInput, resolvePostgresBinding } from './binding';\n\ntype NormalizeOperationTypes<T> = {\n [TypeId in keyof T]: {\n [Method in keyof T[TypeId]]: T[TypeId][Method] extends OperationTypeSignature\n ? T[TypeId][Method]\n : OperationTypeSignature;\n };\n};\n\ntype ToSchemaOperationTypes<T> = T extends OperationTypes ? T : NormalizeOperationTypes<T>;\n\nexport type PostgresTargetId = 'postgres';\n\nexport interface PostgresClient<TContract extends SqlContract<SqlStorage>> {\n readonly sql: SelectBuilder<\n TContract,\n unknown,\n ExtractCodecTypes<TContract>,\n ExtractOperationTypes<TContract>\n >;\n readonly schema: SchemaHandle<\n TContract,\n ExtractCodecTypes<TContract>,\n ToSchemaOperationTypes<ExtractOperationTypes<TContract>>\n >;\n readonly context: ExecutionContext<TContract>;\n readonly stack: SqlExecutionStackWithDriver<PostgresTargetId>;\n runtime(): Promise<Runtime>;\n}\n\nexport interface PostgresOptionsBase<TContract extends SqlContract<SqlStorage>> {\n readonly extensions?: readonly SqlRuntimeExtensionDescriptor<PostgresTargetId>[];\n readonly plugins?: readonly Plugin<TContract>[];\n readonly verify?: RuntimeVerifyOptions;\n readonly cursor?: PostgresDriverCreateOptions['cursor'];\n}\n\nexport type PostgresOptionsWithContract<TContract extends SqlContract<SqlStorage>> =\n PostgresBindingInput &\n PostgresOptionsBase<TContract> & {\n readonly contract: TContract;\n readonly contractJson?: never;\n };\n\nexport type PostgresOptionsWithContractJson<TContract extends SqlContract<SqlStorage>> =\n PostgresBindingInput &\n PostgresOptionsBase<TContract> & {\n readonly contractJson: unknown;\n readonly contract?: never;\n };\n\nexport type PostgresOptions<TContract extends SqlContract<SqlStorage>> =\n | PostgresOptionsWithContract<TContract>\n | PostgresOptionsWithContractJson<TContract>;\n\nfunction hasContractJson<TContract extends SqlContract<SqlStorage>>(\n options: PostgresOptions<TContract>,\n): options is PostgresOptionsWithContractJson<TContract> {\n return 'contractJson' in options;\n}\n\nfunction resolveContract<TContract extends SqlContract<SqlStorage>>(\n options: PostgresOptions<TContract>,\n): TContract {\n const contractInput = hasContractJson(options) ? options.contractJson : options.contract;\n return validateContract<TContract>(contractInput);\n}\n\n/**\n * Creates a lazy Postgres client from either `contractJson` or a TypeScript-authored `contract`.\n * Static query surfaces are available immediately, while `runtime()` instantiates the driver/pool on first call.\n */\nexport default function postgres<TContract extends SqlContract<SqlStorage>>(\n options: PostgresOptionsWithContract<TContract>,\n): PostgresClient<TContract>;\nexport default function postgres<TContract extends SqlContract<SqlStorage>>(\n options: PostgresOptionsWithContractJson<TContract>,\n): PostgresClient<TContract>;\nexport default function postgres<TContract extends SqlContract<SqlStorage>>(\n options: PostgresOptions<TContract>,\n): PostgresClient<TContract> {\n const contract = resolveContract(options);\n const binding = resolvePostgresBinding(options);\n const stack = createSqlExecutionStack({\n target: postgresTarget,\n adapter: postgresAdapter,\n driver:\n options.cursor === undefined\n ? postgresDriver\n : {\n ...postgresDriver,\n create() {\n return postgresDriver.create({ cursor: options.cursor });\n },\n },\n extensionPacks: options.extensions ?? [],\n });\n\n const context = createExecutionContext({\n contract,\n stack,\n });\n\n const schema: PostgresClient<TContract>['schema'] = schemaBuilder(context);\n const sql = sqlBuilder({ context });\n\n let runtimePromise: Promise<Runtime> | undefined;\n\n return {\n sql,\n schema,\n context,\n stack,\n async runtime() {\n if (runtimePromise) {\n return runtimePromise;\n }\n\n runtimePromise = (async () => {\n const stackInstance = instantiateExecutionStack(stack);\n const driver = stackInstance.driver;\n if (driver === undefined) {\n throw new Error('Relational runtime requires a driver descriptor on the execution stack');\n }\n\n try {\n // `binding` is normalized by resolvePostgresBinding(), so this call site remains\n // type-safe in practice while SqlRuntimeDriverInstance currently uses SqlDriver<unknown>.\n await driver.connect(binding);\n\n return createRuntime({\n stackInstance,\n context,\n driver,\n verify: options.verify ?? { mode: 'onFirstUse', requireMarker: false },\n ...ifDefined('plugins', options.plugins),\n });\n } catch (error) {\n await driver.close().catch(() => undefined);\n throw error;\n }\n })();\n\n runtimePromise.catch(() => {\n runtimePromise = undefined;\n });\n\n return runtimePromise;\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prisma-next/postgres",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "One-liner lazy Postgres client composition for Prisma Next",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"pg": "8.16.3",
|
|
9
|
+
"@prisma-next/adapter-postgres": "0.0.1",
|
|
10
|
+
"@prisma-next/core-execution-plane": "0.0.1",
|
|
11
|
+
"@prisma-next/sql-contract": "0.0.1",
|
|
12
|
+
"@prisma-next/driver-postgres": "0.0.1",
|
|
13
|
+
"@prisma-next/sql-relational-core": "0.0.1",
|
|
14
|
+
"@prisma-next/target-postgres": "0.0.1",
|
|
15
|
+
"@prisma-next/sql-runtime": "0.0.1",
|
|
16
|
+
"@prisma-next/sql-lane": "0.0.1",
|
|
17
|
+
"@prisma-next/utils": "0.0.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/pg": "8.16.0",
|
|
21
|
+
"tsdown": "0.18.4",
|
|
22
|
+
"typescript": "5.9.3",
|
|
23
|
+
"vitest": "4.0.17",
|
|
24
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
25
|
+
"@prisma-next/test-utils": "0.0.1",
|
|
26
|
+
"@prisma-next/tsdown": "0.0.0"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/runtime.d.mts",
|
|
38
|
+
"import": "./dist/runtime.mjs"
|
|
39
|
+
},
|
|
40
|
+
"./runtime": {
|
|
41
|
+
"types": "./dist/runtime.d.mts",
|
|
42
|
+
"import": "./dist/runtime.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"main": "./dist/runtime.mjs",
|
|
47
|
+
"module": "./dist/runtime.mjs",
|
|
48
|
+
"types": "./dist/runtime.d.mts",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/prisma/prisma-next.git",
|
|
52
|
+
"directory": "packages/3-targets/8-clients/postgres"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsdown",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:coverage": "vitest run --coverage",
|
|
58
|
+
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
59
|
+
"lint": "biome check . --error-on-warnings",
|
|
60
|
+
"lint:fix": "biome check --write .",
|
|
61
|
+
"lint:fix:unsafe": "biome check --write --unsafe .",
|
|
62
|
+
"clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { PostgresBinding } from '@prisma-next/driver-postgres/runtime';
|
|
2
|
+
export type {
|
|
3
|
+
PostgresClient,
|
|
4
|
+
PostgresOptions,
|
|
5
|
+
PostgresOptionsBase,
|
|
6
|
+
PostgresOptionsWithContract,
|
|
7
|
+
PostgresOptionsWithContractJson,
|
|
8
|
+
} from '../runtime/postgres';
|
|
9
|
+
export { default } from '../runtime/postgres';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { PostgresBinding } from '@prisma-next/driver-postgres/runtime';
|
|
2
|
+
import type { Client, Pool } from 'pg';
|
|
3
|
+
import { Client as PgClient, Pool as PgPool } from 'pg';
|
|
4
|
+
|
|
5
|
+
export type PostgresBindingInput =
|
|
6
|
+
| {
|
|
7
|
+
readonly binding: PostgresBinding;
|
|
8
|
+
readonly url?: never;
|
|
9
|
+
readonly pg?: never;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
readonly url: string;
|
|
13
|
+
readonly binding?: never;
|
|
14
|
+
readonly pg?: never;
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
readonly pg: Pool | Client;
|
|
18
|
+
readonly binding?: never;
|
|
19
|
+
readonly url?: never;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
interface PostgresBindingError extends Error {
|
|
23
|
+
readonly code: 'DRIVER.BINDING_INVALID';
|
|
24
|
+
readonly category: 'RUNTIME';
|
|
25
|
+
readonly severity: 'error';
|
|
26
|
+
readonly details?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function bindingError(message: string, details?: Record<string, unknown>): PostgresBindingError {
|
|
30
|
+
const error = new Error(message) as PostgresBindingError;
|
|
31
|
+
Object.defineProperty(error, 'name', {
|
|
32
|
+
value: 'RuntimeError',
|
|
33
|
+
configurable: true,
|
|
34
|
+
});
|
|
35
|
+
return Object.assign(error, {
|
|
36
|
+
code: 'DRIVER.BINDING_INVALID' as const,
|
|
37
|
+
category: 'RUNTIME' as const,
|
|
38
|
+
severity: 'error' as const,
|
|
39
|
+
message,
|
|
40
|
+
details,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validatePostgresUrl(url: string): string {
|
|
45
|
+
const trimmed = url.trim();
|
|
46
|
+
if (trimmed.length === 0) {
|
|
47
|
+
throw bindingError('Postgres URL must be a non-empty string', {
|
|
48
|
+
field: 'url',
|
|
49
|
+
reason: 'empty',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let parsed: URL;
|
|
54
|
+
try {
|
|
55
|
+
parsed = new URL(trimmed);
|
|
56
|
+
} catch {
|
|
57
|
+
throw bindingError('Postgres URL must be a valid URL', {
|
|
58
|
+
field: 'url',
|
|
59
|
+
reason: 'invalid-url',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (parsed.protocol !== 'postgres:' && parsed.protocol !== 'postgresql:') {
|
|
64
|
+
throw bindingError('Postgres URL must use postgres:// or postgresql://', {
|
|
65
|
+
field: 'url',
|
|
66
|
+
reason: 'invalid-protocol',
|
|
67
|
+
protocol: parsed.protocol,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return trimmed;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function resolvePostgresBinding(options: PostgresBindingInput): PostgresBinding {
|
|
75
|
+
const providedCount =
|
|
76
|
+
Number(options.binding !== undefined) +
|
|
77
|
+
Number(options.url !== undefined) +
|
|
78
|
+
Number(options.pg !== undefined);
|
|
79
|
+
|
|
80
|
+
if (providedCount !== 1) {
|
|
81
|
+
throw bindingError('Provide one binding input: binding, url, or pg', {
|
|
82
|
+
providedCount,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.binding !== undefined) {
|
|
87
|
+
return options.binding;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (options.url !== undefined) {
|
|
91
|
+
return { kind: 'url', url: validatePostgresUrl(options.url) };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const pgBinding = options.pg;
|
|
95
|
+
if (pgBinding === undefined) {
|
|
96
|
+
throw bindingError('Invariant violation: expected pg binding after validation', {
|
|
97
|
+
reason: 'missing-pg-binding',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (pgBinding instanceof PgPool) {
|
|
102
|
+
return { kind: 'pgPool', pool: pgBinding };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (pgBinding instanceof PgClient) {
|
|
106
|
+
return { kind: 'pgClient', client: pgBinding };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
throw bindingError(
|
|
110
|
+
'Unable to determine pg binding type from pg input; use binding with explicit kind',
|
|
111
|
+
{
|
|
112
|
+
reason: 'unknown-pg-instance',
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import postgresAdapter from '@prisma-next/adapter-postgres/runtime';
|
|
2
|
+
import { instantiateExecutionStack } from '@prisma-next/core-execution-plane/stack';
|
|
3
|
+
import type { PostgresDriverCreateOptions } from '@prisma-next/driver-postgres/runtime';
|
|
4
|
+
import postgresDriver from '@prisma-next/driver-postgres/runtime';
|
|
5
|
+
import type {
|
|
6
|
+
ExtractCodecTypes,
|
|
7
|
+
ExtractOperationTypes,
|
|
8
|
+
SqlContract,
|
|
9
|
+
SqlStorage,
|
|
10
|
+
} from '@prisma-next/sql-contract/types';
|
|
11
|
+
import { validateContract } from '@prisma-next/sql-contract/validate';
|
|
12
|
+
import type { SelectBuilder } from '@prisma-next/sql-lane';
|
|
13
|
+
import { sql as sqlBuilder } from '@prisma-next/sql-lane';
|
|
14
|
+
import type { SchemaHandle } from '@prisma-next/sql-relational-core/schema';
|
|
15
|
+
import { schema as schemaBuilder } from '@prisma-next/sql-relational-core/schema';
|
|
16
|
+
import type {
|
|
17
|
+
OperationTypeSignature,
|
|
18
|
+
OperationTypes,
|
|
19
|
+
} from '@prisma-next/sql-relational-core/types';
|
|
20
|
+
import type {
|
|
21
|
+
ExecutionContext,
|
|
22
|
+
Plugin,
|
|
23
|
+
Runtime,
|
|
24
|
+
RuntimeVerifyOptions,
|
|
25
|
+
SqlExecutionStackWithDriver,
|
|
26
|
+
SqlRuntimeExtensionDescriptor,
|
|
27
|
+
} from '@prisma-next/sql-runtime';
|
|
28
|
+
import {
|
|
29
|
+
createExecutionContext,
|
|
30
|
+
createRuntime,
|
|
31
|
+
createSqlExecutionStack,
|
|
32
|
+
} from '@prisma-next/sql-runtime';
|
|
33
|
+
import postgresTarget from '@prisma-next/target-postgres/runtime';
|
|
34
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
35
|
+
import { type PostgresBindingInput, resolvePostgresBinding } from './binding';
|
|
36
|
+
|
|
37
|
+
type NormalizeOperationTypes<T> = {
|
|
38
|
+
[TypeId in keyof T]: {
|
|
39
|
+
[Method in keyof T[TypeId]]: T[TypeId][Method] extends OperationTypeSignature
|
|
40
|
+
? T[TypeId][Method]
|
|
41
|
+
: OperationTypeSignature;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type ToSchemaOperationTypes<T> = T extends OperationTypes ? T : NormalizeOperationTypes<T>;
|
|
46
|
+
|
|
47
|
+
export type PostgresTargetId = 'postgres';
|
|
48
|
+
|
|
49
|
+
export interface PostgresClient<TContract extends SqlContract<SqlStorage>> {
|
|
50
|
+
readonly sql: SelectBuilder<
|
|
51
|
+
TContract,
|
|
52
|
+
unknown,
|
|
53
|
+
ExtractCodecTypes<TContract>,
|
|
54
|
+
ExtractOperationTypes<TContract>
|
|
55
|
+
>;
|
|
56
|
+
readonly schema: SchemaHandle<
|
|
57
|
+
TContract,
|
|
58
|
+
ExtractCodecTypes<TContract>,
|
|
59
|
+
ToSchemaOperationTypes<ExtractOperationTypes<TContract>>
|
|
60
|
+
>;
|
|
61
|
+
readonly context: ExecutionContext<TContract>;
|
|
62
|
+
readonly stack: SqlExecutionStackWithDriver<PostgresTargetId>;
|
|
63
|
+
runtime(): Promise<Runtime>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface PostgresOptionsBase<TContract extends SqlContract<SqlStorage>> {
|
|
67
|
+
readonly extensions?: readonly SqlRuntimeExtensionDescriptor<PostgresTargetId>[];
|
|
68
|
+
readonly plugins?: readonly Plugin<TContract>[];
|
|
69
|
+
readonly verify?: RuntimeVerifyOptions;
|
|
70
|
+
readonly cursor?: PostgresDriverCreateOptions['cursor'];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export type PostgresOptionsWithContract<TContract extends SqlContract<SqlStorage>> =
|
|
74
|
+
PostgresBindingInput &
|
|
75
|
+
PostgresOptionsBase<TContract> & {
|
|
76
|
+
readonly contract: TContract;
|
|
77
|
+
readonly contractJson?: never;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type PostgresOptionsWithContractJson<TContract extends SqlContract<SqlStorage>> =
|
|
81
|
+
PostgresBindingInput &
|
|
82
|
+
PostgresOptionsBase<TContract> & {
|
|
83
|
+
readonly contractJson: unknown;
|
|
84
|
+
readonly contract?: never;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type PostgresOptions<TContract extends SqlContract<SqlStorage>> =
|
|
88
|
+
| PostgresOptionsWithContract<TContract>
|
|
89
|
+
| PostgresOptionsWithContractJson<TContract>;
|
|
90
|
+
|
|
91
|
+
function hasContractJson<TContract extends SqlContract<SqlStorage>>(
|
|
92
|
+
options: PostgresOptions<TContract>,
|
|
93
|
+
): options is PostgresOptionsWithContractJson<TContract> {
|
|
94
|
+
return 'contractJson' in options;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveContract<TContract extends SqlContract<SqlStorage>>(
|
|
98
|
+
options: PostgresOptions<TContract>,
|
|
99
|
+
): TContract {
|
|
100
|
+
const contractInput = hasContractJson(options) ? options.contractJson : options.contract;
|
|
101
|
+
return validateContract<TContract>(contractInput);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Creates a lazy Postgres client from either `contractJson` or a TypeScript-authored `contract`.
|
|
106
|
+
* Static query surfaces are available immediately, while `runtime()` instantiates the driver/pool on first call.
|
|
107
|
+
*/
|
|
108
|
+
export default function postgres<TContract extends SqlContract<SqlStorage>>(
|
|
109
|
+
options: PostgresOptionsWithContract<TContract>,
|
|
110
|
+
): PostgresClient<TContract>;
|
|
111
|
+
export default function postgres<TContract extends SqlContract<SqlStorage>>(
|
|
112
|
+
options: PostgresOptionsWithContractJson<TContract>,
|
|
113
|
+
): PostgresClient<TContract>;
|
|
114
|
+
export default function postgres<TContract extends SqlContract<SqlStorage>>(
|
|
115
|
+
options: PostgresOptions<TContract>,
|
|
116
|
+
): PostgresClient<TContract> {
|
|
117
|
+
const contract = resolveContract(options);
|
|
118
|
+
const binding = resolvePostgresBinding(options);
|
|
119
|
+
const stack = createSqlExecutionStack({
|
|
120
|
+
target: postgresTarget,
|
|
121
|
+
adapter: postgresAdapter,
|
|
122
|
+
driver:
|
|
123
|
+
options.cursor === undefined
|
|
124
|
+
? postgresDriver
|
|
125
|
+
: {
|
|
126
|
+
...postgresDriver,
|
|
127
|
+
create() {
|
|
128
|
+
return postgresDriver.create({ cursor: options.cursor });
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
extensionPacks: options.extensions ?? [],
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const context = createExecutionContext({
|
|
135
|
+
contract,
|
|
136
|
+
stack,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const schema: PostgresClient<TContract>['schema'] = schemaBuilder(context);
|
|
140
|
+
const sql = sqlBuilder({ context });
|
|
141
|
+
|
|
142
|
+
let runtimePromise: Promise<Runtime> | undefined;
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
sql,
|
|
146
|
+
schema,
|
|
147
|
+
context,
|
|
148
|
+
stack,
|
|
149
|
+
async runtime() {
|
|
150
|
+
if (runtimePromise) {
|
|
151
|
+
return runtimePromise;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
runtimePromise = (async () => {
|
|
155
|
+
const stackInstance = instantiateExecutionStack(stack);
|
|
156
|
+
const driver = stackInstance.driver;
|
|
157
|
+
if (driver === undefined) {
|
|
158
|
+
throw new Error('Relational runtime requires a driver descriptor on the execution stack');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// `binding` is normalized by resolvePostgresBinding(), so this call site remains
|
|
163
|
+
// type-safe in practice while SqlRuntimeDriverInstance currently uses SqlDriver<unknown>.
|
|
164
|
+
await driver.connect(binding);
|
|
165
|
+
|
|
166
|
+
return createRuntime({
|
|
167
|
+
stackInstance,
|
|
168
|
+
context,
|
|
169
|
+
driver,
|
|
170
|
+
verify: options.verify ?? { mode: 'onFirstUse', requireMarker: false },
|
|
171
|
+
...ifDefined('plugins', options.plugins),
|
|
172
|
+
});
|
|
173
|
+
} catch (error) {
|
|
174
|
+
await driver.close().catch(() => undefined);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
})();
|
|
178
|
+
|
|
179
|
+
runtimePromise.catch(() => {
|
|
180
|
+
runtimePromise = undefined;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return runtimePromise;
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|