okmodel 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -6
- package/dist/contracts/catalog/document.d.ts +20 -0
- package/dist/contracts/driver.d.ts +7 -0
- package/dist/dialects/pg/introspect.d.ts +6 -0
- package/dist/dialects/pg/role/sql.d.ts +10 -0
- package/dist/dialects/pg/schema.d.ts +15 -6
- package/dist/dialects/pg/view/index.d.ts +20 -6
- package/dist/dialects/pg/view/scratch.d.ts +2 -2
- package/dist/runtime/pg/bun.d.ts +12 -7
- package/dist/runtime/pg/bun.js +4 -1
- package/dist/runtime/pg/pg.d.ts +12 -6
- package/dist/runtime/pg/pg.js +4 -1
- package/dist/runtime/pg/pglite.d.ts +11 -3
- package/dist/runtime/pg/pglite.js +4 -1
- package/dist/runtime/pg/postgresjs.d.ts +12 -6
- package/dist/runtime/pg/postgresjs.js +4 -1
- package/dist/runtime/plan.d.ts +3 -1
- package/dist/runtime/tenancy/index.js +1 -1
- package/dist/runtime/topology.d.ts +191 -0
- package/dist/runtime/traits/index.js +6 -6
- package/dist/runtime/types.d.ts +57 -2
- package/dist/runtime/validate/index.js +1 -1
- package/dist/shared/{nxb42q7h.js → 47xzb9fc.js} +2 -2
- package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
- package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
- package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
- package/dist/shared/{sr5ms7b3.js → 922qbgnw.js} +3 -3
- package/dist/shared/{tny2t3sm.js → 990a291j.js} +4 -4
- package/dist/shared/{jns2wx4t.js → 9tgmr1gh.js} +1 -1
- package/dist/shared/{n5y9zyj1.js → ayz64vzj.js} +13 -12
- package/dist/shared/{kkzwp3d2.js → bt9sfq0z.js} +16 -14
- package/dist/shared/{c5g81ngb.js → bzsjtd7c.js} +7 -5
- package/dist/shared/{radd3z4g.js → ckjbyrt9.js} +1 -1
- package/dist/shared/{zg24x0hm.js → dxjp14rk.js} +10 -2
- package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
- package/dist/shared/{yjw02enp.js → fyqkk1m0.js} +4 -4
- package/dist/shared/{ygem90rn.js → g516zdsb.js} +8 -3
- package/dist/shared/{v7tpnhnz.js → h4nqvjkp.js} +5 -3
- package/dist/shared/{edwqqryd.js → h8ne16fq.js} +1 -1
- package/dist/shared/{ady1ms74.js → jdgqqx31.js} +5 -5
- package/dist/shared/{kafaa5zv.js → k7cc9nzk.js} +24 -7
- package/dist/shared/{7nk68fj8.js → kdkxh3kb.js} +17 -10
- package/dist/shared/nkc9sfqb.js +1161 -0
- package/dist/shared/{awrsqrn7.js → p7pfx624.js} +2 -2
- package/dist/shared/{xrfnq5k8.js → qgvt8nx1.js} +11 -2
- package/dist/shared/{84ptn35q.js → qy9rdq0g.js} +6 -5
- package/dist/shared/{qn0srwwq.js → r6pj4fq2.js} +5 -3
- package/dist/shared/{2321avmn.js → r73pt594.js} +21 -13
- package/dist/shared/{kj680nxv.js → rasvvzgz.js} +8 -6
- package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
- package/dist/shared/{axhk06q1.js → tq9afp8d.js} +1 -1
- package/dist/shared/wxn4b7pp.js +252 -0
- package/dist/shared/{q6zsyh31.js → xdpj5kkm.js} +19 -8
- package/dist/shared/{s4mb6a4b.js → xhh1srw3.js} +54 -10
- package/dist/shared/{jgxzjzcz.js → xsq7v6wp.js} +2 -2
- package/dist/shared/{hqjsxnz7.js → ykv7wh5b.js} +5 -4
- package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
- package/dist/tooling/cli.js +12 -12
- package/dist/tooling/migrate/apply.d.ts +2 -0
- package/dist/tooling/migrate/files.d.ts +7 -0
- package/dist/tooling/migrate/index.js +8 -6
- package/dist/tooling/testing/facts.d.ts +3 -0
- package/dist/tooling/testing/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
okmodel is a catalog-first TypeScript ORM, PostgreSQL first. Other SQL databases are the direction. CI runs PostgreSQL 15 to 18. The schema is the single source. Migrations and queries come from it.
|
|
4
4
|
|
|
5
|
-
Version 0.
|
|
5
|
+
Version 0.5.0. Apache-2.0.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -58,6 +58,8 @@ Version 0.4.0. Apache-2.0.
|
|
|
58
58
|
- [Reference data](#reference-data)
|
|
59
59
|
- [Backfill](#backfill)
|
|
60
60
|
- [Testing](#testing)
|
|
61
|
+
- [Topology](#topology)
|
|
62
|
+
- [Reference app](#reference-app)
|
|
61
63
|
- [Startup](#startup)
|
|
62
64
|
- [Protected targets](#protected-targets)
|
|
63
65
|
- [Commands](#commands)
|
|
@@ -990,6 +992,83 @@ export default async function seed(t: {
|
|
|
990
992
|
bunx okm seed seed.ts
|
|
991
993
|
```
|
|
992
994
|
|
|
995
|
+
### Topology
|
|
996
|
+
|
|
997
|
+
`connect({ primary, replicas }, options)` opens one pool per endpoint. An automatic read chooses a replica that has replayed at least as far as this client's last committed write. `routing.consistency` is `"session"` by default. `"eventual"` does not keep that watermark. `routing.maxLag` is a duration (`"5s"`, `"500ms"`, `"2m"`) or a size (`"16MB"`, `"512KB"`, `"1GB"`, `"4096B"`). A bare number is rejected. `find({ route: "primary" })` reads the primary. `find({ route: "replica" })` requires a replica that satisfies the same watermark and lag, and it does not fall back to the primary. `db.using("primary")` or `db.using("replica")` returns a client that forces that route. That client has no `close` and no `using`. `onRoute` receives `{ op, endpoint, reason }`. A throw from it is ignored. A string or an existing pool is one endpoint and serves either `route` from it, and it has no `using`.
|
|
998
|
+
|
|
999
|
+
The watermark is one value for the whole `connect()`. The root, `for()`, `unscoped()`, `using()`, and `reserve()` share it. A write through any of them moves it for the others. A watermark per `for()` client is not in this version.
|
|
1000
|
+
|
|
1001
|
+
When replicas are configured and consistency is `"session"`, a committed write reads `pg_current_wal_insert_lsn()` before its promise resolves. That is one extra round trip: on the primary pool after an autocommit `execute` or `batch`, and on the reserved connection after a successful `COMMIT`. A rollback does not read it. `"eventual"`, and a connect with no replicas, do not read it either.
|
|
1002
|
+
|
|
1003
|
+
`routing.select` picks among the replicas that passed health, the watermark, and `maxLag`. The default is `"weighted"`. `weighted` is smooth weighted round-robin. Equal weights take turns. A weight of `0` is rejected. `roundRobin` ignores weights and rotates through the eligible replicas. `leastConnections` uses the replica with the fewest statements already in flight on this client. A tie follows configuration order. `latencyAware` uses the replica with the lowest moving average of round-trip time. A function receives each candidate as `{ name, weight, inflight, latencyMs, lag }` and `{ op: "read" }`. `lag` is the number of bytes the replica is behind the primary, or `null` when that distance is unknown. The function returns one of those objects or its `name`. It runs only when an automatic read has two or more candidates.
|
|
1004
|
+
|
|
1005
|
+
A write, a batch, a transaction, or a locking read on a replica route is OKM1840. `route: "replica"` or `using("replica")` with no eligible replica is OKM1843, including a topology with no replicas. An automatic read with `routing.fallback: "error"` and no eligible replica is OKM1844. A migrate target that carries `primary`, `replicas`, `weight`, or `pool` is OKM1845. Waiting for a connection past `timeouts.acquire` is OKM1846, and that wait stays on the endpoint's own pool. The rules are in [topology](https://github.com/omqkhafi/okmodel/blob/main/docs/topology.md).
|
|
1006
|
+
|
|
1007
|
+
A replica whose pool is at its maximum, with no idle connection and callers waiting, is skipped. If every replica that can serve the read is in that state, the read uses the primary and `onRoute` reports `fallback:saturated`. When healthy replicas exist and none has caught up, or none is inside `maxLag`, the reason is `fallback:behind`. `route: "replica"` still uses a saturated replica, and it still requires the watermark and `maxLag`.
|
|
1008
|
+
|
|
1009
|
+
`replicas.ts`:
|
|
1010
|
+
|
|
1011
|
+
```ts
|
|
1012
|
+
import { connect } from "okmodel/pg/postgresjs";
|
|
1013
|
+
|
|
1014
|
+
import schema from "./schema.ts";
|
|
1015
|
+
|
|
1016
|
+
const url = process.env.DATABASE_URL;
|
|
1017
|
+
if (url === undefined || url.length === 0) throw new Error("DATABASE_URL is not set");
|
|
1018
|
+
const east = process.env.REPLICA_URL;
|
|
1019
|
+
if (east === undefined || east.length === 0) throw new Error("REPLICA_URL is not set");
|
|
1020
|
+
const west = process.env.REPLICA_URL_WEST ?? east;
|
|
1021
|
+
|
|
1022
|
+
const endpoints = {
|
|
1023
|
+
primary: url,
|
|
1024
|
+
replicas: [{ url: east, weight: 2, name: "east" }, west],
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
const db = await connect(endpoints, {
|
|
1028
|
+
schema,
|
|
1029
|
+
routing: { probe: "1s", fallback: "primary", consistency: "session", select: "weighted" },
|
|
1030
|
+
onRoute(event) {
|
|
1031
|
+
// { op: "read", endpoint: "east", reason: "auto:east" }
|
|
1032
|
+
void event;
|
|
1033
|
+
},
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
await db.notes.find({ limit: 50 });
|
|
1037
|
+
await db.notes.find({ limit: 50, route: "primary" });
|
|
1038
|
+
await db.notes.find({ limit: 50, route: "replica" });
|
|
1039
|
+
const replica = db.using("replica");
|
|
1040
|
+
await replica.notes.find({ limit: 50 });
|
|
1041
|
+
await db.close();
|
|
1042
|
+
|
|
1043
|
+
const eventual = await connect(endpoints, {
|
|
1044
|
+
schema,
|
|
1045
|
+
routing: { consistency: "eventual" },
|
|
1046
|
+
});
|
|
1047
|
+
await eventual.notes.find({ limit: 50 });
|
|
1048
|
+
await eventual.close();
|
|
1049
|
+
|
|
1050
|
+
for (const select of ["roundRobin", "leastConnections", "latencyAware"] as const) {
|
|
1051
|
+
const client = await connect(endpoints, { schema, routing: { select } });
|
|
1052
|
+
await client.notes.find({ limit: 1 });
|
|
1053
|
+
await client.close();
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const custom = await connect(endpoints, {
|
|
1057
|
+
schema,
|
|
1058
|
+
routing: {
|
|
1059
|
+
select(candidates) {
|
|
1060
|
+
return candidates[0] ?? "east";
|
|
1061
|
+
},
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
await custom.notes.find({ limit: 1 });
|
|
1065
|
+
await custom.close();
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
### Reference app
|
|
1069
|
+
|
|
1070
|
+
The worked example is a project tracker: workspaces as tenants, archive, a function, two views, three generated migrations, and this topology. It is a private package in the repository, not on npm. CI runs it on the primary and two standbys. Board reads go to a replica. A read just after a write, with replay paused, comes back from the primary. The report uses `route: "replica"`. See the [example app](https://github.com/omqkhafi/okmodel/blob/main/docs/example-app.md) and the [reference app notes](https://github.com/omqkhafi/okmodel/blob/main/packages/reference-app/README.md).
|
|
1071
|
+
|
|
993
1072
|
### Startup
|
|
994
1073
|
|
|
995
1074
|
`connect()` compares the app with `okm_history`. A database that is ahead by expand migrations still opens. Ahead by a contract migration, or behind the app, is OKM1520 and names the migration. `okm migrate status` prints the same states, plus `failed at step N (resume with okm migrate apply)`.
|
|
@@ -1064,7 +1143,7 @@ Status is on the [board](https://github.com/users/omqkhafi/projects/1). Each rel
|
|
|
1064
1143
|
- [x] [0.2](https://github.com/omqkhafi/okmodel/milestone/2) — Hidden and sensitive fields, validation, traits, tenancy, archive and restore, richer relations, presets, transactions, and operators for JSON, arrays, ranges, and search.
|
|
1065
1144
|
- [x] [0.3](https://github.com/omqkhafi/okmodel/milestone/3) — Extensions, domains, functions, triggers, views, roles, and grants.
|
|
1066
1145
|
- [x] [0.4](https://github.com/omqkhafi/okmodel/milestone/4) — Safer migration plans, backfill, drift checks, provisioning, reference data, and a testing package.
|
|
1067
|
-
- [
|
|
1146
|
+
- [x] [0.5](https://github.com/omqkhafi/okmodel/milestone/5) — A primary with replicas, read routing, and a reference app.
|
|
1068
1147
|
|
|
1069
1148
|
`okmodel/internal` has no stability promise. Names on that subpath can change or disappear in any release.
|
|
1070
1149
|
|
|
@@ -1076,11 +1155,11 @@ Measured on this release.
|
|
|
1076
1155
|
|
|
1077
1156
|
| | Minified | Gzip | Cold import |
|
|
1078
1157
|
| -------------------------------------------- | -------: | -----: | ----------: |
|
|
1079
|
-
| Runtime entry | 5,288 | 2,026 | 2.
|
|
1080
|
-
| App startup (10 tables, one find) | 89,
|
|
1081
|
-
| App startup, every 0.2 feature in use (full) |
|
|
1158
|
+
| Runtime entry | 5,288 | 2,026 | 2.347 ms |
|
|
1159
|
+
| App startup (10 tables, one find) | 89,751 | 29,801 | 15.061 ms |
|
|
1160
|
+
| App startup, every 0.2 feature in use (full) | 120,132 | 39,162 | 15.956 ms |
|
|
1082
1161
|
|
|
1083
|
-
The full app has column tenancy, `archivable()`, `timestamps()`, validation rules, `one`, `many` and `manyThrough` relations, presets, and calls `include`, `page`, `aggregate`, `tx` and `batch`. A feature costs bytes only in an app that uses it: the full app is 30,
|
|
1162
|
+
The full app has column tenancy, `archivable()`, `timestamps()`, validation rules, `one`, `many` and `manyThrough` relations, presets, and calls `include`, `page`, `aggregate`, `tx` and `batch`. A feature costs bytes only in an app that uses it: the full app is 30,381 minified and 9,361 gzip bytes above the plain one. The runtime entry and the plain app are gated. The full app is printed, not gated. The plain app's cold import on this sample is above the 15 ms local reference. That figure is printed and is not a gate.
|
|
1084
1163
|
|
|
1085
1164
|
The rest of the measurements are in [size](https://github.com/omqkhafi/okmodel/blob/main/docs/size.md).
|
|
1086
1165
|
|
|
@@ -1092,5 +1171,6 @@ The rest of the measurements are in [size](https://github.com/omqkhafi/okmodel/b
|
|
|
1092
1171
|
- [Backfill](https://github.com/omqkhafi/okmodel/blob/main/docs/backfill.md)
|
|
1093
1172
|
- [Provisioning](https://github.com/omqkhafi/okmodel/blob/main/docs/provisioning.md)
|
|
1094
1173
|
- [Testing](https://github.com/omqkhafi/okmodel/blob/main/docs/testing.md)
|
|
1174
|
+
- [Topology](https://github.com/omqkhafi/okmodel/blob/main/docs/topology.md)
|
|
1095
1175
|
- [Known limits](https://github.com/omqkhafi/okmodel/blob/main/docs/known-limits.md)
|
|
1096
1176
|
- [Changelog](https://github.com/omqkhafi/okmodel/blob/main/changelog.md)
|
|
@@ -62,6 +62,26 @@ export declare function serializeCatalog(source: Catalog): string;
|
|
|
62
62
|
* @returns Lowercase hex digest
|
|
63
63
|
*/
|
|
64
64
|
export declare function catalogHash(source: Catalog): string;
|
|
65
|
+
/**
|
|
66
|
+
* The catalog `connect()` compares with `okm_meta` (D208).
|
|
67
|
+
*
|
|
68
|
+
* Roles, grants, and default privileges come from the migration config, not
|
|
69
|
+
* the schema, so they are left out. A `schema()` catalog never has them, so
|
|
70
|
+
* `connect()` hashes it unchanged. `okm check` still verifies them. A
|
|
71
|
+
* catalog without them is returned as is.
|
|
72
|
+
*
|
|
73
|
+
* @param source - Catalog document
|
|
74
|
+
* @returns The catalog without role, grant, and default-privilege objects
|
|
75
|
+
*/
|
|
76
|
+
export declare function startupCatalog(source: Catalog): Catalog;
|
|
77
|
+
/**
|
|
78
|
+
* {@link catalogHash} of {@link startupCatalog}. The hash `okm_meta` and
|
|
79
|
+
* `okm_history` store, and the one `connect()` computes.
|
|
80
|
+
*
|
|
81
|
+
* @param source - Catalog document
|
|
82
|
+
* @returns Lowercase hex digest
|
|
83
|
+
*/
|
|
84
|
+
export declare function startupHash(source: Catalog): string;
|
|
65
85
|
/**
|
|
66
86
|
* Loads a build artifact without validating objects.
|
|
67
87
|
*
|
|
@@ -61,6 +61,13 @@ export type ExecuteOptions = {
|
|
|
61
61
|
* A deadline is kind `timeout`.
|
|
62
62
|
*/
|
|
63
63
|
readonly timeout?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Read routing hint.
|
|
66
|
+
*
|
|
67
|
+
* Drivers ignore it. A topology router reads it. Absent, the router
|
|
68
|
+
* classifies the statement.
|
|
69
|
+
*/
|
|
70
|
+
readonly route?: "primary" | "replica";
|
|
64
71
|
};
|
|
65
72
|
/** Column names and parameter count from a describe. */
|
|
66
73
|
export type DescribeResult = {
|
|
@@ -39,6 +39,12 @@ export type CatalogQuery = {
|
|
|
39
39
|
* @returns A checked catalog
|
|
40
40
|
*/
|
|
41
41
|
export declare function introspectSchema(runner: CatalogQuery, concrete: string, logical?: string, options?: IntrospectOptions): Promise<Catalog>;
|
|
42
|
+
/**
|
|
43
|
+
* A function's argument types as a grant names them, for `pg_proc p`.
|
|
44
|
+
*
|
|
45
|
+
* Types only, comma-separated, no spaces. Argument names are left out.
|
|
46
|
+
*/
|
|
47
|
+
export declare const FUNCTION_ARG_TYPES = "replace(oidvectortypes(p.proargtypes), ' ', '')";
|
|
42
48
|
/**
|
|
43
49
|
* Applies one expression as a check and reads Postgres's reprint.
|
|
44
50
|
*
|
|
@@ -42,6 +42,16 @@ export declare function privilegeSql(before: readonly CatalogObject[], after: re
|
|
|
42
42
|
* @returns The role, or `undefined` when the statement is not `CREATE ROLE`
|
|
43
43
|
*/
|
|
44
44
|
export declare function createdRoleName(sql: string): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Runs a plan's `CREATE ROLE` so a role another session created first is not an error.
|
|
47
|
+
*
|
|
48
|
+
* Roles belong to the cluster. Two databases provisioned at once both find the
|
|
49
|
+
* role absent, and the second `CREATE ROLE` fails on `pg_authid`.
|
|
50
|
+
*
|
|
51
|
+
* @param sql - A `CREATE ROLE` statement from the plan
|
|
52
|
+
* @returns The statement in a `DO` block that ignores a duplicate role
|
|
53
|
+
*/
|
|
54
|
+
export declare function createRoleOnce(sql: string): string;
|
|
45
55
|
/**
|
|
46
56
|
* Reports whether a statement creates or alters a role.
|
|
47
57
|
*
|
|
@@ -144,6 +144,13 @@ type ApplySchemaTraits<TTable, TFields, TTraits> = TTable extends {
|
|
|
144
144
|
readonly "~insert": InsertFrom<TFields>;
|
|
145
145
|
readonly "~update": UpdateFrom<TFields>;
|
|
146
146
|
} & HasArchive<TTraits> & PresetsOfList<TTraits>;
|
|
147
|
+
/**
|
|
148
|
+
* `~views` when `schema({ views })` declared any, so `db.views` is typed.
|
|
149
|
+
* Nothing at runtime carries the key.
|
|
150
|
+
*/
|
|
151
|
+
type SchemaViews<TViews extends readonly unknown[]> = TViews extends readonly [] ? unknown : {
|
|
152
|
+
readonly "~views": TViews;
|
|
153
|
+
};
|
|
147
154
|
/**
|
|
148
155
|
* Compiles tables into a catalog.
|
|
149
156
|
*
|
|
@@ -156,19 +163,21 @@ type ApplySchemaTraits<TTable, TFields, TTraits> = TTable extends {
|
|
|
156
163
|
*/
|
|
157
164
|
export declare function schema<const TTables extends readonly AnyTable[], const TTraits extends readonly {
|
|
158
165
|
readonly fields: Readonly<Record<string, object>>;
|
|
159
|
-
}[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined>(config: Omit<SchemaInput<TTables>, "traits" | "tenancy" | "validation"> & {
|
|
166
|
+
}[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined, const TViews extends readonly unknown[] = readonly []>(config: Omit<SchemaInput<TTables>, "traits" | "tenancy" | "validation" | "views"> & {
|
|
160
167
|
readonly traits: TTraits;
|
|
161
168
|
readonly tenancy?: TTenancy;
|
|
162
169
|
readonly validation?: TValidation;
|
|
163
|
-
|
|
170
|
+
readonly views?: TViews;
|
|
171
|
+
}): ([TValidation] extends [undefined] ? TenancySchema<SchemaWithTraits<TTables, TTraits>, TTables, TTenancy> : TenancySchema<SchemaWithTraits<TTables, TTraits>, TTables, TTenancy> & {
|
|
164
172
|
readonly "~validation": TValidation;
|
|
165
|
-
}
|
|
166
|
-
export declare function schema<const TTables extends readonly AnyTable[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined>(config: Omit<SchemaInput<TTables>, "tenancy" | "validation"> & {
|
|
173
|
+
}) & SchemaViews<TViews>;
|
|
174
|
+
export declare function schema<const TTables extends readonly AnyTable[], const TTenancy extends ColumnTenancy | undefined = undefined, const TValidation = undefined, const TViews extends readonly unknown[] = readonly []>(config: Omit<SchemaInput<TTables>, "tenancy" | "validation" | "views"> & {
|
|
167
175
|
readonly tenancy?: TTenancy;
|
|
168
176
|
readonly validation?: TValidation;
|
|
169
|
-
|
|
177
|
+
readonly views?: TViews;
|
|
178
|
+
}): ([TValidation] extends [undefined] ? TenancySchema<BuiltSchema<TTables>, TTables, TTenancy> : TenancySchema<BuiltSchema<TTables>, TTables, TTenancy> & {
|
|
170
179
|
readonly "~validation": TValidation;
|
|
171
|
-
}
|
|
180
|
+
}) & SchemaViews<TViews>;
|
|
172
181
|
/**
|
|
173
182
|
* Registers a hook from an opt-in module.
|
|
174
183
|
*
|
|
@@ -34,10 +34,15 @@ export type ViewOptions = {
|
|
|
34
34
|
*
|
|
35
35
|
* `contribute` builds the catalog record. Column dependencies are filled
|
|
36
36
|
* from `pg_depend` by scratch verification, not at this call.
|
|
37
|
+
*
|
|
38
|
+
* @typeParam N - View name
|
|
39
|
+
* @typeParam C - Declared output columns
|
|
37
40
|
*/
|
|
38
|
-
export type ViewDeclaration = {
|
|
39
|
-
readonly name:
|
|
41
|
+
export type ViewDeclaration<N extends string = string, C extends readonly ViewColumnInput[] = readonly ViewColumnInput[]> = {
|
|
42
|
+
readonly name: N;
|
|
40
43
|
readonly schema: string;
|
|
44
|
+
/** Declared columns, for types only. `db.views` reads them. */
|
|
45
|
+
readonly "~columns"?: C;
|
|
41
46
|
/**
|
|
42
47
|
* Catalog record for this declaration.
|
|
43
48
|
*
|
|
@@ -73,10 +78,15 @@ export type MaterializedViewOptions = ViewOptions & {
|
|
|
73
78
|
* One declared materialized view.
|
|
74
79
|
*
|
|
75
80
|
* Identity is `(namespace, name)`. Indexes are separate catalog objects.
|
|
81
|
+
*
|
|
82
|
+
* @typeParam N - Materialized view name
|
|
83
|
+
* @typeParam C - Declared output columns
|
|
76
84
|
*/
|
|
77
|
-
export type MaterializedViewDeclaration = {
|
|
78
|
-
readonly name:
|
|
85
|
+
export type MaterializedViewDeclaration<N extends string = string, C extends readonly ViewColumnInput[] = readonly ViewColumnInput[]> = {
|
|
86
|
+
readonly name: N;
|
|
79
87
|
readonly schema: string;
|
|
88
|
+
/** Declared columns, for types only. `db.views` reads them. */
|
|
89
|
+
readonly "~columns"?: C;
|
|
80
90
|
/**
|
|
81
91
|
* Catalog records for this declaration.
|
|
82
92
|
*
|
|
@@ -100,7 +110,9 @@ export type MaterializedViewDeclaration = {
|
|
|
100
110
|
* @param options - Columns and query
|
|
101
111
|
* @returns The definition object `schema({ views })` stores
|
|
102
112
|
*/
|
|
103
|
-
export declare function view(name:
|
|
113
|
+
export declare function view<const N extends string, const C extends readonly ViewColumnInput[]>(name: N, options: ViewOptions & {
|
|
114
|
+
readonly columns: C;
|
|
115
|
+
}): ViewDeclaration<N, C>;
|
|
104
116
|
/**
|
|
105
117
|
* Declares a materialized view.
|
|
106
118
|
*
|
|
@@ -111,4 +123,6 @@ export declare function view(name: string, options: ViewOptions): ViewDeclaratio
|
|
|
111
123
|
* @param options - Columns, query, indexes, and refresh mode
|
|
112
124
|
* @returns The definition object `schema({ views })` stores
|
|
113
125
|
*/
|
|
114
|
-
export declare function materializedView(name:
|
|
126
|
+
export declare function materializedView<const N extends string, const C extends readonly ViewColumnInput[]>(name: N, options: MaterializedViewOptions & {
|
|
127
|
+
readonly columns: C;
|
|
128
|
+
}): MaterializedViewDeclaration<N, C>;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Verifies declared views on a scratch schema.
|
|
3
3
|
*
|
|
4
4
|
* Domain and enum types from the catalog are created there, then the tables,
|
|
5
|
-
* then
|
|
6
|
-
* supplies column edges (D117).
|
|
5
|
+
* then the functions a view may call, then each view. `pg_get_viewdef`
|
|
6
|
+
* replaces the author query and `pg_depend` supplies column edges (D117).
|
|
7
7
|
* The scratch schema is dropped before this returns. Plans never use it.
|
|
8
8
|
*/
|
|
9
9
|
import type { Catalog } from "../../../contracts/catalog/types.js";
|
package/dist/runtime/pg/bun.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `connect` for Bun.sql (`okmodel/pg/bun`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* inside the adapter.
|
|
4
|
+
* A string or an existing pool is one endpoint. A `{ primary, replicas }` object
|
|
5
|
+
* loads the topology module. The client is typed by the schema passed in.
|
|
6
|
+
* This entry runs on Bun. Node cannot load the `bun` import inside the adapter.
|
|
7
7
|
*/
|
|
8
8
|
import type { DriverPool } from "../../contracts/driver.js";
|
|
9
9
|
import type { QuerySchema } from "../../dialects/pg/model.js";
|
|
10
10
|
import { type BunSqlConfig } from "../../adapters/pg/bunsql.js";
|
|
11
|
+
import type { RoutedClient, TopologyInput, TopologyOptions } from "../topology.js";
|
|
11
12
|
import type { ConnectOptions, Connected } from "../types.js";
|
|
12
13
|
export { open, type BunSqlConfig, type BunSqlTls } from "../../adapters/pg/bunsql.js";
|
|
13
14
|
export { capabilities, hasCapability, readCapability } from "../../adapters/pg/bunsql.js";
|
|
@@ -15,16 +16,20 @@ export { DriverError } from "../../adapters/error.js";
|
|
|
15
16
|
/** A connection string or a pool from {@link open}. */
|
|
16
17
|
export type BunSqlTarget = string | DriverPool;
|
|
17
18
|
/** Driver options `connect` forwards to {@link open}. */
|
|
18
|
-
export type BunSqlConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<BunSqlConfig, "url"
|
|
19
|
+
export type BunSqlConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<BunSqlConfig, "url"> & TopologyOptions;
|
|
19
20
|
/**
|
|
20
|
-
* Connects one Postgres endpoint through Bun.sql
|
|
21
|
+
* Connects one Postgres endpoint through Bun.sql, or a primary and its replicas.
|
|
22
|
+
*
|
|
23
|
+
* A string or a pool is one endpoint. A topology loads on demand and the promise
|
|
24
|
+
* resolves to the client. Reads use the first healthy replica.
|
|
21
25
|
*
|
|
22
26
|
* Named prepared statements are off unless `prepared` is `"named"`.
|
|
23
27
|
* `prepared: "named"` is not for transaction-mode poolers.
|
|
24
28
|
*
|
|
25
29
|
* @typeParam S - Schema
|
|
26
|
-
* @param target - URL
|
|
30
|
+
* @param target - URL, an existing pool, or `{ primary, replicas }`
|
|
27
31
|
* @param options - Schema, errors, logger, and driver options
|
|
28
|
-
* @returns The client. Await `connected` for the dialect check
|
|
32
|
+
* @returns The client. A topology returns a promise. Await `connected` for the dialect check
|
|
29
33
|
*/
|
|
34
|
+
export declare function connect<const S extends QuerySchema>(target: TopologyInput, options: BunSqlConnectOptions<S>): Promise<RoutedClient<S>>;
|
|
30
35
|
export declare function connect<const S extends QuerySchema>(target: BunSqlTarget, options: BunSqlConnectOptions<S>): Connected<S>;
|
package/dist/runtime/pg/bun.js
CHANGED
|
@@ -11,10 +11,13 @@ import {
|
|
|
11
11
|
} from "../../shared/rrstp3kb.js";
|
|
12
12
|
import {
|
|
13
13
|
createClient
|
|
14
|
-
} from "../../shared/
|
|
14
|
+
} from "../../shared/bt9sfq0z.js";
|
|
15
15
|
|
|
16
16
|
// src/runtime/pg/bun.ts
|
|
17
17
|
function connect(target, options) {
|
|
18
|
+
if (typeof target === "object" && !isPool(target)) {
|
|
19
|
+
return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open5));
|
|
20
|
+
}
|
|
18
21
|
const owned = !isPool(target);
|
|
19
22
|
const pool = isPool(target) ? target : open5({
|
|
20
23
|
url: target,
|
package/dist/runtime/pg/pg.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `connect` for node-postgres (`okmodel/pg/pg`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* schema passed in, not
|
|
4
|
+
* A string or an existing pool is one endpoint. A `{ primary, replicas }` object
|
|
5
|
+
* loads the topology module. The client is typed by the schema passed in, not
|
|
6
|
+
* by `Register`.
|
|
6
7
|
*/
|
|
7
8
|
import type { DriverPool } from "../../contracts/driver.js";
|
|
8
9
|
import type { QuerySchema } from "../../dialects/pg/model.js";
|
|
9
10
|
import { type NodePostgresConfig } from "../../adapters/pg/nodepostgres.js";
|
|
11
|
+
import type { RoutedClient, TopologyInput, TopologyOptions } from "../topology.js";
|
|
10
12
|
import type { ConnectOptions, Connected } from "../types.js";
|
|
11
13
|
export { open, type NodePostgresConfig } from "../../adapters/pg/nodepostgres.js";
|
|
12
14
|
export { capabilities, hasCapability, readCapability } from "../../adapters/pg/nodepostgres.js";
|
|
@@ -14,16 +16,20 @@ export { DriverError } from "../../adapters/error.js";
|
|
|
14
16
|
/** A connection string or a pool from {@link open}. */
|
|
15
17
|
export type NodePostgresTarget = string | DriverPool;
|
|
16
18
|
/** Driver options `connect` forwards to {@link open}. */
|
|
17
|
-
export type NodePostgresConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<NodePostgresConfig, "url"
|
|
19
|
+
export type NodePostgresConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<NodePostgresConfig, "url"> & TopologyOptions;
|
|
18
20
|
/**
|
|
19
|
-
* Connects one Postgres endpoint through node-postgres
|
|
21
|
+
* Connects one Postgres endpoint through node-postgres, or a primary and its replicas.
|
|
22
|
+
*
|
|
23
|
+
* A string or a pool is one endpoint. A topology loads on demand and the promise
|
|
24
|
+
* resolves to the client. Reads use the first healthy replica.
|
|
20
25
|
*
|
|
21
26
|
* Named prepared statements are off unless `prepared` is `"named"`.
|
|
22
27
|
* `prepared: "named"` is not for transaction-mode poolers.
|
|
23
28
|
*
|
|
24
29
|
* @typeParam S - Schema
|
|
25
|
-
* @param target - URL
|
|
30
|
+
* @param target - URL, an existing pool, or `{ primary, replicas }`
|
|
26
31
|
* @param options - Schema, errors, logger, and driver options
|
|
27
|
-
* @returns The client. Await `connected` for the dialect check
|
|
32
|
+
* @returns The client. A topology returns a promise. Await `connected` for the dialect check
|
|
28
33
|
*/
|
|
34
|
+
export declare function connect<const S extends QuerySchema>(target: TopologyInput, options: NodePostgresConnectOptions<S>): Promise<RoutedClient<S>>;
|
|
29
35
|
export declare function connect<const S extends QuerySchema>(target: NodePostgresTarget, options: NodePostgresConnectOptions<S>): Connected<S>;
|
package/dist/runtime/pg/pg.js
CHANGED
|
@@ -11,10 +11,13 @@ import {
|
|
|
11
11
|
} from "../../shared/rrstp3kb.js";
|
|
12
12
|
import {
|
|
13
13
|
createClient
|
|
14
|
-
} from "../../shared/
|
|
14
|
+
} from "../../shared/bt9sfq0z.js";
|
|
15
15
|
|
|
16
16
|
// src/runtime/pg/pg.ts
|
|
17
17
|
function connect(target, options) {
|
|
18
|
+
if (typeof target === "object" && !isPool(target)) {
|
|
19
|
+
return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open4));
|
|
20
|
+
}
|
|
18
21
|
const owned = !isPool(target);
|
|
19
22
|
const pool = isPool(target) ? target : open4({
|
|
20
23
|
url: target,
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* `connect` for PGlite (`okmodel/pg/pglite`).
|
|
3
3
|
*
|
|
4
4
|
* Opening a database is async. An existing pool is reused and not closed.
|
|
5
|
+
* A `{ primary, replicas }` object loads the topology module. A replica URL is
|
|
6
|
+
* that endpoint's data directory.
|
|
5
7
|
*/
|
|
6
8
|
import type { DriverPool } from "../../contracts/driver.js";
|
|
7
9
|
import type { QuerySchema } from "../../dialects/pg/model.js";
|
|
8
10
|
import { type PgliteConfig } from "../../adapters/pg/pglite.js";
|
|
11
|
+
import type { RoutedClient, TopologyInput, TopologyOptions } from "../topology.js";
|
|
9
12
|
import type { ConnectOptions, Connected } from "../types.js";
|
|
10
13
|
export { open, type PgliteConfig } from "../../adapters/pg/pglite.js";
|
|
11
14
|
export { capabilities, hasCapability, readCapability } from "../../adapters/pg/pglite.js";
|
|
@@ -13,13 +16,18 @@ export { DriverError } from "../../adapters/error.js";
|
|
|
13
16
|
/** A data directory, a pool from {@link open}, or omitted for memory. */
|
|
14
17
|
export type PgliteTarget = string | DriverPool | undefined;
|
|
15
18
|
/** Driver options `connect` forwards to {@link open}. */
|
|
16
|
-
export type PgliteConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<PgliteConfig, "dataDir"
|
|
19
|
+
export type PgliteConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<PgliteConfig, "dataDir"> & TopologyOptions;
|
|
17
20
|
/**
|
|
18
|
-
* Connects one PGlite endpoint and returns a client for `schema`.
|
|
21
|
+
* Connects one PGlite endpoint, or a primary and its replicas, and returns a client for `schema`.
|
|
22
|
+
*
|
|
23
|
+
* A string, a pool, or an omitted target is one endpoint. A topology loads on
|
|
24
|
+
* demand. Reads use the first healthy replica. A replica `url` is that
|
|
25
|
+
* endpoint's data directory.
|
|
19
26
|
*
|
|
20
27
|
* @typeParam S - Schema
|
|
21
|
-
* @param target - Data directory, existing pool,
|
|
28
|
+
* @param target - Data directory, existing pool, omitted for memory, or `{ primary, replicas }`
|
|
22
29
|
* @param options - Schema, errors, and logger
|
|
23
30
|
* @returns The client
|
|
24
31
|
*/
|
|
32
|
+
export declare function connect<const S extends QuerySchema>(target: TopologyInput, options: PgliteConnectOptions<S>): Promise<RoutedClient<S>>;
|
|
25
33
|
export declare function connect<const S extends QuerySchema>(target: PgliteTarget, options: PgliteConnectOptions<S>): Promise<Connected<S>>;
|
|
@@ -11,10 +11,13 @@ import {
|
|
|
11
11
|
} from "../../shared/rrstp3kb.js";
|
|
12
12
|
import {
|
|
13
13
|
createClient
|
|
14
|
-
} from "../../shared/
|
|
14
|
+
} from "../../shared/bt9sfq0z.js";
|
|
15
15
|
|
|
16
16
|
// src/runtime/pg/pglite.ts
|
|
17
17
|
async function connect(target, options) {
|
|
18
|
+
if (typeof target === "object" && !isPool(target)) {
|
|
19
|
+
return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open3));
|
|
20
|
+
}
|
|
18
21
|
const pool = isPool(target) ? target : await open3({
|
|
19
22
|
...target !== undefined ? { dataDir: target } : {},
|
|
20
23
|
...options.timeouts !== undefined ? { timeouts: options.timeouts } : {}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `connect` for postgres.js (`okmodel/pg/postgresjs`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* schema passed in, not
|
|
4
|
+
* A string or an existing pool is one endpoint. A `{ primary, replicas }` object
|
|
5
|
+
* loads the topology module. The client is typed by the schema passed in, not
|
|
6
|
+
* by `Register`.
|
|
6
7
|
*/
|
|
7
8
|
import type { DriverPool } from "../../contracts/driver.js";
|
|
8
9
|
import type { QuerySchema } from "../../dialects/pg/model.js";
|
|
9
10
|
import { type PostgresJsConfig } from "../../adapters/pg/postgresjs.js";
|
|
11
|
+
import type { RoutedClient, TopologyInput, TopologyOptions } from "../topology.js";
|
|
10
12
|
import type { ConnectOptions, Connected } from "../types.js";
|
|
11
13
|
export { open, type PostgresJsConfig } from "../../adapters/pg/postgresjs.js";
|
|
12
14
|
export { capabilities, hasCapability, readCapability } from "../../adapters/pg/postgresjs.js";
|
|
@@ -14,16 +16,20 @@ export { DriverError } from "../../adapters/error.js";
|
|
|
14
16
|
/** A connection string or a pool from {@link open}. */
|
|
15
17
|
export type PostgresTarget = string | DriverPool;
|
|
16
18
|
/** Driver options `connect` forwards to {@link open}. */
|
|
17
|
-
export type PostgresConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<PostgresJsConfig, "url"
|
|
19
|
+
export type PostgresConnectOptions<S extends QuerySchema> = ConnectOptions<S> & Omit<PostgresJsConfig, "url"> & TopologyOptions;
|
|
18
20
|
/**
|
|
19
|
-
* Connects one Postgres endpoint and returns a client for `schema`.
|
|
21
|
+
* Connects one Postgres endpoint, or a primary and its replicas, and returns a client for `schema`.
|
|
22
|
+
*
|
|
23
|
+
* A string or a pool is one endpoint. A topology loads on demand and the promise
|
|
24
|
+
* resolves to the client. Reads use the first healthy replica.
|
|
20
25
|
*
|
|
21
26
|
* Named prepared statements are off unless `prepared` is `"named"`.
|
|
22
27
|
* `prepared: "named"` is not for transaction-mode poolers.
|
|
23
28
|
*
|
|
24
29
|
* @typeParam S - Schema
|
|
25
|
-
* @param target - URL
|
|
30
|
+
* @param target - URL, an existing pool, or `{ primary, replicas }`
|
|
26
31
|
* @param options - Schema, errors, logger, and driver options
|
|
27
|
-
* @returns The client. Await `connected` for the dialect check
|
|
32
|
+
* @returns The client. A topology returns a promise. Await `connected` for the dialect check
|
|
28
33
|
*/
|
|
34
|
+
export declare function connect<const S extends QuerySchema>(target: TopologyInput, options: PostgresConnectOptions<S>): Promise<RoutedClient<S>>;
|
|
29
35
|
export declare function connect<const S extends QuerySchema>(target: PostgresTarget, options: PostgresConnectOptions<S>): Connected<S>;
|
|
@@ -11,10 +11,13 @@ import {
|
|
|
11
11
|
} from "../../shared/rrstp3kb.js";
|
|
12
12
|
import {
|
|
13
13
|
createClient
|
|
14
|
-
} from "../../shared/
|
|
14
|
+
} from "../../shared/bt9sfq0z.js";
|
|
15
15
|
|
|
16
16
|
// src/runtime/pg/postgresjs.ts
|
|
17
17
|
function connect(target, options) {
|
|
18
|
+
if (typeof target === "object" && !isPool(target)) {
|
|
19
|
+
return import("../../shared/nkc9sfqb.js").then((mod) => mod.default(target, options, open2));
|
|
20
|
+
}
|
|
18
21
|
const owned = !isPool(target);
|
|
19
22
|
const pool = isPool(target) ? target : open2({
|
|
20
23
|
url: target,
|
package/dist/runtime/plan.d.ts
CHANGED
|
@@ -41,9 +41,11 @@ export type ReadCall = {
|
|
|
41
41
|
readonly build?: ReadBuild;
|
|
42
42
|
/** Writes the end of the statement, after `limit`. A row lock sets it. */
|
|
43
43
|
readonly tail?: (sink: Sink) => void;
|
|
44
|
-
/** The caller's `signal` and `
|
|
44
|
+
/** The caller's `signal`, `timeout`, and `route`. They never enter the plan or its key. */
|
|
45
45
|
readonly signal?: AbortSignal;
|
|
46
46
|
readonly timeout?: number;
|
|
47
|
+
/** `"primary"` or `"replica"`. Absent means the router classifies the statement. */
|
|
48
|
+
readonly route?: "primary" | "replica";
|
|
47
49
|
/** Set by `for()` or `unscoped()`. Absent on a schema with no tenancy. */
|
|
48
50
|
readonly scope?: CallScope;
|
|
49
51
|
/** Inspect lines from the tenancy object. Absent when the schema has no tenancy. */
|