pocketbase-zod-schema 0.2.3 → 0.2.4

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/dist/index.cjs CHANGED
@@ -42,440 +42,6 @@ var StatusEnum = zod.z.enum([
42
42
  "fail"
43
43
  // Failed project at any stage
44
44
  ]);
45
- var baseSchema = {
46
- id: zod.z.string().describe("unique id"),
47
- collectionId: zod.z.string().describe("collection id"),
48
- collectionName: zod.z.string().describe("collection name"),
49
- expand: zod.z.record(zod.z.any()).describe("expandable fields"),
50
- created: zod.z.string().describe("creation timestamp"),
51
- updated: zod.z.string().describe("last update timestamp")
52
- };
53
- var baseSchemaWithTimestamps = {
54
- ...baseSchema,
55
- created: zod.z.string().describe("creation timestamp"),
56
- updated: zod.z.string().describe("last update timestamp")
57
- };
58
- var baseImageFileSchema = {
59
- ...baseSchema,
60
- thumbnailURL: zod.z.string().optional(),
61
- imageFiles: zod.z.array(zod.z.string())
62
- };
63
- var inputImageFileSchema = {
64
- imageFiles: zod.z.array(zod.z.instanceof(File))
65
- };
66
- var omitImageFilesSchema = {
67
- imageFiles: true
68
- };
69
- function textField(options) {
70
- let schema = zod.z.string();
71
- if (options?.min !== void 0) schema = schema.min(options.min);
72
- if (options?.max !== void 0) schema = schema.max(options.max);
73
- if (options?.pattern !== void 0) schema = schema.regex(options.pattern);
74
- return schema;
75
- }
76
- function emailField() {
77
- return zod.z.string().email();
78
- }
79
- function urlField() {
80
- return zod.z.string().url();
81
- }
82
- function numberField(options) {
83
- let schema = zod.z.number();
84
- if (options?.min !== void 0) schema = schema.min(options.min);
85
- if (options?.max !== void 0) schema = schema.max(options.max);
86
- return schema;
87
- }
88
- function boolField() {
89
- return zod.z.boolean();
90
- }
91
- function dateField() {
92
- return zod.z.date();
93
- }
94
- function selectField(values) {
95
- return zod.z.enum(values);
96
- }
97
- function jsonField(schema) {
98
- return schema ?? zod.z.record(zod.z.any());
99
- }
100
- function fileField() {
101
- return zod.z.instanceof(File);
102
- }
103
- function filesField(options) {
104
- let schema = zod.z.array(zod.z.instanceof(File));
105
- if (options?.min !== void 0) schema = schema.min(options.min);
106
- if (options?.max !== void 0) schema = schema.max(options.max);
107
- return schema;
108
- }
109
- var RELATION_METADATA_KEY = "__pocketbase_relation__";
110
- function RelationField(config) {
111
- const metadata = {
112
- [RELATION_METADATA_KEY]: {
113
- type: "single",
114
- collection: config.collection,
115
- cascadeDelete: config.cascadeDelete ?? false,
116
- maxSelect: 1,
117
- minSelect: 0
118
- }
119
- };
120
- return zod.z.string().describe(JSON.stringify(metadata));
121
- }
122
- function RelationsField(config) {
123
- const metadata = {
124
- [RELATION_METADATA_KEY]: {
125
- type: "multiple",
126
- collection: config.collection,
127
- cascadeDelete: config.cascadeDelete ?? false,
128
- maxSelect: config.maxSelect ?? 999,
129
- minSelect: config.minSelect ?? 0
130
- }
131
- };
132
- let schema = zod.z.array(zod.z.string());
133
- if (config.minSelect !== void 0) {
134
- schema = schema.min(config.minSelect);
135
- }
136
- if (config.maxSelect !== void 0) {
137
- schema = schema.max(config.maxSelect);
138
- }
139
- return schema.describe(JSON.stringify(metadata));
140
- }
141
- function extractRelationMetadata(description) {
142
- if (!description) return null;
143
- try {
144
- const parsed = JSON.parse(description);
145
- if (parsed[RELATION_METADATA_KEY]) {
146
- return parsed[RELATION_METADATA_KEY];
147
- }
148
- } catch {
149
- }
150
- return null;
151
- }
152
- function editorField() {
153
- return zod.z.string();
154
- }
155
- function geoPointField() {
156
- return zod.z.object({
157
- lon: zod.z.number(),
158
- lat: zod.z.number()
159
- });
160
- }
161
- function withPermissions(schema, config) {
162
- const metadata = {
163
- permissions: config
164
- };
165
- return schema.describe(JSON.stringify(metadata));
166
- }
167
- function withIndexes(schema, indexes) {
168
- let existingMetadata = {};
169
- if (schema.description) {
170
- try {
171
- existingMetadata = JSON.parse(schema.description);
172
- } catch {
173
- }
174
- }
175
- const metadata = {
176
- ...existingMetadata,
177
- indexes
178
- };
179
- return schema.describe(JSON.stringify(metadata));
180
- }
181
- function defineCollection(config) {
182
- const { collectionName, schema, permissions, indexes, type, ...futureOptions } = config;
183
- const metadata = {
184
- collectionName
185
- };
186
- if (type) {
187
- metadata.type = type;
188
- }
189
- if (permissions) {
190
- metadata.permissions = permissions;
191
- }
192
- if (indexes) {
193
- metadata.indexes = indexes;
194
- }
195
- if (Object.keys(futureOptions).length > 0) {
196
- Object.assign(metadata, futureOptions);
197
- }
198
- return schema.describe(JSON.stringify(metadata));
199
- }
200
-
201
- // src/utils/permission-templates.ts
202
- var PermissionTemplates = {
203
- /**
204
- * Public access - anyone can perform all operations
205
- */
206
- public: () => ({
207
- listRule: "",
208
- viewRule: "",
209
- createRule: "",
210
- updateRule: "",
211
- deleteRule: ""
212
- }),
213
- /**
214
- * Authenticated users only - requires valid authentication for all operations
215
- */
216
- authenticated: () => ({
217
- listRule: '@request.auth.id != ""',
218
- viewRule: '@request.auth.id != ""',
219
- createRule: '@request.auth.id != ""',
220
- updateRule: '@request.auth.id != ""',
221
- deleteRule: '@request.auth.id != ""'
222
- }),
223
- /**
224
- * Owner-only access - users can only manage their own records
225
- * @param ownerField - Name of the relation field pointing to user (default: 'User')
226
- */
227
- ownerOnly: (ownerField = "User") => ({
228
- listRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
229
- viewRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
230
- createRule: '@request.auth.id != ""',
231
- updateRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
232
- deleteRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`
233
- }),
234
- /**
235
- * Admin/superuser only access
236
- * Assumes a 'role' field exists with 'admin' value
237
- * @param roleField - Name of the role field (default: 'role')
238
- */
239
- adminOnly: (roleField = "role") => ({
240
- listRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
241
- viewRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
242
- createRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
243
- updateRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
244
- deleteRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`
245
- }),
246
- /**
247
- * Public read, authenticated write
248
- * Anyone can list/view, but only authenticated users can create/update/delete
249
- */
250
- readPublic: () => ({
251
- listRule: "",
252
- viewRule: "",
253
- createRule: '@request.auth.id != ""',
254
- updateRule: '@request.auth.id != ""',
255
- deleteRule: '@request.auth.id != ""'
256
- }),
257
- /**
258
- * Locked access - only superusers can perform operations
259
- * All rules are set to null (locked)
260
- */
261
- locked: () => ({
262
- listRule: null,
263
- viewRule: null,
264
- createRule: null,
265
- updateRule: null,
266
- deleteRule: null
267
- }),
268
- /**
269
- * Read-only authenticated - authenticated users can read, no write access
270
- */
271
- readOnlyAuthenticated: () => ({
272
- listRule: '@request.auth.id != ""',
273
- viewRule: '@request.auth.id != ""',
274
- createRule: null,
275
- updateRule: null,
276
- deleteRule: null
277
- })
278
- };
279
- function resolveTemplate(config) {
280
- let baseRules;
281
- switch (config.template) {
282
- case "public":
283
- baseRules = PermissionTemplates.public();
284
- break;
285
- case "authenticated":
286
- baseRules = PermissionTemplates.authenticated();
287
- break;
288
- case "owner-only":
289
- baseRules = PermissionTemplates.ownerOnly(config.ownerField);
290
- break;
291
- case "admin-only":
292
- baseRules = PermissionTemplates.adminOnly(config.roleField);
293
- break;
294
- case "read-public":
295
- baseRules = PermissionTemplates.readPublic();
296
- break;
297
- case "custom":
298
- baseRules = {};
299
- break;
300
- default: {
301
- const _exhaustive = config.template;
302
- throw new Error(`Unknown template type: ${_exhaustive}`);
303
- }
304
- }
305
- return {
306
- ...baseRules,
307
- ...config.customRules
308
- };
309
- }
310
- function isTemplateConfig(config) {
311
- return "template" in config;
312
- }
313
- function isPermissionSchema(config) {
314
- return "listRule" in config || "viewRule" in config || "createRule" in config || "updateRule" in config || "deleteRule" in config || "manageRule" in config;
315
- }
316
- function validatePermissionConfig(config, isAuthCollection2 = false) {
317
- const result = {
318
- valid: true,
319
- errors: [],
320
- warnings: []
321
- };
322
- let permissions;
323
- if (isTemplateConfig(config)) {
324
- if (config.template === "owner-only" && !config.ownerField) {
325
- result.warnings.push("owner-only template without ownerField specified - using default 'User'");
326
- }
327
- if (config.template === "admin-only" && !config.roleField) {
328
- result.warnings.push("admin-only template without roleField specified - using default 'role'");
329
- }
330
- permissions = resolveTemplate(config);
331
- } else {
332
- permissions = config;
333
- }
334
- if (permissions.manageRule !== void 0 && !isAuthCollection2) {
335
- result.errors.push("manageRule is only valid for auth collections");
336
- result.valid = false;
337
- }
338
- const ruleTypes = ["listRule", "viewRule", "createRule", "updateRule", "deleteRule"];
339
- if (isAuthCollection2) {
340
- ruleTypes.push("manageRule");
341
- }
342
- for (const ruleType of ruleTypes) {
343
- const rule = permissions[ruleType];
344
- if (rule !== void 0 && rule !== null && rule !== "") {
345
- const ruleValidation = validateRuleExpression(rule);
346
- if (!ruleValidation.valid) {
347
- result.errors.push(`${ruleType}: ${ruleValidation.errors.join(", ")}`);
348
- result.valid = false;
349
- }
350
- result.warnings.push(...ruleValidation.warnings.map((w) => `${ruleType}: ${w}`));
351
- }
352
- }
353
- return result;
354
- }
355
- function validateRuleExpression(expression) {
356
- const result = {
357
- valid: true,
358
- errors: [],
359
- warnings: []
360
- };
361
- if (expression === null || expression === "") {
362
- return result;
363
- }
364
- let parenCount = 0;
365
- for (const char of expression) {
366
- if (char === "(") parenCount++;
367
- if (char === ")") parenCount--;
368
- if (parenCount < 0) {
369
- result.errors.push("Unbalanced parentheses");
370
- result.valid = false;
371
- return result;
372
- }
373
- }
374
- if (parenCount !== 0) {
375
- result.errors.push("Unbalanced parentheses");
376
- result.valid = false;
377
- }
378
- if (expression.includes("==")) {
379
- result.warnings.push("Use '=' instead of '==' for equality comparison");
380
- }
381
- const requestRefs = expression.match(/@request\.[a-zA-Z_][a-zA-Z0-9_.]*/g) || [];
382
- for (const ref of requestRefs) {
383
- const isValid = ref.startsWith("@request.auth.") || ref === "@request.method" || ref === "@request.context" || ref.startsWith("@request.body.") || ref.startsWith("@request.query.") || ref.startsWith("@request.headers.");
384
- if (!isValid) {
385
- result.errors.push(`Invalid @request reference: '${ref}'`);
386
- result.valid = false;
387
- }
388
- }
389
- return result;
390
- }
391
- function createPermissions(permissions) {
392
- return {
393
- listRule: permissions.listRule ?? null,
394
- viewRule: permissions.viewRule ?? null,
395
- createRule: permissions.createRule ?? null,
396
- updateRule: permissions.updateRule ?? null,
397
- deleteRule: permissions.deleteRule ?? null,
398
- manageRule: permissions.manageRule ?? null
399
- };
400
- }
401
- function mergePermissions(...schemas) {
402
- const merged = {
403
- listRule: null,
404
- viewRule: null,
405
- createRule: null,
406
- updateRule: null,
407
- deleteRule: null,
408
- manageRule: null
409
- };
410
- for (const schema of schemas) {
411
- if (schema.listRule !== void 0) merged.listRule = schema.listRule;
412
- if (schema.viewRule !== void 0) merged.viewRule = schema.viewRule;
413
- if (schema.createRule !== void 0) merged.createRule = schema.createRule;
414
- if (schema.updateRule !== void 0) merged.updateRule = schema.updateRule;
415
- if (schema.deleteRule !== void 0) merged.deleteRule = schema.deleteRule;
416
- if (schema.manageRule !== void 0) merged.manageRule = schema.manageRule;
417
- }
418
- return merged;
419
- }
420
- var ProjectInputSchema = zod.z.object({
421
- // Required fields
422
- title: zod.z.string(),
423
- content: zod.z.string(),
424
- status: StatusEnum,
425
- summary: zod.z.string().optional(),
426
- OwnerUser: RelationField({ collection: "Users" }),
427
- SubscriberUsers: RelationsField({ collection: "Users" })
428
- }).extend(inputImageFileSchema);
429
- var ProjectSchema = ProjectInputSchema.omit(omitImageFilesSchema).extend(baseImageFileSchema);
430
- var ProjectCollection = defineCollection({
431
- collectionName: "Projects",
432
- schema: ProjectSchema,
433
- permissions: {
434
- template: "owner-only",
435
- ownerField: "OwnerUser",
436
- customRules: {
437
- listRule: '@request.auth.id != ""',
438
- viewRule: '@request.auth.id != "" && (OwnerUser = @request.auth.id || SubscriberUsers ?= @request.auth.id)'
439
- }
440
- }
441
- });
442
- var UserInputSchema = zod.z.object({
443
- name: zod.z.string().optional(),
444
- email: zod.z.string().email(),
445
- password: zod.z.string().min(8, "Password must be at least 8 characters"),
446
- passwordConfirm: zod.z.string(),
447
- avatar: zod.z.instanceof(File).optional()
448
- });
449
- var UserCollectionSchema = zod.z.object({
450
- name: zod.z.string().optional(),
451
- email: zod.z.string().email(),
452
- password: zod.z.string().min(8, "Password must be at least 8 characters"),
453
- avatar: zod.z.instanceof(File).optional()
454
- });
455
- var UserSchema = UserCollectionSchema.extend(baseSchema);
456
- var UserCollection = defineCollection({
457
- collectionName: "users",
458
- type: "auth",
459
- schema: UserSchema,
460
- permissions: {
461
- // Users can list their own profile
462
- listRule: "id = @request.auth.id",
463
- // Users can view their own profile
464
- viewRule: "id = @request.auth.id",
465
- // Anyone can create an account (sign up)
466
- createRule: "",
467
- // Users can only update their own profile
468
- updateRule: "id = @request.auth.id",
469
- // Users can only delete their own account
470
- deleteRule: "id = @request.auth.id"
471
- // manageRule is null in PocketBase default (not set)
472
- },
473
- indexes: [
474
- // PocketBase's default indexes for auth collections
475
- "CREATE UNIQUE INDEX `idx_tokenKey__pb_users_auth_` ON `users` (`tokenKey`)",
476
- "CREATE UNIQUE INDEX `idx_email__pb_users_auth_` ON `users` (`email`) WHERE `email` != ''"
477
- ]
478
- });
479
45
  var BaseMutator = class {
480
46
  pb;
481
47
  // Define a default property that subclasses will override
@@ -748,78 +314,436 @@ var BaseMutator = class {
748
314
  throw error;
749
315
  }
750
316
  /**
751
- * Check if an error is a "not found" error
317
+ * Check if an error is a "not found" error
318
+ */
319
+ isNotFoundError(error) {
320
+ return error instanceof Error && (error.message.includes("404") || error.message.toLowerCase().includes("not found"));
321
+ }
322
+ /**
323
+ * Standard error handling wrapper (legacy method, consider using handleError instead)
324
+ */
325
+ errorWrapper(error) {
326
+ console.error(`Error in ${this.constructor.name}:`, error);
327
+ throw error;
328
+ }
329
+ /**
330
+ * Subscribe to changes on a specific record
331
+ * @param id The ID of the record to subscribe to
332
+ * @param callback Function to call when changes occur
333
+ * @param expand Optional expand parameters
334
+ * @returns Promise that resolves to an unsubscribe function
335
+ */
336
+ async subscribeToRecord(id, callback, expand) {
337
+ const finalExpand = this.prepareExpand(expand);
338
+ const options = finalExpand ? { expand: finalExpand } : {};
339
+ return this.getCollection().subscribe(id, callback, options);
340
+ }
341
+ /**
342
+ * Subscribe to changes on the entire collection
343
+ * @param callback Function to call when changes occur
344
+ * @param expand Optional expand parameters
345
+ * @returns Promise that resolves to an unsubscribe function
346
+ */
347
+ async subscribeToCollection(callback, expand) {
348
+ const finalExpand = this.prepareExpand(expand);
349
+ const options = finalExpand ? { expand: finalExpand } : {};
350
+ return this.getCollection().subscribe("*", callback, options);
351
+ }
352
+ /**
353
+ * Unsubscribe from a specific record's changes
354
+ * @param id The ID of the record to unsubscribe from
355
+ */
356
+ unsubscribeFromRecord(id) {
357
+ this.getCollection().unsubscribe(id);
358
+ }
359
+ /**
360
+ * Unsubscribe from collection-wide changes
361
+ */
362
+ unsubscribeFromCollection() {
363
+ this.getCollection().unsubscribe("*");
364
+ }
365
+ /**
366
+ * Unsubscribe from all subscriptions in this collection
367
+ */
368
+ unsubscribeAll() {
369
+ this.getCollection().unsubscribe();
370
+ }
371
+ };
372
+ var baseSchema = {
373
+ id: zod.z.string().describe("unique id"),
374
+ collectionId: zod.z.string().describe("collection id"),
375
+ collectionName: zod.z.string().describe("collection name"),
376
+ expand: zod.z.record(zod.z.any()).describe("expandable fields"),
377
+ created: zod.z.string().describe("creation timestamp"),
378
+ updated: zod.z.string().describe("last update timestamp")
379
+ };
380
+ var baseSchemaWithTimestamps = {
381
+ ...baseSchema,
382
+ created: zod.z.string().describe("creation timestamp"),
383
+ updated: zod.z.string().describe("last update timestamp")
384
+ };
385
+ var baseImageFileSchema = {
386
+ ...baseSchema,
387
+ thumbnailURL: zod.z.string().optional(),
388
+ imageFiles: zod.z.array(zod.z.string())
389
+ };
390
+ var inputImageFileSchema = {
391
+ imageFiles: zod.z.array(zod.z.instanceof(File))
392
+ };
393
+ var omitImageFilesSchema = {
394
+ imageFiles: true
395
+ };
396
+ function textField(options) {
397
+ let schema = zod.z.string();
398
+ if (options?.min !== void 0) schema = schema.min(options.min);
399
+ if (options?.max !== void 0) schema = schema.max(options.max);
400
+ if (options?.pattern !== void 0) schema = schema.regex(options.pattern);
401
+ return schema;
402
+ }
403
+ function emailField() {
404
+ return zod.z.string().email();
405
+ }
406
+ function urlField() {
407
+ return zod.z.string().url();
408
+ }
409
+ function numberField(options) {
410
+ let schema = zod.z.number();
411
+ if (options?.min !== void 0) schema = schema.min(options.min);
412
+ if (options?.max !== void 0) schema = schema.max(options.max);
413
+ return schema;
414
+ }
415
+ function boolField() {
416
+ return zod.z.boolean();
417
+ }
418
+ function dateField() {
419
+ return zod.z.date();
420
+ }
421
+ function selectField(values) {
422
+ return zod.z.enum(values);
423
+ }
424
+ function jsonField(schema) {
425
+ return schema ?? zod.z.record(zod.z.any());
426
+ }
427
+ function fileField() {
428
+ return zod.z.instanceof(File);
429
+ }
430
+ function filesField(options) {
431
+ let schema = zod.z.array(zod.z.instanceof(File));
432
+ if (options?.min !== void 0) schema = schema.min(options.min);
433
+ if (options?.max !== void 0) schema = schema.max(options.max);
434
+ return schema;
435
+ }
436
+ var RELATION_METADATA_KEY = "__pocketbase_relation__";
437
+ function RelationField(config) {
438
+ const metadata = {
439
+ [RELATION_METADATA_KEY]: {
440
+ type: "single",
441
+ collection: config.collection,
442
+ cascadeDelete: config.cascadeDelete ?? false,
443
+ maxSelect: 1,
444
+ minSelect: 0
445
+ }
446
+ };
447
+ return zod.z.string().describe(JSON.stringify(metadata));
448
+ }
449
+ function RelationsField(config) {
450
+ const metadata = {
451
+ [RELATION_METADATA_KEY]: {
452
+ type: "multiple",
453
+ collection: config.collection,
454
+ cascadeDelete: config.cascadeDelete ?? false,
455
+ maxSelect: config.maxSelect ?? 999,
456
+ minSelect: config.minSelect ?? 0
457
+ }
458
+ };
459
+ let schema = zod.z.array(zod.z.string());
460
+ if (config.minSelect !== void 0) {
461
+ schema = schema.min(config.minSelect);
462
+ }
463
+ if (config.maxSelect !== void 0) {
464
+ schema = schema.max(config.maxSelect);
465
+ }
466
+ return schema.describe(JSON.stringify(metadata));
467
+ }
468
+ function extractRelationMetadata(description) {
469
+ if (!description) return null;
470
+ try {
471
+ const parsed = JSON.parse(description);
472
+ if (parsed[RELATION_METADATA_KEY]) {
473
+ return parsed[RELATION_METADATA_KEY];
474
+ }
475
+ } catch {
476
+ }
477
+ return null;
478
+ }
479
+ function editorField() {
480
+ return zod.z.string();
481
+ }
482
+ function geoPointField() {
483
+ return zod.z.object({
484
+ lon: zod.z.number(),
485
+ lat: zod.z.number()
486
+ });
487
+ }
488
+ function withPermissions(schema, config) {
489
+ const metadata = {
490
+ permissions: config
491
+ };
492
+ return schema.describe(JSON.stringify(metadata));
493
+ }
494
+ function withIndexes(schema, indexes) {
495
+ let existingMetadata = {};
496
+ if (schema.description) {
497
+ try {
498
+ existingMetadata = JSON.parse(schema.description);
499
+ } catch {
500
+ }
501
+ }
502
+ const metadata = {
503
+ ...existingMetadata,
504
+ indexes
505
+ };
506
+ return schema.describe(JSON.stringify(metadata));
507
+ }
508
+ function defineCollection(config) {
509
+ const { collectionName, schema, permissions, indexes, type, ...futureOptions } = config;
510
+ const metadata = {
511
+ collectionName
512
+ };
513
+ if (type) {
514
+ metadata.type = type;
515
+ }
516
+ if (permissions) {
517
+ metadata.permissions = permissions;
518
+ }
519
+ if (indexes) {
520
+ metadata.indexes = indexes;
521
+ }
522
+ if (Object.keys(futureOptions).length > 0) {
523
+ Object.assign(metadata, futureOptions);
524
+ }
525
+ return schema.describe(JSON.stringify(metadata));
526
+ }
527
+
528
+ // src/utils/permission-templates.ts
529
+ var PermissionTemplates = {
530
+ /**
531
+ * Public access - anyone can perform all operations
752
532
  */
753
- isNotFoundError(error) {
754
- return error instanceof Error && (error.message.includes("404") || error.message.toLowerCase().includes("not found"));
755
- }
533
+ public: () => ({
534
+ listRule: "",
535
+ viewRule: "",
536
+ createRule: "",
537
+ updateRule: "",
538
+ deleteRule: ""
539
+ }),
756
540
  /**
757
- * Standard error handling wrapper (legacy method, consider using handleError instead)
541
+ * Authenticated users only - requires valid authentication for all operations
758
542
  */
759
- errorWrapper(error) {
760
- console.error(`Error in ${this.constructor.name}:`, error);
761
- throw error;
762
- }
543
+ authenticated: () => ({
544
+ listRule: '@request.auth.id != ""',
545
+ viewRule: '@request.auth.id != ""',
546
+ createRule: '@request.auth.id != ""',
547
+ updateRule: '@request.auth.id != ""',
548
+ deleteRule: '@request.auth.id != ""'
549
+ }),
763
550
  /**
764
- * Subscribe to changes on a specific record
765
- * @param id The ID of the record to subscribe to
766
- * @param callback Function to call when changes occur
767
- * @param expand Optional expand parameters
768
- * @returns Promise that resolves to an unsubscribe function
551
+ * Owner-only access - users can only manage their own records
552
+ * @param ownerField - Name of the relation field pointing to user (default: 'User')
769
553
  */
770
- async subscribeToRecord(id, callback, expand) {
771
- const finalExpand = this.prepareExpand(expand);
772
- const options = finalExpand ? { expand: finalExpand } : {};
773
- return this.getCollection().subscribe(id, callback, options);
774
- }
554
+ ownerOnly: (ownerField = "User") => ({
555
+ listRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
556
+ viewRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
557
+ createRule: '@request.auth.id != ""',
558
+ updateRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`,
559
+ deleteRule: `@request.auth.id != "" && ${ownerField} = @request.auth.id`
560
+ }),
775
561
  /**
776
- * Subscribe to changes on the entire collection
777
- * @param callback Function to call when changes occur
778
- * @param expand Optional expand parameters
779
- * @returns Promise that resolves to an unsubscribe function
562
+ * Admin/superuser only access
563
+ * Assumes a 'role' field exists with 'admin' value
564
+ * @param roleField - Name of the role field (default: 'role')
780
565
  */
781
- async subscribeToCollection(callback, expand) {
782
- const finalExpand = this.prepareExpand(expand);
783
- const options = finalExpand ? { expand: finalExpand } : {};
784
- return this.getCollection().subscribe("*", callback, options);
785
- }
566
+ adminOnly: (roleField = "role") => ({
567
+ listRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
568
+ viewRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
569
+ createRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
570
+ updateRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`,
571
+ deleteRule: `@request.auth.id != "" && @request.auth.${roleField} = "admin"`
572
+ }),
786
573
  /**
787
- * Unsubscribe from a specific record's changes
788
- * @param id The ID of the record to unsubscribe from
574
+ * Public read, authenticated write
575
+ * Anyone can list/view, but only authenticated users can create/update/delete
789
576
  */
790
- unsubscribeFromRecord(id) {
791
- this.getCollection().unsubscribe(id);
792
- }
577
+ readPublic: () => ({
578
+ listRule: "",
579
+ viewRule: "",
580
+ createRule: '@request.auth.id != ""',
581
+ updateRule: '@request.auth.id != ""',
582
+ deleteRule: '@request.auth.id != ""'
583
+ }),
793
584
  /**
794
- * Unsubscribe from collection-wide changes
585
+ * Locked access - only superusers can perform operations
586
+ * All rules are set to null (locked)
795
587
  */
796
- unsubscribeFromCollection() {
797
- this.getCollection().unsubscribe("*");
798
- }
588
+ locked: () => ({
589
+ listRule: null,
590
+ viewRule: null,
591
+ createRule: null,
592
+ updateRule: null,
593
+ deleteRule: null
594
+ }),
799
595
  /**
800
- * Unsubscribe from all subscriptions in this collection
596
+ * Read-only authenticated - authenticated users can read, no write access
801
597
  */
802
- unsubscribeAll() {
803
- this.getCollection().unsubscribe();
804
- }
598
+ readOnlyAuthenticated: () => ({
599
+ listRule: '@request.auth.id != ""',
600
+ viewRule: '@request.auth.id != ""',
601
+ createRule: null,
602
+ updateRule: null,
603
+ deleteRule: null
604
+ })
805
605
  };
