This commit is contained in:
2025-10-05 13:52:22 +02:00
commit 63929c2dce
65 changed files with 10729 additions and 0 deletions

1
package/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

184
package/CHANGELOG.md Normal file
View File

@@ -0,0 +1,184 @@
# astro-pocketbase
## 0.11.0
### Minor Changes
- middleware is externalized in src/lib/pocketbase/middleware.ts
## 0.10.5
### Patch Changes
- update zod-pocketbase
## 0.10.4
### Patch Changes
- avoid the need of path in tsconfig.json for locals declaration
## 0.10.3
### Patch Changes
- update zod-pocketbase
## 0.10.2
### Patch Changes
- c258a34: update zod-pocketbase
## 0.10.1
### Patch Changes
- c558cce: upgrade zod-pocketbase
## 0.10.0
### Minor Changes
- add zod-pocketbase to exports
## 0.9.0
### Minor Changes
- remove magic "pocketbase:astro" and generate schemas and loader directly in "src/lib/pocketbase"
- refactor with zod-pocketbase
## 0.8.0
### Minor Changes
- 0c3e99c: add type and function helpers
### Patch Changes
- 0c3e99c: change default naming from Model to Record as pocketbase calls it
## 0.7.0
### Minor Changes
- 23c9e7f: remove eleventy fetch in favoi of pocketbase sdk and refine the way content is updated
### Patch Changes
- 23c9e7f: secure toolbar app use in astro v4
## 0.6.1
### Patch Changes
- a981658: fix toolbar app that was called during routing with ClientRouter
## 0.6.0
### Minor Changes
- c1027d0: add cacheDir option for fetching
## 0.5.6
### Patch Changes
- 5e7a9ef: Fix empty relation case
## 0.5.5
### Patch Changes
- 98693e2: fix enum type
## 0.5.4
### Patch Changes
- f72ee99: refine select field schema with enum
- f72ee99: pass collection name to stringigyFieldSchema
## 0.5.3
### Patch Changes
- 88e1af6: fix enum property name
## 0.5.2
### Patch Changes
- fc843fe: fix options schema
## 0.5.1
### Patch Changes
- 8dbbc6c: fix enum naming function used
## 0.5.0
### Minor Changes
- fba429f: add naming enum options
## 0.4.0
### Minor Changes
- cee8216: add ignore option
## 0.3.1
### Patch Changes
- 756b35e: fix loader id for refreshContent
## 0.3.0
### Minor Changes
- 257edb1: add a dev toolbar app to clear cache and refresh collections
## 0.2.3
### Patch Changes
- 120e5f5: remove useless code
- 9b462a1: add RecordRef type
## 0.2.2
### Patch Changes
- 295f9ee: add Collection type
## 0.2.1
### Patch Changes
- aeeb739: add eleventy-fetch as peer dependency
## 0.2.0
### Minor Changes
- bfe58e2: replace pocketbase sdk in the loader by eleventy fetch to ease caching
- c993dff: add cache duration option and disable cache in production
### Patch Changes
- b05e991: change astro reference to zod transform
- b05e991: correct schema type for dates
- f91a9ae: use input instead of output schema types for pocketbase sdk type
- f7c285f: replace biome by eslint and prettier
- f91a9ae: replace zod date coercion by transform to respect pocketbase sdk types
- f1e177a: correct date schema
## 0.1.0
### Minor Changes
- df2b603: project initialisation

17
package/README.md Normal file
View File

