@bezoar/pglite 0.0.0-stage → 0.0.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/.dist/index.d.mts +55 -0
- package/.dist/index.mjs +69 -0
- package/LICENSE +21 -0
- package/README.md +68 -2
- package/package.json +33 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PGliteInterface, PGliteOptions, ParserOptions, Transaction } from "@electric-sql/pglite";
|
|
2
|
+
import { Executor } from "@bezoar/core";
|
|
3
|
+
/**
|
|
4
|
+
* The parsers that make a row match the type table of `@bezoar/schema`.
|
|
5
|
+
* PGlite parses `date` and `timestamp` to a `Date`. The table declares them
|
|
6
|
+
* as strings in the form Postgres writes, so these parsers keep the text.
|
|
7
|
+
* Every other type parses as PGlite parses it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PARSERS: ParserOptions;
|
|
10
|
+
/**
|
|
11
|
+
* The prefix of the savepoint a transaction inside a transaction opens. The
|
|
12
|
+
* depth of the transaction follows it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SAVEPOINT = "bezoar_";
|
|
15
|
+
/**
|
|
16
|
+
* What `pglite` takes: nothing for a database in memory, a path for a
|
|
17
|
+
* database on a data directory, PGlite options, or a client the caller made.
|
|
18
|
+
*/
|
|
19
|
+
export type Source = string | PGliteOptions | PGliteInterface;
|
|
20
|
+
/**
|
|
21
|
+
* An {@link Executor} over PGlite. `client` is the PGlite instance, for a
|
|
22
|
+
* caller that needs more of it than the executor exposes. `close` closes the
|
|
23
|
+
* instance.
|
|
24
|
+
*/
|
|
25
|
+
export interface PGliteExecutor extends Executor {
|
|
26
|
+
readonly client: PGliteInterface;
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A connection the executor runs on: the client, or a transaction of it.
|
|
31
|
+
*/
|
|
32
|
+
export type Connection = PGliteInterface | Transaction;
|
|
33
|
+
/**
|
|
34
|
+
* The state the executors over one client share. `changed` is `true` after
|
|
35
|
+
* an `exec` ran and before the client refreshed its array types.
|
|
36
|
+
*/
|
|
37
|
+
export interface Session {
|
|
38
|
+
readonly client: PGliteInterface;
|
|
39
|
+
changed: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds a bezoar `Executor` over PGlite.
|
|
43
|
+
*
|
|
44
|
+
* `query` runs one statement with positional parameters. The rows match the
|
|
45
|
+
* type table of `@bezoar/schema`. `exec` runs text that can hold many
|
|
46
|
+
* statements, and then refreshes the array types of the client, so an array
|
|
47
|
+
* of a type a migration created parses as an array. `transaction` runs a
|
|
48
|
+
* function inside a transaction of the client. A transaction inside a
|
|
49
|
+
* transaction is a savepoint.
|
|
50
|
+
*
|
|
51
|
+
* @param source - Nothing for a database in memory, a path to a data
|
|
52
|
+
* directory, PGlite options, or a client. A client the caller made stays
|
|
53
|
+
* the caller's to close.
|
|
54
|
+
*/
|
|
55
|
+
export declare const pglite: (source?: Source) => PGliteExecutor;
|
package/.dist/index.mjs
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { PGlite, types } from "@electric-sql/pglite";
|
|
2
|
+
const PARSERS = {
|
|
3
|
+
[types.DATE]: (text) => text,
|
|
4
|
+
[types.TIMESTAMP]: (text) => text
|
|
5
|
+
};
|
|
6
|
+
const SAVEPOINT = "bezoar_";
|
|
7
|
+
const outcome = (results) => ({
|
|
8
|
+
rows: results.rows,
|
|
9
|
+
count: results.affectedRows || results.rows.length
|
|
10
|
+
});
|
|
11
|
+
const refresh = async (session) => {
|
|
12
|
+
if (session.changed) {
|
|
13
|
+
session.changed = false;
|
|
14
|
+
await session.client.refreshArrayTypes();
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const savepoint = async (tx, depth, run) => {
|
|
18
|
+
const name = `${SAVEPOINT}${depth}`;
|
|
19
|
+
await tx.query(`SAVEPOINT ${name}`);
|
|
20
|
+
try {
|
|
21
|
+
const result = await run();
|
|
22
|
+
await tx.query(`RELEASE SAVEPOINT ${name}`);
|
|
23
|
+
return result;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
await tx.query(`ROLLBACK TO SAVEPOINT ${name}`);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const defineExecutor = (session, connection, depth) => {
|
|
30
|
+
const query = async (sql, params) => outcome(await connection.query(sql, [...params], { parsers: PARSERS }));
|
|
31
|
+
const exec = async (sql) => {
|
|
32
|
+
await connection.exec(sql);
|
|
33
|
+
session.changed = true;
|
|
34
|
+
if (depth === 0) await refresh(session);
|
|
35
|
+
};
|
|
36
|
+
const transaction = async (run) => {
|
|
37
|
+
if ("transaction" in connection) {
|
|
38
|
+
const result = await connection.transaction((tx) => run(defineExecutor(session, tx, depth + 1)));
|
|
39
|
+
await refresh(session);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
return savepoint(connection, depth, () => run(defineExecutor(session, connection, depth + 1)));
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
query,
|
|
46
|
+
exec,
|
|
47
|
+
transaction
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
const pglite = (source = {}) => {
|
|
51
|
+
let client;
|
|
52
|
+
if (typeof source === "string") client = new PGlite(source);
|
|
53
|
+
else if ("query" in source) client = source;
|
|
54
|
+
else client = new PGlite(source);
|
|
55
|
+
const executor = defineExecutor({
|
|
56
|
+
client,
|
|
57
|
+
changed: false
|
|
58
|
+
}, client, 0);
|
|
59
|
+
const close = async () => {
|
|
60
|
+
await client.waitReady;
|
|
61
|
+
await client.close();
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
...executor,
|
|
65
|
+
client,
|
|
66
|
+
close
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export { PARSERS, SAVEPOINT, pglite };
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexander Thorwaldson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,69 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bezoar/pglite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A bezoar executor over [PGlite](https://pglite.dev), the Postgres that
|
|
4
|
+
runs in-process. `@bezoar/kit` builds on it. `@bezoar/testing` tests
|
|
5
|
+
on it. An app can develop on it with no server.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @bezoar/pglite
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The package carries PGlite.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { makeBezoar } from "@bezoar/core";
|
|
19
|
+
import { pglite } from "@bezoar/pglite";
|
|
20
|
+
import { contract } from "@bbf/database";
|
|
21
|
+
import { queries } from "@bbf/database/queries";
|
|
22
|
+
import { migrations } from "@bbf/database/migrations";
|
|
23
|
+
|
|
24
|
+
const executor = pglite();
|
|
25
|
+
const db = makeBezoar(contract, { executor, queries });
|
|
26
|
+
|
|
27
|
+
await db.migrate(migrations);
|
|
28
|
+
const events = await db.events.list({ day: "sunday" });
|
|
29
|
+
|
|
30
|
+
await executor.close();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`pglite()` makes a database in memory. `pglite("./data")` makes one on a
|
|
34
|
+
data directory. `pglite(options)` passes PGlite options through. The executor
|
|
35
|
+
exposes the instance as `client` and closes it with `close()`.
|
|
36
|
+
|
|
37
|
+
`pglite(client)` wraps a PGlite instance the caller made. The caller closes
|
|
38
|
+
it.
|
|
39
|
+
|
|
40
|
+
## Types
|
|
41
|
+
|
|
42
|
+
The rows of a query match the type table of `@bezoar/schema`. PGlite
|
|
43
|
+
parses most types the same way. The executor sets two parsers of its own:
|
|
44
|
+
`date` and `timestamp` stay strings in the form Postgres writes, where PGlite
|
|
45
|
+
would make a `Date` of them.
|
|
46
|
+
|
|
47
|
+
An `int8` in the safe integer range is a `number`. One outside the range is
|
|
48
|
+
a `bigint`, which the row validator of the service rejects.
|
|
49
|
+
|
|
50
|
+
## Array types
|
|
51
|
+
|
|
52
|
+
PGlite reads the array types of the database when it starts. A type that a
|
|
53
|
+
migration creates has an array type PGlite does not know until it reads them
|
|
54
|
+
again. The executor refreshes them after each `exec`, and after each
|
|
55
|
+
transaction that ran one, so a column such as `event_day[]` parses as an
|
|
56
|
+
array as soon as `migrate` returns.
|
|
57
|
+
|
|
58
|
+
## Transactions
|
|
59
|
+
|
|
60
|
+
`transaction` runs a function inside a transaction of the client. A
|
|
61
|
+
transaction inside a transaction is a savepoint: it is released when the
|
|
62
|
+
function returns and rolled back to when it throws, and the outer transaction
|
|
63
|
+
goes on.
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- [`@bezoar/core`](../core): the service that runs on an executor.
|
|
68
|
+
- `@bezoar/pg`: the executor over node-postgres, for a database with a
|
|
69
|
+
server.
|
package/package.json
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bezoar/pglite",
|
|
3
|
-
"version": "0.0.0
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "A bezoar executor over PGlite",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/zoobzio/bezoar.git",
|
|
9
|
+
"directory": "packages/pglite"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./.dist/index.mjs",
|
|
13
|
+
"types": "./.dist/index.d.mts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./.dist/index.d.mts",
|
|
17
|
+
"import": "./.dist/index.mjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
".dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "obuild",
|
|
25
|
+
"stub": "obuild --stub",
|
|
26
|
+
"lint": "oxlint",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@bezoar/core": "workspace:*",
|
|
32
|
+
"@bezoar/schema": "workspace:*",
|
|
33
|
+
"@electric-sql/pglite": "catalog:"
|
|
34
|
+
}
|
|
35
|
+
}
|