@stacksjs/buddy 0.75.423 → 0.75.431

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.
@@ -11,6 +11,12 @@ export declare function create(buddy: CLI): void;
11
11
  * `buddy new` was asked to do.
12
12
  */
13
13
  export declare function scaffoldTargetOccupants(path: string): string[];
14
+ /**
15
+ * Why github.com cannot be reached, or `null` when it can. Any HTTP answer
16
+ * proves the network is up, a 429 or a 503 included: this used to require a
17
+ * 2xx, so a rate-limited HEAD told a CI runner it was offline.
18
+ */
19
+ export declare function offlineReason(fetcher?: typeof fetch): Promise<string | null>;
14
20
  /**
15
21
  * Uses `@stacksjs/gitit`'s library API directly rather than shelling out to
16
22
  * `bunx --bun @stacksjs/gitit`. `bunx` always resolves the published npm
@@ -1 +1 @@
1
- import{chmodSync,cpSync,existsSync,readFileSync,readdirSync,rmSync,writeFileSync}from"node:fs";import process from"node:process";import{runAction}from"@stacksjs/actions";import{bold,cyan,dim,intro,log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{relative,resolve}from"@stacksjs/path";import{isFolder}from"@stacksjs/storage";import{ExitCode}from"@stacksjs/types";import{uninstallAllFeatures}from"./features";import{ensurePantryDependencies,ensurePantryInstalled,optimizePantryDeps}from"./setup";import{resultFailed}from"../result";import{appIdentity,applyAppAgentsTemplate,applyAppChangelogTemplate,applyAppEnvTemplate,applyAppPackageTemplate,removeFrameworkLicense,renderTemplate}from"../scaffold-app";import{applyAppSiteTemplate}from"../scaffold-site";import{adoptPackagedDefaults,applyAppPackageScripts,applyPackageAppTsconfig,pruneFrameworkOnlyFiles,rewriteCoreContentPaths,stripFrameworkManifestCredits}from"../scaffold-package-app";import{fetchPublishedVersions}from"../registry";import{ensureAuthRoutesEnv}from"../minimal-routes";import{scaffoldDesktopApp}from"../scaffold-desktop";export function create(buddy){const descriptions={name:"The name of the project",command:"Create a new Stacks project",ui:"Are you building a UI?",components:"Are you building UI components?",webComponents:"Automagically built optimized custom elements/web components?",views:"How about views?",functions:"Are you developing functions/composables?",api:"Are you building an API?",database:"Do you need a database?",notifications:"Do you need notifications? e.g. email, SMS, push or chat notifications",cache:"Do you need caching?",email:"Do you need email?",project:"Target a specific project",minimal:"Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) - bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",withCore:"Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",vendorDefaults:"Commit a copy of the framework defaults in `storage/framework/defaults` instead of linking it to node_modules/@stacksjs/defaults. Implied by --with-core.",verbose:"Enable verbose output"};buddy.command("new [name]",descriptions.command).alias("create").option("-n, --name [name]",descriptions.name,{default:!1}).option("-u, --ui",descriptions.ui,{default:!0}).option("-c, --components",descriptions.components,{default:!0}).option("-w, --web-components",descriptions.webComponents,{default:!0}).option("-p, --views",descriptions.views,{default:!0}).option("-f, --functions",descriptions.functions,{default:!0}).option("-a, --api",descriptions.api,{default:!0}).option("-d, --database",descriptions.database,{default:!0}).option("-ca, --cache",descriptions.cache,{default:!1}).option("-e, --email",descriptions.email,{default:!1}).option("-P, --project [project]",descriptions.project,{default:!1}).option("-m, --minimal",descriptions.minimal,{default:!1}).option("--native","Create a minimal offline Craft desktop app",{default:!1}).option("--desktop","Also make it a native Mac app: stx screens drawn by AppKit, with the API running inside the app (config/desktop.ts, macos.agent)",{default:!1}).option("--adopt","Integrate an existing native app without replacing its source (requires --native)",{default:!1}).option("--with-core",descriptions.withCore,{default:!1}).option("--vendor-defaults",descriptions.vendorDefaults,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(name,options)=>{log.debug("Running `buddy new <name>` ...",options);const startTime=await intro("buddy new");name=name??options.name;const path=resolve(process.cwd(),name??".");if(options.adopt&&!options.native)throw Error("--adopt requires --native");if(options.desktop&&options.native)throw Error("--desktop makes a Stacks app with a native Mac app; --native makes a standalone Craft app. Choose one.");if(options.native){const{scaffoldNativeProject}=await import("../native-project"),{version}=await import("../../package.json"),{latest}=await fetchPublishedVersions("@stacksjs/stx");if(!latest)throw Error("The registry has no published stx version.");scaffoldNativeProject(path,{adopt:options.adopt,buddyVersion:version,stxVersion:latest});const installed=await runCommand(["bun","install"],{cwd:path});if(resultFailed(installed))throw installed.error;log.success(`Native app ready at ${path}. Run ./buddy dev to launch.`);await log.flush();return}isFolderCheck(path);await onlineCheck();const result=await download(name,path,options);if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}ensureExecutableScripts(path);applyAppVcsTemplate(path);applyAppConfigTemplate(path);applyAppEnv(path);applyAppPackage(path);applyAppReadme(path);applyAppAgents(path);replaceFrameworkSite(path);removeFrameworkTests(path);await generatePantryDeps(path);await ensureEnv(path,options);if(options.minimal)await stripFeatures(path);await install(path,options);if(!options.withCore){trimFrameworkOnlyFiles(path);await unvendorCore(path,options);applyPackageAppTsconfig(path);if(!options.vendorDefaults)linkFrameworkDefaults(path)}if(options.minimal||!options.withCore&&!options.vendorDefaults)await regenerateAutoImports(path,options);if(options.desktop){const written=scaffoldDesktopApp(path,appIdentity(path));log.success(`Added the Mac app (${written.join(", ")}). Run ${cyan("bun run dev:desktop")} to open it.`)}if(startTime){const time=performance.now()-startTime;log.success(dim(`[${time.toFixed(2)}ms] Completed`))}log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);log.info("To learn more, visit https://stacksjs.com");await log.flush();process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"new")}export function scaffoldTargetOccupants(path){if(!isFolder(path))return[];return readdirSync(path).filter((entry)=>entry!==".git")}function isFolderCheck(path){if(scaffoldTargetOccupants(path).length===0)return;console.error(`Path ${path} already exists`);process.exit(ExitCode.FatalError)}async function onlineCheck(){if(await isOnline())return;log.info("It appears you are disconnected from the internet.");log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");await log.flush();process.exit(ExitCode.FatalError)}async function isOnline(){try{return(await fetch("https://github.com",{method:"HEAD",signal:AbortSignal.timeout(3000)})).ok}catch{return!1}}export async function templateRef(fetchVersions=fetchPublishedVersions){try{const{latest}=await fetchVersions("stacks");return latest?`v${latest}`:null}catch{return null}}export function templateSpec(ref){return ref?`gh:stacksjs/stacks#${ref}`:"gh:stacksjs/stacks"}async function download(name,path,_options){log.info("Setting up your stack.");const ref=await templateRef();try{const{downloadTemplate}=await import("@stacksjs/gitit");try{await downloadTemplate(templateSpec(ref),{dir:name,force:!0})}catch(error){if(!ref)throw error;log.warn(`No ${ref} tag to scaffold from (${error instanceof Error?error.message:String(error)}).`);log.info("Falling back to the default branch. If this app does not typecheck, that gap is why.");await downloadTemplate(templateSpec(null),{dir:name,force:!0})}log.success(`Successfully scaffolded your project at ${cyan(path)}`);return{isErr:!1}}catch(error){return{isErr:!0,error:error instanceof Error?error.message:String(error)}}}function applyAppVcsTemplate(path){const source=resolve(path,"storage/framework/defaults/vcs/github"),destination=resolve(path,".github");if(!existsSync(source)){log.warn("No app CI template found at storage/framework/defaults/vcs/github - leaving .github as downloaded.");return}log.info("Installing app-shaped GitHub workflows...");try{rmSync(destination,{recursive:!0,force:!0});cpSync(source,destination,{recursive:!0});log.success("App CI installed")}catch(error){log.warn(`Could not install the app CI template: ${error instanceof Error?error.message:String(error)}`)}}function applyAppConfigTemplate(path){const source=resolve(path,"storage/framework/defaults/scaffold/config"),destination=resolve(path,"config");if(!existsSync(source)){log.warn("No app config template found at storage/framework/defaults/scaffold/config - leaving config as downloaded.");return}const identity=appIdentity(path);log.info("Installing app-safe infrastructure configuration...");try{for(const file of readdirSync(source)){if(!file.endsWith(".ts"))continue;const template=readFileSync(resolve(source,file),"utf8");writeFileSync(resolve(destination,file),renderTemplate(template,identity))}log.success("App-safe infrastructure configuration installed")}catch(error){log.warn(`Could not install the app config template: ${error instanceof Error?error.message:String(error)}`)}}function applyAppEnv(path){try{const changed=applyAppEnvTemplate(path,appIdentity(path));if(changed.length>0)log.success(`Named the app in .env.example (${changed.join(", ")})`)}catch(error){log.warn(`Could not name the app in .env.example: ${error instanceof Error?error.message:String(error)}`)}}function applyAppPackage(path){try{const identity=appIdentity(path),changed=applyAppPackageTemplate(path,identity);if(changed.length>0)log.success(`Named the app in package.json (${changed.join(", ")})`);if(applyAppChangelogTemplate(path,identity))log.success("Started the app its own CHANGELOG.md");if(removeFrameworkLicense(path))log.success("Removed the framework's LICENSE.md - choose the app's own licence")}catch(error){log.warn(`Could not name the app in package.json: ${error instanceof Error?error.message:String(error)}`)}}function applyAppAgents(path){try{if(applyAppAgentsTemplate(path))log.success("AGENTS.md no longer tells agents to publish as the framework's author")}catch(error){log.warn(`Could not update AGENTS.md: ${error instanceof Error?error.message:String(error)}`)}}function applyAppReadme(path){const source=resolve(path,"storage/framework/defaults/scaffold/README.md");if(!existsSync(source))return;try{writeFileSync(resolve(path,"README.md"),renderTemplate(readFileSync(source,"utf8"),appIdentity(path)))}catch(error){log.warn(`Could not write the app README: ${error instanceof Error?error.message:String(error)}`)}}function replaceFrameworkSite(path){log.info("Replacing the stacksjs.com site with a starter page...");try{const removed=applyAppSiteTemplate(path);log.success(`Starter page installed (${removed.length} stacksjs.com path${removed.length===1?"":"s"} removed)`)}catch(error){log.warn(`Could not install the starter page: ${error instanceof Error?error.message:String(error)}`)}}function removeFrameworkTests(path){const tests=resolve(path,"tests");if(!existsSync(tests))return;log.info("Removing the framework's own test suite...");for(const entry of readdirSync(tests)){if(entry==="setup.ts")continue;rmSync(resolve(tests,entry),{recursive:!0,force:!0})}log.success("App starts with a clean test suite")}function ensureExecutableScripts(path){for(const script of["buddy","bootstrap"])try{chmodSync(resolve(path,script),493)}catch{}}async function ensureEnv(path,_options){log.info("Ensuring your environment is ready...");await ensurePantryInstalled();await ensurePantryDependencies(path);log.success("Environment is ready")}async function install(path,options){log.info("Installing & setting up Stacks");log.info("Copying .env.example \u2192 .env");let result=await runCommand("cp .env.example .env",{...options,cwd:path});if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}log.info("Removing template-encrypted env files...");const{rm}=await import("node:fs/promises");for(const stale of[".env.development",".env.staging",".env.production",".env.keys"])await rm(`${path}/${stale}`,{force:!0});log.info("Generating application key...");const keyResult=await runAction(Action.KeyGenerate,{...options,cwd:path});if(resultFailed(keyResult)){await log.error(keyResult.error);process.exit(ExitCode.FatalError)}if(existsSync(resolve(path,".git")))log.info("Existing git repository detected, skipping git init");else{log.info("Initializing git repository...");result=await runCommand("git init",{...options,cwd:path});if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}}log.success("Installed & set-up \uD83D\uDE80")}async function unvendorCore(path,options){log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");const result=await runCommand("./buddy unpublish:core --all --force",{...options,cwd:path});if(resultFailed(result)){const reason=result.error instanceof Error?result.error.stack??result.error.message:String(result.error??"").trim();log.error("Could not resolve the framework from npm.");log.error(reason.length>0?reason:"The step failed without reporting a reason.");log.error("");log.error(`The project at ${path} is half converted: the vendored framework has been`);log.error("removed and the published packages are not in place yet, so ./buddy cannot");log.error("boot there. Finish it by hand with:");log.error("");await log.error(` cd ${path} && rm -f node_modules/stacks && bun install`);await log.error("");await log.error("Or start over with `--with-core` to keep the framework vendored.");process.exit(ExitCode.FatalError)}log.success("Framework resolved from npm")}async function generatePantryDeps(path){try{if(await optimizePantryDeps(path))rmSync(resolve(path,"deps.yml"),{force:!0})}catch(error){log.warn(`Could not generate deps.yaml from config/deps.ts, keeping the template's deps.yml: ${error instanceof Error?error.message:String(error)}`)}}function trimFrameworkOnlyFiles(path){try{const removed=pruneFrameworkOnlyFiles(path),scripts=applyAppPackageScripts(path);rewriteCoreContentPaths(path);stripFrameworkManifestCredits(path);log.success(`Removed ${removed.length} framework-only path${removed.length===1?"":"s"} and ${scripts.removed.length} framework-only script${scripts.removed.length===1?"":"s"}`)}catch(error){log.warn(`Could not remove the framework-only files: ${error instanceof Error?error.message:String(error)}`)}}function linkFrameworkDefaults(path){log.info("Linking the framework defaults from node_modules (pass --vendor-defaults to commit a copy)...");try{const adopted=adoptPackagedDefaults(path);log.success(`storage/framework/defaults now links to ${relative(path,adopted.packageRoot)}`)}catch(error){log.warn(`Could not move the framework defaults to node_modules: ${error instanceof Error?error.message:String(error)}`)}}async function regenerateAutoImports(path,options){const result=await runCommand("./buddy generate --types",{...options,cwd:path});if(resultFailed(result))log.warn("Could not regenerate the auto-import barrels. Run `./buddy generate --types` in the project before `./buddy dev`.")}async function stripFeatures(path){log.info("Stripping optional feature bundles (--minimal)...");const results=await uninstallAllFeatures({root:path}),authEnv=ensureAuthRoutesEnv(path);if(authEnv.length>0)log.info(` - kept the auth routes mounted (STACKS_DEFAULT_ROUTES=auth in ${authEnv.join(", ")})`);let strippedAny=!1;for(const{feature,configOutcome,filesRemoved}of results)if(configOutcome==="flipped"||filesRemoved.length>0){strippedAny=!0;const fileSummary=filesRemoved.length>0?` (${filesRemoved.length} path${filesRemoved.length===1?"":"s"})`:"";log.info(` - ${feature}${fileSummary}`)}if(!strippedAny)log.info(" \u2192 no feature scaffolding present; nothing to strip.");else log.success("Minimal skeleton ready - run `./buddy <feature>:install` to add features back.")}
1
+ import{chmodSync,cpSync,existsSync,readFileSync,readdirSync,rmSync,writeFileSync}from"node:fs";import process from"node:process";import{runAction}from"@stacksjs/actions";import{bold,cyan,dim,intro,log,onUnknownSubcommand,runCommand}from"@stacksjs/cli";import{Action}from"@stacksjs/enums";import{relative,resolve}from"@stacksjs/path";import{isFolder}from"@stacksjs/storage";import{ExitCode}from"@stacksjs/types";import{uninstallAllFeatures}from"./features";import{ensurePantryDependencies,ensurePantryInstalled,optimizePantryDeps}from"./setup";import{resultFailed}from"../result";import{appIdentity,applyAppAgentsTemplate,applyAppChangelogTemplate,applyAppEnvTemplate,applyAppPackageTemplate,removeFrameworkLicense,renderTemplate}from"../scaffold-app";import{applyAppSiteTemplate}from"../scaffold-site";import{adoptPackagedDefaults,applyAppPackageScripts,applyPackageAppTsconfig,pruneFrameworkOnlyFiles,rewriteCoreContentPaths,stripFrameworkManifestCredits}from"../scaffold-package-app";import{fetchPublishedVersions}from"../registry";import{ensureAuthRoutesEnv}from"../minimal-routes";import{scaffoldDesktopApp}from"../scaffold-desktop";export function create(buddy){const descriptions={name:"The name of the project",command:"Create a new Stacks project",ui:"Are you building a UI?",components:"Are you building UI components?",webComponents:"Automagically built optimized custom elements/web components?",views:"How about views?",functions:"Are you developing functions/composables?",api:"Are you building an API?",database:"Do you need a database?",notifications:"Do you need notifications? e.g. email, SMS, push or chat notifications",cache:"Do you need caching?",email:"Do you need email?",project:"Target a specific project",minimal:"Skip optional feature bundles (cms, commerce, dashboard, marketing, monitoring, realtime, queue) - bare-bones API/SPA starter that can re-add them later via `./buddy <feature>:install`.",withCore:"Keep the framework vendored in `storage/framework/core` as a Bun workspace, for working ON Stacks. Apps that only work WITH Stacks want the default, which resolves every @stacksjs/* package from npm.",vendorDefaults:"Commit a copy of the framework defaults in `storage/framework/defaults` instead of linking it to node_modules/@stacksjs/defaults. Implied by --with-core.",verbose:"Enable verbose output"};buddy.command("new [name]",descriptions.command).alias("create").option("-n, --name [name]",descriptions.name,{default:!1}).option("-u, --ui",descriptions.ui,{default:!0}).option("-c, --components",descriptions.components,{default:!0}).option("-w, --web-components",descriptions.webComponents,{default:!0}).option("-p, --views",descriptions.views,{default:!0}).option("-f, --functions",descriptions.functions,{default:!0}).option("-a, --api",descriptions.api,{default:!0}).option("-d, --database",descriptions.database,{default:!0}).option("-ca, --cache",descriptions.cache,{default:!1}).option("-e, --email",descriptions.email,{default:!1}).option("-P, --project [project]",descriptions.project,{default:!1}).option("-m, --minimal",descriptions.minimal,{default:!1}).option("--native","Create a minimal offline Craft desktop app",{default:!1}).option("--desktop","Also make it a native Mac app: stx screens drawn by AppKit, with the API running inside the app (config/desktop.ts, macos.agent)",{default:!1}).option("--adopt","Integrate an existing native app without replacing its source (requires --native)",{default:!1}).option("--with-core",descriptions.withCore,{default:!1}).option("--vendor-defaults",descriptions.vendorDefaults,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(name,options)=>{log.debug("Running `buddy new <name>` ...",options);const startTime=await intro("buddy new");name=name??options.name;const path=resolve(process.cwd(),name??".");if(options.adopt&&!options.native)throw Error("--adopt requires --native");if(options.desktop&&options.native)throw Error("--desktop makes a Stacks app with a native Mac app; --native makes a standalone Craft app. Choose one.");if(options.native){const{scaffoldNativeProject}=await import("../native-project"),{version}=await import("../../package.json"),{latest}=await fetchPublishedVersions("@stacksjs/stx");if(!latest)throw Error("The registry has no published stx version.");scaffoldNativeProject(path,{adopt:options.adopt,buddyVersion:version,stxVersion:latest});const installed=await runCommand(["bun","install"],{cwd:path});if(resultFailed(installed))throw installed.error;log.success(`Native app ready at ${path}. Run ./buddy dev to launch.`);await log.flush();return}isFolderCheck(path);await onlineCheck();const result=await download(name,path,options);if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}ensureExecutableScripts(path);applyAppVcsTemplate(path);applyAppConfigTemplate(path);applyAppEnv(path);applyAppPackage(path);applyAppReadme(path);applyAppAgents(path);replaceFrameworkSite(path);removeFrameworkTests(path);await generatePantryDeps(path);await ensureEnv(path,options);if(options.minimal)await stripFeatures(path);await install(path,options);if(!options.withCore){trimFrameworkOnlyFiles(path);await unvendorCore(path,options);applyPackageAppTsconfig(path);if(!options.vendorDefaults)linkFrameworkDefaults(path)}if(options.minimal||!options.withCore&&!options.vendorDefaults)await regenerateAutoImports(path,options);if(options.desktop){const written=scaffoldDesktopApp(path,appIdentity(path));log.success(`Added the Mac app (${written.join(", ")}). Run ${cyan("bun run dev:desktop")} to open it.`)}if(startTime){const time=performance.now()-startTime;log.success(dim(`[${time.toFixed(2)}ms] Completed`))}log.info(bold("Welcome to the Stacks Framework! \u269B\uFE0F"));log.info(`Get started: ${cyan(`cd ${name}`)} and then ${cyan("./buddy dev")}`);log.info(`Run ${cyan("./buddy doctor")} anytime to check your setup`);log.info("To learn more, visit https://stacksjs.com");await log.flush();process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"new")}export function scaffoldTargetOccupants(path){if(!isFolder(path))return[];return readdirSync(path).filter((entry)=>entry!==".git")}function isFolderCheck(path){if(scaffoldTargetOccupants(path).length===0)return;console.error(`Path ${path} already exists`);process.exit(ExitCode.FatalError)}async function onlineCheck(){const offline=await offlineReason();if(!offline)return;log.info(`It appears you are disconnected from the internet (github.com: ${offline}).`);log.info("Creating a new project requires a brief internet connection to download the template and install dependencies.");await log.flush();process.exit(ExitCode.FatalError)}export async function offlineReason(fetcher=fetch){try{await fetcher("https://github.com",{method:"HEAD",signal:AbortSignal.timeout(3000)});return null}catch(error){return error instanceof Error?error.message:String(error)}}export async function templateRef(fetchVersions=fetchPublishedVersions){try{const{latest}=await fetchVersions("stacks");return latest?`v${latest}`:null}catch{return null}}export function templateSpec(ref){return ref?`gh:stacksjs/stacks#${ref}`:"gh:stacksjs/stacks"}async function download(name,path,_options){log.info("Setting up your stack.");const ref=await templateRef();try{const{downloadTemplate}=await import("@stacksjs/gitit");try{await downloadTemplate(templateSpec(ref),{dir:name,force:!0})}catch(error){if(!ref)throw error;log.warn(`No ${ref} tag to scaffold from (${error instanceof Error?error.message:String(error)}).`);log.info("Falling back to the default branch. If this app does not typecheck, that gap is why.");await downloadTemplate(templateSpec(null),{dir:name,force:!0})}log.success(`Successfully scaffolded your project at ${cyan(path)}`);return{isErr:!1}}catch(error){return{isErr:!0,error:error instanceof Error?error.message:String(error)}}}function applyAppVcsTemplate(path){const source=resolve(path,"storage/framework/defaults/vcs/github"),destination=resolve(path,".github");if(!existsSync(source)){log.warn("No app CI template found at storage/framework/defaults/vcs/github - leaving .github as downloaded.");return}log.info("Installing app-shaped GitHub workflows...");try{rmSync(destination,{recursive:!0,force:!0});cpSync(source,destination,{recursive:!0});log.success("App CI installed")}catch(error){log.warn(`Could not install the app CI template: ${error instanceof Error?error.message:String(error)}`)}}function applyAppConfigTemplate(path){const source=resolve(path,"storage/framework/defaults/scaffold/config"),destination=resolve(path,"config");if(!existsSync(source)){log.warn("No app config template found at storage/framework/defaults/scaffold/config - leaving config as downloaded.");return}const identity=appIdentity(path);log.info("Installing app-safe infrastructure configuration...");try{for(const file of readdirSync(source)){if(!file.endsWith(".ts"))continue;const template=readFileSync(resolve(source,file),"utf8");writeFileSync(resolve(destination,file),renderTemplate(template,identity))}log.success("App-safe infrastructure configuration installed")}catch(error){log.warn(`Could not install the app config template: ${error instanceof Error?error.message:String(error)}`)}}function applyAppEnv(path){try{const changed=applyAppEnvTemplate(path,appIdentity(path));if(changed.length>0)log.success(`Named the app in .env.example (${changed.join(", ")})`)}catch(error){log.warn(`Could not name the app in .env.example: ${error instanceof Error?error.message:String(error)}`)}}function applyAppPackage(path){try{const identity=appIdentity(path),changed=applyAppPackageTemplate(path,identity);if(changed.length>0)log.success(`Named the app in package.json (${changed.join(", ")})`);if(applyAppChangelogTemplate(path,identity))log.success("Started the app its own CHANGELOG.md");if(removeFrameworkLicense(path))log.success("Removed the framework's LICENSE.md - choose the app's own licence")}catch(error){log.warn(`Could not name the app in package.json: ${error instanceof Error?error.message:String(error)}`)}}function applyAppAgents(path){try{if(applyAppAgentsTemplate(path))log.success("AGENTS.md no longer tells agents to publish as the framework's author")}catch(error){log.warn(`Could not update AGENTS.md: ${error instanceof Error?error.message:String(error)}`)}}function applyAppReadme(path){const source=resolve(path,"storage/framework/defaults/scaffold/README.md");if(!existsSync(source))return;try{writeFileSync(resolve(path,"README.md"),renderTemplate(readFileSync(source,"utf8"),appIdentity(path)))}catch(error){log.warn(`Could not write the app README: ${error instanceof Error?error.message:String(error)}`)}}function replaceFrameworkSite(path){log.info("Replacing the stacksjs.com site with a starter page...");try{const removed=applyAppSiteTemplate(path);log.success(`Starter page installed (${removed.length} stacksjs.com path${removed.length===1?"":"s"} removed)`)}catch(error){log.warn(`Could not install the starter page: ${error instanceof Error?error.message:String(error)}`)}}function removeFrameworkTests(path){const tests=resolve(path,"tests");if(!existsSync(tests))return;log.info("Removing the framework's own test suite...");for(const entry of readdirSync(tests)){if(entry==="setup.ts")continue;rmSync(resolve(tests,entry),{recursive:!0,force:!0})}log.success("App starts with a clean test suite")}function ensureExecutableScripts(path){for(const script of["buddy","bootstrap"])try{chmodSync(resolve(path,script),493)}catch{}}async function ensureEnv(path,_options){log.info("Ensuring your environment is ready...");await ensurePantryInstalled();await ensurePantryDependencies(path);log.success("Environment is ready")}async function install(path,options){log.info("Installing & setting up Stacks");log.info("Copying .env.example \u2192 .env");let result=await runCommand("cp .env.example .env",{...options,cwd:path});if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}log.info("Removing template-encrypted env files...");const{rm}=await import("node:fs/promises");for(const stale of[".env.development",".env.staging",".env.production",".env.keys"])await rm(`${path}/${stale}`,{force:!0});log.info("Generating application key...");const keyResult=await runAction(Action.KeyGenerate,{...options,cwd:path});if(resultFailed(keyResult)){await log.error(keyResult.error);process.exit(ExitCode.FatalError)}if(existsSync(resolve(path,".git")))log.info("Existing git repository detected, skipping git init");else{log.info("Initializing git repository...");result=await runCommand("git init",{...options,cwd:path});if(resultFailed(result)){await log.error(result.error);process.exit(ExitCode.FatalError)}}log.success("Installed & set-up \uD83D\uDE80")}async function unvendorCore(path,options){log.info("Resolving the framework from npm (pass --with-core to keep it vendored)...");const result=await runCommand("./buddy unpublish:core --all --force",{...options,cwd:path});if(resultFailed(result)){const reason=result.error instanceof Error?result.error.stack??result.error.message:String(result.error??"").trim();log.error("Could not resolve the framework from npm.");log.error(reason.length>0?reason:"The step failed without reporting a reason.");log.error("");log.error(`The project at ${path} is half converted: the vendored framework has been`);log.error("removed and the published packages are not in place yet, so ./buddy cannot");log.error("boot there. Finish it by hand with:");log.error("");await log.error(` cd ${path} && rm -f node_modules/stacks && bun install`);await log.error("");await log.error("Or start over with `--with-core` to keep the framework vendored.");process.exit(ExitCode.FatalError)}log.success("Framework resolved from npm")}async function generatePantryDeps(path){try{if(await optimizePantryDeps(path))rmSync(resolve(path,"deps.yml"),{force:!0})}catch(error){log.warn(`Could not generate deps.yaml from config/deps.ts, keeping the template's deps.yml: ${error instanceof Error?error.message:String(error)}`)}}function trimFrameworkOnlyFiles(path){try{const removed=pruneFrameworkOnlyFiles(path),scripts=applyAppPackageScripts(path);rewriteCoreContentPaths(path);stripFrameworkManifestCredits(path);log.success(`Removed ${removed.length} framework-only path${removed.length===1?"":"s"} and ${scripts.removed.length} framework-only script${scripts.removed.length===1?"":"s"}`)}catch(error){log.warn(`Could not remove the framework-only files: ${error instanceof Error?error.message:String(error)}`)}}function linkFrameworkDefaults(path){log.info("Linking the framework defaults from node_modules (pass --vendor-defaults to commit a copy)...");try{const adopted=adoptPackagedDefaults(path);log.success(`storage/framework/defaults now links to ${relative(path,adopted.packageRoot)}`)}catch(error){log.warn(`Could not move the framework defaults to node_modules: ${error instanceof Error?error.message:String(error)}`)}}async function regenerateAutoImports(path,options){const result=await runCommand("./buddy generate --types",{...options,cwd:path});if(resultFailed(result))log.warn("Could not regenerate the auto-import barrels. Run `./buddy generate --types` in the project before `./buddy dev`.")}async function stripFeatures(path){log.info("Stripping optional feature bundles (--minimal)...");const results=await uninstallAllFeatures({root:path}),authEnv=ensureAuthRoutesEnv(path);if(authEnv.length>0)log.info(` - kept the auth routes mounted (STACKS_DEFAULT_ROUTES=auth in ${authEnv.join(", ")})`);let strippedAny=!1;for(const{feature,configOutcome,filesRemoved}of results)if(configOutcome==="flipped"||filesRemoved.length>0){strippedAny=!0;const fileSummary=filesRemoved.length>0?` (${filesRemoved.length} path${filesRemoved.length===1?"":"s"})`:"";log.info(` - ${feature}${fileSummary}`)}if(!strippedAny)log.info(" \u2192 no feature scaffolding present; nothing to strip.");else log.success("Minimal skeleton ready - run `./buddy <feature>:install` to add features back.")}
@@ -1,2 +1,2 @@
1
- import{readFileSync,writeFileSync}from"node:fs";import{join}from"node:path";import process from"node:process";import{desktopSupportMatrix}from"@stacksjs/desktop-build/support";import{assertFrameworkRepo}from"./framework-repo";const root=new URL("../../../../../../../",import.meta.url).pathname,page=join(root,"docs/packages/desktop.md"),BEGIN="<!-- desktop-support-matrix:begin -->",END="<!-- desktop-support-matrix:end -->",PLATFORM_NAMES={darwin:"macOS",linux:"Linux",win32:"Windows"};function link(url){const issue=url.match(/github\.com\/([^/]+\/[^/]+)\/issues\/(\d+)/);if(issue)return`[${issue[1]}#${issue[2]}](${url})`;const run=url.match(/actions\/runs\/(\d+)/);if(run)return`[run ${run[1]}](${url})`;return`[evidence](${url})`}function row(entry){return`| ${[`${PLATFORM_NAMES[entry.platform]} ${entry.architecture}`,`**${entry.status}**`,entry.osVersions.join(", "),entry.packageFormat,entry.signing,entry.notarization,entry.installLaunchEvidence?link(entry.installLaunchEvidence):"none",entry.blockingIssues.map(link).join(", ")||"none"].join(" | ")} |`}export function render(){const header=["| Target | Status | Tested on | Package | Signing | Notarization | Install + launch | Blocked by |","|---|---|---|---|---|---|---|---|"],limitations=desktopSupportMatrix.flatMap((entry)=>entry.limitations.map((limitation)=>`- **${PLATFORM_NAMES[entry.platform]} ${entry.architecture}** - ${limitation}`)),unique=[...new Set(limitations)];return[BEGIN,"","<!-- Generated by `buddy docs:desktop-matrix` from `@stacksjs/desktop`'s"," `desktopSupportMatrix`. Edit that, not this. -->","",...header,...desktopSupportMatrix.map(row),"","### Known limitations","",...unique,"",END].join(`
1
+ import{readFileSync,writeFileSync}from"node:fs";import{join}from"node:path";import process from"node:process";import{desktopSupportMatrix}from"@stacksjs/desktop-build/support";import{assertFrameworkRepo}from"./framework-repo";const root=new URL("../../../../../../../",import.meta.url).pathname,page=join(root,"docs/packages/desktop.md"),BEGIN="<!-- desktop-support-matrix:begin -->",END="<!-- desktop-support-matrix:end -->",PLATFORM_NAMES={darwin:"macOS",linux:"Linux",win32:"Windows"};function link(url){const issue=url.match(/github\.com\/([^/]+\/[^/]+)\/issues\/(\d+)/);if(issue)return`[${issue[1]}#${issue[2]}](${url})`;const run=url.match(/actions\/runs\/(\d+)/);if(run)return`[run ${run[1]}](${url})`;return`[evidence](${url})`}function row(entry){return`| ${[`${PLATFORM_NAMES[entry.platform]} ${entry.architecture}`,`**${entry.status}**`,entry.osVersions.join(", "),entry.packageFormat,entry.signing,entry.notarization,entry.installLaunchEvidence?link(entry.installLaunchEvidence):"none",entry.blockingIssues.map(link).join(", ")||"none"].join(" | ")} |`}export function render(){const header=["| Target | Status | Tested on | Package | Signing | Notarization | Install + launch | Blocked by |","|---|---|---|---|---|---|---|---|"],limitations=desktopSupportMatrix.flatMap((entry)=>entry.limitations.map((limitation)=>`- **${PLATFORM_NAMES[entry.platform]} ${entry.architecture}** - ${limitation}`)),unique=[...new Set(limitations)];return[BEGIN,"","<!-- Generated by `buddy docs:desktop-matrix` from `@stacksjs/desktop-build`'s"," `desktopSupportMatrix`. Edit that, not this. -->","",...header,...desktopSupportMatrix.map(row),"","### Known limitations","",...unique,"",END].join(`
2
2
  `)}function current(){let contents;try{contents=readFileSync(page,"utf8")}catch{return null}const start=contents.indexOf(BEGIN),end=contents.indexOf(END);if(start===-1||end===-1)return null;return contents.slice(start,end+END.length)}function write(){const contents=readFileSync(page,"utf8"),start=contents.indexOf(BEGIN),end=contents.indexOf(END);if(start===-1||end===-1)throw Error(`[docs:desktop-matrix] ${page} is missing the ${BEGIN} / ${END} markers`);const next=contents.slice(0,start)+render()+contents.slice(end+END.length);if(next===contents)return!1;writeFileSync(page,next);return!0}export async function run(){assertFrameworkRepo(root,"docs:desktop-matrix");if(process.argv.includes("--write")){console.log(write()?"\u2713 rewrote the desktop support matrix in docs/packages/desktop.md":"\u2713 the desktop support matrix was already current");return}const published=current();if(published===render()){console.log(`\u2713 the desktop support matrix is current (${desktopSupportMatrix.length} targets)`);return}console.error(published===null?"\u2717 docs/packages/desktop.md is missing its generated support-matrix section":"\u2717 docs/packages/desktop.md no longer matches desktopSupportMatrix");console.error("\nRun `buddy docs:desktop-matrix` to rewrite it from the source.");if(process.argv.includes("--check"))process.exit(1)}if(import.meta.main)await run();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.423",
5
+ "version": "0.75.431",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -95,73 +95,73 @@
95
95
  "prepublishOnly": "bun run build"
96
96
  },
97
97
  "dependencies": {
98
- "@stacksjs/actions": "0.75.423",
99
- "@stacksjs/ai": "0.75.423",
100
- "@stacksjs/alias": "0.75.423",
101
- "@stacksjs/analytics": "0.75.423",
102
- "@stacksjs/api": "0.75.423",
103
- "@stacksjs/arrays": "0.75.423",
104
- "@stacksjs/auth": "0.75.423",
105
- "@stacksjs/browser-extension": "0.75.423",
106
- "@stacksjs/build": "0.75.423",
98
+ "@stacksjs/actions": "0.75.431",
99
+ "@stacksjs/ai": "0.75.431",
100
+ "@stacksjs/alias": "0.75.431",
101
+ "@stacksjs/analytics": "0.75.431",
102
+ "@stacksjs/api": "0.75.431",
103
+ "@stacksjs/arrays": "0.75.431",
104
+ "@stacksjs/auth": "0.75.431",
105
+ "@stacksjs/browser-extension": "0.75.431",
106
+ "@stacksjs/build": "0.75.431",
107
107
  "@stacksjs/bumpx": "^0.2.26",
108
- "@stacksjs/cache": "0.75.423",
109
- "@stacksjs/chat": "0.75.423",
108
+ "@stacksjs/cache": "0.75.431",
109
+ "@stacksjs/chat": "0.75.431",
110
110
  "@stacksjs/clapp": "^0.2.16",
111
- "@stacksjs/cli": "0.75.423",
112
- "@stacksjs/cloud": "0.75.423",
113
- "@stacksjs/cms": "0.75.423",
114
- "@stacksjs/collections": "0.75.423",
115
- "@stacksjs/commerce": "0.75.423",
116
- "@stacksjs/config": "0.75.423",
117
- "@stacksjs/database": "0.75.423",
118
- "@stacksjs/desktop-build": "0.75.423",
119
- "@stacksjs/dns": "0.75.423",
111
+ "@stacksjs/cli": "0.75.431",
112
+ "@stacksjs/cloud": "0.75.431",
113
+ "@stacksjs/cms": "0.75.431",
114
+ "@stacksjs/collections": "0.75.431",
115
+ "@stacksjs/commerce": "0.75.431",
116
+ "@stacksjs/config": "0.75.431",
117
+ "@stacksjs/database": "0.75.431",
118
+ "@stacksjs/desktop-build": "0.75.431",
119
+ "@stacksjs/dns": "0.75.431",
120
120
  "@stacksjs/dnsx": "^0.2.3",
121
- "@stacksjs/email": "0.75.423",
122
- "@stacksjs/enums": "0.75.423",
123
- "@stacksjs/env": "0.75.423",
124
- "@stacksjs/error-handling": "0.75.423",
125
- "@stacksjs/events": "0.75.423",
126
- "@stacksjs/features": "0.75.423",
127
- "@stacksjs/git": "0.75.423",
121
+ "@stacksjs/email": "0.75.431",
122
+ "@stacksjs/enums": "0.75.431",
123
+ "@stacksjs/env": "0.75.431",
124
+ "@stacksjs/error-handling": "0.75.431",
125
+ "@stacksjs/events": "0.75.431",
126
+ "@stacksjs/features": "0.75.431",
127
+ "@stacksjs/git": "0.75.431",
128
128
  "@stacksjs/gitit": "^0.2.5",
129
- "@stacksjs/health": "0.75.423",
129
+ "@stacksjs/health": "0.75.431",
130
130
  "@stacksjs/httx": "^0.1.10",
131
- "@stacksjs/image": "0.75.423",
132
- "@stacksjs/lint": "0.75.423",
133
- "@stacksjs/logging": "0.75.423",
134
- "@stacksjs/notifications": "0.75.423",
135
- "@stacksjs/objects": "0.75.423",
136
- "@stacksjs/orm": "0.75.423",
137
- "@stacksjs/path": "0.75.423",
138
- "@stacksjs/payments": "0.75.423",
139
- "@stacksjs/realtime": "0.75.423",
140
- "@stacksjs/router": "0.75.423",
131
+ "@stacksjs/image": "0.75.431",
132
+ "@stacksjs/lint": "0.75.431",
133
+ "@stacksjs/logging": "0.75.431",
134
+ "@stacksjs/notifications": "0.75.431",
135
+ "@stacksjs/objects": "0.75.431",
136
+ "@stacksjs/orm": "0.75.431",
137
+ "@stacksjs/path": "0.75.431",
138
+ "@stacksjs/payments": "0.75.431",
139
+ "@stacksjs/realtime": "0.75.431",
140
+ "@stacksjs/router": "0.75.431",
141
141
  "@stacksjs/rpx": "^0.11.54",
142
- "@stacksjs/scheduler": "0.75.423",
143
- "@stacksjs/search-engine": "0.75.423",
144
- "@stacksjs/security": "0.75.423",
145
- "@stacksjs/server": "0.75.423",
146
- "@stacksjs/sites": "0.75.423",
147
- "@stacksjs/skills": "0.75.423",
148
- "@stacksjs/socials": "0.75.423",
149
- "@stacksjs/storage": "0.75.423",
150
- "@stacksjs/strings": "0.75.423",
142
+ "@stacksjs/scheduler": "0.75.431",
143
+ "@stacksjs/search-engine": "0.75.431",
144
+ "@stacksjs/security": "0.75.431",
145
+ "@stacksjs/server": "0.75.431",
146
+ "@stacksjs/sites": "0.75.431",
147
+ "@stacksjs/skills": "0.75.431",
148
+ "@stacksjs/socials": "0.75.431",
149
+ "@stacksjs/storage": "0.75.431",
150
+ "@stacksjs/strings": "0.75.431",
151
151
  "@stacksjs/stx": "0.2.784",
152
- "@stacksjs/testing": "0.75.423",
153
- "@stacksjs/tinker": "0.75.423",
152
+ "@stacksjs/testing": "0.75.431",
153
+ "@stacksjs/tinker": "0.75.431",
154
154
  "@stacksjs/tlsx": "^0.13.19",
155
155
  "@stacksjs/ts-cloud": "^0.16.91",
156
- "@stacksjs/tunnel": "0.75.423",
157
- "@stacksjs/types": "0.75.423",
158
- "@stacksjs/ui": "0.75.423",
159
- "@stacksjs/utils": "0.75.423",
160
- "@stacksjs/validation": "0.75.423",
156
+ "@stacksjs/tunnel": "0.75.431",
157
+ "@stacksjs/types": "0.75.431",
158
+ "@stacksjs/ui": "0.75.431",
159
+ "@stacksjs/utils": "0.75.431",
160
+ "@stacksjs/validation": "0.75.431",
161
161
  "ajv": "^8.20.0",
162
162
  "ajv-formats": "^3.0.1",
163
163
  "bun-plugin-stx": "0.2.784",
164
- "ts-pantry": "^0.11.146"
164
+ "ts-pantry": "^0.11.159"
165
165
  },
166
166
  "devDependencies": {
167
167
  "better-dx": "^0.2.24"