11 Commits

17 changed files with 374 additions and 743 deletions

View File

@@ -1,9 +1,32 @@
# zod-pocketbase # zod-pocketbase-continued
Zod tooling for your Pocketbase instance. Zod tooling for your PocketBase instance.
To see how to get started, check out the [docs](https://zod-pocketbase.vercel.app) This repository is a continuation of [Gregory Bouteiller's zod-pocketbase](https://github.com/gbouteiller/zod-pocketbase), as the original repository was outdated for 10 months.
## Licensing ## Documentation
[MIT Licensed](./LICENSE). Made with ❤️ by [Gregory Bouteiller](https://github.com/gbouteiller). [Old documentation is compatible with the current version](https://zod-pocketbase.vercel.app)
## Compatibility
- **PocketBase**: v0.30.0
- **PocketBase JS SDK**: v0.26.2
## Changes from Original
### Project Structure
- Removed `doc/`, `playground/` and monorepo configuration
- Replaced `pnpm` and `node` in favor of `bun`
- Switched `.github/` to `.gitea/` and `gh` to `tea`
### Dependencies & Code
- Migrated from `tsup` to `tsdown`
- Updated all npm dependencies (except `zod`, which was only updated to the latest 3 version)
- Removed unused dependencies
- Fixed `getPocketbase` function in `sdk.ts` to match the latest PocketBase version
- Implemented most TODOs left in `content.ts`
## License
[MIT License](./LICENSE)

View File

@@ -33,6 +33,41 @@ export const RecordModel = z.object({
}); });
export type RecordModel = z.infer<typeof RecordModel>; export type RecordModel = z.infer<typeof RecordModel>;
/******* JSON FIELD *******/
export const pbJsonField = (maxSizeInBytes: number = 1048576) => {
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
const jsonSchema: z.ZodType<any> = z.lazy(() =>
z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])
);
const stringTransform = z.string()
.max(maxSizeInBytes, `JSON field cannot exceed ${maxSizeInBytes} bytes`)
.transform((val: string) => {
if (val === "true") return true;
if (val === "false") return false;
if (val === "null") return null;
if ((val.startsWith('[') && val.endsWith(']'))||(val.startsWith('{') && val.endsWith('}')))
try {
return JSON.parse(val);
} catch {
return val;
}
const num = Number(val);
if (!isNaN(num) && isFinite(num) && val.trim() !== '')
return num;
if (val.startsWith('"') && val.endsWith('"'))
return val.slice(1, -1);
return val;
});
return z.union([jsonSchema, stringTransform]);
};
/******* RECORDS *******/ /******* RECORDS *******/
@@_RECORDS_@@ @@_RECORDS_@@

