import { C as visibilityPredicate, S as unpublish, _ as restore, a as assertSanitize, c as getById, d as list, f as promoteScheduled, g as remove, h as readFlatContentEntries, i as assertContentTransactionIdentity, l as getBySlug, m as put, n as ContentNotFoundError, o as contentEntriesBaseMigrationSql, p as publish, r as ContentStoreContractError, s as contentVisibilityMigrationSql, t as ContentConflictError, u as isContentStoreContractError, v as schedule, x as trash, y as setVisibility } from "./store-D_2h-weW.js";
import { A as contentEntries, C as assertCanManageTaxonomy, D as resolveRequiredContentCapabilities, E as authorizeContentAction, I as contentSchema, L as contentStatusDefinitions, M as contentLifecycleJournal, N as contentMigrationCheckpoints, O as assertActiveContentTransaction, P as contentMigrationWriteJournal, R as contentTombstones, S as ContentAuthzError, T as assertCanPublish, _ as ContentSanitizationError, b as normalizeContentInput, c as contentTaxonomyMigrationSql, j as contentImportJournal, k as contentDefinitionVersions, v as ContentValidationError, w as assertCanModify, x as ContentAuthorizationError, z as contentTypeDefinitions } from "./taxonomy-CmOYaoVZ.js";
import { contentRevisionsMigrationSql } from "./revisions.js";
import * as drizzle from "drizzle-orm";
import { and, asc, desc, eq, exists, gte, inArray, isNull, lte, notInArray, or, sql } from "drizzle-orm";
import { d1AssertPreviousChanges } from "@platform-modules/db/sqlite/d1";
//#region src/search.ts
/** Additive, idempotent-guarded FTS DDL — host splits on `;` and runs per-statement (neon-http). */
const contentSearchMigrationSql = (table = "content_entries", config = "english") => `
ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS search_vector tsvector
  GENERATED ALWAYS AS (
    setweight(to_tsvector('${config}', coalesce(title, '')), 'A') ||
    setweight(to_tsvector('${config}', regexp_replace(coalesce(body, ''), '<[^>]*>', ' ', 'g')), 'B')
  ) STORED;
CREATE INDEX IF NOT EXISTS content_entries_search_idx ON ${table} USING GIN (search_vector);
`.trim();
function regconfigLiteral(config) {
	return drizzle.sql.raw(`'${config.replace(/'/g, "''")}'`);
}
function createContentSearchProvider(opts) {
	const cfgLit = regconfigLiteral(opts?.textSearchConfig ?? "english");
	const types = opts?.types;
	return async (query, ctx, window) => {
		if (!query.trim()) return {
			hits: [],
			total: 0
		};
		const tsq = drizzle.sql`to_tsquery(${cfgLit}, ${query})`;
		const match = drizzle.sql`search_vector @@ ${tsq}`;
		const vis = visibilityPredicate(ctx.viewer);
		const conditions = [match];
		if (vis) conditions.unshift(vis);
		if (types && types.length > 0) conditions.push(drizzle.inArray(contentEntries.type, types));
		const where = conditions.length === 1 ? conditions[0] : drizzle.and(...conditions);
		const rows = normalizeExecuteRows(await ctx.db.execute(drizzle.sql`
      SELECT
        id,
        title,
        ts_rank(search_vector, ${tsq}) AS rank,
        ts_headline(
          ${cfgLit},
          regexp_replace(body, '<[^>]*>', ' ', 'g'),
          ${tsq},
          'StartSel=«,StopSel=»,MaxWords=30,MinWords=15,ShortWord=3'
        ) AS snippet_html,
        count(*) OVER()::int AS total
      FROM content_entries
      WHERE ${where}
      ORDER BY rank DESC, published_at DESC NULLS LAST
      LIMIT ${window.limit} OFFSET ${window.offset}
    `));
		const total = rows[0]?.total ?? 0;
		return {
			hits: rows.map((row) => ({
				entityType: "content",
				id: row.id,
				rank: row.rank,
				title: row.title,
				snippetHtml: row.snippet_html
			})),
			total
		};
	};
}
function normalizeExecuteRows(result) {
	if (Array.isArray(result)) return result;
	return result?.rows ?? [];
}
/** Canonical T5 search adapter: caller supplies the already-authorized SQL predicate. */
function createContentPostgresSearchAdapter(opts) {
	const cfgLit = regconfigLiteral(opts?.textSearchConfig ?? "english");
	return { async searchPage(db, input, authorizedPredicate) {
		const query = input.search?.trim();
		if (!query) return {
			ids: [],
			totalItems: 0,
			rank: {}
		};
		const page = input.page ?? 1;
		const pageSize = input.pageSize ?? 20;
		const offset = (page - 1) * pageSize;
		const tsq = drizzle.sql`websearch_to_tsquery(${cfgLit}, ${query})`;
		const vector = drizzle.sql`(
        setweight(to_tsvector(${cfgLit}, coalesce(title, '')), 'A') ||
        setweight(to_tsvector(${cfgLit}, coalesce(slug, '')), 'A') ||
        setweight(to_tsvector(${cfgLit}, coalesce(excerpt, '')), 'B') ||
        setweight(to_tsvector(${cfgLit}, regexp_replace(coalesce(body, ''), '<[^>]*>', ' ', 'g')), 'C')
      )`;
		const rows = await db.execute(drizzle.sql`
        WITH matches AS (
          SELECT id, ts_rank(${vector}, ${tsq}) AS rank
          FROM content_entries
          WHERE ${authorizedPredicate} AND ${vector} @@ ${tsq}
        ), page_rows AS (
          SELECT id, rank FROM matches
          ORDER BY rank DESC, id ASC
          LIMIT ${pageSize} OFFSET ${offset}
        ), counted AS (
          SELECT COUNT(*)::int AS total FROM matches
        )
        SELECT page_rows.id, page_rows.rank, counted.total
        FROM counted LEFT JOIN page_rows ON TRUE
        ORDER BY page_rows.rank DESC NULLS LAST, page_rows.id ASC
      `);
		const totalItems = Number(rows[0]?.total ?? 0);
		const ids = [];
		const rank = Object.create(null);
		for (const row of rows) {
			if (row.id === null) continue;
			ids.push(row.id);
			rank[row.id] = Number(row.rank ?? 0);
		}
		return {
			ids,
			totalItems,
			rank
		};
	} };
}
//#endregion
//#region src/migrations/content-model.ts
const CONTENT_MODEL_MIGRATION_ID = "content-model-v1";
const STANDARD_STATUSES = /* @__PURE__ */ new Set([
	"draft",
	"pending",
	"scheduled",
	"published",
	"trashed"
]);
function canonicalJson(value) {
	if (value === null) return "null";
	if (value instanceof Date) return JSON.stringify(value.toISOString());
	if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
	if (typeof value === "number") {
		if (!Number.isFinite(value)) throw new TypeError("canonical migration data cannot contain non-finite numbers");
		return JSON.stringify(value);
	}
	if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
	if (typeof value === "object" && value !== null) {
		const record = value;
		return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
	}
	throw new TypeError(`unsupported canonical migration value: ${typeof value}`);
}
async function canonicalContentMigrationHash(value) {
	const bytes = new TextEncoder().encode(canonicalJson(value));
	const digest = await crypto.subtle.digest("SHA-256", bytes);
	return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}
function plainEntry(entry) {
	return {
		id: entry.id,
		slug: entry.slug,
		type: entry.type,
		title: entry.title,
		body: entry.body,
		status: entry.status,
		visibility: entry.visibility,
		publishedAt: entry.publishedAt?.toISOString() ?? null,
		author: entry.author,
		createdAt: entry.createdAt.toISOString(),
		updatedAt: entry.updatedAt.toISOString()
	};
}
async function snapshotFlatContentCorpus(entries) {
	const sorted = [...entries].sort((left, right) => left.id.localeCompare(right.id));
	return Object.freeze({
		count: sorted.length,
		hash: await canonicalContentMigrationHash(sorted.map(plainEntry)),
		lastKey: sorted.at(-1)?.id ?? null
	});
}
function humanizeKey(key) {
	const label = key.replaceAll(/[-_]+/g, " ").trim();
	if (!label) return key;
	return label.replace(/\b\p{L}/gu, (letter) => letter.toUpperCase());
}
function legacyLabels(key) {
	const singular = humanizeKey(key);
	const plural = singular.endsWith("s") ? singular : `${singular}s`;
	return {
		name: plural,
		singularName: singular,
		menuName: plural,
		nameAdminBar: singular,
		addNew: "Add New",
		addNewItem: `Add New ${singular}`,
		editItem: `Edit ${singular}`,
		newItem: `New ${singular}`,
		viewItem: `View ${singular}`,
		viewItems: `View ${plural}`,
		searchItems: `Search ${plural}`,
		notFound: `No ${plural.toLowerCase()} found`,
		notFoundInTrash: `No ${plural.toLowerCase()} found in trash`,
		parentItemColon: `Parent ${singular}:`,
		allItems: `All ${plural}`,
		archives: `${singular} Archives`,
		attributes: `${singular} Attributes`,
		insertIntoItem: `Insert into ${singular.toLowerCase()}`,
		uploadedToThisItem: `Uploaded to this ${singular.toLowerCase()}`,
		featuredImage: "Featured image",
		setFeaturedImage: "Set featured image",
		removeFeaturedImage: "Remove featured image",
		useFeaturedImage: "Use as featured image",
		filterItemsList: `Filter ${plural.toLowerCase()} list`,
		filterByDate: `Filter ${plural.toLowerCase()} by date`,
		itemsListNavigation: `${plural} list navigation`,
		itemsList: `${plural} list`,
		itemPublished: `${singular} published`,
		itemPublishedPrivately: `${singular} published privately`,
		itemRevertedToDraft: `${singular} reverted to draft`,
		itemScheduled: `${singular} scheduled`,
		itemUpdated: `${singular} updated`,
		itemLink: `${singular} link`,
		itemLinkDescription: `A link to a ${singular.toLowerCase()}`
	};
}
function legacyCapabilities(key) {
	const prefix = `content.${key}`;
	return {
		manageType: `${prefix}.manageType`,
		migrateAll: `${prefix}.migrateAll`,
		manageTerms: `${prefix}.manageTerms`,
		create: `${prefix}.create`,
		read: `${prefix}.read`,
		readPrivate: `${prefix}.readPrivate`,
		readProtected: `${prefix}.readProtected`,
		editOwn: `${prefix}.editOwn`,
		editOthers: `${prefix}.editOthers`,
		editPrivate: `${prefix}.editPrivate`,
		editPublished: `${prefix}.editPublished`,
		publish: `${prefix}.publish`,
		deleteOwn: `${prefix}.deleteOwn`,
		deleteOthers: `${prefix}.deleteOthers`,
		deletePrivate: `${prefix}.deletePrivate`,
		deletePublished: `${prefix}.deletePublished`
	};
}
function legacyTypeDefinition(key, statusKeys, defaultTemplateKey) {
	return {
		key,
		labels: legacyLabels(key),
		public: true,
		hierarchical: false,
		excludeFromSearch: false,
		publiclyQueryable: true,
		showUi: true,
		showInMenu: true,
		showInNavMenus: true,
		showInAdminBar: true,
		capabilities: legacyCapabilities(key),
		supports: [
			"title",
			"editor",
			"author",
			"revisions"
		],
		taxonomies: [],
		hasArchive: false,
		rewrite: false,
		queryVariable: false,
		canExport: true,
		deleteWithAuthor: null,
		rest: false,
		defaultTemplateKey,
		statusKeys,
		active: true
	};
}
function legacyStatusDefinition(key) {
	const published = key === "published";
	const scheduled = key === "scheduled";
	return {
		key,
		label: humanizeKey(key),
		published,
		internal: key === "trashed",
		excludeFromSearch: !published,
		publiclyQueryable: published,
		showInAdminAll: true,
		showInAdminStatusFilter: true,
		dateLabel: published ? "published" : scheduled ? "scheduled" : key === "draft" || key === "pending" ? "lastModified" : "created",
		transitionInput: scheduled ? "scheduleAt" : published ? "publishedAt" : "none"
	};
}
async function materializeDefinitions(entries, defaultTemplateKey) {
	const statusKeys = [...new Set(entries.map((entry) => entry.status))].sort();
	const typeKeys = [...new Set(entries.map((entry) => entry.type))].sort();
	const allStatusKeys = [.../* @__PURE__ */ new Set([...STANDARD_STATUSES, ...statusKeys])].sort();
	const definitions = [];
	for (const key of typeKeys) {
		const definition = legacyTypeDefinition(key, allStatusKeys, defaultTemplateKey);
		definitions.push({
			kind: "type",
			key,
			origin: "db",
			revision: 1,
			canonicalHash: await canonicalContentMigrationHash(definition),
			definition
		});
	}
	for (const key of allStatusKeys) {
		const definition = legacyStatusDefinition(key);
		definitions.push({
			kind: "status",
			key,
			origin: STANDARD_STATUSES.has(key) ? "code" : "db",
			revision: 1,
			canonicalHash: await canonicalContentMigrationHash(definition),
			definition
		});
	}
	return Object.freeze(definitions.sort((left, right) => left.kind.localeCompare(right.kind) || left.key.localeCompare(right.key)));
}
async function planContentModelMigration(entries, options) {
	const defaultTemplateKey = options.defaultTemplateKey.trim();
	if (!defaultTemplateKey || defaultTemplateKey.length > 128) throw new TypeError("content migration defaultTemplateKey must be a bounded non-empty key");
	const source = await snapshotFlatContentCorpus(entries);
	const definitions = await materializeDefinitions(entries, defaultTemplateKey);
	const entriesById = [...entries].sort((left, right) => left.id.localeCompare(right.id));
	const projected = Object.freeze(entriesById.map((entry) => Object.freeze({
		...entry,
		parentId: null,
		menuOrder: 0,
		templateKey: null,
		excerpt: "",
		featuredMedia: null,
		commentStatus: "open",
		pingStatus: "open",
		sticky: false,
		format: null,
		deletedAt: null,
		lastEditedBy: entry.author,
		typeDefinitionRevision: 1,
		statusDefinitionRevision: 1
	})));
	const target = Object.freeze({
		count: projected.length,
		hash: await canonicalContentMigrationHash(projected.map((entry) => ({
			...plainEntry(entry),
			parentId: entry.parentId,
			menuOrder: entry.menuOrder,
			templateKey: entry.templateKey,
			excerpt: entry.excerpt,
			featuredMedia: entry.featuredMedia,
			commentStatus: entry.commentStatus,
			pingStatus: entry.pingStatus,
			sticky: entry.sticky,
			format: entry.format,
			deletedAt: entry.deletedAt,
			lastEditedBy: entry.lastEditedBy,
			typeDefinitionRevision: entry.typeDefinitionRevision,
			statusDefinitionRevision: entry.statusDefinitionRevision
		}))),
		lastKey: projected.at(-1)?.id ?? null
	});
	return Object.freeze({
		migrationId: CONTENT_MODEL_MIGRATION_ID,
		source,
		target,
		definitions,
		entries: projected
	});
}
const PG_DEFINITION_TABLE = (table) => `
CREATE TABLE IF NOT EXISTS ${table} (
  key text PRIMARY KEY,
  origin text NOT NULL,
  version integer NOT NULL DEFAULT 1 CHECK (version > 0),
  active boolean NOT NULL DEFAULT true,
  current_revision integer NOT NULL DEFAULT 1 CHECK (current_revision > 0),
  canonical_hash text NOT NULL,
  definition jsonb NOT NULL,
  shadowed_db_version integer,
  host_session_id text,
  created_at timestamptz(3) NOT NULL DEFAULT NOW(),
  updated_at timestamptz(3) NOT NULL DEFAULT NOW()
)`;
const D1_DEFINITION_TABLE = (table) => `
CREATE TABLE IF NOT EXISTS ${table} (
  key TEXT PRIMARY KEY,
  origin TEXT NOT NULL,
  version INTEGER NOT NULL DEFAULT 1 CHECK (version > 0),
  active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0,1)),
  current_revision INTEGER NOT NULL DEFAULT 1 CHECK (current_revision > 0),
  canonical_hash TEXT NOT NULL,
  definition TEXT NOT NULL,
  shadowed_db_version INTEGER,
  host_session_id TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
)`;
function postgresForwardSql() {
	return Object.freeze([
		PG_DEFINITION_TABLE("content_type_definitions"),
		PG_DEFINITION_TABLE("content_status_definitions"),
		`CREATE TABLE IF NOT EXISTS content_definition_versions (
      definition_kind text NOT NULL,
      definition_key text NOT NULL,
      revision integer NOT NULL CHECK (revision > 0),
      canonical_hash text NOT NULL,
      definition jsonb NOT NULL,
      origin text NOT NULL,
      host_session_id text,
      created_at timestamptz(3) NOT NULL DEFAULT NOW(),
      PRIMARY KEY (definition_kind, definition_key, revision)
    )`,
		`CREATE TABLE IF NOT EXISTS content_password_credentials (
      entry_id uuid PRIMARY KEY REFERENCES content_entries(id) ON DELETE CASCADE,
      credential_version text NOT NULL,
      credential text NOT NULL,
      created_at timestamptz(3) NOT NULL DEFAULT NOW(),
      updated_at timestamptz(3) NOT NULL DEFAULT NOW()
    )`,
		`CREATE TABLE IF NOT EXISTS content_lifecycle_journal (
      id uuid PRIMARY KEY DEFAULT gen_random_uuid(), operation_id text NOT NULL UNIQUE, kind text NOT NULL,
      entry_id uuid, definition_kind text, definition_key text, definition_revision integer, state text NOT NULL,
      payload jsonb, created_at timestamptz(3) NOT NULL DEFAULT NOW(), updated_at timestamptz(3) NOT NULL DEFAULT NOW()
    )`,
		`CREATE TABLE IF NOT EXISTS content_import_journal (
      import_id uuid PRIMARY KEY DEFAULT gen_random_uuid(), host_session_id text NOT NULL UNIQUE,
      manifest_hash text NOT NULL, plan_hash text NOT NULL, state text NOT NULL, version integer NOT NULL DEFAULT 0 CHECK (version >= 0),
      next_section text, next_offset integer, high_water_mark text, payload jsonb,
      created_at timestamptz(3) NOT NULL DEFAULT NOW(), updated_at timestamptz(3) NOT NULL DEFAULT NOW()
    )`,
		`CREATE TABLE IF NOT EXISTS content_tombstones (
      id uuid PRIMARY KEY DEFAULT gen_random_uuid(), entry_id uuid NOT NULL, operation_id text NOT NULL,
      type_key text NOT NULL, type_definition_revision integer NOT NULL, status_key text NOT NULL,
      status_definition_revision integer NOT NULL, snapshot jsonb NOT NULL,
      deleted_at timestamptz(3) NOT NULL DEFAULT NOW(), UNIQUE(operation_id, entry_id)
    )`,
		`CREATE TABLE IF NOT EXISTS content_migration_checkpoints (
      migration_id text NOT NULL, adapter text NOT NULL, phase text NOT NULL, batch_ordinal integer NOT NULL DEFAULT 0,
      version integer NOT NULL DEFAULT 0, last_key text, source_count integer NOT NULL, target_count integer,
      source_hash text NOT NULL, target_hash text, high_water_mark text,
      created_at timestamptz(3) NOT NULL DEFAULT NOW(), updated_at timestamptz(3) NOT NULL DEFAULT NOW(),
      PRIMARY KEY (migration_id, adapter, phase, batch_ordinal)
    )`,
		`CREATE TABLE IF NOT EXISTS content_migration_write_journal (
      seq bigserial PRIMARY KEY, migration_id text NOT NULL, operation text NOT NULL, entry_id text NOT NULL,
      before_value jsonb, after_value jsonb, created_at timestamptz(3) NOT NULL DEFAULT NOW()
    )`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS parent_id uuid`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS menu_order integer NOT NULL DEFAULT 0`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS template_key text`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS excerpt text NOT NULL DEFAULT ''`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS featured_media jsonb`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS comment_status text NOT NULL DEFAULT 'open' CHECK (comment_status IN ('open','closed'))`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS ping_status text NOT NULL DEFAULT 'open' CHECK (ping_status IN ('open','closed'))`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS sticky boolean NOT NULL DEFAULT false`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS format text`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS deleted_at timestamptz(3)`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS last_edited_by text`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS type_definition_revision integer NOT NULL DEFAULT 1`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS status_definition_revision integer NOT NULL DEFAULT 1`,
		`ALTER TABLE content_entries ADD COLUMN IF NOT EXISTS host_session_id text`,
		`UPDATE content_entries SET last_edited_by = author WHERE last_edited_by IS NULL`,
		`ALTER TABLE content_entries ALTER COLUMN last_edited_by SET NOT NULL`,
		`CREATE INDEX IF NOT EXISTS content_entries_parent_order_idx ON content_entries (type, parent_id, menu_order, id)`,
		`CREATE INDEX IF NOT EXISTS content_entries_deleted_idx ON content_entries (deleted_at)`,
		`CREATE INDEX IF NOT EXISTS content_entries_type_definition_idx ON content_entries (type, type_definition_revision)`,
		`CREATE INDEX IF NOT EXISTS content_entries_status_definition_idx ON content_entries (status, status_definition_revision)`,
		`CREATE OR REPLACE FUNCTION content_model_migration_journal_fn() RETURNS trigger AS $$
      BEGIN
        INSERT INTO content_migration_write_journal (migration_id, operation, entry_id, before_value, after_value)
        VALUES (
          '${CONTENT_MODEL_MIGRATION_ID}',
          lower(TG_OP),
          CASE WHEN TG_OP = 'DELETE' THEN OLD.id::text ELSE NEW.id::text END,
          CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE to_jsonb(OLD) END,
          CASE WHEN TG_OP = 'DELETE' THEN NULL ELSE to_jsonb(NEW) END
        );
        RETURN NULL;
      END;
    $$ LANGUAGE plpgsql`,
		`DROP TRIGGER IF EXISTS content_model_migration_write_trigger ON content_entries`,
		`CREATE TRIGGER content_model_migration_write_trigger AFTER INSERT OR UPDATE OR DELETE ON content_entries
      FOR EACH ROW EXECUTE FUNCTION content_model_migration_journal_fn()`
	]);
}
function d1JsonRow(prefix) {
	return `json_object(
    'id', ${prefix}.id, 'slug', ${prefix}.slug, 'type', ${prefix}.type, 'title', ${prefix}.title,
    'body', ${prefix}.body, 'status', ${prefix}.status, 'visibility', ${prefix}.visibility,
    'published_at', ${prefix}.published_at, 'author', ${prefix}.author,
    'created_at', ${prefix}.created_at, 'updated_at', ${prefix}.updated_at
  )`;
}
function d1ForwardSql() {
	return Object.freeze([
		D1_DEFINITION_TABLE("content_type_definitions"),
		D1_DEFINITION_TABLE("content_status_definitions"),
		`CREATE TABLE IF NOT EXISTS content_definition_versions (
      definition_kind TEXT NOT NULL, definition_key TEXT NOT NULL, revision INTEGER NOT NULL CHECK (revision > 0),
      canonical_hash TEXT NOT NULL, definition TEXT NOT NULL, origin TEXT NOT NULL, host_session_id TEXT,
      created_at TEXT NOT NULL, PRIMARY KEY (definition_kind, definition_key, revision)
    )`,
		`CREATE TABLE IF NOT EXISTS content_password_credentials (
      entry_id TEXT PRIMARY KEY REFERENCES content_entries(id) ON DELETE CASCADE,
      credential_version TEXT NOT NULL, credential TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
    )`,
		`CREATE TABLE IF NOT EXISTS content_lifecycle_journal (
      id TEXT PRIMARY KEY, operation_id TEXT NOT NULL UNIQUE, kind TEXT NOT NULL, entry_id TEXT,
      definition_kind TEXT, definition_key TEXT, definition_revision INTEGER, state TEXT NOT NULL,
      payload TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
    )`,
		`CREATE TABLE IF NOT EXISTS content_import_journal (
      import_id TEXT PRIMARY KEY, host_session_id TEXT NOT NULL UNIQUE, manifest_hash TEXT NOT NULL,
      plan_hash TEXT NOT NULL, state TEXT NOT NULL, version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
      next_section TEXT, next_offset INTEGER, high_water_mark TEXT, payload TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
    )`,
		`CREATE TABLE IF NOT EXISTS content_tombstones (
      id TEXT PRIMARY KEY, entry_id TEXT NOT NULL, operation_id TEXT NOT NULL, type_key TEXT NOT NULL,
      type_definition_revision INTEGER NOT NULL, status_key TEXT NOT NULL, status_definition_revision INTEGER NOT NULL,
      snapshot TEXT NOT NULL, deleted_at TEXT NOT NULL, UNIQUE(operation_id, entry_id)
    )`,
		`CREATE TABLE IF NOT EXISTS content_migration_checkpoints (
      migration_id TEXT NOT NULL, adapter TEXT NOT NULL, phase TEXT NOT NULL, batch_ordinal INTEGER NOT NULL DEFAULT 0,
      version INTEGER NOT NULL DEFAULT 0, last_key TEXT, source_count INTEGER NOT NULL, target_count INTEGER,
      source_hash TEXT NOT NULL, target_hash TEXT, high_water_mark TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL,
      PRIMARY KEY (migration_id, adapter, phase, batch_ordinal)
    )`,
		`CREATE TABLE IF NOT EXISTS content_migration_write_journal (
      seq INTEGER PRIMARY KEY AUTOINCREMENT, migration_id TEXT NOT NULL, operation TEXT NOT NULL, entry_id TEXT NOT NULL,
      before_value TEXT, after_value TEXT, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
    )`,
		`ALTER TABLE content_entries ADD COLUMN parent_id TEXT`,
		`ALTER TABLE content_entries ADD COLUMN menu_order INTEGER NOT NULL DEFAULT 0`,
		`ALTER TABLE content_entries ADD COLUMN template_key TEXT`,
		`ALTER TABLE content_entries ADD COLUMN excerpt TEXT NOT NULL DEFAULT ''`,
		`ALTER TABLE content_entries ADD COLUMN featured_media TEXT`,
		`ALTER TABLE content_entries ADD COLUMN comment_status TEXT NOT NULL DEFAULT 'open' CHECK (comment_status IN ('open','closed'))`,
		`ALTER TABLE content_entries ADD COLUMN ping_status TEXT NOT NULL DEFAULT 'open' CHECK (ping_status IN ('open','closed'))`,
		`ALTER TABLE content_entries ADD COLUMN sticky INTEGER NOT NULL DEFAULT 0 CHECK (sticky IN (0,1))`,
		`ALTER TABLE content_entries ADD COLUMN format TEXT`,
		`ALTER TABLE content_entries ADD COLUMN deleted_at TEXT`,
		`ALTER TABLE content_entries ADD COLUMN last_edited_by TEXT`,
		`ALTER TABLE content_entries ADD COLUMN type_definition_revision INTEGER NOT NULL DEFAULT 1`,
		`ALTER TABLE content_entries ADD COLUMN status_definition_revision INTEGER NOT NULL DEFAULT 1`,
		`ALTER TABLE content_entries ADD COLUMN host_session_id TEXT`,
		`UPDATE content_entries SET last_edited_by = author WHERE last_edited_by IS NULL`,
		`CREATE INDEX IF NOT EXISTS content_entries_parent_order_idx ON content_entries (type, parent_id, menu_order, id)`,
		`CREATE INDEX IF NOT EXISTS content_entries_deleted_idx ON content_entries (deleted_at)`,
		`CREATE INDEX IF NOT EXISTS content_entries_type_definition_idx ON content_entries (type, type_definition_revision)`,
		`CREATE INDEX IF NOT EXISTS content_entries_status_definition_idx ON content_entries (status, status_definition_revision)`,
		`DROP TRIGGER IF EXISTS content_model_migration_write_insert`,
		`DROP TRIGGER IF EXISTS content_model_migration_write_update`,
		`DROP TRIGGER IF EXISTS content_model_migration_write_delete`,
		`CREATE TRIGGER content_model_migration_write_insert AFTER INSERT ON content_entries BEGIN
      INSERT INTO content_migration_write_journal (migration_id, operation, entry_id, before_value, after_value)
      VALUES ('${CONTENT_MODEL_MIGRATION_ID}', 'insert', NEW.id, NULL, ${d1JsonRow("NEW")});
    END`,
		`CREATE TRIGGER content_model_migration_write_update AFTER UPDATE ON content_entries BEGIN
      INSERT INTO content_migration_write_journal (migration_id, operation, entry_id, before_value, after_value)
      VALUES ('${CONTENT_MODEL_MIGRATION_ID}', 'update', NEW.id, ${d1JsonRow("OLD")}, ${d1JsonRow("NEW")});
    END`,
		`CREATE TRIGGER content_model_migration_write_delete AFTER DELETE ON content_entries BEGIN
      INSERT INTO content_migration_write_journal (migration_id, operation, entry_id, before_value, after_value)
      VALUES ('${CONTENT_MODEL_MIGRATION_ID}', 'delete', OLD.id, ${d1JsonRow("OLD")}, NULL);
    END`
	]);
}
function contentModelForwardMigrationSql(adapter) {
	return adapter === "postgres" ? postgresForwardSql() : d1ForwardSql();
}
function contentModelReverseMigrationSql(adapter) {
	return Object.freeze([
		...adapter === "postgres" ? [`DROP TRIGGER IF EXISTS content_model_migration_write_trigger ON content_entries`, `DROP FUNCTION IF EXISTS content_model_migration_journal_fn()`] : [
			`DROP TRIGGER IF EXISTS content_model_migration_write_insert`,
			`DROP TRIGGER IF EXISTS content_model_migration_write_update`,
			`DROP TRIGGER IF EXISTS content_model_migration_write_delete`
		],
		`DROP INDEX IF EXISTS content_entries_parent_order_idx`,
		`DROP INDEX IF EXISTS content_entries_deleted_idx`,
		`DROP INDEX IF EXISTS content_entries_type_definition_idx`,
		`DROP INDEX IF EXISTS content_entries_status_definition_idx`,
		`ALTER TABLE content_entries DROP COLUMN host_session_id`,
		`ALTER TABLE content_entries DROP COLUMN status_definition_revision`,
		`ALTER TABLE content_entries DROP COLUMN type_definition_revision`,
		`ALTER TABLE content_entries DROP COLUMN last_edited_by`,
		`ALTER TABLE content_entries DROP COLUMN deleted_at`,
		`ALTER TABLE content_entries DROP COLUMN format`,
		`ALTER TABLE content_entries DROP COLUMN sticky`,
		`ALTER TABLE content_entries DROP COLUMN ping_status`,
		`ALTER TABLE content_entries DROP COLUMN comment_status`,
		`ALTER TABLE content_entries DROP COLUMN featured_media`,
		`ALTER TABLE content_entries DROP COLUMN excerpt`,
		`ALTER TABLE content_entries DROP COLUMN template_key`,
		`ALTER TABLE content_entries DROP COLUMN menu_order`,
		`ALTER TABLE content_entries DROP COLUMN parent_id`
	]);
}
//#endregion
//#region src/registry.ts
var ContentDefinitionError = class extends Error {
	code;
	detail;
	field;
	name = "ContentDefinitionError";
	constructor(code, detail, field) {
		super(`content definition ${code}: ${detail}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function isContentDefinitionError(error) {
	return error instanceof ContentDefinitionError;
}
const TYPE_KEY$1 = /^[a-z][a-z0-9_-]{0,63}$/;
const CAPABILITY_KEYS = [
	"manageType",
	"migrateAll",
	"manageTerms",
	"create",
	"read",
	"readPrivate",
	"readProtected",
	"editOwn",
	"editOthers",
	"editPrivate",
	"editPublished",
	"publish",
	"deleteOwn",
	"deleteOthers",
	"deletePrivate",
	"deletePublished"
];
const LABEL_KEYS = [
	"name",
	"singularName",
	"menuName",
	"nameAdminBar",
	"addNew",
	"addNewItem",
	"editItem",
	"newItem",
	"viewItem",
	"viewItems",
	"searchItems",
	"notFound",
	"notFoundInTrash",
	"parentItemColon",
	"allItems",
	"archives",
	"attributes",
	"insertIntoItem",
	"uploadedToThisItem",
	"featuredImage",
	"setFeaturedImage",
	"removeFeaturedImage",
	"useFeaturedImage",
	"filterItemsList",
	"filterByDate",
	"itemsListNavigation",
	"itemsList",
	"itemPublished",
	"itemPublishedPrivately",
	"itemRevertedToDraft",
	"itemScheduled",
	"itemUpdated",
	"itemLink",
	"itemLinkDescription"
];
const FEATURE_SET = /* @__PURE__ */ new Set([
	"title",
	"editor",
	"blocks",
	"author",
	"featuredImage",
	"excerpt",
	"comments",
	"revisions",
	"pageAttributes",
	"postFormats",
	"customFields",
	"trackbacks"
]);
function invalid$1(detail, field) {
	throw new ContentDefinitionError("invalid-definition", detail, field);
}
function bounded$2(value, field, max = 256) {
	if (typeof value !== "string") invalid$1("must be a string", field);
	const normalized = value.trim();
	if (normalized.length === 0 || normalized.length > max) invalid$1(`must contain 1..${max} characters`, field);
	return normalized;
}
function finitePosition(value, field) {
	if (value === void 0) return void 0;
	if (typeof value !== "number" || !Number.isSafeInteger(value)) invalid$1("must be a safe integer", field);
	return value;
}
function uniqueSorted(values, field) {
	const normalized = values.map((value, index) => bounded$2(value, `${field}[${index}]`, 128));
	return Object.freeze([...new Set(normalized)].sort());
}
function normalizeTemplateValue(value, path, depth = 0) {
	if (depth > 32) invalid$1("template nesting exceeds 32 levels", path);
	if (value === null || typeof value === "string" || typeof value === "boolean") return value;
	if (typeof value === "number") {
		if (!Number.isFinite(value)) invalid$1("template numbers must be finite", path);
		return value;
	}
	if (Array.isArray(value)) return Object.freeze(value.map((item, index) => normalizeTemplateValue(item, `${path}[${index}]`, depth + 1)));
	if (typeof value === "object") {
		const out = {};
		for (const key of Object.keys(value).sort()) out[key] = normalizeTemplateValue(value[key], `${path}.${key}`, depth + 1);
		return Object.freeze(out);
	}
	invalid$1("unsupported template value", path);
}
function normalizeTemplateBlock(block, path, depth = 0) {
	if (depth > 32) invalid$1("template nesting exceeds 32 levels", path);
	const type = bounded$2(block.type, `${path}.type`, 128);
	const attributes = block.attributes ? Object.freeze(Object.fromEntries(Object.keys(block.attributes).sort().map((key) => [key, normalizeTemplateValue(block.attributes[key], `${path}.attributes.${key}`)]))) : void 0;
	const children = block.children ? Object.freeze(block.children.map((child, index) => normalizeTemplateBlock(child, `${path}.children[${index}]`, depth + 1))) : void 0;
	return Object.freeze({
		type,
		...attributes ? { attributes } : {},
		...children ? { children } : {}
	});
}
function normalizeContentTypeDefinition(definition) {
	if (!TYPE_KEY$1.test(definition.key)) invalid$1("must be a stable lowercase machine key", "key");
	const labels = Object.fromEntries(LABEL_KEYS.map((key) => [key, bounded$2(definition.labels?.[key], `labels.${key}`, 256)]));
	const capabilities = Object.fromEntries(CAPABILITY_KEYS.map((key) => [key, bounded$2(definition.capabilities?.[key], `capabilities.${key}`, 128)]));
	if (!Array.isArray(definition.supports)) invalid$1("must be an array", "supports");
	for (const [index, feature] of definition.supports.entries()) if (!FEATURE_SET.has(feature)) invalid$1(`unknown feature ${String(feature)}`, `supports[${index}]`);
	const supports = Object.freeze([...new Set(definition.supports)].sort());
	const taxonomies = uniqueSorted(definition.taxonomies ?? [], "taxonomies");
	const statusKeys = definition.statusKeys === void 0 ? void 0 : uniqueSorted(definition.statusKeys, "statusKeys");
	const defaultTemplateKey = bounded$2(definition.defaultTemplateKey, "defaultTemplateKey", 128);
	if (definition.hasArchive !== false && !definition.publiclyQueryable) invalid$1("archive requires publiclyQueryable", "hasArchive");
	if (definition.rewrite !== false && !definition.publiclyQueryable) invalid$1("rewrite requires publiclyQueryable", "rewrite");
	if (definition.template !== void 0 && !supports.includes("blocks")) invalid$1("template requires blocks support", "template");
	if (!definition.hierarchical && supports.includes("pageAttributes")) {}
	if (definition.rest !== false) {
		if (definition.rest.revisions && !supports.includes("revisions")) invalid$1("REST revisions require revisions support", "rest.revisions");
		if (definition.rest.autosaves && (!supports.includes("revisions") || !supports.includes("editor"))) invalid$1("REST autosaves require revisions and editor support", "rest.autosaves");
	}
	const rewrite = definition.rewrite === false ? false : Object.freeze({
		slug: bounded$2(definition.rewrite.slug, "rewrite.slug", 128),
		...definition.rewrite.withFront === void 0 ? {} : { withFront: Boolean(definition.rewrite.withFront) },
		...definition.rewrite.feeds === void 0 ? {} : { feeds: Boolean(definition.rewrite.feeds) },
		...definition.rewrite.pages === void 0 ? {} : { pages: Boolean(definition.rewrite.pages) }
	});
	const rest = definition.rest === false ? false : Object.freeze({
		base: bounded$2(definition.rest.base, "rest.base", 128),
		namespace: bounded$2(definition.rest.namespace, "rest.namespace", 128),
		revisions: Boolean(definition.rest.revisions),
		autosaves: Boolean(definition.rest.autosaves)
	});
	const template = definition.template === void 0 ? void 0 : Object.freeze(definition.template.map((block, index) => normalizeTemplateBlock(block, `template[${index}]`)));
	return Object.freeze({
		key: definition.key,
		labels: Object.freeze(labels),
		...definition.description === void 0 ? {} : { description: bounded$2(definition.description, "description", 4096) },
		public: Boolean(definition.public),
		hierarchical: Boolean(definition.hierarchical),
		excludeFromSearch: Boolean(definition.excludeFromSearch),
		publiclyQueryable: Boolean(definition.publiclyQueryable),
		showUi: Boolean(definition.showUi),
		showInMenu: typeof definition.showInMenu === "string" ? bounded$2(definition.showInMenu, "showInMenu", 128) : Boolean(definition.showInMenu),
		showInNavMenus: Boolean(definition.showInNavMenus),
		showInAdminBar: Boolean(definition.showInAdminBar),
		...finitePosition(definition.menuPosition, "menuPosition") === void 0 ? {} : { menuPosition: definition.menuPosition },
		...definition.menuIcon === void 0 ? {} : { menuIcon: bounded$2(definition.menuIcon, "menuIcon", 256) },
		capabilities: Object.freeze(capabilities),
		supports,
		taxonomies,
		hasArchive: typeof definition.hasArchive === "string" ? bounded$2(definition.hasArchive, "hasArchive", 128) : Boolean(definition.hasArchive),
		rewrite,
		queryVariable: typeof definition.queryVariable === "string" ? bounded$2(definition.queryVariable, "queryVariable", 128) : false,
		canExport: Boolean(definition.canExport),
		deleteWithAuthor: definition.deleteWithAuthor === null ? null : Boolean(definition.deleteWithAuthor),
		rest,
		defaultTemplateKey,
		...template ? { template } : {},
		...definition.templateLock === void 0 ? {} : { templateLock: definition.templateLock },
		...statusKeys ? { statusKeys } : {},
		active: Boolean(definition.active),
		...finitePosition(definition.position, "position") === void 0 ? {} : { position: definition.position }
	});
}
function defineContentType(definition) {
	return Object.freeze({
		...normalizeContentTypeDefinition(definition),
		origin: "code"
	});
}
function parseDefinition(value) {
	return normalizeContentTypeDefinition(typeof value === "string" ? JSON.parse(value) : value);
}
function bool$4(value) {
	return value === true || value === 1 || value === "1";
}
async function definitionRows(db, kind, key) {
	const table = kind === "type" ? "content_type_definitions" : "content_status_definitions";
	const where = key === void 0 ? sql.raw("") : sql` WHERE key = ${key}`;
	return db.execute(sql`SELECT key, origin, version, current_revision AS "currentRevision", canonical_hash AS "canonicalHash", definition, active, shadowed_db_version AS "shadowedDbVersion" FROM ${sql.raw(table)}${where}`);
}
async function resolveContentTypes(db, opts = {}) {
	const rows = await definitionRows(db, "type");
	const code = new Map((opts.codeTypes ?? []).map((definition) => [definition.key, defineContentType(definition)]));
	const dbRows = new Map(rows.map((row) => [row.key, row]));
	const resolved = [];
	for (const definition of code.values()) {
		const collision = dbRows.get(definition.key);
		const canonicalHash = await canonicalContentMigrationHash(definition);
		resolved.push(Object.freeze({
			...definition,
			version: collision?.origin === "code" ? Number(collision.version) : 1,
			revision: collision?.origin === "code" ? Number(collision.currentRevision) : 1,
			canonicalHash,
			...collision && collision.origin !== "code" ? { shadowedDbVersion: Number(collision.version) } : {}
		}));
	}
	for (const row of rows) {
		if (code.has(row.key)) continue;
		const definition = parseDefinition(row.definition);
		resolved.push(Object.freeze({
			...definition,
			active: bool$4(row.active),
			origin: row.origin === "code" ? "code" : "db",
			version: Number(row.version),
			revision: Number(row.currentRevision),
			canonicalHash: row.canonicalHash,
			...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: Number(row.shadowedDbVersion) }
		}));
	}
	return resolved.sort((left, right) => (left.position ?? 0) - (right.position ?? 0) || left.labels.name.localeCompare(right.labels.name) || left.key.localeCompare(right.key));
}
async function resolveContentType(db, key, opts = {}) {
	const found = (await resolveContentTypes(db, opts)).find((definition) => definition.key === key);
	if (!found) throw new ContentDefinitionError("definition-not-found", "content type is not registered");
	return found;
}
function principalScope$2(principal) {
	return `${principal.tenantId ?? ""}:${principal.id}`;
}
async function readDefinitionReceipt(db, operationId) {
	const raw = (await db.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id = ${operationId}`))[0]?.payload;
	if (raw === void 0) return void 0;
	const payload = typeof raw === "string" ? JSON.parse(raw) : raw;
	if (!payload || typeof payload.requestHash !== "string" || typeof payload.principalScope !== "string") throw new ContentDefinitionError("invalid-receipt", "operation receipt is malformed");
	return payload;
}
async function writeDefinitionReceipt(db, input) {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.execute(sql`INSERT INTO content_lifecycle_journal (id, operation_id, kind, state, payload, created_at, updated_at)
    VALUES (${crypto.randomUUID()}, ${input.operationId}, ${input.kind}, 'committed', ${JSON.stringify(input.payload)}, ${now}, ${now})`);
}
async function writeDefinitionVersion(db, input) {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.execute(sql`INSERT INTO content_definition_versions (definition_kind, definition_key, revision, canonical_hash, definition, origin, created_at)
    VALUES (${input.kind}, ${input.key}, ${input.revision}, ${input.canonicalHash}, ${JSON.stringify(input.definition)}, ${input.origin}, ${now})`);
}
async function createContentType(db, principal, input, authorization, effects, opts = {}) {
	const normalized = normalizeContentTypeDefinition({
		...input.definition,
		active: true
	});
	const { policyVersion } = await authorization.assert(db, principal, "createType", normalized.key);
	if ((opts.codeTypes ?? []).some((definition) => definition.key === normalized.key)) throw new ContentDefinitionError("definition-conflict", "database definition cannot shadow a code-owned key", "key");
	const requestHash = await canonicalContentMigrationHash({
		definition: normalized,
		operationId: input.operationId
	});
	const scope = principalScope$2(principal);
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "operation id was already used for different bytes or scope");
		return Object.freeze(replay.result);
	}
	if ((await definitionRows(db, "type", normalized.key)).length !== 0) throw new ContentDefinitionError("definition-conflict", "content type key already exists", "key");
	const canonicalHash = await canonicalContentMigrationHash(normalized);
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.execute(sql`INSERT INTO content_type_definitions (key, origin, version, active, current_revision, canonical_hash, definition, created_at, updated_at)
    VALUES (${normalized.key}, 'db', 1, ${true}, 1, ${canonicalHash}, ${JSON.stringify(normalized)}, ${now}, ${now})`);
	await writeDefinitionVersion(db, {
		kind: "type",
		key: normalized.key,
		revision: 1,
		canonicalHash,
		definition: normalized,
		origin: "db"
	});
	const result = Object.freeze({
		...normalized,
		origin: "db",
		version: 1,
		revision: 1,
		canonicalHash
	});
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:create:type",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "dbDefinitionCreated",
		operationId: input.operationId,
		principalId: principal.id,
		definitionKind: "type",
		definitionKey: normalized.key,
		definitionHash: canonicalHash,
		revision: 1,
		authorizationPolicyVersion: policyVersion
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
const __contentRegistryInternals = Object.freeze({
	definitionRows,
	parseDefinition,
	bool: bool$4
});
//#endregion
//#region src/status.ts
const STATUS_KEY = /^[a-z][a-z0-9_-]{0,63}$/;
const DATE_LABELS = /* @__PURE__ */ new Set([
	"published",
	"scheduled",
	"lastModified",
	"created"
]);
const TRANSITION_INPUTS = /* @__PURE__ */ new Set([
	"none",
	"scheduleAt",
	"publishedAt"
]);
function invalid(detail, field) {
	throw new ContentDefinitionError("invalid-definition", detail, field);
}
function bounded$1(value, field, max = 256) {
	if (typeof value !== "string") invalid("must be a string", field);
	const normalized = value.trim();
	if (normalized.length === 0 || normalized.length > max) invalid(`must contain 1..${max} characters`, field);
	return normalized;
}
function normalizeContentStatusDefinition(definition) {
	if (!STATUS_KEY.test(definition.key)) invalid("must be a stable lowercase machine key", "key");
	if (!DATE_LABELS.has(definition.dateLabel)) invalid("unknown date label", "dateLabel");
	if (!TRANSITION_INPUTS.has(definition.transitionInput)) invalid("unknown transition input", "transitionInput");
	if (definition.transitionInput === "scheduleAt" && definition.key !== "scheduled") invalid("scheduleAt is reserved for the scheduled status", "transitionInput");
	if (definition.transitionInput === "publishedAt" && !definition.published) invalid("publishedAt requires a published status", "transitionInput");
	if (definition.dateLabel === "scheduled" && definition.transitionInput !== "scheduleAt") invalid("scheduled date label requires scheduleAt input", "dateLabel");
	if (definition.position !== void 0 && (!Number.isSafeInteger(definition.position) || !Number.isFinite(definition.position))) invalid("must be a safe integer", "position");
	return Object.freeze({
		key: definition.key,
		label: bounded$1(definition.label, "label"),
		published: Boolean(definition.published),
		internal: Boolean(definition.internal),
		excludeFromSearch: Boolean(definition.excludeFromSearch),
		publiclyQueryable: Boolean(definition.publiclyQueryable),
		showInAdminAll: Boolean(definition.showInAdminAll),
		showInAdminStatusFilter: Boolean(definition.showInAdminStatusFilter),
		dateLabel: definition.dateLabel,
		transitionInput: definition.transitionInput,
		...definition.position === void 0 ? {} : { position: definition.position }
	});
}
function defineContentStatus(definition) {
	return Object.freeze({
		...normalizeContentStatusDefinition(definition),
		origin: "code"
	});
}
function bool$3(value) {
	return value === true || value === 1 || value === "1";
}
function parseStatus$1(value) {
	return normalizeContentStatusDefinition(typeof value === "string" ? JSON.parse(value) : value);
}
async function statusRows(db, key) {
	const where = key === void 0 ? sql.raw("") : sql` WHERE key = ${key}`;
	return db.execute(sql`SELECT key, origin, version, current_revision AS "currentRevision", canonical_hash AS "canonicalHash", definition, active, shadowed_db_version AS "shadowedDbVersion" FROM content_status_definitions${where}`);
}
async function resolveContentStatuses(db, opts = {}) {
	const rows = await statusRows(db);
	const code = new Map((opts.codeStatuses ?? []).map((definition) => [definition.key, defineContentStatus(definition)]));
	const dbRows = new Map(rows.map((row) => [row.key, row]));
	const resolved = [];
	for (const definition of code.values()) {
		const collision = dbRows.get(definition.key);
		resolved.push(Object.freeze({
			...definition,
			active: true,
			version: collision?.origin === "code" ? Number(collision.version) : 1,
			revision: collision?.origin === "code" ? Number(collision.currentRevision) : 1,
			canonicalHash: await canonicalContentMigrationHash(definition),
			...collision && collision.origin !== "code" ? { shadowedDbVersion: Number(collision.version) } : {}
		}));
	}
	for (const row of rows) {
		if (code.has(row.key)) continue;
		const definition = parseStatus$1(row.definition);
		resolved.push(Object.freeze({
			...definition,
			active: bool$3(row.active),
			origin: row.origin === "code" ? "code" : "db",
			version: Number(row.version),
			revision: Number(row.currentRevision),
			canonicalHash: row.canonicalHash,
			...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: Number(row.shadowedDbVersion) }
		}));
	}
	return resolved.sort((left, right) => (left.position ?? 0) - (right.position ?? 0) || left.label.localeCompare(right.label) || left.key.localeCompare(right.key));
}
async function resolveContentStatus(db, key, opts = {}) {
	const status = (await resolveContentStatuses(db, opts)).find((item) => item.key === key);
	if (!status) throw new ContentDefinitionError("definition-not-found", "content status is not registered");
	return status;
}
async function createContentStatus(db, principal, input, authorization, effects, opts = {}) {
	const normalized = normalizeContentStatusDefinition(input.definition);
	const { policyVersion } = await authorization.assert(db, principal, "createStatus", normalized.key);
	if ((opts.codeStatuses ?? []).some((definition) => definition.key === normalized.key)) throw new ContentDefinitionError("definition-conflict", "database status cannot shadow a code-owned key", "key");
	const requestHash = await canonicalContentMigrationHash({
		definition: normalized,
		operationId: input.operationId
	});
	const scope = principalScope$2(principal);
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "operation id was already used for different bytes or scope");
		return Object.freeze(replay.result);
	}
	if ((await statusRows(db, normalized.key)).length !== 0) throw new ContentDefinitionError("definition-conflict", "content status key already exists", "key");
	const canonicalHash = await canonicalContentMigrationHash(normalized);
	const now = (/* @__PURE__ */ new Date()).toISOString();
	await db.execute(sql`INSERT INTO content_status_definitions (key, origin, version, active, current_revision, canonical_hash, definition, created_at, updated_at)
    VALUES (${normalized.key}, 'db', 1, ${true}, 1, ${canonicalHash}, ${JSON.stringify(normalized)}, ${now}, ${now})`);
	await writeDefinitionVersion(db, {
		kind: "status",
		key: normalized.key,
		revision: 1,
		canonicalHash,
		definition: normalized,
		origin: "db"
	});
	const result = Object.freeze({
		...normalized,
		origin: "db",
		version: 1,
		revision: 1,
		active: true,
		canonicalHash
	});
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:create:status",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "dbDefinitionCreated",
		operationId: input.operationId,
		principalId: principal.id,
		definitionKind: "status",
		definitionKey: normalized.key,
		definitionHash: canonicalHash,
		revision: 1,
		authorizationPolicyVersion: policyVersion
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
const __contentStatusInternals = Object.freeze({
	statusRows,
	parseStatus: parseStatus$1,
	bool: bool$3
});
//#endregion
//#region src/definition-lifecycle.ts
function asSchemaValue(value) {
	return JSON.parse(JSON.stringify(value));
}
function parsePayload(value) {
	return typeof value === "string" ? JSON.parse(value) : value;
}
function countValue(value) {
	if (typeof value === "number") return value;
	if (typeof value === "bigint") return Number(value);
	if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
	throw new ContentDefinitionError("invalid-receipt", "adapter returned a non-numeric count");
}
async function typeCorpus(db, key) {
	return db.execute(sql`SELECT id, status, parent_id AS "parentId", menu_order AS "menuOrder",
    template_key AS "templateKey", excerpt, featured_media AS "featuredMedia", comment_status AS "commentStatus",
    ping_status AS "pingStatus", sticky, format, deleted_at AS "deletedAt", updated_at AS "updatedAt"
    FROM content_entries WHERE type = ${key} ORDER BY id`);
}
async function statusCorpus(db, key) {
	return db.execute(sql`SELECT id, type, updated_at AS "updatedAt" FROM content_entries WHERE status = ${key} ORDER BY id`);
}
async function corpusEvidence(rows) {
	const hash = await canonicalContentMigrationHash(rows.map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, value instanceof Date ? value.toISOString() : value]))));
	return {
		version: `sha256:${hash}`,
		hash
	};
}
function removed(before, after) {
	const next = new Set(after ?? []);
	return (before ?? []).filter((item) => !next.has(item));
}
function validateStrategies(strategies) {
	if (strategies.length === 0) throw new ContentDefinitionError("strategy-conflict", "at least one strategy is required");
	if (strategies.some((strategy) => strategy.kind === "reject") && strategies.length !== 1) throw new ContentDefinitionError("strategy-conflict", "reject must be the sole strategy");
	const singular = /* @__PURE__ */ new Set();
	for (const strategy of strategies) {
		const category = strategy.kind === "mapStatus" ? "status" : strategy.kind === "flattenHierarchy" ? "hierarchy" : strategy.kind === "reassignType" || strategy.kind === "deactivate" ? "lifecycle" : void 0;
		if (category && singular.has(category)) throw new ContentDefinitionError("strategy-conflict", `multiple ${category} strategies are not allowed`);
		if (category) singular.add(category);
	}
}
function typeImpactCounts(current, next, rows) {
	const counts = { entries: rows.length };
	const incompatibilities = [];
	for (const status of removed(current.statusKeys, next.statusKeys)) {
		const count = rows.filter((row) => row.status === status).length;
		counts[`status:${status}`] = count;
		if (count > 0) incompatibilities.push(`status:${status}`);
	}
	if (current.hierarchical && !next.hierarchical) {
		const count = rows.filter((row) => row.parentId !== null).length;
		counts.hierarchy = count;
		if (count > 0) incompatibilities.push("hierarchy");
	}
	for (const feature of removed(current.supports, next.supports)) {
		let count = 0;
		if (feature === "excerpt") count = rows.filter((row) => row.excerpt.length > 0).length;
		else if (feature === "featuredImage") count = rows.filter((row) => row.featuredMedia !== null).length;
		else if (feature === "pageAttributes") count = rows.filter((row) => row.parentId !== null || Number(row.menuOrder) !== 0 || row.templateKey !== null).length;
		else if (feature === "postFormats") count = rows.filter((row) => row.format !== null).length;
		else count = rows.length;
		counts[`feature:${feature}`] = count;
		if (count > 0) incompatibilities.push(`feature:${feature}`);
	}
	if ((current.publiclyQueryable && !next.publiclyQueryable || current.rest !== false && next.rest === false || current.hasArchive !== false && next.hasArchive === false || current.rewrite !== false && next.rewrite === false) && rows.length > 0) {
		counts.routing = rows.length;
		incompatibilities.push("routing");
	}
	return {
		counts,
		incompatibilities
	};
}
async function storeImpact(db, plan) {
	const token = await canonicalContentMigrationHash(plan);
	const operationId = `impact:${token}`;
	if ((await db.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id = ${operationId}`)).length === 0) {
		const now = (/* @__PURE__ */ new Date()).toISOString();
		await db.execute(sql`INSERT INTO content_lifecycle_journal (id, operation_id, kind, definition_kind, definition_key, definition_revision, state, payload, created_at, updated_at)
      VALUES (${crypto.randomUUID()}, ${operationId}, 'definition-impact', ${plan.definitionKind}, ${plan.key}, ${plan.expectedVersion}, 'prepared', ${JSON.stringify(plan)}, ${now}, ${now})`);
	}
	return token;
}
async function readImpact(db, token) {
	const rows = await db.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id = ${`impact:${token}`} AND kind = 'definition-impact'`);
	if (!rows[0]) throw new ContentDefinitionError("stale-impact", "impact token is unknown or expired");
	const plan = parsePayload(rows[0].payload);
	if (await canonicalContentMigrationHash(plan) !== token) throw new ContentDefinitionError("stale-impact", "impact token bytes do not match stored plan");
	return plan;
}
function strategyHash(strategies) {
	return canonicalContentMigrationHash(strategies);
}
async function previewContentTypeChange(db, principal, input, authorization, opts = {}) {
	validateStrategies(input.strategies);
	const { policyVersion } = await authorization.assert(db, principal, "changeType", input.key);
	const current = await resolveContentType(db, input.key, opts);
	if (current.origin === "code") throw new ContentDefinitionError("code-definition-owned", "code definitions can only change through reconciliation");
	if (current.version !== input.expectedVersion) throw new ContentDefinitionError("stale-definition", "expected version does not match current definition");
	const next = normalizeContentTypeDefinition({
		...current,
		...input.patch ?? {},
		key: current.key,
		active: input.deactivate || input.delete ? false : current.active
	});
	const rows = await typeCorpus(db, input.key);
	const evidence = await corpusEvidence(rows);
	const impact = typeImpactCounts(current, next, rows);
	if ((input.deactivate || input.delete) && rows.length > 0) impact.incompatibilities.push(input.delete ? "delete-with-entries" : "deactivate-with-entries");
	const plan = {
		definitionKind: "type",
		key: input.key,
		expectedVersion: input.expectedVersion,
		policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: Object.freeze({ ...impact.counts }),
		incompatibilities: Object.freeze([...new Set(impact.incompatibilities)].sort()),
		request: asSchemaValue({
			patch: input.patch ?? null,
			deactivate: Boolean(input.deactivate),
			delete: Boolean(input.delete),
			strategies: input.strategies
		}),
		strategyHash: await strategyHash(input.strategies)
	};
	const token = await storeImpact(db, plan);
	return Object.freeze({
		token,
		definitionKey: input.key,
		expectedVersion: input.expectedVersion,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: plan.affectedCounts,
		incompatibilities: plan.incompatibilities
	});
}
function requestRecord(plan) {
	return plan.request;
}
async function verifyBackups(db, plan, strategies, destructive) {
	const purges = strategies.filter((strategy) => strategy.kind === "purgeFeatureData");
	if (purges.length === 0) return Object.freeze([]);
	if (!destructive) throw new ContentDefinitionError("backup-required", "purge requires destructive backup dependencies");
	const ids = [];
	for (const purge of purges) {
		const expectedCounts = Object.fromEntries(purge.features.map((feature) => [`feature:${feature}`, plan.affectedCounts[`feature:${feature}`] ?? 0]));
		const scopeHash = await canonicalContentMigrationHash({
			definitionKey: plan.key,
			features: [...purge.features].sort(),
			policyVersion: plan.policyVersion,
			corpusVersion: plan.corpusVersion,
			corpusHash: plan.corpusHash
		});
		const manifest = await destructive.backups.verify(db, purge.backupId, scopeHash, plan.corpusVersion, plan.corpusHash, expectedCounts);
		if (!manifest.immutable || manifest.id !== purge.backupId || manifest.scopeHash !== scopeHash || manifest.corpusHash !== plan.corpusHash) throw new ContentDefinitionError("backup-mismatch", "backup manifest does not match the bound destructive scope");
		ids.push(manifest.id);
	}
	return Object.freeze(ids.sort());
}
async function applyTypeStrategies(db, key, strategies, opts) {
	for (const strategy of strategies) {
		if (strategy.kind === "reject" || strategy.kind === "deactivate" || strategy.kind === "retainDormantFeatureData") continue;
		if (strategy.kind === "mapStatus") for (const [from, to] of Object.entries(strategy.mappings).sort(([a], [b]) => a.localeCompare(b))) {
			const target = await resolveContentStatus(db, to, { codeStatuses: opts.codeStatuses });
			if (!target.active) throw new ContentDefinitionError("strategy-conflict", `target status ${to} is inactive`);
			await db.execute(sql`UPDATE content_entries SET status = ${to}, status_definition_revision = ${target.revision} WHERE type = ${key} AND status = ${from}`);
		}
		else if (strategy.kind === "flattenHierarchy") await db.execute(sql`UPDATE content_entries SET parent_id = NULL WHERE type = ${key} AND parent_id IS NOT NULL`);
		else if (strategy.kind === "detachTaxonomies") for (const taxonomy of [...new Set(strategy.taxonomyKeys)].sort()) await db.execute(sql`DELETE FROM content_entry_terms WHERE entry_id IN (SELECT id FROM content_entries WHERE type = ${key}) AND term_id IN (SELECT id FROM content_terms WHERE taxonomy = ${taxonomy})`);
		else if (strategy.kind === "purgeFeatureData") {
			for (const feature of strategy.features) if (feature === "excerpt") await db.execute(sql`UPDATE content_entries SET excerpt = '' WHERE type = ${key}`);
			else if (feature === "featuredImage") await db.execute(sql`UPDATE content_entries SET featured_media = NULL WHERE type = ${key}`);
			else if (feature === "pageAttributes") await db.execute(sql`UPDATE content_entries SET parent_id = NULL, menu_order = 0, template_key = NULL WHERE type = ${key}`);
			else if (feature === "postFormats") await db.execute(sql`UPDATE content_entries SET format = NULL WHERE type = ${key}`);
		} else if (strategy.kind === "reassignType") {
			const target = await resolveContentType(db, strategy.targetTypeKey, { codeTypes: opts.codeTypes });
			if (!target.active) throw new ContentDefinitionError("strategy-conflict", "target type is inactive");
			await db.execute(sql`UPDATE content_entries SET type = ${target.key}, type_definition_revision = ${target.revision} WHERE type = ${key}`);
		}
	}
}
async function applyContentTypeChange(db, principal, input, authorization, effects, opts = {}) {
	validateStrategies(input.strategies);
	await authorization.assert(db, principal, "changeType");
	const plan = await readImpact(db, input.impactToken);
	if (plan.definitionKind !== "type") throw new ContentDefinitionError("stale-impact", "impact token belongs to another definition kind");
	const { policyVersion } = await authorization.assert(db, principal, "changeType", plan.key);
	if (policyVersion !== plan.policyVersion) throw new ContentDefinitionError("authorization-drift", "authorization policy changed after preview");
	if (await strategyHash(input.strategies) !== plan.strategyHash) throw new ContentDefinitionError("stale-impact", "strategies differ from preview");
	const scope = principalScope$2(principal);
	const requestHash = await canonicalContentMigrationHash({
		impactToken: input.impactToken,
		strategies: input.strategies
	});
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "operation id was already used for different bytes or scope");
		return replay.result === null ? null : Object.freeze(replay.result);
	}
	const current = await resolveContentType(db, plan.key, { codeTypes: opts.codeTypes });
	if (current.origin === "code") throw new ContentDefinitionError("code-definition-owned", "code definition became effective after preview");
	if (current.version !== plan.expectedVersion) throw new ContentDefinitionError("stale-impact", "definition version changed after preview");
	const evidence = await corpusEvidence(await typeCorpus(db, plan.key));
	if (evidence.hash !== plan.corpusHash || evidence.version !== plan.corpusVersion) throw new ContentDefinitionError("stale-impact", "affected corpus changed after preview");
	const request = requestRecord(plan);
	const backups = await verifyBackups(db, plan, input.strategies, opts.destructive);
	if (input.strategies[0]?.kind === "reject" && plan.incompatibilities.length > 0) throw new ContentDefinitionError("strategy-conflict", "reject strategy cannot apply an incompatible change");
	await applyTypeStrategies(db, plan.key, input.strategies, opts);
	const remainingCount = countValue((await db.execute(sql`SELECT COUNT(*) AS count FROM content_entries WHERE type = ${plan.key}`))[0]?.count ?? 0);
	if (request.delete && remainingCount > 0) throw new ContentDefinitionError("strategy-conflict", "delete requires zero references or complete reassignment");
	const next = normalizeContentTypeDefinition({
		...current,
		...request.patch ?? {},
		key: current.key,
		active: request.deactivate || request.delete ? false : current.active
	});
	const version = current.version + 1;
	const revision = current.revision + 1;
	const canonicalHash = await canonicalContentMigrationHash(next);
	await writeDefinitionVersion(db, {
		kind: "type",
		key: current.key,
		revision,
		canonicalHash,
		definition: next,
		origin: "db"
	});
	let result;
	if (request.delete) {
		await db.execute(sql`DELETE FROM content_type_definitions WHERE key = ${current.key} AND version = ${current.version}`);
		result = null;
	} else {
		const now = (/* @__PURE__ */ new Date()).toISOString();
		await db.execute(sql`UPDATE content_type_definitions SET version = ${version}, current_revision = ${revision}, active = ${next.active}, canonical_hash = ${canonicalHash}, definition = ${JSON.stringify(next)}, updated_at = ${now} WHERE key = ${current.key} AND version = ${current.version}`);
		result = Object.freeze({
			...next,
			origin: "db",
			version,
			revision,
			canonicalHash
		});
	}
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:change:type",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "dbDefinitionChanged",
		operationId: input.operationId,
		principalId: principal.id,
		definitionKind: "type",
		definitionKey: current.key,
		action: request.delete ? "delete" : request.deactivate ? "deactivate" : "update",
		strategyHash: plan.strategyHash,
		impactToken: input.impactToken,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: plan.corpusVersion,
		corpusHash: plan.corpusHash,
		affectedCounts: plan.affectedCounts,
		backupIds: backups
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
async function previewContentStatusChange(db, principal, input, authorization, opts = {}) {
	const { policyVersion } = await authorization.assert(db, principal, "changeStatus", input.key);
	const current = await resolveContentStatus(db, input.key, opts);
	if (current.origin === "code") throw new ContentDefinitionError("code-definition-owned", "code statuses can only change through reconciliation");
	if (current.version !== input.expectedVersion) throw new ContentDefinitionError("stale-definition", "expected version does not match current status");
	normalizeContentStatusDefinition({
		...current,
		...input.patch ?? {},
		key: current.key
	});
	const rows = await statusCorpus(db, input.key);
	const evidence = await corpusEvidence(rows);
	const counts = Object.freeze({ entries: rows.length });
	const incompatibilities = Object.freeze(rows.length > 0 && (input.delete || input.deactivate || input.patch !== void 0) ? ["status-in-use"] : []);
	const token = await storeImpact(db, {
		definitionKind: "status",
		key: input.key,
		expectedVersion: input.expectedVersion,
		policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: counts,
		incompatibilities,
		request: asSchemaValue({
			patch: input.patch ?? null,
			deactivate: Boolean(input.deactivate),
			delete: Boolean(input.delete),
			strategy: input.strategy
		}),
		strategyHash: await strategyHash(input.strategy)
	});
	return Object.freeze({
		token,
		definitionKey: input.key,
		expectedVersion: input.expectedVersion,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: counts,
		incompatibilities
	});
}
async function applyContentStatusChange(db, principal, input, authorization, effects, opts = {}) {
	await authorization.assert(db, principal, "changeStatus");
	const plan = await readImpact(db, input.impactToken);
	if (plan.definitionKind !== "status") throw new ContentDefinitionError("stale-impact", "impact token belongs to another definition kind");
	const { policyVersion } = await authorization.assert(db, principal, "changeStatus", plan.key);
	if (policyVersion !== plan.policyVersion) throw new ContentDefinitionError("authorization-drift", "authorization policy changed after preview");
	if (await strategyHash(input.strategy) !== plan.strategyHash) throw new ContentDefinitionError("stale-impact", "strategy differs from preview");
	const scope = principalScope$2(principal);
	const requestHash = await canonicalContentMigrationHash({
		impactToken: input.impactToken,
		strategy: input.strategy
	});
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "operation id was already used for different bytes or scope");
		return replay.result === null ? null : Object.freeze(replay.result);
	}
	const current = await resolveContentStatus(db, plan.key, opts);
	if (current.origin === "code") throw new ContentDefinitionError("code-definition-owned", "code status became effective after preview");
	if (current.version !== plan.expectedVersion) throw new ContentDefinitionError("stale-impact", "status version changed after preview");
	const rows = await statusCorpus(db, plan.key);
	if ((await corpusEvidence(rows)).hash !== plan.corpusHash) throw new ContentDefinitionError("stale-impact", "status corpus changed after preview");
	const request = plan.request;
	if (rows.length > 0) {
		if (input.strategy.kind === "reject") throw new ContentDefinitionError("strategy-conflict", "status is still referenced");
		const target = await resolveContentStatus(db, input.strategy.targetStatusKey, opts);
		if (!target.active || target.key === current.key) throw new ContentDefinitionError("strategy-conflict", "status mapping target must be another active status");
		await db.execute(sql`UPDATE content_entries SET status = ${target.key}, status_definition_revision = ${target.revision} WHERE status = ${current.key}`);
	}
	const next = normalizeContentStatusDefinition({
		...current,
		...request.patch ?? {},
		key: current.key
	});
	const version = current.version + 1;
	const revision = current.revision + 1;
	const canonicalHash = await canonicalContentMigrationHash(next);
	await writeDefinitionVersion(db, {
		kind: "status",
		key: current.key,
		revision,
		canonicalHash,
		definition: next,
		origin: "db"
	});
	let result;
	if (request.delete) {
		await db.execute(sql`DELETE FROM content_status_definitions WHERE key = ${current.key} AND version = ${current.version}`);
		result = null;
	} else {
		const active = request.deactivate ? false : current.active;
		const persisted = {
			...next,
			active
		};
		const now = (/* @__PURE__ */ new Date()).toISOString();
		await db.execute(sql`UPDATE content_status_definitions SET version = ${version}, current_revision = ${revision}, active = ${active}, canonical_hash = ${canonicalHash}, definition = ${JSON.stringify(next)}, updated_at = ${now} WHERE key = ${current.key} AND version = ${current.version}`);
		result = Object.freeze({
			...persisted,
			origin: "db",
			version,
			revision,
			canonicalHash
		});
	}
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:change:status",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "dbDefinitionChanged",
		operationId: input.operationId,
		principalId: principal.id,
		definitionKind: "status",
		definitionKey: current.key,
		action: request.delete ? "delete" : request.deactivate ? "deactivate" : "update",
		strategyHash: plan.strategyHash,
		impactToken: input.impactToken,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: plan.corpusVersion,
		corpusHash: plan.corpusHash,
		affectedCounts: plan.affectedCounts,
		backupIds: []
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
async function currentCodeRegistryVersion(db) {
	return canonicalContentMigrationHash({
		types: (await __contentRegistryInternals.definitionRows(db, "type")).filter((row) => row.origin === "code").map((row) => ({
			key: row.key,
			hash: row.canonicalHash,
			version: Number(row.version),
			active: __contentRegistryInternals.bool(row.active)
		})),
		statuses: (await __contentStatusInternals.statusRows(db)).filter((row) => row.origin === "code").map((row) => ({
			key: row.key,
			hash: row.canonicalHash,
			version: Number(row.version),
			active: __contentStatusInternals.bool(row.active)
		}))
	});
}
function normalizedCodeSnapshot(snapshot) {
	const version = typeof snapshot.version === "string" ? snapshot.version.trim() : "";
	if (!version || version.length > 256) throw new ContentDefinitionError("invalid-definition", "code registry version must contain 1..256 characters", "version");
	const typeKeys = /* @__PURE__ */ new Set();
	const statusKeys = /* @__PURE__ */ new Set();
	const types = snapshot.types.map((definition) => {
		const normalized = defineContentType(definition);
		if (typeKeys.has(normalized.key)) throw new ContentDefinitionError("definition-conflict", `duplicate code type ${normalized.key}`);
		typeKeys.add(normalized.key);
		return normalized;
	});
	const statuses = snapshot.statuses.map((definition) => {
		const normalized = defineContentStatus(definition);
		if (statusKeys.has(normalized.key)) throw new ContentDefinitionError("definition-conflict", `duplicate code status ${normalized.key}`);
		statusKeys.add(normalized.key);
		return normalized;
	});
	for (const definition of types) for (const statusKey of definition.statusKeys ?? []) if (!statusKeys.has(statusKey)) throw new ContentDefinitionError("invalid-definition", `code type ${definition.key} references missing status ${statusKey}`, "statusKeys");
	return Object.freeze({
		version,
		types: Object.freeze(types),
		statuses: Object.freeze(statuses)
	});
}
function resolvedTypeRow(row) {
	const definition = __contentRegistryInternals.parseDefinition(row.definition);
	return Object.freeze({
		...definition,
		active: __contentRegistryInternals.bool(row.active),
		origin: row.origin === "code" ? "code" : "db",
		version: Number(row.version),
		revision: Number(row.currentRevision),
		canonicalHash: row.canonicalHash,
		...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: Number(row.shadowedDbVersion) }
	});
}
function resolvedStatusRow(row) {
	const definition = __contentStatusInternals.parseStatus(row.definition);
	return Object.freeze({
		...definition,
		active: __contentStatusInternals.bool(row.active),
		origin: row.origin === "code" ? "code" : "db",
		version: Number(row.version),
		revision: Number(row.currentRevision),
		canonicalHash: row.canonicalHash,
		...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: Number(row.shadowedDbVersion) }
	});
}
async function codeTypeImpact(db, current, next, strategies, policyVersion) {
	const rows = await typeCorpus(db, current.key);
	const nextDefinition = next ?? normalizeContentTypeDefinition({
		...current,
		active: false
	});
	const impact = typeImpactCounts(current, nextDefinition, rows);
	if (next === null && rows.length > 0) impact.incompatibilities.push("deactivate-with-entries");
	const chosen = strategies ?? [{ kind: "reject" }];
	validateStrategies(chosen);
	if (impact.incompatibilities.length > 0 && strategies === void 0) throw new ContentDefinitionError("strategy-conflict", `code type ${current.key} requires explicit narrowing strategies`);
	const evidence = await corpusEvidence(rows);
	const plan = {
		definitionKind: "type",
		key: current.key,
		expectedVersion: current.version,
		policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: Object.freeze({ ...impact.counts }),
		incompatibilities: Object.freeze([...new Set(impact.incompatibilities)].sort()),
		request: asSchemaValue({
			codeReconciliation: true,
			nextDefinition,
			remove: next === null,
			strategies: chosen
		}),
		strategyHash: await strategyHash(chosen)
	};
	const token = await storeImpact(db, plan);
	return Object.freeze({
		token,
		definitionKey: current.key,
		expectedVersion: current.version,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: plan.affectedCounts,
		incompatibilities: plan.incompatibilities
	});
}
async function codeStatusImpact(db, current, next, strategy, policyVersion) {
	const rows = await statusCorpus(db, current.key);
	const chosen = strategy ?? { kind: "reject" };
	if (rows.length > 0 && strategy === void 0) throw new ContentDefinitionError("strategy-conflict", `code status ${current.key} requires an explicit mapping/reject strategy`);
	const evidence = await corpusEvidence(rows);
	const incompatibilities = rows.length > 0 ? Object.freeze(["status-in-use"]) : Object.freeze([]);
	const plan = {
		definitionKind: "status",
		key: current.key,
		expectedVersion: current.version,
		policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: Object.freeze({ entries: rows.length }),
		incompatibilities,
		request: asSchemaValue({
			codeReconciliation: true,
			nextDefinition: next,
			remove: next === null,
			strategy: chosen
		}),
		strategyHash: await strategyHash(chosen)
	};
	const token = await storeImpact(db, plan);
	return Object.freeze({
		token,
		definitionKey: current.key,
		expectedVersion: current.version,
		authorizationPolicyVersion: policyVersion,
		corpusVersion: evidence.version,
		corpusHash: evidence.hash,
		affectedCounts: plan.affectedCounts,
		incompatibilities
	});
}
async function previewContentCodeReconciliation(db, principal, input, authorization) {
	const { policyVersion } = await authorization.assert(db, principal, "reconcileCode");
	const currentVersion = await currentCodeRegistryVersion(db);
	if (input.expectedCurrentVersion !== currentVersion) throw new ContentDefinitionError("stale-definition", "code registry version changed");
	const next = normalizedCodeSnapshot(input.next);
	const nextCanonicalHash = await canonicalContentMigrationHash(next);
	const currentTypeRows = await __contentRegistryInternals.definitionRows(db, "type");
	const currentStatusRows = await __contentStatusInternals.statusRows(db);
	const nextTypes = new Map(next.types.map((definition) => [definition.key, definition]));
	const nextStatuses = new Map(next.statuses.map((definition) => [definition.key, definition]));
	const typeImpacts = [];
	const statusImpacts = [];
	for (const row of currentTypeRows) {
		const nextDefinition = nextTypes.get(row.key);
		const current = resolvedTypeRow(row);
		const nextHash = nextDefinition ? await canonicalContentMigrationHash(nextDefinition) : void 0;
		if (!(row.origin === "code" && nextDefinition === void 0 || nextDefinition !== void 0 && (row.canonicalHash !== nextHash || row.origin !== "code"))) continue;
		typeImpacts.push(await codeTypeImpact(db, current, nextDefinition ?? null, input.typeStrategies[row.key], policyVersion));
	}
	for (const row of currentStatusRows) {
		const nextDefinition = nextStatuses.get(row.key);
		const current = resolvedStatusRow(row);
		const nextHash = nextDefinition ? await canonicalContentMigrationHash(nextDefinition) : void 0;
		if (!(row.origin === "code" && nextDefinition === void 0 || nextDefinition !== void 0 && (row.canonicalHash !== nextHash || row.origin !== "code"))) continue;
		statusImpacts.push(await codeStatusImpact(db, current, nextDefinition ?? null, input.statusStrategies[row.key], policyVersion));
	}
	const stored = Object.freeze({
		currentVersion,
		nextCanonicalHash,
		policyVersion,
		next,
		typeStrategies: Object.freeze({ ...input.typeStrategies }),
		statusStrategies: Object.freeze({ ...input.statusStrategies }),
		typeImpacts: Object.freeze(typeImpacts),
		statusImpacts: Object.freeze(statusImpacts)
	});
	const token = await canonicalContentMigrationHash(stored);
	const now = (/* @__PURE__ */ new Date()).toISOString();
	if ((await db.execute(sql`SELECT operation_id AS "operationId" FROM content_lifecycle_journal WHERE operation_id = ${`code-impact:${token}`}`)).length === 0) await db.execute(sql`INSERT INTO content_lifecycle_journal (id, operation_id, kind, state, payload, created_at, updated_at) VALUES (${crypto.randomUUID()}, ${`code-impact:${token}`}, 'code-registry-impact', 'prepared', ${JSON.stringify(stored)}, ${now}, ${now})`);
	return Object.freeze({
		token,
		fromVersion: currentVersion,
		nextCanonicalHash,
		typeImpacts: stored.typeImpacts,
		statusImpacts: stored.statusImpacts
	});
}
async function readCodePlan(db, token) {
	const rows = await db.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id = ${`code-impact:${token}`} AND kind = 'code-registry-impact'`);
	if (!rows[0]) throw new ContentDefinitionError("stale-impact", "code reconciliation token is unknown");
	const plan = parsePayload(rows[0].payload);
	if (await canonicalContentMigrationHash(plan) !== token) throw new ContentDefinitionError("stale-impact", "code reconciliation token bytes do not match stored plan");
	return plan;
}
async function verifyCodeImpact(db, impact, kind) {
	const stored = await readImpact(db, impact.token);
	if (stored.definitionKind !== kind || stored.key !== impact.definitionKey || stored.expectedVersion !== impact.expectedVersion) throw new ContentDefinitionError("stale-impact", "code impact identity changed");
	if (kind === "type") {
		const current = await resolveContentType(db, stored.key);
		if (current.version !== stored.expectedVersion || current.canonicalHash !== (await __contentRegistryInternals.definitionRows(db, "type", stored.key))[0]?.canonicalHash) throw new ContentDefinitionError("stale-impact", "code type definition changed after preview");
		const evidence = await corpusEvidence(await typeCorpus(db, stored.key));
		if (evidence.hash !== stored.corpusHash || evidence.version !== stored.corpusVersion) throw new ContentDefinitionError("stale-impact", "code type corpus changed after preview");
	} else {
		if ((await resolveContentStatus(db, stored.key)).version !== stored.expectedVersion) throw new ContentDefinitionError("stale-impact", "code status definition changed after preview");
		const evidence = await corpusEvidence(await statusCorpus(db, stored.key));
		if (evidence.hash !== stored.corpusHash || evidence.version !== stored.corpusVersion) throw new ContentDefinitionError("stale-impact", "code status corpus changed after preview");
	}
	return stored;
}
async function applyContentCodeReconciliation(db, principal, input, authorization, effects, destructive) {
	const { policyVersion } = await authorization.assert(db, principal, "reconcileCode");
	const plan = await readCodePlan(db, input.planToken);
	if (plan.policyVersion !== policyVersion) throw new ContentDefinitionError("authorization-drift", "authorization policy changed after code preview");
	const next = normalizedCodeSnapshot(input.next);
	if (await canonicalContentMigrationHash(next) !== plan.nextCanonicalHash) throw new ContentDefinitionError("stale-impact", "next code registry bytes differ from preview");
	if (await canonicalContentMigrationHash(input.typeStrategies) !== await canonicalContentMigrationHash(plan.typeStrategies) || await canonicalContentMigrationHash(input.statusStrategies) !== await canonicalContentMigrationHash(plan.statusStrategies)) throw new ContentDefinitionError("stale-impact", "code reconciliation strategies differ from preview");
	const scope = principalScope$2(principal);
	const requestHash = await canonicalContentMigrationHash({
		planToken: input.planToken,
		nextCanonicalHash: plan.nextCanonicalHash,
		typeStrategies: input.typeStrategies,
		statusStrategies: input.statusStrategies
	});
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "operation id was already used for different code bytes or scope");
		return Object.freeze(replay.result);
	}
	if (await currentCodeRegistryVersion(db) !== plan.currentVersion) throw new ContentDefinitionError("stale-impact", "code registry changed after preview");
	const nextTypeMap = new Map(next.types.map((definition) => [definition.key, definition]));
	const nextStatusMap = new Map(next.statuses.map((definition) => [definition.key, definition]));
	const verifiedBackups = [];
	const affectedCounts = {};
	const corpusHashes = [];
	const verifiedTypeImpacts = [];
	const verifiedStatusImpacts = [];
	for (const impact of plan.typeImpacts) {
		const stored = await verifyCodeImpact(db, impact, "type");
		const strategies = input.typeStrategies[stored.key] ?? [{ kind: "reject" }];
		if (await strategyHash(strategies) !== stored.strategyHash) throw new ContentDefinitionError("stale-impact", `type strategies for ${stored.key} differ from preview`);
		if (strategies[0]?.kind === "reject" && stored.incompatibilities.length > 0) throw new ContentDefinitionError("strategy-conflict", `code type ${stored.key} has unresolved incompatibilities`);
		verifiedBackups.push(...await verifyBackups(db, stored, strategies, destructive));
		verifiedTypeImpacts.push({
			stored,
			strategies
		});
		corpusHashes.push(stored.corpusHash);
		for (const [metric, count] of Object.entries(stored.affectedCounts)) affectedCounts[`type:${stored.key}:${metric}`] = count;
	}
	for (const impact of plan.statusImpacts) {
		const stored = await verifyCodeImpact(db, impact, "status");
		const strategy = input.statusStrategies[stored.key] ?? { kind: "reject" };
		if (await strategyHash(strategy) !== stored.strategyHash) throw new ContentDefinitionError("stale-impact", `status strategy for ${stored.key} differs from preview`);
		const affectedEntries = stored.affectedCounts.entries ?? 0;
		if (affectedEntries > 0 && strategy.kind === "reject") throw new ContentDefinitionError("strategy-conflict", `code status ${stored.key} is still referenced`);
		if (affectedEntries > 0 && strategy.kind === "map") {
			const target = nextStatusMap.get(strategy.targetStatusKey) ?? await resolveContentStatus(db, strategy.targetStatusKey, { codeStatuses: next.statuses });
			if (!target || target.key === stored.key) throw new ContentDefinitionError("strategy-conflict", "code status mapping target must be another active status");
		}
		verifiedStatusImpacts.push({
			stored,
			strategy
		});
		corpusHashes.push(stored.corpusHash);
		for (const [metric, count] of Object.entries(stored.affectedCounts)) affectedCounts[`status:${stored.key}:${metric}`] = count;
	}
	for (const { stored, strategies } of verifiedTypeImpacts) await applyTypeStrategies(db, stored.key, strategies, {
		codeTypes: next.types,
		codeStatuses: next.statuses
	});
	for (const { stored, strategy } of verifiedStatusImpacts) {
		if ((stored.affectedCounts.entries ?? 0) === 0 || strategy.kind === "reject") continue;
		const target = nextStatusMap.get(strategy.targetStatusKey) ?? await resolveContentStatus(db, strategy.targetStatusKey, { codeStatuses: next.statuses });
		const revision = "revision" in target ? target.revision : 1;
		await db.execute(sql`UPDATE content_entries SET status = ${target.key}, status_definition_revision = ${revision} WHERE status = ${stored.key}`);
	}
	const currentTypeRows = await __contentRegistryInternals.definitionRows(db, "type");
	const currentStatusRows = await __contentStatusInternals.statusRows(db);
	const changedKeys = [];
	for (const definition of next.types) {
		const normalized = normalizeContentTypeDefinition(definition);
		const hash = await canonicalContentMigrationHash(normalized);
		const existing = currentTypeRows.find((row) => row.key === normalized.key);
		const now = (/* @__PURE__ */ new Date()).toISOString();
		if (!existing) {
			await db.execute(sql`INSERT INTO content_type_definitions (key, origin, version, active, current_revision, canonical_hash, definition, created_at, updated_at) VALUES (${normalized.key}, 'code', 1, ${true}, 1, ${hash}, ${JSON.stringify(normalized)}, ${now}, ${now})`);
			await writeDefinitionVersion(db, {
				kind: "type",
				key: normalized.key,
				revision: 1,
				canonicalHash: hash,
				definition: normalized,
				origin: "code"
			});
			changedKeys.push(`type:${normalized.key}`);
		} else if (existing.canonicalHash !== hash || existing.origin !== "code" || !__contentRegistryInternals.bool(existing.active)) {
			const version = Number(existing.version) + 1;
			const revision = Number(existing.currentRevision) + 1;
			await db.execute(sql`UPDATE content_type_definitions SET origin = 'code', version = ${version}, active = ${true}, current_revision = ${revision}, canonical_hash = ${hash}, definition = ${JSON.stringify(normalized)}, shadowed_db_version = ${existing.origin === "db" ? Number(existing.version) : existing.shadowedDbVersion}, updated_at = ${now} WHERE key = ${normalized.key}`);
			await writeDefinitionVersion(db, {
				kind: "type",
				key: normalized.key,
				revision,
				canonicalHash: hash,
				definition: normalized,
				origin: "code"
			});
			changedKeys.push(`type:${normalized.key}`);
		}
	}
	for (const existing of currentTypeRows.filter((row) => row.origin === "code" && !nextTypeMap.has(row.key) && __contentRegistryInternals.bool(row.active))) {
		const current = resolvedTypeRow(existing);
		const inactive = normalizeContentTypeDefinition({
			...current,
			active: false
		});
		const version = current.version + 1;
		const revision = current.revision + 1;
		const hash = await canonicalContentMigrationHash(inactive);
		const now = (/* @__PURE__ */ new Date()).toISOString();
		await db.execute(sql`UPDATE content_type_definitions SET version = ${version}, active = ${false}, current_revision = ${revision}, canonical_hash = ${hash}, definition = ${JSON.stringify(inactive)}, updated_at = ${now} WHERE key = ${existing.key}`);
		await writeDefinitionVersion(db, {
			kind: "type",
			key: existing.key,
			revision,
			canonicalHash: hash,
			definition: inactive,
			origin: "code"
		});
		changedKeys.push(`type:${existing.key}`);
	}
	for (const definition of next.statuses) {
		const normalized = normalizeContentStatusDefinition(definition);
		const hash = await canonicalContentMigrationHash(normalized);
		const existing = currentStatusRows.find((row) => row.key === normalized.key);
		const now = (/* @__PURE__ */ new Date()).toISOString();
		if (!existing) {
			await db.execute(sql`INSERT INTO content_status_definitions (key, origin, version, active, current_revision, canonical_hash, definition, created_at, updated_at) VALUES (${normalized.key}, 'code', 1, ${true}, 1, ${hash}, ${JSON.stringify(normalized)}, ${now}, ${now})`);
			await writeDefinitionVersion(db, {
				kind: "status",
				key: normalized.key,
				revision: 1,
				canonicalHash: hash,
				definition: normalized,
				origin: "code"
			});
			changedKeys.push(`status:${normalized.key}`);
		} else if (existing.canonicalHash !== hash || existing.origin !== "code" || !__contentStatusInternals.bool(existing.active)) {
			const version = Number(existing.version) + 1;
			const revision = Number(existing.currentRevision) + 1;
			await db.execute(sql`UPDATE content_status_definitions SET origin = 'code', version = ${version}, active = ${true}, current_revision = ${revision}, canonical_hash = ${hash}, definition = ${JSON.stringify(normalized)}, shadowed_db_version = ${existing.origin === "db" ? Number(existing.version) : existing.shadowedDbVersion}, updated_at = ${now} WHERE key = ${normalized.key}`);
			await writeDefinitionVersion(db, {
				kind: "status",
				key: normalized.key,
				revision,
				canonicalHash: hash,
				definition: normalized,
				origin: "code"
			});
			changedKeys.push(`status:${normalized.key}`);
		}
	}
	for (const existing of currentStatusRows.filter((row) => row.origin === "code" && !nextStatusMap.has(row.key) && __contentStatusInternals.bool(row.active))) {
		const current = resolvedStatusRow(existing);
		const version = current.version + 1;
		const revision = current.revision + 1;
		const nextDefinition = normalizeContentStatusDefinition(current);
		const hash = await canonicalContentMigrationHash(nextDefinition);
		const now = (/* @__PURE__ */ new Date()).toISOString();
		await db.execute(sql`UPDATE content_status_definitions SET version = ${version}, active = ${false}, current_revision = ${revision}, canonical_hash = ${hash}, definition = ${JSON.stringify(nextDefinition)}, updated_at = ${now} WHERE key = ${existing.key}`);
		await writeDefinitionVersion(db, {
			kind: "status",
			key: existing.key,
			revision,
			canonicalHash: hash,
			definition: nextDefinition,
			origin: "code"
		});
		changedKeys.push(`status:${existing.key}`);
	}
	const result = next;
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:reconcile:code",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "codeRegistryReconciled",
		operationId: input.operationId,
		principalId: principal.id,
		fromVersion: plan.currentVersion,
		toVersion: next.version,
		changedKeys: Object.freeze(changedKeys.sort()),
		strategiesHash: await canonicalContentMigrationHash({
			typeStrategies: input.typeStrategies,
			statusStrategies: input.statusStrategies
		}),
		authorizationPolicyVersion: policyVersion,
		corpusHashes: Object.freeze([...new Set(corpusHashes)].sort()),
		affectedCounts: Object.freeze(affectedCounts),
		backupIds: Object.freeze([...new Set(verifiedBackups)].sort())
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
function own(record, key) {
	return Object.prototype.hasOwnProperty.call(record, key);
}
function parseBackupPayload(value) {
	if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ContentDefinitionError("backup-mismatch", "backup payload must be an object");
	const record = value;
	if (record.version !== 1) throw new ContentDefinitionError("backup-mismatch", "backup payload version must be 1");
	for (const section of [
		"entries",
		"typeDefinitions",
		"statusDefinitions"
	]) if (record[section] !== void 0 && !Array.isArray(record[section])) throw new ContentDefinitionError("backup-mismatch", `${section} must be an array`);
	const payload = record;
	const entryIds = /* @__PURE__ */ new Set();
	for (const entry of payload.entries ?? []) {
		if (typeof entry !== "object" || entry === null || typeof entry.id !== "string" || entry.id.length === 0) throw new ContentDefinitionError("backup-mismatch", "backup entry snapshots require a non-empty id");
		if (entryIds.has(entry.id)) throw new ContentDefinitionError("backup-mismatch", `duplicate backup entry ${entry.id}`);
		entryIds.add(entry.id);
		if (own(entry, "excerpt") && typeof entry.excerpt !== "string") throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} excerpt must be a string`);
		if (own(entry, "parentId") && entry.parentId !== null && typeof entry.parentId !== "string") throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} parentId must be string|null`);
		if (own(entry, "menuOrder") && !Number.isSafeInteger(entry.menuOrder)) throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} menuOrder must be a safe integer`);
		if (own(entry, "templateKey") && entry.templateKey !== null && typeof entry.templateKey !== "string") throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} templateKey must be string|null`);
		if (own(entry, "format") && entry.format !== null && typeof entry.format !== "string") throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} format must be string|null`);
		if (![
			"excerpt",
			"featuredMedia",
			"parentId",
			"menuOrder",
			"templateKey",
			"format"
		].some((key) => own(entry, key))) throw new ContentDefinitionError("backup-mismatch", `entry ${entry.id} snapshot contains no restorable fields`);
	}
	const typeKeys = /* @__PURE__ */ new Set();
	for (const item of payload.typeDefinitions ?? []) {
		if (typeof item !== "object" || item === null || typeof item.key !== "string") throw new ContentDefinitionError("backup-mismatch", "type definition snapshot is malformed");
		if (typeKeys.has(item.key)) throw new ContentDefinitionError("backup-mismatch", `duplicate type definition ${item.key}`);
		typeKeys.add(item.key);
		if (normalizeContentTypeDefinition(item.definition).key !== item.key) throw new ContentDefinitionError("backup-mismatch", `type definition key mismatch for ${item.key}`);
	}
	const statusKeys = /* @__PURE__ */ new Set();
	for (const item of payload.statusDefinitions ?? []) {
		if (typeof item !== "object" || item === null || typeof item.key !== "string" || typeof item.active !== "boolean") throw new ContentDefinitionError("backup-mismatch", "status definition snapshot is malformed");
		if (statusKeys.has(item.key)) throw new ContentDefinitionError("backup-mismatch", `duplicate status definition ${item.key}`);
		statusKeys.add(item.key);
		if (normalizeContentStatusDefinition(item.definition).key !== item.key) throw new ContentDefinitionError("backup-mismatch", `status definition key mismatch for ${item.key}`);
	}
	return Object.freeze({
		version: 1,
		...payload.entries ? { entries: Object.freeze([...payload.entries]) } : {},
		...payload.typeDefinitions ? { typeDefinitions: Object.freeze([...payload.typeDefinitions]) } : {},
		...payload.statusDefinitions ? { statusDefinitions: Object.freeze([...payload.statusDefinitions]) } : {}
	});
}
function normalizeJsonColumn(value) {
	if (value === null) return null;
	if (typeof value === "string") try {
		return JSON.parse(value);
	} catch {
		return value;
	}
	return value;
}
async function contentBackupDestinationState(db, payload) {
	const entries = [];
	for (const entry of [...payload.entries ?? []].sort((a, b) => a.id.localeCompare(b.id))) {
		const rows = await db.execute(sql`SELECT id, excerpt, featured_media AS "featuredMedia", parent_id AS "parentId", menu_order AS "menuOrder", template_key AS "templateKey", format FROM content_entries WHERE id = ${entry.id}`);
		const row = rows[0];
		if (!row || rows.length !== 1) throw new ContentDefinitionError("backup-mismatch", `restore destination entry ${entry.id} is missing`);
		const state = { id: entry.id };
		if (own(entry, "excerpt")) state.excerpt = String(row.excerpt);
		if (own(entry, "featuredMedia")) state.featuredMedia = normalizeJsonColumn(row.featuredMedia);
		if (own(entry, "parentId")) state.parentId = row.parentId === null ? null : String(row.parentId);
		if (own(entry, "menuOrder")) state.menuOrder = Number(row.menuOrder);
		if (own(entry, "templateKey")) state.templateKey = row.templateKey === null ? null : String(row.templateKey);
		if (own(entry, "format")) state.format = row.format === null ? null : String(row.format);
		entries.push(state);
	}
	const types = [];
	for (const item of [...payload.typeDefinitions ?? []].sort((a, b) => a.key.localeCompare(b.key))) {
		const current = await resolveContentType(db, item.key);
		const refs = await db.execute(sql`SELECT id, type_definition_revision AS revision FROM content_entries WHERE type = ${item.key} ORDER BY id`);
		types.push({
			key: item.key,
			origin: current.origin,
			version: current.version,
			revision: current.revision,
			active: current.active,
			canonicalHash: current.canonicalHash,
			references: refs.map((row) => ({
				id: row.id,
				revision: Number(row.revision)
			}))
		});
	}
	const statuses = [];
	for (const item of [...payload.statusDefinitions ?? []].sort((a, b) => a.key.localeCompare(b.key))) {
		const current = await resolveContentStatus(db, item.key);
		const refs = await db.execute(sql`SELECT id, status_definition_revision AS revision FROM content_entries WHERE status = ${item.key} ORDER BY id`);
		statuses.push({
			key: item.key,
			origin: current.origin,
			version: current.version,
			revision: current.revision,
			active: current.active,
			canonicalHash: current.canonicalHash,
			references: refs.map((row) => ({
				id: row.id,
				revision: Number(row.revision)
			}))
		});
	}
	return {
		version: 1,
		entries,
		typeDefinitions: types,
		statusDefinitions: statuses
	};
}
async function contentBackupDestinationHash(db, payload) {
	return canonicalContentMigrationHash(await contentBackupDestinationState(db, payload));
}
function manifestsMatch(left, right) {
	return left.id === right.id && left.scopeHash === right.scopeHash && left.corpusVersion === right.corpusVersion && left.corpusHash === right.corpusHash && left.byteCount === right.byteCount && left.immutable === true && right.immutable === true;
}
async function restoreContentBackup(db, principal, input, authorization, effects, deps) {
	const { policyVersion } = await authorization.assert(db, principal, "restoreBackup");
	const scope = principalScope$2(principal);
	const requestHash = await canonicalContentMigrationHash({
		manifestId: input.manifest.id,
		destinationCorpusHash: input.destinationCorpusHash
	});
	const replay = await readDefinitionReceipt(db, input.operationId);
	if (replay) {
		if (replay.requestHash !== requestHash || replay.principalScope !== scope) throw new ContentDefinitionError("operation-conflict", "restore operation id was reused with different identity");
		return Object.freeze(replay.result);
	}
	if (!input.manifest.immutable) throw new ContentDefinitionError("backup-mismatch", "restore requires an immutable backup");
	const verified = await deps.backups.verify(db, input.manifest.id, input.manifest.scopeHash, input.manifest.corpusVersion, input.manifest.corpusHash, input.manifest.itemCounts);
	if (!manifestsMatch(verified, input.manifest) || await canonicalContentMigrationHash(verified.itemCounts) !== await canonicalContentMigrationHash(input.manifest.itemCounts)) throw new ContentDefinitionError("backup-mismatch", "restore manifest verification changed identity");
	const snapshot = await deps.backups.read(db, verified);
	if (!manifestsMatch(snapshot.manifest, verified) || await canonicalContentMigrationHash(snapshot.manifest.itemCounts) !== await canonicalContentMigrationHash(verified.itemCounts)) throw new ContentDefinitionError("backup-mismatch", "backup store returned a different snapshot identity");
	const payload = parseBackupPayload(snapshot.payload);
	const affectedTypeKeys = new Set((payload.typeDefinitions ?? []).map((item) => item.key));
	for (const entry of payload.entries ?? []) {
		const rows = await db.execute(sql`SELECT type FROM content_entries WHERE id = ${entry.id}`);
		if (rows.length !== 1) throw new ContentDefinitionError("backup-mismatch", `restore destination entry ${entry.id} is missing`);
		affectedTypeKeys.add(rows[0].type);
	}
	for (const item of payload.statusDefinitions ?? []) {
		const rows = await db.execute(sql`SELECT DISTINCT type FROM content_entries WHERE status = ${item.key} ORDER BY type`);
		for (const row of rows) affectedTypeKeys.add(row.type);
	}
	for (const key of [...affectedTypeKeys].sort()) if ((await authorization.assert(db, principal, "restoreBackup", key)).policyVersion !== policyVersion) throw new ContentDefinitionError("authorization-drift", "authorization policy changed while authorizing restore scope");
	if (await contentBackupDestinationHash(db, payload) !== input.destinationCorpusHash) throw new ContentDefinitionError("stale-impact", "restore destination corpus changed");
	const typeCurrents = /* @__PURE__ */ new Map();
	for (const item of payload.typeDefinitions ?? []) {
		const current = await resolveContentType(db, item.key);
		if (current.origin !== "db") throw new ContentDefinitionError("code-definition-owned", `cannot restore deploy-owned type ${item.key}`);
		typeCurrents.set(item.key, current);
	}
	const statusCurrents = /* @__PURE__ */ new Map();
	for (const item of payload.statusDefinitions ?? []) {
		const current = await resolveContentStatus(db, item.key);
		if (current.origin !== "db") throw new ContentDefinitionError("code-definition-owned", `cannot restore deploy-owned status ${item.key}`);
		statusCurrents.set(item.key, current);
	}
	const restoredCounts = {};
	const now = (/* @__PURE__ */ new Date()).toISOString();
	for (const entry of payload.entries ?? []) {
		const assignments = [];
		if (own(entry, "excerpt")) assignments.push(sql`excerpt = ${entry.excerpt}`);
		if (own(entry, "featuredMedia")) assignments.push(sql`featured_media = ${entry.featuredMedia === null ? null : JSON.stringify(entry.featuredMedia)}`);
		if (own(entry, "parentId")) assignments.push(sql`parent_id = ${entry.parentId ?? null}`);
		if (own(entry, "menuOrder")) assignments.push(sql`menu_order = ${entry.menuOrder}`);
		if (own(entry, "templateKey")) assignments.push(sql`template_key = ${entry.templateKey ?? null}`);
		if (own(entry, "format")) assignments.push(sql`format = ${entry.format ?? null}`);
		if ((await db.execute(sql`UPDATE content_entries SET ${sql.join(assignments, sql`, `)}, updated_at = ${now} WHERE id = ${entry.id} RETURNING id`)).length !== 1) throw new ContentDefinitionError("stale-impact", `restore destination entry ${entry.id} changed or disappeared`);
	}
	if ((payload.entries ?? []).length > 0) restoredCounts.entries = payload.entries.length;
	for (const item of payload.typeDefinitions ?? []) {
		const current = typeCurrents.get(item.key);
		const next = normalizeContentTypeDefinition(item.definition);
		const revision = current.revision + 1;
		const version = current.version + 1;
		const hash = await canonicalContentMigrationHash(next);
		await writeDefinitionVersion(db, {
			kind: "type",
			key: item.key,
			revision,
			canonicalHash: hash,
			definition: next,
			origin: "db"
		});
		if ((await db.execute(sql`UPDATE content_type_definitions SET version = ${version}, current_revision = ${revision}, active = ${next.active}, canonical_hash = ${hash}, definition = ${JSON.stringify(next)}, updated_at = ${now} WHERE key = ${item.key} AND version = ${current.version} RETURNING key`)).length !== 1) throw new ContentDefinitionError("stale-impact", `type definition ${item.key} changed during restore`);
		await db.execute(sql`UPDATE content_entries SET type_definition_revision = ${revision} WHERE type = ${item.key}`);
	}
	if ((payload.typeDefinitions ?? []).length > 0) restoredCounts.typeDefinitions = payload.typeDefinitions.length;
	for (const item of payload.statusDefinitions ?? []) {
		const current = statusCurrents.get(item.key);
		const next = normalizeContentStatusDefinition(item.definition);
		const revision = current.revision + 1;
		const version = current.version + 1;
		const hash = await canonicalContentMigrationHash(next);
		await writeDefinitionVersion(db, {
			kind: "status",
			key: item.key,
			revision,
			canonicalHash: hash,
			definition: next,
			origin: "db"
		});
		if ((await db.execute(sql`UPDATE content_status_definitions SET version = ${version}, current_revision = ${revision}, active = ${item.active}, canonical_hash = ${hash}, definition = ${JSON.stringify(next)}, updated_at = ${now} WHERE key = ${item.key} AND version = ${current.version} RETURNING key`)).length !== 1) throw new ContentDefinitionError("stale-impact", `status definition ${item.key} changed during restore`);
		await db.execute(sql`UPDATE content_entries SET status_definition_revision = ${revision} WHERE status = ${item.key}`);
	}
	if ((payload.statusDefinitions ?? []).length > 0) restoredCounts.statusDefinitions = payload.statusDefinitions.length;
	const result = Object.freeze({
		operationId: input.operationId,
		manifestId: verified.id,
		destinationCorpusHash: input.destinationCorpusHash,
		restoredCounts: Object.freeze(restoredCounts)
	});
	await writeDefinitionReceipt(db, {
		operationId: input.operationId,
		kind: "definition:restore",
		payload: {
			requestHash,
			principalScope: scope,
			result
		}
	});
	const event = {
		id: crypto.randomUUID(),
		version: 1,
		kind: "backupRestored",
		operationId: input.operationId,
		principalId: principal.id,
		manifestId: verified.id,
		destinationCorpusHash: input.destinationCorpusHash,
		restoredCounts: result.restoredCounts,
		authorizationPolicyVersion: policyVersion
	};
	await effects.audit.record(db, event);
	await effects.outbox.enqueue(db, event);
	return result;
}
//#endregion
//#region src/hierarchy.ts
var ContentHierarchyError = class extends Error {
	code;
	detail;
	name = "ContentHierarchyError";
	constructor(code, detail) {
		super(`content hierarchy ${code}: ${detail}`);
		this.code = code;
		this.detail = detail;
	}
};
function date$2(value) {
	const parsed = value instanceof Date ? new Date(value.getTime()) : new Date(value);
	if (Number.isNaN(parsed.getTime())) throw new ContentHierarchyError("stale-plan", "stored entry version is invalid");
	return parsed;
}
async function row(tx, id) {
	return (await tx.execute(sql`SELECT id, type, status, visibility, author,
    parent_id AS "parentId", updated_at AS "updatedAt", deleted_at AS "deletedAt",
    type_definition_revision AS "typeDefinitionRevision", status_definition_revision AS "statusDefinitionRevision"
    FROM content_entries WHERE id = ${id}`))[0];
}
async function validateContentParent(tx, input) {
	assertActiveContentTransaction(tx);
	if (input.parentId === null) return;
	if (!input.type.hierarchical) throw new ContentHierarchyError("invalid-parent", "flat content types cannot have parents");
	const parent = await row(tx, input.parentId);
	if (!parent || parent.deletedAt !== null || parent.type !== input.type.key) throw new ContentHierarchyError("invalid-parent", "parent is unavailable or belongs to another type");
	const seen = /* @__PURE__ */ new Set();
	let current = parent;
	for (let depth = 0; current; depth++) {
		if (depth > 1e3) throw new ContentHierarchyError("cycle", "hierarchy exceeds the maximum supported depth");
		if (input.entryId && current.id === input.entryId) throw new ContentHierarchyError("cycle", "parent would create a cycle");
		if (seen.has(current.id)) throw new ContentHierarchyError("cycle", "existing hierarchy contains a cycle");
		seen.add(current.id);
		if (current.parentId === null) break;
		current = await row(tx, current.parentId);
		if (!current) throw new ContentHierarchyError("invalid-parent", "ancestor chain is incomplete");
		if (current.type !== input.type.key || current.deletedAt !== null) throw new ContentHierarchyError("invalid-parent", "ancestor chain leaves the active type hierarchy");
	}
}
function principalAuthorizationVersion(principal) {
	return [...principal.capabilities].sort().join("\0");
}
async function authorizeDeleteRow(tx, principal, candidate) {
	try {
		const [type, status] = await Promise.all([resolveContentType(tx, candidate.type), resolveContentStatus(tx, candidate.status)]);
		if (Number(candidate.typeDefinitionRevision) !== type.revision || Number(candidate.statusDefinitionRevision) !== status.revision) throw new Error("definition revision mismatch");
		authorizeContentAction({
			principal,
			type,
			status,
			operation: "delete",
			entry: candidate
		});
	} catch {
		throw new ContentHierarchyError("access-denied", "selected content is unavailable");
	}
}
async function authorizeEditRow(tx, principal, candidate) {
	try {
		const [type, status] = await Promise.all([resolveContentType(tx, candidate.type), resolveContentStatus(tx, candidate.status)]);
		if (Number(candidate.typeDefinitionRevision) !== type.revision || Number(candidate.statusDefinitionRevision) !== status.revision) throw new Error("definition revision mismatch");
		authorizeContentAction({
			principal,
			type,
			status,
			operation: "edit",
			entry: candidate
		});
	} catch {
		throw new ContentHierarchyError("access-denied", "selected content is unavailable");
	}
}
async function preparePermanentContentDelete(tx, items, principal) {
	assertActiveContentTransaction(tx);
	if (!Array.isArray(items) || items.length === 0 || items.length > 500) throw new ContentHierarchyError("stale-plan", "delete selection must contain 1..500 entries");
	const ids = /* @__PURE__ */ new Set();
	const selectedRows = /* @__PURE__ */ new Map();
	for (const item of items) {
		if (!item || typeof item.id !== "string" || !item.id || !(item.expectedUpdatedAt instanceof Date) || Number.isNaN(item.expectedUpdatedAt.getTime()) || ids.has(item.id)) throw new ContentHierarchyError("stale-plan", "delete selection is malformed or duplicated");
		ids.add(item.id);
		const candidate = await row(tx, item.id);
		if (!candidate || candidate.deletedAt !== null || date$2(candidate.updatedAt).getTime() !== item.expectedUpdatedAt.getTime()) throw new ContentHierarchyError("access-denied", "selected content is unavailable");
		await authorizeDeleteRow(tx, principal, candidate);
		selectedRows.set(item.id, candidate);
	}
	const reparent = [];
	const selectedSorted = [...selectedRows.values()].sort((a, b) => a.id.localeCompare(b.id));
	for (const parent of selectedSorted) {
		const children = await tx.execute(sql`SELECT id, type, status, visibility, author,
      parent_id AS "parentId", updated_at AS "updatedAt", deleted_at AS "deletedAt",
    type_definition_revision AS "typeDefinitionRevision", status_definition_revision AS "statusDefinitionRevision"
      FROM content_entries WHERE parent_id = ${parent.id} ORDER BY id`);
		for (const child of children) {
			if (ids.has(child.id) || child.deletedAt !== null) continue;
			let targetId = parent.parentId;
			const ancestorSeen = /* @__PURE__ */ new Set();
			while (targetId !== null && ids.has(targetId)) {
				if (ancestorSeen.has(targetId)) throw new ContentHierarchyError("cycle", "selected hierarchy contains a cycle");
				ancestorSeen.add(targetId);
				const ancestor = selectedRows.get(targetId) ?? await row(tx, targetId);
				if (!ancestor) throw new ContentHierarchyError("stale-plan", "selected ancestor disappeared");
				targetId = ancestor.parentId;
			}
			await authorizeEditRow(tx, principal, child);
			reparent.push({
				childId: child.id,
				fromParentId: parent.id,
				toParentId: targetId,
				expectedUpdatedAt: date$2(child.updatedAt)
			});
		}
	}
	reparent.sort((a, b) => a.childId.localeCompare(b.childId));
	const selected = [...items].sort((a, b) => a.id.localeCompare(b.id)).map((item) => Object.freeze({
		id: item.id,
		expectedUpdatedAt: new Date(item.expectedUpdatedAt)
	}));
	const corpusVersion = await canonicalContentMigrationHash({
		selected: selected.map((item) => ({
			id: item.id,
			updatedAt: item.expectedUpdatedAt.toISOString(),
			parentId: selectedRows.get(item.id)?.parentId ?? null
		})),
		reparent: reparent.map((item) => ({
			...item,
			expectedUpdatedAt: item.expectedUpdatedAt.toISOString()
		}))
	});
	const authorizationVersion = await canonicalContentMigrationHash({
		id: principal.id,
		system: principal.system === true,
		capabilities: principalAuthorizationVersion(principal)
	});
	const token = await canonicalContentMigrationHash({
		corpusVersion,
		authorizationVersion,
		selected: selected.map((item) => ({
			id: item.id,
			expectedUpdatedAt: item.expectedUpdatedAt.toISOString()
		})),
		reparent: reparent.map((item) => ({
			...item,
			expectedUpdatedAt: item.expectedUpdatedAt.toISOString()
		}))
	});
	return Object.freeze({
		token,
		selected: Object.freeze(selected),
		reparent: Object.freeze(reparent.map((item) => Object.freeze({ ...item }))),
		corpusVersion,
		authorizationVersion
	});
}
//#endregion
//#region src/lifecycle.ts
var ContentLifecycleError = class extends Error {
	code;
	detail;
	field;
	name = "ContentLifecycleError";
	constructor(code, detail, field) {
		super(`content lifecycle ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function principalScope$1(principal) {
	return `${principal.tenantId ?? ""}:${principal.id}`;
}
function bool$2(value) {
	return value === true || value === 1 || value === "1" || value === "true";
}
function asDate(value, field) {
	const parsed = value instanceof Date ? new Date(value.getTime()) : new Date(value);
	if (Number.isNaN(parsed.getTime())) throw new ContentLifecycleError("integrity", "stored timestamp is invalid", field);
	return parsed;
}
function parseJson$1(value) {
	if (value === null) return null;
	if (typeof value === "string") try {
		return JSON.parse(value);
	} catch {
		return value;
	}
	return value;
}
function plainObject(value) {
	if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ContentLifecycleError("integrity", "stored object is malformed");
	return value;
}
function nonEmptyString(value, field, max) {
	if (typeof value !== "string") throw new ContentLifecycleError("validation", "must be a string", field);
	const normalized = value.trim();
	if (!normalized || normalized.length > max) throw new ContentLifecycleError("validation", `must contain 1..${max} characters`, field);
	return normalized;
}
function nullableString$1(value, field, max) {
	if (value === null) return null;
	return nonEmptyString(value, field, max);
}
function exactDate(value, field) {
	if (!(value instanceof Date) || Number.isNaN(value.getTime())) throw new ContentLifecycleError("validation", "must be a valid Date", field);
	return new Date(value.getTime());
}
function uniqueStrings(value, field, max = 1e4) {
	if (!Array.isArray(value) || value.length > max) throw new ContentLifecycleError("validation", `must be an array with at most ${max} items`, field);
	const out = [];
	const seen = /* @__PURE__ */ new Set();
	for (const item of value) {
		const string = nonEmptyString(item, field, 256);
		if (!seen.has(string)) {
			seen.add(string);
			out.push(string);
		}
	}
	return out;
}
function entryBase(row, terms) {
	const media = parseJson$1(row.featuredMedia);
	return Object.freeze({
		id: row.id,
		slug: row.slug,
		type: row.type,
		title: row.title,
		body: row.body,
		status: row.status,
		visibility: row.visibility,
		publishedAt: row.publishedAt === null ? null : asDate(row.publishedAt, "publishedAt"),
		author: row.author,
		terms: Object.freeze([...terms]),
		createdAt: asDate(row.createdAt, "createdAt"),
		updatedAt: asDate(row.updatedAt, "updatedAt"),
		parentId: row.parentId,
		menuOrder: Number(row.menuOrder),
		templateKey: row.templateKey,
		excerpt: row.excerpt,
		featuredMedia: media === null ? null : plainObject(media),
		commentStatus: row.commentStatus,
		pingStatus: row.pingStatus,
		passwordProtected: bool$2(row.passwordProtected),
		sticky: bool$2(row.sticky),
		format: row.format,
		deletedAt: row.deletedAt === null ? null : asDate(row.deletedAt, "deletedAt"),
		lastEditedBy: row.lastEditedBy,
		typeDefinitionRevision: Number(row.typeDefinitionRevision),
		statusDefinitionRevision: Number(row.statusDefinitionRevision)
	});
}
const ENTRY_SELECT$1 = sql.raw(`id, slug, type, title, body, status, visibility,
  published_at AS "publishedAt", author, created_at AS "createdAt", updated_at AS "updatedAt",
  parent_id AS "parentId", menu_order AS "menuOrder", template_key AS "templateKey", excerpt,
  featured_media AS "featuredMedia", comment_status AS "commentStatus", ping_status AS "pingStatus",
  sticky, format, deleted_at AS "deletedAt", last_edited_by AS "lastEditedBy",
  type_definition_revision AS "typeDefinitionRevision", status_definition_revision AS "statusDefinitionRevision",
  EXISTS (SELECT 1 FROM content_password_credentials c WHERE c.entry_id = content_entries.id) AS "passwordProtected"`);
async function termsForEntry(tx, entryId) {
	return (await tx.execute(sql`
    SELECT t.id, t.taxonomy, t.slug, t.name, t.parent_id AS "parentId", t.depth
    FROM content_entry_terms et INNER JOIN content_terms t ON t.id = et.term_id
    WHERE et.entry_id = ${entryId} ORDER BY t.depth, t.name, t.id`)).map((row) => Object.freeze({
		id: row.id,
		taxonomy: row.taxonomy,
		slug: row.slug,
		name: row.name,
		parentId: row.parentId,
		depth: Number(row.depth)
	}));
}
async function readEntry(tx, id) {
	const rows = await tx.execute(sql`SELECT ${ENTRY_SELECT$1} FROM content_entries WHERE id = ${id}`);
	if (!rows[0]) return void 0;
	return entryBase(rows[0], await termsForEntry(tx, id));
}
async function requireEntry(tx, id) {
	const entry = await readEntry(tx, id);
	if (!entry) throw new ContentLifecycleError("access-denied", "content is unavailable");
	return entry;
}
function schemaValue$2(value) {
	return JSON.parse(JSON.stringify(value));
}
async function requestHash(value) {
	return canonicalContentMigrationHash(schemaValue$2(value));
}
async function readReceipt$1(tx, operationId) {
	const rows = await tx.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id = ${operationId}`);
	if (!rows[0]) return void 0;
	const receipt = typeof rows[0].payload === "string" ? JSON.parse(rows[0].payload) : rows[0].payload;
	if (!receipt || typeof receipt.requestHash !== "string" || typeof receipt.principalScope !== "string" || receipt.result === void 0) throw new ContentLifecycleError("integrity", "operation receipt is malformed");
	return receipt;
}
async function writeReceipt(tx, input) {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const payload = {
		requestHash: input.requestHash,
		principalScope: principalScope$1(input.principal),
		result: schemaValue$2(input.result)
	};
	await tx.execute(sql`INSERT INTO content_lifecycle_journal (id, operation_id, kind, entry_id, state, payload, created_at, updated_at)
    VALUES (${crypto.randomUUID()}, ${input.operationId}, ${input.kind}, ${input.entryId ?? null}, 'committed', ${JSON.stringify(payload)}, ${now}, ${now})`);
}
async function replayEntry(tx, receipt, expectedHash, principal) {
	if (receipt.requestHash !== expectedHash || receipt.principalScope !== principalScope$1(principal)) throw new ContentLifecycleError("operation-conflict", "operation id was already used for different input or principal");
	const result = plainObject(receipt.result);
	if (typeof result.id !== "string") return void 0;
	return readEntry(tx, result.id);
}
async function eventId(operationId, kind, entryId) {
	return canonicalContentMigrationHash({
		operationId,
		kind,
		entryId
	});
}
async function enqueue(tx, outbox, operationId, kind, entryId, occurredAt) {
	await outbox.enqueue(tx, {
		id: await eventId(operationId, kind, entryId),
		version: 1,
		operationId,
		kind,
		entryId,
		occurredAt
	});
}
function mutationContext(operationId, principal, type, before, proposed) {
	return Object.freeze({
		operationId,
		principal,
		type,
		before: before ? Object.freeze(before) : null,
		proposed: proposed ? Object.freeze(proposed) : null
	});
}
async function runValidateHooks(hooks, ctx) {
	await hooks?.beforeValidate?.(ctx);
	await hooks?.afterValidate?.(ctx);
}
async function runBeforeCommit(hooks, ctx) {
	await hooks?.beforeCommit?.(ctx);
}
function assertFeature(type, feature, field) {
	if (!type.supports.includes(feature)) throw new ContentLifecycleError("validation", `${field} is not supported by content type ${type.key}`, field);
}
function assertTypeActive(type) {
	if (!type.active) throw new ContentLifecycleError("validation", "content type is inactive", "type");
}
function assertVisibility(value) {
	if (value !== "public" && value !== "private" && value !== "members") throw new ContentLifecycleError("validation", "must be public, private, or members", "visibility");
	return value;
}
function assertMediaRef(value) {
	if (value === null) return null;
	if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ContentLifecycleError("validation", "must be an opaque media reference", "featuredMedia");
	const ref = value;
	const id = nonEmptyString(ref.id, "featuredMedia.id", 256);
	if (Object.keys(ref).some((key) => key !== "id" && key !== "kind")) throw new ContentLifecycleError("validation", "contains unsupported media reference keys", "featuredMedia");
	const kind = ref.kind === void 0 ? void 0 : nonEmptyString(ref.kind, "featuredMedia.kind", 128);
	return Object.freeze({
		id,
		...kind ? { kind } : {}
	});
}
async function validateTerms(tx, type, termIds) {
	if (termIds.length === 0) return;
	const rows = await tx.execute(sql`SELECT id, taxonomy FROM content_terms WHERE id IN (${sql.join(termIds.map((id) => sql`${id}`), sql`, `)})`);
	if (rows.length !== termIds.length) throw new ContentLifecycleError("validation", "one or more terms are unavailable", "termIds");
	const found = new Set(rows.map((row) => row.id));
	if (termIds.some((id) => !found.has(id))) throw new ContentLifecycleError("validation", "one or more terms are unavailable", "termIds");
	for (const row of rows) if (!type.taxonomies.includes(row.taxonomy)) throw new ContentLifecycleError("validation", `taxonomy ${row.taxonomy} is not assigned to content type ${type.key}`, "termIds");
}
async function replaceTerms(tx, entryId, termIds) {
	await tx.execute(sql`DELETE FROM content_entry_terms WHERE entry_id = ${entryId}`);
	for (const termId of termIds) await tx.execute(sql`INSERT INTO content_entry_terms (entry_id, term_id) VALUES (${entryId}, ${termId})`);
}
async function initialStatus(tx, type) {
	const statuses = (await resolveContentStatuses(tx)).filter((status) => status.active && (type.statusKeys === void 0 || type.statusKeys.includes(status.key)));
	const found = statuses.find((status) => status.key === "draft") ?? statuses.find((status) => !status.published && !status.internal) ?? statuses[0];
	if (!found) throw new ContentLifecycleError("integrity", "content type has no active status");
	return found;
}
async function resolveExactTypeStatus(tx, entry) {
	try {
		const [type, status] = await Promise.all([resolveContentType(tx, entry.type), resolveContentStatus(tx, entry.status)]);
		if (entry.typeDefinitionRevision !== type.revision || entry.statusDefinitionRevision !== status.revision) throw new ContentLifecycleError("integrity", "entry references stale definition revisions");
		return {
			type,
			status
		};
	} catch (error) {
		if (error instanceof ContentLifecycleError) throw error;
		throw new ContentLifecycleError("integrity", "entry definition metadata is unavailable");
	}
}
function sanitizeHtml(sanitize, value, field) {
	if (typeof sanitize !== "function") throw new ContentLifecycleError("capability-unavailable", "HTML sanitizer is required", field);
	if (typeof value !== "string") throw new ContentLifecycleError("validation", "must be a string", field);
	const safe = sanitize(value);
	if (typeof safe !== "string") throw new ContentLifecycleError("integrity", "sanitizer must return a string", field);
	return safe;
}
function validateTemplate(type, key, templates) {
	if (key === void 0) return void 0;
	assertFeature(type, "pageAttributes", "templateKey");
	if (key === null) return null;
	const normalized = nonEmptyString(key, "templateKey", 256);
	const descriptor = templates.resolve(type.key, normalized);
	if (!descriptor || !descriptor.typeKeys.includes(type.key)) throw new ContentLifecycleError("validation", "template is unavailable for this content type", "templateKey");
	return normalized;
}
async function validateParent(tx, type, entryId, parentId) {
	if (parentId === void 0) return void 0;
	if (parentId !== null) nonEmptyString(parentId, "parentId", 256);
	if (!type.hierarchical && parentId !== null) throw new ContentLifecycleError("validation", "flat content types cannot have parents", "parentId");
	await validateContentParent(tx, {
		...entryId ? { entryId } : {},
		type,
		parentId
	});
	return parentId;
}
function normalizeMenuOrder(type, value) {
	if (value === void 0) return void 0;
	assertFeature(type, "pageAttributes", "menuOrder");
	if (typeof value !== "number" || !Number.isSafeInteger(value) || value < -2147483648 || value > 2147483647) throw new ContentLifecycleError("validation", "must be a 32-bit integer", "menuOrder");
	return value;
}
function normalizeCommentStatus(type, value) {
	if (value === void 0) return void 0;
	assertFeature(type, "comments", "commentStatus");
	if (value !== "open" && value !== "closed") throw new ContentLifecycleError("validation", "must be open or closed", "commentStatus");
	return value;
}
function normalizePingStatus(type, value) {
	if (value === void 0) return void 0;
	assertFeature(type, "trackbacks", "pingStatus");
	if (value !== "open" && value !== "closed") throw new ContentLifecycleError("validation", "must be open or closed", "pingStatus");
	return value;
}
function isUniqueViolation$1(error) {
	let current = error;
	while (current) {
		const code = current.code;
		const message = current instanceof Error ? current.message : String(current);
		if (code === "23505" || /content_entries_type_slug_uq|duplicate key|unique constraint/i.test(message)) return true;
		current = current instanceof Error ? current.cause : void 0;
	}
	return false;
}
async function termRefsByIds(tx, termIds) {
	if (termIds.length === 0) return [];
	return (await tx.execute(sql`
    SELECT id, taxonomy, slug, name, parent_id AS "parentId", depth FROM content_terms
    WHERE id IN (${sql.join(termIds.map((id) => sql`${id}`), sql`, `)}) ORDER BY depth, name, id`)).map((row) => Object.freeze({
		id: row.id,
		taxonomy: row.taxonomy,
		slug: row.slug,
		name: row.name,
		parentId: row.parentId,
		depth: Number(row.depth)
	}));
}
function revisionSnapshot(entry) {
	return schemaValue$2({
		id: entry.id,
		slug: entry.slug,
		type: entry.type,
		title: entry.title,
		body: entry.body,
		status: entry.status,
		visibility: entry.visibility,
		publishedAt: entry.publishedAt?.toISOString() ?? null,
		author: entry.author,
		termIds: entry.terms.map((term) => term.id),
		parentId: entry.parentId,
		menuOrder: entry.menuOrder,
		templateKey: entry.templateKey,
		excerpt: entry.excerpt,
		featuredMedia: entry.featuredMedia,
		commentStatus: entry.commentStatus,
		pingStatus: entry.pingStatus,
		passwordProtected: entry.passwordProtected,
		sticky: entry.sticky,
		format: entry.format,
		deletedAt: entry.deletedAt?.toISOString() ?? null,
		lastEditedBy: entry.lastEditedBy,
		typeDefinitionRevision: entry.typeDefinitionRevision,
		statusDefinitionRevision: entry.statusDefinitionRevision,
		createdAt: entry.createdAt.toISOString(),
		updatedAt: entry.updatedAt.toISOString()
	});
}
async function snapshotRevision(tx, entry, editor) {
	const id = crypto.randomUUID();
	await tx.execute(sql`INSERT INTO content_revisions
    (id, entry_id, title, body, slug, type, term_ids, snapshot, editor, created_at)
    VALUES (${id}, ${entry.id}, ${entry.title}, ${entry.body}, ${entry.slug}, ${entry.type},
      ${JSON.stringify(entry.terms.map((term) => term.id))}, ${JSON.stringify(revisionSnapshot(entry))}, ${editor}, ${(/* @__PURE__ */ new Date()).toISOString()})`);
	return id;
}
/**
* Prepare a parent content revision for one host-owned D1 batch. This function
* performs authorization against caller-held evidence before preflight reads;
* no mutation is executed here.
*/
async function prepareContentRevisionD1(db, input, principal) {
	const expected = input.entry;
	const entryId = nonEmptyString(expected.id, "entry.id", 256);
	const expectedUpdatedAt = exactDate(expected.updatedAt, "entry.updatedAt");
	if (input.type.key !== expected.type || input.type.revision !== expected.typeDefinitionRevision || input.status.key !== expected.status || input.status.revision !== expected.statusDefinitionRevision) throw new ContentLifecycleError("integrity", "caller-held entry definition pins are inconsistent");
	authorizeContentAction({
		principal,
		type: input.type,
		status: input.status,
		operation: "edit",
		entry: expected
	});
	if (!input.type.supports.includes("revisions")) throw new ContentLifecycleError("capability-unavailable", "revisions are disabled for this content type");
	const before = await requireEntry(db, entryId);
	const { type, status } = await resolveExactTypeStatus(db, before);
	if (JSON.stringify(revisionSnapshot(before)) !== JSON.stringify(revisionSnapshot(expected)) || type.revision !== input.type.revision || type.canonicalHash !== input.type.canonicalHash || status.revision !== input.status.revision || status.canonicalHash !== input.status.canonicalHash) throw new ContentLifecycleError("stale-version", "content changed after caller read it");
	const revisionId = crypto.randomUUID();
	const createdAt = (/* @__PURE__ */ new Date()).toISOString();
	const expectedIso = expectedUpdatedAt.toISOString();
	const expectedTermIds = Object.freeze([...expected.terms.map((term) => term.id)].sort());
	const termSetGuard = expectedTermIds.length === 0 ? sql`NOT EXISTS (SELECT 1 FROM content_entry_terms WHERE entry_id=${entryId})` : sql`NOT EXISTS (
        SELECT term_id FROM content_entry_terms WHERE entry_id=${entryId}
        EXCEPT SELECT column1 FROM (VALUES ${sql.join(expectedTermIds.map((termId) => sql`(${termId})`), sql`,`)})
      ) AND NOT EXISTS (
        SELECT column1 FROM (VALUES ${sql.join(expectedTermIds.map((termId) => sql`(${termId})`), sql`,`)})
        EXCEPT SELECT term_id FROM content_entry_terms WHERE entry_id=${entryId}
      )`;
	const guard = db.prepare(sql`SELECT CASE WHEN
    EXISTS (SELECT 1 FROM content_entries WHERE id=${entryId} AND updated_at=${expectedIso}
      AND type=${before.type} AND status=${before.status}
      AND type_definition_revision=${before.typeDefinitionRevision}
      AND status_definition_revision=${before.statusDefinitionRevision})
    AND EXISTS (SELECT 1 FROM content_type_definitions WHERE key=${before.type} AND active=true
      AND current_revision=${type.revision} AND canonical_hash=${type.canonicalHash})
    AND EXISTS (SELECT 1 FROM content_status_definitions WHERE key=${before.status} AND active=true
      AND current_revision=${status.revision} AND canonical_hash=${status.canonicalHash})
    AND ${termSetGuard}
    THEN 1 ELSE json_extract('not-json', '$') END AS ok`);
	const insert = db.prepare(sql`INSERT INTO content_revisions
    (id, entry_id, title, body, slug, type, term_ids, snapshot, editor, created_at)
    VALUES (${revisionId}, ${before.id}, ${before.title}, ${before.body}, ${before.slug}, ${before.type},
      ${JSON.stringify(expectedTermIds)}, ${JSON.stringify(revisionSnapshot(before))}, ${principal.id}, ${createdAt})`);
	return Object.freeze({
		items: Object.freeze([guard, insert]),
		result: Object.freeze({
			revisionId,
			entryId,
			expectedUpdatedAt: expectedIso
		})
	});
}
async function normalizeWrite(tx, type, raw, deps, principal, mode, entryId) {
	if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new ContentLifecycleError("validation", "input must be an object");
	const candidate = raw;
	const allowed = /* @__PURE__ */ new Set([
		"slug",
		"type",
		"title",
		"body",
		"excerpt",
		"visibility",
		"author",
		"termIds",
		"parentId",
		"menuOrder",
		"templateKey",
		"featuredMedia",
		"commentStatus",
		"pingStatus",
		"sticky",
		"format"
	]);
	for (const key of Object.keys(candidate)) if (!allowed.has(key)) throw new ContentLifecycleError("validation", "field is owned by another lifecycle operation", key);
	if (mode === "update" && Object.prototype.hasOwnProperty.call(candidate, "type")) throw new ContentLifecycleError("validation", "content type reassignment requires definition migration machinery", "type");
	if (mode === "update" && Object.keys(candidate).length === 0) throw new ContentLifecycleError("validation", "empty update patches are not allowed");
	const out = {};
	if (mode === "create" || Object.prototype.hasOwnProperty.call(candidate, "slug")) out.slug = nonEmptyString(candidate.slug, "slug", 200);
	if (mode === "create" || Object.prototype.hasOwnProperty.call(candidate, "title")) {
		if (type.supports.includes("title")) out.title = nonEmptyString(candidate.title, "title", 300);
		else {
			if (candidate.title !== "") throw new ContentLifecycleError("validation", "title is not supported by this content type", "title");
			out.title = "";
		}
	}
	if (mode === "create" || Object.prototype.hasOwnProperty.call(candidate, "body")) {
		if (type.supports.includes("editor") || type.supports.includes("blocks")) out.body = sanitizeHtml(deps.sanitize, candidate.body, "body");
		else {
			if (candidate.body !== "") throw new ContentLifecycleError("validation", "body is not supported by this content type", "body");
			out.body = "";
		}
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "excerpt")) {
		assertFeature(type, "excerpt", "excerpt");
		if (typeof candidate.excerpt !== "string" || candidate.excerpt.length > 1e5) throw new ContentLifecycleError("validation", "must be a string up to 100000 characters", "excerpt");
		out.excerpt = candidate.excerpt;
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "visibility")) out.visibility = assertVisibility(candidate.visibility);
	if (Object.prototype.hasOwnProperty.call(candidate, "author")) {
		assertFeature(type, "author", "author");
		const target = nonEmptyString(candidate.author, "author", 256);
		if (!deps.authors) throw new ContentLifecycleError("capability-unavailable", "author authority adapter is required", "author");
		await deps.authors.assertAssign(principal, target);
		out.author = target;
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "termIds")) {
		const termIds = uniqueStrings(candidate.termIds, "termIds");
		authorizeContentAction({
			principal,
			type,
			operation: "manageTerms"
		});
		await validateTerms(tx, type, termIds);
		out.termIds = termIds;
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "parentId")) out.parentId = await validateParent(tx, type, entryId, candidate.parentId);
	if (Object.prototype.hasOwnProperty.call(candidate, "menuOrder")) out.menuOrder = normalizeMenuOrder(type, candidate.menuOrder);
	if (Object.prototype.hasOwnProperty.call(candidate, "templateKey")) out.templateKey = validateTemplate(type, candidate.templateKey, deps.templates);
	if (Object.prototype.hasOwnProperty.call(candidate, "featuredMedia")) {
		assertFeature(type, "featuredImage", "featuredMedia");
		const ref = assertMediaRef(candidate.featuredMedia);
		if (ref !== null) {
			if (!deps.capabilities?.validateMediaRef) throw new ContentLifecycleError("capability-unavailable", "media validation adapter is required", "featuredMedia");
			await deps.capabilities.validateMediaRef(ref, principal);
		}
		out.featuredMedia = ref;
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "commentStatus")) out.commentStatus = normalizeCommentStatus(type, candidate.commentStatus);
	if (Object.prototype.hasOwnProperty.call(candidate, "pingStatus")) out.pingStatus = normalizePingStatus(type, candidate.pingStatus);
	if (Object.prototype.hasOwnProperty.call(candidate, "sticky")) {
		if (typeof candidate.sticky !== "boolean") throw new ContentLifecycleError("validation", "must be a boolean", "sticky");
		out.sticky = candidate.sticky;
	}
	if (Object.prototype.hasOwnProperty.call(candidate, "format")) {
		assertFeature(type, "postFormats", "format");
		out.format = nullableString$1(candidate.format, "format", 128);
	}
	return out;
}
async function createContentEntry(tx, input, principal, deps) {
	assertActiveContentTransaction(tx);
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const typeKey = nonEmptyString(input.type, "type", 128);
	let type;
	try {
		type = await resolveContentType(tx, typeKey);
	} catch {
		throw new ContentLifecycleError("access-denied", "content type is unavailable");
	}
	assertTypeActive(type);
	authorizeContentAction({
		principal,
		type,
		operation: "create"
	});
	const { operationId: _operationId, ...writeInput } = input;
	const normalized = await normalizeWrite(tx, type, writeInput, deps, principal, "create");
	const hash = await requestHash({
		typeKey,
		input: normalized
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		const entry = await replayEntry(tx, replay, hash, principal);
		if (!entry) throw new ContentLifecycleError("operation-conflict", "recorded create result is no longer available");
		return entry;
	}
	const status = await initialStatus(tx, type);
	const now = /* @__PURE__ */ new Date();
	const id = crypto.randomUUID();
	const termIds = normalized.termIds ?? [];
	const termRefs = await termRefsByIds(tx, termIds);
	const proposed = Object.freeze({
		id,
		slug: normalized.slug,
		type: type.key,
		title: normalized.title,
		body: normalized.body,
		status: status.key,
		visibility: normalized.visibility ?? "public",
		publishedAt: null,
		author: normalized.author ?? principal.id,
		terms: termRefs,
		createdAt: now,
		updatedAt: now,
		parentId: normalized.parentId ?? null,
		menuOrder: normalized.menuOrder ?? 0,
		templateKey: normalized.templateKey ?? null,
		excerpt: normalized.excerpt ?? "",
		featuredMedia: normalized.featuredMedia ?? null,
		commentStatus: normalized.commentStatus ?? "open",
		pingStatus: normalized.pingStatus ?? "open",
		passwordProtected: false,
		sticky: normalized.sticky ?? false,
		format: normalized.format ?? null,
		deletedAt: null,
		lastEditedBy: principal.id,
		typeDefinitionRevision: type.revision,
		statusDefinitionRevision: status.revision
	});
	const ctx = mutationContext(operationId, principal, type, null, proposed);
	await runValidateHooks(deps.hooks, ctx);
	await runBeforeCommit(deps.hooks, ctx);
	try {
		await tx.execute(sql`INSERT INTO content_entries
      (id, slug, type, title, body, status, visibility, published_at, author, created_at, updated_at,
       parent_id, menu_order, template_key, excerpt, featured_media, comment_status, ping_status, sticky,
       format, deleted_at, last_edited_by, type_definition_revision, status_definition_revision)
      VALUES (${id}, ${proposed.slug}, ${proposed.type}, ${proposed.title}, ${proposed.body}, ${proposed.status}, ${proposed.visibility}, NULL,
       ${proposed.author}, ${now.toISOString()}, ${now.toISOString()}, ${proposed.parentId}, ${proposed.menuOrder}, ${proposed.templateKey}, ${proposed.excerpt},
       ${proposed.featuredMedia === null ? null : JSON.stringify(proposed.featuredMedia)}, ${proposed.commentStatus}, ${proposed.pingStatus}, ${proposed.sticky},
       ${proposed.format}, NULL, ${principal.id}, ${type.revision}, ${status.revision})`);
	} catch (error) {
		if (isUniqueViolation$1(error)) throw new ContentLifecycleError("conflict", "slug already exists for this content type", "slug");
		throw error;
	}
	await replaceTerms(tx, id, termIds);
	await writeReceipt(tx, {
		operationId,
		kind: "content:create",
		entryId: id,
		requestHash: hash,
		principal,
		result: { id }
	});
	await enqueue(tx, deps.outbox, operationId, "written", id, now);
	return await readEntry(tx, id);
}
async function updateContentEntry(tx, id, input, principal, deps) {
	assertActiveContentTransaction(tx);
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const expectedUpdatedAt = exactDate(input.expectedUpdatedAt, "expectedUpdatedAt");
	const before = await requireEntry(tx, id);
	const { type, status } = await resolveExactTypeStatus(tx, before);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry: before
	});
	const normalized = await normalizeWrite(tx, type, input.patch, deps, principal, "update", id);
	if (normalized.author !== void 0 && normalized.author !== before.author) {
		if (!deps.authors) throw new ContentLifecycleError("capability-unavailable", "author authority adapter is required", "author");
		await deps.authors.assertTransfer(principal, before.author, normalized.author);
	}
	const hash = await requestHash({
		id,
		expectedUpdatedAt: expectedUpdatedAt.toISOString(),
		patch: normalized
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		const entry = await replayEntry(tx, replay, hash, principal);
		if (!entry) throw new ContentLifecycleError("operation-conflict", "recorded update result is no longer available");
		return entry;
	}
	if (before.updatedAt.getTime() !== expectedUpdatedAt.getTime()) throw new ContentLifecycleError("stale-version", "content changed after caller read it");
	const now = /* @__PURE__ */ new Date();
	const termIds = normalized.termIds ?? before.terms.map((term) => term.id);
	const proposed = Object.freeze({
		...before,
		...normalized.slug === void 0 ? {} : { slug: normalized.slug },
		...normalized.title === void 0 ? {} : { title: normalized.title },
		...normalized.body === void 0 ? {} : { body: normalized.body },
		...normalized.excerpt === void 0 ? {} : { excerpt: normalized.excerpt },
		...normalized.visibility === void 0 ? {} : { visibility: normalized.visibility },
		...normalized.author === void 0 ? {} : { author: normalized.author },
		terms: normalized.termIds === void 0 ? before.terms : await termRefsByIds(tx, normalized.termIds),
		...normalized.parentId === void 0 ? {} : { parentId: normalized.parentId },
		...normalized.menuOrder === void 0 ? {} : { menuOrder: normalized.menuOrder },
		...normalized.templateKey === void 0 ? {} : { templateKey: normalized.templateKey },
		...normalized.featuredMedia === void 0 ? {} : { featuredMedia: normalized.featuredMedia },
		...normalized.commentStatus === void 0 ? {} : { commentStatus: normalized.commentStatus },
		...normalized.pingStatus === void 0 ? {} : { pingStatus: normalized.pingStatus },
		...normalized.sticky === void 0 ? {} : { sticky: normalized.sticky },
		...normalized.format === void 0 ? {} : { format: normalized.format },
		updatedAt: now,
		lastEditedBy: principal.id
	});
	const ctx = mutationContext(operationId, principal, type, before, proposed);
	await runValidateHooks(deps.hooks, ctx);
	await runBeforeCommit(deps.hooks, ctx);
	if (type.supports.includes("revisions")) await snapshotRevision(tx, before, principal.id);
	try {
		if ((await tx.execute(sql`UPDATE content_entries SET
      slug = ${proposed.slug}, title = ${proposed.title}, body = ${proposed.body}, visibility = ${proposed.visibility},
      author = ${proposed.author}, parent_id = ${proposed.parentId}, menu_order = ${proposed.menuOrder}, template_key = ${proposed.templateKey},
      excerpt = ${proposed.excerpt}, featured_media = ${proposed.featuredMedia === null ? null : JSON.stringify(proposed.featuredMedia)},
      comment_status = ${proposed.commentStatus}, ping_status = ${proposed.pingStatus}, sticky = ${proposed.sticky}, format = ${proposed.format},
      last_edited_by = ${principal.id}, updated_at = ${now.toISOString()}
      WHERE id = ${id} AND updated_at = ${expectedUpdatedAt.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "content changed during update");
	} catch (error) {
		if (isUniqueViolation$1(error)) throw new ContentLifecycleError("conflict", "slug already exists for this content type", "slug");
		throw error;
	}
	if (normalized.termIds !== void 0) await replaceTerms(tx, id, termIds);
	await writeReceipt(tx, {
		operationId,
		kind: "content:update",
		entryId: id,
		requestHash: hash,
		principal,
		result: { id }
	});
	await enqueue(tx, deps.outbox, operationId, "written", id, now);
	return await readEntry(tx, id);
}
function resolvedFrom(values, key, field) {
	const found = values.find((item) => item.key === key);
	if (!found) throw new ContentLifecycleError("validation", `${field} is unavailable`, field);
	return found;
}
function checkMappedCapability(principal, type, key) {
	const capability = type.capabilities[key];
	if (!principal.capabilities.has(capability)) throw new ContentLifecycleError("access-denied", "content is unavailable");
}
function transitionRule(rules, from, to) {
	const rule = rules.find((candidate) => candidate.to === to && (candidate.from === from || candidate.from === "*"));
	if (!rule) throw new ContentLifecycleError("validation", "status transition is not allowed", "to");
	return rule;
}
async function transitionContent(tx, id, input, principal, deps) {
	assertActiveContentTransaction(tx);
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const to = nonEmptyString(input.to, "to", 128);
	const expectedUpdatedAt = exactDate(input.expectedUpdatedAt, "expectedUpdatedAt");
	const before = await requireEntry(tx, id);
	const type = resolvedFrom(deps.types, before.type, "type");
	const currentStatus = resolvedFrom(deps.statuses, before.status, "status");
	if (before.typeDefinitionRevision !== type.revision || before.statusDefinitionRevision !== currentStatus.revision) throw new ContentLifecycleError("integrity", "entry definition revisions do not match supplied registries");
	authorizeContentAction({
		principal,
		type,
		status: currentStatus,
		operation: "edit",
		entry: before
	});
	const target = resolvedFrom(deps.statuses, to, "to");
	if (!target.active || type.statusKeys !== void 0 && !type.statusKeys.includes(target.key)) throw new ContentLifecycleError("validation", "target status is not active for this content type", "to");
	const rule = transitionRule(deps.rules, before.status, target.key);
	checkMappedCapability(principal, type, rule.capability);
	if (target.published) authorizeContentAction({
		principal,
		type,
		status: currentStatus,
		operation: "publish",
		entry: before
	});
	const scheduleAt = input.scheduleAt === void 0 ? void 0 : exactDate(input.scheduleAt, "scheduleAt");
	const publishedAtInput = input.publishedAt === void 0 ? void 0 : exactDate(input.publishedAt, "publishedAt");
	if (scheduleAt && publishedAtInput) throw new ContentLifecycleError("validation", "scheduleAt and publishedAt are mutually exclusive");
	const now = /* @__PURE__ */ new Date();
	let publicationDate = null;
	if (target.transitionInput === "scheduleAt") {
		if (!scheduleAt || scheduleAt.getTime() <= now.getTime()) throw new ContentLifecycleError("validation", "scheduled transition requires a future scheduleAt", "scheduleAt");
		publicationDate = scheduleAt;
	} else if (scheduleAt) throw new ContentLifecycleError("validation", "scheduleAt is not accepted by the target status", "scheduleAt");
	if (target.transitionInput === "publishedAt") publicationDate = publishedAtInput ?? now;
	else if (publishedAtInput) throw new ContentLifecycleError("validation", "publishedAt is not accepted by the target status", "publishedAt");
	if (rule.requiresScheduleAt && !scheduleAt) throw new ContentLifecycleError("validation", "transition requires scheduleAt", "scheduleAt");
	if (before.status === target.key && !(target.published && publishedAtInput)) throw new ContentLifecycleError("validation", "same-status transition is not a lifecycle change", "to");
	const hash = await requestHash({
		id,
		to,
		scheduleAt: scheduleAt?.toISOString(),
		publishedAt: publishedAtInput?.toISOString(),
		expectedUpdatedAt: expectedUpdatedAt.toISOString()
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		const entry = await replayEntry(tx, replay, hash, principal);
		if (!entry) throw new ContentLifecycleError("operation-conflict", "recorded transition result is no longer available");
		return entry;
	}
	if (before.updatedAt.getTime() !== expectedUpdatedAt.getTime()) throw new ContentLifecycleError("stale-version", "content changed after caller read it");
	const ctx = mutationContext(operationId, principal, type, before, Object.freeze({
		...before,
		status: target.key,
		statusDefinitionRevision: target.revision,
		publishedAt: publicationDate,
		updatedAt: now,
		lastEditedBy: principal.id
	}));
	await runValidateHooks(deps.hooks, ctx);
	await runBeforeCommit(deps.hooks, ctx);
	if (type.supports.includes("revisions")) await snapshotRevision(tx, before, principal.id);
	if ((await tx.execute(sql`UPDATE content_entries SET status = ${target.key}, status_definition_revision = ${target.revision},
    published_at = ${publicationDate?.toISOString() ?? null}, updated_at = ${now.toISOString()}, last_edited_by = ${principal.id}
    WHERE id = ${id} AND updated_at = ${expectedUpdatedAt.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "content changed during transition");
	await writeReceipt(tx, {
		operationId,
		kind: "content:transition",
		entryId: id,
		requestHash: hash,
		principal,
		result: { id }
	});
	await enqueue(tx, deps.outbox, operationId, "transitioned", id, now);
	return await readEntry(tx, id);
}
async function simpleLifecycleStatus(tx, id, input, principal, deps, targetKey, authorizationOperation) {
	const before = await requireEntry(tx, id);
	const type = resolvedFrom(deps.types, before.type, "type");
	const currentStatus = resolvedFrom(deps.statuses, before.status, "status");
	if (before.typeDefinitionRevision !== type.revision || before.statusDefinitionRevision !== currentStatus.revision) throw new ContentLifecycleError("integrity", "entry definition revisions do not match supplied registries");
	authorizeContentAction({
		principal,
		type,
		status: currentStatus,
		operation: authorizationOperation,
		entry: before
	});
	const target = resolvedFrom(deps.statuses, targetKey, "status");
	if (!target.active || type.statusKeys !== void 0 && !type.statusKeys.includes(target.key)) throw new ContentLifecycleError("validation", "lifecycle status is not active for this type");
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const expected = exactDate(input.expectedUpdatedAt, "expectedUpdatedAt");
	const hash = await requestHash({
		id,
		targetKey,
		expectedUpdatedAt: expected.toISOString()
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		const entry = await replayEntry(tx, replay, hash, principal);
		if (!entry) throw new ContentLifecycleError("operation-conflict", "recorded lifecycle result is unavailable");
		return entry;
	}
	if (before.updatedAt.getTime() !== expected.getTime()) throw new ContentLifecycleError("stale-version", "content changed after caller read it");
	const now = /* @__PURE__ */ new Date();
	const ctx = mutationContext(operationId, principal, type, before, Object.freeze({
		...before,
		status: target.key,
		statusDefinitionRevision: target.revision,
		publishedAt: null,
		updatedAt: now,
		lastEditedBy: principal.id
	}));
	await runValidateHooks(deps.hooks, ctx);
	await runBeforeCommit(deps.hooks, ctx);
	if (type.supports.includes("revisions")) await snapshotRevision(tx, before, principal.id);
	if ((await tx.execute(sql`UPDATE content_entries SET status = ${target.key}, status_definition_revision = ${target.revision}, published_at = NULL,
    updated_at = ${now.toISOString()}, last_edited_by = ${principal.id} WHERE id = ${id} AND updated_at = ${expected.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "content changed during lifecycle mutation");
	await writeReceipt(tx, {
		operationId,
		kind: `content:${targetKey}`,
		entryId: id,
		requestHash: hash,
		principal,
		result: { id }
	});
	await enqueue(tx, deps.outbox, operationId, "transitioned", id, now);
	return await readEntry(tx, id);
}
function trashContent(tx, id, input, principal, deps) {
	return simpleLifecycleStatus(tx, id, input, principal, deps, "trashed", "delete");
}
function restoreContent(tx, id, input, principal, deps) {
	return simpleLifecycleStatus(tx, id, input, principal, deps, "draft", "edit");
}
async function changeContentPassword(tx, id, input, principal, deps) {
	assertActiveContentTransaction(tx);
	const before = await requireEntry(tx, id);
	const { type, status } = await resolveExactTypeStatus(tx, before);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry: before
	});
	const expected = exactDate(input.expectedUpdatedAt, "expectedUpdatedAt");
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const requestIdentity = await requestHash({
		id,
		operation: input.operation,
		expectedUpdatedAt: expected.toISOString()
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		const entry = await replayEntry(tx, replay, requestIdentity, principal);
		if (!entry) throw new ContentLifecycleError("operation-conflict", "recorded password result is unavailable");
		return entry;
	}
	if (before.updatedAt.getTime() !== expected.getTime()) throw new ContentLifecycleError("stale-version", "content changed after caller read it");
	const now = /* @__PURE__ */ new Date();
	let nextCredential = null;
	if (input.operation === "set") {
		const password = nonEmptyString(input.password, "password", 4096);
		const hashed = await deps.passwords.hash(password);
		if (!hashed || typeof hashed.credential !== "string" || !hashed.credential || typeof hashed.version !== "string" || !hashed.version) throw new ContentLifecycleError("integrity", "password adapter returned an invalid credential");
		if ((await tx.execute(sql`SELECT credential_version AS "credentialVersion" FROM content_password_credentials WHERE entry_id = ${id}`))[0]?.credentialVersion === hashed.version) throw new ContentLifecycleError("integrity", "password replacement must advance the credential version");
		nextCredential = hashed;
	}
	const ctx = mutationContext(operationId, principal, type, before, Object.freeze({
		...before,
		passwordProtected: input.operation === "set",
		updatedAt: now,
		lastEditedBy: principal.id
	}));
	await runValidateHooks(deps.hooks, ctx);
	await runBeforeCommit(deps.hooks, ctx);
	if (nextCredential) await tx.execute(sql`INSERT INTO content_password_credentials (entry_id, credential_version, credential, created_at, updated_at)
      VALUES (${id}, ${nextCredential.version}, ${nextCredential.credential}, ${now.toISOString()}, ${now.toISOString()})
      ON CONFLICT (entry_id) DO UPDATE SET credential_version = excluded.credential_version, credential = excluded.credential, updated_at = excluded.updated_at`);
	else await tx.execute(sql`DELETE FROM content_password_credentials WHERE entry_id = ${id}`);
	if ((await tx.execute(sql`UPDATE content_entries SET updated_at = ${now.toISOString()}, last_edited_by = ${principal.id}
    WHERE id = ${id} AND updated_at = ${expected.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "content changed during password mutation");
	await writeReceipt(tx, {
		operationId,
		kind: `content:password:${input.operation}`,
		entryId: id,
		requestHash: requestIdentity,
		principal,
		result: { id }
	});
	await enqueue(tx, deps.outbox, operationId, "written", id, now);
	return await readEntry(tx, id);
}
async function verifyContentPassword(tx, id, password, adapter) {
	assertActiveContentTransaction(tx);
	const value = nonEmptyString(password, "password", 4096);
	const credential = (await tx.execute(sql`SELECT credential_version AS "credentialVersion", credential FROM content_password_credentials WHERE entry_id = ${id}`))[0];
	if (!credential || !await adapter.verify(value, credential.credential)) return null;
	return adapter.issueProof(id, credential.credentialVersion);
}
async function readProtectedContent(tx, id, principal, adapter, proof) {
	assertActiveContentTransaction(tx);
	const entry = await readEntry(tx, id);
	if (!entry || entry.deletedAt !== null) return null;
	let type, status;
	try {
		({type, status} = await resolveExactTypeStatus(tx, entry));
	} catch {
		return null;
	}
	try {
		authorizeContentAction({
			principal,
			type,
			status,
			operation: "read",
			entry
		});
	} catch {
		return null;
	}
	if (!entry.passwordProtected) return Object.freeze({
		access: "granted",
		entry
	});
	try {
		authorizeContentAction({
			principal,
			type,
			status,
			operation: "readProtected",
			entry
		});
		return Object.freeze({
			access: "granted",
			entry
		});
	} catch (error) {
		if (!(error instanceof ContentAuthorizationError)) throw error;
	}
	const credential = (await tx.execute(sql`SELECT credential_version AS "credentialVersion" FROM content_password_credentials WHERE entry_id = ${id}`))[0];
	if (credential && typeof proof === "string" && proof && await adapter.verifyProof(id, credential.credentialVersion, proof)) return Object.freeze({
		access: "granted",
		entry
	});
	const { body: _body, ...metadata } = entry;
	return Object.freeze({
		access: "passwordRequired",
		entry: Object.freeze(metadata)
	});
}
function tombstoneEntry(snapshot) {
	const value = plainObject(typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot);
	if (typeof value.id !== "string" || typeof value.author !== "string" || typeof value.status !== "string" || typeof value.type !== "string") throw new ContentLifecycleError("integrity", "tombstone snapshot is malformed");
	if (value.visibility !== "public" && value.visibility !== "private" && value.visibility !== "members") throw new ContentLifecycleError("integrity", "tombstone visibility is malformed");
	return {
		id: value.id,
		author: value.author,
		status: value.status,
		visibility: value.visibility,
		type: value.type,
		typeDefinitionRevision: Number(value.typeDefinitionRevision),
		statusDefinitionRevision: Number(value.statusDefinitionRevision)
	};
}
async function applyPermanentContentDelete(tx, plan, operationIdInput, principal, deps) {
	assertActiveContentTransaction(tx);
	const operationId = nonEmptyString(operationIdInput, "operationId", 256);
	const hash = await requestHash({ planToken: plan.token });
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		if (replay.requestHash !== hash || replay.principalScope !== principalScope$1(principal)) throw new ContentLifecycleError("operation-conflict", "operation id was already used for another delete");
		if (!Array.isArray(replay.result)) throw new ContentLifecycleError("integrity", "delete receipt is malformed");
		const tombstones = await tx.execute(sql`
      SELECT entry_id AS "entryId", snapshot FROM content_tombstones WHERE operation_id = ${operationId} ORDER BY entry_id`);
		const byEntry = new Map(tombstones.map((item) => [item.entryId, item]));
		const results = [];
		for (const raw of replay.result) {
			const item = plainObject(raw);
			if (typeof item.id !== "string" || typeof item.parentDeletionRevisionId !== "string") throw new ContentLifecycleError("integrity", "delete receipt item is malformed");
			const tombstone = byEntry.get(item.id);
			if (!tombstone) throw new ContentLifecycleError("integrity", "delete receipt lost its tombstone");
			const entry = tombstoneEntry(tombstone.snapshot);
			try {
				const [type, status] = await Promise.all([resolveContentType(tx, entry.type), resolveContentStatus(tx, entry.status)]);
				authorizeContentAction({
					principal,
					type,
					status,
					operation: "delete",
					entry
				});
			} catch {
				throw new ContentLifecycleError("access-denied", "content is unavailable");
			}
			results.push({
				id: item.id,
				parentDeletionRevisionId: item.parentDeletionRevisionId
			});
		}
		return Object.freeze(results);
	}
	const current = await preparePermanentContentDelete(tx, plan.selected, principal);
	if (current.token !== plan.token || current.corpusVersion !== plan.corpusVersion || current.authorizationVersion !== plan.authorizationVersion) throw new ContentLifecycleError("stale-version", "delete hierarchy or authorization changed after preparation");
	if (current.reparent.length !== plan.reparent.length || current.reparent.some((item, index) => {
		const expected = plan.reparent[index];
		return !expected || item.childId !== expected.childId || item.fromParentId !== expected.fromParentId || item.toParentId !== expected.toParentId || item.expectedUpdatedAt.getTime() !== expected.expectedUpdatedAt.getTime();
	})) throw new ContentLifecycleError("stale-version", "delete reparent plan changed after preparation");
	const selectedEntries = /* @__PURE__ */ new Map();
	for (const item of plan.selected) selectedEntries.set(item.id, await requireEntry(tx, item.id));
	const childEntries = /* @__PURE__ */ new Map();
	for (const item of plan.reparent) childEntries.set(item.childId, await requireEntry(tx, item.childId));
	for (const item of plan.selected) {
		const before = selectedEntries.get(item.id);
		const ctx = mutationContext(operationId, principal, await resolveContentType(tx, before.type), before, null);
		await runValidateHooks(deps.hooks, ctx);
		await runBeforeCommit(deps.hooks, ctx);
	}
	for (const item of plan.reparent) {
		const before = childEntries.get(item.childId);
		const ctx = mutationContext(operationId, principal, await resolveContentType(tx, before.type), before, Object.freeze({
			...before,
			parentId: item.toParentId
		}));
		await runValidateHooks(deps.hooks, ctx);
		await runBeforeCommit(deps.hooks, ctx);
	}
	const now = /* @__PURE__ */ new Date();
	for (const item of plan.reparent) if ((await tx.execute(sql`UPDATE content_entries SET parent_id = ${item.toParentId}, updated_at = ${now.toISOString()}, last_edited_by = ${principal.id}
      WHERE id = ${item.childId} AND parent_id = ${item.fromParentId} AND updated_at = ${item.expectedUpdatedAt.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "reparented child changed during delete");
	const results = [];
	for (const item of plan.selected) {
		const before = selectedEntries.get(item.id);
		const tombstoneId = crypto.randomUUID();
		await tx.execute(sql`INSERT INTO content_tombstones
      (id, entry_id, operation_id, type_key, type_definition_revision, status_key, status_definition_revision, snapshot, deleted_at)
      VALUES (${tombstoneId}, ${before.id}, ${operationId}, ${before.type}, ${before.typeDefinitionRevision}, ${before.status}, ${before.statusDefinitionRevision},
        ${JSON.stringify(revisionSnapshot(before))}, ${now.toISOString()})`);
		if ((await tx.execute(sql`DELETE FROM content_entries WHERE id = ${before.id} AND updated_at = ${item.expectedUpdatedAt.toISOString()} RETURNING id`)).length !== 1) throw new ContentLifecycleError("stale-version", "selected content changed during delete");
		results.push({
			id: before.id,
			parentDeletionRevisionId: tombstoneId
		});
	}
	for (const item of plan.reparent) await enqueue(tx, deps.outbox, operationId, "reparented", item.childId, now);
	for (const result of results) await enqueue(tx, deps.outbox, operationId, "deleted", result.id, now);
	await writeReceipt(tx, {
		operationId,
		kind: "content:permanent-delete",
		requestHash: hash,
		principal,
		result: results
	});
	return Object.freeze(results);
}
function revisionFromRow(row) {
	const termIdsValue = parseJson$1(row.termIds);
	const snapshotValue = parseJson$1(row.snapshot);
	return Object.freeze({
		id: String(row.id),
		entryId: String(row.entryId),
		seq: Number(row.seq),
		title: String(row.title),
		body: String(row.body),
		slug: String(row.slug),
		type: String(row.type),
		termIds: Array.isArray(termIdsValue) ? termIdsValue.map(String) : [],
		...snapshotValue && typeof snapshotValue === "object" && !Array.isArray(snapshotValue) ? { snapshot: snapshotValue } : {},
		editor: String(row.editor),
		createdAt: asDate(row.createdAt, "createdAt")
	});
}
async function listContentRevisions(tx, entryId, principal, input = {}) {
	assertActiveContentTransaction(tx);
	const entry = await requireEntry(tx, entryId);
	const { type, status } = await resolveExactTypeStatus(tx, entry);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry
	});
	if (!type.supports.includes("revisions")) throw new ContentLifecycleError("capability-unavailable", "revisions are disabled for this content type");
	const limit = input.limit === void 0 ? 20 : input.limit;
	if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new ContentLifecycleError("validation", "limit must be 1..100", "limit");
	if (input.before !== void 0 && (!Number.isSafeInteger(input.before) || input.before < 1)) throw new ContentLifecycleError("validation", "before must be a positive revision sequence", "before");
	const rows = await tx.execute(input.before === void 0 ? sql`SELECT id, entry_id AS "entryId", seq, title, body, slug, type, term_ids AS "termIds", snapshot, editor, created_at AS "createdAt"
        FROM content_revisions WHERE entry_id = ${entryId} ORDER BY seq DESC LIMIT ${limit + 1}` : sql`SELECT id, entry_id AS "entryId", seq, title, body, slug, type, term_ids AS "termIds", snapshot, editor, created_at AS "createdAt"
        FROM content_revisions WHERE entry_id = ${entryId} AND seq < ${input.before} ORDER BY seq DESC LIMIT ${limit + 1}`);
	const more = rows.length > limit;
	const items = (more ? rows.slice(0, limit) : rows).map(revisionFromRow);
	return Object.freeze({
		items: Object.freeze(items),
		nextCursor: more ? items[items.length - 1].seq : null
	});
}
async function getContentRevision(tx, entryId, revisionId, principal) {
	assertActiveContentTransaction(tx);
	const entry = await requireEntry(tx, entryId);
	const { type, status } = await resolveExactTypeStatus(tx, entry);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry
	});
	const rows = await tx.execute(sql`SELECT id, entry_id AS "entryId", seq, title, body, slug, type, term_ids AS "termIds", snapshot, editor, created_at AS "createdAt"
    FROM content_revisions WHERE id = ${revisionId} AND entry_id = ${entryId}`);
	if (!rows[0]) throw new ContentLifecycleError("access-denied", "revision is unavailable");
	return revisionFromRow(rows[0]);
}
async function restoreContentRevision(tx, entryId, revisionId, input, principal, deps) {
	const revision = await getContentRevision(tx, entryId, revisionId, principal);
	if (!revision.snapshot) throw new ContentLifecycleError("integrity", "revision predates full snapshot support");
	const snapshot = revision.snapshot;
	return updateContentEntry(tx, entryId, {
		patch: {
			slug: String(snapshot.slug),
			title: String(snapshot.title),
			body: String(snapshot.body),
			excerpt: String(snapshot.excerpt ?? ""),
			visibility: snapshot.visibility,
			author: String(snapshot.author),
			termIds: Array.isArray(snapshot.termIds) ? snapshot.termIds.map(String) : [],
			parentId: snapshot.parentId === null ? null : String(snapshot.parentId),
			menuOrder: Number(snapshot.menuOrder ?? 0),
			templateKey: snapshot.templateKey === null ? null : String(snapshot.templateKey),
			featuredMedia: snapshot.featuredMedia === null ? null : snapshot.featuredMedia,
			commentStatus: snapshot.commentStatus,
			pingStatus: snapshot.pingStatus,
			sticky: Boolean(snapshot.sticky),
			format: snapshot.format === null ? null : String(snapshot.format)
		},
		expectedUpdatedAt: input.expectedUpdatedAt,
		operationId: input.operationId
	}, principal, deps);
}
function autosaveFromValue(value) {
	const raw = plainObject(value);
	if (typeof raw.id !== "string" || typeof raw.entryId !== "string" || typeof raw.parentRevisionId !== "string" || typeof raw.createdBy !== "string" || typeof raw.createdAt !== "string") throw new ContentLifecycleError("integrity", "autosave payload is malformed");
	return Object.freeze({
		id: raw.id,
		entryId: raw.entryId,
		parentRevisionId: raw.parentRevisionId,
		snapshot: Object.freeze(plainObject(raw.snapshot)),
		createdAt: asDate(raw.createdAt, "createdAt"),
		createdBy: raw.createdBy
	});
}
async function createContentAutosave(tx, entryId, input, principal, deps) {
	assertActiveContentTransaction(tx);
	const entry = await requireEntry(tx, entryId);
	const { type, status } = await resolveExactTypeStatus(tx, entry);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry
	});
	if (type.rest === false || !type.rest.autosaves || !type.rest.revisions || !type.supports.includes("revisions")) throw new ContentLifecycleError("capability-unavailable", "autosaves are disabled for this content type");
	const normalized = await normalizeWrite(tx, type, input.snapshot, deps, principal, "update", entryId);
	if (normalized.author !== void 0 && normalized.author !== entry.author) {
		if (!deps.authors) throw new ContentLifecycleError("capability-unavailable", "author authority adapter is required", "author");
		await deps.authors.assertTransfer(principal, entry.author, normalized.author);
	}
	if (!(await tx.execute(sql`SELECT id FROM content_revisions WHERE id = ${input.parentRevisionId} AND entry_id = ${entryId}`))[0]) throw new ContentLifecycleError("validation", "parent revision is unavailable", "parentRevisionId");
	const operationId = nonEmptyString(input.operationId, "operationId", 256);
	const hash = await requestHash({
		entryId,
		parentRevisionId: input.parentRevisionId,
		snapshot: normalized
	});
	const replay = await readReceipt$1(tx, operationId);
	if (replay) {
		if (replay.requestHash !== hash || replay.principalScope !== principalScope$1(principal)) throw new ContentLifecycleError("operation-conflict", "autosave operation id was reused");
		return autosaveFromValue(replay.result);
	}
	const now = /* @__PURE__ */ new Date();
	const autosave = Object.freeze({
		id: crypto.randomUUID(),
		entryId,
		parentRevisionId: input.parentRevisionId,
		snapshot: Object.freeze(normalized),
		createdAt: now,
		createdBy: principal.id
	});
	const payload = {
		requestHash: hash,
		principalScope: principalScope$1(principal),
		result: schemaValue$2({
			...autosave,
			createdAt: now.toISOString()
		})
	};
	await tx.execute(sql`INSERT INTO content_lifecycle_journal (id, operation_id, kind, entry_id, state, payload, created_at, updated_at)
    VALUES (${autosave.id}, ${operationId}, 'content:autosave', ${entryId}, 'committed', ${JSON.stringify(payload)}, ${now.toISOString()}, ${now.toISOString()})`);
	return autosave;
}
async function getContentAutosave(tx, entryId, autosaveId, principal) {
	assertActiveContentTransaction(tx);
	const entry = await requireEntry(tx, entryId);
	const { type, status } = await resolveExactTypeStatus(tx, entry);
	authorizeContentAction({
		principal,
		type,
		status,
		operation: "edit",
		entry
	});
	const rows = await tx.execute(sql`SELECT payload FROM content_lifecycle_journal WHERE id = ${autosaveId} AND entry_id = ${entryId} AND kind = 'content:autosave'`);
	if (!rows[0]) throw new ContentLifecycleError("access-denied", "autosave is unavailable");
	return autosaveFromValue(plainObject(typeof rows[0].payload === "string" ? JSON.parse(rows[0].payload) : rows[0].payload).result);
}
async function restoreContentAutosave(tx, entryId, autosaveId, input, principal, deps) {
	return updateContentEntry(tx, entryId, {
		patch: (await getContentAutosave(tx, entryId, autosaveId, principal)).snapshot,
		expectedUpdatedAt: input.expectedUpdatedAt,
		operationId: input.operationId
	}, principal, deps);
}
//#endregion
//#region src/query.ts
const MAX_PAGE_SIZE = 200;
const MAX_ARRAY_FILTER = 500;
const MAX_TAXONOMY_FILTERS = 32;
const MAX_SEARCH_LENGTH = 2e3;
const UUID_RE$2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
var ContentQueryError = class extends Error {
	code;
	detail;
	field;
	name = "ContentQueryError";
	constructor(code, detail, field) {
		super(`content query ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function fail$2(detail, field) {
	throw new ContentQueryError("validation", detail, field);
}
function positiveInteger(value, field, max) {
	if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1 || value > max) fail$2(`must be an integer in 1..${max}`, field);
	return value;
}
function normalizedStrings(value, field, max = MAX_ARRAY_FILTER) {
	if (value === void 0) return void 0;
	if (!Array.isArray(value) || value.length > max) fail$2(`must be an array with at most ${max} items`, field);
	const seen = /* @__PURE__ */ new Set();
	const output = [];
	for (const item of value) {
		if (typeof item !== "string" || item.trim().length === 0 || item.length > 256) fail$2("contains an invalid string", field);
		const normalized = item.trim();
		if (!seen.has(normalized)) {
			seen.add(normalized);
			output.push(normalized);
		}
	}
	return output;
}
function normalizedUuidStrings(value, field) {
	const values = normalizedStrings(value, field);
	if (values?.some((item) => !UUID_RE$2.test(item))) fail$2("contains an invalid UUID", field);
	return values;
}
function validDate(value, field) {
	if (value === void 0) return void 0;
	if (!(value instanceof Date) || Number.isNaN(value.getTime())) fail$2("must be a valid Date", field);
	return new Date(value.getTime());
}
function normalizedQuery(query) {
	if (typeof query !== "object" || query === null || Array.isArray(query)) fail$2("must be an object", "query");
	const allowed = /* @__PURE__ */ new Set([
		"types",
		"statuses",
		"authors",
		"parentId",
		"search",
		"taxonomy",
		"date",
		"visibility",
		"includeIds",
		"excludeIds",
		"orderBy",
		"direction",
		"page",
		"pageSize"
	]);
	for (const key of Object.keys(query)) if (!allowed.has(key)) fail$2("contains an unsupported property", `query.${key}`);
	const page = query.page === void 0 ? 1 : positiveInteger(query.page, "page", 1e6);
	const pageSize = query.pageSize === void 0 ? 20 : positiveInteger(query.pageSize, "pageSize", MAX_PAGE_SIZE);
	const orderBy = query.orderBy ?? "updatedAt";
	if (![
		"title",
		"author",
		"createdAt",
		"updatedAt",
		"publishedAt",
		"menuOrder"
	].includes(orderBy)) fail$2("has an unsupported value", "orderBy");
	const direction = query.direction ?? "desc";
	if (direction !== "asc" && direction !== "desc") fail$2("must be asc or desc", "direction");
	const search = query.search === void 0 ? void 0 : query.search.trim();
	if (search !== void 0 && (search.length === 0 || search.length > MAX_SEARCH_LENGTH)) fail$2(`must contain 1..${MAX_SEARCH_LENGTH} characters`, "search");
	const visibility = normalizedStrings(query.visibility, "visibility");
	if (visibility?.some((item) => ![
		"public",
		"private",
		"members"
	].includes(item))) fail$2("contains an invalid visibility", "visibility");
	const parentId = query.parentId;
	if (parentId !== void 0 && parentId !== null && !UUID_RE$2.test(parentId)) fail$2("must be null or a UUID", "parentId");
	const date = query.date === void 0 ? void 0 : {
		after: validDate(query.date.after, "date.after"),
		before: validDate(query.date.before, "date.before"),
		field: query.date.field ?? "publishedAt"
	};
	if (date && ![
		"createdAt",
		"updatedAt",
		"publishedAt"
	].includes(date.field)) fail$2("has an unsupported value", "date.field");
	if (date?.after && date.before && date.after.getTime() > date.before.getTime()) fail$2("after must not be later than before", "date");
	if (query.taxonomy !== void 0 && (!Array.isArray(query.taxonomy) || query.taxonomy.length > MAX_TAXONOMY_FILTERS)) fail$2(`must contain at most ${MAX_TAXONOMY_FILTERS} filters`, "taxonomy");
	const taxonomy = query.taxonomy?.map((item, index) => {
		if (!item || typeof item !== "object" || Array.isArray(item)) fail$2("must be an object", `taxonomy.${index}`);
		if (typeof item.taxonomy !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(item.taxonomy)) fail$2("has an invalid taxonomy key", `taxonomy.${index}.taxonomy`);
		const termIds = normalizedUuidStrings(item.termIds, `taxonomy.${index}.termIds`) ?? [];
		if (termIds.length === 0) fail$2("must contain at least one term id", `taxonomy.${index}.termIds`);
		const operator = item.operator ?? "or";
		if (operator !== "and" && operator !== "or") fail$2("must be and or or", `taxonomy.${index}.operator`);
		return {
			taxonomy: item.taxonomy,
			termIds,
			operator
		};
	});
	return {
		...query,
		page,
		pageSize,
		orderBy,
		direction,
		...search === void 0 ? {} : { search },
		types: normalizedStrings(query.types, "types"),
		statuses: normalizedStrings(query.statuses, "statuses"),
		authors: normalizedStrings(query.authors, "authors"),
		visibility,
		includeIds: normalizedUuidStrings(query.includeIds, "includeIds"),
		excludeIds: normalizedUuidStrings(query.excludeIds, "excludeIds"),
		...date === void 0 ? {} : { date },
		...taxonomy === void 0 ? {} : { taxonomy }
	};
}
function statusMap(context) {
	const map = /* @__PURE__ */ new Map();
	for (const status of context.statuses) {
		if (map.has(status.key)) throw new ContentQueryError("integrity", `duplicate resolved status ${status.key}`);
		map.set(status.key, status);
	}
	return map;
}
function typeMap(context) {
	const map = /* @__PURE__ */ new Map();
	for (const type of context.types) {
		if (map.has(type.key)) throw new ContentQueryError("integrity", `duplicate resolved type ${type.key}`);
		map.set(type.key, type);
	}
	return map;
}
function assertKnownFilters(query, context) {
	const types = typeMap(context);
	const statuses = statusMap(context);
	for (const key of query.types ?? []) if (!types.has(key)) fail$2(`unknown content type ${key}`, "types");
	for (const key of query.statuses ?? []) if (!statuses.has(key)) fail$2(`unknown content status ${key}`, "statuses");
}
function authorizedTypeStatusPredicate(context) {
	if (!context.principal || typeof context.principal.id !== "string" || !context.principal.capabilities) fail$2("principal is required", "principal");
	const statuses = statusMap(context);
	const branches = [];
	for (const type of context.types) {
		const readCapability = type.capabilities.read;
		if (!context.principal.capabilities.has(readCapability)) continue;
		const allowedVisibility = [eq(contentEntries.visibility, "public")];
		if (context.principal.member) allowedVisibility.push(eq(contentEntries.visibility, "members"));
		if (context.principal.capabilities.has(type.capabilities.readPrivate)) allowedVisibility.push(eq(contentEntries.visibility, "private"));
		const statusBranches = [];
		const allowedStatusKeys = type.statusKeys ?? [...statuses.keys()];
		for (const key of allowedStatusKeys) {
			const status = statuses.get(key);
			if (!status) continue;
			statusBranches.push(and(eq(contentEntries.status, key), eq(contentEntries.statusDefinitionRevision, status.revision)));
		}
		if (statusBranches.length === 0) continue;
		branches.push(and(eq(contentEntries.type, type.key), eq(contentEntries.typeDefinitionRevision, type.revision), or(...statusBranches), or(...allowedVisibility), isNull(contentEntries.deletedAt)));
	}
	return branches.length === 0 ? sql`FALSE` : or(...branches);
}
function decodeContentListQuery(value) {
	return normalizedQuery(value);
}
function buildContentQueryPredicate(queryInput, context) {
	const query = normalizedQuery(queryInput);
	assertKnownFilters(query, context);
	const conditions = [authorizedTypeStatusPredicate(context)];
	if (query.types?.length) conditions.push(inArray(contentEntries.type, query.types));
	if (query.statuses?.length) conditions.push(inArray(contentEntries.status, query.statuses));
	if (query.authors?.length) conditions.push(inArray(contentEntries.author, query.authors));
	if (query.parentId !== void 0) conditions.push(query.parentId === null ? isNull(contentEntries.parentId) : eq(contentEntries.parentId, query.parentId));
	if (query.visibility?.length) conditions.push(inArray(contentEntries.visibility, query.visibility));
	if (query.includeIds?.length) conditions.push(inArray(contentEntries.id, query.includeIds));
	if (query.excludeIds?.length) conditions.push(notInArray(contentEntries.id, query.excludeIds));
	if (query.date) {
		const column = query.date.field === "createdAt" ? contentEntries.createdAt : query.date.field === "updatedAt" ? contentEntries.updatedAt : contentEntries.publishedAt;
		if (query.date.after) conditions.push(gte(column, query.date.after));
		if (query.date.before) conditions.push(lte(column, query.date.before));
	}
	for (const filter of query.taxonomy ?? []) if (filter.operator === "or") conditions.push(exists(sql`SELECT 1 FROM content_entry_terms cet JOIN content_terms ct ON ct.id = cet.term_id
        WHERE cet.entry_id = ${contentEntries.id} AND ct.taxonomy = ${filter.taxonomy} AND ct.id IN (${sql.join(filter.termIds.map((id) => sql`${id}::uuid`), sql`, `)})`));
	else for (const termId of filter.termIds) conditions.push(exists(sql`SELECT 1 FROM content_entry_terms cet JOIN content_terms ct ON ct.id = cet.term_id
          WHERE cet.entry_id = ${contentEntries.id} AND ct.taxonomy = ${filter.taxonomy} AND ct.id = ${termId}::uuid`));
	return and(...conditions);
}
function bool$1(value) {
	return value === true || value === 1 || value === "1" || value === "true";
}
function date$1(value, field) {
	const parsed = value instanceof Date ? new Date(value.getTime()) : new Date(value);
	if (Number.isNaN(parsed.getTime())) throw new ContentQueryError("integrity", `stored ${field} timestamp is invalid`);
	return parsed;
}
function json(value) {
	if (typeof value !== "string") return value;
	try {
		return JSON.parse(value);
	} catch {
		return value;
	}
}
const ENTRY_SELECT = sql.raw(`id, slug, type, title, body, status, visibility,
  published_at AS "publishedAt", author, created_at AS "createdAt", updated_at AS "updatedAt",
  parent_id AS "parentId", menu_order AS "menuOrder", template_key AS "templateKey", excerpt,
  featured_media AS "featuredMedia", comment_status AS "commentStatus", ping_status AS "pingStatus",
  sticky, format, deleted_at AS "deletedAt", last_edited_by AS "lastEditedBy",
  type_definition_revision AS "typeDefinitionRevision", status_definition_revision AS "statusDefinitionRevision",
  EXISTS (SELECT 1 FROM content_password_credentials c WHERE c.entry_id = content_entries.id) AS "passwordProtected"`);
async function termsByEntries(tx, ids) {
	const map = /* @__PURE__ */ new Map();
	for (const id of ids) map.set(id, []);
	if (ids.length === 0) return map;
	const rows = await tx.execute(sql`
    SELECT et.entry_id AS "entryId", t.id, t.taxonomy, t.slug, t.name, t.parent_id AS "parentId", t.depth
    FROM content_entry_terms et JOIN content_terms t ON t.id = et.term_id
    WHERE et.entry_id IN (${sql.join(ids.map((id) => sql`${id}::uuid`), sql`, `)})
    ORDER BY et.entry_id, t.depth, t.name, t.id`);
	for (const row of rows) map.get(row.entryId)?.push(Object.freeze({
		id: row.id,
		taxonomy: row.taxonomy,
		slug: row.slug,
		name: row.name,
		parentId: row.parentId,
		depth: Number(row.depth)
	}));
	return map;
}
async function projectRows(tx, rows) {
	const terms = await termsByEntries(tx, rows.map((row) => row.id));
	return rows.map((row) => Object.freeze({
		id: row.id,
		slug: row.slug,
		type: row.type,
		title: row.title,
		body: row.body,
		status: row.status,
		visibility: row.visibility,
		publishedAt: row.publishedAt === null ? null : date$1(row.publishedAt, "publishedAt"),
		author: row.author,
		terms: terms.get(row.id) ?? [],
		createdAt: date$1(row.createdAt, "createdAt"),
		updatedAt: date$1(row.updatedAt, "updatedAt"),
		parentId: row.parentId,
		menuOrder: Number(row.menuOrder),
		templateKey: row.templateKey,
		excerpt: row.excerpt,
		featuredMedia: json(row.featuredMedia) ?? null,
		commentStatus: row.commentStatus,
		pingStatus: row.pingStatus,
		passwordProtected: bool$1(row.passwordProtected),
		sticky: bool$1(row.sticky),
		format: row.format,
		deletedAt: row.deletedAt === null ? null : date$1(row.deletedAt, "deletedAt"),
		lastEditedBy: row.lastEditedBy,
		typeDefinitionRevision: Number(row.typeDefinitionRevision),
		statusDefinitionRevision: Number(row.statusDefinitionRevision)
	}));
}
function orderExpression(query) {
	const column = query.orderBy === "title" ? contentEntries.title : query.orderBy === "author" ? contentEntries.author : query.orderBy === "createdAt" ? contentEntries.createdAt : query.orderBy === "publishedAt" ? contentEntries.publishedAt : query.orderBy === "menuOrder" ? contentEntries.menuOrder : contentEntries.updatedAt;
	return [(query.direction === "asc" ? asc : desc)(column), asc(contentEntries.id)];
}
async function countWithoutSearch(tx, predicate) {
	const rows = await tx.execute(sql`SELECT COUNT(*) AS total FROM content_entries WHERE ${predicate}`);
	return Number(rows[0]?.total ?? 0);
}
async function count(db, queryInput, context) {
	assertActiveContentTransaction(db);
	const query = normalizedQuery(queryInput);
	const predicate = buildContentQueryPredicate(query, context);
	if (query.search !== void 0) {
		const adapter = context.capabilities?.search;
		if (!adapter) throw new ContentQueryError("capability-unavailable", "authoritative search adapter is required", "search");
		const result = await adapter.searchPage(db, {
			...query,
			page: 1,
			pageSize: 1
		}, predicate, context.principal);
		if (!Number.isSafeInteger(result.totalItems) || result.totalItems < 0) throw new ContentQueryError("integrity", "search adapter returned an invalid total");
		return result.totalItems;
	}
	return countWithoutSearch(db, predicate);
}
async function listPage(db, queryInput, context) {
	assertActiveContentTransaction(db);
	const query = normalizedQuery(queryInput);
	const predicate = buildContentQueryPredicate(query, context);
	let rows;
	let totalItems;
	if (query.search !== void 0) {
		const adapter = context.capabilities?.search;
		if (!adapter) throw new ContentQueryError("capability-unavailable", "authoritative search adapter is required", "search");
		const result = await adapter.searchPage(db, query, predicate, context.principal);
		if (!Array.isArray(result.ids) || result.ids.length > query.pageSize || !Number.isSafeInteger(result.totalItems) || result.totalItems < 0 || result.ids.some((id) => !UUID_RE$2.test(id)) || new Set(result.ids).size !== result.ids.length) throw new ContentQueryError("integrity", "search adapter returned an invalid authoritative page");
		totalItems = result.totalItems;
		if (result.ids.length === 0) rows = [];
		else {
			const selected = await db.execute(sql`SELECT ${ENTRY_SELECT} FROM content_entries
        WHERE ${predicate} AND content_entries.id IN (${sql.join(result.ids.map((id) => sql`${id}::uuid`), sql`, `)})`);
			const byId = new Map(selected.map((row) => [row.id, row]));
			if (result.ids.some((id) => !byId.has(id))) throw new ContentQueryError("integrity", "search page no longer matches its authorization predicate");
			rows = result.ids.map((id) => byId.get(id));
		}
	} else {
		totalItems = await countWithoutSearch(db, predicate);
		const offset = (query.page - 1) * query.pageSize;
		const [firstOrder, secondOrder] = orderExpression(query);
		rows = await db.execute(sql`SELECT ${ENTRY_SELECT} FROM content_entries
      WHERE ${predicate} ORDER BY ${firstOrder}, ${secondOrder} LIMIT ${query.pageSize} OFFSET ${offset}`);
	}
	return Object.freeze({
		items: await projectRows(db, rows),
		page: query.page,
		pageSize: query.pageSize,
		totalItems,
		totalPages: totalItems === 0 ? 0 : Math.ceil(totalItems / query.pageSize)
	});
}
async function countByStatus(db, queryInput, context) {
	assertActiveContentTransaction(db);
	if (Object.prototype.hasOwnProperty.call(queryInput, "statuses")) fail$2("statuses is forbidden for countByStatus", "statuses");
	const base = normalizedQuery(queryInput);
	const output = Object.create(null);
	for (const status of context.statuses) output[status.key] = await count(db, {
		...base,
		statuses: [status.key]
	}, context);
	return Object.freeze(output);
}
//#endregion
//#region src/routes.ts
const MAX_ROUTE_SEGMENTS = 128;
const MAX_PAGE = 1e6;
const SEGMENT_RE = /^[^/?#]+$/;
var ContentRouteError = class extends Error {
	code;
	detail;
	field;
	name = "ContentRouteError";
	constructor(code, detail, field) {
		super(`content route ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function cleanSlug(value, field) {
	if (typeof value !== "string") throw new ContentRouteError("invalid-route", "route slug must be a string", field);
	const stripped = value.replace(/^\/+|\/+$/g, "");
	if (!stripped) throw new ContentRouteError("invalid-route", "route slug must not be empty", field);
	let segments;
	try {
		segments = stripped.split("/").map((segment) => decodeURIComponent(segment));
	} catch {
		throw new ContentRouteError("invalid-route", "route slug contains malformed percent encoding", field);
	}
	if (segments.length > MAX_ROUTE_SEGMENTS || segments.some((segment) => !segment || !SEGMENT_RE.test(segment) || segment === "." || segment === "..")) throw new ContentRouteError("invalid-route", "route slug contains an invalid segment", field);
	return Object.freeze(segments);
}
function pathname(segments) {
	return `/${segments.map((s) => encodeURIComponent(s)).join("/")}`;
}
function normalizedPathname(url) {
	return url.pathname.replace(/\/{2,}/g, "/").replace(/\/$/, "") || "/";
}
function decodePath(url) {
	try {
		return normalizedPathname(url).split("/").filter(Boolean).map(decodeURIComponent);
	} catch {
		return [];
	}
}
function startsWith(value, prefix) {
	return prefix.every((segment, index) => value[index] === segment);
}
function routeRecord(type) {
	const singleBase = type.rewrite === false ? null : cleanSlug(type.rewrite.slug, `${type.key}.rewrite.slug`);
	let archiveBase = null;
	if (type.hasArchive === true) {
		if (!singleBase) throw new ContentRouteError("invalid-route", "hasArchive=true requires rewrite routing", `${type.key}.hasArchive`);
		archiveBase = singleBase;
	} else if (typeof type.hasArchive === "string") archiveBase = cleanSlug(type.hasArchive, `${type.key}.hasArchive`);
	const queryVariable = type.queryVariable === false ? null : type.queryVariable;
	if (queryVariable !== null && !/^[a-z][a-z0-9_-]{0,63}$/.test(queryVariable)) throw new ContentRouteError("invalid-route", "query variable must be a stable machine key", `${type.key}.queryVariable`);
	return {
		type,
		singleBase,
		archiveBase,
		queryVariable,
		feeds: type.rewrite !== false && type.rewrite.feeds === true,
		pages: type.rewrite !== false && type.rewrite.pages === true
	};
}
function assertConflicts(records) {
	const singleRoots = /* @__PURE__ */ new Map(), archives = /* @__PURE__ */ new Map(), queries = /* @__PURE__ */ new Map();
	const staticRoutes = /* @__PURE__ */ new Map();
	const claimStatic = (path, typeKey, kind) => {
		const previous = staticRoutes.get(path);
		if (previous && previous.typeKey !== typeKey) throw new ContentRouteError("route-conflict", `${kind} route ${path} for ${typeKey} conflicts with ${previous.kind} route for ${previous.typeKey}`);
		staticRoutes.set(path, {
			typeKey,
			kind
		});
	};
	for (const record of records) {
		if (record.singleBase) {
			const key = pathname(record.singleBase);
			const previous = singleRoots.get(key);
			if (previous && previous !== record.type.key) throw new ContentRouteError("route-conflict", `single route ${key} is shared by ${previous} and ${record.type.key}`);
			singleRoots.set(key, record.type.key);
		}
		if (record.archiveBase) {
			const key = pathname(record.archiveBase);
			const previous = archives.get(key);
			if (previous && previous !== record.type.key) throw new ContentRouteError("route-conflict", `archive route ${key} is shared by ${previous} and ${record.type.key}`);
			archives.set(key, record.type.key);
			claimStatic(key, record.type.key, "archive");
			if (record.feeds) claimStatic(pathname([...record.archiveBase, "feed"]), record.type.key, "feed");
		}
		if (record.queryVariable) {
			const previous = queries.get(record.queryVariable);
			if (previous && previous !== record.type.key) throw new ContentRouteError("route-conflict", `query variable ${record.queryVariable} is shared by ${previous} and ${record.type.key}`);
			queries.set(record.queryVariable, record.type.key);
		}
	}
	for (const [path, typeKey] of archives) {
		const other = singleRoots.get(path);
		if (other && other !== typeKey) throw new ContentRouteError("route-conflict", `archive ${path} for ${typeKey} conflicts with single route for ${other}`);
	}
}
function bySpecificBase(field) {
	return (left, right) => {
		const a = left[field], b = right[field];
		return b.length - a.length || pathname(a).localeCompare(pathname(b)) || left.type.key.localeCompare(right.type.key);
	};
}
function pageNumber(segment) {
	if (segment === void 0 || !/^[1-9][0-9]*$/.test(segment)) return null;
	const value = Number(segment);
	return Number.isSafeInteger(value) && value >= 1 && value <= MAX_PAGE ? value : null;
}
function compileContentRoutes(types) {
	const keys = /* @__PURE__ */ new Set(), records = types.filter((type) => type.publiclyQueryable).map((type) => {
		if (keys.has(type.key)) throw new ContentRouteError("route-conflict", `duplicate resolved type ${type.key}`);
		keys.add(type.key);
		return routeRecord(type);
	});
	assertConflicts(records);
	const byKey = new Map(records.map((record) => [record.type.key, record]));
	const queryRecords = [...records].filter((record) => record.queryVariable).sort((a, b) => a.type.key.localeCompare(b.type.key));
	const archiveRecords = [...records].filter((record) => record.archiveBase).sort(bySpecificBase("archiveBase"));
	const singleRecords = [...records].filter((record) => record.singleBase).sort(bySpecificBase("singleBase"));
	const compiled = {
		records: byKey,
		match(url) {
			for (const record of queryRecords) {
				if (!record.queryVariable) continue;
				const raw = url.searchParams.get(record.queryVariable);
				if (raw !== null && normalizedPathname(url) === "/") {
					let pathSegments;
					try {
						pathSegments = raw.split("/").filter(Boolean).map((segment) => decodeURIComponent(segment));
					} catch {
						continue;
					}
					if (pathSegments.length > 0 && pathSegments.length <= MAX_ROUTE_SEGMENTS && pathSegments.every((segment) => SEGMENT_RE.test(segment))) return Object.freeze({
						kind: "single",
						typeKey: record.type.key,
						pathSegments: Object.freeze(pathSegments)
					});
				}
			}
			const segments = decodePath(url);
			let blockedSingleBaseLength = -1;
			for (const record of archiveRecords) {
				if (!startsWith(segments, record.archiveBase)) continue;
				const rest = segments.slice(record.archiveBase.length);
				if (rest.length === 0) return Object.freeze({
					kind: "archive",
					typeKey: record.type.key,
					page: 1
				});
				if (rest[0] === "page" && record.pages) {
					if (rest.length === 2) {
						const page = pageNumber(rest[1]);
						if (page !== null) return Object.freeze({
							kind: "archive",
							typeKey: record.type.key,
							page
						});
					}
					blockedSingleBaseLength = Math.max(blockedSingleBaseLength, record.archiveBase.length);
					continue;
				}
				if (rest[0] === "feed" && record.feeds) {
					if (rest.length === 1) return Object.freeze({
						kind: "feed",
						typeKey: record.type.key,
						page: 1
					});
					if (record.pages && rest.length === 3 && rest[1] === "page") {
						const page = pageNumber(rest[2]);
						if (page !== null) return Object.freeze({
							kind: "feed",
							typeKey: record.type.key,
							page
						});
					}
					blockedSingleBaseLength = Math.max(blockedSingleBaseLength, record.archiveBase.length);
				}
			}
			for (const record of singleRecords) {
				if (record.singleBase.length <= blockedSingleBaseLength || !startsWith(segments, record.singleBase)) continue;
				const pathSegments = segments.slice(record.singleBase.length);
				if (pathSegments.length === 0 || pathSegments.length > MAX_ROUTE_SEGMENTS) continue;
				if (!record.type.hierarchical && pathSegments.length !== 1) continue;
				return Object.freeze({
					kind: "single",
					typeKey: record.type.key,
					pathSegments: Object.freeze(pathSegments)
				});
			}
			return null;
		},
		canonical(typeKey, entryPath) {
			const record = byKey.get(typeKey);
			if (!record) throw new ContentRouteError("unknown-type", `unknown routable type ${typeKey}`);
			if (entryPath !== void 0) {
				if (entryPath.length === 0 || entryPath.length > MAX_ROUTE_SEGMENTS || entryPath.some((segment) => typeof segment !== "string" || !SEGMENT_RE.test(segment) || !segment)) throw new ContentRouteError("invalid-route", "entry path is invalid", "entryPath");
				if (!record.type.hierarchical && entryPath.length !== 1) throw new ContentRouteError("invalid-route", "flat types require exactly one entry path segment", "entryPath");
				if (record.singleBase) return { pathname: pathname([...record.singleBase, ...entryPath]) };
				if (record.queryVariable) return {
					pathname: "/",
					search: `?${encodeURIComponent(record.queryVariable)}=${encodeURIComponent(entryPath.join("/"))}`
				};
				throw new ContentRouteError("invalid-route", `type ${typeKey} has no single route`);
			}
			if (!record.archiveBase) throw new ContentRouteError("invalid-route", `type ${typeKey} has no archive route`);
			return { pathname: pathname(record.archiveBase) };
		},
		pathFor(match) {
			const record = byKey.get(match.typeKey);
			if (!record) throw new ContentRouteError("unknown-type", `unknown routable type ${match.typeKey}`);
			if (match.kind === "single") return compiled.canonical(match.typeKey, match.pathSegments);
			if (!record.archiveBase) throw new ContentRouteError("invalid-route", `type ${match.typeKey} has no archive route`);
			const base = [...record.archiveBase];
			if (match.kind === "feed") base.push("feed");
			if (match.page > 1) base.push("page", String(match.page));
			return { pathname: pathname(base) };
		}
	};
	return Object.freeze(compiled);
}
async function resolveContentRoute(compiled, lookup, url, principal, passwordProof) {
	const match = compiled.match(url);
	if (!match) return { kind: "notFound" };
	const expected = compiled.pathFor(match);
	const actualPath = normalizedPathname(url);
	if (match.kind !== "single") {
		if (actualPath !== expected.pathname) return {
			kind: "redirect",
			status: 308,
			location: expected.pathname
		};
		return Object.freeze({
			kind: match.kind,
			match
		});
	}
	const content = await lookup.resolve(match.typeKey, match.pathSegments, principal, passwordProof);
	if (!content) return { kind: "notFound" };
	const entryId = content.entry.id;
	const canonicalPath = await lookup.canonicalPath(entryId, principal);
	if (!canonicalPath) return { kind: "notFound" };
	const canonical = compiled.canonical(match.typeKey, canonicalPath);
	const queryCanonical = canonical.search ?? "";
	if (actualPath !== canonical.pathname || queryCanonical && url.search !== queryCanonical) return {
		kind: "redirect",
		status: 308,
		location: `${canonical.pathname}${queryCanonical}`
	};
	return Object.freeze({
		kind: "single",
		match,
		content
	});
}
function templateDescriptor(registry, type, key) {
	const descriptor = registry.resolve(type.key, key);
	if (!descriptor) throw new ContentRouteError("template-resolution", `template ${key} is missing for type ${type.key}`, "templateKey");
	if (descriptor.active === false) throw new ContentRouteError("template-resolution", `template ${key} is inactive`, "templateKey");
	if (!descriptor.typeKeys.includes(type.key)) throw new ContentRouteError("template-resolution", `template ${key} is not assigned to type ${type.key}`, "templateKey");
	return descriptor;
}
function resolveContentTemplate(registry, type, match, entry) {
	if (match.typeKey !== type.key) throw new ContentRouteError("template-resolution", "route type does not match resolved type", "typeKey");
	if (entry && entry.type !== type.key) throw new ContentRouteError("template-resolution", "entry type does not match resolved route type", "entry.type");
	if (entry && entry.typeDefinitionRevision !== type.revision) throw new ContentRouteError("template-resolution", "entry references a stale type definition", "entry.typeDefinitionRevision");
	const key = match.kind === "single" && entry?.templateKey ? entry.templateKey : type.defaultTemplateKey;
	templateDescriptor(registry, type, key);
	const lockedBlocks = type.templateLock && type.template ? type.template : void 0;
	return Object.freeze({
		templateKey: key,
		...lockedBlocks ? { lockedBlocks } : {}
	});
}
//#endregion
//#region src/headless.ts
const UUID_RE$1 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const OPERATION_KEYS = [
	"list",
	"count",
	"get",
	"create",
	"update",
	"transition",
	"trash",
	"restore",
	"delete",
	"listRevisions",
	"getRevision",
	"restoreRevision",
	"createAutosave",
	"getAutosave",
	"restoreAutosave"
];
var ContentHeadlessError = class extends Error {
	code;
	detail;
	field;
	name = "ContentHeadlessError";
	constructor(code, detail, field) {
		super(`content headless ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
const ERRORS = Object.freeze([
	"validation",
	"authorization",
	"notFound",
	"conflict",
	"capability",
	"integrity"
]);
function object(value, field = "input") {
	if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ContentHeadlessError("invalid-input", "must be an object", field);
	return value;
}
function exactKeys(value, allowed, field = "input") {
	for (const key of Object.keys(value)) if (!allowed.includes(key)) throw new ContentHeadlessError("invalid-input", "contains an unsupported property", `${field}.${key}`);
}
function string(value, field, max = 4096) {
	if (typeof value !== "string") throw new ContentHeadlessError("invalid-input", "must be a string", field);
	const normalized = value.trim();
	if (!normalized || normalized.length > max) throw new ContentHeadlessError("invalid-input", `must contain 1..${max} characters`, field);
	return normalized;
}
function uuid$1(value, field) {
	const result = string(value, field, 128);
	if (!UUID_RE$1.test(result)) throw new ContentHeadlessError("invalid-input", "must be a UUID", field);
	return result;
}
function date(value, field) {
	const parsed = value instanceof Date ? new Date(value.getTime()) : typeof value === "string" ? new Date(value) : null;
	if (!parsed || Number.isNaN(parsed.getTime())) throw new ContentHeadlessError("invalid-input", "must be an ISO timestamp or Date", field);
	return parsed;
}
function integer(value, field, min, max) {
	if (typeof value !== "number" || !Number.isSafeInteger(value) || value < min || value > max) throw new ContentHeadlessError("invalid-input", `must be an integer in ${min}..${max}`, field);
	return value;
}
function schemaValue$1(value, path = "schema") {
	if (value === null || typeof value === "string" || typeof value === "boolean") return value;
	if (typeof value === "number") {
		if (!Number.isFinite(value)) throw new ContentHeadlessError("invalid-schema-source", "contains a non-finite number", path);
		return value;
	}
	if (value instanceof Date) return value.toISOString();
	if (Array.isArray(value)) return Object.freeze(value.map((item, index) => schemaValue$1(item, `${path}.${index}`)));
	if (typeof value === "object") {
		const out = {};
		for (const key of Object.keys(value).sort()) {
			const child = value[key];
			if (child !== void 0) out[key] = schemaValue$1(child, `${path}.${key}`);
		}
		return Object.freeze(out);
	}
	throw new ContentHeadlessError("invalid-schema-source", "contains a non-serializable value", path);
}
function uniqueByKey(values, field) {
	const map = /* @__PURE__ */ new Map();
	for (const value of values) {
		if (!value || typeof value.key !== "string" || !value.key) throw new ContentHeadlessError("invalid-schema-source", "contains an invalid key", field);
		if (map.has(value.key)) throw new ContentHeadlessError("invalid-schema-source", `duplicate key ${value.key}`, field);
		map.set(value.key, value);
	}
	return map;
}
function jsonSchema(type, properties, required = []) {
	return Object.freeze({
		type,
		...properties ? { properties: Object.freeze(properties) } : {},
		...required.length ? { required: Object.freeze([...required]) } : {}
	});
}
const STRING = jsonSchema("string");
const NUMBER = jsonSchema("number");
jsonSchema("boolean");
const DATE = Object.freeze({
	type: "string",
	format: "date-time"
});
const UUID = Object.freeze({
	type: "string",
	format: "uuid"
});
const ENTRY = Object.freeze({ $ref: "ContentEntry" });
const REVISION = Object.freeze({ $ref: "ContentRevision" });
const AUTOSAVE = Object.freeze({ $ref: "ContentAutosave" });
function operationSchemas(key) {
	switch (key) {
		case "list": return {
			input: Object.freeze({ $ref: "ContentListQuery" }),
			output: Object.freeze({ $ref: "ContentPage" })
		};
		case "count": return {
			input: Object.freeze({ $ref: "ContentListQuery" }),
			output: NUMBER
		};
		case "get": return {
			input: jsonSchema("object", {
				id: UUID,
				passwordProof: STRING
			}, ["id"]),
			output: Object.freeze({ $ref: "ProtectedContentRead" })
		};
		case "create": return {
			input: Object.freeze({ $ref: "ContentWriteInputWithOperationId" }),
			output: ENTRY
		};
		case "update": return {
			input: jsonSchema("object", {
				id: UUID,
				patch: Object.freeze({ $ref: "ContentUpdatePatch" }),
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"patch",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: ENTRY
		};
		case "transition": return {
			input: jsonSchema("object", {
				id: UUID,
				to: STRING,
				scheduleAt: DATE,
				publishedAt: DATE,
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"to",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: ENTRY
		};
		case "trash":
		case "restore": return {
			input: jsonSchema("object", {
				id: UUID,
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: ENTRY
		};
		case "delete": return {
			input: jsonSchema("object", {
				id: UUID,
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: Object.freeze({ $ref: "EntityRef" })
		};
		case "listRevisions": return {
			input: jsonSchema("object", {
				id: UUID,
				before: NUMBER,
				limit: NUMBER
			}, ["id"]),
			output: Object.freeze({ $ref: "ContentRevisionPage" })
		};
		case "getRevision": return {
			input: jsonSchema("object", {
				id: UUID,
				revisionId: UUID
			}, ["id", "revisionId"]),
			output: REVISION
		};
		case "restoreRevision": return {
			input: jsonSchema("object", {
				id: UUID,
				revisionId: UUID,
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"revisionId",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: ENTRY
		};
		case "createAutosave": return {
			input: jsonSchema("object", {
				id: UUID,
				snapshot: Object.freeze({ $ref: "ContentUpdatePatch" }),
				parentRevisionId: UUID,
				operationId: STRING
			}, [
				"id",
				"snapshot",
				"parentRevisionId",
				"operationId"
			]),
			output: AUTOSAVE
		};
		case "getAutosave": return {
			input: jsonSchema("object", {
				id: UUID,
				autosaveId: UUID
			}, ["id", "autosaveId"]),
			output: AUTOSAVE
		};
		case "restoreAutosave": return {
			input: jsonSchema("object", {
				id: UUID,
				autosaveId: UUID,
				expectedUpdatedAt: DATE,
				operationId: STRING
			}, [
				"id",
				"autosaveId",
				"expectedUpdatedAt",
				"operationId"
			]),
			output: ENTRY
		};
	}
}
function enabledKeys(type) {
	if (type.rest === false) return [];
	const keys = [
		"list",
		"count",
		"get",
		...type.active ? ["create"] : [],
		"update",
		"transition",
		"trash",
		"restore",
		"delete"
	];
	if (type.rest.revisions && type.supports.includes("revisions")) keys.push("listRevisions", "getRevision", "restoreRevision");
	if (type.rest.autosaves && type.rest.revisions && type.supports.includes("revisions")) keys.push("createAutosave", "getAutosave", "restoreAutosave");
	return keys;
}
function capabilityLabel(type, key) {
	switch (key) {
		case "list":
		case "count":
		case "get": return type.capabilities.read;
		case "create": return type.capabilities.create;
		case "delete": return `${type.capabilities.deleteOwn}|${type.capabilities.deleteOthers}`;
		case "transition": return `${type.capabilities.editOwn}|${type.capabilities.editOthers};published:${type.capabilities.publish}`;
		default: return `${type.capabilities.editOwn}|${type.capabilities.editOthers}`;
	}
}
function typeSchema(type, fields) {
	return schemaValue$1({
		key: type.key,
		version: type.version,
		revision: type.revision,
		canonicalHash: type.canonicalHash,
		active: type.active,
		labels: type.labels,
		description: type.description,
		public: type.public,
		hierarchical: type.hierarchical,
		excludeFromSearch: type.excludeFromSearch,
		publiclyQueryable: type.publiclyQueryable,
		supports: type.supports,
		taxonomies: type.taxonomies,
		capabilities: type.capabilities,
		rest: type.rest,
		statusKeys: type.statusKeys,
		defaultTemplateKey: type.defaultTemplateKey,
		template: type.template,
		templateLock: type.templateLock,
		...fields === void 0 ? {} : { fields }
	});
}
function statusSchema(status) {
	return schemaValue$1({
		key: status.key,
		version: status.version,
		revision: status.revision,
		canonicalHash: status.canonicalHash,
		active: status.active,
		label: status.label,
		published: status.published,
		internal: status.internal,
		excludeFromSearch: status.excludeFromSearch,
		publiclyQueryable: status.publiclyQueryable,
		showInAdminAll: status.showInAdminAll,
		showInAdminStatusFilter: status.showInAdminStatusFilter,
		dateLabel: status.dateLabel,
		transitionInput: status.transitionInput
	});
}
async function generateContentSchema(sources) {
	const types = uniqueByKey(sources.types, "types"), statuses = uniqueByKey(sources.statuses, "statuses"), taxonomies = uniqueByKey(sources.taxonomies, "taxonomies");
	for (const taxonomy of taxonomies.values()) if (typeof taxonomy.version !== "string" || !taxonomy.version) throw new ContentHeadlessError("invalid-schema-source", `taxonomy ${taxonomy.key} has no version`, "taxonomies");
	const serializedTypes = [];
	const operations = [];
	for (const type of [...types.values()].sort((a, b) => a.key.localeCompare(b.key))) {
		if (type.rest !== false) {
			for (const statusKey of type.statusKeys ?? []) if (!statuses.has(statusKey)) throw new ContentHeadlessError("invalid-schema-source", `type ${type.key} references missing status ${statusKey}`, "types");
			for (const taxonomyKey of type.taxonomies) if (!taxonomies.has(taxonomyKey)) throw new ContentHeadlessError("invalid-schema-source", `type ${type.key} references missing taxonomy schema ${taxonomyKey}`, "taxonomies");
			if (type.supports.includes("customFields") && !sources.fields) throw new ContentHeadlessError("invalid-schema-source", `type ${type.key} exposes custom fields without a fields schema adapter`, "fields");
		}
		let fields;
		if (sources.fields && type.rest !== false) fields = schemaValue$1(await sources.fields.schemaForType(type.key), `fields.${type.key}`);
		serializedTypes.push(typeSchema(type, fields));
		for (const key of enabledKeys(type)) {
			const schemas = operationSchemas(key);
			operations.push(Object.freeze({
				typeKey: type.key,
				key,
				capability: capabilityLabel(type, key),
				input: schemas.input,
				output: schemas.output,
				errors: ERRORS
			}));
		}
	}
	const serializedStatuses = [...statuses.values()].sort((a, b) => a.key.localeCompare(b.key)).map(statusSchema);
	const serializedTaxonomies = [...taxonomies.values()].sort((a, b) => a.key.localeCompare(b.key)).map((taxonomy) => Object.freeze({
		key: taxonomy.key,
		version: taxonomy.version,
		schema: schemaValue$1(taxonomy.schema, `taxonomies.${taxonomy.key}`)
	}));
	const version = await canonicalContentMigrationHash(schemaValue$1({
		types: serializedTypes,
		statuses: serializedStatuses,
		taxonomies: serializedTaxonomies,
		operations
	}));
	return Object.freeze({
		version,
		types: Object.freeze(serializedTypes),
		statuses: Object.freeze(serializedStatuses),
		taxonomies: Object.freeze(serializedTaxonomies),
		operations: Object.freeze(operations)
	});
}
function rawString(value, field, max, allowEmpty = true) {
	if (typeof value !== "string" || value.length > max || !allowEmpty && value.trim().length === 0) throw new ContentHeadlessError("invalid-input", `must be ${allowEmpty ? "a" : "a non-empty"} string up to ${max} characters`, field);
	return value;
}
function stringArray$1(value, field, max = 500, uuidOnly = false) {
	if (!Array.isArray(value) || value.length > max) throw new ContentHeadlessError("invalid-input", `must be an array with at most ${max} values`, field);
	const out = [];
	const seen = /* @__PURE__ */ new Set();
	for (const item of value) {
		const v = string(item, field, 256);
		if (uuidOnly && !UUID_RE$1.test(v)) throw new ContentHeadlessError("invalid-input", "contains an invalid UUID", field);
		if (!seen.has(v)) {
			seen.add(v);
			out.push(v);
		}
	}
	return out;
}
function mediaRef(value, field) {
	if (value === null) return null;
	const raw = object(value, field);
	exactKeys(raw, ["id", "kind"], field);
	const id = string(raw.id, `${field}.id`, 256);
	const kind = raw.kind === void 0 ? void 0 : string(raw.kind, `${field}.kind`, 128);
	return Object.freeze({
		id,
		...kind ? { kind } : {}
	});
}
const WRITE_KEYS = [
	"slug",
	"type",
	"title",
	"body",
	"excerpt",
	"visibility",
	"author",
	"termIds",
	"parentId",
	"menuOrder",
	"templateKey",
	"featuredMedia",
	"commentStatus",
	"pingStatus",
	"sticky",
	"format"
];
const PATCH_KEYS = WRITE_KEYS.filter((key) => key !== "type");
function decodeWrite(value, mode) {
	const raw = object(value);
	exactKeys(raw, mode === "create" ? WRITE_KEYS : PATCH_KEYS);
	if (mode === "patch" && Object.keys(raw).length === 0) throw new ContentHeadlessError("invalid-input", "patch must contain at least one field", "patch");
	const out = {};
	if (mode === "create") {
		out.slug = string(raw.slug, "slug", 200);
		out.type = string(raw.type, "type", 128);
		out.title = rawString(raw.title, "title", 300);
		out.body = rawString(raw.body, "body", 5e6);
	} else {
		if (raw.slug !== void 0) out.slug = string(raw.slug, "patch.slug", 200);
		if (raw.title !== void 0) out.title = rawString(raw.title, "patch.title", 300);
		if (raw.body !== void 0) out.body = rawString(raw.body, "patch.body", 5e6);
	}
	if (raw.excerpt !== void 0) out.excerpt = rawString(raw.excerpt, `${mode === "patch" ? "patch." : ""}excerpt`, 1e5);
	if (raw.visibility !== void 0) {
		if (![
			"public",
			"private",
			"members"
		].includes(String(raw.visibility))) throw new ContentHeadlessError("invalid-input", "must be public, private, or members", "visibility");
		out.visibility = raw.visibility;
	}
	if (raw.author !== void 0) out.author = string(raw.author, "author", 256);
	if (raw.termIds !== void 0) out.termIds = stringArray$1(raw.termIds, "termIds", 1e4, true);
	if (raw.parentId !== void 0) {
		if (raw.parentId !== null && !UUID_RE$1.test(string(raw.parentId, "parentId", 128))) throw new ContentHeadlessError("invalid-input", "must be null or a UUID", "parentId");
		out.parentId = raw.parentId;
	}
	if (raw.menuOrder !== void 0) out.menuOrder = integer(raw.menuOrder, "menuOrder", -2147483648, 2147483647);
	if (raw.templateKey !== void 0) out.templateKey = raw.templateKey === null ? null : string(raw.templateKey, "templateKey", 256);
	if (raw.featuredMedia !== void 0) out.featuredMedia = mediaRef(raw.featuredMedia, "featuredMedia");
	if (raw.commentStatus !== void 0) {
		if (raw.commentStatus !== "open" && raw.commentStatus !== "closed") throw new ContentHeadlessError("invalid-input", "must be open or closed", "commentStatus");
		out.commentStatus = raw.commentStatus;
	}
	if (raw.pingStatus !== void 0) {
		if (raw.pingStatus !== "open" && raw.pingStatus !== "closed") throw new ContentHeadlessError("invalid-input", "must be open or closed", "pingStatus");
		out.pingStatus = raw.pingStatus;
	}
	if (raw.sticky !== void 0) {
		if (typeof raw.sticky !== "boolean") throw new ContentHeadlessError("invalid-input", "must be a boolean", "sticky");
		out.sticky = raw.sticky;
	}
	if (raw.format !== void 0) out.format = raw.format === null ? null : string(raw.format, "format", 128);
	return Object.freeze(out);
}
function decodeList(value) {
	const raw = object(value);
	let dateValue = raw.date;
	if (dateValue !== void 0) {
		const d = object(dateValue, "date");
		exactKeys(d, [
			"after",
			"before",
			"field"
		], "date");
		dateValue = {
			...d,
			...d.after === void 0 ? {} : { after: date(d.after, "date.after") },
			...d.before === void 0 ? {} : { before: date(d.before, "date.before") }
		};
	}
	return decodeContentListQuery({
		...raw,
		...dateValue === void 0 ? {} : { date: dateValue }
	});
}
function decodedBase(value, extra) {
	const raw = object(value);
	exactKeys(raw, extra);
	return raw;
}
function decodeOperation(key, value) {
	let result;
	switch (key) {
		case "list":
		case "count":
			result = decodeList(value);
			break;
		case "get": {
			const raw = decodedBase(value, ["id", "passwordProof"]);
			result = {
				id: uuid$1(raw.id, "id"),
				...raw.passwordProof === void 0 ? {} : { passwordProof: string(raw.passwordProof, "passwordProof", 4096) }
			};
			break;
		}
		case "create": {
			const raw = object(value);
			exactKeys(raw, [...WRITE_KEYS, "operationId"]);
			const { operationId, ...write } = raw;
			result = {
				...decodeWrite(write, "create"),
				operationId: string(operationId, "operationId", 256)
			};
			break;
		}
		case "update": {
			const raw = decodedBase(value, [
				"id",
				"patch",
				"expectedUpdatedAt",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				patch: decodeWrite(raw.patch, "patch"),
				expectedUpdatedAt: date(raw.expectedUpdatedAt, "expectedUpdatedAt"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		case "transition": {
			const raw = decodedBase(value, [
				"id",
				"to",
				"scheduleAt",
				"publishedAt",
				"expectedUpdatedAt",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				to: string(raw.to, "to", 128),
				...raw.scheduleAt === void 0 ? {} : { scheduleAt: date(raw.scheduleAt, "scheduleAt") },
				...raw.publishedAt === void 0 ? {} : { publishedAt: date(raw.publishedAt, "publishedAt") },
				expectedUpdatedAt: date(raw.expectedUpdatedAt, "expectedUpdatedAt"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		case "trash":
		case "restore":
		case "delete": {
			const raw = decodedBase(value, [
				"id",
				"expectedUpdatedAt",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				expectedUpdatedAt: date(raw.expectedUpdatedAt, "expectedUpdatedAt"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		case "listRevisions": {
			const raw = decodedBase(value, [
				"id",
				"before",
				"limit"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				...raw.before === void 0 ? {} : { before: integer(raw.before, "before", 1, 2147483647) },
				...raw.limit === void 0 ? {} : { limit: integer(raw.limit, "limit", 1, 100) }
			};
			break;
		}
		case "getRevision": {
			const raw = decodedBase(value, ["id", "revisionId"]);
			result = {
				id: uuid$1(raw.id, "id"),
				revisionId: uuid$1(raw.revisionId, "revisionId")
			};
			break;
		}
		case "restoreRevision": {
			const raw = decodedBase(value, [
				"id",
				"revisionId",
				"expectedUpdatedAt",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				revisionId: uuid$1(raw.revisionId, "revisionId"),
				expectedUpdatedAt: date(raw.expectedUpdatedAt, "expectedUpdatedAt"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		case "createAutosave": {
			const raw = decodedBase(value, [
				"id",
				"snapshot",
				"parentRevisionId",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				snapshot: decodeWrite(raw.snapshot, "patch"),
				parentRevisionId: uuid$1(raw.parentRevisionId, "parentRevisionId"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		case "getAutosave": {
			const raw = decodedBase(value, ["id", "autosaveId"]);
			result = {
				id: uuid$1(raw.id, "id"),
				autosaveId: uuid$1(raw.autosaveId, "autosaveId")
			};
			break;
		}
		case "restoreAutosave": {
			const raw = decodedBase(value, [
				"id",
				"autosaveId",
				"expectedUpdatedAt",
				"operationId"
			]);
			result = {
				id: uuid$1(raw.id, "id"),
				autosaveId: uuid$1(raw.autosaveId, "autosaveId"),
				expectedUpdatedAt: date(raw.expectedUpdatedAt, "expectedUpdatedAt"),
				operationId: string(raw.operationId, "operationId", 256)
			};
			break;
		}
		default: throw new ContentHeadlessError("invalid-input", "unknown operation key");
	}
	return result;
}
function hasAny(principal, ...capabilities) {
	return capabilities.some((capability) => principal.capabilities.has(capability));
}
function coarseAuthorize(type, key, input, principal, statuses) {
	if (!principal || typeof principal.id !== "string" || !(principal.capabilities instanceof Set) && typeof principal.capabilities?.has !== "function") throw new ContentAuthorizationError("read", "unknown", "principal is invalid");
	switch (key) {
		case "list":
		case "count":
		case "get":
			if (!principal.capabilities.has(type.capabilities.read)) throw new ContentAuthorizationError("read", principal.id, "missing required read capability");
			return;
		case "create":
			if (!type.active || !principal.capabilities.has(type.capabilities.create)) throw new ContentAuthorizationError("create", principal.id, "missing required create capability or inactive type");
			return;
		case "trash":
		case "delete":
			if (!hasAny(principal, type.capabilities.deleteOwn, type.capabilities.deleteOthers)) throw new ContentAuthorizationError("delete", principal.id, "missing delete capability branch");
			return;
		case "transition": {
			if (!hasAny(principal, type.capabilities.editOwn, type.capabilities.editOthers)) throw new ContentAuthorizationError("edit", principal.id, "missing edit capability branch");
			const target = input.to;
			const status = statuses.get(target);
			if (!status) throw new ContentHeadlessError("invalid-input", `unknown target status ${target}`, "to");
			if (type.statusKeys !== void 0 && !type.statusKeys.includes(target)) throw new ContentHeadlessError("invalid-input", `status ${target} is disabled for type ${type.key}`, "to");
			if (status.published && !principal.capabilities.has(type.capabilities.publish)) throw new ContentAuthorizationError("publish", principal.id, "missing publish capability");
			return;
		}
		default: if (!hasAny(principal, type.capabilities.editOwn, type.capabilities.editOthers)) throw new ContentAuthorizationError("edit", principal.id, "missing edit capability branch");
	}
}
function operationError(error) {
	if (error instanceof ContentHeadlessError) return {
		code: error.code,
		category: error.code === "invalid-input" ? "validation" : "integrity",
		message: error.message,
		...error.field ? { fieldPath: error.field } : {}
	};
	if (error instanceof ContentAuthorizationError) return {
		code: "access-denied",
		category: "authorization",
		message: "operation is not authorized"
	};
	if (error instanceof ContentQueryError) {
		const category = error.code === "validation" ? "validation" : error.code === "capability-unavailable" ? "capability" : "integrity";
		return {
			code: error.code,
			category,
			message: error.message,
			...error.field ? { fieldPath: error.field } : {}
		};
	}
	if (error instanceof ContentLifecycleError) {
		const category = error.code === "validation" ? "validation" : error.code === "access-denied" ? "authorization" : error.code === "not-found" ? "notFound" : error.code === "capability-unavailable" ? "capability" : [
			"conflict",
			"stale-version",
			"operation-conflict"
		].includes(error.code) ? "conflict" : "integrity";
		return {
			code: error.code,
			category,
			message: category === "authorization" ? "operation is not authorized" : error.message,
			...error.field ? { fieldPath: error.field } : {}
		};
	}
	return {
		code: "operation-failed",
		category: "integrity",
		message: "operation failed"
	};
}
function pair(typeKey, key) {
	return `${typeKey}\u0000${key}`;
}
async function createContentHeadlessRuntime(sources, handlers) {
	const schema = await generateContentSchema(sources);
	const advertised = new Set(schema.operations.map((operation) => pair(operation.typeKey, operation.key)));
	const typeMap = uniqueByKey(sources.types, "types"), statusMap = uniqueByKey(sources.statuses, "statuses");
	for (const operation of schema.operations) if (typeof handlers[operation.typeKey]?.[operation.key] !== "function") throw new ContentHeadlessError("handler-mismatch", `missing handler for ${operation.typeKey}.${operation.key}`);
	for (const [typeKey, group] of Object.entries(handlers)) {
		if (typeof group !== "object" || group === null) throw new ContentHeadlessError("handler-mismatch", `handler group ${typeKey} is invalid`);
		for (const [key, handler] of Object.entries(group)) {
			if (handler === void 0) continue;
			if (!OPERATION_KEYS.includes(key) || typeof handler !== "function" || !advertised.has(pair(typeKey, key))) throw new ContentHeadlessError("handler-mismatch", `handler ${typeKey}.${key} is not advertised`);
		}
	}
	return Object.freeze({
		schema,
		async invoke(typeKey, key, untrustedInput, principal) {
			if (!advertised.has(pair(typeKey, key))) return {
				ok: false,
				error: {
					code: "operation-disabled",
					category: "capability",
					message: "operation is not available"
				}
			};
			const type = typeMap.get(typeKey);
			if (!type) return {
				ok: false,
				error: {
					code: "operation-disabled",
					category: "capability",
					message: "operation is not available"
				}
			};
			try {
				const decoded = decodeOperation(key, untrustedInput);
				if (key === "create" && decoded.type !== typeKey) throw new ContentHeadlessError("invalid-input", "create type does not match invoked type", "type");
				coarseAuthorize(type, key, decoded, principal, statusMap);
				const handler = handlers[typeKey]?.[key];
				if (!handler) return {
					ok: false,
					error: {
						code: "operation-disabled",
						category: "capability",
						message: "operation is not available"
					}
				};
				return {
					ok: true,
					value: await handler(decoded, principal)
				};
			} catch (error) {
				return {
					ok: false,
					error: operationError(error)
				};
			}
		}
	});
}
//#endregion
//#region src/import.ts
/** Maximum bind parameters in one native D1 import statement. */
const CONTENT_IMPORT_D1_BIND_BUDGET = 100;
/** Maximum statements one content-import contribution may add to a host-owned D1 batch. Cloudflare's strictest current Workers plan permits 50 D1 queries per invocation. */
const CONTENT_IMPORT_D1_STATEMENT_BUDGET = 50;
var ContentImportError = class extends Error {
	code;
	detail;
	field;
	name = "ContentImportError";
	constructor(code, detail, field) {
		super(`content import ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function isContentImportError(error) {
	return error instanceof ContentImportError;
}
const fail$1 = (code, detail, field) => {
	throw new ContentImportError(code, detail, field);
};
function required(value, code, detail) {
	if (value === void 0) fail$1(code, detail);
	return value;
}
const ALL_BRANCHES = Object.freeze([
	"manageType",
	"migrateAll",
	"manageTerms",
	"create",
	"read",
	"readPrivate",
	"readProtected",
	"editOwn",
	"editOthers",
	"editPrivate",
	"editPublished",
	"publish",
	"deleteOwn",
	"deleteOthers",
	"deletePrivate",
	"deletePublished"
]);
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const KEY_RE = /^[a-z][a-z0-9_-]*$/;
const FORBIDDEN_IMPORT_KEY = /password|credential/i;
function boundedString(value, field, max = 4096) {
	if (typeof value !== "string") fail$1("validation", "must be a string", field);
	const normalized = value.trim();
	if (!normalized || normalized.length > max) fail$1("validation", `must contain 1..${max} characters`, field);
	return normalized;
}
function plain(value, field) {
	if (typeof value !== "object" || value === null || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype) fail$1("validation", "must be a plain object", field);
	return value;
}
function array(value, field, max) {
	if (!Array.isArray(value) || value.length > max) fail$1("limit", `must be an array with at most ${max} items`, field);
	return value;
}
function positive$1(value, field) {
	if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) fail$1("validation", "must be a positive safe integer", field);
	return value;
}
function nonnegative(value, field) {
	if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) fail$1("validation", "must be a non-negative safe integer", field);
	return value;
}
function iso$1(value, field) {
	const raw = boundedString(value, field, 128), date = new Date(raw);
	if (Number.isNaN(date.getTime())) fail$1("validation", "must be an ISO timestamp", field);
	return date.toISOString();
}
function uuid(value, field) {
	const raw = boundedString(value, field, 128);
	if (!UUID_RE.test(raw)) fail$1("validation", "must be a UUID", field);
	return raw;
}
function key(value, field) {
	const raw = boundedString(value, field, 128);
	if (!KEY_RE.test(raw)) fail$1("validation", "must be a normalized key", field);
	return raw;
}
function schemaValue(value, field = "value", depth = 0) {
	if (depth > 32) fail$1("limit", "exceeds maximum nested depth", field);
	if (value === null || typeof value === "string" || typeof value === "boolean") return value;
	if (value instanceof Date) return value.toISOString();
	if (typeof value === "bigint") return value.toString();
	if (typeof value === "number") {
		if (!Number.isFinite(value)) fail$1("validation", "contains non-finite number", field);
		return value;
	}
	if (Array.isArray(value)) return Object.freeze(value.map((item, index) => schemaValue(item, `${field}.${index}`, depth + 1)));
	if (typeof value === "object") {
		if (Object.getPrototypeOf(value) !== Object.prototype) fail$1("validation", "contains a non-plain object", field);
		const out = {};
		for (const [name, child] of Object.entries(value)) {
			if (FORBIDDEN_IMPORT_KEY.test(name)) fail$1("validation", "password/credential material is forbidden", `${field}.${name}`);
			if (child !== void 0) out[name] = schemaValue(child, `${field}.${name}`, depth + 1);
		}
		return Object.freeze(out);
	}
	return fail$1("validation", "contains unsupported value", field);
}
function stableScope(scope) {
	const tenantKey = boundedString(scope.tenantKey, "scope.tenantKey", 256), typeKeys = [...new Set(scope.typeKeys.map((item, index) => key(item, `scope.typeKeys.${index}`)))].sort();
	if (typeKeys.length === 0 || typeKeys.length > 500) fail$1("validation", "scope must contain 1..500 type keys", "scope.typeKeys");
	return Object.freeze({
		tenantKey,
		typeKeys: Object.freeze(typeKeys)
	});
}
function assertD1Scope(scope) {
	const normalized = stableScope(scope);
	if (normalized.typeKeys.length > CONTENT_IMPORT_D1_BIND_BUDGET) fail$1("limit", `native D1 import scope requires ${normalized.typeKeys.length} bind parameters; maximum is ${CONTENT_IMPORT_D1_BIND_BUDGET}`, "scope.typeKeys");
	return normalized;
}
function sameScope(a, b) {
	return a.tenantKey === b.tenantKey && a.typeKeys.length === b.typeKeys.length && a.typeKeys.every((value, index) => value === b.typeKeys[index]);
}
function stableAuthority(input) {
	const map = /* @__PURE__ */ new Map();
	for (const row of input) {
		const typeKey = key(row.typeKey, "requiredAuthority.typeKey"), set = map.get(typeKey) ?? /* @__PURE__ */ new Set();
		for (const branch of row.branches) {
			if (!ALL_BRANCHES.includes(branch)) fail$1("validation", "unknown capability branch", "requiredAuthority.branches");
			set.add(branch);
		}
		map.set(typeKey, set);
	}
	return Object.freeze([...map].sort(([a], [b]) => a.localeCompare(b)).map(([typeKey, branches]) => Object.freeze({
		typeKey,
		branches: Object.freeze(ALL_BRANCHES.filter((branch) => branches.has(branch)))
	})));
}
function sameAuthority(a, b) {
	return JSON.stringify(stableAuthority(a)) === JSON.stringify(stableAuthority(b));
}
function principalScope(principal) {
	return `${principal.tenantId ?? ""}:${principal.id}`;
}
function isUniqueViolation(error) {
	let current = error;
	while (current) {
		const code = current.code, message = current instanceof Error ? current.message : String(current);
		if (code === "23505" || /unique constraint|duplicate key|content_lifecycle_journal_operation_uq/i.test(message)) return true;
		current = current instanceof Error ? current.cause : void 0;
	}
	return false;
}
function rowJson(value) {
	const parsed = typeof value === "string" ? JSON.parse(value) : value;
	if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) fail$1("integrity", "stored journal payload is malformed");
	return parsed;
}
function rowsOf$1(result) {
	if (Array.isArray(result)) return result;
	const rows = result?.rows;
	if (Array.isArray(rows)) return rows;
	return fail$1("integrity", "database adapter returned invalid rows");
}
async function execRows(tx, query) {
	return rowsOf$1(await tx.execute(query));
}
async function execRowsByD1BindChunks(tx, values, query) {
	const rows = [];
	for (let offset = 0; offset < values.length; offset += CONTENT_IMPORT_D1_BIND_BUDGET) rows.push(...await execRows(tx, query(values.slice(offset, offset + CONTENT_IMPORT_D1_BIND_BUDGET))));
	return rows;
}
function nullableString(value, field, max = 4096) {
	return value === null ? null : boundedString(value, field, max);
}
function parseDefinitionVersions(value, field, keyValue, normalize) {
	const versions = array(value, field, 1e3).map((raw, index) => {
		const item = plain(raw, `${field}.${index}`), definition = normalize(item.definition), origin = item.origin;
		if (origin !== "code" && origin !== "db" && origin !== "import") fail$1("validation", "invalid origin", `${field}.${index}.origin`);
		if (definition.key !== keyValue) fail$1("validation", "definition version key mismatch", `${field}.${index}.definition.key`);
		return Object.freeze({
			revision: positive$1(item.revision, `${field}.${index}.revision`),
			canonicalHash: boundedString(item.canonicalHash, `${field}.${index}.canonicalHash`, 128),
			definition,
			origin,
			createdAt: iso$1(item.createdAt, `${field}.${index}.createdAt`)
		});
	});
	duplicates(versions, (item) => String(item.revision), field);
	return Object.freeze(versions.sort((a, b) => a.revision - b.revision));
}
function parseType(raw, index) {
	const item = plain(raw, `types.${index}`), definition = normalizeContentTypeDefinition(item.definition), itemKey = key(item.key, `types.${index}.key`);
	if (definition.key !== itemKey) fail$1("validation", "definition key mismatch", `types.${index}.definition.key`);
	const origin = item.origin;
	if (origin !== "code" && origin !== "db" && origin !== "import") fail$1("validation", "invalid origin", `types.${index}.origin`);
	const versions = parseDefinitionVersions(item.versions, `types.${index}.versions`, itemKey, normalizeContentTypeDefinition);
	const active = Boolean(item.active);
	if (active !== definition.active) fail$1("validation", "type active state differs from normalized definition", `types.${index}.active`);
	return Object.freeze({
		key: itemKey,
		origin,
		version: positive$1(item.version, `types.${index}.version`),
		revision: positive$1(item.revision, `types.${index}.revision`),
		active,
		canonicalHash: boundedString(item.canonicalHash, `types.${index}.canonicalHash`, 128),
		definition,
		versions
	});
}
function parseStatus(raw, index) {
	const item = plain(raw, `statuses.${index}`), definition = normalizeContentStatusDefinition(item.definition), itemKey = key(item.key, `statuses.${index}.key`);
	if (definition.key !== itemKey) fail$1("validation", "definition key mismatch", `statuses.${index}.definition.key`);
	const origin = item.origin;
	if (origin !== "code" && origin !== "db" && origin !== "import") fail$1("validation", "invalid origin", `statuses.${index}.origin`);
	const versions = parseDefinitionVersions(item.versions, `statuses.${index}.versions`, itemKey, normalizeContentStatusDefinition);
	if (typeof item.active !== "boolean") fail$1("validation", "status active must be boolean", `statuses.${index}.active`);
	return Object.freeze({
		key: itemKey,
		origin,
		version: positive$1(item.version, `statuses.${index}.version`),
		revision: positive$1(item.revision, `statuses.${index}.revision`),
		active: item.active,
		canonicalHash: boundedString(item.canonicalHash, `statuses.${index}.canonicalHash`, 128),
		definition,
		versions
	});
}
function parseTaxonomy(raw, index) {
	const item = plain(raw, `taxonomies.${index}`);
	return Object.freeze({
		key: key(item.key, `taxonomies.${index}.key`),
		version: boundedString(item.version, `taxonomies.${index}.version`, 128)
	});
}
function parseTerm(raw, index) {
	const item = plain(raw, `terms.${index}`);
	return Object.freeze({
		id: uuid(item.id, `terms.${index}.id`),
		taxonomy: key(item.taxonomy, `terms.${index}.taxonomy`),
		slug: boundedString(item.slug, `terms.${index}.slug`, 256),
		name: boundedString(item.name, `terms.${index}.name`, 512),
		parentId: item.parentId === null ? null : uuid(item.parentId, `terms.${index}.parentId`),
		depth: nonnegative(item.depth, `terms.${index}.depth`),
		createdAt: iso$1(item.createdAt, `terms.${index}.createdAt`)
	});
}
function parseEntry(raw, index) {
	const item = plain(raw, `entries.${index}`), visibility = item.visibility, commentStatus = item.commentStatus, pingStatus = item.pingStatus;
	if (visibility !== "public" && visibility !== "private" && visibility !== "members") fail$1("validation", "invalid visibility", `entries.${index}.visibility`);
	if (commentStatus !== "open" && commentStatus !== "closed") fail$1("validation", "invalid comment status", `entries.${index}.commentStatus`);
	if (pingStatus !== "open" && pingStatus !== "closed") fail$1("validation", "invalid ping status", `entries.${index}.pingStatus`);
	if (typeof item.sticky !== "boolean") fail$1("validation", "must be boolean", `entries.${index}.sticky`);
	const termIds = array(item.termIds, `entries.${index}.termIds`, 1e4).map((value, i) => uuid(value, `entries.${index}.termIds.${i}`));
	return Object.freeze({
		id: uuid(item.id, `entries.${index}.id`),
		slug: boundedString(item.slug, `entries.${index}.slug`, 256),
		type: key(item.type, `entries.${index}.type`),
		title: typeof item.title === "string" && item.title.length <= 300 ? item.title : fail$1("validation", "invalid title", `entries.${index}.title`),
		body: typeof item.body === "string" && item.body.length <= 2e6 ? item.body : fail$1("validation", "invalid body", `entries.${index}.body`),
		status: key(item.status, `entries.${index}.status`),
		visibility,
		publishedAt: item.publishedAt === null ? null : iso$1(item.publishedAt, `entries.${index}.publishedAt`),
		author: boundedString(item.author, `entries.${index}.author`, 512),
		createdAt: iso$1(item.createdAt, `entries.${index}.createdAt`),
		updatedAt: iso$1(item.updatedAt, `entries.${index}.updatedAt`),
		parentId: item.parentId === null ? null : uuid(item.parentId, `entries.${index}.parentId`),
		menuOrder: nonnegative(item.menuOrder, `entries.${index}.menuOrder`),
		templateKey: nullableString(item.templateKey, `entries.${index}.templateKey`, 256),
		excerpt: typeof item.excerpt === "string" && item.excerpt.length <= 1e5 ? item.excerpt : fail$1("validation", "invalid excerpt", `entries.${index}.excerpt`),
		featuredMedia: item.featuredMedia === null ? null : schemaValue(item.featuredMedia, `entries.${index}.featuredMedia`),
		commentStatus,
		pingStatus,
		sticky: item.sticky,
		format: nullableString(item.format, `entries.${index}.format`, 128),
		deletedAt: item.deletedAt === null ? null : iso$1(item.deletedAt, `entries.${index}.deletedAt`),
		lastEditedBy: boundedString(item.lastEditedBy, `entries.${index}.lastEditedBy`, 512),
		typeDefinitionRevision: positive$1(item.typeDefinitionRevision, `entries.${index}.typeDefinitionRevision`),
		statusDefinitionRevision: positive$1(item.statusDefinitionRevision, `entries.${index}.statusDefinitionRevision`),
		termIds: Object.freeze(termIds)
	});
}
function parseRevision(raw, index) {
	const item = plain(raw, `revisions.${index}`), termIds = array(item.termIds, `revisions.${index}.termIds`, 1e4).map((value, i) => uuid(value, `revisions.${index}.termIds.${i}`));
	return Object.freeze({
		id: uuid(item.id, `revisions.${index}.id`),
		entryId: uuid(item.entryId, `revisions.${index}.entryId`),
		seq: positive$1(item.seq, `revisions.${index}.seq`),
		title: typeof item.title === "string" ? item.title : fail$1("validation", "invalid title", `revisions.${index}.title`),
		body: typeof item.body === "string" ? item.body : fail$1("validation", "invalid body", `revisions.${index}.body`),
		slug: boundedString(item.slug, `revisions.${index}.slug`, 256),
		type: key(item.type, `revisions.${index}.type`),
		termIds: Object.freeze(termIds),
		...item.snapshot === void 0 ? {} : { snapshot: schemaValue(item.snapshot, `revisions.${index}.snapshot`) },
		editor: boundedString(item.editor, `revisions.${index}.editor`, 512),
		createdAt: iso$1(item.createdAt, `revisions.${index}.createdAt`)
	});
}
function duplicates(items, id, field) {
	const seen = /* @__PURE__ */ new Set();
	for (const item of items) {
		const value = id(item);
		if (seen.has(value)) fail$1("validation", "contains duplicate identity", field);
		seen.add(value);
	}
}
function assertTermGraph(terms) {
	const byId = new Map(terms.map((item) => [item.id, item]));
	for (const item of terms) {
		if (item.parentId === null) {
			if (item.depth !== 0) fail$1("validation", "root term depth must be zero", `term:${item.id}`);
			continue;
		}
		const parent = byId.get(item.parentId);
		if (!parent) throw new ContentImportError("validation", "term references missing parent", `term:${item.id}`);
		if (parent.taxonomy !== item.taxonomy) fail$1("validation", "term parent must share taxonomy", `term:${item.id}`);
		if (item.depth !== parent.depth + 1) fail$1("validation", "term depth does not match parent", `term:${item.id}`);
		const seen = /* @__PURE__ */ new Set([item.id]);
		let cursor = parent;
		while (cursor) {
			if (seen.has(cursor.id)) fail$1("validation", "term hierarchy contains a cycle", `term:${item.id}`);
			seen.add(cursor.id);
			cursor = cursor.parentId === null ? void 0 : byId.get(cursor.parentId);
		}
	}
}
function assertEntryGraph(entries) {
	const byId = new Map(entries.map((item) => [item.id, item]));
	for (const item of entries) {
		if (item.parentId === null) continue;
		const parent = byId.get(item.parentId);
		if (!parent) throw new ContentImportError("validation", "entry references missing parent", `entry:${item.id}`);
		if (parent.type !== item.type) fail$1("validation", "entry parent must share content type", `entry:${item.id}`);
		const seen = /* @__PURE__ */ new Set([item.id]);
		let cursor = parent;
		while (cursor) {
			if (seen.has(cursor.id)) fail$1("validation", "entry hierarchy contains a cycle", `entry:${item.id}`);
			seen.add(cursor.id);
			cursor = cursor.parentId === null ? void 0 : byId.get(cursor.parentId);
		}
	}
}
function assertLimits(limits) {
	for (const [name, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value < 1) fail$1("validation", "must be a positive safe integer", `limits.${name}`);
	if (limits.batchSize > 1e3) fail$1("validation", "batchSize must be <= 1000", "limits.batchSize");
}
async function parseBundle(input, limits) {
	assertLimits(limits);
	if (input.byteLength > limits.maxBytes) fail$1("limit", "input exceeds maxBytes", "input");
	let decoded;
	try {
		decoded = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(input));
	} catch {
		fail$1("validation", "input is not valid UTF-8 JSON", "input");
	}
	const root = plain(decoded, "bundle");
	schemaValue(root, "bundle");
	for (const name of Object.keys(root)) if (![
		"version",
		"generatedAt",
		"types",
		"statuses",
		"taxonomies",
		"terms",
		"entries",
		"revisions"
	].includes(name)) fail$1("validation", "unsupported bundle property", name);
	if (root.version !== 1) fail$1("validation", "unsupported bundle version", "version");
	const generatedAt = iso$1(root.generatedAt, "generatedAt");
	const types = array(root.types, "types", limits.maxDefinitions).map(parseType), statuses = array(root.statuses, "statuses", limits.maxDefinitions).map(parseStatus), taxonomies = array(root.taxonomies, "taxonomies", limits.maxDefinitions).map(parseTaxonomy);
	if (types.length + statuses.length + taxonomies.length > limits.maxDefinitions) fail$1("limit", "definitions exceed maxDefinitions", "definitions");
	const terms = array(root.terms, "terms", Math.max(limits.maxEntries * 10, limits.maxDefinitions)).map(parseTerm), entries = array(root.entries, "entries", limits.maxEntries).map(parseEntry), revisions = array(root.revisions, "revisions", limits.maxRevisions).map(parseRevision);
	for (const item of types) {
		if (await canonicalContentMigrationHash(item.definition) !== item.canonicalHash) fail$1("validation", "type canonicalHash does not match normalized definition", `type:${item.key}`);
		for (const version of item.versions) if (await canonicalContentMigrationHash(version.definition) !== version.canonicalHash) fail$1("validation", "type version hash does not match normalized definition", `type:${item.key}@${version.revision}`);
		const current = item.versions.find((version) => version.revision === item.revision);
		if (!current || current.canonicalHash !== item.canonicalHash) fail$1("validation", "current type revision is absent or mismatched", `type:${item.key}`);
	}
	for (const item of statuses) {
		if (await canonicalContentMigrationHash(item.definition) !== item.canonicalHash) fail$1("validation", "status canonicalHash does not match normalized definition", `status:${item.key}`);
		for (const version of item.versions) if (await canonicalContentMigrationHash(version.definition) !== version.canonicalHash) fail$1("validation", "status version hash does not match normalized definition", `status:${item.key}@${version.revision}`);
		const current = item.versions.find((version) => version.revision === item.revision);
		if (!current || current.canonicalHash !== item.canonicalHash) fail$1("validation", "current status revision is absent or mismatched", `status:${item.key}`);
	}
	duplicates(types, (item) => item.key, "types");
	duplicates(statuses, (item) => item.key, "statuses");
	duplicates(taxonomies, (item) => item.key, "taxonomies");
	duplicates(terms, (item) => item.id, "terms");
	duplicates(entries, (item) => item.id, "entries");
	duplicates(revisions, (item) => item.id, "revisions");
	assertTermGraph(terms);
	assertEntryGraph(entries);
	const taxonomyKeys = new Set(taxonomies.map((item) => item.key)), termIds = new Set(terms.map((item) => item.id)), termById = new Map(terms.map((item) => [item.id, item])), entryIds = new Set(entries.map((item) => item.id)), typeKeys = new Set(types.map((item) => item.key)), statusKeys = new Set(statuses.map((item) => item.key));
	for (const type of types) for (const definition of [type.definition, ...type.versions.map((version) => version.definition)]) {
		for (const statusKey of definition.statusKeys ?? []) if (!statusKeys.has(statusKey)) fail$1("validation", "type definition references missing status", `type:${type.key}`);
		for (const taxonomy of definition.taxonomies) if (!taxonomyKeys.has(taxonomy)) fail$1("validation", "type definition references missing taxonomy", `type:${type.key}`);
	}
	for (const term of terms) {
		if (!taxonomyKeys.has(term.taxonomy)) fail$1("validation", "term references missing taxonomy", `term:${term.id}`);
		if (term.parentId !== null && !termIds.has(term.parentId)) fail$1("validation", "term references missing parent", `term:${term.id}`);
	}
	for (const entry of entries) {
		const type = types.find((item) => item.key === entry.type), status = statuses.find((item) => item.key === entry.status);
		if (!type || !typeKeys.has(entry.type)) throw new ContentImportError("validation", "entry references missing type", `entry:${entry.id}`);
		if (!status || !statusKeys.has(entry.status)) throw new ContentImportError("validation", "entry references missing status", `entry:${entry.id}`);
		const typeVersion = type.versions.find((version) => version.revision === entry.typeDefinitionRevision), statusVersion = status.versions.find((version) => version.revision === entry.statusDefinitionRevision);
		if (!typeVersion) throw new ContentImportError("validation", "entry type revision is absent from bundle", `entry:${entry.id}`);
		if (!statusVersion) throw new ContentImportError("validation", "entry status revision is absent from bundle", `entry:${entry.id}`);
		if (typeVersion.definition.statusKeys !== void 0 && !typeVersion.definition.statusKeys.includes(entry.status)) fail$1("validation", "entry status is not allowed by bundled type", `entry:${entry.id}`);
		if (entry.parentId !== null && !entryIds.has(entry.parentId)) fail$1("validation", "entry references missing parent", `entry:${entry.id}`);
		for (const id of entry.termIds) {
			const term = termById.get(id);
			if (!term) fail$1("validation", "entry references missing term", `entry:${entry.id}`);
			if (!typeVersion.definition.taxonomies.includes(term.taxonomy)) fail$1("validation", "entry term taxonomy is not allowed by pinned type definition", `entry:${entry.id}`);
		}
	}
	for (const revision of revisions) {
		const entry = entries.find((item) => item.id === revision.entryId);
		if (!entry || !entryIds.has(revision.entryId)) throw new ContentImportError("validation", "revision references missing entry", `revision:${revision.id}`);
		if (revision.type !== entry.type) fail$1("validation", "revision type does not match entry", `revision:${revision.id}`);
		for (const id of revision.termIds) if (!termIds.has(id)) fail$1("validation", "revision references missing term", `revision:${revision.id}`);
	}
	const bundle = Object.freeze({
		version: 1,
		generatedAt,
		types: Object.freeze(types.map((item) => schemaValue(item))),
		statuses: Object.freeze(statuses.map((item) => schemaValue(item))),
		taxonomies: Object.freeze(taxonomies.map((item) => schemaValue(item))),
		terms: Object.freeze(terms.map((item) => schemaValue(item))),
		entries: Object.freeze(entries.map((item) => schemaValue(item))),
		revisions: Object.freeze(revisions.map((item) => schemaValue(item)))
	});
	return Object.freeze({
		bundle,
		types: Object.freeze(types),
		statuses: Object.freeze(statuses),
		taxonomies: Object.freeze(taxonomies),
		terms: Object.freeze(terms),
		entries: Object.freeze(entries),
		revisions: Object.freeze(revisions),
		manifestHash: await canonicalContentMigrationHash(bundle)
	});
}
async function parseContentImport(input, limits) {
	return (await parseBundle(input, limits)).bundle;
}
function sessionFromRow(row) {
	const stored = plain(rowJson(row.payload).session, "journal.session"), staging = plain(stored.staging, "journal.session.staging");
	return Object.freeze({
		...stored,
		id: String(row.importId),
		planHash: String(row.planHash),
		manifestHash: String(row.manifestHash),
		state: row.state,
		journalVersion: String(row.version),
		nextSection: row.nextSection,
		nextOffset: Number(row.nextOffset ?? 0),
		...row.highWaterMark === null ? {} : { publishedCorpusVersion: String(row.highWaterMark) },
		staging: Object.freeze({
			...staging,
			hostSessionId: String(row.hostSessionId)
		})
	});
}
async function readJournalRow(tx, sessionId) {
	return (await execRows(tx, sql`SELECT import_id AS "importId",host_session_id AS "hostSessionId",manifest_hash AS "manifestHash",plan_hash AS "planHash",state,version,next_section AS "nextSection",next_offset AS "nextOffset",high_water_mark AS "highWaterMark",payload FROM content_import_journal WHERE import_id=${sessionId}`))[0];
}
async function readReceiptEvidence(tx, operationId) {
	const row = (await execRows(tx, sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id=${operationId} AND kind LIKE 'content:import:%' AND state='committed' LIMIT 1`))[0];
	if (!row) return;
	const value = rowJson(row.payload);
	if (typeof value.kind !== "string" || typeof value.commandHash !== "string" || typeof value.principalScope !== "string" || typeof value.session !== "object") fail$1("integrity", "committed import receipt payload is malformed");
	return Object.freeze({
		receipt: value,
		payloadBytes: typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload)
	});
}
async function readReceipt(tx, operationId) {
	return (await readReceiptEvidence(tx, operationId))?.receipt;
}
function prepareExactReplayGuard(db, operationId, kind, evidence, row, session) {
	const journalPayloadBytes = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload), section = session.nextSection === null ? sql`journal.next_section IS NULL` : sql`journal.next_section=${session.nextSection}`;
	return db.prepare(sql`SELECT CASE WHEN EXISTS (SELECT 1 FROM content_lifecycle_journal AS receipt JOIN content_import_journal AS journal ON journal.import_id=${session.id} WHERE receipt.operation_id=${operationId} AND receipt.kind=${`content:import:${kind}`} AND receipt.state='committed' AND receipt.payload=${evidence.payloadBytes} AND journal.payload=${journalPayloadBytes} AND journal.host_session_id=${session.staging.hostSessionId} AND journal.plan_hash=${session.planHash} AND journal.manifest_hash=${session.manifestHash} AND journal.state=${session.state} AND journal.version=${Number(session.journalVersion)} AND ${section} AND journal.next_offset=${session.nextOffset} AND journal.high_water_mark IS ${session.publishedCorpusVersion ?? null}) THEN 1 ELSE json_extract('not-json','$') END AS ok`);
}
async function insertPendingReceipt(tx, operationId, kind, hash, scope) {
	const now = (/* @__PURE__ */ new Date()).toISOString(), payload = schemaValue({
		kind,
		commandHash: hash,
		principalScope: scope
	});
	await tx.execute(sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${operationId},${`content:import:${kind}`},'pending',${JSON.stringify(payload)},${now},${now})`);
}
async function commitReceipt(tx, operationId, kind, hash, scope, session) {
	const payload = schemaValue({
		kind,
		commandHash: hash,
		principalScope: scope,
		session
	});
	if ((await execRows(tx, sql`UPDATE content_lifecycle_journal SET state='committed',payload=${JSON.stringify(payload)},updated_at=${(/* @__PURE__ */ new Date()).toISOString()} WHERE operation_id=${operationId} AND state='pending' RETURNING id`)).length !== 1) fail$1("integrity", "import command claim disappeared");
}
function createDbContentImportJournal() {
	return Object.freeze({
		async claimStart(tx, principal, authorizationInput, authorization, operationId, canonicalStartHash) {
			assertActiveContentTransaction(tx);
			boundedString(operationId, "operationId", 256);
			const auth = await authorization.assert(tx, principal, "start", authorizationInput);
			if (authorizationInput.expectedPolicyVersion !== void 0 && auth.policyVersion !== authorizationInput.expectedPolicyVersion) fail$1("authorization", "import scope is unavailable");
			const receipt = await readReceipt(tx, operationId);
			if (receipt) {
				if (receipt.kind !== "start" || receipt.commandHash !== canonicalStartHash || receipt.principalScope !== principalScope(principal)) return { kind: "conflict" };
				return {
					kind: "replay",
					session: receipt.session
				};
			}
			try {
				await insertPendingReceipt(tx, operationId, "start", canonicalStartHash, principalScope(principal));
				return { kind: "new" };
			} catch (error) {
				if (isUniqueViolation(error)) return { kind: "conflict" };
				throw error;
			}
		},
		async create(plan, staging, principal, operationId, tx) {
			assertActiveContentTransaction(tx);
			if (staging.visibility !== "hiddenUntilPublish") fail$1("validation", "unsupported staging visibility", "staging.visibility");
			const id = uuid(staging.hostSessionId, "staging.hostSessionId"), session = Object.freeze({
				id,
				planHash: plan.planHash,
				manifestHash: plan.manifestHash,
				state: "validated",
				staging: Object.freeze(staging),
				scope: plan.scope,
				conflictPolicy: plan.conflictPolicy,
				requiredAuthority: plan.requiredAuthority,
				authorizationPolicyVersion: plan.authorizationPolicyVersion,
				destinationCorpusVersion: plan.destinationCorpusVersion,
				mappings: plan.mappings,
				appliedBatches: 0,
				journalVersion: "0",
				nextSection: "type",
				nextOffset: 0
			}), payload = {
				session,
				batches: Object.freeze([])
			}, now = (/* @__PURE__ */ new Date()).toISOString();
			try {
				await tx.execute(sql`INSERT INTO content_import_journal (import_id,host_session_id,manifest_hash,plan_hash,state,version,next_section,next_offset,payload,created_at,updated_at) VALUES (${id},${staging.hostSessionId},${plan.manifestHash},${plan.planHash},'validated',0,'type',0,${JSON.stringify(schemaValue(payload))},${now},${now})`);
			} catch (error) {
				if (isUniqueViolation(error)) fail$1("conflict", "staging session already exists");
				throw error;
			}
			const pending = (await execRows(tx, sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id=${operationId} AND state='pending'`))[0];
			if (!pending) fail$1("integrity", "start claim disappeared");
			const raw = rowJson(pending.payload);
			await commitReceipt(tx, operationId, "start", boundedString(raw.commandHash, "receipt.commandHash", 128), boundedString(raw.principalScope, "receipt.principalScope", 1024), session);
			return session;
		},
		async claimCommand(tx, sessionId, principal, kind, command, canonicalCommandHash, authorization) {
			assertActiveContentTransaction(tx);
			boundedString(sessionId, "sessionId", 128);
			boundedString(command.operationId, "operationId", 256);
			const authInput = {
				scope: stableScope(command.scope),
				requiredAuthority: stableAuthority(command.requiredAuthority),
				expectedPolicyVersion: command.expectedAuthorizationPolicyVersion
			};
			if ((await authorization.assert(tx, principal, kind, authInput)).policyVersion !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
			const receipt = await readReceipt(tx, command.operationId);
			if (receipt) {
				if (receipt.kind !== kind || receipt.commandHash !== canonicalCommandHash || receipt.principalScope !== principalScope(principal)) return { kind: "conflict" };
				return {
					kind: "replay",
					session: receipt.session
				};
			}
			const expectedVersion = Number(command.expectedJournalVersion);
			if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0) fail$1("validation", "expectedJournalVersion is invalid", "expectedJournalVersion");
			const sectionPredicate = command.expectedSection === null ? sql`next_section IS NULL` : sql`next_section=${command.expectedSection}`;
			const row = (await execRows(tx, sql`UPDATE content_import_journal SET version=version+1,updated_at=${(/* @__PURE__ */ new Date()).toISOString()} WHERE import_id=${sessionId} AND plan_hash=${command.expectedPlanHash} AND manifest_hash=${command.expectedManifestHash} AND version=${expectedVersion} AND ${sectionPredicate} AND next_offset=${command.expectedOffset} RETURNING import_id AS "importId",host_session_id AS "hostSessionId",manifest_hash AS "manifestHash",plan_hash AS "planHash",state,version,next_section AS "nextSection",next_offset AS "nextOffset",high_water_mark AS "highWaterMark",payload`))[0];
			if (!row) {
				if (!await readJournalRow(tx, sessionId)) fail$1("authorization", "import session is unavailable");
				fail$1("stale-journal", "journal coordinate changed");
			}
			const session = sessionFromRow(row);
			if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import session is unavailable");
			try {
				await insertPendingReceipt(tx, command.operationId, kind, canonicalCommandHash, principalScope(principal));
			} catch (error) {
				if (isUniqueViolation(error)) return { kind: "conflict" };
				throw error;
			}
			return {
				kind: "new",
				session
			};
		},
		async get(tx, sessionId, _principal) {
			assertActiveContentTransaction(tx);
			const row = await readJournalRow(tx, sessionId);
			if (!row) fail$1("authorization", "import session is unavailable");
			return sessionFromRow(row);
		},
		async recordBatch(sessionId, batch, beforeImage, tx) {
			assertActiveContentTransaction(tx);
			if (!Number.isSafeInteger(batch) || batch < 0) fail$1("validation", "batch must be non-negative", "batch");
			const row = await readJournalRow(tx, sessionId);
			if (!row) fail$1("authorization", "import session is unavailable");
			const payload = rowJson(row.payload), batches = Array.isArray(payload.batches) ? payload.batches : [];
			if (batches.some((item) => item.batch === batch)) fail$1("integrity", "duplicate import batch");
			const next = schemaValue({
				...payload,
				batches: [...batches, {
					batch,
					beforeImage
				}]
			});
			await tx.execute(sql`UPDATE content_import_journal SET payload=${JSON.stringify(next)},updated_at=${(/* @__PURE__ */ new Date()).toISOString()} WHERE import_id=${sessionId}`);
		},
		async readBatchesReverse(tx, sessionId, expectedPlanHash, expectedManifestHash) {
			assertActiveContentTransaction(tx);
			const row = await readJournalRow(tx, sessionId);
			if (!row || row.planHash !== expectedPlanHash || row.manifestHash !== expectedManifestHash) fail$1("authorization", "import session is unavailable");
			const payload = rowJson(row.payload);
			const sorted = [...Array.isArray(payload.batches) ? payload.batches : []].sort((a, b) => b.batch - a.batch);
			for (let i = 0; i < sorted.length; i++) if (sorted[i].batch !== sorted.length - 1 - i) fail$1("integrity", "import batch ledger is not contiguous");
			return Object.freeze(sorted.map((item) => Object.freeze(item)));
		},
		async transition(sessionId, from, to, tx) {
			assertActiveContentTransaction(tx);
			if ((await execRows(tx, sql`UPDATE content_import_journal SET state=${to},updated_at=${(/* @__PURE__ */ new Date()).toISOString()} WHERE import_id=${sessionId} AND state=${from} RETURNING import_id AS "importId"`)).length !== 1) fail$1("stale-journal", "import state changed");
		},
		async completeCommand(tx, sessionId, kind, command, canonicalCommandHash, result) {
			assertActiveContentTransaction(tx);
			const row = await readJournalRow(tx, sessionId);
			if (!row) fail$1("authorization", "import session is unavailable");
			const next = schemaValue({
				...rowJson(row.payload),
				session: result
			}), highWaterMark = result.publishedCorpusVersion ?? row.highWaterMark;
			if ((await execRows(tx, sql`UPDATE content_import_journal SET state=${result.state},next_section=${result.nextSection},next_offset=${result.nextOffset},high_water_mark=${highWaterMark},payload=${JSON.stringify(next)},updated_at=${(/* @__PURE__ */ new Date()).toISOString()} WHERE import_id=${sessionId} AND version=${Number(result.journalVersion)} RETURNING import_id AS "importId"`)).length !== 1) fail$1("stale-journal", "journal claim was lost");
			const pending = (await execRows(tx, sql`SELECT payload FROM content_lifecycle_journal WHERE operation_id=${command.operationId} AND state='pending'`))[0];
			if (!pending) fail$1("integrity", "command claim disappeared");
			const receipt = rowJson(pending.payload);
			if (receipt.commandHash !== canonicalCommandHash) fail$1("integrity", "command claim hash changed");
			await commitReceipt(tx, command.operationId, kind, canonicalCommandHash, boundedString(receipt.principalScope, "receipt.principalScope", 1024), result);
		}
	});
}
function bundleLimits(bundle) {
	const bytes = new TextEncoder().encode(JSON.stringify(bundle)).byteLength;
	return Object.freeze({
		maxBytes: Math.max(bytes, 1),
		maxDefinitions: Math.max(bundle.types.length + bundle.statuses.length + bundle.taxonomies.length, 1),
		maxEntries: Math.max(bundle.entries.length, bundle.terms.length, 1),
		maxRevisions: Math.max(bundle.revisions.length, 1),
		batchSize: 100
	});
}
function mappingFor(parsed) {
	const out = {};
	for (const item of parsed.types) out[`type:${item.key}`] = item.key;
	for (const item of parsed.statuses) out[`status:${item.key}`] = item.key;
	for (const item of parsed.taxonomies) out[`taxonomy:${item.key}`] = item.key;
	for (const item of parsed.terms) out[`term:${item.id}`] = item.id;
	for (const item of parsed.entries) out[`entry:${item.id}`] = item.id;
	for (const item of parsed.revisions) out[`revision:${item.id}`] = item.id;
	return out;
}
function definitionRevisionMappingKey(kind, keyValue, sourceRevision) {
	return `${kind}:${keyValue}@${sourceRevision}`;
}
function mappedDefinitionRevision(mappings, kind, keyValue, sourceRevision) {
	const raw = mappings[definitionRevisionMappingKey(kind, keyValue, sourceRevision)], revision = Number(raw);
	if (!Number.isSafeInteger(revision) || revision < 1) fail$1("stale-plan", `missing ${kind} definition revision mapping for ${keyValue}@${sourceRevision}`);
	return revision;
}
function conservativeAuthority(scope) {
	return Object.freeze(scope.typeKeys.map((typeKey) => Object.freeze({
		typeKey,
		branches: ALL_BRANCHES
	})));
}
function conflict(code, message, fieldPath) {
	return Object.freeze({
		code,
		category: "conflict",
		message,
		...fieldPath ? { fieldPath } : {}
	});
}
function addBranch(branches, typeKey, ...items) {
	const set = branches.get(typeKey);
	if (!set) fail$1("authorization", "import scope is unavailable");
	for (const item of items) set.add(item);
}
async function assertImportAuthorized(tx, authorization, principal, action, input) {
	try {
		const result = await authorization.assert(tx, principal, action, input);
		if (!result || typeof result.policyVersion !== "string" || !result.policyVersion) fail$1("authorization", "import scope is unavailable");
		return result.policyVersion;
	} catch (error) {
		if (error instanceof ContentImportError) throw error;
		return fail$1("authorization", "import scope is unavailable");
	}
}
function sqlList$1(values) {
	return sql.join(values.map((value) => sql`${value}`), sql`, `);
}
function corpusJson(value, field) {
	if (typeof value !== "string") return schemaValue(value, field);
	try {
		return schemaValue(JSON.parse(value), field);
	} catch {
		return fail$1("integrity", `${field} contains invalid JSON`);
	}
}
function corpusDate(value, field) {
	const date = value instanceof Date ? value : new Date(String(value));
	if (Number.isNaN(date.getTime())) return fail$1("integrity", `${field} contains an invalid timestamp`);
	return date.toISOString();
}
function corpusNullableDate(value, field) {
	return value === null || value === void 0 ? null : corpusDate(value, field);
}
function corpusNullableString(value) {
	return value === null || value === void 0 ? null : String(value);
}
function corpusInteger(value, field) {
	const number = Number(value);
	if (!Number.isSafeInteger(number)) return fail$1("integrity", `${field} contains an invalid integer`);
	return number;
}
function canonicalDefinitionRow(row) {
	return schemaValue({
		key: String(row.key),
		origin: String(row.origin),
		version: corpusInteger(row.version, "definition.version"),
		active: boolValue(row.active),
		currentRevision: corpusInteger(row.currentRevision, "definition.currentRevision"),
		canonicalHash: String(row.canonicalHash),
		definition: corpusJson(row.definition, "definition.definition"),
		shadowedDbVersion: row.shadowedDbVersion === null || row.shadowedDbVersion === void 0 ? null : corpusInteger(row.shadowedDbVersion, "definition.shadowedDbVersion")
	});
}
function canonicalDefinitionVersionRow(row) {
	return schemaValue({
		kind: String(row.kind),
		key: String(row.key),
		revision: corpusInteger(row.revision, "definitionVersion.revision"),
		canonicalHash: String(row.canonicalHash),
		definition: corpusJson(row.definition, "definitionVersion.definition"),
		origin: String(row.origin),
		createdAt: corpusDate(row.createdAt, "definitionVersion.createdAt")
	});
}
function canonicalEntryRow(row) {
	return schemaValue({
		id: String(row.id),
		slug: String(row.slug),
		type: String(row.type),
		title: String(row.title),
		body: String(row.body),
		status: String(row.status),
		visibility: String(row.visibility),
		publishedAt: corpusNullableDate(row.publishedAt, "entry.publishedAt"),
		author: String(row.author),
		createdAt: corpusDate(row.createdAt, "entry.createdAt"),
		updatedAt: corpusDate(row.updatedAt, "entry.updatedAt"),
		parentId: corpusNullableString(row.parentId),
		menuOrder: corpusInteger(row.menuOrder, "entry.menuOrder"),
		templateKey: corpusNullableString(row.templateKey),
		excerpt: String(row.excerpt),
		featuredMedia: row.featuredMedia === null || row.featuredMedia === void 0 ? null : corpusJson(row.featuredMedia, "entry.featuredMedia"),
		commentStatus: String(row.commentStatus),
		pingStatus: String(row.pingStatus),
		sticky: boolValue(row.sticky),
		format: corpusNullableString(row.format),
		deletedAt: corpusNullableDate(row.deletedAt, "entry.deletedAt"),
		lastEditedBy: String(row.lastEditedBy),
		typeDefinitionRevision: corpusInteger(row.typeDefinitionRevision, "entry.typeDefinitionRevision"),
		statusDefinitionRevision: corpusInteger(row.statusDefinitionRevision, "entry.statusDefinitionRevision"),
		hostSessionId: corpusNullableString(row.hostSessionId)
	});
}
function canonicalAssignmentRow(row) {
	return schemaValue({
		entryId: String(row.entryId),
		termId: String(row.termId)
	});
}
function canonicalTermRow(row) {
	return schemaValue({
		id: String(row.id),
		taxonomy: String(row.taxonomy),
		slug: String(row.slug),
		name: String(row.name),
		parentId: corpusNullableString(row.parentId),
		depth: corpusInteger(row.depth, "term.depth"),
		createdAt: corpusDate(row.createdAt, "term.createdAt"),
		hostSessionId: corpusNullableString(row.hostSessionId)
	});
}
function canonicalRevisionRow(row) {
	return schemaValue({
		id: String(row.id),
		entryId: String(row.entryId),
		title: String(row.title),
		body: String(row.body),
		slug: String(row.slug),
		type: String(row.type),
		termIds: corpusJson(row.termIds, "revision.termIds"),
		snapshot: row.snapshot === null || row.snapshot === void 0 ? null : corpusJson(row.snapshot, "revision.snapshot"),
		editor: String(row.editor),
		createdAt: corpusDate(row.createdAt, "revision.createdAt"),
		hostSessionId: corpusNullableString(row.hostSessionId)
	});
}
async function readDestinationCorpus(tx, scope, extraTaxonomyKeys = []) {
	const mappedTypeKeys = scope.typeKeys;
	const typeRows = [...await execRowsByD1BindChunks(tx, mappedTypeKeys, (chunk) => sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion" FROM content_type_definitions WHERE key IN (${sqlList$1(chunk)}) ORDER BY key`)].sort((a, b) => String(a.key).localeCompare(String(b.key)));
	const types = Object.freeze(typeRows.map(canonicalDefinitionRow));
	const statusRows = await execRows(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion" FROM content_status_definitions ORDER BY key`);
	const statuses = Object.freeze(statusRows.map(canonicalDefinitionRow));
	const statusKeys = statusRows.map((row) => String(row.key));
	const taxonomyKeys = [.../* @__PURE__ */ new Set([...extraTaxonomyKeys, ...typeRows.flatMap((row) => {
		try {
			return normalizeContentTypeDefinition(typeof row.definition === "string" ? JSON.parse(row.definition) : row.definition).taxonomies;
		} catch {
			return [];
		}
	})])].sort();
	const definitionKeys = [...mappedTypeKeys.map((key) => `type:${key}`), ...statusKeys.map((key) => `status:${key}`)];
	const versionRows = definitionKeys.length ? [...await execRowsByD1BindChunks(tx, definitionKeys, (chunk) => sql`SELECT definition_kind AS kind,definition_key AS key,revision,canonical_hash AS "canonicalHash",definition,origin,created_at AS "createdAt" FROM content_definition_versions WHERE (definition_kind||':'||definition_key) IN (${sqlList$1(chunk)}) ORDER BY definition_kind,definition_key,revision`)].sort((a, b) => `${a.kind}:${a.key}:${String(a.revision).padStart(20, "0")}`.localeCompare(`${b.kind}:${b.key}:${String(b.revision).padStart(20, "0")}`)) : [];
	const versions = Object.freeze(versionRows.map(canonicalDefinitionVersionRow));
	const entryRows = [...await execRowsByD1BindChunks(tx, mappedTypeKeys, (chunk) => sql`SELECT id,slug,type,title,body,status,visibility,published_at AS "publishedAt",author,created_at AS "createdAt",updated_at AS "updatedAt",parent_id AS "parentId",menu_order AS "menuOrder",template_key AS "templateKey",excerpt,featured_media AS "featuredMedia",comment_status AS "commentStatus",ping_status AS "pingStatus",sticky,format,deleted_at AS "deletedAt",last_edited_by AS "lastEditedBy",type_definition_revision AS "typeDefinitionRevision",status_definition_revision AS "statusDefinitionRevision",host_session_id AS "hostSessionId" FROM content_entries WHERE type IN (${sqlList$1(chunk)}) ORDER BY id`)].sort((a, b) => String(a.id).localeCompare(String(b.id)));
	const entries = Object.freeze(entryRows.map(canonicalEntryRow));
	const entryIds = entryRows.map((row) => String(row.id));
	const assignmentRows = entryIds.length ? [...await execRowsByD1BindChunks(tx, entryIds, (chunk) => sql`SELECT entry_id AS "entryId",term_id AS "termId" FROM content_entry_terms WHERE entry_id IN (${sqlList$1(chunk)}) ORDER BY entry_id,term_id`)].sort((a, b) => `${a.entryId}:${a.termId}`.localeCompare(`${b.entryId}:${b.termId}`)) : [];
	const assignments = Object.freeze(assignmentRows.map(canonicalAssignmentRow));
	const termRows = taxonomyKeys.length ? [...await execRowsByD1BindChunks(tx, taxonomyKeys, (chunk) => sql`SELECT id,taxonomy,slug,name,parent_id AS "parentId",depth,created_at AS "createdAt",host_session_id AS "hostSessionId" FROM content_terms WHERE taxonomy IN (${sqlList$1(chunk)}) ORDER BY taxonomy,depth,id`)].sort((a, b) => String(a.taxonomy).localeCompare(String(b.taxonomy)) || Number(a.depth) - Number(b.depth) || String(a.id).localeCompare(String(b.id))) : [];
	const terms = Object.freeze(termRows.map(canonicalTermRow));
	const revisionRows = entryIds.length ? [...await execRowsByD1BindChunks(tx, entryIds, (chunk) => sql`SELECT id,entry_id AS "entryId",title,body,slug,type,term_ids AS "termIds",snapshot,editor,created_at AS "createdAt",host_session_id AS "hostSessionId" FROM content_revisions WHERE entry_id IN (${sqlList$1(chunk)}) ORDER BY entry_id,id`)].sort((a, b) => String(a.entryId).localeCompare(String(b.entryId)) || String(a.id).localeCompare(String(b.id))) : [];
	const revisions = Object.freeze(revisionRows.map(canonicalRevisionRow));
	return Object.freeze({
		scope,
		mappedTypeKeys: Object.freeze([...mappedTypeKeys]),
		statusKeys: Object.freeze(statusKeys),
		taxonomyKeys: Object.freeze(taxonomyKeys),
		types,
		statuses,
		versions,
		entries,
		assignments,
		terms,
		revisions
	});
}
async function hashDestinationCorpus(snapshot) {
	return canonicalContentMigrationHash(snapshot);
}
async function destinationCorpusVersion(tx, scope) {
	return hashDestinationCorpus(await readDestinationCorpus(tx, scope));
}
function mappedTypeDefinition(item, mappings) {
	const keyValue = mappings[`type:${item.key}`] ?? item.key, statusKeys = item.definition.statusKeys?.map((key) => mappings[`status:${key}`] ?? key), taxonomies = item.definition.taxonomies.map((key) => mappings[`taxonomy:${key}`] ?? key);
	return normalizeContentTypeDefinition({
		...item.definition,
		key: keyValue,
		statusKeys,
		taxonomies
	});
}
function mappedStatusDefinition(item, mappings) {
	return normalizeContentStatusDefinition({
		...item.definition,
		key: mappings[`status:${item.key}`] ?? item.key
	});
}
function authorityForImportedEntry(entry, status, principal, mode) {
	const branches = [];
	if (mode === "create") {
		branches.push("create", entry.author === principal.id ? "deleteOwn" : "deleteOthers");
		if (entry.visibility === "private") branches.push("deletePrivate");
		if (status.definition.published) branches.push("deletePublished");
	} else {
		branches.push(entry.author === principal.id ? "editOwn" : "editOthers");
		if (entry.visibility === "private") branches.push("editPrivate");
		if (status.definition.published) branches.push("editPublished");
	}
	if (status.definition.published) branches.push("publish");
	if (entry.termIds.length) branches.push("manageTerms");
	return branches;
}
function authorityForExistingEntry(entry, statusPublished, principal) {
	const branches = [entry.author === principal.id ? "editOwn" : "editOthers"];
	if (entry.visibility === "private") branches.push("editPrivate");
	if (statusPublished) branches.push("editPublished");
	return branches;
}
async function deterministicUuid(namespace, source, manifestHash) {
	const chars = (await canonicalContentMigrationHash({
		namespace,
		source,
		manifestHash
	})).slice(0, 32).split("");
	chars[12] = "5";
	chars[16] = (Number.parseInt(chars[16], 16) & 3 | 8).toString(16);
	const value = chars.join("");
	return `${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}`;
}
function planMaterial(plan) {
	return schemaValue(plan);
}
async function assertPlanHash(plan) {
	const { planHash, ...rest } = plan;
	if (await canonicalContentMigrationHash(planMaterial(rest)) !== planHash) fail$1("integrity", "import plan hash is invalid");
}
async function planContentImport(tx, authorization, principal, bundle, input) {
	const scope = stableScope(input.scope);
	if (input.conflictPolicy !== "reject" && input.conflictPolicy !== "preserve" && input.conflictPolicy !== "mappedReplacement") fail$1("validation", "invalid conflict policy", "conflictPolicy");
	const firstPolicy = await assertImportAuthorized(tx, authorization, principal, "plan", {
		scope,
		requiredAuthority: conservativeAuthority(scope)
	});
	const bytes = new TextEncoder().encode(JSON.stringify(bundle));
	const parsed = await parseBundle(bytes, bundleLimits(bundle));
	if (parsed.types.some((item) => !scope.typeKeys.includes(item.key)) || parsed.entries.some((item) => !scope.typeKeys.includes(item.type))) fail$1("authorization", "import scope is unavailable");
	const mappings = mappingFor(parsed);
	const conflicts = [];
	const branches = new Map(scope.typeKeys.map((typeKey) => [typeKey, /* @__PURE__ */ new Set()]));
	const typeKeys = parsed.types.map((item) => item.key), statusKeys = parsed.statuses.map((item) => item.key), taxonomyKeys = parsed.taxonomies.map((item) => item.key);
	const currentTypes = typeKeys.length ? await execRows(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition FROM content_type_definitions WHERE key IN (${sqlList$1(typeKeys)})`) : [];
	const currentStatuses = statusKeys.length ? await execRows(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition FROM content_status_definitions WHERE key IN (${sqlList$1(statusKeys)})`) : [];
	const definitionKeys = [...typeKeys.map((key) => `type:${key}`), ...statusKeys.map((key) => `status:${key}`)];
	const currentVersions = definitionKeys.length ? await execRows(tx, sql`SELECT definition_kind AS kind,definition_key AS key,revision,canonical_hash AS "canonicalHash" FROM content_definition_versions WHERE (definition_kind||':'||definition_key) IN (${sqlList$1(definitionKeys)})`) : [];
	const currentTypeByKey = new Map(currentTypes.map((row) => [row.key, row])), currentStatusByKey = new Map(currentStatuses.map((row) => [row.key, row]));
	for (const item of parsed.types) {
		const current = currentTypeByKey.get(item.key), destVersions = currentVersions.filter((row) => row.kind === "type" && row.key === item.key), maxRevision = Math.max(0, ...destVersions.map((row) => Number(row.revision)));
		let allocated = 0;
		addBranch(branches, item.key, "manageType");
		for (const version of item.versions) {
			const match = destVersions.find((row) => row.canonicalHash === version.canonicalHash);
			mappings[definitionRevisionMappingKey("type", item.key, version.revision)] = String(match ? Number(match.revision) : maxRevision + ++allocated);
		}
		if (current) {
			const differs = current.canonicalHash !== item.canonicalHash || boolValue(current.active) !== item.active;
			if (input.conflictPolicy === "reject") conflicts.push(conflict("type-conflict", `type ${item.key} already exists`, `type:${item.key}`));
			else if (input.conflictPolicy === "preserve" && differs) conflicts.push(conflict("type-definition-mismatch", `type ${item.key} differs from preserved destination`, `type:${item.key}`));
			else if (input.conflictPolicy === "mappedReplacement" && current.origin === "code" && differs && parsed.entries.some((entry) => entry.type === item.key)) conflicts.push(conflict("code-type-owned", `type ${item.key} is code-owned and cannot be replaced for imported entries`, `type:${item.key}`));
			else if (input.conflictPolicy === "mappedReplacement" && current.origin !== "code" && differs) addBranch(branches, item.key, "migrateAll");
		}
	}
	for (const item of parsed.statuses) {
		for (const typeKey of scope.typeKeys) addBranch(branches, typeKey, "manageType");
		const current = currentStatusByKey.get(item.key), destVersions = currentVersions.filter((row) => row.kind === "status" && row.key === item.key), maxRevision = Math.max(0, ...destVersions.map((row) => Number(row.revision)));
		let allocated = 0;
		for (const version of item.versions) {
			const match = destVersions.find((row) => row.canonicalHash === version.canonicalHash);
			mappings[definitionRevisionMappingKey("status", item.key, version.revision)] = String(match ? Number(match.revision) : maxRevision + ++allocated);
		}
		if (current) {
			const differs = current.canonicalHash !== item.canonicalHash || boolValue(current.active) !== item.active;
			if (input.conflictPolicy === "reject") conflicts.push(conflict("status-conflict", `status ${item.key} already exists`, `status:${item.key}`));
			else if (input.conflictPolicy === "preserve" && differs) conflicts.push(conflict("status-definition-mismatch", `status ${item.key} differs from preserved destination`, `status:${item.key}`));
			else if (input.conflictPolicy === "mappedReplacement" && current.origin === "code" && differs && parsed.entries.some((entry) => entry.status === item.key)) conflicts.push(conflict("code-status-owned", `status ${item.key} is code-owned and cannot be replaced for imported entries`, `status:${item.key}`));
			else if (input.conflictPolicy === "mappedReplacement" && current.origin !== "code" && differs) for (const typeKey of scope.typeKeys) addBranch(branches, typeKey, "migrateAll");
		}
	}
	const existingTerms = taxonomyKeys.length ? await execRows(tx, sql`SELECT id,taxonomy,slug,parent_id AS "parentId",depth FROM content_terms WHERE taxonomy IN (${sqlList$1(taxonomyKeys)}) ORDER BY depth,id`) : [];
	const termById = new Map(existingTerms.map((row) => [row.id, row]));
	const termByPath = new Map(existingTerms.map((row) => [`${row.taxonomy}:${row.parentId ?? ""}:${row.slug}`, row]));
	for (const item of [...parsed.terms].sort((a, b) => a.depth - b.depth || a.id.localeCompare(b.id))) {
		const mappedTax = mappings[`taxonomy:${item.taxonomy}`] ?? item.taxonomy;
		const mappedParent = item.parentId === null ? null : mappings[`term:${item.parentId}`] ?? item.parentId;
		const byId = termById.get(item.id), byPath = termByPath.get(`${mappedTax}:${mappedParent ?? ""}:${item.slug}`), existing = byId ?? byPath;
		if (byId && byPath && byId.id !== byPath.id) {
			conflicts.push(conflict("term-conflict-ambiguous", `term ${item.id} collides by both id and sibling slug`, `term:${item.id}`));
			continue;
		}
		if (existing) {
			if (input.conflictPolicy === "reject") conflicts.push(conflict("term-conflict", `term ${item.id} conflicts with destination term ${existing.id}`, `term:${item.id}`));
			else mappings[`term:${item.id}`] = existing.id;
		}
	}
	if (parsed.terms.length) {
		for (const type of parsed.types) if (type.definition.taxonomies.some((taxonomy) => taxonomyKeys.includes(taxonomy))) addBranch(branches, type.key, "manageTerms");
	}
	const existingEntries = typeKeys.length ? await execRows(tx, sql`SELECT id,type,slug,author,status,visibility FROM content_entries WHERE type IN (${sqlList$1(typeKeys)})`) : [];
	const entryById = new Map(existingEntries.map((row) => [row.id, row])), entryByPath = new Map(existingEntries.map((row) => [`${row.type}:${row.slug}`, row]));
	const statusPublished = /* @__PURE__ */ new Map();
	for (const row of currentStatuses) try {
		statusPublished.set(row.key, normalizeContentStatusDefinition(typeof row.definition === "string" ? JSON.parse(row.definition) : row.definition).published);
	} catch {
		fail$1("integrity", "destination status definition is malformed");
	}
	const entryConflictSources = /* @__PURE__ */ new Set();
	for (const item of parsed.entries) {
		const mappedType = mappings[`type:${item.type}`] ?? item.type, byId = entryById.get(item.id), byPath = entryByPath.get(`${mappedType}:${item.slug}`), existing = byId ?? byPath;
		if (byId && byPath && byId.id !== byPath.id) {
			conflicts.push(conflict("entry-conflict-ambiguous", `entry ${item.id} collides by both id and type/slug`, `entry:${item.id}`));
			continue;
		}
		const importedStatus = parsed.statuses.find((status) => status.key === item.status);
		if (existing) {
			entryConflictSources.add(item.id);
			if (input.conflictPolicy === "reject") conflicts.push(conflict("entry-conflict", `entry ${item.id} conflicts with destination entry ${existing.id}`, `entry:${item.id}`));
			else mappings[`entry:${item.id}`] = existing.id;
			if (input.conflictPolicy === "mappedReplacement") addBranch(branches, item.type, ...authorityForExistingEntry(existing, statusPublished.get(existing.status) === true, principal), ...authorityForImportedEntry(item, importedStatus, principal, "replace"));
		} else addBranch(branches, item.type, ...authorityForImportedEntry(item, importedStatus, principal, "create"));
	}
	const existingRevisions = parsed.revisions.length ? await execRows(tx, sql`SELECT id,entry_id AS "entryId" FROM content_revisions WHERE id IN (${sqlList$1(parsed.revisions.map((item) => item.id))})`) : [];
	const revisionIds = new Set(existingRevisions.map((row) => row.id));
	for (const item of parsed.revisions) {
		if (revisionIds.has(item.id)) {
			if (input.conflictPolicy === "reject") conflicts.push(conflict("revision-conflict", `revision ${item.id} already exists`, `revision:${item.id}`));
			else if (input.conflictPolicy === "mappedReplacement") mappings[`revision:${item.id}`] = await deterministicUuid("revision", item.id, parsed.manifestHash);
		}
		if (input.conflictPolicy === "preserve" && entryConflictSources.has(item.entryId)) continue;
	}
	const frozenMappings = Object.freeze({ ...mappings });
	const destination = await destinationCorpusVersion(tx, scope);
	const requiredAuthority = stableAuthority([...branches].map(([typeKey, set]) => ({
		typeKey,
		branches: [...set]
	})));
	const policy = await assertImportAuthorized(tx, authorization, principal, "plan", {
		scope,
		requiredAuthority
	});
	if (policy !== firstPolicy) fail$1("authorization", "import scope is unavailable");
	const counts = Object.freeze({
		types: parsed.types.length,
		statuses: parsed.statuses.length,
		taxonomies: parsed.taxonomies.length,
		terms: parsed.terms.length,
		entries: parsed.entries.length,
		revisions: parsed.revisions.length
	});
	const rest = {
		manifestHash: parsed.manifestHash,
		destinationCorpusVersion: destination,
		authorizationPolicyVersion: policy,
		scope,
		conflictPolicy: input.conflictPolicy,
		requiredAuthority,
		counts,
		mappings: frozenMappings,
		conflicts: Object.freeze(conflicts),
		estimatedBytes: bytes.byteLength
	};
	return Object.freeze({
		planHash: await canonicalContentMigrationHash(planMaterial(rest)),
		...rest
	});
}
function authorizationInputFromPlan(plan) {
	return Object.freeze({
		scope: plan.scope,
		requiredAuthority: plan.requiredAuthority,
		expectedPolicyVersion: plan.authorizationPolicyVersion
	});
}
function authorizationInputFromCommand(command) {
	return Object.freeze({
		scope: stableScope(command.scope),
		requiredAuthority: stableAuthority(command.requiredAuthority),
		expectedPolicyVersion: command.expectedAuthorizationPolicyVersion
	});
}
async function canonicalCommandHash(kind, sessionId, principal, value) {
	return canonicalContentMigrationHash(schemaValue({
		kind,
		sessionId,
		principalScope: principalScope(principal),
		value
	}));
}
async function parsedBundleObject(bundle) {
	return parseBundle(new TextEncoder().encode(JSON.stringify(bundle)), bundleLimits(bundle));
}
async function revalidatePlan(tx, authorization, principal, bundle, expected) {
	await assertPlanHash(expected);
	const current = await planContentImport(tx, authorization, principal, bundle, {
		scope: expected.scope,
		conflictPolicy: expected.conflictPolicy
	});
	if (current.planHash !== expected.planHash || current.destinationCorpusVersion !== expected.destinationCorpusVersion || current.manifestHash !== expected.manifestHash) fail$1("stale-plan", "destination corpus or plan evidence changed");
}
async function startContentImport(tx, journal, authorization, principal, bundle, input) {
	assertActiveContentTransaction(tx);
	await assertPlanHash(input.plan);
	const operationId = boundedString(input.operationId, "operationId", 256), parsed = await parsedBundleObject(bundle);
	if (parsed.manifestHash !== input.plan.manifestHash) fail$1("stale-plan", "bundle manifest differs from plan");
	if (input.plan.conflicts.length > 0) fail$1("conflict", "import plan contains unresolved conflicts");
	const authInput = authorizationInputFromPlan(input.plan), policy = await assertImportAuthorized(tx, authorization, principal, "start", authInput);
	if (policy !== input.plan.authorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const currentCorpus = await destinationCorpusVersion(tx, input.plan.scope), startHash = await canonicalCommandHash("start", null, principal, {
		manifestHash: parsed.manifestHash,
		plan: input.plan,
		staging: input.staging,
		authorizationInput: authInput,
		currentPolicyVersion: policy,
		destinationCorpusVersion: currentCorpus
	});
	const claim = await journal.claimStart(tx, principal, authInput, authorization, operationId, startHash);
	if (claim.kind === "conflict") fail$1("conflict", "operation id was already used for another import start");
	if (claim.kind === "replay") return claim.session;
	if (currentCorpus !== input.plan.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after planning");
	await revalidatePlan(tx, authorization, principal, bundle, input.plan);
	return journal.create(input.plan, input.staging, principal, operationId, tx);
}
/** Prepare a content-import start for one host-owned atomic D1 batch. */
async function prepareStartContentImportD1(db, authorization, principal, bundle, input) {
	assertD1Scope(input.plan.scope);
	await assertPlanHash(input.plan);
	const operationId = boundedString(input.operationId, "operationId", 256);
	const parsed = await parsedBundleObject(bundle);
	if (parsed.manifestHash !== input.plan.manifestHash) fail$1("stale-plan", "bundle manifest differs from plan");
	if (input.plan.conflicts.length > 0) fail$1("conflict", "import plan contains unresolved conflicts");
	if (input.staging.visibility !== "hiddenUntilPublish") fail$1("validation", "unsupported staging visibility", "staging.visibility");
	const id = uuid(input.staging.hostSessionId, "staging.hostSessionId");
	const authInput = authorizationInputFromPlan(input.plan);
	const policy = await assertImportAuthorized(db, authorization, principal, "start", authInput);
	if (policy !== input.plan.authorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const destinationSnapshot = await readDestinationCorpus(db, input.plan.scope);
	const currentCorpus = await hashDestinationCorpus(destinationSnapshot);
	if (currentCorpus !== input.plan.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after planning");
	const startHash = await canonicalCommandHash("start", null, principal, {
		manifestHash: parsed.manifestHash,
		plan: input.plan,
		staging: input.staging,
		authorizationInput: authInput,
		currentPolicyVersion: policy,
		destinationCorpusVersion: currentCorpus
	});
	const receiptEvidence = await readReceiptEvidence(db, operationId), receipt = receiptEvidence?.receipt;
	if (receipt && receiptEvidence) {
		if (receipt.kind !== "start" || receipt.commandHash !== startHash || receipt.principalScope !== principalScope(principal) || receipt.session.id !== id) fail$1("conflict", "operation id was already used for another import start");
		const persisted = required(await readJournalRow(db, id), "integrity", "committed start receipt has no import journal");
		const session = sessionFromRow(persisted);
		if (JSON.stringify(session) !== JSON.stringify(receipt.session)) fail$1("integrity", "committed start receipt and import journal differ");
		const guard = prepareExactReplayGuard(db, operationId, "start", receiptEvidence, persisted, session);
		return Object.freeze({
			items: Object.freeze([guard]),
			result: session
		});
	}
	assertD1CorpusCountGuardBindBudget(destinationSnapshot);
	assertD1StatementBudget("start", exactCorpusGuardCount(destinationSnapshot) + 6);
	const session = Object.freeze({
		id,
		planHash: input.plan.planHash,
		manifestHash: input.plan.manifestHash,
		state: "validated",
		staging: Object.freeze({
			...input.staging,
			hostSessionId: id
		}),
		scope: input.plan.scope,
		conflictPolicy: input.plan.conflictPolicy,
		requiredAuthority: input.plan.requiredAuthority,
		authorizationPolicyVersion: input.plan.authorizationPolicyVersion,
		destinationCorpusVersion: input.plan.destinationCorpusVersion,
		mappings: input.plan.mappings,
		appliedBatches: 0,
		journalVersion: "0",
		nextSection: "type",
		nextOffset: 0
	});
	const now = (/* @__PURE__ */ new Date()).toISOString(), receiptId = crypto.randomUUID(), scope = principalScope(principal);
	const pendingPayload = JSON.stringify(schemaValue({
		kind: "start",
		commandHash: startHash,
		principalScope: scope
	}));
	const journalPayload = JSON.stringify(schemaValue({
		session,
		batches: [],
		plan: input.plan,
		staging: input.staging,
		authorizationInput: authInput,
		destinationCorpusVersion: currentCorpus,
		preflightDestinationCorpusSnapshot: destinationSnapshot
	}));
	const committedPayload = JSON.stringify(schemaValue({
		kind: "start",
		commandHash: startHash,
		principalScope: scope,
		session
	}));
	const claim = db.prepare(sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${receiptId},${operationId},'content:import:start','pending',${pendingPayload},${now},${now})`);
	const claimGuard = db.prepare(d1AssertPreviousChanges(1));
	const create = db.prepare(sql`INSERT INTO content_import_journal (import_id,host_session_id,manifest_hash,plan_hash,state,version,next_section,next_offset,payload,created_at,updated_at) VALUES (${id},${id},${input.plan.manifestHash},${input.plan.planHash},'validated',0,'type',0,${journalPayload},${now},${now})`);
	const createGuard = db.prepare(d1AssertPreviousChanges(1));
	const complete = db.prepare(sql`UPDATE content_lifecycle_journal SET state='committed',payload=${committedPayload},updated_at=${now} WHERE operation_id=${operationId} AND kind='content:import:start' AND state='pending' AND payload=${pendingPayload}`);
	const completeGuard = db.prepare(d1AssertPreviousChanges(1));
	return Object.freeze({
		items: Object.freeze([
			...d1ExactCorpusGuards(db, destinationSnapshot),
			claim,
			claimGuard,
			create,
			createGuard,
			complete,
			completeGuard
		]),
		result: session
	});
}
/** Prepare a read-only staged-state guard for one host-owned atomic D1 batch. */
async function prepareStagedContentImportGuardD1(db, authorization, principal, sessionIdInput, command) {
	assertD1Scope(command.scope);
	const sessionId = uuid(sessionIdInput, "sessionId");
	boundedString(command.operationId, "operationId", 256);
	boundedString(command.expectedPlanHash, "expectedPlanHash", 128);
	boundedString(command.expectedManifestHash, "expectedManifestHash", 128);
	const expectedVersion = Number(command.expectedJournalVersion);
	if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0) fail$1("validation", "expectedJournalVersion is invalid", "expectedJournalVersion");
	if (command.expectedSection !== null) fail$1("stale-journal", "staged import must have a terminal section coordinate");
	if (!Number.isSafeInteger(command.expectedOffset) || command.expectedOffset !== 0) fail$1("stale-journal", "staged import must have a terminal offset coordinate");
	const authInput = authorizationInputFromCommand(command);
	const policy = await assertImportAuthorized(db, authorization, principal, "resume", authInput);
	if (policy !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable");
	const payload = rowJson(row.payload), session = sessionFromRow(row), storedSession = payload.session, storedPlan = payload.plan;
	if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== policy) fail$1("authorization", "import session is unavailable");
	if (session.planHash !== command.expectedPlanHash || session.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "command plan evidence differs from session");
	if (session.journalVersion !== command.expectedJournalVersion || session.nextSection !== null || session.nextOffset !== 0) fail$1("stale-journal", "journal coordinate changed");
	if (session.state !== "staged") fail$1("stale-journal", "import is not staged");
	if (session.staging.visibility !== "hiddenUntilPublish" || session.staging.hostSessionId !== String(row.hostSessionId)) fail$1("integrity", "staging identity differs from journal");
	if (!storedPlan || typeof storedPlan !== "object") fail$1("integrity", "import journal has no retained plan evidence");
	const retainedPlan = storedPlan;
	await assertPlanHash(retainedPlan);
	if (retainedPlan.planHash !== session.planHash || retainedPlan.manifestHash !== session.manifestHash || !sameScope(retainedPlan.scope, session.scope) || !sameAuthority(retainedPlan.requiredAuthority, session.requiredAuthority) || retainedPlan.authorizationPolicyVersion !== session.authorizationPolicyVersion || retainedPlan.destinationCorpusVersion !== session.destinationCorpusVersion) fail$1("integrity", "retained plan evidence differs from session");
	if (JSON.stringify(schemaValue(storedSession, "journal.session")) !== JSON.stringify(schemaValue(session, "session"))) fail$1("integrity", "retained session evidence differs from journal");
	const batches = Array.isArray(payload.batches) ? payload.batches : [];
	if (batches.length !== session.appliedBatches || batches.some((item, index) => item.batch !== index)) fail$1("integrity", "import batch ledger is not contiguous");
	for (const item of batches) stagedBatchFromValue(item.beforeImage);
	const exactPayload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload);
	const guard = db.prepare(sql`SELECT CASE WHEN EXISTS (SELECT 1 FROM content_import_journal WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state='staged' AND version=${expectedVersion} AND next_section IS NULL AND next_offset=0 AND payload=${exactPayload}) THEN 1 ELSE json_extract('not-json','$') END AS ok`);
	return Object.freeze({
		items: Object.freeze([guard]),
		result: session
	});
}
function sectionItems(parsed, section) {
	switch (section) {
		case "type": return parsed.types;
		case "status": return parsed.statuses;
		case "taxonomy": return parsed.taxonomies;
		case "term": return [...parsed.terms].sort((a, b) => a.depth - b.depth || a.id.localeCompare(b.id));
		case "entry": return sortEntriesParentFirst(parsed.entries);
		case "revision": return [...parsed.revisions].sort((a, b) => a.entryId.localeCompare(b.entryId) || a.seq - b.seq || a.id.localeCompare(b.id));
	}
}
function sortEntriesParentFirst(entries) {
	const byId = new Map(entries.map((item) => [item.id, item])), depthCache = /* @__PURE__ */ new Map();
	const depth = (item) => {
		const cached = depthCache.get(item.id);
		if (cached !== void 0) return cached;
		const result = item.parentId === null ? 0 : depth(byId.get(item.parentId)) + 1;
		depthCache.set(item.id, result);
		return result;
	};
	return Object.freeze([...entries].sort((a, b) => depth(a) - depth(b) || a.id.localeCompare(b.id)));
}
function nextCoordinate(parsed, current, offset, batchSize) {
	const order = [
		"type",
		"status",
		"taxonomy",
		"term",
		"entry",
		"revision"
	];
	if (offset < sectionItems(parsed, current).length) return {
		section: current,
		offset
	};
	let index = order.indexOf(current) + 1;
	while (index < order.length) {
		const section = order[index];
		if (sectionItems(parsed, section).length > 0) return {
			section,
			offset: 0
		};
		index++;
	}
	return {
		section: null,
		offset: 0
	};
}
async function currentDefinitionSnapshot(tx, kind, keyValue) {
	const currentRows = kind === "type" ? await execRows(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion",host_session_id AS "hostSessionId",created_at AS "createdAt",updated_at AS "updatedAt" FROM content_type_definitions WHERE key=${keyValue}`) : await execRows(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion",host_session_id AS "hostSessionId",created_at AS "createdAt",updated_at AS "updatedAt" FROM content_status_definitions WHERE key=${keyValue}`);
	const versions = await execRows(tx, sql`SELECT definition_kind AS kind,definition_key AS key,revision,canonical_hash AS "canonicalHash",definition,origin,host_session_id AS "hostSessionId",created_at AS "createdAt" FROM content_definition_versions WHERE definition_kind=${kind} AND definition_key=${keyValue} ORDER BY revision`);
	return {
		current: currentRows[0] ? schemaValue(currentRows[0]) : null,
		versions: Object.freeze(versions.map((row) => schemaValue(row)))
	};
}
async function mappedTypeItem(item, mappings) {
	const definition = mappedTypeDefinition(item, mappings), canonicalHash = await canonicalContentMigrationHash(definition), versions = [];
	for (const version of item.versions) {
		const mapped = normalizeContentTypeDefinition({
			...version.definition,
			key: mappings[`type:${item.key}`] ?? item.key,
			statusKeys: version.definition.statusKeys?.map((key) => mappings[`status:${key}`] ?? key),
			taxonomies: version.definition.taxonomies.map((key) => mappings[`taxonomy:${key}`] ?? key)
		}), revision = mappedDefinitionRevision(mappings, "type", item.key, version.revision);
		versions.push(schemaValue({
			revision,
			canonicalHash: await canonicalContentMigrationHash(mapped),
			definition: mapped,
			origin: "import",
			createdAt: version.createdAt
		}));
	}
	return schemaValue({
		key: mappings[`type:${item.key}`] ?? item.key,
		origin: "import",
		version: item.version,
		revision: mappedDefinitionRevision(mappings, "type", item.key, item.revision),
		active: item.active,
		canonicalHash,
		definition,
		versions
	});
}
async function mappedStatusItem(item, mappings) {
	const definition = mappedStatusDefinition(item, mappings), canonicalHash = await canonicalContentMigrationHash(definition), versions = [];
	for (const version of item.versions) {
		const mapped = normalizeContentStatusDefinition({
			...version.definition,
			key: mappings[`status:${item.key}`] ?? item.key
		}), revision = mappedDefinitionRevision(mappings, "status", item.key, version.revision);
		versions.push(schemaValue({
			revision,
			canonicalHash: await canonicalContentMigrationHash(mapped),
			definition: mapped,
			origin: "import",
			createdAt: version.createdAt
		}));
	}
	return schemaValue({
		key: mappings[`status:${item.key}`] ?? item.key,
		origin: "import",
		version: item.version,
		revision: mappedDefinitionRevision(mappings, "status", item.key, item.revision),
		active: item.active,
		canonicalHash,
		definition,
		versions
	});
}
async function stageMutation(tx, session, section, raw) {
	if (section === "type") {
		const item = raw, destKey = session.mappings[`type:${item.key}`] ?? item.key, before = await currentDefinitionSnapshot(tx, "type", destKey), current = before.current === null ? null : plain(before.current, "type.before.current"), mapped = plain(await mappedTypeItem(item, session.mappings), "type.after"), same = current !== null && String(current.canonicalHash) === String(mapped.canonicalHash) && boolValue(current.active) === boolValue(mapped.active), action = current === null ? "create" : session.conflictPolicy === "preserve" || same ? "preserve" : current.origin === "code" ? "shadow" : "replace", destinationVersion = current === null ? 1 : action === "preserve" ? Number(current.version) : Number(current.version) + 1;
		return schemaValue({
			kind: "type",
			action,
			destKey,
			after: {
				...mapped,
				version: destinationVersion
			},
			before
		});
	}
	if (section === "status") {
		const item = raw, destKey = session.mappings[`status:${item.key}`] ?? item.key, before = await currentDefinitionSnapshot(tx, "status", destKey), current = before.current === null ? null : plain(before.current, "status.before.current"), mapped = plain(await mappedStatusItem(item, session.mappings), "status.after"), same = current !== null && String(current.canonicalHash) === String(mapped.canonicalHash) && boolValue(current.active) === boolValue(mapped.active), action = current === null ? "create" : session.conflictPolicy === "preserve" || same ? "preserve" : current.origin === "code" ? "shadow" : "replace", destinationVersion = current === null ? 1 : action === "preserve" ? Number(current.version) : Number(current.version) + 1;
		return schemaValue({
			kind: "status",
			action,
			destKey,
			after: {
				...mapped,
				version: destinationVersion
			},
			before
		});
	}
	if (section === "taxonomy") {
		const item = raw;
		return schemaValue({
			kind: "taxonomy",
			action: "noop",
			source: item,
			destKey: session.mappings[`taxonomy:${item.key}`] ?? item.key
		});
	}
	if (section === "term") {
		const item = raw, destId = session.mappings[`term:${item.id}`] ?? item.id, destTaxonomy = session.mappings[`taxonomy:${item.taxonomy}`] ?? item.taxonomy, destParentId = item.parentId === null ? null : session.mappings[`term:${item.parentId}`] ?? item.parentId;
		const rows = await execRows(tx, sql`SELECT id,taxonomy,slug,name,parent_id AS "parentId",depth,created_at AS "createdAt",host_session_id AS "hostSessionId" FROM content_terms WHERE id=${destId}`), before = rows[0] ? schemaValue(rows[0]) : null;
		return schemaValue({
			kind: "term",
			action: before === null ? "create" : session.conflictPolicy === "preserve" ? "preserve" : "replace",
			destId,
			after: {
				id: destId,
				taxonomy: destTaxonomy,
				slug: item.slug,
				name: item.name,
				parentId: destParentId,
				depth: item.depth,
				createdAt: item.createdAt
			},
			before
		});
	}
	if (section === "entry") {
		const item = raw, destId = session.mappings[`entry:${item.id}`] ?? item.id, destType = session.mappings[`type:${item.type}`] ?? item.type, destStatus = session.mappings[`status:${item.status}`] ?? item.status, destParentId = item.parentId === null ? null : session.mappings[`entry:${item.parentId}`] ?? item.parentId, destTermIds = item.termIds.map((id) => session.mappings[`term:${id}`] ?? id);
		const rows = await execRows(tx, sql`SELECT id,slug,type,title,body,status,visibility,published_at AS "publishedAt",author,created_at AS "createdAt",updated_at AS "updatedAt",parent_id AS "parentId",menu_order AS "menuOrder",template_key AS "templateKey",excerpt,featured_media AS "featuredMedia",comment_status AS "commentStatus",ping_status AS "pingStatus",sticky,format,deleted_at AS "deletedAt",last_edited_by AS "lastEditedBy",type_definition_revision AS "typeDefinitionRevision",status_definition_revision AS "statusDefinitionRevision",host_session_id AS "hostSessionId" FROM content_entries WHERE id=${destId}`), before = rows[0] ? schemaValue(rows[0]) : null, beforeTerms = before === null ? [] : await execRows(tx, sql`SELECT term_id AS "termId" FROM content_entry_terms WHERE entry_id=${destId} ORDER BY term_id`);
		return schemaValue({
			kind: "entry",
			action: before === null ? "create" : session.conflictPolicy === "preserve" ? "preserve" : "replace",
			destId,
			after: {
				id: destId,
				slug: item.slug,
				type: destType,
				title: item.title,
				body: item.body,
				status: destStatus,
				visibility: item.visibility,
				publishedAt: item.publishedAt,
				author: item.author,
				createdAt: item.createdAt,
				updatedAt: item.updatedAt,
				parentId: destParentId,
				menuOrder: item.menuOrder,
				templateKey: item.templateKey,
				excerpt: item.excerpt,
				featuredMedia: item.featuredMedia,
				commentStatus: item.commentStatus,
				pingStatus: item.pingStatus,
				sticky: item.sticky,
				format: item.format,
				deletedAt: item.deletedAt,
				lastEditedBy: item.lastEditedBy,
				typeDefinitionRevision: mappedDefinitionRevision(session.mappings, "type", item.type, item.typeDefinitionRevision),
				statusDefinitionRevision: mappedDefinitionRevision(session.mappings, "status", item.status, item.statusDefinitionRevision),
				termIds: destTermIds
			},
			before,
			beforeTermIds: beforeTerms.map((row) => row.termId)
		});
	}
	const item = raw, destId = session.mappings[`revision:${item.id}`] ?? item.id, destEntryId = session.mappings[`entry:${item.entryId}`] ?? item.entryId, destTermIds = item.termIds.map((id) => session.mappings[`term:${id}`] ?? id);
	const entryExists = (await execRows(tx, sql`SELECT id FROM content_entries WHERE id=${destEntryId}`))[0] !== void 0;
	const rows = await execRows(tx, sql`SELECT id,entry_id AS "entryId",seq,title,body,slug,type,term_ids AS "termIds",snapshot,editor,created_at AS "createdAt",host_session_id AS "hostSessionId" FROM content_revisions WHERE id=${destId}`), before = rows[0] ? schemaValue(rows[0]) : null;
	return schemaValue({
		kind: "revision",
		action: session.conflictPolicy === "preserve" && entryExists ? "preserve" : before === null ? "create" : session.conflictPolicy === "preserve" ? "preserve" : "replace",
		destId,
		after: {
			id: destId,
			entryId: destEntryId,
			seq: item.seq,
			title: item.title,
			body: item.body,
			slug: item.slug,
			type: session.mappings[`type:${item.type}`] ?? item.type,
			termIds: destTermIds,
			...item.snapshot === void 0 ? {} : { snapshot: item.snapshot },
			editor: item.editor,
			createdAt: item.createdAt
		},
		before
	});
}
function stagedBatchFromValue(value) {
	const objectValue = plain(value, "stagedBatch"), rawSection = objectValue.section;
	if (rawSection !== "type" && rawSection !== "status" && rawSection !== "taxonomy" && rawSection !== "term" && rawSection !== "entry" && rawSection !== "revision") throw new ContentImportError("integrity", "staged batch section is invalid");
	const section = rawSection, offset = Number(objectValue.offset), rawMutations = objectValue.mutations;
	if (!Number.isSafeInteger(offset) || offset < 0) fail$1("integrity", "staged batch offset is invalid");
	if (!Array.isArray(rawMutations)) throw new ContentImportError("integrity", "staged batch mutations are invalid");
	return Object.freeze({
		section,
		offset,
		mutations: Object.freeze(rawMutations.map((item, index) => schemaValue(item, `stagedBatch.mutations.${index}`)))
	});
}
async function resumeContentImport(tx, journal, authorization, principal, bundle, sessionIdInput, command) {
	assertActiveContentTransaction(tx);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), parsed = await parsedBundleObject(bundle);
	if (parsed.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "bundle manifest differs from command");
	const commandHash = await canonicalCommandHash("resume", sessionId, principal, {
		command,
		bundleManifestHash: parsed.manifestHash
	}), claim = await journal.claimCommand(tx, sessionId, principal, "resume", command, commandHash, authorization);
	if (claim.kind === "conflict") throw new ContentImportError("conflict", "operation id was already used for another import command");
	if (claim.kind === "replay") return claim.session;
	const session = claim.session;
	if (!session) throw new ContentImportError("integrity", "resume claim did not return session");
	if (session.state !== "validated" && session.state !== "applying") fail$1("stale-journal", "import is not resumable");
	if (session.nextSection === null) throw new ContentImportError("stale-journal", "import is already staged");
	if (parsed.manifestHash !== session.manifestHash) fail$1("stale-plan", "bundle manifest differs from session");
	const currentPlan = await planContentImport(tx, authorization, principal, bundle, {
		scope: session.scope,
		conflictPolicy: session.conflictPolicy
	});
	if (currentPlan.planHash !== session.planHash || currentPlan.destinationCorpusVersion !== session.destinationCorpusVersion) fail$1("stale-plan", "destination corpus or plan evidence changed");
	if (await destinationCorpusVersion(tx, session.scope) !== session.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after planning");
	const section = session.nextSection, offset = session.nextOffset, batchSize = bundleLimits(bundle).batchSize, slice = sectionItems(parsed, section).slice(offset, offset + batchSize), mutations = [];
	for (const item of slice) mutations.push(await stageMutation(tx, session, section, item));
	const next = nextCoordinate(parsed, section, offset + slice.length, batchSize), result = Object.freeze({
		...session,
		state: next.section === null ? "staged" : "applying",
		appliedBatches: session.appliedBatches + 1,
		nextSection: next.section,
		nextOffset: next.offset
	});
	const batch = Object.freeze({
		section,
		offset,
		mutations: Object.freeze(mutations)
	});
	await journal.recordBatch(sessionId, session.appliedBatches, schemaValue(batch), tx);
	await journal.completeCommand(tx, sessionId, "resume", command, commandHash, result);
	return result;
}
/** Prepare one hidden content-import staging batch for a host-owned atomic D1 batch. */
async function prepareResumeContentImportD1(db, authorization, principal, bundle, sessionIdInput, command) {
	assertD1Scope(command.scope);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), operationId = boundedString(command.operationId, "operationId", 256), parsed = await parsedBundleObject(bundle);
	if (parsed.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "bundle manifest differs from command");
	const authInput = authorizationInputFromCommand(command), policy = await assertImportAuthorized(db, authorization, principal, "resume", authInput);
	if (policy !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const commandHash = await canonicalCommandHash("resume", sessionId, principal, {
		command,
		bundleManifestHash: parsed.manifestHash
	}), scope = principalScope(principal), receiptEvidence = await readReceiptEvidence(db, operationId), receipt = receiptEvidence?.receipt;
	if (receipt && receiptEvidence) {
		if (receipt.kind !== "resume" || receipt.commandHash !== commandHash || receipt.principalScope !== scope || receipt.session.id !== sessionId) fail$1("conflict", "operation id was already used for another import command");
		const persisted = required(await readJournalRow(db, sessionId), "integrity", "committed resume receipt has no import journal");
		const session = sessionFromRow(persisted);
		if (JSON.stringify(session) !== JSON.stringify(receipt.session)) fail$1("integrity", "committed resume receipt and import journal differ");
		const guard = prepareExactReplayGuard(db, operationId, "resume", receiptEvidence, persisted, session);
		return Object.freeze({
			items: Object.freeze([guard]),
			result: session
		});
	}
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable");
	const session = sessionFromRow(row), payload = rowJson(row.payload), storedPlan = payload.plan;
	if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== policy) fail$1("authorization", "import session is unavailable");
	if (session.planHash !== command.expectedPlanHash || session.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "command plan evidence differs from session");
	if (session.journalVersion !== command.expectedJournalVersion || session.nextSection !== command.expectedSection || session.nextOffset !== command.expectedOffset) fail$1("stale-journal", "journal coordinate changed");
	if (session.state !== "validated" && session.state !== "applying") fail$1("stale-journal", "import is not resumable");
	if (session.nextSection === null) fail$1("stale-journal", "import is already staged");
	if (!storedPlan || typeof storedPlan !== "object") fail$1("integrity", "import journal has no retained plan evidence");
	const retainedPlan = storedPlan;
	await assertPlanHash(retainedPlan);
	if (retainedPlan.planHash !== session.planHash || retainedPlan.manifestHash !== session.manifestHash || !sameScope(retainedPlan.scope, session.scope) || retainedPlan.authorizationPolicyVersion !== session.authorizationPolicyVersion || retainedPlan.destinationCorpusVersion !== session.destinationCorpusVersion) fail$1("integrity", "retained plan evidence differs from session");
	const batches = Array.isArray(payload.batches) ? payload.batches : [];
	if (batches.length !== session.appliedBatches || batches.some((item, index) => item.batch !== index)) fail$1("integrity", "import batch ledger is not contiguous");
	for (const item of batches) stagedBatchFromValue(item.beforeImage);
	const destinationSnapshot = await readDestinationCorpus(db, session.scope);
	if (await hashDestinationCorpus(destinationSnapshot) !== session.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after planning");
	const section = session.nextSection;
	if (section === null) throw new ContentImportError("stale-journal", "import is already staged");
	const offset = session.nextOffset, slice = sectionItems(parsed, section).slice(offset, offset + bundleLimits(bundle).batchSize), mutations = [];
	for (const item of slice) mutations.push(await stageMutation(db, session, section, item));
	const staged = Object.freeze({
		section,
		offset,
		mutations: Object.freeze(mutations)
	}), ordinal = session.appliedBatches, next = nextCoordinate(parsed, section, offset + slice.length, bundleLimits(bundle).batchSize);
	const result = Object.freeze({
		...session,
		state: next.section === null ? "staged" : "applying",
		appliedBatches: ordinal + 1,
		journalVersion: String(Number(session.journalVersion) + 1),
		nextSection: next.section,
		nextOffset: next.offset
	});
	const nextPayload = JSON.stringify(schemaValue({
		...payload,
		preflightDestinationCorpusSnapshot: destinationSnapshot,
		session: result,
		batches: [...batches, {
			batch: ordinal,
			beforeImage: schemaValue(staged)
		}]
	})), pendingPayload = JSON.stringify(schemaValue({
		kind: "resume",
		commandHash,
		principalScope: scope
	})), committedPayload = JSON.stringify(schemaValue({
		kind: "resume",
		commandHash,
		principalScope: scope,
		session: result
	})), now = (/* @__PURE__ */ new Date()).toISOString();
	const claim = db.prepare(sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${operationId},'content:import:resume','pending',${pendingPayload},${now},${now})`), claimGuard = db.prepare(d1AssertPreviousChanges(1));
	const sectionPredicate = session.nextSection === null ? sql`next_section IS NULL` : sql`next_section=${session.nextSection}`;
	const cas = db.prepare(sql`UPDATE content_import_journal SET state=${result.state},version=version+1,next_section=${result.nextSection},next_offset=${result.nextOffset},payload=${nextPayload},updated_at=${now} WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state=${session.state} AND version=${Number(session.journalVersion)} AND ${sectionPredicate} AND next_offset=${session.nextOffset} AND payload=${JSON.stringify(schemaValue(payload))}`), casGuard = db.prepare(d1AssertPreviousChanges(1));
	const ledgerOperationId = `${sessionId}:batch:${ordinal}`, ledgerPayload = JSON.stringify(schemaValue({
		sessionId,
		planHash: session.planHash,
		manifestHash: session.manifestHash,
		batch: ordinal,
		staged
	}));
	const append = db.prepare(sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${ledgerOperationId},'content:import:batch','committed',${ledgerPayload},${now},${now})`), appendGuard = db.prepare(d1AssertPreviousChanges(1));
	const complete = db.prepare(sql`UPDATE content_lifecycle_journal SET state='committed',payload=${committedPayload},updated_at=${now} WHERE operation_id=${operationId} AND kind='content:import:resume' AND state='pending' AND payload=${pendingPayload}`), completeGuard = db.prepare(d1AssertPreviousChanges(1));
	assertD1StatementBudget("resume", exactCorpusGuardCount(destinationSnapshot) + 8);
	return Object.freeze({
		items: Object.freeze([
			...d1ExactCorpusGuards(db, destinationSnapshot),
			claim,
			claimGuard,
			cas,
			casGuard,
			append,
			appendGuard,
			complete,
			completeGuard
		]),
		result
	});
}
function parseMutation(value) {
	const record = plain(value, "mutation"), kind = record.kind, action = record.action;
	if (kind !== "type" && kind !== "status" && kind !== "taxonomy" && kind !== "term" && kind !== "entry" && kind !== "revision") throw new ContentImportError("integrity", "staged mutation kind is invalid");
	if (action !== "create" && action !== "replace" && action !== "preserve" && action !== "shadow" && action !== "noop") throw new ContentImportError("integrity", "staged mutation action is invalid");
	return {
		kind,
		action,
		record
	};
}
function mutationAfter(record) {
	return plain(record.after, "mutation.after");
}
function mutationBefore(record) {
	const value = record.before;
	return value === null ? null : schemaValue(value, "mutation.before");
}
function boolValue(value) {
	return value === true || value === 1 || value === "1";
}
function nullableJson(value) {
	return value === null || value === void 0 ? null : JSON.stringify(value);
}
async function applyDefinitionMutation(tx, session, mutation) {
	if (mutation.kind !== "type" && mutation.kind !== "status") return false;
	const after = mutationAfter(mutation.record), keyValue = boundedString(after.key, "definition.key", 128), revision = positive$1(Number(after.revision), "definition.revision"), version = positive$1(Number(after.version), "definition.version"), canonicalHash = boundedString(after.canonicalHash, "definition.canonicalHash", 128), definition = plain(after.definition, "definition.definition"), versions = array(after.versions, "definition.versions", 1e3), now = (/* @__PURE__ */ new Date()).toISOString();
	if (mutation.action === "preserve") return true;
	for (let index = 0; index < versions.length; index++) {
		const row = plain(versions[index], `definition.versions.${index}`), rowRevision = positive$1(Number(row.revision), `definition.versions.${index}.revision`), rowHash = boundedString(row.canonicalHash, `definition.versions.${index}.canonicalHash`, 128), rowDefinition = plain(row.definition, `definition.versions.${index}.definition`), createdAt = iso$1(row.createdAt, `definition.versions.${index}.createdAt`);
		await tx.execute(sql`INSERT INTO content_definition_versions (definition_kind,definition_key,revision,canonical_hash,definition,origin,host_session_id,created_at) VALUES (${mutation.kind},${keyValue},${rowRevision},${rowHash},${JSON.stringify(rowDefinition)},'import',${session.staging.hostSessionId},${createdAt}) ON CONFLICT (definition_kind,definition_key,revision) DO NOTHING`);
	}
	if (mutation.action === "shadow") {
		if (mutation.kind === "type") await tx.execute(sql`UPDATE content_type_definitions SET shadowed_db_version=${version},updated_at=${now} WHERE key=${keyValue} AND origin='code'`);
		else await tx.execute(sql`UPDATE content_status_definitions SET shadowed_db_version=${version},updated_at=${now} WHERE key=${keyValue} AND origin='code'`);
		return true;
	}
	const active = boolValue(after.active);
	if (mutation.kind === "type") await tx.execute(sql`INSERT INTO content_type_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${keyValue},'import',${version},${active},${revision},${canonicalHash},${JSON.stringify(definition)},NULL,${session.staging.hostSessionId},${now},${now}) ON CONFLICT (key) DO UPDATE SET origin='import',version=EXCLUDED.version,active=EXCLUDED.active,current_revision=EXCLUDED.current_revision,canonical_hash=EXCLUDED.canonical_hash,definition=EXCLUDED.definition,shadowed_db_version=NULL,host_session_id=EXCLUDED.host_session_id,updated_at=EXCLUDED.updated_at`);
	else await tx.execute(sql`INSERT INTO content_status_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${keyValue},'import',${version},${active},${revision},${canonicalHash},${JSON.stringify(definition)},NULL,${session.staging.hostSessionId},${now},${now}) ON CONFLICT (key) DO UPDATE SET origin='import',version=EXCLUDED.version,active=EXCLUDED.active,current_revision=EXCLUDED.current_revision,canonical_hash=EXCLUDED.canonical_hash,definition=EXCLUDED.definition,shadowed_db_version=NULL,host_session_id=EXCLUDED.host_session_id,updated_at=EXCLUDED.updated_at`);
	return true;
}
async function applyTermMutation(tx, session, mutation) {
	if (mutation.kind === "taxonomy") return true;
	if (mutation.kind !== "term") return false;
	if (mutation.action === "preserve") return true;
	const after = mutationAfter(mutation.record), id = uuid(after.id, "term.id"), taxonomy = key(after.taxonomy, "term.taxonomy"), slug = boundedString(after.slug, "term.slug", 256), name = boundedString(after.name, "term.name", 512), parentId = after.parentId === null ? null : uuid(after.parentId, "term.parentId"), depth = nonnegative(Number(after.depth), "term.depth"), createdAt = iso$1(after.createdAt, "term.createdAt");
	await tx.execute(sql`INSERT INTO content_terms (id,taxonomy,slug,name,parent_id,depth,created_at,host_session_id) VALUES (${id},${taxonomy},${slug},${name},${parentId},${depth},${createdAt},${session.staging.hostSessionId}) ON CONFLICT (id) DO UPDATE SET taxonomy=EXCLUDED.taxonomy,slug=EXCLUDED.slug,name=EXCLUDED.name,parent_id=EXCLUDED.parent_id,depth=EXCLUDED.depth,host_session_id=EXCLUDED.host_session_id`);
	return true;
}
async function applyEntryMutation(tx, session, mutation) {
	if (mutation.kind !== "entry") return false;
	if (mutation.action === "preserve") return true;
	const after = mutationAfter(mutation.record), id = uuid(after.id, "entry.id"), slug = boundedString(after.slug, "entry.slug", 256), type = key(after.type, "entry.type"), title = typeof after.title === "string" ? after.title : fail$1("integrity", "staged entry title is invalid"), body = typeof after.body === "string" ? after.body : fail$1("integrity", "staged entry body is invalid"), status = key(after.status, "entry.status"), visibility = after.visibility, author = boundedString(after.author, "entry.author", 512), createdAt = iso$1(after.createdAt, "entry.createdAt"), updatedAt = iso$1(after.updatedAt, "entry.updatedAt"), parentId = after.parentId === null ? null : uuid(after.parentId, "entry.parentId"), menuOrder = nonnegative(Number(after.menuOrder), "entry.menuOrder"), templateKey = after.templateKey === null ? null : boundedString(after.templateKey, "entry.templateKey", 256), excerpt = typeof after.excerpt === "string" ? after.excerpt : fail$1("integrity", "staged entry excerpt is invalid"), commentStatus = after.commentStatus, pingStatus = after.pingStatus, sticky = after.sticky, lastEditedBy = boundedString(after.lastEditedBy, "entry.lastEditedBy", 512), typeRevision = positive$1(Number(after.typeDefinitionRevision), "entry.typeDefinitionRevision"), statusRevision = positive$1(Number(after.statusDefinitionRevision), "entry.statusDefinitionRevision"), termIds = array(after.termIds, "entry.termIds", 1e4).map((value, index) => uuid(value, `entry.termIds.${index}`));
	if (visibility !== "public" && visibility !== "private" && visibility !== "members") fail$1("integrity", "staged entry visibility is invalid");
	if (commentStatus !== "open" && commentStatus !== "closed") fail$1("integrity", "staged entry comment status is invalid");
	if (pingStatus !== "open" && pingStatus !== "closed") fail$1("integrity", "staged entry ping status is invalid");
	if (typeof sticky !== "boolean") fail$1("integrity", "staged entry sticky value is invalid");
	const publishedAt = after.publishedAt === null ? null : iso$1(after.publishedAt, "entry.publishedAt"), deletedAt = after.deletedAt === null ? null : iso$1(after.deletedAt, "entry.deletedAt"), format = after.format === null ? null : boundedString(after.format, "entry.format", 128), featuredMedia = nullableJson(after.featuredMedia);
	await tx.execute(sql`INSERT INTO content_entries (id,slug,type,title,body,status,visibility,published_at,author,created_at,updated_at,parent_id,menu_order,template_key,excerpt,featured_media,comment_status,ping_status,sticky,format,deleted_at,last_edited_by,type_definition_revision,status_definition_revision,host_session_id) VALUES (${id},${slug},${type},${title},${body},${status},${visibility},${publishedAt},${author},${createdAt},${updatedAt},${parentId},${menuOrder},${templateKey},${excerpt},${featuredMedia},${commentStatus},${pingStatus},${sticky},${format},${deletedAt},${lastEditedBy},${typeRevision},${statusRevision},${session.staging.hostSessionId}) ON CONFLICT (id) DO UPDATE SET slug=EXCLUDED.slug,type=EXCLUDED.type,title=EXCLUDED.title,body=EXCLUDED.body,status=EXCLUDED.status,visibility=EXCLUDED.visibility,published_at=EXCLUDED.published_at,author=EXCLUDED.author,updated_at=EXCLUDED.updated_at,parent_id=EXCLUDED.parent_id,menu_order=EXCLUDED.menu_order,template_key=EXCLUDED.template_key,excerpt=EXCLUDED.excerpt,featured_media=EXCLUDED.featured_media,comment_status=EXCLUDED.comment_status,ping_status=EXCLUDED.ping_status,sticky=EXCLUDED.sticky,format=EXCLUDED.format,deleted_at=EXCLUDED.deleted_at,last_edited_by=EXCLUDED.last_edited_by,type_definition_revision=EXCLUDED.type_definition_revision,status_definition_revision=EXCLUDED.status_definition_revision,host_session_id=EXCLUDED.host_session_id`);
	await tx.execute(sql`DELETE FROM content_entry_terms WHERE entry_id=${id}`);
	for (const termId of termIds) await tx.execute(sql`INSERT INTO content_entry_terms (entry_id,term_id) VALUES (${id},${termId}) ON CONFLICT (entry_id,term_id) DO NOTHING`);
	return true;
}
async function applyRevisionMutation(tx, session, mutation) {
	if (mutation.kind !== "revision") return false;
	if (mutation.action === "preserve") return true;
	const after = mutationAfter(mutation.record), id = uuid(after.id, "revision.id"), entryId = uuid(after.entryId, "revision.entryId"), title = typeof after.title === "string" ? after.title : fail$1("integrity", "staged revision title is invalid"), body = typeof after.body === "string" ? after.body : fail$1("integrity", "staged revision body is invalid"), slug = boundedString(after.slug, "revision.slug", 256), type = key(after.type, "revision.type"), termIds = array(after.termIds, "revision.termIds", 1e4).map((value, index) => uuid(value, `revision.termIds.${index}`)), snapshot = after.snapshot === void 0 ? null : nullableJson(after.snapshot), editor = boundedString(after.editor, "revision.editor", 512), createdAt = iso$1(after.createdAt, "revision.createdAt");
	if ((await execRows(tx, sql`SELECT id FROM content_revisions WHERE id=${id}`))[0] !== void 0) await tx.execute(sql`UPDATE content_revisions SET entry_id=${entryId},title=${title},body=${body},slug=${slug},type=${type},term_ids=${JSON.stringify(termIds)},snapshot=${snapshot},editor=${editor},created_at=${createdAt},host_session_id=${session.staging.hostSessionId} WHERE id=${id}`);
	else await tx.execute(sql`INSERT INTO content_revisions (id,entry_id,title,body,slug,type,term_ids,snapshot,editor,created_at,host_session_id) VALUES (${id},${entryId},${title},${body},${slug},${type},${JSON.stringify(termIds)},${snapshot},${editor},${createdAt},${session.staging.hostSessionId})`);
	return true;
}
async function applyStagedMutation(tx, session, value) {
	const mutation = parseMutation(value);
	if (await applyDefinitionMutation(tx, session, mutation)) return;
	if (await applyTermMutation(tx, session, mutation)) return;
	if (await applyEntryMutation(tx, session, mutation)) return;
	if (await applyRevisionMutation(tx, session, mutation)) return;
	fail$1("integrity", "unknown staged mutation");
}
async function restoreDefinitionMutation(tx, session, mutation) {
	if (mutation.kind !== "type" && mutation.kind !== "status") return false;
	const keyValue = boundedString(mutationAfter(mutation.record).key, "definition.key", 128), beforeCurrent = plain(mutation.record.before, "definition.before").current;
	await tx.execute(sql`DELETE FROM content_definition_versions WHERE definition_kind=${mutation.kind} AND definition_key=${keyValue} AND host_session_id=${session.staging.hostSessionId}`);
	if (mutation.action === "preserve") return true;
	if (beforeCurrent === null) {
		if (mutation.kind === "type") await tx.execute(sql`DELETE FROM content_type_definitions WHERE key=${keyValue} AND host_session_id=${session.staging.hostSessionId}`);
		else await tx.execute(sql`DELETE FROM content_status_definitions WHERE key=${keyValue} AND host_session_id=${session.staging.hostSessionId}`);
		return true;
	}
	const before = plain(beforeCurrent, "definition.before.current"), origin = boundedString(before.origin, "definition.before.origin", 16), version = positive$1(Number(before.version), "definition.before.version"), active = boolValue(before.active), revision = positive$1(Number(before.currentRevision), "definition.before.currentRevision"), hash = boundedString(before.canonicalHash, "definition.before.canonicalHash", 128), definition = plain(before.definition, "definition.before.definition"), shadow = before.shadowedDbVersion === null || before.shadowedDbVersion === void 0 ? null : positive$1(Number(before.shadowedDbVersion), "definition.before.shadowedDbVersion"), host = before.hostSessionId === null || before.hostSessionId === void 0 ? null : String(before.hostSessionId), createdAt = iso$1(before.createdAt, "definition.before.createdAt"), updatedAt = iso$1(before.updatedAt, "definition.before.updatedAt");
	if (origin !== "code" && origin !== "db" && origin !== "import") fail$1("integrity", "definition before-image origin is invalid");
	if (mutation.kind === "type") await tx.execute(sql`INSERT INTO content_type_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${keyValue},${origin},${version},${active},${revision},${hash},${JSON.stringify(definition)},${shadow},${host},${createdAt},${updatedAt}) ON CONFLICT (key) DO UPDATE SET origin=EXCLUDED.origin,version=EXCLUDED.version,active=EXCLUDED.active,current_revision=EXCLUDED.current_revision,canonical_hash=EXCLUDED.canonical_hash,definition=EXCLUDED.definition,shadowed_db_version=EXCLUDED.shadowed_db_version,host_session_id=EXCLUDED.host_session_id,created_at=EXCLUDED.created_at,updated_at=EXCLUDED.updated_at`);
	else await tx.execute(sql`INSERT INTO content_status_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${keyValue},${origin},${version},${active},${revision},${hash},${JSON.stringify(definition)},${shadow},${host},${createdAt},${updatedAt}) ON CONFLICT (key) DO UPDATE SET origin=EXCLUDED.origin,version=EXCLUDED.version,active=EXCLUDED.active,current_revision=EXCLUDED.current_revision,canonical_hash=EXCLUDED.canonical_hash,definition=EXCLUDED.definition,shadowed_db_version=EXCLUDED.shadowed_db_version,host_session_id=EXCLUDED.host_session_id,created_at=EXCLUDED.created_at,updated_at=EXCLUDED.updated_at`);
	return true;
}
async function restoreTermMutation(tx, session, mutation) {
	if (mutation.kind === "taxonomy") return true;
	if (mutation.kind !== "term") return false;
	if (mutation.action === "preserve") return true;
	const destId = uuid(mutation.record.destId, "term.destId"), before = mutationBefore(mutation.record);
	if (before === null) {
		await tx.execute(sql`DELETE FROM content_terms WHERE id=${destId} AND host_session_id=${session.staging.hostSessionId}`);
		return true;
	}
	const row = plain(before, "term.before"), taxonomy = key(row.taxonomy, "term.before.taxonomy"), slug = boundedString(row.slug, "term.before.slug", 256), name = boundedString(row.name, "term.before.name", 512), parentId = row.parentId === null ? null : uuid(row.parentId, "term.before.parentId"), depth = nonnegative(Number(row.depth), "term.before.depth"), createdAt = iso$1(row.createdAt, "term.before.createdAt"), host = row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId);
	await tx.execute(sql`INSERT INTO content_terms (id,taxonomy,slug,name,parent_id,depth,created_at,host_session_id) VALUES (${destId},${taxonomy},${slug},${name},${parentId},${depth},${createdAt},${host}) ON CONFLICT (id) DO UPDATE SET taxonomy=EXCLUDED.taxonomy,slug=EXCLUDED.slug,name=EXCLUDED.name,parent_id=EXCLUDED.parent_id,depth=EXCLUDED.depth,created_at=EXCLUDED.created_at,host_session_id=EXCLUDED.host_session_id`);
	return true;
}
async function restoreEntryMutation(tx, session, mutation) {
	if (mutation.kind !== "entry") return false;
	if (mutation.action === "preserve") return true;
	const destId = uuid(mutation.record.destId, "entry.destId"), before = mutationBefore(mutation.record);
	if (before === null) {
		await tx.execute(sql`DELETE FROM content_entries WHERE id=${destId} AND host_session_id=${session.staging.hostSessionId}`);
		return true;
	}
	const row = plain(before, "entry.before"), slug = boundedString(row.slug, "entry.before.slug", 256), type = key(row.type, "entry.before.type"), title = typeof row.title === "string" ? row.title : fail$1("integrity", "entry before title is invalid"), body = typeof row.body === "string" ? row.body : fail$1("integrity", "entry before body is invalid"), status = key(row.status, "entry.before.status"), visibility = row.visibility, publishedAt = row.publishedAt === null ? null : iso$1(row.publishedAt, "entry.before.publishedAt"), author = boundedString(row.author, "entry.before.author", 512), createdAt = iso$1(row.createdAt, "entry.before.createdAt"), updatedAt = iso$1(row.updatedAt, "entry.before.updatedAt"), parentId = row.parentId === null ? null : uuid(row.parentId, "entry.before.parentId"), menuOrder = nonnegative(Number(row.menuOrder), "entry.before.menuOrder"), templateKey = row.templateKey === null ? null : boundedString(row.templateKey, "entry.before.templateKey", 256), excerpt = typeof row.excerpt === "string" ? row.excerpt : fail$1("integrity", "entry before excerpt is invalid"), commentStatus = row.commentStatus, pingStatus = row.pingStatus, sticky = boolValue(row.sticky), format = row.format === null ? null : boundedString(row.format, "entry.before.format", 128), deletedAt = row.deletedAt === null ? null : iso$1(row.deletedAt, "entry.before.deletedAt"), lastEditedBy = boundedString(row.lastEditedBy, "entry.before.lastEditedBy", 512), typeRevision = positive$1(Number(row.typeDefinitionRevision), "entry.before.typeDefinitionRevision"), statusRevision = positive$1(Number(row.statusDefinitionRevision), "entry.before.statusDefinitionRevision"), host = row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId), featuredMedia = nullableJson(row.featuredMedia), beforeTerms = array(mutation.record.beforeTermIds ?? [], "entry.beforeTermIds", 1e4).map((value, index) => uuid(value, `entry.beforeTermIds.${index}`));
	if (visibility !== "public" && visibility !== "private" && visibility !== "members") fail$1("integrity", "entry before visibility is invalid");
	if (commentStatus !== "open" && commentStatus !== "closed") fail$1("integrity", "entry before comment status is invalid");
	if (pingStatus !== "open" && pingStatus !== "closed") fail$1("integrity", "entry before ping status is invalid");
	await tx.execute(sql`INSERT INTO content_entries (id,slug,type,title,body,status,visibility,published_at,author,created_at,updated_at,parent_id,menu_order,template_key,excerpt,featured_media,comment_status,ping_status,sticky,format,deleted_at,last_edited_by,type_definition_revision,status_definition_revision,host_session_id) VALUES (${destId},${slug},${type},${title},${body},${status},${visibility},${publishedAt},${author},${createdAt},${updatedAt},${parentId},${menuOrder},${templateKey},${excerpt},${featuredMedia},${commentStatus},${pingStatus},${sticky},${format},${deletedAt},${lastEditedBy},${typeRevision},${statusRevision},${host}) ON CONFLICT (id) DO UPDATE SET slug=EXCLUDED.slug,type=EXCLUDED.type,title=EXCLUDED.title,body=EXCLUDED.body,status=EXCLUDED.status,visibility=EXCLUDED.visibility,published_at=EXCLUDED.published_at,author=EXCLUDED.author,created_at=EXCLUDED.created_at,updated_at=EXCLUDED.updated_at,parent_id=EXCLUDED.parent_id,menu_order=EXCLUDED.menu_order,template_key=EXCLUDED.template_key,excerpt=EXCLUDED.excerpt,featured_media=EXCLUDED.featured_media,comment_status=EXCLUDED.comment_status,ping_status=EXCLUDED.ping_status,sticky=EXCLUDED.sticky,format=EXCLUDED.format,deleted_at=EXCLUDED.deleted_at,last_edited_by=EXCLUDED.last_edited_by,type_definition_revision=EXCLUDED.type_definition_revision,status_definition_revision=EXCLUDED.status_definition_revision,host_session_id=EXCLUDED.host_session_id`);
	await tx.execute(sql`DELETE FROM content_entry_terms WHERE entry_id=${destId}`);
	for (const termId of beforeTerms) await tx.execute(sql`INSERT INTO content_entry_terms (entry_id,term_id) VALUES (${destId},${termId}) ON CONFLICT (entry_id,term_id) DO NOTHING`);
	return true;
}
async function restoreRevisionMutation(tx, session, mutation) {
	if (mutation.kind !== "revision") return false;
	if (mutation.action === "preserve") return true;
	const destId = uuid(mutation.record.destId, "revision.destId"), before = mutationBefore(mutation.record);
	if (before === null) {
		await tx.execute(sql`DELETE FROM content_revisions WHERE id=${destId} AND host_session_id=${session.staging.hostSessionId}`);
		return true;
	}
	const row = plain(before, "revision.before"), entryId = uuid(row.entryId, "revision.before.entryId"), title = typeof row.title === "string" ? row.title : fail$1("integrity", "revision before title is invalid"), body = typeof row.body === "string" ? row.body : fail$1("integrity", "revision before body is invalid"), slug = boundedString(row.slug, "revision.before.slug", 256), type = key(row.type, "revision.before.type"), termIds = array(row.termIds, "revision.before.termIds", 1e4).map((value, index) => uuid(value, `revision.before.termIds.${index}`)), snapshot = row.snapshot === null || row.snapshot === void 0 ? null : nullableJson(row.snapshot), editor = boundedString(row.editor, "revision.before.editor", 512), createdAt = iso$1(row.createdAt, "revision.before.createdAt"), host = row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId);
	if ((await execRows(tx, sql`UPDATE content_revisions SET entry_id=${entryId},title=${title},body=${body},slug=${slug},type=${type},term_ids=${JSON.stringify(termIds)},snapshot=${snapshot},editor=${editor},created_at=${createdAt},host_session_id=${host} WHERE id=${destId} RETURNING id`)).length !== 1) fail$1("integrity", "revision before-image target disappeared");
	return true;
}
async function restoreStagedMutation(tx, session, value) {
	const mutation = parseMutation(value);
	if (await restoreRevisionMutation(tx, session, mutation)) return;
	if (await restoreEntryMutation(tx, session, mutation)) return;
	if (await restoreTermMutation(tx, session, mutation)) return;
	if (await restoreDefinitionMutation(tx, session, mutation)) return;
	fail$1("integrity", "unknown staged mutation");
}
async function readValidatedLedger(tx, journal, session) {
	const reverse = await journal.readBatchesReverse(tx, session.id, session.planHash, session.manifestHash);
	if (reverse.length !== session.appliedBatches) fail$1("integrity", "import batch ledger count differs from session");
	const forward = [...reverse].reverse().map((item) => ({
		batch: item.batch,
		staged: stagedBatchFromValue(item.beforeImage)
	}));
	for (let i = 0; i < forward.length; i++) if (forward[i].batch !== i) fail$1("integrity", "import batch ledger sequence is invalid");
	let lastSection = -1, lastEnd = 0;
	for (const item of forward) {
		const sectionIndex = [
			"type",
			"status",
			"taxonomy",
			"term",
			"entry",
			"revision"
		].indexOf(item.staged.section);
		if (sectionIndex < lastSection) fail$1("integrity", "import batch section order is invalid");
		if (sectionIndex === lastSection && item.staged.offset !== lastEnd) fail$1("integrity", "import batch offset sequence is invalid");
		if (sectionIndex > lastSection) {
			lastSection = sectionIndex;
			lastEnd = 0;
			if (item.staged.offset !== 0) fail$1("integrity", "import section does not start at offset zero");
		}
		lastEnd = item.staged.offset + item.staged.mutations.length;
	}
	return Object.freeze(forward);
}
function replaceCorpusRow(rows, identity, id, next) {
	const result = rows.filter((row) => identity(plain(row, "corpus.row")) !== id);
	if (next !== null) result.push(next);
	return Object.freeze(result.sort((a, b) => identity(plain(a, "corpus.row")).localeCompare(identity(plain(b, "corpus.row")))));
}
function projectTermsToTaxonomies(terms, taxonomyKeys) {
	const visible = new Set(taxonomyKeys);
	return Object.freeze(terms.filter((value) => visible.has(String(plain(value, "corpus.term").taxonomy))));
}
function predictPostPublishSnapshot(snapshot, ledger, session) {
	let types = snapshot.types, statuses = snapshot.statuses, versions = snapshot.versions, terms = snapshot.terms, entries = snapshot.entries, assignments = snapshot.assignments, revisions = snapshot.revisions;
	for (const { staged } of ledger) for (const raw of staged.mutations) {
		const mutation = parseMutation(raw);
		if (mutation.action === "preserve" || mutation.action === "noop") continue;
		if (mutation.kind === "type" || mutation.kind === "status") {
			const after = mutationAfter(mutation.record), definitionKey = String(after.key), current = (mutation.kind === "type" ? types : statuses).find((row) => String(plain(row, "definition").key) === definitionKey), currentRecord = current ? plain(current, "definition") : null;
			for (const rawVersion of array(after.versions, "definition.versions", 1e3)) {
				const version = plain(rawVersion, "definition.version"), identity = `${mutation.kind}:${definitionKey}:${Number(version.revision)}`;
				if (!versions.some((row) => {
					const value = plain(row, "definitionVersion");
					return `${value.kind}:${value.key}:${Number(value.revision)}` === identity;
				})) versions = replaceCorpusRow(versions, (row) => `${row.kind}:${row.key}:${Number(row.revision)}`, identity, schemaValue({
					kind: mutation.kind,
					key: definitionKey,
					revision: Number(version.revision),
					canonicalHash: String(version.canonicalHash),
					definition: version.definition,
					origin: "import",
					createdAt: String(version.createdAt)
				}));
			}
			let next;
			if (mutation.action === "shadow") {
				if (currentRecord === null) fail$1("integrity", "shadow mutation has no destination definition");
				next = schemaValue({
					...currentRecord,
					shadowedDbVersion: Number(after.version)
				});
			} else next = schemaValue({
				key: definitionKey,
				origin: "import",
				version: Number(after.version),
				active: boolValue(after.active),
				currentRevision: Number(after.revision),
				canonicalHash: String(after.canonicalHash),
				definition: after.definition,
				shadowedDbVersion: null
			});
			if (mutation.kind === "type") types = replaceCorpusRow(types, (row) => String(row.key), definitionKey, next);
			else statuses = replaceCorpusRow(statuses, (row) => String(row.key), definitionKey, next);
			continue;
		}
		if (mutation.kind === "term") {
			const after = mutationAfter(mutation.record), id = String(after.id);
			terms = replaceCorpusRow(terms, (row) => String(row.id), id, schemaValue({
				...after,
				hostSessionId: session.staging.hostSessionId
			}));
			continue;
		}
		if (mutation.kind === "entry") {
			const after = mutationAfter(mutation.record), id = String(after.id), termIds = array(after.termIds, "entry.termIds", 1e4).map(String);
			entries = replaceCorpusRow(entries, (row) => String(row.id), id, schemaValue({
				...after,
				hostSessionId: session.staging.hostSessionId,
				termIds: void 0
			}));
			assignments = assignments.filter((row) => String(plain(row, "assignment").entryId) !== id);
			assignments = Object.freeze([...assignments, ...termIds.map((termId) => schemaValue({
				entryId: id,
				termId
			}))].sort((a, b) => {
				const left = plain(a, "assignment"), right = plain(b, "assignment");
				return `${left.entryId}:${left.termId}`.localeCompare(`${right.entryId}:${right.termId}`);
			}));
			continue;
		}
		if (mutation.kind === "revision") {
			const after = mutationAfter(mutation.record), id = String(after.id);
			revisions = replaceCorpusRow(revisions, (row) => String(row.id), id, schemaValue({
				id,
				entryId: after.entryId,
				title: after.title,
				body: after.body,
				slug: after.slug,
				type: after.type,
				termIds: after.termIds,
				snapshot: after.snapshot ?? null,
				editor: after.editor,
				createdAt: after.createdAt,
				hostSessionId: session.staging.hostSessionId
			}));
		}
	}
	const statusKeys = Object.freeze(statuses.map((row) => String(plain(row, "status").key)).sort()), taxonomyKeys = Object.freeze([...new Set(types.flatMap((row) => {
		try {
			return normalizeContentTypeDefinition(plain(row, "type").definition).taxonomies;
		} catch {
			return [];
		}
	}))].sort());
	terms = projectTermsToTaxonomies(terms, taxonomyKeys);
	return Object.freeze({
		...snapshot,
		statusKeys,
		taxonomyKeys,
		types,
		statuses,
		versions,
		terms,
		entries,
		assignments,
		revisions
	});
}
function d1Ledger(row, session) {
	const payload = rowJson(row.payload), batches = Array.isArray(payload.batches) ? payload.batches : [];
	if (batches.length !== session.appliedBatches) fail$1("integrity", "import batch ledger count differs from session");
	const ledger = batches.map((item, index) => {
		if (item.batch !== index) fail$1("integrity", "import batch ledger sequence is invalid");
		return {
			batch: item.batch,
			staged: stagedBatchFromValue(item.beforeImage)
		};
	});
	let section = -1, end = 0;
	for (const item of ledger) {
		const next = [
			"type",
			"status",
			"taxonomy",
			"term",
			"entry",
			"revision"
		].indexOf(item.staged.section);
		if (next < section || next === section && item.staged.offset !== end || next > section && item.staged.offset !== 0) fail$1("integrity", "import batch ledger is not contiguous");
		section = next;
		end = item.staged.offset + item.staged.mutations.length;
	}
	return Object.freeze(ledger);
}
async function readRetainedLedger(db, session) {
	const row = (await execRows(db, sql`SELECT import_id AS "importId",host_session_id AS "hostSessionId",manifest_hash AS "manifestHash",plan_hash AS "planHash",state,version,next_section AS "nextSection",next_offset AS "nextOffset",high_water_mark AS "highWaterMark",payload FROM content_import_journal WHERE import_id=${session.id} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash}`))[0];
	if (!row) fail$1("authorization", "import session is unavailable");
	const retained = sessionFromRow(row);
	if (retained.journalVersion !== session.journalVersion || retained.state !== session.state || retained.nextSection !== session.nextSection || retained.nextOffset !== session.nextOffset || retained.appliedBatches !== session.appliedBatches) fail$1("stale-journal", "journal coordinate changed");
	return d1Ledger(row, session);
}
/** Predict the exact semantic corpus hash produced by publishing a retained staged ledger. */
async function predictPublishedContentImportCorpus(db, session) {
	const ledger = await readRetainedLedger(db, session), extraTaxonomyKeys = ledger.flatMap(({ staged }) => staged.mutations.flatMap((raw) => {
		const mutation = parseMutation(raw);
		if (mutation.kind !== "type") return [];
		try {
			return normalizeContentTypeDefinition(mutationAfter(mutation.record).definition).taxonomies;
		} catch {
			return [];
		}
	}));
	return hashDestinationCorpus(predictPostPublishSnapshot(await readDestinationCorpus(db, session.scope, extraTaxonomyKeys), ledger, session));
}
function affectedCounts(ledger) {
	const counts = {};
	for (const { staged } of ledger) for (const raw of staged.mutations) {
		const mutation = parseMutation(raw);
		if (mutation.action === "preserve" || mutation.action === "noop") continue;
		counts[mutation.kind] = (counts[mutation.kind] ?? 0) + 1;
	}
	return Object.freeze(counts);
}
async function beforeImageHash(ledger) {
	return canonicalContentMigrationHash(schemaValue(ledger.map((item) => ({
		batch: item.batch,
		staged: item.staged
	}))));
}
function lifecycleEvent(kind, operationId, session, principal, counts) {
	return Object.freeze({
		id: crypto.randomUUID(),
		version: 1,
		kind,
		operationId,
		sessionId: session.id,
		planHash: session.planHash,
		manifestHash: session.manifestHash,
		principalId: principal.id,
		scope: session.scope,
		authorizationPolicyVersion: session.authorizationPolicyVersion,
		affectedCounts: counts
	});
}
async function emitLifecycleEffects(tx, effects, event) {
	await effects.audit.record(tx, event);
	await effects.outbox.enqueue(tx, event);
}
function addChecked(items, db, query, expected = 1) {
	items.push(db.prepare(query), db.prepare(d1AssertPreviousChanges(expected)));
}
function assertD1StatementBudget(operation, count) {
	if (!Number.isSafeInteger(count) || count < 1 || count > 50) fail$1("limit", `${operation} contribution requires ${count} D1 statements; maximum is 50`, "d1Statements");
}
function exactCorpusGuardCount(snapshot) {
	return 1 + snapshot.types.length + snapshot.statuses.length + snapshot.versions.length + snapshot.terms.length + snapshot.entries.length + snapshot.assignments.length + snapshot.revisions.length;
}
function insertedDefinitionVersions(mutation) {
	const before = plain(mutation.record.before, "definition.before"), retained = new Set(array(before.versions, "definition.before.versions", 1e3).map((value) => Number(plain(value, "definition.before.version").revision)));
	return Object.freeze(array(mutationAfter(mutation.record).versions, "definition.versions", 1e3).filter((value) => !retained.has(Number(plain(value, "definition.version").revision))));
}
function publishMutationStatementCount(raw) {
	const mutation = parseMutation(raw);
	if (mutation.action === "preserve" || mutation.action === "noop") return 0;
	if (mutation.kind === "type" || mutation.kind === "status") return insertedDefinitionVersions(mutation).length + (mutation.action === "create" ? 1 : 2);
	if (mutation.kind === "term" || mutation.kind === "revision") return mutation.action === "create" ? 1 : 2;
	const after = mutationAfter(mutation.record);
	return (mutation.action === "create" ? 1 : 2) + 2 + array(after.termIds, "entry.termIds", 1e4).length;
}
function reverseMutationStatementCount(raw) {
	const mutation = parseMutation(raw);
	if (mutation.action === "preserve" || mutation.action === "noop") return 0;
	if (mutation.kind === "entry") {
		mutationBefore(mutation.record);
		return 4 + array(mutation.record.beforeTermIds ?? [], "entry.beforeTermIds", 1e4).length;
	}
	if (mutation.kind === "type" || mutation.kind === "status") return 4;
	return 2;
}
function preparePublishMutation(items, db, session, raw, now, phase) {
	const mutation = parseMutation(raw);
	if (mutation.action === "preserve" || mutation.action === "noop") return;
	const after = mutationAfter(mutation.record), host = session.staging.hostSessionId;
	if (mutation.kind === "type" || mutation.kind === "status") {
		const definitionKey = key(after.key, "definition.key");
		if (phase === "versions") {
			for (const value of insertedDefinitionVersions(mutation)) {
				const version = plain(value, "definition.version");
				items.push(db.prepare(sql`INSERT INTO content_definition_versions (definition_kind,definition_key,revision,canonical_hash,definition,origin,host_session_id,created_at) VALUES (${mutation.kind},${definitionKey},${positive$1(Number(version.revision), "definition.version.revision")},${boundedString(version.canonicalHash, "definition.version.canonicalHash", 128)},${JSON.stringify(plain(version.definition, "definition.version.definition"))},'import',${host},${iso$1(version.createdAt, "definition.version.createdAt")})`));
			}
			return;
		}
		const version = positive$1(Number(after.version), "definition.version"), revision = positive$1(Number(after.revision), "definition.revision"), hash = boundedString(after.canonicalHash, "definition.canonicalHash", 128), definition = JSON.stringify(plain(after.definition, "definition.definition")), active = boolValue(after.active);
		if (mutation.action === "shadow") {
			addChecked(items, db, mutation.kind === "type" ? sql`UPDATE content_type_definitions SET shadowed_db_version=${version},updated_at=${now} WHERE key=${definitionKey} AND origin='code'` : sql`UPDATE content_status_definitions SET shadowed_db_version=${version},updated_at=${now} WHERE key=${definitionKey} AND origin='code'`);
			return;
		}
		if (mutation.action === "create") {
			items.push(db.prepare(mutation.kind === "type" ? sql`INSERT INTO content_type_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${definitionKey},'import',${version},${active},${revision},${hash},${definition},NULL,${host},${now},${now})` : sql`INSERT INTO content_status_definitions (key,origin,version,active,current_revision,canonical_hash,definition,shadowed_db_version,host_session_id,created_at,updated_at) VALUES (${definitionKey},'import',${version},${active},${revision},${hash},${definition},NULL,${host},${now},${now})`));
			return;
		}
		const before = plain(plain(mutation.record.before, "definition.before").current, "definition.before.current"), where = sql`key=${definitionKey} AND origin=${String(before.origin)} AND version=${Number(before.version)} AND current_revision=${Number(before.currentRevision)} AND canonical_hash=${String(before.canonicalHash)}`;
		addChecked(items, db, mutation.kind === "type" ? sql`UPDATE content_type_definitions SET origin='import',version=${version},active=${active},current_revision=${revision},canonical_hash=${hash},definition=${definition},shadowed_db_version=NULL,host_session_id=${host},updated_at=${now} WHERE ${where}` : sql`UPDATE content_status_definitions SET origin='import',version=${version},active=${active},current_revision=${revision},canonical_hash=${hash},definition=${definition},shadowed_db_version=NULL,host_session_id=${host},updated_at=${now} WHERE ${where}`);
		return;
	}
	if (phase === "versions") return;
	if (mutation.kind === "term") {
		const id = uuid(after.id, "term.id"), taxonomy = key(after.taxonomy, "term.taxonomy"), slug = boundedString(after.slug, "term.slug", 256), name = boundedString(after.name, "term.name", 512), parent = after.parentId === null ? null : uuid(after.parentId, "term.parentId"), depth = nonnegative(Number(after.depth), "term.depth"), created = iso$1(after.createdAt, "term.createdAt");
		if (mutation.action === "create") items.push(db.prepare(sql`INSERT INTO content_terms (id,taxonomy,slug,name,parent_id,depth,created_at,host_session_id) VALUES (${id},${taxonomy},${slug},${name},${parent},${depth},${created},${host})`));
		else addChecked(items, db, sql`UPDATE content_terms SET taxonomy=${taxonomy},slug=${slug},name=${name},parent_id=${parent},depth=${depth},host_session_id=${host} WHERE id=${id}`);
		return;
	}
	if (mutation.kind === "entry") {
		const id = uuid(after.id, "entry.id"), terms = array(after.termIds, "entry.termIds", 1e4).map((value, index) => uuid(value, `entry.termIds.${index}`)), values = sql`${id},${String(after.slug)},${String(after.type)},${String(after.title)},${String(after.body)},${String(after.status)},${String(after.visibility)},${after.publishedAt === null ? null : String(after.publishedAt)},${String(after.author)},${String(after.createdAt)},${String(after.updatedAt)},${after.parentId === null ? null : String(after.parentId)},${Number(after.menuOrder)},${after.templateKey === null ? null : String(after.templateKey)},${String(after.excerpt)},${nullableJson(after.featuredMedia)},${String(after.commentStatus)},${String(after.pingStatus)},${boolValue(after.sticky)},${after.format === null ? null : String(after.format)},${after.deletedAt === null ? null : String(after.deletedAt)},${String(after.lastEditedBy)},${Number(after.typeDefinitionRevision)},${Number(after.statusDefinitionRevision)},${host}`;
		if (mutation.action === "create") items.push(db.prepare(sql`INSERT INTO content_entries (id,slug,type,title,body,status,visibility,published_at,author,created_at,updated_at,parent_id,menu_order,template_key,excerpt,featured_media,comment_status,ping_status,sticky,format,deleted_at,last_edited_by,type_definition_revision,status_definition_revision,host_session_id) VALUES (${values})`));
		else addChecked(items, db, sql`UPDATE content_entries SET slug=${String(after.slug)},type=${String(after.type)},title=${String(after.title)},body=${String(after.body)},status=${String(after.status)},visibility=${String(after.visibility)},published_at=${after.publishedAt === null ? null : String(after.publishedAt)},author=${String(after.author)},updated_at=${String(after.updatedAt)},parent_id=${after.parentId === null ? null : String(after.parentId)},menu_order=${Number(after.menuOrder)},template_key=${after.templateKey === null ? null : String(after.templateKey)},excerpt=${String(after.excerpt)},featured_media=${nullableJson(after.featuredMedia)},comment_status=${String(after.commentStatus)},ping_status=${String(after.pingStatus)},sticky=${boolValue(after.sticky)},format=${after.format === null ? null : String(after.format)},deleted_at=${after.deletedAt === null ? null : String(after.deletedAt)},last_edited_by=${String(after.lastEditedBy)},type_definition_revision=${Number(after.typeDefinitionRevision)},status_definition_revision=${Number(after.statusDefinitionRevision)},host_session_id=${host} WHERE id=${id}`);
		addChecked(items, db, sql`DELETE FROM content_entry_terms WHERE entry_id=${id}`, array(mutation.record.beforeTermIds ?? [], "entry.beforeTermIds", 1e4).length);
		for (const term of terms) items.push(db.prepare(sql`INSERT INTO content_entry_terms (entry_id,term_id) VALUES (${id},${term})`));
		return;
	}
	const id = uuid(after.id, "revision.id"), entryId = uuid(after.entryId, "revision.entryId"), termIds = JSON.stringify(array(after.termIds, "revision.termIds", 1e4)), snapshot = after.snapshot === void 0 ? null : nullableJson(after.snapshot);
	if (mutation.action === "create") items.push(db.prepare(sql`INSERT INTO content_revisions (id,entry_id,title,body,slug,type,term_ids,snapshot,editor,created_at,host_session_id) VALUES (${id},${entryId},${String(after.title)},${String(after.body)},${String(after.slug)},${String(after.type)},${termIds},${snapshot},${String(after.editor)},${String(after.createdAt)},${host})`));
	else addChecked(items, db, sql`UPDATE content_revisions SET entry_id=${entryId},title=${String(after.title)},body=${String(after.body)},slug=${String(after.slug)},type=${String(after.type)},term_ids=${termIds},snapshot=${snapshot},editor=${String(after.editor)},created_at=${String(after.createdAt)},host_session_id=${host} WHERE id=${id}`);
}
async function preparePublishContentImportD1(db, authorization, principal, sessionIdInput, command) {
	assertD1Scope(command.scope);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), operationId = boundedString(command.operationId, "operationId", 256), authInput = authorizationInputFromCommand(command), policy = await assertImportAuthorized(db, authorization, principal, "publish", authInput);
	if (policy !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const commandHash = await canonicalCommandHash("publish", sessionId, principal, command), scope = principalScope(principal), receiptEvidence = await readReceiptEvidence(db, operationId), receipt = receiptEvidence?.receipt;
	if (receipt && receiptEvidence) {
		if (receipt.kind !== "publish" || receipt.commandHash !== commandHash || receipt.principalScope !== scope || receipt.session.id !== sessionId) fail$1("conflict", "operation id was already used for another import command");
		const persisted = required(await readJournalRow(db, sessionId), "integrity", "committed publish receipt has no import journal");
		const result = sessionFromRow(persisted);
		if (JSON.stringify(result) !== JSON.stringify(receipt.session)) fail$1("integrity", "committed publish receipt and import journal differ");
		const guard = prepareExactReplayGuard(db, operationId, "publish", receiptEvidence, persisted, result);
		return Object.freeze({
			items: Object.freeze([guard]),
			result
		});
	}
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable");
	const session = sessionFromRow(row);
	if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== policy) fail$1("authorization", "import session is unavailable");
	if (session.planHash !== command.expectedPlanHash || session.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "command plan evidence differs from session");
	if (session.journalVersion !== command.expectedJournalVersion || session.nextSection !== command.expectedSection || session.nextOffset !== command.expectedOffset || session.state !== "staged" || session.nextSection !== null || session.nextOffset !== 0) fail$1("stale-journal", "import is not staged for publication");
	if (await destinationCorpusVersion(db, session.scope) !== session.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after staging");
	const ledger = d1Ledger(row, session), counts = affectedCounts(ledger), extra = ledger.flatMap(({ staged }) => staged.mutations.flatMap((raw) => {
		const mutation = parseMutation(raw);
		return mutation.kind === "type" ? normalizeContentTypeDefinition(mutationAfter(mutation.record).definition).taxonomies : [];
	})), snapshot = await readDestinationCorpus(db, session.scope, extra), predictedSnapshot = predictPostPublishSnapshot(snapshot, ledger, session), predicted = await hashDestinationCorpus(predictedSnapshot), result = Object.freeze({
		...session,
		state: "published",
		publishedCorpusVersion: predicted,
		journalVersion: String(Number(session.journalVersion) + 1),
		nextSection: null,
		nextOffset: 0
	});
	const payload = rowJson(row.payload), storedPayload = JSON.stringify(schemaValue(payload)), nextPayload = JSON.stringify(schemaValue({
		...payload,
		session: result
	})), pendingPayload = JSON.stringify(schemaValue({
		kind: "publish",
		commandHash,
		principalScope: scope
	})), committedPayload = JSON.stringify(schemaValue({
		kind: "publish",
		commandHash,
		principalScope: scope,
		session: result
	})), now = (/* @__PURE__ */ new Date()).toISOString();
	const mutationStatements = ledger.reduce((count, { staged }) => count + staged.mutations.reduce((sum, mutation) => sum + publishMutationStatementCount(mutation), 0), 0);
	assertD1StatementBudget("publish", 7 + exactCorpusGuardCount(snapshot) + mutationStatements + exactCorpusGuardCount(predictedSnapshot));
	const items = [];
	addChecked(items, db, sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${operationId},'content:import:publish','pending',${pendingPayload},${now},${now})`);
	items.push(...d1ExactCorpusGuards(db, snapshot));
	items.push(db.prepare(sql`SELECT CASE WHEN EXISTS (SELECT 1 FROM content_import_journal WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state='staged' AND version=${Number(session.journalVersion)} AND next_section IS NULL AND next_offset=0 AND payload=${storedPayload}) THEN 1 ELSE json_extract('not-json','$') END AS ok`));
	for (const phase of ["versions", "canonical"]) for (const { staged } of ledger) for (const mutation of staged.mutations) preparePublishMutation(items, db, session, mutation, now, phase);
	items.push(...d1ExactCorpusGuards(db, predictedSnapshot));
	addChecked(items, db, sql`UPDATE content_import_journal SET state='published',version=version+1,high_water_mark=${predicted},payload=${nextPayload},updated_at=${now} WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state='staged' AND version=${Number(session.journalVersion)} AND next_section IS NULL AND next_offset=0 AND payload=${storedPayload}`);
	addChecked(items, db, sql`UPDATE content_lifecycle_journal SET state='committed',payload=${committedPayload},updated_at=${now} WHERE operation_id=${operationId} AND kind='content:import:publish' AND state='pending' AND payload=${pendingPayload}`);
	return Object.freeze({
		items: Object.freeze(items),
		result,
		affectedCounts: counts
	});
}
async function publishContentImport(tx, journal, authorization, effects, principal, sessionIdInput, command) {
	assertActiveContentTransaction(tx);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), commandHash = await canonicalCommandHash("publish", sessionId, principal, command), claim = await journal.claimCommand(tx, sessionId, principal, "publish", command, commandHash, authorization);
	if (claim.kind === "conflict") throw new ContentImportError("conflict", "operation id was already used for another import command");
	if (claim.kind === "replay") return claim.session;
	const session = claim.session;
	if (!session) throw new ContentImportError("integrity", "publish claim did not return session");
	if (session.state !== "staged" || session.nextSection !== null || session.nextOffset !== 0) fail$1("stale-journal", "import is not staged for publication");
	if (await destinationCorpusVersion(tx, session.scope) !== session.destinationCorpusVersion) fail$1("stale-plan", "destination corpus changed after staging");
	const ledger = await readValidatedLedger(tx, journal, session), counts = affectedCounts(ledger), predictedPublishedCorpus = await predictPublishedContentImportCorpus(tx, session);
	for (const { staged } of ledger) for (const mutation of staged.mutations) await applyStagedMutation(tx, session, mutation);
	const publishedCorpus = await destinationCorpusVersion(tx, session.scope);
	if (publishedCorpus !== predictedPublishedCorpus) fail$1("integrity", "predicted published corpus differs from committed publication");
	const result = Object.freeze({
		...session,
		state: "published",
		publishedCorpusVersion: publishedCorpus,
		nextSection: null,
		nextOffset: 0
	});
	await emitLifecycleEffects(tx, effects, lifecycleEvent("published", command.operationId, result, principal, counts));
	await journal.completeCommand(tx, session.id, "publish", command, commandHash, result);
	return result;
}
/** Prepare a staged content-import rollback for one host-owned atomic D1 batch. */
async function prepareRollbackContentImportD1(db, authorization, principal, sessionIdInput, command) {
	assertD1Scope(command.scope);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), operationId = boundedString(command.operationId, "operationId", 256), authInput = authorizationInputFromCommand(command);
	const policy = await assertImportAuthorized(db, authorization, principal, "rollback", authInput);
	if (policy !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const commandHash = await canonicalCommandHash("rollback", sessionId, principal, command), scope = principalScope(principal), receiptEvidence = await readReceiptEvidence(db, operationId), receipt = receiptEvidence?.receipt;
	if (receipt && receiptEvidence) {
		if (receipt.kind !== "rollback" || receipt.commandHash !== commandHash || receipt.principalScope !== scope || receipt.session.id !== sessionId) fail$1("conflict", "operation id was already used for another import command");
		const persisted = required(await readJournalRow(db, sessionId), "integrity", "committed rollback receipt has no import journal");
		const result = sessionFromRow(persisted);
		if (JSON.stringify(result) !== JSON.stringify(receipt.session)) fail$1("integrity", "committed rollback receipt and import journal differ");
		const guard = prepareExactReplayGuard(db, operationId, "rollback", receiptEvidence, persisted, result), ledgerGuard = db.prepare(sql`SELECT CASE WHEN NOT EXISTS (SELECT 1 FROM content_lifecycle_journal WHERE kind='content:import:batch' AND json_extract(payload,'$.sessionId')=${sessionId}) THEN 1 ELSE json_extract('not-json','$') END AS ok`);
		return Object.freeze({
			items: Object.freeze([guard, ledgerGuard]),
			result
		});
	}
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable");
	const session = sessionFromRow(row);
	if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== policy) fail$1("authorization", "import session is unavailable");
	if (session.planHash !== command.expectedPlanHash || session.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "command plan evidence differs from session");
	if (session.journalVersion !== command.expectedJournalVersion || session.nextSection !== command.expectedSection || session.nextOffset !== command.expectedOffset) fail$1("stale-journal", "journal coordinate changed");
	if (session.state !== "validated" && session.state !== "applying" && session.state !== "staged") fail$1("stale-journal", "published or terminal imports cannot use staged rollback");
	const ledger = d1Ledger(row, session), counts = affectedCounts(ledger), payload = rowJson(row.payload), storedPayload = JSON.stringify(schemaValue(payload)), result = Object.freeze({
		...session,
		state: "rolledBack",
		journalVersion: String(Number(session.journalVersion) + 1),
		nextSection: null,
		nextOffset: 0
	});
	const nextPayload = JSON.stringify(schemaValue({
		...payload,
		session: result,
		batches: []
	})), pendingPayload = JSON.stringify(schemaValue({
		kind: "rollback",
		commandHash,
		principalScope: scope
	})), committedPayload = JSON.stringify(schemaValue({
		kind: "rollback",
		commandHash,
		principalScope: scope,
		session: result
	})), now = (/* @__PURE__ */ new Date()).toISOString(), items = [];
	addChecked(items, db, sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${operationId},'content:import:rollback','pending',${pendingPayload},${now},${now})`);
	addChecked(items, db, sql`UPDATE content_import_journal SET state='rolledBack',version=version+1,next_section=NULL,next_offset=0,payload=${nextPayload},updated_at=${now} WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state=${session.state} AND version=${Number(session.journalVersion)} AND next_section ${session.nextSection === null ? sql`IS NULL` : sql`=${session.nextSection}`} AND next_offset=${session.nextOffset} AND payload=${storedPayload}`);
	addChecked(items, db, sql`DELETE FROM content_lifecycle_journal WHERE kind='content:import:batch' AND json_extract(payload,'$.sessionId')=${sessionId} AND json_extract(payload,'$.planHash')=${session.planHash} AND json_extract(payload,'$.manifestHash')=${session.manifestHash}`, ledger.length);
	addChecked(items, db, sql`UPDATE content_lifecycle_journal SET state='committed',payload=${committedPayload},updated_at=${now} WHERE operation_id=${operationId} AND kind='content:import:rollback' AND state='pending' AND payload=${pendingPayload}`);
	return Object.freeze({
		items: Object.freeze(items),
		result,
		affectedCounts: counts
	});
}
async function rollbackContentImport(tx, journal, authorization, effects, principal, sessionIdInput, command) {
	assertActiveContentTransaction(tx);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), commandHash = await canonicalCommandHash("rollback", sessionId, principal, command), claim = await journal.claimCommand(tx, sessionId, principal, "rollback", command, commandHash, authorization);
	if (claim.kind === "conflict") throw new ContentImportError("conflict", "operation id was already used for another import command");
	if (claim.kind === "replay") return claim.session;
	const session = claim.session;
	if (!session) throw new ContentImportError("integrity", "rollback claim did not return session");
	if (session.state !== "validated" && session.state !== "applying" && session.state !== "staged") fail$1("stale-journal", "published or terminal imports cannot use staged rollback");
	const ledger = await readValidatedLedger(tx, journal, session), counts = affectedCounts(ledger);
	for (const item of ledger) stagedBatchFromValue(schemaValue(item.staged));
	const result = Object.freeze({
		...session,
		state: "rolledBack",
		nextSection: null,
		nextOffset: 0
	});
	await emitLifecycleEffects(tx, effects, lifecycleEvent("rolledBack", command.operationId, result, principal, counts));
	await journal.completeCommand(tx, session.id, "rollback", command, commandHash, result);
	return result;
}
function reversePlanMaterial(plan) {
	return schemaValue(plan);
}
async function assertReversePlanHash(plan) {
	const { reversePlanHash, ...rest } = plan;
	if (await canonicalContentMigrationHash(reversePlanMaterial(rest)) !== reversePlanHash) fail$1("integrity", "published reverse plan hash is invalid");
}
async function planPublishedContentImportReverse(tx, journal, authorization, principal, sessionIdInput, authorizationInput) {
	assertActiveContentTransaction(tx);
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), scope = stableScope(authorizationInput.scope), requiredAuthority = stableAuthority(authorizationInput.requiredAuthority), policy = await assertImportAuthorized(tx, authorization, principal, "reversePublished", {
		scope,
		requiredAuthority,
		...authorizationInput.expectedPolicyVersion ? { expectedPolicyVersion: authorizationInput.expectedPolicyVersion } : {}
	});
	if (authorizationInput.expectedPolicyVersion !== void 0 && policy !== authorizationInput.expectedPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const session = await journal.get(tx, sessionId, principal);
	if (session.state !== "published") fail$1("stale-journal", "import is not published");
	if (!sameScope(scope, session.scope) || !sameAuthority(requiredAuthority, session.requiredAuthority) || policy !== session.authorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	if (!session.publishedCorpusVersion) throw new ContentImportError("integrity", "published import has no corpus high-water mark");
	const publishedCorpusVersion = session.publishedCorpusVersion;
	if (await destinationCorpusVersion(tx, scope) !== publishedCorpusVersion) fail$1("stale-plan", "published corpus changed after import");
	const ledger = await readValidatedLedger(tx, journal, session), hash = await beforeImageHash(ledger), counts = affectedCounts(ledger), rest = {
		sessionId,
		originalPlanHash: session.planHash,
		manifestHash: session.manifestHash,
		publishedCorpusVersion,
		beforeImageHash: hash,
		affectedCounts: counts,
		scope,
		requiredAuthority,
		authorizationPolicyVersion: policy
	};
	return Object.freeze({
		reversePlanHash: await canonicalContentMigrationHash(reversePlanMaterial(rest)),
		...rest
	});
}
/** Plan reversal of a published content import using read-only native D1 evidence. */
async function planPublishedContentImportReverseD1(db, authorization, principal, sessionIdInput, authorizationInput) {
	const sessionId = boundedString(sessionIdInput, "sessionId", 128), scope = assertD1Scope(authorizationInput.scope), requiredAuthority = stableAuthority(authorizationInput.requiredAuthority);
	const policy = await assertImportAuthorized(db, authorization, principal, "reversePublished", {
		scope,
		requiredAuthority,
		...authorizationInput.expectedPolicyVersion ? { expectedPolicyVersion: authorizationInput.expectedPolicyVersion } : {}
	});
	if (authorizationInput.expectedPolicyVersion !== void 0 && policy !== authorizationInput.expectedPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable"), session = sessionFromRow(row), storedSession = rowJson(row.payload).session;
	if (JSON.stringify(schemaValue(storedSession, "journal.session")) !== JSON.stringify(schemaValue(session, "session"))) fail$1("integrity", "retained published import evidence differs from journal");
	if (session.state !== "published") fail$1("stale-journal", "import is not published");
	if (!sameScope(scope, session.scope) || !sameAuthority(requiredAuthority, session.requiredAuthority) || policy !== session.authorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const publishedCorpusVersion = required(session.publishedCorpusVersion, "integrity", "published import has no corpus high-water mark");
	if (await destinationCorpusVersion(db, scope) !== publishedCorpusVersion) fail$1("stale-plan", "published corpus changed after import");
	const ledger = d1Ledger(row, session), rest = {
		sessionId,
		originalPlanHash: session.planHash,
		manifestHash: session.manifestHash,
		publishedCorpusVersion,
		beforeImageHash: await beforeImageHash(ledger),
		affectedCounts: affectedCounts(ledger),
		scope,
		requiredAuthority,
		authorizationPolicyVersion: policy
	};
	return Object.freeze({
		reversePlanHash: await canonicalContentMigrationHash(reversePlanMaterial(rest)),
		...rest
	});
}
function predictPrePublishSnapshot(snapshot, ledger) {
	let types = snapshot.types, statuses = snapshot.statuses, versions = snapshot.versions, terms = snapshot.terms, entries = snapshot.entries, assignments = snapshot.assignments, revisions = snapshot.revisions;
	for (const { staged } of [...ledger].reverse()) for (const raw of [...staged.mutations].reverse()) {
		const mutation = parseMutation(raw);
		if (mutation.action === "preserve" || mutation.action === "noop") continue;
		if (mutation.kind === "type" || mutation.kind === "status") {
			const after = mutationAfter(mutation.record), definitionKey = String(after.key), before = plain(mutation.record.before, "definition.before"), beforeCurrent = before.current === null ? null : canonicalDefinitionRow(plain(before.current, "definition.before.current"));
			if (mutation.kind === "type") types = replaceCorpusRow(types, (row) => String(row.key), definitionKey, beforeCurrent);
			else statuses = replaceCorpusRow(statuses, (row) => String(row.key), definitionKey, beforeCurrent);
			const retained = new Set(array(before.versions, "definition.before.versions", 1e3).map((value) => Number(plain(value, "definition.before.version").revision))), inserted = new Set(array(after.versions, "definition.versions", 1e3).map((value) => Number(plain(value, "definition.version").revision)).filter((revision) => !retained.has(revision)).map((revision) => `${mutation.kind}:${definitionKey}:${revision}`));
			versions = Object.freeze(versions.filter((value) => {
				const row = plain(value, "definition.version");
				return !inserted.has(`${row.kind}:${row.key}:${Number(row.revision)}`);
			}));
			continue;
		}
		if (mutation.kind === "term") {
			const id = String(mutation.record.destId), before = mutationBefore(mutation.record);
			terms = replaceCorpusRow(terms, (row) => String(row.id), id, before === null ? null : canonicalTermRow(plain(before, "term.before")));
			continue;
		}
		if (mutation.kind === "entry") {
			const id = String(mutation.record.destId), before = mutationBefore(mutation.record);
			entries = replaceCorpusRow(entries, (row) => String(row.id), id, before === null ? null : canonicalEntryRow(plain(before, "entry.before")));
			assignments = assignments.filter((value) => String(plain(value, "assignment").entryId) !== id);
			if (before !== null) assignments = Object.freeze([...assignments, ...array(mutation.record.beforeTermIds ?? [], "entry.beforeTermIds", 1e4).map((termId) => schemaValue({
				entryId: id,
				termId: String(termId)
			}))].sort((a, b) => {
				const left = plain(a, "assignment"), right = plain(b, "assignment");
				return `${left.entryId}:${left.termId}`.localeCompare(`${right.entryId}:${right.termId}`);
			}));
			continue;
		}
		const id = String(mutation.record.destId), before = mutationBefore(mutation.record);
		revisions = replaceCorpusRow(revisions, (row) => String(row.id), id, before === null ? null : canonicalRevisionRow(plain(before, "revision.before")));
	}
	const statusKeys = Object.freeze(statuses.map((value) => String(plain(value, "status").key)).sort()), taxonomyKeys = Object.freeze([...new Set(types.flatMap((value) => normalizeContentTypeDefinition(plain(value, "type").definition).taxonomies))].sort());
	terms = projectTermsToTaxonomies(terms, taxonomyKeys);
	return Object.freeze({
		...snapshot,
		statusKeys,
		taxonomyKeys,
		types,
		statuses,
		versions,
		terms,
		entries,
		assignments,
		revisions
	});
}
function d1CorpusCountGuardBindCount(snapshot) {
	return snapshot.mappedTypeKeys.length * 3 + snapshot.statusKeys.length + snapshot.taxonomyKeys.length + snapshot.entries.length * 2;
}
function assertD1CorpusCountGuardBindBudget(snapshot) {
	const count = d1CorpusCountGuardBindCount(snapshot);
	if (count > CONTENT_IMPORT_D1_BIND_BUDGET) fail$1("limit", `exact D1 corpus count guard requires ${count} bind parameters; maximum is ${CONTENT_IMPORT_D1_BIND_BUDGET}`, "d1BindParameters");
}
function d1CorpusCountGuard(db, snapshot) {
	assertD1CorpusCountGuardBindBudget(snapshot);
	const checks = [
		sql`(SELECT COUNT(*) FROM content_type_definitions WHERE key IN (${sqlList$1(snapshot.mappedTypeKeys)}))=${snapshot.types.length}`,
		sql`(SELECT COUNT(*) FROM content_status_definitions)=${snapshot.statuses.length}`,
		sql`(SELECT COUNT(*) FROM content_entries WHERE type IN (${sqlList$1(snapshot.mappedTypeKeys)}))=${snapshot.entries.length}`
	];
	const definitionKeys = [...snapshot.mappedTypeKeys.map((value) => `type:${value}`), ...snapshot.statusKeys.map((value) => `status:${value}`)];
	if (definitionKeys.length) checks.push(sql`(SELECT COUNT(*) FROM content_definition_versions WHERE (definition_kind||':'||definition_key) IN (${sqlList$1(definitionKeys)}))=${snapshot.versions.length}`);
	if (snapshot.taxonomyKeys.length) checks.push(sql`(SELECT COUNT(*) FROM content_terms WHERE taxonomy IN (${sqlList$1(snapshot.taxonomyKeys)}))=${snapshot.terms.length}`);
	const entryIds = snapshot.entries.map((value) => String(plain(value, "corpus.entry").id));
	if (entryIds.length) checks.push(sql`(SELECT COUNT(*) FROM content_entry_terms WHERE entry_id IN (${sqlList$1(entryIds)}))=${snapshot.assignments.length}`, sql`(SELECT COUNT(*) FROM content_revisions WHERE entry_id IN (${sqlList$1(entryIds)}))=${snapshot.revisions.length}`);
	return db.prepare(sql`SELECT CASE WHEN ${sql.join(checks, sql` AND `)} THEN 1 ELSE json_extract('not-json','$') END AS ok`);
}
function d1ExactCorpusGuards(db, snapshot) {
	const items = [d1CorpusCountGuard(db, snapshot)], guard = (predicate) => {
		items.push(db.prepare(sql`SELECT CASE WHEN EXISTS (SELECT 1 WHERE ${predicate}) THEN 1 ELSE json_extract('not-json','$') END AS ok`));
	};
	for (const value of snapshot.types) {
		const row = plain(value, "corpus.type");
		guard(sql`EXISTS (SELECT 1 FROM content_type_definitions WHERE key=${String(row.key)} AND origin=${String(row.origin)} AND version=${Number(row.version)} AND active=${boolValue(row.active)} AND current_revision=${Number(row.currentRevision)} AND canonical_hash=${String(row.canonicalHash)} AND json(definition)=json(${JSON.stringify(row.definition)}) AND shadowed_db_version IS ${row.shadowedDbVersion === null ? null : Number(row.shadowedDbVersion)})`);
	}
	for (const value of snapshot.statuses) {
		const row = plain(value, "corpus.status");
		guard(sql`EXISTS (SELECT 1 FROM content_status_definitions WHERE key=${String(row.key)} AND origin=${String(row.origin)} AND version=${Number(row.version)} AND active=${boolValue(row.active)} AND current_revision=${Number(row.currentRevision)} AND canonical_hash=${String(row.canonicalHash)} AND json(definition)=json(${JSON.stringify(row.definition)}) AND shadowed_db_version IS ${row.shadowedDbVersion === null ? null : Number(row.shadowedDbVersion)})`);
	}
	for (const value of snapshot.versions) {
		const row = plain(value, "corpus.version");
		guard(sql`EXISTS (SELECT 1 FROM content_definition_versions WHERE definition_kind=${String(row.kind)} AND definition_key=${String(row.key)} AND revision=${Number(row.revision)} AND canonical_hash=${String(row.canonicalHash)} AND json(definition)=json(${JSON.stringify(row.definition)}) AND origin=${String(row.origin)} AND created_at=${String(row.createdAt)})`);
	}
	for (const value of snapshot.terms) {
		const row = plain(value, "corpus.term");
		guard(sql`EXISTS (SELECT 1 FROM content_terms WHERE id=${String(row.id)} AND taxonomy=${String(row.taxonomy)} AND slug=${String(row.slug)} AND name=${String(row.name)} AND parent_id IS ${row.parentId === null ? null : String(row.parentId)} AND depth=${Number(row.depth)} AND created_at=${String(row.createdAt)} AND host_session_id IS ${row.hostSessionId === null ? null : String(row.hostSessionId)})`);
	}
	for (const value of snapshot.entries) {
		const row = plain(value, "corpus.entry");
		guard(sql`EXISTS (SELECT 1 FROM content_entries WHERE id=${String(row.id)} AND slug=${String(row.slug)} AND type=${String(row.type)} AND title=${String(row.title)} AND body=${String(row.body)} AND status=${String(row.status)} AND visibility=${String(row.visibility)} AND published_at IS ${row.publishedAt === null ? null : String(row.publishedAt)} AND author=${String(row.author)} AND created_at=${String(row.createdAt)} AND updated_at=${String(row.updatedAt)} AND parent_id IS ${row.parentId === null ? null : String(row.parentId)} AND menu_order=${Number(row.menuOrder)} AND template_key IS ${row.templateKey === null ? null : String(row.templateKey)} AND excerpt=${String(row.excerpt)} AND featured_media IS ${row.featuredMedia === null ? null : JSON.stringify(row.featuredMedia)} AND comment_status=${String(row.commentStatus)} AND ping_status=${String(row.pingStatus)} AND sticky=${boolValue(row.sticky)} AND format IS ${row.format === null ? null : String(row.format)} AND deleted_at IS ${row.deletedAt === null ? null : String(row.deletedAt)} AND last_edited_by=${String(row.lastEditedBy)} AND type_definition_revision=${Number(row.typeDefinitionRevision)} AND status_definition_revision=${Number(row.statusDefinitionRevision)} AND host_session_id IS ${row.hostSessionId === null ? null : String(row.hostSessionId)})`);
	}
	for (const value of snapshot.assignments) {
		const row = plain(value, "corpus.assignment");
		guard(sql`EXISTS (SELECT 1 FROM content_entry_terms WHERE entry_id=${String(row.entryId)} AND term_id=${String(row.termId)})`);
	}
	for (const value of snapshot.revisions) {
		const row = plain(value, "corpus.revision");
		guard(sql`EXISTS (SELECT 1 FROM content_revisions WHERE id=${String(row.id)} AND entry_id=${String(row.entryId)} AND title=${String(row.title)} AND body=${String(row.body)} AND slug=${String(row.slug)} AND type=${String(row.type)} AND json(term_ids)=json(${JSON.stringify(row.termIds)}) AND ${row.snapshot === null ? sql`snapshot IS NULL` : sql`json(snapshot)=json(${JSON.stringify(row.snapshot)})`} AND editor=${String(row.editor)} AND created_at=${String(row.createdAt)} AND host_session_id IS ${row.hostSessionId === null ? null : String(row.hostSessionId)})`);
	}
	return Object.freeze(items);
}
function prepareReverseMutation(items, db, session, raw, now) {
	const mutation = parseMutation(raw);
	if (mutation.action === "preserve" || mutation.action === "noop") return;
	const host = session.staging.hostSessionId;
	if (mutation.kind === "revision") {
		const id = uuid(mutation.record.destId, "revision.destId"), before = mutationBefore(mutation.record);
		if (before === null) {
			addChecked(items, db, sql`DELETE FROM content_revisions WHERE id=${id} AND host_session_id=${host}`);
			return;
		}
		const row = plain(before, "revision.before"), entryId = uuid(row.entryId, "revision.before.entryId"), termIds = JSON.stringify(array(row.termIds, "revision.before.termIds", 1e4)), snapshot = row.snapshot === null || row.snapshot === void 0 ? null : nullableJson(row.snapshot);
		addChecked(items, db, sql`UPDATE content_revisions SET entry_id=${entryId},title=${String(row.title)},body=${String(row.body)},slug=${String(row.slug)},type=${String(row.type)},term_ids=${termIds},snapshot=${snapshot},editor=${String(row.editor)},created_at=${String(row.createdAt)},host_session_id=${row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId)} WHERE id=${id} AND host_session_id=${host}`);
		return;
	}
	if (mutation.kind === "entry") {
		const id = uuid(mutation.record.destId, "entry.destId"), afterTerms = array(mutationAfter(mutation.record).termIds, "entry.after.termIds", 1e4), before = mutationBefore(mutation.record);
		addChecked(items, db, sql`DELETE FROM content_entry_terms WHERE entry_id=${id}`, afterTerms.length);
		if (before === null) {
			addChecked(items, db, sql`DELETE FROM content_entries WHERE id=${id} AND host_session_id=${host}`);
			return;
		}
		const row = plain(before, "entry.before"), featured = nullableJson(row.featuredMedia), previousHost = row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId);
		addChecked(items, db, sql`UPDATE content_entries SET slug=${String(row.slug)},type=${String(row.type)},title=${String(row.title)},body=${String(row.body)},status=${String(row.status)},visibility=${String(row.visibility)},published_at=${row.publishedAt === null ? null : String(row.publishedAt)},author=${String(row.author)},created_at=${String(row.createdAt)},updated_at=${String(row.updatedAt)},parent_id=${row.parentId === null ? null : String(row.parentId)},menu_order=${Number(row.menuOrder)},template_key=${row.templateKey === null ? null : String(row.templateKey)},excerpt=${String(row.excerpt)},featured_media=${featured},comment_status=${String(row.commentStatus)},ping_status=${String(row.pingStatus)},sticky=${boolValue(row.sticky)},format=${row.format === null ? null : String(row.format)},deleted_at=${row.deletedAt === null ? null : String(row.deletedAt)},last_edited_by=${String(row.lastEditedBy)},type_definition_revision=${Number(row.typeDefinitionRevision)},status_definition_revision=${Number(row.statusDefinitionRevision)},host_session_id=${previousHost} WHERE id=${id} AND host_session_id=${host}`);
		for (const termId of array(mutation.record.beforeTermIds ?? [], "entry.beforeTermIds", 1e4)) items.push(db.prepare(sql`INSERT INTO content_entry_terms (entry_id,term_id) VALUES (${id},${uuid(termId, "entry.beforeTermId")})`));
		return;
	}
	if (mutation.kind === "term") {
		const id = uuid(mutation.record.destId, "term.destId"), before = mutationBefore(mutation.record);
		if (before === null) {
			addChecked(items, db, sql`DELETE FROM content_terms WHERE id=${id} AND host_session_id=${host}`);
			return;
		}
		const row = plain(before, "term.before"), previousHost = row.hostSessionId === null || row.hostSessionId === void 0 ? null : String(row.hostSessionId);
		addChecked(items, db, sql`UPDATE content_terms SET taxonomy=${String(row.taxonomy)},slug=${String(row.slug)},name=${String(row.name)},parent_id=${row.parentId === null ? null : String(row.parentId)},depth=${Number(row.depth)},created_at=${String(row.createdAt)},host_session_id=${previousHost} WHERE id=${id} AND host_session_id=${host}`);
		return;
	}
	if (mutation.kind !== "type" && mutation.kind !== "status") return;
	const after = mutationAfter(mutation.record), definitionKey = key(after.key, "definition.key"), beforeBundle = plain(mutation.record.before, "definition.before"), beforeCurrent = beforeBundle.current, table = mutation.kind === "type" ? sql.raw("content_type_definitions") : sql.raw("content_status_definitions");
	if (beforeCurrent === null) addChecked(items, db, sql`DELETE FROM ${table} WHERE key=${definitionKey} AND host_session_id=${host}`);
	else {
		const before = plain(beforeCurrent, "definition.before.current"), previousHost = before.hostSessionId === null || before.hostSessionId === void 0 ? null : String(before.hostSessionId);
		addChecked(items, db, sql`UPDATE ${table} SET origin=${String(before.origin)},version=${Number(before.version)},active=${boolValue(before.active)},current_revision=${Number(before.currentRevision)},canonical_hash=${String(before.canonicalHash)},definition=${JSON.stringify(plain(before.definition, "definition.before.definition"))},shadowed_db_version=${before.shadowedDbVersion === null || before.shadowedDbVersion === void 0 ? null : Number(before.shadowedDbVersion)},host_session_id=${previousHost},created_at=${String(before.createdAt)},updated_at=${String(before.updatedAt)} WHERE key=${definitionKey} AND ${mutation.action === "shadow" ? sql`origin='code' AND shadowed_db_version=${Number(after.version)}` : sql`host_session_id=${host}`}`);
	}
	const retainedVersions = new Set(array(beforeBundle.versions, "definition.before.versions", 1e3).map((value) => Number(plain(value, "definition.before.version").revision))), insertedVersions = array(after.versions, "definition.versions", 1e3).filter((value) => !retainedVersions.has(Number(plain(value, "definition.version").revision))).length;
	addChecked(items, db, sql`DELETE FROM content_definition_versions WHERE definition_kind=${mutation.kind} AND definition_key=${definitionKey} AND host_session_id=${host}`, insertedVersions);
}
/** Prepare reversal of a published content import for one host-owned atomic D1 batch. */
async function prepareReversePublishedContentImportD1(db, authorization, principal, plan, command) {
	assertD1Scope(command.scope);
	assertD1Scope(plan.scope);
	await assertReversePlanHash(plan);
	if (command.expectedReversePlanHash !== plan.reversePlanHash || command.expectedPublishedCorpusVersion !== plan.publishedCorpusVersion) fail$1("conflict", "reverse command identity differs from reverse plan");
	const sessionId = boundedString(plan.sessionId, "sessionId", 128), operationId = boundedString(command.operationId, "operationId", 256), authInput = authorizationInputFromCommand(command);
	const policy = await assertImportAuthorized(db, authorization, principal, "reversePublished", authInput);
	if (policy !== command.expectedAuthorizationPolicyVersion) fail$1("authorization", "import scope is unavailable");
	const commandHash = await canonicalCommandHash("reversePublished", sessionId, principal, {
		plan,
		command
	}), scope = principalScope(principal), receiptEvidence = await readReceiptEvidence(db, operationId), receipt = receiptEvidence?.receipt;
	if (receipt && receiptEvidence) {
		if (receipt.kind !== "reversePublished" || receipt.commandHash !== commandHash || receipt.principalScope !== scope || receipt.session.id !== sessionId) fail$1("conflict", "operation id was already used for another import command");
		const persisted = required(await readJournalRow(db, sessionId), "integrity", "committed published reverse receipt has no import journal");
		const result = sessionFromRow(persisted);
		if (JSON.stringify(result) !== JSON.stringify(receipt.session)) fail$1("integrity", "committed published reverse receipt and import journal differ");
		const guard = prepareExactReplayGuard(db, operationId, "reversePublished", receiptEvidence, persisted, result);
		return Object.freeze({
			items: Object.freeze([guard]),
			result
		});
	}
	const row = required(await readJournalRow(db, sessionId), "authorization", "import session is unavailable");
	const session = sessionFromRow(row);
	if (!sameScope(session.scope, authInput.scope) || !sameAuthority(session.requiredAuthority, authInput.requiredAuthority) || session.authorizationPolicyVersion !== policy) fail$1("authorization", "import session is unavailable");
	if (session.state !== "published") fail$1("stale-journal", "import is not published");
	if (session.planHash !== plan.originalPlanHash || session.manifestHash !== plan.manifestHash || !sameScope(session.scope, plan.scope) || !sameAuthority(session.requiredAuthority, plan.requiredAuthority) || session.authorizationPolicyVersion !== plan.authorizationPolicyVersion) fail$1("conflict", "reverse plan differs from published session");
	if (session.planHash !== command.expectedPlanHash || session.manifestHash !== command.expectedManifestHash) fail$1("stale-plan", "command plan evidence differs from session");
	if (session.journalVersion !== command.expectedJournalVersion || session.nextSection !== command.expectedSection || session.nextOffset !== command.expectedOffset) fail$1("stale-journal", "journal coordinate changed");
	if (session.publishedCorpusVersion !== plan.publishedCorpusVersion) fail$1("stale-plan", "published corpus high-water differs from reverse plan");
	const publishedSnapshot = await readDestinationCorpus(db, session.scope);
	if (await hashDestinationCorpus(publishedSnapshot) !== plan.publishedCorpusVersion) fail$1("stale-plan", "published corpus changed after reverse planning");
	const ledger = d1Ledger(row, session);
	if (await beforeImageHash(ledger) !== plan.beforeImageHash) fail$1("stale-plan", "retained before-image ledger changed");
	const restoredSnapshot = predictPrePublishSnapshot(publishedSnapshot, ledger);
	if (await hashDestinationCorpus(restoredSnapshot) !== session.destinationCorpusVersion) fail$1("integrity", "retained before-images do not restore the original corpus");
	const payload = rowJson(row.payload), storedPayload = JSON.stringify(schemaValue(payload)), reversing = Object.freeze({
		...session,
		state: "reversing",
		journalVersion: String(Number(session.journalVersion) + 1),
		nextSection: null,
		nextOffset: 0
	}), result = Object.freeze({
		...reversing,
		state: "reversed",
		journalVersion: String(Number(session.journalVersion) + 2)
	});
	const reversingPayload = JSON.stringify(schemaValue({
		...payload,
		session: reversing
	})), resultPayload = JSON.stringify(schemaValue({
		...payload,
		session: result
	})), pendingPayload = JSON.stringify(schemaValue({
		kind: "reversePublished",
		commandHash,
		principalScope: scope
	})), committedPayload = JSON.stringify(schemaValue({
		kind: "reversePublished",
		commandHash,
		principalScope: scope,
		session: result
	})), now = (/* @__PURE__ */ new Date()).toISOString();
	const mutationStatements = ledger.reduce((count, { staged }) => count + staged.mutations.reduce((sum, mutation) => sum + reverseMutationStatementCount(mutation), 0), 0);
	assertD1StatementBudget("reversePublished", 9 + exactCorpusGuardCount(publishedSnapshot) + mutationStatements + exactCorpusGuardCount(restoredSnapshot));
	const items = [];
	addChecked(items, db, sql`INSERT INTO content_lifecycle_journal (id,operation_id,kind,state,payload,created_at,updated_at) VALUES (${crypto.randomUUID()},${operationId},'content:import:reversePublished','pending',${pendingPayload},${now},${now})`);
	items.push(...d1ExactCorpusGuards(db, publishedSnapshot));
	items.push(db.prepare(sql`SELECT CASE WHEN EXISTS (SELECT 1 FROM content_import_journal WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state='published' AND version=${Number(session.journalVersion)} AND high_water_mark=${plan.publishedCorpusVersion} AND next_section IS NULL AND next_offset=0 AND payload=${storedPayload}) THEN 1 ELSE json_extract('not-json','$') END AS ok`));
	addChecked(items, db, sql`UPDATE content_import_journal SET state='reversing',version=version+1,payload=${reversingPayload},updated_at=${now} WHERE import_id=${sessionId} AND host_session_id=${session.staging.hostSessionId} AND plan_hash=${session.planHash} AND manifest_hash=${session.manifestHash} AND state='published' AND version=${Number(session.journalVersion)} AND high_water_mark=${plan.publishedCorpusVersion} AND next_section IS NULL AND next_offset=0 AND payload=${storedPayload}`);
	for (const { staged } of [...ledger].reverse()) for (const mutation of [...staged.mutations].reverse()) prepareReverseMutation(items, db, session, mutation, now);
	items.push(...d1ExactCorpusGuards(db, restoredSnapshot));
	addChecked(items, db, sql`UPDATE content_import_journal SET state='reversed',version=version+1,payload=${resultPayload},updated_at=${now} WHERE import_id=${sessionId} AND state='reversing' AND version=${Number(session.journalVersion) + 1} AND high_water_mark=${plan.publishedCorpusVersion} AND payload=${reversingPayload}`);
	addChecked(items, db, sql`UPDATE content_lifecycle_journal SET state='committed',payload=${committedPayload},updated_at=${now} WHERE operation_id=${operationId} AND kind='content:import:reversePublished' AND state='pending' AND payload=${pendingPayload}`);
	return Object.freeze({
		items: Object.freeze(items),
		result,
		affectedCounts: plan.affectedCounts
	});
}
async function reversePublishedContentImport(tx, journal, authorization, effects, principal, plan, command) {
	assertActiveContentTransaction(tx);
	await assertReversePlanHash(plan);
	if (command.expectedReversePlanHash !== plan.reversePlanHash || command.expectedPublishedCorpusVersion !== plan.publishedCorpusVersion) fail$1("conflict", "reverse command identity differs from reverse plan");
	const commandHash = await canonicalCommandHash("reversePublished", plan.sessionId, principal, {
		plan,
		command
	}), claim = await journal.claimCommand(tx, plan.sessionId, principal, "reversePublished", command, commandHash, authorization);
	if (claim.kind === "conflict") throw new ContentImportError("conflict", "operation id was already used for another import command");
	if (claim.kind === "replay") return claim.session;
	const session = claim.session;
	if (!session) throw new ContentImportError("integrity", "reverse claim did not return session");
	if (session.state !== "published") fail$1("stale-journal", "import is not published");
	if (session.planHash !== plan.originalPlanHash || session.manifestHash !== plan.manifestHash || !sameScope(session.scope, plan.scope) || !sameAuthority(session.requiredAuthority, plan.requiredAuthority) || session.authorizationPolicyVersion !== plan.authorizationPolicyVersion) fail$1("conflict", "reverse plan differs from published session");
	if (await destinationCorpusVersion(tx, session.scope) !== plan.publishedCorpusVersion) fail$1("stale-plan", "published corpus changed after reverse planning");
	const ledger = await readValidatedLedger(tx, journal, session);
	if (await beforeImageHash(ledger) !== plan.beforeImageHash) fail$1("stale-plan", "retained before-image ledger changed");
	await journal.transition(session.id, "published", "reversing", tx);
	for (const { staged } of [...ledger].reverse()) for (const mutation of [...staged.mutations].reverse()) await restoreStagedMutation(tx, session, mutation);
	if (await destinationCorpusVersion(tx, session.scope) !== session.destinationCorpusVersion) fail$1("integrity", "published reverse did not restore original corpus");
	const result = Object.freeze({
		...session,
		state: "reversed",
		nextSection: null,
		nextOffset: 0
	});
	await emitLifecycleEffects(tx, effects, lifecycleEvent("reversed", command.operationId, result, principal, plan.affectedCounts));
	await journal.completeCommand(tx, session.id, "reversePublished", command, commandHash, result);
	return result;
}
//#endregion
//#region src/export.ts
var ContentExportError = class extends Error {
	code;
	detail;
	field;
	name = "ContentExportError";
	constructor(code, detail, field) {
		super(`content export ${code}: ${detail}${field ? ` (${field})` : ""}`);
		this.code = code;
		this.detail = detail;
		this.field = field;
	}
};
function isContentExportError(error) {
	return error instanceof ContentExportError;
}
const fail = (code, detail, field) => {
	throw new ContentExportError(code, detail, field);
};
const TYPE_KEY = /^[a-z][a-z0-9_-]*$/;
function bounded(value, field, max) {
	if (typeof value !== "string") fail("validation", "must be a string", field);
	const normalized = value.trim();
	if (!normalized || normalized.length > max) fail("validation", `must contain 1..${max} characters`, field);
	return normalized;
}
function typeKey(value, field) {
	const normalized = bounded(value, field, 128);
	if (!TYPE_KEY.test(normalized)) fail("validation", "must be a normalized content type key", field);
	return normalized;
}
function positive(value, field) {
	const number = Number(value);
	if (!Number.isSafeInteger(number) || number < 1) fail("integrity", "must be a positive safe integer", field);
	return number;
}
function bool(value) {
	return value === true || value === 1 || value === "1";
}
function iso(value, field) {
	const date = value instanceof Date ? value : new Date(String(value));
	if (Number.isNaN(date.getTime())) fail("integrity", "must be a timestamp", field);
	return date.toISOString();
}
function parseJson(value, field) {
	if (typeof value !== "string") return value;
	try {
		return JSON.parse(value);
	} catch {
		fail("integrity", "contains invalid JSON", field);
	}
}
const FORBIDDEN = /(?:password|credential)/i;
function storage(value, field = "value", depth = 0) {
	if (depth > 32) fail("limit", "exceeds maximum nested depth", field);
	if (value === null || typeof value === "string" || typeof value === "boolean") return value;
	if (value instanceof Date) return value.toISOString();
	if (typeof value === "number") {
		if (!Number.isFinite(value)) fail("integrity", "contains non-finite number", field);
		return value;
	}
	if (Array.isArray(value)) return Object.freeze(value.map((item, index) => storage(item, `${field}.${index}`, depth + 1)));
	if (typeof value === "object") {
		const output = {};
		for (const [key, child] of Object.entries(value)) {
			if (FORBIDDEN.test(key)) fail("integrity", "contains forbidden password/credential material", `${field}.${key}`);
			if (child !== void 0) output[key] = storage(child, `${field}.${key}`, depth + 1);
		}
		return Object.freeze(output);
	}
	return fail("integrity", "contains unsupported value", field);
}
function rowsOf(value) {
	if (Array.isArray(value)) return value;
	const rows = value?.rows;
	if (Array.isArray(rows)) return rows;
	return fail("integrity", "database adapter returned invalid rows");
}
async function execute(tx, query) {
	return rowsOf(await tx.execute(query));
}
function sqlList(values) {
	return sql.join(values.map((value) => sql`${value}`), sql`, `);
}
function validateLimits(limits) {
	for (const [key, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value < 1) fail("validation", "must be a positive safe integer", `limits.${key}`);
	if (limits.batchSize > 1e3) fail("validation", "batchSize must be <= 1000", "limits.batchSize");
}
function origin(value, field) {
	if (value === "code" || value === "db" || value === "import") return value;
	return fail("integrity", "invalid definition origin", field);
}
function resolvedType(row, version) {
	const definition = normalizeContentTypeDefinition(version ? parseJson(version.definition, "type.version.definition") : parseJson(row.definition, "type.definition"));
	return Object.freeze({
		...definition,
		active: bool(row.active),
		origin: (version ? origin(version.origin, "type.version.origin") : origin(row.origin, "type.origin")) === "code" ? "code" : "db",
		version: positive(row.version, "type.version"),
		revision: version ? positive(version.revision, "type.revision") : positive(row.currentRevision, "type.currentRevision"),
		canonicalHash: String(version?.canonicalHash ?? row.canonicalHash),
		...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: positive(row.shadowedDbVersion, "type.shadowedDbVersion") }
	});
}
function resolvedStatus(row, version) {
	const definition = normalizeContentStatusDefinition(version ? parseJson(version.definition, "status.version.definition") : parseJson(row.definition, "status.definition"));
	return Object.freeze({
		...definition,
		active: bool(row.active),
		origin: (version ? origin(version.origin, "status.version.origin") : origin(row.origin, "status.origin")) === "code" ? "code" : "db",
		version: positive(row.version, "status.version"),
		revision: version ? positive(version.revision, "status.revision") : positive(row.currentRevision, "status.currentRevision"),
		canonicalHash: String(version?.canonicalHash ?? row.canonicalHash),
		...row.shadowedDbVersion === null ? {} : { shadowedDbVersion: positive(row.shadowedDbVersion, "status.shadowedDbVersion") }
	});
}
function definitionBundle(row, versions) {
	const currentDefinition = parseJson(row.definition, "definition.current"), items = [...versions].sort((a, b) => Number(a.revision) - Number(b.revision)).map((version) => storage({
		revision: positive(version.revision, "definition.version.revision"),
		canonicalHash: String(version.canonicalHash),
		definition: parseJson(version.definition, "definition.version.definition"),
		origin: origin(version.origin, "definition.version.origin"),
		createdAt: iso(version.createdAt, "definition.version.createdAt")
	}, "definition.version"));
	return storage({
		key: String(row.key),
		origin: origin(row.origin, "definition.origin"),
		version: positive(row.version, "definition.version"),
		revision: positive(row.currentRevision, "definition.currentRevision"),
		active: bool(row.active),
		canonicalHash: String(row.canonicalHash),
		definition: currentDefinition,
		versions: items
	}, "definition");
}
function stringArray(value, field) {
	const parsed = parseJson(value, field);
	if (!Array.isArray(parsed)) throw new ContentExportError("integrity", "must be an array", field);
	return Object.freeze(parsed.map((item, index) => bounded(item, `${field}.${index}`, 128)));
}
async function exportContent(tx, principal, input, deps) {
	assertActiveContentTransaction(tx);
	validateLimits(input.limits);
	if (typeof input.includeRevisions !== "boolean") fail("validation", "must be boolean", "includeRevisions");
	const requested = [...new Set(input.typeKeys.map((value, index) => typeKey(value, `typeKeys.${index}`)))].sort();
	if (requested.length === 0 || requested.length > input.limits.maxDefinitions) fail("limit", "typeKeys exceed export definition limit", "typeKeys");
	const typeRows = await execute(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion" FROM content_type_definitions WHERE key IN (${sqlList(requested)}) ORDER BY key`);
	if (typeRows.length !== requested.length) {
		const found = new Set(typeRows.map((row) => row.key)), missing = requested.find((key) => !found.has(key));
		fail("not-found", `content type ${missing ?? "unknown"} is unavailable`, "typeKeys");
	}
	const currentTypes = new Map(typeRows.map((row) => [row.key, resolvedType(row)])), overridden = [];
	for (const key of requested) {
		const type = currentTypes.get(key);
		authorizeContentAction({
			principal,
			type,
			operation: "read"
		});
		if (type.canExport) continue;
		if (!input.overrideCanExport) fail("forbidden", `content type ${key} does not allow export`);
		authorizeContentAction({
			principal,
			type,
			operation: "manageType"
		});
		overridden.push(key);
	}
	let override;
	if (overridden.length) {
		const reason = bounded(input.overrideCanExport.reason, "overrideCanExport.reason", 512), operationId = bounded(input.overrideCanExport.operationId, "overrideCanExport.operationId", 256);
		override = Object.freeze({
			reason,
			operationId
		});
	}
	const entryRows = await execute(tx, sql`SELECT id,slug,type,title,body,status,visibility,published_at AS "publishedAt",author,created_at AS "createdAt",updated_at AS "updatedAt",parent_id AS "parentId",menu_order AS "menuOrder",template_key AS "templateKey",excerpt,featured_media AS "featuredMedia",comment_status AS "commentStatus",ping_status AS "pingStatus",sticky,format,deleted_at AS "deletedAt",last_edited_by AS "lastEditedBy",type_definition_revision AS "typeDefinitionRevision",status_definition_revision AS "statusDefinitionRevision",EXISTS (SELECT 1 FROM content_password_credentials c WHERE c.entry_id=content_entries.id) AS "passwordProtected" FROM content_entries WHERE type IN (${sqlList(requested)}) ORDER BY id`);
	if (entryRows.length > input.limits.maxEntries) fail("limit", "entries exceed export maxEntries");
	const typeVersionRows = await execute(tx, sql`SELECT definition_kind AS kind,definition_key AS key,revision,canonical_hash AS "canonicalHash",definition,origin,created_at AS "createdAt" FROM content_definition_versions WHERE definition_kind='type' AND definition_key IN (${sqlList(requested)}) ORDER BY definition_key,revision`);
	const typeVersionMap = new Map(typeVersionRows.map((row) => [`type:${row.key}:${Number(row.revision)}`, row]));
	for (const row of typeRows) if (!typeVersionMap.has(`type:${row.key}:${Number(row.currentRevision)}`)) fail("integrity", `current type ${row.key} has no immutable revision`);
	const referencedTypeDefinitions = [...typeRows.map((row) => normalizeContentTypeDefinition(parseJson(row.definition, "type.definition"))), ...typeVersionRows.map((row) => normalizeContentTypeDefinition(parseJson(row.definition, "type.version.definition")))];
	const explicitStatusKeys = referencedTypeDefinitions.flatMap((definition) => definition.statusKeys ?? []), statusKeys = [.../* @__PURE__ */ new Set([...explicitStatusKeys, ...entryRows.map((row) => row.status)])].sort();
	const taxonomyKeys = [...new Set(referencedTypeDefinitions.flatMap((definition) => definition.taxonomies))].sort();
	const statusRows = statusKeys.length ? await execute(tx, sql`SELECT key,origin,version,active,current_revision AS "currentRevision",canonical_hash AS "canonicalHash",definition,shadowed_db_version AS "shadowedDbVersion" FROM content_status_definitions WHERE key IN (${sqlList(statusKeys)}) ORDER BY key`) : [];
	if (statusRows.length !== statusKeys.length) fail("integrity", "selected corpus references an unavailable status");
	const currentStatuses = new Map(statusRows.map((row) => [row.key, row]));
	const statusVersionRows = statusKeys.length ? await execute(tx, sql`SELECT definition_kind AS kind,definition_key AS key,revision,canonical_hash AS "canonicalHash",definition,origin,created_at AS "createdAt" FROM content_definition_versions WHERE definition_kind='status' AND definition_key IN (${sqlList(statusKeys)}) ORDER BY definition_key,revision`) : [];
	const versionRows = Object.freeze([...typeVersionRows, ...statusVersionRows]), versionMap = new Map(versionRows.map((row) => [`${row.kind}:${row.key}:${Number(row.revision)}`, row]));
	for (const row of statusRows) if (!versionMap.has(`status:${row.key}:${Number(row.currentRevision)}`)) fail("integrity", `current status ${row.key} has no immutable revision`);
	for (const entry of entryRows) {
		const typeRow = typeRows.find((row) => row.key === entry.type), statusRow = currentStatuses.get(entry.status), typeVersion = versionMap.get(`type:${entry.type}:${Number(entry.typeDefinitionRevision)}`), statusVersion = versionMap.get(`status:${entry.status}:${Number(entry.statusDefinitionRevision)}`);
		if (!typeRow || !statusRow || !typeVersion || !statusVersion) throw new ContentExportError("integrity", `entry ${entry.id} has unresolved definition pins`);
		const type = resolvedType(typeRow, typeVersion), status = resolvedStatus(statusRow, statusVersion);
		authorizeContentAction({
			principal,
			type,
			operation: bool(entry.passwordProtected) ? "readProtected" : "read",
			entry: {
				id: entry.id,
				author: entry.author,
				status: entry.status,
				visibility: entry.visibility
			},
			status
		});
	}
	const entryIds = entryRows.map((row) => row.id), assignments = entryIds.length ? await execute(tx, sql`SELECT entry_id AS "entryId",term_id AS "termId" FROM content_entry_terms WHERE entry_id IN (${sqlList(entryIds)}) ORDER BY entry_id,term_id`) : [];
	const assignmentIds = [...new Set(assignments.map((row) => row.termId))];
	const termRows = taxonomyKeys.length ? await execute(tx, sql`SELECT id,taxonomy,slug,name,parent_id AS "parentId",depth,created_at AS "createdAt" FROM content_terms WHERE taxonomy IN (${sqlList(taxonomyKeys)}) ORDER BY taxonomy,depth,id`) : [];
	if (termRows.length > Math.max(input.limits.maxEntries * 10, input.limits.maxDefinitions)) fail("limit", "taxonomy terms exceed export limit");
	const termIds = new Set(termRows.map((row) => row.id));
	for (const id of assignmentIds) if (!termIds.has(id)) fail("integrity", `entry assignment references term ${id} outside selected type taxonomies`);
	const terms = Object.freeze(termRows.map((row) => storage({
		id: row.id,
		taxonomy: row.taxonomy,
		slug: row.slug,
		name: row.name,
		parentId: row.parentId,
		depth: Number(row.depth),
		createdAt: iso(row.createdAt, "term.createdAt")
	}, "term")));
	const taxonomies = Object.freeze(taxonomyKeys.map((key) => storage({
		key,
		version: "1"
	}, "taxonomy")));
	const assignmentMap = /* @__PURE__ */ new Map();
	for (const row of assignments) {
		const values = assignmentMap.get(row.entryId) ?? [];
		values.push(row.termId);
		assignmentMap.set(row.entryId, values);
	}
	const entries = Object.freeze(entryRows.map((row) => storage({
		id: row.id,
		slug: row.slug,
		type: row.type,
		title: row.title,
		body: row.body,
		status: row.status,
		visibility: row.visibility,
		publishedAt: row.publishedAt === null ? null : iso(row.publishedAt, "entry.publishedAt"),
		author: row.author,
		createdAt: iso(row.createdAt, "entry.createdAt"),
		updatedAt: iso(row.updatedAt, "entry.updatedAt"),
		parentId: row.parentId,
		menuOrder: Number(row.menuOrder),
		templateKey: row.templateKey,
		excerpt: row.excerpt,
		featuredMedia: row.featuredMedia === null ? null : storage(parseJson(row.featuredMedia, "entry.featuredMedia"), "entry.featuredMedia"),
		commentStatus: row.commentStatus,
		pingStatus: row.pingStatus,
		sticky: bool(row.sticky),
		format: row.format,
		deletedAt: row.deletedAt === null ? null : iso(row.deletedAt, "entry.deletedAt"),
		lastEditedBy: row.lastEditedBy,
		typeDefinitionRevision: Number(row.typeDefinitionRevision),
		statusDefinitionRevision: Number(row.statusDefinitionRevision),
		termIds: Object.freeze(assignmentMap.get(row.id) ?? [])
	}, "entry")));
	const revisions = [];
	if (input.includeRevisions && entryIds.length) {
		const rows = await execute(tx, sql`SELECT id,entry_id AS "entryId",seq,title,body,slug,type,term_ids AS "termIds",snapshot,editor,created_at AS "createdAt" FROM content_revisions WHERE entry_id IN (${sqlList(entryIds)}) ORDER BY entry_id,seq,id`);
		if (rows.length > input.limits.maxRevisions) fail("limit", "revisions exceed export maxRevisions");
		for (const row of rows) revisions.push(storage({
			id: row.id,
			entryId: row.entryId,
			seq: Number(row.seq),
			title: row.title,
			body: row.body,
			slug: row.slug,
			type: row.type,
			termIds: stringArray(row.termIds, "revision.termIds"),
			...row.snapshot === null ? {} : { snapshot: storage(parseJson(row.snapshot, "revision.snapshot"), "revision.snapshot") },
			editor: row.editor,
			createdAt: iso(row.createdAt, "revision.createdAt")
		}, "revision"));
	}
	const types = Object.freeze(typeRows.map((row) => definitionBundle(row, versionRows.filter((version) => version.kind === "type" && version.key === row.key))));
	const statuses = Object.freeze(statusRows.map((row) => definitionBundle(row, versionRows.filter((version) => version.kind === "status" && version.key === row.key))));
	if (types.length + statuses.length + taxonomies.length > input.limits.maxDefinitions) fail("limit", "export definitions exceed maxDefinitions");
	const bundle = Object.freeze({
		version: 1,
		generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
		types,
		statuses,
		taxonomies,
		terms,
		entries,
		revisions: Object.freeze(revisions)
	});
	if (new TextEncoder().encode(JSON.stringify(bundle)).byteLength > input.limits.maxBytes) fail("limit", "export bundle exceeds maxBytes");
	if (override) await deps.audit.recordOverride(tx, principal, Object.freeze(overridden.sort()), override);
	await canonicalContentMigrationHash(bundle);
	return bundle;
}
//#endregion
export { CONTENT_IMPORT_D1_STATEMENT_BUDGET, CONTENT_MODEL_MIGRATION_ID, ContentAuthorizationError, ContentAuthzError, ContentConflictError, ContentDefinitionError, ContentExportError, ContentHeadlessError, ContentHierarchyError, ContentImportError, ContentLifecycleError, ContentNotFoundError, ContentQueryError, ContentRouteError, ContentSanitizationError, ContentStoreContractError, ContentValidationError, applyContentCodeReconciliation, applyContentStatusChange, applyContentTypeChange, applyPermanentContentDelete, assertCanManageTaxonomy, assertCanModify, assertCanPublish, assertContentTransactionIdentity, assertSanitize, authorizeContentAction, buildContentQueryPredicate, canonicalContentMigrationHash, changeContentPassword, compileContentRoutes, contentBackupDestinationHash, contentDefinitionVersions, contentEntries, contentEntriesBaseMigrationSql, contentImportJournal, contentLifecycleJournal, contentMigrationCheckpoints, contentMigrationWriteJournal, contentModelForwardMigrationSql, contentModelReverseMigrationSql, contentRevisionsMigrationSql, contentSchema, contentSearchMigrationSql, contentStatusDefinitions, contentTaxonomyMigrationSql, contentTombstones, contentTypeDefinitions, contentVisibilityMigrationSql, count, countByStatus, createContentAutosave, createContentEntry, createContentHeadlessRuntime, createContentPostgresSearchAdapter, createContentSearchProvider, createContentStatus, createContentType, createDbContentImportJournal, decodeContentListQuery, defineContentStatus, defineContentType, exportContent, generateContentSchema, getById, getBySlug, getContentAutosave, getContentRevision, isContentDefinitionError, isContentExportError, isContentImportError, isContentStoreContractError, list, listContentRevisions, listPage, normalizeContentInput, normalizeContentStatusDefinition, normalizeContentTypeDefinition, parseContentImport, planContentImport, planContentModelMigration, planPublishedContentImportReverse, planPublishedContentImportReverseD1, predictPublishedContentImportCorpus, prepareContentRevisionD1, preparePermanentContentDelete, preparePublishContentImportD1, prepareResumeContentImportD1, prepareReversePublishedContentImportD1, prepareRollbackContentImportD1, prepareStagedContentImportGuardD1, prepareStartContentImportD1, previewContentCodeReconciliation, previewContentStatusChange, previewContentTypeChange, promoteScheduled, publish, publishContentImport, put, readFlatContentEntries, readProtectedContent, remove, resolveContentRoute, resolveContentStatus, resolveContentStatuses, resolveContentTemplate, resolveContentType, resolveContentTypes, resolveRequiredContentCapabilities, restore, restoreContent, restoreContentAutosave, restoreContentBackup, restoreContentRevision, resumeContentImport, reversePublishedContentImport, rollbackContentImport, schedule, setVisibility, snapshotFlatContentCorpus, startContentImport, transitionContent, trash, trashContent, unpublish, updateContentEntry, validateContentParent, verifyContentPassword };
