@stacksjs/actions 0.75.98 → 0.75.100

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/build/dmg.js CHANGED
@@ -1,4 +1,4 @@
1
- import{cpSync,existsSync,mkdirSync,mkdtempSync,readdirSync,readFileSync,rmSync,statSync,symlinkSync,writeFileSync}from"node:fs";import{tmpdir}from"node:os";import{basename,dirname,join}from"node:path";import process from"node:process";import{log,runCommand}from"@stacksjs/cli";import{appPath,projectPath,publicPath,resourcesPath,storagePath}from"@stacksjs/path";import{bundleDirectoryFor,codesignArgs,describeRuntimeDuplication,looksLikeBunExecutable,renderUserlandPlistEntries}from"@stacksjs/desktop-build";import{runBuildStep}from"./run-build-step";if(process.platform!=="darwin")throw Error("DMG packaging only runs on macOS");const desktopDist=storagePath("framework/desktop-dist"),manifestPath=join(desktopDist,"desktop.json");if(!existsSync(manifestPath))throw Error("Run `buddy build:desktop` before packaging a DMG");const manifest=JSON.parse(readFileSync(manifestPath,"utf8")),ownsLauncher=manifest.launcher==="userland",appName=process.env.DESKTOP_APP_NAME||manifest.title||process.env.APP_NAME||"Stacks",bundleId=process.env.DESKTOP_BUNDLE_ID||`sh.stacks.${appName.toLowerCase().replace(/[^a-z0-9]+/g,"-")}`,version=process.env.DESKTOP_APP_VERSION||"0.0.0",signingIdentity=process.env.DESKTOP_SIGNING_IDENTITY,notaryProfile=process.env.DESKTOP_NOTARY_PROFILE,outputDir=storagePath("framework/desktop-dmg");if(existsSync(outputDir))rmSync(outputDir,{recursive:!0});mkdirSync(outputDir,{recursive:!0});const staging=mkdtempSync(join(tmpdir(),"stacks-dmg-")),scratch=mkdtempSync(join(tmpdir(),"stacks-dmg-work-")),appDir=join(staging,`${appName}.app`),macosDir=join(appDir,"Contents/MacOS"),resourcesDir=join(appDir,"Contents/Resources");mkdirSync(macosDir,{recursive:!0});mkdirSync(resourcesDir,{recursive:!0});const builtLauncher="stacks-desktop",launcherName=appName,bundledFiles=readdirSync(desktopDist).filter((name)=>statSync(join(desktopDist,name)).isFile());for(const file of bundledFiles){const target=file===builtLauncher?launcherName:file,directory=bundleDirectoryFor(join(desktopDist,file))==="MacOS"?macosDir:resourcesDir;await runBuildStep(["cp",join(desktopDist,file),join(directory,target)],{cwd:projectPath(),describe:`Copying ${file} into the bundle`})}if(!existsSync(join(macosDir,launcherName)))throw Error(`build:desktop did not produce ${builtLauncher}`);const runtimes=readdirSync(macosDir).filter((name)=>statSync(join(macosDir,name)).isFile()).filter((name)=>looksLikeBunExecutable(join(macosDir,name))).map((name)=>({name,bytes:statSync(join(macosDir,name)).size})),duplication=describeRuntimeDuplication(runtimes);if(duplication)log.warn(duplication);const userlandResources=appPath("Desktop/Resources");if(existsSync(userlandResources))cpSync(userlandResources,resourcesDir,{recursive:!0});async function buildIcon(){const source=[resourcesPath("assets/images/app-icon.png"),publicPath("images/app-icon.png"),publicPath("apple-touch-icon.png")].find((candidate)=>existsSync(candidate));if(!source)return;const iconset=join(scratch,"icon.iconset");mkdirSync(iconset,{recursive:!0});for(const size of[16,32,128,256,512]){await runCommand(["sips","-z",String(size),String(size),source,"--out",join(iconset,`icon_${size}x${size}.png`)],{cwd:scratch,silent:!0});await runCommand(["sips","-z",String(size*2),String(size*2),source,"--out",join(iconset,`icon_${size}x${size}@2x.png`)],{cwd:scratch,silent:!0})}const icns=join(resourcesDir,"AppIcon.icns");if((await runCommand(["iconutil","-c","icns",iconset,"-o",icns],{cwd:scratch})).isErr||!existsSync(icns)){log.warn(`Could not build an .icns from ${source}; the bundle will use the default icon.`);return}return"AppIcon"}const iconFile=await buildIcon();function readUserlandPlist(){const declared=appPath("Desktop/Info.plist.json");if(!existsSync(declared))return{};try{return JSON.parse(readFileSync(declared,"utf8"))}catch(error){throw Error(`app/Desktop/Info.plist.json is not valid JSON: ${error instanceof Error?error.message:error}`)}}const declaredPlist=readUserlandPlist(),{xml:extraPlistEntries,ignored:ignoredPlistKeys}=renderUserlandPlistEntries(declaredPlist);if(ignoredPlistKeys.length>0)log.info(`Ignoring reserved Info.plist keys from app/Desktop/Info.plist.json: ${ignoredPlistKeys.join(", ")}`);function overriddenByApp(key){return Object.hasOwn(declaredPlist,key)}writeFileSync(join(appDir,"Contents/Info.plist"),`<?xml version="1.0" encoding="UTF-8"?>
1
+ import{cpSync,existsSync,mkdirSync,mkdtempSync,readdirSync,readFileSync,rmSync,statSync,symlinkSync,writeFileSync}from"node:fs";import{tmpdir}from"node:os";import{basename,dirname,join}from"node:path";import process from"node:process";import{log,runCommand}from"@stacksjs/cli";import{appPath,projectPath,publicPath,resourcesPath,storagePath}from"@stacksjs/path";import{bundleDirectoryFor,codesignArgs,describeRuntimeDuplication,dmgVolumeName,looksLikeBunExecutable,renderUserlandPlistEntries}from"@stacksjs/desktop-build";import{runBuildStep}from"./run-build-step";if(process.platform!=="darwin")throw Error("DMG packaging only runs on macOS");const desktopDist=storagePath("framework/desktop-dist"),manifestPath=join(desktopDist,"desktop.json");if(!existsSync(manifestPath))throw Error("Run `buddy build:desktop` before packaging a DMG");const manifest=JSON.parse(readFileSync(manifestPath,"utf8")),ownsLauncher=manifest.launcher==="userland",appName=process.env.DESKTOP_APP_NAME||manifest.title||process.env.APP_NAME||"Stacks",bundleId=process.env.DESKTOP_BUNDLE_ID||`sh.stacks.${appName.toLowerCase().replace(/[^a-z0-9]+/g,"-")}`,version=process.env.DESKTOP_APP_VERSION||"0.0.0",signingIdentity=process.env.DESKTOP_SIGNING_IDENTITY,notaryProfile=process.env.DESKTOP_NOTARY_PROFILE,outputDir=storagePath("framework/desktop-dmg");if(existsSync(outputDir))rmSync(outputDir,{recursive:!0});mkdirSync(outputDir,{recursive:!0});const staging=mkdtempSync(join(tmpdir(),"stacks-dmg-")),scratch=mkdtempSync(join(tmpdir(),"stacks-dmg-work-"));process.on("exit",()=>{rmSync(staging,{recursive:!0,force:!0});rmSync(scratch,{recursive:!0,force:!0})});const appDir=join(staging,`${appName}.app`),macosDir=join(appDir,"Contents/MacOS"),resourcesDir=join(appDir,"Contents/Resources");mkdirSync(macosDir,{recursive:!0});mkdirSync(resourcesDir,{recursive:!0});const builtLauncher="stacks-desktop",launcherName=appName,bundledFiles=readdirSync(desktopDist).filter((name)=>statSync(join(desktopDist,name)).isFile());for(const file of bundledFiles){const target=file===builtLauncher?launcherName:file,directory=bundleDirectoryFor(join(desktopDist,file))==="MacOS"?macosDir:resourcesDir;await runBuildStep(["cp",join(desktopDist,file),join(directory,target)],{cwd:projectPath(),describe:`Copying ${file} into the bundle`})}if(!existsSync(join(macosDir,launcherName)))throw Error(`build:desktop did not produce ${builtLauncher}`);const runtimes=readdirSync(macosDir).filter((name)=>statSync(join(macosDir,name)).isFile()).filter((name)=>looksLikeBunExecutable(join(macosDir,name))).map((name)=>({name,bytes:statSync(join(macosDir,name)).size})),duplication=describeRuntimeDuplication(runtimes);if(duplication)log.warn(duplication);const userlandResources=appPath("Desktop/Resources");if(existsSync(userlandResources))cpSync(userlandResources,resourcesDir,{recursive:!0});async function buildIcon(){const source=[resourcesPath("assets/images/app-icon.png"),publicPath("images/app-icon.png"),publicPath("apple-touch-icon.png")].find((candidate)=>existsSync(candidate));if(!source)return;const iconset=join(scratch,"icon.iconset");mkdirSync(iconset,{recursive:!0});for(const size of[16,32,128,256,512]){await runCommand(["sips","-z",String(size),String(size),source,"--out",join(iconset,`icon_${size}x${size}.png`)],{cwd:scratch,silent:!0});await runCommand(["sips","-z",String(size*2),String(size*2),source,"--out",join(iconset,`icon_${size}x${size}@2x.png`)],{cwd:scratch,silent:!0})}const icns=join(resourcesDir,"AppIcon.icns");if((await runCommand(["iconutil","-c","icns",iconset,"-o",icns],{cwd:scratch})).isErr||!existsSync(icns)){log.warn(`Could not build an .icns from ${source}; the bundle will use the default icon.`);return}return"AppIcon"}const iconFile=await buildIcon();function readUserlandPlist(){const declared=appPath("Desktop/Info.plist.json");if(!existsSync(declared))return{};try{return JSON.parse(readFileSync(declared,"utf8"))}catch(error){throw Error(`app/Desktop/Info.plist.json is not valid JSON: ${error instanceof Error?error.message:error}`)}}const declaredPlist=readUserlandPlist(),{xml:extraPlistEntries,ignored:ignoredPlistKeys}=renderUserlandPlistEntries(declaredPlist);if(ignoredPlistKeys.length>0)log.info(`Ignoring reserved Info.plist keys from app/Desktop/Info.plist.json: ${ignoredPlistKeys.join(", ")}`);function overriddenByApp(key){return Object.hasOwn(declaredPlist,key)}writeFileSync(join(appDir,"Contents/Info.plist"),`<?xml version="1.0" encoding="UTF-8"?>
2
2
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
3
  <plist version="1.0">
4
4
  <dict>
@@ -28,4 +28,4 @@ ${overriddenByApp("LSMinimumSystemVersion")?"":` <key>LSMinimumSystemVersion</k
28
28
  <key>CFBundleIconFile</key><string>${iconFile}</string>`:""}${extraPlistEntries}
29
29
  </dict>
30
30
  </plist>
31
- `);if(signingIdentity){const nested=readdirSync(macosDir).filter((name)=>name!==launcherName).map((name)=>join(macosDir,name)),declaredEntitlements=appPath("Desktop/Entitlements.plist"),entitlements=existsSync(declaredEntitlements)?declaredEntitlements:void 0;for(const target of[...nested,join(macosDir,launcherName),appDir])await runBuildStep(codesignArgs({identity:signingIdentity,target,entitlements}),{cwd:staging,describe:`Signing ${basename(target)}`})}async function notarize(target,what){if(!signingIdentity)throw Error("DESKTOP_NOTARY_PROFILE needs DESKTOP_SIGNING_IDENTITY: Apple only notarizes signed code.");const upload=target.endsWith(".app")?join(scratch,`${basename(target)}.zip`):target;if(upload!==target)await runBuildStep(["ditto","-c","-k","--keepParent",target,upload],{cwd:scratch,describe:`Zipping ${basename(target)} for notarization`});log.info(`Notarizing ${what} (Apple usually answers within a few minutes)...`);const submitted=Bun.spawnSync(["xcrun","notarytool","submit",upload,"--keychain-profile",notaryProfile,"--wait"],{stderr:"pipe"}),report=`${submitted.stdout.toString()}${submitted.stderr.toString()}`,status=report.match(/status: (\w[\w ]*)/g)?.pop()?.replace("status: ","");if(submitted.exitCode!==0||status!=="Accepted"){console.error(report.trim());const id=report.match(/id: ([0-9a-f-]{36})/)?.[1];throw Error(`Notarization of ${what} ${status??"failed"}${id?` - see \`xcrun notarytool log ${id} --keychain-profile ${notaryProfile}\``:""}`)}await runBuildStep(["xcrun","stapler","staple",target],{cwd:dirname(target),describe:`Stapling the notarization ticket to ${what}`})}if(notaryProfile)await notarize(appDir,basename(appDir));symlinkSync("/Applications",join(staging,"Applications"));const dmgPath=join(outputDir,`${appName}-${version}.dmg`),created=await runCommand(["hdiutil","create","-volname",appName,"-srcfolder",staging,"-ov","-format","UDZO",dmgPath],{cwd:staging});if(created.isErr)throw Error(`hdiutil failed to create ${dmgPath}`);rmSync(staging,{recursive:!0,force:!0});rmSync(scratch,{recursive:!0,force:!0});if(signingIdentity)await runBuildStep(["codesign","--force","--timestamp","--sign",signingIdentity,dmgPath],{cwd:outputDir,describe:"Signing the DMG"});if(notaryProfile){await notarize(dmgPath,"the DMG");log.success("Notarized and stapled")}log.success(`Built ${dmgPath}`);if(!signingIdentity)log.info("Unsigned build: Gatekeeper blocks the first launch. Right-click the app and choose Open, or set DESKTOP_SIGNING_IDENTITY and DESKTOP_NOTARY_PROFILE before distributing.");else if(!notaryProfile)log.info("Signed but not notarized: other Macs still warn on first open. Set DESKTOP_NOTARY_PROFILE to notarize.");
31
+ `);if(signingIdentity){const nested=readdirSync(macosDir).filter((name)=>name!==launcherName).map((name)=>join(macosDir,name)),declaredEntitlements=appPath("Desktop/Entitlements.plist"),entitlements=existsSync(declaredEntitlements)?declaredEntitlements:void 0;for(const target of[...nested,join(macosDir,launcherName),appDir])await runBuildStep(codesignArgs({identity:signingIdentity,target,entitlements}),{cwd:staging,describe:`Signing ${basename(target)}`})}async function notarize(target,what){if(!signingIdentity)throw Error("DESKTOP_NOTARY_PROFILE needs DESKTOP_SIGNING_IDENTITY: Apple only notarizes signed code.");const upload=target.endsWith(".app")?join(scratch,`${basename(target)}.zip`):target;if(upload!==target)await runBuildStep(["ditto","-c","-k","--keepParent",target,upload],{cwd:scratch,describe:`Zipping ${basename(target)} for notarization`});log.info(`Notarizing ${what} (Apple usually answers within a few minutes)...`);const submitted=Bun.spawnSync(["xcrun","notarytool","submit",upload,"--keychain-profile",notaryProfile,"--wait"],{stderr:"pipe"}),report=`${submitted.stdout.toString()}${submitted.stderr.toString()}`,status=report.match(/status: (\w[\w ]*)/g)?.pop()?.replace("status: ","");if(submitted.exitCode!==0||status!=="Accepted"){console.error(report.trim());const id=report.match(/id: ([0-9a-f-]{36})/)?.[1];throw Error(`Notarization of ${what} ${status??"failed"}${id?` - see \`xcrun notarytool log ${id} --keychain-profile ${notaryProfile}\``:""}`)}await runBuildStep(["xcrun","stapler","staple",target],{cwd:dirname(target),describe:`Stapling the notarization ticket to ${what}`})}if(notaryProfile)await notarize(appDir,basename(appDir));symlinkSync("/Applications",join(staging,"Applications"));const dmgPath=join(outputDir,`${appName}-${version}.dmg`),volumeName=dmgVolumeName({appName,version,override:process.env.DESKTOP_VOLUME_NAME});if(volumeName===appName)log.warn(`DESKTOP_VOLUME_NAME is the app's own name, so the bundle stages at /Volumes/${appName}/${appName}.app. That path is refused on a Mac where ${appName} is already installed; drop the variable to get "${appName} ${version}".`);const created=await runCommand(["hdiutil","create","-volname",volumeName,"-srcfolder",staging,"-ov","-format","UDZO",dmgPath],{cwd:staging});if(created.isErr)throw Error(`hdiutil failed to create ${dmgPath}`);rmSync(staging,{recursive:!0,force:!0});rmSync(scratch,{recursive:!0,force:!0});if(signingIdentity)await runBuildStep(["codesign","--force","--timestamp","--sign",signingIdentity,dmgPath],{cwd:outputDir,describe:"Signing the DMG"});if(notaryProfile){await notarize(dmgPath,"the DMG");log.success("Notarized and stapled")}log.success(`Built ${dmgPath}`);if(!signingIdentity)log.info("Unsigned build: Gatekeeper blocks the first launch. Right-click the app and choose Open, or set DESKTOP_SIGNING_IDENTITY and DESKTOP_NOTARY_PROFILE before distributing.");else if(!notaryProfile)log.info("Signed but not notarized: other Macs still warn on first open. Set DESKTOP_NOTARY_PROFILE to notarize.");
package/dist/dev/views.js CHANGED
@@ -1 +1 @@
1
- import{existsSync}from"node:fs";import{join}from"node:path";import{config,enterRequestScope,feature,installRequestScope,overridesReady,resolveViewPatterns}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{projectPath,siteConfigPath}from"@stacksjs/path";import{seedCsrfPageResponse}from"./csrf";import{resolveDefaultsResources}from"./defaults-resources";import{exitWithParent}from"./exit-with-parent";installRequestScope();exitWithParent();const projectServe=projectPath("serve.ts");try{if(await Bun.file(projectServe).exists())await import(projectServe);else await startDefaultServer()}catch{await startDefaultServer()}async function startDefaultServer(){await overridesReady;const{applyViewSecurityHeaders,describeApiProxyRules,describeRedirectRules,describeRewriteRules,injectGlobalAutoImports,isApiBoundRequest,proxyToBackend,resolveApiProxyRules,resolveEmbeddableRules,resolveRedirect,resolveRedirectRules,resolveRewrite,resolveRewriteRules,viewClientAddress}=await import("@stacksjs/server"),{applyRequestLocale}=await import("@stacksjs/i18n");await injectGlobalAutoImports();const{serve}=await import("bun-plugin-stx/serve"),{site:siteConfig,i18n:i18nConfig}=await loadStxSiteConfig(),defaultsResources=resolveDefaultsResources(),userViewsPath="resources/views",defaultViewsPath=join(defaultsResources,"views"),userComponentsPath=await firstExistingPath(["resources/components","components"])??"resources/components",userLayoutsPath=await firstExistingPath(["resources/views/layouts","resources/layouts"])??"resources/views/layouts",defaultLayoutsPath=join(defaultsResources,"layouts"),userPartialsPath=await firstExistingPath(["resources/partials","resources/views/partials","partials","resources/components"]),preferredPort=Number(process.env.PORT)||3000,apiPort=Number(process.env.PORT_API)||3008,docsPort=Number(process.env.PORT_DOCS)||config.ports?.docs||3006,apiBase=`http://127.0.0.1:${apiPort}`,docsBase=`http://127.0.0.1:${docsPort}`,apiProxyRules=resolveApiProxyRules(config.server?.proxy),redirectRules=resolveRedirectRules(config.server?.redirects),rewriteRules=resolveRewriteRules(config.server?.rewrites),embeddableRules=resolveEmbeddableRules(config.server?.security?.embeddable);if(apiProxyRules.paths.length>0||apiProxyRules.prefixes.length>1)console.log(` API proxy: ${describeApiProxyRules(apiProxyRules)}`);if(embeddableRules.paths.length>0||embeddableRules.prefixes.length>0)console.log(` Frameable by other origins: ${[...embeddableRules.paths,...embeddableRules.prefixes].join(" ")}`);if(redirectRules.size>0)console.log(` Redirects: ${describeRedirectRules(redirectRules)}`);if(rewriteRules.size>0)console.log(` Rewrites: ${describeRewriteRules(rewriteRules)}`);const{createSiteSeoHandler,describeSeoAtBoot}=await import("../seo"),seoOptions={root:projectPath(),seo:config.app?.seo,appUrl:config.app?.url},siteSeo=createSiteSeoHandler({...seoOptions,cacheTtlMs:0});for(const note of await describeSeoAtBoot(seoOptions).catch(()=>[]))log.warn(note);const hasUserDocsView=["docs.stx","docs/index.stx"].some((rel)=>existsSync(projectPath(`${userViewsPath}/${rel}`))),hasDocsSite=existsSync(projectPath("docs")),docsProxyEnabled=!hasUserDocsView&&hasDocsSite,{authCookieName,stxPageAuthMiddleware}=await import("@stacksjs/auth"),{enhanceRequest,loadMiddlewareHandlers,registerPeerSource}=await import("@stacksjs/router"),authCookie=authCookieName(),pageMiddleware=await loadMiddlewareHandlers(),{mountedDefaultRouteBundles}=await import("@stacksjs/router"),viewPatterns=resolveViewPatterns(userViewsPath,defaultViewsPath,config?.ui?.defaultViews,(path)=>existsSync(projectPath(path)),void 0,mountedDefaultRouteBundles(feature));for(const name of viewPatterns.missing)log.warn(`ui.defaultViews lists "${name}", which does not exist under ${defaultViewsPath} - ignoring.`);if(viewPatterns.withheld.length>0)log.debug(`Default views without a mounted route bundle behind them, not served: ${viewPatterns.withheld.join(", ")}`);await serve({patterns:viewPatterns.patterns,exclude:viewPatterns.exclude,port:preferredPort,componentsDir:userComponentsPath,fallbackComponentsDir:join(defaultsResources,"components"),layoutsDir:userLayoutsPath,...userPartialsPath&&{partialsDir:userPartialsPath},fallbackLayoutsDir:defaultLayoutsPath,fallbackPartialsDir:defaultViewsPath,quiet:!0,openPath:process.env.STACKS_DEV_ENTRY_PATH||"/",...i18nConfig&&{i18n:i18nConfig},...siteConfig?.url&&{site:siteConfig},auth:{cookieName:authCookie,redirectTo:"/login"},middleware:{...pageMiddleware,...stxPageAuthMiddleware({cookieName:authCookie,redirectTo:"/login"})},prepareMiddlewareRequest:(request)=>enhanceRequest(request),clientAddress:viewClientAddress,onRequest:async(req,server)=>{const snapshot=enterRequestScope(req);registerPeerSource(server);const url=new URL(req.url),{maintenanceGate}=await import("@stacksjs/server"),gated=await maintenanceGate(req);if(gated)return gated;const redirected=resolveRedirect(url,redirectRules);if(redirected)return redirected;const rewritten=resolveRewrite(url.pathname,rewriteRules);if(rewritten){const target=new URL(`${rewritten}${url.search}`,url.origin);return proxyToBackend(new Request(target,req),apiBase,void 0,req)}const seoFile=await siteSeo(req);if(seoFile)return seoFile;if(existsSync(projectPath("resources/views/blog.stx"))){const{renderBlogFeed}=await import("../blog"),feed=await renderBlogFeed(req);if(feed)return feed}else{const{renderBlog}=await import("../blog"),blogResponse=await renderBlog(req);if(blogResponse)return blogResponse}if(docsProxyEnabled&&(url.pathname==="/docs"||url.pathname.startsWith("/docs/")))return proxyToBackend(req,docsBase,"/docs");if(isApiBoundRequest(req,url.pathname,apiProxyRules))return proxyToBackend(req,apiBase);if(i18nConfig&&req.method==="GET"){const localeSwitch=url.pathname.match(/^\/locale\/([a-z]{2}(?:-[a-z]{2})?)\/?$/i);if(localeSwitch){const{createLocaleSwitchResponse}=await import("@stacksjs/i18n");return createLocaleSwitchResponse(req,localeSwitch[1],i18nConfig)}}snapshot.locale=await applyRequestLocale(req);if(config.sites?.enabled){const sites=await import("@stacksjs/sites"),resolved=await sites.resolveSiteByHost(sites.requestHost(req.headers,sites.sitesOptions()));sites.setCurrentSite(resolved);snapshot.site=sites.toSiteSnapshot(resolved)}if(config.analytics?.capturePageviews)import("@stacksjs/analytics").then(({recordPageview})=>recordPageview(req)).catch(()=>{});return null},onResponse:async(req,response)=>{const secured=applyViewSecurityHeaders(req,response,embeddableRules),baseline=secured??response;let current=baseline;if(current.status===404&&config.sites?.enabled)try{const{cmsNotFoundFallback}=await import("@stacksjs/cms"),cmsResponse=await cmsNotFoundFallback(req);if(cmsResponse)current=cmsResponse}catch(error){log.debug(`CMS fallback skipped: ${error.message}`)}return await seedCsrfPageResponse(req,current)??(current===baseline?secured:current)}})}async function firstExistingPath(candidates){const existing=candidates.filter((candidate)=>existsSync(projectPath(candidate)));if(existing.length===0)return null;for(const candidate of existing)if(containsTemplates(projectPath(candidate)))return candidate;return existing[0]}function containsTemplates(dir){try{return[...new Bun.Glob("**/*.stx").scanSync({cwd:dir,onlyFiles:!0})].length>0}catch{return!1}}function fallbackI18nFromSite(site){const locales=site.i18n.locales,defaultLocale=site.i18n.defaultLocale??locales[0];return{locales,defaultLocale,labels:site.i18n.labels??Object.fromEntries(locales.map((c)=>[c,c.toUpperCase()])),translations:{},pickerSelector:site.i18n.pickerSelector??"#lang-picker"}}async function resolveSiteI18n(site){try{const{resolveI18n}=await import("@stacksjs/stx");if(typeof resolveI18n==="function"){const i18n=resolveI18n(site,projectPath());if(i18n)return i18n}}catch{}return fallbackI18nFromSite(site)}async function loadStxSiteConfig(){const sitePath=siteConfigPath();if(!existsSync(sitePath))return{};try{const mod=await import(sitePath),site=mod.default??mod.site??mod.config;if(!site)return{};if(!site.i18n)return{site};const i18n=await resolveSiteI18n(site);return{site,i18n}}catch{}return{}}
1
+ import{existsSync}from"node:fs";import{join}from"node:path";import{config,enterRequestScope,feature,installRequestScope,overridesReady,resolveViewPatterns}from"@stacksjs/config";import{log}from"@stacksjs/logging";import{projectPath,siteConfigPath}from"@stacksjs/path";import{seedCsrfPageResponse}from"./csrf";import{resolveDefaultsResources}from"./defaults-resources";import{exitWithParent}from"./exit-with-parent";installRequestScope();exitWithParent();const projectServe=projectPath("serve.ts");try{if(await Bun.file(projectServe).exists())await import(projectServe);else await startDefaultServer()}catch{await startDefaultServer()}async function startDefaultServer(){await overridesReady;const{applyViewSecurityHeaders,describeApiProxyRules,describeRedirectRules,describeRewriteRules,injectGlobalAutoImports,isApiBoundRequest,proxyToBackend,resolveApiProxyRules,resolveEmbeddableRules,resolveRedirect,resolveRedirectRules,resolveRewrite,resolveRewriteRules,viewClientAddress}=await import("@stacksjs/server"),{applyRequestLocale}=await import("@stacksjs/i18n");await injectGlobalAutoImports();const{serve}=await import("bun-plugin-stx/serve"),{site:siteConfig,i18n:i18nConfig}=await loadStxSiteConfig(),defaultsResources=resolveDefaultsResources(),userViewsPath="resources/views",defaultViewsPath=join(defaultsResources,"views"),userComponentsPath=await firstExistingPath(["resources/components","components"])??"resources/components",userLayoutsPath=await firstExistingPath(["resources/views/layouts","resources/layouts"])??"resources/views/layouts",defaultLayoutsPath=join(defaultsResources,"layouts"),userPartialsPath=await firstExistingPath(["resources/partials","resources/views/partials","partials","resources/components"]),preferredPort=Number(process.env.PORT)||3000,apiPort=Number(process.env.PORT_API)||3008,docsPort=Number(process.env.PORT_DOCS)||config.ports?.docs||3006,apiBase=`http://127.0.0.1:${apiPort}`,docsBase=`http://127.0.0.1:${docsPort}`,apiProxyRules=resolveApiProxyRules(config.server?.proxy),redirectRules=resolveRedirectRules(config.server?.redirects),rewriteRules=resolveRewriteRules(config.server?.rewrites),embeddableRules=resolveEmbeddableRules(config.server?.security?.embeddable);if(apiProxyRules.paths.length>0||apiProxyRules.prefixes.length>1)console.log(` API proxy: ${describeApiProxyRules(apiProxyRules)}`);if(embeddableRules.paths.length>0||embeddableRules.prefixes.length>0)console.log(` Frameable by other origins: ${[...embeddableRules.paths,...embeddableRules.prefixes].join(" ")}`);if(redirectRules.size>0)console.log(` Redirects: ${describeRedirectRules(redirectRules)}`);if(rewriteRules.size>0)console.log(` Rewrites: ${describeRewriteRules(rewriteRules)}`);const{createSiteSeoHandler,describeSeoAtBoot}=await import("../seo"),seoOptions={root:projectPath(),seo:config.app?.seo,appUrl:config.app?.url},siteSeo=createSiteSeoHandler({...seoOptions,cacheTtlMs:0});for(const note of await describeSeoAtBoot(seoOptions).catch(()=>[]))log.warn(note);const hasUserDocsView=["docs.stx","docs/index.stx"].some((rel)=>existsSync(projectPath(`${userViewsPath}/${rel}`))),hasDocsSite=existsSync(projectPath("docs")),docsProxyEnabled=!hasUserDocsView&&hasDocsSite,{authCookieName,stxPageAuthMiddleware}=await import("@stacksjs/auth"),{enhanceRequest,loadMiddlewareHandlers,registerPeerSource}=await import("@stacksjs/router"),authCookie=authCookieName(),pageMiddleware=await loadMiddlewareHandlers(),{mountedDefaultRouteBundles}=await import("@stacksjs/router"),viewPatterns=resolveViewPatterns(userViewsPath,defaultViewsPath,config?.ui?.defaultViews,(path)=>existsSync(projectPath(path)),void 0,mountedDefaultRouteBundles(feature));for(const name of viewPatterns.missing)log.warn(`ui.defaultViews lists "${name}", which does not exist under ${defaultViewsPath} - ignoring.`);if(viewPatterns.withheld.length>0)log.debug(`Default views without a mounted route bundle behind them, not served: ${viewPatterns.withheld.join(", ")}`);await serve({patterns:viewPatterns.patterns,exclude:viewPatterns.exclude,port:preferredPort,componentsDir:userComponentsPath,fallbackComponentsDir:join(defaultsResources,"components"),layoutsDir:userLayoutsPath,...userPartialsPath&&{partialsDir:userPartialsPath},fallbackLayoutsDir:defaultLayoutsPath,fallbackPartialsDir:defaultViewsPath,quiet:!0,openPath:process.env.STACKS_DEV_ENTRY_PATH||"/",...i18nConfig&&{i18n:i18nConfig},...siteConfig?.url&&{site:siteConfig},...config.server?.domains&&{domains:config.server.domains},auth:{cookieName:authCookie,redirectTo:"/login"},middleware:{...pageMiddleware,...stxPageAuthMiddleware({cookieName:authCookie,redirectTo:"/login"})},prepareMiddlewareRequest:(request)=>enhanceRequest(request),clientAddress:viewClientAddress,onRequest:async(req,server)=>{const snapshot=enterRequestScope(req);registerPeerSource(server);const url=new URL(req.url),{maintenanceGate}=await import("@stacksjs/server"),gated=await maintenanceGate(req);if(gated)return gated;const redirected=resolveRedirect(url,redirectRules);if(redirected)return redirected;const rewritten=resolveRewrite(url.pathname,rewriteRules);if(rewritten){const target=new URL(`${rewritten}${url.search}`,url.origin);return proxyToBackend(new Request(target,req),apiBase,void 0,req)}const seoFile=await siteSeo(req);if(seoFile)return seoFile;if(existsSync(projectPath("resources/views/blog.stx"))){const{renderBlogFeed}=await import("../blog"),feed=await renderBlogFeed(req);if(feed)return feed}else{const{renderBlog}=await import("../blog"),blogResponse=await renderBlog(req);if(blogResponse)return blogResponse}if(docsProxyEnabled&&(url.pathname==="/docs"||url.pathname.startsWith("/docs/")))return proxyToBackend(req,docsBase,"/docs");if(isApiBoundRequest(req,url.pathname,apiProxyRules))return proxyToBackend(req,apiBase);if(i18nConfig&&req.method==="GET"){const localeSwitch=url.pathname.match(/^\/locale\/([a-z]{2}(?:-[a-z]{2})?)\/?$/i);if(localeSwitch){const{createLocaleSwitchResponse}=await import("@stacksjs/i18n");return createLocaleSwitchResponse(req,localeSwitch[1],i18nConfig)}}snapshot.locale=await applyRequestLocale(req);if(config.sites?.enabled){const sites=await import("@stacksjs/sites"),resolved=await sites.resolveSiteByHost(sites.requestHost(req.headers,sites.sitesOptions()));sites.setCurrentSite(resolved);snapshot.site=sites.toSiteSnapshot(resolved)}if(config.analytics?.capturePageviews)import("@stacksjs/analytics").then(({recordPageview})=>recordPageview(req)).catch(()=>{});return null},onResponse:async(req,response)=>{const secured=applyViewSecurityHeaders(req,response,embeddableRules),baseline=secured??response;let current=baseline;if(current.status===404&&config.sites?.enabled)try{const{cmsNotFoundFallback}=await import("@stacksjs/cms"),cmsResponse=await cmsNotFoundFallback(req);if(cmsResponse)current=cmsResponse}catch(error){log.debug(`CMS fallback skipped: ${error.message}`)}return await seedCsrfPageResponse(req,current)??(current===baseline?secured:current)}})}async function firstExistingPath(candidates){const existing=candidates.filter((candidate)=>existsSync(projectPath(candidate)));if(existing.length===0)return null;for(const candidate of existing)if(containsTemplates(projectPath(candidate)))return candidate;return existing[0]}function containsTemplates(dir){try{return[...new Bun.Glob("**/*.stx").scanSync({cwd:dir,onlyFiles:!0})].length>0}catch{return!1}}function fallbackI18nFromSite(site){const locales=site.i18n.locales,defaultLocale=site.i18n.defaultLocale??locales[0];return{locales,defaultLocale,labels:site.i18n.labels??Object.fromEntries(locales.map((c)=>[c,c.toUpperCase()])),translations:{},pickerSelector:site.i18n.pickerSelector??"#lang-picker"}}async function resolveSiteI18n(site){try{const{resolveI18n}=await import("@stacksjs/stx");if(typeof resolveI18n==="function"){const i18n=resolveI18n(site,projectPath());if(i18n)return i18n}}catch{}return fallbackI18nFromSite(site)}async function loadStxSiteConfig(){const sitePath=siteConfigPath();if(!existsSync(sitePath))return{};try{const mod=await import(sitePath),site=mod.default??mod.site??mod.config;if(!site)return{};if(!site.i18n)return{site};const i18n=await resolveSiteI18n(site);return{site,i18n}}catch{}return{}}
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.98",
5
+ "version": "0.75.100",
6
6
  "description": "The Stacks actions.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -66,53 +66,53 @@
66
66
  "prepublishOnly": "bun run build"
67
67
  },
68
68
  "dependencies": {
69
- "@stacksjs/action-runner": "0.75.98",
70
- "@stacksjs/ai": "0.75.98",
71
- "@stacksjs/analytics": "0.75.98",
72
- "@stacksjs/api": "0.75.98",
73
- "@stacksjs/auth": "0.75.98",
74
- "@stacksjs/build": "0.75.98",
69
+ "@stacksjs/action-runner": "0.75.100",
70
+ "@stacksjs/ai": "0.75.100",
71
+ "@stacksjs/analytics": "0.75.100",
72
+ "@stacksjs/api": "0.75.100",
73
+ "@stacksjs/auth": "0.75.100",
74
+ "@stacksjs/build": "0.75.100",
75
75
  "@stacksjs/bumpx": "^0.2.18",
76
76
  "@stacksjs/bunpress": "^0.2.15",
77
- "@stacksjs/cli": "0.75.98",
78
- "@stacksjs/cloud": "0.75.98",
79
- "@stacksjs/cms": "0.75.98",
77
+ "@stacksjs/cli": "0.75.100",
78
+ "@stacksjs/cloud": "0.75.100",
79
+ "@stacksjs/cms": "0.75.100",
80
80
  "@stacksjs/components": "^0.2.406",
81
- "@stacksjs/config": "0.75.98",
82
- "@stacksjs/database": "0.75.98",
83
- "@stacksjs/desktop-build": "0.75.98",
84
- "@stacksjs/dns": "0.75.98",
85
- "@stacksjs/enums": "0.75.98",
86
- "@stacksjs/env": "0.75.98",
87
- "@stacksjs/error-handling": "0.75.98",
81
+ "@stacksjs/config": "0.75.100",
82
+ "@stacksjs/database": "0.75.100",
83
+ "@stacksjs/desktop-build": "0.75.100",
84
+ "@stacksjs/dns": "0.75.100",
85
+ "@stacksjs/enums": "0.75.100",
86
+ "@stacksjs/env": "0.75.100",
87
+ "@stacksjs/error-handling": "0.75.100",
88
88
  "@stacksjs/gitit": "^0.2.5",
89
- "@stacksjs/i18n": "0.75.98",
90
- "@stacksjs/image": "0.75.98",
91
- "@stacksjs/logging": "0.75.98",
89
+ "@stacksjs/i18n": "0.75.100",
90
+ "@stacksjs/image": "0.75.100",
91
+ "@stacksjs/logging": "0.75.100",
92
92
  "@stacksjs/logsmith": "^0.2.11",
93
- "@stacksjs/mobile": "0.75.98",
94
- "@stacksjs/orm": "0.75.98",
95
- "@stacksjs/path": "0.75.98",
96
- "@stacksjs/payments": "0.75.98",
97
- "@stacksjs/queue": "0.75.98",
98
- "@stacksjs/registry": "0.75.98",
99
- "@stacksjs/router": "0.75.98",
93
+ "@stacksjs/mobile": "0.75.100",
94
+ "@stacksjs/orm": "0.75.100",
95
+ "@stacksjs/path": "0.75.100",
96
+ "@stacksjs/payments": "0.75.100",
97
+ "@stacksjs/queue": "0.75.100",
98
+ "@stacksjs/registry": "0.75.100",
99
+ "@stacksjs/router": "0.75.100",
100
100
  "@stacksjs/rpx": "^0.11.54",
101
- "@stacksjs/scheduler": "0.75.98",
102
- "@stacksjs/search-engine": "0.75.98",
103
- "@stacksjs/security": "0.75.98",
104
- "@stacksjs/server": "0.75.98",
105
- "@stacksjs/sites": "0.75.98",
106
- "@stacksjs/storage": "0.75.98",
107
- "@stacksjs/strings": "0.75.98",
101
+ "@stacksjs/scheduler": "0.75.100",
102
+ "@stacksjs/search-engine": "0.75.100",
103
+ "@stacksjs/security": "0.75.100",
104
+ "@stacksjs/server": "0.75.100",
105
+ "@stacksjs/sites": "0.75.100",
106
+ "@stacksjs/storage": "0.75.100",
107
+ "@stacksjs/strings": "0.75.100",
108
108
  "@stacksjs/stx": "^0.2.408",
109
109
  "@stacksjs/tlsx": "^0.13.19",
110
110
  "@stacksjs/ts-cloud": "^0.16.47",
111
111
  "@stacksjs/ts-md": "^0.1.1",
112
- "@stacksjs/tunnel": "0.75.98",
113
- "@stacksjs/types": "0.75.98",
114
- "@stacksjs/utils": "0.75.98",
115
- "@stacksjs/whois": "0.75.98",
112
+ "@stacksjs/tunnel": "0.75.100",
113
+ "@stacksjs/types": "0.75.100",
114
+ "@stacksjs/utils": "0.75.100",
115
+ "@stacksjs/whois": "0.75.100",
116
116
  "bun-plugin-stx": "^0.2.408"
117
117
  },
118
118
  "peerDependencies": {
@@ -128,7 +128,7 @@
128
128
  }
129
129
  },
130
130
  "devDependencies": {
131
- "@stacksjs/validation": "0.75.98",
131
+ "@stacksjs/validation": "0.75.100",
132
132
  "better-dx": "^0.2.24"
133
133
  }
134
134
  }