/** node --env-file=.env.local scripts/migrations/2026-10-01-add-dn-processing-preview.mjs [--apply] * Adds one optional JSON field only; no receipt data or access rules change. */ import PocketBase from 'pocketbase'; import { mkdtemp, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { equalJson } from './2026-09-30-add-supplier-reconciliation-rules.mjs'; async function main() { if (process.argv.slice(2).some(arg => arg !== '--apply')) throw new Error('Only --apply is supported'); const url = process.env.NEXT_PUBLIC_POCKETBASE_URL || process.env.PB_URL; if (!url) throw new Error('PocketBase URL missing'); const pb = new PocketBase(url); if (process.env.POCKETBASE_ADMIN_TOKEN) pb.authStore.save(process.env.POCKETBASE_ADMIN_TOKEN); else await pb.collection('_superusers').authWithPassword(process.env.POCKETBASE_ADMIN_EMAIL, process.env.POCKETBASE_ADMIN_PASSWORD); const schema = await pb.collections.getOne('dn_batch_notes'); const name = 'reconciliation_preview'; const existing = schema.fields.find(f => f.name === name); if (existing && (existing.type !== 'json' || existing.required || existing.maxSize < 2000000)) throw new Error('Existing preview field incompatible'); console.log(JSON.stringify({ mode: process.argv.includes('--apply') ? 'apply' : 'dry-run', collection: schema.name, addField: !existing })); if (!process.argv.includes('--apply')) return; if (!existing) { const backup = join(await mkdtemp(join(tmpdir(), 'mega-dn-preview-schema-')), 'before.json'); await writeFile(backup, JSON.stringify(schema, null, 2), { mode: 0o600 }); console.log(`Schema backup: ${backup}`); if (!equalJson(schema, await pb.collections.getOne(schema.id))) throw new Error('Schema changed during migration'); await pb.collections.update(schema.id, { fields: [...schema.fields, { name, type: 'json', required: false, maxSize: 2000000 }] }); } const after = await pb.collections.getOne(schema.id); if (!after.fields.some(f => f.name === name && f.type === 'json' && !f.required)) throw new Error('Field verification failed'); if (!equalJson(schema.fields.filter(f => f.name !== name), after.fields.filter(f => f.name !== name))) throw new Error('Existing fields changed'); for (const key of ['listRule', 'viewRule', 'createRule', 'updateRule', 'deleteRule', 'indexes']) { if (!equalJson(schema[key], after[key])) throw new Error(`Collection ${key} changed`); } console.log('Verified optional preview storage; existing fields, access rules and indexes preserved.'); } main().catch(error => { console.error(error?.status ? `PocketBase request failed (HTTP ${error.status})` : error.message); process.exitCode = 1; });