From 9c55a4d1ca60915d5cc494891de16b8b5c15f5a0 Mon Sep 17 00:00:00 2001 From: Lane Campbell Date: Thu, 25 Jun 2026 13:58:14 -0700 Subject: [PATCH] fix: remove duplicate media_asset seed, fix test regressions; release v3.0.0-beta.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - document-types-seed: remove duplicate media_asset registration (lines 208–227) that overwrote internal:true, causing media_asset to appear in content dropdowns - admin-content-docbacked + api-content-crud tests: update COUNT assertion from 2→1; versioning-off publish() deletes old published row so only v2 survives - on-cron-tick tests: fix swapped (ctx, event) → (event, ctx) argument order - Bump version to 3.0.0-beta.14 (skip beta.13, published from diverged branch) Co-Authored-By: Claude Sonnet 4.6 --- package.json | 2 +- ...dmin-content-docbacked.integration.test.ts | 5 ++-- ...content-crud-documents.integration.test.ts | 3 ++- .../email-plugin/hooks/on-cron-tick.test.ts | 24 +++++++++---------- .../core/src/services/document-types-seed.ts | 21 ---------------- packages/create-app/package.json | 2 +- packages/create-app/src/cli.js | 2 +- www/src/lib/version.ts | 2 +- 8 files changed, 21 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 962d835c7..5844102e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sonicjs", - "version": "3.0.0-beta.15", + "version": "3.0.0-beta.14", "private": true, "type": "module", "workspaces": [ diff --git a/packages/core/src/__tests__/routes/admin-content-docbacked.integration.test.ts b/packages/core/src/__tests__/routes/admin-content-docbacked.integration.test.ts index f1163ada2..ade2dfedc 100644 --- a/packages/core/src/__tests__/routes/admin-content-docbacked.integration.test.ts +++ b/packages/core/src/__tests__/routes/admin-content-docbacked.integration.test.ts @@ -120,8 +120,9 @@ describe('admin-content Option B (document-backed blog_post) — integration', ( body: form({ _method: 'PUT', collection_id: COLL, title: 'Post updateme v2', slug: 'updateme', content: '

v2

