Skip to content

Commit

Permalink
chore: Add vue docs (#197)
Browse files Browse the repository at this point in the history
* Add both static and typed injection tools to vue

* Add Vue docs

* Fix dead links

* Fix style issues

* Fix vue docs with type

* Fix vue docs nits
  • Loading branch information
msfstef authored Aug 14, 2024
1 parent cbda36f commit 446c2df
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-coins-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@electric-sql/pglite-vue': patch
---

Include both utility for typed injection and static injectors.
5 changes: 4 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ export default defineConfig({
link: '/react',
base: '/docs/framework-hooks',
collapsed: true,
items: [{ text: 'React', link: '/react' }],
items: [
{ text: 'React', link: '/react' },
{ text: 'Vue', link: '/vue' },
],
},
{ text: 'Multi-tab Worker', link: '/docs/multi-tab-worker' },
{ text: 'REPL Component', link: '/docs/repl' },
Expand Down
149 changes: 149 additions & 0 deletions docs/docs/framework-hooks/vue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
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-react'
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-react'

This comment has been minimized.

Copy link
@pixelspark

pixelspark Aug 15, 2024

Shouldn't this import from '@electric-sql/pglite-vue' instead?

const db = injectPGlite()
const insertItem = () => {
db.query("INSERT INTO my_table (name, number) VALUES ('Arthur', 42);")
}
</script>
<template>
// ...
<button @click="insertItem">Insert item</button>
// ...
</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/vector'
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>,
): 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>
```

### 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>,
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 { useLiveInceremntalQuery } from '@electric-sql/pglite-vue'
const maxNumber = 100
const items = useLiveInceremntalQuery(
`
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>
```
11 changes: 8 additions & 3 deletions packages/pglite-react/test/provider.test-d.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expectTypeOf } from 'vitest'
import { PGlite } from '@electric-sql/pglite'
import { PGlite, PGliteInterfaceExtensions } from '@electric-sql/pglite'
import { live } from '@electric-sql/pglite/live'
import { vector } from '@electric-sql/pglite/vector'
import { makePGliteProvider } from '../src/provider.js'
Expand All @@ -18,8 +18,13 @@ describe('provider', () => {
},
})

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

// @ts-expect-error cannot pass db with just live extension
;() => <PGliteProvider db={dbLive}></PGliteProvider>
Expand Down
9 changes: 5 additions & 4 deletions packages/pglite-vue/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PGlite Vue.js Bindings
# PGlite Vue Bindings

This package implements Vue.js hooks for [PGLite](https://pglite.dev/) on top of the [live query plugin](https://pglite.dev/docs/live-queries). Full documentation is available at [pglite.dev/docs/framework-hooks](https://pglite.dev/docs/framework-hooks#react).
This package implements Vue hooks for [PGLite](https://pglite.dev/) on top of the [live query plugin](https://pglite.dev/docs/live-queries). Full documentation is available at [pglite.dev/docs/framework-hooks](https://pglite.dev/docs/framework-hooks#react).

To install:

Expand All @@ -10,7 +10,8 @@ npm install @electric-sql/pglite-vue

The hooks this package provides are:

- [proidePGlite](https://pglite.dev/docs/framework-hooks#providepglite): Provide a PGlite instance to all child components.
- [injectPGlite](https://pglite.dev/docs/framework-hooks#injectpglite): Retrieve the provided PGlite instance.
- [providePGlite](https://pglite.dev/docs/framework-hook/vuevye#providepglite): Provide a PGlite instance to all child components.
- [injectPGlite](https://pglite.dev/docs/framework-hooks/vue#injectpglite): Retrieve the provided PGlite instance.
- [makePGliteDependencyInjector](https://pglite.dev/docs/framework-hooks/vue#makepglitedependencyinjector): Utility to create a typed version of `providePGlite` and `injectPGlite`.
- [useLiveQuery](https://pglite.dev/docs/framework-hooks#uselivequery): Reactively receive results of a live query change
- [useLiveIncrementalQuery](https://pglite.dev/docs/framework-hooks#useliveincrementalquery): Reactively receive results of a live query change by offloading the diff to PGlite
2 changes: 1 addition & 1 deletion packages/pglite-vue/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@electric-sql/pglite-vue",
"version": "0.2.0",
"description": "Vue.js hooks for using PGlite",
"description": "Vue hooks for using PGlite",
"type": "module",
"private": false,
"publishConfig": {
Expand Down
15 changes: 4 additions & 11 deletions packages/pglite-vue/src/dependency-injection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const PGliteKey = Symbol('PGliteProvider')
* @returns An object with two functions: `providePGlite` and `injectPGlite`.
*
*/
export function makePGliteDependencyInjector<
function makePGliteDependencyInjector<
T extends PGliteWithLive,
>(): PGliteDependencyInjection<T> {
const providePGlite = (db: Ref<T | undefined> | (T | undefined)): void =>
Expand All @@ -42,14 +42,7 @@ export function makePGliteDependencyInjector<
}
}

/**
* This "static" injector is used internally by our reactive methods
* to get access to the provided PGlite instance.
* It loses information about the extensions present on PGlite,
* but we only need the `live` extension information for our methods.
* However, users preferably don't lose extension type information,
* therefore, they can use {@link makePGliteDependencyInjector}.
*/
const { injectPGlite: injectPGliteUntyped } = makePGliteDependencyInjector()
const { injectPGlite, providePGlite } =
makePGliteDependencyInjector<PGliteWithLive>()

export { injectPGliteUntyped }
export { makePGliteDependencyInjector, injectPGlite, providePGlite }
10 changes: 5 additions & 5 deletions packages/pglite-vue/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
isRef,
} from 'vue-demi'
import { Results } from '@electric-sql/pglite'
import { injectPGliteUntyped } from './dependency-injection'
import { injectPGlite } from './dependency-injection'

type UnsubscribeFn = () => Promise<void>
type QueryParams = unknown[] | undefined | null
Expand All @@ -25,8 +25,8 @@ function useLiveQueryImpl<T = { [key: string]: unknown }>(
query: string | WatchSource<string>,
params?: QueryParams | WatchSource<QueryParams>,
key?: string | WatchSource<string>,
): LiveQueryResults<T> | undefined {
const db = injectPGliteUntyped()!
): LiveQueryResults<T> {
const db = injectPGlite()!

const liveUpdate = shallowReactive<
| Omit<Results<T>, 'affectedRows'>
Expand Down Expand Up @@ -86,14 +86,14 @@ function useLiveQueryImpl<T = { [key: string]: unknown }>(
export function useLiveQuery<T = { [key: string]: unknown }>(
query: string | WatchSource<string>,
params?: QueryParams | WatchSource<QueryParams>,
): LiveQueryResults<T> | undefined {
): LiveQueryResults<T> {
return useLiveQueryImpl<T>(query, params)
}

export function useLiveIncrementalQuery<T = { [key: string]: unknown }>(
query: string | WatchSource<string>,
params: QueryParams | WatchSource<QueryParams>,
key: string | WatchSource<string>,
): LiveQueryResults<T> | undefined {
): LiveQueryResults<T> {
return useLiveQueryImpl<T>(query, params, key)
}
11 changes: 8 additions & 3 deletions packages/pglite-vue/test/injection.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @vitest-environment node
*/
import { describe, it, expectTypeOf } from 'vitest'
import { PGlite } from '@electric-sql/pglite'
import { PGlite, PGliteInterfaceExtensions } from '@electric-sql/pglite'
import { live } from '@electric-sql/pglite/live'
import { makePGliteDependencyInjector } from '../src'
import { vector } from '@electric-sql/pglite/vector'
Expand All @@ -21,8 +21,13 @@ describe('dependency injection', () => {
vector,
},
})
const { providePGlite, injectPGlite } =
makePGliteDependencyInjector<typeof dbLiveVector>()
const { providePGlite, injectPGlite } = makePGliteDependencyInjector<
PGlite &
PGliteInterfaceExtensions<{
live: typeof live
vector: typeof vector
}>
>()

// @ts-expect-error name is a string
providePGlite(dbLive)
Expand Down

0 comments on commit 446c2df

Please sign in to comment.