pocketbase-zod-schema 0.7.2 → 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.
Files changed (67) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +198 -25
  3. package/dist/cli/index.cjs +3740 -1233
  4. package/dist/cli/index.cjs.map +1 -1
  5. package/dist/cli/index.js +3737 -1232
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/migrate.cjs +3815 -1201
  8. package/dist/cli/migrate.cjs.map +1 -1
  9. package/dist/cli/migrate.js +3811 -1199
  10. package/dist/cli/migrate.js.map +1 -1
  11. package/dist/cli/utils/index.cjs +52 -13
  12. package/dist/cli/utils/index.cjs.map +1 -1
  13. package/dist/cli/utils/index.d.cts +46 -1
  14. package/dist/cli/utils/index.d.ts +46 -1
  15. package/dist/cli/utils/index.js +51 -13
  16. package/dist/cli/utils/index.js.map +1 -1
  17. package/dist/migration/analyzer.cjs +123 -42
  18. package/dist/migration/analyzer.cjs.map +1 -1
  19. package/dist/migration/analyzer.js +123 -42
  20. package/dist/migration/analyzer.js.map +1 -1
  21. package/dist/migration/diff.cjs +13 -0
  22. package/dist/migration/diff.cjs.map +1 -1
  23. package/dist/migration/diff.js +13 -0
  24. package/dist/migration/diff.js.map +1 -1
  25. package/dist/migration/engine.cjs +3263 -0
  26. package/dist/migration/engine.cjs.map +1 -0
  27. package/dist/migration/engine.d.cts +105 -0
  28. package/dist/migration/engine.d.ts +105 -0
  29. package/dist/migration/engine.js +3173 -0
  30. package/dist/migration/engine.js.map +1 -0
  31. package/dist/migration/generator.cjs +40 -18
  32. package/dist/migration/generator.cjs.map +1 -1
  33. package/dist/migration/generator.d.cts +45 -2
  34. package/dist/migration/generator.d.ts +45 -2
  35. package/dist/migration/generator.js +39 -19
  36. package/dist/migration/generator.js.map +1 -1
  37. package/dist/migration/index.cjs +3479 -1145
  38. package/dist/migration/index.cjs.map +1 -1
  39. package/dist/migration/index.d.cts +21 -4
  40. package/dist/migration/index.d.ts +21 -4
  41. package/dist/migration/index.js +3433 -1142
  42. package/dist/migration/index.js.map +1 -1
  43. package/dist/migration/snapshot.cjs +2200 -804
  44. package/dist/migration/snapshot.cjs.map +1 -1
  45. package/dist/migration/snapshot.d.cts +24 -66
  46. package/dist/migration/snapshot.d.ts +24 -66
  47. package/dist/migration/snapshot.js +2198 -801
  48. package/dist/migration/snapshot.js.map +1 -1
  49. package/dist/migration/utils/index.cjs +51 -9
  50. package/dist/migration/utils/index.cjs.map +1 -1
  51. package/dist/migration/utils/index.d.cts +1 -1
  52. package/dist/migration/utils/index.d.ts +1 -1
  53. package/dist/migration/utils/index.js +50 -10
  54. package/dist/migration/utils/index.js.map +1 -1
  55. package/dist/server.cjs +3837 -1248
  56. package/dist/server.cjs.map +1 -1
  57. package/dist/server.d.cts +6 -4
  58. package/dist/server.d.ts +6 -4
  59. package/dist/server.js +3791 -1245
  60. package/dist/server.js.map +1 -1
  61. package/dist/{type-mapper-Bu9qjCdd.d.cts → type-mapper-BSPTj8t5.d.cts} +23 -1
  62. package/dist/{type-mapper-JBaVuKE2.d.ts → type-mapper-D31WYDee.d.ts} +23 -1
  63. package/dist/types-B_MLb1Ob.d.ts +475 -0
  64. package/dist/types-upZBVMfb.d.cts +475 -0
  65. package/dist/verify-BF72n2df.d.ts +402 -0
  66. package/dist/verify-DTzYpiXX.d.cts +402 -0
  67. package/package.json +7 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0](https://github.com/dastron/pocketbase-zod-schema/compare/pocketbase-zod-schema-v0.7.2...pocketbase-zod-schema-v1.0.0) (2026-07-28)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * remove the static migration parser, execute migrations only
9
+
10
+ ### Features
11
+
12
+ * add migration execution engine replacing static analysis ([5fc9e81](https://github.com/dastron/pocketbase-zod-schema/commit/5fc9e8191fb7863dd1b96f87c165a44033f05341))
13
+ * add migration execution engine replacing static analysis as default ([eb094c7](https://github.com/dastron/pocketbase-zod-schema/commit/eb094c79f4f57c71402e68223e12122c99b8f474))
14
+ * add migration execution engine replacing static analysis as default ([b787cf0](https://github.com/dastron/pocketbase-zod-schema/commit/b787cf0ba4a9e0e3d80f4eb0951e90890bf33ccd))
15
+ * deprecation warning for static processor ([865f96d](https://github.com/dastron/pocketbase-zod-schema/commit/865f96db51eefd05fc53ff8e412cd4f49e9853e5))
16
+ * **engine:** _migrations awareness, record simulation, goja lint ([ce799d3](https://github.com/dastron/pocketbase-zod-schema/commit/ce799d38d2d50e6596cf7bb880656caff7c5631e))
17
+ * **engine:** verify down migrations round-trip ([35509f5](https://github.com/dastron/pocketbase-zod-schema/commit/35509f500ae8034a095115e8263779daef2aed94))
18
+ * **engine:** verify down migrations round-trip ([0b74452](https://github.com/dastron/pocketbase-zod-schema/commit/0b74452ff8c564c579de2462407908b2207ff0cb))
19
+ * remove the static migration parser, execute migrations only ([b5bb6cb](https://github.com/dastron/pocketbase-zod-schema/commit/b5bb6cb204a7132041b112a1c68051bdd4238a02))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * address review comments ([61e5441](https://github.com/dastron/pocketbase-zod-schema/commit/61e544128d57b49c3377ec9938d06e47d17a3df5))
25
+ * e2e test regression ([cb6e166](https://github.com/dastron/pocketbase-zod-schema/commit/cb6e166b0fa4850087344ae999b9e5098950e16b))
26
+ * flakey test ([4902cf2](https://github.com/dastron/pocketbase-zod-schema/commit/4902cf23aa944872a072d814f12590bd3182236c))
27
+
3
28
  ## [0.7.2](https://github.com/dastron/pocketbase-zod-schema/compare/pocketbase-zod-schema-v0.7.1...pocketbase-zod-schema-v0.7.2) (2026-07-26)
4
29
 
5
30
 
package/README.md CHANGED
@@ -7,8 +7,13 @@ Define your PocketBase collections using Zod schemas and automatically generate
7
7
  - **Type-safe schema definitions** - Use Zod to define your PocketBase collections with full TypeScript support
8
8
  - **Automatic migrations** - Generate PocketBase-compatible migration files from your schema changes
9
9
  - **Relation support** - Easily define single and multiple relations between collections
10
+ - **View collections** - Read-only collections backed by a SQL query, declared alongside the rest
10
11
  - **Permission templates** - Built-in templates for common permission patterns
11
12
  - **Index definitions** - Declare indexes alongside your schema
13
+ - **Execution engine** - Current state is reconstructed by *executing* your migrations in a
14
+ simulated PocketBase JSVM, so loops, helper functions and computed values are understood
15
+ - **Verification & linting** - Round-trip `up()`/`down()` before writing a migration, and catch
16
+ JavaScript that PocketBase's goja runtime cannot run
12
17
  - **CLI & programmatic API** - Use the CLI for quick generation or the API for custom workflows
13
18
 
14
19
  ## Installation
@@ -260,16 +265,18 @@ The library provides explicit field helper functions for all PocketBase field ty
260
265
 
261
266
  **FileField(options?)**
262
267
  - `mimeTypes?: string[]` - Allowed MIME types (e.g., `["image/*", "application/pdf"]`)
263
- - `maxSize?: number` - Maximum file size in bytes
268
+ - `maxSize?: ByteSize` - Maximum file size: a number of bytes, or a string with a `K`/`M`/`G`
269
+ suffix (`"5M"`, `"1G"`; max `"8G"`)
264
270
  - `thumbs?: string[]` - Thumbnail sizes to generate (e.g., `["100x100", "200x200"]`)
265
271
  - `protected?: boolean` - Whether file requires auth to access
266
- - Returns: `z.ZodType<File>`
272
+ - Returns: `z.ZodType<string, File | string>` — accepts a `File` on input, parses to the stored
273
+ filename string
267
274
 
268
275
  **FilesField(options?)**
269
276
  - All `FileField` options plus:
270
277
  - `minSelect?: number` - Minimum number of files required
271
278
  - `maxSelect?: number` - Maximum number of files allowed
272
- - Returns: `z.ZodArray<z.ZodType<File>>`
279
+ - Returns: `z.ZodType<string[], (File | string)[]>`
273
280
 
274
281
  **JSONField(schema?)**
275
282
  - `schema?: z.ZodTypeAny` - Optional Zod schema for JSON structure validation
@@ -419,13 +426,61 @@ const ProjectSchema = z.object({
419
426
  **`RelationField(config)`** - Single relation
420
427
  - `collection: string` - Target collection name (required)
421
428
  - `cascadeDelete?: boolean` - Delete related records when this record is deleted (default: `false`)
429
+ - `displayFields?: string[] | null` - Fields to display in the PocketBase admin UI
422
430
 
423
431
  **`RelationsField(config)`** - Multiple relations
424
- - `collection: string` - Target collection name (required)
425
- - `cascadeDelete?: boolean` - Delete related records when this record is deleted (default: `false`)
432
+ - All `RelationField` options plus:
426
433
  - `minSelect?: number` - Minimum number of relations required (default: `0`)
427
434
  - `maxSelect?: number` - Maximum number of relations allowed (default: `999`)
428
435
 
436
+ > Relations can also be detected from field naming (a `z.string()` starting with an uppercase
437
+ > letter, or a `z.array(z.string())` containing one). That fallback exists for backward
438
+ > compatibility — prefer the helpers, which name the target collection explicitly.
439
+
440
+ ### Defining View Collections
441
+
442
+ Use `defineView()` for a read-only collection backed by a SQL query. PocketBase derives the
443
+ collection's fields by running the query, so the Zod schema describes the row shape for TypeScript
444
+ only:
445
+
446
+ ```typescript
447
+ import { z } from "zod";
448
+ import { baseSchema, defineView, sql } from "pocketbase-zod-schema/schema";
449
+
450
+ export const ProductStatsSchema = z
451
+ .object({
452
+ vendor: z.string(),
453
+ productCount: z.number(),
454
+ })
455
+ .extend(baseSchema);
456
+
457
+ export default defineView({
458
+ collectionName: "ProductStats",
459
+ schema: ProductStatsSchema,
460
+ viewQuery: sql`
461
+ SELECT p.vendor AS id,
462
+ p.vendor AS vendor,
463
+ COUNT(*) AS productCount
464
+ FROM products p
465
+ GROUP BY p.vendor
466
+ `,
467
+ permissions: {
468
+ listRule: "vendor.owner = @request.auth.id",
469
+ viewRule: "vendor.owner = @request.auth.id",
470
+ },
471
+ });
472
+ ```
473
+
474
+ - Views are read-only: `defineView()` accepts only `listRule` and `viewRule`, and rejects indexes.
475
+ - The outermost `SELECT` must expose a unique `id` column.
476
+ - The generated migration contains the query and **no** `fields`/`indexes` array.
477
+ - Editing the SQL produces an in-place update that keeps the collection id stable; re-indenting it
478
+ produces no migration at all.
479
+ - Deleting a view is not a destructive change — a view stores no data.
480
+
481
+ `defineCollection({ type: "view", viewQuery })` is equivalent, but `defineView()` turns those
482
+ constraints into compile errors instead of apply-time failures.
483
+
429
484
  ### Defining Permissions
430
485
 
431
486
  Use `defineCollection()` with permissions, or `withPermissions()` to attach API rules to your schema:
@@ -475,7 +530,22 @@ const PostSchemaAlt = withPermissions(
475
530
  |----------|-------------|
476
531
  | `"public"` | All operations are public (no auth required) |
477
532
  | `"authenticated"` | All operations require authentication |
478
- | `"owner-only"` | Only the owner can perform operations |
533
+ | `"owner-only"` | Only the owner can perform operations (`ownerField`, default `"User"`) |
534
+ | `"admin-only"` | Requires `@request.auth.<roleField> = "admin"` (`roleField`, default `"role"`) |
535
+ | `"read-public"` | Public read, authenticated write |
536
+ | `"custom"` | No base rules — `customRules` supplies everything |
537
+
538
+ Two more have no template name; call them and pass the result as `permissions`:
539
+
540
+ ```typescript
541
+ import { PermissionTemplates } from "pocketbase-zod-schema/schema";
542
+
543
+ PermissionTemplates.locked(); // every rule null (superusers only)
544
+ PermissionTemplates.readOnlyAuthenticated(); // authenticated read, writes locked
545
+ ```
546
+
547
+ Rule values: `null` = superusers only, `""` = public, any other string = a PocketBase filter
548
+ expression. `manageRule` applies to auth collections only.
479
549
 
480
550
  #### Template with Custom Overrides
481
551
 
@@ -557,11 +627,76 @@ npx pocketbase-migrate generate
557
627
  # Show what would be generated without writing files
558
628
  npx pocketbase-migrate status
559
629
 
630
+ # Compare the files on disk against PocketBase's _migrations table
631
+ npx pocketbase-migrate status --verify
632
+
633
+ # Check migrations against PocketBase's goja runtime (Node-only globals, async, ...)
634
+ npx pocketbase-migrate lint
635
+
560
636
  # Generate TypeScript definitions from schemas
561
637
  npx pocketbase-migrate generate-types
562
638
 
563
639
  # Force generation even with destructive changes
564
640
  npx pocketbase-migrate generate --force
641
+
642
+ # Round-trip up() and down() before writing; refuse a migration that doesn't roll back
643
+ npx pocketbase-migrate generate --verify
644
+
645
+ # Restrict the diff to matching collection or field names (regex supported)
646
+ npx pocketbase-migrate generate Posts Comments
647
+ ```
648
+
649
+ ### `generate` Command
650
+
651
+ ```bash
652
+ pocketbase-migrate generate [filters...] [options]
653
+
654
+ Options:
655
+ -o, --output <directory> Output directory for migration files
656
+ -f, --force Force generation even with destructive changes or duplicates
657
+ --dry-run Show what would be generated without writing files
658
+ --schema-dir <directory> Directory containing Zod schema files
659
+ --verify Execute up() and down() before writing
660
+ --no-verify Skip verification even when enabled in the config file
661
+ ```
662
+
663
+ One file is written per collection operation.
664
+
665
+ ### `status` Command
666
+
667
+ ```bash
668
+ pocketbase-migrate status [options]
669
+
670
+ Options:
671
+ --schema-dir <directory> Directory containing Zod schema files
672
+ --json Output status as JSON
673
+ --verify Compare disk against PocketBase's _migrations table; exit non-zero on drift
674
+ --pb-data <path> PocketBase data directory or data.db file
675
+ ```
676
+
677
+ `--verify` needs Node >= 22.5 (`node:sqlite`).
678
+
679
+ ### `lint` Command
680
+
681
+ ```bash
682
+ pocketbase-migrate lint [files...] [options]
683
+
684
+ Options:
685
+ -o, --output <directory> Directory containing migration files
686
+ --no-execute Static checks only
687
+ ```
688
+
689
+ Catches JavaScript that runs in Node but not in goja: `require`, `process`, `fetch`, `setTimeout`,
690
+ `async`/`await`, `import`/`export`, class fields. Exits non-zero on any error-severity finding.
691
+
692
+ ### Global Options
693
+
694
+ ```
695
+ -c, --config <path> Configuration file path
696
+ -v, --version Print the version ← -v is version, not verbose
697
+ --verbose Enable verbose logging
698
+ --quiet Suppress non-essential output
699
+ --no-color Disable colored output
565
700
  ```
566
701
 
567
702
  ### `generate-types` Command
@@ -572,10 +707,8 @@ Generate type-safe TypeScript definitions from your Zod schemas.
572
707
  pocketbase-migrate generate-types [options]
573
708
 
574
709
  Options:
575
- -c, --config <path> Configuration file path
576
710
  -o, --output <path> Output file path (default: pocketbase-types.ts)
577
711
  --schema-dir <directory> Directory containing Zod schema files
578
- -v, --verbose Enable verbose logging
579
712
  ```
580
713
 
581
714
  **What it generates:**
@@ -642,24 +775,42 @@ export default {
642
775
  schema: {
643
776
  // Directory containing your Zod schema files
644
777
  directory: "./src/schema",
645
-
646
- // Files to exclude from schema discovery
778
+
779
+ // Files to exclude from schema discovery (replaces the default list)
647
780
  exclude: ["*.test.ts", "*.spec.ts", "base.ts", "index.ts"],
648
781
  },
649
782
  migrations: {
650
- // Directory to output migration files
783
+ // Directory to output migration files, and to reconstruct state from
651
784
  directory: "./pocketbase/pb_migrations",
785
+
786
+ // Round-trip up()/down() before writing a migration (default: false)
787
+ verify: false,
788
+
789
+ // PocketBase data directory, for reading the _migrations table.
790
+ // "" means the pb_data directory next to the migrations directory.
791
+ dataDirectory: "",
652
792
  },
653
793
  diff: {
654
794
  // Warn when collections or fields would be deleted
655
795
  warnOnDelete: true,
656
-
796
+
657
797
  // Require --force flag for destructive changes
658
798
  requireForceForDestructive: true,
659
799
  },
800
+ typeGen: {
801
+ // Default output for `generate-types`
802
+ outPath: "pocketbase-types.ts",
803
+ },
660
804
  };
661
805
  ```
662
806
 
807
+ There is no snapshot file to configure. The current database state is reconstructed by executing
808
+ the migration files: the newest `*_collections_snapshot.js` plus everything after it.
809
+
810
+ Environment overrides: `MIGRATION_SCHEMA_DIR`, `MIGRATION_SCHEMA_EXCLUDE`, `MIGRATION_OUTPUT_DIR`,
811
+ `MIGRATION_VERIFY`, `MIGRATION_DATA_DIR`, `MIGRATION_REQUIRE_FORCE`. Precedence is
812
+ CLI > environment > config file > defaults.
813
+
663
814
  ---
664
815
 
665
816
  ## Programmatic API
@@ -671,7 +822,7 @@ import {
671
822
  parseSchemaFiles,
672
823
  compare,
673
824
  generate,
674
- loadSnapshotIfExists,
825
+ loadSnapshotWithMigrations,
675
826
  } from "pocketbase-zod-schema/migration";
676
827
 
677
828
  async function generateMigration() {
@@ -681,26 +832,31 @@ async function generateMigration() {
681
832
  // Parse all schema files
682
833
  const currentSchema = await parseSchemaFiles(schemaDir);
683
834
 
684
- // Load the last known state from existing migrations
685
- const previousSnapshot = loadSnapshotIfExists({
686
- migrationsPath: migrationsDir
835
+ // Reconstruct the current database state by executing the existing migrations.
836
+ // Returns null when there is nothing to replay; throws SnapshotError if a
837
+ // migration cannot be executed.
838
+ const previousSnapshot = loadSnapshotWithMigrations({
839
+ migrationsPath: migrationsDir,
687
840
  });
688
841
 
689
842
  // Compare schemas and detect changes
690
843
  const diff = compare(currentSchema, previousSnapshot);
691
844
 
692
- // Generate migration file
693
- if (diff.collectionsToCreate.length > 0 ||
694
- diff.collectionsToModify.length > 0 ||
695
- diff.collectionsToDelete.length > 0) {
696
- const migrationPath = generate(diff, migrationsDir);
697
- console.log(`Migration created: ${migrationPath}`);
698
- } else {
699
- console.log("No changes detected");
700
- }
845
+ // Write one migration file per collection operation; returns the paths written
846
+ const paths = generate(diff, migrationsDir);
847
+
848
+ console.log(paths.length ? `Migrations created:\n${paths.join("\n")}` : "No changes detected");
701
849
  }
702
850
  ```
703
851
 
852
+ > Use `loadSnapshotWithMigrations`, not `loadSnapshotIfExists`. The latter executes **only** the
853
+ > newest snapshot file and ignores every migration after it, so it is almost never the current
854
+ > state.
855
+
856
+ The execution engine, round-trip verification and the goja lint are also available directly from
857
+ `pocketbase-zod-schema/migration/engine` — see the
858
+ [Execution Engine guide](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/EXECUTION_ENGINE.md).
859
+
704
860
  ---
705
861
 
706
862
  ## Complete Example
@@ -821,6 +977,23 @@ export const CommentCollection = defineCollection({
821
977
 
822
978
  ---
823
979
 
980
+ ## Documentation
981
+
982
+ Full guides live in the repository:
983
+
984
+ - [API Reference](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/API.md)
985
+ - [Execution Engine](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/EXECUTION_ENGINE.md)
986
+ - [Configuration](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/CONFIGURATION.md)
987
+ - [Migration Guide](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/MIGRATION_GUIDE.md)
988
+ - [Type Mapping](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/TYPE_MAPPING.md)
989
+ - [View Collections](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/VIEW_COLLECTIONS.md)
990
+ - [Naming Conventions](https://github.com/dastron/pocketbase-zod-schema/blob/main/docs/NAMING_CONVENTIONS.md)
991
+
992
+ ## Requirements
993
+
994
+ Node.js 20 or higher. `status --verify`, which reads PocketBase's `_migrations` table, additionally
995
+ requires Node 22.5+ for `node:sqlite`.
996
+
824
997
  ## License
825
998
 
826
999
  MIT