@@ -0,0 +1,17 @@
# `astro-pocketbase`
This is an [Astro integration](https://docs.astro.build/en/guides/integrations-guide/) that ease the use of PocketBase in your Astro projects
## Usage
To see how to get started, check out the [docs](https://astro-pocketbase-five.vercel.app)
## Licensing
[MIT Licensed](https://github.com/gbouteiller/astro-pocketbase/blob/main/LICENSE). Made with ❤️ by [Gregory Bouteiller](https://github.com/gbouteiller).
## Acknowledgements
- [`astro-integration-kit`](https://github.com/florian-lefebvre/astro-integration-kit) by Florian Lefebvre
- [`pocketbase`](https://github.com/pocketbase/js-sdk) by Gani Georgiev

14
package/assets/env.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
import type { TypedPocketbase } from "../../../src/lib/pocketbase/schemas";
import type { helpersFrom } from "astro-pocketbase";
declare global {
namespace App {
interface Locals {
pocketbase: TypedPocketbase;
getRecord: ReturnType<typeof helpersFrom>["getRecord"];
getRecords: ReturnType<typeof helpersFrom>["getRecords"];
}
}
}
export {};

57
package/assets/loader.ts Normal file
View File

@@ -0,0 +1,57 @@
// This file was automatically generated by Astro PocketBase.
import type { Collection, TypedPocketbase } from "./schemas";
import type { LoaderContext } from "astro/loaders";
import Pocketbase, { type AdminAuthResponse } from "pocketbase";
let pocketbase: TypedPocketbase;
let auth: Promise<AdminAuthResponse>;
let isAuthenticating = false;
export function pocketbaseLoader({ collection }: PocketbaseLoaderOptions) {
return {
name: "pocketbase-loader",
load: async ({ store, logger, meta, parseData }: LoaderContext) => {
const { ASTRO_POCKETBASE_ADMIN_EMAIL, ASTRO_POCKETBASE_ADMIN_PASSWORD, PUBLIC_ASTRO_POCKETBASE_URL } = import.meta.env;
if (!ASTRO_POCKETBASE_ADMIN_EMAIL || !ASTRO_POCKETBASE_ADMIN_PASSWORD || !PUBLIC_ASTRO_POCKETBASE_URL)
return logger.error("Environment variables not set");
logger.info(`Loading ${collection}`);
if (!pocketbase) pocketbase = new Pocketbase(PUBLIC_ASTRO_POCKETBASE_URL);
try {
if (!isAuthenticating && !pocketbase.authStore.isValid) {
isAuthenticating = true;
auth = pocketbase.admins.authWithPassword(ASTRO_POCKETBASE_ADMIN_EMAIL, ASTRO_POCKETBASE_ADMIN_PASSWORD);
}
await auth;
const lastUpdatedItems = await pocketbase
.collection(collection)
.getList(1, 1, { fields: "updated", skipTotal: true, sort: "updated", order: "desc" });
const lastUpdated = lastUpdatedItems.items[0]?.updated;
if (lastUpdated !== meta.get("last-updated")) {
logger.info(`Refreshing ${collection}`);
meta.set("last-updated", lastUpdated);
const items = await pocketbase.collection(collection).getFullList();
for (const { id, updated, ...rest } of items) {
const data = await parseData({ id, data: { id, updated, ...rest } });
store.set({ data, digest: updated, id });
}
}
logger.info(`Loaded ${collection}`);
} catch (error) {
logger.error(`Error fetching ${collection}: ${error}`);
return;
}
},
};
}
export type PocketbaseLoaderOptions = {
collection: Collection;
};

View File

@@ -0,0 +1,17 @@
// This file was automatically generated by Astro PocketBase.
import { defineMiddleware } from "astro:middleware";
import { helpersFrom } from "astro-pocketbase";
import PocketBase from "pocketbase";
const middleware = defineMiddleware((context, next) => {
const pocketbase = new PocketBase(import.meta.env.PUBLIC_ASTRO_POCKETBASE_URL);
const { getRecord, getRecords } = helpersFrom({ pocketbase });
context.locals.pocketbase = pocketbase;
context.locals.getRecord = getRecord;
context.locals.getRecords = getRecords;
return next();
});
// You should NOT change the exported name as it is used by the Astro PocketBase integration.
export { middleware as onRequest };

17
package/assets/toolbar.ts Normal file
View File

@@ -0,0 +1,17 @@
import { defineToolbarApp } from "astro/toolbar";
export default defineToolbarApp({
init(_canvas, app, server) {
let pending = false;
// const button = document.querySelector("astro-dev-toolbar")?.shadowRoot.querySelector("button[data-app-id='astro-pocketbase']");
app.onToggled(({ state }) => {
if (!state) return;
app.toggleNotification({ level: "error", state: true });
if (pending) return;
pending = true;
server.send("astro-pocketbase:refresh", undefined);
});
},
});

1
package/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="astro/client" />

54
package/package.json Normal file
View File

@@ -0,0 +1,54 @@
{
"name": "astro-pocketbase",
"version": "0.11.0",
"description": "Astro integration to ease the use of PocketBase in your Astro projects",
"author": {
"email": "gregory.bouteiller@niama.re",
"name": "Gregory Bouteiller",
"url": "https://github.com/gbouteiller"
},
"license": "MIT",
"keywords": [
"astro-integration",
"astro-component",
"withastro",
"astro",
"pocketbase"
],
"homepage": "https://github.com/gbouteiller/astro-pocketbase",
"publishConfig": {
"access": "public"
},
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": [
"dist",
"assets"
],
"scripts": {
"dev": "tsup --watch",
"build": "tsup"
},
"type": "module",
"peerDependencies": {
"astro": "^4.15.1",
"pocketbase": "<0.22.0",
"zod": "^3.23.8"
},
"dependencies": {
"astro-integration-kit": "^0.18.0",
"dotenv": "^16.4.7",
"es-toolkit": "^1.30.1",
"zod-pocketbase": "^0.5.0"
},
"devDependencies": {
"pocketbase": "<0.22.0",
"tsup": "^8.3.5",
"zod": "^3.24.1"
}
}

4
package/src/index.ts Normal file
View File

@@ -0,0 +1,4 @@
import { integration } from "./integration.js";
export * from "zod-pocketbase";
export default integration;

View File

@@ -0,0 +1,75 @@
import { createResolver, defineIntegration } from "astro-integration-kit";
import dotenv from "dotenv";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
import type { CollectionModel } from "pocketbase";
import { Config, Credentials, defaultConfig, fetchCollections } from "zod-pocketbase";
import { generate } from "zod-pocketbase/server";
dotenv.config();
export const integration = defineIntegration({
name: "astro-pocketbase",
optionsSchema: Config.omit({ adminEmail: true, adminPassword: true, output: true, url: true }).default(defaultConfig),
setup({ options }) {
const { resolve } = createResolver(import.meta.url);
let collections: CollectionModel[] = [];
return {
hooks: {
"astro:config:setup": async (params) => {
const { addDevToolbarApp, addMiddleware, config, logger } = params;
const { srcDir } = config;
const {
ASTRO_POCKETBASE_ADMIN_EMAIL: adminEmail,
ASTRO_POCKETBASE_ADMIN_PASSWORD: adminPassword,
PUBLIC_ASTRO_POCKETBASE_URL: url,
} = process.env;
try {
const output = `${srcDir.pathname}lib/pocketbase/schemas.ts`;
const config = Config.parse({ ...options, adminEmail, adminPassword, url, output });
const credentials = Credentials.parse(config);
const allCollections = await fetchCollections(credentials);
collections = allCollections.filter(({ name }) => !config.ignore.includes(name));
await generate(collections, config);
} catch (error) {
logger.error(error instanceof Error ? error.message : "unknown error");
}
if (!existsSync(new URL("lib/pocketbase/loader.ts", srcDir))) {
const loaderContent = readFileSync(resolve("../assets/loader.ts"), "utf-8");
writeFileSync(new URL("lib/pocketbase/loader.ts", srcDir), loaderContent);
}
if (!existsSync(new URL("lib/pocketbase/middleware.ts", srcDir))) {
const middlewareContent = readFileSync(resolve("../assets/middleware.ts"), "utf-8");
writeFileSync(new URL("lib/pocketbase/middleware.ts", srcDir), middlewareContent);
}
addMiddleware({ entrypoint: new URL("lib/pocketbase/middleware.ts", srcDir), order: "pre" });
addDevToolbarApp({
id: "astro-pocketbase",
name: "Astro PocketBase",
icon: `<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>PocketBase</title><path fill="currentColor" d="M5.684 12a.632.632 0 0 1-.631-.632V4.421c0-.349.282-.632.631-.632h2.37c.46 0 .889.047 1.287.139.407.084.758.23 1.053.44.303.202.541.475.715.82.173.335.26.75.26 1.246 0 .479-.092.894-.273 1.247a2.373 2.373 0 0 1-.715.869 3.11 3.11 0 0 1-1.053.503c-.398.11-.823.164-1.273.164h-.46a.632.632 0 0 0-.632.632v1.52a.632.632 0 0 1-.632.631Zm1.279-4.888c0 .349.283.632.632.632h.343c1.04 0 1.56-.437 1.56-1.31 0-.428-.135-.73-.404-.907-.26-.176-.645-.264-1.156-.264h-.343a.632.632 0 0 0-.632.631Zm6.3 13.098a.632.632 0 0 1-.631-.631v-6.947a.63.63 0 0 1 .631-.632h2.203c.44 0 .845.034 1.216.1.38.06.708.169.984.328.276.16.492.37.647.63.164.26.246.587.246.982 0 .185-.03.37-.09.554a1.537 1.537 0 0 1-.26.516 1.857 1.857 0 0 1-1.076.7.031.031 0 0 0-.023.03c0 .015.01.028.025.03.591.111 1.04.32 1.346.626.311.31.466.743.466 1.297 0 .42-.082.78-.246 1.083a2.153 2.153 0 0 1-.685.755 3.4 3.4 0 0 1-1.036.441 5.477 5.477 0 0 1-1.268.139zm1.271-5.542c0 .349.283.631.632.631h.21c.465 0 .802-.088 1.009-.264.207-.176.31-.424.31-.743 0-.302-.107-.516-.323-.642-.207-.135-.535-.202-.984-.202h-.222a.632.632 0 0 0-.632.632Zm0 3.463c0 .349.283.631.632.631h.39c1.019 0 1.528-.369 1.528-1.108 0-.36-.125-.621-.376-.78-.241-.16-.625-.24-1.152-.24h-.39a.632.632 0 0 0-.632.632zM1.389 0C.629 0 0 .629 0 1.389V15.03a1.4 1.4 0 0 0 1.389 1.39H8.21a.632.632 0 0 0 .63-.632.632.632 0 0 0-.63-.63H1.389c-.078 0-.125-.05-.125-.128V1.39c0-.078.047-.125.125-.125H15.03c.078 0 .127.047.127.125v6.82a.632.632 0 0 0 .631.63.632.632 0 0 0 .633-.63V1.389A1.4 1.4 0 0 0 15.032 0ZM15.79 7.578a.632.632 0 0 0-.632.633.632.632 0 0 0 .631.63h6.822c.078 0 .125.05.125.128V22.61c0 .078-.047.125-.125.125H8.97c-.077 0-.127-.047-.127-.125v-6.82a.632.632 0 0 0-.631-.63.632.632 0 0 0-.633.63v6.822A1.4 1.4 0 0 0 8.968 24h13.643c.76 0 1.389-.629 1.389-1.389V8.97a1.4 1.4 0 0 0-1.389-1.39Z"/></svg>`,
entrypoint: resolve("../assets/toolbar.ts"),
});
},
"astro:config:done": ({ injectTypes }) => {
const content = readFileSync(resolve("../assets/env.d.ts"), "utf-8");
injectTypes({ filename: "env.d.ts", content });
},
//@ts-ignore
"astro:server:setup": ({ refreshContent, toolbar }) => {
toolbar.on("astro-pocketbase:refresh", async () => {
if (!refreshContent) console.warn("Content can only be refreshed in Astro v5.0.0 or higher");
await refreshContent?.({ loaders: ["pocketbase-loader"] });
});
},
},
};
},
});

9
package/tsconfig.json Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": "astro/tsconfigs/strictest",
"compilerOptions": {
"module": "Node16",
"moduleResolution": "Node16",
"jsx": "preserve"
},
"exclude": ["dist"]
}

19
package/tsup.config.ts Normal file
View File

@@ -0,0 +1,19 @@
import { defineConfig } from "tsup";
import { peerDependencies } from "./package.json";
export default defineConfig((options) => {
const dev = !!options.watch;
return {
entry: ["src/**/*.(ts|js)"],
format: ["esm"],
target: "node18",
bundle: true,
dts: true,
sourcemap: true,
clean: true,
splitting: false,
minify: !dev,
external: [...Object.keys(peerDependencies)],
tsconfig: "tsconfig.json",
};
});