{"version":3,"file":"runner-DhmBwJXL.mjs","names":["up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","upPostgres","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","getDefaultLocale","up","hasColumn","assertSingleLocale","down","up","down","up","down","up","isSearchEnabled","getSearchableFields","rebuildIndex","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","DUPLICATE_COLUMN_RE","up","down","addColumnIfMissing","deepErrorMessage","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","ACTIVATION_KEY","up","sortableUtcTimestamp","down","addColumnIfMissing","deepErrorMessage","up","down","up","sortableUtcTimestamp","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","up","down","m001","m002","m003","m004","m005","m006","m007","m008","m009","m011","m012","m013","m014","m015","m016","m017","m018","m019","m020","m021","m022","m023","m024","m025","m026","m027","m028","m029","m030","m031","m032","m033","m034","m035","m036","m037","m038","m039","m040","m041","m042","m043","m044","m045","m046","m047","m048","m049","m050","m051","m052","m053","m054","m055","m056","m057","m058","m059","m060","m061","m062","m063","m064","m065","m066","m067","m068","m069","m070","m071","m072","m073","m074","m075"],"sources":["../src/database/migrations/001_initial.ts","../src/database/migrations/002_media_status.ts","../src/database/migrations/003_schema_registry.ts","../src/database/migrations/004_plugins.ts","../src/database/migrations/005_menus.ts","../src/database/migrations/006_taxonomy_defs.ts","../src/database/migrations/007_widgets.ts","../src/database/migrations/008_auth.ts","../src/database/migrations/009_user_disabled.ts","../src/database/migrations/011_sections.ts","../src/database/migrations/012_search.ts","../src/database/migrations/013_scheduled_publishing.ts","../src/database/migrations/014_draft_revisions.ts","../src/database/migrations/015_indexes.ts","../src/database/migrations/016_api_tokens.ts","../src/database/migrations/017_authorization_codes.ts","../src/database/migrations/018_seo.ts","../src/database/migrations/019_i18n.ts","../src/database/migrations/020_collection_url_pattern.ts","../src/database/migrations/021_remove_section_categories.ts","../src/database/migrations/022_marketplace_plugin_state.ts","../src/database/migrations/023_plugin_metadata.ts","../src/database/migrations/024_media_placeholders.ts","../src/database/migrations/025_oauth_clients.ts","../src/database/migrations/026_cron_tasks.ts","../src/database/migrations/027_comments.ts","../src/database/migrations/028_drop_author_url.ts","../src/database/migrations/029_redirects.ts","../src/database/migrations/030_widen_scheduled_index.ts","../src/database/migrations/031_bylines.ts","../src/database/migrations/032_rate_limits.ts","../src/database/migrations/033_optimize_content_indexes.ts","../src/database/migrations/034_published_at_index.ts","../src/database/migrations/035_bounded_404_log.ts","../src/database/migrations/036_i18n_menus_and_taxonomies.ts","../src/database/migrations/037_credential_algorithm.ts","../src/database/migrations/038_registry_plugin_state.ts","../src/database/migrations/039_fix_fts5_triggers.ts","../src/database/migrations/040_byline_i18n.ts","../src/database/migrations/041_content_locale_list_index.ts","../src/database/migrations/042_byline_fields.ts","../src/database/migrations/043_content_references.ts","../src/database/migrations/044_comment_reactions.ts","../src/database/migrations/045_taxonomy_parent_group.ts","../src/database/migrations/046_media_usage_index.ts","../src/database/migrations/047_restore_taxonomy_parent_index.ts","../src/database/migrations/048_restore_content_taxonomies_term_index.ts","../src/database/migrations/049_taxonomies_name_locale_index.ts","../src/database/migrations/050_media_usage_index_status.ts","../src/database/migrations/051_content_taxonomies_denorm.ts","../src/database/migrations/052_media_usage_read_index.ts","../src/database/migrations/053_plugin_mcp_tools.ts","../src/database/migrations/054_media_upload_attempts.ts","../src/database/migrations/055_content_translation_group_locale_index.ts","../src/database/migrations/056_taxonomy_term_sort_order.ts","../src/database/migrations/057_collection_hidden.ts","../src/database/migrations/058_collection_sort_order.ts","../src/database/migrations/059_revision_prune_queue.ts","../src/database/migrations/060_collection_admin_config.ts","../src/database/migrations/061_media_usage_cleanup.ts","../src/database/migrations/062_media_usage_cleanup_fence.ts","../src/database/migrations/063_media_usage_incremental_work.ts","../src/database/migrations/064_fts_plain_text.ts","../src/database/migrations/065_media_usage_collection_deletion.ts","../src/database/migrations/066_media_usage_reconciliation.ts","../src/database/migrations/067_indexed_content_fields.ts","../src/database/migrations/068_content_taxonomy_entry_groups.ts","../src/database/migrations/069_collection_title_date_fields.ts","../src/database/migrations/070_collection_routable.ts","../src/database/migrations/071_restore_content_bylines_table.ts","../src/database/migrations/072_authz.ts","../src/database/migrations/073_billing_usage.ts","../src/database/migrations/074_collection_storage.ts","../src/database/migrations/075_api_token_cors.ts","../src/database/migrations/runner.ts"],"sourcesContent":["import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Initial schema migration\n *\n * Note: Content tables (ec_posts, ec_pages, etc.) are created dynamically\n * by the SchemaRegistry when collections are added via the admin UI.\n * This migration only creates system tables.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Revisions - stores snapshots of content entries\n\t// References entries in ec_* tables by collection + entry_id\n\tawait db.schema\n\t\t.createTable(\"revisions\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull()) // e.g., 'posts'\n\t\t.addColumn(\"entry_id\", \"text\", (col) => col.notNull()) // ID in the ec_* table\n\t\t.addColumn(\"data\", \"text\", (col) => col.notNull()) // JSON snapshot of all fields\n\t\t.addColumn(\"author_id\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_revisions_entry\")\n\t\t.ifNotExists()\n\t\t.on(\"revisions\")\n\t\t.columns([\"collection\", \"entry_id\"])\n\t\t.execute();\n\n\t// Taxonomies\n\tawait db.schema\n\t\t.createTable(\"taxonomies\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"parent_id\", \"text\")\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addUniqueConstraint(\"taxonomies_name_slug_unique\", [\"name\", \"slug\"])\n\t\t.addForeignKeyConstraint(\"taxonomies_parent_fk\", [\"parent_id\"], \"taxonomies\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"set null\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_name\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.column(\"name\")\n\t\t.execute();\n\n\t// Content-Taxonomy junction - references entries in ec_* tables\n\tawait db.schema\n\t\t.createTable(\"content_taxonomies\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull()) // e.g., 'posts'\n\t\t.addColumn(\"entry_id\", \"text\", (col) => col.notNull()) // ID in the ec_* table\n\t\t.addColumn(\"taxonomy_id\", \"text\", (col) => col.notNull())\n\t\t.addPrimaryKeyConstraint(\"content_taxonomies_pk\", [\"collection\", \"entry_id\", \"taxonomy_id\"])\n\t\t.addForeignKeyConstraint(\n\t\t\t\"content_taxonomies_taxonomy_fk\",\n\t\t\t[\"taxonomy_id\"],\n\t\t\t\"taxonomies\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\t// Media\n\tawait db.schema\n\t\t.createTable(\"media\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"filename\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"mime_type\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"size\", \"integer\")\n\t\t.addColumn(\"width\", \"integer\")\n\t\t.addColumn(\"height\", \"integer\")\n\t\t.addColumn(\"alt\", \"text\")\n\t\t.addColumn(\"caption\", \"text\")\n\t\t.addColumn(\"storage_key\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"content_hash\", \"text\") // xxHash64 for deduplication\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"author_id\", \"text\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_media_content_hash\")\n\t\t.ifNotExists()\n\t\t.on(\"media\")\n\t\t.column(\"content_hash\")\n\t\t.execute();\n\n\t// Users\n\tawait db.schema\n\t\t.createTable(\"users\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"email\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"password_hash\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"name\", \"text\")\n\t\t.addColumn(\"role\", \"text\", (col) => col.defaultTo(\"subscriber\"))\n\t\t.addColumn(\"avatar_id\", \"text\")\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_users_email\")\n\t\t.ifNotExists()\n\t\t.on(\"users\")\n\t\t.column(\"email\")\n\t\t.execute();\n\n\t// Options (key-value store)\n\tawait db.schema\n\t\t.createTable(\"options\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"name\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"value\", \"text\", (col) => col.notNull())\n\t\t.execute();\n\n\t// Audit logs (security events)\n\tawait db.schema\n\t\t.createTable(\"audit_logs\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"timestamp\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"actor_id\", \"text\")\n\t\t.addColumn(\"actor_ip\", \"text\")\n\t\t.addColumn(\"action\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"resource_type\", \"text\")\n\t\t.addColumn(\"resource_id\", \"text\")\n\t\t.addColumn(\"details\", \"text\")\n\t\t.addColumn(\"status\", \"text\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_audit_actor\")\n\t\t.ifNotExists()\n\t\t.on(\"audit_logs\")\n\t\t.column(\"actor_id\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_audit_action\")\n\t\t.ifNotExists()\n\t\t.on(\"audit_logs\")\n\t\t.column(\"action\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_audit_timestamp\")\n\t\t.ifNotExists()\n\t\t.on(\"audit_logs\")\n\t\t.column(\"timestamp\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"audit_logs\").execute();\n\tawait db.schema.dropTable(\"options\").execute();\n\tawait db.schema.dropTable(\"users\").execute();\n\tawait db.schema.dropTable(\"media\").execute();\n\tawait db.schema.dropTable(\"content_taxonomies\").execute();\n\tawait db.schema.dropTable(\"taxonomies\").execute();\n\tawait db.schema.dropTable(\"revisions\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Add status column to media table for tracking upload state.\n * Status values: 'pending' | 'ready' | 'failed'\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.alterTable(\"media\")\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"ready\"))\n\t\t.execute();\n\n\tawait db.schema.createIndex(\"idx_media_status\").on(\"media\").column(\"status\").execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_media_status\").execute();\n\n\t// Note: SQLite doesn't support DROP COLUMN directly\n\t// For a proper down migration in SQLite, you'd need to:\n\t// 1. Create a new table without the column\n\t// 2. Copy data over\n\t// 3. Drop old table\n\t// 4. Rename new table\n\t// For simplicity, we'll leave this as a no-op since down migrations are rarely used\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Schema Registry Tables\n *\n * Creates the schema registry tables that store collection and field definitions.\n * This enables dynamic schema management where D1 is the source of truth.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Collection definitions (like WordPress post types, but stored in DB)\n\tawait db.schema\n\t\t.createTable(\"_emdash_collections\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"label_singular\", \"text\")\n\t\t.addColumn(\"description\", \"text\")\n\t\t.addColumn(\"icon\", \"text\")\n\t\t.addColumn(\"supports\", \"text\") // JSON array: [\"revisions\", \"drafts\", \"preview\"]\n\t\t.addColumn(\"source\", \"text\") // 'template:blog', 'import:wordpress', 'manual'\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Field definitions for each collection\n\tawait db.schema\n\t\t.createTable(\"_emdash_fields\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"collection_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull()) // 'string', 'number', 'boolean', 'portableText', etc.\n\t\t.addColumn(\"column_type\", \"text\", (col) => col.notNull()) // 'TEXT', 'REAL', 'INTEGER', 'JSON'\n\t\t.addColumn(\"required\", \"integer\", (col) => col.defaultTo(0)) // boolean as 0/1\n\t\t.addColumn(\"unique\", \"integer\", (col) => col.defaultTo(0)) // boolean as 0/1\n\t\t.addColumn(\"default_value\", \"text\") // JSON-encoded default\n\t\t.addColumn(\"validation\", \"text\") // JSON: { min: 0, max: 100, pattern: \"^[a-z]+$\" }\n\t\t.addColumn(\"widget\", \"text\") // UI widget hint: 'textarea', 'richtext', 'select'\n\t\t.addColumn(\"options\", \"text\") // JSON: widget-specific config\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\n\t\t\t\"fields_collection_fk\",\n\t\t\t[\"collection_id\"],\n\t\t\t\"_emdash_collections\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\t// Unique constraint on collection + field slug\n\tawait db.schema\n\t\t.createIndex(\"idx_fields_collection_slug\")\n\t\t.on(\"_emdash_fields\")\n\t\t.columns([\"collection_id\", \"slug\"])\n\t\t.unique()\n\t\t.execute();\n\n\t// Index for faster field lookups\n\tawait db.schema\n\t\t.createIndex(\"idx_fields_collection\")\n\t\t.on(\"_emdash_fields\")\n\t\t.column(\"collection_id\")\n\t\t.execute();\n\n\t// Index for sorting\n\tawait db.schema\n\t\t.createIndex(\"idx_fields_sort\")\n\t\t.on(\"_emdash_fields\")\n\t\t.columns([\"collection_id\", \"sort_order\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_fields\").execute();\n\tawait db.schema.dropTable(\"_emdash_collections\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Plugin System Tables\n *\n * Creates the plugin storage table and plugin state tracking.\n * Plugin storage uses a document store with declared indexes.\n *\n * @see PLUGIN-SYSTEM.md § Plugin Storage\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Core storage table for plugin documents\n\tawait db.schema\n\t\t.createTable(\"_plugin_storage\")\n\t\t.addColumn(\"plugin_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"data\", \"text\", (col) => col.notNull()) // JSON\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"pk_plugin_storage\", [\"plugin_id\", \"collection\", \"id\"])\n\t\t.execute();\n\n\t// Base index for listing\n\tawait db.schema\n\t\t.createIndex(\"idx_plugin_storage_list\")\n\t\t.on(\"_plugin_storage\")\n\t\t.columns([\"plugin_id\", \"collection\", \"created_at\"])\n\t\t.execute();\n\n\t// Plugin state tracking for lifecycle hooks\n\tawait db.schema\n\t\t.createTable(\"_plugin_state\")\n\t\t.addColumn(\"plugin_id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"version\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"installed\")) // installed, active, inactive\n\t\t.addColumn(\"installed_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"activated_at\", \"text\")\n\t\t.addColumn(\"deactivated_at\", \"text\")\n\t\t.addColumn(\"data\", \"text\") // JSON for plugin-specific state\n\t\t.execute();\n\n\t// Index tracking for dynamic expression indexes on plugin storage\n\t// This tracks which indexes have been created so we can manage them\n\tawait db.schema\n\t\t.createTable(\"_plugin_indexes\")\n\t\t.addColumn(\"plugin_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"index_name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"fields\", \"text\", (col) => col.notNull()) // JSON array of field paths\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"pk_plugin_indexes\", [\"plugin_id\", \"collection\", \"index_name\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_plugin_indexes\").execute();\n\tawait db.schema.dropTable(\"_plugin_state\").execute();\n\tawait db.schema.dropTable(\"_plugin_storage\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Navigation Menus migration\n *\n * Creates tables for admin-editable navigation menus.\n * Menu items can reference content entries, taxonomy terms, or custom URLs.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Menu definitions\n\tawait db.schema\n\t\t.createTable(\"_emdash_menus\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Menu items (ordered, hierarchical)\n\tawait db.schema\n\t\t.createTable(\"_emdash_menu_items\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"menu_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"parent_id\", \"text\")\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull()) // 'page', 'post', 'custom', 'taxonomy', 'collection'\n\t\t.addColumn(\"reference_collection\", \"text\")\n\t\t.addColumn(\"reference_id\", \"text\")\n\t\t.addColumn(\"custom_url\", \"text\")\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"title_attr\", \"text\")\n\t\t.addColumn(\"target\", \"text\")\n\t\t.addColumn(\"css_classes\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"menu_items_menu_fk\", [\"menu_id\"], \"_emdash_menus\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.addForeignKeyConstraint(\n\t\t\t\"menu_items_parent_fk\",\n\t\t\t[\"parent_id\"],\n\t\t\t\"_emdash_menu_items\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\t// Index for efficient menu item queries\n\tawait db.schema\n\t\t.createIndex(\"idx_menu_items_menu\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.columns([\"menu_id\", \"sort_order\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_menu_items_parent\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_menu_items\").execute();\n\tawait db.schema.dropTable(\"_emdash_menus\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Taxonomy definitions migration\n *\n * Adds _emdash_taxonomy_defs table to store taxonomy definitions (category, tag, custom)\n * and seeds default category and tag taxonomies.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Taxonomy definitions (what taxonomies exist)\n\tawait db.schema\n\t\t.createTable(\"_emdash_taxonomy_defs\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull().unique()) // 'category', 'tag'\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull()) // 'Categories'\n\t\t.addColumn(\"label_singular\", \"text\") // 'Category'\n\t\t.addColumn(\"hierarchical\", \"integer\", (col) => col.defaultTo(0)) // 0 or 1\n\t\t.addColumn(\"collections\", \"text\") // JSON array: [\"posts\"]\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Seed default taxonomies\n\tawait db\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely migration runs against unknown schema\n\t\t.insertInto(\"_emdash_taxonomy_defs\" as never)\n\t\t.values([\n\t\t\t{\n\t\t\t\tid: \"taxdef_category\",\n\t\t\t\tname: \"category\",\n\t\t\t\tlabel: \"Categories\",\n\t\t\t\tlabel_singular: \"Category\",\n\t\t\t\thierarchical: 1,\n\t\t\t\tcollections: JSON.stringify([\"posts\"]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"taxdef_tag\",\n\t\t\t\tname: \"tag\",\n\t\t\t\tlabel: \"Tags\",\n\t\t\t\tlabel_singular: \"Tag\",\n\t\t\t\thierarchical: 0,\n\t\t\t\tcollections: JSON.stringify([\"posts\"]),\n\t\t\t},\n\t\t])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_taxonomy_defs\").execute();\n}\n","import { type Kysely, sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Widget areas table\n\tawait db.schema\n\t\t.createTable(\"_emdash_widget_areas\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"description\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.execute();\n\n\t// Widgets table\n\tawait db.schema\n\t\t.createTable(\"_emdash_widgets\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"area_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_widget_areas.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull()) // 'content', 'menu', 'component'\n\t\t.addColumn(\"title\", \"text\")\n\t\t.addColumn(\"content\", \"text\") // JSON: Portable Text for content type\n\t\t.addColumn(\"menu_name\", \"text\") // For menu type\n\t\t.addColumn(\"component_id\", \"text\") // For component type: 'core:recent-posts'\n\t\t.addColumn(\"component_props\", \"text\") // JSON: props for component\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.execute();\n\n\t// Index for efficient lookups by area and order\n\tawait db.schema\n\t\t.createIndex(\"idx_widgets_area\")\n\t\t.on(\"_emdash_widgets\")\n\t\t.columns([\"area_id\", \"sort_order\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_widgets\").execute();\n\tawait db.schema.dropTable(\"_emdash_widget_areas\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { binaryType, currentTimestamp, currentTimestampValue } from \"../dialect-helpers.js\";\n\n/**\n * Auth migration - passkey-first authentication\n *\n * Changes:\n * - Removes password_hash from users (no passwords)\n * - Adds role as integer (RBAC levels)\n * - Adds email_verified, avatar_url, updated_at to users\n * - Creates credentials table (passkeys)\n * - Creates auth_tokens table (magic links, invites)\n * - Creates oauth_accounts table (external provider links)\n * - Creates allowed_domains table (self-signup)\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// SQLite doesn't support DROP COLUMN directly, so we need to recreate the table\n\t// Create new users table with updated schema\n\tawait db.schema\n\t\t.createTable(\"users_new\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"email\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"name\", \"text\")\n\t\t.addColumn(\"avatar_url\", \"text\")\n\t\t.addColumn(\"role\", \"integer\", (col) => col.notNull().defaultTo(10)) // SUBSCRIBER\n\t\t.addColumn(\"email_verified\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Migrate existing data (map old role strings to new integer levels)\n\tawait sql`\n\t\tINSERT INTO users_new (id, email, name, role, data, created_at, updated_at)\n\t\tSELECT\n\t\t\tid,\n\t\t\temail,\n\t\t\tname,\n\t\t\tCASE role\n\t\t\t\tWHEN 'admin' THEN 50\n\t\t\t\tWHEN 'editor' THEN 40\n\t\t\t\tWHEN 'author' THEN 30\n\t\t\t\tWHEN 'contributor' THEN 20\n\t\t\t\tELSE 10\n\t\t\tEND,\n\t\t\tdata,\n\t\t\tcreated_at,\n\t\t\t${currentTimestampValue(db)}\n\t\tFROM users\n\t`.execute(db);\n\n\t// Drop old table and rename new one\n\tawait db.schema.dropTable(\"users\").execute();\n\tawait sql`ALTER TABLE users_new RENAME TO users`.execute(db);\n\n\t// Recreate index\n\tawait db.schema.createIndex(\"idx_users_email\").on(\"users\").column(\"email\").execute();\n\n\t// Passkey credentials\n\tawait db.schema\n\t\t.createTable(\"credentials\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey()) // Base64url credential ID\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"public_key\", binaryType(db), (col) => col.notNull()) // COSE public key\n\t\t.addColumn(\"counter\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"device_type\", \"text\", (col) => col.notNull()) // 'singleDevice' | 'multiDevice'\n\t\t.addColumn(\"backed_up\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"transports\", \"text\") // JSON array\n\t\t.addColumn(\"name\", \"text\") // User-friendly name\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"last_used_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"credentials_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema.createIndex(\"idx_credentials_user\").on(\"credentials\").column(\"user_id\").execute();\n\n\t// Auth tokens (magic links, email verification, invites, recovery)\n\tawait db.schema\n\t\t.createTable(\"auth_tokens\")\n\t\t.addColumn(\"hash\", \"text\", (col) => col.primaryKey()) // SHA-256 hash of token\n\t\t.addColumn(\"user_id\", \"text\")\n\t\t.addColumn(\"email\", \"text\") // For pre-user tokens\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull()) // 'magic_link' | 'email_verify' | 'invite' | 'recovery'\n\t\t.addColumn(\"role\", \"integer\") // For invites\n\t\t.addColumn(\"invited_by\", \"text\")\n\t\t.addColumn(\"expires_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"auth_tokens_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.addForeignKeyConstraint(\"auth_tokens_invited_by_fk\", [\"invited_by\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"set null\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema.createIndex(\"idx_auth_tokens_email\").on(\"auth_tokens\").column(\"email\").execute();\n\n\t// OAuth accounts (external provider links)\n\tawait db.schema\n\t\t.createTable(\"oauth_accounts\")\n\t\t.addColumn(\"provider\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"provider_account_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"oauth_accounts_pk\", [\"provider\", \"provider_account_id\"])\n\t\t.addForeignKeyConstraint(\"oauth_accounts_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_oauth_accounts_user\")\n\t\t.on(\"oauth_accounts\")\n\t\t.column(\"user_id\")\n\t\t.execute();\n\n\t// Allowed domains for self-signup\n\tawait db.schema\n\t\t.createTable(\"allowed_domains\")\n\t\t.addColumn(\"domain\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"default_role\", \"integer\", (col) => col.notNull().defaultTo(20)) // CONTRIBUTOR\n\t\t.addColumn(\"enabled\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// WebAuthn challenges (ephemeral, with TTL)\n\tawait db.schema\n\t\t.createTable(\"auth_challenges\")\n\t\t.addColumn(\"challenge\", \"text\", (col) => col.primaryKey()) // Base64url challenge\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull()) // 'registration' | 'authentication'\n\t\t.addColumn(\"user_id\", \"text\") // For registration, the user being registered\n\t\t.addColumn(\"data\", \"text\") // JSON for additional context\n\t\t.addColumn(\"expires_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Index for efficient cleanup of expired challenges\n\tawait db.schema\n\t\t.createIndex(\"idx_auth_challenges_expires\")\n\t\t.on(\"auth_challenges\")\n\t\t.column(\"expires_at\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// Drop new tables\n\tawait db.schema.dropTable(\"auth_challenges\").execute();\n\tawait db.schema.dropTable(\"allowed_domains\").execute();\n\tawait db.schema.dropTable(\"oauth_accounts\").execute();\n\tawait db.schema.dropTable(\"auth_tokens\").execute();\n\tawait db.schema.dropTable(\"credentials\").execute();\n\n\t// Recreate old users table with password_hash\n\tawait db.schema\n\t\t.createTable(\"users_old\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"email\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"password_hash\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"name\", \"text\")\n\t\t.addColumn(\"role\", \"text\", (col) => col.defaultTo(\"subscriber\"))\n\t\t.addColumn(\"avatar_id\", \"text\")\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Migrate data back (users will have empty password_hash)\n\tawait sql`\n\t\tINSERT INTO users_old (id, email, password_hash, name, role, data, created_at)\n\t\tSELECT\n\t\t\tid,\n\t\t\temail,\n\t\t\t'', -- No way to restore password\n\t\t\tname,\n\t\t\tCASE role\n\t\t\t\tWHEN 50 THEN 'admin'\n\t\t\t\tWHEN 40 THEN 'editor'\n\t\t\t\tWHEN 30 THEN 'author'\n\t\t\t\tWHEN 20 THEN 'contributor'\n\t\t\t\tELSE 'subscriber'\n\t\t\tEND,\n\t\t\tdata,\n\t\t\tcreated_at\n\t\tFROM users\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"users\").execute();\n\tawait sql`ALTER TABLE users_old RENAME TO users`.execute(db);\n\n\tawait db.schema.createIndex(\"idx_users_email\").on(\"users\").column(\"email\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * User disabled column - for soft-disabling users\n *\n * Changes:\n * - Adds disabled column to users table (INTEGER, default 0)\n * - Disabled users cannot log in\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// SQLite supports ADD COLUMN\n\tawait sql`ALTER TABLE users ADD COLUMN disabled INTEGER NOT NULL DEFAULT 0`.execute(db);\n\n\t// Create index for querying active users\n\tawait db.schema.createIndex(\"idx_users_disabled\").on(\"users\").column(\"disabled\").execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// SQLite doesn't support DROP COLUMN directly, but we can drop the index\n\t// For full rollback, table would need to be recreated\n\tawait db.schema.dropIndex(\"idx_users_disabled\").execute();\n\n\t// SQLite 3.35.0+ supports DROP COLUMN, but for compatibility:\n\t// We'll leave the column but document that it's deprecated\n\t// In production, you'd recreate the table without the column\n}\n","import { type Kysely, sql } from \"kysely\";\n\n/**\n * Migration: Add sections tables and performance indexes\n *\n * Sections are reusable content blocks that can be inserted into any Portable Text field.\n * They provide a library of pre-built page sections (heroes, CTAs, testimonials, etc.)\n * that content authors can browse and insert with a single click.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Section categories table\n\tawait db.schema\n\t\t.createTable(\"_emdash_section_categories\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.execute();\n\n\t// Sections table\n\tawait db.schema\n\t\t.createTable(\"_emdash_sections\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"title\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"description\", \"text\")\n\t\t// Categorization\n\t\t.addColumn(\"category_id\", \"text\", (col) =>\n\t\t\tcol.references(\"_emdash_section_categories.id\").onDelete(\"set null\"),\n\t\t)\n\t\t.addColumn(\"keywords\", \"text\") // JSON array for search\n\t\t// Content (Portable Text array)\n\t\t.addColumn(\"content\", \"text\", (col) => col.notNull()) // JSON\n\t\t// Preview image (optional)\n\t\t.addColumn(\"preview_media_id\", \"text\")\n\t\t// Source tracking\n\t\t.addColumn(\"source\", \"text\", (col) => col.notNull().defaultTo(\"user\")) // 'theme', 'user', 'import'\n\t\t.addColumn(\"theme_id\", \"text\") // Which theme provided it (if source='theme')\n\t\t// Metadata\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.execute();\n\n\t// Index for efficient category lookups\n\tawait db.schema\n\t\t.createIndex(\"idx_sections_category\")\n\t\t.on(\"_emdash_sections\")\n\t\t.columns([\"category_id\"])\n\t\t.execute();\n\n\t// Index for filtering by source\n\tawait db.schema\n\t\t.createIndex(\"idx_sections_source\")\n\t\t.on(\"_emdash_sections\")\n\t\t.columns([\"source\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_sections_source\").execute();\n\tawait db.schema.dropIndex(\"idx_sections_category\").execute();\n\tawait db.schema.dropTable(\"_emdash_sections\").execute();\n\tawait db.schema.dropTable(\"_emdash_section_categories\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Migration: Search Support\n *\n * Adds search configuration to collections and searchable flag to fields.\n * FTS5 tables are created dynamically when search is enabled for a collection.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Add search_config to collections (JSON: { enabled, weights })\n\tawait db.schema.alterTable(\"_emdash_collections\").addColumn(\"search_config\", \"text\").execute();\n\n\t// Add searchable flag to fields\n\tawait db.schema\n\t\t.alterTable(\"_emdash_fields\")\n\t\t.addColumn(\"searchable\", \"integer\", (col) => col.defaultTo(0))\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// SQLite doesn't support DROP COLUMN in older versions, but modern SQLite does\n\t// These columns are safe to drop\n\tawait db.schema.alterTable(\"_emdash_fields\").dropColumn(\"searchable\").execute();\n\tawait db.schema.alterTable(\"_emdash_collections\").dropColumn(\"search_config\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Add scheduled publishing support\n *\n * Adds scheduled_at column to all ec_* content tables.\n * When scheduled_at is set and status is 'scheduled', the content\n * will be auto-published when the scheduled time is reached.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Get all ec_* content tables\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\t// Add scheduled_at column to each content table\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(table.name)} \n\t\t\tADD COLUMN scheduled_at TEXT\n\t\t`.execute(db);\n\n\t\t// Create index for efficient scheduled content queries\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)} \n\t\t\tON ${sql.ref(table.name)} (scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL AND status = 'scheduled'\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// Get all ec_* content tables\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\t// Drop scheduled_at column from each content table\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\t\t// Drop index first\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(table.name)} \n\t\t\tDROP COLUMN scheduled_at\n\t\t`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Get all content tables\n\tconst tables = await db\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely migration runs against unknown schema\n\t\t.selectFrom(\"_emdash_collections\" as never)\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely migration runs against unknown schema\n\t\t.select(\"slug\" as never)\n\t\t.execute();\n\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely execute returns unknown[]; narrowing to known shape\n\tfor (const row of tables as Array<{ slug: string }>) {\n\t\tconst tableName = `ec_${row.slug}`;\n\n\t\t// Add live_revision_id column\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tADD COLUMN live_revision_id TEXT REFERENCES revisions(id)\n\t\t`.execute(db);\n\n\t\t// Add draft_revision_id column\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tADD COLUMN draft_revision_id TEXT REFERENCES revisions(id)\n\t\t`.execute(db);\n\n\t\t// Create indexes for the new columns\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${row.slug}_live_revision`)}\n\t\t\tON ${sql.ref(tableName)} (live_revision_id)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${row.slug}_draft_revision`)}\n\t\t\tON ${sql.ref(tableName)} (draft_revision_id)\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tables = await db\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely migration runs against unknown schema\n\t\t.selectFrom(\"_emdash_collections\" as never)\n\t\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely migration runs against unknown schema\n\t\t.select(\"slug\" as never)\n\t\t.execute();\n\n\t// eslint-disable-next-line typescript/no-unsafe-type-assertion -- Kysely execute returns unknown[]; narrowing to known shape\n\tfor (const row of tables as Array<{ slug: string }>) {\n\t\tconst tableName = `ec_${row.slug}`;\n\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${row.slug}_draft_revision`)}\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${row.slug}_live_revision`)}\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN draft_revision_id\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN live_revision_id\n\t\t`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Add performance indexes for common query patterns.\n *\n * Covers:\n * 1. Media table: mime_type, filename, created_at\n * 2. content_taxonomies: reverse lookup by taxonomy_id\n * 3. taxonomies: parent_id FK\n * 4. audit_logs: compound (resource_type, resource_id)\n * 5. Retroactive author_id + updated_at on existing ec_* content tables\n *    (new tables get these from createContentTable() in registry.ts)\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// ── Media indexes ────────────────────────────────────────────────\n\tawait db.schema.createIndex(\"idx_media_mime_type\").on(\"media\").column(\"mime_type\").execute();\n\tawait db.schema.createIndex(\"idx_media_filename\").on(\"media\").column(\"filename\").execute();\n\tawait db.schema.createIndex(\"idx_media_created_at\").on(\"media\").column(\"created_at\").execute();\n\n\t// ── Taxonomy indexes ─────────────────────────────────────────────\n\t// Reverse lookup: find entries with a specific term\n\tawait db.schema\n\t\t.createIndex(\"idx_content_taxonomies_term\")\n\t\t.on(\"content_taxonomies\")\n\t\t.column(\"taxonomy_id\")\n\t\t.execute();\n\n\t// Hierarchical queries filter on parent_id FK\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_parent\")\n\t\t.on(\"taxonomies\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n\n\t// ── Audit log indexes ────────────────────────────────────────────\n\t// findByResource() compound query\n\tawait db.schema\n\t\t.createIndex(\"idx_audit_resource\")\n\t\t.on(\"audit_logs\")\n\t\t.columns([\"resource_type\", \"resource_id\"])\n\t\t.execute();\n\n\t// ── Retroactive content table indexes ────────────────────────────\n\t// Add author_id and updated_at indexes to all existing ec_* tables.\n\t// New tables created after this migration get these from createContentTable().\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_author`)} \n\t\t\tON ${sql.ref(table.name)} (author_id)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_updated`)} \n\t\t\tON ${sql.ref(table.name)} (updated_at)\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// Drop retroactive content table indexes\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_updated`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_author`)}`.execute(db);\n\t}\n\n\t// Drop system table indexes\n\tawait db.schema.dropIndex(\"idx_audit_resource\").execute();\n\tawait db.schema.dropIndex(\"idx_taxonomies_parent\").execute();\n\tawait db.schema.dropIndex(\"idx_content_taxonomies_term\").execute();\n\tawait db.schema.dropIndex(\"idx_media_created_at\").execute();\n\tawait db.schema.dropIndex(\"idx_media_filename\").execute();\n\tawait db.schema.dropIndex(\"idx_media_mime_type\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * API token tables for programmatic access.\n *\n * Three tables:\n * 1. _emdash_api_tokens — Personal Access Tokens (ec_pat_...)\n * 2. _emdash_oauth_tokens — OAuth access/refresh tokens (ec_oat_/ec_ort_...)\n * 3. _emdash_device_codes — OAuth Device Flow state (RFC 8628)\n *\n * Every CREATE is guarded with `.ifNotExists()` so the migration is safe to\n * re-run against a partially-applied schema. See #954 for the failure mode:\n * if `up()` crashes mid-way (D1 subrequest limit, isolate cancellation,\n * transient connection error), the migration record never gets inserted\n * into `_emdash_migrations`, and the next request retries `up()` from the\n * top. Without these guards, the retry crashed with `table ... already\n * exists` and blocked every subsequent boot of the Worker.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// ── Personal Access Tokens ───────────────────────────────────────\n\tawait db.schema\n\t\t.createTable(\"_emdash_api_tokens\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"token_hash\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"prefix\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"scopes\", \"text\", (col) => col.notNull()) // JSON array\n\t\t.addColumn(\"expires_at\", \"text\") // null = no expiry\n\t\t.addColumn(\"last_used_at\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"api_tokens_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_api_tokens_token_hash\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_api_tokens\")\n\t\t.column(\"token_hash\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_api_tokens_user_id\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_api_tokens\")\n\t\t.column(\"user_id\")\n\t\t.execute();\n\n\t// ── OAuth Tokens ─────────────────────────────────────────────────\n\tawait db.schema\n\t\t.createTable(\"_emdash_oauth_tokens\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"token_hash\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"token_type\", \"text\", (col) => col.notNull()) // 'access' | 'refresh'\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"scopes\", \"text\", (col) => col.notNull()) // JSON array\n\t\t.addColumn(\"client_type\", \"text\", (col) => col.notNull().defaultTo(\"cli\"))\n\t\t.addColumn(\"expires_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"refresh_token_hash\", \"text\") // links access → refresh\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"oauth_tokens_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_oauth_tokens_user_id\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_oauth_tokens\")\n\t\t.column(\"user_id\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_oauth_tokens_expires\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_oauth_tokens\")\n\t\t.column(\"expires_at\")\n\t\t.execute();\n\n\t// ── Device Codes (OAuth Device Flow, RFC 8628) ───────────────────\n\tawait db.schema\n\t\t.createTable(\"_emdash_device_codes\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"device_code\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"user_code\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"scopes\", \"text\", (col) => col.notNull()) // JSON array\n\t\t.addColumn(\"user_id\", \"text\") // set when user authorizes\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"pending\"))\n\t\t.addColumn(\"expires_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"interval\", \"integer\", (col) => col.notNull().defaultTo(5))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_device_codes\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_oauth_tokens\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_api_tokens\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Authorization codes for OAuth 2.1 Authorization Code + PKCE flow.\n *\n * Used by MCP clients (Claude Desktop, VS Code, etc.) to authenticate\n * via the standard OAuth authorization code grant.\n *\n * Also adds client_id tracking to oauth_tokens for per-client revocation.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_authorization_codes\")\n\t\t.addColumn(\"code_hash\", \"text\", (col) => col.primaryKey()) // SHA-256 hash of authorization code\n\t\t.addColumn(\"client_id\", \"text\", (col) => col.notNull()) // CIMD URL or opaque string\n\t\t.addColumn(\"redirect_uri\", \"text\", (col) => col.notNull()) // Must match exactly on exchange\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"scopes\", \"text\", (col) => col.notNull()) // JSON array\n\t\t.addColumn(\"code_challenge\", \"text\", (col) => col.notNull()) // S256 challenge\n\t\t.addColumn(\"code_challenge_method\", \"text\", (col) => col.notNull().defaultTo(\"S256\"))\n\t\t.addColumn(\"resource\", \"text\") // RFC 8707 resource indicator\n\t\t.addColumn(\"expires_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addForeignKeyConstraint(\"auth_codes_user_fk\", [\"user_id\"], \"users\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_auth_codes_expires\")\n\t\t.on(\"_emdash_authorization_codes\")\n\t\t.column(\"expires_at\")\n\t\t.execute();\n\n\t// Track which client obtained a token (for per-client revocation)\n\tawait sql`ALTER TABLE _emdash_oauth_tokens ADD COLUMN client_id TEXT`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_authorization_codes\").execute();\n\t// SQLite doesn't support DROP COLUMN, but this is only for dev rollback\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Migration: SEO support\n *\n * Creates:\n * - `_emdash_seo` table: per-content SEO metadata (separate from content tables)\n * - `has_seo` column on `_emdash_collections`: opt-in flag per collection\n *\n * SEO is not a universal concern — only collections representing web pages\n * need it. The `has_seo` flag controls whether the admin shows SEO fields\n * and whether the collection's content appears in sitemaps.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Create the SEO table\n\tawait db.schema\n\t\t.createTable(\"_emdash_seo\")\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"seo_title\", \"text\")\n\t\t.addColumn(\"seo_description\", \"text\")\n\t\t.addColumn(\"seo_image\", \"text\")\n\t\t.addColumn(\"seo_canonical\", \"text\")\n\t\t.addColumn(\"seo_no_index\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"_emdash_seo_pk\", [\"collection\", \"content_id\"])\n\t\t.execute();\n\n\t// Index for batch lookups by collection (PK covers point lookups).\n\t// Sitemap queries join on (collection, content_id) which the PK covers,\n\t// and filter seo_no_index. This index supports getMany() batch queries.\n\tawait sql`\n\t\tCREATE INDEX idx_emdash_seo_collection\n\t\tON _emdash_seo (collection)\n\t`.execute(db);\n\n\t// Add has_seo flag to collections\n\tawait sql`\n\t\tALTER TABLE _emdash_collections\n\t\tADD COLUMN has_seo INTEGER NOT NULL DEFAULT 0\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`DROP TABLE IF EXISTS _emdash_seo`.execute(db);\n\n\t// SQLite doesn't support DROP COLUMN before 3.35.0, but D1 does\n\tawait sql`\n\t\tALTER TABLE _emdash_collections\n\t\tDROP COLUMN has_seo\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { isSqlite, listTablesLike } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * Migration: i18n support (row-per-locale model)\n *\n * Each piece of content can exist in multiple locales. Translations of the\n * same content share a `translation_group` ULID while each row carries its\n * own `locale` code. Slugs are unique per-locale, not globally.\n *\n * Changes:\n * 1. For every ec_* content table:\n *    - Rebuild the table to replace inline `slug TEXT UNIQUE` with\n *      `slug TEXT` + a compound `UNIQUE(slug, locale)` constraint.\n *    - Add `locale TEXT NOT NULL DEFAULT 'en'`\n *    - Add `translation_group TEXT`\n *    - Backfill `translation_group = id` for existing rows\n *    - Recreate all standard indexes plus new locale/translation_group indexes\n *\n * 2. Add `translatable` column to `_emdash_fields`\n *\n * The table-rebuild approach is required because SQLite cannot drop an inline\n * UNIQUE constraint via ALTER TABLE. We use PRAGMA table_info to discover all\n * columns (including dynamic user-defined fields) and rebuild dynamically.\n */\n\n// Column info returned by PRAGMA table_info\ninterface ColumnInfo {\n\tcid: number;\n\tname: string;\n\ttype: string;\n\tnotnull: number;\n\tdflt_value: string | null;\n\tpk: number;\n}\n\n// Index info returned by PRAGMA index_list\ninterface IndexInfo {\n\tseq: number;\n\tname: string;\n\tunique: number;\n\torigin: string;\n\tpartial: number;\n}\n\n// Index column info returned by PRAGMA index_info\ninterface IndexColumnInfo {\n\tseqno: number;\n\tcid: number;\n\tname: string;\n}\n\n/**\n * Quote an identifier for use in raw SQL. Escapes embedded double-quotes\n * per SQL standard (double them). The name should first pass\n * validateIdentifier() or validateTableName() for defense-in-depth.\n */\nconst DOUBLE_QUOTE_RE = /\"/g;\nfunction quoteIdent(name: string): string {\n\treturn `\"${name.replace(DOUBLE_QUOTE_RE, '\"\"')}\"`;\n}\n\n/** Suffix added to tmp tables during i18n migration rebuild. */\nconst I18N_TMP_SUFFIX = /_i18n_tmp$/;\n\n/** Table names from sqlite_master are ec_{slug} — validate the pattern. */\nconst TABLE_NAME_PATTERN = /^ec_[a-z][a-z0-9_]*$/;\nfunction validateTableName(name: string): void {\n\tif (!TABLE_NAME_PATTERN.test(name)) {\n\t\tthrow new Error(`Invalid content table name: \"${name}\"`);\n\t}\n}\n\n/** SQLite column types produced by EmDash's schema registry. */\nconst ALLOWED_COLUMN_TYPES = new Set([\"TEXT\", \"INTEGER\", \"REAL\", \"BLOB\", \"JSON\", \"NUMERIC\", \"\"]);\nfunction validateColumnType(type: string, colName: string): void {\n\tif (!ALLOWED_COLUMN_TYPES.has(type.toUpperCase())) {\n\t\tthrow new Error(`Unexpected column type \"${type}\" for column \"${colName}\"`);\n\t}\n}\n\n/**\n * Validate that a default value expression from PRAGMA table_info is safe\n * to interpolate into DDL. Allows: string literals, numeric literals,\n * NULL, and known function calls like datetime('now').\n *\n * Note: PRAGMA table_info strips the outer parens from expression defaults,\n * so `DEFAULT (datetime('now'))` is reported as `datetime('now')`.\n * We accept both forms and re-wrap in parens via normalizeDdlDefault().\n */\nconst SAFE_DEFAULT_PATTERN =\n\t/^(?:'[^']*'|NULL|-?\\d+(?:\\.\\d+)?|\\(?datetime\\('now'\\)\\)?|\\(?json\\('[^']*'\\)\\)?|0|1)$/i;\nfunction validateDefaultValue(value: string, colName: string): void {\n\tif (!SAFE_DEFAULT_PATTERN.test(value)) {\n\t\tthrow new Error(`Unexpected default value \"${value}\" for column \"${colName}\"`);\n\t}\n}\n\n/**\n * Normalize a PRAGMA table_info default value for use in DDL.\n * Function-call defaults (e.g. `datetime('now')`) must be wrapped in parens\n * to form valid expression defaults: `DEFAULT (datetime('now'))`.\n * PRAGMA strips the outer parens, so we re-add them here.\n */\nconst FUNCTION_DEFAULT_PATTERN = /^(?:datetime|json)\\(/i;\nfunction normalizeDdlDefault(value: string): string {\n\t// Already wrapped in parens — return as-is\n\tif (value.startsWith(\"(\")) return value;\n\tif (FUNCTION_DEFAULT_PATTERN.test(value)) return `(${value})`;\n\treturn value;\n}\n\n/**\n * Validate that a CREATE INDEX statement from sqlite_master is safe to replay.\n * Must start with CREATE [UNIQUE] INDEX and not contain suspicious patterns.\n */\nconst CREATE_INDEX_PATTERN = /^CREATE\\s+(UNIQUE\\s+)?INDEX\\s+/i;\nfunction validateCreateIndexSql(sqlStr: string, idxName: string): void {\n\tif (!CREATE_INDEX_PATTERN.test(sqlStr)) {\n\t\tthrow new Error(`Unexpected index SQL for \"${idxName}\": does not match CREATE INDEX pattern`);\n\t}\n\t// Reject semicolons which could allow statement injection\n\tif (sqlStr.includes(\";\")) {\n\t\tthrow new Error(`Unexpected index SQL for \"${idxName}\": contains semicolon`);\n\t}\n}\n\n/**\n * PostgreSQL path: ALTER TABLE supports ADD COLUMN and DROP CONSTRAINT directly.\n * No table rebuild needed.\n */\nasync function upPostgres(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const t of tableNames) {\n\t\tvalidateTableName(t);\n\n\t\t// Check if already migrated (idempotency)\n\t\tconst hasLocale = await sql<{ exists: boolean }>`\n\t\t\tSELECT EXISTS(\n\t\t\t\tSELECT 1 FROM information_schema.columns\n\t\t\t\tWHERE table_schema = current_schema() AND table_name = ${t} AND column_name = 'locale'\n\t\t\t) as exists\n\t\t`.execute(db);\n\t\tif (hasLocale.rows[0]?.exists === true) continue;\n\n\t\t// Add i18n columns\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} ADD COLUMN locale TEXT NOT NULL DEFAULT 'en'`.execute(db);\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} ADD COLUMN translation_group TEXT`.execute(db);\n\n\t\t// Drop existing unique constraint on slug (Postgres auto-names these)\n\t\t// Find the constraint name first\n\t\tconst constraints = await sql<{ conname: string }>`\n\t\t\tSELECT conname FROM pg_constraint\n\t\t\tWHERE conrelid = ${t}::regclass\n\t\t\tAND contype = 'u'\n\t\t\tAND array_length(conkey, 1) = 1\n\t\t\tAND conkey[1] = (\n\t\t\t\tSELECT attnum FROM pg_attribute\n\t\t\t\tWHERE attrelid = ${t}::regclass AND attname = 'slug'\n\t\t\t)\n\t\t`.execute(db);\n\n\t\tfor (const c of constraints.rows) {\n\t\t\tawait sql`ALTER TABLE ${sql.ref(t)} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);\n\t\t}\n\n\t\t// Add compound unique constraint\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(t)}\n\t\t\tADD CONSTRAINT ${sql.ref(`${t}_slug_locale_unique`)} UNIQUE (slug, locale)\n\t\t`.execute(db);\n\n\t\t// Backfill translation_group\n\t\tawait sql`UPDATE ${sql.ref(t)} SET translation_group = id`.execute(db);\n\n\t\t// Create indexes\n\t\tawait sql`CREATE INDEX ${sql.ref(`idx_${t}_locale`)} ON ${sql.ref(t)} (locale)`.execute(db);\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${t}_translation_group`)}\n\t\t\tON ${sql.ref(t)} (translation_group)\n\t\t`.execute(db);\n\t}\n\n\t// Add translatable flag to fields table\n\tconst hasTranslatable = await sql<{ exists: boolean }>`\n\t\tSELECT EXISTS(\n\t\t\tSELECT 1 FROM information_schema.columns\n\t\t\tWHERE table_schema = current_schema() AND table_name = '_emdash_fields' AND column_name = 'translatable'\n\t\t) as exists\n\t`.execute(db);\n\tif (hasTranslatable.rows[0]?.exists !== true) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_fields\n\t\t\tADD COLUMN translatable INTEGER NOT NULL DEFAULT 1\n\t\t`.execute(db);\n\t}\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!isSqlite(db)) {\n\t\treturn upPostgres(db);\n\t}\n\n\t// Clean up orphaned tmp tables from a previous partial run.\n\t// On D1 (no transactions), a crash mid-migration can leave these behind.\n\tconst orphanedTmps = await listTablesLike(db, \"ec_%_i18n_tmp\");\n\tfor (const tmpName of orphanedTmps) {\n\t\tvalidateTableName(tmpName.replace(I18N_TMP_SUFFIX, \"\"));\n\t\tawait sql`DROP TABLE IF EXISTS ${sql.ref(tmpName)}`.execute(db);\n\t}\n\n\t// Discover all ec_* content tables\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tconst tables = { rows: tableNames.map((name) => ({ name })) };\n\n\tfor (const table of tables.rows) {\n\t\tconst t = table.name;\n\t\tvalidateTableName(t);\n\t\tconst tmp = `${t}_i18n_tmp`;\n\n\t\t// Note: no transaction wrapper — D1 doesn't support transactions,\n\t\t// and SQLite/better-sqlite3 is single-writer so crash-safety is\n\t\t// handled by the journal mode. The tmp table approach is already\n\t\t// crash-recoverable (orphaned tmp tables are harmless).\n\t\t{\n\t\t\tconst trx = db;\n\t\t\t// ── 1. Read existing column definitions ──────────────────────\n\t\t\tconst colResult = await sql<ColumnInfo>`\n\t\t\t\tPRAGMA table_info(${sql.ref(t)})\n\t\t\t`.execute(trx);\n\t\t\tconst columns = colResult.rows;\n\n\t\t\t// ── Idempotency: skip tables already migrated ───────────────\n\t\t\t// On D1, the migrator can't use transactions, so a partially-\n\t\t\t// applied migration may not be recorded. If the table already\n\t\t\t// has a `locale` column, it was already rebuilt — skip it.\n\t\t\tif (columns.some((col) => col.name === \"locale\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// ── 2. Read existing indexes (to recreate after rebuild) ─────\n\t\t\tconst idxResult = await sql<IndexInfo>`\n\t\t\t\tPRAGMA index_list(${sql.ref(t)})\n\t\t\t`.execute(trx);\n\n\t\t\t// Collect non-autoindex, non-primary-key indexes for recreation\n\t\t\tconst indexDefs: { name: string; unique: boolean; columns: string[]; partial: number }[] = [];\n\t\t\tfor (const idx of idxResult.rows) {\n\t\t\t\t// Skip autoindexes (created by inline UNIQUE) — we're removing them\n\t\t\t\tif (idx.origin === \"pk\" || idx.name.startsWith(\"sqlite_autoindex_\")) continue;\n\n\t\t\t\tconst idxColResult = await sql<IndexColumnInfo>`\n\t\t\t\t\tPRAGMA index_info(${sql.ref(idx.name)})\n\t\t\t\t`.execute(trx);\n\n\t\t\t\tindexDefs.push({\n\t\t\t\t\tname: idx.name,\n\t\t\t\t\tunique: idx.unique === 1,\n\t\t\t\t\tcolumns: idxColResult.rows.map((c) => c.name),\n\t\t\t\t\tpartial: idx.partial,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// For partial indexes we need the original CREATE statement\n\t\t\tconst partialSqls = new Map<string, string>();\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\tif (idx.partial) {\n\t\t\t\t\tconst createResult = await sql<{ sql: string }>`\n\t\t\t\t\t\tSELECT sql FROM sqlite_master \n\t\t\t\t\t\tWHERE type = 'index' AND name = ${idx.name}\n\t\t\t\t\t`.execute(trx);\n\t\t\t\t\tif (createResult.rows[0]?.sql) {\n\t\t\t\t\t\tpartialSqls.set(idx.name, createResult.rows[0].sql);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── 3. Build column defs for the new table ──────────────────\n\t\t\t// Validate all column names from PRAGMA before using them in raw SQL.\n\t\t\t// These originate from our own schema, but defense-in-depth matters.\n\t\t\tfor (const col of columns) {\n\t\t\t\tvalidateIdentifier(col.name, \"column name\");\n\t\t\t}\n\n\t\t\t// Replace slug's inline UNIQUE with a table-level UNIQUE(slug, locale)\n\t\t\tconst colDefs: string[] = [];\n\t\t\tconst colNames: string[] = [];\n\n\t\t\tfor (const col of columns) {\n\t\t\t\tvalidateColumnType(col.type || \"TEXT\", col.name);\n\t\t\t\tcolNames.push(quoteIdent(col.name));\n\t\t\t\tlet def = `${quoteIdent(col.name)} ${col.type || \"TEXT\"}`;\n\n\t\t\t\tif (col.pk) {\n\t\t\t\t\tdef += \" PRIMARY KEY\";\n\t\t\t\t} else if (col.name === \"slug\") {\n\t\t\t\t\t// Intentionally omit UNIQUE — compound unique below\n\t\t\t\t} else {\n\t\t\t\t\tif (col.notnull) def += \" NOT NULL\";\n\t\t\t\t}\n\n\t\t\t\tif (col.dflt_value !== null) {\n\t\t\t\t\tvalidateDefaultValue(col.dflt_value, col.name);\n\t\t\t\t\tdef += ` DEFAULT ${normalizeDdlDefault(col.dflt_value)}`;\n\t\t\t\t}\n\n\t\t\t\tcolDefs.push(def);\n\t\t\t}\n\n\t\t\t// Append new i18n columns\n\t\t\tcolDefs.push(\"\\\"locale\\\" TEXT NOT NULL DEFAULT 'en'\");\n\t\t\tcolDefs.push('\"translation_group\" TEXT');\n\n\t\t\t// Compound unique: same slug + locale must be unique\n\t\t\tcolDefs.push('UNIQUE(\"slug\", \"locale\")');\n\n\t\t\tconst createColsSql = colDefs.join(\",\\n\\t\\t\\t\\t\");\n\t\t\tconst selectColsSql = colNames.join(\", \");\n\n\t\t\t// ── 4. Rebuild the table ────────────────────────────────────\n\t\t\t// Drop all existing indexes first (they reference the old table)\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);\n\t\t\t}\n\n\t\t\t// Create new table with updated schema\n\t\t\tawait sql\n\t\t\t\t.raw(`CREATE TABLE ${quoteIdent(tmp)} (\\n\\t\\t\\t\\t${createColsSql}\\n\\t\\t\\t)`)\n\t\t\t\t.execute(trx);\n\n\t\t\t// Copy existing data, backfilling locale='en' and translation_group=id\n\t\t\tawait sql\n\t\t\t\t.raw(\n\t\t\t\t\t`INSERT INTO ${quoteIdent(tmp)} (${selectColsSql}, \"locale\", \"translation_group\")\\n\\t\\t\\t SELECT ${selectColsSql}, 'en', \"id\" FROM ${quoteIdent(t)}`,\n\t\t\t\t)\n\t\t\t\t.execute(trx);\n\n\t\t\t// Swap tables\n\t\t\tawait sql`DROP TABLE ${sql.ref(t)}`.execute(trx);\n\t\t\tawait sql.raw(`ALTER TABLE ${quoteIdent(tmp)} RENAME TO ${quoteIdent(t)}`).execute(trx);\n\n\t\t\t// ── 5. Recreate all original indexes ────────────────────────\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\t// Skip the old slug-only index — replaced by slug_locale below\n\t\t\t\tif (idx.name === `idx_${t}_slug`) continue;\n\n\t\t\t\tif (idx.partial && partialSqls.has(idx.name)) {\n\t\t\t\t\t// Partial indexes — validate the SQL before replaying\n\t\t\t\t\tconst idxSql = partialSqls.get(idx.name)!;\n\t\t\t\t\tvalidateCreateIndexSql(idxSql, idx.name);\n\t\t\t\t\tawait sql.raw(idxSql).execute(trx);\n\t\t\t\t} else {\n\t\t\t\t\t// Validate index column names before interpolation\n\t\t\t\t\tfor (const c of idx.columns) {\n\t\t\t\t\t\tvalidateIdentifier(c, \"index column name\");\n\t\t\t\t\t}\n\t\t\t\t\tconst cols = idx.columns.map((c) => quoteIdent(c)).join(\", \");\n\t\t\t\t\tconst unique = idx.unique ? \"UNIQUE \" : \"\";\n\t\t\t\t\tawait sql\n\t\t\t\t\t\t.raw(`CREATE ${unique}INDEX ${quoteIdent(idx.name)} ON ${quoteIdent(t)} (${cols})`)\n\t\t\t\t\t\t.execute(trx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── 6. Create new i18n indexes ──────────────────────────────\n\t\t\t// slug_locale unique is handled by the table constraint above,\n\t\t\t// but we still want a regular slug index for non-locale-aware queries\n\t\t\tawait sql`\n\t\t\t\tCREATE INDEX ${sql.ref(`idx_${t}_slug`)} \n\t\t\t\tON ${sql.ref(t)} (slug)\n\t\t\t`.execute(trx);\n\n\t\t\tawait sql`\n\t\t\t\tCREATE INDEX ${sql.ref(`idx_${t}_locale`)} \n\t\t\t\tON ${sql.ref(t)} (locale)\n\t\t\t`.execute(trx);\n\n\t\t\tawait sql`\n\t\t\t\tCREATE INDEX ${sql.ref(`idx_${t}_translation_group`)} \n\t\t\t\tON ${sql.ref(t)} (translation_group)\n\t\t\t`.execute(trx);\n\t\t}\n\t}\n\n\t// ── 7. Add translatable flag to fields table ────────────────────\n\t// Guard against duplicate column — on D1 the migration may have\n\t// partially applied without being recorded (no transaction support).\n\tconst fieldCols = await sql<ColumnInfo>`\n\t\tPRAGMA table_info(_emdash_fields)\n\t`.execute(db);\n\tif (!fieldCols.rows.some((col) => col.name === \"translatable\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_fields \n\t\t\tADD COLUMN translatable INTEGER NOT NULL DEFAULT 1\n\t\t`.execute(db);\n\t}\n}\n\n/**\n * PostgreSQL down path: straightforward ALTER TABLE operations.\n */\nasync function downPostgres(db: Kysely<unknown>): Promise<void> {\n\tawait sql`ALTER TABLE _emdash_fields DROP COLUMN translatable`.execute(db);\n\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tfor (const t of tableNames) {\n\t\tvalidateTableName(t);\n\n\t\t// Drop i18n indexes\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${t}_locale`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${t}_translation_group`)}`.execute(db);\n\n\t\t// Drop compound unique constraint\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} DROP CONSTRAINT IF EXISTS ${sql.ref(`${t}_slug_locale_unique`)}`.execute(\n\t\t\tdb,\n\t\t);\n\n\t\t// Restore simple unique constraint on slug\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} ADD CONSTRAINT ${sql.ref(`${t}_slug_unique`)} UNIQUE (slug)`.execute(\n\t\t\tdb,\n\t\t);\n\n\t\t// Drop i18n columns\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} DROP COLUMN locale`.execute(db);\n\t\tawait sql`ALTER TABLE ${sql.ref(t)} DROP COLUMN translation_group`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (!isSqlite(db)) {\n\t\treturn downPostgres(db);\n\t}\n\n\t// Remove translatable column from fields table\n\tawait sql`\n\t\tALTER TABLE _emdash_fields\n\t\tDROP COLUMN translatable\n\t`.execute(db);\n\n\t// Discover all ec_* content tables\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst t = tableName;\n\t\tvalidateTableName(t);\n\t\tconst tmp = `${t}_i18n_tmp`;\n\n\t\t// No transaction — see comment in up() above.\n\t\t{\n\t\t\tconst trx = db;\n\t\t\t// ── 1. Read current column definitions ──────────────────────\n\t\t\tconst colResult = await sql<ColumnInfo>`\n\t\t\t\tPRAGMA table_info(${sql.ref(t)})\n\t\t\t`.execute(trx);\n\t\t\tconst columns = colResult.rows;\n\n\t\t\t// ── 2. Read current indexes ─────────────────────────────────\n\t\t\tconst idxResult = await sql<IndexInfo>`\n\t\t\t\tPRAGMA index_list(${sql.ref(t)})\n\t\t\t`.execute(trx);\n\n\t\t\tconst indexDefs: { name: string; unique: boolean; columns: string[]; partial: number }[] = [];\n\t\t\tfor (const idx of idxResult.rows) {\n\t\t\t\tif (idx.origin === \"pk\" || idx.name.startsWith(\"sqlite_autoindex_\")) continue;\n\n\t\t\t\tconst idxColResult = await sql<IndexColumnInfo>`\n\t\t\t\t\tPRAGMA index_info(${sql.ref(idx.name)})\n\t\t\t\t`.execute(trx);\n\n\t\t\t\tindexDefs.push({\n\t\t\t\t\tname: idx.name,\n\t\t\t\t\tunique: idx.unique === 1,\n\t\t\t\t\tcolumns: idxColResult.rows.map((c) => c.name),\n\t\t\t\t\tpartial: idx.partial,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Save partial index SQL\n\t\t\tconst partialSqls = new Map<string, string>();\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\tif (idx.partial) {\n\t\t\t\t\tconst createResult = await sql<{ sql: string }>`\n\t\t\t\t\t\tSELECT sql FROM sqlite_master \n\t\t\t\t\t\tWHERE type = 'index' AND name = ${idx.name}\n\t\t\t\t\t`.execute(trx);\n\t\t\t\t\tif (createResult.rows[0]?.sql) {\n\t\t\t\t\t\tpartialSqls.set(idx.name, createResult.rows[0].sql);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// ── 3. Build column defs WITHOUT locale/translation_group ───\n\t\t\t// Validate all column names\n\t\t\tfor (const col of columns) {\n\t\t\t\tif (col.name === \"locale\" || col.name === \"translation_group\") continue;\n\t\t\t\tvalidateIdentifier(col.name, \"column name\");\n\t\t\t}\n\n\t\t\t// Restore slug's inline UNIQUE\n\t\t\tconst colDefs: string[] = [];\n\t\t\tconst colNames: string[] = [];\n\n\t\t\tfor (const col of columns) {\n\t\t\t\t// Skip i18n columns\n\t\t\t\tif (col.name === \"locale\" || col.name === \"translation_group\") continue;\n\n\t\t\t\tvalidateColumnType(col.type || \"TEXT\", col.name);\n\t\t\t\tcolNames.push(quoteIdent(col.name));\n\t\t\t\tlet def = `${quoteIdent(col.name)} ${col.type || \"TEXT\"}`;\n\n\t\t\t\tif (col.pk) {\n\t\t\t\t\tdef += \" PRIMARY KEY\";\n\t\t\t\t} else if (col.name === \"slug\") {\n\t\t\t\t\t// Restore inline UNIQUE\n\t\t\t\t\tdef += \" UNIQUE\";\n\t\t\t\t} else {\n\t\t\t\t\tif (col.notnull) def += \" NOT NULL\";\n\t\t\t\t}\n\n\t\t\t\tif (col.dflt_value !== null) {\n\t\t\t\t\tvalidateDefaultValue(col.dflt_value, col.name);\n\t\t\t\t\tdef += ` DEFAULT ${normalizeDdlDefault(col.dflt_value)}`;\n\t\t\t\t}\n\n\t\t\t\tcolDefs.push(def);\n\t\t\t}\n\n\t\t\tconst createColsSql = colDefs.join(\",\\n\\t\\t\\t\\t\");\n\t\t\tconst selectColsSql = colNames.join(\", \");\n\n\t\t\t// ── 4. Rebuild the table ────────────────────────────────────\n\t\t\t// Drop all existing indexes first\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(idx.name)}`.execute(trx);\n\t\t\t}\n\n\t\t\t// Create table with original schema (slug UNIQUE, no i18n columns)\n\t\t\tawait sql\n\t\t\t\t.raw(`CREATE TABLE ${quoteIdent(tmp)} (\\n\\t\\t\\t\\t${createColsSql}\\n\\t\\t\\t)`)\n\t\t\t\t.execute(trx);\n\n\t\t\t// Copy data — keep only one row per content item.\n\t\t\t// Prefer locale='en' rows. For items without an 'en' row, pick the\n\t\t\t// row with the smallest locale code (deterministic, unlike bare GROUP BY).\n\t\t\t// Handle NULL translation_group by treating each such row as its own group.\n\t\t\t// INSERT OR IGNORE skips any duplicate slugs from the fallback pass.\n\t\t\tawait sql\n\t\t\t\t.raw(\n\t\t\t\t\t`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})\n\t\t\t SELECT ${selectColsSql} FROM ${quoteIdent(t)}\n\t\t\t WHERE \"locale\" = 'en'`,\n\t\t\t\t)\n\t\t\t\t.execute(trx);\n\n\t\t\tawait sql\n\t\t\t\t.raw(\n\t\t\t\t\t`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})\n\t\t\t SELECT ${selectColsSql} FROM ${quoteIdent(t)}\n\t\t\t WHERE \"id\" NOT IN (SELECT \"id\" FROM ${quoteIdent(tmp)})\n\t\t\t AND \"id\" IN (\n\t\t\t\tSELECT \"id\" FROM ${quoteIdent(t)} AS t2\n\t\t\t\tWHERE t2.\"translation_group\" IS NOT NULL\n\t\t\t\tAND t2.\"locale\" = (\n\t\t\t\t\tSELECT MIN(t3.\"locale\") FROM ${quoteIdent(t)} AS t3\n\t\t\t\t\tWHERE t3.\"translation_group\" = t2.\"translation_group\"\n\t\t\t\t)\n\t\t\t )`,\n\t\t\t\t)\n\t\t\t\t.execute(trx);\n\n\t\t\t// Pick up any rows with NULL translation_group that weren't already copied\n\t\t\tawait sql\n\t\t\t\t.raw(\n\t\t\t\t\t`INSERT OR IGNORE INTO ${quoteIdent(tmp)} (${selectColsSql})\n\t\t\t SELECT ${selectColsSql} FROM ${quoteIdent(t)}\n\t\t\t WHERE \"id\" NOT IN (SELECT \"id\" FROM ${quoteIdent(tmp)})\n\t\t\t AND \"translation_group\" IS NULL`,\n\t\t\t\t)\n\t\t\t\t.execute(trx);\n\n\t\t\t// Swap tables\n\t\t\tawait sql`DROP TABLE ${sql.ref(t)}`.execute(trx);\n\t\t\tawait sql.raw(`ALTER TABLE ${quoteIdent(tmp)} RENAME TO ${quoteIdent(t)}`).execute(trx);\n\n\t\t\t// ── 5. Recreate indexes ─────────────────────────────────────\n\t\t\tfor (const idx of indexDefs) {\n\t\t\t\t// Skip i18n-specific indexes — they don't exist in the old schema\n\t\t\t\tif (idx.name === `idx_${t}_locale`) continue;\n\t\t\t\tif (idx.name === `idx_${t}_translation_group`) continue;\n\n\t\t\t\tif (idx.partial && partialSqls.has(idx.name)) {\n\t\t\t\t\t// Partial indexes — validate the SQL before replaying\n\t\t\t\t\tconst idxSql = partialSqls.get(idx.name)!;\n\t\t\t\t\tvalidateCreateIndexSql(idxSql, idx.name);\n\t\t\t\t\tawait sql.raw(idxSql).execute(trx);\n\t\t\t\t} else {\n\t\t\t\t\t// Filter out i18n columns from any index that might reference them\n\t\t\t\t\tconst cols = idx.columns.filter((c) => c !== \"locale\" && c !== \"translation_group\");\n\t\t\t\t\tif (cols.length === 0) continue;\n\n\t\t\t\t\t// Validate column names\n\t\t\t\t\tfor (const c of cols) {\n\t\t\t\t\t\tvalidateIdentifier(c, \"index column name\");\n\t\t\t\t\t}\n\t\t\t\t\tconst colsSql = cols.map((c) => quoteIdent(c)).join(\", \");\n\t\t\t\t\tconst unique = idx.unique ? \"UNIQUE \" : \"\";\n\t\t\t\t\tawait sql\n\t\t\t\t\t\t.raw(`CREATE ${unique}INDEX ${quoteIdent(idx.name)} ON ${quoteIdent(t)} (${colsSql})`)\n\t\t\t\t\t\t.execute(trx);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Migration: URL pattern for collections\n *\n * Adds `url_pattern` column to `_emdash_collections` so each collection\n * can declare its own URL structure (e.g. \"/{slug}\" for pages, \"/blog/{slug}\"\n * for posts). Used for menu URL resolution, sitemaps, and path-based lookups.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE _emdash_collections\n\t\tADD COLUMN url_pattern TEXT\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE _emdash_collections\n\t\tDROP COLUMN url_pattern\n\t`.execute(db);\n}\n","import { type Kysely, sql } from \"kysely\";\n\n/**\n * Migration: Remove section categories\n *\n * Section categories had a complete backend but no UI to create or manage them.\n * Rather than building the missing UI for a feature with very little need at this stage,\n * we're removing the feature entirely.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Drop index before column — SQLite requires this order\n\tawait db.schema.dropIndex(\"idx_sections_category\").ifExists().execute();\n\n\tawait db.schema.alterTable(\"_emdash_sections\").dropColumn(\"category_id\").execute();\n\n\tawait db.schema.dropTable(\"_emdash_section_categories\").execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// Recreate section categories table\n\tawait db.schema\n\t\t.createTable(\"_emdash_section_categories\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(sql`CURRENT_TIMESTAMP`))\n\t\t.execute();\n\n\t// Re-add category_id column to sections\n\tawait db.schema\n\t\t.alterTable(\"_emdash_sections\")\n\t\t.addColumn(\"category_id\", \"text\", (col) =>\n\t\t\tcol.references(\"_emdash_section_categories.id\").onDelete(\"set null\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_sections_category\")\n\t\t.on(\"_emdash_sections\")\n\t\t.columns([\"category_id\"])\n\t\t.execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Migration: Add marketplace fields to _plugin_state\n *\n * Adds `source` and `marketplace_version` columns to track\n * whether a plugin was installed from config or marketplace,\n * and which marketplace version is installed.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// source: 'config' (declared in astro.config) or 'marketplace' (installed from registry)\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tADD COLUMN source TEXT NOT NULL DEFAULT 'config'\n\t`.execute(db);\n\n\t// marketplace_version: tracks installed version for update checking\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tADD COLUMN marketplace_version TEXT\n\t`.execute(db);\n\n\t// Index for efficient marketplace plugin queries on cold start\n\tawait sql`\n\t\tCREATE INDEX idx_plugin_state_source\n\t\tON _plugin_state (source)\n\t\tWHERE source = 'marketplace'\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tDROP INDEX IF EXISTS idx_plugin_state_source\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN marketplace_version\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN source\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Migration: Add display metadata to _plugin_state\n *\n * Stores display_name and description for marketplace plugins\n * so the admin UI can show meaningful info without re-fetching\n * from the marketplace on every page load.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tADD COLUMN display_name TEXT\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tADD COLUMN description TEXT\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN description\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN display_name\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Migration: Add placeholder columns to media table\n *\n * Stores blurhash and dominant_color for LQIP (Low Quality Image Placeholder)\n * support. Generated at upload time from image pixel data.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE media\n\t\tADD COLUMN blurhash TEXT\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE media\n\t\tADD COLUMN dominant_color TEXT\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tALTER TABLE media\n\t\tDROP COLUMN dominant_color\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE media\n\t\tDROP COLUMN blurhash\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Create OAuth clients table\n *\n * Implements the oauth_clients registry so that the authorization endpoint\n * can validate client_id and enforce a per-client redirect URI allowlist.\n *\n * Each client has a set of pre-registered redirect URIs (JSON array).\n * The authorize endpoint rejects any redirect_uri not in the client's list.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_oauth_clients\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey()) // Client ID (e.g. URL or opaque string)\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull()) // Human-readable name\n\t\t.addColumn(\"redirect_uris\", \"text\", (col) => col.notNull()) // JSON array of allowed redirect URIs\n\t\t.addColumn(\"scopes\", \"text\") // JSON array of allowed scopes (null = all)\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_oauth_clients\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Create cron tasks table for plugin scheduled tasks.\n *\n * Each plugin can register cron tasks (recurring or one-shot) which are\n * stored here and executed by the platform-specific scheduler.\n *\n * The `next_run_at` + `status` + `enabled` index drives the \"find overdue\n * tasks\" query used by CronExecutor.tick().\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_cron_tasks\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"plugin_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"task_name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"schedule\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"is_oneshot\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"data\", \"text\") // JSON\n\t\t.addColumn(\"next_run_at\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"last_run_at\", \"text\")\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"idle\"))\n\t\t.addColumn(\"locked_at\", \"text\")\n\t\t.addColumn(\"enabled\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"uq_cron_tasks_plugin_task\", [\"plugin_id\", \"task_name\"])\n\t\t.execute();\n\n\t// Equality columns first (enabled, status), then range column (next_run_at)\n\t// for optimal B-tree index usage in the tick query.\n\tawait db.schema\n\t\t.createIndex(\"idx_cron_tasks_due\")\n\t\t.on(\"_emdash_cron_tasks\")\n\t\t.columns([\"enabled\", \"status\", \"next_run_at\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_cron_tasks_plugin\")\n\t\t.on(\"_emdash_cron_tasks\")\n\t\t.column(\"plugin_id\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_cron_tasks\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Create the comments table\n\tawait db.schema\n\t\t.createTable(\"_emdash_comments\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"collection\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"parent_id\", \"text\", (col) =>\n\t\t\tcol.references(\"_emdash_comments.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"author_name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"author_email\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"author_url\", \"text\")\n\t\t.addColumn(\"author_user_id\", \"text\", (col) => col.references(\"users.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"body\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"pending\"))\n\t\t.addColumn(\"ip_hash\", \"text\")\n\t\t.addColumn(\"user_agent\", \"text\")\n\t\t.addColumn(\"moderation_metadata\", \"text\") // JSON\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Indexes\n\tawait db.schema\n\t\t.createIndex(\"idx_comments_content\")\n\t\t.on(\"_emdash_comments\")\n\t\t.columns([\"collection\", \"content_id\", \"status\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_comments_parent\")\n\t\t.on(\"_emdash_comments\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_comments_status\")\n\t\t.on(\"_emdash_comments\")\n\t\t.columns([\"status\", \"created_at\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_comments_author_email\")\n\t\t.on(\"_emdash_comments\")\n\t\t.column(\"author_email\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_comments_author_user\")\n\t\t.on(\"_emdash_comments\")\n\t\t.column(\"author_user_id\")\n\t\t.execute();\n\n\t// Add collection-level comment settings columns\n\tawait db.schema\n\t\t.alterTable(\"_emdash_collections\")\n\t\t.addColumn(\"comments_enabled\", \"integer\", (col) => col.defaultTo(0))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.alterTable(\"_emdash_collections\")\n\t\t.addColumn(\"comments_moderation\", \"text\", (col) => col.defaultTo(\"first_time\"))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.alterTable(\"_emdash_collections\")\n\t\t.addColumn(\"comments_closed_after_days\", \"integer\", (col) => col.defaultTo(90))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.alterTable(\"_emdash_collections\")\n\t\t.addColumn(\"comments_auto_approve_users\", \"integer\", (col) => col.defaultTo(1))\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_comments\").execute();\n\n\t// Note: SQLite doesn't support DROP COLUMN before 3.35.0.\n\t// For down migrations on the collection settings columns, the table\n\t// would need to be rebuilt. Skipping for simplicity in v0.\n}\n","import { sql, type Kysely } from \"kysely\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait sql`ALTER TABLE _emdash_comments DROP COLUMN author_url`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.alterTable(\"_emdash_comments\").addColumn(\"author_url\", \"text\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// Redirect rules table\n\tawait db.schema\n\t\t.createTable(\"_emdash_redirects\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"source\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"destination\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"type\", \"integer\", (col) => col.notNull().defaultTo(301))\n\t\t.addColumn(\"is_pattern\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"enabled\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t.addColumn(\"hits\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"last_hit_at\", \"text\")\n\t\t.addColumn(\"group_name\", \"text\")\n\t\t.addColumn(\"auto\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// Unique source for exact (non-pattern) rules\n\t// SQLite doesn't support partial indexes with WHERE on all versions,\n\t// so we use a regular index and enforce uniqueness in the application layer\n\tawait db.schema\n\t\t.createIndex(\"idx_redirects_source\")\n\t\t.on(\"_emdash_redirects\")\n\t\t.column(\"source\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_redirects_enabled\")\n\t\t.on(\"_emdash_redirects\")\n\t\t.column(\"enabled\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_redirects_group\")\n\t\t.on(\"_emdash_redirects\")\n\t\t.column(\"group_name\")\n\t\t.execute();\n\n\t// 404 log table\n\tawait db.schema\n\t\t.createTable(\"_emdash_404_log\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"path\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"referrer\", \"text\")\n\t\t.addColumn(\"user_agent\", \"text\")\n\t\t.addColumn(\"ip\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema.createIndex(\"idx_404_log_path\").on(\"_emdash_404_log\").column(\"path\").execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_404_log_created\")\n\t\t.on(\"_emdash_404_log\")\n\t\t.column(\"created_at\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_404_log\").execute();\n\tawait db.schema.dropTable(\"_emdash_redirects\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Widen scheduled publishing index\n *\n * The original partial index (013) only covered status='scheduled'.\n * Published posts can now have scheduled draft changes, so widen the\n * index to cover all rows where scheduled_at IS NOT NULL.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)}\n\t\t\tON ${sql.ref(table.name)} (scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_scheduled`)}\n\t\t`.execute(db);\n\n\t\t// Restore the original narrower index\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_scheduled`)}\n\t\t\tON ${sql.ref(table.name)} (scheduled_at)\n\t\t\tWHERE scheduled_at IS NOT NULL AND status = 'scheduled'\n\t\t`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { currentTimestamp, listTablesLike } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_bylines\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"display_name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"bio\", \"text\")\n\t\t.addColumn(\"avatar_media_id\", \"text\", (col) => col.references(\"media.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"website_url\", \"text\")\n\t\t.addColumn(\"user_id\", \"text\", (col) => col.references(\"users.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"is_guest\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait sql`\n\t\tCREATE UNIQUE INDEX ${sql.ref(\"idx_bylines_user_id_unique\")}\n\t\tON ${sql.ref(\"_emdash_bylines\")} (user_id)\n\t\tWHERE user_id IS NOT NULL\n\t`.execute(db);\n\n\tawait db.schema.createIndex(\"idx_bylines_slug\").on(\"_emdash_bylines\").column(\"slug\").execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_bylines_display_name\")\n\t\t.on(\"_emdash_bylines\")\n\t\t.column(\"display_name\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_content_bylines\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"collection_slug\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"byline_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_bylines.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"role_label\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"content_bylines_unique\", [\"collection_slug\", \"content_id\", \"byline_id\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_content\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.columns([\"collection_slug\", \"content_id\", \"sort_order\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_byline\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.column(\"byline_id\")\n\t\t.execute();\n\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tfor (const tableName of tableNames) {\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tADD COLUMN primary_byline_id TEXT\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${tableName}_primary_byline`)}\n\t\t\tON ${sql.ref(tableName)} (primary_byline_id)\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tfor (const tableName of tableNames) {\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_primary_byline`)}\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tALTER TABLE ${sql.ref(tableName)}\n\t\t\tDROP COLUMN primary_byline_id\n\t\t`.execute(db);\n\t}\n\n\tawait db.schema.dropTable(\"_emdash_content_bylines\").execute();\n\tawait db.schema.dropTable(\"_emdash_bylines\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Migration: Rate limits table + device code polling tracking.\n *\n * 1. Create _emdash_rate_limits for database-backed rate limiting\n *    of unauthenticated endpoints (device code, magic link, passkey).\n *\n * 2. Add last_polled_at column to _emdash_device_codes for\n *    RFC 8628 slow_down enforcement.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// ── Rate limits table ────────────────────────────────────────────\n\tawait db.schema\n\t\t.createTable(\"_emdash_rate_limits\")\n\t\t.addColumn(\"key\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"window\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"count\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t.addPrimaryKeyConstraint(\"pk_rate_limits\", [\"key\", \"window\"])\n\t\t.execute();\n\n\t// Index on window for efficient cleanup of expired entries\n\tawait db.schema\n\t\t.createIndex(\"idx_rate_limits_window\")\n\t\t.on(\"_emdash_rate_limits\")\n\t\t.column(\"window\")\n\t\t.execute();\n\n\t// ── Device code polling tracking ─────────────────────────────────\n\tawait db.schema.alterTable(\"_emdash_device_codes\").addColumn(\"last_polled_at\", \"text\").execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_rate_limits\").execute();\n\n\t// SQLite doesn't support DROP COLUMN before 3.35.0, but since this is\n\t// dev-only (v0, no migrations in production), we accept the limitation.\n\tawait db.schema.alterTable(\"_emdash_device_codes\").dropColumn(\"last_polled_at\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Optimize content table indexes for D1 performance\n *\n * Addresses GitHub issue #131: Full table scans causing massive D1 row reads.\n *\n * Changes:\n * 1. Replaces single-column indexes with composite indexes on ec_* tables\n * 2. Adds partial indexes for _emdash_comments status counting\n *\n * Impact: Reduces D1 row reads by 90%+ for admin panel operations.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\t// Drop redundant single-column indexes that will be replaced by composites\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_status`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_created`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_updated`)}`.execute(db);\n\n\t\t// Composite index for listing queries: WHERE deleted_at IS NULL ORDER BY updated_at DESC\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_updated_id`)}\n\t\t\tON ${sql.ref(table.name)} (deleted_at, updated_at DESC, id DESC)\n\t\t`.execute(db);\n\n\t\t// Composite index for count-by-status queries: WHERE deleted_at IS NULL AND status = ?\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_status`)}\n\t\t\tON ${sql.ref(table.name)} (deleted_at, status)\n\t\t`.execute(db);\n\n\t\t// Composite index for created-at ordering: WHERE deleted_at IS NULL ORDER BY created_at DESC\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_created_id`)}\n\t\t\tON ${sql.ref(table.name)} (deleted_at, created_at DESC, id DESC)\n\t\t`.execute(db);\n\t}\n\n\t// Add partial indexes for efficient comment status counting\n\t// Each index contains only rows for one status, enabling fast COUNT queries\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS idx_comments_pending\n\t\tON _emdash_comments (id)\n\t\tWHERE status = 'pending'\n\t`.execute(db);\n\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS idx_comments_approved\n\t\tON _emdash_comments (id)\n\t\tWHERE status = 'approved'\n\t`.execute(db);\n\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS idx_comments_spam\n\t\tON _emdash_comments (id)\n\t\tWHERE status = 'spam'\n\t`.execute(db);\n\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS idx_comments_trash\n\t\tON _emdash_comments (id)\n\t\tWHERE status = 'trash'\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\t// Drop composite indexes\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_updated_id`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_status`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_created_id`)}`.execute(db);\n\n\t\t// Restore original single-column indexes\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_status`)}\n\t\t\tON ${sql.ref(table.name)} (status)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_created`)}\n\t\t\tON ${sql.ref(table.name)} (created_at)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_deleted`)}\n\t\t\tON ${sql.ref(table.name)} (deleted_at)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX ${sql.ref(`idx_${table.name}_updated`)}\n\t\t\tON ${sql.ref(table.name)} (updated_at)\n\t\t`.execute(db);\n\t}\n\n\t// Drop partial indexes for comments\n\tawait sql`DROP INDEX IF EXISTS idx_comments_pending`.execute(db);\n\tawait sql`DROP INDEX IF EXISTS idx_comments_approved`.execute(db);\n\tawait sql`DROP INDEX IF EXISTS idx_comments_spam`.execute(db);\n\tawait sql`DROP INDEX IF EXISTS idx_comments_trash`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table.name}_deleted_published_id`)}\n\t\t\tON ${sql.ref(table.name)} (deleted_at, published_at DESC, id DESC)\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tconst table = { name: tableName };\n\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${table.name}_deleted_published_id`)}`.execute(\n\t\t\tdb,\n\t\t);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Migration: Bounded 404 logging\n *\n * Hardens `_emdash_404_log` against unauthenticated DoS. Previously every 404\n * inserted a new row, so an attacker could grow the table without bound.\n *\n * Changes:\n *   - Adds `hits` (default 1, NOT NULL)\n *   - Adds `last_seen_at` (nullable; SQLite can't add NOT NULL with a\n *     non-constant default to a populated table, so the column is nullable\n *     at the schema level and backfilled from `created_at` for existing rows;\n *     new inserts via `log404` always set it)\n *   - Deduplicates existing rows by path, keeping the most recent row per\n *     path and summing hits\n *   - Adds a UNIQUE index on `path` so upsert semantics work\n */\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst hitsExists = await columnExists(db, \"_emdash_404_log\", \"hits\");\n\n\t// 1. Add columns.\n\tif (!hitsExists) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_404_log\")\n\t\t\t.addColumn(\"hits\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t\t.execute();\n\t}\n\n\t// SQLite won't accept a non-constant default when adding a NOT NULL column\n\t// to a table with existing rows, so backfill in two steps: add nullable,\n\t// populate, then rely on the application layer / future inserts to set it.\n\tif (!(await columnExists(db, \"_emdash_404_log\", \"last_seen_at\"))) {\n\t\tawait db.schema.alterTable(\"_emdash_404_log\").addColumn(\"last_seen_at\", \"text\").execute();\n\t}\n\n\t// Backfill last_seen_at from created_at for existing rows.\n\tawait sql`\n\t\tUPDATE _emdash_404_log\n\t\tSET last_seen_at = created_at\n\t\tWHERE last_seen_at IS NULL\n\t`.execute(db);\n\n\t// 2. Deduplicate existing rows by path.\n\t//    For each path, roll up hits and pick the freshest last_seen_at onto\n\t//    a single keeper row, then delete the non-keepers. Uses window\n\t//    functions (ROW_NUMBER) so the dedup SQL is valid on both SQLite\n\t//    (3.25+, 2018) and Postgres. The previous GROUP BY approach was\n\t//    accepted by SQLite but invalid on Postgres because `id` wasn't in\n\t//    the GROUP BY or wrapped in an aggregate.\n\tif (!hitsExists) {\n\t\tawait sql`\n\t\t\tWITH ranked AS (\n\t\t\t\tSELECT\n\t\t\t\t\tid,\n\t\t\t\t\tpath,\n\t\t\t\t\tROW_NUMBER() OVER (\n\t\t\t\t\t\tPARTITION BY path\n\t\t\t\t\t\tORDER BY created_at DESC, id DESC\n\t\t\t\t\t) AS rn,\n\t\t\t\t\tCOUNT(*) OVER (PARTITION BY path) AS path_count,\n\t\t\t\t\tMAX(created_at) OVER (PARTITION BY path) AS latest_created_at\n\t\t\t\tFROM _emdash_404_log\n\t\t\t)\n\t\t\tUPDATE _emdash_404_log\n\t\t\tSET\n\t\t\t\thits = (SELECT path_count FROM ranked WHERE ranked.id = _emdash_404_log.id),\n\t\t\t\tlast_seen_at = (SELECT latest_created_at FROM ranked WHERE ranked.id = _emdash_404_log.id)\n\t\t\tWHERE id IN (SELECT id FROM ranked WHERE rn = 1)\n\t\t`.execute(db);\n\n\t\t// Delete the non-keepers (every row except the freshest per path).\n\t\tawait sql`\n\t\t\tDELETE FROM _emdash_404_log\n\t\t\tWHERE id IN (\n\t\t\t\tSELECT id FROM (\n\t\t\t\t\tSELECT\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tROW_NUMBER() OVER (\n\t\t\t\t\t\t\tPARTITION BY path\n\t\t\t\t\t\t\tORDER BY created_at DESC, id DESC\n\t\t\t\t\t\t) AS rn\n\t\t\t\t\tFROM _emdash_404_log\n\t\t\t\t) AS ranked\n\t\t\t\tWHERE rn > 1\n\t\t\t)\n\t\t`.execute(db);\n\t}\n\n\t// 3. Add unique index on path for upsert semantics.\n\tawait db.schema\n\t\t.createIndex(\"idx_404_log_path_unique\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_404_log\")\n\t\t.column(\"path\")\n\t\t.unique()\n\t\t.execute();\n\n\t// Drop the old non-unique index; the unique one covers the same lookups.\n\tawait db.schema.dropIndex(\"idx_404_log_path\").ifExists().execute();\n\n\t// 4. Index on last_seen_at for eviction ordering.\n\tawait db.schema\n\t\t.createIndex(\"idx_404_log_last_seen\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_404_log\")\n\t\t.column(\"last_seen_at\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_404_log_last_seen\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx_404_log_path_unique\").ifExists().execute();\n\n\t// Restore the original non-unique path index.\n\tawait db.schema\n\t\t.createIndex(\"idx_404_log_path\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_404_log\")\n\t\t.column(\"path\")\n\t\t.execute();\n\n\tawait db.schema.alterTable(\"_emdash_404_log\").dropColumn(\"last_seen_at\").execute();\n\tawait db.schema.alterTable(\"_emdash_404_log\").dropColumn(\"hits\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { getI18nConfig } from \"../../i18n/config.js\";\nimport { currentTimestamp, isSqlite } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * i18n for menus + taxonomies. Adds `locale` + `translation_group` to system\n * tables and stores translation_groups (not row ids) in\n * `_emdash_menu_items.reference_id` and `content_taxonomies.taxonomy_id`.\n * Backfill locale and column DEFAULTs use the site's configured defaultLocale.\n */\n\nfunction getDefaultLocale(): string {\n\treturn getI18nConfig()?.defaultLocale ?? \"en\";\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst defaultLocale = getDefaultLocale();\n\n\tif (isSqlite(db)) {\n\t\t// Rebuild children before parents to drop FKs that would CASCADE\n\t\t// on D1. D1 enforces FKs but ignores `PRAGMA foreign_keys = OFF`\n\t\t// (the standard SQLite escape), and its replacement\n\t\t// `PRAGMA defer_foreign_keys = ON` only defers constraint\n\t\t// validation, not CASCADE actions — so DROP TABLE <parent> still\n\t\t// wipes child rows (#1021). The FKs have to be physically removed\n\t\t// before the drop.\n\t\t// - `content_taxonomies.taxonomy_id` → `taxonomies(id) ON DELETE CASCADE`\n\t\t// - `_emdash_menu_items.menu_id` → `_emdash_menus(id) ON DELETE CASCADE`\n\t\t// - `_emdash_menu_items.parent_id` → `_emdash_menu_items(id) ON DELETE CASCADE`\n\t\t// Both FKs on `_emdash_menu_items` are stripped during the rebuild.\n\t\t// The runtime (`MenuRepository.delete` / `setItems`) already\n\t\t// performs the child-delete explicitly, so the loss of the cascade\n\t\t// is invisible to callers.\n\t\tawait rebuildContentTaxonomies(db);\n\t\tawait rebuildMenuItems(db, defaultLocale);\n\t\tawait rebuildMenus(db, defaultLocale);\n\t\tawait rebuildTaxonomies(db, defaultLocale);\n\t\tawait rebuildTaxonomyDefs(db, defaultLocale);\n\t\tawait remapMenuItemRefs(db);\n\t\treturn;\n\t}\n\n\tawait pgWiden(db, \"_emdash_menus\", [\"name\"], [\"name\", \"locale\"], defaultLocale);\n\tawait pgWiden(db, \"_emdash_menu_items\", null, null, defaultLocale);\n\tawait pgWiden(db, \"taxonomies\", [\"name\", \"slug\"], [\"name\", \"slug\", \"locale\"], defaultLocale);\n\tawait pgWiden(db, \"_emdash_taxonomy_defs\", [\"name\"], [\"name\", \"locale\"], defaultLocale);\n\tawait pgRemapContentTaxonomies(db);\n\tawait remapMenuItemRefs(db);\n}\n\nasync function rebuildMenus(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tif (await hasColumn(db, \"_emdash_menus\", \"locale\")) return;\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_menus_new\"`).execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_menus_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.addUniqueConstraint(\"_emdash_menus_name_locale_unique\", [\"name\", \"locale\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_menus_new (id, name, label, created_at, updated_at, locale, translation_group)\n\t\tSELECT id, name, label, created_at, updated_at, ${defaultLocale}, id FROM _emdash_menus\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_menus\").execute();\n\tawait sql`ALTER TABLE _emdash_menus_new RENAME TO _emdash_menus`.execute(db);\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_menus_locale\")\n\t\t.on(\"_emdash_menus\")\n\t\t.column(\"locale\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_menus_translation_group\")\n\t\t.on(\"_emdash_menus\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n}\n\nasync function rebuildMenuItems(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\t// Full table rebuild rather than ALTER TABLE ADD COLUMN: this strips\n\t// the `menu_id` and `parent_id` FKs from migration 005 so the\n\t// subsequent `DROP TABLE _emdash_menus` can't cascade-wipe menu items\n\t// on D1 (#1021). The FKs were never load-bearing at runtime — D1\n\t// disables FK enforcement, and `MenuRepository` always deletes\n\t// children explicitly. Mirrors `rebuildContentTaxonomies` below.\n\tif (await hasColumn(db, \"_emdash_menu_items\", \"locale\")) return;\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_menu_items_new\"`).execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_menu_items_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"menu_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_id\", \"text\")\n\t\t.addColumn(\"sort_order\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"type\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"reference_collection\", \"text\")\n\t\t.addColumn(\"reference_id\", \"text\")\n\t\t.addColumn(\"custom_url\", \"text\")\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"title_attr\", \"text\")\n\t\t.addColumn(\"target\", \"text\")\n\t\t.addColumn(\"css_classes\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_menu_items_new (\n\t\t\tid, menu_id, parent_id, sort_order, type, reference_collection,\n\t\t\treference_id, custom_url, label, title_attr, target, css_classes,\n\t\t\tcreated_at, locale, translation_group\n\t\t)\n\t\tSELECT\n\t\t\tid, menu_id, parent_id, sort_order, type, reference_collection,\n\t\t\treference_id, custom_url, label, title_attr, target, css_classes,\n\t\t\tcreated_at, ${defaultLocale}, id\n\t\tFROM _emdash_menu_items\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_menu_items\").execute();\n\tawait sql`ALTER TABLE _emdash_menu_items_new RENAME TO _emdash_menu_items`.execute(db);\n\n\t// Indexes from migration 005 are dropped with the underlying table; recreate.\n\tawait db.schema\n\t\t.createIndex(\"idx_menu_items_menu\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.columns([\"menu_id\", \"sort_order\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_menu_items_parent\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_menu_items_locale\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.column(\"locale\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_menu_items_translation_group\")\n\t\t.on(\"_emdash_menu_items\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n}\n\nasync function rebuildTaxonomies(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tif (await hasColumn(db, \"taxonomies\", \"locale\")) return;\n\tawait sql.raw(`DROP TABLE IF EXISTS \"taxonomies_new\"`).execute(db);\n\tawait sql`DROP INDEX IF EXISTS idx_taxonomies_name`.execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"taxonomies_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_id\", \"text\")\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.addUniqueConstraint(\"taxonomies_name_slug_locale_unique\", [\"name\", \"slug\", \"locale\"])\n\t\t// Self-FK points at `taxonomies_new` (not `taxonomies`) so dropping\n\t\t// the old table doesn't fire ON DELETE SET NULL against parent_id\n\t\t// values on D1. SQLite's RENAME rewrites the FK target to the new\n\t\t// name automatically.\n\t\t.addForeignKeyConstraint(\n\t\t\t\"taxonomies_parent_fk\",\n\t\t\t[\"parent_id\"],\n\t\t\t\"taxonomies_new\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"set null\"),\n\t\t)\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO taxonomies_new (id, name, slug, label, parent_id, data, locale, translation_group)\n\t\tSELECT id, name, slug, label, parent_id, data, ${defaultLocale}, id FROM taxonomies\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"taxonomies\").execute();\n\tawait sql`ALTER TABLE taxonomies_new RENAME TO taxonomies`.execute(db);\n\n\tawait db.schema.createIndex(\"idx_taxonomies_name\").on(\"taxonomies\").column(\"name\").execute();\n\tawait db.schema.createIndex(\"idx_taxonomies_locale\").on(\"taxonomies\").column(\"locale\").execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_translation_group\")\n\t\t.on(\"taxonomies\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n\t// Dropping the old table dropped idx_taxonomies_parent (from 015); recreate\n\t// it here for parity with rebuildMenuItems/rebuildContentTaxonomies. Existing\n\t// installs recover it via migration 047.\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_parent\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n}\n\nasync function rebuildTaxonomyDefs(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tif (await hasColumn(db, \"_emdash_taxonomy_defs\", \"locale\")) return;\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_taxonomy_defs_new\"`).execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_taxonomy_defs_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label_singular\", \"text\")\n\t\t.addColumn(\"hierarchical\", \"integer\", (c) => c.defaultTo(0))\n\t\t.addColumn(\"collections\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.addUniqueConstraint(\"_emdash_taxonomy_defs_name_locale_unique\", [\"name\", \"locale\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_taxonomy_defs_new\n\t\t\t(id, name, label, label_singular, hierarchical, collections, created_at, locale, translation_group)\n\t\tSELECT id, name, label, label_singular, hierarchical, collections, created_at, ${defaultLocale}, id\n\t\tFROM _emdash_taxonomy_defs\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_taxonomy_defs\").execute();\n\tawait sql`ALTER TABLE _emdash_taxonomy_defs_new RENAME TO _emdash_taxonomy_defs`.execute(db);\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_taxonomy_defs_locale\")\n\t\t.on(\"_emdash_taxonomy_defs\")\n\t\t.column(\"locale\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_taxonomy_defs_translation_group\")\n\t\t.on(\"_emdash_taxonomy_defs\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n}\n\nasync function rebuildContentTaxonomies(db: Kysely<unknown>): Promise<void> {\n\t// Drops the FK so `taxonomy_id` can point at a translation_group rather\n\t// than a row id. Runs before `rebuildTaxonomies` so the drop is safe on D1.\n\t// No remap is needed here: `rebuildTaxonomies` later seeds `translation_group\n\t// = id` for every preserved row, so the row-id values we copy resolve as\n\t// translation_group references after the migration completes. This coupling\n\t// is load-bearing — if the translation_group seed ever changes, this needs\n\t// an explicit remap *after* `rebuildTaxonomies` runs.\n\tconst fks = await sql<{ id: number }>`PRAGMA foreign_key_list(content_taxonomies)`.execute(db);\n\tif (fks.rows.length > 0) {\n\t\tawait sql.raw(`DROP TABLE IF EXISTS \"content_taxonomies_new\"`).execute(db);\n\t\tawait db.schema\n\t\t\t.createTable(\"content_taxonomies_new\")\n\t\t\t.addColumn(\"collection\", \"text\", (c) => c.notNull())\n\t\t\t.addColumn(\"entry_id\", \"text\", (c) => c.notNull())\n\t\t\t.addColumn(\"taxonomy_id\", \"text\", (c) => c.notNull())\n\t\t\t.addPrimaryKeyConstraint(\"content_taxonomies_pk\", [\"collection\", \"entry_id\", \"taxonomy_id\"])\n\t\t\t.execute();\n\n\t\tawait sql`\n\t\t\tINSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)\n\t\t\tSELECT collection, entry_id, taxonomy_id FROM content_taxonomies\n\t\t`.execute(db);\n\n\t\tawait db.schema.dropTable(\"content_taxonomies\").execute();\n\t\tawait sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);\n\t}\n\n\t// Recreate the taxonomy_id index from migration 015 unconditionally, OUTSIDE\n\t// the FK-presence guard above. The rebuild both strips the FK and drops the\n\t// old table's indexes, but D1 auto-commits each DDL statement — so a first\n\t// run that failed after the drop leaves the table with no FK and no index.\n\t// The retry then finds zero FKs and skips the rebuild; if the index recreate\n\t// lived inside that guard it would never run and the index would be lost for\n\t// good (#1701). `IF NOT EXISTS` keeps this a no-op once present.\n\tawait sql`CREATE INDEX IF NOT EXISTS idx_content_taxonomies_term ON content_taxonomies(taxonomy_id)`.execute(\n\t\tdb,\n\t);\n}\n\nasync function remapMenuItemRefs(db: Kysely<unknown>): Promise<void> {\n\t// Items with `reference_collection IS NULL` are left untouched — the\n\t// runtime fallback in `menus/index.ts` resolves them by id.\n\tconst collections = await sql<{ slug: string }>`SELECT slug FROM _emdash_collections`.execute(db);\n\tfor (const { slug } of collections.rows) {\n\t\tvalidateIdentifier(slug, \"collection slug\");\n\t\tconst ec = sql.ref(`ec_${slug}`);\n\t\tawait sql`\n\t\t\tUPDATE _emdash_menu_items SET reference_id = (\n\t\t\t\tSELECT translation_group FROM ${ec} WHERE ${ec}.id = _emdash_menu_items.reference_id\n\t\t\t)\n\t\t\tWHERE reference_collection = ${slug} AND reference_id IS NOT NULL\n\t\t\t\tAND EXISTS (SELECT 1 FROM ${ec} WHERE ${ec}.id = _emdash_menu_items.reference_id)\n\t\t`.execute(db);\n\t}\n\tawait sql`\n\t\tUPDATE _emdash_menu_items SET reference_id = (\n\t\t\tSELECT translation_group FROM taxonomies WHERE taxonomies.id = _emdash_menu_items.reference_id\n\t\t)\n\t\tWHERE type = 'taxonomy' AND reference_id IS NOT NULL\n\t\t\tAND EXISTS (SELECT 1 FROM taxonomies WHERE taxonomies.id = _emdash_menu_items.reference_id)\n\t`.execute(db);\n}\n\nasync function pgWiden(\n\tdb: Kysely<unknown>,\n\ttable: string,\n\toldCols: string[] | null,\n\tnewCols: string[] | null,\n\tdefaultLocale: string,\n): Promise<void> {\n\tvalidateSystemIdent(table);\n\tconst ref = sql.ref(table);\n\tawait sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS locale TEXT NOT NULL DEFAULT ${sql.lit(defaultLocale)}`.execute(\n\t\tdb,\n\t);\n\tawait sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS translation_group TEXT`.execute(db);\n\tawait sql`UPDATE ${ref} SET translation_group = id WHERE translation_group IS NULL`.execute(db);\n\tawait sql`CREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_locale`)} ON ${ref} (locale)`.execute(\n\t\tdb,\n\t);\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${table}_translation_group`)} ON ${ref} (translation_group)\n\t`.execute(db);\n\n\tif (!oldCols || !newCols) return;\n\tfor (const c of [...oldCols, ...newCols]) validateSystemIdent(c);\n\tconst cons = await sql<{ conname: string }>`\n\t\tSELECT conname FROM pg_constraint c\n\t\tWHERE c.conrelid = ${table}::regclass AND c.contype = 'u'\n\t\t\tAND array_length(c.conkey, 1) = ${oldCols.length}\n\t\t\tAND (\n\t\t\t\tSELECT array_agg(a.attname ORDER BY pos.ord)\n\t\t\t\tFROM unnest(c.conkey) WITH ORDINALITY AS pos(attnum, ord)\n\t\t\t\tJOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = pos.attnum\n\t\t\t)::text[] = ${oldCols}::text[]\n\t`.execute(db);\n\tfor (const c of cons.rows) {\n\t\tawait sql`ALTER TABLE ${ref} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);\n\t}\n\tconst cols = sql.join(\n\t\tnewCols.map((c) => sql.ref(c)),\n\t\tsql`, `,\n\t);\n\tawait sql`\n\t\tALTER TABLE ${ref}\n\t\tADD CONSTRAINT ${sql.ref(`${table}_${newCols.join(\"_\")}_unique`)} UNIQUE (${cols})\n\t`.execute(db);\n}\n\nasync function pgRemapContentTaxonomies(db: Kysely<unknown>): Promise<void> {\n\tconst fks = await sql<{ conname: string }>`\n\t\tSELECT conname FROM pg_constraint\n\t\tWHERE conrelid = 'content_taxonomies'::regclass AND contype = 'f'\n\t`.execute(db);\n\tfor (const c of fks.rows) {\n\t\tawait sql`ALTER TABLE content_taxonomies DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);\n\t}\n\tawait sql`\n\t\tUPDATE content_taxonomies SET taxonomy_id = t.translation_group\n\t\tFROM taxonomies t WHERE t.id = content_taxonomies.taxonomy_id\n\t`.execute(db);\n}\n\nasync function hasColumn(db: Kysely<unknown>, table: string, column: string): Promise<boolean> {\n\tconst rows = await sql<{ name: string }>`PRAGMA table_info(${sql.ref(table)})`.execute(db);\n\treturn rows.rows.some((r) => r.name === column);\n}\n\nconst SYSTEM_IDENT = /^[_a-z][a-z0-9_]*$/;\nfunction validateSystemIdent(name: string): void {\n\tif (!SYSTEM_IDENT.test(name)) throw new Error(`Invalid identifier: \"${name}\"`);\n}\n\n/**\n * down() restores the FK on content_taxonomies. Rows whose taxonomy_id doesn't\n * resolve to a (translation_group, defaultLocale) pair would fail the rebuild\n * after other tables are already stripped — leaving the user mid-rollback.\n * Surface dangling rows up front instead.\n */\nasync function assertContentTaxonomiesResolve(\n\tdb: Kysely<unknown>,\n\tdefaultLocale: string,\n): Promise<void> {\n\tconst result = await sql<{ count: number | string }>`\n\t\tSELECT COUNT(*) AS count FROM content_taxonomies ct\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT 1 FROM taxonomies t\n\t\t\tWHERE t.translation_group = ct.taxonomy_id AND t.locale = ${defaultLocale}\n\t\t)\n\t`.execute(db);\n\tconst count = Number(result.rows[0]?.count ?? 0);\n\tif (count > 0) {\n\t\tthrow new Error(\n\t\t\t`Cannot revert migration 036_i18n_menus_and_taxonomies: ` +\n\t\t\t\t`${count} row(s) in \"content_taxonomies\" reference a translation_group ` +\n\t\t\t\t`with no row in \"taxonomies\" at locale=\"${defaultLocale}\". ` +\n\t\t\t\t`Clean up the dangling associations before rolling back.`,\n\t\t);\n\t}\n}\n\n/**\n * down() is destructive on multi-locale installs (dropping `locale` collapses\n * translated rows onto an ambiguous unique key). Refuse to run when any row\n * sits at a locale other than the configured defaultLocale.\n */\nasync function assertSingleLocale(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tconst tables = [\"_emdash_menus\", \"_emdash_menu_items\", \"taxonomies\", \"_emdash_taxonomy_defs\"];\n\tfor (const table of tables) {\n\t\tvalidateSystemIdent(table);\n\t\tconst result = await sql<{ count: number | string }>`\n\t\t\tSELECT COUNT(*) AS count FROM ${sql.ref(table)} WHERE locale != ${defaultLocale}\n\t\t`.execute(db);\n\t\tconst count = Number(result.rows[0]?.count ?? 0);\n\t\tif (count > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot revert migration 036_i18n_menus_and_taxonomies: ` +\n\t\t\t\t\t`${count} row(s) in \"${table}\" use a non-default locale ` +\n\t\t\t\t\t`(defaultLocale=\"${defaultLocale}\"). ` +\n\t\t\t\t\t`Reverting would drop them silently. Export translations first ` +\n\t\t\t\t\t`(or delete them) and re-run the rollback. ` +\n\t\t\t\t\t`See packages/core/src/database/migrations/036_i18n_menus_and_taxonomies.ts.`,\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst defaultLocale = getDefaultLocale();\n\tawait assertSingleLocale(db, defaultLocale);\n\tawait assertContentTaxonomiesResolve(db, defaultLocale);\n\n\tconst widenedTables = [\n\t\t\"_emdash_menus\",\n\t\t\"_emdash_menu_items\",\n\t\t\"taxonomies\",\n\t\t\"_emdash_taxonomy_defs\",\n\t];\n\n\tif (isSqlite(db)) {\n\t\t// Indexes first: the locale index on _emdash_menu_items would\n\t\t// otherwise block its DROP COLUMN.\n\t\tfor (const t of widenedTables) {\n\t\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);\n\t\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);\n\t\t}\n\n\t\t// Remap content_taxonomies values back to row ids while `taxonomies`\n\t\t// still has `translation_group` + `locale`. No FK is restored yet, so\n\t\t// the subsequent DROP TABLE taxonomies can't cascade on D1 (#1021).\n\t\tawait remapContentTaxonomiesDown(db, defaultLocale);\n\t\t// Menus first: safe because up() stripped the cascade FK from\n\t\t// _emdash_menu_items.menu_id, so dropping _emdash_menus doesn't\n\t\t// cascade. The FK from migration 005 is NOT restored on\n\t\t// _emdash_menu_items: the runtime deletes children explicitly,\n\t\t// so the only observable effect of the FK was the #1021 cascade\n\t\t// we're trying to avoid.\n\t\tawait rebuildMenusDown(db);\n\t\tawait rebuildMenuItemsDown(db);\n\t\tawait rebuildTaxonomiesDown(db);\n\t\tawait rebuildTaxonomyDefsDown(db);\n\t\tawait restoreContentTaxonomiesFk(db);\n\t\treturn;\n\t}\n\n\tfor (const t of widenedTables) {\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_${t}_locale`).execute(db);\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_${t}_translation_group`).execute(db);\n\t\tawait sql.raw(`ALTER TABLE \"${t}\" DROP COLUMN IF EXISTS locale`).execute(db);\n\t\tawait sql.raw(`ALTER TABLE \"${t}\" DROP COLUMN IF EXISTS translation_group`).execute(db);\n\t}\n}\n\nasync function remapContentTaxonomiesDown(\n\tdb: Kysely<unknown>,\n\tdefaultLocale: string,\n): Promise<void> {\n\t// Map translation_group back to row id (assertSingleLocale guarantees 1:1)\n\t// without restoring the FK — that happens after `taxonomies` is rebuilt.\n\tawait sql`\n\t\tUPDATE content_taxonomies\n\t\tSET taxonomy_id = COALESCE(\n\t\t\t(SELECT t.id FROM taxonomies t\n\t\t\t WHERE t.translation_group = content_taxonomies.taxonomy_id\n\t\t\t\t AND t.locale = ${defaultLocale}),\n\t\t\ttaxonomy_id\n\t\t)\n\t`.execute(db);\n}\n\nasync function restoreContentTaxonomiesFk(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"content_taxonomies_new\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"content_taxonomies_new\")\n\t\t.addColumn(\"collection\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"entry_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"taxonomy_id\", \"text\", (c) => c.notNull())\n\t\t.addPrimaryKeyConstraint(\"content_taxonomies_pk\", [\"collection\", \"entry_id\", \"taxonomy_id\"])\n\t\t.addForeignKeyConstraint(\n\t\t\t\"content_taxonomies_taxonomy_fk\",\n\t\t\t[\"taxonomy_id\"],\n\t\t\t\"taxonomies\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT OR IGNORE INTO content_taxonomies_new (collection, entry_id, taxonomy_id)\n\t\tSELECT collection, entry_id, taxonomy_id FROM content_taxonomies\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"content_taxonomies\").execute();\n\tawait sql`ALTER TABLE content_taxonomies_new RENAME TO content_taxonomies`.execute(db);\n\n\tawait sql`CREATE INDEX IF NOT EXISTS idx_content_taxonomies_term ON content_taxonomies(taxonomy_id)`.execute(\n\t\tdb,\n\t);\n}\n\nasync function rebuildMenusDown(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_menus_old\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"_emdash_menus_old\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\tawait sql`\n\t\tINSERT INTO _emdash_menus_old (id, name, label, created_at, updated_at)\n\t\tSELECT id, name, label, created_at, updated_at FROM _emdash_menus\n\t`.execute(db);\n\tawait db.schema.dropTable(\"_emdash_menus\").execute();\n\tawait sql`ALTER TABLE _emdash_menus_old RENAME TO _emdash_menus`.execute(db);\n}\n\nasync function rebuildMenuItemsDown(db: Kysely<unknown>): Promise<void> {\n\t// No UNIQUE on (locale,…) here, so DROP COLUMN suffices. The migration-005\n\t// FKs are NOT restored: up() removed them to fix #1021, and the runtime\n\t// already deletes child rows explicitly so the cascade isn't needed.\n\tawait sql.raw(`ALTER TABLE _emdash_menu_items DROP COLUMN locale`).execute(db);\n\tawait sql.raw(`ALTER TABLE _emdash_menu_items DROP COLUMN translation_group`).execute(db);\n}\n\nasync function rebuildTaxonomiesDown(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"taxonomies_old\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"taxonomies_old\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_id\", \"text\")\n\t\t.addColumn(\"data\", \"text\")\n\t\t.addUniqueConstraint(\"taxonomies_name_slug_unique\", [\"name\", \"slug\"])\n\t\t.addForeignKeyConstraint(\n\t\t\t\"taxonomies_parent_fk\",\n\t\t\t[\"parent_id\"],\n\t\t\t\"taxonomies_old\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"set null\"),\n\t\t)\n\t\t.execute();\n\tawait sql`\n\t\tINSERT INTO taxonomies_old (id, name, slug, label, parent_id, data)\n\t\tSELECT id, name, slug, label, parent_id, data FROM taxonomies\n\t`.execute(db);\n\tawait db.schema.dropTable(\"taxonomies\").execute();\n\tawait sql`ALTER TABLE taxonomies_old RENAME TO taxonomies`.execute(db);\n\tawait db.schema.createIndex(\"idx_taxonomies_name\").on(\"taxonomies\").column(\"name\").execute();\n\t// Restore the pre-036 (migration 015) parent index so the rollback is faithful.\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_parent\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n}\n\nasync function rebuildTaxonomyDefsDown(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_taxonomy_defs_old\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"_emdash_taxonomy_defs_old\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"label_singular\", \"text\")\n\t\t.addColumn(\"hierarchical\", \"integer\", (c) => c.defaultTo(0))\n\t\t.addColumn(\"collections\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\tawait sql`\n\t\tINSERT INTO _emdash_taxonomy_defs_old\n\t\t\t(id, name, label, label_singular, hierarchical, collections, created_at)\n\t\tSELECT id, name, label, label_singular, hierarchical, collections, created_at\n\t\tFROM _emdash_taxonomy_defs\n\t`.execute(db);\n\tawait db.schema.dropTable(\"_emdash_taxonomy_defs\").execute();\n\tawait sql`ALTER TABLE _emdash_taxonomy_defs_old RENAME TO _emdash_taxonomy_defs`.execute(db);\n}\n","import { type Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"credentials\", \"algorithm\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"credentials\")\n\t\t\t.addColumn(\"algorithm\", \"integer\", (col) => col.notNull().defaultTo(-7))\n\t\t\t.execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await columnExists(db, \"credentials\", \"algorithm\")) {\n\t\tawait db.schema.alterTable(\"credentials\").dropColumn(\"algorithm\").execute();\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { isSqlite } from \"../dialect-helpers.js\";\n\ninterface ColumnInfo {\n\tname: string;\n}\n\ninterface IndexInfo {\n\tname: string;\n}\n\n/**\n * Migration: Add registry fields to _plugin_state\n *\n * Extends the marketplace columns added in 022 to support the\n * experimental decentralized plugin registry (see RFC #694). Rather\n * than introducing a separate `_registry_plugin_state` table, we\n * reuse the same row shape and distinguish registry installs via the\n * existing `source` column (now `'config' | 'marketplace' | 'registry'`).\n *\n * Registry plugins are addressed by `(publisher_did, slug)` in their\n * lexicon records but stored under a hashed, opaque `plugin_id` for\n * runtime compatibility -- see `packages/core/src/registry/plugin-id.ts`.\n * The `(publisher_did, slug)` pair is preserved here for update\n * resolution against the currently configured aggregator and for admin\n * UI rendering (\"by @example.dev\").\n *\n * All new columns are nullable; existing marketplace and config rows\n * keep working unchanged.\n *\n * Idempotency: D1 and SQLite don't honor the migration runner's\n * advisory lock, so a partial re-apply (cold start race between two\n * isolates, retry after a connection drop) can re-enter this `up`\n * function with the columns or index already in place. Each step\n * checks before adding to keep the migration safe under partial\n * re-application. The same pattern is used in 019_i18n.ts.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (isSqlite(db)) {\n\t\tawait upSqlite(db);\n\t} else {\n\t\tawait upPostgres(db);\n\t}\n}\n\nasync function upSqlite(db: Kysely<unknown>): Promise<void> {\n\tconst cols = await sql<ColumnInfo>`PRAGMA table_info(_plugin_state)`.execute(db);\n\tconst colNames = new Set(cols.rows.map((c) => c.name));\n\n\tif (!colNames.has(\"registry_publisher_did\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _plugin_state\n\t\t\tADD COLUMN registry_publisher_did TEXT\n\t\t`.execute(db);\n\t}\n\n\tif (!colNames.has(\"registry_slug\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _plugin_state\n\t\t\tADD COLUMN registry_slug TEXT\n\t\t`.execute(db);\n\t}\n\n\tconst indexes = await sql<IndexInfo>`PRAGMA index_list(_plugin_state)`.execute(db);\n\tconst indexNames = new Set(indexes.rows.map((i) => i.name));\n\n\tif (!indexNames.has(\"idx_plugin_state_registry\")) {\n\t\tawait sql`\n\t\t\tCREATE INDEX idx_plugin_state_registry\n\t\t\tON _plugin_state (source)\n\t\t\tWHERE source = 'registry'\n\t\t`.execute(db);\n\t}\n}\n\nasync function upPostgres(db: Kysely<unknown>): Promise<void> {\n\t// Scope the column check to the connection's current schema.\n\t// Without `table_schema = current_schema()`, a `_plugin_state` table\n\t// in another schema (per-tenant Postgres, shared Postgres clusters,\n\t// per-test schemas) makes this query see columns from the wrong\n\t// table and skip the ALTERs entirely, leaving the active schema's\n\t// `_plugin_state` missing the registry columns.\n\tconst cols = await sql<{ column_name: string }>`\n\t\tSELECT column_name FROM information_schema.columns\n\t\tWHERE table_name = '_plugin_state'\n\t\t  AND table_schema = current_schema()\n\t`.execute(db);\n\tconst colNames = new Set(cols.rows.map((c) => c.column_name));\n\n\tif (!colNames.has(\"registry_publisher_did\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _plugin_state\n\t\t\tADD COLUMN registry_publisher_did TEXT\n\t\t`.execute(db);\n\t}\n\n\tif (!colNames.has(\"registry_slug\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _plugin_state\n\t\t\tADD COLUMN registry_slug TEXT\n\t\t`.execute(db);\n\t}\n\n\t// pg's CREATE INDEX IF NOT EXISTS handles the race natively; partial\n\t// index syntax differs from SQLite (`WHERE` is supported), so the\n\t// statement is otherwise identical.\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS idx_plugin_state_registry\n\t\tON _plugin_state (source)\n\t\tWHERE source = 'registry'\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tDROP INDEX IF EXISTS idx_plugin_state_registry\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN registry_slug\n\t`.execute(db);\n\n\tawait sql`\n\t\tALTER TABLE _plugin_state\n\t\tDROP COLUMN registry_publisher_did\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { isSqlite } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * Migration: Rebuild FTS5 indexes with corruption-safe triggers\n *\n * Background: FTS5 virtual tables created with `content='ec_<slug>'` are\n * *external content* tables. Pre-fix versions of EmDash used the\n * *contentless*-table sync pattern in their triggers\n * (`DELETE FROM fts WHERE rowid = OLD.rowid`), which is unsafe for\n * external-content FTS5 because FTS5 then reads the *current* row from\n * `ec_<slug>` to compute the tokens to remove. By the time\n * `AFTER UPDATE` fires the row already holds NEW values, so the wrong\n * tokens are removed and the inverted index drifts out of sync until\n * SQLite raises `SQLITE_CORRUPT_VTAB` on the next mutation\n * (see issue #649, PR #768).\n *\n * The fix that ships in the same release rewrites the triggers to use\n * the documented external-content-safe form:\n *\n *     INSERT INTO fts(fts, rowid, col1, col2)\n *     VALUES('delete', OLD.rowid, OLD.col1, OLD.col2);\n *\n * gated on `OLD.deleted_at IS NULL` so we don't try to remove rows that\n * were never indexed (which would itself raise `SQLITE_CORRUPT_VTAB`).\n *\n * This migration finalises the fix for *upgrading* sites. New triggers\n * alone are not enough: existing sites still carry the broken triggers in\n * `sqlite_master` and, in many cases, an already-corrupted FTS shadow\n * index. We:\n *\n *   1. Find every collection with FTS enabled\n *      (`_emdash_collections.search_config -> enabled = true`).\n *   2. Drop its three sync triggers and the FTS5 virtual table itself\n *      (which removes the shadow tables `<fts>_data`, `<fts>_idx`,\n *      `<fts>_docsize`, `<fts>_config`, `<fts>_content`).\n *   3. Recreate the FTS5 table and triggers with the corruption-safe\n *      `'delete'` form.\n *   4. Repopulate from `ec_<slug> WHERE deleted_at IS NULL`.\n *\n * The trigger SQL emitted here MUST stay in lock-step with\n * `FTSManager.createTriggers` in `src/search/fts-manager.ts`. The two\n * code paths build the same triggers from the same field list; keeping\n * them aligned means a fresh install (via FTSManager) and an upgraded\n * install (via this migration) end up with identical schemas. If\n * `createTriggers` ever changes again, add a new migration rather than\n * editing this one -- migrations are forward-only.\n *\n * Postgres: no-op. FTS5 is SQLite-only; the `FTSManager` already\n * short-circuits on non-SQLite, and there are no FTS tables to rebuild.\n *\n * D1: the migration is idempotent at the granularity we care about\n * (drop-then-create + repopulate). A partial re-apply that gets as far\n * as dropping the FTS table but not recreating it leaves the collection\n * without an index; the next runtime call to `verifyAndRepairIndex`\n * (e.g. on a search request) detects the missing table and rebuilds.\n * Re-running this migration in full is also safe -- it always replaces\n * the index from `ec_<slug>` content.\n */\n\ninterface CollectionRow {\n\tslug: string;\n\tsearch_config: string | null;\n}\n\ninterface FieldRow {\n\tslug: string;\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!isSqlite(db)) return;\n\n\tconst collections = await sql<CollectionRow>`\n\t\tSELECT slug, search_config FROM _emdash_collections\n\t\tWHERE search_config IS NOT NULL\n\t`.execute(db);\n\n\tfor (const collection of collections.rows) {\n\t\tif (!isSearchEnabled(collection.search_config)) continue;\n\n\t\t// Slug came from `_emdash_collections.slug`, where the schema\n\t\t// registry validates it against the same identifier rules on\n\t\t// write. Re-validate here defensively before interpolating it\n\t\t// into raw SQL -- this is a migration, so a malformed slug\n\t\t// that somehow landed in the DB must not become an injection\n\t\t// vector.\n\t\ttry {\n\t\t\tvalidateIdentifier(collection.slug, \"collection slug\");\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[migration 039] skipping FTS rebuild for collection \"${collection.slug}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fields = await getSearchableFields(db, collection.slug);\n\t\tif (fields.length === 0) {\n\t\t\t// `search_config.enabled = true` but no searchable fields:\n\t\t\t// disable search and drop any orphan FTS objects. This\n\t\t\t// matches FTSManager's \"no fields -> disable\" behavior.\n\t\t\tawait dropFtsObjects(db, collection.slug);\n\t\t\tawait sql`\n\t\t\t\tUPDATE _emdash_collections\n\t\t\t\tSET search_config = json_set(search_config, '$.enabled', json('false'))\n\t\t\t\tWHERE slug = ${collection.slug}\n\t\t\t`.execute(db);\n\t\t\tcontinue;\n\t\t}\n\n\t\tawait rebuildIndex(db, collection.slug, fields);\n\t}\n}\n\n/**\n * Forward-only migration. Down is a no-op: we cannot meaningfully\n * \"restore the broken triggers\" and there is no migration-level state\n * to roll back. The FTS tables themselves are managed by `FTSManager`\n * at runtime, not by this migration, so leaving them in their\n * corruption-safe state on rollback is correct.\n */\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// no-op\n}\n\nfunction isSearchEnabled(searchConfig: string | null): boolean {\n\tif (!searchConfig) return false;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(searchConfig);\n\t\treturn (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"enabled\" in parsed &&\n\t\t\tparsed.enabled === true\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function getSearchableFields(db: Kysely<unknown>, collectionSlug: string): Promise<string[]> {\n\tconst rows = await sql<FieldRow>`\n\t\tSELECT f.slug FROM _emdash_fields f\n\t\tINNER JOIN _emdash_collections c ON c.id = f.collection_id\n\t\tWHERE c.slug = ${collectionSlug} AND f.searchable = 1\n\t`.execute(db);\n\n\tconst out: string[] = [];\n\tfor (const row of rows.rows) {\n\t\ttry {\n\t\t\tvalidateIdentifier(row.slug, \"searchable field name\");\n\t\t\tout.push(row.slug);\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t`[migration 039] skipping invalid searchable field \"${row.slug}\" on collection \"${collectionSlug}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn out;\n}\n\nasync function rebuildIndex(\n\tdb: Kysely<unknown>,\n\tcollectionSlug: string,\n\tfields: string[],\n): Promise<void> {\n\tconst ftsTable = `_emdash_fts_${collectionSlug}`;\n\tconst contentTable = `ec_${collectionSlug}`;\n\tconst columnList = [\"id UNINDEXED\", \"locale UNINDEXED\", ...fields].join(\", \");\n\tconst fieldList = fields.join(\", \");\n\tconst newFieldList = fields.map((f) => `NEW.${f}`).join(\", \");\n\tconst oldFieldList = fields.map((f) => `OLD.${f}`).join(\", \");\n\n\tawait dropFtsObjects(db, collectionSlug);\n\n\t// `IF NOT EXISTS` on every CREATE so concurrent migrators on D1\n\t// (no advisory lock, see runner.ts:264) converge instead of one\n\t// failing the other. Duplicate-rowid INSERTs into an external-\n\t// content FTS5 table dedupe via the docsize shadow table, so a\n\t// double populate ends with one row per content row. The brief\n\t// window between DROP and CREATE where another isolate could fire\n\t// a trigger against a missing FTS table is pre-existing -- it also\n\t// exists in FTSManager.rebuildIndex at runtime.\n\tawait sql\n\t\t.raw(`\n\t\tCREATE VIRTUAL TABLE IF NOT EXISTS \"${ftsTable}\" USING fts5(\n\t\t\t${columnList},\n\t\t\tcontent='${contentTable}',\n\t\t\tcontent_rowid='rowid',\n\t\t\ttokenize='porter unicode61'\n\t\t)\n\t`)\n\t\t.execute(db);\n\n\t// Insert trigger -- only index non-deleted content.\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_insert\"\n\t\tAFTER INSERT ON \"${contentTable}\"\n\t\tWHEN NEW.deleted_at IS NULL\n\t\tBEGIN\n\t\t\tINSERT INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\t\tVALUES (NEW.rowid, NEW.id, NEW.locale, ${newFieldList});\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\t// Update trigger -- corruption-safe external-content `'delete'` form,\n\t// gated on OLD.deleted_at IS NULL so we never issue `'delete'` for a\n\t// rowid that was never indexed.\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_update\"\n\t\tAFTER UPDATE ON \"${contentTable}\"\n\t\tBEGIN\n\t\t\tINSERT INTO \"${ftsTable}\"(\"${ftsTable}\", rowid, id, locale, ${fieldList})\n\t\t\tSELECT 'delete', OLD.rowid, OLD.id, OLD.locale, ${oldFieldList}\n\t\t\tWHERE OLD.deleted_at IS NULL;\n\t\t\tINSERT INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\t\tSELECT NEW.rowid, NEW.id, NEW.locale, ${newFieldList}\n\t\t\tWHERE NEW.deleted_at IS NULL;\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\t// Delete trigger -- same corruption-safe form, same gate.\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_delete\"\n\t\tAFTER DELETE ON \"${contentTable}\"\n\t\tBEGIN\n\t\t\tINSERT INTO \"${ftsTable}\"(\"${ftsTable}\", rowid, id, locale, ${fieldList})\n\t\t\tSELECT 'delete', OLD.rowid, OLD.id, OLD.locale, ${oldFieldList}\n\t\t\tWHERE OLD.deleted_at IS NULL;\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\t// Populate from existing content (non-deleted rows only). Concurrent\n\t// re-population is safe -- FTS5 INSERT into an external-content table\n\t// dedupes by rowid; a second pass over the same content rows leaves\n\t// the index with one entry per row, matching what we'd get from a\n\t// single populate.\n\tawait sql\n\t\t.raw(`\n\t\tINSERT INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\tSELECT rowid, id, locale, ${fieldList} FROM \"${contentTable}\"\n\t\tWHERE deleted_at IS NULL\n\t`)\n\t\t.execute(db);\n}\n\nasync function dropFtsObjects(db: Kysely<unknown>, collectionSlug: string): Promise<void> {\n\tconst ftsTable = `_emdash_fts_${collectionSlug}`;\n\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_insert\"`).execute(db);\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_update\"`).execute(db);\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_delete\"`).execute(db);\n\tawait sql.raw(`DROP TABLE IF EXISTS \"${ftsTable}\"`).execute(db);\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { getI18nConfig } from \"../../i18n/config.js\";\nimport { currentTimestamp, isSqlite, listTablesLike } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * i18n for bylines. Adds `locale` + `translation_group` to `_emdash_bylines`\n * and stores translation_groups (not row ids) in\n * `_emdash_content_bylines.byline_id` and `ec_*.primary_byline_id`. Backfill\n * locale and column DEFAULTs use the site's configured defaultLocale.\n *\n * Mirrors the row-per-locale + `translation_group` model PR #916 (migration\n * 036) applied to menus and taxonomies.\n *\n * Key consequences of the model:\n * - `(slug, locale)` is unique on `_emdash_bylines`; a single slug can repeat\n *   across locales (one row per locale variant of a byline).\n * - The partial unique on `user_id` widens to `(user_id, locale)` so a CMS\n *   user can have one byline per locale.\n * - `_emdash_content_bylines.byline_id` no longer FKs to `_emdash_bylines.id`\n *   (it holds a `translation_group`, not a row id). The runtime is\n *   responsible for cascading on byline delete — see `BylineRepository.delete`.\n *\n * Hydration is strict per locale (see `BylineRepository.getContentBylines`):\n * a credit at locale X renders iff a byline row exists at locale X within the\n * credited translation group. This mirrors `getEntryTerms` and the convention\n * established by #916. There is no read-time fallback.\n */\n\nfunction getDefaultLocale(): string {\n\treturn getI18nConfig()?.defaultLocale ?? \"en\";\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst defaultLocale = getDefaultLocale();\n\n\tif (isSqlite(db)) {\n\t\t// Rebuild children before parents to drop FKs that would CASCADE\n\t\t// on D1 (#1021). `_emdash_content_bylines.byline_id` has an FK to\n\t\t// `_emdash_bylines(id) ON DELETE CASCADE` from migration 031.\n\t\t// Stripping it first lets us rebuild `_emdash_bylines` without\n\t\t// risking a cascading wipe of credits on D1.\n\t\tawait rebuildContentBylines(db);\n\t\tawait rebuildBylines(db, defaultLocale);\n\t\tawait remapPrimaryBylineIds(db);\n\t\treturn;\n\t}\n\n\tawait pgWidenBylines(db, defaultLocale);\n\tawait pgDropContentBylinesFk(db);\n\tawait remapPrimaryBylineIds(db);\n}\n\nasync function rebuildContentBylines(db: Kysely<unknown>): Promise<void> {\n\t// Drops the FK so `byline_id` can point at a translation_group rather\n\t// than a row id. Runs before `rebuildBylines` so the drop is safe on D1.\n\t// No remap is needed here: `rebuildBylines` later seeds `translation_group\n\t// = id` for every preserved row, so the row-id values we copy resolve as\n\t// translation_group references after the migration completes. This coupling\n\t// is load-bearing — if the translation_group seed ever changes, this needs\n\t// an explicit remap *after* `rebuildBylines` runs.\n\tconst fks = await sql<{ id: number }>`PRAGMA foreign_key_list(_emdash_content_bylines)`.execute(\n\t\tdb,\n\t);\n\tif (fks.rows.length === 0) return;\n\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_content_bylines_new\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"_emdash_content_bylines_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"collection_slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"byline_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"sort_order\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"role_label\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"content_bylines_unique\", [\"collection_slug\", \"content_id\", \"byline_id\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_content_bylines_new\n\t\t\t(id, collection_slug, content_id, byline_id, sort_order, role_label, created_at)\n\t\tSELECT id, collection_slug, content_id, byline_id, sort_order, role_label, created_at\n\t\tFROM _emdash_content_bylines\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_content_bylines\").execute();\n\tawait sql`ALTER TABLE _emdash_content_bylines_new RENAME TO _emdash_content_bylines`.execute(db);\n\n\t// Indexes from migration 031 dropped with the table; restore.\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_content\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.columns([\"collection_slug\", \"content_id\", \"sort_order\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_byline\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.column(\"byline_id\")\n\t\t.execute();\n}\n\nasync function rebuildBylines(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tif (await hasColumn(db, \"_emdash_bylines\", \"locale\")) return;\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_bylines_new\"`).execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_bylines_new\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"display_name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"bio\", \"text\")\n\t\t.addColumn(\"avatar_media_id\", \"text\", (c) => c.references(\"media.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"website_url\", \"text\")\n\t\t.addColumn(\"user_id\", \"text\", (c) => c.references(\"users.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"is_guest\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.addUniqueConstraint(\"_emdash_bylines_slug_locale_unique\", [\"slug\", \"locale\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_bylines_new (\n\t\t\tid, slug, display_name, bio, avatar_media_id, website_url,\n\t\t\tuser_id, is_guest, created_at, updated_at, locale, translation_group\n\t\t)\n\t\tSELECT\n\t\t\tid, slug, display_name, bio, avatar_media_id, website_url,\n\t\t\tuser_id, is_guest, created_at, updated_at, ${defaultLocale}, id\n\t\tFROM _emdash_bylines\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_bylines\").execute();\n\tawait sql`ALTER TABLE _emdash_bylines_new RENAME TO _emdash_bylines`.execute(db);\n\n\t// Indexes from migration 031 dropped with the table; restore the\n\t// per-slug and per-display-name indexes. The partial unique on\n\t// user_id widens to (user_id, locale) so one CMS user can own one\n\t// byline per locale.\n\tawait db.schema.createIndex(\"idx_bylines_slug\").on(\"_emdash_bylines\").column(\"slug\").execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_bylines_display_name\")\n\t\t.on(\"_emdash_bylines\")\n\t\t.column(\"display_name\")\n\t\t.execute();\n\tawait sql`\n\t\tCREATE UNIQUE INDEX ${sql.ref(\"idx_bylines_user_id_locale_unique\")}\n\t\tON ${sql.ref(\"_emdash_bylines\")} (user_id, locale)\n\t\tWHERE user_id IS NOT NULL\n\t`.execute(db);\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_bylines_locale\")\n\t\t.on(\"_emdash_bylines\")\n\t\t.column(\"locale\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_bylines_translation_group\")\n\t\t.on(\"_emdash_bylines\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n\n\t// One row per (translation_group, locale): the row-per-locale model\n\t// (PR #916) requires that each locale variant of a byline appears\n\t// exactly once in its translation group. `UNIQUE(slug, locale)` doesn't\n\t// catch the case where two siblings in the same group use different\n\t// slugs at the same locale — this partial unique does. `WHERE\n\t// translation_group IS NOT NULL` is defensive: post-040 every row has a\n\t// value, but the column is nullable in the schema.\n\tawait sql`\n\t\tCREATE UNIQUE INDEX ${sql.ref(\"idx_bylines_group_locale_unique\")}\n\t\tON ${sql.ref(\"_emdash_bylines\")} (translation_group, locale)\n\t\tWHERE translation_group IS NOT NULL\n\t`.execute(db);\n}\n\nasync function remapPrimaryBylineIds(db: Kysely<unknown>): Promise<void> {\n\t// Walks every `ec_*` table and remaps `primary_byline_id` (row id →\n\t// translation_group). On a fresh install translation_group equals id\n\t// for every row, so the values look unchanged — but the column\n\t// semantics flip from \"FK to _emdash_bylines.id\" to \"translation_group\n\t// in _emdash_bylines.translation_group\". Once translations exist, the\n\t// remap is meaningful: a credit pointing at the en-row id still\n\t// resolves to the same byline group at every locale variant.\n\tconst collections = await listTablesLike(db, \"ec_%\");\n\tfor (const table of collections) {\n\t\tvalidateIdentifier(table, \"content table\");\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(table)} SET primary_byline_id = (\n\t\t\t\tSELECT translation_group FROM _emdash_bylines\n\t\t\t\tWHERE _emdash_bylines.id = ${sql.ref(table)}.primary_byline_id\n\t\t\t)\n\t\t\tWHERE primary_byline_id IS NOT NULL\n\t\t\t\tAND EXISTS (\n\t\t\t\t\tSELECT 1 FROM _emdash_bylines\n\t\t\t\t\tWHERE _emdash_bylines.id = ${sql.ref(table)}.primary_byline_id\n\t\t\t\t)\n\t\t`.execute(db);\n\t}\n}\n\nasync function pgWidenBylines(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tconst ref = sql.ref(\"_emdash_bylines\");\n\tawait sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS locale TEXT NOT NULL DEFAULT ${sql.lit(defaultLocale)}`.execute(\n\t\tdb,\n\t);\n\tawait sql`ALTER TABLE ${ref} ADD COLUMN IF NOT EXISTS translation_group TEXT`.execute(db);\n\tawait sql`UPDATE ${ref} SET translation_group = id WHERE translation_group IS NULL`.execute(db);\n\tawait sql`CREATE INDEX IF NOT EXISTS ${sql.ref(\"idx__emdash_bylines_locale\")} ON ${ref} (locale)`.execute(\n\t\tdb,\n\t);\n\tawait sql`\n\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(\"idx__emdash_bylines_translation_group\")}\n\t\tON ${ref} (translation_group)\n\t`.execute(db);\n\n\t// Widen UNIQUE(slug) -> UNIQUE(slug, locale)\n\tconst slugCons = await sql<{ conname: string }>`\n\t\tSELECT conname FROM pg_constraint c\n\t\tWHERE c.conrelid = '_emdash_bylines'::regclass AND c.contype = 'u'\n\t\t\tAND array_length(c.conkey, 1) = 1\n\t\t\tAND (\n\t\t\t\tSELECT array_agg(a.attname ORDER BY pos.ord)\n\t\t\t\tFROM unnest(c.conkey) WITH ORDINALITY AS pos(attnum, ord)\n\t\t\t\tJOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = pos.attnum\n\t\t\t)::text[] = ARRAY['slug']\n\t`.execute(db);\n\tfor (const c of slugCons.rows) {\n\t\tawait sql`ALTER TABLE ${ref} DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(db);\n\t}\n\tawait sql`\n\t\tALTER TABLE ${ref}\n\t\tADD CONSTRAINT _emdash_bylines_slug_locale_unique UNIQUE (slug, locale)\n\t`.execute(db);\n\n\t// Replace the partial unique on (user_id) with (user_id, locale).\n\tawait sql`DROP INDEX IF EXISTS idx_bylines_user_id_unique`.execute(db);\n\tawait sql`\n\t\tCREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref(\"idx_bylines_user_id_locale_unique\")}\n\t\tON ${ref} (user_id, locale) WHERE user_id IS NOT NULL\n\t`.execute(db);\n\n\t// One row per (translation_group, locale): see SQLite branch above.\n\tawait sql`\n\t\tCREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref(\"idx_bylines_group_locale_unique\")}\n\t\tON ${ref} (translation_group, locale) WHERE translation_group IS NOT NULL\n\t`.execute(db);\n}\n\nasync function pgDropContentBylinesFk(db: Kysely<unknown>): Promise<void> {\n\tconst fks = await sql<{ conname: string }>`\n\t\tSELECT conname FROM pg_constraint\n\t\tWHERE conrelid = '_emdash_content_bylines'::regclass AND contype = 'f'\n\t`.execute(db);\n\tfor (const c of fks.rows) {\n\t\tawait sql`ALTER TABLE _emdash_content_bylines DROP CONSTRAINT ${sql.ref(c.conname)}`.execute(\n\t\t\tdb,\n\t\t);\n\t}\n}\n\nasync function hasColumn(db: Kysely<unknown>, table: string, column: string): Promise<boolean> {\n\tconst rows = await sql<{ name: string }>`PRAGMA table_info(${sql.ref(table)})`.execute(db);\n\treturn rows.rows.some((r) => r.name === column);\n}\n\n/**\n * down() restores the FK on `_emdash_content_bylines.byline_id`. Rows whose\n * `byline_id` doesn't resolve to a (translation_group, defaultLocale) row in\n * `_emdash_bylines` would fail the rebuild after other tables are already\n * stripped — leaving the user mid-rollback. Surface dangling rows up front.\n */\nasync function assertContentBylinesResolve(\n\tdb: Kysely<unknown>,\n\tdefaultLocale: string,\n): Promise<void> {\n\tconst result = await sql<{ count: number | string }>`\n\t\tSELECT COUNT(*) AS count FROM _emdash_content_bylines cb\n\t\tWHERE NOT EXISTS (\n\t\t\tSELECT 1 FROM _emdash_bylines b\n\t\t\tWHERE b.translation_group = cb.byline_id AND b.locale = ${defaultLocale}\n\t\t)\n\t`.execute(db);\n\tconst count = Number(result.rows[0]?.count ?? 0);\n\tif (count > 0) {\n\t\tthrow new Error(\n\t\t\t`Cannot revert migration 040_byline_i18n: ` +\n\t\t\t\t`${count} row(s) in \"_emdash_content_bylines\" reference a translation_group ` +\n\t\t\t\t`with no row in \"_emdash_bylines\" at locale=\"${defaultLocale}\". ` +\n\t\t\t\t`Clean up the dangling credits before rolling back.`,\n\t\t);\n\t}\n}\n\n/**\n * down() is destructive on multi-locale installs (dropping `locale` collapses\n * translated rows onto an ambiguous unique key). Refuse to run when any row\n * sits at a locale other than the configured defaultLocale.\n */\nasync function assertSingleLocale(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tconst result = await sql<{ count: number | string }>`\n\t\tSELECT COUNT(*) AS count FROM _emdash_bylines WHERE locale != ${defaultLocale}\n\t`.execute(db);\n\tconst count = Number(result.rows[0]?.count ?? 0);\n\tif (count > 0) {\n\t\tthrow new Error(\n\t\t\t`Cannot revert migration 040_byline_i18n: ` +\n\t\t\t\t`${count} row(s) in \"_emdash_bylines\" use a non-default locale ` +\n\t\t\t\t`(defaultLocale=\"${defaultLocale}\"). ` +\n\t\t\t\t`Reverting would drop them silently. Export translations first ` +\n\t\t\t\t`(or delete them) and re-run the rollback. ` +\n\t\t\t\t`See packages/core/src/database/migrations/040_byline_i18n.ts.`,\n\t\t);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst defaultLocale = getDefaultLocale();\n\tawait assertSingleLocale(db, defaultLocale);\n\tawait assertContentBylinesResolve(db, defaultLocale);\n\n\tif (isSqlite(db)) {\n\t\t// Indexes first to avoid blocking the table rebuilds.\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx__emdash_bylines_locale`).execute(db);\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx__emdash_bylines_translation_group`).execute(db);\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_bylines_user_id_locale_unique`).execute(db);\n\t\tawait sql.raw(`DROP INDEX IF EXISTS idx_bylines_group_locale_unique`).execute(db);\n\n\t\t// Remap `_emdash_content_bylines.byline_id` and `ec_*.primary_byline_id`\n\t\t// from translation_group back to the row id of the defaultLocale\n\t\t// anchor. assertSingleLocale guarantees the mapping is 1:1, so the\n\t\t// rebuilt FK can validate every reference.\n\t\tawait remapPrimaryBylineIdsDown(db, defaultLocale);\n\t\tawait remapContentBylinesDown(db, defaultLocale);\n\n\t\t// Now safe to rebuild `_emdash_bylines` (strips locale +\n\t\t// translation_group, restores UNIQUE(slug) and the partial unique\n\t\t// on user_id alone).\n\t\tawait rebuildBylinesDown(db);\n\t\t// And finally restore the FK on `_emdash_content_bylines.byline_id`.\n\t\tawait restoreContentBylinesFk(db);\n\t\treturn;\n\t}\n\n\tawait remapPrimaryBylineIdsDown(db, defaultLocale);\n\tawait sql`\n\t\tUPDATE _emdash_content_bylines\n\t\tSET byline_id = COALESCE(\n\t\t\t(SELECT b.id FROM _emdash_bylines b\n\t\t\t WHERE b.translation_group = _emdash_content_bylines.byline_id\n\t\t\t\t AND b.locale = ${defaultLocale}),\n\t\t\tbyline_id\n\t\t)\n\t`.execute(db);\n\n\tawait sql.raw(`DROP INDEX IF EXISTS idx__emdash_bylines_locale`).execute(db);\n\tawait sql.raw(`DROP INDEX IF EXISTS idx__emdash_bylines_translation_group`).execute(db);\n\tawait sql.raw(`DROP INDEX IF EXISTS idx_bylines_user_id_locale_unique`).execute(db);\n\tawait sql.raw(`DROP INDEX IF EXISTS idx_bylines_group_locale_unique`).execute(db);\n\tawait sql\n\t\t.raw(\n\t\t\t`ALTER TABLE \"_emdash_bylines\" DROP CONSTRAINT IF EXISTS _emdash_bylines_slug_locale_unique`,\n\t\t)\n\t\t.execute(db);\n\tawait sql.raw(`ALTER TABLE \"_emdash_bylines\" DROP COLUMN IF EXISTS locale`).execute(db);\n\tawait sql\n\t\t.raw(`ALTER TABLE \"_emdash_bylines\" DROP COLUMN IF EXISTS translation_group`)\n\t\t.execute(db);\n\tawait sql\n\t\t.raw(`ALTER TABLE \"_emdash_bylines\" ADD CONSTRAINT _emdash_bylines_slug_unique UNIQUE (slug)`)\n\t\t.execute(db);\n\tawait sql`\n\t\tCREATE UNIQUE INDEX IF NOT EXISTS ${sql.ref(\"idx_bylines_user_id_unique\")}\n\t\tON _emdash_bylines (user_id) WHERE user_id IS NOT NULL\n\t`.execute(db);\n\tawait sql`\n\t\tALTER TABLE _emdash_content_bylines\n\t\tADD CONSTRAINT _emdash_content_bylines_byline_fk\n\t\tFOREIGN KEY (byline_id) REFERENCES _emdash_bylines(id) ON DELETE CASCADE\n\t`.execute(db);\n}\n\nasync function remapPrimaryBylineIdsDown(\n\tdb: Kysely<unknown>,\n\tdefaultLocale: string,\n): Promise<void> {\n\tconst collections = await listTablesLike(db, \"ec_%\");\n\tfor (const table of collections) {\n\t\tvalidateIdentifier(table, \"content table\");\n\t\tawait sql`\n\t\t\tUPDATE ${sql.ref(table)}\n\t\t\tSET primary_byline_id = COALESCE(\n\t\t\t\t(SELECT b.id FROM _emdash_bylines b\n\t\t\t\t WHERE b.translation_group = ${sql.ref(table)}.primary_byline_id\n\t\t\t\t\t AND b.locale = ${defaultLocale}),\n\t\t\t\tprimary_byline_id\n\t\t\t)\n\t\t\tWHERE primary_byline_id IS NOT NULL\n\t\t`.execute(db);\n\t}\n}\n\nasync function remapContentBylinesDown(db: Kysely<unknown>, defaultLocale: string): Promise<void> {\n\tawait sql`\n\t\tUPDATE _emdash_content_bylines\n\t\tSET byline_id = COALESCE(\n\t\t\t(SELECT b.id FROM _emdash_bylines b\n\t\t\t WHERE b.translation_group = _emdash_content_bylines.byline_id\n\t\t\t\t AND b.locale = ${defaultLocale}),\n\t\t\tbyline_id\n\t\t)\n\t`.execute(db);\n}\n\nasync function rebuildBylinesDown(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_bylines_old\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"_emdash_bylines_old\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (c) => c.notNull().unique())\n\t\t.addColumn(\"display_name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"bio\", \"text\")\n\t\t.addColumn(\"avatar_media_id\", \"text\", (c) => c.references(\"media.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"website_url\", \"text\")\n\t\t.addColumn(\"user_id\", \"text\", (c) => c.references(\"users.id\").onDelete(\"set null\"))\n\t\t.addColumn(\"is_guest\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\tawait sql`\n\t\tINSERT INTO _emdash_bylines_old (\n\t\t\tid, slug, display_name, bio, avatar_media_id, website_url,\n\t\t\tuser_id, is_guest, created_at, updated_at\n\t\t)\n\t\tSELECT\n\t\t\tid, slug, display_name, bio, avatar_media_id, website_url,\n\t\t\tuser_id, is_guest, created_at, updated_at\n\t\tFROM _emdash_bylines\n\t`.execute(db);\n\tawait db.schema.dropTable(\"_emdash_bylines\").execute();\n\tawait sql`ALTER TABLE _emdash_bylines_old RENAME TO _emdash_bylines`.execute(db);\n\n\t// Restore the indexes that existed pre-040.\n\tawait db.schema.createIndex(\"idx_bylines_slug\").on(\"_emdash_bylines\").column(\"slug\").execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_bylines_display_name\")\n\t\t.on(\"_emdash_bylines\")\n\t\t.column(\"display_name\")\n\t\t.execute();\n\tawait sql`\n\t\tCREATE UNIQUE INDEX ${sql.ref(\"idx_bylines_user_id_unique\")}\n\t\tON ${sql.ref(\"_emdash_bylines\")} (user_id)\n\t\tWHERE user_id IS NOT NULL\n\t`.execute(db);\n}\n\nasync function restoreContentBylinesFk(db: Kysely<unknown>): Promise<void> {\n\tawait sql.raw(`DROP TABLE IF EXISTS \"_emdash_content_bylines_old\"`).execute(db);\n\tawait db.schema\n\t\t.createTable(\"_emdash_content_bylines_old\")\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"collection_slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"byline_id\", \"text\", (c) =>\n\t\t\tc.notNull().references(\"_emdash_bylines.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"sort_order\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"role_label\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"content_bylines_unique\", [\"collection_slug\", \"content_id\", \"byline_id\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_content_bylines_old\n\t\t\t(id, collection_slug, content_id, byline_id, sort_order, role_label, created_at)\n\t\tSELECT id, collection_slug, content_id, byline_id, sort_order, role_label, created_at\n\t\tFROM _emdash_content_bylines\n\t`.execute(db);\n\n\tawait db.schema.dropTable(\"_emdash_content_bylines\").execute();\n\tawait sql`ALTER TABLE _emdash_content_bylines_old RENAME TO _emdash_content_bylines`.execute(db);\n\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_content\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.columns([\"collection_slug\", \"content_id\", \"sort_order\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_byline\")\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.column(\"byline_id\")\n\t\t.execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Migration: locale-aware composite indexes for content list queries.\n *\n * Addresses GitHub issue #1219. When i18n is enabled the admin content list\n * filters by `locale` and orders by `updated_at`/`created_at`. The existing\n * composite indexes (033/034) cover `(deleted_at, updated_at DESC, id DESC)`\n * etc. but omit `locale`, so a locale-filtered ordered list can't be served\n * by a single index on large tables. These indexes restore index-only paging\n * for the locale-scoped case.\n *\n * Forward-only and idempotent (`IF NOT EXISTS`).\n *\n * Index names use a short `loc_upd`/`loc_crt` suffix rather than spelling out\n * `deleted_locale_updated_id`: Postgres truncates identifiers to 63 bytes, and\n * the longer form pushes the `updated`/`created` discriminator past byte 63 for\n * slugs as short as 40 chars, making both names truncate to the same string.\n * Keep these identical to the names in `schema/registry.ts`.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_loc_upd`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, updated_at DESC, id DESC)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_loc_crt`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, locale, created_at DESC, id DESC)\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_loc_upd`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_loc_crt`)}`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Byline custom fields (Discussion #1174). Adds three tables and a\n * version-counter row in `options`. Purely additive: no change to\n * `_emdash_bylines` columns landed by migration 040, no change to\n * `_emdash_content_bylines`.\n *\n * Storage model (D11 in the design spec):\n *\n * - `_emdash_byline_fields` — definitions. One row per registered field.\n * - `_emdash_byline_field_values` — translatable values, keyed by\n *   `(byline_id, field_id)`. One value per locale variant of a byline.\n * - `_emdash_byline_field_group_values` — non-translatable values, keyed\n *   by `(translation_group, field_id)`. One value shared across every\n *   locale variant of the byline's translation group.\n *\n * The per-field `translatable` flag (column on `_emdash_byline_fields`)\n * decides which value table a field writes to. The split is at the\n * storage level rather than per-row so a single SELECT … IN per locale\n * bucket suffices for batched hydration (see Phase 3 of the PR plan).\n *\n * `options('byline_fields_version', '0')` is the version counter the\n * field-definitions cache reads (Phase 3). Every mutation on\n * `_emdash_byline_fields` bumps this row; cached defs invalidate on a\n * mismatch. Storing it in `options` (the same table `settings/index.ts`\n * uses) means we get the request-cache + persisted-version pattern for\n * free — no new infrastructure.\n *\n * Idempotency: every `CREATE TABLE` and `CREATE INDEX` uses\n * `.ifNotExists()`, so a partial prior run (a crash mid-migration, retried\n * after the runner's race-recovery path or after a manual fix) re-applies\n * cleanly — including any indexes that landed in the failed pass after the\n * table itself. A coarse table-level guard would skip the index step if the\n * table existed but indexes didn't (the realistic crash window between\n * `CREATE TABLE` and the next `CREATE INDEX`). The runner records applied\n * migrations only after a successful pass, so a crashed-then-retried `up()`\n * is the normal recovery path here.\n *\n * D1 has no transactions — the schema builder and `INSERT` for the\n * version row execute one statement at a time. Order matters: parent\n * tables first (`_emdash_byline_fields`), then the value tables that\n * reference it. If `down()` runs after a partial `up()`, missing tables\n * are tolerated (`DROP TABLE IF EXISTS`).\n */\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_byline_fields\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"label\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"type\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"required\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"translatable\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t.addColumn(\"validation\", \"text\") // JSON: { options?: string[] }\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_byline_fields_sort_order\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_byline_fields\")\n\t\t.column(\"sort_order\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_byline_field_values\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"byline_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_bylines.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"field_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_byline_fields.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"value\", \"text\") // JSON-encoded CustomFieldValue\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"_emdash_byline_field_values_pk\", [\"byline_id\", \"field_id\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_byline_field_values_byline\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_byline_field_values\")\n\t\t.column(\"byline_id\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_byline_field_values_field\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_byline_field_values\")\n\t\t.column(\"field_id\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_byline_field_group_values\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"translation_group\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"field_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_byline_fields.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"value\", \"text\") // JSON-encoded CustomFieldValue\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"_emdash_byline_field_group_values_pk\", [\n\t\t\t\"translation_group\",\n\t\t\t\"field_id\",\n\t\t])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_byline_field_group_values_group\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_byline_field_group_values\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_byline_field_group_values_field\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_byline_field_group_values\")\n\t\t.column(\"field_id\")\n\t\t.execute();\n\n\t// Version-counter row read by the field-definitions cache (Phase 3).\n\t// `options.value` stores JSON, so the initial counter is the JSON literal\n\t// `0`. `INSERT … ON CONFLICT DO NOTHING` so a retry after a crash that\n\t// landed the row but failed later in the migration does not double-insert.\n\tawait sql`\n\t\tINSERT INTO options (name, value) VALUES ('byline_fields_version', '0')\n\t\tON CONFLICT (name) DO NOTHING\n\t`.execute(db);\n}\n\n/**\n * Reverses the schema additions and removes the version-counter row.\n * Used by the test suite and by `rollbackMigration`; the runner itself\n * never invokes `down()` automatically.\n *\n * The drops are unconditional `IF EXISTS` so a `down()` after a partial\n * `up()` (only one or two tables landed) still settles the database back\n * to its pre-042 state.\n */\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\t// Child tables first to avoid FK reference issues on Postgres. SQLite +\n\t// D1 accept either order with `IF EXISTS`, but explicit ordering keeps\n\t// the dialects parity-safe.\n\tawait db.schema.dropTable(\"_emdash_byline_field_group_values\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_byline_field_values\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_byline_fields\").ifExists().execute();\n\tawait sql`DELETE FROM options WHERE name = 'byline_fields_version'`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\n\nimport { getI18nConfig } from \"../../i18n/config.js\";\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Content references.\n *\n * `_emdash_relations` defines relationship types (row-per-locale, mirroring\n * `_emdash_taxonomy_defs`): which collection is the parent, which is the child\n * (the side that may multiply), and localized labels for each role.\n *\n * `_emdash_content_references` holds directed `parent → child` edges between\n * content entries. Both endpoints and the relation are referenced by\n * `translation_group`, so edges are locale-agnostic. As with\n * `content_taxonomies`, group-linking precludes SQL foreign keys; referential\n * cleanup is an application-layer concern.\n *\n * Idempotency: every `CREATE TABLE` and `CREATE INDEX` uses `.ifNotExists()`,\n * so a partial prior run (a crash mid-migration, or a retry after the runner's\n * race-recovery path) re-applies cleanly — including any indexes that landed in\n * the failed pass after their table.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst defaultLocale = getI18nConfig()?.defaultLocale ?? \"en\";\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_relations\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"name\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_collection\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"child_collection\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"child_label\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"locale\", \"text\", (c) => c.notNull().defaultTo(defaultLocale))\n\t\t.addColumn(\"translation_group\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"_emdash_relations_name_locale_unique\", [\"name\", \"locale\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_relations_locale\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_relations\")\n\t\t.column(\"locale\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_relations_translation_group\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_relations\")\n\t\t.column(\"translation_group\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_relations_parent_collection\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_relations\")\n\t\t.column(\"parent_collection\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_relations_child_collection\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_relations\")\n\t\t.column(\"child_collection\")\n\t\t.execute();\n\n\t// One row per (translation_group, locale): the row-per-locale model wants a\n\t// relation to have at most one variant per locale. Migration 040 enforces\n\t// the same invariant for `_emdash_bylines` with a *partial* unique\n\t// (`WHERE translation_group IS NOT NULL`) only because it back-fills an\n\t// existing table; this table is new and `translation_group` is `NOT NULL`,\n\t// so a plain unique index suffices.\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_relations_group_locale_unique\")\n\t\t.ifNotExists()\n\t\t.unique()\n\t\t.on(\"_emdash_relations\")\n\t\t.columns([\"translation_group\", \"locale\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_content_references\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"relation_group\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"parent_group\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"child_group\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"sort_order\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"content_references_unique\", [\n\t\t\t\"relation_group\",\n\t\t\t\"parent_group\",\n\t\t\t\"child_group\",\n\t\t])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_content_references_parent\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_content_references\")\n\t\t.columns([\"parent_group\", \"relation_group\", \"sort_order\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_content_references_child\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_content_references\")\n\t\t.columns([\"child_group\", \"relation_group\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_content_references_relation\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_content_references\")\n\t\t.column(\"relation_group\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_content_references\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_relations\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Comment reactions (Tier 1 of the best-in-class comments RFC).\n *\n * One row per (comment, voter, reaction). Toggle semantics are enforced by the\n * unique index — a voter either has a given reaction on a comment or doesn't.\n * `voter_hash` is a salted SHA-256 of the visitor's IP (same primitive as\n * `_emdash_comments.ip_hash`), so no cleartext identity is stored.\n *\n * Additive and backward-compatible: a new table, no change to existing schema.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_comment_reactions\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"comment_id\", \"text\", (col) =>\n\t\t\tcol.notNull().references(\"_emdash_comments.id\").onDelete(\"cascade\"),\n\t\t)\n\t\t.addColumn(\"reaction\", \"text\", (col) => col.notNull().defaultTo(\"like\"))\n\t\t.addColumn(\"voter_hash\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\t// One reaction of a given type per voter per comment (toggle semantics).\n\tawait db.schema\n\t\t.createIndex(\"idx_comment_reactions_unique\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_comment_reactions\")\n\t\t.columns([\"comment_id\", \"voter_hash\", \"reaction\"])\n\t\t.unique()\n\t\t.execute();\n\n\t// Aggregate counts per comment (GROUP BY comment_id, reaction).\n\tawait db.schema\n\t\t.createIndex(\"idx_comment_reactions_comment\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_comment_reactions\")\n\t\t.column(\"comment_id\")\n\t\t.execute();\n\n\t// Per-voter rate limiting (recent reactions by a voter).\n\tawait db.schema\n\t\t.createIndex(\"idx_comment_reactions_voter\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_comment_reactions\")\n\t\t.columns([\"voter_hash\", \"created_at\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_comment_reactions\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Store the parent's `translation_group` in `taxonomies.parent_id` instead of a\n * locale-bound row id.\n *\n * Before this, a term's `parent_id` pointed at one specific parent *row* (one\n * locale). A child translated in another locale could end up parented to a row\n * in a different locale, silently flattening that locale's tree (#1347). Every\n * other taxonomy i18n relationship already keys off `translation_group`\n * (`content_taxonomies.taxonomy_id`, `_emdash_menu_items.reference_id`), so this\n * aligns parentage with that model: a child links to the parent *concept* and\n * stays nested in whichever locale that parent has been translated into.\n *\n * Backfill rewrites every existing `parent_id` from the referenced parent row's\n * id to that parent's `translation_group`. A `translation_group` normally equals\n * its anchor row's id, so the self-FK on `parent_id` stays valid. We only\n * rewrite when that anchor row still exists; if a parent's anchor was deleted\n * but a sibling translation survives, the existing locale-bound id is left as-is\n * rather than rewritten to a dangling FK value. Such a child then renders as a\n * root rather than nested — an accepted degradation for an already-inconsistent\n * dataset (its `translation_group` already points at a deleted row, which breaks\n * `content_taxonomies` joins too). New deletes can't recreate this state: a\n * parent with children in any locale is undeletable. The correlated subquery is\n * a no-op for rows that already hold a group (an anchor row's id resolves to\n * itself), so the migration is safe to re-run.\n *\n * Dialect-independent: the correlated-subquery `UPDATE` runs on SQLite (incl.\n * D1) and Postgres alike.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tUPDATE taxonomies\n\t\tSET parent_id = (\n\t\t\tSELECT p.translation_group FROM taxonomies p WHERE p.id = taxonomies.parent_id\n\t\t)\n\t\tWHERE parent_id IS NOT NULL\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT 1 FROM taxonomies p\n\t\t\t\tWHERE p.id = taxonomies.parent_id\n\t\t\t\t\tAND p.translation_group IS NOT NULL\n\t\t\t\t\t-- Only rewrite to a translation_group that is itself a live row\n\t\t\t\t\t-- id, so the self-FK on parent_id can never dangle.\n\t\t\t\t\tAND EXISTS (SELECT 1 FROM taxonomies a WHERE a.id = p.translation_group)\n\t\t\t)\n\t`.execute(db);\n}\n\n/**\n * Map each `parent_id` group back to the row id of the parent in the same\n * locale (the pre-migration shape). Rows with no same-locale parent are left as\n * they are rather than nulled, so a rollback never silently detaches a subtree.\n */\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait sql`\n\t\tUPDATE taxonomies\n\t\tSET parent_id = COALESCE(\n\t\t\t(\n\t\t\t\tSELECT c.id FROM taxonomies c\n\t\t\t\tWHERE c.translation_group = taxonomies.parent_id\n\t\t\t\t\tAND c.locale = taxonomies.locale\n\t\t\t),\n\t\t\tparent_id\n\t\t)\n\t\tWHERE parent_id IS NOT NULL\n\t`.execute(db);\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Internal media usage projection tables.\n *\n * This migration is DDL-only by design: no backfill, no runtime media/indexing\n * imports, and no content-table scans. Production rows are introduced by later\n * phases once the central snapshot/indexer path is reviewed.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_sources\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"source_key\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"source_type\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"collection_slug\", \"text\")\n\t\t.addColumn(\"content_id\", \"text\")\n\t\t.addColumn(\"source_variant\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"locale\", \"text\")\n\t\t.addColumn(\"translation_group\", \"text\")\n\t\t.addColumn(\"content_slug\", \"text\")\n\t\t.addColumn(\"content_title\", \"text\")\n\t\t.addColumn(\"content_status\", \"text\")\n\t\t.addColumn(\"content_scheduled_at\", \"text\")\n\t\t.addColumn(\"content_deleted_at\", \"text\")\n\t\t.addColumn(\"revision_id\", \"text\")\n\t\t.addColumn(\"current_generation\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"schema_version\", \"integer\", (c) => c.notNull().defaultTo(1))\n\t\t.addColumn(\"indexed_at\", \"text\", (c) => c.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_content\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"source_type\", \"collection_slug\", \"content_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_variant\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"source_type\", \"source_variant\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_locale\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"collection_slug\", \"locale\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_deleted\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.column(\"content_deleted_at\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_translation_group\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"collection_slug\", \"translation_group\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"source_key\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"generation\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"field_slug\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"field_path\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"occurrence_index\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"reference_type\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"media_id\", \"text\")\n\t\t.addColumn(\"provider\", \"text\", (c) => c.notNull().defaultTo(\"local\"))\n\t\t.addColumn(\"provider_asset_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"media_kind\", \"text\")\n\t\t.addColumn(\"mime_type\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_unique_occurrence\")\n\t\t.ifNotExists()\n\t\t.unique()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"source_key\", \"generation\", \"field_path\", \"occurrence_index\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_media_id\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.column(\"media_id\")\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_provider_asset\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"provider\", \"provider_asset_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_source_generation\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"source_key\", \"generation\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_media_usage\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_sources\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Restore `idx_taxonomies_parent` on `taxonomies(parent_id)` (#1665).\n *\n * Migration 015 created this index, but 036's SQLite table rebuild\n * (`taxonomies_new` → drop → rename) dropped every index attached to the old\n * table and only recreated `name`/`locale`/`translation_group`. The parent\n * index — which backs hierarchical (parent/child) lookups — was silently lost\n * on any install migrated through 036. The Postgres path in 036 alters the\n * table in place and keeps its indexes, so it is unaffected; `ifNotExists`\n * makes this a no-op there.\n *\n * Forward-only: 036 has already shipped, so existing installs only recover the\n * index here. Fresh installs also get it in 036's rebuild for parity with the\n * other rebuilt tables, making this a no-op for them.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_parent\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.column(\"parent_id\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_taxonomies_parent\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Restore `idx_content_taxonomies_term` on `content_taxonomies(taxonomy_id)` (#1701).\n *\n * Migration 015 created this index. 036's SQLite/D1 table rebuild recreated it,\n * but the recreate was gated behind an FK-presence early-return: if a first run\n * committed the rebuild (stripping the FK and dropping the old index) and then\n * failed before the recreate, the retry saw zero FKs, skipped the whole body,\n * and never restored the index — while 036 was still recorded as applied. The\n * result was an install missing only this one index, with no forward migration\n * to recover it (the sibling `idx_taxonomies_parent` case was fixed by 047).\n *\n * 036 has since been fixed to recreate the index unconditionally, so fresh\n * installs are fine. This recovers the index on installs that hit the trap\n * before that fix. The Postgres path in 036 alters in place and keeps its\n * indexes, so it is unaffected; `ifNotExists` makes this a no-op there.\n *\n * Forward-only.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(\"idx_content_taxonomies_term\")\n\t\t.ifNotExists()\n\t\t.on(\"content_taxonomies\")\n\t\t.column(\"taxonomy_id\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_content_taxonomies_term\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Add composite `idx_taxonomies_name_locale` on `taxonomies(name, locale)` (#1723).\n *\n * `TaxonomyRepository.findByName` filters `WHERE name = ? AND locale = ?`, but\n * the only indexes were the single-column `idx_taxonomies_name(name)` and\n * `idx_taxonomies_locale(locale)`. On SQLite/D1 with no `sqlite_stat1` the\n * planner picks `idx_taxonomies_locale` and reads *every* term in the locale,\n * filtering `name` in memory — once per facet rendered. On a site where one\n * taxonomy dominates a locale, each small facet still walks the whole locale.\n *\n * The composite resolves both equalities, so the planner searches only the one\n * taxonomy's terms. Its leftmost prefix (`name`) also serves every name-only\n * lookup, so it supersedes `idx_taxonomies_name`, which we drop. The\n * single-column `idx_taxonomies_locale` stays for locale-only lookups.\n *\n * Strictly additive: no query changes, and the dropped index is fully covered\n * by the new composite's prefix. Create-before-drop keeps name lookups indexed\n * at every point. Both statements are idempotent so a partial apply can retry.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_name_locale\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.columns([\"name\", \"locale\"])\n\t\t.execute();\n\n\tawait db.schema.dropIndex(\"idx_taxonomies_name\").ifExists().execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(\"idx_taxonomies_name\")\n\t\t.ifNotExists()\n\t\t.on(\"taxonomies\")\n\t\t.column(\"name\")\n\t\t.execute();\n\n\tawait db.schema.dropIndex(\"idx_taxonomies_name_locale\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists, currentTimestamp } from \"../dialect-helpers.js\";\n\nconst SOURCE_TABLE = \"_emdash_media_usage_sources\";\nconst DUPLICATE_COLUMN_RE = /(?:duplicate column|column .* already exists|already exists.*column)/i;\n\n/**\n * Media usage index metadata.\n *\n * DDL-only by design: adds freshness/completeness metadata and scope status\n * tracking for later reference-index reconciliation. No content scans, no\n * runtime imports, and no backfill.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait addSourceMetadataColumns(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_index_status\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"adapter_id\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"scope_type\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"scope_key\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"status\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"schema_version\", \"integer\", (c) => c.notNull().defaultTo(1))\n\t\t.addColumn(\"started_at\", \"text\")\n\t\t.addColumn(\"completed_at\", \"text\")\n\t\t.addColumn(\"cursor\", \"text\")\n\t\t.addColumn(\"indexed_source_count\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"failed_source_count\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addPrimaryKeyConstraint(\"_emdash_media_usage_index_status_pk\", [\n\t\t\t\"adapter_id\",\n\t\t\t\"scope_type\",\n\t\t\t\"scope_key\",\n\t\t])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_completeness\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"source_type\", \"collection_slug\", \"source_completeness\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_fingerprint\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.column(\"source_fingerprint\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_index_status_status\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_index_status\")\n\t\t.columns([\"adapter_id\", \"status\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_index_status_status\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_sources_fingerprint\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_sources_completeness\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_index_status\").ifExists().execute();\n\n\tawait dropSourceMetadataColumns(db);\n}\n\nasync function addSourceMetadataColumns(db: Kysely<unknown>): Promise<void> {\n\tawait addColumnIfMissing(db, \"source_updated_at\", () =>\n\t\tdb.schema.alterTable(SOURCE_TABLE).addColumn(\"source_updated_at\", \"text\").execute(),\n\t);\n\tawait addColumnIfMissing(db, \"source_version\", () =>\n\t\tdb.schema.alterTable(SOURCE_TABLE).addColumn(\"source_version\", \"integer\").execute(),\n\t);\n\tawait addColumnIfMissing(db, \"source_fingerprint\", () =>\n\t\tdb.schema.alterTable(SOURCE_TABLE).addColumn(\"source_fingerprint\", \"text\").execute(),\n\t);\n\tawait addColumnIfMissing(db, \"source_completeness\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(SOURCE_TABLE)\n\t\t\t.addColumn(\"source_completeness\", \"text\", (c) => c.notNull().defaultTo(\"unknown\"))\n\t\t\t.execute(),\n\t);\n\tawait addColumnIfMissing(db, \"last_attempted_at\", () =>\n\t\tdb.schema.alterTable(SOURCE_TABLE).addColumn(\"last_attempted_at\", \"text\").execute(),\n\t);\n\tawait addColumnIfMissing(db, \"last_error_code\", () =>\n\t\tdb.schema.alterTable(SOURCE_TABLE).addColumn(\"last_error_code\", \"text\").execute(),\n\t);\n}\n\nasync function addColumnIfMissing(\n\tdb: Kysely<unknown>,\n\tcolumnName: string,\n\taddColumn: () => Promise<void>,\n): Promise<void> {\n\tif (await columnExists(db, SOURCE_TABLE, columnName)) return;\n\n\ttry {\n\t\tawait addColumn();\n\t} catch (error) {\n\t\tif (DUPLICATE_COLUMN_RE.test(deepErrorMessage(error))) {\n\t\t\tif (await columnExists(db, SOURCE_TABLE, columnName)) return;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nfunction deepErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst own = error.message ?? \"\";\n\t\tif (error.cause) {\n\t\t\tconst causeMessage = deepErrorMessage(error.cause);\n\t\t\treturn own ? `${own}: ${causeMessage}` : causeMessage;\n\t\t}\n\t\treturn own;\n\t}\n\tif (typeof error === \"string\") return error;\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\nasync function dropSourceMetadataColumns(db: Kysely<unknown>): Promise<void> {\n\tfor (const columnName of [\n\t\t\"last_error_code\",\n\t\t\"last_attempted_at\",\n\t\t\"source_completeness\",\n\t\t\"source_fingerprint\",\n\t\t\"source_version\",\n\t\t\"source_updated_at\",\n\t] as const) {\n\t\tif (await columnExists(db, SOURCE_TABLE, columnName)) {\n\t\t\tawait db.schema.alterTable(SOURCE_TABLE).dropColumn(columnName).execute();\n\t\t}\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { columnExists, listTablesLike } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * Denormalize the filter + sort columns from `ec_*` onto `content_taxonomies`\n * and mirror `ec_*`'s composite sort indexes onto the pivot (#1834).\n *\n * A taxonomy-filtered listing used to apply the term filter as a correlated\n * `EXISTS` on `SELECT * FROM ec_<collection> … ORDER BY … LIMIT ?`. The pivot\n * carried only `(collection, entry_id, taxonomy_id)`, so the filter\n * (`deleted_at`, `status`, `locale`) and the sort (`published_at`/`created_at`)\n * could only be evaluated on the `ec_*` row. On stats-blind SQLite/D1 a\n * selective term never fills the `LIMIT`, so the query walked the whole\n * collection (~75k D1 rows read for a one-row page).\n *\n * Copying those columns onto the pivot and indexing them keyed by\n * `(taxonomy_id, collection, …)` lets the loader seek the term and walk a\n * sort-ordered pivot index, short-circuiting on `LIMIT`, then touch `ec_*` only\n * by primary key to hydrate the page.\n *\n * The columns are advisory (D1 has no transactions; the write-path re-stamp is\n * non-atomic), so the read path re-checks the real predicates on the joined\n * `ec_*` row — see loader.ts. `updated_at` is deliberately not denormalized: it\n * moves on every edit and is seldom a public sort, so its write cost outweighs\n * its read value; `updated_at`-sorted taxonomy listings temp-sort instead.\n *\n * Order matters: add the columns, backfill, THEN build the indexes so each\n * index is built once over populated data rather than maintained through the\n * backfill `UPDATE`.\n *\n * Forward-only.\n */\n\nconst DENORM_COLUMNS = [\n\t\"status\",\n\t\"scheduled_at\",\n\t\"deleted_at\",\n\t\"locale\",\n\t\"published_at\",\n\t\"created_at\",\n] as const;\n\n// `entry_id DESC` (not ASC) matters: the listing orders `<sort> DESC, entry_id\n// DESC` (the common case), and a DESC tiebreaker lets SQLite satisfy the whole\n// ORDER BY from the index — no temp B-tree, clean early-`LIMIT`. An ASC\n// tiebreaker forces `USE TEMP B-TREE FOR LAST TERM OF ORDER BY`, which buffers\n// a whole equal-`sortval` block (e.g. a bulk import sharing one timestamp)\n// before emitting. `DESC` also serves the rarer ASC listing via a backward\n// index scan. Mirrors `ec_*`'s `(deleted_at, [locale,] <sort> DESC, id DESC)`.\nconst INDEXES: { name: string; columns: string }[] = [\n\t{\n\t\tname: \"idx_content_taxonomies_pub\",\n\t\tcolumns: \"taxonomy_id, collection, deleted_at, published_at DESC, entry_id DESC\",\n\t},\n\t{\n\t\tname: \"idx_content_taxonomies_crt\",\n\t\tcolumns: \"taxonomy_id, collection, deleted_at, created_at DESC, entry_id DESC\",\n\t},\n\t{\n\t\tname: \"idx_content_taxonomies_loc_pub\",\n\t\tcolumns: \"taxonomy_id, collection, deleted_at, locale, published_at DESC, entry_id DESC\",\n\t},\n\t{\n\t\tname: \"idx_content_taxonomies_loc_crt\",\n\t\tcolumns: \"taxonomy_id, collection, deleted_at, locale, created_at DESC, entry_id DESC\",\n\t},\n];\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// 1. Add the six denormalized columns (all nullable, no default). Guarded so\n\t//    a partial apply — or a re-run after a mid-migration failure — can retry\n\t//    (`ALTER TABLE ADD COLUMN` is not itself idempotent on either dialect).\n\tfor (const column of DENORM_COLUMNS) {\n\t\tif (await columnExists(db, \"content_taxonomies\", column)) continue;\n\t\tawait sql`\n\t\t\tALTER TABLE content_taxonomies ADD COLUMN ${sql.ref(column)} TEXT\n\t\t`.execute(db);\n\t}\n\n\t// 2. Backfill from each content table by primary key. Drive from the actual\n\t//    `ec_*` tables (not `_emdash_collections`) so collections whose table was\n\t//    dropped are skipped naturally; the pivot's `collection` value is the\n\t//    slug, i.e. the table name without its `ec_` prefix.\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tfor (const tableName of tableNames) {\n\t\tvalidateIdentifier(tableName, \"content table name\");\n\t\tconst slug = tableName.slice(\"ec_\".length);\n\t\tawait sql`\n\t\t\tUPDATE content_taxonomies\n\t\t\tSET (status, scheduled_at, deleted_at, locale, published_at, created_at) = (\n\t\t\t\tSELECT status, scheduled_at, deleted_at, locale, published_at, created_at\n\t\t\t\tFROM ${sql.ref(tableName)}\n\t\t\t\tWHERE ${sql.ref(tableName)}.id = content_taxonomies.entry_id\n\t\t\t)\n\t\t\tWHERE collection = ${slug}\n\t\t`.execute(db);\n\t}\n\n\t// 3. Build the composite sort indexes over the now-populated columns.\n\tfor (const index of INDEXES) {\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(index.name)}\n\t\t\tON content_taxonomies (${sql.raw(index.columns)})\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tfor (const index of INDEXES) {\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(index.name)}`.execute(db);\n\t}\n\tfor (const column of DENORM_COLUMNS) {\n\t\tawait sql`\n\t\t\tALTER TABLE content_taxonomies DROP COLUMN ${sql.ref(column)}\n\t\t`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\n\nconst OLD_INDEX = \"idx__emdash_media_usage_media_id\";\nconst READ_INDEX = \"idx__emdash_media_usage_media_source_generation\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// D1 DDL is non-transactional: create the replacement before dropping the\n\t// old index so an interrupted migration always leaves a media-leading index.\n\tawait db.schema\n\t\t.createIndex(READ_INDEX)\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"media_id\", \"source_key\", \"generation\"])\n\t\t.execute();\n\tawait db.schema.dropIndex(OLD_INDEX).ifExists().execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(OLD_INDEX)\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.column(\"media_id\")\n\t\t.execute();\n\tawait db.schema.dropIndex(READ_INDEX).ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/** Persist explicit per-plugin MCP enablement and the declaration consented to by an admin. */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_plugin_state\", \"mcp_tools_enabled\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_plugin_state\")\n\t\t\t.addColumn(\"mcp_tools_enabled\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t\t.execute();\n\t}\n\tif (!(await columnExists(db, \"_plugin_state\", \"mcp_tools_consent\"))) {\n\t\tawait db.schema.alterTable(\"_plugin_state\").addColumn(\"mcp_tools_consent\", \"text\").execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.alterTable(\"_plugin_state\").dropColumn(\"mcp_tools_consent\").execute();\n\tawait db.schema.alterTable(\"_plugin_state\").dropColumn(\"mcp_tools_enabled\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_upload_attempts\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"storage_key\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"media_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"status\", \"text\", (col) => col.notNull().defaultTo(\"active\"))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.notNull().defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_media_upload_attempts_media_id\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_upload_attempts\")\n\t\t.column(\"media_id\")\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_media_upload_attempts_status_created_at\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_upload_attempts\")\n\t\t.columns([\"status\", \"created_at\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_media_storage_key\")\n\t\t.ifNotExists()\n\t\t.on(\"media\")\n\t\t.column(\"storage_key\")\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropIndex(\"idx_media_storage_key\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_upload_attempts\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\n\n/**\n * Migration: replace the single-column content `translation_group` index with\n * one index per translation-group read shape.\n *\n * There are two shapes. Translation-group reads filter `deleted_at IS NULL`\n * with `translation_group = ?` / `IN (...)` and order by locale; menu and\n * reference resolution looks a group up by `translation_group` (+ `locale`)\n * alone, with no `deleted_at` term.\n *\n * Neither shape can borrow the other's index. Seeking migration 041's\n * `(deleted_at, locale, ...)` composites on `deleted_at` alone already returns\n * rows in locale order, so a stats-blind planner prefers them over a\n * single-column `translation_group` index and reads every non-deleted row in\n * the table; the batched variant is worse still, because the `IN (...)` list\n * multiplies the planner's row estimate for a `translation_group`-leading index\n * and it falls back to a `deleted_at` composite from a handful of groups\n * onward. Going the other way, an index leading with `deleted_at` cannot seek a\n * lookup that never constrains that column. D1 never has `sqlite_stat1`, so the\n * index shape is the only lever and each shape needs a prefix matching it term\n * for term.\n *\n * Forward-only and idempotent (`IF NOT EXISTS`).\n *\n * Index names use short `tg_locale` / `del_tg_locale` suffixes rather than\n * spelling out `translation_group`: Postgres truncates identifiers to 63 bytes,\n * and the longer forms truncate to the same value for long collection slugs.\n * Keep these identical to the names in `schema/registry.ts`.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\t// D1 DDL is non-transactional: create the replacements before dropping the\n\t\t// old index so an interrupted migration always leaves a\n\t\t// translation_group-leading index in place.\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (translation_group, locale)\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_del_tg_locale`)}\n\t\t\tON ${sql.ref(tableName)} (deleted_at, translation_group, locale)\n\t\t`.execute(db);\n\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_translation_group`)}`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\n\tfor (const tableName of tableNames) {\n\t\tawait sql`\n\t\t\tCREATE INDEX IF NOT EXISTS ${sql.ref(`idx_${tableName}_translation_group`)}\n\t\t\tON ${sql.ref(tableName)} (translation_group)\n\t\t`.execute(db);\n\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_del_tg_locale`)}`.execute(db);\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(`idx_${tableName}_tg_locale`)}`.execute(db);\n\t}\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Add `taxonomies.sort_order` and mint a position for every existing term.\n *\n * A position belongs to a `translation_group`, not a row: every row of a group\n * carries the same value. Groups are numbered by `(label, id)` — the order term\n * listings used before this migration — taking the label from the group's lowest\n * row id. That locale's rendered order is unchanged; every other locale is\n * re-sorted to match it, since only one locale's collation can win.\n *\n * Sibling groups are keyed on the raw `parent_id` column, which holds the\n * parent's translation_group, so a group whose rows disagree about their parent\n * is reconciled before numbering — otherwise it lands in whichever sibling group\n * one arbitrary row happens to name.\n *\n * Only the `ALTER` is guarded; the passes below run unconditionally so a run that\n * dies partway can be retried. Guarding the whole migration strands a partial\n * numbering, because the retry early-returns on the column it added.\n */\n\n/**\n * Translation groups per `UPDATE`. Each costs three bound parameters — a CASE\n * `WHEN`/`THEN` pair plus one slot in the `IN` list — which keeps a statement\n * inside D1's 100-parameter ceiling. Writing one group per statement instead\n * spends a subrequest per term, and a large taxonomy exhausts the 1,000 a\n * Worker gets on the free plan before the migration finishes.\n */\nconst GROUPS_PER_UPDATE = 32;\n\ninterface TermRow {\n\tid: string;\n\tname: string;\n\tlabel: string | null;\n\tparent_id: string | null;\n\ttranslation_group: string;\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst fresh = !(await columnExists(db, \"taxonomies\", \"sort_order\"));\n\tif (fresh) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"taxonomies\")\n\t\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t\t.execute();\n\t}\n\n\t// Migration 036 seeded `translation_group = id` on every row it rebuilt, and\n\t// the repository has set it on every insert since, so this normally matches\n\t// nothing. It runs anyway because everything below keys sibling groups on the\n\t// column directly: a row that slipped through with a null would be numbered\n\t// into no group at all, and would already be invisible to translation lookups\n\t// and to `content_taxonomies`, which joins on the same value.\n\tawait sql`UPDATE taxonomies SET translation_group = id WHERE translation_group IS NULL`.execute(\n\t\tdb,\n\t);\n\n\tconst { rows } = await sql<TermRow>`\n\t\tSELECT id, name, label, parent_id, translation_group FROM taxonomies\n\t`.execute(db);\n\n\tawait repairParents(db, rows);\n\tawait mintPositions(db, rows, fresh);\n}\n\n/**\n * Give every row of a translation_group the same `parent_id`, mutating `rows` so\n * the numbering below sees the reconciled tree.\n *\n * `parent_id` holds the parent's translation_group, so it is locale-agnostic and\n * a group's rows should all agree. Reparenting used to write a single row, which\n * left a term nested in the locale it was moved in and a root everywhere else.\n * A non-null parent wins: it is the move somebody made, and the null is the row\n * that missed it.\n *\n * The winner is copied from a sibling row, so it already satisfies the self-FK\n * on `parent_id` and can't be rewritten into a dangling reference.\n */\nasync function repairParents(db: Kysely<unknown>, rows: TermRow[]): Promise<void> {\n\tconst byGroup = new Map<string, TermRow[]>();\n\tfor (const row of rows) {\n\t\tconst group = row.translation_group;\n\t\tlet members = byGroup.get(group);\n\t\tif (!members) byGroup.set(group, (members = []));\n\t\tmembers.push(row);\n\t}\n\n\tconst winners = new Map<string, string>();\n\tfor (const [group, members] of byGroup) {\n\t\tif (new Set(members.map((member) => member.parent_id)).size < 2) continue;\n\n\t\tconst parented = members.filter((member) => member.parent_id !== null);\n\t\t// The anchor row decides when it names a parent, else the lowest row id\n\t\t// that does, so the choice is the same however the rows come back.\n\t\tconst anchor = parented.find((member) => member.id === group);\n\t\tconst decider = anchor ?? parented.toSorted((a, b) => a.id.localeCompare(b.id))[0];\n\t\tconst winner = decider?.parent_id;\n\t\tif (winner === undefined || winner === null) continue;\n\n\t\twinners.set(group, winner);\n\t\tfor (const member of members) member.parent_id = winner;\n\t}\n\n\tawait applyByGroup(db, \"parent_id\", winners);\n}\n\n/**\n * Number each sibling group by `(label, id)`.\n *\n * When the column was just added every row already reads 0, so those positions\n * are left to the default. On a retry the column is already there and may hold\n * numbers from the run that failed, so every position is written explicitly.\n */\nasync function mintPositions(\n\tdb: Kysely<unknown>,\n\trows: readonly TermRow[],\n\tfresh: boolean,\n): Promise<void> {\n\t// One entry per translation_group: a group holds a single position, and its\n\t// rows can disagree on label across locales, so the lowest row id decides for\n\t// the whole group. Taxonomy names are `[a-z][a-z0-9_]*` and parents are\n\t// ULIDs, so \"/\" can't appear in either half of the sibling key.\n\tconst groups = new Map<string, { sibling: string; label: string; id: string }>();\n\tfor (const row of rows) {\n\t\tconst group = row.translation_group;\n\t\tconst chosen = groups.get(group);\n\t\tif (chosen && chosen.id <= row.id) continue;\n\t\tgroups.set(group, {\n\t\t\tsibling: row.name + \"/\" + (row.parent_id ?? \"\"),\n\t\t\tlabel: row.label ?? \"\",\n\t\t\tid: row.id,\n\t\t});\n\t}\n\n\tconst siblings = new Map<string, { group: string; label: string; id: string }[]>();\n\tfor (const [group, { sibling, label, id }] of groups) {\n\t\tlet members = siblings.get(sibling);\n\t\tif (!members) siblings.set(sibling, (members = []));\n\t\tmembers.push({ group, label, id });\n\t}\n\n\tconst positions = new Map<string, number>();\n\tfor (const members of siblings.values()) {\n\t\tmembers.sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));\n\t\tfor (const [position, member] of members.entries()) {\n\t\t\tif (position === 0 && fresh) continue;\n\t\t\tpositions.set(member.group, position);\n\t\t}\n\t}\n\n\tawait applyByGroup(db, \"sort_order\", positions);\n}\n\n/**\n * Set one column per translation_group, chunked to stay inside the parameter\n * ceiling.\n */\nasync function applyByGroup(\n\tdb: Kysely<unknown>,\n\tcolumn: \"parent_id\" | \"sort_order\",\n\tvalues: ReadonlyMap<string, string | number>,\n): Promise<void> {\n\tconst entries = [...values];\n\t// The CAST types the bound value. Postgres resolves a CASE whose THEN arms are\n\t// all untyped parameters to text, then refuses to assign text to an integer\n\t// column.\n\tconst valueType = column === \"sort_order\" ? sql`INTEGER` : sql`TEXT`;\n\tfor (let index = 0; index < entries.length; index += GROUPS_PER_UPDATE) {\n\t\tconst chunk = entries.slice(index, index + GROUPS_PER_UPDATE);\n\t\tconst arms = sql.join(\n\t\t\tchunk.map(([group, value]) => sql`WHEN ${group} THEN CAST(${value} AS ${valueType})`),\n\t\t\tsql` `,\n\t\t);\n\t\tconst keys = sql.join(chunk.map(([group]) => sql`${group}`));\n\t\tawait sql`\n\t\t\tUPDATE taxonomies\n\t\t\tSET ${sql.ref(column)} = CASE translation_group ${arms} END\n\t\t\tWHERE translation_group IN (${keys})\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await columnExists(db, \"taxonomies\", \"sort_order\")) {\n\t\tawait db.schema.alterTable(\"taxonomies\").dropColumn(\"sort_order\").execute();\n\t}\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Migration: hide a collection from the admin sidebar.\n *\n * Adds `hidden` to `_emdash_collections`. A hidden collection stays fully\n * functional (REST API, MCP, plugin hooks, direct `/content/:collection`\n * URLs) — only its auto-generated sidebar entry is omitted, so plugins that\n * own a collection end to end can steer editors to their own admin UI.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"hidden\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_collections\")\n\t\t\t.addColumn(\"hidden\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t\t.execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.alterTable(\"_emdash_collections\").dropColumn(\"hidden\").execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Migration: explicit collection order in the admin sidebar.\n *\n * Adds `sort_order` to `_emdash_collections`. A NULL `sort_order` means no\n * explicit position: those collections sort after the ordered ones, keeping\n * the alphabetical-by-slug order.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"sort_order\"))) {\n\t\tawait db.schema.alterTable(\"_emdash_collections\").addColumn(\"sort_order\", \"integer\").execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.alterTable(\"_emdash_collections\").dropColumn(\"sort_order\").execute();\n}\n","import { sql, type Kysely } from \"kysely\";\n\nconst REVISION_KEEP_COUNT = 50;\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_revision_prune_queue\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"collection\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"entry_id\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"revision_id\", \"text\", (column) => column.notNull())\n\t\t.addPrimaryKeyConstraint(\"revision_prune_queue_pk\", [\"collection\", \"entry_id\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_revision_prune_queue_revision_id\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_revision_prune_queue\")\n\t\t.column(\"revision_id\")\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_revision_prune_queue (collection, entry_id, revision_id)\n\t\tSELECT collection, entry_id, MAX(id)\n\t\tFROM revisions\n\t\tWHERE true\n\t\tGROUP BY collection, entry_id\n\t\tHAVING COUNT(*) > ${REVISION_KEEP_COUNT}\n\t\tON CONFLICT (collection, entry_id)\n\t\tDO UPDATE SET revision_id = excluded.revision_id\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_revision_prune_queue\").ifExists().execute();\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/** Persist collection-level admin presentation options. */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"admin_config\"))) {\n\t\tawait db.schema.alterTable(\"_emdash_collections\").addColumn(\"admin_config\", \"text\").execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await columnExists(db, \"_emdash_collections\", \"admin_config\")) {\n\t\tawait db.schema.alterTable(\"_emdash_collections\").dropColumn(\"admin_config\").execute();\n\t}\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport { currentTimestamp } from \"../dialect-helpers.js\";\n\nconst CLEANUP_TASK_KEY = \"projection_gc\";\nconst INITIAL_ELIGIBLE_AT = \"1970-01-01T00:00:00.000Z\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_cleanup_scan\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"created_at\", \"id\", \"source_key\", \"generation\"])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_cleanup\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"task_key\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"lease_token\", \"text\")\n\t\t.addColumn(\"lease_expires_at\", \"text\")\n\t\t.addColumn(\"next_eligible_at\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"cursor_created_at\", \"text\")\n\t\t.addColumn(\"cursor_id\", \"text\")\n\t\t.addColumn(\"scan_before_at\", \"text\")\n\t\t.addColumn(\"consecutive_failures\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_started_at\", \"text\")\n\t\t.addColumn(\"last_completed_at\", \"text\")\n\t\t.addColumn(\"last_candidate_count\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_deleted_orphans\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_deleted_stale\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_deleted_abandoned\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_deleted_write_leases\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_backlog_lower_bound\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_scan_has_more\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_duration_ms\", \"integer\", (c) => c.notNull().defaultTo(0))\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.notNull().defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_generation_writes\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"source_key\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"generation\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"lease_token\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"expires_at\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"created_at\", \"text\", (c) => c.notNull().defaultTo(currentTimestamp(db)))\n\t\t.addUniqueConstraint(\"_emdash_media_usage_generation_writes_source_generation\", [\n\t\t\t\"source_key\",\n\t\t\t\"generation\",\n\t\t])\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_generation_writes_expiry\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_generation_writes\")\n\t\t.columns([\"expires_at\", \"lease_token\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_media_usage_cleanup (task_key, next_eligible_at)\n\t\tVALUES (${CLEANUP_TASK_KEY}, ${INITIAL_ELIGIBLE_AT})\n\t\tON CONFLICT (task_key) DO NOTHING\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.dropIndex(\"idx__emdash_media_usage_generation_writes_expiry\")\n\t\t.ifExists()\n\t\t.execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_generation_writes\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_cleanup\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_cleanup_scan\").ifExists().execute();\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport { columnExists, currentTimestamp, isPostgres } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_media_usage\", \"cleanup_lease_token\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_media_usage\")\n\t\t\t.addColumn(\"cleanup_lease_token\", \"text\")\n\t\t\t.execute();\n\t}\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_cleanup_fence\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"task_key\", \"text\", (c) => c.primaryKey())\n\t\t.addColumn(\"generation_floor\", \"text\", (c) => c.notNull())\n\t\t.addColumn(\"updated_at\", \"text\", (c) => c.notNull().defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tif (isPostgres(db)) {\n\t\tawait sql\n\t\t\t.raw(\"DROP TRIGGER IF EXISTS emdash_media_usage_record_cleanup_fence ON _emdash_media_usage\")\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_update ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_insert ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_update ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_insert ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_delete ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE OR REPLACE FUNCTION emdash_media_usage_lock_cleanup()\n\t\t\tRETURNS trigger\n\t\t\tLANGUAGE plpgsql\n\t\t\tAS $$\n\t\t\tBEGIN\n\t\t\t\tPERFORM 1\n\t\t\t\tFROM _emdash_media_usage_cleanup AS cleanup\n\t\t\t\tWHERE cleanup.task_key = 'projection_gc'\n\t\t\t\tFOR SHARE;\n\t\t\t\tRETURN NULL;\n\t\t\tEND;\n\t\t\t$$\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE OR REPLACE FUNCTION emdash_media_usage_fence_source_generation()\n\t\t\tRETURNS trigger\n\t\t\tLANGUAGE plpgsql\n\t\t\tAS $$\n\t\t\tBEGIN\n\t\t\t\tIF EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM _emdash_media_usage_cleanup_fence AS fence\n\t\t\t\t\tWHERE fence.task_key = 'projection_gc'\n\t\t\t\t\t\tAND NEW.current_generation <= fence.generation_floor\n\t\t\t\t) AND NOT EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM _emdash_media_usage_generation_writes AS writer\n\t\t\t\t\tWHERE writer.source_key = NEW.source_key\n\t\t\t\t\t\tAND writer.generation = NEW.current_generation\n\t\t\t\t\t\tAND writer.expires_at::timestamptz > clock_timestamp()\n\t\t\t\t) THEN\n\t\t\t\t\tRETURN NULL;\n\t\t\t\tEND IF;\n\t\t\t\tRETURN NEW;\n\t\t\tEND;\n\t\t\t$$\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_lock_cleanup_insert\n\t\t\tBEFORE INSERT ON _emdash_media_usage_sources\n\t\t\tFOR EACH STATEMENT\n\t\t\tEXECUTE FUNCTION emdash_media_usage_lock_cleanup()\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_lock_cleanup_update\n\t\t\tBEFORE UPDATE OF current_generation ON _emdash_media_usage_sources\n\t\t\tFOR EACH STATEMENT\n\t\t\tEXECUTE FUNCTION emdash_media_usage_lock_cleanup()\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_lock_cleanup_delete\n\t\t\tBEFORE DELETE ON _emdash_media_usage_sources\n\t\t\tFOR EACH STATEMENT\n\t\t\tEXECUTE FUNCTION emdash_media_usage_lock_cleanup()\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE OR REPLACE FUNCTION emdash_media_usage_record_cleanup_fence()\n\t\t\tRETURNS trigger\n\t\t\tLANGUAGE plpgsql\n\t\t\tAS $$\n\t\t\tBEGIN\n\t\t\t\tIF OLD.cleanup_lease_token IS NOT NULL AND EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM _emdash_media_usage_cleanup AS cleanup\n\t\t\t\t\tWHERE cleanup.task_key = 'projection_gc'\n\t\t\t\t\t\tAND cleanup.lease_token = OLD.cleanup_lease_token\n\t\t\t\t) THEN\n\t\t\t\t\tINSERT INTO _emdash_media_usage_cleanup_fence (\n\t\t\t\t\t\ttask_key,\n\t\t\t\t\t\tgeneration_floor,\n\t\t\t\t\t\tupdated_at\n\t\t\t\t\t)\n\t\t\t\t\tVALUES ('projection_gc', OLD.generation, CURRENT_TIMESTAMP::text)\n\t\t\t\t\tON CONFLICT (task_key) DO UPDATE SET\n\t\t\t\t\t\tgeneration_floor = CASE\n\t\t\t\t\t\t\tWHEN EXCLUDED.generation_floor > _emdash_media_usage_cleanup_fence.generation_floor\n\t\t\t\t\t\t\tTHEN EXCLUDED.generation_floor\n\t\t\t\t\t\t\tELSE _emdash_media_usage_cleanup_fence.generation_floor\n\t\t\t\t\t\tEND,\n\t\t\t\t\t\tupdated_at = EXCLUDED.updated_at;\n\t\t\t\tEND IF;\n\t\t\t\tRETURN OLD;\n\t\t\tEND;\n\t\t\t$$\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_fence_source_generation_insert\n\t\t\tBEFORE INSERT ON _emdash_media_usage_sources\n\t\t\tFOR EACH ROW\n\t\t\tEXECUTE FUNCTION emdash_media_usage_fence_source_generation()\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_fence_source_generation_update\n\t\t\tBEFORE UPDATE OF current_generation ON _emdash_media_usage_sources\n\t\t\tFOR EACH ROW\n\t\t\tEXECUTE FUNCTION emdash_media_usage_fence_source_generation()\n\t\t`)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(`\n\t\t\tCREATE TRIGGER emdash_media_usage_record_cleanup_fence\n\t\t\tBEFORE DELETE ON _emdash_media_usage\n\t\t\tFOR EACH ROW\n\t\t\tWHEN (OLD.cleanup_lease_token IS NOT NULL)\n\t\t\tEXECUTE FUNCTION emdash_media_usage_record_cleanup_fence()\n\t\t`)\n\t\t\t.execute(db);\n\t\treturn;\n\t}\n\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS emdash_media_usage_fence_source_generation_insert\n\t\tBEFORE INSERT ON _emdash_media_usage_sources\n\t\tWHEN EXISTS (\n\t\t\tSELECT 1\n\t\t\tFROM _emdash_media_usage_cleanup_fence AS fence\n\t\t\tWHERE fence.task_key = 'projection_gc'\n\t\t\t\tAND NEW.current_generation <= fence.generation_floor\n\t\t) AND NOT EXISTS (\n\t\t\tSELECT 1\n\t\t\tFROM _emdash_media_usage_generation_writes AS writer\n\t\t\tWHERE writer.source_key = NEW.source_key\n\t\t\t\tAND writer.generation = NEW.current_generation\n\t\t\t\tAND writer.expires_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n\t\t)\n\t\tBEGIN\n\t\t\tSELECT RAISE(IGNORE);\n\t\tEND\n\t`)\n\t\t.execute(db);\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS emdash_media_usage_fence_source_generation_update\n\t\tBEFORE UPDATE OF current_generation ON _emdash_media_usage_sources\n\t\tWHEN NEW.current_generation <> OLD.current_generation\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM _emdash_media_usage_cleanup_fence AS fence\n\t\t\t\tWHERE fence.task_key = 'projection_gc'\n\t\t\t\t\tAND NEW.current_generation <= fence.generation_floor\n\t\t\t) AND NOT EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM _emdash_media_usage_generation_writes AS writer\n\t\t\t\tWHERE writer.source_key = NEW.source_key\n\t\t\t\t\tAND writer.generation = NEW.current_generation\n\t\t\t\t\tAND writer.expires_at > strftime('%Y-%m-%dT%H:%M:%fZ', 'now')\n\t\t\t)\n\t\tBEGIN\n\t\t\tSELECT RAISE(IGNORE);\n\t\tEND\n\t`)\n\t\t.execute(db);\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS emdash_media_usage_record_cleanup_fence\n\t\tBEFORE DELETE ON _emdash_media_usage\n\t\tWHEN OLD.cleanup_lease_token IS NOT NULL AND EXISTS (\n\t\t\tSELECT 1\n\t\t\tFROM _emdash_media_usage_cleanup AS cleanup\n\t\t\tWHERE cleanup.task_key = 'projection_gc'\n\t\t\t\tAND cleanup.lease_token = OLD.cleanup_lease_token\n\t\t)\n\t\tBEGIN\n\t\t\tINSERT INTO _emdash_media_usage_cleanup_fence (\n\t\t\t\ttask_key,\n\t\t\t\tgeneration_floor,\n\t\t\t\tupdated_at\n\t\t\t)\n\t\t\tVALUES ('projection_gc', OLD.generation, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))\n\t\t\tON CONFLICT (task_key) DO UPDATE SET\n\t\t\t\tgeneration_floor = CASE\n\t\t\t\t\tWHEN excluded.generation_floor > _emdash_media_usage_cleanup_fence.generation_floor\n\t\t\t\t\tTHEN excluded.generation_floor\n\t\t\t\t\tELSE _emdash_media_usage_cleanup_fence.generation_floor\n\t\t\t\tEND,\n\t\t\t\tupdated_at = excluded.updated_at;\n\t\tEND\n\t`)\n\t\t.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (isPostgres(db)) {\n\t\tawait sql\n\t\t\t.raw(\"DROP TRIGGER IF EXISTS emdash_media_usage_record_cleanup_fence ON _emdash_media_usage\")\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_update ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_insert ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_update ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_insert ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\n\t\t\t\t\"DROP TRIGGER IF EXISTS emdash_media_usage_lock_cleanup_delete ON _emdash_media_usage_sources\",\n\t\t\t)\n\t\t\t.execute(db);\n\t\tawait sql.raw(\"DROP FUNCTION IF EXISTS emdash_media_usage_record_cleanup_fence()\").execute(db);\n\t\tawait sql\n\t\t\t.raw(\"DROP FUNCTION IF EXISTS emdash_media_usage_fence_source_generation()\")\n\t\t\t.execute(db);\n\t\tawait sql.raw(\"DROP FUNCTION IF EXISTS emdash_media_usage_lock_cleanup()\").execute(db);\n\t} else {\n\t\tawait sql.raw(\"DROP TRIGGER IF EXISTS emdash_media_usage_record_cleanup_fence\").execute(db);\n\t\tawait sql\n\t\t\t.raw(\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_update\")\n\t\t\t.execute(db);\n\t\tawait sql\n\t\t\t.raw(\"DROP TRIGGER IF EXISTS emdash_media_usage_fence_source_generation_insert\")\n\t\t\t.execute(db);\n\t}\n\tawait db.schema.dropTable(\"_emdash_media_usage_cleanup_fence\").ifExists().execute();\n\tif (await columnExists(db, \"_emdash_media_usage\", \"cleanup_lease_token\")) {\n\t\tawait db.schema.alterTable(\"_emdash_media_usage\").dropColumn(\"cleanup_lease_token\").execute();\n\t}\n}\n","import { sql, type Kysely, type RawBuilder } from \"kysely\";\n\nimport { columnExists, isPostgres, tableExists } from \"../dialect-helpers.js\";\n\nconst ACTIVATION_KEY = \"incremental_capture\";\nconst CONTENT_ADAPTER_ID = \"content-media\";\nconst COLLECTION_SCOPE = \"collection\";\nconst DUPLICATE_COLUMN_RE = /(?:duplicate column|column .* already exists|already exists.*column)/i;\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_activation\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"task_key\", \"text\", (column) => column.primaryKey())\n\t\t.addColumn(\"state\", \"text\", (column) => column.notNull().defaultTo(\"expanded\"))\n\t\t.addColumn(\"runtime_generation\", \"integer\", (column) => column.notNull().defaultTo(1))\n\t\t.addColumn(\"collection_cursor\", \"text\")\n\t\t.addColumn(\"drain_confirmed_at\", \"text\")\n\t\t.addColumn(\"lease_token\", \"text\")\n\t\t.addColumn(\"lease_expires_at\", \"text\")\n\t\t.addColumn(\"attempt_count\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t.addColumn(\"last_attempted_at\", \"text\")\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"activated_at\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.addColumn(\"updated_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.execute();\n\n\tawait sql`\n\t\tINSERT INTO _emdash_media_usage_activation (task_key, state)\n\t\tVALUES (${ACTIVATION_KEY}, 'expanded')\n\t\tON CONFLICT (task_key) DO NOTHING\n\t`.execute(db);\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_work\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"collection_id\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"collection_slug\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"content_id\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"change_epoch\", \"bigint\", (column) => column.notNull())\n\t\t.addColumn(\"work_version\", \"bigint\", (column) => column.notNull().defaultTo(1))\n\t\t.addColumn(\"state\", \"text\", (column) => column.notNull().defaultTo(\"pending\"))\n\t\t.addColumn(\"attempt_count\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t.addColumn(\"next_attempt_at\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"lease_token\", \"text\")\n\t\t.addColumn(\"lease_expires_at\", \"text\")\n\t\t.addColumn(\"last_attempted_at\", \"text\")\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.addColumn(\"updated_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.addPrimaryKeyConstraint(\"_emdash_media_usage_work_pk\", [\"collection_id\", \"content_id\"])\n\t\t.execute();\n\n\tawait addStatusColumns(db);\n\tawait addSourceColumns(db);\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_work_due\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_work\")\n\t\t.columns([\"state\", \"next_attempt_at\", \"updated_at\", \"collection_id\", \"content_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_work_lease\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_work\")\n\t\t.columns([\"state\", \"lease_expires_at\", \"collection_id\", \"content_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_work_operator\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_work\")\n\t\t.columns([\"collection_id\", \"state\", \"updated_at\", \"content_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_status_collection\")\n\t\t.ifNotExists()\n\t\t.unique()\n\t\t.on(\"_emdash_media_usage_index_status\")\n\t\t.columns([\"adapter_id\", \"scope_type\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_identity\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"source_type\", \"collection_id\", \"content_id\", \"source_variant\"])\n\t\t.execute();\n\n\tawait sql`\n\t\tDELETE FROM _emdash_media_usage_index_status\n\t\tWHERE adapter_id = ${CONTENT_ADAPTER_ID}\n\t\t\tAND scope_type = ${COLLECTION_SCOPE}\n\t\t\tAND (\n\t\t\t\t(\n\t\t\t\t\tcollection_id IS NULL\n\t\t\t\t\tAND NOT EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\t\t\tWHERE collection.slug = _emdash_media_usage_index_status.scope_key\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t\tOR (\n\t\t\t\t\tcollection_id IS NOT NULL\n\t\t\t\t\tAND capture_state = 'installing'\n\t\t\t\t\tAND EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM _emdash_media_usage_activation AS activation\n\t\t\t\t\t\tWHERE activation.task_key = ${ACTIVATION_KEY}\n\t\t\t\t\t\t\tAND activation.state = 'expanded'\n\t\t\t\t\t)\n\t\t\t\t\tAND NOT EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\t\t\tWHERE collection.id = _emdash_media_usage_index_status.collection_id\n\t\t\t\t\t\t\tAND collection.slug = _emdash_media_usage_index_status.scope_key\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t`.execute(db);\n\n\tawait sql`\n\t\tUPDATE _emdash_media_usage_index_status\n\t\tSET collection_id = (\n\t\t\t\tSELECT collection.id\n\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.slug = _emdash_media_usage_index_status.scope_key\n\t\t\t),\n\t\t\treconciliation_required = 1,\n\t\t\tcapture_state = COALESCE(capture_state, 'installing')\n\t\tWHERE adapter_id = ${CONTENT_ADAPTER_ID}\n\t\t\tAND scope_type = ${COLLECTION_SCOPE}\n\t\t\tAND (\n\t\t\t\tcollection_id IS NULL\n\t\t\t\tOR capture_state IS NULL\n\t\t\t)\n\t\t\tAND EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM _emdash_collections AS collection\n\t\t\t\tWHERE collection.id = COALESCE(\n\t\t\t\t\t_emdash_media_usage_index_status.collection_id,\n\t\t\t\t\tcollection.id\n\t\t\t\t)\n\t\t\t\t\tAND collection.slug = _emdash_media_usage_index_status.scope_key\n\t\t\t)\n\t`.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait assertRollbackIsEmptyAndInactive(db);\n\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_sources_identity\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_status_collection\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_work_operator\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_work_lease\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_work_due\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_work\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_activation\").ifExists().execute();\n\tif (isPostgres(db)) {\n\t\tawait sql`DROP FUNCTION IF EXISTS emdash_media_usage_capture_work()`.execute(db);\n\t}\n\n\tfor (const columnName of [\"identity_version\", \"collection_id\"] as const) {\n\t\tif (await columnExists(db, \"_emdash_media_usage_sources\", columnName)) {\n\t\t\tawait db.schema.alterTable(\"_emdash_media_usage_sources\").dropColumn(columnName).execute();\n\t\t}\n\t}\n\n\tfor (const columnName of [\n\t\t\"capture_state\",\n\t\t\"last_incremental_success_at\",\n\t\t\"reconciliation_required\",\n\t\t\"change_epoch\",\n\t\t\"collection_id\",\n\t] as const) {\n\t\tif (await columnExists(db, \"_emdash_media_usage_index_status\", columnName)) {\n\t\t\tawait db.schema\n\t\t\t\t.alterTable(\"_emdash_media_usage_index_status\")\n\t\t\t\t.dropColumn(columnName)\n\t\t\t\t.execute();\n\t\t}\n\t}\n}\n\nasync function addStatusColumns(db: Kysely<unknown>): Promise<void> {\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_index_status\", \"collection_id\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_index_status\")\n\t\t\t.addColumn(\"collection_id\", \"text\")\n\t\t\t.execute(),\n\t);\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_index_status\", \"change_epoch\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_index_status\")\n\t\t\t.addColumn(\"change_epoch\", \"bigint\", (column) => column.notNull().defaultTo(0))\n\t\t\t.execute(),\n\t);\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_index_status\", \"reconciliation_required\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_index_status\")\n\t\t\t.addColumn(\"reconciliation_required\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t\t.execute(),\n\t);\n\tawait addStatusTextColumn(db, \"last_incremental_success_at\");\n\tawait addStatusTextColumn(db, \"capture_state\");\n}\n\nasync function assertRollbackIsEmptyAndInactive(db: Kysely<unknown>): Promise<void> {\n\tif (await hasCaptureTriggers(db)) {\n\t\tthrow new Error(\"Cannot roll back media usage capture while capture triggers are installed\");\n\t}\n\n\tif (await tableExists(db, \"_emdash_media_usage_activation\")) {\n\t\tconst activation = await sql<{ state: string }>`\n\t\t\tSELECT state\n\t\t\tFROM _emdash_media_usage_activation\n\t\t\tWHERE task_key = ${ACTIVATION_KEY}\n\t\t`.execute(db);\n\t\tif (activation.rows[0] && activation.rows[0].state !== \"expanded\") {\n\t\t\tthrow new Error(\"Cannot roll back media usage capture after activation has started\");\n\t\t}\n\t}\n\n\tif (await tableExists(db, \"_emdash_media_usage_work\")) {\n\t\tconst work = await sql<{ present: number }>`\n\t\t\tSELECT 1 AS present FROM _emdash_media_usage_work LIMIT 1\n\t\t`.execute(db);\n\t\tif (work.rows.length > 0) {\n\t\t\tthrow new Error(\"Cannot roll back media usage capture while durable work exists\");\n\t\t}\n\t}\n\n\tconst sourceHasCollectionId = await columnExists(\n\t\tdb,\n\t\t\"_emdash_media_usage_sources\",\n\t\t\"collection_id\",\n\t);\n\tconst sourceHasIdentityVersion = await columnExists(\n\t\tdb,\n\t\t\"_emdash_media_usage_sources\",\n\t\t\"identity_version\",\n\t);\n\tif (sourceHasCollectionId || sourceHasIdentityVersion) {\n\t\tconst canonicalSources = sourceHasCollectionId\n\t\t\t? await sql<{ present: number }>`\n\t\t\t\t\tSELECT 1 AS present\n\t\t\t\t\tFROM _emdash_media_usage_sources\n\t\t\t\t\tWHERE collection_id IS NOT NULL\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(db)\n\t\t\t: await sql<{ present: number }>`\n\t\t\t\t\tSELECT 1 AS present\n\t\t\t\t\tFROM _emdash_media_usage_sources\n\t\t\t\t\tWHERE identity_version IS NOT NULL\n\t\t\t\t\tLIMIT 1\n\t\t\t\t`.execute(db);\n\t\tif (canonicalSources.rows.length > 0) {\n\t\t\tthrow new Error(\"Cannot roll back media usage capture after canonical sources exist\");\n\t\t}\n\t\tif (sourceHasCollectionId && sourceHasIdentityVersion) {\n\t\t\tconst versionedSources = await sql<{ present: number }>`\n\t\t\t\tSELECT 1 AS present\n\t\t\t\tFROM _emdash_media_usage_sources\n\t\t\t\tWHERE identity_version IS NOT NULL\n\t\t\t\tLIMIT 1\n\t\t\t`.execute(db);\n\t\t\tif (versionedSources.rows.length > 0) {\n\t\t\t\tthrow new Error(\"Cannot roll back media usage capture after canonical sources exist\");\n\t\t\t}\n\t\t}\n\t}\n\n\tif (await columnExists(db, \"_emdash_media_usage_index_status\", \"capture_state\")) {\n\t\tconst lifecycle = await sql<{ present: number }>`\n\t\t\tSELECT 1 AS present FROM _emdash_media_usage_index_status\n\t\t\tWHERE capture_state IN ('active', 'deleting') LIMIT 1\n\t\t`.execute(db);\n\t\tif (lifecycle.rows.length > 0) throwRollbackLifecycleError();\n\t}\n\tif (await columnExists(db, \"_emdash_media_usage_index_status\", \"change_epoch\")) {\n\t\tconst epoch = await sql<{ present: number }>`\n\t\t\tSELECT 1 AS present FROM _emdash_media_usage_index_status\n\t\t\tWHERE change_epoch <> 0 LIMIT 1\n\t\t`.execute(db);\n\t\tif (epoch.rows.length > 0) throwRollbackLifecycleError();\n\t}\n\tif (await columnExists(db, \"_emdash_media_usage_index_status\", \"last_incremental_success_at\")) {\n\t\tconst success = await sql<{ present: number }>`\n\t\t\tSELECT 1 AS present FROM _emdash_media_usage_index_status\n\t\t\tWHERE last_incremental_success_at IS NOT NULL LIMIT 1\n\t\t`.execute(db);\n\t\tif (success.rows.length > 0) throwRollbackLifecycleError();\n\t}\n}\n\nfunction throwRollbackLifecycleError(): never {\n\tthrow new Error(\"Cannot roll back media usage capture while capture lifecycle state exists\");\n}\n\nasync function hasCaptureTriggers(db: Kysely<unknown>): Promise<boolean> {\n\tif (isPostgres(db)) {\n\t\tconst result = await sql<{ present: boolean }>`\n\t\t\tSELECT EXISTS (\n\t\t\t\tSELECT 1\n\t\t\t\tFROM pg_trigger AS trigger\n\t\t\t\tINNER JOIN pg_class AS relation ON relation.oid = trigger.tgrelid\n\t\t\t\tINNER JOIN pg_namespace AS namespace ON namespace.oid = relation.relnamespace\n\t\t\t\tWHERE namespace.nspname = current_schema()\n\t\t\t\t\tAND NOT trigger.tgisinternal\n\t\t\t\t\tAND left(trigger.tgname, 10) = 'emdash_mu_'\n\t\t\t) AS present\n\t\t`.execute(db);\n\t\treturn result.rows[0]?.present === true;\n\t}\n\n\tconst result = await sql<{ present: number }>`\n\t\tSELECT 1 AS present\n\t\tFROM sqlite_master\n\t\tWHERE type = 'trigger' AND substr(name, 1, 10) = 'emdash_mu_'\n\t\tLIMIT 1\n\t`.execute(db);\n\treturn result.rows.length > 0;\n}\n\nasync function addStatusTextColumn(db: Kysely<unknown>, columnName: string): Promise<void> {\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_index_status\", columnName, () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_index_status\")\n\t\t\t.addColumn(columnName, \"text\")\n\t\t\t.execute(),\n\t);\n}\n\nasync function addSourceColumns(db: Kysely<unknown>): Promise<void> {\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_sources\", \"collection_id\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_sources\")\n\t\t\t.addColumn(\"collection_id\", \"text\")\n\t\t\t.execute(),\n\t);\n\tawait addColumnIfMissing(db, \"_emdash_media_usage_sources\", \"identity_version\", () =>\n\t\tdb.schema\n\t\t\t.alterTable(\"_emdash_media_usage_sources\")\n\t\t\t.addColumn(\"identity_version\", \"integer\")\n\t\t\t.execute(),\n\t);\n}\n\nasync function addColumnIfMissing(\n\tdb: Kysely<unknown>,\n\ttableName: string,\n\tcolumnName: string,\n\taddColumn: () => Promise<void>,\n): Promise<void> {\n\tif (await columnExists(db, tableName, columnName)) return;\n\n\ttry {\n\t\tawait addColumn();\n\t} catch (error) {\n\t\tif (DUPLICATE_COLUMN_RE.test(deepErrorMessage(error))) {\n\t\t\tif (await columnExists(db, tableName, columnName)) return;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nfunction sortableUtcTimestamp(db: Kysely<unknown>): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql`to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"')`;\n\t}\n\treturn sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`;\n}\n\nfunction deepErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst own = error.message ?? \"\";\n\t\tif (error.cause) {\n\t\t\tconst causeMessage = deepErrorMessage(error.cause);\n\t\t\treturn own ? `${own}: ${causeMessage}` : causeMessage;\n\t\t}\n\t\treturn own;\n\t}\n\tif (typeof error === \"string\") return error;\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { isSqlite } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\n/**\n * Migration: Rebuild FTS5 indexes as self-contained tables indexing\n * extracted Portable Text prose\n *\n * Background: FTS tables were external-content (`content='ec_<slug>'`),\n * which forces the index to mirror the raw column values — and Portable\n * Text fields store JSON, so the index was polluted with structural tokens\n * (`_type`, `span`, style values like `normal`, `_key` ULIDs). Searches for\n * those tokens matched nearly every document and snippets showed JSON\n * fragments.\n *\n * The fix rebuilds each search-enabled collection's FTS table as a\n * self-contained FTS5 table (no `content=` option) whose Portable Text\n * columns hold extracted prose — every JSON string under a `text`, `alt`,\n * `caption`, or `code` key — with sync triggers computing the same\n * extraction in SQL. The update trigger carries a WHEN guard so only real\n * value changes re-tokenize; shipping the guard inside this rebuild spares\n * existing sites a second full re-tokenization from a follow-up migration.\n * Self-contained tables also retire the external-content\n * `'delete'` choreography and its corruption modes (see migration 039).\n * The rebuilt table keeps the collection's configured `tokenize` from\n * search_config rather than resetting it to the default.\n *\n * The SQL emitted here MUST stay in lock-step with\n * `FTSManager.createTriggers` / `createFtsTable` / `populateFromContent` in\n * `src/search/fts-manager.ts`. If those change again, add a new migration\n * rather than editing this one — migrations are forward-only.\n *\n * Postgres: no-op. FTS5 is SQLite-only.\n *\n * D1: idempotent at the granularity we care about (drop-then-create +\n * repopulate with `INSERT OR REPLACE`, so concurrent migrators converge).\n * A partial apply that drops the FTS table without recreating it is healed\n * by the next `verifyAndRepairIndex` call at runtime.\n */\n\ninterface CollectionRow {\n\tslug: string;\n\tsearch_config: string | null;\n}\n\ninterface FieldRow {\n\tslug: string;\n\ttype: string;\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!isSqlite(db)) return;\n\n\tconst collections = await sql<CollectionRow>`\n\t\tSELECT slug, search_config FROM _emdash_collections\n\t\tWHERE search_config IS NOT NULL\n\t`.execute(db);\n\n\tfor (const collection of collections.rows) {\n\t\tif (!isSearchEnabled(collection.search_config)) continue;\n\n\t\t// Defensive re-validation before raw SQL interpolation, mirroring 039.\n\t\ttry {\n\t\t\tvalidateIdentifier(collection.slug, \"collection slug\");\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t`[migration 064] skipping FTS rebuild for collection \"${collection.slug}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst fields = await getSearchableFields(db, collection.slug);\n\t\tif (fields.length === 0) continue;\n\n\t\tawait rebuildIndex(db, collection.slug, fields, searchTokenizer(collection.search_config));\n\t}\n}\n\n/**\n * Forward-only. Down is a no-op: the FTS tables are managed by FTSManager\n * at runtime and the self-contained shape remains fully functional for\n * older code paths that only MATCH and join on id.\n */\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// no-op\n}\n\nfunction isSearchEnabled(searchConfig: string | null): boolean {\n\tif (!searchConfig) return false;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(searchConfig);\n\t\treturn (\n\t\t\ttypeof parsed === \"object\" &&\n\t\t\tparsed !== null &&\n\t\t\t\"enabled\" in parsed &&\n\t\t\tparsed.enabled === true\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Tokenizers this migration knows how to carry across the rebuild — a frozen\n * copy of SEARCH_TOKENIZERS as of this migration, not an import: a replay\n * after the live allowlist changes must not reinterpret configs this\n * migration never shipped with.\n */\nconst KNOWN_TOKENIZERS = [\"porter unicode61\", \"unicode61\", \"trigram\"];\n\n/**\n * Tokenizer for the rebuilt table, from the collection's search_config.\n * Values outside the allowlist (or unparsable config) fall back to the\n * default rather than reaching the raw CREATE VIRTUAL TABLE statement.\n */\nfunction searchTokenizer(searchConfig: string | null): string {\n\tif (!searchConfig) return \"porter unicode61\";\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(searchConfig);\n\t\tif (typeof parsed === \"object\" && parsed !== null && \"tokenize\" in parsed) {\n\t\t\tconst configured = KNOWN_TOKENIZERS.find((tokenizer) => tokenizer === parsed.tokenize);\n\t\t\tif (configured !== undefined) return configured;\n\t\t}\n\t} catch {\n\t\treturn \"porter unicode61\";\n\t}\n\treturn \"porter unicode61\";\n}\n\nasync function getSearchableFields(\n\tdb: Kysely<unknown>,\n\tcollectionSlug: string,\n): Promise<FieldRow[]> {\n\tconst rows = await sql<FieldRow>`\n\t\tSELECT f.slug, f.type FROM _emdash_fields f\n\t\tINNER JOIN _emdash_collections c ON c.id = f.collection_id\n\t\tWHERE c.slug = ${collectionSlug} AND f.searchable = 1\n\t`.execute(db);\n\n\tconst out: FieldRow[] = [];\n\tfor (const row of rows.rows) {\n\t\ttry {\n\t\t\tvalidateIdentifier(row.slug, \"searchable field name\");\n\t\t\tout.push(row);\n\t\t} catch {\n\t\t\tconsole.warn(\n\t\t\t\t`[migration 064] skipping invalid searchable field \"${row.slug}\" on collection \"${collectionSlug}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Indexed-value expression for one field; lock-step with FTSManager.searchValueExpr. */\nfunction searchValueExpr(ref: string, fieldType: string): string {\n\tif (fieldType !== \"portableText\") return ref;\n\treturn (\n\t\t`CASE WHEN ${ref} IS NULL THEN NULL ` +\n\t\t`WHEN json_valid(${ref}) AND json_type(${ref}) IN ('array', 'object') THEN (` +\n\t\t`SELECT group_concat(j.value, ' ') FROM json_tree(${ref}) AS j ` +\n\t\t`WHERE j.key IN ('text', 'alt', 'caption', 'code') AND j.type = 'text') ` +\n\t\t`ELSE ${ref} END`\n\t);\n}\n\nasync function rebuildIndex(\n\tdb: Kysely<unknown>,\n\tcollectionSlug: string,\n\tfields: FieldRow[],\n\ttokenizer: string,\n): Promise<void> {\n\tconst ftsTable = `_emdash_fts_${collectionSlug}`;\n\tconst contentTable = `ec_${collectionSlug}`;\n\tconst slugs = fields.map((f) => f.slug);\n\tconst columnList = [\"id UNINDEXED\", \"locale UNINDEXED\", ...slugs].join(\", \");\n\tconst fieldList = slugs.join(\", \");\n\tconst newValueList = fields.map((f) => searchValueExpr(`NEW.${f.slug}`, f.type)).join(\", \");\n\t// The WHEN guard compares raw column values (null-safe IS NOT) so the\n\t// update trigger fires only when an indexed value, the row's locale, or\n\t// its trash state actually changed; without it every UPDATE re-tokenizes\n\t// the whole document. deleted_at must stay in the guard or trash and\n\t// restore stop syncing the index.\n\tconst changedCondition = [\"deleted_at\", \"locale\", ...slugs]\n\t\t.map((f) => `OLD.${f} IS NOT NEW.${f}`)\n\t\t.join(\" OR \");\n\t// Table-qualified: a bare column reference inside the json_tree extraction\n\t// subquery binds to json_tree's own key/value/type/... columns.\n\tconst selectValueList = fields\n\t\t.map((f) => searchValueExpr(`\"${contentTable}\".\"${f.slug}\"`, f.type))\n\t\t.join(\", \");\n\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_insert\"`).execute(db);\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_update\"`).execute(db);\n\tawait sql.raw(`DROP TRIGGER IF EXISTS \"${ftsTable}_delete\"`).execute(db);\n\tawait sql.raw(`DROP TABLE IF EXISTS \"${ftsTable}\"`).execute(db);\n\n\tawait sql\n\t\t.raw(`\n\t\tCREATE VIRTUAL TABLE IF NOT EXISTS \"${ftsTable}\" USING fts5(\n\t\t\t${columnList},\n\t\t\ttokenize='${tokenizer}'\n\t\t)\n\t`)\n\t\t.execute(db);\n\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_insert\"\n\t\tAFTER INSERT ON \"${contentTable}\"\n\t\tWHEN NEW.deleted_at IS NULL\n\t\tBEGIN\n\t\t\tINSERT OR REPLACE INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\t\tVALUES (NEW.rowid, NEW.id, NEW.locale, ${newValueList});\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_update\"\n\t\tAFTER UPDATE ON \"${contentTable}\"\n\t\tWHEN ${changedCondition}\n\t\tBEGIN\n\t\t\tDELETE FROM \"${ftsTable}\" WHERE rowid = OLD.rowid;\n\t\t\tINSERT INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\t\tSELECT NEW.rowid, NEW.id, NEW.locale, ${newValueList}\n\t\t\tWHERE NEW.deleted_at IS NULL;\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\tawait sql\n\t\t.raw(`\n\t\tCREATE TRIGGER IF NOT EXISTS \"${ftsTable}_delete\"\n\t\tAFTER DELETE ON \"${contentTable}\"\n\t\tBEGIN\n\t\t\tDELETE FROM \"${ftsTable}\" WHERE rowid = OLD.rowid;\n\t\tEND\n\t`)\n\t\t.execute(db);\n\n\tawait sql\n\t\t.raw(`\n\t\tINSERT OR REPLACE INTO \"${ftsTable}\"(rowid, id, locale, ${fieldList})\n\t\tSELECT rowid, id, locale, ${selectValueList} FROM \"${contentTable}\"\n\t\tWHERE deleted_at IS NULL\n\t`)\n\t\t.execute(db);\n}\n","import { sql, type Kysely, type RawBuilder } from \"kysely\";\n\nimport { isPostgres } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_collection_deletions\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"collection_id\", \"text\", (column) => column.notNull().primaryKey())\n\t\t.addColumn(\"collection_slug\", \"text\", (column) => column.notNull().unique())\n\t\t.addColumn(\"force_delete\", \"integer\", (column) => column.notNull())\n\t\t.addColumn(\"state\", \"text\", (column) => column.notNull().defaultTo(\"pending\"))\n\t\t.addColumn(\"phase\", \"text\", (column) => column.notNull().defaultTo(\"fence\"))\n\t\t.addColumn(\"work_cursor\", \"text\")\n\t\t.addColumn(\"source_key\", \"text\")\n\t\t.addColumn(\"occurrence_cursor\", \"text\")\n\t\t.addColumn(\"attempt_count\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t.addColumn(\"next_attempt_at\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"lease_token\", \"text\")\n\t\t.addColumn(\"lease_expires_at\", \"text\")\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.addColumn(\"updated_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_collection_deletions_due\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_collection_deletions\")\n\t\t.columns([\"state\", \"next_attempt_at\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_collection_deletions_lease\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_collection_deletions\")\n\t\t.columns([\"state\", \"lease_expires_at\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_collection_deletions_operator\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_collection_deletions\")\n\t\t.columns([\"state\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_sources_collection_cursor\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_sources\")\n\t\t.columns([\"source_type\", \"collection_id\", \"source_key\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_source_cursor\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage\")\n\t\t.columns([\"source_key\", \"id\"])\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tconst deletion = await sql<{ present: number }>`\n\t\tSELECT 1 AS present\n\t\tFROM _emdash_media_usage_collection_deletions\n\t\tLIMIT 1\n\t`.execute(db);\n\tif (deletion.rows.length > 0) {\n\t\tthrow new Error(\"Cannot roll back while durable collection deletion evidence exists\");\n\t}\n\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_source_cursor\").ifExists().execute();\n\tawait db.schema\n\t\t.dropIndex(\"idx__emdash_media_usage_sources_collection_cursor\")\n\t\t.ifExists()\n\t\t.execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_collection_deletions\").ifExists().execute();\n}\n\nfunction sortableUtcTimestamp(db: Kysely<unknown>): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql`to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"')`;\n\t}\n\treturn sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`;\n}\n","import { sql, type Kysely, type RawBuilder } from \"kysely\";\n\nimport { columnExists, isPostgres, tableExists } from \"../dialect-helpers.js\";\n\nconst ACTIVATION_KEY = \"incremental_capture\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_media_usage_reconciliations\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"collection_id\", \"text\", (column) => column.notNull().primaryKey())\n\t\t.addColumn(\"collection_slug\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"run_token\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"target_epoch\", \"bigint\")\n\t\t.addColumn(\"field_fingerprint\", \"text\")\n\t\t.addColumn(\"state\", \"text\", (column) => column.notNull().defaultTo(\"pending\"))\n\t\t.addColumn(\"phase\", \"text\", (column) => column.notNull().defaultTo(\"scan\"))\n\t\t.addColumn(\"scan_cursor\", \"text\")\n\t\t.addColumn(\"scan_upper_id\", \"text\")\n\t\t.addColumn(\"source_cursor\", \"text\")\n\t\t.addColumn(\"source_upper_key\", \"text\")\n\t\t.addColumn(\"attempt_count\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t.addColumn(\"next_attempt_at\", \"text\", (column) => column.notNull())\n\t\t.addColumn(\"lease_token\", \"text\")\n\t\t.addColumn(\"lease_expires_at\", \"text\")\n\t\t.addColumn(\"last_error_code\", \"text\")\n\t\t.addColumn(\"created_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.addColumn(\"updated_at\", \"text\", (column) =>\n\t\t\tcolumn.notNull().defaultTo(sortableUtcTimestamp(db)),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_reconciliations_due\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_reconciliations\")\n\t\t.columns([\"state\", \"next_attempt_at\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_reconciliations_lease\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_reconciliations\")\n\t\t.columns([\"state\", \"lease_expires_at\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_reconciliations_failed\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_reconciliations\")\n\t\t.columns([\"state\", \"updated_at\", \"collection_id\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx__emdash_media_usage_status_reconciliation\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_media_usage_index_status\")\n\t\t.columns([\n\t\t\t\"adapter_id\",\n\t\t\t\"scope_type\",\n\t\t\t\"capture_state\",\n\t\t\t\"reconciliation_required\",\n\t\t\t\"collection_id\",\n\t\t])\n\t\t.execute();\n\n\tif (!(await columnExists(db, \"_emdash_media_usage_activation\", \"media_usage_maintenance_turn\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_media_usage_activation\")\n\t\t\t.addColumn(\"media_usage_maintenance_turn\", \"integer\", (column) =>\n\t\t\t\tcolumn.notNull().defaultTo(2),\n\t\t\t)\n\t\t\t.execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await tableExists(db, \"_emdash_media_usage_reconciliations\")) {\n\t\tconst evidence = await sql<{ present: number }>`\n\t\t\tSELECT 1 AS present\n\t\t\tFROM _emdash_media_usage_reconciliations\n\t\t\tLIMIT 1\n\t\t`.execute(db);\n\t\tif (evidence.rows.length > 0) {\n\t\t\tthrow new Error(\"Cannot roll back while durable reconciliation evidence exists\");\n\t\t}\n\t}\n\n\tconst activation = await sql<{ state: string }>`\n\t\tSELECT state\n\t\tFROM _emdash_media_usage_activation\n\t\tWHERE task_key = ${ACTIVATION_KEY}\n\t`.execute(db);\n\tif (activation.rows[0]?.state !== \"expanded\") {\n\t\tthrow new Error(\"Cannot roll back media usage reconciliation after activation has started\");\n\t}\n\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_status_reconciliation\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_reconciliations_failed\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_reconciliations_lease\").ifExists().execute();\n\tawait db.schema.dropIndex(\"idx__emdash_media_usage_reconciliations_due\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_media_usage_reconciliations\").ifExists().execute();\n\tif (await columnExists(db, \"_emdash_media_usage_activation\", \"media_usage_maintenance_turn\")) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_media_usage_activation\")\n\t\t\t.dropColumn(\"media_usage_maintenance_turn\")\n\t\t\t.execute();\n\t}\n}\n\nfunction sortableUtcTimestamp(db: Kysely<unknown>): RawBuilder<string> {\n\tif (isPostgres(db)) {\n\t\treturn sql`to_char(clock_timestamp() AT TIME ZONE 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS.MS\"Z\"')`;\n\t}\n\treturn sql`(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`;\n}\n","import { sql, type Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\nconst FIELD_ID_PATTERN = /^[0-9A-Z]{26}$/;\n\n/** Mark custom fields whose structured list queries are backed by a physical index. */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_fields\", \"indexed\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_fields\")\n\t\t\t.addColumn(\"indexed\", \"integer\", (column) => column.notNull().defaultTo(0))\n\t\t\t.execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_fields\", \"indexed\"))) {\n\t\treturn;\n\t}\n\n\tconst indexedFields = await sql<{ id: string }>`\n\t\tSELECT id FROM _emdash_fields WHERE indexed = 1\n\t`.execute(db);\n\n\tfor (const field of indexedFields.rows) {\n\t\tif (!FIELD_ID_PATTERN.test(field.id)) {\n\t\t\tthrow new Error(`Invalid indexed field id \"${field.id}\"`);\n\t\t}\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_cf_${field.id.toLowerCase()}`)}\n\t\t`.execute(db);\n\t\tawait sql`\n\t\t\tDROP INDEX IF EXISTS ${sql.ref(`idx_cf_${field.id.toLowerCase()}_loc`)}\n\t\t`.execute(db);\n\t}\n\n\tawait db.schema.alterTable(\"_emdash_fields\").dropColumn(\"indexed\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { listTablesLike } from \"../dialect-helpers.js\";\nimport { validateIdentifier } from \"../validate.js\";\n\nconst LEGACY_DENORM_INDEXES = [\n\t\"idx_content_taxonomies_pub\",\n\t\"idx_content_taxonomies_crt\",\n\t\"idx_content_taxonomies_loc_pub\",\n\t\"idx_content_taxonomies_loc_crt\",\n] as const;\n\n/**\n * Store the content translation_group in content_taxonomies.entry_id.\n * Existing per-locale rows are merged into one assignment per content group\n * and term group. Rows whose content table or content entry no longer exists\n * are left untouched.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst tableNames = await listTablesLike(db, \"ec_%\");\n\tfor (const tableName of tableNames) {\n\t\tvalidateIdentifier(tableName, \"content table name\");\n\t\tconst collection = tableName.slice(\"ec_\".length);\n\n\t\tawait sql`\n\t\t\tINSERT INTO content_taxonomies (collection, entry_id, taxonomy_id)\n\t\t\tSELECT DISTINCT ${collection}, content.translation_group, pivot.taxonomy_id\n\t\t\tFROM content_taxonomies AS pivot\n\t\t\tINNER JOIN ${sql.ref(tableName)} AS content ON content.id = pivot.entry_id\n\t\t\tWHERE pivot.collection = ${collection}\n\t\t\t\tAND content.translation_group IS NOT NULL\n\t\t\tON CONFLICT (collection, entry_id, taxonomy_id) DO NOTHING\n\t\t`.execute(db);\n\n\t\tawait sql`\n\t\t\tDELETE FROM content_taxonomies\n\t\t\tWHERE collection = ${collection}\n\t\t\t\tAND entry_id IN (\n\t\t\t\t\tSELECT id\n\t\t\t\t\tFROM ${sql.ref(tableName)}\n\t\t\t\t\tWHERE translation_group IS NOT NULL AND id != translation_group\n\t\t\t\t)\n\t\t`.execute(db);\n\t}\n\n\tfor (const indexName of LEGACY_DENORM_INDEXES) {\n\t\tawait sql`DROP INDEX IF EXISTS ${sql.ref(indexName)}`.execute(db);\n\t}\n\tawait db.schema\n\t\t.createIndex(\"idx_content_taxonomies_group_lookup\")\n\t\t.ifNotExists()\n\t\t.on(\"content_taxonomies\")\n\t\t.columns([\"taxonomy_id\", \"collection\", \"entry_id\"])\n\t\t.execute();\n}\n\n/** The collapsed per-locale rows cannot be reconstructed. */\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// no-op\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Migration: configurable title and date fields for collections\n *\n * Adds `title_field` and `date_field` columns to `_emdash_collections` so a\n * collection can override which field powers the Title column and which field\n * powers the Date column in the admin content list. Both are nullable — NULL\n * preserves the current defaults (title-style display, last-updated date).\n *\n * Column adds/drops are guarded by `columnExists`: D1 has no transactions, so a\n * migration can partially apply without being recorded and later re-run.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"title_field\"))) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_collections\n\t\t\tADD COLUMN title_field TEXT\n\t\t`.execute(db);\n\t}\n\tif (!(await columnExists(db, \"_emdash_collections\", \"date_field\"))) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_collections\n\t\t\tADD COLUMN date_field TEXT\n\t\t`.execute(db);\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await columnExists(db, \"_emdash_collections\", \"date_field\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_collections\n\t\t\tDROP COLUMN date_field\n\t\t`.execute(db);\n\t}\n\tif (await columnExists(db, \"_emdash_collections\", \"title_field\")) {\n\t\tawait sql`\n\t\t\tALTER TABLE _emdash_collections\n\t\t\tDROP COLUMN title_field\n\t\t`.execute(db);\n\t}\n}\n","import type { Kysely } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"routable\"))) {\n\t\tawait db.schema\n\t\t\t.alterTable(\"_emdash_collections\")\n\t\t\t.addColumn(\"routable\", \"integer\", (col) => col.notNull().defaultTo(1))\n\t\t\t.execute();\n\t}\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tif (await columnExists(db, \"_emdash_collections\", \"routable\")) {\n\t\tawait db.schema.alterTable(\"_emdash_collections\").dropColumn(\"routable\").execute();\n\t}\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { isSqlite, tableExists } from \"../dialect-helpers.js\";\n\n/**\n * Restore `_emdash_content_bylines` where migration 040's SQLite rebuild\n * stopped between dropping the old table and renaming the staged copy.\n *\n * 040 guards its rebuild on `PRAGMA foreign_key_list`, which returns no rows\n * for a missing table, so a retry after that partial run skips the rename and\n * leaves `_emdash_content_bylines_new` behind with the credits inside. The\n * indexes went with the dropped table.\n *\n * The Postgres path of 040 alters the table in place, so this is a no-op there.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!isSqlite(db)) return;\n\n\tif (!(await tableExists(db, \"_emdash_content_bylines\"))) {\n\t\tif (!(await tableExists(db, \"_emdash_content_bylines_new\"))) return;\n\t\tawait sql`ALTER TABLE _emdash_content_bylines_new RENAME TO _emdash_content_bylines`.execute(\n\t\t\tdb,\n\t\t);\n\t}\n\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_content\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.columns([\"collection_slug\", \"content_id\", \"sort_order\"])\n\t\t.execute();\n\tawait db.schema\n\t\t.createIndex(\"idx_content_bylines_byline\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_content_bylines\")\n\t\t.column(\"byline_id\")\n\t\t.execute();\n}\n\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// no-op\n}\n","import { BUILTIN_ROLES, builtinPolicies } from \"@premium-cms/auth\";\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { columnExists, currentTimestamp } from \"../dialect-helpers.js\";\n\n/**\n * Roles → policies → routes.\n *\n * Adds the authorization model on top of the fixed role levels:\n *\n *   _emdash_roles          one per role; `level` is the legacy tier the role\n *                          most resembles (kept so code that has not been\n *                          taught about policies still has a floor)\n *   _emdash_policies       named bundles of rules (routes, permissions,\n *                          admin pages, …) stored as JSON\n *   _emdash_role_policies  which policies a role holds\n *\n * and links users, invites, signup domains and API tokens to it:\n *\n *   users.role_id, auth_tokens.role_id, allowed_domains.default_role_id\n *   _emdash_api_tokens.policies   JSON array of policy slugs; NULL means the\n *                                 token predates policies and carries scopes\n *\n * The five built-in roles are seeded with a policy each that reproduces the\n * old level exactly, and every existing row is backfilled from its level, so\n * upgrading changes nothing for anyone until an admin edits a policy.\n *\n * Built-in ids are deterministic (`role:admin`, `policy:core-admin`) so they\n * are identical across every environment and safe to reference from seeds.\n *\n * Every step is guarded so a retry after a partial run (D1 subrequest limit,\n * isolate cancellation — see #954) picks up where it left off.\n */\n\nexport const BUILTIN_ROLE_ID_PREFIX = \"role:\";\nexport const BUILTIN_POLICY_ID_PREFIX = \"policy:\";\n\nexport function builtinRoleId(slug: string): string {\n\treturn `${BUILTIN_ROLE_ID_PREFIX}${slug}`;\n}\n\nexport function builtinPolicyId(slug: string): string {\n\treturn `${BUILTIN_POLICY_ID_PREFIX}${slug}`;\n}\n\nasync function addColumnIfMissing(\n\tdb: Kysely<unknown>,\n\ttable: string,\n\tcolumn: string,\n\tdefinition: string,\n): Promise<void> {\n\tif (await columnExists(db, table, column)) return;\n\tawait sql.raw(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).execute(db);\n}\n\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\t// ── Tables ────────────────────────────────────────────────────────\n\tawait db.schema\n\t\t.createTable(\"_emdash_roles\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"description\", \"text\")\n\t\t.addColumn(\"level\", \"integer\", (col) => col.notNull().defaultTo(10))\n\t\t.addColumn(\"builtin\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_policies\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"slug\", \"text\", (col) => col.notNull().unique())\n\t\t.addColumn(\"name\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"description\", \"text\")\n\t\t.addColumn(\"builtin\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"rules\", \"text\", (col) => col.notNull().defaultTo(\"{}\"))\n\t\t.addColumn(\"created_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.addColumn(\"updated_at\", \"text\", (col) => col.defaultTo(currentTimestamp(db)))\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createTable(\"_emdash_role_policies\")\n\t\t.ifNotExists()\n\t\t.addColumn(\"role_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"policy_id\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"sort_order\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addPrimaryKeyConstraint(\"role_policies_pk\", [\"role_id\", \"policy_id\"])\n\t\t.addForeignKeyConstraint(\"role_policies_role_fk\", [\"role_id\"], \"_emdash_roles\", [\"id\"], (cb) =>\n\t\t\tcb.onDelete(\"cascade\"),\n\t\t)\n\t\t.addForeignKeyConstraint(\n\t\t\t\"role_policies_policy_fk\",\n\t\t\t[\"policy_id\"],\n\t\t\t\"_emdash_policies\",\n\t\t\t[\"id\"],\n\t\t\t(cb) => cb.onDelete(\"cascade\"),\n\t\t)\n\t\t.execute();\n\n\tawait db.schema\n\t\t.createIndex(\"idx_role_policies_policy\")\n\t\t.ifNotExists()\n\t\t.on(\"_emdash_role_policies\")\n\t\t.column(\"policy_id\")\n\t\t.execute();\n\n\t// ── Links from existing tables ────────────────────────────────────\n\t// No FK constraints on the added columns: SQLite cannot add one to an\n\t// existing table without a rebuild, and D1 is SQLite. Referential\n\t// integrity is enforced in the repository (a role in use cannot be\n\t// deleted) and the resolver treats a dangling id as \"no role\".\n\tawait addColumnIfMissing(db, \"users\", \"role_id\", \"TEXT\");\n\tawait addColumnIfMissing(db, \"auth_tokens\", \"role_id\", \"TEXT\");\n\tawait addColumnIfMissing(db, \"allowed_domains\", \"default_role_id\", \"TEXT\");\n\tawait addColumnIfMissing(db, \"_emdash_api_tokens\", \"policies\", \"TEXT\");\n\n\tawait db.schema\n\t\t.createIndex(\"idx_users_role_id\")\n\t\t.ifNotExists()\n\t\t.on(\"users\")\n\t\t.column(\"role_id\")\n\t\t.execute();\n\n\t// ── Seed built-ins ────────────────────────────────────────────────\n\tconst now = new Date().toISOString();\n\n\tfor (const role of BUILTIN_ROLES) {\n\t\tawait sql`\n\t\t\tINSERT INTO _emdash_roles (id, slug, name, description, level, builtin, created_at, updated_at)\n\t\t\tVALUES (${builtinRoleId(role.slug)}, ${role.slug}, ${role.name}, ${role.description}, ${role.level}, 1, ${now}, ${now})\n\t\t\tON CONFLICT (id) DO NOTHING\n\t\t`.execute(db);\n\t}\n\n\tfor (const policy of builtinPolicies()) {\n\t\tawait sql`\n\t\t\tINSERT INTO _emdash_policies (id, slug, name, description, builtin, rules, created_at, updated_at)\n\t\t\tVALUES (${builtinPolicyId(policy.slug)}, ${policy.slug}, ${policy.name}, ${policy.description}, 1, ${JSON.stringify(policy.rules)}, ${now}, ${now})\n\t\t\tON CONFLICT (id) DO NOTHING\n\t\t`.execute(db);\n\t}\n\n\tfor (const role of BUILTIN_ROLES) {\n\t\tawait sql`\n\t\t\tINSERT INTO _emdash_role_policies (role_id, policy_id, sort_order)\n\t\t\tVALUES (${builtinRoleId(role.slug)}, ${builtinPolicyId(role.policy)}, 0)\n\t\t\tON CONFLICT (role_id, policy_id) DO NOTHING\n\t\t`.execute(db);\n\t}\n\n\t// ── Backfill from legacy levels ───────────────────────────────────\n\t// Highest matching tier wins, mirroring builtinRoleForLevel().\n\tconst tiers = BUILTIN_ROLES.toSorted((a, b) => b.level - a.level);\n\tconst caseExpr = (column: string) =>\n\t\t`CASE ${tiers\n\t\t\t.map((r) => `WHEN ${column} >= ${r.level} THEN '${builtinRoleId(r.slug)}'`)\n\t\t\t.join(\" \")} ELSE '${builtinRoleId(tiers.at(-1)!.slug)}' END`;\n\n\tawait sql.raw(`UPDATE users SET role_id = ${caseExpr(\"role\")} WHERE role_id IS NULL`).execute(db);\n\tawait sql\n\t\t.raw(\n\t\t\t`UPDATE auth_tokens SET role_id = ${caseExpr(\"role\")} WHERE role_id IS NULL AND role IS NOT NULL`,\n\t\t)\n\t\t.execute(db);\n\tawait sql\n\t\t.raw(\n\t\t\t`UPDATE allowed_domains SET default_role_id = ${caseExpr(\"default_role\")} WHERE default_role_id IS NULL`,\n\t\t)\n\t\t.execute(db);\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_role_policies\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_policies\").ifExists().execute();\n\tawait db.schema.dropTable(\"_emdash_roles\").ifExists().execute();\n\t// Added columns are left in place: SQLite needs a table rebuild to drop\n\t// them, and a NULL role_id is harmless to the pre-policy code paths.\n}\n","import type { Kysely } from \"kysely\";\n\n/**\n * Migration: per-instance billing ledger (`_emdash_usage`).\n *\n * The recursive-hosting platform bills each instance \"cost plus\": the parent\n * that provisions an instance meters the instance's real Cloudflare running\n * cost (requests, CPU, D1 rows, R2 ops/storage), applies a markup, and writes\n * charge rows into THIS ledger inside the instance's own D1 — so an instance is\n * always the source of truth for its own bill. Credit top-ups land here too as\n * negative-charge rows. The running balance is therefore `-SUM(charge_micros)`.\n *\n * All monetary amounts are integer micro-dollars (1e-6 USD) to avoid float\n * drift. `cost_micros` is the provider's own cost; `charge_micros` is what the\n * instance is billed (cost × markup), negative for credit purchases. Rows are\n * append-only — the immutable log reads straight from here. `ref` is a caller\n * supplied idempotency key (unique when present) so a re-run of a daily usage\n * sync cannot double-charge.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema\n\t\t.createTable(\"_emdash_usage\")\n\t\t.addColumn(\"id\", \"text\", (col) => col.primaryKey())\n\t\t.addColumn(\"ts\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"day\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"kind\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"key\", \"text\", (col) => col.notNull())\n\t\t.addColumn(\"quantity\", \"real\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"cost_micros\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"charge_micros\", \"integer\", (col) => col.notNull().defaultTo(0))\n\t\t.addColumn(\"actor_id\", \"text\")\n\t\t.addColumn(\"meta\", \"text\")\n\t\t.addColumn(\"source\", \"text\", (col) => col.notNull().defaultTo(\"parent\"))\n\t\t.addColumn(\"ref\", \"text\")\n\t\t.execute();\n\n\t// Chronological listing (immutable log + recent-activity views).\n\tawait db.schema\n\t\t.createIndex(\"idx_usage_ts\")\n\t\t.on(\"_emdash_usage\")\n\t\t.column(\"ts\")\n\t\t.execute();\n\n\t// Daily rollups for the billing analytics screen.\n\tawait db.schema\n\t\t.createIndex(\"idx_usage_day_key\")\n\t\t.on(\"_emdash_usage\")\n\t\t.columns([\"day\", \"key\"])\n\t\t.execute();\n\n\t// Idempotent charge writes: a non-null ref may appear at most once, so a\n\t// repeated usage sync (same day, same metric) is a no-op instead of a\n\t// double charge. NULL refs are exempt (SQLite treats NULLs as distinct).\n\tawait db.schema\n\t\t.createIndex(\"uq_usage_ref\")\n\t\t.on(\"_emdash_usage\")\n\t\t.column(\"ref\")\n\t\t.unique()\n\t\t.where(\"ref\", \"is not\", null)\n\t\t.execute();\n}\n\nexport async function down(db: Kysely<unknown>): Promise<void> {\n\tawait db.schema.dropTable(\"_emdash_usage\").execute();\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\nimport { columnExists } from \"../dialect-helpers.js\";\n\n/**\n * Where a collection's entries live.\n *\n *   _emdash_collections.storage   'db'  (default) rows in the ec_* table\n *                                 'git' JSON files in the site's git repo\n *                                       (content/<collection>/<slug>.json);\n *                                       the ec_* table stays for schema only\n *\n * Git-backed collections are read from and written to the repo directly —\n * saving in the admin is a commit — so seldom-changing, version-worthy\n * content (form definitions, site structure) lives with the site's code,\n * while frequently edited or sensitive data stays in the database.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tif (!(await columnExists(db, \"_emdash_collections\", \"storage\"))) {\n\t\tawait sql`ALTER TABLE ${sql.ref(\"_emdash_collections\")} ADD COLUMN ${sql.ref(\"storage\")} text NOT NULL DEFAULT 'db'`.execute(\n\t\t\tdb,\n\t\t);\n\t}\n}\n\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// Forward-only: SQLite can't drop columns portably and 'db' rows are unaffected.\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\n\n/**\n * Per-token CORS opt-in for browser callers.\n *\n * `cors = 1` lets responses to requests authenticated by THIS token carry\n * `Access-Control-Allow-Origin: <request origin>` (see middleware/cors.ts).\n * Same-origin policy exists to protect ambient credentials (cookies); a\n * deliberately attached Bearer token is not ambient, so cross-origin browser\n * use is safe to grant — but only by explicit opt-in, per token.\n *\n * Guarded against re-run (the #954 partial-apply failure mode): ALTER TABLE\n * ADD COLUMN has no IF NOT EXISTS, so probe the schema first.\n */\nexport async function up(db: Kysely<unknown>): Promise<void> {\n\tconst info = await sql<{ name: string }>`PRAGMA table_info(_emdash_api_tokens)`.execute(db);\n\tif (!info.rows.some((r) => r.name === \"cors\")) {\n\t\tawait sql`ALTER TABLE _emdash_api_tokens ADD COLUMN cors INTEGER NOT NULL DEFAULT 0`.execute(\n\t\t\tdb,\n\t\t);\n\t}\n}\n\nexport async function down(_db: Kysely<unknown>): Promise<void> {\n\t// SQLite DROP COLUMN support varies by version; the column is inert when\n\t// unused, so rollback leaves it in place.\n}\n","import { type Kysely, sql } from \"kysely\";\nimport { type Migration, type MigrationProvider, Migrator } from \"kysely/migration\";\n\nimport { MIGRATION_LOCK_BUSY_MESSAGE } from \"../pg-migration-lock.js\";\nimport type { Database } from \"../types.js\";\n// Import migrations statically for bundling\nimport * as m001 from \"./001_initial.js\";\nimport * as m002 from \"./002_media_status.js\";\nimport * as m003 from \"./003_schema_registry.js\";\nimport * as m004 from \"./004_plugins.js\";\nimport * as m005 from \"./005_menus.js\";\nimport * as m006 from \"./006_taxonomy_defs.js\";\nimport * as m007 from \"./007_widgets.js\";\nimport * as m008 from \"./008_auth.js\";\nimport * as m009 from \"./009_user_disabled.js\";\nimport * as m011 from \"./011_sections.js\";\nimport * as m012 from \"./012_search.js\";\nimport * as m013 from \"./013_scheduled_publishing.js\";\nimport * as m014 from \"./014_draft_revisions.js\";\nimport * as m015 from \"./015_indexes.js\";\nimport * as m016 from \"./016_api_tokens.js\";\nimport * as m017 from \"./017_authorization_codes.js\";\nimport * as m018 from \"./018_seo.js\";\nimport * as m019 from \"./019_i18n.js\";\nimport * as m020 from \"./020_collection_url_pattern.js\";\nimport * as m021 from \"./021_remove_section_categories.js\";\nimport * as m022 from \"./022_marketplace_plugin_state.js\";\nimport * as m023 from \"./023_plugin_metadata.js\";\nimport * as m024 from \"./024_media_placeholders.js\";\nimport * as m025 from \"./025_oauth_clients.js\";\nimport * as m026 from \"./026_cron_tasks.js\";\nimport * as m027 from \"./027_comments.js\";\nimport * as m028 from \"./028_drop_author_url.js\";\nimport * as m029 from \"./029_redirects.js\";\nimport * as m030 from \"./030_widen_scheduled_index.js\";\nimport * as m031 from \"./031_bylines.js\";\nimport * as m032 from \"./032_rate_limits.js\";\nimport * as m033 from \"./033_optimize_content_indexes.js\";\nimport * as m034 from \"./034_published_at_index.js\";\nimport * as m035 from \"./035_bounded_404_log.js\";\nimport * as m036 from \"./036_i18n_menus_and_taxonomies.js\";\nimport * as m037 from \"./037_credential_algorithm.js\";\nimport * as m038 from \"./038_registry_plugin_state.js\";\nimport * as m039 from \"./039_fix_fts5_triggers.js\";\nimport * as m040 from \"./040_byline_i18n.js\";\nimport * as m041 from \"./041_content_locale_list_index.js\";\nimport * as m042 from \"./042_byline_fields.js\";\nimport * as m043 from \"./043_content_references.js\";\nimport * as m044 from \"./044_comment_reactions.js\";\nimport * as m045 from \"./045_taxonomy_parent_group.js\";\nimport * as m046 from \"./046_media_usage_index.js\";\nimport * as m047 from \"./047_restore_taxonomy_parent_index.js\";\nimport * as m048 from \"./048_restore_content_taxonomies_term_index.js\";\nimport * as m049 from \"./049_taxonomies_name_locale_index.js\";\nimport * as m050 from \"./050_media_usage_index_status.js\";\nimport * as m051 from \"./051_content_taxonomies_denorm.js\";\nimport * as m052 from \"./052_media_usage_read_index.js\";\nimport * as m053 from \"./053_plugin_mcp_tools.js\";\nimport * as m054 from \"./054_media_upload_attempts.js\";\nimport * as m055 from \"./055_content_translation_group_locale_index.js\";\nimport * as m056 from \"./056_taxonomy_term_sort_order.js\";\nimport * as m057 from \"./057_collection_hidden.js\";\nimport * as m058 from \"./058_collection_sort_order.js\";\nimport * as m059 from \"./059_revision_prune_queue.js\";\nimport * as m060 from \"./060_collection_admin_config.js\";\nimport * as m061 from \"./061_media_usage_cleanup.js\";\nimport * as m062 from \"./062_media_usage_cleanup_fence.js\";\nimport * as m063 from \"./063_media_usage_incremental_work.js\";\nimport * as m064 from \"./064_fts_plain_text.js\";\nimport * as m065 from \"./065_media_usage_collection_deletion.js\";\nimport * as m066 from \"./066_media_usage_reconciliation.js\";\nimport * as m067 from \"./067_indexed_content_fields.js\";\nimport * as m068 from \"./068_content_taxonomy_entry_groups.js\";\nimport * as m069 from \"./069_collection_title_date_fields.js\";\nimport * as m070 from \"./070_collection_routable.js\";\nimport * as m071 from \"./071_restore_content_bylines_table.js\";\nimport * as m072 from \"./072_authz.js\";\nimport * as m073 from \"./073_billing_usage.js\";\nimport * as m074 from \"./074_collection_storage.js\";\nimport * as m075 from \"./075_api_token_cors.js\";\n\nconst MIGRATIONS: Readonly<Record<string, Migration>> = Object.freeze({\n\t\"001_initial\": m001,\n\t\"002_media_status\": m002,\n\t\"003_schema_registry\": m003,\n\t\"004_plugins\": m004,\n\t\"005_menus\": m005,\n\t\"006_taxonomy_defs\": m006,\n\t\"007_widgets\": m007,\n\t\"008_auth\": m008,\n\t\"009_user_disabled\": m009,\n\t\"011_sections\": m011,\n\t\"012_search\": m012,\n\t\"013_scheduled_publishing\": m013,\n\t\"014_draft_revisions\": m014,\n\t\"015_indexes\": m015,\n\t\"016_api_tokens\": m016,\n\t\"017_authorization_codes\": m017,\n\t\"018_seo\": m018,\n\t\"019_i18n\": m019,\n\t\"020_collection_url_pattern\": m020,\n\t\"021_remove_section_categories\": m021,\n\t\"022_marketplace_plugin_state\": m022,\n\t\"023_plugin_metadata\": m023,\n\t\"024_media_placeholders\": m024,\n\t\"025_oauth_clients\": m025,\n\t\"026_cron_tasks\": m026,\n\t\"027_comments\": m027,\n\t\"028_drop_author_url\": m028,\n\t\"029_redirects\": m029,\n\t\"030_widen_scheduled_index\": m030,\n\t\"031_bylines\": m031,\n\t\"032_rate_limits\": m032,\n\t\"033_optimize_content_indexes\": m033,\n\t\"034_published_at_index\": m034,\n\t\"035_bounded_404_log\": m035,\n\t\"036_i18n_menus_and_taxonomies\": m036,\n\t\"037_credential_algorithm\": m037,\n\t\"038_registry_plugin_state\": m038,\n\t\"039_fix_fts5_triggers\": m039,\n\t\"040_byline_i18n\": m040,\n\t\"041_content_locale_list_index\": m041,\n\t\"042_byline_fields\": m042,\n\t\"043_content_references\": m043,\n\t\"044_comment_reactions\": m044,\n\t\"045_taxonomy_parent_group\": m045,\n\t\"046_media_usage_index\": m046,\n\t\"047_restore_taxonomy_parent_index\": m047,\n\t\"048_restore_content_taxonomies_term_index\": m048,\n\t\"049_taxonomies_name_locale_index\": m049,\n\t\"050_media_usage_index_status\": m050,\n\t\"051_content_taxonomies_denorm\": m051,\n\t\"052_media_usage_read_index\": m052,\n\t\"053_plugin_mcp_tools\": m053,\n\t\"054_media_upload_attempts\": m054,\n\t\"055_content_translation_group_locale_index\": m055,\n\t\"056_taxonomy_term_sort_order\": m056,\n\t\"057_collection_hidden\": m057,\n\t\"058_collection_sort_order\": m058,\n\t\"059_revision_prune_queue\": m059,\n\t\"060_collection_admin_config\": m060,\n\t\"061_media_usage_cleanup\": m061,\n\t\"062_media_usage_cleanup_fence\": m062,\n\t\"063_media_usage_incremental_work\": m063,\n\t\"064_fts_plain_text\": m064,\n\t\"065_media_usage_collection_deletion\": m065,\n\t\"066_media_usage_reconciliation\": m066,\n\t\"067_indexed_content_fields\": m067,\n\t\"068_content_taxonomy_entry_groups\": m068,\n\t\"069_collection_title_date_fields\": m069,\n\t\"070_collection_routable\": m070,\n\t\"071_restore_content_bylines_table\": m071,\n\t\"072_authz\": m072,\n\t\"073_billing_usage\": m073,\n\t\"074_collection_storage\": m074,\n\t\"075_api_token_cors\": m075,\n});\n\n/** Ordered names from the statically registered migration set. */\nexport const MIGRATION_NAMES: readonly string[] = Object.freeze(Object.keys(MIGRATIONS));\n\n/** Total number of registered migrations. Exported for use in tests. */\nexport const MIGRATION_COUNT = MIGRATION_NAMES.length;\n\n/**\n * Migration provider that uses statically imported migrations.\n * This approach works well with bundlers and avoids filesystem access.\n */\nclass StaticMigrationProvider implements MigrationProvider {\n\tasync getMigrations(): Promise<Record<string, Migration>> {\n\t\treturn MIGRATIONS;\n\t}\n}\n\nexport interface MigrationStatus {\n\tapplied: string[];\n\tpending: string[];\n}\n\nexport interface ExactMigrationStatus {\n\tknownApplied: string[];\n\tpending: string[];\n\tunknownApplied: string[];\n}\n\n/**\n * Thrown when another instance held the migration lock for the whole wait\n * window. This is NOT a migration failure — the holder may simply be slow\n * (e.g. many pending migrations over a remote Postgres connection) — so\n * callers with failure-backoff logic (`getDatabase` in emdash-runtime.ts)\n * exempt it: the next request waits again instead of backing off, and init\n * recovers as soon as the holder finishes.\n */\nexport class ConcurrentMigrationTimeoutError extends Error {\n\tconstructor(waitMs: number) {\n\t\tsuper(\n\t\t\t`Timed out waiting for another instance's migrations: the migration lock was still held after ${waitMs}ms. ` +\n\t\t\t\t\"It may still be applying migrations, or its migration may be failing repeatedly.\",\n\t\t);\n\t\tthis.name = \"ConcurrentMigrationTimeoutError\";\n\t}\n}\n\n/** Custom migration table name */\nconst MIGRATION_TABLE = \"_emdash_migrations\";\nconst MIGRATION_LOCK_TABLE = \"_emdash_migrations_lock\";\n\nexport interface MigrationOptions {\n\tmigrationTableSchema?: string;\n\t/**\n\t * Override how long to wait for a concurrent migrator to finish before\n\t * giving up. Defaults to MIGRATION_RACE_WAIT_MS; tests shorten it so the\n\t * give-up path doesn't take 10 seconds per case.\n\t */\n\traceWaitMs?: number;\n}\n\nfunction createMigrator(db: Kysely<Database>, options?: MigrationOptions): Migrator {\n\treturn new Migrator({\n\t\tdb,\n\t\tprovider: new StaticMigrationProvider(),\n\t\tmigrationTableName: MIGRATION_TABLE,\n\t\tmigrationLockTableName: MIGRATION_LOCK_TABLE,\n\t\tmigrationTableSchema: options?.migrationTableSchema,\n\t});\n}\n\n/**\n * Get migration status\n */\nexport async function getMigrationStatus(\n\tdb: Kysely<Database>,\n\toptions?: MigrationOptions,\n): Promise<MigrationStatus> {\n\tconst migrator = createMigrator(db, options);\n\n\tconst migrations = await migrator.getMigrations();\n\n\tconst applied: string[] = [];\n\tconst pending: string[] = [];\n\n\tfor (const migration of migrations) {\n\t\tif (migration.executedAt) {\n\t\t\tapplied.push(migration.name);\n\t\t} else {\n\t\t\tpending.push(migration.name);\n\t\t}\n\t}\n\n\treturn { applied, pending };\n}\n\n/** Pattern for escaping special regex characters. Matches the shared helper in `database/repositories/content.ts`. */\nconst REGEX_ESCAPE_PATTERN = /[.*+?^${}()|[\\]\\\\]/g;\n\n/** Escape special regex characters so a string can be embedded literally in `new RegExp()`. */\nfunction escapeRegExp(value: string): string {\n\treturn value.replace(REGEX_ESCAPE_PATTERN, \"\\\\$&\");\n}\n\n/**\n * Pattern used to detect the concurrent-migration race. The Kysely\n * `SqliteAdapter.acquireMigrationLock` is a no-op (inherited by `kysely-d1`\n * and our `EmDashD1Dialect`), so two isolates running migrations against the\n * same database can both attempt `INSERT INTO _emdash_migrations` for the\n * same migration name. The losing insert fails with a UNIQUE constraint\n * error, which is benign: the other isolate is applying the same schema.\n *\n * We match on the table name (not the full error text) because different\n * SQLite drivers phrase the message differently\n * (`UNIQUE constraint failed: _emdash_migrations.name` for better-sqlite3,\n * `D1_ERROR: UNIQUE constraint failed: _emdash_migrations.name: SQLITE_CONSTRAINT`\n * for D1, etc.). The pattern is built from `MIGRATION_TABLE` so a rename\n * cannot silently disable race detection.\n */\nconst MIGRATION_RACE_PATTERN = new RegExp(\n\t`UNIQUE constraint failed: ${escapeRegExp(MIGRATION_TABLE)}\\\\.name`,\n\t\"i\",\n);\n\n/**\n * How long to wait for a concurrent migrator to finish before giving up.\n * Exported because the db init lock's reclaim deadline must comfortably\n * exceed it (see DB_INIT_DEADLINE_MS in emdash-runtime.ts) — a healthy\n * init can legitimately block this long inside waitForConcurrentMigrator.\n */\nexport const MIGRATION_RACE_WAIT_MS = 10_000;\n/** Polling interval while waiting for a concurrent migrator. */\nconst MIGRATION_RACE_POLL_MS = 100;\n\n/**\n * Pattern used to detect \"table does not exist\" errors across the dialects\n * EmDash supports. The phrasing differs by driver:\n *\n *   - better-sqlite3: `no such table: _emdash_migrations`\n *   - D1:             `D1_ERROR: no such table: _emdash_migrations: SQLITE_ERROR`\n *   - PostgreSQL:     `relation \"_emdash_migrations\" does not exist`\n *                     (also occasionally `table \"_emdash_migrations\" does not exist`)\n *\n * We deliberately match on the migration table name (rather than using the\n * generic `isMissingTableError` helper) so an unexpected missing-table error\n * naming a different table — implausible today since\n * `getAppliedMigrationCount` only references `MIGRATION_TABLE`, but cheap\n * insurance against future edits — is not silently swallowed. The pattern is\n * built from `MIGRATION_TABLE` so a rename cannot drift.\n */\nconst MIGRATION_TABLE_MISSING_PATTERN = new RegExp(\n\t`(?:no such table:\\\\s*(?:[a-z][a-z0-9_]*\\\\.)?${escapeRegExp(MIGRATION_TABLE)}\\\\b` +\n\t\t`|(?:relation|table)\\\\s+\"?(?:[a-z][a-z0-9_]*\\\\.)?${escapeRegExp(MIGRATION_TABLE)}\"?\\\\s+does(?:n't| not) exist\\\\b)`,\n\t\"i\",\n);\n\n/**\n * Read the count of applied migrations.\n *\n * Returns `null` only when the migration table does not exist yet (which is\n * the normal state on a fresh database before the first migration runs).\n * Any other error is rethrown so callers — particularly\n * `waitForConcurrentMigrator` — don't silently mask connection failures,\n * permission errors, or other unexpected driver problems behind a 10s wait\n * and a bogus \"we're done\" verdict.\n */\nasync function getAppliedMigrationCount(db: Kysely<Database>): Promise<number | null> {\n\ttry {\n\t\tconst result = await sql<{ count: number }>`\n\t\t\tSELECT COUNT(*) as count FROM ${sql.ref(MIGRATION_TABLE)}\n\t\t`.execute(db);\n\t\treturn Number(result.rows[0]?.count ?? 0);\n\t} catch (error) {\n\t\tif (MIGRATION_TABLE_MISSING_PATTERN.test(deepErrorMessage(error))) {\n\t\t\treturn null;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Wait for a concurrent migrator to finish applying all migrations.\n *\n * Resolves to `true` once the migration table contains at least\n * `MIGRATION_COUNT` rows (i.e. every migration this build knows about has\n * been recorded), `false` if the deadline elapses first. We use `>=` rather\n * than `===` so that an old isolate observing a database that has already\n * been migrated by a newer build still treats the wait as settled instead\n * of timing out.\n */\nasync function waitForConcurrentMigrator(\n\tdb: Kysely<Database>,\n\twaitMs: number = MIGRATION_RACE_WAIT_MS,\n): Promise<boolean> {\n\tconst deadline = Date.now() + waitMs;\n\twhile (Date.now() < deadline) {\n\t\tconst count = await getAppliedMigrationCount(db);\n\t\tif (count !== null && count >= MIGRATION_COUNT) {\n\t\t\treturn true;\n\t\t}\n\t\tawait new Promise((resolve) => setTimeout(resolve, MIGRATION_RACE_POLL_MS));\n\t}\n\tconst finalCount = await getAppliedMigrationCount(db);\n\treturn finalCount !== null && finalCount >= MIGRATION_COUNT;\n}\n\n/** Extract the deepest error message available from a thrown value. */\nfunction deepErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\tconst own = error.message ?? \"\";\n\t\tif (error.cause) {\n\t\t\tconst causeMsg = deepErrorMessage(error.cause);\n\t\t\treturn own ? `${own}: ${causeMsg}` : causeMsg;\n\t\t}\n\t\treturn own;\n\t}\n\tif (typeof error === \"string\") return error;\n\ttry {\n\t\treturn JSON.stringify(error);\n\t} catch {\n\t\treturn String(error);\n\t}\n}\n\n/**\n * Read exact migration status without invoking Kysely's migration\n * introspection. Registered names retain execution order; unknown database\n * records are sorted so reports remain stable across dialects.\n */\nexport async function getExactMigrationStatus(\n\tdb: Kysely<Database>,\n\toptions?: MigrationOptions,\n): Promise<ExactMigrationStatus> {\n\tconst table = options?.migrationTableSchema\n\t\t? sql`${sql.ref(options.migrationTableSchema)}.${sql.ref(MIGRATION_TABLE)}`\n\t\t: sql.ref(MIGRATION_TABLE);\n\n\tlet rows: readonly { name: string }[];\n\ttry {\n\t\tconst result = await sql<{ name: string }>`SELECT name FROM ${table}`.execute(db);\n\t\trows = result.rows;\n\t} catch (error) {\n\t\tif (MIGRATION_TABLE_MISSING_PATTERN.test(deepErrorMessage(error))) {\n\t\t\treturn {\n\t\t\t\tknownApplied: [],\n\t\t\t\tpending: [...MIGRATION_NAMES],\n\t\t\t\tunknownApplied: [],\n\t\t\t};\n\t\t}\n\t\tthrow error;\n\t}\n\n\tconst appliedNames = new Set(rows.map((row) => row.name));\n\tconst knownApplied = MIGRATION_NAMES.filter((name) => appliedNames.has(name));\n\tconst pending = MIGRATION_NAMES.filter((name) => !appliedNames.has(name));\n\tconst knownNames = new Set(MIGRATION_NAMES);\n\tconst unknownApplied = [...appliedNames].filter((name) => !knownNames.has(name)).toSorted();\n\n\treturn { knownApplied, pending, unknownApplied };\n}\n\n/**\n * Run all pending migrations.\n *\n * Includes a fast-path: if the migration table already exists and contains\n * at least MIGRATION_COUNT rows, all migrations this build knows about have\n * been applied and we can skip the Kysely Migrator entirely. This avoids\n * the expensive `pragma_table_info` introspection that Kysely runs for\n * every table in the database (twice!) just to check if the migration\n * tables exist. On D1 with ~57 tables, that's ~116 queries saved per init.\n *\n * Concurrent-migration safety: the Kysely Migrator's `acquireMigrationLock`\n * is a no-op for SQLite (and therefore D1), so two callers running this\n * concurrently against the same database will both try to apply pending\n * migrations. SQLite serializes the writes, but the loser still surfaces a\n * `UNIQUE constraint failed: _emdash_migrations.name` error. We treat that\n * specific error as benign: another caller is already applying the same\n * schema. We wait for the concurrent migrator to finish, then return\n * success. This matches the user-observable expectation that running\n * migrations twice in a row is a no-op.\n */\nexport async function runMigrations(\n\tdb: Kysely<Database>,\n\toptions?: MigrationOptions,\n): Promise<{ applied: string[] }> {\n\t// Fast path: check if all migrations are already applied.\n\t// A single cheap query vs the Migrator's full schema introspection.\n\t// We use `>=` rather than `===` so a database with extra rows from a\n\t// newer build (e.g. mid-deploy old isolate, or downgrade) still skips\n\t// the migrator instead of falling through to the race-recovery path\n\t// unnecessarily.\n\tif (!options?.migrationTableSchema) {\n\t\tconst initialCount = await getAppliedMigrationCount(db);\n\t\tif (initialCount !== null && initialCount >= MIGRATION_COUNT) {\n\t\t\treturn { applied: [] };\n\t\t}\n\t}\n\n\tconst migrator = createMigrator(db, options);\n\n\tconst { error, results } = await migrator.migrateToLatest();\n\n\tconst applied = results?.filter((r) => r.status === \"Success\").map((r) => r.migrationName) ?? [];\n\n\tif (error) {\n\t\t// Walk error.cause to get the underlying driver message — Kysely\n\t\t// often wraps with an empty top-level message.\n\t\tconst msg = deepErrorMessage(error);\n\t\tconst failedMigration = results?.find((r) => r.status === \"Error\");\n\n\t\t// Concurrent-migration race: another caller is applying (or just\n\t\t// applied) the same migration. SQLite/D1 surface it as the\n\t\t// bookkeeping UNIQUE violation (their migration lock is a no-op);\n\t\t// Postgres surfaces it as the fail-fast advisory try-lock reporting\n\t\t// busy (see pg-migration-lock.ts). Either way: wait for the\n\t\t// concurrent migrator to finish, then verify the schema is fully\n\t\t// migrated and treat as success.\n\t\tconst lockBusy = msg.includes(MIGRATION_LOCK_BUSY_MESSAGE);\n\t\tif (MIGRATION_RACE_PATTERN.test(msg) || lockBusy) {\n\t\t\tconst settled = await waitForConcurrentMigrator(db, options?.raceWaitMs);\n\t\t\tif (settled) {\n\t\t\t\treturn { applied };\n\t\t\t}\n\t\t\tif (lockBusy) {\n\t\t\t\t// The lock holder didn't finish within the wait window —\n\t\t\t\t// either it's slow or its migration is failing. Surface a\n\t\t\t\t// distinct error type instead of the raw sentinel message so\n\t\t\t\t// callers can tell \"still in progress\" from \"failed\".\n\t\t\t\tthrow new ConcurrentMigrationTimeoutError(options?.raceWaitMs ?? MIGRATION_RACE_WAIT_MS);\n\t\t\t}\n\t\t}\n\n\t\tconst failedSuffix = failedMigration ? ` (migration: ${failedMigration.migrationName})` : \"\";\n\t\tthrow new Error(`Migration failed: ${msg || \"unknown error\"}${failedSuffix}`);\n\t}\n\n\treturn { applied };\n}\n\n/**\n * Rollback the last migration\n */\nexport async function rollbackMigration(\n\tdb: Kysely<Database>,\n\toptions?: MigrationOptions,\n): Promise<{ rolledBack: string | null }> {\n\tconst migrator = createMigrator(db, options);\n\n\tconst { error, results } = await migrator.migrateDown();\n\n\tconst rolledBack = results?.[0]?.status === \"Success\" ? results[0].migrationName : null;\n\n\tif (error) {\n\t\tconst msg = error instanceof Error ? error.message : JSON.stringify(error);\n\t\tthrow new Error(`Rollback failed: ${msg}`);\n\t}\n\n\treturn { rolledBack };\n}\n"],"mappings":"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"}