pocketbase-to-zod 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/index.ts +95 -0
- package/package.json +40 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# pocketbase-to-zod
|
|
2
|
+
|
|
3
|
+
A lightweight CLI tool to automatically generate **Zod schemas** and **TypeScript types** directly from your **PocketBase** collections.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
* ⚡ **Real-time Generation**: Connects to your PocketBase instance via URL.
|
|
7
|
+
* 🛡️ **Type Safety**: Generates both Zod schemas for runtime validation and TS types for compile-time safety.
|
|
8
|
+
* 🔄 **Smart Mapping**: Automatically maps PocketBase field types (Select, Relation, File, Date, etc.) to the appropriate Zod logic.
|
|
9
|
+
* 📦 **Fullstack Ready**: Perfect for sharing schemas between your backend (Nitro/Node) and your frontend.
|
|
10
|
+
* 🏎️ **Lightweight & Fast**: Minimal dependencies for quick setup and execution.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g pocketbase-to-zod
|
|
16
|
+
# or use it via npx
|
|
17
|
+
npx pocketbase-to-zod [options]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pocketbase-to-zod --url http://localhost:8090 --email admin@example.com --password yourpassword --output ./schemas.ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Options
|
|
27
|
+
- `--url <url>`: PocketBase instance URL (default: `http://localhost:8090`) [Required]
|
|
28
|
+
- `--email <email>`: Admin email for authentication [Required]
|
|
29
|
+
- `--password <password>`: Admin password for authentication [Required]
|
|
30
|
+
- `--output <file>`: Output file path for generated schemas (default: `./pocketbase-schemas.ts`) [Optional]
|
|
31
|
+
|
|
32
|
+
## Example Output
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { z } from "zod";
|
|
36
|
+
|
|
37
|
+
export const UserSchema = z.object({
|
|
38
|
+
id: z.string(),
|
|
39
|
+
created: z.string().refine((date) => !isNaN(Date.parse(date)), {
|
|
40
|
+
message: "Invalid date format",
|
|
41
|
+
}),
|
|
42
|
+
updated: z.string().refine((date) => !isNaN(Date.parse(date)), {
|
|
43
|
+
message: "Invalid date format",
|
|
44
|
+
}),
|
|
45
|
+
email: z.string().email(),
|
|
46
|
+
verified: z.boolean(),
|
|
47
|
+
username: z.string().min(3).max(50),
|
|
48
|
+
// ... other fields
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type User = z.infer<typeof UserSchema>;
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Contributing
|
|
55
|
+
Contributions are welcome! Please open an issue or submit a pull request on GitHub.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const pocketbase_1 = __importDefault(require("pocketbase"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const program = new commander_1.Command();
|
|
12
|
+
program
|
|
13
|
+
.name('pocketbase-to-zod')
|
|
14
|
+
.description('Generate Zod schemas from a PocketBase instance')
|
|
15
|
+
.requiredOption('-u, --url <char>', 'PocketBase URL')
|
|
16
|
+
.requiredOption('-e, --email <char>', 'Admin email')
|
|
17
|
+
.requiredOption('-p, --password <char>', 'Admin password')
|
|
18
|
+
.option('-o, --output <char>', 'Output file path', './pocketbase-schema.ts')
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
const pb = new pocketbase_1.default(options.url);
|
|
21
|
+
try {
|
|
22
|
+
console.log('--- Authenticating with PocketBase ---');
|
|
23
|
+
await pb.collection('_superusers').authWithPassword(options.email, options.password);
|
|
24
|
+
console.log('--- Fetching collections ---');
|
|
25
|
+
const collections = await pb.collections.getFullList();
|
|
26
|
+
let fileContent = `import { z } from 'zod';\n\n`;
|
|
27
|
+
for (const col of collections) {
|
|
28
|
+
fileContent += `// Schema for collection: ${col.name}\n`;
|
|
29
|
+
fileContent += `export const ${col.name}Schema = z.object({\n`;
|
|
30
|
+
// Base PocketBase fields
|
|
31
|
+
fileContent += ` id: z.string(),\n`;
|
|
32
|
+
fileContent += ` created: z.string(),\n`;
|
|
33
|
+
fileContent += ` updated: z.string(),\n`;
|
|
34
|
+
for (const field of col.schema) {
|
|
35
|
+
let zodType = 'z.any()';
|
|
36
|
+
switch (field.type) {
|
|
37
|
+
case 'text':
|
|
38
|
+
case 'editor':
|
|
39
|
+
case 'url':
|
|
40
|
+
case 'email':
|
|
41
|
+
zodType = 'z.string()';
|
|
42
|
+
break;
|
|
43
|
+
case 'number':
|
|
44
|
+
zodType = 'z.number()';
|
|
45
|
+
break;
|
|
46
|
+
case 'bool':
|
|
47
|
+
zodType = 'z.boolean()';
|
|
48
|
+
break;
|
|
49
|
+
case 'date':
|
|
50
|
+
zodType = 'z.string().datetime()'; // PocketBase sends dates as ISO strings
|
|
51
|
+
break;
|
|
52
|
+
case 'select':
|
|
53
|
+
if (field.options.values) {
|
|
54
|
+
const values = field.options.values.map((v) => `'${v}'`).join(', ');
|
|
55
|
+
zodType = `z.enum([${values}])`;
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
case 'relation':
|
|
59
|
+
zodType = field.options.maxSelect === 1 ? 'z.string()' : 'z.array(z.string())';
|
|
60
|
+
break;
|
|
61
|
+
case 'file':
|
|
62
|
+
zodType = field.options.maxSelect === 1 ? 'z.string()' : 'z.array(z.string())';
|
|
63
|
+
break;
|
|
64
|
+
case 'json':
|
|
65
|
+
zodType = 'z.unknown()';
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (!field.required) {
|
|
69
|
+
zodType += '.optional().nullable()';
|
|
70
|
+
}
|
|
71
|
+
fileContent += ` ${field.name}: ${zodType},\n`;
|
|
72
|
+
}
|
|
73
|
+
fileContent += `});\n\n`;
|
|
74
|
+
fileContent += `export type ${col.name.charAt(0).toUpperCase() + col.name.slice(1)} = z.infer<typeof ${col.name}Schema>;\n\n`;
|
|
75
|
+
}
|
|
76
|
+
const outputPath = path_1.default.resolve(process.cwd(), options.output);
|
|
77
|
+
fs_1.default.writeFileSync(outputPath, fileContent);
|
|
78
|
+
console.log(`✅ Schemas generated successfully at: ${outputPath}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error('❌ Error:', error.message);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/index.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import PocketBase from 'pocketbase';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('pocketbase-to-zod')
|
|
12
|
+
.description('Generate Zod schemas from a PocketBase instance')
|
|
13
|
+
.requiredOption('-u, --url <char>', 'PocketBase URL')
|
|
14
|
+
.requiredOption('-e, --email <char>', 'Admin email')
|
|
15
|
+
.requiredOption('-p, --password <char>', 'Admin password')
|
|
16
|
+
.option('-o, --output <char>', 'Output file path', './pocketbase-schema.ts')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const pb = new PocketBase(options.url);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
console.log('--- Authenticating with PocketBase ---');
|
|
22
|
+
await pb.collection('_superusers').authWithPassword(options.email, options.password);
|
|
23
|
+
|
|
24
|
+
console.log('--- Fetching collections ---');
|
|
25
|
+
const collections = await pb.collections.getFullList();
|
|
26
|
+
|
|
27
|
+
let fileContent = `import { z } from 'zod';\n\n`;
|
|
28
|
+
|
|
29
|
+
for (const col of collections) {
|
|
30
|
+
fileContent += `// Schema for collection: ${col.name}\n`;
|
|
31
|
+
fileContent += `export const ${col.name}Schema = z.object({\n`;
|
|
32
|
+
|
|
33
|
+
// Base PocketBase fields
|
|
34
|
+
fileContent += ` id: z.string(),\n`;
|
|
35
|
+
fileContent += ` created: z.string(),\n`;
|
|
36
|
+
fileContent += ` updated: z.string(),\n`;
|
|
37
|
+
|
|
38
|
+
for (const field of col.schema) {
|
|
39
|
+
let zodType = 'z.any()';
|
|
40
|
+
|
|
41
|
+
switch (field.type) {
|
|
42
|
+
case 'text':
|
|
43
|
+
case 'editor':
|
|
44
|
+
case 'url':
|
|
45
|
+
case 'email':
|
|
46
|
+
zodType = 'z.string()';
|
|
47
|
+
break;
|
|
48
|
+
case 'number':
|
|
49
|
+
zodType = 'z.number()';
|
|
50
|
+
break;
|
|
51
|
+
case 'bool':
|
|
52
|
+
zodType = 'z.boolean()';
|
|
53
|
+
break;
|
|
54
|
+
case 'date':
|
|
55
|
+
zodType = 'z.string().datetime()'; // PocketBase sends dates as ISO strings
|
|
56
|
+
break;
|
|
57
|
+
case 'select':
|
|
58
|
+
if (field.options.values) {
|
|
59
|
+
const values = field.options.values.map((v: string) => `'${v}'`).join(', ');
|
|
60
|
+
zodType = `z.enum([${values}])`;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case 'relation':
|
|
64
|
+
zodType = field.options.maxSelect === 1 ? 'z.string()' : 'z.array(z.string())';
|
|
65
|
+
break;
|
|
66
|
+
case 'file':
|
|
67
|
+
zodType = field.options.maxSelect === 1 ? 'z.string()' : 'z.array(z.string())';
|
|
68
|
+
break;
|
|
69
|
+
case 'json':
|
|
70
|
+
zodType = 'z.unknown()';
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!field.required) {
|
|
75
|
+
zodType += '.optional().nullable()';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fileContent += ` ${field.name}: ${zodType},\n`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fileContent += `});\n\n`;
|
|
82
|
+
fileContent += `export type ${col.name.charAt(0).toUpperCase() + col.name.slice(1)} = z.infer<typeof ${col.name}Schema>;\n\n`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const outputPath = path.resolve(process.cwd(), options.output);
|
|
86
|
+
fs.writeFileSync(outputPath, fileContent);
|
|
87
|
+
console.log(`✅ Schemas generated successfully at: ${outputPath}`);
|
|
88
|
+
|
|
89
|
+
} catch (error: any) {
|
|
90
|
+
console.error('❌ Error:', error.message);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pocketbase-to-zod",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "This project is an utility to generate Zod schemas from Pocketbase instance ",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"pb-to-zod": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc && chmod +x dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/ajomuch92/pocketbase-to-zod.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"Zod",
|
|
18
|
+
"schemas",
|
|
19
|
+
"pocketbase",
|
|
20
|
+
"pb",
|
|
21
|
+
"validations",
|
|
22
|
+
"instance"
|
|
23
|
+
],
|
|
24
|
+
"author": "Aarón Montes",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/ajomuch92/pocketbase-to-zod/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/ajomuch92/pocketbase-to-zod#readme",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"commander": "^14.0.2",
|
|
32
|
+
"pocketbase": "^0.26.6",
|
|
33
|
+
"zod": "^4.3.5"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.0.9",
|
|
37
|
+
"ts-node": "^10.9.2",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"declaration": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["index.ts"]
|
|
15
|
+
}
|