', author: 'Ada', difficulty: 'beginner', status: 'published' }), }) expect([200, 302]).toContain(res.status) - // A new version exists and exactly one published row, now at v2. - expect(db.raw.prepare("SELECT COUNT(*) n FROM documents WHERE root_id=?").get(rootId).n).toBe(2) + // With versioning off (default), publish() deletes the old published row when it is no longer + // the current draft — so only v2 survives. The version_number still advances to 2. + expect(db.raw.prepare("SELECT COUNT(*) n FROM documents WHERE root_id=?").get(rootId).n).toBe(1) expect(db.raw.prepare("SELECT COUNT(*) n FROM documents WHERE root_id=? AND is_published=1").get(rootId).n).toBe(1) expect(db.raw.prepare("SELECT version_number v FROM documents WHERE root_id=? AND is_published=1").get(rootId).v).toBe(2) }) diff --git a/packages/core/src/__tests__/routes/api-content-crud-documents.integration.test.ts b/packages/core/src/__tests__/routes/api-content-crud-documents.integration.test.ts index d186119c5..f7dc12ece 100644 --- a/packages/core/src/__tests__/routes/api-content-crud-documents.integration.test.ts +++ b/packages/core/src/__tests__/routes/api-content-crud-documents.integration.test.ts @@ -67,7 +67,8 @@ describe('api-content-crud → documents (decommission step)', () => { const created = (await (await app.request('/api/content', json('POST', { collectionId: 'blog_post', title: 'V1', slug: 'v', status: 'published', data: {} }))).json()).data const res = await app.request(`/api/content/${created.id}`, json('PUT', { data: { body: 'v2' }, status: 'published' })) expect(res.status).toBe(200) - expect(db.raw.prepare('SELECT COUNT(*) n FROM documents WHERE root_id=?').get(created.id).n).toBe(2) + // With versioning off (default), publish() deletes the old published row — only v2 survives. + expect(db.raw.prepare('SELECT COUNT(*) n FROM documents WHERE root_id=?').get(created.id).n).toBe(1) expect(db.raw.prepare('SELECT version_number v FROM documents WHERE root_id=? AND is_published=1').get(created.id).v).toBe(2) }) diff --git a/packages/core/src/plugins/core-plugins/email-plugin/hooks/on-cron-tick.test.ts b/packages/core/src/plugins/core-plugins/email-plugin/hooks/on-cron-tick.test.ts index 64bcb0a63..74299d858 100644 --- a/packages/core/src/plugins/core-plugins/email-plugin/hooks/on-cron-tick.test.ts +++ b/packages/core/src/plugins/core-plugins/email-plugin/hooks/on-cron-tick.test.ts @@ -36,12 +36,12 @@ describe('onCronTick — hookFamily filter', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) const ctx = makeCtx({}) // no credentials, no DB - await onCronTick(ctx, { + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'some-other-plugin-cron', triggeredAt: 1700000000000, - }) + }, ctx) expect(warn).not.toHaveBeenCalled() }) @@ -52,12 +52,12 @@ describe('onCronTick — credential check', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) const ctx = makeCtx({ DB: {}, EMAIL_API_TOKEN: 'tok' }) - await onCronTick(ctx, { + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0, - }) + }, ctx) expect(warn).toHaveBeenCalledWith( expect.stringContaining('CF GraphQL credentials missing'), @@ -69,12 +69,12 @@ describe('onCronTick — credential check', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) const ctx = makeCtx({ DB: {}, CF_ZONE_ID: 'zone' }) - await onCronTick(ctx, { + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0, - }) + }, ctx) expect(warn).toHaveBeenCalledWith( expect.stringContaining('CF GraphQL credentials missing'), @@ -87,12 +87,12 @@ describe('onCronTick — credential check', () => { const ctx = makeCtx({ DB: {} }) await expect( - onCronTick(ctx, { + onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0, - }), + }, ctx), ).resolves.toBeUndefined() }) }) @@ -120,7 +120,7 @@ describe('onCronTick — happy path (credentials present)', () => { ) as never const ctx = makeCtx({ DB: {} as unknown, CF_ZONE_ID: 'zone', EMAIL_API_TOKEN: 'tok' }) - await onCronTick(ctx, { type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 1700000000000 }) + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 1700000000000 }, ctx) // Pre-#701: this outcome was silently swallowed — making "cron working but // CF returned 0 rows" indistinguishable from "cron never fired" in worker logs. @@ -147,7 +147,7 @@ describe('onCronTick — happy path (credentials present)', () => { ) as never const ctx = makeCtx({ DB: makeDb(), CF_ZONE_ID: 'zone', EMAIL_API_TOKEN: 'tok' }) - await onCronTick(ctx, { type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 1700000000000 }) + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 1700000000000 }, ctx) expect(log).toHaveBeenCalledWith( expect.stringContaining('reconciliation cron: ok'), @@ -163,7 +163,7 @@ describe('onCronTick — happy path (credentials present)', () => { globalThis.fetch = vi.fn(async () => new Response('Internal Server Error', { status: 500 })) as never const ctx = makeCtx({ DB: makeDb(), CF_ZONE_ID: 'zone', EMAIL_API_TOKEN: 'tok' }) - await onCronTick(ctx, { type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0 }) + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0 }, ctx) expect(warn).toHaveBeenCalledWith( expect.stringContaining('GraphQL error'), @@ -190,7 +190,7 @@ describe('onCronTick — D1 settings fallback', () => { })), } as unknown as D1Database const ctx = makeCtx({ DB: db, CF_ZONE_ID: 'zone' }) // EMAIL_API_TOKEN absent - await onCronTick(ctx, { type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0 }) + await onCronTick({ type: 'cron:tick', schedule: '*/5 * * * *', hookFamily: 'email-reconciliation', triggeredAt: 0 }, ctx) expect(debug).toHaveBeenCalledWith(expect.stringContaining('EMAIL_API_TOKEN read from D1')) }) }) diff --git a/packages/core/src/services/document-types-seed.ts b/packages/core/src/services/document-types-seed.ts index c5ba4e98d..fecfcb115 100644 --- a/packages/core/src/services/document-types-seed.ts +++ b/packages/core/src/services/document-types-seed.ts @@ -229,27 +229,6 @@ export async function bootstrapDocumentTypes(db: D1Database): Promise { ], }) - // Media asset: every file upload creates a media_asset document (document-authoritative). - // File bytes stay in R2; this document holds intrinsic metadata (dimensions, mime, r2Key…). - await registry.register({ - id: 'media_asset', - name: 'media_asset', - displayName: 'Media Asset', - description: 'Media file metadata (R2 object key + intrinsic properties; URL derived at read time)', - source: 'system', - schema: anyObject, - settings: { - baseGrants: { public: ['read'], admin: ['read', 'create', 'update', 'delete', 'publish', 'manage'], editor: ['read', 'create', 'update'] }, - maxVersionsPerRoot: 5, - }, - queryableFields: [ - { name: 'mimeType', kind: 'scalar', type: 'text', column: 'q_media_mime' }, - { name: 'folder', kind: 'scalar', type: 'text', column: 'q_media_folder' }, - { name: 'size', kind: 'scalar', type: 'integer', column: 'q_media_size' }, - { name: 'tags', kind: 'facet', type: 'text' }, - ], - }) - // ── RBAC (auth-owned). 3 document types replace 4 relational tables: ────────── // rbac_role slug = roleId, data.grants[] embedded (replaces role_grants) // rbac_verb slug = verbId diff --git a/packages/create-app/package.json b/packages/create-app/package.json index f2941e0a9..dd1639a50 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "create-sonicjs", - "version": "3.0.0-beta.15", + "version": "3.0.0-beta.14", "description": "Create a new SonicJS application with zero configuration", "type": "module", "bin": { diff --git a/packages/create-app/src/cli.js b/packages/create-app/src/cli.js index 03d90a68c..e829fed13 100644 --- a/packages/create-app/src/cli.js +++ b/packages/create-app/src/cli.js @@ -409,7 +409,7 @@ async function copyTemplate(templateName, targetDir, options) { // Add @sonicjs-cms/core dependency packageJson.dependencies = { - '@sonicjs-cms/core': '^3.0.0-beta.15', + '@sonicjs-cms/core': '^3.0.0-beta.14', ...packageJson.dependencies } diff --git a/www/src/lib/version.ts b/www/src/lib/version.ts index 3d7cae8e6..08588e94a 100644 --- a/www/src/lib/version.ts +++ b/www/src/lib/version.ts @@ -5,7 +5,7 @@ // When releasing a new version, run `npm run version:patch` (or minor/major) from the root // which will update this file via scripts/sync-versions.js -export const VERSION = '3.0.0-beta.15' +export const VERSION = '3.0.0-beta.14' // Helper function to get the current month/year for "Last updated" export function getLastUpdatedDate(): string {