init
This commit is contained in:
21
doc/.gitignore
vendored
Normal file
21
doc/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
4
doc/.vscode/extensions.json
vendored
Normal file
4
doc/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
doc/.vscode/launch.json
vendored
Normal file
11
doc/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
doc/CHANGELOG.md
Normal file
13
doc/CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# doc
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- remove "cache" option for "helpersFrom" and replace it with "fetch" to allow a more generic custom fetch and remove "@11ty/eleventy-fetch" dependency
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- update packages
|
||||
55
doc/README.md
Normal file
55
doc/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Starlight Starter Kit: Basics
|
||||
|
||||
[](https://starlight.astro.build)
|
||||
|
||||
```
|
||||
npm create astro@latest -- --template starlight
|
||||
```
|
||||
|
||||
[](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)
|
||||
[](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)
|
||||
[](https://app.netlify.com/start/deploy?repository=https://github.com/withastro/starlight&create_from_path=examples/basics)
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro + Starlight project, you'll see the following folders and files:
|
||||
|
||||
```
|
||||
.
|
||||
├── public/
|
||||
├── src/
|
||||
│ ├── assets/
|
||||
│ ├── content/
|
||||
│ │ ├── docs/
|
||||
│ │ └── config.ts
|
||||
│ └── env.d.ts
|
||||
├── astro.config.mjs
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
|
||||
|
||||
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
|
||||
|
||||
Static assets, like favicons, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
|
||||
29
doc/astro.config.mjs
Normal file
29
doc/astro.config.mjs
Normal file
@@ -0,0 +1,29 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import starlight from "@astrojs/starlight";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
starlight({
|
||||
title: "Zod PocketBase",
|
||||
social: {
|
||||
github: "https://github.com/gbouteiller/zod-pocketbase",
|
||||
},
|
||||
sidebar: [
|
||||
{
|
||||
label: "Start here",
|
||||
autogenerate: { directory: "start-here" },
|
||||
},
|
||||
{
|
||||
label: "Guides",
|
||||
autogenerate: { directory: "guides" },
|
||||
},
|
||||
{
|
||||
label: "Reference",
|
||||
autogenerate: { directory: "reference" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
19
doc/package.json
Normal file
19
doc/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "doc",
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/starlight": "^0.30.3",
|
||||
"astro": "^5.1.1",
|
||||
"sharp": "^0.33.5",
|
||||
"typescript": "^5.7.2"
|
||||
}
|
||||
}
|
||||
5217
doc/pnpm-lock.yaml
generated
Normal file
5217
doc/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
doc/public/favicon.svg
Normal file
1
doc/public/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><path fill-rule="evenodd" d="M81 36 64 0 47 36l-1 2-9-10a6 6 0 0 0-9 9l10 10h-2L0 64l36 17h2L28 91a6 6 0 1 0 9 9l9-10 1 2 17 36 17-36v-2l9 10a6 6 0 1 0 9-9l-9-9 2-1 36-17-36-17-2-1 9-9a6 6 0 1 0-9-9l-9 10v-2Zm-17 2-2 5c-4 8-11 15-19 19l-5 2 5 2c8 4 15 11 19 19l2 5 2-5c4-8 11-15 19-19l5-2-5-2c-8-4-15-11-19-19l-2-5Z" clip-rule="evenodd"/><path d="M118 19a6 6 0 0 0-9-9l-3 3a6 6 0 1 0 9 9l3-3Zm-96 4c-2 2-6 2-9 0l-3-3a6 6 0 1 1 9-9l3 3c3 2 3 6 0 9Zm0 82c-2-2-6-2-9 0l-3 3a6 6 0 1 0 9 9l3-3c3-2 3-6 0-9Zm96 4a6 6 0 0 1-9 9l-3-3a6 6 0 1 1 9-9l3 3Z"/><style>path{fill:#000}@media (prefers-color-scheme:dark){path{fill:#fff}}</style></svg>
|
||||
|
After Width: | Height: | Size: 696 B |
BIN
doc/src/assets/cli.png
Normal file
BIN
doc/src/assets/cli.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
doc/src/assets/houston.webp
Normal file
BIN
doc/src/assets/houston.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
6
doc/src/content/config.ts
Normal file
6
doc/src/content/config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { defineCollection } from 'astro:content';
|
||||
import { docsSchema } from '@astrojs/starlight/schema';
|
||||
|
||||
export const collections = {
|
||||
docs: defineCollection({ schema: docsSchema() }),
|
||||
};
|
||||
71
doc/src/content/docs/guides/cli.mdx
Normal file
71
doc/src/content/docs/guides/cli.mdx
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: CLI
|
||||
description: The CLI generates Zod schemas for your PocketBase instance.
|
||||
---
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
import { Image } from 'astro:assets';
|
||||
import cliImage from '../../../assets/cli.png';
|
||||
|
||||
The CLI generates Zod schemas for your PocketBase instance.
|
||||
|
||||
## Usage
|
||||
|
||||
To generate your schemas, run the following from your project directory:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="npm">
|
||||
```shell
|
||||
npx zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="pnpm">
|
||||
```shell
|
||||
pnpm zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="yarn">
|
||||
```shell
|
||||
yarn zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<Image src={cliImage} alt="Zod PocketBase CLI." />
|
||||
|
||||
## Config File
|
||||
|
||||
You can provide a `zod-pocketbase.config.ts` or `zod-pocketbase.config.js` config file.
|
||||
|
||||
```ts title="zod-pocketbase.config.ts"
|
||||
import type { Config } from "zod-pocketbase";
|
||||
|
||||
export default {
|
||||
// default values
|
||||
adminEmail: undefined,
|
||||
adminPassword: undefined,
|
||||
ignore: [],
|
||||
nameEnum: (name: string) => snakeCase(name).toUpperCase(),
|
||||
nameEnumField: (collectionName: string, fieldName: string) => `${collectionName}${pascalCase(fieldName)}`,
|
||||
nameEnumSchema: (name: string) => pascalCase(name),
|
||||
nameEnumType: (name: string) => pascalCase(name),
|
||||
nameEnumValues: (name: string) => `${name}Values`,
|
||||
nameRecordSchema: (name: string) => `${pascalCase(name)}Record`,
|
||||
nameRecordType: (name: string) => `${pascalCase(name)}Record`,
|
||||
output: "./zod-pocketbase.ts",
|
||||
url: undefined,
|
||||
} satisfies Config;
|
||||
```
|
||||
:::tip[For more details]
|
||||
See the [reference](/reference/config).
|
||||
:::
|
||||
|
||||
|
||||
## Environment variables
|
||||
|
||||
You can provide your admin credentials and your **PocketBase** instance url as environment variables:
|
||||
|
||||
```shell
|
||||
ZOD_POCKETBASE_ADMIN_EMAIL="admin@mydomain.com"
|
||||
ZOD_POCKETBASE_ADMIN_PASSWORD="mypassword"
|
||||
ZOD_POCKETBASE_URL="https://myproject.pockethost.io"
|
||||
```
|
||||
52
doc/src/content/docs/guides/helpers.md
Normal file
52
doc/src/content/docs/guides/helpers.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Helpers
|
||||
description: Helpers are syntactic sugar to get records from your PocketBase instance.
|
||||
---
|
||||
|
||||
Helpers are syntactic sugar to get records from your PocketBase instance. In addition to simplifying the process of fetching records, they also provide type safety, autocompletion and caching.
|
||||
|
||||
To access helpers, you have to, at least, provide an instance of PocketBase SDK to the `helpersFrom` function.
|
||||
|
||||
```ts
|
||||
import Fetch from "@11ty/eleventy-fetch";
|
||||
import { helpersFrom } from "zod-pocketbase";
|
||||
import Pocketbase from "pocketbase";
|
||||
|
||||
const { getRecord, getRecords } = helpersFrom({
|
||||
pocketbase: new Pocketbase(import.meta.env.ZOD_POCKETBASE_URL),
|
||||
fetch: async (url, fetchOptions) => {
|
||||
const { body, ...init } = await Fetch(url, { fetchOptions, returnType: "response", type: "json" });
|
||||
return new Response(JSON.stringify(body), init);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## getRecord
|
||||
|
||||
`getRecord` is a helper to get a single record from your PocketBase instance. It takes a [RecordRef](/reference/types#recordref) as its first argument and an object with a schema as its second argument.
|
||||
|
||||
```ts
|
||||
const myFirstPost = await getRecord({ collection: "posts", slug: "my-first-post" }, { schema: PostRecord });
|
||||
const mySecondPost = await getRecord({ collection: "posts", id: "3vwc4g9d23orc1r" }, { schema: PostRecord });
|
||||
```
|
||||
|
||||
:::tip[For more details]
|
||||
See the [reference](/reference/methods#getrecord).
|
||||
:::
|
||||
|
||||
## getRecords
|
||||
|
||||
`getRecords` is a helper to get multiple records from your PocketBase instance. It takes a collection name as its first argument and an object with some options (at least a record schema) as its second argument.
|
||||
|
||||
```ts
|
||||
const { items: myPosts } = await getRecords("posts", { schema: PostRecord });
|
||||
const { items: someSpecificPosts } = await getRecords("posts", { schema: PostRecord, sort: "-updated", page: 2, perPage: 10 });
|
||||
```
|
||||
|
||||
:::caution
|
||||
The provided schema is only for a record. The method returns a [RecordsList](/reference/types/#recordslist) object.
|
||||
:::
|
||||
|
||||
:::tip[For more details]
|
||||
See the [reference](/reference/methods#getrecords).
|
||||
:::
|
||||
121
doc/src/content/docs/guides/schemas.md
Normal file
121
doc/src/content/docs/guides/schemas.md
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Schemas
|
||||
description: How to secure and simplify schemas for your PocketBase instance.
|
||||
---
|
||||
|
||||
Here you can find how to secure and simplify schemas for your PocketBase instance:
|
||||
|
||||
1. with the CLI generated schemas
|
||||
2. with the `expand` method
|
||||
3. with the `pick` method
|
||||
4. with the `select` method
|
||||
|
||||
## 0 - A Zod schema for a Post collection
|
||||
|
||||
Here is the original schema for a `Post` collection:
|
||||
|
||||
```ts
|
||||
const Post = z
|
||||
.object({
|
||||
content: z.string(),
|
||||
expand: z.object({
|
||||
author: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
expand: z.object({
|
||||
image: z.object({
|
||||
alt: z.string(),
|
||||
src: z.string(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand })),
|
||||
image: z.object({
|
||||
alt: z.string(),
|
||||
src: z.string(),
|
||||
}),
|
||||
}),
|
||||
title: z.string(),
|
||||
updated: z.coerce.date(),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
```
|
||||
|
||||
## 1 - With CLI generated schema
|
||||
|
||||
All schemas are generated from your PocketBase.
|
||||
|
||||
```ts
|
||||
import {AuthorRecord, ImageRecord, PostRecord} from "./schemas";
|
||||
|
||||
const Post = PostRecord.pick({ content: true, title: true, updated: true })
|
||||
.extend({
|
||||
expand: z.object({
|
||||
author: AuthorRecord.pick({ name: true })
|
||||
.extend({
|
||||
expand: z.object({
|
||||
image: ImageRecord.pick({ alt: true, src: true }),
|
||||
}),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand })),
|
||||
image: ImageRecord.pick({ alt: true, src: true }),
|
||||
}),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
```
|
||||
|
||||
## 2 - With pick
|
||||
|
||||
`pick` is a syntactic sugar for native zod `pick`.
|
||||
|
||||
```ts
|
||||
import { pick } from "zod-pocketbase";
|
||||
import {AuthorRecord, ImageRecord, PostRecord} from "./schemas";
|
||||
|
||||
const Post = pick(PostRecord, ["content", "title", "updated"])
|
||||
.extend({
|
||||
expand: z.object({
|
||||
author: pick(AuthorRecord, ["name"])
|
||||
.extend({
|
||||
expand: z.object({
|
||||
image: pick(ImageRecord, ["alt", "src"]),
|
||||
}),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand })),
|
||||
image: pick(ImageRecord, ["alt", "src"]),
|
||||
}),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
```
|
||||
|
||||
## 3 - With expand
|
||||
|
||||
`expand` is a syntactic sugar for native zod `extend` on the property `expand` coupled with `transform`.
|
||||
|
||||
```ts
|
||||
import { expand, pick } from "zod-pocketbase";
|
||||
import {AuthorRecord, ImageRecord, PostRecord} from "./schemas";
|
||||
|
||||
const Post = expand(pick(PostRecord, ["content", "title", "updated"]), {
|
||||
author: expand(pick(AuthorRecord, ["name"]), {
|
||||
image: pick(ImageRecord, ["alt", "src"])
|
||||
}),
|
||||
image: pick(ImageRecord, ["alt", "src"])
|
||||
});
|
||||
```
|
||||
|
||||
## 4 - With select
|
||||
|
||||
`select` is the union of `pick` and `expand`.
|
||||
|
||||
```ts
|
||||
import { select } from "zod-pocketbase";
|
||||
import {AuthorRecord, ImageRecord, PostRecord} from "./schemas";
|
||||
|
||||
const Post = select(PostRecord, ["content", "title", "updated"], {
|
||||
author: select(AuthorRecord, ["name"], {
|
||||
image: select(ImageRecord, ["alt", "src"])
|
||||
}),
|
||||
image: select(ImageRecord, ["alt", "src"])
|
||||
});
|
||||
```
|
||||
30
doc/src/content/docs/index.mdx
Normal file
30
doc/src/content/docs/index.mdx
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: Welcome to Zod PocketBase
|
||||
description: This library adds Zod safety with ease to your PocketBase instance.
|
||||
template: splash
|
||||
hero:
|
||||
tagline: Add Zod to PocketBase with ease!
|
||||
image:
|
||||
file: ../../assets/houston.webp
|
||||
actions:
|
||||
- text: Why this library?
|
||||
link: /start-here/why/
|
||||
icon: right-arrow
|
||||
- text: View on GitHub
|
||||
link: https://github.com/gbouteiller/zod-pocketbase
|
||||
icon: external
|
||||
variant: minimal
|
||||
---
|
||||
|
||||
import { CardGrid, Card } from '@astrojs/starlight/components';
|
||||
|
||||
## Also check out...
|
||||
|
||||
<CardGrid stagger>
|
||||
<Card title="Astro PocketBase" icon="puzzle">
|
||||
Add [PocketBase to Astro](https://astro-pocketbase-five.vercel.app) with ease!
|
||||
</Card>
|
||||
<Card title="Astro Superforms" icon="puzzle">
|
||||
Add [Superforms to Astro](https://astro-superforms.vercel.app) with ease!
|
||||
</Card>
|
||||
</CardGrid>
|
||||
122
doc/src/content/docs/reference/config.md
Normal file
122
doc/src/content/docs/reference/config.md
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Config
|
||||
description: A reference for the Config file.
|
||||
---
|
||||
|
||||
## ignore
|
||||
|
||||
- **Type:** `string[]`
|
||||
- **Default:** `[]`
|
||||
|
||||
The `ignore` option allows you to ignore specific collections from being processed.
|
||||
|
||||
```ts
|
||||
{
|
||||
ignore: ["users"],
|
||||
}
|
||||
```
|
||||
|
||||
## adminEmail
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
|
||||
`adminEmail` represents the email of an admin user of your PocketBase instance.
|
||||
|
||||
```ts
|
||||
{
|
||||
adminEmail: "admin@mydomain.com",
|
||||
}
|
||||
```
|
||||
|
||||
## adminPassword
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
|
||||
`adminPassword` represents the password of the above admin user of your PocketBase instance.
|
||||
|
||||
```ts
|
||||
{
|
||||
adminPassword: "mypassword",
|
||||
}
|
||||
```
|
||||
|
||||
## nameEnum
|
||||
|
||||
- **Type:** `(enumFieldName: string) => string`
|
||||
- **Default:** `(enumFieldName) => snakeCase(enumFieldName).toUpperCase()`
|
||||
|
||||
`nameEnum` is a function that takes an enum field name and returns the name of the generated enum.
|
||||
|
||||
## nameEnumField
|
||||
|
||||
- **Type:** `(collectionName: string, fieldName: string) => string`
|
||||
- **Default:** `(collectionName, fieldName) => collectionName + pascalName(fieldName)`
|
||||
|
||||
`nameEnumField` is a function that takes a field name and its collection name and returns the name of the generated enum field.
|
||||
|
||||
## nameEnumSchema
|
||||
|
||||
- **Type:** `(enumFieldName: string) => string`
|
||||
- **Default:** `(enumFieldName) => pascalName(enumFieldName)`
|
||||
|
||||
`nameEnumSchema` is a function that takes an enum field name and returns the name of the generated enum schema.
|
||||
|
||||
## nameEnumType
|
||||
|
||||
- **Type:** `(enumFieldName: string) => string`
|
||||
- **Default:** `(enumFieldName) => pascalName(enumFieldName)`
|
||||
|
||||
`nameEnumType` is a function that takes an enum field name and returns the name of the generated enum type.
|
||||
|
||||
## nameEnumValues
|
||||
|
||||
- **Type:** `(enumFieldName: string) => string`
|
||||
- **Default:** `(enumFieldName) => enumFieldName + "Values"`
|
||||
|
||||
`nameEnumValues` is a function that takes an enum field name and returns the name of the generated enum values.
|
||||
|
||||
## nameRecordSchema
|
||||
|
||||
- **Type:** `(collectionName: string) => string`
|
||||
- **Default:** `(collectionName) => pascalName(collectionName) + "Record"`
|
||||
|
||||
`nameRecordSchema` is a function that takes a collection name and returns the name of the generated record schema.
|
||||
|
||||
## nameRecordType
|
||||
|
||||
- **Type:** `(collectionName: string) => string`
|
||||
- **Default:** `(collectionName) => pascalName(collectionName) + "Record"`
|
||||
|
||||
`nameRecordType` is a function that takes a collection name and returns the name of the generated record type.
|
||||
|
||||
## output
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `./zod-pocketbase.ts`
|
||||
|
||||
`output` represents the path of the generated file.
|
||||
|
||||
```ts
|
||||
{
|
||||
output: "./src/lib/pocketbase/schemas.ts",
|
||||
}
|
||||
```
|
||||
|
||||
:::caution
|
||||
`output` must be a relative path.
|
||||
:::
|
||||
|
||||
## url
|
||||
|
||||
- **Type:** `string`
|
||||
- **Default:** `undefined`
|
||||
|
||||
`url` represents the url of your PocketBase instance.
|
||||
|
||||
```ts
|
||||
{
|
||||
url: "https://myproject.pockethost.io",
|
||||
}
|
||||
```
|
||||
88
doc/src/content/docs/reference/methods.md
Normal file
88
doc/src/content/docs/reference/methods.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Methods
|
||||
description: A reference for the methods.
|
||||
---
|
||||
|
||||
## helpersFrom
|
||||
|
||||
```ts
|
||||
import { helpersFrom } from "zod-pocketbase";
|
||||
|
||||
const helpers = helpersFrom({ fetch, pocketbase });
|
||||
```
|
||||
|
||||
The `helpersFrom` method returns an object with two methods: `getRecord` and `getRecords` described below.
|
||||
|
||||
### fetch
|
||||
|
||||
- **Type:** `(url: RequestInfo | URL, config?: RequestInit) => Promise<Response>`
|
||||
|
||||
Optional custom fetch function to use for sending the request.
|
||||
|
||||
### pocketbase
|
||||
|
||||
- **Required**
|
||||
- **Type:** `TypedPocketbase`
|
||||
|
||||
The `pocketbase` parameter is a mandatory parameter that specifies a PocketBase instance.
|
||||
|
||||
## getRecord
|
||||
|
||||
```ts
|
||||
const { getRecord } = helpersFrom({ pocketbase });
|
||||
const record = await getRecord(reference, { schema });
|
||||
```
|
||||
|
||||
The `getRecord` method returns a single record from your PocketBase instance.
|
||||
|
||||
### reference
|
||||
|
||||
- **Required**
|
||||
- **Type:** [`RecordRef`](/reference/types#recordref)
|
||||
|
||||
### schema
|
||||
|
||||
- **Required**
|
||||
- **Type:** [`AnyZodRecord`](/reference/types#anyzodrecord)
|
||||
|
||||
## getRecords
|
||||
|
||||
```ts
|
||||
const { getRecords } = helpersFrom({ pocketbase });
|
||||
const recordsList = await getRecords(collection, { filter, page, perPage, schema, skipTotal, sort });
|
||||
```
|
||||
|
||||
The `getRecords` method returns a records list from your PocketBase instance.
|
||||
|
||||
### collection
|
||||
|
||||
- **Required**
|
||||
- **Type:** `string`
|
||||
|
||||
### filter
|
||||
|
||||
- **Type:** `string`
|
||||
|
||||
### page
|
||||
|
||||
- **Type:** `number`
|
||||
- **Default:** `1`
|
||||
|
||||
### perPage
|
||||
|
||||
- **Type:** `number`
|
||||
- **Default:** `200`
|
||||
|
||||
### schema
|
||||
|
||||
- **Required**
|
||||
- **Type:** [`AnyZodRecord`](/reference/types#anyzodrecord)
|
||||
|
||||
### skipTotal
|
||||
|
||||
- **Type:** `boolean`
|
||||
- **Default:** `true`
|
||||
|
||||
### sort
|
||||
|
||||
- **Type:** [`ZodRecordSort`](/reference/types#zodrecordsort)
|
||||
80
doc/src/content/docs/reference/types.md
Normal file
80
doc/src/content/docs/reference/types.md
Normal file
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Types
|
||||
description: A reference for the types.
|
||||
---
|
||||
|
||||
## RecordIdRef
|
||||
|
||||
`RecordIdRef` represents a reference to a record in a collection by its `id` and its `collection` name.
|
||||
|
||||
```ts
|
||||
type RecordIdRef<C extends string> = { collection: C; id: string };
|
||||
```
|
||||
|
||||
## RecordSlugRef
|
||||
|
||||
`RecordSlugRef` represents a reference to a record in a collection by its `slug` and its `collection` name.
|
||||
|
||||
```ts
|
||||
type RecordSlugRef<C extends string> = { collection: C; slug: string };
|
||||
```
|
||||
|
||||
:::tip[What is slug?]
|
||||
`slug` is not a default field of a collection but it can be used as another unique identifier for a record more readble than its `id`.
|
||||
:::
|
||||
|
||||
## RecordRef
|
||||
|
||||
`RecordRef` represents either a `RecordIdRef` or a `RecordSlugRef`.
|
||||
|
||||
```ts
|
||||
type RecordRef<C extends string> = RecordIdRef<C> | RecordSlugRef<C>;
|
||||
```
|
||||
|
||||
## RecordFullListOpts
|
||||
|
||||
```ts
|
||||
type RecordFullListOpts<S extends AnyZodRecord> = RecordListOpts<S> & { batch?: number };
|
||||
```
|
||||
|
||||
## RecordListOpts
|
||||
|
||||
```ts
|
||||
type RecordListOpts<S extends AnyZodRecord> = {
|
||||
filter?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
skipTotal?: boolean;
|
||||
sort?: ZodRecordSort<S>;
|
||||
};
|
||||
```
|
||||
|
||||
## AnyZodRecord
|
||||
|
||||
`AnyZodRecord` represents the type for a record schema.
|
||||
|
||||
```ts
|
||||
type AnyZodRecord = AnyZodObject | ZodEffects<AnyZodObject>;
|
||||
```
|
||||
|
||||
## ZodRecordSort
|
||||
|
||||
`ZodRecordSort` represents the type for a record sort options.
|
||||
|
||||
```ts
|
||||
export type ZodRecordSort<S extends AnyZodRecord> = `${"+" | "-"}${ZodRecordMainKeys<S>}` | "@random";
|
||||
```
|
||||
|
||||
## RecordsList
|
||||
|
||||
`RecordsList` represents the type for a records list.
|
||||
|
||||
```ts
|
||||
export type RecordsList<S extends AnyZodRecord> = {
|
||||
items: S["_output"][];
|
||||
page: number;
|
||||
perPage: number;
|
||||
totalItems: number;
|
||||
totalPages: number;
|
||||
};
|
||||
```
|
||||
32
doc/src/content/docs/start-here/getting-started.mdx
Normal file
32
doc/src/content/docs/start-here/getting-started.mdx
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
title: Getting started
|
||||
description: This library adds Zod safety with ease to your PocketBase instance.
|
||||
---
|
||||
import { Tabs, TabItem } from '@astrojs/starlight/components';
|
||||
|
||||
This library adds Zod safety with ease to your PocketBase instance. It provides you with:
|
||||
- a CLI to generate schemas for your selected collections
|
||||
- methods to make pocketbase SDK typesafe
|
||||
- helpers to simplify the way you fetch your collections
|
||||
|
||||
## Installation
|
||||
|
||||
To install `zod-pocketbase`, run the following from your project directory:
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="npm">
|
||||
```shell
|
||||
npm install zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="pnpm">
|
||||
```shell
|
||||
pnpm add zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem label="yarn">
|
||||
```shell
|
||||
yarn install zod-pocketbase
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
202
doc/src/content/docs/start-here/why.md
Normal file
202
doc/src/content/docs/start-here/why.md
Normal file
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Why this library?
|
||||
description: A guide in my new Starlight docs site.
|
||||
---
|
||||
|
||||
You want to get the last 10 updated posts from your PocketBase collection. So you start with...
|
||||
|
||||
## The PocketBase SDK
|
||||
|
||||
```ts
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
const options = {
|
||||
sort: "-updated"
|
||||
};
|
||||
|
||||
const { items: firstPosts } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
```
|
||||
|
||||
Oops, you forgot to expand the `author` field because you don't want the author id but the author name. So you...
|
||||
|
||||
## Expand
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
const options = {
|
||||
sort: "-updated",
|
||||
+ expand: ["author"],
|
||||
};
|
||||
|
||||
const { items: firstPosts } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
```
|
||||
|
||||
Great, but now you want to validate the data you get from PocketBase because the rule is that you should not trust anything for the outer world.
|
||||
You also just include the fields that you really need for your app and remove the ugly `expand` properties. So you add...
|
||||
|
||||
## A Zod Schema
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
+ const Post = z
|
||||
+ .object({
|
||||
+ content: z.string(),
|
||||
+ expand: z.object({
|
||||
+ author: z.object({
|
||||
+ name: z.string(),
|
||||
+ }),
|
||||
+ }),
|
||||
+ title: z.string(),
|
||||
+ })
|
||||
+ .transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
|
||||
const options = {
|
||||
sort: "-updated",
|
||||
expand: ["author"],
|
||||
};
|
||||
|
||||
const { items } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
+ const firstPosts = Post.array().parse(items);
|
||||
```
|
||||
|
||||
Better, but even if you get your validated structured data now, you could reduce the size of the response from the server by adding...
|
||||
|
||||
## Fields
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
const Post = z
|
||||
.object({
|
||||
content: z.string(),
|
||||
expand: z.object({
|
||||
author: z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
}),
|
||||
title: z.string(),
|
||||
})
|
||||
.transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
|
||||
const options = {
|
||||
sort: "-updated",
|
||||
expand: ["author"],
|
||||
+ fields: ["content", "expand.author.name", "title"],
|
||||
};
|
||||
|
||||
const { items } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
const firstPosts = Post.array().parse(items);
|
||||
```
|
||||
|
||||
Cool! So what would you need Zod PocketBase for?
|
||||
|
||||
## Generated Schemas
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
+ import { pick, select } from "zod-pocketbase";
|
||||
+ import { AuthorRecord, PostRecord } from "./schemas";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
- const Post = z
|
||||
- .object({
|
||||
- content: z.string(),
|
||||
- expand: z.object({
|
||||
- author: z.object({
|
||||
- name: z.string(),
|
||||
- }),
|
||||
- }),
|
||||
- title: z.string(),
|
||||
- })
|
||||
- .transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
|
||||
+ const Post = PostRecord.pick({content: true, title: true }).extend({
|
||||
+ expand: z.object({
|
||||
+ author: AuthorRecord.pick({ name: true })
|
||||
+ }),
|
||||
+ }).transform(({ expand, ...rest }) => ({ ...rest, ...expand }));
|
||||
|
||||
/* Or, for the syntactic sugar addicts */
|
||||
+ const Post = select(PostRecord, ["content", "title"], {
|
||||
+ author: select(AuthorRecord, ["name"])
|
||||
+ });
|
||||
|
||||
const options = {
|
||||
sort: "-updated",
|
||||
expand: ["author"],
|
||||
fields: ["content", "expand.author.name", "title"],
|
||||
};
|
||||
|
||||
const { items } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
const firstPosts = Post.array().parse(items);
|
||||
```
|
||||
|
||||
## Type-safe expand and fields options
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
import { select } from "zod-pocketbase";
|
||||
+ import { expandFrom, fieldsFrom, listOptionsFrom } from "zod-pocketbase";
|
||||
import { AuthorRecord, PostRecord } from "./schemas";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
|
||||
const Post = select(PostRecord, ["content", "title"], {
|
||||
author: select(AuthorRecord, ["name"])
|
||||
});
|
||||
|
||||
const options = {
|
||||
sort: "-updated",
|
||||
- expand: ["author"],
|
||||
+ expand: expandFrom(Post),
|
||||
- fields: ["content", "expand.author.name", "title"],
|
||||
+ fields: fieldsFrom(Post),
|
||||
};
|
||||
|
||||
/* Or, for the syntactic sugar addicts */
|
||||
+ const options = listOptionsFrom(Post, { sort: "-updated" });
|
||||
|
||||
const { items } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
const firstPosts = Post.array().parse(items);
|
||||
```
|
||||
|
||||
## More sugar with helpers
|
||||
|
||||
```diff lang="ts"
|
||||
import PocketBase from "pocketbase";
|
||||
import { z } from "zod";
|
||||
import { select } from "zod-pocketbase";
|
||||
+ import { helpersFrom } from "zod-pocketbase";
|
||||
import { AuthorRecord, PostRecord } from "./schemas";
|
||||
|
||||
const pocketbase = new PocketBase("https://my-pocketbase.com");
|
||||
+ const { getRecords } = helpersFrom({ pocketbase });
|
||||
|
||||
const Post = select(PostRecord, ["content", "title"], {
|
||||
author: select(AuthorRecord, ["name"])
|
||||
});
|
||||
|
||||
- const options = optionsFrom(Post, { sort: "-updated" });
|
||||
+ const options = { perPage: 10, schema: Post, sort: "-updated" };
|
||||
|
||||
- const { items } = await pocketbase.collection("posts").getList(1, 10, options);
|
||||
- const firstPosts = Post.array().parse(items);
|
||||
+ const { items: firstPosts } = getRecords("posts", options);
|
||||
```
|
||||
2
doc/src/env.d.ts
vendored
Normal file
2
doc/src/env.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
3
doc/tsconfig.json
Normal file
3
doc/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict"
|
||||
}
|
||||
Reference in New Issue
Block a user