@stacksjs/actions 0.75.151 → 0.75.152

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 process from"node:process";import{ensureUtcDatetimeColumns,generateMigrations,migrateAuthTables,migrateNotificationTables,migrateRbacTables,migrateTraitTables,resetDatabase,runDatabaseMigration}from"@stacksjs/database";import{log}from"@stacksjs/logging";const resetResult=await resetDatabase();if(resetResult?.isErr){console.error(resetResult.error);await log.error("resetDatabase failed",resetResult.error);process.exit(1)}const genResult=await generateMigrations();if(genResult?.isErr){console.error(genResult.error);await log.error("generateMigrations failed",genResult.error);process.exit(1)}const authResult=await migrateAuthTables();if(!authResult.success)log.error(`Failed to migrate auth tables: ${authResult.error}`);const notificationBootstrap=await migrateNotificationTables({tables:["notifications","notification_deliveries"]});if(!notificationBootstrap.success)log.error(`Failed to bootstrap notification tables: ${notificationBootstrap.error}`);const migrateResult=await runDatabaseMigration(),notifResult=await migrateNotificationTables();if(!notifResult.success)log.error(`Failed to migrate notification tables: ${notifResult.error}`);const rbacResult=await migrateRbacTables();if(!rbacResult.success)log.error(`Failed to migrate RBAC tables: ${rbacResult.error}`);const traitResult=await migrateTraitTables();if(!traitResult.success)log.error(`Failed to migrate polymorphic trait tables: ${traitResult.error}`);const datetimeResult=await ensureUtcDatetimeColumns();if(!datetimeResult.success)log.error(`Failed to convert TIMESTAMP columns to DATETIME: ${datetimeResult.error}`);if(migrateResult.isErr){await log.error("runDatabaseMigration failed");await log.error(migrateResult.error);process.exit(1)}process.exit(0);
1
+ import process from"node:process";import{ensureUtcDatetimeColumns,generateMigrations,migrateAuthTables,migrateNotificationTables,migrateRbacTables,migrateTraitTables,resetDatabase,runDatabaseMigration}from"@stacksjs/database";import{log}from"@stacksjs/logging";const resetResult=await resetDatabase();if(resetResult?.isErr){console.error(resetResult.error);await log.error("resetDatabase failed",resetResult.error);process.exit(1)}const genResult=await generateMigrations();if(genResult?.isErr){console.error(genResult.error);await log.error("generateMigrations failed",genResult.error);process.exit(1)}const authResult=await migrateAuthTables();if(!authResult.success)log.error(`Failed to migrate auth tables: ${authResult.error}`);const migrateResult=await runDatabaseMigration(),notifResult=await migrateNotificationTables();if(!notifResult.success)log.error(`Failed to migrate notification tables: ${notifResult.error}`);const rbacResult=await migrateRbacTables();if(!rbacResult.success)log.error(`Failed to migrate RBAC tables: ${rbacResult.error}`);const traitResult=await migrateTraitTables();if(!traitResult.success)log.error(`Failed to migrate polymorphic trait tables: ${traitResult.error}`);const datetimeResult=await ensureUtcDatetimeColumns();if(!datetimeResult.success)log.error(`Failed to convert TIMESTAMP columns to DATETIME: ${datetimeResult.error}`);if(migrateResult.isErr){await log.error("runDatabaseMigration failed");await log.error(migrateResult.error);process.exit(1)}process.exit(0);
package/dist/seo.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import{existsSync,readFileSync,statSync}from"node:fs";import{join}from"node:path";import{generateRobotsTxt,generateSitemap,scanForSitemapEntries}from"@stacksjs/stx/seo";import{usableOrigin}from"./site-origin";export const NON_PAGE_DIRS=["components","layouts","partials","emails"],DEFAULT_ROBOTS_DISALLOW=["/api/"];function withoutComments(source){return source.replace(/<!--[\s\S]*?-->/g,"").replace(/\{\{--[\s\S]*?--\}\}/g,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/(^|[^:\\'"`])\/\/[^\n]*/g,"$1")}export function declaresNoindex(source){const code=withoutComments(source);for(const tag of code.match(/<meta\b[^>]*>/gi)??[])if(/\bname\s*=\s*["']?(?:robots|googlebot)\b/i.test(tag)&&/\bcontent\s*=\s*["'][^"']*\bnoindex\b/i.test(tag))return!0;if(/\brobots\s*:\s*(["'`])[^"'`]*\bnoindex\b/i.test(code))return!0;if(/\b(?:const|let|var)\s+noindex\s*(?::\s*boolean\s*)?=\s*true\b/.test(code))return!0;if(/\bsitemap\s*:\s*false\b/.test(code))return!0;return!1}export function requiresAuth(source){const match=withoutComments(source).match(/\bmiddleware\s*:\s*(\[[^\]]*\]|["'][^"']*["'])/);return!!match&&/["']auth["']/.test(match[1])}export function isErrorViewPath(path){const segments=path.split("/").filter(Boolean),last=segments.at(-1)??"";return segments.includes("errors")||/^(?:\d{3}|error|coming-soon|maintenance)$/.test(last)}export function isNonPagePath(path){const segments=path.split("/").filter(Boolean);return segments.slice(0,-1).some((segment)=>NON_PAGE_DIRS.includes(segment))||segments.some((segment)=>segment.startsWith("_")||segment.startsWith("."))}export function isDynamicPath(path){return/\[[^\]]*\]/.test(path)}export function pathMatchesExclude(path,pattern){if(!pattern)return!1;if(pattern.endsWith("*")){const prefix=pattern.slice(0,-1);return path.startsWith(prefix)||`${path}/`===prefix}if(pattern.endsWith("/")&&pattern!=="/")return path.startsWith(pattern)||`${path}/`===pattern;return path===pattern||pattern!=="/"&&path===pattern.replace(/\/+$/,"")}function longestRobotsMatch(path,rules){let best=-1;for(const rule of rules){if(!rule)continue;const anchored=rule.endsWith("$"),pattern=(anchored?rule.slice(0,-1):rule).split("*").map((part)=>part.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");if(new RegExp(`^${pattern}${anchored?"$":""}`).test(path))best=Math.max(best,rule.length)}return best}export function isDisallowedByRobots(path,group){const disallow=longestRobotsMatch(path,group.disallow??[]);return disallow>=0&&disallow>longestRobotsMatch(path,group.allow??[])}function sitemapOptions(seo){if(seo?.sitemap===!1)return;return seo?.sitemap??{}}function robotsOptions(seo){if(seo?.robots===!1)return;return seo?.robots??{}}export function robotsDefaultGroup(seo){const disallow=[...new Set([...DEFAULT_ROBOTS_DISALLOW,...robotsOptions(seo)?.disallow??[]])];return{userAgent:"*",allow:["/"],disallow}}export function resolveSeoOrigin(options){const configured=usableOrigin(options.seo?.origin)||usableOrigin(options.appUrl);if(configured)return configured;if(!options.request)return"";const url=new URL(options.request.url),host=options.request.headers.get("x-forwarded-host")?.split(",")[0]?.trim()||url.host;return`${options.request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim()||url.protocol.replace(":","")}://${host}`}export function buildRobotsTxt(options){const robots=robotsOptions(options.seo),rules=[robotsDefaultGroup(options.seo),...robots?.rules??[]];return`# Generated by Stacks from resources/views and config/app.ts (seo).
2
2
  ${generateRobotsTxt({rules,...sitemapOptions(options.seo)&&options.origin&&{sitemap:`${options.origin}/sitemap.xml`}})}
3
- `}function toEntry(value){return typeof value==="string"?{loc:value}:value}export function locPath(loc){if(!/^https?:\/\//i.test(loc))return loc.startsWith("/")?loc:`/${loc}`;try{return new URL(loc).pathname}catch{return loc}}export function sitemapLocs(xml){return[...xml.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/gi)].map((match)=>match[1].replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'"))}function viewFileFor(viewsDir,path){const rel=path==="/"?"index":path.replace(/^\//,"");for(const candidate of[`${rel}.stx`,`${rel}/index.stx`]){const file=join(viewsDir,candidate);if(existsSync(file))return file}return}export async function scanViewSitemapEntries(viewsDir,seo){const result={entries:[],skipped:[]};if(!existsSync(viewsDir))return result;const scanned=await scanForSitemapEntries(viewsDir,{baseUrl:"",extensions:[".stx"],ignore:["node_modules"]}),exclude=sitemapOptions(seo)?.exclude??[],robotsGroup=robotsOptions(seo)?robotsDefaultGroup(seo):void 0,seen=new Set;for(const entry of scanned){const path=entry.loc;if(seen.has(path))continue;seen.add(path);const skip=(reason)=>{result.skipped.push({path,reason})};if(isNonPagePath(path)){skip("non-page");continue}if(isDynamicPath(path)){skip("dynamic");continue}if(isErrorViewPath(path)){skip("error-page");continue}if(exclude.some((pattern)=>pathMatchesExclude(path,pattern))){skip("excluded");continue}if(robotsGroup&&isDisallowedByRobots(path,robotsGroup)){skip("disallowed");continue}const file=viewFileFor(viewsDir,path),source=file?readFileSync(file,"utf8"):"";if(declaresNoindex(source)){skip("noindex");continue}if(requiresAuth(source)){skip("auth");continue}result.entries.push({loc:path,...entry.lastmod&&{lastmod:entry.lastmod},priority:path==="/"?1:0.5})}return result}async function blogEntries(root){const contentDir=join(root,"content/blog");if(existsSync(contentDir))try{const{blogSitemapPaths}=await import("./blog"),paths=blogSitemapPaths(contentDir);if(paths.length>0)return paths.map((loc)=>({loc,priority:loc==="/blog"?0.7:0.6}))}catch{}const built=join(root,"dist/blog/sitemap.xml");if(existsSync(built))try{return sitemapLocs(readFileSync(built,"utf8")).map((loc)=>({loc:locPath(loc),priority:0.6}))}catch{}return[]}export async function collectSitemapEntries(inputs){const options=sitemapOptions(inputs.seo);if(!options)return[];const views=await scanViewSitemapEntries(join(inputs.root,"resources/views"),inputs.seo),configured=[...options.include??[]];if(options.entries&&!inputs.skipDynamicEntries)try{configured.push(...await options.entries())}catch(error){console.warn(`[seo] seo.sitemap.entries() failed; serving the sitemap without them: ${error.message}`)}const exclude=options.exclude??[],robotsGroup=robotsOptions(inputs.seo)?robotsDefaultGroup(inputs.seo):void 0,admits=(path)=>!exclude.some((pattern)=>pathMatchesExclude(path,pattern))&&!(robotsGroup&&isDisallowedByRobots(path,robotsGroup)),byPath=new Map;for(const entry of views.entries)byPath.set(entry.loc,entry);for(const entry of await blogEntries(inputs.root)){const path=locPath(entry.loc);if(!byPath.has(path)&&admits(path))byPath.set(path,entry)}for(const raw of configured){const entry=toEntry(raw),path=locPath(entry.loc);if(admits(path))byPath.set(path,{...byPath.get(path),...entry})}return[...byPath.values()].sort((a,b)=>locPath(a.loc).localeCompare(locPath(b.loc)))}export async function buildSitemapXml(inputs){const entries=await collectSitemapEntries(inputs);return`${generateSitemap(entries,{baseUrl:inputs.origin,includeLastmod:!1})}
3
+ `}function toEntry(value){return typeof value==="string"?{loc:value}:value}export function locPath(loc){if(!/^https?:\/\//i.test(loc))return loc.startsWith("/")?loc:`/${loc}`;try{return new URL(loc).pathname}catch{return loc}}export function sitemapLocs(xml){return[...xml.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/gi)].map((match)=>match[1].replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&apos;/g,"'"))}function viewFileFor(viewsDir,path){const rel=path==="/"?"index":path.replace(/^\//,"");for(const candidate of[`${rel}.stx`,`${rel}/index.stx`]){const file=join(viewsDir,candidate);if(existsSync(file))return file}return}export async function scanViewSitemapEntries(viewsDir,seo){const result={entries:[],skipped:[]};if(!existsSync(viewsDir))return result;const scanned=await scanForSitemapEntries(viewsDir,{baseUrl:"",extensions:[".stx"],ignore:["node_modules"]}),exclude=sitemapOptions(seo)?.exclude??[],robotsGroup=robotsOptions(seo)?robotsDefaultGroup(seo):void 0,seen=new Set;for(const entry of scanned){const path=entry.loc;if(seen.has(path))continue;seen.add(path);const skip=(reason)=>{result.skipped.push({path,reason})};if(isNonPagePath(path)){skip("non-page");continue}if(isDynamicPath(path)){skip("dynamic");continue}if(isErrorViewPath(path)){skip("error-page");continue}if(exclude.some((pattern)=>pathMatchesExclude(path,pattern))){skip("excluded");continue}if(robotsGroup&&isDisallowedByRobots(path,robotsGroup)){skip("disallowed");continue}const file=viewFileFor(viewsDir,path),source=file?readFileSync(file,"utf8"):"";if(declaresNoindex(source)){skip("noindex");continue}if(requiresAuth(source)){skip("auth");continue}result.entries.push({loc:path,...entry.lastmod&&{lastmod:entry.lastmod},priority:path==="/"?1:0.5})}return result}async function blogEntries(root){const contentDir=join(root,"content/blog");if(existsSync(contentDir))try{const{blogSitemapPaths}=await import("./blog"),paths=blogSitemapPaths(contentDir);if(paths.length>0)return paths.map((loc)=>({loc,priority:loc==="/blog"?0.7:0.6}))}catch{}const built=join(root,"dist/blog/sitemap.xml");if(existsSync(built))try{return sitemapLocs(readFileSync(built,"utf8")).map((loc)=>({loc:locPath(loc),priority:0.6}))}catch{}return[]}export async function collectSitemapEntries(inputs){const options=sitemapOptions(inputs.seo);if(!options)return[];const views=await scanViewSitemapEntries(join(inputs.root,"resources/views"),inputs.seo),configured=[...options.include??[]];if(options.entries&&!inputs.skipDynamicEntries)try{configured.push(...await options.entries())}catch(error){console.warn(`[seo] seo.sitemap.entries() failed; serving the sitemap without them: ${error.message}`)}const exclude=options.exclude??[],robotsGroup=robotsOptions(inputs.seo)?robotsDefaultGroup(inputs.seo):void 0,admits=(path)=>!exclude.some((pattern)=>pathMatchesExclude(path,pattern))&&!(robotsGroup&&isDisallowedByRobots(path,robotsGroup)),byPath=new Map;for(const entry of views.entries)byPath.set(entry.loc,entry);for(const entry of await blogEntries(inputs.root)){const path=locPath(entry.loc);if(!byPath.has(path)&&admits(path))byPath.set(path,entry)}for(const raw of configured){const entry=toEntry(raw),path=locPath(entry.loc);if(admits(path))byPath.set(path,{...byPath.get(path),...entry})}const entries=[...byPath.values()].sort((a,b)=>locPath(a.loc).localeCompare(locPath(b.loc)));return withAlternates(entries,options)}function withAlternates(entries,options){const hook=options.alternates;if(!hook)return entries;try{return entries.map((entry)=>{if(entry.alternates)return entry;const alternates=hook(locPath(entry.loc));return alternates?.length?{...entry,alternates}:entry})}catch(error){console.warn(`[seo] seo.sitemap.alternates() failed; serving the sitemap without language alternates: ${error.message}`);return entries}}export async function buildSitemapXml(inputs){const entries=await collectSitemapEntries(inputs);return`${generateSitemap(entries,{baseUrl:inputs.origin,includeLastmod:!1})}
4
4
  `}export function pathsMissingFromSitemap(handwrittenXml,generated){const listed=new Set(sitemapLocs(handwrittenXml).map((loc)=>locPath(loc).replace(/(.)\/+$/,"$1")));return generated.map((entry)=>locPath(entry.loc)).filter((path)=>!listed.has(path))}export function seoFileFor(req){if(req.method!=="GET"&&req.method!=="HEAD")return;const{pathname}=new URL(req.url);if(pathname==="/sitemap.xml")return"sitemap.xml";if(pathname==="/robots.txt")return"robots.txt";return}export function createSiteSeoHandler(options={}){const root=options.root??process.cwd(),ttl=options.cacheTtlMs??0,cache=new Map;return async(req)=>{const file=seoFileFor(req);if(!file)return null;const seo=options.seo;if(file==="sitemap.xml"&&!sitemapOptions(seo))return null;if(file==="robots.txt"&&!robotsOptions(seo))return null;if((seo?.handwritten??"prefer")==="prefer"&&existsSync(join(root,"public",file)))return null;const origin=resolveSeoOrigin({seo,appUrl:options.appUrl,request:req}),key=`${file} ${origin}`,cached=cache.get(key);let body=cached&&Date.now()-cached.at<ttl?cached.body:void 0;if(body===void 0){body=file==="sitemap.xml"?await buildSitemapXml({root,seo,origin}):buildRobotsTxt({seo,origin});if(ttl>0)cache.set(key,{at:Date.now(),body})}return new Response(req.method==="HEAD"?null:body,{headers:{"Content-Type":file==="sitemap.xml"?"application/xml; charset=utf-8":"text/plain; charset=utf-8","Cache-Control":ttl>0?"public, max-age=3600":"no-cache"}})}}export async function describeSeoAtBoot(options={}){const root=options.root??process.cwd(),seo=options.seo;if((seo?.handwritten??"prefer")!=="prefer")return[];const notes=[],sitemapFile=join(root,"public/sitemap.xml");if(sitemapOptions(seo)&&existsSync(sitemapFile)&&statSync(sitemapFile).isFile()){const generated=await collectSitemapEntries({root,seo,origin:"",skipDynamicEntries:!0}),missing=pathsMissingFromSitemap(readFileSync(sitemapFile,"utf8"),generated);if(missing.length>0)notes.push(`public/sitemap.xml is hand-written and does not list ${missing.length} page${missing.length===1?"":"s"} Stacks would: ${missing.slice(0,8).join(", ")}${missing.length>8?", ...":""}. Delete it to serve the generated /sitemap.xml, or set app.seo.handwritten to 'replace'.`)}const robotsFile=join(root,"public/robots.txt");if(robotsOptions(seo)&&existsSync(robotsFile)&&statSync(robotsFile).isFile()){if(!/^\s*sitemap\s*:/im.test(readFileSync(robotsFile,"utf8")))notes.push("public/robots.txt is hand-written and names no Sitemap. Delete it to serve the generated /robots.txt, which does.")}return notes}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/actions",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.151",
5
+ "version": "0.75.152",
6
6
  "description": "The Stacks actions.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -66,54 +66,54 @@
66
66
  "prepublishOnly": "bun run build"
67
67
  },
68
68
  "dependencies": {
69
- "@stacksjs/action-runner": "0.75.151",
70
- "@stacksjs/ai": "0.75.151",
71
- "@stacksjs/analytics": "0.75.151",
72
- "@stacksjs/api": "0.75.151",
73
- "@stacksjs/auth": "0.75.151",
74
- "@stacksjs/build": "0.75.151",
69
+ "@stacksjs/action-runner": "0.75.152",
70
+ "@stacksjs/ai": "0.75.152",
71
+ "@stacksjs/analytics": "0.75.152",
72
+ "@stacksjs/api": "0.75.152",
73
+ "@stacksjs/auth": "0.75.152",
74
+ "@stacksjs/build": "0.75.152",
75
75
  "@stacksjs/bumpx": "^0.2.21",
76
76
  "@stacksjs/bunpress": "^0.2.15",
77
- "@stacksjs/cli": "0.75.151",
78
- "@stacksjs/cloud": "0.75.151",
79
- "@stacksjs/cms": "0.75.151",
80
- "@stacksjs/components": "^0.2.445",
81
- "@stacksjs/config": "0.75.151",
82
- "@stacksjs/database": "0.75.151",
83
- "@stacksjs/desktop-build": "0.75.151",
84
- "@stacksjs/dns": "0.75.151",
85
- "@stacksjs/enums": "0.75.151",
86
- "@stacksjs/env": "0.75.151",
87
- "@stacksjs/error-handling": "0.75.151",
77
+ "@stacksjs/cli": "0.75.152",
78
+ "@stacksjs/cloud": "0.75.152",
79
+ "@stacksjs/cms": "0.75.152",
80
+ "@stacksjs/components": "^0.2.483",
81
+ "@stacksjs/config": "0.75.152",
82
+ "@stacksjs/database": "0.75.152",
83
+ "@stacksjs/desktop-build": "0.75.152",
84
+ "@stacksjs/dns": "0.75.152",
85
+ "@stacksjs/enums": "0.75.152",
86
+ "@stacksjs/env": "0.75.152",
87
+ "@stacksjs/error-handling": "0.75.152",
88
88
  "@stacksjs/gitit": "^0.2.5",
89
- "@stacksjs/i18n": "0.75.151",
90
- "@stacksjs/image": "0.75.151",
91
- "@stacksjs/logging": "0.75.151",
89
+ "@stacksjs/i18n": "0.75.152",
90
+ "@stacksjs/image": "0.75.152",
91
+ "@stacksjs/logging": "0.75.152",
92
92
  "@stacksjs/logsmith": "^0.2.11",
93
- "@stacksjs/mobile": "0.75.151",
94
- "@stacksjs/orm": "0.75.151",
95
- "@stacksjs/path": "0.75.151",
96
- "@stacksjs/payments": "0.75.151",
97
- "@stacksjs/queue": "0.75.151",
98
- "@stacksjs/registry": "0.75.151",
99
- "@stacksjs/router": "0.75.151",
93
+ "@stacksjs/mobile": "0.75.152",
94
+ "@stacksjs/orm": "0.75.152",
95
+ "@stacksjs/path": "0.75.152",
96
+ "@stacksjs/payments": "0.75.152",
97
+ "@stacksjs/queue": "0.75.152",
98
+ "@stacksjs/registry": "0.75.152",
99
+ "@stacksjs/router": "0.75.152",
100
100
  "@stacksjs/rpx": "^0.11.54",
101
- "@stacksjs/scheduler": "0.75.151",
102
- "@stacksjs/search-engine": "0.75.151",
103
- "@stacksjs/security": "0.75.151",
104
- "@stacksjs/server": "0.75.151",
105
- "@stacksjs/sites": "0.75.151",
106
- "@stacksjs/storage": "0.75.151",
107
- "@stacksjs/strings": "0.75.151",
108
- "@stacksjs/stx": "^0.2.445",
101
+ "@stacksjs/scheduler": "0.75.152",
102
+ "@stacksjs/search-engine": "0.75.152",
103
+ "@stacksjs/security": "0.75.152",
104
+ "@stacksjs/server": "0.75.152",
105
+ "@stacksjs/sites": "0.75.152",
106
+ "@stacksjs/storage": "0.75.152",
107
+ "@stacksjs/strings": "0.75.152",
108
+ "@stacksjs/stx": "^0.2.483",
109
109
  "@stacksjs/tlsx": "^0.13.19",
110
110
  "@stacksjs/ts-cloud": "^0.16.52",
111
111
  "@stacksjs/ts-md": "^0.1.1",
112
- "@stacksjs/tunnel": "0.75.151",
113
- "@stacksjs/types": "0.75.151",
114
- "@stacksjs/utils": "0.75.151",
115
- "@stacksjs/whois": "0.75.151",
116
- "bun-plugin-stx": "^0.2.445"
112
+ "@stacksjs/tunnel": "0.75.152",
113
+ "@stacksjs/types": "0.75.152",
114
+ "@stacksjs/utils": "0.75.152",
115
+ "@stacksjs/whois": "0.75.152",
116
+ "bun-plugin-stx": "^0.2.483"
117
117
  },
118
118
  "peerDependencies": {
119
119
  "craft-native": ">=0.0.135",
@@ -128,7 +128,7 @@
128
128
  }
129
129
  },
130
130
  "devDependencies": {
131
- "@stacksjs/validation": "0.75.151",
131
+ "@stacksjs/validation": "0.75.152",
132
132
  "better-dx": "^0.2.24"
133
133
  }
134
134
  }