@stacksjs/actions 0.75.13 → 0.75.15

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/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { StxLintConfig, StxLintReport, StxLintResult } from './lint/stx-gate';
2
+ export type { RouteActionReference } from './upgrade/route-actions';
2
3
  export type { ProjectStructureChange } from './upgrade/package-project';
3
4
  export type { CrudField } from './scaffold-crud';
4
5
  export type { DiscoveredPackagesManifest, PackageStacksMeta } from './discover-packages';
@@ -39,6 +40,7 @@ export { DEFAULT_STX_LINT_CONFIG, loadStxLintConfig, runStxLint } from './lint/s
39
40
  // import. The cheap version-level check lives in @stacksjs/path, because the
40
41
  // boot path consults it and cannot depend on this package.
41
42
  export { measureDefaultsDrift, summarizeStructureChanges } from './upgrade/package-project';
43
+ export { describeDanglingRouteActions, findDanglingRouteActions } from './upgrade/route-actions';
42
44
  export * from './setup/index';
43
45
  // makeFactory,
44
46
  export {
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{setActionRunner}from"@stacksjs/action-runner";import{runAction}from"./helpers";setActionRunner((action,options)=>runAction(action,options));export*from"./action";export{add as runAdd}from"./add";export{BlogAdminError,deleteBlogPost,getBlogPost,listBlogPosts,saveBlogPost,slugify as blogSlugify}from"./blog-admin";export{commit as runCommit}from"./commit";export*from"./dev";export*from"./generate";export*from"./helpers";export*from"./library";export{formatProject,lintFix,lintProject}from"./lint/lint";export{DEFAULT_STX_LINT_CONFIG,loadStxLintConfig,runStxLint}from"./lint/stx-gate";export{measureDefaultsDrift,summarizeStructureChanges}from"./upgrade/package-project";export*from"./setup";export{createComponent,createDatabase,createFactory,createFunction,createLanguage,createMiddleware,createMigration,createModel,createMail,createNotification,createPage,isDryRunActive,makeAction,makeCertificate,makeComponent,makeDatabase,makeFunction,makeLanguage,makeMail,makeNotification,makePage,makeQueueTable,makeStack,make as runMake,setDryRun}from"./make";export{discoverPackages,ensureDiscoveredPackages}from"./discover-packages";export{parseFields,scaffoldCrud}from"./scaffold-crud";export{installStack,uninstallStack,listStacks}from"./stacks";export{makeJob}from"./make-job";export{makeCommand}from"./make-command";export{makePolicy}from"./make-policy";export{makeResource}from"./make-resource";
1
+ import{setActionRunner}from"@stacksjs/action-runner";import{runAction}from"./helpers";setActionRunner((action,options)=>runAction(action,options));export*from"./action";export{add as runAdd}from"./add";export{BlogAdminError,deleteBlogPost,getBlogPost,listBlogPosts,saveBlogPost,slugify as blogSlugify}from"./blog-admin";export{commit as runCommit}from"./commit";export*from"./dev";export*from"./generate";export*from"./helpers";export*from"./library";export{formatProject,lintFix,lintProject}from"./lint/lint";export{DEFAULT_STX_LINT_CONFIG,loadStxLintConfig,runStxLint}from"./lint/stx-gate";export{measureDefaultsDrift,summarizeStructureChanges}from"./upgrade/package-project";export{describeDanglingRouteActions,findDanglingRouteActions}from"./upgrade/route-actions";export*from"./setup";export{createComponent,createDatabase,createFactory,createFunction,createLanguage,createMiddleware,createMigration,createModel,createMail,createNotification,createPage,isDryRunActive,makeAction,makeCertificate,makeComponent,makeDatabase,makeFunction,makeLanguage,makeMail,makeNotification,makePage,makeQueueTable,makeStack,make as runMake,setDryRun}from"./make";export{discoverPackages,ensureDiscoveredPackages}from"./discover-packages";export{parseFields,scaffoldCrud}from"./scaffold-crud";export{installStack,uninstallStack,listStacks}from"./stacks";export{makeJob}from"./make-job";export{makeCommand}from"./make-command";export{makePolicy}from"./make-policy";export{makeResource}from"./make-resource";
@@ -1,8 +1,9 @@
1
- import{chmodSync,copyFileSync,cpSync,existsSync,mkdirSync,mkdtempSync,readFileSync,rmSync,statSync,writeFileSync}from"node:fs";import{tmpdir}from"node:os";import{dirname,join}from"node:path";import process from"node:process";import{parseOptions}from"@stacksjs/cli";import{downloadTemplate}from"@stacksjs/gitit";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{buildTemplateString,detectLocalStacks,diffSnapshots,diffSnapshotsDetailed,MANAGED_PATHS,readChannel,readSyncedVersion,readVersion,resolveLatestStableTag,resolveSuccessMessage,resolveUpgradeContext,resolveUpgradeMessage,shouldAutoDetectLocalStacks,shouldCheckDirtyManagedPaths,shouldShortCircuit,snapshotTree,writeChannel,writeSyncedVersion}from"./framework-utils";import{detectDependencyInstaller,resolveDependencyRefreshCommand,resolvePantryExecutable,runPostSyncDependencyRefresh,runPostSyncMigration,shouldRefreshPostSyncDependencies,shouldRunPostSyncHooks}from"./framework-hooks";const options=parseOptions(),projectRoot=p.projectPath();if(!existsSync(p.projectPath("storage/framework/core"))){const{upgradeStacksPackages}=await import("./packages");await upgradeStacksPackages(projectRoot,{version:options.version,canary:options.canary,stable:options.stable,force:options.force,dryRun:options.dryRun,noPostinstall:options.noPostinstall??options.postinstall===!1})}const channelFile=join(projectRoot,".stacks-channel"),versionFile=join(projectRoot,".stacks-version"),currentChannel=readChannel(channelFile),corePkgPath=p.projectPath("storage/framework/core/buddy/package.json"),currentVersion=readVersion(corePkgPath),willResolveStable=!options.version&&!options.canary&&(!!options.stable||currentChannel!=="canary");let latestStableRef="";if(willResolveStable)latestStableRef=await resolveLatestStableTag()??(currentVersion?`v${currentVersion}`:"main");const ctx=resolveUpgradeContext(options,currentChannel,latestStableRef),explicitLocal=options.from?p.projectPath(options.from):null,detectedLocal=explicitLocal??(shouldAutoDetectLocalStacks(options)?detectLocalStacks(projectRoot):null),usingLocal=!!detectedLocal;console.log(resolveUpgradeMessage(ctx,currentChannel,!!options.stable));console.log(usingLocal?` source: local checkout at ${detectedLocal}`:` source: github:stacksjs/stacks#${ctx.ref}`);if(options.dryRun){await runDryRunPreview();process.exit(ExitCode.Success)}const alreadyRestarted=process.argv.includes("--__restarted");if(shouldCheckDirtyManagedPaths({force:!!options.force,alreadyRestarted})){const dirty=await detectDirtyManagedPaths(projectRoot);if(dirty.length>0){console.warn(`
1
+ import{chmodSync,copyFileSync,cpSync,existsSync,mkdirSync,mkdtempSync,readFileSync,rmSync,statSync,writeFileSync}from"node:fs";import{tmpdir}from"node:os";import{dirname,join}from"node:path";import process from"node:process";import{parseOptions}from"@stacksjs/cli";import{downloadTemplate}from"@stacksjs/gitit";import{path as p}from"@stacksjs/path";import{ExitCode}from"@stacksjs/types";import{buildTemplateString,detectLocalStacks,diffSnapshots,diffSnapshotsDetailed,MANAGED_PATHS,readChannel,readSyncedVersion,readVersion,resolveLatestStableTag,resolveSuccessMessage,resolveUpgradeContext,resolveUpgradeMessage,shouldAutoDetectLocalStacks,shouldCheckDirtyManagedPaths,shouldShortCircuit,snapshotTree,writeChannel,writeSyncedVersion}from"./framework-utils";import{detectDependencyInstaller,resolveDependencyRefreshCommand,resolvePantryExecutable,runPostSyncDependencyRefresh,runPostSyncMigration,shouldRefreshPostSyncDependencies,shouldRunPostSyncHooks}from"./framework-hooks";import{describeDanglingRouteActions,findDanglingRouteActions,pruneRetiredScaffoldRoutes}from"./route-actions";const options=parseOptions(),projectRoot=p.projectPath();if(!existsSync(p.projectPath("storage/framework/core"))){const{upgradeStacksPackages}=await import("./packages");await upgradeStacksPackages(projectRoot,{version:options.version,canary:options.canary,stable:options.stable,force:options.force,dryRun:options.dryRun,noPostinstall:options.noPostinstall??options.postinstall===!1})}const channelFile=join(projectRoot,".stacks-channel"),versionFile=join(projectRoot,".stacks-version"),currentChannel=readChannel(channelFile),corePkgPath=p.projectPath("storage/framework/core/buddy/package.json"),currentVersion=readVersion(corePkgPath),willResolveStable=!options.version&&!options.canary&&(!!options.stable||currentChannel!=="canary");let latestStableRef="";if(willResolveStable)latestStableRef=await resolveLatestStableTag()??(currentVersion?`v${currentVersion}`:"main");const ctx=resolveUpgradeContext(options,currentChannel,latestStableRef),explicitLocal=options.from?p.projectPath(options.from):null,detectedLocal=explicitLocal??(shouldAutoDetectLocalStacks(options)?detectLocalStacks(projectRoot):null),usingLocal=!!detectedLocal;console.log(resolveUpgradeMessage(ctx,currentChannel,!!options.stable));console.log(usingLocal?` source: local checkout at ${detectedLocal}`:` source: github:stacksjs/stacks#${ctx.ref}`);if(options.dryRun){await runDryRunPreview();process.exit(ExitCode.Success)}const alreadyRestarted=process.argv.includes("--__restarted");if(shouldCheckDirtyManagedPaths({force:!!options.force,alreadyRestarted})){const dirty=await detectDirtyManagedPaths(projectRoot);if(dirty.length>0){console.warn(`
2
2
  \u26A0 The following framework-managed paths have uncommitted changes:`);for(const path of dirty)console.warn(` - ${path}`);console.warn(`
3
3
  Aborting to avoid clobbering your edits.`);console.warn(` Re-run with --force to overwrite, or commit / stash the changes first.
4
4
  `);process.exit(ExitCode.FatalError)}}let resolvedRemoteSha=null;if(!options.force&&!ctx.targetVersion){resolvedRemoteSha=await getRemoteSha(ctx,usingLocal,detectedLocal);const synced=readSyncedVersion(versionFile);if(shouldShortCircuit({force:!!options.force,targetVersion:ctx.targetVersion,remoteSha:resolvedRemoteSha,synced,channel:ctx.channel})){console.log(`\u2714 Already on the latest ${ctx.channel} (${resolvedRemoteSha.slice(0,7)}).`);process.exit(ExitCode.Success)}}const ownPath=p.frameworkPath("core/actions/src/upgrade/framework.ts"),ownHashBefore=await hashFileOrZero(ownPath),aggregate={added:0,changed:0,removed:0,unchanged:0},perPath=[];try{for(const managed of MANAGED_PATHS){const localTarget=join(projectRoot,managed.localPath),before=managed.isFile?await singleFileSnapshot(localTarget):await snapshotTree(localTarget,managed.skip);if(usingLocal&&detectedLocal)await syncFromLocal(detectedLocal,managed,localTarget);else{if(managed.isFile)continue;await syncFromGitHub(ctx.ref,managed,localTarget,!!options.force)}const after=managed.isFile?await singleFileSnapshot(localTarget):await snapshotTree(localTarget,managed.skip),summary=diffSnapshots(before,after);aggregate.added+=summary.added;aggregate.changed+=summary.changed;aggregate.removed+=summary.removed;aggregate.unchanged+=summary.unchanged;perPath.push({managed,summary})}if(!usingLocal)await syncRootFilesFromGitHub(ctx.ref)}catch(err){console.error("\u2718 Failed to upgrade Stacks framework:",err?.message||err);process.exit(ExitCode.FatalError)}const newVersion=readVersion(corePkgPath),ownHashAfter=await hashFileOrZero(ownPath);if(ownHashBefore!==0n&&ownHashAfter!==0n&&ownHashBefore!==ownHashAfter&&!alreadyRestarted){console.log(`Upgrade pulled a newer version of the upgrade action - re-running once to pick up new sync paths and hooks.
5
- `);const args=process.argv.slice(2).filter((a)=>a!=="--__restarted"),code=await Bun.spawn({cmd:[process.argv[0]||"bun",ownPath,...args,"--__restarted"],cwd:projectRoot,stdout:"inherit",stderr:"inherit"}).exited;process.exit(code??0)}if(!ctx.targetVersion){writeChannel(channelFile,ctx.channel);if(!resolvedRemoteSha)resolvedRemoteSha=await getRemoteSha(ctx,usingLocal,detectedLocal);if(resolvedRemoteSha)writeSyncedVersion(versionFile,ctx.channel,resolvedRemoteSha)}console.log(`\u2714 ${resolveSuccessMessage(ctx,currentVersion,newVersion,currentChannel,!!options.force)}`);console.log("Sync summary:");for(const{managed,summary}of perPath)if(managed.isFile){const moved=summary.added+summary.changed>0;console.log(` ${managed.label.padEnd(10)} ${moved?"updated":"unchanged"}`)}else if(summary.added+summary.changed+summary.removed===0)console.log(` ${managed.label.padEnd(10)} unchanged (${summary.unchanged} files)`);else console.log(` ${managed.label.padEnd(10)} +${summary.added} ~${summary.changed} -${summary.removed} (${summary.unchanged} unchanged)`);const runHooks=!options.noPostinstall&&options.postinstall!==!1;if(runHooks)try{await runPostSyncHooks({aggregate,perPath,projectRoot,alreadyRestarted})}catch(err){console.error(`Post-sync hooks failed: ${err?.message||err}`);process.exit(ExitCode.FatalError)}if(ctx.channel==="canary"&&!ctx.targetVersion)console.warn("\u26A0 Canary builds may contain bugs or breaking changes. Not recommended for production.");process.exit(ExitCode.Success);async function runDryRunPreview(){console.log(`
5
+ `);const args=process.argv.slice(2).filter((a)=>a!=="--__restarted"),code=await Bun.spawn({cmd:[process.argv[0]||"bun",ownPath,...args,"--__restarted"],cwd:projectRoot,stdout:"inherit",stderr:"inherit"}).exited;process.exit(code??0)}if(!ctx.targetVersion){writeChannel(channelFile,ctx.channel);if(!resolvedRemoteSha)resolvedRemoteSha=await getRemoteSha(ctx,usingLocal,detectedLocal);if(resolvedRemoteSha)writeSyncedVersion(versionFile,ctx.channel,resolvedRemoteSha)}console.log(`\u2714 ${resolveSuccessMessage(ctx,currentVersion,newVersion,currentChannel,!!options.force)}`);console.log("Sync summary:");for(const{managed,summary}of perPath)if(managed.isFile){const moved=summary.added+summary.changed>0;console.log(` ${managed.label.padEnd(10)} ${moved?"updated":"unchanged"}`)}else if(summary.added+summary.changed+summary.removed===0)console.log(` ${managed.label.padEnd(10)} unchanged (${summary.unchanged} files)`);else console.log(` ${managed.label.padEnd(10)} +${summary.added} ~${summary.changed} -${summary.removed} (${summary.unchanged} unchanged)`);{const pruned=pruneRetiredScaffoldRoutes(projectRoot);for(const route of pruned)console.log(`Removed ${route.file}:${route.line}, the scaffold route to ${route.action}: removed in ${route.removedIn}, ${route.reason}`);const dangling=findDanglingRouteActions(projectRoot);if(dangling.length>0){console.warn(`${dangling.length} route(s) name an action this version does not ship. Point them at an action that exists, or delete them:`);for(const line of describeDanglingRouteActions(dangling).split(`
6
+ `))console.warn(` ${line}`)}}const runHooks=!options.noPostinstall&&options.postinstall!==!1;if(runHooks)try{await runPostSyncHooks({aggregate,perPath,projectRoot,alreadyRestarted})}catch(err){console.error(`Post-sync hooks failed: ${err?.message||err}`);process.exit(ExitCode.FatalError)}if(ctx.channel==="canary"&&!ctx.targetVersion)console.warn("\u26A0 Canary builds may contain bugs or breaking changes. Not recommended for production.");process.exit(ExitCode.Success);async function runDryRunPreview(){console.log(`
6
7
  DRY RUN - preview only. No files will be written, nothing will be installed, no migrations will run.
7
8
  `);const dirty=await detectDirtyManagedPaths(projectRoot);if(dirty.length>0){console.warn(" Note: these framework-managed paths have uncommitted changes (a real run would abort without --force):");for(const path of dirty)console.warn(` - ${path}`);console.warn("")}let previewSha=null;if(!options.force&&!ctx.targetVersion){previewSha=await getRemoteSha(ctx,usingLocal,detectedLocal);const synced=readSyncedVersion(versionFile);if(shouldShortCircuit({force:!!options.force,targetVersion:ctx.targetVersion,remoteSha:previewSha,synced,channel:ctx.channel})){console.log(`\u2714 Already on the latest ${ctx.channel} (${previewSha.slice(0,7)}). A real run would change nothing.`);return}}const aggregate={added:0,changed:0,removed:0,unchanged:0},perPath=[];let sawRemoved=0,sourceVersion=null;const tmpBase=mkdtempSync(join(tmpdir(),"stacks-upgrade-dry-run-"));try{let repoTmp=null;for(const managed of MANAGED_PATHS){const localTarget=join(projectRoot,managed.localPath),before=managed.isFile?await singleFileSnapshot(localTarget):await snapshotTree(localTarget,managed.skip);let after;if(usingLocal&&detectedLocal){const src=join(detectedLocal,managed.subPath);if(!existsSync(src)){console.log(` ${managed.label.padEnd(10)} skip: source missing at ${src}`);continue}after=managed.isFile?await singleFileSnapshot(src):await snapshotTree(src,managed.skip)}else try{if(managed.isFile){if(!repoTmp){repoTmp=join(tmpBase,"repo");await downloadTemplate(`github:stacksjs/stacks#${ctx.ref}`,{dir:repoTmp,force:!0,forceClean:!0,preferOffline:!0,silent:!0})}after=await singleFileSnapshot(join(repoTmp,managed.subPath))}else{const dir=join(tmpBase,managed.label);await downloadTemplate(buildTemplateString(ctx.ref,managed.subPath),{dir,force:!0,forceClean:!0,preferOffline:!options.force,silent:!0});after=await snapshotTree(dir,managed.skip)}}catch(err){console.log(` ${managed.label.padEnd(10)} preview unavailable (${err?.message||err}); a real run would sync from github:stacksjs/stacks#${ctx.ref}`);continue}const summary=diffSnapshotsDetailed(before,after);aggregate.added+=summary.added;aggregate.changed+=summary.changed;aggregate.removed+=summary.removed;aggregate.unchanged+=summary.unchanged;perPath.push({managed,summary});sawRemoved+=summary.removed;if(managed.isFile){const moved=summary.added+summary.changed>0;console.log(` ${managed.label.padEnd(10)} ${moved?"would update":"unchanged"}`)}else if(summary.added+summary.changed+summary.removed===0)console.log(` ${managed.label.padEnd(10)} unchanged (${summary.unchanged} files)`);else{console.log(` ${managed.label.padEnd(10)} +${summary.added} ~${summary.changed} -${summary.removed} (${summary.unchanged} unchanged)`);const samples=[...summary.addedFiles.map((f)=>`+ ${f}`),...summary.changedFiles.map((f)=>`~ ${f}`)];for(const line of samples.slice(0,8))console.log(` ${line}`);if(samples.length>8)console.log(` ... and ${samples.length-8} more`)}}const sourcePkgPath=usingLocal&&detectedLocal?join(detectedLocal,"storage/framework/core/buddy/package.json"):repoTmp?join(repoTmp,"storage/framework/core/buddy/package.json"):join(tmpBase,"core","buddy","package.json");sourceVersion=readVersion(sourcePkgPath)}finally{rmSync(tmpBase,{recursive:!0,force:!0})}console.log("");if(currentVersion||sourceVersion)console.log(currentVersion===sourceVersion?` version: unchanged (v${currentVersion??"unknown"})`:` version: v${currentVersion??"unknown"} -> v${sourceVersion??"unknown"}`);console.log(`
8
9
  A real run would:`);console.log(" - sync the managed paths above into place");if(!ctx.targetVersion){console.log(` - write channel "${ctx.channel}" to .stacks-channel`);console.log(previewSha?` - record synced sha ${previewSha.slice(0,7)} in .stacks-version`:" - record the resolved commit sha in .stacks-version (unavailable in this preview)")}if(!(!options.noPostinstall&&options.postinstall!==!1))console.log(" - skip post-sync hooks (--no-postinstall)");else if(aggregate.added+aggregate.changed+aggregate.removed===0)console.log(" - skip post-sync hooks (nothing changed)");else{console.log(" - regenerate the auto-import manifest");if(perPath.some((entry)=>{const{managed,summary}=entry;if(managed.isFile)return!1;if(managed.label!=="core"&&managed.label!=="defaults")return!1;if(summary.added+summary.changed===0)return!1;return pkgJsonInTree(join(projectRoot,managed.localPath))}))console.log(" - run `bun install` to refresh dependencies");if(existsSync(join(projectRoot,"database","migrations")))console.log(" - run pending migrations")}if(sawRemoved>0)console.log(`
@@ -1,4 +1,4 @@
1
- import{existsSync,readdirSync,readFileSync,statSync,writeFileSync}from"node:fs";import{join,relative}from"node:path";import process from"node:process";import{runCommand}from"@stacksjs/cli";import{installedDefaultsVersion}from"@stacksjs/path";import{detectProjectAiProviders,measureDefaultsDrift,migratePackageProjectManifest,migratePackageProjectTsconfig,summarizeStructureChanges,syncPackageProjectFiles}from"./package-project";export function hasStacksDependency(pkg){return Boolean(pkg.dependencies?.stacks||pkg.devDependencies?.stacks)}export function standalonePackageUpdateCommand(){return"bun update"}export function frameworkPackageUpdateCommand(){return"bun update stacks"}export function installedStacksVersion(projectRoot){try{const manifest=JSON.parse(readFileSync(join(projectRoot,"node_modules/stacks/package.json"),"utf-8"));return typeof manifest.version==="string"?manifest.version:null}catch{return null}}export function needsFrameworkInstall(projectRoot,target){return installedStacksVersion(projectRoot)!==target}async function updateStandalonePackage(projectRoot,options){console.log(`
1
+ import{existsSync,readdirSync,readFileSync,statSync,writeFileSync}from"node:fs";import{join,relative}from"node:path";import process from"node:process";import{runCommand}from"@stacksjs/cli";import{installedDefaultsVersion}from"@stacksjs/path";import{detectProjectAiProviders,measureDefaultsDrift,migratePackageProjectManifest,migratePackageProjectTsconfig,summarizeStructureChanges,syncPackageProjectFiles}from"./package-project";import{describeDanglingRouteActions,findDanglingRouteActions,pruneRetiredScaffoldRoutes}from"./route-actions";export function hasStacksDependency(pkg){return Boolean(pkg.dependencies?.stacks||pkg.devDependencies?.stacks)}export function standalonePackageUpdateCommand(){return"bun update"}export function frameworkPackageUpdateCommand(){return"bun update stacks"}export function installedStacksVersion(projectRoot){try{const manifest=JSON.parse(readFileSync(join(projectRoot,"node_modules/stacks/package.json"),"utf-8"));return typeof manifest.version==="string"?manifest.version:null}catch{return null}}export function needsFrameworkInstall(projectRoot,target){return installedStacksVersion(projectRoot)!==target}async function updateStandalonePackage(projectRoot,options){console.log(`
2
2
  Standalone package detected. No Stacks framework source is installed.`);if(options.dryRun){console.log(" --dry-run: would refresh the package dependencies with `bun update`. No files were changed.\n");process.exit(0)}if(options.noPostinstall){console.log(" --no-postinstall: skipping the dependency refresh. Run `bun update` when ready.\n");process.exit(0)}console.log(` Refreshing declared dependencies...
3
3
  `);if((await runCommand(standalonePackageUpdateCommand(),{cwd:projectRoot})).isErr){console.error("\n\u2717 The dependency refresh failed. Resolve the reported error and re-run `buddy update`.\n");process.exit(1)}console.log(`
4
4
  \u2714 Standalone package dependencies are up to date.
@@ -6,18 +6,20 @@ import{existsSync,readdirSync,readFileSync,statSync,writeFileSync}from"node:fs";
6
6
  Upgrading the Stacks framework${options.canary?" (canary)":""} \u2192 ${target}`);if(current)console.log(` current \`stacks\` constraint: ${current}
7
7
  `);const lockstep=lockstepPackages(metaDeps,target),changes=applyBumps(pkg,target,lockstep),projectManifestChanges=migratePackageProjectManifest(pkg),manifestChanges=reconcileVendoredManifests(projectRoot,target,{dryRun:options.dryRun,context:{lockstep,metaDeps}});if(changes.length>0){console.log(" The following dependencies will be updated:");const width=Math.max(...changes.map((c)=>c.name.length));for(const c of changes)console.log(` ${c.name.padEnd(width)} ${c.from} \u2192 ${c.to}`);console.log("")}if(manifestChanges.length>0){console.log(` Vendored manifests under storage/framework (${manifestChanges.length}):`);const width=Math.max(...manifestChanges.map((c)=>c.name.length));for(const c of manifestChanges)console.log(` ${c.name.padEnd(width)} ${c.from} \u2192 ${c.to} ${c.file}`);console.log("")}if(projectManifestChanges.length>0)console.log(` Project manifest migrations: ${projectManifestChanges.length}
8
8
  `);if(options.dryRun){const pending=measureDefaultsDrift(projectRoot);if(pending&&pending.length>0){const installed=installedDefaultsVersion(projectRoot);console.log(` Managed project files, against the installed @stacksjs/defaults${installed?` (${installed})`:""}:`);console.log(` ${summarizeStructureChanges(pending)} in storage/framework/defaults and project support files
9
- `)}console.log(` --dry-run: no files were written and nothing was installed.
9
+ `)}reportRouteActions(projectRoot,!0);console.log(` --dry-run: no files were written and nothing was installed.
10
10
  `);process.exit(0)}if(changes.length>0||projectManifestChanges.length>0){const trailing=raw.endsWith(`
11
11
  `)?`
12
12
  `:"";writeFileSync(pkgPath,`${JSON.stringify(pkg,null,2)}${trailing}`);console.log("\u2714 Updated package.json")}if(options.noPostinstall){console.log(" --no-postinstall: skipping install. Run this to pull the new versions:");console.log(` ${frameworkPackageUpdateCommand()}
13
13
  `);process.exit(0)}const dependenciesNeedInstall=needsFrameworkInstall(projectRoot,target);if(changes.length>0||manifestChanges.length>0||projectManifestChanges.length>0||dependenciesNeedInstall||options.force){console.log(` Installing\u2026
14
- `);if((await runCommand(frameworkPackageUpdateCommand(),{cwd:projectRoot})).isErr){console.error("\n\u2717 The install step failed. Your package.json was updated - resolve the error and re-run `bun update`.\n");process.exit(1)}}const defaultsPackageRoot=join(projectRoot,"node_modules/@stacksjs/defaults"),structureChanges=existsSync(join(defaultsPackageRoot,"package.json"))?syncPackageProjectFiles(projectRoot,defaultsPackageRoot):[],tsconfigChanges=migratePackageProjectTsconfig(projectRoot);if(structureChanges.length>0||tsconfigChanges.length>0){const added=structureChanges.filter((change)=>change.action==="add").length,updated=structureChanges.filter((change)=>change.action==="update").length+tsconfigChanges.length,removed=structureChanges.filter((change)=>change.action==="remove").length;console.log(` Project structure: +${added} ~${updated} -${removed}`)}if(structureChanges.length>0)await regenerateAutoImports(projectRoot);for(const provider of detectProjectAiProviders(projectRoot))if((await runCommand(`./buddy setup:ai ${provider} --force`,{cwd:projectRoot})).isErr){console.error(`
14
+ `);if((await runCommand(frameworkPackageUpdateCommand(),{cwd:projectRoot})).isErr){console.error("\n\u2717 The install step failed. Your package.json was updated - resolve the error and re-run `bun update`.\n");process.exit(1)}}const defaultsPackageRoot=join(projectRoot,"node_modules/@stacksjs/defaults"),structureChanges=existsSync(join(defaultsPackageRoot,"package.json"))?syncPackageProjectFiles(projectRoot,defaultsPackageRoot):[],tsconfigChanges=migratePackageProjectTsconfig(projectRoot),prunedRoutes=reportRouteActions(projectRoot,!1);if(structureChanges.length>0||tsconfigChanges.length>0){const added=structureChanges.filter((change)=>change.action==="add").length,updated=structureChanges.filter((change)=>change.action==="update").length+tsconfigChanges.length,removed=structureChanges.filter((change)=>change.action==="remove").length;console.log(` Project structure: +${added} ~${updated} -${removed}`)}if(structureChanges.length>0)await regenerateAutoImports(projectRoot);for(const provider of detectProjectAiProviders(projectRoot))if((await runCommand(`./buddy setup:ai ${provider} --force`,{cwd:projectRoot})).isErr){console.error(`
15
15
  \u2717 AI setup refresh failed for ${provider}. Re-run \`buddy setup:ai ${provider} --force\`.
16
- `);process.exit(1)}if(!(changes.length>0||manifestChanges.length>0||projectManifestChanges.length>0||dependenciesNeedInstall||structureChanges.length>0||tsconfigChanges.length>0)&&!options.force)console.log(`
16
+ `);process.exit(1)}if(!(changes.length>0||manifestChanges.length>0||projectManifestChanges.length>0||dependenciesNeedInstall||structureChanges.length>0||tsconfigChanges.length>0||prunedRoutes>0)&&!options.force)console.log(`
17
17
  \u2714 Already up to date - dependencies and managed project files match the target.
18
18
  `);else console.log(`
19
19
  \u2714 Upgraded to stacks@${target}. Review the changelog: https://github.com/stacksjs/stacks/releases/tag/v${target}
20
- `);process.exit(0)}async function regenerateAutoImports(projectRoot){const proc=Bun.spawn({cmd:[process.execPath,"-e","await (await import('@stacksjs/server')).generateAutoImportFiles()"],cwd:projectRoot,stdout:"ignore",stderr:"pipe"});if(await proc.exited!==0){const stderr=(await new Response(proc.stderr).text()).trim();console.warn(` auto-imports were not regenerated (non-fatal): ${stderr.split(`
20
+ `);process.exit(0)}function reportRouteActions(projectRoot,dryRun){const pruned=pruneRetiredScaffoldRoutes(projectRoot,{dryRun});for(const route of pruned)console.log(` ${dryRun?"Would remove":"Removed"} ${route.file}:${route.line}, the scaffold route to ${route.action}: removed in ${route.removedIn}, ${route.reason}`);const dangling=findDanglingRouteActions(projectRoot);if(dangling.length>0){console.warn(`
21
+ ${dangling.length} route${dangling.length===1?"":"s"} name${dangling.length===1?"s":""} an action this version does not ship. Point ${dangling.length===1?"it":"them"} at an action that exists, or delete ${dangling.length===1?"it":"them"}:`);for(const line of describeDanglingRouteActions(dangling).split(`
22
+ `))console.warn(` ${line}`);console.warn("")}return pruned.length}async function regenerateAutoImports(projectRoot){const proc=Bun.spawn({cmd:[process.execPath,"-e","await (await import('@stacksjs/server')).generateAutoImportFiles()"],cwd:projectRoot,stdout:"ignore",stderr:"pipe"});if(await proc.exited!==0){const stderr=(await new Response(proc.stderr).text()).trim();console.warn(` auto-imports were not regenerated (non-fatal): ${stderr.split(`
21
23
  `).pop()||`exit ${proc.exitCode}`}`);return}console.log(" Regenerated auto-imports")}export function resolveManifestSpec(name,spec,target,context){if(name!=="stacks"&&!name.startsWith("@stacksjs/"))return null;if(context&&!context.lockstep.has(name)){const declared=context.metaDeps[name];if(!declared)return null;return baseVersion(declared)===baseVersion(spec)?null:declared}if(spec.startsWith("workspace:"))return`^${target}`;if(baseVersion(spec)===target)return null;if(!/^[\^~]?\d/.test(spec))return null;return`${/^[\^~]/.test(spec)?spec[0]:""}${target}`}export function reconcileVendoredManifests(projectRoot,target,options={}){const root=join(projectRoot,"storage/framework");if(!existsSync(root))return[];const changes=[],walk=(dir)=>{let entries;try{entries=readdirSync(dir)}catch{return}for(const entry of entries){if(entry==="node_modules"||entry==="dist"||entry===".git")continue;const full=join(dir,entry);let stat;try{stat=statSync(full)}catch{continue}if(stat.isDirectory()){walk(full);continue}if(entry!=="package.json")continue;let raw;try{raw=readFileSync(full,"utf-8")}catch{continue}let pkg;try{pkg=JSON.parse(raw)}catch{continue}let touched=!1;for(const field of["dependencies","devDependencies"]){const deps=pkg[field];if(!deps)continue;for(const[name,spec]of Object.entries(deps)){const next=resolveManifestSpec(name,spec,target,options.context);if(!next)continue;changes.push({file:relative(projectRoot,full),name,from:spec,to:next});deps[name]=next;touched=!0}}if(touched&&!options.dryRun){const suffix=raw.endsWith(`
22
24
  `)?`
23
25
  `:"";writeFileSync(full,`${JSON.stringify(pkg,null,2)}${suffix}`)}}};walk(root);return changes}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Where an `'Actions/…'` handler can live: the app first, then the framework
3
+ * defaults, vendored or installed. The same order the router resolves in.
4
+ */
5
+ export declare function actionRoots(projectRoot: string): string[];
6
+ export declare function actionResolves(projectRoot: string, action: string): boolean;
7
+ /**
8
+ * Every `'Actions/…'` string in the app's route files that resolves to no file.
9
+ *
10
+ * Literal handler strings only: a resource route composes its five names at
11
+ * runtime, and a `Controller@method` handler is not an action path.
12
+ */
13
+ export declare function findDanglingRouteActions(projectRoot: string): RouteActionReference[];
14
+ /**
15
+ * Remove the retired scaffold routes an app still carries, where it carries
16
+ * them unchanged and the action is gone. Returns what was (or, with `dryRun`,
17
+ * would be) removed.
18
+ */
19
+ export declare function pruneRetiredScaffoldRoutes(projectRoot: string, options?: { dryRun?: boolean }): PrunedScaffoldRoute[];
20
+ /** One line per reference, for the upgrade summary and `buddy doctor`. */
21
+ export declare function describeDanglingRouteActions(references: RouteActionReference[]): string;
22
+ export declare const RETIRED_SCAFFOLD_ROUTES: RetiredScaffoldRoute[];
23
+ export declare interface RetiredScaffoldRoute {
24
+ file: string
25
+ statement: string
26
+ action: string
27
+ removedIn: string
28
+ reason: string
29
+ }
30
+ export declare interface RouteActionReference {
31
+ file: string
32
+ line: number
33
+ action: string
34
+ }
35
+ export declare interface PrunedScaffoldRoute extends RouteActionReference {
36
+ removedIn: string
37
+ reason: string
38
+ }
@@ -0,0 +1,5 @@
1
+ import{existsSync,readdirSync,readFileSync,statSync,writeFileSync}from"node:fs";import{join,relative}from"node:path";export const RETIRED_SCAFFOLD_ROUTES=[{file:"routes/buddy.ts",statement:"route.get('/commands', 'Actions/Buddy/CommandsAction')",action:"Actions/Buddy/CommandsAction",removedIn:"0.74.5",reason:"the hand-written commands endpoint was dropped for `buddy list --json` (stacksjs/stacks#2056)"}];export function actionRoots(projectRoot){return[join(projectRoot,"app"),join(projectRoot,"storage/framework/defaults/app"),join(projectRoot,"node_modules/@stacksjs/defaults/app")]}export function actionResolves(projectRoot,action){return actionRoots(projectRoot).some((root)=>[".ts",".js"].some((extension)=>existsSync(join(root,`${action}${extension}`))))}function routeFiles(dir){if(!existsSync(dir))return[];const files=[];for(const entry of readdirSync(dir)){const path=join(dir,entry);if(statSync(path).isDirectory())files.push(...routeFiles(path));else if(/\.[cm]?ts$/.test(entry)&&!entry.endsWith(".d.ts"))files.push(path)}return files.sort()}function withoutLineComment(line){let quote=null;for(let i=0;i<line.length;i++){const char=line[i];if(quote){if(char==="\\")i++;else if(char===quote)quote=null;continue}if(char==="'"||char==='"'||char==="`")quote=char;else if(char==="/"&&line[i+1]==="/")return line.slice(0,i)}return line}const normalize=(statement)=>statement.replace(/\s+/g,"").replace(/;$/,"");export function findDanglingRouteActions(projectRoot){const dangling=[];for(const path of routeFiles(join(projectRoot,"routes")))readFileSync(path,"utf8").split(`
2
+ `).forEach((line,index)=>{const code=withoutLineComment(line);if(/^\s*(?:\*|\/\*)/.test(code))return;for(const match of code.matchAll(/(['"`])(Actions\/[\w/.-]+)\1/g)){const action=match[2];if(!actionResolves(projectRoot,action))dangling.push({file:relative(projectRoot,path),line:index+1,action})}});return dangling}export function pruneRetiredScaffoldRoutes(projectRoot,options={}){const pruned=[];for(const retired of RETIRED_SCAFFOLD_ROUTES){const path=join(projectRoot,retired.file);if(!existsSync(path)||actionResolves(projectRoot,retired.action))continue;const lines=readFileSync(path,"utf8").split(`
3
+ `),kept=lines.filter((line,index)=>{if(normalize(withoutLineComment(line))!==normalize(retired.statement))return!0;pruned.push({file:retired.file,line:index+1,action:retired.action,removedIn:retired.removedIn,reason:retired.reason});return!1});if(kept.length!==lines.length&&!options.dryRun)writeFileSync(path,kept.join(`
4
+ `))}return pruned}export function describeDanglingRouteActions(references){return references.map((ref)=>`${ref.file}:${ref.line} names ${ref.action}, which no longer exists`).join(`
5
+ `)}
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.13",
5
+ "version": "0.75.15",
6
6
  "description": "The Stacks actions.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -60,53 +60,53 @@
