@stacksjs/cms 0.74.46 → 0.74.47

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.
@@ -1 +1 @@
1
- import{formatDate}from"@stacksjs/orm";import{slugify}from"ts-slug";import{parseStoredBlocks,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};export async function fetchPageDocument(siteId,pageId){const row=await(await getDb()).selectFrom("pages").where("id","=",pageId).where("site_id","=",siteId).selectAll().executeTakeFirst();if(!row)return null;return{id:Number(row.id),siteId:Number(row.site_id),title:String(row.title??""),slug:String(row.slug??""),path:String(row.path??""),parentId:row.parent_id===null||row.parent_id===void 0?null:Number(row.parent_id),template:row.template===null||row.template===void 0?null:String(row.template),metaDescription:row.meta_description===null||row.meta_description===void 0?null:String(row.meta_description),status:String(row.status??"draft"),scheduledAt:row.scheduled_at===null||row.scheduled_at===void 0?null:String(row.scheduled_at),publishedAt:row.published_at===null||row.published_at===void 0?null:String(row.published_at),updatedAt:row.updated_at===null||row.updated_at===void 0?null:String(row.updated_at),blocks:parseStoredBlocks(row.blocks)}}
1
+ import{parseSqlDateTime,sqlDateTime}from"@stacksjs/database";import{formatDate}from"@stacksjs/orm";import{slugify}from"ts-slug";import{parseStoredBlocks,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};function storedTimestamp(value){if(value===null||value===void 0||value==="")return null;if(typeof value==="string")return value;const parsed=parseSqlDateTime(value);return parsed===null?null:sqlDateTime(parsed)}export async function fetchPageDocument(siteId,pageId){const row=await(await getDb()).selectFrom("pages").where("id","=",pageId).where("site_id","=",siteId).selectAll().executeTakeFirst();if(!row)return null;return{id:Number(row.id),siteId:Number(row.site_id),title:String(row.title??""),slug:String(row.slug??""),path:String(row.path??""),parentId:row.parent_id===null||row.parent_id===void 0?null:Number(row.parent_id),template:row.template===null||row.template===void 0?null:String(row.template),metaDescription:row.meta_description===null||row.meta_description===void 0?null:String(row.meta_description),status:String(row.status??"draft"),scheduledAt:storedTimestamp(row.scheduled_at),publishedAt:storedTimestamp(row.published_at),updatedAt:storedTimestamp(row.updated_at),blocks:parseStoredBlocks(row.blocks)}}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/cms",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.74.46",
5
+ "version": "0.74.47",
6
6
  "description": "Stacks cms utilities.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,14 +58,14 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/database": "0.74.46",
62
- "@stacksjs/error-handling": "0.74.46",
63
- "@stacksjs/orm": "0.74.46",
64
- "@stacksjs/path": "0.74.46",
65
- "@stacksjs/sites": "0.74.46",
66
- "@stacksjs/slug": "0.74.46",
61
+ "@stacksjs/database": "0.74.47",
62
+ "@stacksjs/error-handling": "0.74.47",
63
+ "@stacksjs/orm": "0.74.47",
64
+ "@stacksjs/path": "0.74.47",
65
+ "@stacksjs/sites": "0.74.47",
66
+ "@stacksjs/slug": "0.74.47",
67
67
  "@stacksjs/stx": "^0.2.286",
68
- "@stacksjs/validation": "0.74.46",
68
+ "@stacksjs/validation": "0.74.47",
69
69
  "ts-slug": "^0.1.0",
70
70
  "ts-spreadsheets": "^0.2.0"
71
71
  },