palgres 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +2 -2
  3. package/dist/chunks/5DSZT7RI.js +5 -0
  4. package/dist/chunks/7676QADW.js +2 -0
  5. package/dist/chunks/W7VRQDZA.js +2 -0
  6. package/dist/dsl.js +2 -0
  7. package/dist/index.js +372 -0
  8. package/dist/testing.js +2 -0
  9. package/package.json +32 -4
  10. package/types/internal/src/data.d.ts +15 -0
  11. package/types/palgres/src/adopt.d.ts +17 -0
  12. package/types/palgres/src/connect.d.ts +37 -0
  13. package/types/palgres/src/dsl/columns.d.ts +388 -0
  14. package/types/palgres/src/dsl/foreign-key.d.ts +33 -0
  15. package/types/palgres/src/dsl/index.d.ts +11 -0
  16. package/types/palgres/src/dsl/policy.d.ts +181 -0
  17. package/types/palgres/src/dsl/raw.d.ts +43 -0
  18. package/types/palgres/src/dsl/schema.d.ts +288 -0
  19. package/types/palgres/src/dsl/to-model.d.ts +6 -0
  20. package/types/palgres/src/forget.d.ts +12 -0
  21. package/types/palgres/src/index.d.ts +15 -0
  22. package/types/palgres/src/introspect.d.ts +17 -0
  23. package/types/palgres/src/migrate.d.ts +189 -0
  24. package/types/palgres/src/model.d.ts +181 -0
  25. package/types/palgres/src/plan.d.ts +107 -0
  26. package/types/palgres/src/pull.d.ts +13 -0
  27. package/types/palgres/src/query/admission.d.ts +80 -0
  28. package/types/palgres/src/query/catalog.d.ts +50 -0
  29. package/types/palgres/src/query/client.d.ts +154 -0
  30. package/types/palgres/src/query/data-mark.d.ts +7 -0
  31. package/types/palgres/src/query/diagnostics.d.ts +76 -0
  32. package/types/palgres/src/query/driver.d.ts +299 -0
  33. package/types/palgres/src/query/env-gen.d.ts +14 -0
  34. package/types/palgres/src/query/errors.d.ts +168 -0
  35. package/types/palgres/src/query/guards.d.ts +62 -0
  36. package/types/palgres/src/query/index.d.ts +76 -0
  37. package/types/palgres/src/query/options.d.ts +75 -0
  38. package/types/palgres/src/query/relations.d.ts +15 -0
  39. package/types/palgres/src/query/repository.d.ts +42 -0
  40. package/types/palgres/src/query/request.d.ts +54 -0
  41. package/types/palgres/src/query/search.d.ts +55 -0
  42. package/types/palgres/src/query/surface.d.ts +9 -0
  43. package/types/palgres/src/query/tx-plan.d.ts +229 -0
  44. package/types/palgres/src/query/typed.d.ts +537 -0
  45. package/types/palgres/src/query/unique.d.ts +16 -0
  46. package/types/palgres/src/search-worker.d.ts +36 -0
  47. package/types/palgres/src/search.d.ts +107 -0
  48. package/types/palgres/src/testing/fake.d.ts +22 -0
  49. package/types/palgres/src/testing/index.d.ts +2 -0
  50. package/types/palgres/src/units.d.ts +24 -0