60
60
  "prepublishOnly": "bun run build"
61
61
  },
62
62
  "dependencies": {
63
- "@stacksjs/action-runner": "0.75.13",
64
- "@stacksjs/ai": "0.75.13",
65
- "@stacksjs/analytics": "0.75.13",
66
- "@stacksjs/api": "0.75.13",
67
- "@stacksjs/auth": "0.75.13",
68
- "@stacksjs/build": "0.75.13",
63
+ "@stacksjs/action-runner": "0.75.15",
64
+ "@stacksjs/ai": "0.75.15",
65
+ "@stacksjs/analytics": "0.75.15",
66
+ "@stacksjs/api": "0.75.15",
67
+ "@stacksjs/auth": "0.75.15",
68
+ "@stacksjs/build": "0.75.15",
69
69
  "@stacksjs/bumpx": "^0.2.6",
70
70
  "@stacksjs/bunpress": "^0.2.8",
71
- "@stacksjs/cli": "0.75.13",
72
- "@stacksjs/cloud": "0.75.13",
73
- "@stacksjs/cms": "0.75.13",
74
- "@stacksjs/components": "^0.2.320",
75
- "@stacksjs/config": "0.75.13",
76
- "@stacksjs/database": "0.75.13",
77
- "@stacksjs/desktop-build": "0.75.13",
78
- "@stacksjs/dns": "0.75.13",
79
- "@stacksjs/enums": "0.75.13",
80
- "@stacksjs/env": "0.75.13",
81
- "@stacksjs/error-handling": "0.75.13",
71
+ "@stacksjs/cli": "0.75.15",
72
+ "@stacksjs/cloud": "0.75.15",
73
+ "@stacksjs/cms": "0.75.15",
74
+ "@stacksjs/components": "^0.2.321",
75
+ "@stacksjs/config": "0.75.15",
76
+ "@stacksjs/database": "0.75.15",
77
+ "@stacksjs/desktop-build": "0.75.15",
78
+ "@stacksjs/dns": "0.75.15",
79
+ "@stacksjs/enums": "0.75.15",
80
+ "@stacksjs/env": "0.75.15",
81
+ "@stacksjs/error-handling": "0.75.15",
82
82
  "@stacksjs/gitit": "^0.2.5",
83
- "@stacksjs/i18n": "0.75.13",
84
- "@stacksjs/image": "0.75.13",
85
- "@stacksjs/logging": "0.75.13",
83
+ "@stacksjs/i18n": "0.75.15",
84
+ "@stacksjs/image": "0.75.15",
85
+ "@stacksjs/logging": "0.75.15",
86
86
  "@stacksjs/logsmith": "^0.2.3",
87
- "@stacksjs/orm": "0.75.13",
88
- "@stacksjs/path": "0.75.13",
89
- "@stacksjs/payments": "0.75.13",
90
- "@stacksjs/queue": "0.75.13",
91
- "@stacksjs/registry": "0.75.13",
92
- "@stacksjs/router": "0.75.13",
87
+ "@stacksjs/orm": "0.75.15",
88
+ "@stacksjs/path": "0.75.15",
89
+ "@stacksjs/payments": "0.75.15",
90
+ "@stacksjs/queue": "0.75.15",
91
+ "@stacksjs/registry": "0.75.15",
92
+ "@stacksjs/router": "0.75.15",
93
93
  "@stacksjs/rpx": "^0.11.54",
94
- "@stacksjs/scheduler": "0.75.13",
95
- "@stacksjs/search-engine": "0.75.13",
96
- "@stacksjs/security": "0.75.13",
97
- "@stacksjs/server": "0.75.13",
98
- "@stacksjs/sites": "0.75.13",
99
- "@stacksjs/storage": "0.75.13",
100
- "@stacksjs/strings": "0.75.13",
101
- "@stacksjs/stx": "^0.2.320",
94
+ "@stacksjs/scheduler": "0.75.15",
95
+ "@stacksjs/search-engine": "0.75.15",
96
+ "@stacksjs/security": "0.75.15",
97
+ "@stacksjs/server": "0.75.15",
98
+ "@stacksjs/sites": "0.75.15",
99
+ "@stacksjs/storage": "0.75.15",
100
+ "@stacksjs/strings": "0.75.15",
101
+ "@stacksjs/stx": "^0.2.321",
102
102
  "@stacksjs/tlsx": "^0.13.19",
103
103
  "@stacksjs/ts-cloud": "^0.16.14",
104
104
  "@stacksjs/ts-md": "^0.1.1",
105
- "@stacksjs/tunnel": "0.75.13",
106
- "@stacksjs/types": "0.75.13",
107
- "@stacksjs/utils": "0.75.13",
108
- "@stacksjs/whois": "0.75.13",
109
- "bun-plugin-stx": "^0.2.320"
105
+ "@stacksjs/tunnel": "0.75.15",
106
+ "@stacksjs/types": "0.75.15",
107
+ "@stacksjs/utils": "0.75.15",
108
+ "@stacksjs/whois": "0.75.15",
109
+ "bun-plugin-stx": "^0.2.321"
110
110
  },
111
111
  "peerDependencies": {
112
112
  "craft-native": ">=0.0.72",
@@ -121,7 +121,7 @@
121
121
  }
122
122
  },
123
123
  "devDependencies": {
124
- "@stacksjs/validation": "0.75.13",
124
+ "@stacksjs/validation": "0.75.15",
125
125
  "better-dx": "^0.2.24"
126
126
  }
127
127
  }