@stacksjs/buddy 0.74.43 → 0.74.45

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,4 +1,4 @@
1
- import process from"node:process";import{log,onUnknownSubcommand,outro,runCommand}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{resultFailed}from"../result";export function configure(buddy){const descriptions={configure:"Configure options",aws:"Configure the AWS connection",project:"Target a specific project",profile:"The AWS profile to use",verbose:"Enable verbose output"};buddy.command("configure",descriptions.configure).option("--aws",descriptions.aws,{default:!1}).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy configure` ...",options);if(options?.aws){await configureAws(options);process.exit(ExitCode.Success)}log.info("Not implemented yet. Please use the --aws flag to configure AWS.");await log.flush();process.exit(ExitCode.Success)});buddy.command("configure:aws",descriptions.aws).option("-p, --project [project]",descriptions.project,{default:!1}).option("--profile",descriptions.profile,{default:process.env.AWS_PROFILE}).option("--verbose",descriptions.verbose,{default:!1}).option("--access-key-id","The AWS access key").option("--secret-access-key","The AWS secret access key").option("--region","The AWS region").option("--output","The AWS output format").option("--quiet","Suppress output").action(async(options)=>{log.debug("Running `buddy configure:aws` ...",options);await configureAws(options);process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"configure")}async function configureAws(options){const startTime=performance.now(),awsAccessKeyId=options?.accessKeyId??process.env.AWS_ACCESS_KEY_ID,awsSecretAccessKey=options?.secretAccessKey??process.env.AWS_SECRET_ACCESS_KEY,defaultRegion="us-east-1",defaultOutputFormat=options?.output??"json",profile=process.env.AWS_PROFILE??options?.profile,command=profile?`aws configure --profile ${profile}`:"aws configure",input=`${awsAccessKeyId}
1
+ import process from"node:process";import{log,onUnknownSubcommand,outro,runCommand}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{resultFailed}from"../result";export function configure(buddy){const descriptions={configure:"Configure options",aws:"Configure the AWS connection",project:"Target a specific project",profile:"The AWS profile to use",verbose:"Enable verbose output"};buddy.command("configure",descriptions.configure).option("--aws",descriptions.aws,{default:!1}).option("-p, --project [project]",descriptions.project,{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy configure` ...",options);if(options?.aws){await configureAws(options);process.exit(ExitCode.Success)}log.info("Not implemented yet. Please use the --aws flag to configure AWS.");await log.flush();process.exit(ExitCode.Success)});buddy.command("configure:aws",descriptions.aws).option("-p, --project [project]",descriptions.project,{default:!1}).option("--profile",descriptions.profile).option("--verbose",descriptions.verbose,{default:!1}).option("--access-key-id","The AWS access key").option("--secret-access-key","The AWS secret access key").option("--region","The AWS region").option("--output","The AWS output format").option("--quiet","Suppress output").action(async(options)=>{log.debug("Running `buddy configure:aws` ...",options);await configureAws(options);process.exit(ExitCode.Success)});onUnknownSubcommand(buddy,"configure")}async function configureAws(options){const startTime=performance.now(),awsAccessKeyId=options?.accessKeyId??process.env.AWS_ACCESS_KEY_ID,awsSecretAccessKey=options?.secretAccessKey??process.env.AWS_SECRET_ACCESS_KEY,defaultRegion="us-east-1",defaultOutputFormat=options?.output??"json",profile=process.env.AWS_PROFILE??options?.profile,command=profile?`aws configure --profile ${profile}`:"aws configure",input=`${awsAccessKeyId}
2
2
  ${awsSecretAccessKey}
3
3
  ${defaultRegion}
4
4
  ${defaultOutputFormat}
@@ -43,12 +43,30 @@ export type { FeatureName } from '@stacksjs/features';
43
43
  */
44
44
  export declare function featurePathsPresent(feature: FeatureName, root?: string): string[];
45
45
  /**
46
- * Recursively delete every file/dir listed in the feature's manifest under
47
- * `root` (defaults to `projectPath()`). Missing entries are skipped
48
- * silently — the operation is safe to re-run. Returns the list of paths
49
- * actually removed so the caller can print a useful summary.
46
+ * Delete the paths in a feature's manifest that are still as this command left
47
+ * them, and keep the ones that are not (stacksjs/stacks#2598).
48
+ *
49
+ * This used to `rm -rf` every claimed path unconditionally, which made the two
50
+ * halves of the same file disagree: `copyFeatureFiles` below refuses to
51
+ * overwrite a path that already exists, "don't overwrite the user's
52
+ * possibly-customised file", and this removed that same customised file
53
+ * without looking. The framework protected your edits on the way in and
54
+ * deleted them on the way out.
55
+ *
56
+ * The rule it follows now is the one `stack:uninstall` already used - a
57
+ * command may remove what it put there and left untouched, and anything the
58
+ * developer changed needs `--force`. `uninstallStack` compares a checksum
59
+ * recorded at install time; features keep no such record, so the comparison is
60
+ * against the template in `storage/framework/defaults/<path>` instead. That is
61
+ * weaker in one way worth knowing: a file edited and then edited back to match
62
+ * the template reads as untouched.
63
+ *
64
+ * Directory entries - which most feature paths are - are compared per file, so
65
+ * one edited action does not strand the other twenty beside it.
66
+ *
67
+ * Missing entries are skipped silently, so the operation stays safe to re-run.
50
68
  */
51
- export declare function deleteFeatureFiles(feature: FeatureName, root?: string): Promise<string[]>;
69
+ export declare function deleteFeatureFiles(feature: FeatureName, root?: string, options?: DeleteFeatureFilesOptions): Promise<{ removed: string[], preserved: string[] }>;
52
70
  /**
53
71
  * Copy every path listed in the feature's manifest from the framework
54
72
  * defaults tree (`storage/framework/defaults/<path>`) into the project
@@ -94,6 +112,10 @@ export declare function setFeatureEnabled(feature: FeatureName, enabled: boolean
94
112
  */
95
113
  export declare function uninstallAllFeatures(options?: { root?: string }): Promise<UninstallAllFeaturesResult[]>;
96
114
  export declare function features(buddy: CLI): void;
115
+ export declare interface DeleteFeatureFilesOptions {
116
+ force?: boolean
117
+ source?: string
118
+ }
97
119
  export declare interface CopyFeatureFilesOptions {
98
120
  force?: boolean
99
121
  source?: string
@@ -1,4 +1,4 @@
1
- import{existsSync}from"node:fs";import{cp,rm}from"node:fs/promises";import{join}from"node:path";import process from"node:process";import{frameworkPath,projectPath}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";export{appModelClaimsTable,FEATURE_FILES,FEATURE_NAMES,FEATURE_TABLES,migrationFeature,migrationTable}from"@stacksjs/features";import{FEATURE_FILES,FEATURE_NAMES}from"@stacksjs/features";export function featurePathsPresent(feature,root=projectPath()){return FEATURE_FILES[feature].filter((rel)=>existsSync(`${root}/${rel}`))}export async function deleteFeatureFiles(feature,root=projectPath()){const removed=[];for(const rel of FEATURE_FILES[feature]){const full=`${root}/${rel}`;if(!existsSync(full))continue;await rm(full,{recursive:!0,force:!0});removed.push(rel)}return removed}export async function copyFeatureFiles(feature,options={}){const source=options.source??frameworkPath("defaults"),target=options.target??projectPath(),force=options.force===!0,copied=[],skipped=[];for(const rel of FEATURE_FILES[feature]){const sourceFull=join(source,rel);if(!existsSync(sourceFull)){skipped.push(rel);continue}const targetFull=join(target,rel);if(existsSync(targetFull)&&!force){skipped.push(rel);continue}await cp(sourceFull,targetFull,{recursive:!0,force});copied.push(rel)}return{copied,skipped}}const FEATURE_DESCRIPTIONS={dashboard:"Admin SPA shell + Activity/Log/Request/Deployment/Notification dashboards.",commerce:"Order/Cart/Product/Customer/Coupon/GiftCard/Shipping + storefront API.",cms:"Post/Page/Author/Comment/Tag models + content edit dashboards.",forms:"User-defined forms: builder models, conditional fields, public submit + CSV export.",marketing:"/api/email/subscribe, /api/contact, Campaign/EmailList/SocialPost.",monitoring:"Error model + error-tracking views and actions.",realtime:"WebSocket broadcaster + Websocket model + realtime-stats actions.",queue:"Job + FailedJob models + queue dashboard pages."},STARTER_TEMPLATES={dashboard:`import type { DashboardConfig } from '@stacksjs/types'
1
+ import{existsSync,readdirSync,readFileSync,rmdirSync,statSync}from"node:fs";import{cp,rm}from"node:fs/promises";import{join}from"node:path";import process from"node:process";import{frameworkPath,projectPath}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";export{appModelClaimsTable,FEATURE_FILES,FEATURE_NAMES,FEATURE_TABLES,migrationFeature,migrationTable}from"@stacksjs/features";import{FEATURE_FILES,FEATURE_NAMES}from"@stacksjs/features";export function featurePathsPresent(feature,root=projectPath()){return FEATURE_FILES[feature].filter((rel)=>existsSync(`${root}/${rel}`))}function filesUnder(dir,prefix=""){const out=[];for(const entry of readdirSync(dir,{withFileTypes:!0})){const rel=prefix?`${prefix}/${entry.name}`:entry.name;if(entry.isDirectory())out.push(...filesUnder(join(dir,entry.name),rel));else out.push(rel)}return out}function matchesTemplate(projectFile,templateFile){if(!existsSync(templateFile))return!1;try{return readFileSync(projectFile).equals(readFileSync(templateFile))}catch{return!1}}function pruneEmptyDirs(root,dir){let current=dir;while(current.startsWith(root)&&current!==root){try{if(readdirSync(current).length>0)return;rmdirSync(current)}catch{return}current=join(current,"..")}}export async function deleteFeatureFiles(feature,root=projectPath(),options={}){const source=options.source??frameworkPath("defaults"),force=options.force===!0,removed=[],preserved=[];for(const rel of FEATURE_FILES[feature]){const full=join(root,rel);if(!existsSync(full))continue;if(force){await rm(full,{recursive:!0,force:!0});removed.push(rel);continue}const template=join(source,rel);if(!statSync(full).isDirectory()){if(matchesTemplate(full,template)){await rm(full,{force:!0});removed.push(rel)}else preserved.push(rel);continue}let keptAny=!1;for(const child of filesUnder(full)){const projectFile=join(full,child);if(matchesTemplate(projectFile,join(template,child))){await rm(projectFile,{force:!0});removed.push(`${rel}${child}`)}else{keptAny=!0;preserved.push(`${rel}${child}`)}}pruneEmptyDirs(root,full);if(!keptAny&&existsSync(full))await rm(full,{recursive:!0,force:!0})}return{removed,preserved}}export async function copyFeatureFiles(feature,options={}){const source=options.source??frameworkPath("defaults"),target=options.target??projectPath(),force=options.force===!0,copied=[],skipped=[];for(const rel of FEATURE_FILES[feature]){const sourceFull=join(source,rel);if(!existsSync(sourceFull)){skipped.push(rel);continue}const targetFull=join(target,rel);if(existsSync(targetFull)&&!force){skipped.push(rel);continue}await cp(sourceFull,targetFull,{recursive:!0,force});copied.push(rel)}return{copied,skipped}}const FEATURE_DESCRIPTIONS={dashboard:"Admin SPA shell + Activity/Log/Request/Deployment/Notification dashboards.",commerce:"Order/Cart/Product/Customer/Coupon/GiftCard/Shipping + storefront API.",cms:"Post/Page/Author/Comment/Tag models + content edit dashboards.",forms:"User-defined forms: builder models, conditional fields, public submit + CSV export.",marketing:"/api/email/subscribe, /api/contact, Campaign/EmailList/SocialPost.",monitoring:"Error model + error-tracking views and actions.",realtime:"WebSocket broadcaster + Websocket model + realtime-stats actions.",queue:"Job + FailedJob models + queue dashboard pages."},STARTER_TEMPLATES={dashboard:`import type { DashboardConfig } from '@stacksjs/types'
2
2
 
3
3
  /**
4
4
  * **Dashboard Configuration**
@@ -127,4 +127,4 @@ export default {
127
127
  },
128
128
  } satisfies QueueConfig
129
129
  `};export async function setFeatureEnabled(feature,enabled,options){const path=options.root?join(options.root,`config/${feature}.ts`):projectPath(`config/${feature}.ts`),file=Bun.file(path);if(!await file.exists()){if(!options.createIfMissing)return"missing";await Bun.write(path,STARTER_TEMPLATES[feature]);return"created"}const src=await file.text(),enabledRegex=/(^|\{)(\s*)enabled\s*:\s*(true|false)(\s*,?)/m;if(enabledRegex.test(src)){const replaced=src.replace(enabledRegex,(_full,anchor,ws,current,trailing)=>{if(current===String(enabled))return`${anchor}${ws}enabled: ${current}${trailing}`;return`${anchor}${ws}enabled: ${enabled}${trailing}`});if(replaced===src)return"unchanged";await Bun.write(path,replaced);return"flipped"}const insertRegex=/(export default\s*\{)/;if(!insertRegex.test(src))throw Error(`Could not locate \`export default {\` in ${path} - please add \`enabled: ${enabled}\` manually.`);const next=src.replace(insertRegex,`$1
130
- enabled: ${enabled},`);await Bun.write(path,next);return"flipped"}export async function uninstallAllFeatures(options={}){const root=options.root??projectPath(),results=[];for(const feature of FEATURE_NAMES){const configOutcome=await setFeatureEnabled(feature,!1,{createIfMissing:!1,root}),filesRemoved=await deleteFeatureFiles(feature,root);results.push({feature,configOutcome,filesRemoved})}return results}function registerInstallPair(buddy,feature){const desc=FEATURE_DESCRIPTIONS[feature],configRel=`config/${feature}.ts`;buddy.command(`${feature}:install`,`Activate the ${feature} feature bundle. ${desc}`).option("--force",`Overwrite any existing ${feature} files in the project (default skips existing paths so the install is idempotent).`).action(async(options)=>{try{switch(await setFeatureEnabled(feature,!0,{createIfMissing:!0})){case"created":console.log(`\u2713 Created ${configRel} with '${feature}' enabled.`);break;case"flipped":console.log(`\u2713 Enabled '${feature}' in ${configRel}.`);break;case"unchanged":console.log(`\u2713 '${feature}' is already enabled in ${configRel}.`);break}const{copied}=await copyFeatureFiles(feature,{force:options.force===!0});if(copied.length>0){console.log(`\u2713 Copied ${copied.length} stamped path(s) from framework defaults:`);for(const path of copied)console.log(` - ${path}`)}else if(featurePathsPresent(feature).length>0)console.log(` \u2192 ${feature} scaffolding already in place - use --force to overwrite.`);console.log(` \u2192 next ./buddy dev will boot with ${feature} loaded.`);process.exit(ExitCode.Success)}catch(err){console.error(`\u2717 Failed to install ${feature}:`,err);process.exit(ExitCode.FatalError)}});buddy.command(`${feature}:uninstall`,`Deactivate the ${feature} feature bundle.`).option("--keep-files",`Don't delete the ${feature} scaffolding (action/model/view files). Flip the flag only.`).action(async(options)=>{try{switch(await setFeatureEnabled(feature,!1,{createIfMissing:!1})){case"missing":console.log(`\u2713 '${feature}' is already disabled (${configRel} is absent).`);break;case"flipped":console.log(`\u2713 Disabled '${feature}' in ${configRel}. Custom config preserved.`);break;case"unchanged":console.log(`\u2713 '${feature}' is already disabled in ${configRel}.`);break;case"created":console.log(`\u2717 Unexpected create on uninstall for ${feature}; please re-run with --force or report this bug.`);break}if(options.keepFiles){const stillPresent=featurePathsPresent(feature);if(stillPresent.length>0)console.log(` \u2192 ${stillPresent.length} stamped path(s) preserved (--keep-files).`)}else{const removed=await deleteFeatureFiles(feature);if(removed.length>0){console.log(`\u2713 Removed ${removed.length} stamped path(s):`);for(const path of removed)console.log(` - ${path}`)}}console.log(` \u2192 next ./buddy dev will boot without ${feature}.`);process.exit(ExitCode.Success)}catch(err){console.error(`\u2717 Failed to uninstall ${feature}:`,err);process.exit(ExitCode.FatalError)}})}export function features(buddy){for(const feature of FEATURE_NAMES)registerInstallPair(buddy,feature)}
130
+ enabled: ${enabled},`);await Bun.write(path,next);return"flipped"}export async function uninstallAllFeatures(options={}){const root=options.root??projectPath(),results=[];for(const feature of FEATURE_NAMES){const configOutcome=await setFeatureEnabled(feature,!1,{createIfMissing:!1,root}),{removed:filesRemoved}=await deleteFeatureFiles(feature,root,{force:!0});results.push({feature,configOutcome,filesRemoved})}return results}function registerInstallPair(buddy,feature){const desc=FEATURE_DESCRIPTIONS[feature],configRel=`config/${feature}.ts`;buddy.command(`${feature}:install`,`Activate the ${feature} feature bundle. ${desc}`).option("--force",`Overwrite any existing ${feature} files in the project (default skips existing paths so the install is idempotent).`).action(async(options)=>{try{switch(await setFeatureEnabled(feature,!0,{createIfMissing:!0})){case"created":console.log(`\u2713 Created ${configRel} with '${feature}' enabled.`);break;case"flipped":console.log(`\u2713 Enabled '${feature}' in ${configRel}.`);break;case"unchanged":console.log(`\u2713 '${feature}' is already enabled in ${configRel}.`);break}const{copied}=await copyFeatureFiles(feature,{force:options.force===!0});if(copied.length>0){console.log(`\u2713 Copied ${copied.length} stamped path(s) from framework defaults:`);for(const path of copied)console.log(` - ${path}`)}else if(featurePathsPresent(feature).length>0)console.log(` \u2192 ${feature} scaffolding already in place - use --force to overwrite.`);console.log(` \u2192 next ./buddy dev will boot with ${feature} loaded.`);process.exit(ExitCode.Success)}catch(err){console.error(`\u2717 Failed to install ${feature}:`,err);process.exit(ExitCode.FatalError)}});buddy.command(`${feature}:uninstall`,`Deactivate the ${feature} feature bundle.`).option("--keep-files",`Don't delete the ${feature} scaffolding (action/model/view files). Flip the flag only.`).option("--force",`Delete the ${feature} scaffolding even where you have edited it. Without this, changed files are kept.`).action(async(options)=>{try{switch(await setFeatureEnabled(feature,!1,{createIfMissing:!1})){case"missing":console.log(`\u2713 '${feature}' is already disabled (${configRel} is absent).`);break;case"flipped":console.log(`\u2713 Disabled '${feature}' in ${configRel}. Custom config preserved.`);break;case"unchanged":console.log(`\u2713 '${feature}' is already disabled in ${configRel}.`);break;case"created":console.log(`\u2717 Unexpected create on uninstall for ${feature}; please re-run with --force or report this bug.`);break}if(options.keepFiles){const stillPresent=featurePathsPresent(feature);if(stillPresent.length>0)console.log(` \u2192 ${stillPresent.length} stamped path(s) preserved (--keep-files).`)}else{const{removed,preserved}=await deleteFeatureFiles(feature,projectPath(),{force:options.force});if(removed.length>0){console.log(`\u2713 Removed ${removed.length} stamped path(s):`);for(const path of removed)console.log(` - ${path}`)}if(preserved.length>0){console.log(` \u2192 Kept ${preserved.length} path(s) you have edited (re-run with --force to remove):`);for(const path of preserved)console.log(` - ${path}`)}}console.log(` \u2192 next ./buddy dev will boot without ${feature}.`);process.exit(ExitCode.Success)}catch(err){console.error(`\u2717 Failed to uninstall ${feature}:`,err);process.exit(ExitCode.FatalError)}})}export function features(buddy){for(const feature of FEATURE_NAMES)registerInstallPair(buddy,feature)}
@@ -1 +1 @@
1
- import process from"node:process";import{intro,log,onUnknownSubcommand,outro}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";export function seed(buddy){const descriptions={seed:"Seed your database",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("seed",descriptions.seed).alias("db:seed").option("-p, --project [project]",descriptions.project,{default:!1}).option("--only [models]","Comma-separated list of models to seed",{default:""}).option("--except [models]","Comma-separated list of models to skip",{default:""}).option("--only-seeders [seeders]","Comma-separated list of application seeder classes to run",{default:""}).option("--except-seeders [seeders]","Comma-separated list of application seeder classes to skip",{default:""}).option("--skip-models","Skip model-factory seeding",{default:!1}).option("--skip-application-seeders","Skip application seeders",{default:!1}).option("--include-defaults","Also seed the framework's built-in models",{default:!1}).option("--allow-protected","Seed auth/oauth models even on a non-fresh DB (will invalidate live tokens)",{default:!1}).option("--fresh","Truncate tables before seeding",{default:!1}).option("--append","Add rows to tables that already have some, instead of skipping them",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy seed` ...",options);const perf=await intro("buddy seed"),{injectGlobalAutoImports}=await import("@stacksjs/server");await injectGlobalAutoImports();const{runApplicationSeeders,seed:seedDatabase}=await import("@stacksjs/database"),list=(value)=>value?value.split(",").map((entry)=>entry.trim()).filter(Boolean):void 0,summary=options.skipModels?{total:0,successful:0,failed:0,results:[],duration:0}:await seedDatabase({verbose:options.verbose,fresh:options.fresh,append:options.append,only:list(options.only),except:list(options.except),includeDefaults:options.includeDefaults,allowProtected:options.allowProtected}),applicationSummary=options.skipApplicationSeeders?{total:0,successful:0,failed:0,results:[],duration:0}:await runApplicationSeeders({verbose:options.verbose,only:list(options.onlySeeders),except:list(options.exceptSeeders)}),APP_ENV=process.env.APP_ENV||"local";if(summary.total===0&&applicationSummary.total===0){await outro("No matching model or application seeders were found.",{startTime:perf,useSeconds:!0});process.exit(ExitCode.Success)}const failures=summary.failed+applicationSummary.failed;await outro(`Seeded your ${APP_ENV} database. ${summary.successful}/${summary.total} model(s) and ${applicationSummary.successful}/${applicationSummary.total} application seeder(s) completed${failures>0?`, ${failures} failed`:""}.`,{startTime:perf,useSeconds:!0,type:failures>0?"error":"success"});process.exit(failures>0?ExitCode.FatalError:ExitCode.Success)});buddy.command("seed:roles","Seed default RBAC role packs (admin, dev, client)").alias("roles:seed").action(async()=>{const perf=await intro("buddy seed:roles");try{const{seedDefaultRoles}=await import("@stacksjs/auth"),result=await seedDefaultRoles();if(result.created.length===0&&result.skipped.length>0)await outro(`All ${result.skipped.length} default role packs already exist - nothing to do.`,{startTime:perf,useSeconds:!0});else{const createdNames=result.created.map((r)=>r.name).join(", ");await outro(`Created ${result.created.length} role pack(s): ${createdNames}. Skipped ${result.skipped.length} existing.`,{startTime:perf,useSeconds:!0})}process.exit(ExitCode.Success)}catch(err){await outro("Failed to seed default roles. Most often: migrations haven't run yet (try `./buddy migrate` first).",{startTime:perf,useSeconds:!0},err);process.exit(ExitCode.FatalError)}});onUnknownSubcommand(buddy,"seed")}
1
+ import process from"node:process";import{intro,log,onUnknownSubcommand,outro}from"@stacksjs/cli";import{ExitCode}from"@stacksjs/types";export function seed(buddy){const descriptions={seed:"Seed your database",project:"Target a specific project",verbose:"Enable verbose output"};buddy.command("seed",descriptions.seed).alias("db:seed").option("-p, --project [project]",descriptions.project,{default:!1}).option("--only [models]","Comma-separated list of models to seed",{default:""}).option("--except [models]","Comma-separated list of models to skip",{default:""}).option("--only-seeders [seeders]","Comma-separated list of application seeder classes to run",{default:""}).option("--tag [tags]","Comma-separated list of seeder tags to run (e.g. deploy)",{default:""}).option("--except-seeders [seeders]","Comma-separated list of application seeder classes to skip",{default:""}).option("--skip-models","Skip model-factory seeding",{default:!1}).option("--skip-application-seeders","Skip application seeders",{default:!1}).option("--include-defaults","Also seed the framework's built-in models",{default:!1}).option("--allow-protected","Seed auth/oauth models even on a non-fresh DB (will invalidate live tokens)",{default:!1}).option("--fresh","Truncate tables before seeding",{default:!1}).option("--append","Add rows to tables that already have some, instead of skipping them",{default:!1}).option("--verbose",descriptions.verbose,{default:!1}).action(async(options)=>{log.debug("Running `buddy seed` ...",options);const perf=await intro("buddy seed"),{injectGlobalAutoImports}=await import("@stacksjs/server");await injectGlobalAutoImports();const{runApplicationSeeders,seed:seedDatabase}=await import("@stacksjs/database"),list=(value)=>value?value.split(",").map((entry)=>entry.trim()).filter(Boolean):void 0,summary=options.skipModels?{total:0,successful:0,failed:0,results:[],duration:0}:await seedDatabase({verbose:options.verbose,fresh:options.fresh,append:options.append,only:list(options.only),except:list(options.except),includeDefaults:options.includeDefaults,allowProtected:options.allowProtected}),applicationSummary=options.skipApplicationSeeders?{total:0,successful:0,failed:0,results:[],duration:0}:await runApplicationSeeders({verbose:options.verbose,only:list(options.onlySeeders),except:list(options.exceptSeeders),tags:list(options.tag)}),APP_ENV=process.env.APP_ENV||"local";if(summary.total===0&&applicationSummary.total===0){await outro("No matching model or application seeders were found.",{startTime:perf,useSeconds:!0});process.exit(ExitCode.Success)}const failures=summary.failed+applicationSummary.failed;await outro(`Seeded your ${APP_ENV} database. ${summary.successful}/${summary.total} model(s) and ${applicationSummary.successful}/${applicationSummary.total} application seeder(s) completed${failures>0?`, ${failures} failed`:""}.`,{startTime:perf,useSeconds:!0,type:failures>0?"error":"success"});process.exit(failures>0?ExitCode.FatalError:ExitCode.Success)});buddy.command("seed:roles","Seed default RBAC role packs (admin, dev, client)").alias("roles:seed").action(async()=>{const perf=await intro("buddy seed:roles");try{const{seedDefaultRoles}=await import("@stacksjs/auth"),result=await seedDefaultRoles();if(result.created.length===0&&result.skipped.length>0)await outro(`All ${result.skipped.length} default role packs already exist - nothing to do.`,{startTime:perf,useSeconds:!0});else{const createdNames=result.created.map((r)=>r.name).join(", ");await outro(`Created ${result.created.length} role pack(s): ${createdNames}. Skipped ${result.skipped.length} existing.`,{startTime:perf,useSeconds:!0})}process.exit(ExitCode.Success)}catch(err){await outro("Failed to seed default roles. Most often: migrations haven't run yet (try `./buddy migrate` first).",{startTime:perf,useSeconds:!0},err);process.exit(ExitCode.FatalError)}});onUnknownSubcommand(buddy,"seed")}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.74.43",
5
+ "version": "0.74.45",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -95,66 +95,66 @@
95
95
  "prepublishOnly": "bun run build"
96
96
  },
97
97
  "dependencies": {
98
- "@stacksjs/actions": "^0.74.43",
99
- "@stacksjs/ai": "^0.74.43",
100
- "@stacksjs/alias": "^0.74.43",
101
- "@stacksjs/analytics": "^0.74.43",
102
- "@stacksjs/api": "^0.74.43",
103
- "@stacksjs/arrays": "^0.74.43",
104
- "@stacksjs/auth": "^0.74.43",
105
- "@stacksjs/browser-extension": "^0.74.43",
106
- "@stacksjs/build": "^0.74.43",
107
- "@stacksjs/cache": "^0.74.43",
108
- "@stacksjs/chat": "^0.74.43",
98
+ "@stacksjs/actions": "^0.74.45",
99
+ "@stacksjs/ai": "^0.74.45",
100
+ "@stacksjs/alias": "^0.74.45",
101
+ "@stacksjs/analytics": "^0.74.45",
102
+ "@stacksjs/api": "^0.74.45",
103
+ "@stacksjs/arrays": "^0.74.45",
104
+ "@stacksjs/auth": "^0.74.45",
105
+ "@stacksjs/browser-extension": "^0.74.45",
106
+ "@stacksjs/build": "^0.74.45",
107
+ "@stacksjs/cache": "^0.74.45",
108
+ "@stacksjs/chat": "^0.74.45",
109
109
  "@stacksjs/clapp": "^0.2.12",
110
- "@stacksjs/cli": "^0.74.43",
111
- "@stacksjs/cloud": "^0.74.43",
112
- "@stacksjs/cms": "^0.74.43",
113
- "@stacksjs/collections": "^0.74.43",
114
- "@stacksjs/config": "^0.74.43",
115
- "@stacksjs/database": "^0.74.43",
116
- "@stacksjs/desktop-build": "^0.74.43",
117
- "@stacksjs/dns": "^0.74.43",
110
+ "@stacksjs/cli": "^0.74.45",
111
+ "@stacksjs/cloud": "^0.74.45",
112
+ "@stacksjs/cms": "^0.74.45",
113
+ "@stacksjs/collections": "^0.74.45",
114
+ "@stacksjs/config": "^0.74.45",
115
+ "@stacksjs/database": "^0.74.45",
116
+ "@stacksjs/desktop-build": "^0.74.45",
117
+ "@stacksjs/dns": "^0.74.45",
118
118
  "@stacksjs/dnsx": "^0.2.3",
119
- "@stacksjs/email": "^0.74.43",
120
- "@stacksjs/enums": "^0.74.43",
121
- "@stacksjs/env": "^0.74.43",
122
- "@stacksjs/error-handling": "^0.74.43",
123
- "@stacksjs/events": "^0.74.43",
124
- "@stacksjs/features": "^0.74.43",
125
- "@stacksjs/git": "^0.74.43",
119
+ "@stacksjs/email": "^0.74.45",
120
+ "@stacksjs/enums": "^0.74.45",
121
+ "@stacksjs/env": "^0.74.45",
122
+ "@stacksjs/error-handling": "^0.74.45",
123
+ "@stacksjs/events": "^0.74.45",
124
+ "@stacksjs/features": "^0.74.45",
125
+ "@stacksjs/git": "^0.74.45",
126
126
  "@stacksjs/gitit": "^0.2.5",
127
- "@stacksjs/health": "^0.74.43",
127
+ "@stacksjs/health": "^0.74.45",
128
128
  "@stacksjs/httx": "^0.1.10",
129
- "@stacksjs/image": "^0.74.43",
130
- "@stacksjs/lint": "^0.74.43",
131
- "@stacksjs/logging": "^0.74.43",
132
- "@stacksjs/notifications": "^0.74.43",
133
- "@stacksjs/objects": "^0.74.43",
134
- "@stacksjs/orm": "^0.74.43",
135
- "@stacksjs/path": "^0.74.43",
136
- "@stacksjs/payments": "^0.74.43",
137
- "@stacksjs/realtime": "^0.74.43",
138
- "@stacksjs/router": "^0.74.43",
129
+ "@stacksjs/image": "^0.74.45",
130
+ "@stacksjs/lint": "^0.74.45",
131
+ "@stacksjs/logging": "^0.74.45",
132
+ "@stacksjs/notifications": "^0.74.45",
133
+ "@stacksjs/objects": "^0.74.45",
134
+ "@stacksjs/orm": "^0.74.45",
135
+ "@stacksjs/path": "^0.74.45",
136
+ "@stacksjs/payments": "^0.74.45",
137
+ "@stacksjs/realtime": "^0.74.45",
138
+ "@stacksjs/router": "^0.74.45",
139
139
  "@stacksjs/rpx": "^0.11.53",
140
- "@stacksjs/scheduler": "^0.74.43",
141
- "@stacksjs/search-engine": "^0.74.43",
142
- "@stacksjs/security": "^0.74.43",
143
- "@stacksjs/server": "^0.74.43",
144
- "@stacksjs/sites": "^0.74.43",
145
- "@stacksjs/skills": "^0.74.43",
146
- "@stacksjs/storage": "^0.74.43",
147
- "@stacksjs/strings": "^0.74.43",
140
+ "@stacksjs/scheduler": "^0.74.45",
141
+ "@stacksjs/search-engine": "^0.74.45",
142
+ "@stacksjs/security": "^0.74.45",
143
+ "@stacksjs/server": "^0.74.45",
144
+ "@stacksjs/sites": "^0.74.45",
145
+ "@stacksjs/skills": "^0.74.45",
146
+ "@stacksjs/storage": "^0.74.45",
147
+ "@stacksjs/strings": "^0.74.45",
148
148
  "@stacksjs/stx": "^0.2.286",
149
- "@stacksjs/testing": "^0.74.43",
150
- "@stacksjs/tinker": "^0.74.43",
149
+ "@stacksjs/testing": "^0.74.45",
150
+ "@stacksjs/tinker": "^0.74.45",
151
151
  "@stacksjs/tlsx": "^0.13.19",
152
152
  "@stacksjs/ts-cloud": "^0.16.0",
153
- "@stacksjs/tunnel": "^0.74.43",
154
- "@stacksjs/types": "^0.74.43",
155
- "@stacksjs/ui": "^0.74.43",
156
- "@stacksjs/utils": "^0.74.43",
157
- "@stacksjs/validation": "^0.74.43",
153
+ "@stacksjs/tunnel": "^0.74.45",
154
+ "@stacksjs/types": "^0.74.45",
155
+ "@stacksjs/ui": "^0.74.45",
156
+ "@stacksjs/utils": "^0.74.45",
157
+ "@stacksjs/validation": "^0.74.45",
158
158
  "ajv": "^8.20.0",
159
159
  "ajv-formats": "^3.0.1",
160
160
  "bun-plugin-stx": "^0.2.286",