714
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
description = "Zod-PocketBase development environment with Bun"; description = "Zod-PocketBase-Continue development environment with Bun";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
@@ -30,7 +30,7 @@
]; ];
shellHook = '' shellHook = ''
echo "🚀 Zod-PocketBase development environment (Bun-powered)" echo "🚀 Zod-PocketBase-Continue development environment (Bun-powered)"
echo "Bun version: $(bun --version)" echo "Bun version: $(bun --version)"
echo "Tea version: $(tea --version)" echo "Tea version: $(tea --version)"
echo "" echo ""
@@ -52,7 +52,7 @@
}; };
packages.default = pkgs.stdenv.mkDerivation { packages.default = pkgs.stdenv.mkDerivation {
name = "zod-pocketbase"; name = "zod-pocketbase-continue";
src = ./.; src = ./.;
buildInputs = [ bun ]; buildInputs = [ bun ];

View File

@@ -1,7 +1,7 @@
{ {
"name": "zod-pocketbase", "name": "zod-pocketbase-continue",
"version": "0.5.0", "version": "0.5.0",
"description": "", "description": "Zod tooling for your PocketBase instance.",
"author": { "author": {
"email": "garandplg@garandplg.com", "email": "garandplg@garandplg.com",
"name": "Garand_PLG", "name": "Garand_PLG",
@@ -16,7 +16,7 @@
"type generation", "type generation",
"zod" "zod"
], ],
"homepage": "https://gitea.garandplg.com/GarandPLG/zod-pocketbase", "homepage": "https://gitea.garandplg.com/GarandPLG/zod-pocketbase-continue",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -28,7 +28,7 @@
}, },
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
"zod-pocketbase": "dist/server/cli.js" "zod-pocketbase-continue": "dist/server/cli.js"
}, },
"exports": { "exports": {
".": { ".": {
@@ -45,8 +45,8 @@
"assets" "assets"
], ],
"scripts": { "scripts": {
"dev": "tsup --watch", "dev": "tsdown --watch",
"build": "tsup", "build": "tsdown",
"changeset": "changeset", "changeset": "changeset",
"release": "bun scripts/release.mjs" "release": "bun scripts/release.mjs"
}, },
@@ -54,20 +54,16 @@
"@clack/prompts": "^0.11.0", "@clack/prompts": "^0.11.0",
"c12": "^3.3.0", "c12": "^3.3.0",
"citty": "^0.1.6", "citty": "^0.1.6",
"es-toolkit": "^1.39.10" "es-toolkit": "^1.40.0"
}, },
"devDependencies": { "devDependencies": {
"@changesets/cli": "^2.29.7", "@changesets/cli": "^2.29.7",
"@types/node": "^24.6.2", "@types/node": "^24.7.1",
"@typescript-eslint/parser": "^8.45.0", "@typescript-eslint/parser": "^8.46.0",
"eslint": "^9.37.0", "eslint": "^9.37.0",
"eslint-plugin-astro": "^1.3.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"pocketbase": "^0.26.2", "pocketbase": "^0.26.2",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-astro": "^0.14.1", "tsdown": "^0.15.6",
"prettier-plugin-tailwindcss": "^0.6.14",
"tsup": "^8.5.0",
"zod": "^3.25.76" "zod": "^3.25.76"
}, },
"peerDependencies": { "peerDependencies": {

View File

@@ -1,6 +1,4 @@
/** @type {import("prettier").Config} */ /** @type {import("prettier").Config} */
export default { export default {
printWidth: 140, printWidth: 140,
plugins: ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
overrides: [{ files: "*.astro", options: { parser: "astro" } }],
}; };

View File

@@ -13,7 +13,7 @@ export const defaultConfig = {
nameEnumValues: (name: string) => `${name}Values`, nameEnumValues: (name: string) => `${name}Values`,
nameRecordSchema: (name: string) => `${pascalCase(name)}Record`, nameRecordSchema: (name: string) => `${pascalCase(name)}Record`,
nameRecordType: (name: string) => `${pascalCase(name)}Record`, nameRecordType: (name: string) => `${pascalCase(name)}Record`,
output: "./zod-pocketbase.ts", output: "./zod-pocketbase-continue.ts",
}; };
/** /**

View File

@@ -1,6 +1,6 @@
import { sortBy } from "es-toolkit"; import { sortBy } from "es-toolkit";
import type { CollectionModel, CollectionField } from "pocketbase"; import type { CollectionModel, CollectionField } from "pocketbase";
import type { GenerateOpts } from "./server/utils.ts"; import type { GenerateOpts } from "@/server/utils.ts";
export function stringifyContent(collections: CollectionModel[], opts: GenerateOpts) { export function stringifyContent(collections: CollectionModel[], opts: GenerateOpts) {
function getCollectionSelectFields() { function getCollectionSelectFields() {
@@ -9,7 +9,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
.filter((field: CollectionField) => field.type === "select") .filter((field: CollectionField) => field.type === "select")
.map((field: CollectionField) => ({ .map((field: CollectionField) => ({
name: opts.nameEnumField(collection.name, field.name), name: opts.nameEnumField(collection.name, field.name),
values: ((field as any).options?.values ?? []) as string[], values: ((field as any).values ?? []) as string[],
})), })),
); );
} }
@@ -26,17 +26,7 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
const schemaName = opts.nameRecordSchema(name); const schemaName = opts.nameRecordSchema(name);
const typeName = opts.nameRecordType(name); const typeName = opts.nameRecordType(name);
// Filter out system fields that are already inherited from RecordModel or should be omitted const systemFields = new Set(["id", "created", "updated", "collectionId", "collectionName", "expand", "password", "tokenKey"]);
const systemFields = new Set([
"id",
"created",
"updated",
"collectionId",
"collectionName",
"expand", // inherited from BaseModel/RecordModel
"password",
"tokenKey", // should not be in response schema
]);
const customFields = fields.filter((field) => !systemFields.has(field.name)); const customFields = fields.filter((field) => !systemFields.has(field.name));
const fieldStrings = sortBy(customFields, ["name"]).map((field) => stringifyField(field, name)); const fieldStrings = sortBy(customFields, ["name"]).map((field) => stringifyField(field, name));
@@ -44,125 +34,6 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
return `export const ${schemaName} = z.object({\n\t...RecordModel.omit({ expand: true }).shape,\n\tcollectionName: z.literal("${name}"),\n\t${fieldStrings.join(",\n\t")},\n});\nexport type ${typeName} = z.infer<typeof ${schemaName}>;`; return `export const ${schemaName} = z.object({\n\t...RecordModel.omit({ expand: true }).shape,\n\tcollectionName: z.literal("${name}"),\n\t${fieldStrings.join(",\n\t")},\n});\nexport type ${typeName} = z.infer<typeof ${schemaName}>;`;
} }
function stringifyField(field: CollectionField, collectionName: string) {
let schema: string;
// TODO:
console.log(`${collectionName}: ${field.type}`);
switch (field.type) {
case "bool":
schema = stringifyBoolField(field);
break;
case "date":
schema = stringifyDateField(field);
break;
case "editor":
schema = stringifyEditorField(field);
break;
case "email":
schema = stringifyEmailField(field);
break;
case "file":
schema = stringifyFileField(field);
break;
case "json":
schema = stringifyJsonField(field);
break;
case "number":
schema = stringifyNumberField(field);
break;
case "relation":
schema = stringifyRelationField(field);
break;
case "select":
schema = stringifySelectField(field, collectionName);
break;
case "text":
schema = stringifyTextField(field);
break;
case "url":
schema = stringifyUrlField(field);
break;
case "geoPoint":
schema = stringifyGeoPointField(field);
break;
default:
console.warn(`Unknown field type "${field.type}" for field "${field.name}". Using z.any() as fallback.`);
schema = "z.any()";
break;
}
return `${field.name}: ${schema}${(field as any).required ? "" : ".optional()"}`;
}
function stringifyBoolField(_: CollectionField) {
return "z.boolean()";
}
function stringifyDateField(_field: CollectionField) {
// TODO: implement min and max
return "z.string().pipe(z.coerce.date())";
}
function stringifyEditorField(_field: CollectionField) {
// TODO: implement convertUrls
return "z.string()";
}
function stringifyEmailField(_field: CollectionField) {
// TODO: implement exceptDomains and onlyDomains
return "z.string().email()";
}
function stringifyFileField(field: CollectionField) {
const maxSelect = (field as any).options?.maxSelect;
// TODO: implement maxSize, mimeTypes, protected, thumbs
return `z.string()${maxSelect === 1 ? "" : `.array()${maxSelect ? `.max(${maxSelect})` : ""}`}`;
}
function stringifyJsonField(_field: CollectionField) {
// TODO: implement maxSize and json schema
return "z.any()";
}
function stringifyNumberField(field: CollectionField) {
const options = (field as any).options || {};
const { max, min, noDecimal } = options;
return `z.number()${noDecimal ? ".int()" : ""}${min ? `.min(${min})` : ""}${max ? `.max(${max})` : ""}`;
}
function stringifyRelationField(field: CollectionField) {
const options = (field as any).options || {};
const required = (field as any).required;
const { maxSelect, minSelect } = options;
// TODO: implement cascadeDelete, displayFields
const min = minSelect ? `.min(${minSelect})` : "";
const max = maxSelect ? `.max(${maxSelect})` : "";
const multiple = maxSelect === 1 ? "" : `.array()${min}${max}`;
const isOptional = required || maxSelect !== 1 ? `` : `.transform((id) => id === "" ? undefined : id)`;
return `z.string()${isOptional}${multiple}`;
}
function stringifySelectField(field: CollectionField, collectionName: string) {
const maxSelect = (field as any).options?.maxSelect;
// TODO: implement values
return `${opts.nameEnumSchema(opts.nameEnumField(collectionName, field.name))}${maxSelect === 1 ? "" : `.array().max(${maxSelect})`}`;
}
function stringifyTextField(field: CollectionField) {
const options = (field as any).options || {};
const { max, min } = options;
// TODO: implement pattern
return `z.string()${min ? `.min(${min})` : ""}${max ? `.max(${max})` : ""}`;
}
function stringifyUrlField(_field: CollectionField) {
// TODO: implement exceptDomains and onlyDomains
return "z.string().url()";
}
function stringifyGeoPointField(_field: CollectionField) {
return "z.object({ lat: z.number().min(-90).max(90), lng: z.number().min(-180).max(180) })";
}
function stringifySchemasEntry({ name }: CollectionModel) { function stringifySchemasEntry({ name }: CollectionModel) {
return `["${name}", ${opts.nameRecordSchema(name)}]`; return `["${name}", ${opts.nameRecordSchema(name)}]`;
} }
@@ -171,6 +42,124 @@ export function stringifyContent(collections: CollectionModel[], opts: GenerateO
return `\t\tcollection(idOrName: "${name}"): RecordService<z.input<typeof ${opts.nameRecordSchema(name)}>>;`; return `\t\tcollection(idOrName: "${name}"): RecordService<z.input<typeof ${opts.nameRecordSchema(name)}>>;`;
} }
function stringifyField(field: CollectionField, collectionName: string) {
let schema: string;
switch (field.type) {
case "bool":
schema = "z.boolean()";
break;
case "date":
const minConstraintDate = field.min ? `.min(new Date("${field.min}"))` : "";
const maxConstraintDate = field.max ? `.max(new Date("${field.max}"))` : "";
schema = `z.string().pipe(z.coerce.date()${minConstraintDate}${maxConstraintDate})`;
break;
case "editor":
// TODO: implement convertUrls
schema = "z.string()";
break;
case "email":
const onlyDomainsConstraint = createDomainConstraint(field.onlyDomains, true, "email");
const exceptDomainsConstraint = createDomainConstraint(field.exceptDomains, false, "email");
schema = `z.string().email()${onlyDomainsConstraint}${exceptDomainsConstraint}`;
break;
case "file":
const maxSelectFile: number = field.maxSelect;
const maxSizeFile: number = field.maxSize;
const mimeTypesArray: string[] = field.mimeTypes || [];
// const protectedFile: boolean = field.protected;
// const thumbsFileArray: string[] = field.thumbs || [];
const fileFieldMaxSelect = maxSelectFile ? `.max(${maxSelectFile})` : "";
const fileFieldTypeArray = maxSelectFile === 1 ? "" : `.array()${fileFieldMaxSelect}`;
let fileValidation = "z.instanceof(File)";
if (maxSizeFile) fileValidation += `.refine((file) => file.size <= ${maxSizeFile}, { message: "File size too large" })`;
if (mimeTypesArray.length > 0)
fileValidation += `.refine((file) => ${JSON.stringify(mimeTypesArray)}.includes(file.type), { message: "Invalid file type" })`;
const baseFileSchema = `z.union([z.string(), ${fileValidation}])`;
schema = `${baseFileSchema}${fileFieldTypeArray}`;
break;
case "json":
schema = field.maxSize > 0 ? `pbJsonField(${field.maxSize})` : "pbJsonField()";
break;
case "number":
const maxNumber = field.maxNumber ? `.max(${field.maxNumber})` : "";
const minNumber = field.minNumber ? `.min(${field.minNumber})` : "";
const noDecimal = field.noDecimal ? ".int()" : "";
schema = `z.number()${noDecimal}${minNumber}${maxNumber}`;
break;
case "relation":
// TODO: implement cascadeDelete, displayFields, multiple records query
const multiple = field.maxSelect === 1 ? "" : `.array().min(${field.minSelect}).max(${field.maxSelect})`;
const isOptional = field.required || field.maxSelect !== 1 ? `` : `.transform((id: string) => id === "" ? undefined : id)`;
schema = `z.string()${isOptional}${multiple}`;
break;
case "select":
const maxSelect = field.maxSelect === 1 ? "" : `.array().max(${field.maxSelect})`;
schema = `${opts.nameEnumSchema(opts.nameEnumField(collectionName, field.name))}${maxSelect}`;
break;
case "text":
const patternText =
field.pattern && field.pattern.trim() !== "" ? `.regex(new RegExp("${field.pattern.replace(/"/g, '\\"')}"))` : "";
const maxText = field.max ? `.max(${field.max})` : "";
const minText = field.min ? `.min(${field.min})` : "";
schema = `z.string()${minText}${maxText}${patternText}`;
break;
case "url":
const onlyDomainsUrlConstraint = createDomainConstraint(field.onlyDomains, true, "url");
const exceptDomainsUrlConstraint = createDomainConstraint(field.exceptDomains, false, "url");
schema = `z.string().url()${onlyDomainsUrlConstraint}${exceptDomainsUrlConstraint}`;
break;
case "geoPoint":
schema = "z.object({ lat: z.number().min(-90).max(90), lon: z.number().min(-180).max(180) })";
break;
default:
console.warn(`Unknown field type "${field.type}" for field "${field.name}". Using z.any() as fallback.`);
schema = "z.any()";
break;
}
return `${field.name}: ${schema}${(field as any).required ? "" : ".optional()"}`;
}
/* Helpers */
const createDomainConstraint = (domains: string[], isWhitelist: boolean, type: "email" | "url") => {
if (!domains?.length) return "";
const domainsList = domains.map((domain) => `"${domain}"`).join(", ");
const messageType = isWhitelist ? "isn't one of the allowed ones" : "is one of the disallowed ones";
const negation = isWhitelist ? "" : "!";
const domainExtraction = type === "email" ? 'const domain = value.split("@")[1];' : "const domain = new URL(value).hostname;";
const errorHandling = type === "url" ? "try { " : "";
const errorCatch = type === "url" ? " } catch { return false; }" : "";
return `.refine((value: string) => { ${errorHandling}${domainExtraction} const domainsArray = [${domainsList}]; return domain && ${negation}domainsArray.includes(domain);${errorCatch} }, { message: "Invalid ${type}, domain ${messageType}" })`;
};
return { return {
collectionNames: `[\n\t${collections.map(({ name }) => `"${name}"`).join(",\n\t")},\n]`, collectionNames: `[\n\t${collections.map(({ name }) => `"${name}"`).join(",\n\t")},\n]`,
enums: getCollectionSelectFields().map(stringifyEnum).join("\n\n"), enums: getCollectionSelectFields().map(stringifyEnum).join("\n\n"),

View File

@@ -1,7 +1,8 @@
import type { default as Pocketbase, SendOptions } from "pocketbase"; import type { default as Pocketbase, SendOptions } from "pocketbase";
import { fullListOptionsFrom, optionsFrom } from "./options.js"; import { fullListOptionsFrom, optionsFrom } from "@/options.ts";
import type { AnyZodRecord, RecordFullListOpts, RecordIdRef, RecordRef, RecordSlugRef } from "./types.ts"; import type { AnyZodRecord, RecordFullListOpts, RecordIdRef, RecordRef, RecordSlugRef } from "@/types.ts";
import { AnyRecordsList, type RecordsList } from "./schemas.ts"; import { AnyRecordsList } from "@/schemas.ts";
import type { RecordsList } from "@/schemas.ts";
export function helpersFrom({ fetch, pocketbase }: HelpersFromOpts) { export function helpersFrom({ fetch, pocketbase }: HelpersFromOpts) {
async function getRecord<C extends string, S extends AnyZodRecord>(ref: RecordSlugRef<C>, opts: GetRecordOpts<S>): Promise<S["_output"]>; async function getRecord<C extends string, S extends AnyZodRecord>(ref: RecordSlugRef<C>, opts: GetRecordOpts<S>): Promise<S["_output"]>;

View File

@@ -1,5 +1,6 @@
import { z, type AnyZodObject, type ZodTypeAny } from "zod"; import { z } from "zod";
import type { AnyZodRecord, RecordFullListOpts, RecordListOpts } from "./types.ts"; import type { AnyZodObject, ZodTypeAny } from "zod";
import type { AnyZodRecord, RecordFullListOpts, RecordListOpts } from "@/types.ts";
/** /**
* Extends the given schema with the given expansion. * Extends the given schema with the given expansion.

View File

@@ -1,5 +1,6 @@
import { type AnyZodObject, type objectUtil, z, type ZodEffects, type ZodObject, ZodOptional, type ZodRawShape } from "zod"; import { z, ZodOptional, ZodEffects } from "zod";
import type { AnyZodRecord, HasRequiredKeys, ZodRecordKeys } from "./types.ts"; import type { AnyZodObject, objectUtil, ZodObject, ZodRawShape } from "zod";
import type { AnyZodRecord, HasRequiredKeys, ZodRecordKeys } from "@/types.ts";
/** /**
* Records list schema. * Records list schema.

View File

@@ -1,5 +1,5 @@
import Pocketbase from "pocketbase"; import Pocketbase from "pocketbase";
import type { Credentials } from "./config.ts"; import type { Credentials } from "@/config.ts";
let adminPocketbase: Pocketbase; let adminPocketbase: Pocketbase;

View File

@@ -1,16 +1,17 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { Config, type ResolvedConfig } from "../config.ts"; import { Config } from "@/config.ts";
import type { ResolvedConfig } from "@/config.ts";
import pkg from "../../package.json" with { type: "json" }; import pkg from "../../package.json" with { type: "json" };
import { loadConfig } from "c12"; import { loadConfig } from "c12";
import { defineCommand, runMain } from "citty"; import { defineCommand, runMain } from "citty";
import { cancel, group, intro, log, outro, confirm, text, spinner, multiselect, isCancel } from "@clack/prompts"; import { cancel, group, intro, log, outro, confirm, text, spinner, multiselect, isCancel } from "@clack/prompts";
import { fetchCollections } from "../utils.ts"; import { fetchCollections } from "@/utils.ts";
import { generate } from "./utils.ts"; import { generate } from "@/server/utils.ts";
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
async function getConfig() { async function getConfig() {
const { config } = await loadConfig({ name: "zod-pocketbase", rcFile: false, dotenv: true }); const { config } = await loadConfig({ name: "zod-pocketbase-continue", rcFile: false, dotenv: true });
const { ZOD_POCKETBASE_ADMIN_EMAIL: adminEmail, ZOD_POCKETBASE_ADMIN_PASSWORD: adminPassword, ZOD_POCKETBASE_URL: url } = process.env; const { ZOD_POCKETBASE_ADMIN_EMAIL: adminEmail, ZOD_POCKETBASE_ADMIN_PASSWORD: adminPassword, ZOD_POCKETBASE_URL: url } = process.env;
const result = Config.safeParse({ ...config, adminEmail, adminPassword, url }); const result = Config.safeParse({ ...config, adminEmail, adminPassword, url });
if (!result.success) { if (!result.success) {
@@ -72,7 +73,7 @@ async function setGeneratedFilePath(config: ResolvedConfig) {
} }
const main = defineCommand({ const main = defineCommand({
meta: { name: "zod-pocketbase", version: pkg.version, description: "Generate Zod schemas for your pocketbase instance." }, meta: { name: "zod-pocketbase-continue", version: pkg.version, description: "Generate Zod schemas for your pocketbase instance." },
run: async () => { run: async () => {
intro(`ZOD POCKETBASE`); intro(`ZOD POCKETBASE`);
const config = await getConfig(); const config = await getConfig();

View File

@@ -2,8 +2,8 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path"; import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import type { CollectionModel } from "pocketbase"; import type { CollectionModel } from "pocketbase";
import { stringifyContent } from "../content.js"; import { stringifyContent } from "@/content.js";
import type { ResolvedConfig } from "../config.ts"; import type { ResolvedConfig } from "@/config.ts";
export async function generate(collections: CollectionModel[], opts: GenerateOpts) { export async function generate(collections: CollectionModel[], opts: GenerateOpts) {
const stub = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../assets/stubs/index.ts"), "utf-8"); const stub = readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), "../../assets/stubs/index.ts"), "utf-8");

View File

@@ -1,7 +1,7 @@
import { sortBy } from "es-toolkit"; import { sortBy } from "es-toolkit";
import type { CollectionModel } from "pocketbase"; import type { CollectionModel } from "pocketbase";
import { getPocketbase } from "./sdk.js"; import { getPocketbase } from "@/sdk.js";
import type { Credentials } from "./config.ts"; import type { Credentials } from "@/config.ts";
export async function fetchCollections(credentials: Credentials): Promise<CollectionModel[]> { export async function fetchCollections(credentials: Credentials): Promise<CollectionModel[]> {
const pocketbase = await getPocketbase(credentials); const pocketbase = await getPocketbase(credentials);

View File

@@ -46,7 +46,11 @@
// Report an error for unreachable code instead of just a warning. // Report an error for unreachable code instead of just a warning.
"allowUnreachableCode": false, "allowUnreachableCode": false,
// Report an error for unused labels instead of just a warning. // Report an error for unused labels instead of just a warning.
"allowUnusedLabels": false "allowUnusedLabels": false,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}, },
"exclude": ["dist", "assets/stubs"] "exclude": ["dist", "assets/stubs"]
} }

View File

@@ -1,4 +1,4 @@
import { defineConfig } from "tsup"; import { defineConfig } from "tsdown";
import packageJson from "./package.json" with { type: "json" }; import packageJson from "./package.json" with { type: "json" };
export default defineConfig((options) => { export default defineConfig((options) => {
@@ -7,7 +7,7 @@ export default defineConfig((options) => {
entry: ["src/**/*.(ts|js)"], entry: ["src/**/*.(ts|js)"],
format: ["esm"], format: ["esm"],
target: "node24", target: "node24",
bundle: true, unbundle: true,
dts: true, dts: true,
sourcemap: true, sourcemap: true,
clean: true, clean: true,