@stacksjs/cms 0.70.379 → 0.71.1
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.
- package/dist/blocks/defaults.d.ts +10 -0
- package/dist/blocks/registry.d.ts +19 -0
- package/dist/blocks/types.d.ts +32 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +2 -1
- package/dist/menus/index.d.ts +14 -0
- package/dist/pages/document.d.ts +37 -0
- package/dist/public/fallback.d.ts +18 -0
- package/dist/public/render.d.ts +13 -0
- package/dist/public/resolve.d.ts +20 -0
- package/dist/public/sanitize.d.ts +1 -0
- package/dist/publish/index.d.ts +7 -0
- package/dist/redirects/index.d.ts +18 -0
- package/dist/revisions/index.d.ts +29 -0
- package/package.json +4 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BlockDefinition } from './types';
|
|
2
|
+
/** Idempotent - both the serving layer and the dashboard call this at boot. */
|
|
3
|
+
export declare function registerDefaultBlocks(): void;
|
|
4
|
+
/**
|
|
5
|
+
* The block vocabulary every Stacks CMS site starts with. Apps register
|
|
6
|
+
* domain blocks (staff-grid, athletics-schedule, ...) on top and may
|
|
7
|
+
* re-register any of these to change its schema; partials resolve
|
|
8
|
+
* app-dir-first, so overriding the LOOK never needs a re-register.
|
|
9
|
+
*/
|
|
10
|
+
export declare const defaultBlocks: BlockDefinition[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BlockDefinition, PageBlock, ValidateBlocksResult } from './types';
|
|
2
|
+
export declare function defineBlock(definition: BlockDefinition): BlockDefinition;
|
|
3
|
+
/** Register blocks. Later registrations win, so an app can replace a default. */
|
|
4
|
+
export declare function registerBlocks(definitions: BlockDefinition[]): void;
|
|
5
|
+
export declare function getBlock(type: string): BlockDefinition | undefined;
|
|
6
|
+
export declare function allBlocks(): BlockDefinition[];
|
|
7
|
+
/**
|
|
8
|
+
* Validate an untrusted block document (dashboard save, API write) into a
|
|
9
|
+
* typed one. Unknown block types, unknown props, missing required props and
|
|
10
|
+
* failed rules all reject - a page document is either fully valid or not
|
|
11
|
+
* stored.
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateBlocks(input: unknown): Promise<ValidateBlocksResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Parse a stored `blocks` column. Storage is trusted (it passed
|
|
16
|
+
* validateBlocks on the way in), so this only guards shape, not schemas -
|
|
17
|
+
* a render must not 500 because a block type was since unregistered.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseStoredBlocks(stored: unknown): PageBlock[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** One block in a page document: stable id, registered type, typed props. */
|
|
2
|
+
export declare interface PageBlock {
|
|
3
|
+
id: string
|
|
4
|
+
type: string
|
|
5
|
+
props: Record<string, unknown>
|
|
6
|
+
}
|
|
7
|
+
export declare interface BlockPropDefinition {
|
|
8
|
+
rule: BlockPropRule
|
|
9
|
+
required?: boolean
|
|
10
|
+
}
|
|
11
|
+
export declare interface BlockDefinition {
|
|
12
|
+
type: string
|
|
13
|
+
label: string
|
|
14
|
+
component: string
|
|
15
|
+
schema: Record<string, BlockPropDefinition>
|
|
16
|
+
icon?: string
|
|
17
|
+
}
|
|
18
|
+
export declare interface BlockError {
|
|
19
|
+
index: number
|
|
20
|
+
type?: string
|
|
21
|
+
prop?: string
|
|
22
|
+
message: string
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A prop validator: return true for valid, or a message for invalid. Kept
|
|
26
|
+
* structural (not tied to ts-validation's class) so block schemas can use
|
|
27
|
+
* `schema.*` rules or plain functions interchangeably.
|
|
28
|
+
*/
|
|
29
|
+
export type BlockPropRule = | { validate: (value: unknown) => boolean | Promise<boolean> }
|
|
30
|
+
| ((value: unknown) => boolean | string);
|
|
31
|
+
export type ValidateBlocksResult = | { ok: true, blocks: PageBlock[] }
|
|
32
|
+
| { ok: false, errors: BlockError[] }
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import * as pages from './pages/index';
|
|
|
4
4
|
import * as postCategories from './categorizables/index';
|
|
5
5
|
import * as posts from './posts/index';
|
|
6
6
|
import * as tags from './taggables/index';
|
|
7
|
+
export type { BlockDefinition, BlockError, PageBlock, ValidateBlocksResult } from './blocks/types';
|
|
8
|
+
export type { MenuTreeItem } from './menus/index';
|
|
9
|
+
export type { SavedPageDocument, SavePageDocumentInput } from './pages/document';
|
|
10
|
+
export type { PublishedPage } from './public/resolve';
|
|
7
11
|
export declare const cms: CmsNamespace;
|
|
8
12
|
export declare interface CmsNamespace {
|
|
9
13
|
posts: PostsModule
|
|
@@ -27,4 +31,16 @@ export {
|
|
|
27
31
|
posts,
|
|
28
32
|
tags,
|
|
29
33
|
};
|
|
34
|
+
// Real pages: block documents, revisions, redirects, menus, public serving.
|
|
35
|
+
export { defaultBlocks, registerDefaultBlocks } from './blocks/defaults';
|
|
36
|
+
export { allBlocks, defineBlock, getBlock, parseStoredBlocks, registerBlocks, validateBlocks } from './blocks/registry';
|
|
37
|
+
export { fetchMenuTree } from './menus/index';
|
|
38
|
+
export { createPageDocument, PageDocumentError, updatePageDocument } from './pages/document';
|
|
39
|
+
export { publishDuePages } from './publish/index';
|
|
40
|
+
export { cmsNotFoundFallback, cmsPageFallback } from './public/fallback';
|
|
41
|
+
export { renderCmsPage } from './public/render';
|
|
42
|
+
export { normalizePath, resolvePublishedPage } from './public/resolve';
|
|
43
|
+
export { sanitizeRichText } from './public/sanitize';
|
|
44
|
+
export { recordSlugChangeRedirects, resolveRedirect } from './redirects/index';
|
|
45
|
+
export { fetchRevisions, restoreRevision, storeRevision } from './revisions/index';
|
|
30
46
|
export default cms;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var iM=Object.defineProperty;var sM=(M)=>M;function oM(M,f){this[M]=sM.bind(null,f)}var Q=(M,f)=>{for(var B in f)iM(M,B,{get:f[B],enumerable:!0,configurable:!0,set:oM.bind(f,B)})};var dM=import.meta.require;var F={};Q(F,{update:()=>s,store:()=>b,findOrCreate:()=>l,findByUuid:()=>D,findByUserId:()=>u,findByName:()=>p,findByEmail:()=>m,fetchById:()=>_,fetchAll:()=>w,destroy:()=>y});var c;async function C(){return c??=import("@stacksjs/database").then((M)=>M.db),c}async function y(M){let f=await C();try{let B=await f.deleteFrom("authors").where("id","=",M).returningAll().executeTakeFirst();if(!B)throw Error(`Author with ID ${M} not found`);return B}catch(B){if(B instanceof Error)throw TypeError(`Author with ID ${M} not found`);throw B}}async function _(M){return await(await C()).selectFrom("authors").where("id","=",M).selectAll().executeTakeFirst()}async function w(){return await(await C()).selectFrom("authors").selectAll().execute()}async function p(M){return await(await C()).selectFrom("authors").where("name","=",M).selectAll().executeTakeFirst()}async function m(M){return await(await C()).selectFrom("authors").where("email","=",M).selectAll().executeTakeFirst()}async function D(M){return await(await C()).selectFrom("authors").where("uuid","=",M).selectAll().executeTakeFirst()}async function u(M){return await(await C()).selectFrom("authors").where("user_id","=",M).selectAll().executeTakeFirst()}var{randomUUIDv7:g}=globalThis.Bun;function $(M){return!!M&&typeof M==="object"&&"id"in M}function E(M){if($(M)&&typeof M.id==="number")return M.id;let f=M?.lastInsertRowid;if(f===void 0||f===null)return;let B=Number(f);return Number.isSafeInteger(B)&&B>0?B:void 0}async function z(M,f,B){if($(B))return B;let x=E(B);if(x===void 0)return;return await M.selectFrom(f).where("id","=",x).selectAll().executeTakeFirst()}function U(M,f){let B=M?.[f]??M?.changes;if(typeof B==="number"||typeof B==="bigint")return Number(B);let x=B?.changes;return typeof x==="number"||typeof x==="bigint"?Number(x):0}import{HttpError as W}from"@stacksjs/error-handling";import{formatDate as V,isUniqueViolation as n}from"@stacksjs/orm";async function l(M){let f=await C();try{let B=await f.selectFrom("authors").where("email","=",M.email).orWhere("name","=",M.name).selectAll().executeTakeFirst();if(B)return B;let x=await f.selectFrom("users").where("email","=",M.email).selectAll().executeTakeFirst();if(!x){let Z=await f.insertInto("users").values({email:M.email,name:M.name,password:g(),uuid:g(),created_at:V(new Date),updated_at:V(new Date)}).returningAll().executeTakeFirst();if(x=await z(f,"users",Z),!x)throw Error("Failed to create user")}let P={user_id:x.id,name:M.name,email:M.email,created_at:V(new Date),updated_at:V(new Date)},J=await f.insertInto("authors").values(P).returningAll().executeTakeFirst(),X=await z(f,"authors",J);if(!X)throw Error("Failed to create author");return X}catch(B){if(B instanceof W)throw B;if(n(B))throw new W(409,"An author with this email already exists");if(B instanceof Error)throw TypeError(`Failed to find or create author: ${B.message}`);throw B}}async function b(M){let f=await C();try{let B=await f.selectFrom("authors").where("email","=",M.email).orWhere("name","=",M.name).selectAll().executeTakeFirst();if(B)return B;let x={user_id:M.user_id,name:M.name,email:M.email,created_at:V(new Date),updated_at:V(new Date)},P=await f.insertInto("authors").values(x).returningAll().executeTakeFirst(),J=await z(f,"authors",P);if(!J)throw Error("Failed to create author");return J}catch(B){if(B instanceof W)throw B;if(n(B))throw new W(409,"An author with this email already exists");if(B instanceof Error)throw TypeError(`Failed to find or create author: ${B.message}`);throw B}}import{HttpError as i}from"@stacksjs/error-handling";import{formatDate as rM,isUniqueViolation as eM}from"@stacksjs/orm";async function s(M,f){let B=await C();try{let x={...f,updated_at:rM(new Date)},P=await B.updateTable("authors").set(x).where("id","=",M).returningAll().executeTakeFirst();if($(P))return P;return await _(M)}catch(x){if(x instanceof i)throw x;if(eM(x))throw new i(409,"An author with this email already exists");if(x instanceof Error)throw TypeError(`Failed to update author: ${x.message}`);throw x}}var N={};Q(N,{update:()=>PM,storeCategorizableModel:()=>fM,store:()=>BM,fetchWithPosts:()=>e,fetchBySlug:()=>r,fetchByName:()=>d,fetchById:()=>L,fetchAll:()=>o,destroy:()=>t,bulkStore:()=>xM,bulkDestroy:()=>a});import{slugify as TB}from"ts-slug";async function L(M){return await(await C()).selectFrom("categorizables").where("id","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function o(){return await(await C()).selectFrom("categorizables").selectAll().execute()}async function d(M){return await(await C()).selectFrom("categorizables").where("name","=",M).where("is_active","=",!0).selectAll().execute()}async function r(M){return await(await C()).selectFrom("categorizables").where("slug","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function e(M){return await(await C()).selectFrom("categorizables").where("id","=",M).where("is_active","=",!0).selectAll().executeTakeFirst()}async function t(M){let f=await C();try{if(!await L(M))throw Error(`Category with ID ${M} not found`);return(await f.deleteFrom("categorizables").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete category: ${B.message}`);throw B}}async function a(M){let f=await C();if(!M.length)return 0;try{let B=await f.deleteFrom("categorizables").where("id","in",M).executeTakeFirst();return Number(B.numDeletedRows)||0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete categories in bulk: ${B.message}`);throw B}}import{slugify as MM}from"ts-slug";async function BM(M){let f=await C();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 B={name:M.name,slug:MM(M.name),description:M.description,categorizable_type:M.categorizable_type,is_active:M.is_active??!0},x=await f.insertInto("categorizables").values(B).returningAll().executeTakeFirst(),P=await z(f,"categorizables",x);if(!P)throw Error("Failed to create category");return P}catch(B){if(B instanceof Error)throw TypeError(`Failed to create category: ${B.message}`);throw B}}async function fM(M){let f=await C();try{let B={category_id:M.category_id,categorizable_type:M.categorizable_type},x=await f.insertInto("categorizable_models").values(B).returningAll().executeTakeFirst(),P=await z(f,"categorizable_models",x);if(!P)throw Error("Failed to create categorizable model relationship");return P}catch(B){if(B instanceof Error)throw TypeError(`Failed to create categorizable model relationship: ${B.message}`);throw B}}async function xM(M){let f=await C();try{return await f.transaction(async(x)=>{let P=[];for(let J of M){let X={name:J.name,slug:MM(J.name),description:J.description,categorizable_type:J.categorizable_type,is_active:J.is_active??!0},Z=await x.insertInto("categorizables").values(X).returningAll().executeTakeFirst(),Y=await z(x,"categorizables",Z);if(!Y)throw Error(`Failed to create category: ${J.name}`);P.push(Y)}return P})}catch(B){if(B instanceof Error)throw TypeError(`Failed to create categories: ${B.message}`);throw B}}import{slugify as tM}from"ts-slug";async function PM(M){let f=await C();try{let B=M.id;if(!B)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=tM(M.name)}let{id:x,...P}=M,J=await f.updateTable("categorizables").set(P).where("id","=",B).returningAll().executeTakeFirst();if($(J))return J;let X=await L(B);if(!X)throw Error("Failed to update category");return X}catch(B){if(B instanceof Error)throw TypeError(`Failed to update category: ${B.message}`);throw B}}var q={};Q(q,{update:()=>VM,store:()=>QM,fetchStatusDistributionDonut:()=>zM,fetchPostsWithMostComments:()=>h,fetchMonthlyCommentCounts:()=>GM,fetchCommentsByStatus:()=>YM,fetchCommentsByCommentables:()=>JM,fetchComments:()=>CM,fetchCommentCountByPeriod:()=>XM,fetchCommentCountBarGraph:()=>$M,fetchCommentById:()=>k,destroy:()=>AM,calculateApprovalRate:()=>ZM,bulkDestroy:()=>OM});import{sqlDateTime as aM}from"@stacksjs/database";import{formatDate as H}from"@stacksjs/orm";async function CM(M={}){let B=(await C()).selectFrom("commentables");if(M.status)B=B.where("status","=",M.status);if(M.commentables_id)B=B.where("commentables_id","=",M.commentables_id);if(M.commentables_type)B=B.where("commentables_type","=",M.commentables_type);if(M.limit)B=B.limit(M.limit);if(M.offset)B=B.offset(M.offset);return B.selectAll().execute()}async function k(M){return(await C()).selectFrom("commentables").where("id","=",M).selectAll().executeTakeFirst()}async function JM(M,f,B={}){let P=(await C()).selectFrom("commentables").where("commentables_id","=",M).where("commentables_type","=",f);if(B.status)P=P.where("status","=",B.status);if(B.limit)P=P.limit(B.limit);if(B.offset)P=P.offset(B.offset);return P.selectAll().execute()}async function XM(M){let f=await C();try{let B=await f.selectFrom("commentables").where("created_at",">=",aM(new Date(Date.now()-M*24*60*60*1000))).count();return Number(B)||0}catch(B){if(B instanceof Error)throw TypeError(`Failed to fetch comment count: ${B.message}`);throw B}}async function YM(M,f={}){let B=await C();try{let x=B.selectFrom("commentables").where("status","=",M);if(f.limit)x=x.limit(f.limit);if(f.offset)x=x.offset(f.offset);return x.selectAll().execute()}catch(x){if(x instanceof Error)throw TypeError(`Failed to fetch comments by status: ${x.message}`);throw x}}async function ZM(){let M=await C();try{let[f,B]=await Promise.all([M.selectFrom("commentables").where("status","=","approved").count(),M.selectFrom("commentables").count()]),x=Number(f||0),P=Number(B||0),J=P>0?x/P*100:0;return{approved:x,total:P,rate:J}}catch(f){if(f instanceof Error)throw TypeError(`Failed to calculate approval rate: ${f.message}`);throw f}}async function h(M,f={}){let B=await C();try{let x=B.selectFrom("posts").leftJoin("commentables","posts.id","=","commentables.commentables_id").where("commentables.created_at",">=",H(M.startDate)).where("commentables.created_at","<=",H(M.endDate)).select(["posts.id","posts.title"]).groupBy("posts.id","posts.title").orderBy("posts.id","desc");if(f.limit)x=x.limit(f.limit);return(await x.execute()).map((J)=>({id:Number(J.id),title:String(J.title),comment_count:Number(J.comment_count||0)}))}catch(x){if(x instanceof Error)throw TypeError(`Failed to fetch posts with most comments: ${x.message}`);throw x}}async function $M(M,f={}){try{let B=await h(M,f);return{labels:B.map((x)=>x.title),values:B.map((x)=>x.comment_count)}}catch(B){if(B instanceof Error)throw TypeError(`Failed to fetch bar graph data: ${B.message}`);throw B}}async function zM(M){let f=await C();try{let x=await f.selectFrom("commentables").where("created_at",">=",H(M.startDate)).where("created_at","<=",H(M.endDate)).select(["status"]).groupBy("status").execute(),P=x.reduce((Y,O)=>Y+Number(O.count||0),0),J=x.map((Y)=>String(Y.status)),X=x.map((Y)=>Number(Y.count||0)),Z=X.map((Y)=>P>0?Y/P*100:0);return{labels:J,values:X,percentages:Z}}catch(B){if(B instanceof Error)throw TypeError(`Failed to fetch status distribution: ${B.message}`);throw B}}async function GM(M){let f=await C();try{let B=await f.selectFrom("commentables").where("created_at",">=",H(M.startDate)).where("created_at","<=",H(M.endDate)).select(["created_at"]).groupBy("created_at").orderBy("created_at","asc").execute(),x=new Map;B.forEach((X)=>{if(!X.created_at)return;let Z=new Date(X.created_at),Y=`${Z.getFullYear()}-${String(Z.getMonth()+1).padStart(2,"0")}`;x.set(Y,(x.get(Y)||0)+Number(X.count||0))});let P=[],J=[];return x.forEach((X,Z)=>{let[Y,O]=Z.split("-");P.push(new Date(Number(Y),Number(O)-1).toLocaleString("default",{month:"short",year:"numeric"})),J.push(X)}),{labels:P,values:J}}catch(B){if(B instanceof Error)throw TypeError(`Failed to fetch monthly comment counts: ${B.message}`);throw B}}async function AM(M){let f=await C();try{if(!await k(M))throw Error(`Comment with ID ${M} not found`);return(await f.deleteFrom("commentables").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete comment: ${B.message}`);throw B}}async function OM(M){let f=await C();if(!M.length)return 0;try{let B=await f.deleteFrom("commentables").where("id","in",M).executeTakeFirst();return Number(B.numDeletedRows)||0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete comments in bulk: ${B.message}`);throw B}}import{sqlDateTime as Cf}from"@stacksjs/database";async function QM(M){let f=await C();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 B=["pending","approved","rejected"];if(M.status&&!B.includes(M.status))throw Error(`Invalid comment status: ${M.status}`);let x={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},P=await f.insertInto("commentables").values(x).returningAll().executeTakeFirst(),J=await z(f,"commentables",P);if(!J)throw Error("Failed to create comment");return J}catch(B){if(B instanceof Error)throw TypeError(`Failed to store comment: ${B.message}`);throw B}}import{formatDate as MB}from"@stacksjs/orm";async function VM(M,f){let B=await C();try{if(f.title!==void 0&&f.title.trim()==="")throw Error("Comment title cannot be empty");if(f.body!==void 0&&f.body.trim()==="")throw Error("Comment body cannot be empty");let x=["pending","approved","rejected"];if(f.status!==void 0&&!x.includes(f.status))throw Error(`Invalid comment status: ${f.status}`);let P={updated_at:MB(new Date)};if(f.title!==void 0)P.title=f.title;if(f.body!==void 0)P.body=f.body;if(f.status!==void 0)P.status=f.status;if(f.commentables_id!==void 0)P.commentables_id=f.commentables_id;if(f.commentables_type!==void 0)P.commentables_type=f.commentables_type;if(f.approved_at!==void 0)P.approved_at=f.approved_at;if(f.rejected_at!==void 0)P.rejected_at=f.rejected_at;return await B.updateTable("commentables").set(P).where("id","=",M).execute(),await B.selectFrom("commentables").where("id","=",M).selectAll().executeTakeFirst()}catch(x){if(x instanceof Error)throw TypeError(`Failed to update comment: ${x.message}`);throw x}}var v={};Q(v,{update:()=>UM,store:()=>EM,fetchPublishedAfter:()=>WM,fetchByMinViews:()=>SM,fetchByMinConversions:()=>_M,fetchById:()=>S,fetchByAuthor:()=>HM,fetchAll:()=>LM,destroy:()=>kM,bulkDestroy:()=>jM});async function S(M){return await(await C()).selectFrom("pages").where("id","=",M).selectAll().executeTakeFirst()}async function LM(){return await(await C()).selectFrom("pages").selectAll().execute()}async function HM(M){return await(await C()).selectFrom("pages").where("author_id","=",M).selectAll().execute()}async function SM(M){return await(await C()).selectFrom("pages").where("views",">=",M).selectAll().execute()}async function _M(M){return await(await C()).selectFrom("pages").where("conversions",">=",M).selectAll().execute()}async function WM(M){return await(await C()).selectFrom("pages").where("published_at",">",M).selectAll().execute()}async function kM(M){let f=await C();try{if(!await S(M))throw Error(`Page with ID ${M} not found`);return(await f.deleteFrom("pages").where("id","=",M).executeTakeFirst()).numDeletedRows>0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete page: ${B.message}`);throw B}}async function jM(M){let f=await C();if(!M.length)return 0;try{let B=await f.deleteFrom("pages").where("id","in",M).executeTakeFirst();return Number(B.numDeletedRows)||0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete pages in bulk: ${B.message}`);throw B}}import{formatDate as KM}from"@stacksjs/orm";async function EM(M){let f=await C();try{let B={author_id:M.author_id,title:M.title,template:M.template,views:M.views||0,conversions:M.conversions||0,created_at:KM(new Date),updated_at:KM(new Date)},x=await f.insertInto("pages").values(B).returningAll().executeTakeFirst(),P=await z(f,"pages",x);if(!P)throw Error("Failed to create page");return P}catch(B){if(B instanceof Error)throw TypeError(`Failed to create page: ${B.message}`);throw B}}import{formatDate as BB}from"@stacksjs/orm";async function UM(M,f){let B=await C();try{let x={...f,updated_at:BB(new Date)},P=await B.updateTable("pages").set(x).where("id","=",M).returningAll().executeTakeFirst();if($(P))return P;let J=await S(M);if(!J)throw Error("Failed to update page");return J}catch(x){if(x instanceof Error)throw TypeError(`Failed to update page: ${x.message}`);throw x}}var I={};Q(I,{update:()=>mM,sync:()=>pM,store:()=>cM,fetchPublishedAfter:()=>IM,fetchByStatus:()=>NM,fetchByMinViews:()=>vM,fetchById:()=>A,fetchByCategory:()=>hM,fetchByAuthor:()=>qM,fetchAll:()=>FM,detach:()=>wM,destroy:()=>TM,bulkDestroy:()=>RM,attach:()=>yM});async function A(M){return await(await C()).selectFrom("posts").where("id","=",M).selectAll().executeTakeFirst()}async function FM(){return await(await C()).selectFrom("posts").selectAll().execute()}async function NM(M){return await(await C()).selectFrom("posts").where("status","=",M).selectAll().execute()}async function hM(M){return await(await C()).selectFrom("posts").where("category","=",M).selectAll().execute()}async function qM(M){return await(await C()).selectFrom("posts").where("author_id","=",M).selectAll().execute()}async function vM(M){return await(await C()).selectFrom("posts").where("views",">=",M).selectAll().execute()}async function IM(M){return await(await C()).selectFrom("posts").where("published_at",">",M).selectAll().execute()}async function TM(M){let f=await C();try{if(!await A(M))throw Error(`Post with ID ${M} not found`);let x=await f.deleteFrom("posts").where("id","=",M).executeTakeFirst();return U(x,"numDeletedRows")>0}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete post: ${B.message}`);throw B}}async function RM(M){let f=await C();if(!M.length)return 0;try{let x=(await f.selectFrom("posts").select(["id"]).where("id","in",M).execute()).map((J)=>J.id);if(!x.length)return 0;let P=0;for(let J of x){let X=await f.deleteFrom("posts").where("id","=",J).executeTakeFirst();P+=U(X,"numDeletedRows")}return P}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete posts in bulk: ${B.message}`);throw B}}var{randomUUIDv7:fB}=globalThis.Bun;import{formatDate as j}from"@stacksjs/orm";var xB="draft",PB="published";async function cM(M){let f=await C();try{if(!M.title||typeof M.title==="string"&&M.title.trim()==="")throw Error("Post title is required");let B=M.content||M.body,x=M,P=M.status||xB,J={author_id:x.author_id,uuid:fB(),title:M.title,poster:M.poster,content:B,excerpt:M.excerpt,is_featured:x.is_featured?Date.now():void 0,views:M.views||0,published_at:x.published_at??(P===PB?Date.now():void 0),status:P},X=await f.insertInto("posts").values(J).returningAll().executeTakeFirst();if($(X))return X;let Z=E(X),Y=Z===void 0?void 0:await A(Z);if(!Y)throw Error("Failed to create post");return Y}catch(B){if(B instanceof Error)throw TypeError(`Failed to create post: ${B.message}`);throw B}}async function yM(M,f,B){let x=await C();try{let P=f==="categorizable_models"?"categorizable_id":"taggable_id",J=f==="categorizable_models"?"categorizable_type":"taggable_type",X=f==="categorizable_models"?"category_id":"tag_id",Z=B.map((Y)=>({[X]:Y,[P]:M,[J]:"posts",created_at:j(new Date),updated_at:j(new Date)}));for(let Y of Z)await x.insertInto(f).values(Y).execute()}catch(P){if(P instanceof Error)throw TypeError(`Failed to attach records: ${P.message}`);throw P}}async function wM(M,f,B){let x=await C();try{let P=f==="categorizable_models"?"categorizable_id":"taggable_id",J=f==="categorizable_models"?"categorizable_type":"taggable_type",X=f==="categorizable_models"?"category_id":"tag_id",Z=x.deleteFrom(f).where(P,"=",M).where(J,"=","posts");if(B){for(let Y of B)await x.deleteFrom(f).where(P,"=",M).where(J,"=","posts").where(X,"=",Y).execute();return}await Z.execute()}catch(P){if(P instanceof Error)throw TypeError(`Failed to detach records: ${P.message}`);throw P}}async function pM(M,f,B){let x=await C();try{let P=f==="categorizable_models"?"categorizable_id":"taggable_id",J=f==="categorizable_models"?"categorizable_type":"taggable_type",X=f==="categorizable_models"?"category_id":"tag_id",Y=(await x.selectFrom(f).select([X]).where(P,"=",M).where(J,"=","posts").execute()).map((G)=>G[X]),O=Y.filter((G)=>!B.includes(G)),R=B.filter((G)=>!Y.includes(G));if(O.length>0)for(let G of O)await x.deleteFrom(f).where(P,"=",M).where(J,"=","posts").where(X,"=",G).execute();if(R.length>0){let G=R.map((bM)=>({[X]:bM,[P]:M,[J]:"posts",created_at:j(new Date),updated_at:j(new Date)}));await x.insertInto(f).values(G).execute()}}catch(P){if(P instanceof Error)throw TypeError(`Failed to sync records: ${P.message}`);throw P}}async function mM(M,f){let B=await C();try{if(f.title!==void 0&&typeof f.title==="string"&&f.title.trim()==="")throw Error("Post title cannot be empty");let x=["published","draft","archived"];if(f.status!==void 0&&typeof f.status==="string"&&!x.includes(f.status))throw Error(`Invalid post status: ${f.status}`);if(f.views!==void 0&&typeof f.views==="number"&&f.views<0)throw Error("Views count cannot be negative");let P=await B.updateTable("posts").set(f).where("id","=",M).returningAll().executeTakeFirst();if($(P))return P;let J=await A(M);if(!J)throw Error(`Post with ID ${M} not found`);return J}catch(x){if(x instanceof Error)throw TypeError(`Failed to update post: ${x.message}`);throw x}}var T={};Q(T,{update:()=>lM,store:()=>gM,fetchTags:()=>nM,fetchTagById:()=>K,destroy:()=>DM,bulkDestroy:()=>uM});async function DM(M){let f=await C();if(!await f.selectFrom("taggables").where("id","=",M).selectAll().executeTakeFirst())throw Error(`Tag with ID ${M} not found`);await f.deleteFrom("taggables").where("id","=",M).executeTakeFirst()}async function uM(M){let f=await C();try{await f.deleteFrom("taggables").where("id","in",M).execute()}catch(B){if(B instanceof Error)throw TypeError(`Failed to delete tags: ${B.message}`);throw B}}import{slugify as CB}from"ts-slug";async function gM(M){let f=await C();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 B=CB(M.name);if(await f.selectFrom("taggables").selectAll().where("slug","=",B).executeTakeFirst())throw Error(`Tag with unique slug "${B}" already exists`);let P={name:M.name,slug:B,description:M.description,is_active:M.is_active??!0,taggable_type:M.taggable_type};if(M.taggable_id!==void 0)P.taggable_id=M.taggable_id;let J=await f.insertInto("taggables").values(P).returningAll().executeTakeFirst(),X=await z(f,"taggables",J);if(!X)throw Error("Failed to create tag");return X}catch(B){if(B instanceof Error)throw TypeError(`Failed to store tag: ${B.message}`);throw B}}async function K(M){let f=await C();try{let B=await f.selectFrom("taggables").where("id","=",M).selectAll().executeTakeFirst();if(!B)return;return B}catch(B){if(B instanceof Error)throw TypeError(`Failed to fetch tag: ${B.message}`);throw B}}async function nM(){let M=await C();try{return await M.selectFrom("taggables").where("is_active","=",!0).selectAll().execute()}catch(f){if(f instanceof Error)throw TypeError(`Failed to fetch tags: ${f.message}`);throw f}}import{uniqueSlug as JB}from"@stacksjs/slug";async function lM(M){let f=await C();try{let B=M.id;if(!B)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 Z=await JB(M.name,{table:"taggables",column:"slug"});if(await f.selectFrom("taggables").selectAll().where("slug","=",Z).where("id","!=",B).executeTakeFirst())throw Error(`Tag with unique slug "${Z}" already exists`);M.slug=Z}let{id:x,...P}=M,J=await f.updateTable("taggables").set(P).where("id","=",B).returningAll().executeTakeFirst();if($(J))return J;let X=await K(B);if(!X)throw Error("Failed to update tag");return X}catch(B){if(B instanceof Error)throw TypeError(`Failed to update tag: ${B.message}`);throw B}}var XB={posts:I,postCategories:N,tags:T,comments:q,authors:F,pages:v},Sx=XB;export{T as tags,I as posts,v as pages,Sx as default,q as comments,XB as cms,N as categorizable,F as authors};
|
|
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)=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[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};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The nested items of a site's menu by handle (`main`, `footer`, ...).
|
|
3
|
+
* Page-linked items resolve to the page's current path - so a slug change
|
|
4
|
+
* updates navigation for free - and drop out while the page is unpublished.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fetchMenuTree(siteId: number, handle: string): Promise<MenuTreeItem[]>;
|
|
7
|
+
export declare interface MenuTreeItem {
|
|
8
|
+
id: number
|
|
9
|
+
label: string
|
|
10
|
+
href: string
|
|
11
|
+
target: '_self' | '_blank'
|
|
12
|
+
visibility: 'public' | 'auth'
|
|
13
|
+
children: MenuTreeItem[]
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PageBlock } from '../blocks/types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a page as a validated block document. `siteId` is a REQUIRED first
|
|
4
|
+
* argument - the route boundary resolves it once (`requireSite()` for public
|
|
5
|
+
* surfaces, the site switcher for the dashboard); nothing in this layer
|
|
6
|
+
* defaults it from ambient context.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createPageDocument(siteId: number, input: SavePageDocumentInput): Promise<SavedPageDocument>;
|
|
9
|
+
/**
|
|
10
|
+
* Update a page as a document: validates blocks, snapshots the previous
|
|
11
|
+
* state as a revision, recomputes `path` when the slug or parent moved,
|
|
12
|
+
* rewrites descendant paths, and records 301s for every path that changed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function updatePageDocument(siteId: number, pageId: number, input: SavePageDocumentInput): Promise<SavedPageDocument>;
|
|
15
|
+
export declare interface SavePageDocumentInput {
|
|
16
|
+
title: string
|
|
17
|
+
slug?: string
|
|
18
|
+
parentId?: number | null
|
|
19
|
+
template?: string
|
|
20
|
+
blocks?: unknown
|
|
21
|
+
metaDescription?: string | null
|
|
22
|
+
status?: 'draft' | 'published' | 'scheduled' | 'archived'
|
|
23
|
+
scheduledAt?: string | null
|
|
24
|
+
authorId?: number | null
|
|
25
|
+
note?: string | null
|
|
26
|
+
}
|
|
27
|
+
export declare interface SavedPageDocument {
|
|
28
|
+
id: number
|
|
29
|
+
path: string
|
|
30
|
+
blocks: PageBlock[]
|
|
31
|
+
}
|
|
32
|
+
declare class PageDocumentError extends Error {
|
|
33
|
+
readonly status: number;
|
|
34
|
+
readonly details?: unknown;
|
|
35
|
+
constructor(message: string, details?: unknown);
|
|
36
|
+
}
|
|
37
|
+
export { PageDocumentError };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CmsSiteContextLike } from './render';
|
|
2
|
+
/**
|
|
3
|
+
* The CMS catch-all for both stx servers' `onRequest`, called AFTER
|
|
4
|
+
* file-routed stx views have had their chance (coded pages win over CMS
|
|
5
|
+
* pages). Returns a Response to serve, or null to fall through to the
|
|
6
|
+
* server's normal 404.
|
|
7
|
+
*
|
|
8
|
+
* Order: published page -> recorded redirect -> null. Only GET/HEAD; only
|
|
9
|
+
* with a resolved site - the platform's own hosts have no CMS tree.
|
|
10
|
+
*/
|
|
11
|
+
export declare function cmsPageFallback(req: Request, site: CmsSiteContextLike | null | undefined): Promise<Response | null>;
|
|
12
|
+
/**
|
|
13
|
+
* The one-call version both stx servers use from their `onResponse` hooks
|
|
14
|
+
* when stx-serve produced a 404: coded views win by construction (they never
|
|
15
|
+
* 404), and only then does the CMS tree get a say. Cheap exits first - the
|
|
16
|
+
* asset-extension guard keeps missing .css/.png lookups off the database.
|
|
17
|
+
*/
|
|
18
|
+
export declare function cmsNotFoundFallback(req: Request): Promise<Response | null>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PublishedPage } from './resolve';
|
|
2
|
+
/**
|
|
3
|
+
* Render a published page to a full HTML response. The page template
|
|
4
|
+
* (`cms/page.stx`, app-overridable) receives the pre-rendered block HTML as
|
|
5
|
+
* `content` plus `page`/`site` for the head and chrome.
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderCmsPage(site: CmsSiteContextLike, page: PublishedPage): Promise<Response>;
|
|
8
|
+
export declare interface CmsSiteContextLike {
|
|
9
|
+
id: number
|
|
10
|
+
name?: string
|
|
11
|
+
subdomain?: string
|
|
12
|
+
settings?: Record<string, unknown>
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PageBlock } from '../blocks/types';
|
|
2
|
+
/** Trim a trailing slash (except the root) so `/about/` and `/about` are one page. */
|
|
3
|
+
export declare function normalizePath(path: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* The published page at `path` on a site, or null. Only `status='published'`
|
|
6
|
+
* rows with a past `published_at` serve - a draft or scheduled page is
|
|
7
|
+
* invisible here no matter what its path says, which is the entire reason
|
|
8
|
+
* public reads do not ride the pages API.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolvePublishedPage(siteId: number, rawPath: string): Promise<PublishedPage | null>;
|
|
11
|
+
export declare interface PublishedPage {
|
|
12
|
+
id: number
|
|
13
|
+
siteId: number
|
|
14
|
+
title: string
|
|
15
|
+
path: string
|
|
16
|
+
template: string
|
|
17
|
+
metaDescription: string | null
|
|
18
|
+
blocks: PageBlock[]
|
|
19
|
+
publishedAt: string | null
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sanitizeRichText(html: string): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flip due `scheduled` pages to `published`. `published_at` is stamped at
|
|
3
|
+
* flip time (not the scheduled time) so the row records when the page
|
|
4
|
+
* actually went live. Run every minute by the default
|
|
5
|
+
* `PublishScheduledPages` job; returns how many pages went live.
|
|
6
|
+
*/
|
|
7
|
+
export declare function publishDuePages(now?: Date): Promise<number>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Look up a redirect for a missed path on a site, or null. */
|
|
2
|
+
export declare function resolveRedirect(siteId: number, path: string): Promise<ResolvedRedirect | null>;
|
|
3
|
+
/**
|
|
4
|
+
* Record redirects for a set of path moves (a slug change plus every
|
|
5
|
+
* descendant it dragged along). Upserts: a later move overwrites an older
|
|
6
|
+
* redirect from the same path, and a redirect that would point at itself is
|
|
7
|
+
* skipped. Also drops any old redirect TO a path that just became a source,
|
|
8
|
+
* so chains collapse instead of loop.
|
|
9
|
+
*/
|
|
10
|
+
export declare function recordSlugChangeRedirects(siteId: number, moves: PathMove[]): Promise<void>;
|
|
11
|
+
export declare interface ResolvedRedirect {
|
|
12
|
+
toPath: string
|
|
13
|
+
statusCode: 301 | 302
|
|
14
|
+
}
|
|
15
|
+
export declare interface PathMove {
|
|
16
|
+
fromPath: string
|
|
17
|
+
toPath: string
|
|
18
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot a page's CURRENT document before an overwrite. Callers pass the
|
|
3
|
+
* pre-update state; the row records what the save replaced, so "restore
|
|
4
|
+
* revision N" recovers exactly what the editor saw before save N+1.
|
|
5
|
+
* Prunes to the newest `keep` revisions per page.
|
|
6
|
+
*/
|
|
7
|
+
export declare function storeRevision(pageId: number, snapshot: PageDocumentSnapshot, options?: { authorId?: number | null, note?: string | null, keep?: number }): Promise<number>;
|
|
8
|
+
export declare function fetchRevisions(pageId: number, limit?: number): Promise<PageRevisionRow[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Copy a revision's document back onto its page. The page's current state is
|
|
11
|
+
* snapshotted first, so a restore is itself undoable.
|
|
12
|
+
*/
|
|
13
|
+
export declare function restoreRevision(revisionId: number, options?: { authorId?: number | null }): Promise<void>;
|
|
14
|
+
export declare interface PageDocumentSnapshot {
|
|
15
|
+
title: string
|
|
16
|
+
blocks: string | null
|
|
17
|
+
metaDescription: string | null
|
|
18
|
+
}
|
|
19
|
+
export declare interface PageRevisionRow {
|
|
20
|
+
id: number
|
|
21
|
+
page_id: number
|
|
22
|
+
author_id: number | null
|
|
23
|
+
revision: number
|
|
24
|
+
title: string
|
|
25
|
+
blocks: string | null
|
|
26
|
+
meta_description: string | null
|
|
27
|
+
note: string | null
|
|
28
|
+
created_at: string | null
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/cms",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.71.1",
|
|
6
6
|
"description": "Stacks cms utilities.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,10 +58,12 @@
|
|
|
58
58
|
"prepublishOnly": "bun run build"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
+
"@stacksjs/sites": "0.71.1",
|
|
62
|
+
"@stacksjs/validation": "0.71.1",
|
|
61
63
|
"ts-slug": "^0.1.0",
|
|
62
64
|
"ts-spreadsheets": "^0.2.0"
|
|
63
65
|
},
|
|
64
66
|
"devDependencies": {
|
|
65
|
-
"better-dx": "^0.2.
|
|
67
|
+
"better-dx": "^0.2.23"
|
|
66
68
|
}
|
|
67
69
|
}
|