{"version":3,"file":"_id_.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/content/[collection]/[id].ts"],"sourcesContent":["/**\n * Single content item endpoints - injected by EmDash integration\n *\n * GET    /_emdash/api/content/{collection}/{id} - Get content\n * PUT    /_emdash/api/content/{collection}/{id} - Update content\n * DELETE /_emdash/api/content/{collection}/{id} - Delete content\n */\n\nimport { hasPermission } from \"@premium-cms/auth\";\nimport type { APIRoute } from \"astro\";\n\nimport { requirePerm, requireOwnerPerm } from \"#api/authorize.js\";\nimport { apiError, mapErrorStatus, unwrapResult } from \"#api/error.js\";\nimport { parseBody, isParseError } from \"#api/parse.js\";\nimport { contentUpdateBody } from \"#api/schemas.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ params, url, locals }) => {\n\tconst { emdash, user } = locals;\n\tif (!emdash?.handleContentGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\tconst denied = requirePerm(user, \"content:read\");\n\tif (denied) return denied;\n\tconst collection = params.collection!;\n\tconst id = params.id!;\n\tconst locale = url.searchParams.get(\"locale\") || undefined;\n\n\tconst result = await emdash.handleContentGet(collection, id, locale);\n\n\t// Hide non-published items from users without content:read_drafts. Return\n\t// 404 (not 403) so subscribers can't enumerate draft IDs by status code.\n\tif (result.success && !hasPermission(user, \"content:read_drafts\")) {\n\t\tconst data =\n\t\t\tresult.data && typeof result.data === \"object\"\n\t\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- handler returns unknown data; narrowed by typeof check\n\t\t\t\t\t(result.data as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst item =\n\t\t\tdata?.item && typeof data.item === \"object\"\n\t\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed by typeof check\n\t\t\t\t\t(data.item as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\tconst status = typeof item?.status === \"string\" ? item.status : null;\n\t\tif (status !== \"published\") {\n\t\t\treturn apiError(\"NOT_FOUND\", `Content item not found: ${id}`, 404);\n\t\t}\n\n\t\t// Strip draft hydration data from response for users without read_drafts.\n\t\t// handleContentGet overlays draft revision data onto item.data and exposes\n\t\t// the published values in item.liveData. Without this, subscribers see\n\t\t// unpublished edits in the data field.\n\t\tif (item) {\n\t\t\tif (item.liveData && typeof item.liveData === \"object\") {\n\t\t\t\titem.data = item.liveData;\n\t\t\t}\n\t\t\tdelete item.liveData;\n\t\t\tdelete item.draftRevisionId;\n\t\t}\n\t}\n\n\treturn unwrapResult(result);\n};\n\nexport const PUT: APIRoute = async ({ params, request, locals, cache }) => {\n\tconst { emdash, user } = locals;\n\tconst collection = params.collection!;\n\tconst id = params.id!;\n\tconst locale = new URL(request.url).searchParams.get(\"locale\") || undefined;\n\tconst body = await parseBody(request, contentUpdateBody);\n\tif (isParseError(body)) return body;\n\n\tif (!emdash?.handleContentUpdate || !emdash?.handleContentGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\t// Fetch item to check ownership\n\tconst existing = await emdash.handleContentGet(collection, id, locale);\n\tif (!existing.success) {\n\t\treturn apiError(\n\t\t\texisting.error?.code ?? \"UNKNOWN_ERROR\",\n\t\t\texisting.error?.message ?? \"Unknown error\",\n\t\t\tmapErrorStatus(existing.error?.code),\n\t\t);\n\t}\n\n\tconst existingData =\n\t\texisting.data && typeof existing.data === \"object\"\n\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- handler returns unknown data; narrowed by typeof check above\n\t\t\t\t(existing.data as Record<string, unknown>)\n\t\t\t: undefined;\n\t// Handler returns { item, _rev } — extract the item for ownership and ID resolution\n\tconst existingItem =\n\t\texistingData?.item && typeof existingData.item === \"object\"\n\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed by typeof check above\n\t\t\t\t(existingData.item as Record<string, unknown>)\n\t\t\t: existingData;\n\tconst authorId = typeof existingItem?.authorId === \"string\" ? existingItem.authorId : \"\";\n\tconst editDenied = requireOwnerPerm(user, authorId, \"content:edit_own\", \"content:edit_any\");\n\tif (editDenied) return editDenied;\n\n\t// Only EDITOR+ can write publishedAt directly — incl. clearing to null.\n\tif (body.publishedAt !== undefined && !hasPermission(user, \"content:publish_any\")) {\n\t\treturn apiError(\n\t\t\t\"FORBIDDEN\",\n\t\t\t\"Writing publishedAt requires content:publish_any permission\",\n\t\t\t403,\n\t\t);\n\t}\n\n\t// Use the resolved ID (handles slug → ID resolution)\n\tconst resolvedId = typeof existingItem?.id === \"string\" ? existingItem.id : id;\n\n\t// Only allow authorId changes if user has content:edit_any permission (editor+)\n\tconst canChangeAuthor =\n\t\tbody.authorId !== undefined && user && hasPermission(user, \"content:edit_any\");\n\tconst updateBody = canChangeAuthor ? body : { ...body, authorId: undefined };\n\n\t// Pass _rev through for optimistic concurrency validation\n\tconst result = await emdash.handleContentUpdate(collection, resolvedId, {\n\t\t...updateBody,\n\t\tlocale,\n\t\t_rev: body._rev,\n\t});\n\n\tif (!result.success) return unwrapResult(result);\n\n\tif (cache?.enabled && result.liveContentChanged !== false) {\n\t\tawait cache.invalidate({ tags: [collection, resolvedId] });\n\t}\n\n\treturn unwrapResult(result);\n};\n\nexport const DELETE: APIRoute = async ({ params, locals, url, cache }) => {\n\tconst { emdash, user } = locals;\n\tconst collection = params.collection!;\n\tconst id = params.id!;\n\n\tif (!emdash?.handleContentDelete || !emdash?.handleContentGet) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tconst locale = url.searchParams.get(\"locale\") || undefined;\n\n\t// Fetch item to check ownership\n\tconst existing = await emdash.handleContentGet(collection, id, locale);\n\tif (!existing.success) {\n\t\treturn apiError(\n\t\t\texisting.error?.code ?? \"UNKNOWN_ERROR\",\n\t\t\texisting.error?.message ?? \"Unknown error\",\n\t\t\tmapErrorStatus(existing.error?.code),\n\t\t);\n\t}\n\n\tconst deleteData =\n\t\texisting.data && typeof existing.data === \"object\"\n\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- handler returns unknown data; narrowed by typeof check above\n\t\t\t\t(existing.data as Record<string, unknown>)\n\t\t\t: undefined;\n\t// Handler returns { item, _rev } — extract the item for ownership and ID resolution\n\tconst deleteItem =\n\t\tdeleteData?.item && typeof deleteData.item === \"object\"\n\t\t\t? // eslint-disable-next-line typescript/no-unsafe-type-assertion -- narrowed by typeof check above\n\t\t\t\t(deleteData.item as Record<string, unknown>)\n\t\t\t: deleteData;\n\tconst authorId = typeof deleteItem?.authorId === \"string\" ? deleteItem.authorId : \"\";\n\tconst deleteDenied = requireOwnerPerm(user, authorId, \"content:delete_own\", \"content:delete_any\");\n\tif (deleteDenied) return deleteDenied;\n\n\t// Use the resolved ID (handles slug → ID resolution)\n\tconst resolvedId = typeof deleteItem?.id === \"string\" ? deleteItem.id : id;\n\n\tconst result = await emdash.handleContentDelete(collection, resolvedId);\n\n\tif (!result.success) return unwrapResult(result);\n\n\tif (cache?.enabled) await cache.invalidate({ tags: [collection, resolvedId] });\n\n\treturn unwrapResult(result);\n};\n"],"mappings":"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"}