## File: README.md

# PGlite - Postgres in WASM



PGlite is a WASM Postgres build packaged into a TypeScript client library that enables you to run Postgres in the browser, Node.js, Bun and Deno, with no need to install any other dependencies. It is only 3mb gzipped and has support for many Postgres extensions, including [pgvector](https://github.com/pgvector/pgvector) and [PostGIS](https://postgis.net).

```javascript
import { PGlite } from "@electric-sql/pglite";

const db = new PGlite();
await db.query("select 'Hello world' as message;");
// -> { rows: [ { message: "Hello world" } ] }
```

It can be used as an ephemeral in-memory database, or with persistence either to the file system (Node/Bun/Deno) or indexedDB (Browser).

Unlike previous "Postgres in the browser" projects, PGlite does not use a Linux virtual machine - it is simply Postgres in WASM.

For full documentation and user guides see [pglite.dev](https://pglite.dev).

## Browser

It can be installed and imported using your usual package manager:

```js
import { PGlite } from "@electric-sql/pglite";
```
or using a CDN such as JSDeliver:

```js
import { PGlite } from "https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js";
```

Then for an in-memory Postgres:

```js
const db = new PGlite()
await db.query("select 'Hello world' as message;")
// -> { rows: [ { message: "Hello world" } ] }
```

or to persist the database to indexedDB:

```js
const db = new PGlite("idb://my-pgdata");
```

## Node/Bun/Deno

Install into your project:

**NodeJS**

```bash
npm install @electric-sql/pglite
```

**Bun**

```bash
bun install @electric-sql/pglite
```

**Deno**

```bash
deno add npm:@electric-sql/pglite
```

To use the in-memory Postgres:

```javascript
import { PGlite } from "@electric-sql/pglite";

const db = new PGlite();
await db.query("select 'Hello world' as message;");
// -> { rows: [ { message: "Hello world" } ] }
```

or to persist to the filesystem:

```javascript
const db = new PGlite("./path/to/pgdata");
```

## How it works

PostgreSQL typically operates using a process forking model; whenever a client initiates a connection, a new process is forked to manage that connection. However, programs compiled with Emscripten - a C to WebAssembly (WASM) compiler - cannot fork new processes, and operates strictly in a single-process mode. As a result, PostgreSQL cannot be directly compiled to WASM for conventional operation.

Fortunately, PostgreSQL includes a "single user mode" primarily intended for command-line usage during bootstrapping and recovery procedures. Building upon this capability, PGlite introduces an input/output pathway that facilitates interaction with PostgreSQL when it is compiled to WASM within a JavaScript environment.

## Limitations

- PGlite is single user/connection.

## How to build PGlite and contribute

The build process of PGlite is split into two parts:

1. Building the Postgres WASM module.
2. Building the PGlite client library and other TypeScript packages.

Docker is required to build the WASM module, along with Node (v20 or above) and [pnpm](https://pnpm.io/) for package management and building the TypeScript packages.

To start checkout the repository and install dependencies:

```bash
git clone --recurse-submodules https://github.com/electric-sql/pglite
cd pglite
pnpm install
```

To build everything, we have the convenient `pnpm build:all` command in the root of the repository. This command will:

1. Use Docker to build the Postgres WASM module. The artifacts produced by this step are then copied to `/packages/pglite/release`.
2. Build the PGlite client library and other TypeScript packages.

To _only_ build the Postgres WASM module (i.e. point 1 above), run

```bash
pnpm wasm:build
```

If you don't want to build the WASM module and assorted WASM binaries from scratch, they are generated automatically on Github after each successful PR merge. You can download the latest binaries by going to the last successfully merged PR and clicking the link after the comment _Interim build files:_. Extract the files and place them under `packages/pglite/release` in your local repo copy.

To build all TypeScript packages (i.e. point 2 of the above), run:

```bash
pnpm ts:build
```

This will build all packages in the correct order based on their dependency relationships. You can now develop any individual package using the `build` and `test` scripts, as well as the `stylecheck` and `typecheck` scripts to ensure style and type validity.

Or alternatively to build a single package, move into the package directory and run:

```bash
cd packages/pglite
pnpm build
```

When ready to open a PR, run the following command at the root of the repository:
```bash
pnpm changeset
```
And follow the instructions to create an appropriate changeset. Please ensure any contributions that touch code are accompanied by a changeset.

## Acknowledgments

PGlite builds on the work of [Stas Kelvich](https://github.com/kelvich) of [Neon](https://neon.tech) in this [Postgres fork](https://github.com/electric-sql/postgres-pglite).

## Sponsors

Big shoutout to everybody supporting us!

### Blacksmith

<a href="https://blacksmith.sh">
  
</a>

## License

PGlite is dual-licensed under the terms of the [Apache License 2.0](https://github.com/electric-sql/pglite/blob/main/LICENSE) and the [PostgreSQL License](https://github.com/electric-sql/pglite/blob/main/POSTGRES-LICENSE), you can choose which you prefer.

Changes to the [Postgres source](https://github.com/electric-sql/postgres-pglite) are licensed under the PostgreSQL License.

---

## File: docs/docs/framework-hooks/react.md

---
outline: [2, 3]
---

# React

To aid integration of PGlite into a [React](https://react.dev/) project we have a `PGliteProvider` with a corresponding `usePGlite` and hooks for the [live query](../live-queries.md) plugin.

### PGliteProvider

The `PGliteProvider` enables you to initiate a PGlite database and pass it to all child components for use with the [`usePGlite`](#usepglite), [`useLiveQuery`](#uselivequery), and [`useLiveIncrementalQuery`](#useliveincrementalquery) hooks.

To use it, pass a PGlite instance as the `db` property.

```ts
import { PGlite } from "@electric-sql/pglite"
import { live } from "@electric-sql/pglite/live"
import { PGliteProvider } from "@electric-sql/pglite-react"

const db = await PGlite.create({
  extensions: { live }
})

const App = () => {
  // ...

  return (
    
      // ...
    
  )
}
```

### usePGlite

You can retrieve the provided PGlite instance using `usePGlite` and then query it from within your components.

```ts
import { usePGlite } from "@electric-sql/pglite-react"

const MyComponent = () => {
  const db = usePGlite()

  const insertItem = () => {
    db.query("INSERT INTO my_table (name, number) VALUES ('Arthur', 42);")
  }

  return (
    <>
      
  )
}
```

### makePGliteProvider

The `makePGliteProvider` function returns a `PGliteProvider` component and a `usePGlite` hook with the specified type, which enables you to provide a PGlite instance with all added extensions and retain then namespaces and types added to it.

```ts
import { PGlite, PGliteInterfaceExtensions } from '@electric-sql/pglite'
import { LiveNamespace } from '@electric-sql/pglite/live'
import { VectorNamespace } from '@electric-sql/pglite-pgvector'
import { makePGliteProvider } from '@electric-sql/pglite-react'

const { PGliteProvider, usePGlite } = makePGliteProvider<
  PGlite &
    PGliteInterfaceExtensions<{
      live: typeof live
      vector: typeof vector
    }>
>()

export { PGliteProvider, usePGlite }
```

### useLiveQuery

The `useLiveQuery` hook enables you to reactively re-render your component whenever the results of a live query change. It wraps the [`.live.query()`](../live-queries.md#livequery) API.

It has the interface:

```ts
function useLiveQuery<T = { [key: string]: unknown }>(
  query: string,
  params: unknown[] | undefined | null,
): Results<T>
```

And its arguments are:

1. the SQL query
2. optional parameters for the query

```ts
import { useLiveQuery } from '@electric-sql/pglite-react'

const MyComponent = () => {
  const maxNumber = 100
  const items = useLiveQuery(`
    SELECT *
    FROM my_table
    WHERE number <= $1
    ORDER BY number;
  `, [maxNumber])

  return (
    <>
      {
        items.map((item) =>
          <MyItem item={item} />
        )
      }
    </>
  )
}
```

### useLiveQuery.sql

Similarly to how you can use [`    sql`` `](../api.md#sql) to to construct SQL queries through string templating, you can do that with `    useLiveQuery.sql`` ` for the hook equivalent. See the [templating](../api.md#tagged-template-queries) section of the API for more details.

```ts
import { useLiveQuery } from '@electric-sql/pglite-react'

const MyComponent = () => {
  const maxNumber = 100
  const items = useLiveQuery.sql`
    SELECT *
    FROM my_table
    WHERE number <= ${maxNumber}
    ORDER BY number;
  `
// ...
```

### useLiveIncrementalQuery

The `useLiveIncrementalQuery` hook enables you to reactively re-render your component whenever the results of a live query change. It wraps the [`.live.incrementalQuery()`](../live-queries.md#liveincrementalquery) API, which provides a way to efficiently diff the query results in Postgres.

It has the interface:

```ts
function useLiveIncrementalQuery<T = { [key: string]: unknown }>(
  query: string,
  params: unknown[] | undefined | null,
  key: string,
): Results<T>
```

And its arguments are:

1. the SQL query
2. optional parameters for the query
3. the name of the column to key the diff algorithm on

```ts
import { useLiveIncrementalQuery } from '@electric-sql/pglite-react'

const MyComponent = () => {
  const maxNumber = 100
  const items = useLiveIncrementalQuery(`
    SELECT *
    FROM my_table
    WHERE number <= $1
    ORDER BY number;
  `, [maxNumber], 'id')

  return (
    <>
      {
        items.map((item) =>
          <MyItem item={item} />
        )
      }
    </>
  )
}
```

---

## File: docs/docs/framework-hooks/vue.md

---
outline: [2, 3]
---

# Vue

To aid integration of PGlite into a [Vue](https://vuejs.org/) project we have a `providePGlite` with a corresponding `injectPGlite` and hooks for the [live query](../live-queries.md) plugin.

### providePGlite

The `providePGlite` API, which follows the [Vue provide / inject pattern](https://vuejs.org/guide/components/provide-inject), enables you to initiate a PGlite database and pass it to all child components for use with the corresponding [`injectPGlite`](#injectpglite) method, as well as with the [`useLiveQuery`](#uselivequery) and [`useLiveIncrementalQuery`](#useliveincrementalquery) hooks.

To use it, pass a PGlite instance as the `db` property.

```vue
<script lang="ts" setup>
import { PGlite } from '@electric-sql/pglite'
import { providePGlite } from '@electric-sql/pglite-vue'

const db = new PGlite()
providePGlite(db)
</script>
// ...
```

### injectPGlite

You can retrieve the provided PGlite instance using `injectPGlite` and then query it from within your components.

```vue
<script lang="ts" setup>
import { onMounted, shallowRef } from 'vue'
import { injectPGlite } from '@electric-sql/pglite-vue'

const db = injectPGlite()

const insertItem = () => {
  db.query("INSERT INTO my_table (name, number) VALUES ('Arthur', 42);")
}
</script>

<template>
  // ...
  Insert item
  // ...
</template>
```

### makePGliteDependencyInjector

The `makePGliteDependencyInjector` function returns typed versions of `providePGlite` and `injectPGlite`, which enables you to provide a PGlite instance with all added extensions and retain then namespaces and types added to it.

```ts
import { PGlite, PGliteInterfaceExtensions } from '@electric-sql/pglite'
import { live } from '@electric-sql/pglite/live'
import { vector } from '@electric-sql/pglite-pgvector'
import { makePGliteDependencyInjector } from '@electric-sql/pglite-vue'

const { providePGlite, injectPGlite } = makePGliteDependencyInjector<
  PGlite &
    PGliteInterfaceExtensions<{
      live: typeof live
      vector: typeof vector
    }>
>()

export { providePGlite, injectPGlite }
```

### useLiveQuery

The `useLiveQuery` hook enables you to reactively receive updates to the results of a live query change. It wraps the [`.live.query()`](../live-queries.md#livequery) API.

It has the interface:

```ts
function useLiveQuery<T = { [key: string]: unknown }>(
  query: string | WatchSource<string>,
  params?: QueryParams | WatchSource<QueryParams> | WatchSource[],
): LiveQueryResults<T>
```

And its arguments, which can also be [watch sources](https://vuejs.org/guide/essentials/watchers.html#watch-source-types) that will trigger a re-run, are:

1. the SQL query
2. optional parameters for the query

```vue
<script lang="ts">
import { useLiveQuery } from '@electric-sql/pglite-vue'

const maxNumber = 100
const items = useLiveQuery(
  `
    SELECT *
    FROM my_table
    WHERE number <= $1
    ORDER BY number;
  `,
  [maxNumber],
)
</script>

<template>
  <MyItem v-for="item in items" :item="item" :key="item.id" />
</template>
```

### useLiveQuery.sql

Similarly to how you can use [`    sql`` `](../api.md#sql) to to construct SQL queries through string templating, you can do that with `    useLiveQuery.sql`` ` for the hook equivalent. See the [templating](../api.md#tagged-template-queries) section of the API for more details.

```vue
<script lang="ts">
import { useLiveQuery } from '@electric-sql/pglite-vue'

const maxNumber = 100
const items = useLiveQuery.sql`
  SELECT *
  FROM my_table
  WHERE number <= ${maxNumber}
  ORDER BY number;
`
</script>
// ...
```

### useLiveIncrementalQuery

The `useLiveIncrementalQuery` hook enables you to reactively receive updates whenever the results of a live query change. It wraps the [`.live.incrementalQuery()`](../live-queries.md#liveincrementalquery) API, which provides a way to efficiently diff the query results in Postgres.

It has the interface:

```ts
export function useLiveIncrementalQuery<T = { [key: string]: unknown }>(
  query: string | WatchSource<string>,
  params: QueryParams | WatchSource<QueryParams> | WatchSource[],
  key: string | WatchSource<string>,
): LiveQueryResults<T>
```

And its arguments, which can also be [watch sources](https://vuejs.org/guide/essentials/watchers.html#watch-source-types) that will trigger a re-run, are:

1. the SQL query
2. optional parameters for the query
3. the name of the column to key the diff algorithm on

```vue
<script lang="ts">
import { useLiveIncrementalQuery } from '@electric-sql/pglite-vue'

const maxNumber = 100
const items = useLiveIncrementalQuery(
  `
    SELECT *
    FROM my_table
    WHERE number <= $1
    ORDER BY number;
  `,
  [maxNumber],
  'id',
)
</script>

<template>
  <MyItem v-for="item in items" :item="item" :key="item.id" />
</template>
```

---

## File: docs/docs/about.md

# What is PGlite

PGlite is a [WASM](https://webassembly.org/) Postgres build packaged into a TypeScript/JavaScript client library, that enables you to run Postgres in the browser, [Node.js](https://nodejs.org/) and [Bun](https://bun.sh/), with no need to install any other dependencies. It's under 3mb Gzipped, and has support for many [Postgres extensions](../extensions/), including [pgvector](../extensions/#pgvector).

Getting started with PGlite is simple: just install and import the NPM package, then create your embedded database:

```js
import { PGlite } from '@electric-sql/pglite'

const db = new PGlite()
await db.query("select 'Hello world' as message;")
// -> { rows: [ { message: "Hello world" } ] }
```

It can be used as an ephemeral in-memory database, or with persistence either to the file system (Node/Bun), or IndexedDB (browser).

Unlike previous "Postgres in the browser" projects, PGlite does not use a Linux virtual machine - it is simply Postgres in WASM.

It's being developed by [Electric](https://electric.ax/) for our use case of embedding into applications, either locally or at the edge, allowing users to sync a subset of their server-side Postgres database.

However, there are many more use cases for PGlite beyond its use as an embedded application database:

- **Unit and CI testing**
  PGlite is very fast to start and tear down. It's perfect for unit tests - you can have a unique fresh Postgres for each test.

- **Local development**
  You can use PGlite as an alternative to a full local Postgres for development; simplifying your development environments.

- **Remote development, or local web containers**
  As PGlite is so lightweight it can be easily embedded into remote containerised development environments, or in-browser [web containers](https://webcontainers.io).

- **On-device or edge AI and RAG**
  PGlite has full support for [pgvector](../extensions/#pgvector), enabling a local or edge retrieval augmented generation (RAG) workflow.

We are very keen to establish PGlite both as an open source, and open contribution, project, working to build a community around it, so as to develop its capabilities for all use cases.

Read more in our [getting started guide](./index.md).

---

## File: docs/docs/api.md

---
outline: [2, 3]
---

# PGlite API

## Main Constructor

The main constructor is imported as:

```ts
import { PGlite } from '@electric-sql/pglite'
```

The preferred way to create a PGlite instance is with the `PGlite.create()` static method that returns a promise, resolving to the new PGlite instance.

`await PGlite.create(dataDir: string, options: PGliteOptions)`
`await PGlite.create(options: PGliteOptions)`

There are a couple of advantages to using the static method:

- This awaits the [`.waitReady`](#waitready) promise, ensuring that the database has been fully initialised.
- When using TypeScript and extensions, the returned PGlite instance will have the extensions namespace on its type. This is not possible with the standard constructor due to TypesScript limitations.

A new PGlite instance can also be created using the `new PGlite()` constructor.

`new PGlite(dataDir: string, options: PGliteOptions)`
`new PGlite(options: PGliteOptions)`

#### `dataDir`

Path to the directory for storing the Postgres database. You can provide a URI scheme for various storage backends:

- `file://` or unprefixed
  File system storage, available in Node and Bun.
- `idb://`
  [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) storage, available in the browser.
- `memory://`
  In-memory ephemeral storage, available in all platforms.

#### `options`

- `dataDir?: string`
  The directory in which to store the Postgres database when not provided as the first argument.
- `debug?: 1-5`
  the Postgres debug level. Logs are sent to the console.
- `relaxedDurability?: boolean`
  Under relaxed durability mode, PGlite will not wait for flushes to storage to complete after each query before returning results. This is particularly useful when using the IndexedDB file system.
- `fs?: Filesystem`
  The alternative to providing a dataDir with a filesystem prefix is to initialise a `Filesystem` yourself and provide it here. See [Filesystems](./filesystems.md)
- `loadDataDir?: Blob | File`
  A tarball of a PGlite `datadir` to load when the database starts. This should be a tarball produced from the related [`.dumpDataDir()`](#dumpdatadir) method.
- `extensions?: Extensions`
  An object containing the extensions you wish to load.
- `username?: string`
  The username of the user to connect to the database as. Permissions will be applied in the context of this user.
- `database?: string`
  The database from the Postgres cluster within the `dataDir` to connect to.
- `initialMemory?: number`
  The initial amount of memory in bytes to allocate for the PGlite instance. PGlite will grow the memory automatically, but if you have a particularly large database you can set this higher to prevent the pause during memory growth.
- `pgliteWasmModule?: WebAssembly.Module`
  A precompiled pglite WASM module to use instead of downloading the default version, or when using a bundler that either can, or requires, loading the WASM module with a ESM import.
- `initdbWasmModule?: WebAssembly.Module`
  A precompiled initdb WASM module to use instead of downloading the default version, or when using a bundler that either can, or requires, loading the WASM module with a ESM import.
- `fsBundle?: Blob | File`
  A filesystem bundle to use instead of downloading the default version. This is useful if in a restricted environment such as an edge worker.
- `parsers: ParserOptions` 
  An object of type `{ [pgType: number]: (value: string) => any; }` mapping Postgres data type IDs to parser functions. For convenience, the `pglite` package exports a constant for most common Postgres types.

  ```ts
  import { PGlite, types } from '@electric-sql/pglite'

  const pg = await PGlite.create({
    parsers: {
      [types.TEXT]: (value) => value.toUpperCase(),
    },
  })
  ```

- `serializers: SerializerOptions` 
  An object of type `{ [pgType: number]: (value: any) => string; }` mapping Postgres data type IDs to serializer functions.

  ```ts
  import { PGlite, types } from '@electric-sql/pglite'

  const pg = await PGlite.create({
    serializers: {
      [types.NUMERIC]: (value) => value.toString(),
    },
  })
  ```

- `startParams?: string[]` 
  An array of strings that will be passed to the Postgres process as start parameters. This is the set of parameters one would pass to a native PostgreSQL instance.

  ```ts
  import { PGlite } from '@electric-sql/pglite'

  const pg = await PGlite.create({
    startParams: [
      ...PGlite.defaultStartParams,
      '-c',
      'application_name=My awesome backend',
    ],
  })
  ```

- `initDbStartParams?: string[]` 
  An array of strings that will be passed to the initdb process as start parameters. This is the set of parameters one would pass to a native initdb exe.

  ```ts
  import { PGlite } from '@electric-sql/pglite'

  const pg = await PGlite.create({
    initDbStartParams: ['--locale-provider=icu', '--icu-locale=de'],
  })
  ```

- `postgresqlconf?: string[] | string` 
  A `postgresql.conf` file as an array of strings or a single string. You can use it to pass parameters to the PostgreSQL process. See [official documentation](https://www.postgresql.org/docs/current/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE) for details.

#### `options.extensions`

PGlite and Postgres extensions are loaded into a PGLite instance on start, and can include both a WASM build of a Postgres extension and/or a PGlite client plugin.

The `options.extensions` parameter is an object of `namespace: extension` parings. The namespace is used to expose the PGlite client plugin included in the extension. An example of this is the [live queries](./live-queries.md) extension.

```ts
import { PGlite } from '@electric-sql/pglite'
import { live } from '@electric-sql/pglite/live'
import { vector } from '@electric-sql/pglite-pgvector'

const pg = await PGlite.create({
  extensions: {
    live, // Live query extension, is a PGlite client plugin
    vector, // Postgres pgvector extension
  },
})

// The `live` namespace is added by the use of the
// `live` key in the `extensions` object.
pg.live.query('...')
```

For information on how to develop a PGlite extension see [Extension Development](../extensions/development.md).

## Methods

### query

`.query<T>(query: string, params?: any[], options?: QueryOptions): Promise<Results<T>>`

Execute a single statement, optionally with parameters.

Uses the _extended query_ Postgres wire protocol.

Returns single [result object](#results-t-objects).

##### Example

```ts
await pg.query('INSERT INTO test (name) VALUES ($1);', ['test'])
// { affectedRows: 1 },
```

##### Query Options

The `query` and `exec` methods take an optional `options` objects with the following parameters:

- `rowMode: "object" | "array"` 
  The returned row object type, either an object of `fieldName: value` mappings or an array of positional values. Defaults to `"object"`.
- `parsers: ParserOptions` 
  An object mapping Postgres data type IDs to parser functions. This option overrides any parsers set at the instance level.
  ```ts
  import { types } from '@electric-sql/pglite'
  await pg.query(`SELECT * FROM test WHERE name = $1;`, ['test'], {
    parsers: {
      [types.TEXT]: (value) => value.toUpperCase(),
    },
  })
  ```
- `serializers: SerializerOptions` 
  An object mapping Postgres data type IDs to serializer functions. This option overrides any serializers set at the instance level.
  ```ts
  import { types } from '@electric-sql/pglite'
  await pg.query(`INSERT INTO test (numeric) VALUES ($1);`, [100n], {
    serializers: {
      [types.NUMERIC]: (value: number | bigint) => value.toString(),
    },
  })
  ```
- `blob: Blob | File` 
  Attach a `Blob` or `File` object to the query that can used with a `COPY FROM` command by using the virtual `/dev/blob` device, see [importing and exporting](#dev-blob).

### sql

`   .sql<T>``: Promise<Results<T>>`

`.sql<T>(strings: TemplateStringsArray, ...params: any[]): Promise<Results<T>>`

You can also use the [`    sql`` `](#tagged-template-queries) tagged template literal to create queries:

```ts
await pg.sql`SELECT * FROM test WHERE name = ${'test'}`
// equivalent of pg.query('SELECT * FROM test WHERE name = $1', ['test'])
```

See the [templating](#tagged-template-queries) section for more details on how to use the tagged template literal along with various helpers for more complex cases.

### exec

`.exec(query: string, options?: QueryOptions): Promise<Array<Results>>`

Execute one or more statements. _(note that parameters are not supported)_

This is useful for applying database migrations, or running multi-statement SQL that doesn't use parameters.

Uses the _simple query_ Postgres wire protocol.

Returns array of [result objects](#results-t-objects); one for each statement.

##### Example

```ts
await pg.exec(`
  CREATE TABLE IF NOT EXISTS test (
    id SERIAL PRIMARY KEY,
    name TEXT
  );
  INSERT INTO test (name) VALUES ('test');
  SELECT * FROM test;
`)
// [
//   { affectedRows: 0 },
//   { affectedRows: 1 },
//   {
//     rows: [
//       { id: 1, name: 'test' }
//     ]
//     affectedRows: 0,
//     fields: [
//       { name: 'id', dataTypeID: '23' },
//       { name: 'name', dataTypeID: '25' },
//     ]
//   }
// ]
```

### transaction

`.transaction<T>(callback: (tx: Transaction) => Promise<T>)`

To start an interactive transaction, pass a callback to the transaction method. It is passed a `Transaction` object which can be used to perform operations within the transaction.

The transaction will be committed when the promise returned from your callback resolves, and automatically rolled back if the promise is rejected.

##### `Transaction` objects

- `tx.query<T>(query: string, params?: any[], options?: QueryOptions): Promise<Results<T>>`
  The same as the main [`.query` method](#querytquery-string-params-any-promiseresultst).
- `   tx.sql<T>``: Promise<Results<T>>`
  The same as the main [`.sql` template string method](#tagged-template-queries).
- `tx.exec(query: string, options?: QueryOptions): Promise<Array<Results>>`
  The same as the main [`.exec` method](#execquery-string-promisearrayresults).
- `tx.rollback()`
  Rollback and close the current transaction.

##### Example

```ts
await pg.transaction(async (tx) => {
  await tx.query(
    'INSERT INTO test (name) VALUES ('$1');',
    [ 'test' ]
  );
  return await ts.query('SELECT * FROM test;');
});
```

### close

`.close(): Promise<void>`

Close the database, ensuring it is shut down cleanly.

### listen

`.listen(channel: string, callback: (payload: string) => void): Promise<(() => Promise<void>>`

Subscribe to a [pg_notify](https://www.postgresql.org/docs/current/sql-notify.html) channel. The callback will receive the payload from the notification.

Returns an unsubscribe function to unsubscribe from the channel.

##### Example

```ts
const unsub = await pg.listen('test', (payload) => {
  console.log('Received:', payload)
})
await pg.query("NOTIFY test, 'Hello, world!'")
```

Channel names are case sensitive if double-quoted (`pg.listen('"TeST"')`). Otherwise channel name will be lower cased (`pg.listen('TeStiNG')` == `pg.listen('testing')`).

### unlisten

`.unlisten(channel: string, callback?: (payload: string) => void): Promise<void>`

Unsubscribe from the channel. If a callback is provided it removes only that callback from the subscription. When no callback is provided, it unsubscribes all callbacks for the channel.

### onNotification

`onNotification(callback: (channel: string, payload: string) => void): () => void`

Add an event handler for all notifications received from Postgres.

**Note:** This does not subscribe to the notification; you will need to manually subscribe with `LISTEN channel_name`.

### offNotification

`offNotification(callback: (channel: string, payload: string) => void): void`

Remove an event handler for all notifications received from Postgres.

### dumpDataDir

`dumpDataDir(compression?: 'auto' | 'gzip' | 'none'): Promise<File | Blob>`

Dump the Postgres `datadir` to a Gzipped tarball.

The compression option defaults to `auto` which uses compression where possible. You can explicit opt in or out of compression with `gzip` and `none`. When you specify that compression is required with `gzip`, if the environment doesn't support a suitable compression API it will throw an error.

This can then be used in combination with the [`loadDataDir`](#options) option when starting PGlite to load a dumped database from storage.

::: tip NOTE

The datadir dump may not be compatible with other Postgres versions; it is only designed for importing back into PGlite.

:::

### execProtocol

`execProtocol(message: Uint8Array, options?: ExecProtocolOptions): Promise<Array<[BackendMessage, Uint8Array]>>`

Execute a Postgres wire protocol message, returning an array of tuples, one for each wire protocol result message, consisting of:

1. The passed message object - see [pg-protocol](https://github.com/brianc/node-postgres/tree/master/packages/pg-protocol)
2. The raw `Uint8Array` for that message.

This API is safe to use alongside the other PGlite query APIs as it handles error, transactions and notifications.

### execProtocolRaw

`execProtocolRaw(message: Uint8Array, options?: ExecProtocolOptions): Promise`

Execute a Postgres wire protocol message, returning the unparsed result `Uint8Array`, this includes all wire protocol result messages emitted as a result of your message and will require external passing. This is the lowest level API exposed by PGlite and can be used to interact with a PGlite database using existing Postgres clients. It is likely that you will want to use something such as [pg-gateway](https://github.com/supabase-community/pg-gateway) that uses this internally to expose the database on a TCP socket.

::: warning WARNING

`execProtocolRaw` bypasses PGlite's protocol wrappers that manage error/notice messages,
transactions, and notification listeners. Only use if you need to bypass these wrappers and don't intend to use the above features. [`execProtocol`](#execprotocol) is a safer alternative.

:::

`execProtocolRawStream(message: Uint8Array, options: ExecProtocolOptionsStream): void`
Same as `execProtocolRaw` but returns raw messages in the `options.onRawData` callback as they arrive from the backend. This is particularly useful when expecting large sets of data.
See a usage example in the `pglite-socket` project, where the server uses this method to pass the results to the client socket as they arrive.

### describeQuery

`.describeQuery(query: string, options?: QueryOptions): Promise<DescribeQueryResult>`

Get type information about a query's parameters and result fields without executing it. This is useful for understanding the expected parameter types and the structure of the results that would be returned. It can be used to build a type inference system for queries using PGlite.

Returns an object with:

- `queryParams: Array<{ dataTypeID: number, serializer: Function }>` 
  Information about each parameter's Postgres type ID and the serializer function used to convert JavaScript values to Postgres format.

- `resultFields: Array<{ name: string, dataTypeID: number, parser: Function }>` 
  Information about each result field including its name, Postgres type ID, and the parser function used to convert Postgres values to JavaScript format.

### refreshArrayTypes

`.refreshArrayTypes(): Promise<void>`

Refresh the array types in the database. This is useful when you have added columns that contain array types (ie. array of enums) and need to update the internal array type cache. This is done automatically when the database is started, but can be called manually if needed.

##### Example

```ts
await pg.describeQuery('SELECT * FROM test WHERE name = $1', ['test'])

// returns:
{
  queryParams: [{ dataTypeID: 25, serializer: Function }],
  resultFields: [{ name: "id", dataTypeID: 23, parser: Function }],
}
```

### clone

`.clone(): Promise`

Clones the current instance. This is useful when a series of operations, like unit or integration test, need to be run on the same database without having to recreate the database each time, or for each test.

### copyToFS

`.copyToFS(filePath: string, data: Uint8Array, mode?: number)`

Copies the `data` to a file at `filePath` in the virtual filesystem and sets the optional `mode` of the file.

##### Example

```ts
await pg.copyToFS(
  '/tmp/test.txt',
  new TextEncoder().encode('PGlite says hi!'),
  0o0644,
)
```

## Properties

### ready

`.ready` _`boolean (read only)`_

Whether the database is ready to accept queries.

### closed

`.closed` _`boolean (read only)`_

Whether the database is closed and no longer accepting queries.

### waitReady

`.waitReady` _`Promise<void>`_

Promise that resolves when the database is ready to use.

::: tip NOTE

Query methods will wait for the `waitReady` promise to resolve if called before the database has fully initialised, and so it is not necessary to wait for it explicitly.

:::

## `Results<T>` Objects

Result objects have the following properties:

- `rows: Row<T>[]`
  The rows returned by the query.

- `affectedRows?: number` 
  Count of the rows affected by the query. Note, this is _not_ the count of rows returned, it is the number or rows in the database changed by the query.

- `command?: string` 
  The command of the query that was run, taken from the command tag reported by Postgres, e.g. `SELECT`, `INSERT` or `CREATE`. Matches the `command` field of node-postgres query results.

- `rowCount?: number` 
  The number of rows reported by the command tag, e.g. the rows returned by a `SELECT` or changed by an `UPDATE`. Not set when the tag carries no count (e.g. `CREATE TABLE`). Unlike `affectedRows` this is per statement and not cumulative; it matches the `rowCount` field of node-postgres query results.

- `fields: { name: string; dataTypeID: number }[]`
  Field name and Postgres data type ID for each field returned.

- `blob?: Blob` 
  A `Blob` containing the data written to the virtual `/dev/blob/` device by a `COPY TO` command. See [/dev/blob](#dev-blob).

## `Row<T>` Objects

Rows objects are a key / value mapping for each row returned by the query.

The `.query<T>()` method can take a TypeScript type describing the expected shape of the returned rows.

::: tip NOTE

These types are not validated at run time, the result is only cast to the provided type.

:::

## Tagged Template Queries

PGlite has support for using [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) to construct SQL queries via the `    .sql`` ` method on both the main PGlite instance and [Transaction](#transaction-objects) objects. Substituted template values are automatically converted to query parameters, and you receive the same results as calling the [`query`](#query) API directly.

```ts
const name = getName()
await pg.sql`SELECT * FROM test WHERE name = ${name}`
```

There are helpers in the `template` export to create more complex and parametrizeable templated queries:

- `    identifier`` ` 
  Tag identifiers like column and table names to escape them with quotes and prevent them from becoming parameters.
- `    raw`` ` 
  Tag any string that you want to avoid being interpreted as a parameter. Will allow you to still do regular string templating.
- `    sql`` ` 
  Tag nested templated literals to preserve behaviour and parametrization. Will allow you to create reusable templating utilities
- `    query`` ` 
  Use at the top level tag in order to generate the parametrized query without passing it to the `query` API. Returns a `{ query: string, params: any[] }` object with the parametrized query and any parameters used in the query.

If you require additional configurations or complex binary parameters it's best to use the [`query` method](#query).

##### Example

```ts
import { identifier, raw, sql, query } from '@electric-sql/pglite/template'

await pg.sql`SELECT * FROM ${identifier`test`} WHERE name = ${'test'}`
// equivalent of pg.query('SELECT * FROM "test" WHERE name = $1', ['test'])

const filterStmt = (filterVar?: string) =>
  filterVar ? sql`WHERE name = ${filterVar}` : raw`WHERE 1=1`

await pg.sql`SELECT * FROM test ${filterStmt('test')}`
// equivalent of pg.query('SELECT * FROM "test" WHERE name = $1', ['test'])
await pg.sql`SELECT * FROM test ${filterStmt(null)}`
// equivalent of pg.query('SELECT * FROM "test" WHERE 1=1, [])

query`SELECT * FROM ${identifier`test`} WHERE name = ${'test'}`
// { query: 'SELECT * FROM "test" WHERE name = $1', params: ['test'] }
```

## /dev/blob

PGlite has support for importing and exporting via the SQL `COPY TO/FROM` command by using a virtual `/dev/blob` device.

To import a file, pass the `File` or `Blob` in the query options as `blob`, and copy from the `/dev/blob` device.

```ts
await pg.query("COPY my_table FROM '/dev/blob';", [], {
  blob: MyBlob,
})
```

To export a table or query to a file, you just need to write to the `/dev/blob` device; the file will be returned as `blob` on the query results:

```ts
const ret = await pg.query("COPY my_table TO '/dev/blob';")
// ret.blob is a `Blob` object with the data from the copy.
```

---

## File: docs/docs/bundler-support.md

# Bundler Support

Some bundlers require additional configuration to work with PGlite.

:::tip

If you come across any issues with PGlite and a specific bundler, please [open an issue](https://github.com/electric-sql/pglite/issues/new), we'd also love any contributions to this bundler documentation if you're able to help out.

:::

## Vite

When using [Vite](https://vitejs.dev/), make sure to exclude `pglite` from dependency optimization using the `optimizeDeps` option inside `vite.config.js`:

```js
import { defineConfig } from 'vite'

export default defineConfig({
  optimizeDeps: {
    exclude: ['@electric-sql/pglite'],
  },
})
```

### Additional configuration for the Multi-tab Worker

When using the Multi-tab Worker, you might encounter errors during a production build related to workers being bundle in `iife` format, to resolve this modify the `worker.format` option in `vite.config.js` to `'es'` (the default is `'iife'`)

```ts
import { defineConfig } from 'vite'

export default defineConfig({
  optimizeDeps: {
    exclude: ['@electric-sql/pglite'],
  },
  worker: {
    format: 'es',
  },
})
```

When importing the worker in your script, you can use the recommended [?worker](https://vitejs.dev/guide/features#static-assets) import method from Vite:

```ts
import PGWorker from './worker.js?worker'

export const pglite = new PGliteWorker(
  new PGWorker({
    type: 'module',
      name: 'pglite-worker',
    }),
    {
      // ...your options here
    }
  },
)
```

## esbuild

[esbuild](https://esbuild.github.io/) does not support `new URL('./file', import.meta.url)` pattern that PGlite uses to locate its WebAssembly and data files. This means the automatic file resolution won't work out of the box.

### Workaround: manually provide `pgliteWasmModule`, `initdbWasmModule` and `fsBundle`

1. Copy `pglite.wasm`, `initdb.wasm` and `pglite.data` from `node_modules/@electric-sql/pglite/dist/` to your public/build directory so your web server can serve them.

2. Pass them manually when creating a PGlite instance:

```ts
import { PGlite } from '@electric-sql/pglite'

const [pgliteWasmModule, initdbWasmModule, fsBundle] = await Promise.all([
  WebAssembly.compileStreaming(fetch('/pglite.wasm')),
  WebAssembly.compileStreaming(fetch('/initdb.wasm')),
  fetch('/pglite.data').then((response) => response.blob()),
])

const db = await PGlite.create({
  pgliteWasmModule,
  initdbWasmModule,
  fsBundle,
})
```

Alternatively, you can use an esbuild plugin like [`@chialab/esbuild-plugin-meta-url`](https://chialab.github.io/rna/guide/esbuild-plugin-meta-url) to handle `new URL()` imports automatically.

## Next.js

When using [Next.js](https://nextjs.org/), make sure to add `@electric-sql/pglite` to the `transpilePackages` array in `next.config.js`:

```js
const nextConfig = {
  swcMinify: false,
  transpilePackages: [
    '@electric-sql/pglite-react', // Optional
    '@electric-sql/pglite',
  ],
}

export default nextConfig
```

---

## File: docs/docs/filesystems.md

# Filesystems

PGlite has a virtual file system layer that allows it to run in environments that don't traditionally have filesystem access.

PGlite VFSs are under active development, and we plan to extend the range of options in future, as well as make it easy for users to create their own filesystems.

We would recommend using the IndexedDB VFS in the browser at the current time as the OPFS VFS is not supported by Safari.

## In-memory FS

The in-memory FS is the default when starting PGlite, and it is available on all platforms. All files are kept in memory and there is no persistence, other than calling [`pg.dumpDataDir()`](./api.md#dumpdatadir) and then using the [`loadDataDir`](./api.md#options) option at start.

To use the in-memory FS you can use one of these methods:

- Don't provide a `dataDir` option
  ```ts
  const pg = new PGlite()
  ```
- Set the `dataDir` to `memory://`
  ```ts
  const pg = new PGlite('memory://')
  ```
- Import and pass the FS explicitly
  ```ts
  import { MemoryFS } from '@electric-sql/pglite'
  const pg = new PGlite({
    fs: new MemoryFS(),
  })
  ```

### Platform Support

| Node | Bun | Deno | Chrome | Safari | Firefox |
| ---- | --- | ---- | ------ | ------ | ------- |
| ✓    | ✓   | ✓    | ✓      | ✓      | ✓       |

## Node FS

The Node FS uses the Node.js file system API to implement a VFS for PGLite. It is available in both Node and Bun.

To use the Node FS you can use one of these methods:

- Set the `dataDir` to a directory on your filesystem
  ```ts
  const pg = new PGlite('./path/to/datadir/')
  ```
- Import and pass the FS explicitly
  ```ts
  import { NodeFS } from '@electric-sql/pglite'
  const pg = new PGlite({
    fs: new NodeFS('./path/to/datadir/'),
  })
  ```

#### Platform Support

| Node | Bun | Deno | Chrome | Safari | Firefox |
| ---- | --- | ---- | ------ | ------ | ------- |
| ✓    | ✓   | ✓    |        |        |         |

## IndexedDB FS

The [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) FS persists the database to IndexedDB in the browser. It's a layer over the in-memory filesystem, loading all files for the database into memory on start, and flushing them to IndexedDB after each query if they have changed.

To use the IndexedDB FS you can use one of these methods:

- Set the `dataDir` with a `idb://` prefix, the database will be stored in an IndexedDB named with the path provided
  ```ts
  const pg = new PGlite('idb://my-database')
  ```
- Import and pass the FS explicitly
  ```ts
  import { IdbFs } from '@electric-sql/pglite'
  const pg = new PGlite({
    fs: new IdbFs('my-database'),
  })
  ```

The IndexedDB filesystem works at the file level, storing whole files (Postgres has a single file per table or index) as blobs in IndexedDB. Flushing whole files can take a few milliseconds after each query. To aid in building responsive apps we provide a `relaxedDurability` mode that can be [configured when starting](./api.md#options) PGlite. Under this mode, the results of a query are returned immediately, and the flush to IndexedDB is scheduled to occur asynchronously afterwards. Typically, this is immediately after the query returns with no delay.

### Platform Support

| Node | Bun | Deno | Chrome | Safari | Firefox |
| ---- | --- | ---- | ------ | ------ | ------- |
|      |     |      | ✓      | ✓      | ✓       |

## OPFS AHP FS

The OPFS AHP filesystem is built on top of the [Origin Private Filesystem](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) in the browser and uses an "access handle pool". It is only available when PGlite is run in a Web Worker, this could be any worker you configure. We provide a [Multi Tab Worker](./multi-tab-worker.md) to aid in using PGlite from multiple tabs in the browser.

To use the OPFS AHP FS you can use one of these methods:

- Set the `dataDir` to a directory within the origins OPFS
  ```ts
  const pg = new PGlite('opfs-ahp://path/to/datadir/')
  ```
- Import and pass the FS explicitly
  ```ts
  import { OpfsAhpFS } from '@electric-sql/pglite/opfs-ahp'
  const pg = new PGlite({
    fs: new OpfsAhpFS('./path/to/datadir/'),
  })
  ```

### Platform Support

| Node | Bun | Deno | Chrome | Safari | Firefox |
| ---- | --- | ---- | ------ | ------ | ------- |
|      |     |      | ✓      |        | ✓       |

Unfortunately, Safari appears to have a limit of 252 open sync access handles, this prevents this VFS from working due to a standard Postgres install consisting of over 300 files.

### What is an "access handle pool"?

The Origin Private Filesystem API provides both asynchronous and synchronous methods, but the synchronous methods are limited to read, write and flush. You are unable to traverse the filesystem or open files synchronously. PGlite is a fully synchronous WASM build of Postgres and unable to call async APIs while handling a query. While it is possible to build an async WASM Postgres using [Asyncify](https://emscripten.org/docs/porting/asyncify.html), it adds significant overhead in both file size and performance.

To overcome these limitations, and to provide a fully synchronous file system to PGlite on top of OPFS, we use something called an "access handle pool". When you first start PGlite we open a pool of OPFS access handles with randomised file names; these are then allocated to files as needed. After each query, a pool maintenance job is scheduled that maintains its size. When you inspect the OPFS directory where the database is stored, you will not see the normal Postgres directory layout, but rather a pool of files and a state file containing the directory tree mapping along with file metadata.

The PGlite OPFS AHP FS is inspired by the [wa-sqlite](https://github.com/rhashimoto/wa-sqlite) access handle pool file system by [Roy Hashimoto](https://github.com/rhashimoto).

---

## File: docs/docs/index.md

# Getting started with PGlite

PGlite can be used in both Node/Bun/Deno or the browser, and with any JavaScript framework.

## Install and start in Node/Bun/Deno

Install into your project:

::: code-group

```bash [npm]
npm install @electric-sql/pglite
```

```bash [pnpm]
pnpm install @electric-sql/pglite
```

```bash [yarn]
yarn add @electric-sql/pglite
```

```bash [bun]
bun install @electric-sql/pglite
```

```bash [deno]
deno add npm:@electric-sql/pglite
```

:::

To use the in-memory Postgres:

```js
import { PGlite } from '@electric-sql/pglite'

const db = new PGlite()
```

or to persist to the native filesystem:

```js
const db = new PGlite('./path/to/pgdata')
```

## Install and start in the browser

It can be installed and imported using your usual package manager:

```js
import { PGlite } from '@electric-sql/pglite'
```

or using a CDN such as JSDeliver:

```js
import { PGlite } from 'https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js'
```

Then for an in-memory Postgres:

```js
const db = new PGlite()
```

or to persist the database to IndexedDB:

```js
const db = new PGlite('idb://my-pgdata')
```

## Making a query

There are two methods for querying the database, `.query` and `.exec`. The former supports parameters, while the latter supports multiple statements.

First, let's create a table and insert some test data using the `.exec` method:

```js
await db.exec(`
  CREATE TABLE IF NOT EXISTS todo (
    id SERIAL PRIMARY KEY,
    task TEXT,
    done BOOLEAN DEFAULT false
  );
  INSERT INTO todo (task, done) VALUES ('Install PGlite from NPM', true);
  INSERT INTO todo (task, done) VALUES ('Load PGlite', true);
  INSERT INTO todo (task, done) VALUES ('Create a table', true);
  INSERT INTO todo (task, done) VALUES ('Insert some data', true);
  INSERT INTO todo (task) VALUES ('Update a task');
`)
```

The `.exec` method is perfect for migrations and batch inserts with raw SQL.

Now, let's retrieve an item using `.query` method:

```js
const ret = await db.query(`
  SELECT * from todo WHERE id = 1;
`)
console.log(ret.rows)
```

Output:

```js
;[
  {
    id: 1,
    task: 'Install PGlite from NPM',
    done: false,
  },
]
```

## Using parameterised queries

When working with user supplied values, it's always best to use parameterized queries; these are supported on the `.query` method.

We can use this to update a task:

```js
const ret = await db.query(
  'UPDATE todo SET task = $2, done = $3 WHERE id = $1',
  [5, 'Update a task using parameterized queries', true],
)
```

## What next?

- To learn more about [querying](./api.md#query) and [transactions](./api.md#transaction) along with the other methods and options available, you can read the main [PGlite API documentation](./api.md).

- There is also a [live-query extension](./live-queries.md) that enables reactive queries to update a UI when the underlying database changes.

- PGlite has a number of built-in [virtual file systems](./filesystems.md) to provide persistence for your database.

- There are [framework hooks](./framework-hooks/react.md) to make working with PGlite within React and Vue much easier with less boilerplate.

- For help configuring PGlite with your bundler, see the [bundler support](./bundler-support.md) page.

- As PGlite only has a single exclusive connection to the database, we provide a [multi-tab worker](./multi-tab-worker.md) to enable sharing a PGlite instance between multiple browser tabs.

- There is a [REPL component](./repl.md) that can be easily embedded into a web-app to aid in debugging and development, or as part of a database application itself.

- We maintain a [list of ORMs and query builders](./orm-support.md) that support PGlite.

- PGlite supports both Postgres extensions and PGlite Plugins via its [extensions API](./api.md#options-extensions), and there is a list of [supported extensions](../extensions/).

- We have a [page of examples](../examples.md) that you can open to test out PGlite in the browser.

---

## File: docs/docs/live-queries.md

# Live Queries

The "live" extension enables you to subscribe to a query and receive updated results when the underlying tables change.

To use the extension, it needs to be added to the PGlite instance when creating it:

```ts
import { PGlite } from '@electric-sql/pglite'
import { live } from '@electric-sql/pglite/live'

const pg = await PGlite.create({
  extensions: {
    live,
  },
})
```

There are three methods on the `live` namespace:

- `live.query()` for basic live queries. With less machinery in PGlite, it's quicker for small results sets and narrow rows.
- `live.incrementalQuery()` for incremental queries. It materialises the full result set on each update from only the changes emitted by the `live.changes` API. Perfect for feeding into React, and with good performance for large result sets and wide rows.
- `live.changes()` a lower level API that emits the changes (insert/update/delete) that can then be mapped to mutations in a UI or other datastore.

## live.query

`live.query<T>()`

This is very similar to a standard query, but takes an additional callback that receives the results whenever they change:

```js
const ret = pg.live.query('SELECT * FROM test ORDER BY rand;', [], (res) => {
  // res is the same as a standard query result object
})
```

You can also pass an options object with `offset` and `limit` to implement efficient windowed queries:

```js
const ret = pg.live.query({
  query: 'SELECT * FROM test ORDER BY rand;',
  offset: 0,
  limit: 10,
  callback: (res) => {
    // res includes:
    // - rows: the rows for the current window
    // - offset: the current offset
    // - limit: the current limit
    // - totalCount: total number of rows in the full result set
  },
})
```

The windowed query is optimized to:

- Only transfer the rows within the requested window
- Efficiently update when rows enter/leave the window
- Track the total count separately to avoid slow COUNT(\*) queries blocking updates

You can update the window position by calling refresh with new offset/limit values:

```js
// Move to page 2
await ret.refresh(10, 10)
```

The returned value from the call is a Promise of an object with this interface:

```ts
interface LiveQueryReturn<T> {
  initialResults: Results<T> & {
    offset?: number // Current offset (if windowed)
    limit?: number // Current limit (if windowed)
    totalCount?: number // Total row count (if windowed)
  }
  unsubscribe: () => Promise<void>
  refresh: (options?: { offset?: number; limit?: number }) => Promise<void>
}
```

- `initialResults` is the initial results set (also sent to the callback)
- `unsubscribe` allows you to unsubscribe from the live query
- `refresh` allows you to force a refresh of the query with the updated results sent to the callback. For windowed queries you can optionally provide new offset/limit values.

Internally it watches the tables that the query depends on, and reruns the query whenever they are changed.

## live.incrementalQuery

`live.incrementalQuery<T>()`

Similar to above, but maintains a temporary table of the previous state inside of Postgres. When the tables it depends on change, the query is re-run and diffed with the last state. Only the changes from the last version of the query are copied from WASM into JS.

It requires an additional `key` argument - the name of a column (often a primary key) on which to key the diff.

```ts
const ret = pg.live.incrementalQuery(
  'SELECT * FROM test ORDER BY rand;',
  [],
  'id',
  (res) => {
    // res is the same as a standard query result object
  },
)
```

The returned value is of the same type as the `query` method above.

## live.changes

`live.changes()`

A lower-level API which is the backend for the `incrementalQuery`, it emits the changes that have occurred. It requires a `key` on which to compare row differences:

```ts
const ret = pg.live.changes(
  'SELECT * FROM test ORDER BY rand;',
  [],
  'id',
  (res) => {
    // res is a change result object
  },
)
```

The call returns a Promise of an object defined by this interface:

```ts
interface LiveChangesReturn<T = { [key: string]: any }> {
  fields: { name: string; dataTypeID: number }[]
  initialChanges: Array<Change<T>>
  unsubscribe: () => Promise<void>
  refresh: () => Promise<void>
}
```

The results passed to the callback are an array of `Change` objects:

```ts
type ChangeInsert<T> = {
  __changed_columns__: string[]
  __op__: 'INSERT'
  __after__: number
} & T

type ChangeDelete<T> = {
  __changed_columns__: string[]
  __op__: 'DELETE'
  __after__: undefined
} & T

type ChangeUpdate<T> = {
  __changed_columns__: string[]
  __op__: 'UPDATE'
  __after__: number
} & T

type Change<T> = ChangeInsert<T> | ChangeDelete<T> | ChangeUpdate<T>
```

Each `Change` includes the new values along with:

- `__changed_columns__` the column names that were changed.
- `__op__` the operation that is required to update the state (`INSERT`, `UPDATE`, `DELETE`).
- `__after__` the `key` of the row after which _this_ row should be positioned; it will be included in `__changed_columns__` if it has been changed. This allows for very efficient moves within an ordered set of results.

This API can be used to implement very efficient in-place DOM updates.

---

## File: docs/docs/multi-tab-worker.md

# Multi-tab Worker

It's likely that you will want to run PGlite in a Web Worker so that it doesn't block the main thread. Additionally, as PGlite is single connection only, you may want to proxy multiple browser tabs to a single PGlite instance.

To aid in this, we provide a `PGliteWorker` with the same API as the standard PGlite, and a `worker` wrapper that exposes a PGlite instance to other tabs.

## Using PGliteWorker

First, you need to create a js file for your worker instance. You use the `worker` wrapper with an `init` option that returns a PGlite instance to start that database and expose it to all tabs:

```js
// my-pglite-worker.js
import { PGlite } from '@electric-sql/pglite'
import { worker } from '@electric-sql/pglite/worker'

worker({
  async init() {
    // Create and return a PGlite instance
    return new PGlite()
  },
})
```

Then connect the `PGliteWorker` to your new worker process in your main script:

```js
import { PGliteWorker } from '@electric-sql/pglite/worker'

const pg = new PGliteWorker(
  new Worker(new URL('./my-pglite-worker.js', import.meta.url), {
    type: 'module',
  }),
)

// `pg` has the same interface as a standard PGlite interface
```

Internally, this starts a worker for each tab, but then runs an a election to nominate one as the leader. Only the leader then starts PGlite by calling the `init` function, and handles all queries. When the leader tab is closed, a new election is run, and a new PGlite instance is started.

In addition to having all the standard methods of the [`PGlite` interface](./api.md), `PGliteWorker` also has the following methods and properties:

- `onLeaderChange(callback: () => void)`
  This allows you to subscribe to a notification when the leader worker is changed. It returns an unsubscribe function.
- `offLeaderChange(callback: () => void)`
  This allows you to unsubscribe from the leader change notification.
- `isLeader: bool`
  A boolean property indicating if this instance is the leader.

## Passing options to a worker

`PGliteWorker` takes an optional second parameter `options`; this can include any standard [PGlite options](./api.md#options) along with these additional options:

- `id: string`
  This is an optional `id` to group your PGlite workers. The leader election is run between all `PGliteWorker`s with the same `id`.
  If not provided, the url to the worker is concatenated with the `dataDir` option to create an id.
- `meta: any`
  Any additional metadata you would like to pass to the worker process `init` function.

The `worker()` wrapper takes a single options argument, with a single `init` property. `init` is a function that takes any options passed to `PGliteWorker`, excluding extensions, and returns a `PGlite` instance. You can use the options passed to decide how to configure your instance:

```js
// my-pglite-worker.js
import { PGlite } from '@electric-sql/pglite'
import { worker } from '@electric-sql/pglite/worker'

worker({
  async init(options) {
    const meta = options.meta
    // Do something with additional metadata.
    // or even run your own code in the leader along side the PGlite
    return new PGlite({
      dataDir: options.dataDir,
    })
  },
})

// my-app.js
import { PGliteWorker } from '@electric-sql/pglite/worker'

const pg = new PGliteWorker(
  new Worker(new URL('./my-pglite-worker.js', import.meta.url), {
    type: 'module',
  }),
  {
    dataDir: 'idb://my-db',
    meta: {
      // additional metadata passed to `init`
    },
  },
)
```

## Extension support

`PGliteWorker` has support for both Postgres extensions and PGlite plugins using the normal [extension api](./api.md#optionsextensions).

Any extension can be used by the PGlite instance inside the worker, however the extensions namespace is not exposed on a connecting `PGliteWorker` on the main thread.

```js
// my-pglite-worker.js
import { PGlite } from '@electric-sql/pglite'
import { worker } from '@electric-sql/pglite/worker'
import { vector } from '@electric-sql/pglite-pgvector'

worker({
  async init() {
    return new PGlite({
      extensions: {
        vector,
      },
    })
  },
})
```

Extensions that only use the PGlite plugin interface, such as live queries, can be used on the main thread with `PGliteWorker` to expose their functionality; this is done by providing a standard options object as a second argument to the `PGliteWorker` constructor:

```js
import { PGliteWorker } from '@electric-sql/pglite/worker'
import { live } from '@electric-sql/pglite/live'

const pg = new PGliteWorker(
  new Worker(new URL('./my-pglite-worker.js', import.meta.url), {
    type: 'module',
  }),
  {
    extensions: {
      live,
    },
  },
)
```

`PGliteWorker` also has a `create` static method that resolves to a new instance when it is fully initiated. This also adds the correct types for any extensions to the `PGliteWorker` instance:

```ts
import { PGliteWorker } from '@electric-sql/pglite/worker'
import { live } from '@electric-sql/pglite/live'

const pg = await PGliteWorker.create(
  new Worker(new URL('./my-pglite-worker.js', import.meta.url), {
    type: 'module',
  }),
  {
    extensions: {
      live,
    },
  },
)

// TypeScript is aware of the `pg.live` namespace:
pg.live.query(/* ... */)
```