pocketbase-zod-schema 0.2.4 → 0.2.5
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/CHANGELOG.md +7 -0
- package/README.md +209 -24
- package/dist/cli/index.cjs +34 -0
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +3 -1
- package/dist/cli/index.d.ts +3 -1
- package/dist/cli/index.js +34 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/migrate.cjs +34 -0
- package/dist/cli/migrate.cjs.map +1 -1
- package/dist/cli/migrate.js +34 -0
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/utils/index.d.cts +3 -1
- package/dist/cli/utils/index.d.ts +3 -1
- package/dist/fields-YjcpBXVp.d.cts +348 -0
- package/dist/fields-YjcpBXVp.d.ts +348 -0
- package/dist/index.cjs +222 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +208 -1
- package/dist/index.js.map +1 -1
- package/dist/migration/analyzer.cjs +34 -0
- package/dist/migration/analyzer.cjs.map +1 -1
- package/dist/migration/analyzer.d.cts +2 -1
- package/dist/migration/analyzer.d.ts +2 -1
- package/dist/migration/analyzer.js +34 -0
- package/dist/migration/analyzer.js.map +1 -1
- package/dist/migration/diff.d.cts +3 -1
- package/dist/migration/diff.d.ts +3 -1
- package/dist/migration/generator.d.cts +3 -1
- package/dist/migration/generator.d.ts +3 -1
- package/dist/migration/index.cjs +36 -0
- package/dist/migration/index.cjs.map +1 -1
- package/dist/migration/index.d.cts +2 -1
- package/dist/migration/index.d.ts +2 -1
- package/dist/migration/index.js +35 -1
- package/dist/migration/index.js.map +1 -1
- package/dist/migration/snapshot.cjs.map +1 -1
- package/dist/migration/snapshot.d.cts +3 -1
- package/dist/migration/snapshot.d.ts +3 -1
- package/dist/migration/snapshot.js.map +1 -1
- package/dist/migration/utils/index.cjs +16 -0
- package/dist/migration/utils/index.cjs.map +1 -1
- package/dist/migration/utils/index.d.cts +2 -2
- package/dist/migration/utils/index.d.ts +2 -2
- package/dist/migration/utils/index.js +15 -1
- package/dist/migration/utils/index.js.map +1 -1
- package/dist/schema.cjs +200 -0
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +186 -1
- package/dist/schema.js.map +1 -1
- package/dist/{types-z1Dkjg8m.d.ts → types-LFBGHl9Y.d.ts} +2 -2
- package/dist/{types-BbTgmg6H.d.cts → types-mhQXWNi3.d.cts} +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.5](https://github.com/dastron/pocketbase-zod-schema/compare/pocketbase-zod-schema-v0.2.4...pocketbase-zod-schema-v0.2.5) (2026-01-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* Add Field Helpers ([bbe8185](https://github.com/dastron/pocketbase-zod-schema/commit/bbe81850a9fd6eba9c73d08cb0b21c7f944a6673))
|
|
9
|
+
|
|
3
10
|
## [0.2.4](https://github.com/dastron/pocketbase-zod-schema/compare/pocketbase-zod-schema-v0.2.3...pocketbase-zod-schema-v0.2.4) (2026-01-03)
|
|
4
11
|
|
|
5
12
|
|
package/README.md
CHANGED
|
@@ -31,15 +31,18 @@ Create a schema file in your project (e.g., `src/schema/post.ts`):
|
|
|
31
31
|
import { z } from "zod";
|
|
32
32
|
import {
|
|
33
33
|
defineCollection,
|
|
34
|
+
TextField,
|
|
35
|
+
EditorField,
|
|
36
|
+
BoolField,
|
|
34
37
|
RelationField,
|
|
35
38
|
RelationsField,
|
|
36
39
|
} from "pocketbase-zod-schema/schema";
|
|
37
40
|
|
|
38
41
|
// Define the Zod schema
|
|
39
42
|
export const PostSchema = z.object({
|
|
40
|
-
title:
|
|
41
|
-
content:
|
|
42
|
-
published:
|
|
43
|
+
title: TextField({ min: 1, max: 200 }),
|
|
44
|
+
content: EditorField(),
|
|
45
|
+
published: BoolField(),
|
|
43
46
|
|
|
44
47
|
// Single relation to users collection
|
|
45
48
|
author: RelationField({ collection: "users" }),
|
|
@@ -98,11 +101,11 @@ The recommended way to define collections is using `defineCollection()`, which p
|
|
|
98
101
|
|
|
99
102
|
```typescript
|
|
100
103
|
import { z } from "zod";
|
|
101
|
-
import { defineCollection, RelationField } from "pocketbase-zod-schema/schema";
|
|
104
|
+
import { defineCollection, TextField, EditorField, RelationField } from "pocketbase-zod-schema/schema";
|
|
102
105
|
|
|
103
106
|
export const PostCollectionSchema = z.object({
|
|
104
|
-
title:
|
|
105
|
-
content:
|
|
107
|
+
title: TextField({ min: 1, max: 200 }),
|
|
108
|
+
content: EditorField(),
|
|
106
109
|
author: RelationField({ collection: "users" }),
|
|
107
110
|
});
|
|
108
111
|
|
|
@@ -151,7 +154,185 @@ This pattern allows:
|
|
|
151
154
|
|
|
152
155
|
### Field Types
|
|
153
156
|
|
|
154
|
-
The library
|
|
157
|
+
The library provides explicit field helper functions for all PocketBase field types. These helpers embed PocketBase-specific metadata and provide type-safe configuration options.
|
|
158
|
+
|
|
159
|
+
#### Field Helper Functions
|
|
160
|
+
|
|
161
|
+
| Field Helper | PocketBase Type | Description | Example |
|
|
162
|
+
|--------------|-----------------|-------------|---------|
|
|
163
|
+
| `BoolField()` | bool | Boolean field | `active: BoolField()` |
|
|
164
|
+
| `NumberField(options?)` | number | Number field with optional constraints | `price: NumberField({ min: 0 })` |
|
|
165
|
+
| `TextField(options?)` | text | Text field with optional constraints | `name: TextField({ min: 1, max: 200 })` |
|
|
166
|
+
| `EmailField()` | email | Email field with validation | `email: EmailField()` |
|
|
167
|
+
| `URLField()` | url | URL field with validation | `website: URLField()` |
|
|
168
|
+
| `EditorField()` | editor | Rich text editor field | `content: EditorField()` |
|
|
169
|
+
| `DateField(options?)` | date | Date field with optional constraints | `birthdate: DateField()` |
|
|
170
|
+
| `AutodateField(options?)` | autodate | Auto-managed timestamp field | `createdAt: AutodateField({ onCreate: true })` |
|
|
171
|
+
| `SelectField(values, options?)` | select | Single or multiple select field | `status: SelectField(["draft", "published"])` |
|
|
172
|
+
| `FileField(options?)` | file | Single file upload field | `avatar: FileField({ mimeTypes: ["image/*"] })` |
|
|
173
|
+
| `FilesField(options?)` | file | Multiple file upload field | `images: FilesField({ maxSelect: 5 })` |
|
|
174
|
+
| `JSONField(schema?)` | json | JSON field with optional schema | `metadata: JSONField()` |
|
|
175
|
+
| `GeoPointField()` | geoPoint | Geographic coordinates field | `location: GeoPointField()` |
|
|
176
|
+
| `RelationField(config)` | relation | Single relation field | `author: RelationField({ collection: "users" })` |
|
|
177
|
+
| `RelationsField(config)` | relation | Multiple relation field | `tags: RelationsField({ collection: "tags" })` |
|
|
178
|
+
|
|
179
|
+
#### Field Options
|
|
180
|
+
|
|
181
|
+
**BoolField()**
|
|
182
|
+
- No options
|
|
183
|
+
- Returns: `z.ZodBoolean`
|
|
184
|
+
|
|
185
|
+
**NumberField(options?)**
|
|
186
|
+
- `min?: number` - Minimum value constraint
|
|
187
|
+
- `max?: number` - Maximum value constraint
|
|
188
|
+
- `noDecimal?: boolean` - Disallow decimal values (integers only)
|
|
189
|
+
- Returns: `z.ZodNumber`
|
|
190
|
+
|
|
191
|
+
**TextField(options?)**
|
|
192
|
+
- `min?: number` - Minimum length constraint
|
|
193
|
+
- `max?: number` - Maximum length constraint
|
|
194
|
+
- `pattern?: RegExp | string` - Pattern constraint (regex)
|
|
195
|
+
- `autogeneratePattern?: string` - Auto-generate pattern (e.g., `"[A-Z]{3}-[0-9]{6}"`)
|
|
196
|
+
- Returns: `z.ZodString`
|
|
197
|
+
|
|
198
|
+
**EmailField()**
|
|
199
|
+
- No options (includes email validation)
|
|
200
|
+
- Returns: `z.ZodString`
|
|
201
|
+
|
|
202
|
+
**URLField()**
|
|
203
|
+
- No options (includes URL validation)
|
|
204
|
+
- Returns: `z.ZodString`
|
|
205
|
+
|
|
206
|
+
**EditorField()**
|
|
207
|
+
- No options
|
|
208
|
+
- Returns: `z.ZodString`
|
|
209
|
+
|
|
210
|
+
**DateField(options?)**
|
|
211
|
+
- `min?: Date | string` - Minimum date constraint
|
|
212
|
+
- `max?: Date | string` - Maximum date constraint
|
|
213
|
+
- Returns: `z.ZodString`
|
|
214
|
+
|
|
215
|
+
**AutodateField(options?)**
|
|
216
|
+
- `onCreate?: boolean` - Set date automatically on record creation
|
|
217
|
+
- `onUpdate?: boolean` - Update date automatically on record update
|
|
218
|
+
- Returns: `z.ZodString`
|
|
219
|
+
|
|
220
|
+
**SelectField(values, options?)**
|
|
221
|
+
- `values: [string, ...string[]]` - Array of allowed values (required)
|
|
222
|
+
- `maxSelect?: number` - Maximum selections (default: 1, >1 enables multiple selection)
|
|
223
|
+
- Returns: `z.ZodEnum<T>` or `z.ZodArray<z.ZodEnum<T>>`
|
|
224
|
+
|
|
225
|
+
**FileField(options?)**
|
|
226
|
+
- `mimeTypes?: string[]` - Allowed MIME types (e.g., `["image/*", "application/pdf"]`)
|
|
227
|
+
- `maxSize?: number` - Maximum file size in bytes
|
|
228
|
+
- `thumbs?: string[]` - Thumbnail sizes to generate (e.g., `["100x100", "200x200"]`)
|
|
229
|
+
- `protected?: boolean` - Whether file requires auth to access
|
|
230
|
+
- Returns: `z.ZodType<File>`
|
|
231
|
+
|
|
232
|
+
**FilesField(options?)**
|
|
233
|
+
- All `FileField` options plus:
|
|
234
|
+
- `minSelect?: number` - Minimum number of files required
|
|
235
|
+
- `maxSelect?: number` - Maximum number of files allowed
|
|
236
|
+
- Returns: `z.ZodArray<z.ZodType<File>>`
|
|
237
|
+
|
|
238
|
+
**JSONField(schema?)**
|
|
239
|
+
- `schema?: z.ZodTypeAny` - Optional Zod schema for JSON structure validation
|
|
240
|
+
- Returns: `T | z.ZodRecord<z.ZodString, z.ZodAny>`
|
|
241
|
+
|
|
242
|
+
**GeoPointField()**
|
|
243
|
+
- No options
|
|
244
|
+
- Returns: `z.ZodObject<{ lon: z.ZodNumber; lat: z.ZodNumber }>`
|
|
245
|
+
|
|
246
|
+
#### Field Helper Examples
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { z } from "zod";
|
|
250
|
+
import {
|
|
251
|
+
defineCollection,
|
|
252
|
+
BoolField,
|
|
253
|
+
NumberField,
|
|
254
|
+
TextField,
|
|
255
|
+
EmailField,
|
|
256
|
+
URLField,
|
|
257
|
+
EditorField,
|
|
258
|
+
DateField,
|
|
259
|
+
AutodateField,
|
|
260
|
+
SelectField,
|
|
261
|
+
FileField,
|
|
262
|
+
FilesField,
|
|
263
|
+
JSONField,
|
|
264
|
+
GeoPointField,
|
|
265
|
+
RelationField,
|
|
266
|
+
RelationsField,
|
|
267
|
+
} from "pocketbase-zod-schema/schema";
|
|
268
|
+
|
|
269
|
+
const ProductSchema = z.object({
|
|
270
|
+
// Text fields
|
|
271
|
+
name: TextField({ min: 1, max: 200 }),
|
|
272
|
+
sku: TextField({ autogeneratePattern: "[A-Z]{3}-[0-9]{6}" }),
|
|
273
|
+
description: EditorField(),
|
|
274
|
+
website: URLField().optional(),
|
|
275
|
+
|
|
276
|
+
// Number fields
|
|
277
|
+
price: NumberField({ min: 0 }),
|
|
278
|
+
quantity: NumberField({ min: 0, noDecimal: true }),
|
|
279
|
+
rating: NumberField({ min: 0, max: 5 }).optional(),
|
|
280
|
+
|
|
281
|
+
// Boolean field
|
|
282
|
+
active: BoolField(),
|
|
283
|
+
featured: BoolField().optional(),
|
|
284
|
+
|
|
285
|
+
// Date fields
|
|
286
|
+
releaseDate: DateField().optional(),
|
|
287
|
+
createdAt: AutodateField({ onCreate: true }),
|
|
288
|
+
updatedAt: AutodateField({ onUpdate: true }),
|
|
289
|
+
|
|
290
|
+
// Select fields
|
|
291
|
+
status: SelectField(["draft", "published", "archived"]),
|
|
292
|
+
categories: SelectField(["electronics", "clothing", "food"], { maxSelect: 3 }),
|
|
293
|
+
|
|
294
|
+
// File fields
|
|
295
|
+
thumbnail: FileField({
|
|
296
|
+
mimeTypes: ["image/*"],
|
|
297
|
+
maxSize: 5242880, // 5MB
|
|
298
|
+
thumbs: ["100x100", "200x200"],
|
|
299
|
+
}),
|
|
300
|
+
images: FilesField({
|
|
301
|
+
mimeTypes: ["image/*"],
|
|
302
|
+
maxSelect: 5,
|
|
303
|
+
}),
|
|
304
|
+
|
|
305
|
+
// JSON field
|
|
306
|
+
metadata: JSONField(),
|
|
307
|
+
settings: JSONField(z.object({
|
|
308
|
+
theme: z.string(),
|
|
309
|
+
notifications: z.boolean(),
|
|
310
|
+
})).optional(),
|
|
311
|
+
|
|
312
|
+
// GeoPoint field
|
|
313
|
+
location: GeoPointField().optional(),
|
|
314
|
+
|
|
315
|
+
// Relation fields
|
|
316
|
+
vendor: RelationField({ collection: "vendors" }),
|
|
317
|
+
tags: RelationsField({ collection: "tags", maxSelect: 10 }),
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
export const ProductCollection = defineCollection({
|
|
321
|
+
collectionName: "products",
|
|
322
|
+
schema: ProductSchema,
|
|
323
|
+
permissions: {
|
|
324
|
+
listRule: "",
|
|
325
|
+
viewRule: "",
|
|
326
|
+
createRule: '@request.auth.id != ""',
|
|
327
|
+
updateRule: "vendor.owner = @request.auth.id",
|
|
328
|
+
deleteRule: "vendor.owner = @request.auth.id",
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Backward Compatibility
|
|
334
|
+
|
|
335
|
+
The library still supports plain Zod types for backward compatibility. The migration generator will infer PocketBase field types from Zod types when field helpers are not used:
|
|
155
336
|
|
|
156
337
|
| Zod Type | PocketBase Type | Example |
|
|
157
338
|
|----------|-----------------|---------|
|
|
@@ -163,8 +344,8 @@ The library maps Zod types to PocketBase field types automatically:
|
|
|
163
344
|
| `z.date()` | date | `birthdate: z.date()` |
|
|
164
345
|
| `z.enum([...])` | select | `status: z.enum(["draft", "published"])` |
|
|
165
346
|
| `z.instanceof(File)` | file | `avatar: z.instanceof(File)` |
|
|
166
|
-
|
|
167
|
-
|
|
347
|
+
|
|
348
|
+
**Recommendation:** Use field helpers for new schemas to get explicit field type declarations and access to PocketBase-specific options.
|
|
168
349
|
|
|
169
350
|
### Defining Relations
|
|
170
351
|
|
|
@@ -420,22 +601,22 @@ Here's a complete example of a blog schema with users, posts, and comments:
|
|
|
420
601
|
```typescript
|
|
421
602
|
// src/schema/user.ts
|
|
422
603
|
import { z } from "zod";
|
|
423
|
-
import { baseSchema, defineCollection } from "pocketbase-zod-schema/schema";
|
|
604
|
+
import { baseSchema, defineCollection, TextField, EmailField } from "pocketbase-zod-schema/schema";
|
|
424
605
|
|
|
425
606
|
// Input schema for forms (includes passwordConfirm for validation)
|
|
426
607
|
export const UserInputSchema = z.object({
|
|
427
|
-
name:
|
|
428
|
-
email:
|
|
429
|
-
password:
|
|
608
|
+
name: TextField({ max: 100 }).optional(),
|
|
609
|
+
email: EmailField(),
|
|
610
|
+
password: TextField({ min: 8 }),
|
|
430
611
|
passwordConfirm: z.string(),
|
|
431
612
|
avatar: z.instanceof(File).optional(),
|
|
432
613
|
});
|
|
433
614
|
|
|
434
615
|
// Database schema (excludes passwordConfirm)
|
|
435
616
|
const UserCollectionSchema = z.object({
|
|
436
|
-
name:
|
|
437
|
-
email:
|
|
438
|
-
password:
|
|
617
|
+
name: TextField({ max: 100 }).optional(),
|
|
618
|
+
email: EmailField(),
|
|
619
|
+
password: TextField({ min: 8 }),
|
|
439
620
|
avatar: z.instanceof(File).optional(),
|
|
440
621
|
});
|
|
441
622
|
|
|
@@ -464,18 +645,22 @@ export const UserCollection = defineCollection({
|
|
|
464
645
|
import { z } from "zod";
|
|
465
646
|
import {
|
|
466
647
|
defineCollection,
|
|
648
|
+
TextField,
|
|
649
|
+
EditorField,
|
|
650
|
+
BoolField,
|
|
651
|
+
DateField,
|
|
467
652
|
RelationField,
|
|
468
653
|
RelationsField,
|
|
469
654
|
} from "pocketbase-zod-schema/schema";
|
|
470
655
|
|
|
471
656
|
// Define the Zod schema
|
|
472
657
|
export const PostSchema = z.object({
|
|
473
|
-
title:
|
|
474
|
-
slug:
|
|
475
|
-
content:
|
|
476
|
-
excerpt:
|
|
477
|
-
published:
|
|
478
|
-
publishedAt:
|
|
658
|
+
title: TextField({ min: 1, max: 200 }),
|
|
659
|
+
slug: TextField({ pattern: /^[a-z0-9-]+$/ }),
|
|
660
|
+
content: EditorField(),
|
|
661
|
+
excerpt: TextField({ max: 500 }).optional(),
|
|
662
|
+
published: BoolField(),
|
|
663
|
+
publishedAt: DateField().optional(),
|
|
479
664
|
|
|
480
665
|
// Relations
|
|
481
666
|
author: RelationField({ collection: "users" }),
|
|
@@ -500,11 +685,11 @@ export const PostCollection = defineCollection({
|
|
|
500
685
|
```typescript
|
|
501
686
|
// src/schema/comment.ts
|
|
502
687
|
import { z } from "zod";
|
|
503
|
-
import { defineCollection, RelationField } from "pocketbase-zod-schema/schema";
|
|
688
|
+
import { defineCollection, TextField, RelationField } from "pocketbase-zod-schema/schema";
|
|
504
689
|
|
|
505
690
|
// Define the Zod schema
|
|
506
691
|
export const CommentSchema = z.object({
|
|
507
|
-
content:
|
|
692
|
+
content: TextField({ min: 1 }),
|
|
508
693
|
|
|
509
694
|
// Relations with cascade delete
|
|
510
695
|
post: RelationField({ collection: "posts", cascadeDelete: true }),
|
package/dist/cli/index.cjs
CHANGED
|
@@ -63,6 +63,18 @@ function extractRelationMetadata(description) {
|
|
|
63
63
|
}
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
|
+
var FIELD_METADATA_KEY = "__pocketbase_field__";
|
|
67
|
+
function extractFieldMetadata(description) {
|
|
68
|
+
if (!description) return null;
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(description);
|
|
71
|
+
if (parsed[FIELD_METADATA_KEY]) {
|
|
72
|
+
return parsed[FIELD_METADATA_KEY];
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
66
78
|
|
|
67
79
|
// src/migration/errors.ts
|
|
68
80
|
var MigrationError = class _MigrationError extends Error {
|
|
@@ -1301,6 +1313,28 @@ function isAuthCollection(fields) {
|
|
|
1301
1313
|
return hasEmail && hasPassword;
|
|
1302
1314
|
}
|
|
1303
1315
|
function buildFieldDefinition(fieldName, zodType) {
|
|
1316
|
+
const fieldMetadata = extractFieldMetadata(zodType.description);
|
|
1317
|
+
if (fieldMetadata) {
|
|
1318
|
+
const required2 = isFieldRequired(zodType);
|
|
1319
|
+
const fieldDef2 = {
|
|
1320
|
+
name: fieldName,
|
|
1321
|
+
type: fieldMetadata.type,
|
|
1322
|
+
required: required2,
|
|
1323
|
+
options: fieldMetadata.options
|
|
1324
|
+
};
|
|
1325
|
+
if (fieldMetadata.type === "relation") {
|
|
1326
|
+
const relationMetadata2 = extractRelationMetadata(zodType.description);
|
|
1327
|
+
if (relationMetadata2) {
|
|
1328
|
+
fieldDef2.relation = {
|
|
1329
|
+
collection: relationMetadata2.collection,
|
|
1330
|
+
maxSelect: relationMetadata2.maxSelect,
|
|
1331
|
+
minSelect: relationMetadata2.minSelect,
|
|
1332
|
+
cascadeDelete: relationMetadata2.cascadeDelete
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return fieldDef2;
|
|
1337
|
+
}
|
|
1304
1338
|
const fieldType = mapZodTypeToPocketBase(zodType, fieldName);
|
|
1305
1339
|
const required = isFieldRequired(zodType);
|
|
1306
1340
|
const options = extractFieldOptions(zodType);
|