uql-orm 0.30.0 → 0.31.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 +11 -3
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/pglite/index.d.ts +3 -0
- package/dist/pglite/index.js +3 -0
- package/dist/pglite/pgliteDialect.d.ts +14 -0
- package/dist/pglite/pgliteDialect.js +14 -0
- package/dist/pglite/pgliteQuerier.d.ts +36 -0
- package/dist/pglite/pgliteQuerier.js +30 -0
- package/dist/pglite/pgliteQuerierPool.d.ts +36 -0
- package/dist/pglite/pgliteQuerierPool.js +36 -0
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +42 -0
- package/dist/querier/abstractSharedHandleQuerierPool.js +48 -0
- package/dist/sqlite/localSqliteQuerierPool.d.ts +8 -11
- package/dist/sqlite/localSqliteQuerierPool.js +8 -15
- package/dist/turso/tursoLocalQuerierPool.d.ts +5 -11
- package/dist/turso/tursoLocalQuerierPool.js +4 -14
- package/dist/type/entity.d.ts +9 -7
- package/dist/type/query.d.ts +5 -5
- package/dist/type/queryWhere.d.ts +5 -5
- package/dist/type/universalQuerier.d.ts +4 -2
- package/dist/type/utility.d.ts +7 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -50,13 +50,13 @@ from the browser to the server. The same object runs on every supported database
|
|
|
50
50
|
|
|
51
51
|
- **Serializable queries (JSON), not method chains.** Plain JSON in, typed rows out. No DSL to learn.
|
|
52
52
|
- **Type-safe to the leaf, nothing to generate.** Every key is checked against your entity, down into populated relations and [JSON/JSONB](https://uql-orm.dev/querying/json) dot-paths, so `$like` on a numeric column is a compile error. Entities are plain classes on the standard TC39 decorators: no `.prisma` file, no generated client, no `reflect-metadata`, no `experimentalDecorators`.
|
|
53
|
-
- **One API, everywhere it runs.** PostgreSQL, CockroachDB, MySQL, MariaDB, SQLite, Turso, libSQL, Neon, Cloudflare D1, Bun's native SQL, and even MongoDB. The same code on Node 24+, Bun, Deno, [Cloudflare Workers](https://uql-orm.dev/cloudflare-d1), [AWS Lambda and Vercel](https://uql-orm.dev/serverless), and [the browser](https://uql-orm.dev/browser), with no native binaries on the `fetch`-based drivers.
|
|
53
|
+
- **One API, everywhere it runs.** PostgreSQL, PGlite, CockroachDB, MySQL, MariaDB, SQLite, Turso, libSQL, Neon, Cloudflare D1, Bun's native SQL, and even MongoDB. The same code on Node 24+, Bun, Deno, [Cloudflare Workers](https://uql-orm.dev/cloudflare-d1), [AWS Lambda and Vercel](https://uql-orm.dev/serverless), and [the browser](https://uql-orm.dev/browser), with no native binaries on the `fetch`-based drivers.
|
|
54
54
|
- **Relations without N+1.** [`$populate`](https://uql-orm.dev/querying/relations) loads a to-many with one query for all parents, not one per parent. Nothing is lazy, so nothing fires behind your back in a serializer.
|
|
55
55
|
- **Migrations you read before they run.** Edit an entity, run `uql-migrate generate:entities`, review the SQL in the PR like any other file. [`drift:check`](https://uql-orm.dev/migrations) catches a database that no longer matches.
|
|
56
56
|
- **Raw SQL when you want it.** [`raw()`](https://uql-orm.dev/querying/raw-sql) fits anywhere in a query, [virtual fields](https://uql-orm.dev/entities/virtual-fields) are sub-queries you can filter on, and a migration can be plain SQL.
|
|
57
57
|
- **Light.** Zero runtime dependencies, under 280 kB on the wire, every dialect included. See [what we deleted to get there](https://uql-orm.dev/blog/zero-dependencies).
|
|
58
58
|
- **The hard things are built in.** [Semantic and vector search](https://uql-orm.dev/ai-semantic-search), [multi-tenant filters you cannot bypass by accident](https://uql-orm.dev/multi-tenancy), [soft-delete with restore](https://uql-orm.dev/entities/soft-delete), [streaming](https://uql-orm.dev/querying/streaming), and [a REST API from your entities](https://uql-orm.dev/http).
|
|
59
|
-
- **The fastest ORM.** On a full PostgreSQL round trip it adds the least over hand-written driver code of any ORM in our open-source [benchmark](https://github.com/rogerpadilla/ts-orm-benchmark), by roughly 3x over the next closest and an order of magnitude over the slowest.
|
|
59
|
+
- **The fastest ORM.** On a full PostgreSQL round trip it adds the least over hand-written driver code of any ORM in our open-source [benchmark](https://github.com/rogerpadilla/ts-orm-benchmark), by roughly 3x over the next closest and an order of magnitude over the slowest, on Bun, Node and Deno alike.
|
|
60
60
|
|
|
61
61
|
## Get started
|
|
62
62
|
|
|
@@ -71,8 +71,16 @@ Release notes live in [CHANGELOG.md](https://github.com/rogerpadilla/uql/blob/ma
|
|
|
71
71
|
|
|
72
72
|
## Made with UQL
|
|
73
73
|
|
|
74
|
-
**[Variability.ai](https://variability.ai)** - AI meeting recorder and video summarizer for Zoom, Meet, and Teams. Instant summaries with action items in
|
|
74
|
+
**[Variability.ai](https://variability.ai)** - AI meeting recorder and video summarizer for Zoom, Meet, and Teams. Instant summaries with action items in 35+ languages.
|
|
75
75
|
|
|
76
76
|
Built something? [Open a PR](https://github.com/rogerpadilla/uql/blob/main/CONTRIBUTING.md) and add it here.
|
|
77
77
|
|
|
78
78
|
[](https://uql-orm.dev)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## ⭐ Like what we're doing? Give us a star
|
|
83
|
+
|
|
84
|
+
It is how other people find the project.
|
|
85
|
+
|
|
86
|
+
[](https://github.com/rogerpadilla/uql)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var W=[];function F(B){for(let V of W)V(B)}function _(B){W.push(B);let V=W.length-1;return()=>{W.splice(V,1)}}class f extends Error{status;constructor(B,V){super(B);this.status=V;this.name="RequestError"}}function N(B,V){return G(B,{method:"get"},V)}function Q(B,V,I){let X=JSON.stringify(V);return G(B,{method:"post",body:X},I)}function J(B,V,I){let X=JSON.stringify(V);return G(B,{method:"patch",body:X},I)}function L(B,V,I){let X=JSON.stringify(V);return G(B,{method:"put",body:X},I)}function x(B,V){return G(B,{method:"delete"},V)}function T(B,V,I){let X=JSON.stringify(V);return G(B,{method:"QUERY",body:X},I)}function G(B,V,I){if(F({phase:"start",opts:I}),V.headers={accept:"application/json","content-type":"application/json",...I?.headers},I?.signal)V.signal=I.signal;return fetch(B,V).then((X)=>X.json().then((Z)=>{if(X.status>=200&&X.status<300)return F({phase:"success",opts:I}),Z;let Y=Z,M={message:Y?.error?.message??X.statusText,code:Y?.error?.code??X.status};throw F({phase:"error",error:M,opts:I}),new f(M.message,M.code)})).finally(()=>{F({phase:"complete",opts:I})})}function z(B){return B?Object.keys(B):[]}function K(B){let V=B.charAt(0).toLowerCase();for(let I=1;I<B.length;++I)V+=B[I]===B[I].toUpperCase()?"-"+B[I].toLowerCase():B[I];return V}var $={findMany:{method:"GET",path:""},findOne:{method:"GET",path:"/one"},count:{method:"GET",path:"/count"},findOneById:{method:"GET",path:"/:id"},insertOne:{method:"POST",path:""},insertMany:{method:"POST",path:"/many"},saveOne:{method:"PUT",path:""},saveMany:{method:"PUT",path:"/many"},updateMany:{method:"PATCH",path:""},updateOneById:{method:"PATCH",path:"/:id"},deleteOneById:{method:"DELETE",path:"/:id"},deleteMany:{method:"DELETE",path:""}},k=z($),b=new Map(k.filter((B)=>$[B].method==="GET"&&$[B].path!=="/:id").map((B)=>[$[B].path,B]));function D(B){return K(B.name)}var c=["$select","$populate","$exclude","$where","$sort"],v=new Set([...c,"$skip","$limit","hardDelete","count"]);function A(B){if(!B)return"";let V=new URLSearchParams;for(let X of z(B)){let Z=B[X];if(Z===void 0)continue;V.append(X,typeof Z==="object"&&Z!==null?JSON.stringify(Z):String(Z))}let I=V.toString();return I?`?${I}`:""}class C{basePath;defaults;constructor(B,V={}){this.basePath=B;this.defaults=V}findOneById(B,V,I,X){let Z=this.getBasePath(B),H=A(I);return N(`${Z}/${V}${H}`,this.buildOptions(X))}findOne(B,V,I){return this.read(`${this.getBasePath(B)}${$.findOne.path}`,V,I)}findMany(B,V,I){let X={...V};if(I?.count)X.count=!0;return this.read(this.getBasePath(B),X,I)}async findManyAndCount(B,V,I){let X=await this.findMany(B,V,{...I,count:!0});if(typeof X.count!=="number")throw TypeError("findManyAndCount response has an invalid count");return{...X,count:X.count}}count(B,V,I){return this.read(`${this.getBasePath(B)}${$.count.path}`,V,I)}insertOne(B,V,I){let X=this.getBasePath(B);return Q(X,V,this.buildOptions(I))}insertMany(B,V,I){let X=this.getBasePath(B);return Q(`${X}${$.insertMany.path}`,V,this.buildOptions(I))}updateOneById(B,V,I,X){let Z=this.getBasePath(B);return J(`${Z}/${V}`,I,this.buildOptions(X))}updateMany(B,V,I,X){let Z=this.getBasePath(B),H=A(V);return J(`${Z}${H}`,I,this.buildOptions(X))}saveOne(B,V,I){let X=this.getBasePath(B);return L(X,V,this.buildOptions(I))}saveMany(B,V,I){let X=this.getBasePath(B);return L(`${X}${$.saveMany.path}`,V,this.buildOptions(I))}deleteOneById(B,V,I={}){let X=this.getBasePath(B),Z=I.hardDelete?A({hardDelete:I.hardDelete}):"";return x(`${X}/${V}${Z}`,this.buildOptions(I))}deleteMany(B,V,I={}){let X=this.getBasePath(B),Z=A(I.hardDelete?{...V,hardDelete:I.hardDelete}:V);return x(`${X}${Z}`,this.buildOptions(I))}getBasePath(B){return`${this.basePath}/${D(B)}`}read(B,V,I){if(this.defaults.readMethod==="QUERY")return T(B,V??{},this.buildOptions(I));return N(`${B}${A(V)}`,this.buildOptions(I))}buildOptions(B){if(!this.defaults.headers&&!B?.headers)return B;return{...B,headers:{...this.defaults.headers,...B?.headers}}}}var P={getQuerier:()=>new C("/api")};function y(B){P=B}function g(){return P}function p(){return g().getQuerier()}export{y as setQuerierPool,x as remove,T as query,L as put,Q as post,J as patch,_ as on,F as notify,g as getQuerierPool,p as getQuerier,N as get,f as RequestError,C as HttpQuerier};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=63F5785EAD0E521C64756E2164756E21
|
|
4
4
|
//# sourceMappingURL=uql-browser.min.js.map
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
"import { HttpQuerier } from './querier/httpQuerier.js';\nimport type { ClientQuerier, ClientQuerierPool } from './type/index.js';\n\nlet defaultPool: ClientQuerierPool = {\n getQuerier: () => new HttpQuerier('/api'),\n};\n\nexport function setQuerierPool<T extends ClientQuerierPool>(pool: T) {\n defaultPool = pool;\n}\n\nexport function getQuerierPool(): ClientQuerierPool {\n return defaultPool;\n}\n\nexport function getQuerier(): ClientQuerier {\n return getQuerierPool().getQuerier();\n}\n"
|
|
13
13
|
],
|
|
14
14
|
"mappings": "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",
|
|
15
|
-
"debugId": "
|
|
15
|
+
"debugId": "63F5785EAD0E521C64756E2164756E21",
|
|
16
16
|
"names": []
|
|
17
17
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PostgresDialect } from '../postgres/postgresDialect.js';
|
|
2
|
+
/**
|
|
3
|
+
* Postgres dialect for PGlite, the WASM Postgres build.
|
|
4
|
+
*
|
|
5
|
+
* @remarks Keeps every {@link PostgresDialect} default, `dialectName` included: PGlite *is* Postgres,
|
|
6
|
+
* so the introspector, schema generator and CLI must all resolve to the Postgres ones. Both driver
|
|
7
|
+
* capabilities are inherited deliberately rather than by omission. `nativeArrays: true` holds because
|
|
8
|
+
* PGlite registers an array serializer for every built-in array type at boot and resolves each
|
|
9
|
+
* parameter's type from a server-side `Describe` before binding; `explicitJsonCast: false` holds
|
|
10
|
+
* because `PgLikeSqlDialect` already emits `$n::jsonb`, which is what makes that `Describe` report
|
|
11
|
+
* JSONB and select PGlite's `JSON.stringify` serializer. A bare `$n` would bind `[object Object]`.
|
|
12
|
+
*/
|
|
13
|
+
export declare class PgliteDialect extends PostgresDialect {
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PostgresDialect } from '../postgres/postgresDialect.js';
|
|
2
|
+
/**
|
|
3
|
+
* Postgres dialect for PGlite, the WASM Postgres build.
|
|
4
|
+
*
|
|
5
|
+
* @remarks Keeps every {@link PostgresDialect} default, `dialectName` included: PGlite *is* Postgres,
|
|
6
|
+
* so the introspector, schema generator and CLI must all resolve to the Postgres ones. Both driver
|
|
7
|
+
* capabilities are inherited deliberately rather than by omission. `nativeArrays: true` holds because
|
|
8
|
+
* PGlite registers an array serializer for every built-in array type at boot and resolves each
|
|
9
|
+
* parameter's type from a server-side `Describe` before binding; `explicitJsonCast: false` holds
|
|
10
|
+
* because `PgLikeSqlDialect` already emits `$n::jsonb`, which is what makes that `Describe` report
|
|
11
|
+
* JSONB and select PGlite's `JSON.stringify` serializer. A bare `$n` would bind `[object Object]`.
|
|
12
|
+
*/
|
|
13
|
+
export class PgliteDialect extends PostgresDialect {
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AbstractSqlQuerier } from '../querier/index.js';
|
|
2
|
+
import type { ExtraOptions } from '../type/index.js';
|
|
3
|
+
import type { PgliteDialect } from './pgliteDialect.js';
|
|
4
|
+
/**
|
|
5
|
+
* Structural subset of the `@electric-sql/pglite` API actually used here, declared locally so this
|
|
6
|
+
* package does not couple its published types to a pre-1.0 dependency.
|
|
7
|
+
*
|
|
8
|
+
* @remarks This is what uql *consumes* from the driver, so it is two methods and stating them costs
|
|
9
|
+
* nothing. `PglitePoolOptions` is the opposite case and imports PGlite's own type: those options are
|
|
10
|
+
* the caller's input to the driver, so restating them would mean re-deriving its whole option surface
|
|
11
|
+
* and then casting at the `PGlite.create` call.
|
|
12
|
+
*/
|
|
13
|
+
export type PgliteDatabase = {
|
|
14
|
+
query<T>(query: string, params?: unknown[]): Promise<{
|
|
15
|
+
rows: T[];
|
|
16
|
+
affectedRows?: number;
|
|
17
|
+
}>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Querier for PGlite, Postgres compiled to WASM and run in this process.
|
|
22
|
+
*
|
|
23
|
+
* @remarks Extends {@link AbstractSqlQuerier} rather than `AbstractPgQuerier`, whose `internalStream`
|
|
24
|
+
* hands a `pg-query-stream` object to `query()`: PGlite has no cursor API, so streaming falls back to
|
|
25
|
+
* the base class buffering the whole result. `BEGIN`/`COMMIT` are plain statements on the single
|
|
26
|
+
* connection, leaving transactions to the base class.
|
|
27
|
+
*/
|
|
28
|
+
export declare class PgliteQuerier extends AbstractSqlQuerier {
|
|
29
|
+
readonly db: PgliteDatabase;
|
|
30
|
+
readonly extra?: ExtraOptions | undefined;
|
|
31
|
+
constructor(db: PgliteDatabase, dialect: PgliteDialect, extra?: ExtraOptions | undefined);
|
|
32
|
+
internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
|
|
33
|
+
internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
|
|
34
|
+
/** The handle belongs to the pool, which hands out one querier per unit of work over it. */
|
|
35
|
+
internalRelease(): Promise<void>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AbstractSqlQuerier } from '../querier/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Querier for PGlite, Postgres compiled to WASM and run in this process.
|
|
4
|
+
*
|
|
5
|
+
* @remarks Extends {@link AbstractSqlQuerier} rather than `AbstractPgQuerier`, whose `internalStream`
|
|
6
|
+
* hands a `pg-query-stream` object to `query()`: PGlite has no cursor API, so streaming falls back to
|
|
7
|
+
* the base class buffering the whole result. `BEGIN`/`COMMIT` are plain statements on the single
|
|
8
|
+
* connection, leaving transactions to the base class.
|
|
9
|
+
*/
|
|
10
|
+
export class PgliteQuerier extends AbstractSqlQuerier {
|
|
11
|
+
db;
|
|
12
|
+
extra;
|
|
13
|
+
constructor(db, dialect, extra) {
|
|
14
|
+
super(dialect, extra);
|
|
15
|
+
this.db = db;
|
|
16
|
+
this.extra = extra;
|
|
17
|
+
}
|
|
18
|
+
async internalAll(query, values) {
|
|
19
|
+
const res = await this.db.query(query, values);
|
|
20
|
+
return res.rows;
|
|
21
|
+
}
|
|
22
|
+
async internalRun(query, values) {
|
|
23
|
+
const res = await this.db.query(query, values);
|
|
24
|
+
// `affectedRows`, not `rowCount`: PGlite derives the former from the command tag of a write only,
|
|
25
|
+
// where the latter also counts a `SELECT`'s rows and is absent altogether from a DDL tag.
|
|
26
|
+
return this.buildUpdateResult({ rows: res.rows, changes: res.affectedRows ?? 0 });
|
|
27
|
+
}
|
|
28
|
+
/** The handle belongs to the pool, which hands out one querier per unit of work over it. */
|
|
29
|
+
async internalRelease() { }
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PGliteOptions } from '@electric-sql/pglite';
|
|
2
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
3
|
+
import type { ExtraOptions } from '../type/index.js';
|
|
4
|
+
import { PgliteDialect } from './pgliteDialect.js';
|
|
5
|
+
import { type PgliteDatabase, PgliteQuerier } from './pgliteQuerier.js';
|
|
6
|
+
/**
|
|
7
|
+
* The driver's own options, minus the `dataDir` this pool takes as its first argument.
|
|
8
|
+
*
|
|
9
|
+
* @remarks Imported rather than restated so extensions and the filesystem hooks keep their real types:
|
|
10
|
+
* `extensions: { vector }` from `@electric-sql/pglite-pgvector` is how a vector column becomes usable,
|
|
11
|
+
* mirroring `LocalSqlitePoolOptions.extensions` for `sqlite-vec`. Type-only, like the `pg` imports in
|
|
12
|
+
* `abstractPgQuerierPool.ts`, so nothing here reaches a runtime without the peer installed.
|
|
13
|
+
*/
|
|
14
|
+
export type PglitePoolOptions = Omit<PGliteOptions, 'dataDir'>;
|
|
15
|
+
/**
|
|
16
|
+
* Pool for PGlite, Postgres compiled to WASM and run in this process. No server, no container.
|
|
17
|
+
*
|
|
18
|
+
* PGlite is single connection, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
|
|
19
|
+
* Where PGlite differs from the two SQLite-family pools there is that it does not refuse a second
|
|
20
|
+
* `BEGIN`: a querier that opens a transaction while another already has one silently joins it, and that
|
|
21
|
+
* one's `ROLLBACK` then discards both queriers' writes. Nothing reports it, so a unit of work that needs
|
|
22
|
+
* a transaction of its own needs its own pool, and therefore its own database.
|
|
23
|
+
*
|
|
24
|
+
* @remarks Transactions are plain `BEGIN`/`COMMIT` statements rather than `db.transaction()`, whose
|
|
25
|
+
* callback holds PGlite's transaction mutex and would block every other querier's reads until commit.
|
|
26
|
+
* The cost is that PGlite cannot see the transaction, so it flushes to the filesystem after each
|
|
27
|
+
* statement within one: pass `relaxedDurability: true` on a persistent `dataDir` to skip waiting on
|
|
28
|
+
* those flushes.
|
|
29
|
+
*/
|
|
30
|
+
export declare class PgliteQuerierPool extends AbstractSharedHandleQuerierPool<PgliteDatabase, PgliteQuerier, PgliteDialect> {
|
|
31
|
+
readonly dataDir: string;
|
|
32
|
+
readonly opts?: PglitePoolOptions | undefined;
|
|
33
|
+
constructor(dataDir?: string, opts?: PglitePoolOptions | undefined, extra?: ExtraOptions);
|
|
34
|
+
protected openDb(): Promise<PgliteDatabase>;
|
|
35
|
+
protected buildQuerier(db: PgliteDatabase): PgliteQuerier;
|
|
36
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
|
+
import { PgliteDialect } from './pgliteDialect.js';
|
|
3
|
+
import { PgliteQuerier } from './pgliteQuerier.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pool for PGlite, Postgres compiled to WASM and run in this process. No server, no container.
|
|
6
|
+
*
|
|
7
|
+
* PGlite is single connection, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
|
|
8
|
+
* Where PGlite differs from the two SQLite-family pools there is that it does not refuse a second
|
|
9
|
+
* `BEGIN`: a querier that opens a transaction while another already has one silently joins it, and that
|
|
10
|
+
* one's `ROLLBACK` then discards both queriers' writes. Nothing reports it, so a unit of work that needs
|
|
11
|
+
* a transaction of its own needs its own pool, and therefore its own database.
|
|
12
|
+
*
|
|
13
|
+
* @remarks Transactions are plain `BEGIN`/`COMMIT` statements rather than `db.transaction()`, whose
|
|
14
|
+
* callback holds PGlite's transaction mutex and would block every other querier's reads until commit.
|
|
15
|
+
* The cost is that PGlite cannot see the transaction, so it flushes to the filesystem after each
|
|
16
|
+
* statement within one: pass `relaxedDurability: true` on a persistent `dataDir` to skip waiting on
|
|
17
|
+
* those flushes.
|
|
18
|
+
*/
|
|
19
|
+
export class PgliteQuerierPool extends AbstractSharedHandleQuerierPool {
|
|
20
|
+
dataDir;
|
|
21
|
+
opts;
|
|
22
|
+
constructor(dataDir = 'memory://', opts, extra) {
|
|
23
|
+
super(new PgliteDialect({ namingStrategy: extra?.namingStrategy }), extra);
|
|
24
|
+
this.dataDir = dataDir;
|
|
25
|
+
this.opts = opts;
|
|
26
|
+
}
|
|
27
|
+
async openDb() {
|
|
28
|
+
const { PGlite } = await import('@electric-sql/pglite');
|
|
29
|
+
// The declared return type is what checks {@link PgliteDatabase} against the real driver, so no
|
|
30
|
+
// cast is needed here or anywhere below it.
|
|
31
|
+
return PGlite.create(this.dataDir, this.opts);
|
|
32
|
+
}
|
|
33
|
+
buildQuerier(db) {
|
|
34
|
+
return new PgliteQuerier(db, this.dialect, this.extra);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AbstractSqlDialect } from '../dialect/index.js';
|
|
2
|
+
import type { SqlQuerier } from '../type/index.js';
|
|
3
|
+
import { AbstractSqlQuerierPool } from './abstractSqlQuerierPool.js';
|
|
4
|
+
/**
|
|
5
|
+
* Base pool for an engine that gives one connection per database and keeps it open for the pool's
|
|
6
|
+
* lifetime: every local SQLite driver, the embedded Turso engine, and PGlite.
|
|
7
|
+
*
|
|
8
|
+
* The handle is shared, but each acquisition gets its own querier, so transaction state stays per unit
|
|
9
|
+
* of work. That state is not *isolated*, which is the one way these differ from a real pool: there is a
|
|
10
|
+
* single connection under every querier, so two of them cannot hold independent transactions, and a
|
|
11
|
+
* unit of work that needs one needs its own pool and therefore its own database.
|
|
12
|
+
*
|
|
13
|
+
* What a second `BEGIN` then does is the engine's, not this class's: SQLite and the embedded Turso
|
|
14
|
+
* engine both refuse it ("cannot start a transaction within a transaction"), while PGlite accepts it
|
|
15
|
+
* into the transaction already open - see {@link PgliteQuerierPool}, which is why that one is worth
|
|
16
|
+
* saying out loud.
|
|
17
|
+
*
|
|
18
|
+
* Subclasses supply only how to open the handle and how to wrap it, the way {@link AbstractPgQuerierPool}
|
|
19
|
+
* takes `buildQuerier` alone. The lazy open and the close were written out once per pool before, along
|
|
20
|
+
* with three partial copies of the paragraph above.
|
|
21
|
+
*
|
|
22
|
+
* @remarks Deliberately not re-exported from `querier/index.ts`, which the root entry point re-exports:
|
|
23
|
+
* only the three driver entries need this, and each imports it by path, as `postgres/abstractPgQuerier.ts`
|
|
24
|
+
* is imported.
|
|
25
|
+
*/
|
|
26
|
+
export declare abstract class AbstractSharedHandleQuerierPool<DB extends {
|
|
27
|
+
close(): unknown;
|
|
28
|
+
}, Q extends SqlQuerier, D extends AbstractSqlDialect> extends AbstractSqlQuerierPool<Q, D> {
|
|
29
|
+
/**
|
|
30
|
+
* The open, not the handle: `db ??= await openDb()` reads before the await and assigns after, so
|
|
31
|
+
* callers arriving while the first open is in flight each start one of their own. The extra handles
|
|
32
|
+
* are then unreachable and never closed, and on an in-memory database they are separate databases,
|
|
33
|
+
* so a querier built on one writes where nothing else will ever read.
|
|
34
|
+
*/
|
|
35
|
+
private opening?;
|
|
36
|
+
/** Opens the one connection. Called on the first acquisition, and again after an {@link end}. */
|
|
37
|
+
protected abstract openDb(): Promise<DB>;
|
|
38
|
+
/** Wraps the shared handle in a querier: the only thing that varies between these pools. */
|
|
39
|
+
protected abstract buildQuerier(db: DB): Q;
|
|
40
|
+
getQuerier(): Promise<Q>;
|
|
41
|
+
end(): Promise<void>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AbstractSqlQuerierPool } from './abstractSqlQuerierPool.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base pool for an engine that gives one connection per database and keeps it open for the pool's
|
|
4
|
+
* lifetime: every local SQLite driver, the embedded Turso engine, and PGlite.
|
|
5
|
+
*
|
|
6
|
+
* The handle is shared, but each acquisition gets its own querier, so transaction state stays per unit
|
|
7
|
+
* of work. That state is not *isolated*, which is the one way these differ from a real pool: there is a
|
|
8
|
+
* single connection under every querier, so two of them cannot hold independent transactions, and a
|
|
9
|
+
* unit of work that needs one needs its own pool and therefore its own database.
|
|
10
|
+
*
|
|
11
|
+
* What a second `BEGIN` then does is the engine's, not this class's: SQLite and the embedded Turso
|
|
12
|
+
* engine both refuse it ("cannot start a transaction within a transaction"), while PGlite accepts it
|
|
13
|
+
* into the transaction already open - see {@link PgliteQuerierPool}, which is why that one is worth
|
|
14
|
+
* saying out loud.
|
|
15
|
+
*
|
|
16
|
+
* Subclasses supply only how to open the handle and how to wrap it, the way {@link AbstractPgQuerierPool}
|
|
17
|
+
* takes `buildQuerier` alone. The lazy open and the close were written out once per pool before, along
|
|
18
|
+
* with three partial copies of the paragraph above.
|
|
19
|
+
*
|
|
20
|
+
* @remarks Deliberately not re-exported from `querier/index.ts`, which the root entry point re-exports:
|
|
21
|
+
* only the three driver entries need this, and each imports it by path, as `postgres/abstractPgQuerier.ts`
|
|
22
|
+
* is imported.
|
|
23
|
+
*/
|
|
24
|
+
export class AbstractSharedHandleQuerierPool extends AbstractSqlQuerierPool {
|
|
25
|
+
/**
|
|
26
|
+
* The open, not the handle: `db ??= await openDb()` reads before the await and assigns after, so
|
|
27
|
+
* callers arriving while the first open is in flight each start one of their own. The extra handles
|
|
28
|
+
* are then unreachable and never closed, and on an in-memory database they are separate databases,
|
|
29
|
+
* so a querier built on one writes where nothing else will ever read.
|
|
30
|
+
*/
|
|
31
|
+
opening;
|
|
32
|
+
async getQuerier() {
|
|
33
|
+
// Cleared on failure, so a driver that could not start once is retried rather than refused forever.
|
|
34
|
+
this.opening ??= this.openDb().catch((err) => {
|
|
35
|
+
this.opening = undefined;
|
|
36
|
+
throw err;
|
|
37
|
+
});
|
|
38
|
+
return this.buildQuerier(await this.opening);
|
|
39
|
+
}
|
|
40
|
+
async end() {
|
|
41
|
+
const opening = this.opening;
|
|
42
|
+
this.opening = undefined;
|
|
43
|
+
// An open still in flight is awaited rather than abandoned: closing is what releases its file or port.
|
|
44
|
+
// One that failed leaves nothing to close, and `getQuerier` already reported it to its own caller.
|
|
45
|
+
const db = await opening?.catch(() => undefined);
|
|
46
|
+
await db?.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
2
|
import type { ExtraOptions } from '../type/index.js';
|
|
3
3
|
import { SqliteDialect } from './sqliteDialect.js';
|
|
4
4
|
import { type SqliteDatabase, SqliteQuerier } from './sqliteQuerier.js';
|
|
@@ -12,20 +12,17 @@ export type LocalSqlitePoolOptions = {
|
|
|
12
12
|
extensions?: readonly string[];
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
* Pool for a SQLite database opened in this process, whichever driver provides it.
|
|
15
|
+
* Pool for a SQLite database opened in this process, whichever driver provides it. SQLite gives one
|
|
16
|
+
* connection per file, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
|
|
20
|
-
* and `node:sqlite`, and were written out once per pool before.
|
|
18
|
+
* Subclasses supply only {@link createDb}: loading the extensions on the way up is the same for
|
|
19
|
+
* `better-sqlite3`, `bun:sqlite` and `node:sqlite`, and was written out once per pool before.
|
|
21
20
|
*/
|
|
22
|
-
export declare abstract class AbstractLocalSqliteQuerierPool<O extends LocalSqlitePoolOptions> extends
|
|
21
|
+
export declare abstract class AbstractLocalSqliteQuerierPool<O extends LocalSqlitePoolOptions> extends AbstractSharedHandleQuerierPool<SqliteDatabase, SqliteQuerier, SqliteDialect> {
|
|
23
22
|
readonly opts?: O | undefined;
|
|
24
|
-
private db?;
|
|
25
23
|
constructor(opts?: O | undefined, extra?: ExtraOptions);
|
|
26
24
|
/** Opens the driver's database. Extensions are loaded by the caller, not here. */
|
|
27
25
|
protected abstract createDb(): Promise<SqliteDatabase>;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end(): Promise<void>;
|
|
26
|
+
protected openDb(): Promise<SqliteDatabase>;
|
|
27
|
+
protected buildQuerier(db: SqliteDatabase): SqliteQuerier;
|
|
31
28
|
}
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
2
|
import { SqliteDialect } from './sqliteDialect.js';
|
|
3
3
|
import { SqliteQuerier } from './sqliteQuerier.js';
|
|
4
4
|
/**
|
|
5
|
-
* Pool for a SQLite database opened in this process, whichever driver provides it.
|
|
5
|
+
* Pool for a SQLite database opened in this process, whichever driver provides it. SQLite gives one
|
|
6
|
+
* connection per file, so the shared-handle lifecycle is {@link AbstractSharedHandleQuerierPool}'s.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* lifecycle, and loading the extensions on the way up, are the same for `better-sqlite3`, `bun:sqlite`
|
|
10
|
-
* and `node:sqlite`, and were written out once per pool before.
|
|
8
|
+
* Subclasses supply only {@link createDb}: loading the extensions on the way up is the same for
|
|
9
|
+
* `better-sqlite3`, `bun:sqlite` and `node:sqlite`, and was written out once per pool before.
|
|
11
10
|
*/
|
|
12
|
-
export class AbstractLocalSqliteQuerierPool extends
|
|
11
|
+
export class AbstractLocalSqliteQuerierPool extends AbstractSharedHandleQuerierPool {
|
|
13
12
|
opts;
|
|
14
|
-
db;
|
|
15
13
|
constructor(opts, extra) {
|
|
16
14
|
super(new SqliteDialect({ namingStrategy: extra?.namingStrategy }), extra);
|
|
17
15
|
this.opts = opts;
|
|
18
16
|
}
|
|
19
|
-
async getQuerier() {
|
|
20
|
-
this.db ??= await this.openDb();
|
|
21
|
-
return new SqliteQuerier(this.db, this.dialect, this.extra);
|
|
22
|
-
}
|
|
23
17
|
async openDb() {
|
|
24
18
|
const db = await this.createDb();
|
|
25
19
|
for (const extension of this.opts?.extensions ?? []) {
|
|
@@ -27,8 +21,7 @@ export class AbstractLocalSqliteQuerierPool extends AbstractSqlQuerierPool {
|
|
|
27
21
|
}
|
|
28
22
|
return db;
|
|
29
23
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.db = undefined;
|
|
24
|
+
buildQuerier(db) {
|
|
25
|
+
return new SqliteQuerier(db, this.dialect, this.extra);
|
|
33
26
|
}
|
|
34
27
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
2
|
import type { ExtraOptions } from '../type/index.js';
|
|
3
3
|
import { TursoDialect } from './tursoDialect.js';
|
|
4
|
-
import { TursoLocalQuerier } from './tursoLocalQuerier.js';
|
|
4
|
+
import { type TursoDatabase, TursoLocalQuerier } from './tursoLocalQuerier.js';
|
|
5
5
|
/** Subset of `DatabaseOpts` from `@tursodatabase/database`, declared locally to avoid the coupling. */
|
|
6
6
|
export type TursoLocalOptions = {
|
|
7
7
|
readonly?: boolean;
|
|
@@ -17,16 +17,10 @@ export type TursoLocalOptions = {
|
|
|
17
17
|
* package ships native binaries that do not resolve on edge runtimes. Separating them guarantees a
|
|
18
18
|
* bundle targeting Workers never reaches the native import.
|
|
19
19
|
*/
|
|
20
|
-
export declare class TursoLocalQuerierPool extends
|
|
20
|
+
export declare class TursoLocalQuerierPool extends AbstractSharedHandleQuerierPool<TursoDatabase, TursoLocalQuerier, TursoDialect> {
|
|
21
21
|
readonly filename: string;
|
|
22
22
|
readonly opts?: TursoLocalOptions | undefined;
|
|
23
|
-
private db?;
|
|
24
23
|
constructor(filename?: string, opts?: TursoLocalOptions | undefined, extra?: ExtraOptions);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* so transaction state stays per unit of work.
|
|
28
|
-
*/
|
|
29
|
-
getQuerier(): Promise<TursoLocalQuerier>;
|
|
30
|
-
private openDb;
|
|
31
|
-
end(): Promise<void>;
|
|
24
|
+
protected openDb(): Promise<TursoDatabase>;
|
|
25
|
+
protected buildQuerier(db: TursoDatabase): TursoLocalQuerier;
|
|
32
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbstractSharedHandleQuerierPool } from '../querier/abstractSharedHandleQuerierPool.js';
|
|
2
2
|
import { TursoDialect } from './tursoDialect.js';
|
|
3
3
|
import { TursoLocalQuerier } from './tursoLocalQuerier.js';
|
|
4
4
|
/**
|
|
@@ -8,23 +8,14 @@ import { TursoLocalQuerier } from './tursoLocalQuerier.js';
|
|
|
8
8
|
* package ships native binaries that do not resolve on edge runtimes. Separating them guarantees a
|
|
9
9
|
* bundle targeting Workers never reaches the native import.
|
|
10
10
|
*/
|
|
11
|
-
export class TursoLocalQuerierPool extends
|
|
11
|
+
export class TursoLocalQuerierPool extends AbstractSharedHandleQuerierPool {
|
|
12
12
|
filename;
|
|
13
13
|
opts;
|
|
14
|
-
db;
|
|
15
14
|
constructor(filename = ':memory:', opts, extra) {
|
|
16
15
|
super(new TursoDialect({ namingStrategy: extra?.namingStrategy }), extra);
|
|
17
16
|
this.filename = filename;
|
|
18
17
|
this.opts = opts;
|
|
19
18
|
}
|
|
20
|
-
/**
|
|
21
|
-
* The database handle is shared (single connection), but each acquisition gets its own querier
|
|
22
|
-
* so transaction state stays per unit of work.
|
|
23
|
-
*/
|
|
24
|
-
async getQuerier() {
|
|
25
|
-
this.db ??= await this.openDb();
|
|
26
|
-
return new TursoLocalQuerier(this.db, this.dialect, this.extra);
|
|
27
|
-
}
|
|
28
19
|
async openDb() {
|
|
29
20
|
const { connect } = await import('@tursodatabase/database');
|
|
30
21
|
// Annotated rather than cast, so the structural contract is checked against the real driver.
|
|
@@ -33,8 +24,7 @@ export class TursoLocalQuerierPool extends AbstractSqlQuerierPool {
|
|
|
33
24
|
await db.pragma('foreign_keys = ON');
|
|
34
25
|
return db;
|
|
35
26
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.db = undefined;
|
|
27
|
+
buildQuerier(db) {
|
|
28
|
+
return new TursoLocalQuerier(db, this.dialect, this.extra);
|
|
39
29
|
}
|
|
40
30
|
}
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ForeignKeyAction, IndexType } from '../schema/types.js';
|
|
2
2
|
import type { FilterOptions } from './query.js';
|
|
3
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
4
|
-
import type { Except, Json, Scalar, Type, Unpacked } from './utility.js';
|
|
4
|
+
import type { Except, IsMany, Json, Scalar, Type, Unpacked } from './utility.js';
|
|
5
5
|
import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
|
|
6
6
|
/**
|
|
7
7
|
* Allow to customize the name of the property that identifies an entity
|
|
@@ -18,10 +18,12 @@ export type Key<E> = keyof E & string;
|
|
|
18
18
|
* (without it, optional properties leak `undefined` into the union).
|
|
19
19
|
*
|
|
20
20
|
* The check is bracketed so `any` resolves once rather than matching both this and
|
|
21
|
-
* {@link RelationKey}: an unbracketed `any extends X` satisfies either branch.
|
|
21
|
+
* {@link RelationKey}: an unbracketed `any extends X` satisfies either branch. It reads
|
|
22
|
+
* `readonly Scalar[]`, which every mutable one satisfies too, so declaring a vector or a scalar
|
|
23
|
+
* array `readonly` does not push the field over into {@link RelationKey}.
|
|
22
24
|
*/
|
|
23
25
|
export type FieldKey<E> = {
|
|
24
|
-
readonly [K in keyof E]-?: [NonNullable<E[K]>] extends [Scalar | Scalar[] | Json] ? K : never;
|
|
26
|
+
readonly [K in keyof E]-?: [NonNullable<E[K]>] extends [Scalar | readonly Scalar[] | Json] ? K : never;
|
|
25
27
|
}[Key<E>];
|
|
26
28
|
/**
|
|
27
29
|
* Infers the relation names of an entity: whatever is left once its fields and its methods are
|
|
@@ -79,7 +81,7 @@ export type JsonFieldPathValue<E, P extends string> = P extends `${infer F}.${in
|
|
|
79
81
|
* Used by `$push` and `$pull` to provide type-safe element targets.
|
|
80
82
|
*/
|
|
81
83
|
export type JsonArrayFields<T> = {
|
|
82
|
-
[K in keyof T as
|
|
84
|
+
[K in keyof T as IsMany<T[K]> extends true ? K & string : never]?: Unpacked<NonNullable<T[K]>>;
|
|
83
85
|
};
|
|
84
86
|
/**
|
|
85
87
|
* Operator shape accepted by JSON/JSONB fields in update payloads: `$set`/`$unset` target object
|
|
@@ -119,7 +121,7 @@ export type JsonUpdateOp<T = unknown> = {
|
|
|
119
121
|
* `JSON_SET(arr, '$.k', v)` is a no-op on MySQL and SQLite. Replace the whole value instead.
|
|
120
122
|
* `Json<unknown>` stays permissive, since `unknown` is not an array.
|
|
121
123
|
*/
|
|
122
|
-
type JsonUpdateOpFor<V, T = UnwrapJson<NonNullable<V>>> = [T] extends [never] ? never : T extends
|
|
124
|
+
type JsonUpdateOpFor<V, T = UnwrapJson<NonNullable<V>>> = [T] extends [never] ? never : IsMany<T> extends true ? never : JsonUpdateOp<T>;
|
|
123
125
|
/**
|
|
124
126
|
* Accepted value for a single field in an update payload: the value itself, `null` where the column
|
|
125
127
|
* is nullable, `QueryRaw` for a raw SQL expression (e.g. `raw('NOW()')`), and - for JSON object
|
|
@@ -369,8 +371,8 @@ export type RelationTarget<V> = NonNullable<Unpacked<NonNullable<V>>>;
|
|
|
369
371
|
*/
|
|
370
372
|
export type RelationOptionsFor<V> = Omit<RelationOptions<RelationTarget<V>>, 'entity' | 'cardinality'> & {
|
|
371
373
|
readonly entity: EntityGetter<RelationTarget<V>>;
|
|
372
|
-
readonly cardinality:
|
|
373
|
-
} & (
|
|
374
|
+
readonly cardinality: IsMany<V> extends true ? '1m' | 'mm' : '11' | 'm1';
|
|
375
|
+
} & (IsMany<V> extends true ? RelationJoin<RelationTarget<V>> : unknown);
|
|
374
376
|
/**
|
|
375
377
|
* The method names of an entity, so hook registrations name a method that exists.
|
|
376
378
|
*/
|
package/dist/type/query.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { FieldKey, JsonFieldPaths, RelationKey } from './entity.js';
|
|
1
|
+
import type { FieldKey, JsonFieldPaths, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QueryLock } from './queryLock.js';
|
|
3
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
4
4
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
|
-
import type { BooleanLike, Except,
|
|
5
|
+
import type { BooleanLike, Except, IsMany, PrimaryKey } from './utility.js';
|
|
6
6
|
import type { QueryVectorSearch } from './vector.js';
|
|
7
7
|
export type QueryOptions = {
|
|
8
8
|
/**
|
|
@@ -64,9 +64,9 @@ export type QueryConflictPaths<E> = {
|
|
|
64
64
|
[K in FieldKey<E>]?: true;
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* Options to populate a relation declared as `V`, by its cardinality.
|
|
68
68
|
*/
|
|
69
|
-
export type QueryPopulateRelationOptions<
|
|
69
|
+
export type QueryPopulateRelationOptions<V> = (IsMany<V> extends true ? Except<Query<RelationTarget<V>>, '$lock'> : QueryUnique<RelationTarget<V>>) & {
|
|
70
70
|
$required?: boolean;
|
|
71
71
|
};
|
|
72
72
|
/**
|
|
@@ -126,7 +126,7 @@ export type QuerySortMap<E, Vector extends boolean = true> = {
|
|
|
126
126
|
} & {
|
|
127
127
|
[P in JsonFieldPaths<E>]?: QuerySortDirection;
|
|
128
128
|
} & {
|
|
129
|
-
[K in RelationKey<E> as
|
|
129
|
+
[K in RelationKey<E> as IsMany<E[K]> extends true ? never : K]?: QuerySortMap<RelationTarget<E[K]>, false>;
|
|
130
130
|
};
|
|
131
131
|
/**
|
|
132
132
|
* pager options.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FieldKey, IdValue, JsonFieldPaths, JsonFieldPathValue, RelationKey } from './entity.js';
|
|
1
|
+
import type { FieldKey, IdValue, JsonFieldPaths, JsonFieldPathValue, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QueryRaw } from './queryRaw.js';
|
|
3
|
-
import type { ExpandScalar, QueryComparableScalar, Scalar
|
|
3
|
+
import type { ExpandScalar, IsMany, QueryComparableScalar, Scalar } from './utility.js';
|
|
4
4
|
/**
|
|
5
5
|
* options for full-text-search operator.
|
|
6
6
|
*/
|
|
@@ -35,7 +35,7 @@ export type QueryWhereFieldMap<E> = {
|
|
|
35
35
|
export type QueryWhereMap<E> = QueryWhereFieldMap<E> & QueryWhereRootOperator<E> & {
|
|
36
36
|
[P in JsonFieldPaths<E>]?: QueryWhereFieldValue<JsonFieldPathValue<E, P>>;
|
|
37
37
|
} & {
|
|
38
|
-
[K in RelationKey<E>]?: QueryWhereMap<
|
|
38
|
+
[K in RelationKey<E>]?: QueryWhereMap<RelationTarget<E[K]>> | QueryRelationSizeFilter;
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
41
41
|
* Filter a to-many relation by its row count.
|
|
@@ -246,7 +246,7 @@ type QueryCommonOp = Exclude<keyof QueryWhereFieldOperatorMap<unknown>, QueryStr
|
|
|
246
246
|
* Operator keys applicable to a field of type `T`. Brackets prevent union distribution so an
|
|
247
247
|
* optional field (`string | undefined`) or a literal union (`'a' | 'b'`) gates as one type.
|
|
248
248
|
*/
|
|
249
|
-
type QueryAllowedOp<T> = QueryCommonOp | ([NonNullable<T>] extends [QueryComparableScalar] ? QueryOrderedOp : never) | ([NonNullable<T>] extends [string] ? QueryStringOp : never) | (
|
|
249
|
+
type QueryAllowedOp<T> = QueryCommonOp | ([NonNullable<T>] extends [QueryComparableScalar] ? QueryOrderedOp : never) | ([NonNullable<T>] extends [string] ? QueryStringOp : never) | (IsMany<T> extends true ? QueryArrayOp : never);
|
|
250
250
|
/**
|
|
251
251
|
* Operators applicable to a field of type `T`: string operators require string fields, ordering
|
|
252
252
|
* operators comparable fields, array operators array fields. `unknown` stays fully permissive
|
|
@@ -261,7 +261,7 @@ export type QueryWhereFieldOperators<T> = unknown extends T ? QueryWhereFieldOpe
|
|
|
261
261
|
* `null` is accepted on a nullable field (an optional property is a nullable column), matching what
|
|
262
262
|
* `$eq: null` already took.
|
|
263
263
|
*/
|
|
264
|
-
export type QueryWhereFieldValue<T> = T | (undefined extends T ? null : never) | (
|
|
264
|
+
export type QueryWhereFieldValue<T> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : T[]) | QueryWhereFieldOperators<T> | QueryRaw;
|
|
265
265
|
/**
|
|
266
266
|
* query filter array - used for `$and`, `$or`, `$not`, `$nor` operators.
|
|
267
267
|
*/
|
|
@@ -55,8 +55,10 @@ export interface UniversalQuerier {
|
|
|
55
55
|
/**
|
|
56
56
|
* Insert a single record and return its ID (provided, `onInsert`-generated, or
|
|
57
57
|
* database-generated - see {@link UniversalQuerier.insertMany} for the exact semantics).
|
|
58
|
-
* Returns `undefined`
|
|
59
|
-
*
|
|
58
|
+
* Returns `undefined` only where the database cannot report one: MySQL, whose `LAST_INSERT_ID()`
|
|
59
|
+
* speaks for `AUTO_INCREMENT` columns alone and is left *stale* rather than cleared otherwise, so
|
|
60
|
+
* a non-auto-increment key the caller did not supply has no id to give and a header read would
|
|
61
|
+
* hand back an earlier row's. Every other backend uses `RETURNING` and is exact, SQLite included.
|
|
60
62
|
* @param entity the entity to persist on
|
|
61
63
|
* @param payload the data to be persisted
|
|
62
64
|
* @return the ID
|
package/dist/type/utility.d.ts
CHANGED
|
@@ -58,3 +58,10 @@ export type Except<T, K extends keyof T> = {
|
|
|
58
58
|
[P in keyof T as P extends K ? never : P]: T[P];
|
|
59
59
|
};
|
|
60
60
|
export type Unpacked<T> = T extends readonly (infer U)[] ? U : T extends (...args: unknown[]) => infer U ? U : T extends Promise<infer U> ? U : T;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the value a property holds is many rather than one: a to-many relation, a scalar array, a
|
|
63
|
+
* vector. Every array test in the type layer goes through this, because writing one by hand gets some
|
|
64
|
+
* part of it wrong in ways nothing reports. `isMany.test-d.ts` has one case per part, and says what
|
|
65
|
+
* each is load-bearing for.
|
|
66
|
+
*/
|
|
67
|
+
export type IsMany<V> = [NonNullable<V>] extends [readonly unknown[]] ? true : false;
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uql-orm",
|
|
3
3
|
"homepage": "https://uql-orm.dev",
|
|
4
|
-
"description": "JSON-native TypeScript ORM for Node.js, Bun and Deno. Supports PostgreSQL, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
4
|
+
"description": "JSON-native TypeScript ORM for Node.js, Bun and Deno. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.31.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"./turso": "./dist/turso/index.js",
|
|
48
48
|
"./turso/local": "./dist/turso/local.js",
|
|
49
49
|
"./neon": "./dist/neon/index.js",
|
|
50
|
+
"./pglite": "./dist/pglite/index.js",
|
|
50
51
|
"./d1": "./dist/d1/index.js",
|
|
51
52
|
"./bunSql": "./dist/bunSql/index.js",
|
|
52
53
|
"./package.json": "./package.json"
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
"clean": "rimraf dist .tsconfig.tsbuildinfo"
|
|
65
66
|
},
|
|
66
67
|
"peerDependencies": {
|
|
68
|
+
"@electric-sql/pglite": ">=0.5.0",
|
|
67
69
|
"@libsql/client": ">=0.6.0",
|
|
68
70
|
"@neondatabase/serverless": ">=0.9.0",
|
|
69
71
|
"@nestjs/common": ">=10.0.0",
|
|
@@ -80,6 +82,9 @@
|
|
|
80
82
|
"rxjs": ">=7.0.0"
|
|
81
83
|
},
|
|
82
84
|
"peerDependenciesMeta": {
|
|
85
|
+
"@electric-sql/pglite": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
83
88
|
"@libsql/client": {
|
|
84
89
|
"optional": true
|
|
85
90
|
},
|
|
@@ -124,6 +129,8 @@
|
|
|
124
129
|
}
|
|
125
130
|
},
|
|
126
131
|
"devDependencies": {
|
|
132
|
+
"@electric-sql/pglite": "0.5.7",
|
|
133
|
+
"@electric-sql/pglite-pgvector": "0.0.8",
|
|
127
134
|
"@libsql/client": "^0.17.4",
|
|
128
135
|
"@neondatabase/serverless": "^1.1.0",
|
|
129
136
|
"@nestjs/common": "^11.2.1",
|
|
@@ -172,6 +179,8 @@
|
|
|
172
179
|
"postgres",
|
|
173
180
|
"postgresql",
|
|
174
181
|
"pg",
|
|
182
|
+
"pglite",
|
|
183
|
+
"wasm",
|
|
175
184
|
"mysql",
|
|
176
185
|
"mariadb",
|
|
177
186
|
"sqlite",
|