Files
hermes-config/skills/self-hosting/shop-pro-quote/references/pb-0.39-migration-quirks.md
T
2026-07-12 10:17:17 -04:00

3.6 KiB

PB 0.39.1 JS Migration Quirks

Collected during SPQ-v2 Phase 4 implementation (2026-07-07).

API Rule Validation Ordering Bug

Symptom: listRule / viewRule / createRule that reference custom fields fail with invalid left operand "foo" - unknown field "foo" even when the field IS in the schema.

Root cause: PB 0.39 validates API rules against its internal schema snapshot at rule-set time. If you create a collection AND set rules in the same migrate() call, the rules validator is running against the PRE-COMMIT snapshot (which doesn't have the new fields yet).

What does NOT work (all three tried, all failed):

  1. Saving with empty rules, then setting rules on the same object + app.save() again — FAILS
  2. Saving with empty rules, then app.findCollectionByNameOrId(), setting rules on fetched object + app.save() — FAILS
  3. Separate migration file (M27 — after M25/M26 committed) — STILL FAILS

The rules validator always runs against the pre-commit snapshot. The only workaround for single-shop deployments: use empty rules ('') and rely on frontend filtering + createRule: null for write protection on server-side hook-only collections.

When you need scoped rules: Use the PocketBase admin UI after deployment, not JS migrations.

Missing Field Constructors

Wanted Use Instead
DateTimeField TextField — store ISO strings
DateField TextField — store ISO strings

Available constructors (confirmed working in PB 0.39.1):

  • TextField
  • EmailField
  • SelectField
  • JSONField
  • BoolField
  • NumberField (likely, not confirmed)

JS Runtime Support

PB 0.39.1 uses Goja (Go-based ES runtime). Confirmed working:

  • const, let, var
  • Template literals `hello ${world}`
  • Arrow functions (x) => x + 1
  • Object.keys(), Array.forEach(), JSON.stringify()
  • new Collection({ name, type, schema: [...] })
  • new Record(collection, data)

Hook Event Names

Correct API (PB 0.39) Names that DON'T exist
app.onRecordCreateRequest((e) => { ... })
app.onRecordUpdateRequest((e) => { ... }) onRecordBeforeUpdateRequest, onRecordAfterUpdateRequest
app.onRecordDeleteRequest((e) => { ... })

Only the three "Request" hooks exist — there are no "Before"/"After" variants.

Record Access in Hooks

  • e.record — the record being saved (in update hooks, this is the NEW data)
  • e.oldRecord — available in update hooks, the current DB state before the update
  • e.dao — data access object for querying other collections
  • e.httpContext — access to the HTTP request (query params, headers)

Creating Records Inside Hooks

var collection = app.findCollectionByNameOrId('myCollection');
var record = new Record(collection, {
  field1: 'value1',
  field2: 'value2',
});
e.dao.saveRecord(record);

Public Share-Token Access Pattern

To detect if a request is using the public share-token path (vs. authenticated API):

var shareToken = e.httpContext?.request?.query?.get('shareToken') || '';
if (shareToken) {
  // This is a public (unauthenticated) update via share link
  // Enforce expiry, restrict fields, etc.
}

Mailer API (for sending email in hooks)

var mailer = app.getMailer();
await mailer.send({
  to: [{ address: 'user@example.com', name: 'Name' }],
  subject: 'Subject',
  html: '<p>HTML body</p>',
});

Requires SMTP env vars set on the PB container (PB will crash on startup without valid PB_SMTP_* vars once mailer is used).