Files
eleith 2be09b7e8f
ci/woodpecker/pr/web Pipeline was successful
ci/woodpecker/push/web Pipeline was successful
[web] simplify editor workflow
2026-11-10 07:46:49 -08:00

143 lines
3.9 KiB
TypeScript

import { mkdir, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { asCoreDatabase, migrate, Pieces, StorageFileSystem } from '@luzzle/core'
import { createAppDb, runWebMigrations } from '@luzzle/web.db'
export const ADMIN = { username: 'admin', password: 'e2e-only-admin-password' }
export const notebook = {
file: 'reading/meteor notes.notes.md',
editorUrl: '/admin/piece/reading/meteor%20notes.notes.md/source',
publicUrl: '/pieces/notes/meteor-notebook',
type: 'notes',
slug: 'meteor-notebook',
title: 'Meteor Notebook'
}
const note = `Original observation.
> First quoted paragraph.
>
> Second quoted paragraph.
`
const noteHtml = `<section class="markdown">
<p>Original observation.</p>
<blockquote>
<p>First quoted paragraph.</p>
<p>Second quoted paragraph.</p>
</blockquote>
</section>`
const pieces = [
notebook,
{
file: 'forest.notes.md',
type: 'notes',
slug: 'forest-notebook',
title: 'Forest Notebook'
}
]
export async function createFixture(directory: string, origin: string) {
const dateConsumed = '2024-01-01'
const consumedAt = Date.parse(`${dateConsumed}T00:00:00Z`)
const archive = path.join(directory, 'archive')
await mkdir(path.join(archive, '.luzzle/schemas'), { recursive: true })
await mkdir(path.join(archive, 'reading'), { recursive: true })
await mkdir(path.join(directory, 'data'), { recursive: true })
await writeFile(
path.join(archive, '.luzzle/schemas/notes.json'),
JSON.stringify({
title: 'notes',
type: 'object',
properties: {
title: { type: 'string' },
date: { type: 'string', format: 'date' }
},
required: ['title', 'date'],
additionalProperties: false
})
)
for (const piece of pieces) {
await writeFile(
path.join(archive, piece.file),
`---\ntitle: ${piece.title}\ndate: ${dateConsumed}\n---\n\n${note}`
)
}
const database = path.join(directory, 'data/luzzle.sqlite')
await writeFile(
path.join(directory, 'config.yaml'),
JSON.stringify({
url: { app: origin },
storage: { root: archive },
paths: { database },
content: { text: { title: 'E2E Garden' } },
auth: { secret: 'e2e-only-session-secret-at-least-32-characters', credentials: ADMIN },
pieces: [
{
type: 'notes',
fields: { title: 'title', date_consumed: 'date' },
templates: { page: { name: 'note' } }
}
]
})
)
const db = createAppDb(database)
try {
const core = await migrate(asCoreDatabase(db))
if (core.error) throw core.error
const web = await runWebMigrations(db)
if (web.error) throw web.error
const schemas = new Pieces(new StorageFileSystem(archive))
for await (const result of await schemas.sync(asCoreDatabase(db))) {
if (result.error) throw new Error(result.message)
}
const notes = await schemas.getPiece('notes')
for await (const result of await notes.sync(
asCoreDatabase(db),
pieces.map((piece) => piece.file)
)) {
if (result.error) throw new Error(result.message)
}
const cachedPieces = await db
.selectFrom('pieces_cache')
.select(['file_path', 'content_hash'])
.execute()
// Seed the published read model directly; no Publish job is being simulated.
await db
.insertInto('web_pieces')
.values(
pieces.map((piece) => ({
id: piece.slug,
key: piece.slug,
slug: piece.slug,
type: piece.type,
file_path: piece.file,
content_hash: cachedPieces.find((cached) => cached.file_path === piece.file)!
.content_hash,
title: piece.title,
note,
json_metadata: JSON.stringify({ title: piece.title, date: dateConsumed }),
date_added: consumedAt,
date_consumed: consumedAt
}))
)
.execute()
await db
.insertInto('web_pieces_assets')
.values(
pieces.map((piece) => ({
piece_file_path: piece.file,
piece_key: piece.slug,
asset_key: piece.slug,
transformation: 'markdown',
mime_type: 'text/html',
is_embedded: 1,
content: noteHtml
}))
)
.execute()
} finally {
await db.destroy()
}
}