@stacksjs/actions 0.75.285 → 0.75.288

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.
@@ -25,6 +25,7 @@ export declare function uploadAndroidMapping(mappingPath: string, host: string,
25
25
  * Connect reads and no background refresh.
26
26
  */
27
27
  export declare const CRAFT_ANDROID_FLOORS: {
28
+ audioRecording: '0.0.324';
28
29
  /** The native app shell: tabs, paths to screens, the app script, synchronous stores. */
29
30
  nativeShell: '0.0.169';
30
31
  /** Verified HTTPS hosts and ordered website path exclusions. */
@@ -1 +1 @@
1
- import{NATIVE_APP_ROUTE,normalizeMobileUrl,toCraftCrashReporting}from"./ios-config";import{nativeScreenRoutes}from"./native-screens";const CAPABILITY_KEYS={speechRecognition:"enableSpeechRecognition",haptics:"enableHaptics",share:"enableShare",camera:"enableCamera",biometric:"enableBiometric",pushNotifications:"enablePushNotifications",secureStorage:"enableSecureStorage",geolocation:"enableGeolocation",backgroundLocation:"enableBackgroundLocation",keepAwake:"enableKeepAwake",deepLinks:"enableDeepLinks",healthConnect:"enableHealthConnect",bluetooth:"enableBluetooth",heartRate:"enableHeartRate",localNetwork:"enableLocalNetwork",localDatabase:"enableLocalDatabase",localNotifications:"enableLocalNotifications"};export const CRAFT_ANDROID_FLOORS={nativeShell:"0.0.169",appLinks:"0.0.201",bluetooth:"0.0.178",heartRate:"0.0.247",healthConnect:"0.0.169",backgroundRefresh:"0.0.169",appBundle:"0.0.169",crashReporting:"0.0.179",notificationIcon:"0.0.241"};export function assertCraftSupportsAndroidMapProvider(config,providers){if(config.mapProvider&&!providers?.includes(config.mapProvider))throw Error(`The resolved Craft Android builder does not advertise native map provider '${config.mapProvider}'. Upgrade craft-native to a published SDK supporting that provider before building this app.`)}export function androidCraftFeatures(config,options={}){const features=[];if(config.renderer==="native"&&Object.keys(config.nativeScreens??{}).length)features.push("nativeShell");if(config.appLinks?.length&&config.capabilities?.deepLinks)features.push("appLinks");if(config.capabilities?.bluetooth)features.push("bluetooth");if(config.capabilities?.heartRate)features.push("heartRate");if(config.capabilities?.healthConnect&&config.renderer==="native")features.push("healthConnect");if(config.backgroundRefresh?.enabled)features.push("backgroundRefresh");if(options.appBundle)features.push("appBundle");if(config.crashReporting?.key)features.push("crashReporting");if(config.notificationIcon||config.notificationColor)features.push("notificationIcon");return features}export function assertCraftSupportsAndroid(config,version,source,options={}){const missing=androidCraftFeatures(config,options).filter((feature)=>!Bun.semver.satisfies(version,`>=${CRAFT_ANDROID_FLOORS[feature]}`));if(!missing.length)return;const needed=missing.map((feature)=>CRAFT_ANDROID_FLOORS[feature]).reduce((max,next)=>Bun.semver.order(next,max)>0?next:max,"0.0.0");throw Error(`The Craft Android builder is craft-native ${version} (${source}), which would build the app without ${missing.map((feature)=>`${feature} (needs >= ${CRAFT_ANDROID_FLOORS[feature]})`).join(", ")}. Install craft-native >= ${needed}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export function toCraftAndroidConfig(config){const devServerURL=normalizeMobileUrl(config.url),trustedOrigins=new Set(config.trustedOrigins??[]);if(devServerURL)trustedOrigins.add(new URL(devServerURL).origin);const native=config.renderer==="native",craft={mapProvider:config.mapProvider,nativeFonts:config.nativeFonts,appName:config.appName,packageName:config.packageName,version:config.version,versionCode:config.versionCode,minSdk:config.minSdk,targetSdk:config.targetSdk,darkMode:config.darkMode,appearance:config.appearance,backgroundColor:config.backgroundColor,backgroundColorDark:config.backgroundColorDark,devServerURL,trustedOrigins:[...trustedOrigins],urlSchemes:config.urlSchemes,appLinks:config.appLinks,appIconPath:config.appIcon,notificationColor:config.notificationColor,googleServicesFile:config.googleServicesFile,mapsApiKey:config.mapsApiKey,renderer:config.renderer,nativeScreens:native?nativeScreenRoutes(config):void 0,nativeApp:native&&config.nativeApp?NATIVE_APP_ROUTE:void 0,tabs:native?config.tabs:void 0,backgroundRefresh:native?config.backgroundRefresh:void 0,healthConnect:config.capabilities?.healthConnect?config.healthConnect:void 0,privacyPolicyURL:config.privacyPolicyUrl,crashReporting:toCraftCrashReporting(config.crashReporting)};for(const[key,nativeKey]of Object.entries(CAPABILITY_KEYS)){const enabled=config.capabilities?.[key];if(enabled!==void 0)craft[nativeKey]=enabled}if(config.capabilities?.backgroundLocation)craft.enableGeolocation=!0;for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function validateAndroidMobileConfig(config){if(config.mapProvider!==void 0&&config.mapProvider!=="google"&&config.mapProvider!=="maplibre")throw Error(`android.mapProvider must be 'google' or 'maplibre', not ${String(config.mapProvider)}`);if(config.mapProvider&&config.renderer!=="native")throw Error("android.mapProvider configures native map views: set android.renderer to 'native'");if(!config.appName?.trim())throw Error("config/mobile.ts must define android.appName");if(!/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+$/.test(config.packageName))throw Error(`Invalid Android package name: ${config.packageName}`);if(config.notificationColor!==void 0&&!/^#[0-9a-f]{6}$/i.test(config.notificationColor))throw Error(`android.notificationColor must be #RRGGBB, not ${String(config.notificationColor)}`);if(config.notificationIcon!==void 0&&!/\.(png|xml)$/i.test(config.notificationIcon))throw Error("android.notificationIcon must be an Android vector drawable (.xml) or a white-on-transparent .png");if(config.mapsApiKey!==void 0&&(typeof config.mapsApiKey!=="string"||!config.mapsApiKey.trim()))throw Error("android.mapsApiKey must be a non-empty Google Maps Android SDK key");if(config.renderer!==void 0&&config.renderer!=="web"&&config.renderer!=="native")throw Error(`android.renderer must be 'web' or 'native', not ${String(config.renderer)}`);if(config.renderer==="native"){if(!Object.keys(config.nativeScreens??{}).length)throw Error("android.renderer 'native' draws every screen natively: list them in android.nativeScreens");if(config.webAssets)throw Error("android.renderer 'native' has no web view to show android.webAssets in: set android.url to the app's site instead");if(config.fallbackWebAssets)throw Error("android.fallbackWebAssets is the web view's offline page; android.renderer 'native' has no web view");if(!config.url)throw Error("android.renderer 'native' needs android.url: the app's site, where its API answers")}else{if(config.url&&config.webAssets)throw Error("Choose either android.url or android.webAssets in config/mobile.ts, not both");if(!config.url&&!config.webAssets)throw Error("config/mobile.ts must define android.url or android.webAssets");if(config.fallbackWebAssets&&!config.url)throw Error("android.fallbackWebAssets requires android.url")}if(config.nativeApp&&config.renderer!=="native")throw Error("android.nativeApp runs beside native screens: set android.renderer to 'native'");for(const tab of config.tabs??[])if(!tab.id?.startsWith("/"))throw Error(`An android.tabs entry needs an id that is an app path, like /m (got ${String(tab.id)})`);if(config.url){const normalized=normalizeMobileUrl(config.url),url=new URL(normalized),isLocal=["localhost","127.0.0.1","10.0.2.2"].includes(url.hostname);if(url.protocol!=="https:"&&!isLocal)throw Error("android.url must use HTTPS outside local development")}if(config.capabilities?.healthConnect&&(config.minSdk??26)<26)throw Error("Android Health Connect requires android.minSdk 26 or newer");if(config.healthConnect&&!config.capabilities?.healthConnect)throw Error("android.healthConnect lists Health Connect data, but android.capabilities.healthConnect is off");const minutes=config.backgroundRefresh?.minimumIntervalMinutes;if(minutes!==void 0&&(!Number.isFinite(minutes)||minutes<15))throw Error("android.backgroundRefresh.minimumIntervalMinutes is at least 15 (WorkManager's shortest period)");for(const scheme of config.urlSchemes??[])if(!/^[a-z][a-z0-9+.-]*$/i.test(scheme))throw Error(`Invalid Android URL scheme: ${scheme}`)}export function androidRelease(config){if(config.crashReporting?.release)return config.crashReporting.release;return`${config.version||"1.0.0"}+${config.versionCode||1}`}export async function uploadAndroidMapping(mappingPath,host,release,token,send=fetch){if(!token)return{ok:!1,message:"BUGHQ_UPLOAD_TOKEN is not set, so the R8 mapping was not uploaded and this build's Android crashes will show obfuscated names."};const file=Bun.file(mappingPath);if(!await file.exists())return{ok:!1,message:`No R8 mapping at ${mappingPath}, so nothing was uploaded.`};try{const body=Bun.gzipSync(new Uint8Array(await file.arrayBuffer())),response=await send(`${host.replace(/\/$/,"")}/api/debug-files?kind=proguard&release=${encodeURIComponent(release)}`,{method:"POST",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/octet-stream"},body});if(!response.ok)return{ok:!1,message:`bughq refused the R8 mapping (${response.status}): ${(await response.text()).slice(0,200)}`};return{ok:!0,message:`Uploaded the R8 mapping for ${release} to bughq.`}}catch(error){return{ok:!1,message:`Could not upload the R8 mapping: ${error instanceof Error?error.message:String(error)}`}}}
1
+ import{NATIVE_APP_ROUTE,normalizeMobileUrl,toCraftCrashReporting}from"./ios-config";import{nativeScreenRoutes}from"./native-screens";const CAPABILITY_KEYS={speechRecognition:"enableSpeechRecognition",audioRecording:"enableAudioRecording",haptics:"enableHaptics",share:"enableShare",camera:"enableCamera",biometric:"enableBiometric",pushNotifications:"enablePushNotifications",secureStorage:"enableSecureStorage",geolocation:"enableGeolocation",backgroundLocation:"enableBackgroundLocation",keepAwake:"enableKeepAwake",deepLinks:"enableDeepLinks",healthConnect:"enableHealthConnect",bluetooth:"enableBluetooth",heartRate:"enableHeartRate",localNetwork:"enableLocalNetwork",localDatabase:"enableLocalDatabase",localNotifications:"enableLocalNotifications"};export const CRAFT_ANDROID_FLOORS={audioRecording:"0.0.324",nativeShell:"0.0.169",appLinks:"0.0.201",bluetooth:"0.0.178",heartRate:"0.0.247",healthConnect:"0.0.169",backgroundRefresh:"0.0.169",appBundle:"0.0.169",crashReporting:"0.0.179",notificationIcon:"0.0.241"};export function assertCraftSupportsAndroidMapProvider(config,providers){if(config.mapProvider&&!providers?.includes(config.mapProvider))throw Error(`The resolved Craft Android builder does not advertise native map provider '${config.mapProvider}'. Upgrade craft-native to a published SDK supporting that provider before building this app.`)}export function androidCraftFeatures(config,options={}){const features=[];if(config.renderer==="native"&&Object.keys(config.nativeScreens??{}).length)features.push("nativeShell");if(config.appLinks?.length&&config.capabilities?.deepLinks)features.push("appLinks");if(config.capabilities?.bluetooth)features.push("bluetooth");if(config.capabilities?.heartRate)features.push("heartRate");if(config.capabilities?.audioRecording)features.push("audioRecording");if(config.capabilities?.healthConnect&&config.renderer==="native")features.push("healthConnect");if(config.backgroundRefresh?.enabled)features.push("backgroundRefresh");if(options.appBundle)features.push("appBundle");if(config.crashReporting?.key)features.push("crashReporting");if(config.notificationIcon||config.notificationColor)features.push("notificationIcon");return features}export function assertCraftSupportsAndroid(config,version,source,options={}){const missing=androidCraftFeatures(config,options).filter((feature)=>!Bun.semver.satisfies(version,`>=${CRAFT_ANDROID_FLOORS[feature]}`));if(!missing.length)return;const needed=missing.map((feature)=>CRAFT_ANDROID_FLOORS[feature]).reduce((max,next)=>Bun.semver.order(next,max)>0?next:max,"0.0.0");throw Error(`The Craft Android builder is craft-native ${version} (${source}), which would build the app without ${missing.map((feature)=>`${feature} (needs >= ${CRAFT_ANDROID_FLOORS[feature]})`).join(", ")}. Install craft-native >= ${needed}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export function toCraftAndroidConfig(config){const devServerURL=normalizeMobileUrl(config.url),trustedOrigins=new Set(config.trustedOrigins??[]);if(devServerURL)trustedOrigins.add(new URL(devServerURL).origin);const native=config.renderer==="native",craft={mapProvider:config.mapProvider,nativeFonts:config.nativeFonts,appName:config.appName,packageName:config.packageName,version:config.version,versionCode:config.versionCode,minSdk:config.minSdk,targetSdk:config.targetSdk,darkMode:config.darkMode,appearance:config.appearance,backgroundColor:config.backgroundColor,backgroundColorDark:config.backgroundColorDark,devServerURL,trustedOrigins:[...trustedOrigins],urlSchemes:config.urlSchemes,appLinks:config.appLinks,appIconPath:config.appIcon,notificationColor:config.notificationColor,googleServicesFile:config.googleServicesFile,mapsApiKey:config.mapsApiKey,renderer:config.renderer,nativeScreens:native?nativeScreenRoutes(config):void 0,nativeApp:native&&config.nativeApp?NATIVE_APP_ROUTE:void 0,tabs:native?config.tabs:void 0,backgroundRefresh:native?config.backgroundRefresh:void 0,healthConnect:config.capabilities?.healthConnect?config.healthConnect:void 0,privacyPolicyURL:config.privacyPolicyUrl,crashReporting:toCraftCrashReporting(config.crashReporting)};for(const[key,nativeKey]of Object.entries(CAPABILITY_KEYS)){const enabled=config.capabilities?.[key];if(enabled!==void 0)craft[nativeKey]=enabled}if(config.capabilities?.backgroundLocation)craft.enableGeolocation=!0;for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function validateAndroidMobileConfig(config){if(config.mapProvider!==void 0&&config.mapProvider!=="google"&&config.mapProvider!=="maplibre")throw Error(`android.mapProvider must be 'google' or 'maplibre', not ${String(config.mapProvider)}`);if(config.mapProvider&&config.renderer!=="native")throw Error("android.mapProvider configures native map views: set android.renderer to 'native'");if(!config.appName?.trim())throw Error("config/mobile.ts must define android.appName");if(!/^[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)+$/.test(config.packageName))throw Error(`Invalid Android package name: ${config.packageName}`);if(config.notificationColor!==void 0&&!/^#[0-9a-f]{6}$/i.test(config.notificationColor))throw Error(`android.notificationColor must be #RRGGBB, not ${String(config.notificationColor)}`);if(config.notificationIcon!==void 0&&!/\.(png|xml)$/i.test(config.notificationIcon))throw Error("android.notificationIcon must be an Android vector drawable (.xml) or a white-on-transparent .png");if(config.mapsApiKey!==void 0&&(typeof config.mapsApiKey!=="string"||!config.mapsApiKey.trim()))throw Error("android.mapsApiKey must be a non-empty Google Maps Android SDK key");if(config.renderer!==void 0&&config.renderer!=="web"&&config.renderer!=="native")throw Error(`android.renderer must be 'web' or 'native', not ${String(config.renderer)}`);if(config.renderer==="native"){if(!Object.keys(config.nativeScreens??{}).length)throw Error("android.renderer 'native' draws every screen natively: list them in android.nativeScreens");if(config.webAssets)throw Error("android.renderer 'native' has no web view to show android.webAssets in: set android.url to the app's site instead");if(config.fallbackWebAssets)throw Error("android.fallbackWebAssets is the web view's offline page; android.renderer 'native' has no web view");if(!config.url)throw Error("android.renderer 'native' needs android.url: the app's site, where its API answers")}else{if(config.url&&config.webAssets)throw Error("Choose either android.url or android.webAssets in config/mobile.ts, not both");if(!config.url&&!config.webAssets)throw Error("config/mobile.ts must define android.url or android.webAssets");if(config.fallbackWebAssets&&!config.url)throw Error("android.fallbackWebAssets requires android.url")}if(config.nativeApp&&config.renderer!=="native")throw Error("android.nativeApp runs beside native screens: set android.renderer to 'native'");for(const tab of config.tabs??[])if(!tab.id?.startsWith("/"))throw Error(`An android.tabs entry needs an id that is an app path, like /m (got ${String(tab.id)})`);if(config.url){const normalized=normalizeMobileUrl(config.url),url=new URL(normalized),isLocal=["localhost","127.0.0.1","10.0.2.2"].includes(url.hostname);if(url.protocol!=="https:"&&!isLocal)throw Error("android.url must use HTTPS outside local development")}if(config.capabilities?.healthConnect&&(config.minSdk??26)<26)throw Error("Android Health Connect requires android.minSdk 26 or newer");if(config.healthConnect&&!config.capabilities?.healthConnect)throw Error("android.healthConnect lists Health Connect data, but android.capabilities.healthConnect is off");const minutes=config.backgroundRefresh?.minimumIntervalMinutes;if(minutes!==void 0&&(!Number.isFinite(minutes)||minutes<15))throw Error("android.backgroundRefresh.minimumIntervalMinutes is at least 15 (WorkManager's shortest period)");for(const scheme of config.urlSchemes??[])if(!/^[a-z][a-z0-9+.-]*$/i.test(scheme))throw Error(`Invalid Android URL scheme: ${scheme}`)}export function androidRelease(config){if(config.crashReporting?.release)return config.crashReporting.release;return`${config.version||"1.0.0"}+${config.versionCode||1}`}export async function uploadAndroidMapping(mappingPath,host,release,token,send=fetch){if(!token)return{ok:!1,message:"BUGHQ_UPLOAD_TOKEN is not set, so the R8 mapping was not uploaded and this build's Android crashes will show obfuscated names."};const file=Bun.file(mappingPath);if(!await file.exists())return{ok:!1,message:`No R8 mapping at ${mappingPath}, so nothing was uploaded.`};try{const body=Bun.gzipSync(new Uint8Array(await file.arrayBuffer())),response=await send(`${host.replace(/\/$/,"")}/api/debug-files?kind=proguard&release=${encodeURIComponent(release)}`,{method:"POST",headers:{Authorization:`Bearer ${token}`,"Content-Type":"application/octet-stream"},body});if(!response.ok)return{ok:!1,message:`bughq refused the R8 mapping (${response.status}): ${(await response.text()).slice(0,200)}`};return{ok:!0,message:`Uploaded the R8 mapping for ${release} to bughq.`}}catch(error){return{ok:!1,message:`Could not upload the R8 mapping: ${error instanceof Error?error.message:String(error)}`}}}
@@ -1,4 +1,4 @@
1
- import{existsSync,readFileSync,writeFileSync}from"node:fs";import{isAbsolute,join,relative,resolve}from"node:path";import{spotlightActivityTypes}from"@stacksjs/mobile";import{nativeScreenRoutes}from"./native-screens";export const NATIVE_APP_ROUTE="__app";const CAPABILITY_KEYS={speechRecognition:"enableSpeechRecognition",haptics:"enableHaptics",share:"enableShare",camera:"enableCamera",biometric:"enableBiometric",pushNotifications:"enablePushNotifications",secureStorage:"enableSecureStorage",geolocation:"enableGeolocation",backgroundLocation:"enableBackgroundLocation",clipboard:"enableClipboard",contacts:"enableContacts",calendar:"enableCalendar",localNotifications:"enableLocalNotifications",inAppPurchase:"enableInAppPurchase",keepAwake:"enableKeepAwake",orientationLock:"enableOrientationLock",deepLinks:"enableDeepLinks",qrScanner:"enableQRScanner",localNetwork:"enableLocalNetwork",filePicker:"enableFilePicker",fileDownload:"enableFileDownload",socialAuth:"enableSocialAuth",audioRecording:"enableAudioRecording",videoRecording:"enableVideoRecording",motionSensors:"enableMotionSensors",localDatabase:"enableLocalDatabase",bluetooth:"enableBluetooth",nfc:"enableNFC",healthKit:"enableHealthKit",appleMusic:"enableAppleMusic",heartRate:"enableHeartRate",backgroundAudio:"enableBackgroundAudio",scheduledWorkouts:"enableScheduledWorkouts",liveActivities:"enableLiveActivities",watchApp:"enableWatchApp",backgroundTasks:"enableBackgroundTasks",screenCapture:"enableScreenCapture",pdfViewer:"enablePDFViewer",augmentedReality:"enableAR",machineLearning:"enableMLKit"};export const CRAFT_CAPABILITY_FLOORS={appleMusic:"0.0.144",heartRate:"0.0.152",backgroundAudio:"0.0.162",scheduledWorkouts:"0.0.163"};export function unsupportedCraftCapabilities(capabilities,version){const missing=[];for(const[capability,needs]of Object.entries(CRAFT_CAPABILITY_FLOORS))if(capabilities?.[capability]&&!Bun.semver.satisfies(version,`>=${needs}`))missing.push({capability,needs});return missing}export function assertCraftSupportsCapabilities(capabilities,version,source){const missing=unsupportedCraftCapabilities(capabilities,version);if(missing.length===0)return;const needed=missing.reduce((max,{needs})=>Bun.semver.order(needs,max)>0?needs:max,"0.0.0");throw Error(`The Craft iOS builder is craft-native ${version} (${source}), which would build the app without ${missing.map(({capability,needs})=>`ios.capabilities.${capability} (needs >= ${needs})`).join(", ")}. Install craft-native >= ${needed}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export const CRAFT_CRASH_REPORTING_FLOOR="0.0.179";export function resolveCrashReporting(shared,platform){const set=(config)=>Object.fromEntries(Object.entries(config??{}).filter(([,value])=>value!==void 0&&value!=="")),merged={...set(shared),...set(platform)};if(!merged.key?.trim())return;if(merged.host){const url=new URL(normalizeMobileUrl(merged.host)),local=["localhost","127.0.0.1","::1","10.0.2.2"].includes(url.hostname);if(url.protocol!=="https:"&&!local)throw Error("crashReporting.host must use HTTPS outside local development");merged.host=url.origin}return merged}export function toCraftCrashReporting(config){if(!config?.key)return;const craft={key:config.key,host:config.host,environment:config.environment,release:config.release,sessions:config.sessions};for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function assertCraftSupportsCrashReporting(config,version,source){if(!config?.key||Bun.semver.satisfies(version,`>=${CRAFT_CRASH_REPORTING_FLOOR}`))return;throw Error(`The Craft builder is craft-native ${version} (${source}), which would build the app without crash reporting (crashReporting needs >= ${CRAFT_CRASH_REPORTING_FLOOR}). Install craft-native >= ${CRAFT_CRASH_REPORTING_FLOOR}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export function normalizeMobileUrl(value){const input=value?.trim();if(!input)return;return new URL(/^https?:\/\//i.test(input)?input:`https://${input}`).toString().replace(/\/$/,"")}export function portablePaths(value,projectDir,appRoot){if(typeof value==="string")return isAbsolute(value)&&!relative(appRoot,value).startsWith("..")?relative(projectDir,value)||".":value;if(Array.isArray(value))return value.map((item)=>portablePaths(item,projectDir,appRoot));if(value&&typeof value==="object"){const out={};for(const[key,item]of Object.entries(value))out[key]=typeof item==="string"&&!/(?:path(?:dark)?|bundle)$|^(?:src|license)$/i.test(key)?item:portablePaths(item,projectDir,appRoot);return out}return value}export function resolveMobilePath(root,value){if(!value)return;return isAbsolute(value)?value:resolve(root,value)}export function resolveSwipeNavigation(config){if(config.swipeNavigation!==void 0)return config.swipeNavigation;return config.swipeBack==="webview"}export function toCraftIosConfig(config){const devServerURL=normalizeMobileUrl(config.url),trustedOrigins=new Set(config.trustedOrigins??[]);if(devServerURL)trustedOrigins.add(new URL(devServerURL).origin);const declaresAppBoundDomains=(config.appBoundDomains?.length??0)>0,craft={nativeFonts:config.nativeFonts,appName:config.appName,bundleId:config.bundleId,version:config.version,buildNumber:config.buildNumber,darkMode:config.darkMode,appearance:config.appearance,backgroundColor:config.backgroundColor,backgroundColorDark:config.backgroundColorDark,swipeNavigation:resolveSwipeNavigation(config),allowsLinkPreview:config.allowsLinkPreview,keyboardAccessory:config.keyboardAccessory,disableZoom:config.disableZoom,splashMaxSeconds:config.splashMaxSeconds,requestTimeoutSeconds:config.requestTimeoutSeconds,backgroundRefresh:config.backgroundRefresh,iosVersion:config.deploymentTarget,watchosVersion:config.watchDeploymentTarget,teamId:config.teamId,devServerURL,urlSchemes:config.urlSchemes,appShortcuts:config.appShortcuts,trustedOrigins:[...trustedOrigins],appBoundDomains:config.appBoundDomains,limitNavigationsToAppBoundDomains:config.limitNavigationsToAppBoundDomains??(declaresAppBoundDomains||void 0),associatedDomains:config.associatedDomains,renderer:config.renderer,nativeScreens:nativeScreenRoutes(config),nativeApp:config.nativeApp?NATIVE_APP_ROUTE:void 0,nativeScriptThread:config.nativeScriptThread,tabs:config.tabs,shareStorage:config.shareStorage,appGroups:config.appGroups,appIconPath:config.appIcon,privacy:config.privacy,usageDescriptions:config.usageDescriptions,orientations:config.orientations,deviceFamilies:config.deviceFamilies,crashReporting:toCraftCrashReporting(config.crashReporting)};for(const[key,nativeKey]of Object.entries(CAPABILITY_KEYS)){const enabled=config.capabilities?.[key];if(enabled!==void 0)craft[nativeKey]=enabled}if(config.capabilities?.backgroundLocation)craft.enableGeolocation=!0;for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function validateIosMobileConfig(config){if(!config.appName?.trim())throw Error("config/mobile.ts must define ios.appName");if(!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(config.bundleId))throw Error(`Invalid iOS bundle identifier: ${config.bundleId}`);if(config.url&&config.webAssets)throw Error("Choose either ios.url or ios.webAssets in config/mobile.ts, not both");if(!config.url&&!config.webAssets)throw Error("config/mobile.ts must define ios.url or ios.webAssets");if(config.renderer!==void 0&&config.renderer!=="web"&&config.renderer!=="native")throw Error(`ios.renderer must be 'web' or 'native', not ${String(config.renderer)}`);if(config.renderer==="native"){if(!Object.keys(config.nativeScreens??{}).length)throw Error("ios.renderer 'native' draws every screen natively: list them in ios.nativeScreens");if(config.webAssets)throw Error("ios.renderer 'native' has no web view to show ios.webAssets in: set ios.url to the app's site instead");if(config.fallbackWebAssets)throw Error("ios.fallbackWebAssets is the web view's offline page; ios.renderer 'native' has no web view")}if(config.fallbackWebAssets&&!config.url)throw Error("ios.fallbackWebAssets requires ios.url");if(config.deviceFamilies&&(config.deviceFamilies.length===0||config.deviceFamilies.some((family)=>family!=="iphone"&&family!=="ipad")))throw Error("ios.deviceFamilies must contain iphone and/or ipad");if(config.url){const url=new URL(normalizeMobileUrl(config.url)),isLocal=["localhost","127.0.0.1","::1"].includes(url.hostname);if(url.protocol!=="https:"&&!isLocal)throw Error("ios.url must use HTTPS outside local development")}for(const domain of config.associatedDomains??[])if(!/^(applinks|webcredentials|activitycontinuation):[^/\s]+$/.test(domain))throw Error(`Invalid iOS associated domain: ${domain}`);for(const key of["splashMaxSeconds","requestTimeoutSeconds"]){const value=config[key];if(value!==void 0&&(!Number.isFinite(value)||value<=0))throw Error(`ios.${key} must be a positive number of seconds`)}const refresh=config.backgroundRefresh;if(refresh){if(refresh.identifier!==void 0&&!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(refresh.identifier))throw Error(`Invalid iOS background refresh identifier: ${refresh.identifier}`);if(refresh.minimumIntervalMinutes!==void 0&&(!Number.isFinite(refresh.minimumIntervalMinutes)||refresh.minimumIntervalMinutes<=0))throw Error("ios.backgroundRefresh.minimumIntervalMinutes must be a positive number of minutes")}if(config.capabilities?.watchApp){const target=Number.parseFloat(config.watchDeploymentTarget??"9.0");if(!Number.isFinite(target)||target<9)throw Error("ios.watchDeploymentTarget must be watchOS 9.0 or newer")}}const ACTIVITY_TYPES_BLOCK=/[ \t]*<key>NSUserActivityTypes<\/key>\s*<array>[\s\S]*?<\/array>\n?/;export function withActivityTypes(plist,types){const entries=types.map((type)=>` <string>${escapeXml(type)}</string>`).join(`
1
+ import{existsSync,readFileSync,writeFileSync}from"node:fs";import{isAbsolute,join,relative,resolve}from"node:path";import{spotlightActivityTypes}from"@stacksjs/mobile";import{nativeScreenRoutes}from"./native-screens";export const NATIVE_APP_ROUTE="__app";const CAPABILITY_KEYS={speechRecognition:"enableSpeechRecognition",haptics:"enableHaptics",share:"enableShare",camera:"enableCamera",biometric:"enableBiometric",pushNotifications:"enablePushNotifications",secureStorage:"enableSecureStorage",geolocation:"enableGeolocation",backgroundLocation:"enableBackgroundLocation",clipboard:"enableClipboard",contacts:"enableContacts",calendar:"enableCalendar",localNotifications:"enableLocalNotifications",inAppPurchase:"enableInAppPurchase",keepAwake:"enableKeepAwake",orientationLock:"enableOrientationLock",deepLinks:"enableDeepLinks",qrScanner:"enableQRScanner",localNetwork:"enableLocalNetwork",filePicker:"enableFilePicker",fileDownload:"enableFileDownload",socialAuth:"enableSocialAuth",audioRecording:"enableAudioRecording",videoRecording:"enableVideoRecording",motionSensors:"enableMotionSensors",localDatabase:"enableLocalDatabase",bluetooth:"enableBluetooth",nfc:"enableNFC",healthKit:"enableHealthKit",appleMusic:"enableAppleMusic",heartRate:"enableHeartRate",backgroundAudio:"enableBackgroundAudio",scheduledWorkouts:"enableScheduledWorkouts",liveActivities:"enableLiveActivities",watchApp:"enableWatchApp",backgroundTasks:"enableBackgroundTasks",screenCapture:"enableScreenCapture",pdfViewer:"enablePDFViewer",augmentedReality:"enableAR",machineLearning:"enableMLKit"};export const CRAFT_CAPABILITY_FLOORS={audioRecording:"0.0.324",appleMusic:"0.0.144",heartRate:"0.0.152",backgroundAudio:"0.0.162",scheduledWorkouts:"0.0.163"};export function unsupportedCraftCapabilities(capabilities,version){const missing=[];for(const[capability,needs]of Object.entries(CRAFT_CAPABILITY_FLOORS))if(capabilities?.[capability]&&!Bun.semver.satisfies(version,`>=${needs}`))missing.push({capability,needs});return missing}export function assertCraftSupportsCapabilities(capabilities,version,source){const missing=unsupportedCraftCapabilities(capabilities,version);if(missing.length===0)return;const needed=missing.reduce((max,{needs})=>Bun.semver.order(needs,max)>0?needs:max,"0.0.0");throw Error(`The Craft iOS builder is craft-native ${version} (${source}), which would build the app without ${missing.map(({capability,needs})=>`ios.capabilities.${capability} (needs >= ${needs})`).join(", ")}. Install craft-native >= ${needed}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export const CRAFT_CRASH_REPORTING_FLOOR="0.0.179";export function resolveCrashReporting(shared,platform){const set=(config)=>Object.fromEntries(Object.entries(config??{}).filter(([,value])=>value!==void 0&&value!=="")),merged={...set(shared),...set(platform)};if(!merged.key?.trim())return;if(merged.host){const url=new URL(normalizeMobileUrl(merged.host)),local=["localhost","127.0.0.1","::1","10.0.2.2"].includes(url.hostname);if(url.protocol!=="https:"&&!local)throw Error("crashReporting.host must use HTTPS outside local development");merged.host=url.origin}return merged}export function toCraftCrashReporting(config){if(!config?.key)return;const craft={key:config.key,host:config.host,environment:config.environment,release:config.release,sessions:config.sessions};for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function assertCraftSupportsCrashReporting(config,version,source){if(!config?.key||Bun.semver.satisfies(version,`>=${CRAFT_CRASH_REPORTING_FLOOR}`))return;throw Error(`The Craft builder is craft-native ${version} (${source}), which would build the app without crash reporting (crashReporting needs >= ${CRAFT_CRASH_REPORTING_FLOOR}). Install craft-native >= ${CRAFT_CRASH_REPORTING_FLOOR}: look for an older copy nested under @stacksjs/mobile in bun.lock, or raise the app's own craft-native pin.`)}export function normalizeMobileUrl(value){const input=value?.trim();if(!input)return;return new URL(/^https?:\/\//i.test(input)?input:`https://${input}`).toString().replace(/\/$/,"")}export function portablePaths(value,projectDir,appRoot){if(typeof value==="string")return isAbsolute(value)&&!relative(appRoot,value).startsWith("..")?relative(projectDir,value)||".":value;if(Array.isArray(value))return value.map((item)=>portablePaths(item,projectDir,appRoot));if(value&&typeof value==="object"){const out={};for(const[key,item]of Object.entries(value))out[key]=typeof item==="string"&&!/(?:path(?:dark)?|bundle)$|^(?:src|license)$/i.test(key)?item:portablePaths(item,projectDir,appRoot);return out}return value}export function resolveMobilePath(root,value){if(!value)return;return isAbsolute(value)?value:resolve(root,value)}export function resolveSwipeNavigation(config){if(config.swipeNavigation!==void 0)return config.swipeNavigation;return config.swipeBack==="webview"}export function toCraftIosConfig(config){const devServerURL=normalizeMobileUrl(config.url),trustedOrigins=new Set(config.trustedOrigins??[]);if(devServerURL)trustedOrigins.add(new URL(devServerURL).origin);const declaresAppBoundDomains=(config.appBoundDomains?.length??0)>0,craft={nativeFonts:config.nativeFonts,appName:config.appName,bundleId:config.bundleId,version:config.version,buildNumber:config.buildNumber,darkMode:config.darkMode,appearance:config.appearance,backgroundColor:config.backgroundColor,backgroundColorDark:config.backgroundColorDark,swipeNavigation:resolveSwipeNavigation(config),allowsLinkPreview:config.allowsLinkPreview,keyboardAccessory:config.keyboardAccessory,disableZoom:config.disableZoom,splashMaxSeconds:config.splashMaxSeconds,requestTimeoutSeconds:config.requestTimeoutSeconds,backgroundRefresh:config.backgroundRefresh,iosVersion:config.deploymentTarget,watchosVersion:config.watchDeploymentTarget,teamId:config.teamId,devServerURL,urlSchemes:config.urlSchemes,appShortcuts:config.appShortcuts,trustedOrigins:[...trustedOrigins],appBoundDomains:config.appBoundDomains,limitNavigationsToAppBoundDomains:config.limitNavigationsToAppBoundDomains??(declaresAppBoundDomains||void 0),associatedDomains:config.associatedDomains,renderer:config.renderer,nativeScreens:nativeScreenRoutes(config),nativeApp:config.nativeApp?NATIVE_APP_ROUTE:void 0,nativeScriptThread:config.nativeScriptThread,tabs:config.tabs,shareStorage:config.shareStorage,appGroups:config.appGroups,appIconPath:config.appIcon,privacy:config.privacy,usageDescriptions:config.usageDescriptions,orientations:config.orientations,deviceFamilies:config.deviceFamilies,crashReporting:toCraftCrashReporting(config.crashReporting)};for(const[key,nativeKey]of Object.entries(CAPABILITY_KEYS)){const enabled=config.capabilities?.[key];if(enabled!==void 0)craft[nativeKey]=enabled}if(config.capabilities?.backgroundLocation)craft.enableGeolocation=!0;for(const key of Object.keys(craft))if(craft[key]===void 0)delete craft[key];return craft}export function validateIosMobileConfig(config){if(!config.appName?.trim())throw Error("config/mobile.ts must define ios.appName");if(!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(config.bundleId))throw Error(`Invalid iOS bundle identifier: ${config.bundleId}`);if(config.url&&config.webAssets)throw Error("Choose either ios.url or ios.webAssets in config/mobile.ts, not both");if(!config.url&&!config.webAssets)throw Error("config/mobile.ts must define ios.url or ios.webAssets");if(config.renderer!==void 0&&config.renderer!=="web"&&config.renderer!=="native")throw Error(`ios.renderer must be 'web' or 'native', not ${String(config.renderer)}`);if(config.renderer==="native"){if(!Object.keys(config.nativeScreens??{}).length)throw Error("ios.renderer 'native' draws every screen natively: list them in ios.nativeScreens");if(config.webAssets)throw Error("ios.renderer 'native' has no web view to show ios.webAssets in: set ios.url to the app's site instead");if(config.fallbackWebAssets)throw Error("ios.fallbackWebAssets is the web view's offline page; ios.renderer 'native' has no web view")}if(config.fallbackWebAssets&&!config.url)throw Error("ios.fallbackWebAssets requires ios.url");if(config.deviceFamilies&&(config.deviceFamilies.length===0||config.deviceFamilies.some((family)=>family!=="iphone"&&family!=="ipad")))throw Error("ios.deviceFamilies must contain iphone and/or ipad");if(config.url){const url=new URL(normalizeMobileUrl(config.url)),isLocal=["localhost","127.0.0.1","::1"].includes(url.hostname);if(url.protocol!=="https:"&&!isLocal)throw Error("ios.url must use HTTPS outside local development")}for(const domain of config.associatedDomains??[])if(!/^(applinks|webcredentials|activitycontinuation):[^/\s]+$/.test(domain))throw Error(`Invalid iOS associated domain: ${domain}`);for(const key of["splashMaxSeconds","requestTimeoutSeconds"]){const value=config[key];if(value!==void 0&&(!Number.isFinite(value)||value<=0))throw Error(`ios.${key} must be a positive number of seconds`)}const refresh=config.backgroundRefresh;if(refresh){if(refresh.identifier!==void 0&&!/^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/.test(refresh.identifier))throw Error(`Invalid iOS background refresh identifier: ${refresh.identifier}`);if(refresh.minimumIntervalMinutes!==void 0&&(!Number.isFinite(refresh.minimumIntervalMinutes)||refresh.minimumIntervalMinutes<=0))throw Error("ios.backgroundRefresh.minimumIntervalMinutes must be a positive number of minutes")}if(config.capabilities?.watchApp){const target=Number.parseFloat(config.watchDeploymentTarget??"9.0");if(!Number.isFinite(target)||target<9)throw Error("ios.watchDeploymentTarget must be watchOS 9.0 or newer")}}const ACTIVITY_TYPES_BLOCK=/[ \t]*<key>NSUserActivityTypes<\/key>\s*<array>[\s\S]*?<\/array>\n?/;export function withActivityTypes(plist,types){const entries=types.map((type)=>` <string>${escapeXml(type)}</string>`).join(`
2
2
  `),block=types.length===0?"":` <key>NSUserActivityTypes</key>
3
3
  <array>
4
4
  ${entries}
@@ -10,6 +10,10 @@ export declare function frameworkPackageUpdateCommand(force?: boolean): string;
10
10
  export declare function installedStacksVersion(projectRoot: string): string | null;
11
11
  /** Whether the declared target still needs to be installed locally. */
12
12
  export declare function needsFrameworkInstall(projectRoot: string, target: string): boolean;
13
+ /** Independently versioned frontend packages paired with a published framework. */
14
+ export declare function publishedRuntimePins(dependencies: Record<string, string>): Record<string, string>;
15
+ /** Align existing direct declarations and flat installer rules; never add dependencies. */
16
+ export declare function applyRuntimePins(pkg: PkgJson, pins: Record<string, string>): Array<{ name: string, from: string, to: string, field?: 'overrides' | 'resolutions' }>;
13
17
  /** Strip a range operator (`^`, `~`, `>=`, …) so a declared range compares to a bare version. */
14
18
  export declare function baseVersion(range: string): string;
15
19
  /*` tooling
@@ -2,9 +2,9 @@ import{existsSync,lstatSync,readdirSync,readFileSync,writeFileSync}from"node:fs"
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.
5
- `);process.exit(0)}async function resolveTarget(options){const res=await fetch("https://registry.npmjs.org/stacks").catch(()=>null);if(!res||!res.ok)throw Error("Could not reach the npm registry to resolve the target `stacks` version.");const meta=await res.json(),distTags=meta["dist-tags"]??{},versions=meta.versions??{};let version;if(options.version){if(!versions[options.version])throw Error(`stacks@${options.version} was not found on the npm registry.`);version=options.version}else{const tag=options.canary?"canary":"latest",resolved=distTags[tag];if(!resolved)throw Error(`The \`${tag}\` dist-tag is not published for \`stacks\`.`);version=resolved}const metaDeps=versions[version]?.dependencies??{};return{version,metaDeps}}export function baseVersion(range){return range.replace(/^[\^~>=<\s]+/,"").trim()}export function lockstepPackages(metaDeps,target){const lockstep=new Set(["stacks"]);for(const[name,range]of Object.entries(metaDeps))if(name.startsWith("@stacksjs/")&&baseVersion(range)===target)lockstep.add(name);return lockstep}export function applyBumps(pkg,target,lockstep){const changes=[];for(const field of["dependencies","devDependencies"]){const deps=pkg[field];if(!deps)continue;for(const[name,spec]of Object.entries(deps)){if(!lockstep.has(name))continue;const next=`${/^[\^~]/.test(spec)?spec[0]:/^\d/.test(spec)?"":"^"}${target}`;if(spec!==next){changes.push({name,from:spec,to:next});deps[name]=next}}}for(const field of["overrides","resolutions"]){const rules=pkg[field];if(!rules||typeof rules!=="object"||Array.isArray(rules))continue;for(const[name,spec]of Object.entries(rules)){if(!lockstep.has(name)||typeof spec!=="string"||!/^[\^~]?\d/.test(spec))continue;const next=resolveManifestSpec(name,spec,target);if(!next)continue;changes.push({name,from:spec,to:next,field});rules[name]=next}}return changes}export async function upgradeStacksPackages(projectRoot,options){const pkgPath=join(projectRoot,"package.json");if(!existsSync(pkgPath)){console.error("No package.json found - nothing to upgrade.");process.exit(1)}const raw=readFileSync(pkgPath,"utf-8"),pkg=JSON.parse(raw),current=pkg.dependencies?.stacks??pkg.devDependencies?.stacks;if(!hasStacksDependency(pkg))return updateStandalonePackage(projectRoot,options);const{version:target,metaDeps}=await resolveTarget(options).catch((err)=>{console.error(`\u2717 ${err.message}`);process.exit(1)});console.log(`
5
+ `);process.exit(0)}async function resolveTarget(options){const res=await fetch("https://registry.npmjs.org/stacks").catch(()=>null);if(!res||!res.ok)throw Error("Could not reach the npm registry to resolve the target `stacks` version.");const meta=await res.json(),distTags=meta["dist-tags"]??{},versions=meta.versions??{};let version;if(options.version){if(!versions[options.version])throw Error(`stacks@${options.version} was not found on the npm registry.`);version=options.version}else{const tag=options.canary?"canary":"latest",resolved=distTags[tag];if(!resolved)throw Error(`The \`${tag}\` dist-tag is not published for \`stacks\`.`);version=resolved}const metaDeps=versions[version]?.dependencies??{},actions=await fetch(`https://registry.npmjs.org/@stacksjs%2factions/${version}`);if(!actions.ok)throw Error(`Could not resolve the frontend dependencies shipped by @stacksjs/actions@${version}.`);const manifest=await actions.json();if(manifest.version!==version)throw Error("The published actions manifest does not match the requested framework release.");return{version,metaDeps,runtimePins:publishedRuntimePins(manifest.dependencies??{})}}export function publishedRuntimePins(dependencies){const pins={};for(const name of["@stacksjs/stx","@stacksjs/components","bun-plugin-stx"]){const version=dependencies[name];if(version&&/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(version))pins[name]=version}return pins}export function applyRuntimePins(pkg,pins){const changes=[];for(const field of["dependencies","devDependencies","overrides","resolutions"]){const declarations=pkg[field];if(!declarations||typeof declarations!=="object"||Array.isArray(declarations))continue;for(const[name,target]of Object.entries(pins)){const spec=declarations[name];if(typeof spec!=="string"||!/^[\^~]?\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(spec))continue;const next=`${/^[\^~]/.test(spec)?spec[0]:""}${target}`;if(next===spec)continue;changes.push({name,from:spec,to:next,...field==="overrides"||field==="resolutions"?{field}:{}});declarations[name]=next}}return changes}export function baseVersion(range){return range.replace(/^[\^~>=<\s]+/,"").trim()}export function lockstepPackages(metaDeps,target){const lockstep=new Set(["stacks"]);for(const[name,range]of Object.entries(metaDeps))if(name.startsWith("@stacksjs/")&&baseVersion(range)===target)lockstep.add(name);return lockstep}export function applyBumps(pkg,target,lockstep){const changes=[];for(const field of["dependencies","devDependencies"]){const deps=pkg[field];if(!deps)continue;for(const[name,spec]of Object.entries(deps)){if(!lockstep.has(name))continue;const next=`${/^[\^~]/.test(spec)?spec[0]:/^\d/.test(spec)?"":"^"}${target}`;if(spec!==next){changes.push({name,from:spec,to:next});deps[name]=next}}}for(const field of["overrides","resolutions"]){const rules=pkg[field];if(!rules||typeof rules!=="object"||Array.isArray(rules))continue;for(const[name,spec]of Object.entries(rules)){if(!lockstep.has(name)||typeof spec!=="string"||!/^[\^~]?\d/.test(spec))continue;const next=resolveManifestSpec(name,spec,target);if(!next)continue;changes.push({name,from:spec,to:next,field});rules[name]=next}}return changes}export async function upgradeStacksPackages(projectRoot,options){const pkgPath=join(projectRoot,"package.json");if(!existsSync(pkgPath)){console.error("No package.json found - nothing to upgrade.");process.exit(1)}const raw=readFileSync(pkgPath,"utf-8"),pkg=JSON.parse(raw),current=pkg.dependencies?.stacks??pkg.devDependencies?.stacks;if(!hasStacksDependency(pkg))return updateStandalonePackage(projectRoot,options);const{version:target,metaDeps,runtimePins}=await resolveTarget(options).catch((err)=>{console.error(`\u2717 ${err.message}`);process.exit(1)});console.log(`
6
6
  Upgrading the Stacks framework${options.canary?" (canary)":""} \u2192 ${target}`);if(current)console.log(` current \`stacks\` constraint: ${current}
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 reported=changes.map((change)=>({...change,label:change.field?`${change.name} (${change.field})`:change.name})),width=Math.max(...reported.map((c)=>c.label.length));for(const c of reported)console.log(` ${c.label.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}
7
+ `);const lockstep=lockstepPackages(metaDeps,target),changes=[...applyBumps(pkg,target,lockstep),...applyRuntimePins(pkg,runtimePins)],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 reported=changes.map((change)=>({...change,label:change.field?`${change.name} (${change.field})`:change.name})),width=Math.max(...reported.map((c)=>c.label.length));for(const c of reported)console.log(` ${c.label.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
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(`
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.285",
5
+ "version": "0.75.288",
6
6
  "description": "The Stacks actions.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -66,55 +66,55 @@
66
66
  "prepublishOnly": "bun run build"
67
67
  },
68
68
  "dependencies": {
69
- "@stacksjs/action-runner": "0.75.285",
70
- "@stacksjs/ai": "0.75.285",
71
- "@stacksjs/analytics": "0.75.285",
72
- "@stacksjs/api": "0.75.285",
73
- "@stacksjs/auth": "0.75.285",
74
- "@stacksjs/build": "0.75.285",
69
+ "@stacksjs/action-runner": "0.75.288",
70
+ "@stacksjs/ai": "0.75.288",
71
+ "@stacksjs/analytics": "0.75.288",
72
+ "@stacksjs/api": "0.75.288",
73
+ "@stacksjs/auth": "0.75.288",
74
+ "@stacksjs/build": "0.75.288",
75
75
  "@stacksjs/bumpx": "^0.2.26",
76
76
  "@stacksjs/bunpress": "^0.2.15",
77
- "@stacksjs/cli": "0.75.285",
78
- "@stacksjs/cloud": "0.75.285",
79
- "@stacksjs/cms": "0.75.285",
80
- "@stacksjs/components": "0.2.676",
81
- "@stacksjs/config": "0.75.285",
82
- "@stacksjs/database": "0.75.285",
83
- "@stacksjs/desktop-build": "0.75.285",
84
- "@stacksjs/dns": "0.75.285",
85
- "@stacksjs/enums": "0.75.285",
86
- "@stacksjs/env": "0.75.285",
87
- "@stacksjs/error-handling": "0.75.285",
88
- "@stacksjs/github": "0.75.285",
77
+ "@stacksjs/cli": "0.75.288",
78
+ "@stacksjs/cloud": "0.75.288",
79
+ "@stacksjs/cms": "0.75.288",
80
+ "@stacksjs/components": "0.2.679",
81
+ "@stacksjs/config": "0.75.288",
82
+ "@stacksjs/database": "0.75.288",
83
+ "@stacksjs/desktop-build": "0.75.288",
84
+ "@stacksjs/dns": "0.75.288",
85
+ "@stacksjs/enums": "0.75.288",
86
+ "@stacksjs/env": "0.75.288",
87
+ "@stacksjs/error-handling": "0.75.288",
88
+ "@stacksjs/github": "0.75.288",
89
89
  "@stacksjs/gitit": "^0.2.5",
90
- "@stacksjs/i18n": "0.75.285",
91
- "@stacksjs/image": "0.75.285",
92
- "@stacksjs/logging": "0.75.285",
90
+ "@stacksjs/i18n": "0.75.288",
91
+ "@stacksjs/image": "0.75.288",
92
+ "@stacksjs/logging": "0.75.288",
93
93
  "@stacksjs/logsmith": "^0.2.11",
94
- "@stacksjs/mobile": "0.75.285",
95
- "@stacksjs/orm": "0.75.285",
96
- "@stacksjs/path": "0.75.285",
97
- "@stacksjs/payments": "0.75.285",
98
- "@stacksjs/queue": "0.75.285",
99
- "@stacksjs/registry": "0.75.285",
100
- "@stacksjs/router": "0.75.285",
94
+ "@stacksjs/mobile": "0.75.288",
95
+ "@stacksjs/orm": "0.75.288",
96
+ "@stacksjs/path": "0.75.288",
97
+ "@stacksjs/payments": "0.75.288",
98
+ "@stacksjs/queue": "0.75.288",
99
+ "@stacksjs/registry": "0.75.288",
100
+ "@stacksjs/router": "0.75.288",
101
101
  "@stacksjs/rpx": "^0.11.54",
102
- "@stacksjs/scheduler": "0.75.285",
103
- "@stacksjs/search-engine": "0.75.285",
104
- "@stacksjs/security": "0.75.285",
105
- "@stacksjs/server": "0.75.285",
106
- "@stacksjs/sites": "0.75.285",
107
- "@stacksjs/storage": "0.75.285",
108
- "@stacksjs/strings": "0.75.285",
109
- "@stacksjs/stx": "0.2.676",
102
+ "@stacksjs/scheduler": "0.75.288",
103
+ "@stacksjs/search-engine": "0.75.288",
104
+ "@stacksjs/security": "0.75.288",
105
+ "@stacksjs/server": "0.75.288",
106
+ "@stacksjs/sites": "0.75.288",
107
+ "@stacksjs/storage": "0.75.288",
108
+ "@stacksjs/strings": "0.75.288",
109
+ "@stacksjs/stx": "0.2.679",
110
110
  "@stacksjs/tlsx": "^0.13.19",
111
111
  "@stacksjs/ts-cloud": "^0.16.58",
112
112
  "@stacksjs/ts-md": "^0.1.1",
113
- "@stacksjs/tunnel": "0.75.285",
114
- "@stacksjs/types": "0.75.285",
115
- "@stacksjs/utils": "0.75.285",
116
- "@stacksjs/whois": "0.75.285",
117
- "bun-plugin-stx": "0.2.676"
113
+ "@stacksjs/tunnel": "0.75.288",
114
+ "@stacksjs/types": "0.75.288",
115
+ "@stacksjs/utils": "0.75.288",
116
+ "@stacksjs/whois": "0.75.288",
117
+ "bun-plugin-stx": "0.2.679"
118
118
  },
119
119
  "peerDependencies": {
120
120
  "craft-native": ">=0.0.319",
@@ -129,7 +129,7 @@
129
129
  }
130
130
  },
131
131
  "devDependencies": {
132
- "@stacksjs/validation": "0.75.285",
132
+ "@stacksjs/validation": "0.75.288",
133
133
  "better-dx": "^0.2.24"
134
134
  }
135
135
  }