@stacksjs/cms 0.71.2 → 0.71.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/authors/destroy.js +1 -0
  2. package/dist/authors/fetch.js +1 -0
  3. package/dist/authors/index.js +1 -0
  4. package/dist/authors/store.js +1 -0
  5. package/dist/authors/update.js +1 -0
  6. package/dist/blocks/defaults.js +1 -0
  7. package/dist/blocks/registry.js +1 -0
  8. package/dist/blocks/types.js +0 -0
  9. package/dist/build.d.ts +6 -0
  10. package/dist/build.js +876 -0
  11. package/dist/categorizables/destroy.js +1 -0
  12. package/dist/categorizables/fetch.js +1 -0
  13. package/dist/categorizables/index.js +1 -0
  14. package/dist/categorizables/store.js +1 -0
  15. package/dist/categorizables/update.js +1 -0
  16. package/dist/commentables/destroy.js +1 -0
  17. package/dist/commentables/fetch.js +1 -0
  18. package/dist/commentables/index.js +1 -0
  19. package/dist/commentables/store.js +1 -0
  20. package/dist/commentables/update.js +1 -0
  21. package/dist/database.js +1 -0
  22. package/dist/index.js +1 -3
  23. package/dist/menus/index.js +1 -0
  24. package/dist/pages/destroy.js +1 -0
  25. package/dist/pages/document.js +1 -0
  26. package/dist/pages/fetch.js +1 -0
  27. package/dist/pages/index.js +1 -0
  28. package/dist/pages/store.js +1 -0
  29. package/dist/pages/update.js +1 -0
  30. package/dist/posts/destroy.js +1 -0
  31. package/dist/posts/fetch.js +1 -0
  32. package/dist/posts/index.js +1 -0
  33. package/dist/posts/store.js +1 -0
  34. package/dist/posts/update.js +1 -0
  35. package/dist/public/fallback.js +1 -0
  36. package/dist/public/render.js +2 -0
  37. package/dist/public/resolve.js +1 -0
  38. package/dist/public/sanitize.js +1 -0
  39. package/dist/publish/index.js +1 -0
  40. package/dist/redirects/index.js +1 -0
  41. package/dist/results.js +1 -0
  42. package/dist/revisions/index.js +1 -0
  43. package/dist/taggables/destroy.js +1 -0
  44. package/dist/taggables/fetch.js +1 -0
  45. package/dist/taggables/index.js +1 -0
  46. package/dist/taggables/store.js +1 -0
  47. package/dist/taggables/update.js +1 -0
  48. package/dist/tests/setup.d.ts +5 -0
  49. package/dist/tests/setup.js +100 -0
  50. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";export async function destroy(id){const db=await getDb();try{if(!await fetchById(id))throw Error(`Category with ID ${id} not found`);return(await db.deleteFrom("categorizables").where("id","=",id).executeTakeFirst()).numDeletedRows>0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete category: ${error.message}`);throw error}}export async function bulkDestroy(ids){const db=await getDb();if(!ids.length)return 0;try{const result=await db.deleteFrom("categorizables").where("id","in",ids).executeTakeFirst();return Number(result.numDeletedRows)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete categories in bulk: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{slugify}from"ts-slug";export async function fetchById(id){return await(await getDb()).selectFrom("categorizables").where("id","=",id).where("is_active","=",!0).selectAll().executeTakeFirst()}export async function fetchAll(){return await(await getDb()).selectFrom("categorizables").selectAll().execute()}export async function fetchByName(name){return await(await getDb()).selectFrom("categorizables").where("name","=",name).where("is_active","=",!0).selectAll().execute()}export async function fetchBySlug(slug){return await(await getDb()).selectFrom("categorizables").where("slug","=",slug).where("is_active","=",!0).selectAll().executeTakeFirst()}export async function fetchWithPosts(id){return await(await getDb()).selectFrom("categorizables").where("id","=",id).where("is_active","=",!0).selectAll().executeTakeFirst()}export async function firstOrCreate(name,categorizableType,description){const db=await getDb();try{const existingCategory=await db.selectFrom("categorizables").selectAll().where("name","=",name).executeTakeFirst();if(existingCategory)return existingCategory;const now=new Date,categoryData={name,slug:slugify(name),description,is_active:!0,created_at:now.toDateString(),categorizable_type:categorizableType},result=await db.insertInto("categorizables").values(categoryData).returningAll().executeTakeFirst();if(!result)throw Error("Failed to create category");return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to find or create category: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ export{bulkDestroy,destroy}from"./destroy";export{fetchAll,fetchById,fetchByName,fetchBySlug,fetchWithPosts}from"./fetch";export{bulkStore,store,storeCategorizableModel}from"./store";export{update}from"./update";
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{resolveWrittenRow}from"../results";import{slugify}from"ts-slug";export async function store(data){const db=await getDb();try{if(!data.name||data.name.trim()==="")throw Error("Category name is required");if(!data.categorizable_type||data.categorizable_type.trim()==="")throw Error("Category categorizable_type is required");const categoryData={name:data.name,slug:slugify(data.name),description:data.description,categorizable_type:data.categorizable_type,is_active:data.is_active??!0},written=await db.insertInto("categorizables").values(categoryData).returningAll().executeTakeFirst(),category=await resolveWrittenRow(db,"categorizables",written);if(!category)throw Error("Failed to create category");return category}catch(error){if(error instanceof Error)throw TypeError(`Failed to create category: ${error.message}`);throw error}}export async function storeCategorizableModel(data){const db=await getDb();try{const modelData={category_id:data.category_id,categorizable_type:data.categorizable_type},written=await db.insertInto("categorizable_models").values(modelData).returningAll().executeTakeFirst(),result=await resolveWrittenRow(db,"categorizable_models",written);if(!result)throw Error("Failed to create categorizable model relationship");return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to create categorizable model relationship: ${error.message}`);throw error}}export async function bulkStore(data){const db=await getDb();try{return await db.transaction(async(trx)=>{const categories=[];for(const item of data){const categoryData={name:item.name,slug:slugify(item.name),description:item.description,categorizable_type:item.categorizable_type,is_active:item.is_active??!0},written=await trx.insertInto("categorizables").values(categoryData).returningAll().executeTakeFirst(),category=await resolveWrittenRow(trx,"categorizables",written);if(!category)throw Error(`Failed to create category: ${item.name}`);categories.push(category)}return categories})}catch(error){if(error instanceof Error)throw TypeError(`Failed to create categories: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";import{isRow}from"../results";import{slugify}from"ts-slug";export async function update(data){const db=await getDb();try{const id=data.id;if(!id)throw Error("Category ID is required for update");if(data.name!==void 0){if(data.name.trim()==="")throw Error("Category name cannot be empty");data.slug=slugify(data.name)}const{id:_id,...updateData}=data,result=await db.updateTable("categorizables").set(updateData).where("id","=",id).returningAll().executeTakeFirst();if(isRow(result))return result;const category=await fetchById(id);if(!category)throw Error("Failed to update category");return category}catch(error){if(error instanceof Error)throw TypeError(`Failed to update category: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchCommentById}from"./fetch";export async function destroy(id){const db=await getDb();try{if(!await fetchCommentById(id))throw Error(`Comment with ID ${id} not found`);return(await db.deleteFrom("commentables").where("id","=",id).executeTakeFirst()).numDeletedRows>0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete comment: ${error.message}`);throw error}}export async function bulkDestroy(ids){const db=await getDb();if(!ids.length)return 0;try{const result=await db.deleteFrom("commentables").where("id","in",ids).executeTakeFirst();return Number(result.numDeletedRows)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete comments in bulk: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{sqlDateTime}from"@stacksjs/database";import{getDb}from"../database";import{formatDate}from"@stacksjs/orm";export async function fetchComments(options={}){let query=(await getDb()).selectFrom("commentables");if(options.status)query=query.where("status","=",options.status);if(options.commentables_id)query=query.where("commentables_id","=",options.commentables_id);if(options.commentables_type)query=query.where("commentables_type","=",options.commentables_type);if(options.limit)query=query.limit(options.limit);if(options.offset)query=query.offset(options.offset);return query.selectAll().execute()}export async function fetchCommentById(id){return(await getDb()).selectFrom("commentables").where("id","=",id).selectAll().executeTakeFirst()}export async function fetchCommentsByCommentables(commentables_id,commentables_type,options={}){let query=(await getDb()).selectFrom("commentables").where("commentables_id","=",commentables_id).where("commentables_type","=",commentables_type);if(options.status)query=query.where("status","=",options.status);if(options.limit)query=query.limit(options.limit);if(options.offset)query=query.offset(options.offset);return query.selectAll().execute()}export async function fetchCommentCountByPeriod(days){const db=await getDb();try{const result=await db.selectFrom("commentables").where("created_at",">=",sqlDateTime(new Date(Date.now()-days*24*60*60*1000))).count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch comment count: ${error.message}`);throw error}}export async function fetchCommentsByStatus(status,options={}){const db=await getDb();try{let query=db.selectFrom("commentables").where("status","=",status);if(options.limit)query=query.limit(options.limit);if(options.offset)query=query.offset(options.offset);return query.selectAll().execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch comments by status: ${error.message}`);throw error}}export async function calculateApprovalRate(){const db=await getDb();try{const[approvedCount,totalCount]=await Promise.all([db.selectFrom("commentables").where("status","=","approved").count(),db.selectFrom("commentables").count()]),approved=Number(approvedCount||0),total=Number(totalCount||0),rate=total>0?approved/total*100:0;return{approved,total,rate}}catch(error){if(error instanceof Error)throw TypeError(`Failed to calculate approval rate: ${error.message}`);throw error}}export async function fetchPostsWithMostComments(dateRange,options={}){const db=await getDb();try{let query=db.selectFrom("posts").leftJoin("commentables","posts.id","=","commentables.commentables_id").where("commentables.created_at",">=",formatDate(dateRange.startDate)).where("commentables.created_at","<=",formatDate(dateRange.endDate)).select(["posts.id","posts.title"]).groupBy("posts.id","posts.title").orderBy("posts.id","desc");if(options.limit)query=query.limit(options.limit);return(await query.execute()).map((row)=>({id:Number(row.id),title:String(row.title),comment_count:Number(row.comment_count||0)}))}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch posts with most comments: ${error.message}`);throw error}}export async function fetchCommentCountBarGraph(dateRange,options={}){try{const posts=await fetchPostsWithMostComments(dateRange,options);return{labels:posts.map((post)=>post.title),values:posts.map((post)=>post.comment_count)}}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch bar graph data: ${error.message}`);throw error}}export async function fetchStatusDistributionDonut(dateRange){const db=await getDb();try{const typedResults=await db.selectFrom("commentables").where("created_at",">=",formatDate(dateRange.startDate)).where("created_at","<=",formatDate(dateRange.endDate)).select(["status"]).groupBy("status").execute(),total=typedResults.reduce((sum,row)=>sum+Number(row.count||0),0),labels=typedResults.map((row)=>String(row.status)),values=typedResults.map((row)=>Number(row.count||0)),percentages=values.map((value)=>total>0?value/total*100:0);return{labels,values,percentages}}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch status distribution: ${error.message}`);throw error}}export async function fetchMonthlyCommentCounts(dateRange){const db=await getDb();try{const results=await db.selectFrom("commentables").where("created_at",">=",formatDate(dateRange.startDate)).where("created_at","<=",formatDate(dateRange.endDate)).select(["created_at"]).groupBy("created_at").orderBy("created_at","asc").execute(),monthlyCounts=new Map;results.forEach((row)=>{if(!row.created_at)return;const date=new Date(row.created_at),key=`${date.getFullYear()}-${String(date.getMonth()+1).padStart(2,"0")}`;monthlyCounts.set(key,(monthlyCounts.get(key)||0)+Number(row.count||0))});const labels=[],values=[];monthlyCounts.forEach((count,key)=>{const[year,month]=key.split("-");labels.push(new Date(Number(year),Number(month)-1).toLocaleString("default",{month:"short",year:"numeric"}));values.push(count)});return{labels,values}}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch monthly comment counts: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ export{bulkDestroy,destroy}from"./destroy";export{calculateApprovalRate,fetchCommentById,fetchCommentCountBarGraph,fetchCommentCountByPeriod,fetchComments,fetchCommentsByCommentables,fetchCommentsByStatus,fetchMonthlyCommentCounts,fetchPostsWithMostComments,fetchStatusDistributionDonut}from"./fetch";export{store}from"./store";export{update}from"./update";
@@ -0,0 +1 @@
1
+ import{sqlDateTime}from"@stacksjs/database";import{getDb}from"../database";import{resolveWrittenRow}from"../results";async function readComment(db,id){const row=await db.selectFrom("commentables").where("id","=",id).selectAll().executeTakeFirst();if(!row)throw Error(`Comment ${id} not found after write`);return row}export async function createComment(data){const db=await getDb(),now=new Date,commentData={title:data.title,body:data.body,commentables_id:data.commentables_id,commentables_type:data.commentables_type,status:"pending",created_at:sqlDateTime(now)},written=await db.insertInto("commentables").values(commentData).returningAll().executeTakeFirst(),comment=await resolveWrittenRow(db,"commentables",written);if(!comment)throw Error("Failed to create comment");return comment}export async function updateComment(id,input){const db=await getDb();await db.updateTable("commentables").set({...input,updated_at:sqlDateTime()}).where("id","=",id).execute();return readComment(db,id)}export async function approveComment(id){const db=await getDb();await db.updateTable("commentables").set({status:"approved",approved_at:Date.now(),updated_at:sqlDateTime()}).where("id","=",id).execute();return readComment(db,id)}export async function rejectComment(id){const db=await getDb();await db.updateTable("commentables").set({status:"rejected",rejected_at:Date.now(),updated_at:sqlDateTime()}).where("id","=",id).execute();return readComment(db,id)}export async function deleteComment(id){await(await getDb()).deleteFrom("commentables").where("id","=",id).execute()}export async function store(data){const db=await getDb();try{if(!data.title||data.title.trim()==="")throw Error("Comment title is required");if(!data.body||data.body.trim()==="")throw Error("Comment body is required");if(!data.commentables_type||data.commentables_type.trim()==="")throw Error("Comment commentables_type is required");const validStatuses=["pending","approved","rejected"];if(data.status&&!validStatuses.includes(data.status))throw Error(`Invalid comment status: ${data.status}`);const commentData={title:data.title,body:data.body,status:data.status,commentables_id:data.commentables_id,commentables_type:data.commentables_type,user_id:data.user_id,is_active:data.is_active},written=await db.insertInto("commentables").values(commentData).returningAll().executeTakeFirst(),result=await resolveWrittenRow(db,"commentables",written);if(!result)throw Error("Failed to create comment");return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to store comment: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{formatDate}from"@stacksjs/orm";export async function update(id,data){const db=await getDb();try{if(data.title!==void 0&&data.title.trim()==="")throw Error("Comment title cannot be empty");if(data.body!==void 0&&data.body.trim()==="")throw Error("Comment body cannot be empty");const validStatuses=["pending","approved","rejected"];if(data.status!==void 0&&!validStatuses.includes(data.status))throw Error(`Invalid comment status: ${data.status}`);const commentData={updated_at:formatDate(new Date)};if(data.title!==void 0)commentData.title=data.title;if(data.body!==void 0)commentData.body=data.body;if(data.status!==void 0)commentData.status=data.status;if(data.commentables_id!==void 0)commentData.commentables_id=data.commentables_id;if(data.commentables_type!==void 0)commentData.commentables_type=data.commentables_type;if(data.approved_at!==void 0)commentData.approved_at=data.approved_at;if(data.rejected_at!==void 0)commentData.rejected_at=data.rejected_at;await db.updateTable("commentables").set(commentData).where("id","=",id).execute();return await db.selectFrom("commentables").where("id","=",id).selectAll().executeTakeFirst()}catch(error){if(error instanceof Error)throw TypeError(`Failed to update comment: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ let dbPromise;export async function getDb(){dbPromise??=import("@stacksjs/database").then((module)=>module.db);return dbPromise}
package/dist/index.js CHANGED
@@ -1,3 +1 @@
1
- // @bun
2
- var f2=Object.defineProperty;var q2=(M)=>M;function R2(M,A){this[M]=q2.bind(null,A)}var U=(M,A)=>{for(var $ in A)f2(M,$,{get:A[$],enumerable:!0,configurable:!0,set:R2.bind(A,$)})};var D=import.meta.require;var b={};U(b,{update:()=>CM,store:()=>xM,findOrCreate:()=>KM,findByUuid:()=>HM,findByUserId:()=>VM,findByName:()=>QM,findByEmail:()=>zM,fetchById:()=>P,fetchAll:()=>BM,destroy:()=>YM});var LM;async function Z(){return LM??=import("@stacksjs/database").then((M)=>M.db),LM}async function YM(M){let A=await Z();try{let $=await A.deleteFrom("authors").where("id","=",M).returningAll().executeTakeFirst();if(!$)throw Error(`Author with ID ${M} not found`);return $}catch($){if($ instanceof Error)throw TypeError(`Author with ID ${M} not found`);throw $}}async function P(M){return await(await Z()).selectFrom("authors").where("id","=",M).selectAll().executeTakeFirst()}async function BM(){return await(await Z()).selectFrom("authors").selectAll().execute()}async function QM(M){return await(await Z()).selectFrom("authors").where("name","=",M).selectAll().executeTakeFirst()}async function zM(M){return await(await Z()).selectFrom("authors").where("email","=",M).selectAll().executeTakeFirst()}async function HM(M){return await(await Z()).selectFrom("authors").where("uuid","=",M).selectAll().executeTakeFirst()}async function VM(M){return await(await Z()).selectFrom("authors").where("user_id","=",M).selectAll().executeTakeFirst()}var{randomUUIDv7:WM}=globalThis.Bun;function V(M){return!!M&&typeof M==="object"&&"id"in M}function m(M){if(V(M)&&typeof M.id==="number")return M.id;let A=M?.lastInsertRowid;if(A===void 0||A===null)return;let $=Number(A);return Number.isSafeInteger($)&&$>0?$:void 0}async function W(M,A,$){if(V($))return $;let J=m($);if(J===void 0)return;return await M.selectFrom(A).where("id","=",J).selectAll().executeTakeFirst()}function c(M,A){let $=M?.[A]??M?.changes;if(typeof $==="number"||typeof $==="bigint")return Number($);let J=$?.changes;return typeof J==="number"||typeof J==="bigint"?Number(J):0}import{HttpError as T}from"@stacksjs/error-handling";import{formatDate as S,isUniqueViolation as _M}from"@stacksjs/orm";async function KM(M){let A=await Z();try{let $=await A.selectFrom("authors").where("email","=",M.email).orWhere("name","=",M.name).selectAll().executeTakeFirst();if($)return $;let J=await A.selectFrom("users").where("email","=",M.email).selectAll().executeTakeFirst();if(!J){let B=await A.insertInto("users").values({email:M.email,name:M.name,password:WM(),uuid:WM(),created_at:S(new Date),updated_at:S(new Date)}).returningAll().executeTakeFirst();if(J=await W(A,"users",B),!J)throw Error("Failed to create user")}let X={user_id:J.id,name:M.name,email:M.email,created_at:S(new Date),updated_at:S(new Date)},G=await A.insertInto("authors").values(X).returningAll().executeTakeFirst(),L=await W(A,"authors",G);if(!L)throw Error("Failed to create author");return L}catch($){if($ instanceof T)throw $;if(_M($))throw new T(409,"An author with this email already exists");if($ instanceof Error)throw TypeError(`Failed to find or create author: ${$.message}`);throw $}}async function xM(M){let A=await Z();try{let $=await A.selectFrom("authors").where("email","=",M.email).orWhere("name","=",M.name).selectAll().executeTakeFirst();if($)return $;let J={user_id:M.user_id,name:M.name,email:M.email,created_at:S(new Date),updated_at:S(new Date)},X=await A.insertInto("authors").values(J).returningAll().executeTakeFirst(),G=await W(A,"authors",X);if(!G)throw Error("Failed to create author");return G}catch($){if($ instanceof T)throw $;if(_M($))throw new T(409,"An author with this email already exists");if($ instanceof Error)throw TypeError(`Failed to find or create author: ${$.message}`);throw $}}import{HttpError as OM}from"@stacksjs/error-handling";import{formatDate as k2,isUniqueViolation as y2}from"@stacksjs/orm";async function CM(M,A){let $=await Z();try{let J={...A,updated_at:k2(new Date)},X=await $.updateTable("authors").set(J).where("id","=",M).returningAll().executeTakeFirst();if(V(X))return X;return await P(M)}catch(J){if(J instanceof OM)throw J;if(y2(J))throw new OM(409,"An author with this email already exists");if(J instanceof Error)throw TypeError(`Failed to update author: ${J.message}`);throw J}}var g={};U(g,{update:()=>RM,storeCategorizableModel:()=>fM,store:()=>TM,fetchWithPosts:()=>EM,fetchBySlug:()=>jM,fetchByName:()=>SM,fetchById:()=>j,fetchAll:()=>UM,destroy:()=>FM,bulkStore:()=>qM,bulkDestroy:()=>NM});import{slugify as D$}from"ts-slug";async function j(M){return await(await Z()).selectFrom("categorizables").where("id","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function UM(){return await(await Z()).selectFrom("categorizables").selectAll().execute()}async function SM(M){return await(await Z()).selectFrom("categorizables").where("name","=",M).where("is_active","=",!0).selectAll().execute()}async function jM(M){return await(await Z()).selectFrom("categorizables").where("slug","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function EM(M){return await(await Z()).selectFrom("categorizables").where("id","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function FM(M){let A=await Z();try{if(!await j(M))throw Error(`Category with ID ${M} not found`);return(await A.deleteFrom("categorizables").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete category: ${$.message}`);throw $}}async function NM(M){let A=await Z();if(!M.length)return 0;try{let $=await A.deleteFrom("categorizables").where("id","in",M).executeTakeFirst();return Number($.numDeletedRows)||0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete categories in bulk: ${$.message}`);throw $}}import{slugify as PM}from"ts-slug";async function TM(M){let A=await Z();try{if(!M.name||M.name.trim()==="")throw Error("Category name is required");if(!M.categorizable_type||M.categorizable_type.trim()==="")throw Error("Category categorizable_type is required");let $={name:M.name,slug:PM(M.name),description:M.description,categorizable_type:M.categorizable_type,is_active:M.is_active??!0},J=await A.insertInto("categorizables").values($).returningAll().executeTakeFirst(),X=await W(A,"categorizables",J);if(!X)throw Error("Failed to create category");return X}catch($){if($ instanceof Error)throw TypeError(`Failed to create category: ${$.message}`);throw $}}async function fM(M){let A=await Z();try{let $={category_id:M.category_id,categorizable_type:M.categorizable_type},J=await A.insertInto("categorizable_models").values($).returningAll().executeTakeFirst(),X=await W(A,"categorizable_models",J);if(!X)throw Error("Failed to create categorizable model relationship");return X}catch($){if($ instanceof Error)throw TypeError(`Failed to create categorizable model relationship: ${$.message}`);throw $}}async function qM(M){let A=await Z();try{return await A.transaction(async(J)=>{let X=[];for(let G of M){let L={name:G.name,slug:PM(G.name),description:G.description,categorizable_type:G.categorizable_type,is_active:G.is_active??!0},B=await J.insertInto("categorizables").values(L).returningAll().executeTakeFirst(),Y=await W(J,"categorizables",B);if(!Y)throw Error(`Failed to create category: ${G.name}`);X.push(Y)}return X})}catch($){if($ instanceof Error)throw TypeError(`Failed to create categories: ${$.message}`);throw $}}import{slugify as I2}from"ts-slug";async function RM(M){let A=await Z();try{let $=M.id;if(!$)throw Error("Category ID is required for update");if(M.name!==void 0){if(M.name.trim()==="")throw Error("Category name cannot be empty");M.slug=I2(M.name)}let{id:J,...X}=M,G=await A.updateTable("categorizables").set(X).where("id","=",$).returningAll().executeTakeFirst();if(V(G))return G;let L=await j($);if(!L)throw Error("Failed to update category");return L}catch($){if($ instanceof Error)throw TypeError(`Failed to update category: ${$.message}`);throw $}}var u={};U(u,{update:()=>pM,store:()=>gM,fetchStatusDistributionDonut:()=>DM,fetchPostsWithMostComments:()=>p,fetchMonthlyCommentCounts:()=>mM,fetchCommentsByStatus:()=>vM,fetchCommentsByCommentables:()=>yM,fetchComments:()=>kM,fetchCommentCountByPeriod:()=>IM,fetchCommentCountBarGraph:()=>hM,fetchCommentById:()=>f,destroy:()=>cM,calculateApprovalRate:()=>wM,bulkDestroy:()=>bM});import{sqlDateTime as v2}from"@stacksjs/database";import{formatDate as E}from"@stacksjs/orm";async function kM(M={}){let $=(await Z()).selectFrom("commentables");if(M.status)$=$.where("status","=",M.status);if(M.commentables_id)$=$.where("commentables_id","=",M.commentables_id);if(M.commentables_type)$=$.where("commentables_type","=",M.commentables_type);if(M.limit)$=$.limit(M.limit);if(M.offset)$=$.offset(M.offset);return $.selectAll().execute()}async function f(M){return(await Z()).selectFrom("commentables").where("id","=",M).selectAll().executeTakeFirst()}async function yM(M,A,$={}){let X=(await Z()).selectFrom("commentables").where("commentables_id","=",M).where("commentables_type","=",A);if($.status)X=X.where("status","=",$.status);if($.limit)X=X.limit($.limit);if($.offset)X=X.offset($.offset);return X.selectAll().execute()}async function IM(M){let A=await Z();try{let $=await A.selectFrom("commentables").where("created_at",">=",v2(new Date(Date.now()-M*24*60*60*1000))).count();return Number($)||0}catch($){if($ instanceof Error)throw TypeError(`Failed to fetch comment count: ${$.message}`);throw $}}async function vM(M,A={}){let $=await Z();try{let J=$.selectFrom("commentables").where("status","=",M);if(A.limit)J=J.limit(A.limit);if(A.offset)J=J.offset(A.offset);return J.selectAll().execute()}catch(J){if(J instanceof Error)throw TypeError(`Failed to fetch comments by status: ${J.message}`);throw J}}async function wM(){let M=await Z();try{let[A,$]=await Promise.all([M.selectFrom("commentables").where("status","=","approved").count(),M.selectFrom("commentables").count()]),J=Number(A||0),X=Number($||0),G=X>0?J/X*100:0;return{approved:J,total:X,rate:G}}catch(A){if(A instanceof Error)throw TypeError(`Failed to calculate approval rate: ${A.message}`);throw A}}async function p(M,A={}){let $=await Z();try{let J=$.selectFrom("posts").leftJoin("commentables","posts.id","=","commentables.commentables_id").where("commentables.created_at",">=",E(M.startDate)).where("commentables.created_at","<=",E(M.endDate)).select(["posts.id","posts.title"]).groupBy("posts.id","posts.title").orderBy("posts.id","desc");if(A.limit)J=J.limit(A.limit);return(await J.execute()).map((G)=>({id:Number(G.id),title:String(G.title),comment_count:Number(G.comment_count||0)}))}catch(J){if(J instanceof Error)throw TypeError(`Failed to fetch posts with most comments: ${J.message}`);throw J}}async function hM(M,A={}){try{let $=await p(M,A);return{labels:$.map((J)=>J.title),values:$.map((J)=>J.comment_count)}}catch($){if($ instanceof Error)throw TypeError(`Failed to fetch bar graph data: ${$.message}`);throw $}}async function DM(M){let A=await Z();try{let J=await A.selectFrom("commentables").where("created_at",">=",E(M.startDate)).where("created_at","<=",E(M.endDate)).select(["status"]).groupBy("status").execute(),X=J.reduce((Y,Q)=>Y+Number(Q.count||0),0),G=J.map((Y)=>String(Y.status)),L=J.map((Y)=>Number(Y.count||0)),B=L.map((Y)=>X>0?Y/X*100:0);return{labels:G,values:L,percentages:B}}catch($){if($ instanceof Error)throw TypeError(`Failed to fetch status distribution: ${$.message}`);throw $}}async function mM(M){let A=await Z();try{let $=await A.selectFrom("commentables").where("created_at",">=",E(M.startDate)).where("created_at","<=",E(M.endDate)).select(["created_at"]).groupBy("created_at").orderBy("created_at","asc").execute(),J=new Map;$.forEach((L)=>{if(!L.created_at)return;let B=new Date(L.created_at),Y=`${B.getFullYear()}-${String(B.getMonth()+1).padStart(2,"0")}`;J.set(Y,(J.get(Y)||0)+Number(L.count||0))});let X=[],G=[];return J.forEach((L,B)=>{let[Y,Q]=B.split("-");X.push(new Date(Number(Y),Number(Q)-1).toLocaleString("default",{month:"short",year:"numeric"})),G.push(L)}),{labels:X,values:G}}catch($){if($ instanceof Error)throw TypeError(`Failed to fetch monthly comment counts: ${$.message}`);throw $}}async function cM(M){let A=await Z();try{if(!await f(M))throw Error(`Comment with ID ${M} not found`);return(await A.deleteFrom("commentables").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete comment: ${$.message}`);throw $}}async function bM(M){let A=await Z();if(!M.length)return 0;try{let $=await A.deleteFrom("commentables").where("id","in",M).executeTakeFirst();return Number($.numDeletedRows)||0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete comments in bulk: ${$.message}`);throw $}}import{sqlDateTime as BA}from"@stacksjs/database";async function gM(M){let A=await Z();try{if(!M.title||M.title.trim()==="")throw Error("Comment title is required");if(!M.body||M.body.trim()==="")throw Error("Comment body is required");if(!M.commentables_type||M.commentables_type.trim()==="")throw Error("Comment commentables_type is required");let $=["pending","approved","rejected"];if(M.status&&!$.includes(M.status))throw Error(`Invalid comment status: ${M.status}`);let J={title:M.title,body:M.body,status:M.status,commentables_id:M.commentables_id,commentables_type:M.commentables_type,user_id:M.user_id,is_active:M.is_active},X=await A.insertInto("commentables").values(J).returningAll().executeTakeFirst(),G=await W(A,"commentables",X);if(!G)throw Error("Failed to create comment");return G}catch($){if($ instanceof Error)throw TypeError(`Failed to store comment: ${$.message}`);throw $}}import{formatDate as w2}from"@stacksjs/orm";async function pM(M,A){let $=await Z();try{if(A.title!==void 0&&A.title.trim()==="")throw Error("Comment title cannot be empty");if(A.body!==void 0&&A.body.trim()==="")throw Error("Comment body cannot be empty");let J=["pending","approved","rejected"];if(A.status!==void 0&&!J.includes(A.status))throw Error(`Invalid comment status: ${A.status}`);let X={updated_at:w2(new Date)};if(A.title!==void 0)X.title=A.title;if(A.body!==void 0)X.body=A.body;if(A.status!==void 0)X.status=A.status;if(A.commentables_id!==void 0)X.commentables_id=A.commentables_id;if(A.commentables_type!==void 0)X.commentables_type=A.commentables_type;if(A.approved_at!==void 0)X.approved_at=A.approved_at;if(A.rejected_at!==void 0)X.rejected_at=A.rejected_at;return await $.updateTable("commentables").set(X).where("id","=",M).execute(),await $.selectFrom("commentables").where("id","=",M).selectAll().executeTakeFirst()}catch(J){if(J instanceof Error)throw TypeError(`Failed to update comment: ${J.message}`);throw J}}var l={};U(l,{update:()=>tM,store:()=>aM,fetchPublishedAfter:()=>sM,fetchByMinViews:()=>nM,fetchByMinConversions:()=>iM,fetchById:()=>F,fetchByAuthor:()=>lM,fetchAll:()=>uM,destroy:()=>oM,bulkDestroy:()=>dM});async function F(M){return await(await Z()).selectFrom("pages").where("id","=",M).selectAll().executeTakeFirst()}async function uM(){return await(await Z()).selectFrom("pages").selectAll().execute()}async function lM(M){return await(await Z()).selectFrom("pages").where("author_id","=",M).selectAll().execute()}async function nM(M){return await(await Z()).selectFrom("pages").where("views",">=",M).selectAll().execute()}async function iM(M){return await(await Z()).selectFrom("pages").where("conversions",">=",M).selectAll().execute()}async function sM(M){return await(await Z()).selectFrom("pages").where("published_at",">",M).selectAll().execute()}async function oM(M){let A=await Z();try{if(!await F(M))throw Error(`Page with ID ${M} not found`);return(await A.deleteFrom("pages").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete page: ${$.message}`);throw $}}async function dM(M){let A=await Z();if(!M.length)return 0;try{let $=await A.deleteFrom("pages").where("id","in",M).executeTakeFirst();return Number($.numDeletedRows)||0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete pages in bulk: ${$.message}`);throw $}}import{formatDate as rM}from"@stacksjs/orm";async function aM(M){let A=await Z();try{let $={author_id:M.author_id,title:M.title,template:M.template,views:M.views||0,conversions:M.conversions||0,created_at:rM(new Date),updated_at:rM(new Date)},J=await A.insertInto("pages").values($).returningAll().executeTakeFirst(),X=await W(A,"pages",J);if(!X)throw Error("Failed to create page");return X}catch($){if($ instanceof Error)throw TypeError(`Failed to create page: ${$.message}`);throw $}}import{formatDate as h2}from"@stacksjs/orm";async function tM(M,A){let $=await Z();try{let J={...A,updated_at:h2(new Date)},X=await $.updateTable("pages").set(J).where("id","=",M).returningAll().executeTakeFirst();if(V(X))return X;let G=await F(M);if(!G)throw Error("Failed to update page");return G}catch(J){if(J instanceof Error)throw TypeError(`Failed to update page: ${J.message}`);throw J}}var n={};U(n,{update:()=>z2,sync:()=>Q2,store:()=>L2,fetchPublishedAfter:()=>X2,fetchByStatus:()=>M2,fetchByMinViews:()=>J2,fetchById:()=>O,fetchByCategory:()=>$2,fetchByAuthor:()=>A2,fetchAll:()=>eM,detach:()=>B2,destroy:()=>Z2,bulkDestroy:()=>G2,attach:()=>Y2});async function O(M){return await(await Z()).selectFrom("posts").where("id","=",M).selectAll().executeTakeFirst()}async function eM(){return await(await Z()).selectFrom("posts").selectAll().execute()}async function M2(M){return await(await Z()).selectFrom("posts").where("status","=",M).selectAll().execute()}async function $2(M){return await(await Z()).selectFrom("posts").where("category","=",M).selectAll().execute()}async function A2(M){return await(await Z()).selectFrom("posts").where("author_id","=",M).selectAll().execute()}async function J2(M){return await(await Z()).selectFrom("posts").where("views",">=",M).selectAll().execute()}async function X2(M){return await(await Z()).selectFrom("posts").where("published_at",">",M).selectAll().execute()}async function Z2(M){let A=await Z();try{if(!await O(M))throw Error(`Post with ID ${M} not found`);let J=await A.deleteFrom("posts").where("id","=",M).executeTakeFirst();return c(J,"numDeletedRows")>0}catch($){if($ instanceof Error)throw TypeError(`Failed to delete post: ${$.message}`);throw $}}async function G2(M){let A=await Z();if(!M.length)return 0;try{let J=(await A.selectFrom("posts").select(["id"]).where("id","in",M).execute()).map((G)=>G.id);if(!J.length)return 0;let X=0;for(let G of J){let L=await A.deleteFrom("posts").where("id","=",G).executeTakeFirst();X+=c(L,"numDeletedRows")}return X}catch($){if($ instanceof Error)throw TypeError(`Failed to delete posts in bulk: ${$.message}`);throw $}}var{randomUUIDv7:D2}=globalThis.Bun;import{formatDate as q}from"@stacksjs/orm";var m2="draft",c2="published";async function L2(M){let A=await Z();try{if(!M.title||typeof M.title==="string"&&M.title.trim()==="")throw Error("Post title is required");let $=M.content||M.body,J=M,X=M.status||m2,G={author_id:J.author_id,uuid:D2(),title:M.title,poster:M.poster,content:$,excerpt:M.excerpt,is_featured:J.is_featured?Date.now():void 0,views:M.views||0,published_at:J.published_at??(X===c2?Date.now():void 0),status:X},L=await A.insertInto("posts").values(G).returningAll().executeTakeFirst();if(V(L))return L;let B=m(L),Y=B===void 0?void 0:await O(B);if(!Y)throw Error("Failed to create post");return Y}catch($){if($ instanceof Error)throw TypeError(`Failed to create post: ${$.message}`);throw $}}async function Y2(M,A,$){let J=await Z();try{let X=A==="categorizable_models"?"categorizable_id":"taggable_id",G=A==="categorizable_models"?"categorizable_type":"taggable_type",L=A==="categorizable_models"?"category_id":"tag_id",B=$.map((Y)=>({[L]:Y,[X]:M,[G]:"posts",created_at:q(new Date),updated_at:q(new Date)}));for(let Y of B)await J.insertInto(A).values(Y).execute()}catch(X){if(X instanceof Error)throw TypeError(`Failed to attach records: ${X.message}`);throw X}}async function B2(M,A,$){let J=await Z();try{let X=A==="categorizable_models"?"categorizable_id":"taggable_id",G=A==="categorizable_models"?"categorizable_type":"taggable_type",L=A==="categorizable_models"?"category_id":"tag_id",B=J.deleteFrom(A).where(X,"=",M).where(G,"=","posts");if($){for(let Y of $)await J.deleteFrom(A).where(X,"=",M).where(G,"=","posts").where(L,"=",Y).execute();return}await B.execute()}catch(X){if(X instanceof Error)throw TypeError(`Failed to detach records: ${X.message}`);throw X}}async function Q2(M,A,$){let J=await Z();try{let X=A==="categorizable_models"?"categorizable_id":"taggable_id",G=A==="categorizable_models"?"categorizable_type":"taggable_type",L=A==="categorizable_models"?"category_id":"tag_id",Y=(await J.selectFrom(A).select([L]).where(X,"=",M).where(G,"=","posts").execute()).map((z)=>z[L]),Q=Y.filter((z)=>!$.includes(z)),H=$.filter((z)=>!Y.includes(z));if(Q.length>0)for(let z of Q)await J.deleteFrom(A).where(X,"=",M).where(G,"=","posts").where(L,"=",z).execute();if(H.length>0){let z=H.map((x)=>({[L]:x,[X]:M,[G]:"posts",created_at:q(new Date),updated_at:q(new Date)}));await J.insertInto(A).values(z).execute()}}catch(X){if(X instanceof Error)throw TypeError(`Failed to sync records: ${X.message}`);throw X}}async function z2(M,A){let $=await Z();try{if(A.title!==void 0&&typeof A.title==="string"&&A.title.trim()==="")throw Error("Post title cannot be empty");let J=["published","draft","archived"];if(A.status!==void 0&&typeof A.status==="string"&&!J.includes(A.status))throw Error(`Invalid post status: ${A.status}`);if(A.views!==void 0&&typeof A.views==="number"&&A.views<0)throw Error("Views count cannot be negative");let X=await $.updateTable("posts").set(A).where("id","=",M).returningAll().executeTakeFirst();if(V(X))return X;let G=await O(M);if(!G)throw Error(`Post with ID ${M} not found`);return G}catch(J){if(J instanceof Error)throw TypeError(`Failed to update post: ${J.message}`);throw J}}var i={};U(i,{update:()=>K2,store:()=>W2,fetchTags:()=>_2,fetchTagById:()=>R,destroy:()=>H2,bulkDestroy:()=>V2});async function H2(M){let A=await Z();if(!await A.selectFrom("taggables").where("id","=",M).selectAll().executeTakeFirst())throw Error(`Tag with ID ${M} not found`);await A.deleteFrom("taggables").where("id","=",M).executeTakeFirst()}async function V2(M){let A=await Z();try{await A.deleteFrom("taggables").where("id","in",M).execute()}catch($){if($ instanceof Error)throw TypeError(`Failed to delete tags: ${$.message}`);throw $}}import{slugify as b2}from"ts-slug";async function W2(M){let A=await Z();try{if(!M.name||M.name.trim()==="")throw Error("Tag name is required");if(!M.taggable_type||M.taggable_type.trim()==="")throw Error("Tag taggable_type is required");let $=b2(M.name);if(await A.selectFrom("taggables").selectAll().where("slug","=",$).executeTakeFirst())throw Error(`Tag with unique slug "${$}" already exists`);let X={name:M.name,slug:$,description:M.description,is_active:M.is_active??!0,taggable_type:M.taggable_type};if(M.taggable_id!==void 0)X.taggable_id=M.taggable_id;let G=await A.insertInto("taggables").values(X).returningAll().executeTakeFirst(),L=await W(A,"taggables",G);if(!L)throw Error("Failed to create tag");return L}catch($){if($ instanceof Error)throw TypeError(`Failed to store tag: ${$.message}`);throw $}}async function R(M){let A=await Z();try{let $=await A.selectFrom("taggables").where("id","=",M).selectAll().executeTakeFirst();if(!$)return;return $}catch($){if($ instanceof Error)throw TypeError(`Failed to fetch tag: ${$.message}`);throw $}}async function _2(){let M=await Z();try{return await M.selectFrom("taggables").where("is_active","=",!0).selectAll().execute()}catch(A){if(A instanceof Error)throw TypeError(`Failed to fetch tags: ${A.message}`);throw A}}import{uniqueSlug as g2}from"@stacksjs/slug";async function K2(M){let A=await Z();try{let $=M.id;if(!$)throw Error("Tag ID is required for update");if(M.name!==void 0){if(M.name.trim()==="")throw Error("Tag name cannot be empty");let B=await g2(M.name,{table:"taggables",column:"slug"});if(await A.selectFrom("taggables").selectAll().where("slug","=",B).where("id","!=",$).executeTakeFirst())throw Error(`Tag with unique slug "${B}" already exists`);M.slug=B}let{id:J,...X}=M,G=await A.updateTable("taggables").set(X).where("id","=",$).returningAll().executeTakeFirst();if(V(G))return G;let L=await R($);if(!L)throw Error("Failed to update tag");return L}catch($){if($ instanceof Error)throw TypeError(`Failed to update tag: ${$.message}`);throw $}}import{schema as n2}from"@stacksjs/validation";var p2=Symbol.for("stacks.cms.blockRegistry"),s=globalThis[p2]??=new Map;function K(M){return M}function o(M){for(let A of M)s.set(A.type,A)}function k(M){return s.get(M)}function u2(){return[...s.values()]}async function l2(M,A,$){let J=M.schema[A];if(!J)return`unknown prop "${A}"`;let X=J.rule;if(typeof X==="function"){let L=X($);if(L===!0)return null;return typeof L==="string"?L:`invalid value for "${A}"`}return await X.validate($)?null:`invalid value for "${A}"`}async function y(M){if(!Array.isArray(M))return{ok:!1,errors:[{index:-1,message:"blocks must be an array"}]};let A=[],$=[];for(let[J,X]of M.entries()){if(!X||typeof X!=="object"||Array.isArray(X)){A.push({index:J,message:"block must be an object"});continue}let G=X,L=typeof G.type==="string"?G.type:"",B=L?k(L):void 0;if(!B){A.push({index:J,type:L,message:L?`unknown block type "${L}"`:"block is missing a type"});continue}let Y=typeof G.id==="string"&&G.id?G.id:crypto.randomUUID(),Q=G.props&&typeof G.props==="object"&&!Array.isArray(G.props)?G.props:{};for(let[H,z]of Object.entries(Q)){let x=await l2(B,H,z);if(x)A.push({index:J,type:L,prop:H,message:x})}for(let[H,z]of Object.entries(B.schema))if(z.required&&Q[H]===void 0)A.push({index:J,type:L,prop:H,message:`missing required prop "${H}"`});$.push({id:Y,type:L,props:Q})}return A.length>0?{ok:!1,errors:A}:{ok:!0,blocks:$}}function d(M){let A=M;if(typeof A==="string"&&A)try{A=JSON.parse(A)}catch{return[]}if(!Array.isArray(A))return[];return A.filter(($)=>!!$&&typeof $==="object"&&typeof $.type==="string")}var _=(M=1000)=>({validate:async(A)=>typeof A==="string"&&A.length<=M}),N=()=>({validate:async(M)=>{if(typeof M!=="string"||!M)return!1;if(M.startsWith("/"))return!0;try{let A=await n2.string().url().validate(M);return typeof A==="boolean"?A:Boolean(A.valid)}catch{return!1}}}),r=(M)=>(A)=>typeof A==="string"&&M.includes(A)?!0:`must be one of ${M.join(", ")}`,x2=[K({type:"hero",label:"Hero",component:"cms/blocks/hero",icon:"i-hugeicons-layout-top",schema:{heading:{rule:_(300),required:!0},subheading:{rule:_(600)},imageUrl:{rule:N()},ctaLabel:{rule:_(80)},ctaHref:{rule:N()},align:{rule:r(["left","center"])}}}),K({type:"rich-text",label:"Text",component:"cms/blocks/rich-text",icon:"i-hugeicons-text",schema:{html:{rule:_(1e5),required:!0}}}),K({type:"image",label:"Image",component:"cms/blocks/image",icon:"i-hugeicons-image-01",schema:{src:{rule:N(),required:!0},alt:{rule:_(300),required:!0},caption:{rule:_(500)},width:{rule:r(["content","wide","full"])}}}),K({type:"columns",label:"Columns",component:"cms/blocks/columns",icon:"i-hugeicons-layout-3-column",schema:{columns:{rule:(M)=>Array.isArray(M)&&M.length>=1&&M.length<=4&&M.every((A)=>A&&typeof A==="object"&&typeof A.html==="string")?!0:"1-4 columns, each with an html string",required:!0}}}),K({type:"cta",label:"Call to action",component:"cms/blocks/cta",icon:"i-hugeicons-cursor-pointer-02",schema:{heading:{rule:_(300),required:!0},body:{rule:_(1000)},buttonLabel:{rule:_(80),required:!0},buttonHref:{rule:N(),required:!0}}}),K({type:"embed",label:"Embed",component:"cms/blocks/embed",icon:"i-hugeicons-embed",schema:{src:{rule:N(),required:!0},title:{rule:_(300),required:!0},aspect:{rule:r(["16:9","4:3","1:1"])}}}),K({type:"form",label:"Form",component:"cms/blocks/form",icon:"i-hugeicons-checklist",schema:{formUuid:{rule:(M)=>typeof M==="string"&&/^[0-9a-f-]{36}$/i.test(M)?!0:"must be a form uuid",required:!0}}})];function a(){o(x2)}async function i2(M,A){let $=await Z(),J=await $.selectFrom("menus").where("site_id","=",M).where("handle","=",A).select(["id"]).executeTakeFirst();if(!J)return[];let X=await $.selectFrom("menu_items").leftJoin("pages","pages.id","=","menu_items.page_id").where("menu_items.menu_id","=",J.id).select(["menu_items.id as id","menu_items.label as label","menu_items.url as url","menu_items.target as target","menu_items.parent_id as parent_id","menu_items.position as position","menu_items.visibility as visibility","pages.path as page_path","pages.status as page_status"]).orderBy("menu_items.position","asc").execute(),G=(Y)=>{let Q=Y.url??(Y.page_status==="published"?Y.page_path:null);if(!Q)return null;return{id:Y.id,label:Y.label,href:Q,target:Y.target==="_blank"?"_blank":"_self",visibility:Y.visibility==="auth"?"auth":"public",children:[]}},L=new Map,B=[];for(let Y of X){let Q=G(Y);if(Q)L.set(Y.id,Q)}for(let Y of X){let Q=L.get(Y.id);if(!Q)continue;let H=Y.parent_id?L.get(Y.parent_id):void 0;if(H)H.children.push(Q);else B.push(Q)}return B}import{formatDate as C2}from"@stacksjs/orm";import{slugify as d2}from"ts-slug";import{formatDate as t}from"@stacksjs/orm";async function e(M,A){let J=await(await Z()).selectFrom("redirects").where("site_id","=",M).where("from_path","=",A).select(["to_path","status_code"]).executeTakeFirst();if(!J)return null;return{toPath:J.to_path,statusCode:J.status_code===302?302:301}}async function MM(M,A){let $=await Z();for(let J of A){if(!J.fromPath||J.fromPath===J.toPath)continue;await $.deleteFrom("redirects").where("site_id","=",M).where("from_path","=",J.toPath).execute(),await $.deleteFrom("redirects").where("site_id","=",M).where("from_path","=",J.fromPath).execute(),await $.updateTable("redirects").set({to_path:J.toPath,updated_at:t(new Date)}).where("site_id","=",M).where("to_path","=",J.fromPath).execute(),await $.insertInto("redirects").values({site_id:M,from_path:J.fromPath,to_path:J.toPath,status_code:301,source:"slug-change",created_at:t(new Date),updated_at:t(new Date)}).execute()}}import{formatDate as $M}from"@stacksjs/orm";var O2=["id","page_id","author_id","revision","title","blocks","meta_description","note","created_at"];async function I(M,A,$={}){let J=await Z(),X=$.keep??50,L=((await J.selectFrom("page_revisions").where("page_id","=",M).select(["revision"]).orderBy("revision","desc").limit(1).executeTakeFirst())?.revision??0)+1;await J.insertInto("page_revisions").values({page_id:M,author_id:$.authorId??null,revision:L,title:A.title,blocks:A.blocks,meta_description:A.metaDescription,note:$.note??null,created_at:$M(new Date),updated_at:$M(new Date)}).execute();let B=await J.selectFrom("page_revisions").where("page_id","=",M).select(["id"]).orderBy("revision","desc").offset(X).limit(1000).execute();if(B.length>0)await J.deleteFrom("page_revisions").where("id","in",B.map((Y)=>Y.id)).execute();return L}async function s2(M,A=50){return await(await Z()).selectFrom("page_revisions").where("page_id","=",M).select([...O2]).orderBy("revision","desc").limit(A).execute()}async function o2(M,A={}){let $=await Z(),J=await $.selectFrom("page_revisions").where("id","=",M).select([...O2]).executeTakeFirst();if(!J)throw Error(`Page revision ${M} not found`);let X=await $.selectFrom("pages").where("id","=",J.page_id).select(["id","title","blocks","meta_description"]).executeTakeFirst();if(!X)throw Error(`Page ${J.page_id} not found for revision ${M}`);await I(X.id,{title:X.title,blocks:X.blocks,metaDescription:X.meta_description},{authorId:A.authorId,note:`before restoring revision ${J.revision}`}),await $.updateTable("pages").set({title:J.title,blocks:J.blocks,meta_description:J.meta_description,updated_at:$M(new Date)}).where("id","=",X.id).execute()}class C extends Error{details;status=422;constructor(M,A){super(M);this.details=A;this.name="PageDocumentError"}}async function U2(M,A,$){if(!$)return"";let J=await M.selectFrom("pages").where("id","=",$).where("site_id","=",A).select(["path"]).executeTakeFirst();if(!J?.path)throw new C(`Parent page ${$} not found on this site`);return J.path==="/"?"":J.path}async function S2(M,A,$,J,X){let G=d2(J);if(!G)throw new C("Slug resolves to an empty string");let L=G;for(let B=2;B<100;B++){let Y=`${$}/${L}`,Q=M.selectFrom("pages").where("site_id","=",A).where("path","=",Y).select(["id"]);if(X)Q=Q.where("id","!=",X);if(!await Q.executeTakeFirst())return{slug:L,path:Y};L=`${G}-${B}`}throw new C(`Could not find a free path for "${G}"`)}async function r2(M,A){let $=await Z(),J=await y(A.blocks??[]);if(!J.ok)throw new C("Invalid page blocks",J.errors);let X=A.slug==="/",G=X?"":await U2($,M,A.parentId),{slug:L,path:B}=X?{slug:"/",path:"/"}:await S2($,M,G,A.slug||A.title),Y=C2(new Date),Q=await $.insertInto("pages").values({site_id:M,author_id:A.authorId??null,parent_id:X?null:A.parentId??null,title:A.title,slug:L,path:B,template:A.template??"default",blocks:JSON.stringify(J.blocks),meta_description:A.metaDescription??null,status:A.status??"draft",scheduled_at:A.scheduledAt??null,published_at:A.status==="published"?Y:null,views:0,conversions:0,created_at:Y,updated_at:Y}).returningAll().executeTakeFirst(),H=Number(Q?.id);if(!H){let z=await $.selectFrom("pages").where("site_id","=",M).where("path","=",B).select(["id"]).executeTakeFirst();if(!z)throw Error("Failed to create page");return{id:Number(z.id),path:B,blocks:J.blocks}}return{id:H,path:B,blocks:J.blocks}}async function a2(M,A,$){let J=await Z(),X=await J.selectFrom("pages").where("id","=",A).where("site_id","=",M).select(["id","site_id","parent_id","title","slug","path","blocks","meta_description"]).executeTakeFirst();if(!X)throw new C(`Page ${A} not found on this site`);let G=await y($.blocks??[]);if(!G.ok)throw new C("Invalid page blocks",G.errors);await I(A,{title:X.title,blocks:X.blocks,metaDescription:X.meta_description},{authorId:$.authorId,note:$.note??null});let L=($.slug??X.slug)==="/",B=$.parentId!==void 0?$.parentId:X.parent_id,Y=L?"":await U2(J,M,B),Q=$.slug||X.slug||$.title,{slug:H,path:z}=L?{slug:"/",path:"/"}:await S2(J,M,Y,Q,A),x=C2(new Date);if(await J.updateTable("pages").set({title:$.title,slug:H,path:z,parent_id:L?null:B??null,...$.template!==void 0&&{template:$.template},blocks:JSON.stringify(G.blocks),meta_description:$.metaDescription??null,...$.status!==void 0&&{status:$.status},...$.scheduledAt!==void 0&&{scheduled_at:$.scheduledAt},...$.status==="published"&&{published_at:x},updated_at:x}).where("id","=",A).execute(),X.path&&X.path!==z){let ZM=[{fromPath:X.path,toPath:z}],T2=await J.selectFrom("pages").where("site_id","=",M).where("path","like",`${X.path}/%`).select(["id","path"]).execute();for(let h of T2){let GM=z+h.path.slice(X.path.length);await J.updateTable("pages").set({path:GM,updated_at:x}).where("id","=",h.id).execute(),ZM.push({fromPath:h.path,toPath:GM})}await MM(M,ZM)}return{id:A,path:z,blocks:G.blocks}}import{formatDate as t2}from"@stacksjs/orm";async function e2(M=new Date){let A=await Z(),$=t2(M),J=await A.selectFrom("pages").where("status","=","scheduled").where("scheduled_at","is not",null).where("scheduled_at","<=",$).select(["id"]).execute();if(J.length===0)return 0;return await A.updateTable("pages").set({status:"published",published_at:$,updated_at:$}).where("id","in",J.map((X)=>X.id)).execute(),J.length}import{requestHost as L$,resolveSiteByHost as Y$,sitesOptions as B$}from"@stacksjs/sites";import{existsSync as X$}from"fs";import{join as j2}from"path";import E2 from"process";var M$=/<\/?(?:script|style|iframe|object|embed|form|link|meta|base)\b[^>]*>/gi,$$=/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi,A$=/\s+(href|src)\s*=\s*(["']?)\s*javascript:[^"'\s>]*\2/gi,J$=/\s+(href|src)\s*=\s*(["']?)\s*data:text\/html[^"'\s>]*\2/gi;function v(M){return M.replace(M$,"").replace($$,"").replace(A$,' $1="#"').replace(J$,' $1="#"')}var AM=null;async function F2(){if(!AM)AM=(await import("@stacksjs/stx")).renderTemplate;return AM}function N2(M){let A=[j2(E2.cwd(),"resources/views",M),j2(E2.cwd(),"storage/framework/defaults/resources/views",M)];for(let $ of A)if(X$($))return $;return null}async function Z$(M,A){let $=k(M.type);if(!$)return"";let J=N2(`${$.component}.stx`);if(!J)return"";let X={...M.props};if(typeof X.html==="string")X.html=v(X.html);if(Array.isArray(X.columns))X.columns=X.columns.map((L)=>L&&typeof L==="object"&&typeof L.html==="string"?{...L,html:v(L.html)}:L);let G=await F2();try{return await G(J,{context:{block:{id:M.id,type:M.type},props:X,site:A},templateOnly:!0,processClientScripts:!1,injectCSS:!1})}catch(L){return console.error(`[cms] block "${M.type}" failed to render: ${L.message}`),""}}async function JM(M,A){let J=(await Promise.all(A.blocks.map((B)=>Z$(B,M)))).join(`
3
- `),X=N2("cms/page.stx");if(!X)return new Response(`<!DOCTYPE html><html><head><title>${G$(A.title)}</title></head><body>${J}</body></html>`,{headers:{"Content-Type":"text/html; charset=utf-8"}});let L=await(await F2())(X,{context:{site:M,page:{id:A.id,title:A.title,path:A.path,template:A.template,metaDescription:A.metaDescription??""},content:J},injectCSS:!0,title:A.title});return new Response(L,{headers:{"Content-Type":"text/html; charset=utf-8","Cache-Control":"public, max-age=60",Vary:"Host"}})}function G$(M){return M.replace(/[&<>"']/g,(A)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[A])}function w(M){if(!M.startsWith("/"))M=`/${M}`;return M.length>1&&M.endsWith("/")?M.slice(0,-1):M}async function XM(M,A){let $=await Z(),J=w(A),X=await $.selectFrom("pages").where("site_id","=",M).where("path","=",J).where("status","=","published").select(["id","site_id","title","path","template","meta_description","blocks","published_at"]).executeTakeFirst();if(!X)return null;return{id:Number(X.id),siteId:Number(X.site_id),title:X.title,path:X.path,template:X.template,metaDescription:X.meta_description,blocks:d(X.blocks),publishedAt:X.published_at}}async function P2(M,A){if(!A)return null;if(M.method!=="GET"&&M.method!=="HEAD")return null;a();let $=new URL(M.url),J=w($.pathname),X=await XM(A.id,J);if(X)return await JM(A,X);let G=await e(A.id,J);if(G)return new Response(null,{status:G.statusCode,headers:{Location:G.toPath+$.search}});return null}async function Q$(M){if(M.method!=="GET"&&M.method!=="HEAD")return null;let A=new URL(M.url);if(/\.[a-z0-9]{1,8}$/i.test(A.pathname))return null;let $=B$();if(!$.enabled)return null;let J=await Y$(L$(M.headers,$),void 0,$);return await P2(M,J)}var z$={posts:n,postCategories:g,tags:i,comments:u,authors:b,pages:l},G1=z$;export{y as validateBlocks,a2 as updatePageDocument,i as tags,I as storeRevision,v as sanitizeRichText,o2 as restoreRevision,e as resolveRedirect,XM as resolvePublishedPage,JM as renderCmsPage,a as registerDefaultBlocks,o as registerBlocks,MM as recordSlugChangeRedirects,e2 as publishDuePages,n as posts,d as parseStoredBlocks,l as pages,w as normalizePath,k as getBlock,s2 as fetchRevisions,i2 as fetchMenuTree,K as defineBlock,x2 as defaultBlocks,G1 as default,r2 as createPageDocument,u as comments,P2 as cmsPageFallback,Q$ as cmsNotFoundFallback,z$ as cms,g as categorizable,b as authors,u2 as allBlocks,C as PageDocumentError};
1
+ import*as authors from"./authors";import*as postCategories from"./categorizables";import*as comments from"./commentables";import*as pages from"./pages";import*as posts from"./posts";import*as tags from"./taggables";export const cms={posts,postCategories,tags,comments,authors,pages};export default cms;export{authors,postCategories as categorizable,comments,pages,posts,tags};export{defaultBlocks,registerDefaultBlocks}from"./blocks/defaults";export{allBlocks,defineBlock,getBlock,parseStoredBlocks,registerBlocks,validateBlocks}from"./blocks/registry";export{fetchMenuTree}from"./menus";export{createPageDocument,PageDocumentError,updatePageDocument}from"./pages/document";export{publishDuePages}from"./publish";export{cmsNotFoundFallback,cmsPageFallback}from"./public/fallback";export{renderCmsPage}from"./public/render";export{normalizePath,resolvePublishedPage}from"./public/resolve";export{sanitizeRichText}from"./public/sanitize";export{recordSlugChangeRedirects,resolveRedirect}from"./redirects";export{fetchRevisions,restoreRevision,storeRevision}from"./revisions";
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";export async function fetchMenuTree(siteId,handle){const db=await getDb(),menu=await db.selectFrom("menus").where("site_id","=",siteId).where("handle","=",handle).select(["id"]).executeTakeFirst();if(!menu)return[];const rows=await db.selectFrom("menu_items").leftJoin("pages","pages.id","=","menu_items.page_id").where("menu_items.menu_id","=",menu.id).select(["menu_items.id as id","menu_items.label as label","menu_items.url as url","menu_items.target as target","menu_items.parent_id as parent_id","menu_items.position as position","menu_items.visibility as visibility","pages.path as page_path","pages.status as page_status"]).orderBy("menu_items.position","asc").execute(),toItem=(row)=>{const href=row.url??(row.page_status==="published"?row.page_path:null);if(!href)return null;return{id:row.id,label:row.label,href,target:row.target==="_blank"?"_blank":"_self",visibility:row.visibility==="auth"?"auth":"public",children:[]}},byId=new Map,roots=[];for(const row of rows){const item=toItem(row);if(item)byId.set(row.id,item)}for(const row of rows){const item=byId.get(row.id);if(!item)continue;const parent=row.parent_id?byId.get(row.parent_id):void 0;if(parent)parent.children.push(item);else roots.push(item)}return roots}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";export async function destroy(id){const db=await getDb();try{if(!await fetchById(id))throw Error(`Page with ID ${id} not found`);return(await db.deleteFrom("pages").where("id","=",id).executeTakeFirst()).numDeletedRows>0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete page: ${error.message}`);throw error}}export async function bulkDestroy(ids){const db=await getDb();if(!ids.length)return 0;try{const result=await db.deleteFrom("pages").where("id","in",ids).executeTakeFirst();return Number(result.numDeletedRows)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete pages in bulk: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{formatDate}from"@stacksjs/orm";import{slugify}from"ts-slug";import{validateBlocks}from"../blocks/registry";import{getDb}from"../database";import{recordSlugChangeRedirects}from"../redirects";import{storeRevision}from"../revisions";class PageDocumentError extends Error{details;status=422;constructor(message,details){super(message);this.details=details;this.name="PageDocumentError"}}async function parentPath(db,siteId,parentId){if(!parentId)return"";const parent=await db.selectFrom("pages").where("id","=",parentId).where("site_id","=",siteId).select(["path"]).executeTakeFirst();if(!parent?.path)throw new PageDocumentError(`Parent page ${parentId} not found on this site`);return parent.path==="/"?"":parent.path}async function uniquePathFor(db,siteId,basePath,requestedSlug,excludePageId){const cleaned=slugify(requestedSlug);if(!cleaned)throw new PageDocumentError("Slug resolves to an empty string");let candidate=cleaned;for(let attempt=2;attempt<100;attempt++){const path=`${basePath}/${candidate}`;let query=db.selectFrom("pages").where("site_id","=",siteId).where("path","=",path).select(["id"]);if(excludePageId)query=query.where("id","!=",excludePageId);if(!await query.executeTakeFirst())return{slug:candidate,path};candidate=`${cleaned}-${attempt}`}throw new PageDocumentError(`Could not find a free path for "${cleaned}"`)}export async function createPageDocument(siteId,input){const db=await getDb(),validated=await validateBlocks(input.blocks??[]);if(!validated.ok)throw new PageDocumentError("Invalid page blocks",validated.errors);const isRoot=input.slug==="/",base=isRoot?"":await parentPath(db,siteId,input.parentId),{slug,path}=isRoot?{slug:"/",path:"/"}:await uniquePathFor(db,siteId,base,input.slug||input.title),now=formatDate(new Date),written=await db.insertInto("pages").values({site_id:siteId,author_id:input.authorId??null,parent_id:isRoot?null:input.parentId??null,title:input.title,slug,path,template:input.template??"default",blocks:JSON.stringify(validated.blocks),meta_description:input.metaDescription??null,status:input.status??"draft",scheduled_at:input.scheduledAt??null,published_at:input.status==="published"?now:null,views:0,conversions:0,created_at:now,updated_at:now}).returningAll().executeTakeFirst(),id=Number(written?.id);if(!id){const row=await db.selectFrom("pages").where("site_id","=",siteId).where("path","=",path).select(["id"]).executeTakeFirst();if(!row)throw Error("Failed to create page");return{id:Number(row.id),path,blocks:validated.blocks}}return{id,path,blocks:validated.blocks}}export async function updatePageDocument(siteId,pageId,input){const db=await getDb(),current=await db.selectFrom("pages").where("id","=",pageId).where("site_id","=",siteId).select(["id","site_id","parent_id","title","slug","path","blocks","meta_description"]).executeTakeFirst();if(!current)throw new PageDocumentError(`Page ${pageId} not found on this site`);const validated=await validateBlocks(input.blocks??[]);if(!validated.ok)throw new PageDocumentError("Invalid page blocks",validated.errors);await storeRevision(pageId,{title:current.title,blocks:current.blocks,metaDescription:current.meta_description},{authorId:input.authorId,note:input.note??null});const isRoot=(input.slug??current.slug)==="/",nextParentId=input.parentId!==void 0?input.parentId:current.parent_id,base=isRoot?"":await parentPath(db,siteId,nextParentId),requestedSlug=input.slug||current.slug||input.title,{slug,path}=isRoot?{slug:"/",path:"/"}:await uniquePathFor(db,siteId,base,requestedSlug,pageId),now=formatDate(new Date);await db.updateTable("pages").set({title:input.title,slug,path,parent_id:isRoot?null:nextParentId??null,...input.template!==void 0&&{template:input.template},blocks:JSON.stringify(validated.blocks),meta_description:input.metaDescription??null,...input.status!==void 0&&{status:input.status},...input.scheduledAt!==void 0&&{scheduled_at:input.scheduledAt},...input.status==="published"&&{published_at:now},updated_at:now}).where("id","=",pageId).execute();if(current.path&&current.path!==path){const moves=[{fromPath:current.path,toPath:path}],descendants=await db.selectFrom("pages").where("site_id","=",siteId).where("path","like",`${current.path}/%`).select(["id","path"]).execute();for(const descendant of descendants){const nextPath=path+descendant.path.slice(current.path.length);await db.updateTable("pages").set({path:nextPath,updated_at:now}).where("id","=",descendant.id).execute();moves.push({fromPath:descendant.path,toPath:nextPath})}await recordSlugChangeRedirects(siteId,moves)}return{id:pageId,path,blocks:validated.blocks}}export{PageDocumentError};
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";export async function fetchById(id){return await(await getDb()).selectFrom("pages").where("id","=",id).selectAll().executeTakeFirst()}export async function fetchAll(){return await(await getDb()).selectFrom("pages").selectAll().execute()}export async function fetchByTemplate(template){return await(await getDb()).selectFrom("pages").where("template","=",template).selectAll().execute()}export async function fetchByAuthor(authorId){return await(await getDb()).selectFrom("pages").where("author_id","=",authorId).selectAll().execute()}export async function fetchByMinViews(minViews){return await(await getDb()).selectFrom("pages").where("views",">=",minViews).selectAll().execute()}export async function fetchByMinConversions(minConversions){return await(await getDb()).selectFrom("pages").where("conversions",">=",minConversions).selectAll().execute()}export async function fetchPublishedAfter(timestamp){return await(await getDb()).selectFrom("pages").where("published_at",">",timestamp).selectAll().execute()}
@@ -0,0 +1 @@
1
+ export{bulkDestroy,destroy}from"./destroy";export{fetchAll,fetchByAuthor,fetchById,fetchByMinConversions,fetchByMinViews,fetchPublishedAfter}from"./fetch";export{store}from"./store";export{update}from"./update";
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{resolveWrittenRow}from"../results";import{formatDate}from"@stacksjs/orm";export async function store(data){const db=await getDb();try{const pageData={author_id:data.author_id,title:data.title,template:data.template,views:data.views||0,conversions:data.conversions||0,created_at:formatDate(new Date),updated_at:formatDate(new Date)},written=await db.insertInto("pages").values(pageData).returningAll().executeTakeFirst(),result=await resolveWrittenRow(db,"pages",written);if(!result)throw Error("Failed to create page");return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to create page: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";import{isRow}from"../results";import{formatDate}from"@stacksjs/orm";export async function update(id,data){const db=await getDb();try{const updateData={...data,updated_at:formatDate(new Date)},result=await db.updateTable("pages").set(updateData).where("id","=",id).returningAll().executeTakeFirst();if(isRow(result))return result;const page=await fetchById(id);if(!page)throw Error("Failed to update page");return page}catch(error){if(error instanceof Error)throw TypeError(`Failed to update page: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";import{affectedRows}from"../results";export async function destroy(id){const db=await getDb();try{if(!await fetchById(id))throw Error(`Post with ID ${id} not found`);const result=await db.deleteFrom("posts").where("id","=",id).executeTakeFirst();return affectedRows(result,"numDeletedRows")>0}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete post: ${error.message}`);throw error}}export async function bulkDestroy(ids){const db=await getDb();if(!ids.length)return 0;try{const existingIds=(await db.selectFrom("posts").select(["id"]).where("id","in",ids).execute()).map((post)=>post.id);if(!existingIds.length)return 0;let deleted=0;for(const existingId of existingIds){const result=await db.deleteFrom("posts").where("id","=",existingId).executeTakeFirst();deleted+=affectedRows(result,"numDeletedRows")}return deleted}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete posts in bulk: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";export async function fetchById(id){return await(await getDb()).selectFrom("posts").where("id","=",id).selectAll().executeTakeFirst()}export async function fetchAll(){return await(await getDb()).selectFrom("posts").selectAll().execute()}export async function fetchByStatus(status){return await(await getDb()).selectFrom("posts").where("status","=",status).selectAll().execute()}export async function fetchByCategory(category){return await(await getDb()).selectFrom("posts").where("category","=",category).selectAll().execute()}export async function fetchByAuthor(authorId){return await(await getDb()).selectFrom("posts").where("author_id","=",authorId).selectAll().execute()}export async function fetchByMinViews(minViews){return await(await getDb()).selectFrom("posts").where("views",">=",minViews).selectAll().execute()}export async function fetchPublishedAfter(timestamp){return await(await getDb()).selectFrom("posts").where("published_at",">",timestamp).selectAll().execute()}
@@ -0,0 +1 @@
1
+ export{bulkDestroy,destroy}from"./destroy";export{fetchAll,fetchByAuthor,fetchByCategory,fetchById,fetchByMinViews,fetchByStatus,fetchPublishedAfter}from"./fetch";export{attach,detach,store,sync}from"./store";export{update}from"./update";
@@ -0,0 +1 @@
1
+ import{randomUUIDv7}from"bun";import{getDb}from"../database";import{fetchById}from"./fetch";import{insertedId,isRow}from"../results";import{formatDate}from"@stacksjs/orm";export const POST_STATUS_DRAFT="draft",POST_STATUS_PUBLISHED="published",POST_STATUS_ARCHIVED="archived";export async function store(data){const db=await getDb();try{if(!data.title||typeof data.title==="string"&&data.title.trim()==="")throw Error("Post title is required");const content=data.content||data.body,d=data,status=data.status||POST_STATUS_DRAFT,postData={author_id:d.author_id,uuid:randomUUIDv7(),title:data.title,poster:data.poster,content,excerpt:data.excerpt,is_featured:d.is_featured?Date.now():void 0,views:data.views||0,published_at:d.published_at??(status===POST_STATUS_PUBLISHED?Date.now():void 0),status},result=await db.insertInto("posts").values(postData).returningAll().executeTakeFirst();if(isRow(result))return result;const id=insertedId(result),post=id===void 0?void 0:await fetchById(id);if(!post)throw Error("Failed to create post");return post}catch(error){if(error instanceof Error)throw TypeError(`Failed to create post: ${error.message}`);throw error}}export async function attach(postId,tableName,ids){const db=await getDb();try{const postForeignKey=tableName==="categorizable_models"?"categorizable_id":"taggable_id",postTypeField=tableName==="categorizable_models"?"categorizable_type":"taggable_type",tablePrimary=tableName==="categorizable_models"?"category_id":"tag_id",pivotData=ids.map((id)=>({[tablePrimary]:id,[postForeignKey]:postId,[postTypeField]:"posts",created_at:formatDate(new Date),updated_at:formatDate(new Date)}));for(const data of pivotData)await db.insertInto(tableName).values(data).execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to attach records: ${error.message}`);throw error}}export async function detach(postId,tableName,ids){const db=await getDb();try{const postForeignKey=tableName==="categorizable_models"?"categorizable_id":"taggable_id",postTypeField=tableName==="categorizable_models"?"categorizable_type":"taggable_type",relatedForeignKey=tableName==="categorizable_models"?"category_id":"tag_id",query=db.deleteFrom(tableName).where(postForeignKey,"=",postId).where(postTypeField,"=","posts");if(ids){for(const relatedId of ids)await db.deleteFrom(tableName).where(postForeignKey,"=",postId).where(postTypeField,"=","posts").where(relatedForeignKey,"=",relatedId).execute();return}await query.execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to detach records: ${error.message}`);throw error}}export async function sync(postId,tableName,ids){const db=await getDb();try{const postForeignKey=tableName==="categorizable_models"?"categorizable_id":"taggable_id",postTypeField=tableName==="categorizable_models"?"categorizable_type":"taggable_type",relatedForeignKey=tableName==="categorizable_models"?"category_id":"tag_id",existingIds=(await db.selectFrom(tableName).select([relatedForeignKey]).where(postForeignKey,"=",postId).where(postTypeField,"=","posts").execute()).map((rel)=>rel[relatedForeignKey]),idsToRemove=existingIds.filter((id)=>!ids.includes(id)),idsToAdd=ids.filter((id)=>!existingIds.includes(id));if(idsToRemove.length>0)for(const relatedId of idsToRemove)await db.deleteFrom(tableName).where(postForeignKey,"=",postId).where(postTypeField,"=","posts").where(relatedForeignKey,"=",relatedId).execute();if(idsToAdd.length>0){const pivotData=idsToAdd.map((id)=>({[relatedForeignKey]:id,[postForeignKey]:postId,[postTypeField]:"posts",created_at:formatDate(new Date),updated_at:formatDate(new Date)}));await db.insertInto(tableName).values(pivotData).execute()}}catch(error){if(error instanceof Error)throw TypeError(`Failed to sync records: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchById}from"./fetch";import{isRow}from"../results";export async function update(id,data){const db=await getDb();try{if(data.title!==void 0&&typeof data.title==="string"&&data.title.trim()==="")throw Error("Post title cannot be empty");const validStatuses=["published","draft","archived"];if(data.status!==void 0&&typeof data.status==="string"&&!validStatuses.includes(data.status))throw Error(`Invalid post status: ${data.status}`);if(data.views!==void 0&&typeof data.views==="number"&&data.views<0)throw Error("Views count cannot be negative");const result=await db.updateTable("posts").set(data).where("id","=",id).returningAll().executeTakeFirst();if(isRow(result))return result;const post=await fetchById(id);if(!post)throw Error(`Post with ID ${id} not found`);return post}catch(error){if(error instanceof Error)throw TypeError(`Failed to update post: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{requestHost,resolveSiteByHost,sitesOptions}from"@stacksjs/sites";import{registerDefaultBlocks}from"../blocks/defaults";import{resolveRedirect}from"../redirects";import{renderCmsPage}from"./render";import{normalizePath,resolvePublishedPage}from"./resolve";export async function cmsPageFallback(req,site){if(!site)return null;if(req.method!=="GET"&&req.method!=="HEAD")return null;registerDefaultBlocks();const url=new URL(req.url),path=normalizePath(url.pathname),page=await resolvePublishedPage(site.id,path);if(page)return await renderCmsPage(site,page);const redirect=await resolveRedirect(site.id,path);if(redirect)return new Response(null,{status:redirect.statusCode,headers:{Location:redirect.toPath+url.search}});return null}export async function cmsNotFoundFallback(req){if(req.method!=="GET"&&req.method!=="HEAD")return null;const url=new URL(req.url);if(/\.[a-z0-9]{1,8}$/i.test(url.pathname))return null;const options=sitesOptions();if(!options.enabled)return null;const site=await resolveSiteByHost(requestHost(req.headers,options),void 0,options);return await cmsPageFallback(req,site)}
@@ -0,0 +1,2 @@
1
+ import{existsSync}from"node:fs";import{join}from"node:path";import process from"node:process";import{getBlock}from"../blocks/registry";import{sanitizeRichText}from"./sanitize";let renderTemplateFn=null;async function getRenderTemplate(){if(!renderTemplateFn)renderTemplateFn=(await import("@stacksjs/stx")).renderTemplate;return renderTemplateFn}function resolveViewFile(relative){const candidates=[join(process.cwd(),"resources/views",relative),join(process.cwd(),"storage/framework/defaults/resources/views",relative)];for(const candidate of candidates)if(existsSync(candidate))return candidate;return null}async function renderBlock(block,site){const definition=getBlock(block.type);if(!definition)return"";const partial=resolveViewFile(`${definition.component}.stx`);if(!partial)return"";const props={...block.props};if(typeof props.html==="string")props.html=sanitizeRichText(props.html);if(Array.isArray(props.columns))props.columns=props.columns.map((column)=>column&&typeof column==="object"&&typeof column.html==="string"?{...column,html:sanitizeRichText(column.html)}:column);const renderTemplate=await getRenderTemplate();try{return await renderTemplate(partial,{context:{block:{id:block.id,type:block.type},props,site},templateOnly:!0,processClientScripts:!1,injectCSS:!1})}catch(error){console.error(`[cms] block "${block.type}" failed to render: ${error.message}`);return""}}export async function renderCmsPage(site,page){const content=(await Promise.all(page.blocks.map((block)=>renderBlock(block,site)))).join(`
2
+ `),template=resolveViewFile("cms/page.stx");if(!template)return new Response(`<!DOCTYPE html><html><head><title>${escapeHtml(page.title)}</title></head><body>${content}</body></html>`,{headers:{"Content-Type":"text/html; charset=utf-8"}});const html=await(await getRenderTemplate())(template,{context:{site,page:{id:page.id,title:page.title,path:page.path,template:page.template,metaDescription:page.metaDescription??""},content},injectCSS:!0,title:page.title});return new Response(html,{headers:{"Content-Type":"text/html; charset=utf-8","Cache-Control":"public, max-age=60",Vary:"Host"}})}function escapeHtml(value){return value.replace(/[&<>"']/g,(char)=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[char])}
@@ -0,0 +1 @@
1
+ import{parseStoredBlocks}from"../blocks/registry";import{getDb}from"../database";export function normalizePath(path){if(!path.startsWith("/"))path=`/${path}`;return path.length>1&&path.endsWith("/")?path.slice(0,-1):path}export async function resolvePublishedPage(siteId,rawPath){const db=await getDb(),path=normalizePath(rawPath),row=await db.selectFrom("pages").where("site_id","=",siteId).where("path","=",path).where("status","=","published").select(["id","site_id","title","path","template","meta_description","blocks","published_at"]).executeTakeFirst();if(!row)return null;return{id:Number(row.id),siteId:Number(row.site_id),title:row.title,path:row.path,template:row.template,metaDescription:row.meta_description,blocks:parseStoredBlocks(row.blocks),publishedAt:row.published_at}}
@@ -0,0 +1 @@
1
+ const VOID_OF_CONTENT=/<(script|style|iframe|object|embed|noscript|template)\b[^>]*>[\s\S]*?<\/\1\s*>/gi,BLOCKED_TAGS=/<\/?(?:script|style|iframe|object|embed|noscript|template|form|link|meta|base|svg|math)\b[^>]*>?/gi,EVENT_ATTRS=/\s+on[a-z]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi,DANGEROUS_URLS=/\s+(href|src|xlink:href|formaction|action)\s*=\s*(["']?)\s*(?:javascript|vbscript|data:text\/html)[^"'\s>]*\2/gi,MAX_PASSES=8;export function sanitizeRichText(html){let current=html;for(let pass=0;pass<MAX_PASSES;pass++){const before=current;current=current.replace(VOID_OF_CONTENT,"").replace(BLOCKED_TAGS,"").replace(EVENT_ATTRS,"").replace(DANGEROUS_URLS,' $1="#"');if(current===before)return current}return current.replace(/<[^>]*>/g,"")}
@@ -0,0 +1 @@
1
+ import{formatDate}from"@stacksjs/orm";import{getDb}from"../database";export async function publishDuePages(now=new Date){const db=await getDb(),cutoff=formatDate(now),due=await db.selectFrom("pages").where("status","=","scheduled").where("scheduled_at","is not",null).where("scheduled_at","<=",cutoff).select(["id"]).execute();if(due.length===0)return 0;await db.updateTable("pages").set({status:"published",published_at:cutoff,updated_at:cutoff}).where("id","in",due.map((row)=>row.id)).execute();return due.length}
@@ -0,0 +1 @@
1
+ import{formatDate}from"@stacksjs/orm";import{getDb}from"../database";export async function resolveRedirect(siteId,path){const row=await(await getDb()).selectFrom("redirects").where("site_id","=",siteId).where("from_path","=",path).select(["to_path","status_code"]).executeTakeFirst();if(!row)return null;return{toPath:row.to_path,statusCode:row.status_code===302?302:301}}export async function recordSlugChangeRedirects(siteId,moves){const db=await getDb();for(const move of moves){if(!move.fromPath||move.fromPath===move.toPath)continue;await db.deleteFrom("redirects").where("site_id","=",siteId).where("from_path","=",move.toPath).execute();await db.deleteFrom("redirects").where("site_id","=",siteId).where("from_path","=",move.fromPath).execute();await db.updateTable("redirects").set({to_path:move.toPath,updated_at:formatDate(new Date)}).where("site_id","=",siteId).where("to_path","=",move.fromPath).execute();await db.insertInto("redirects").values({site_id:siteId,from_path:move.fromPath,to_path:move.toPath,status_code:301,source:"slug-change",created_at:formatDate(new Date),updated_at:formatDate(new Date)}).execute()}}
@@ -0,0 +1 @@
1
+ export function isRow(value){return!!value&&typeof value==="object"&&"id"in value}export function insertedId(result){if(isRow(result)&&typeof result.id==="number")return result.id;const rowid=result?.lastInsertRowid;if(rowid===void 0||rowid===null)return;const id=Number(rowid);return Number.isSafeInteger(id)&&id>0?id:void 0}export async function resolveWrittenRow(db,table,result){if(isRow(result))return result;const id=insertedId(result);if(id===void 0)return;return await db.selectFrom(table).where("id","=",id).selectAll().executeTakeFirst()}export function affectedRows(result,key){const raw=result?.[key]??result?.changes;if(typeof raw==="number"||typeof raw==="bigint")return Number(raw);const changes=raw?.changes;return typeof changes==="number"||typeof changes==="bigint"?Number(changes):0}
@@ -0,0 +1 @@
1
+ import{formatDate}from"@stacksjs/orm";import{getDb}from"../database";const REVISION_COLUMNS=["id","page_id","author_id","revision","title","blocks","meta_description","note","created_at"];export async function storeRevision(pageId,snapshot,options={}){const db=await getDb(),keep=options.keep??50,revision=((await db.selectFrom("page_revisions").where("page_id","=",pageId).select(["revision"]).orderBy("revision","desc").limit(1).executeTakeFirst())?.revision??0)+1;await db.insertInto("page_revisions").values({page_id:pageId,author_id:options.authorId??null,revision,title:snapshot.title,blocks:snapshot.blocks,meta_description:snapshot.metaDescription,note:options.note??null,created_at:formatDate(new Date),updated_at:formatDate(new Date)}).execute();const stale=await db.selectFrom("page_revisions").where("page_id","=",pageId).select(["id"]).orderBy("revision","desc").offset(keep).limit(1000).execute();if(stale.length>0)await db.deleteFrom("page_revisions").where("id","in",stale.map((row)=>row.id)).execute();return revision}export async function fetchRevisions(pageId,limit=50){return await(await getDb()).selectFrom("page_revisions").where("page_id","=",pageId).select([...REVISION_COLUMNS]).orderBy("revision","desc").limit(limit).execute()}export async function restoreRevision(revisionId,options={}){const db=await getDb(),revision=await db.selectFrom("page_revisions").where("id","=",revisionId).select([...REVISION_COLUMNS]).executeTakeFirst();if(!revision)throw Error(`Page revision ${revisionId} not found`);const page=await db.selectFrom("pages").where("id","=",revision.page_id).select(["id","title","blocks","meta_description"]).executeTakeFirst();if(!page)throw Error(`Page ${revision.page_id} not found for revision ${revisionId}`);await storeRevision(page.id,{title:page.title,blocks:page.blocks,metaDescription:page.meta_description},{authorId:options.authorId,note:`before restoring revision ${revision.revision}`});await db.updateTable("pages").set({title:revision.title,blocks:revision.blocks,meta_description:revision.meta_description,updated_at:formatDate(new Date)}).where("id","=",page.id).execute()}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";export async function destroy(id){const db=await getDb();if(!await db.selectFrom("taggables").where("id","=",id).selectAll().executeTakeFirst())throw Error(`Tag with ID ${id} not found`);await db.deleteFrom("taggables").where("id","=",id).executeTakeFirst()}export async function bulkDestroy(ids){const db=await getDb();try{await db.deleteFrom("taggables").where("id","in",ids).execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to delete tags: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{findOrCreate}from"./store";export async function fetchTagById(id){const db=await getDb();try{const result=await db.selectFrom("taggables").where("id","=",id).selectAll().executeTakeFirst();if(!result)return;return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tag: ${error.message}`);throw error}}export async function fetchTags(){const db=await getDb();try{return await db.selectFrom("taggables").where("is_active","=",!0).selectAll().execute()}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tags: ${error.message}`);throw error}}export async function firstOrCreate(name,taggableType,description){try{return await findOrCreate({name,taggable_type:taggableType,description})}catch(error){if(error instanceof Error)throw TypeError(`Failed to find or create tag: ${error.message}`);throw error}}export async function countTaggedPosts(taggableType){const db=await getDb();try{const result=await db.selectFrom("taggable_models").where("taggable_type","=",taggableType).count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count tagged posts: ${error.message}`);throw error}}export async function countTotalTags(){const db=await getDb();try{const result=await db.selectFrom("taggables").count();return Number(result)||0}catch(error){if(error instanceof Error)throw TypeError(`Failed to count total tags: ${error.message}`);throw error}}export async function findMostUsedTag(taggableType){const db=await getDb();try{let query=db.selectFrom("taggable_models").innerJoin("taggables","taggables.id","=","taggable_models.tag_id").select(["taggables.name"]).groupBy("taggables.name");if(taggableType)query=query.where("taggable_models.taggable_type","=",taggableType);const result=await query.orderBy("taggables.name","asc").executeTakeFirst();if(!result)return null;return{name:result.name,count:Number(result.usage_count||0)}}catch(error){if(error instanceof Error)throw TypeError(`Failed to find most used tag: ${error.message}`);throw error}}export async function findLeastUsedTag(){const db=await getDb();try{const result=await db.selectFrom("taggable_models").innerJoin("taggables","taggables.id","=","taggable_models.tag_id").select(["taggables.name"]).groupBy("taggables.name").orderBy("taggables.name","asc").executeTakeFirst();if(!result)return null;return{name:result.name,count:Number(result.usage_count||0)}}catch(error){if(error instanceof Error)throw TypeError(`Failed to find least used tag: ${error.message}`);throw error}}export async function fetchTagsWithPostCounts(){const db=await getDb();try{return(await db.selectFrom("taggables").leftJoin("taggable_models","taggables.id","=","taggable_models.tag_id").select(["taggables.name"]).groupBy("taggables.name").orderBy("taggables.name","desc").limit(10).execute()).map((row)=>({name:row.name,postCount:Number(row.post_count||0)}))}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tags with post counts: ${error.message}`);throw error}}export async function fetchTagDistribution(){const db=await getDb();try{const typedResult=await db.selectFrom("taggables").leftJoin("taggable_models","taggables.id","=","taggable_models.tag_id").select(["taggables.name"]).groupBy("taggables.name").orderBy("taggables.name","desc").execute(),totalCount=typedResult.reduce((sum,row)=>sum+Number(row.count||0),0);return typedResult.map((row)=>({name:row.name,count:Number(row.count||0),percentage:totalCount>0?Number(row.count||0)/totalCount*100:0}))}catch(error){if(error instanceof Error)throw TypeError(`Failed to fetch tag distribution: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ export{bulkDestroy,destroy}from"./destroy";export{fetchTagById,fetchTags}from"./fetch";export{store}from"./store";export{update}from"./update";
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{resolveWrittenRow}from"../results";import{slugify}from"ts-slug";export async function findOrCreateMany(names,taggableType){const tagIds=[];for(const name of names){const tag=await findOrCreate({name,taggable_type:taggableType});tagIds.push(tag.id)}return tagIds}export async function findOrCreate(data){const db=await getDb();try{const existingTag=await db.selectFrom("taggables").selectAll().where("name","=",data.name).executeTakeFirst();if(existingTag)return existingTag;return await store(data)}catch(error){if(error instanceof Error)throw TypeError(`Failed to find or create tag: ${error.message}`);throw error}}export async function store(data){const db=await getDb();try{if(!data.name||data.name.trim()==="")throw Error("Tag name is required");if(!data.taggable_type||data.taggable_type.trim()==="")throw Error("Tag taggable_type is required");const slug=slugify(data.name);if(await db.selectFrom("taggables").selectAll().where("slug","=",slug).executeTakeFirst())throw Error(`Tag with unique slug "${slug}" already exists`);const tagData={name:data.name,slug,description:data.description,is_active:data.is_active??!0,taggable_type:data.taggable_type};if(data.taggable_id!==void 0)tagData.taggable_id=data.taggable_id;const written=await db.insertInto("taggables").values(tagData).returningAll().executeTakeFirst(),result=await resolveWrittenRow(db,"taggables",written);if(!result)throw Error("Failed to create tag");return result}catch(error){if(error instanceof Error)throw TypeError(`Failed to store tag: ${error.message}`);throw error}}
@@ -0,0 +1 @@
1
+ import{getDb}from"../database";import{fetchTagById}from"./fetch";import{isRow}from"../results";import{uniqueSlug}from"@stacksjs/slug";export async function update(data){const db=await getDb();try{const id=data.id;if(!id)throw Error("Tag ID is required for update");if(data.name!==void 0){if(data.name.trim()==="")throw Error("Tag name cannot be empty");const slug=await uniqueSlug(data.name,{table:"taggables",column:"slug"});if(await db.selectFrom("taggables").selectAll().where("slug","=",slug).where("id","!=",id).executeTakeFirst())throw Error(`Tag with unique slug "${slug}" already exists`);data.slug=slug}const{id:_id,...updateData}=data,result=await db.updateTable("taggables").set(updateData).where("id","=",id).returningAll().executeTakeFirst();if(isRow(result))return result;const tag=await fetchTagById(id);if(!tag)throw Error("Failed to update tag");return tag}catch(error){if(error instanceof Error)throw TypeError(`Failed to update tag: ${error.message}`);throw error}}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Wipe the trait tables between tests. DELETE (not DROP) keeps the
3
+ * schema warm.
4
+ */
5
+ export declare function refreshDatabase(): Promise<void>;
@@ -0,0 +1,100 @@
1
+ import{existsSync,unlinkSync}from"node:fs";import{tmpdir}from"node:os";import{join}from"node:path";import process from"node:process";const DB_PATH=join(tmpdir(),`stacks-cms-${process.pid}.sqlite`);process.env.DB_CONNECTION="sqlite";process.env.DB_DATABASE_PATH=DB_PATH;process.env.APP_ENV="testing";const{acquireDbConfigLock,db,ensureDatabaseConfigLoaded,initializeDbConfig}=await import("@stacksjs/database"),releaseDbConfigLock=await acquireDbConfigLock();async function forceConfig(){await ensureDatabaseConfigLoaded();initializeDbConfig({app:{env:"testing"},database:{default:"sqlite",connections:{sqlite:{database:DB_PATH,prefix:""}}}})}for(const suffix of["","-wal","-shm"])if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`);await forceConfig();await db.unsafe(`
2
+ CREATE TABLE IF NOT EXISTS categorizables (
3
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ name VARCHAR(255) NOT NULL,
5
+ slug VARCHAR(255) NOT NULL,
6
+ description TEXT,
7
+ categorizable_type VARCHAR(255) NOT NULL,
8
+ is_active BOOLEAN NOT NULL DEFAULT 1,
9
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
10
+ updated_at TIMESTAMP
11
+ )
12
+ `).execute();await db.unsafe(`
13
+ CREATE TABLE IF NOT EXISTS categorizable_models (
14
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15
+ category_id INTEGER NOT NULL,
16
+ categorizable_id INTEGER NOT NULL,
17
+ categorizable_type VARCHAR(255) NOT NULL,
18
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
19
+ updated_at TIMESTAMP
20
+ )
21
+ `).execute();await db.unsafe(`
22
+ CREATE TABLE IF NOT EXISTS taggable_models (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ tag_id INTEGER NOT NULL,
25
+ taggable_id INTEGER NOT NULL,
26
+ taggable_type VARCHAR(255) NOT NULL,
27
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
28
+ updated_at TIMESTAMP
29
+ )
30
+ `).execute();await db.unsafe(`
31
+ CREATE TABLE IF NOT EXISTS pages (
32
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+ uuid VARCHAR(255),
34
+ site_id INTEGER,
35
+ author_id INTEGER,
36
+ parent_id INTEGER,
37
+ title VARCHAR(255) NOT NULL,
38
+ slug VARCHAR(255),
39
+ path VARCHAR(2048),
40
+ template VARCHAR(255) NOT NULL DEFAULT 'default',
41
+ blocks TEXT,
42
+ meta_description VARCHAR(320),
43
+ status VARCHAR(32) NOT NULL DEFAULT 'draft',
44
+ scheduled_at TIMESTAMP,
45
+ views INTEGER NOT NULL DEFAULT 0,
46
+ conversions INTEGER NOT NULL DEFAULT 0,
47
+ published_at TIMESTAMP,
48
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
49
+ updated_at TIMESTAMP
50
+ )
51
+ `).execute();await db.unsafe(`
52
+ CREATE UNIQUE INDEX IF NOT EXISTS pages_site_path_unique ON pages (site_id, path)
53
+ `).execute();await db.unsafe(`
54
+ CREATE TABLE IF NOT EXISTS page_revisions (
55
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
56
+ page_id INTEGER NOT NULL,
57
+ author_id INTEGER,
58
+ revision INTEGER NOT NULL,
59
+ title VARCHAR(255) NOT NULL,
60
+ blocks TEXT,
61
+ meta_description VARCHAR(320),
62
+ note VARCHAR(500),
63
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
64
+ updated_at TIMESTAMP
65
+ )
66
+ `).execute();await db.unsafe(`
67
+ CREATE TABLE IF NOT EXISTS redirects (
68
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
69
+ site_id INTEGER NOT NULL,
70
+ from_path VARCHAR(2048) NOT NULL,
71
+ to_path VARCHAR(2048) NOT NULL,
72
+ status_code INTEGER NOT NULL DEFAULT 301,
73
+ source VARCHAR(32) NOT NULL DEFAULT 'manual',
74
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
75
+ updated_at TIMESTAMP
76
+ )
77
+ `).execute();await db.unsafe(`
78
+ CREATE TABLE IF NOT EXISTS menus (
79
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
80
+ site_id INTEGER NOT NULL,
81
+ handle VARCHAR(64) NOT NULL,
82
+ name VARCHAR(255) NOT NULL,
83
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
84
+ updated_at TIMESTAMP
85
+ )
86
+ `).execute();await db.unsafe(`
87
+ CREATE TABLE IF NOT EXISTS menu_items (
88
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
89
+ menu_id INTEGER NOT NULL,
90
+ page_id INTEGER,
91
+ label VARCHAR(120) NOT NULL,
92
+ url VARCHAR(2048),
93
+ target VARCHAR(8) DEFAULT '_self',
94
+ parent_id INTEGER,
95
+ position INTEGER DEFAULT 0,
96
+ visibility VARCHAR(16) DEFAULT 'public',
97
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
98
+ updated_at TIMESTAMP
99
+ )
100
+ `).execute();const tableNames=["categorizables","categorizable_models","taggable_models","pages","page_revisions","redirects","menus","menu_items"];export async function refreshDatabase(){await forceConfig();for(const table of tableNames)await db.unsafe(`DELETE FROM ${table}`).execute()}process.on("exit",()=>{for(const suffix of["","-wal","-shm"])try{if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`)}catch{}releaseDbConfigLock()});
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/cms",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.71.2",
5
+ "version": "0.71.10",
6
6
  "description": "Stacks cms utilities.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,8 +58,8 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/sites": "0.71.2",
62
- "@stacksjs/validation": "0.71.2",
61
+ "@stacksjs/sites": "0.71.10",
62
+ "@stacksjs/validation": "0.71.10",
63
63
  "ts-slug": "^0.1.0",
64
64
  "ts-spreadsheets": "^0.2.0"
65
65
  },