@@ -0,0 +1,154 @@
1
+ import type { DatabaseDiagnostics } from './diagnostics.js';
2
+ import type { AtomicOptions, InsertManyOptions, Page, RawPageOptions } from './options.js';
3
+ import type { TxPlanBody, TxPlanResponse } from './tx-plan.js';
4
+ export interface FindManyOptions {
5
+ /** One ordering or an ordered list. `nulls` pins where NULLs go — Postgres' default changes with the direction. */
6
+ orderBy?: {
7
+ column: string;
8
+ direction?: 'asc' | 'desc';
9
+ nulls?: 'first' | 'last';
10
+ } | {
11
+ column: string;
12
+ direction?: 'asc' | 'desc';
13
+ nulls?: 'first' | 'last';
14
+ }[];
15
+ /** Only these columns; an unknown name is refused before SQL. */
16
+ select?: string[];
17
+ /** Related rows too, in the same statement (LATERAL): `has` filters through a relation, `with` fetches it. */
18
+ with?: Record<string, unknown>;
19
+ limit?: number;
20
+ /** Only with `limit`: an offset into an unbounded result is not a page. */
21
+ offset?: number;
22
+ }
23
+ /**
24
+ * A row lock, held until the transaction ends: `update` FOR UPDATE (the row
25
+ * is about to change or go), `noKeyUpdate` FOR NO KEY UPDATE (it changes,
26
+ * its key stays — what updateMany takes), `share` FOR SHARE (it must not
27
+ * change while this transaction reads on). Postgres applies the table's
28
+ * UPDATE policies to a locking read, so a row the caller may read but not
29
+ * update is not returned locked.
30
+ */
31
+ export type RowLock = 'update' | 'share' | 'noKeyUpdate';
32
+ /** How a table's rows are searched. */
33
+ export interface SearchOptions {
34
+ /** How many rows at most (default 20; 1 to 100). */
35
+ limit?: number;
36
+ /** Only rows this filter matches — findMany's filter. */
37
+ where?: Record<string, unknown>;
38
+ /** "hybrid" (default): by meaning and by word, fused; "text": by word alone; "vector": by meaning alone. */
39
+ mode?: 'hybrid' | 'text' | 'vector';
40
+ /** Only rows whose _score is at least this. */
41
+ minScore?: number;
42
+ }
43
+ /** The operations a request's transaction, a savepoint and an $atomic scope all have. */
44
+ export interface DBOps {
45
+ /** Null unless the request asked for diagnostics or a budget. Parameter values are never in it. */
46
+ diagnostics(): DatabaseDiagnostics | null;
47
+ /** One row by exactly one declared unique key; null never stands for a unique value. With `lock`, the row is locked in the same statement, until the transaction ends. */
48
+ findUnique(table: string, where: Record<string, unknown>, opts?: {
49
+ select?: readonly string[];
50
+ with?: Record<string, unknown>;
51
+ lock?: RowLock;
52
+ }): Promise<Record<string, unknown> | null>;
53
+ /** Keyset pagination: opaque cursors sealed to the query and the caller, a unique ordering. */
54
+ page(table: string, query?: Record<string, unknown>, opts?: RawPageOptions): Promise<Page<Record<string, unknown>>>;
55
+ /**
56
+ * Raw SQL on this transaction — the request's own, a savepoint's or an
57
+ * $atomic scope's. Not read-only: a write here commits with it, and what
58
+ * stands between it and the data is RLS and the role. Parameters are $1…$n;
59
+ * an array of scalars is a Postgres array (`= ANY($1::uuid[])`), an object
60
+ * is JSON, JSON text for a jsonb parameter is that JSON. Rows are plain
61
+ * objects; bigint and numeric are strings, a timestamp an ISO string.
62
+ * Without a type argument the rows are `unknown`: only the author knows
63
+ * what raw SQL returns.
64
+ */
65
+ query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
66
+ insert(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
67
+ /**
68
+ * sum/avg (always text: exact), min/max, count, optionally grouped. `count`
69
+ * is `true` for the rows, or named filters — `{ open: { done: false } }` —
70
+ * each counted in the same statement (count(*) FILTER (WHERE …)).
71
+ */
72
+ aggregate(table: string, q: {
73
+ where?: Record<string, unknown>;
74
+ sum?: readonly string[];
75
+ avg?: readonly string[];
76
+ min?: readonly string[];
77
+ max?: readonly string[];
78
+ count?: boolean | Readonly<Record<string, Record<string, unknown>>>;
79
+ groupBy?: readonly string[];
80
+ }): Promise<Record<string, unknown> | Record<string, unknown>[]>;
81
+ /** Many rows, ONE statement; every row has the same keys — the column list is shared. `action: "ignore"` alone skips a row that collides with any unique key. */
82
+ insertMany(table: string, rows: readonly Record<string, unknown>[], opts: InsertManyOptions & {
83
+ returning: false;
84
+ }): Promise<number>;
85
+ insertMany(table: string, rows: readonly Record<string, unknown>[], opts?: InsertManyOptions & {
86
+ returning?: true;
87
+ }): Promise<Record<string, unknown>[]>;
88
+ insertMany(table: string, rows: readonly Record<string, unknown>[], opts?: InsertManyOptions): Promise<Record<string, unknown>[] | number>;
89
+ /** Insert, or update on a collision of `onConflict` — one statement, so two racing callers cannot both lose. */
90
+ put(table: string, data: Record<string, unknown>, opts: {
91
+ onConflict: readonly string[];
92
+ }): Promise<Record<string, unknown>>;
93
+ /** The row with this id updated, or null when none matched (absent, or hidden by RLS). */
94
+ update(table: string, id: string, data: Record<string, unknown>): Promise<Record<string, unknown> | null>;
95
+ delete(table: string, id: string): Promise<void>;
96
+ findById(table: string, id: string): Promise<Record<string, unknown> | null>;
97
+ /**
98
+ * Every row the filter matches, updated in one statement; an empty filter is
99
+ * refused. Zero rows is neither an error nor a success: with the condition
100
+ * in `where` (`{ id, balance: { gte: amount } }`), the returned array is the
101
+ * only thing that says whether it applied.
102
+ */
103
+ updateMany(table: string, where: Record<string, unknown>, set: Record<string, unknown>, opts?: {
104
+ returning?: boolean;
105
+ }): Promise<Record<string, unknown>[] | number>;
106
+ /**
107
+ * Claims an idempotency key: `{ inserted: true }` this call wrote the row,
108
+ * `{ inserted: false, row }` someone did first and `row` is theirs — both
109
+ * successes. `unique` finds the row, `extra` is only written.
110
+ */
111
+ claim(table: string, unique: Record<string, unknown>, extra?: Record<string, unknown>): Promise<{
112
+ inserted: boolean;
113
+ row: Record<string, unknown>;
114
+ }>;
115
+ /** Locks these rows in an order the engine sets (by primary key), not the caller's: two callers cannot take them in opposite orders. */
116
+ lockRows(table: string, ids: readonly string[]): Promise<void>;
117
+ /** Locks the rows a filter matches, selected and locked in one statement, ordered by primary key. An empty filter is refused. */
118
+ lockRowsWhere(table: string, where: Record<string, unknown>, opts?: {
119
+ mode?: RowLock;
120
+ }): Promise<void>;
121
+ /** Locks a NAME, not a row, until this transaction ends — "one invoice run at a time". Never session-scoped: that would outlive the request. */
122
+ advisoryXactLock(key: string): Promise<void>;
123
+ /** Deletes every row the filter matches; how many. An empty filter is refused. */
124
+ deleteMany(table: string, where: Record<string, unknown>): Promise<number>;
125
+ /** How many rows match; an empty filter counts the table. */
126
+ count(table: string, where?: Record<string, unknown>): Promise<number>;
127
+ findMany(table: string, query?: Record<string, unknown>, opts?: FindManyOptions): Promise<Record<string, unknown>[]>;
128
+ /**
129
+ * The rows of a table that declares search nearest to `query` — by meaning
130
+ * (its embedding against the rows') and by word (full text), fused — each
131
+ * with its `_score`, best first. It runs as the caller: a row their
132
+ * policies hide is never found. A row not embedded yet is found by its
133
+ * words; when the query cannot be embedded, so is every row ("hybrid").
134
+ */
135
+ search(table: string, query: string, opts?: SearchOptions): Promise<(Record<string, unknown> & {
136
+ _score: number;
137
+ })[]>;
138
+ }
139
+ export interface DBClient extends DBOps {
140
+ /** An independent transaction: resolves after its COMMIT, retries the whole callback on a fresh one. One identity throughout. */
141
+ atomic<T>(fn: (tx: AtomicClient) => Promise<T>, options?: AtomicOptions): Promise<T>;
142
+ /** A plan (`$transaction`) run on this transaction inside one savepoint. */
143
+ txPlan(plan: TxPlanBody): Promise<TxPlanResponse>;
144
+ /**
145
+ * `fn` in a SAVEPOINT of this transaction: a failure in it rolls back only
146
+ * what its handle wrote. The handle is a parameter, not an ambient swap — a
147
+ * concurrent Promise.all branch's writes must stay outside the boundary.
148
+ */
149
+ attempt<T>(fn: (tx: DBOps) => Promise<T>): Promise<T>;
150
+ /** The same operations as service_role, which passes row-level security — its own transaction on its own connection. No asService of its own. */
151
+ asService(): Omit<DBClient, 'asService'>;
152
+ }
153
+ /** A root transaction cannot change identity or open another root. */
154
+ export type AtomicClient = Omit<DBClient, 'asService' | 'atomic'>;
@@ -0,0 +1,7 @@
1
+ export { carryMark, isData, isValue, markData, markRows } from '../../../internal/src/data.js';
2
+ /**
3
+ * The 400 for data where structure goes. `field` is where in the call it
4
+ * stood (`where.token`, `orderBy`); the words reach the caller, so they name
5
+ * the field, never the table.
6
+ */
7
+ export declare function refuseDataAsStructure(v: unknown, field: string, what: string): void;
@@ -0,0 +1,76 @@
1
+ export interface DatabaseBudget {
2
+ /** Statements, failed ones and retries included. */
3
+ maxQueries?: number;
4
+ /** Rows returned; nested relation rows count in bytes. */
5
+ maxRows?: number;
6
+ /** Estimated JSON bytes of the results — not protocol traffic. */
7
+ maxBytes?: number;
8
+ }
9
+ export interface DatabaseQueryEvent {
10
+ ordinal: number;
11
+ fingerprint: string;
12
+ name?: string;
13
+ sql?: string;
14
+ source?: string;
15
+ durationMs: number;
16
+ rows: number;
17
+ bytes: number;
18
+ errorCode?: string;
19
+ }
20
+ export interface DatabaseDiagnostics {
21
+ endpoint?: string;
22
+ requestId?: string;
23
+ outcome: 'active' | 'committed' | 'failed';
24
+ elapsedMs: number;
25
+ queries: number;
26
+ rows: number;
27
+ bytes: number;
28
+ queueMs: number;
29
+ /** Until the driver's transaction callback starts; includes BEGIN. */
30
+ poolMs: number;
31
+ /** From the transaction callback through the acknowledged COMMIT/ROLLBACK. */
32
+ connectionMs?: number;
33
+ setupMs: number;
34
+ /** Waiting on the driver: Postgres' execution, locks, the network. */
35
+ sqlMs: number;
36
+ commitMs: number;
37
+ rollbackMs: number;
38
+ commits: number;
39
+ commitFailures: number;
40
+ rollbackRequests: number;
41
+ retries: number;
42
+ /** The first 200 statements; the totals count every one. */
43
+ statements: readonly DatabaseQueryEvent[];
44
+ }
45
+ export interface DatabaseDiagnosticsOptions extends DatabaseBudget {
46
+ endpoint?: string;
47
+ requestId?: string;
48
+ /** A stack capture costs work on every statement, so it is asked for. */
49
+ captureSource?: boolean;
50
+ /** Parameters are never recorded; raw SQL text can carry inline literals. */
51
+ includeSql?: boolean;
52
+ onQuery?: (event: DatabaseQueryEvent) => void | Promise<void>;
53
+ onComplete?: (diagnostics: DatabaseDiagnostics) => void | Promise<void>;
54
+ }
55
+ type Timing = 'queueMs' | 'poolMs' | 'setupMs' | 'sqlMs' | 'commitMs' | 'rollbackMs' | 'connectionMs';
56
+ export declare class DatabaseTrace {
57
+ private readonly start;
58
+ private readonly options;
59
+ private ended?;
60
+ private failure?;
61
+ private readonly state;
62
+ constructor(options: DatabaseDiagnosticsOptions);
63
+ check(): void;
64
+ time(key: Timing, started: number): void;
65
+ retry(): void;
66
+ committed(): void;
67
+ commitFailed(): void;
68
+ failed(): void;
69
+ rolledBack(): void;
70
+ private refuse;
71
+ private emit;
72
+ query(sql: string, run: () => PromiseLike<unknown>): Promise<unknown>;
73
+ get snapshot(): DatabaseDiagnostics;
74
+ complete(outcome: 'committed' | 'failed'): void;
75
+ }
76
+ export {};
@@ -0,0 +1,299 @@
1
+ /** A statement in flight. */
2
+ export type Statement = PromiseLike<unknown>;
3
+ /** What the engine says of a statement it wrote itself. */
4
+ export interface StatementHint {
5
+ /**
6
+ * Every $n in the text is the engine's own placeholder, and no part of the
7
+ * text came from outside the engine: the statement may travel in the round
8
+ * trip that opens its transaction, its values as literals (inlineParams).
9
+ */
10
+ inline?: boolean;
11
+ /**
12
+ * A plain read — a SELECT without a locking clause, no function the
13
+ * engine calls writes. A transaction of nothing else has nothing to commit.
14
+ */
15
+ read?: boolean;
16
+ }
17
+ export interface SqlTx {
18
+ unsafe(sql: string, params?: unknown[], hint?: StatementHint): Statement;
19
+ savepoint<T>(fn: (sp: SqlTx) => Promise<T>): Promise<T>;
20
+ /**
21
+ * Gives the transaction's connection up: closed now, whatever is in flight
22
+ * on it — the server rolls back what it held — and never lent again; the
23
+ * transaction ends without a word more on it. For a connection that stopped
24
+ * answering (control.ts' CANCEL_GRACE_MS). A driver that cannot close one
25
+ * connection of its pool has none.
26
+ */
27
+ abandon?(): void;
28
+ }
29
+ /** How a transaction starts. */
30
+ export interface Opening {
31
+ /** Statements separated by ";", without parameters: what the transaction runs first. */
32
+ sql: string;
33
+ /** Gets the rows of the opening's last statement, before any statement after it is answered. */
34
+ opened?(rows: Record<string, unknown>[]): void;
35
+ /**
36
+ * A name of its own for the round trip that carries the opening, said in
37
+ * it as a comment: cancelOpening(tag) stops that round trip while it runs,
38
+ * before its backend is known by anything else.
39
+ */
40
+ tag?: string;
41
+ }
42
+ export interface SqlDriver {
43
+ /**
44
+ * A statement on its own. It goes on a connection that has just answered,
45
+ * and once: a connection the server closed while it sat idle in the pool —
46
+ * a database that slept, restarted or moved (a switchover terminates every
47
+ * backend, 57P01) — is dropped before the statement is sent, and another
48
+ * taken; a connection lost after it was sent is the statement's failure,
49
+ * never a second run.
50
+ */
51
+ unsafe(sql: string, params?: unknown[]): Statement;
52
+ /**
53
+ * A transaction: commits when `fn` resolves, rolls back when it rejects.
54
+ * It runs `opening` first, in as few round trips as the driver can: Bun's
55
+ * sends it with the BEGIN, and with the transaction's first statement when
56
+ * the engine wrote that one (StatementHint) — one round trip for all three.
57
+ */
58
+ begin<T>(fn: (tx: SqlTx) => Promise<T>, opening?: Opening): Promise<T>;
59
+ /**
60
+ * Cancels one backend's statement through a connection of its own. The
61
+ * ticket names the backend (pid, backend_start) and its transaction
62
+ * (xact_start): a pooler hands the same server connection to the next
63
+ * client, and a late cancel must never reach that client's query.
64
+ */
65
+ cancelBackend?(pid: number, startedAt: string, xactStartedAt: string): Promise<void>;
66
+ /** Cancels the round trip that opens a transaction, by its tag, while it runs: true when it found it running. */
67
+ cancelOpening?(tag: string): Promise<boolean>;
68
+ }
69
+ /** A parameter as the text Postgres reads, or null. */
70
+ export declare function encodeParam(value: unknown): string | null;
71
+ /** A JS array as a Postgres array literal. Unquoted NULL is the null element; "NULL" would be the four-letter string. */
72
+ export declare function encodePgArray(value: readonly unknown[]): string;
73
+ /**
74
+ * postgres.js' `types` option for a pool the engine uses: postgres.js
75
+ * serializes a parameter by the type the server describes, and its json,
76
+ * boolean, date and bytea serializers would re-encode the text the engine
77
+ * already wrote. Parsing (results) is postgres.js' own.
78
+ */
79
+ export declare const POSTGRES_JS_TYPES: {
80
+ json: {
81
+ to: number;
82
+ from: number[];
83
+ serialize: (x: unknown) => unknown;
84
+ parse: (x: string) => unknown;
85
+ };
86
+ boolean: {
87
+ to: number;
88
+ from: number[];
89
+ serialize: (x: unknown) => unknown;
90
+ parse: (x: string) => x is "t";
91
+ };
92
+ date: {
93
+ to: number;
94
+ from: number[];
95
+ serialize: (x: unknown) => unknown;
96
+ parse: (x: string) => Date;
97
+ };
98
+ bytea: {
99
+ to: number;
100
+ from: number[];
101
+ serialize: (x: unknown) => unknown;
102
+ parse: (x: string) => Buffer<ArrayBuffer>;
103
+ };
104
+ };
105
+ /** Bun's SQL as the engine sees it (structural: this package does not depend on Bun's types). */
106
+ export interface BunPool {
107
+ unsafe(sql: string, params?: unknown[]): Statement;
108
+ reserve(): Promise<BunReserved>;
109
+ /**
110
+ * A connection it lent has none under it (bunSlotWithoutConnection), and
111
+ * nothing was sent on it. Told so, a pool that can (renewingBunPool) takes
112
+ * it back and opens itself anew. One that cannot — Bun's own — is not given
113
+ * it back: it would lend it again.
114
+ */
115
+ discard?(reserved: BunReserved): void;
116
+ }
117
+ /** One of the pool's connections, held. */
118
+ export interface BunReserved {
119
+ unsafe(sql: string, params?: unknown[]): Statement & {
120
+ simple(): PromiseLike<unknown>;
121
+ };
122
+ /** Back to the pool, as it is. */
123
+ release(): void;
124
+ /** Closed: the pool opens another in its place. It waits for what is in flight on it unless told `timeout: 0`. */
125
+ close(options?: {
126
+ timeout?: number;
127
+ }): PromiseLike<unknown> | void;
128
+ }
129
+ /** The rows of a simple query's last statement: Bun answers several statements with one array per statement. */
130
+ export declare function lastRows(result: unknown): Record<string, unknown>[];
131
+ /**
132
+ * `text` with each $n as its value's literal: what lets a statement travel in
133
+ * a round trip that takes no parameters. Only for a statement the engine
134
+ * wrote (StatementHint.inline): its quotes are the engine's own — "…"
135
+ * identifiers, '…' and E'…' literals, every quote in them doubled — and its
136
+ * only $ are its placeholders, each bound once. Whatever else is in it — a
137
+ * comment, a second statement, a backslash, a $ that is no placeholder, a
138
+ * placeholder without a value or a value without one — answers undefined,
139
+ * and the statement goes on its own with its parameters. A value is an
140
+ * untyped literal, which Postgres reads as it reads an untyped parameter: by
141
+ * its context.
142
+ */
143
+ export declare function inlineParams(text: string, params: readonly unknown[]): string | undefined;
144
+ /**
145
+ * Tries to open a transaction — or send a statement on its own — on a live
146
+ * connection. A server that closed the pool's connections leaves as many dead
147
+ * ones as the pool held, each failing at once: the next one is tried, up to
148
+ * this many — and so is a connection lent with none under it, each such
149
+ * counted among them. A connection that does not open says the database is
150
+ * not reachable: one more try, then the failure.
151
+ */
152
+ export declare const OPEN_ATTEMPTS = 10;
153
+ export declare const OPEN_ATTEMPTS_UNREACHABLE = 2;
154
+ /**
155
+ * A Bun SQL pool as a driver. A transaction is a connection held for it
156
+ * (reserve), not Bun's begin(): Bun sends statements without prepared
157
+ * statements one round trip at a time, so the BEGIN, the transaction's
158
+ * opening and — when the engine wrote it — its first statement go as ONE
159
+ * simple query: a request of one statement is two round trips (that one and
160
+ * its COMMIT) where begin() takes four.
161
+ *
162
+ * Nothing is sent until the transaction's first statement. A connection the
163
+ * server had closed fails in that first round trip, and it is sent again on
164
+ * another: no COMMIT was in it, so whatever of it ran on a connection that
165
+ * was then lost is rolled back by the server.
166
+ *
167
+ * A statement on its own (unsafe) is a transaction of one, its BEGIN sent
168
+ * alone first. Bun's pool learns that the server closed an idle connection
169
+ * only when it reads the server's FATAL, and a statement given it before
170
+ * then fails with that FATAL (57P01) — the error a statement the server
171
+ * terminated while it ran gets too, so nothing in it says the statement did
172
+ * not run. The BEGIN is what meets the dead connection: it fails, the
173
+ * connection is dropped, and the statement goes on another. What fails after
174
+ * the statement went is its failure, never sent again — on its own it would
175
+ * have committed as it ended, and a nextval() is never rolled back. Two
176
+ * round trips more than the pool's own unsafe (the BEGIN, the COMMIT): it is
177
+ * the engine's checks and its cancels, never a request's statements.
178
+ *
179
+ * A connection Bun lent with no connection under it — the server ended it in
180
+ * the read that opened it (bunSlotWithoutConnection) — refuses the first
181
+ * round trip before anything is written. The round trip goes on another, and
182
+ * the pool is told (BunPool.discard): renewingBunPool opens itself anew, so
183
+ * that it lends its full count again. Bun's own pool cannot be told: the
184
+ * connection is kept, never given back — it would be lent again — and that
185
+ * pool lends one fewer from then on.
186
+ *
187
+ * The connection goes back to the pool only with its transaction ended: one
188
+ * released inside a transaction hands it to the next borrower (measured, Bun
189
+ * 1.4.2). One whose end did not answer is closed instead. `control`, a second
190
+ * small pool, carries cancellation: a cancel sent on the application pool
191
+ * would wait behind the statement it is meant to stop.
192
+ */
193
+ export declare function bunDriver(pool: BunPool, control?: BunPool): SqlDriver;
194
+ /**
195
+ * How long a Bun pool's close() may take past its own timeout. Past its
196
+ * timeout it has closed every connection it holds, each telling the pool at
197
+ * once; what it may still wait for is a connection lent with none under it
198
+ * (bunSlotWithoutConnection), whose close never comes — there is no socket
199
+ * left to close.
200
+ */
201
+ export declare const BUN_CLOSE_GRACE_MS = 1000;
202
+ /**
203
+ * Closes a Bun pool: its work given `timeout` seconds to end, then every
204
+ * connection closed — and settled by then, BUN_CLOSE_GRACE_MS later at the
205
+ * latest: Bun 1.4.2's close() never settles once it has lent a connection
206
+ * with none under it (oven-sh/bun#42804).
207
+ */
208
+ export declare function closeBunPool(pool: {
209
+ close(options?: {
210
+ timeout?: number;
211
+ }): PromiseLike<unknown>;
212
+ }, timeout: number): Promise<void>;
213
+ /** Bun's SQL client as renewingBunPool opens it. */
214
+ export interface BunSqlPool extends BunPool {
215
+ close(options?: {
216
+ timeout?: number;
217
+ }): PromiseLike<unknown>;
218
+ }
219
+ /** A Bun pool that opens itself anew when it lent a connection with none under it. */
220
+ export interface RenewingBunPool<P extends BunSqlPool = BunSqlPool> extends BunPool {
221
+ /** The pool it lends from now. */
222
+ readonly current: P;
223
+ /** How many times it opened itself anew. */
224
+ readonly renewals: number;
225
+ discard(reserved: BunReserved): void;
226
+ /** Closes the pool it lends from and the ones it left, settled by `timeout` and BUN_CLOSE_GRACE_MS at the latest (closeBunPool). */
227
+ close(options?: {
228
+ timeout?: number;
229
+ }): Promise<void>;
230
+ }
231
+ /**
232
+ * A Bun pool that stays whole when Bun's is not (Bun 1.4.2): told that a
233
+ * connection it lent has none under it (bunSlotWithoutConnection), it lends
234
+ * from then on from a pool it opens anew — `open()`, as many connections —
235
+ * and leaves the old one: that one lends nothing more, its connections lent
236
+ * go on to their end, and once they have it closes them all. The connection
237
+ * with none under it goes back to it, which is what it waits for. The pool
238
+ * Bun cannot mend is replaced, never shrunk: however many times it happens,
239
+ * the app has its full count of connections again, and no more than that
240
+ * beside the ones still at work in the pools it left.
241
+ */
242
+ export declare function renewingBunPool<P extends BunSqlPool>(open: () => P): RenewingBunPool<P>;
243
+ /**
244
+ * cancelOpening through a pool of its own: the backend whose statement in
245
+ * flight starts with the tag's comment — this login's, in this database. A
246
+ * tag names one round trip, so a cancel that arrives late finds nothing: the
247
+ * backend is idle, or runs a statement that does not start with it.
248
+ */
249
+ export declare function cancelOpeningThrough(control: {
250
+ unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
251
+ }): NonNullable<SqlDriver['cancelOpening']>;
252
+ /**
253
+ * cancelBackend through a pool of its own (logged in as the app): it matches
254
+ * pid, backend_start AND xact_start, this login and this database — a ticket
255
+ * of a transaction that has ended cancels nothing.
256
+ */
257
+ export declare function cancelBackendThrough(control: {
258
+ unsafe(sql: string, params?: unknown[]): PromiseLike<unknown>;
259
+ }): NonNullable<SqlDriver['cancelBackend']>;
260
+ export declare class DriverRefused extends Error {
261
+ constructor(what: string);
262
+ }
263
+ /** Asks Postgres what it received for each kind of text the engine sends, and refuses a driver that changed it. */
264
+ export declare function verifyDriver(sql: SqlDriver): Promise<void>;
265
+ /** What postgresJsDriver needs of postgres.js: its pool, as `postgres(url, { types: POSTGRES_JS_TYPES })` makes it. */
266
+ export interface PostgresJs {
267
+ unsafe(text: string, params?: unknown[]): PromiseLike<unknown> & {
268
+ simple(): PromiseLike<unknown>;
269
+ };
270
+ begin<T>(fn: (tx: PostgresJsTx) => Promise<T>): Promise<T>;
271
+ }
272
+ interface PostgresJsTx {
273
+ unsafe(text: string, params?: unknown[]): PromiseLike<unknown> & {
274
+ simple(): PromiseLike<unknown>;
275
+ };
276
+ savepoint<T>(fn: (tx: PostgresJsTx) => Promise<T>): Promise<T>;
277
+ }
278
+ /**
279
+ * postgres.js as the engine's driver: its transaction runs the opening after
280
+ * its own BEGIN, before the work — a round trip more than Bun's, the same
281
+ * transaction. `control`, a pool of one, is what a cancel goes through.
282
+ *
283
+ * postgres.js sends a statement on a connection whose close it has not read
284
+ * yet, and fails it then (CONNECTION_CLOSED). A transaction whose BEGIN or
285
+ * opening failed so — before the work was handed the transaction — goes on
286
+ * another connection; a statement on its own is a transaction of one, so
287
+ * the same holds for it. Nothing that reached the work is sent again.
288
+ *
289
+ * Once postgres.js has seen a transaction's connection close, nothing more
290
+ * is given to it. postgres.js 3.4.9 writes what it is given next — the
291
+ * ROLLBACK of the transaction the failure ended, its COMMIT, a statement —
292
+ * to the closed socket, and throws where no caller can catch it
293
+ * (porsager/postgres#1133): a Node app would stop on every transaction a
294
+ * database that moved cut. So a statement after the close is refused with
295
+ * it, and the transaction's end — or a savepoint's — is never asked for:
296
+ * postgres.js has already rejected the transaction's begin() with the close.
297
+ */
298
+ export declare function postgresJsDriver(pool: PostgresJs, control: PostgresJs): SqlDriver;
299
+ export {};
@@ -0,0 +1,14 @@
1
+ import { type SchemaDef } from '../dsl/schema.js';
2
+ /** The lines that wrap the generated block, so a tool can replace it in a file that holds more. */
3
+ export declare const ENV_BLOCK_START = "// palbase:env:begin";
4
+ export declare const ENV_BLOCK_END = "// palbase:env:end";
5
+ /**
6
+ * The palbase-env.d.ts text for a project's schemas (db/public.ts,
7
+ * db/billing.ts, …), augmenting `module`'s `Tables` and `Schemas`. It ends in
8
+ * `export {};`: without it the file is a global script and `declare module`
9
+ * there would DECLARE a module that shadows the real one instead of
10
+ * augmenting it.
11
+ */
12
+ export declare function makeEnvDts(schemas: readonly SchemaDef[], options?: {
13
+ module?: string;
14
+ }): string;