This repository has been archived by the owner on Nov 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Satoshi
committed
Dec 31, 2023
1 parent
fccb610
commit 65c3528
Showing
15 changed files
with
6,173 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Changesets | ||
|
||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works | ||
with multi-package repos, or single-package repos to help you version and publish your code. You can | ||
find the full documentation for it [in our repository](https://github.com/changesets/changesets) | ||
|
||
We have a quick list of common questions to get you started engaging with this project in | ||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json", | ||
"changelog": "@changesets/cli/changelog", | ||
"commit": false, | ||
"fixed": [], | ||
"linked": [], | ||
"access": "restricted", | ||
"baseBranch": "main", | ||
"updateInternalDependencies": "patch", | ||
"ignore": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
name: Publish to NPM | ||
|
||
on: | ||
release: | ||
types: [created] | ||
|
||
concurrency: ${{ github.workflow }}-${{ github.ref }} | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: pnpm/action-setup@v2 | ||
with: | ||
version: latest | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 20 | ||
cache: pnpm | ||
|
||
- run: pnpm install --frozen-lockfile | ||
|
||
- name: Create Release Pull Request | ||
id: changesets | ||
uses: changesets/action@v1 | ||
with: | ||
publish: pnpm run build | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Run tests using Vitest | ||
|
||
on: | ||
push: | ||
branches: | ||
- '**' | ||
pull_request: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: pnpm/action-setup@v2 | ||
with: | ||
version: latest | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 20 | ||
cache: pnpm | ||
|
||
- run: pnpm install --frozen-lockfile | ||
- run: pnpm run lint && pnpm run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"useTabs": true, | ||
"singleQuote": true, | ||
"trailingComma": "none", | ||
"printWidth": 100, | ||
"tabWidth": 4, | ||
"semi": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
# PocketBase Type-safe Option Builder | ||
|
||
Option builder for [PocketBase JavaScript SDK](https://github.com/pocketbase/js-sdk), that also helps with typing the response. | ||
|
||
This is how you would normally write options for the PocketBase SDK: | ||
```js | ||
{ | ||
expand: 'comments(post),tags', | ||
fields: 'id,title,expand.comments(post).user,expand.comments(post).message,expand.tags.id,expand.tags.name' | ||
} | ||
``` | ||
Writing options manually like this is very error-prone and hard to read/maintain. | ||
|
||
This option builder allows you to write it like this instead: | ||
```js | ||
{ | ||
key: 'posts', | ||
fields: ['id', 'title'], | ||
expand: [ | ||
{ key: 'comments(post)', fields: ['user', 'message'] }, | ||
{ key: 'tags', fields: ['id', 'name'] } | ||
] | ||
} | ||
``` | ||
It comes with autocomplete for `key`, `fields` and `expand` options, and also provides you a way to **type the response**. | ||
|
||
|
||
## Installation | ||
```sh | ||
npm install pb-option-builder | ||
``` | ||
|
||
## Usage | ||
|
||
### Defining schema and relations | ||
Below is an example of how you would define the schema for [this](https://pocketbase.io/docs/working-with-relations/) in the PocketBase docs. | ||
```ts | ||
interface PocketbaseCollection { | ||
id: string | ||
created: string | ||
updated: string | ||
} | ||
|
||
interface User extends PocketBaseCollection { | ||
name: string | ||
} | ||
|
||
interface Post extends PocketBaseCollection { | ||
title: string | ||
tags: Array<string> | ||
} | ||
|
||
interface Tag extends PocketBaseCollection { | ||
name: string | ||
} | ||
|
||
interface Comment extends PocketBaseCollection { | ||
post: string | ||
user: string | ||
message: string | ||
} | ||
|
||
// You need to use "type" instead of "interface" for these as interfaces are "mutable" | ||
// TypeScript needs to know the keys are guaranteed to be of type "string" | ||
type Schema = { | ||
// Table names as keys | ||
users: User | ||
posts: Post | ||
tags: Tag | ||
comments: Comment | ||
} | ||
|
||
type Relations = { | ||
// column names as keys | ||
post: Post | ||
user: User | ||
|
||
// if the relation is one-to-many or many-to-many, use Array<> | ||
tags: Array<Tag> | ||
|
||
// back-relations | ||
"posts(tags)": Array<Post> | ||
"comments(post)": Array<Comment> | ||
"comments(user)": Array<Comment> | ||
} | ||
``` | ||
### Initializing builder | ||
```ts | ||
import { initializeBuilder } from 'pb-option-builder' | ||
|
||
const optionBuilder = initializeBuilder<Schema, Relations>() | ||
``` | ||
|
||
### Building query | ||
```ts | ||
const [optionsObj, typeObj] = optionBuilder({ | ||
key: "posts", | ||
// you can specify fields to be returned in the response | ||
fields: ["id", "title", "tags"], | ||
expand: [ | ||
{ | ||
key: "tags", | ||
// returns all fields if it's not specified | ||
}, | ||
{ | ||
key: "comments(post)", | ||
// nesting "expand" is supported | ||
expand: [ | ||
{ | ||
key: "user", | ||
fields: ["name"] | ||
} | ||
] | ||
} | ||
] | ||
}) | ||
|
||
const result = await pb.collection('posts').getOne(optionsObj); | ||
``` | ||
|
||
### Typing response: | ||
|
||
The second item in the returned array (`typeObj` in the example above) is an empty object type cast as the type of the response. | ||
You can use it to type the response: | ||
|
||
```ts | ||
const result = await pb.collection('posts').getOne<typeof typeObj>(optionsObj); | ||
``` | ||
|
||
It's a bit hacky and not very pretty, but does the job. | ||
|
||
|
||
### Parameter type for `optionBuilder`: | ||
```ts | ||
{ | ||
// Table name as defined in "Schema" | ||
key: keyof Schema | ||
|
||
// Array of fields you want to be returned in the response | ||
fields?: Array<keyof Schema[key]> // defaults to all fields if not specified | ||
|
||
// Array of relations you want to be returned in the response | ||
expand?: Array<ExpandItem> | ||
|
||
// These will be passed to the SDK as is | ||
sort?: string | ||
filter?: string | ||
requestKey?: string | ||
} | ||
|
||
ExpandItem { | ||
// Relation name as defined in "Relations" | ||
key: keyof Relations | ||
|
||
fields?: // same as above | ||
expand?: // same as above | ||
} | ||
|
||
``` | ||
|
||
|
||
### Fields | ||
|
||
You might run into a situation where you have a component that requires a specific set of fields to be passed to it, and it makes sense to fetch the item directly in one route, but in another, it makes sense to do so through `expand`. | ||
|
||
|
||
Because of the way the argument for the option builder is structured, the `fields` array is portable. | ||
You can define the fields in one place, and use it either at the top level, or in the `expand` option **as is** . | ||
|
||
Example: | ||
|
||
```ts | ||
// CommentBlock.svelte | ||
export const commentFields = ["user", "message"] satisfies keyof Comment | ||
``` | ||
|
||
```ts | ||
// [comment]/+page.ts | ||
import { commentFields } from '$lib/CommentBlock.svelte' | ||
|
||
const [optionsObj, typeObj] = optionBuilder({ | ||
key: "comments", | ||
// you can use the imported fields here | ||
fields: commentFields | ||
}) | ||
``` | ||
|
||
```ts | ||
// [post]/+page.ts | ||
import { commentFields } from '$lib/CommentBlock.svelte' | ||
|
||
const [optionsObj, typeObj] = optionBuilder({ | ||
key: "posts", | ||
fields: ["id", "title", "tags"], | ||
expand: [ | ||
{ | ||
key: "comments(post)", | ||
// or here. No need to alter the imported fields | ||
fields: commentFields | ||
} | ||
] | ||
}) | ||
``` | ||
|
||
## Caveat: | ||
In order for back-relations to work, you need to have the forward-relations defined as well. | ||
```ts | ||
type Relations = { | ||
// This alone is not enough | ||
"comments(post)": Array<Comment> | ||
|
||
// You need to have this defined as well | ||
post: Post | ||
} | ||
|
||
const [optionsObj, typeObj] = optionBuilder({ | ||
key: "posts", | ||
expand: [ | ||
{ | ||
// Without "post: Post", TS will complain and you won't get autocomplete or typesafety | ||
key: "comments(post)", | ||
} | ||
] | ||
}) | ||
``` | ||
|
||
## Why not just integrate this into the SDK? | ||
- This way, you can start using this in existing projects without having to change anything. I think most of the time, you don't need to pass in any options to the SDK, so installing a new custom SDK for a very few instances where you need to seems like an overkill. | ||
- There are many functionalities of the official SDK that I don't use or understand fully, and I don't want to maintain a fork of it just for this. |
Oops, something went wrong.