806
-
807
- // src/mutator/userMutator.ts
808
- var UserMutator = class extends BaseMutator {
809
- setDefaults() {
810
- return {
811
- expand: [],
812
- filter: [],
813
- sort: ["-updated"]
814
- };
606
+ function resolveTemplate(config) {
607
+ let baseRules;
608
+ switch (config.template) {
609
+ case "public":
610
+ baseRules = PermissionTemplates.public();
611
+ break;
612
+ case "authenticated":
613
+ baseRules = PermissionTemplates.authenticated();
614
+ break;
615
+ case "owner-only":
616
+ baseRules = PermissionTemplates.ownerOnly(config.ownerField);
617
+ break;
618
+ case "admin-only":
619
+ baseRules = PermissionTemplates.adminOnly(config.roleField);
620
+ break;
621
+ case "read-public":
622
+ baseRules = PermissionTemplates.readPublic();
623
+ break;
624
+ case "custom":
625
+ baseRules = {};
626
+ break;
627
+ default: {
628
+ const _exhaustive = config.template;
629
+ throw new Error(`Unknown template type: ${_exhaustive}`);
630
+ }
631
+ }
632
+ return {
633
+ ...baseRules,
634
+ ...config.customRules
635
+ };
636
+ }
637
+ function isTemplateConfig(config) {
638
+ return "template" in config;
639
+ }
640
+ function isPermissionSchema(config) {
641
+ return "listRule" in config || "viewRule" in config || "createRule" in config || "updateRule" in config || "deleteRule" in config || "manageRule" in config;
642
+ }
643
+ function validatePermissionConfig(config, isAuthCollection2 = false) {
644
+ const result = {
645
+ valid: true,
646
+ errors: [],
647
+ warnings: []
648
+ };
649
+ let permissions;
650
+ if (isTemplateConfig(config)) {
651
+ if (config.template === "owner-only" && !config.ownerField) {
652
+ result.warnings.push("owner-only template without ownerField specified - using default 'User'");
653
+ }
654
+ if (config.template === "admin-only" && !config.roleField) {
655
+ result.warnings.push("admin-only template without roleField specified - using default 'role'");
656
+ }
657
+ permissions = resolveTemplate(config);
658
+ } else {
659
+ permissions = config;
660
+ }
661
+ if (permissions.manageRule !== void 0 && !isAuthCollection2) {
662
+ result.errors.push("manageRule is only valid for auth collections");
663
+ result.valid = false;
815
664
  }
816
- getCollection() {
817
- return this.pb.collection("Users");
665
+ const ruleTypes = ["listRule", "viewRule", "createRule", "updateRule", "deleteRule"];
666
+ if (isAuthCollection2) {
667
+ ruleTypes.push("manageRule");
818
668
  }
819
- async validateInput(input) {
820
- return UserInputSchema.parse(input);
669
+ for (const ruleType of ruleTypes) {
670
+ const rule = permissions[ruleType];
671
+ if (rule !== void 0 && rule !== null && rule !== "") {
672
+ const ruleValidation = validateRuleExpression(rule);
673
+ if (!ruleValidation.valid) {
674
+ result.errors.push(`${ruleType}: ${ruleValidation.errors.join(", ")}`);
675
+ result.valid = false;
676
+ }
677
+ result.warnings.push(...ruleValidation.warnings.map((w) => `${ruleType}: ${w}`));
678
+ }
821
679
  }
822
- };
680
+ return result;
681
+ }
682
+ function validateRuleExpression(expression) {
683
+ const result = {
684
+ valid: true,
685
+ errors: [],
686
+ warnings: []
687
+ };
688
+ if (expression === null || expression === "") {
689
+ return result;
690
+ }
691
+ let parenCount = 0;
692
+ for (const char of expression) {
693
+ if (char === "(") parenCount++;
694
+ if (char === ")") parenCount--;
695
+ if (parenCount < 0) {
696
+ result.errors.push("Unbalanced parentheses");
697
+ result.valid = false;
698
+ return result;
699
+ }
700
+ }
701
+ if (parenCount !== 0) {
702
+ result.errors.push("Unbalanced parentheses");
703
+ result.valid = false;
704
+ }
705
+ if (expression.includes("==")) {
706
+ result.warnings.push("Use '=' instead of '==' for equality comparison");
707
+ }
708
+ const requestRefs = expression.match(/@request\.[a-zA-Z_][a-zA-Z0-9_.]*/g) || [];
709
+ for (const ref of requestRefs) {
710
+ const isValid = ref.startsWith("@request.auth.") || ref === "@request.method" || ref === "@request.context" || ref.startsWith("@request.body.") || ref.startsWith("@request.query.") || ref.startsWith("@request.headers.");
711
+ if (!isValid) {
712
+ result.errors.push(`Invalid @request reference: '${ref}'`);
713
+ result.valid = false;
714
+ }
715
+ }
716
+ return result;
717
+ }
718
+ function createPermissions(permissions) {
719
+ return {
720
+ listRule: permissions.listRule ?? null,
721
+ viewRule: permissions.viewRule ?? null,
722
+ createRule: permissions.createRule ?? null,
723
+ updateRule: permissions.updateRule ?? null,
724
+ deleteRule: permissions.deleteRule ?? null,
725
+ manageRule: permissions.manageRule ?? null
726
+ };
727
+ }
728
+ function mergePermissions(...schemas) {
729
+ const merged = {
730
+ listRule: null,
731
+ viewRule: null,
732
+ createRule: null,
733
+ updateRule: null,
734
+ deleteRule: null,
735
+ manageRule: null
736
+ };
737
+ for (const schema of schemas) {
738
+ if (schema.listRule !== void 0) merged.listRule = schema.listRule;
739
+ if (schema.viewRule !== void 0) merged.viewRule = schema.viewRule;
740
+ if (schema.createRule !== void 0) merged.createRule = schema.createRule;
741
+ if (schema.updateRule !== void 0) merged.updateRule = schema.updateRule;
742
+ if (schema.deleteRule !== void 0) merged.deleteRule = schema.deleteRule;
743
+ if (schema.manageRule !== void 0) merged.manageRule = schema.manageRule;
744
+ }
745
+ return merged;
746
+ }
823
747
 
824
748
  // src/migration/errors.ts
825
749
  var MigrationError = class _MigrationError extends Error {
@@ -5346,6 +5270,7 @@ async function executeStatus(options) {
5346
5270
  }
5347
5271
  }
5348
5272
 
5273
+ exports.BaseMutator = BaseMutator;
5349
5274
  exports.CLIUsageError = CLIUsageError;
5350
5275
  exports.ConfigurationError = ConfigurationError;
5351
5276
  exports.DiffEngine = DiffEngine;
@@ -5356,9 +5281,6 @@ exports.MigrationGenerationError = MigrationGenerationError;
5356
5281
  exports.MigrationGenerator = MigrationGenerator;
5357
5282
  exports.POCKETBASE_FIELD_TYPES = POCKETBASE_FIELD_TYPES;
5358
5283
  exports.PermissionTemplates = PermissionTemplates;
5359
- exports.ProjectCollection = ProjectCollection;
5360
- exports.ProjectInputSchema = ProjectInputSchema;
5361
- exports.ProjectSchema = ProjectSchema;
5362
5284
  exports.RelationField = RelationField;
5363
5285
  exports.RelationsField = RelationsField;
5364
5286
  exports.SchemaAnalyzer = SchemaAnalyzer;
@@ -5366,11 +5288,6 @@ exports.SchemaParsingError = SchemaParsingError;
5366
5288
  exports.SnapshotError = SnapshotError;
5367
5289
  exports.SnapshotManager = SnapshotManager;
5368
5290
  exports.StatusEnum = StatusEnum;
5369
- exports.UserCollection = UserCollection;
5370
- exports.UserCollectionSchema = UserCollectionSchema;
5371
- exports.UserInputSchema = UserInputSchema;
5372
- exports.UserMutator = UserMutator;
5373
- exports.UserSchema = UserSchema;
5374
5291
  exports.aggregateChanges = aggregateChanges;
5375
5292
  exports.baseImageFileSchema = baseImageFileSchema;
5376
5293
  exports.baseSchema = baseSchema;