@stacksjs/actions 0.75.173 → 0.75.175
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/android-config.d.ts +32 -0
- package/dist/build/android-config.js +1 -1
- package/dist/build/android-icons.d.ts +15 -0
- package/dist/build/android-icons.js +2 -0
- package/dist/build/android.js +1 -1
- package/dist/build/ios-toolchain.d.ts +45 -0
- package/dist/build/ios-toolchain.js +2 -0
- package/dist/build/ios.js +1 -1
- package/dist/build/native-screens.d.ts +3 -3
- package/dist/build/native-screens.js +1 -1
- package/dist/bump.js +4 -2
- package/dist/github-release.d.ts +65 -0
- package/dist/github-release.js +11 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -1
- package/package.json +39 -39
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import type { AndroidMobileConfig } from '@stacksjs/types';
|
|
2
|
+
/** Which floored features this android config uses. */
|
|
3
|
+
export declare function androidCraftFeatures(config: AndroidMobileConfig, options?: { appBundle?: boolean }): AndroidCraftFeature[];
|
|
4
|
+
/** Refuse to generate an app the resolved Craft would build without what config/mobile.ts asks for. */
|
|
5
|
+
export declare function assertCraftSupportsAndroid(config: AndroidMobileConfig, version: string, source: string, options?: { appBundle?: boolean }): void;
|
|
2
6
|
export declare function toCraftAndroidConfig(config: AndroidMobileConfig): CraftAndroidConfig;
|
|
3
7
|
export declare function validateAndroidMobileConfig(config: AndroidMobileConfig): void;
|
|
8
|
+
/**
|
|
9
|
+
* The first craft-native release whose Android template honours a feature
|
|
10
|
+
* of `config/mobile.ts`'s android section, the Android sibling of
|
|
11
|
+
* ios-config.ts's CRAFT_CAPABILITY_FLOORS. An older template copies the
|
|
12
|
+
* keys into craft.config.json and has no Kotlin reading them: the build
|
|
13
|
+
* succeeds and the app opens one bare screen, with no tabs, no Health
|
|
14
|
+
* Connect reads and no background refresh.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CRAFT_ANDROID_FLOORS: {
|
|
17
|
+
/** The native app shell: tabs, paths to screens, the app script, synchronous stores. */
|
|
18
|
+
nativeShell: '0.0.169';
|
|
19
|
+
/** Health Connect for native screens (workouts, sleep, heart rate, mindful, nutrition). */
|
|
20
|
+
healthConnect: '0.0.169';
|
|
21
|
+
/** WorkManager background refresh for the app script. */
|
|
22
|
+
backgroundRefresh: '0.0.169';
|
|
23
|
+
/** `bundleRelease` (an AAB for Google Play) and release signing from the environment. */
|
|
24
|
+
appBundle: '0.0.169'
|
|
25
|
+
};
|
|
4
26
|
export declare interface CraftAndroidConfig {
|
|
5
27
|
[key: string]: unknown
|
|
6
28
|
appName: string
|
|
@@ -10,11 +32,21 @@ export declare interface CraftAndroidConfig {
|
|
|
10
32
|
minSdk?: number
|
|
11
33
|
targetSdk?: number
|
|
12
34
|
darkMode?: boolean
|
|
35
|
+
appearance?: AndroidMobileConfig['appearance']
|
|
13
36
|
backgroundColor?: string
|
|
37
|
+
backgroundColorDark?: string
|
|
14
38
|
devServerURL?: string
|
|
15
39
|
trustedOrigins?: string[]
|
|
16
40
|
urlSchemes?: string[]
|
|
17
41
|
appIconPath?: string
|
|
18
42
|
googleServicesFile?: string
|
|
19
43
|
enableHealthConnect?: boolean
|
|
44
|
+
renderer?: AndroidMobileConfig['renderer']
|
|
45
|
+
nativeScreens?: Record<string, string>
|
|
46
|
+
nativeApp?: string
|
|
47
|
+
tabs?: AndroidMobileConfig['tabs']
|
|
48
|
+
backgroundRefresh?: AndroidMobileConfig['backgroundRefresh']
|
|
49
|
+
healthConnect?: AndroidMobileConfig['healthConnect']
|
|
50
|
+
privacyPolicyURL?: string
|
|
20
51
|
}
|
|
52
|
+
export type AndroidCraftFeature = keyof typeof CRAFT_ANDROID_FLOORS;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{normalizeMobileUrl}from"./ios-config";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",localNetwork:"enableLocalNetwork"};export function toCraftAndroidConfig(config){const devServerURL=normalizeMobileUrl(config.url),trustedOrigins=new Set(config.trustedOrigins??[]);if(devServerURL)trustedOrigins.add(new URL(devServerURL).origin);const craft={appName:config.appName,packageName:config.packageName,version:config.version,versionCode:config.versionCode,minSdk:config.minSdk,targetSdk:config.targetSdk,darkMode:config.darkMode,backgroundColor:config.backgroundColor,devServerURL,trustedOrigins:[...trustedOrigins],urlSchemes:config.urlSchemes,appIconPath:config.appIcon,googleServicesFile:config.googleServicesFile};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;return craft}export function validateAndroidMobileConfig(config){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.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.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");for(const scheme of config.urlSchemes??[])if(!/^[a-z][a-z0-9+.-]*$/i.test(scheme))throw Error(`Invalid Android URL scheme: ${scheme}`)}
|
|
1
|
+
import{NATIVE_APP_ROUTE,normalizeMobileUrl}from"./ios-config";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",localNetwork:"enableLocalNetwork"};export const CRAFT_ANDROID_FLOORS={nativeShell:"0.0.169",healthConnect:"0.0.169",backgroundRefresh:"0.0.169",appBundle:"0.0.169"};export function androidCraftFeatures(config,options={}){const features=[];if(config.renderer==="native"&&Object.keys(config.nativeScreens??{}).length)features.push("nativeShell");if(config.capabilities?.healthConnect&&config.renderer==="native")features.push("healthConnect");if(config.backgroundRefresh?.enabled)features.push("backgroundRefresh");if(options.appBundle)features.push("appBundle");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={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,appIconPath:config.appIcon,googleServicesFile:config.googleServicesFile,renderer:config.renderer,nativeScreens:native?config.nativeScreens: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};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.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.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}`)}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app's icons as vectors for the Android app, which has no SF Symbols:
|
|
3
|
+
* every Lucide icon stx maps to a symbol (`LUCIDE_TO_SF`), by its own name and
|
|
4
|
+
* by the symbol, so an `<Icon>`, a tab and a bar button draw the icon the web
|
|
5
|
+
* shows. Lucide comes from the app's `@iconify-json/lucide`; an app without it
|
|
6
|
+
* gets no table, and Craft draws Android's nearest glyphs instead.
|
|
7
|
+
*/
|
|
8
|
+
export declare function androidIconTable(root: string, lucideToSf: Record<string, string>): AndroidIconTable | null;
|
|
9
|
+
/** Writes the table next to the generated project and answers its path, or null. */
|
|
10
|
+
export declare function writeAndroidIconTable(root: string, output: string, lucideToSf: Record<string, string>): string | null;
|
|
11
|
+
export declare interface AndroidIconTable {
|
|
12
|
+
viewBox: number
|
|
13
|
+
strokeWidth: number
|
|
14
|
+
icons: Record<string, string>
|
|
15
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync,readFileSync,writeFileSync}from"node:fs";import{createRequire}from"node:module";import{join}from"node:path";export function androidIconTable(root,lucideToSf){let collection;try{const file=createRequire(join(root,"package.json")).resolve("@iconify-json/lucide/icons.json");collection=JSON.parse(readFileSync(file,"utf8"))}catch{return null}const bodyOf=(name)=>{const icon=collection.icons?.[name];if(icon?.body)return icon.body;const parent=collection.aliases?.[name]?.parent;return parent?collection.icons?.[parent]?.body:void 0},icons={};for(const[name,symbol]of Object.entries(lucideToSf)){const body=bodyOf(name);if(!body)continue;icons[name]=body;icons[symbol]??=body}return{viewBox:collection.width??24,strokeWidth:2,icons}}export function writeAndroidIconTable(root,output,lucideToSf){const table=androidIconTable(root,lucideToSf);if(!table||!Object.keys(table.icons).length)return null;const file=join(output,"craft-icons.json");writeFileSync(file,`${JSON.stringify(table)}
|
|
2
|
+
`);return existsSync(file)?file:null}
|
package/dist/build/android.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{existsSync,readFileSync,writeFileSync}from"node:fs";import{pathToFileURL}from"node:url";import process from"node:process";import{log}from"@stacksjs/cli";import{projectPath,storagePath}from"@stacksjs/path";import{craftBuilderSpecifier}from"./craft-entry";import{resolveCraftBuilderProvenance}from"./craft-provenance";import{toCraftAndroidConfig,validateAndroidMobileConfig}from"./android-config";import{resolveMobilePath}from"./ios-config";process.exitCode=1;
|
|
1
|
+
import{existsSync,readFileSync,writeFileSync}from"node:fs";import{pathToFileURL}from"node:url";import process from"node:process";import{log}from"@stacksjs/cli";import{projectPath,storagePath}from"@stacksjs/path";import{craftBuilderSpecifier,craftBuilderVersion}from"./craft-entry";import{resolveCraftBuilderProvenance}from"./craft-provenance";import{assertCraftSupportsAndroid,toCraftAndroidConfig,validateAndroidMobileConfig}from"./android-config";import{resolveMobilePath}from"./ios-config";import{compileNativeScreens}from"./native-screens";import{writeAndroidIconTable}from"./android-icons";process.exitCode=1;function craftAndroidEntry(){const explicit=process.env.CRAFT_ANDROID_SRC;return explicit?pathToFileURL(explicit).href:craftBuilderSpecifier("android",projectPath())}async function loadCraftAndroidBuilder(){try{return await import(craftAndroidEntry())}catch(error){throw Error("Craft Android builder is unavailable. Install the current craft-native package or set CRAFT_ANDROID_SRC to packages/android/src/index.ts in a Craft checkout.",{cause:error})}}const configPath=projectPath("config/mobile.ts");if(!existsSync(configPath))throw Error("Missing config/mobile.ts. Add an android section before running `buddy build:android`.");const configModule=await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`),config=configModule.default.android;if(!config)throw Error("config/mobile.ts must define an android section");validateAndroidMobileConfig(config);const output=resolveMobilePath(projectPath(),config.output)??storagePath("framework/mobile/android"),webAssets=resolveMobilePath(projectPath(),config.webAssets),fallbackWebAssets=resolveMobilePath(projectPath(),config.fallbackWebAssets),craftConfig=toCraftAndroidConfig(config);craftConfig.appIconPath=resolveMobilePath(projectPath(),config.appIcon);craftConfig.googleServicesFile=resolveMobilePath(projectPath(),config.googleServicesFile);if(process.env.NODE_ENV==="production"&&config.capabilities?.pushNotifications&&!craftConfig.googleServicesFile)throw Error("Production Android push notifications require android.googleServicesFile in config/mobile.ts");const appBundle=process.env.STACKS_ANDROID_BUNDLE==="1",release=appBundle||process.env.NODE_ENV==="production",native=config.renderer==="native",nativeBundle=native?await compileNativeScreens(config,projectPath(),output,void 0,"android"):null;if(nativeBundle){for(const diagnostic of nativeBundle.diagnostics??[])log.warn(`${diagnostic.file?`${diagnostic.file}: `:""}${diagnostic.message}`);log.info(`Compiled ${Object.keys(config.nativeScreens??{}).length} native screen path(s) into ${nativeBundle.outFile}`)}const{LUCIDE_TO_SF}=native?await import("@stacksjs/stx/native"):{LUCIDE_TO_SF:{}},iconsPath=native?writeAndroidIconTable(projectPath(),output,LUCIDE_TO_SF):null;if(native&&!iconsPath)log.warn("No @iconify-json/lucide in the app: Android draws its own glyphs for the screens' icons.");const builder=await loadCraftAndroidBuilder(),craftVersion=craftBuilderVersion(craftAndroidEntry(),projectPath());if(craftVersion)assertCraftSupportsAndroid(config,craftVersion.version,craftVersion.manifest,{appBundle});else log.warn(`Could not read the craft-native version behind ${craftAndroidEntry()}, so the build cannot check that it supports what config/mobile.ts asks for.`);await builder.init({name:config.appName,packageName:config.packageName,output,config:craftConfig});await builder.build(native?{output,nativeBundlePath:nativeBundle.outFile,iconsPath:iconsPath??void 0,release,bundle:appBundle,compile:process.env.STACKS_ANDROID_SKIP_GRADLE!=="1"}:{output,htmlPath:webAssets??fallbackWebAssets,devServer:craftConfig.devServerURL,release,bundle:appBundle,compile:process.env.STACKS_ANDROID_SKIP_GRADLE!=="1"});const sourceRevision=Bun.spawnSync(["git","rev-parse","HEAD"],{cwd:projectPath()}).stdout.toString().trim(),generatedConfig=JSON.parse(readFileSync(`${output}/craft.config.json`,"utf8"));writeFileSync(`${output}/stacks-mobile.json`,`${JSON.stringify({schemaVersion:"1.0.0",platform:"android",sourceRevision,source:native?{kind:"native",site:craftConfig.devServerURL}:webAssets?{kind:"bundled",path:webAssets}:{kind:"remote",url:craftConfig.devServerURL,fallback:fallbackWebAssets?{kind:"bundled",path:fallbackWebAssets}:void 0},capabilities:config.capabilities??{},builder:resolveCraftBuilderProvenance(process.env.CRAFT_ANDROID_SRC),craft:generatedConfig},null,2)}
|
|
2
2
|
`);log.success(`Built the Craft Android project in ${output}`);await log.flush();process.exitCode=0;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where Pantry keeps installed packages, most specific first: the project's
|
|
3
|
+
* own `pantry/`, the user install (`pantry install --user`, which `setup:ios`
|
|
4
|
+
* uses), then a system-wide one.
|
|
5
|
+
*/
|
|
6
|
+
export declare function pantryPackageRoots(projectRoot: string, env?: Env): string[];
|
|
7
|
+
/**
|
|
8
|
+
* The highest installed version of `pkg` under `roots` that satisfies
|
|
9
|
+
* `constraint` and ships `program`, searching the roots in order.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findPantryProgram(pkg: string, constraint: string, program: string, roots: string[], fs?: Filesystem): PantryTool | null;
|
|
12
|
+
/**
|
|
13
|
+
* `iosDependencies` from config/deps.ts over the framework default, so an app
|
|
14
|
+
* that pins one tool still gets the others.
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadIosDependencies(projectRoot: string): Promise<Record<string, string>>;
|
|
17
|
+
/** `pantry install` arguments for the iOS tools, into the user install. */
|
|
18
|
+
export declare function pantryInstallArgs(dependencies: Record<string, string>): string[];
|
|
19
|
+
/** The Pantry executable: PANTRY_BIN, the project's, the user's, else `pantry` on PATH. */
|
|
20
|
+
export declare function pantryExecutable(projectRoot: string, env?: Env, exists?: (path: string) => boolean): string;
|
|
21
|
+
export declare function missingToolMessage(pkg: string, constraint: string, program: string): string;
|
|
22
|
+
/** xcodegen from Pantry, or an {@link IosToolchainError} that says how to install it. */
|
|
23
|
+
export declare function resolveXcodegen(projectRoot: string, options?: { env?: Env, fs?: Filesystem }): Promise<PantryTool>;
|
|
24
|
+
/*.xcodeproj` from its project.yml with the given xcodegen. */
|
|
25
|
+
export declare function generateXcodeProject(xcodegen: PantryTool, output: string): void;
|
|
26
|
+
/** The selected Xcode's developer directory, or null when there is none. */
|
|
27
|
+
export declare function xcodeDeveloperDir(): string | null;
|
|
28
|
+
export declare const XCODEGEN_PACKAGE: 'github.com/yonaskolb/XcodeGen';
|
|
29
|
+
/** The tools `build:ios` runs, when config/deps.ts declares no `iosDependencies`. */
|
|
30
|
+
export declare const DEFAULT_IOS_DEPENDENCIES: Readonly<Record<string, string>>;
|
|
31
|
+
export declare const XCODE_NOTE: 'Xcode itself does not come from Pantry: install it from the Mac App Store or developer.apple.com, then select it with `sudo xcode-select --switch /Applications/Xcode.app`.';
|
|
32
|
+
export declare interface PantryTool {
|
|
33
|
+
pkg: string
|
|
34
|
+
constraint: string
|
|
35
|
+
version: string
|
|
36
|
+
path: string
|
|
37
|
+
}
|
|
38
|
+
declare interface Filesystem {
|
|
39
|
+
exists: (path: string) => boolean
|
|
40
|
+
list: (path: string) => string[]
|
|
41
|
+
}
|
|
42
|
+
declare type Env = Record<string, string | undefined>;
|
|
43
|
+
export declare class IosToolchainError extends Error {
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{existsSync,readdirSync}from"node:fs";import{homedir}from"node:os";import{join}from"node:path";import process from"node:process";import{pathToFileURL}from"node:url";export const XCODEGEN_PACKAGE="github.com/yonaskolb/XcodeGen",DEFAULT_IOS_DEPENDENCIES={[XCODEGEN_PACKAGE]:"^2.46.0"},XCODE_NOTE="Xcode itself does not come from Pantry: install it from the Mac App Store or developer.apple.com, then select it with `sudo xcode-select --switch /Applications/Xcode.app`.";export class IosToolchainError extends Error{name="IosToolchainError"}const realFilesystem={exists:existsSync,list:(path)=>{try{return readdirSync(path)}catch{return[]}}};export function pantryPackageRoots(projectRoot,env=process.env){const home=env.HOME||homedir(),data=env.XDG_DATA_HOME||join(home,".local","share");return[join(projectRoot,"pantry"),join(data,"pantry","global","packages"),"/usr/local/packages"]}export function findPantryProgram(pkg,constraint,program,roots,fs=realFilesystem){for(const root of roots){const versions=fs.list(join(root,pkg)).filter((entry)=>/^v\d+\.\d+\.\d+$/.test(entry)).map((entry)=>entry.slice(1)).filter((version)=>Bun.semver.satisfies(version,constraint)).sort((a,b)=>Bun.semver.order(b,a));for(const version of versions){const path=join(root,pkg,`v${version}`,"bin",program);if(fs.exists(path))return{pkg,constraint,version,path}}}return null}export async function loadIosDependencies(projectRoot){const depsPath=join(projectRoot,"config","deps.ts");if(!existsSync(depsPath))return{...DEFAULT_IOS_DEPENDENCIES};const deps=await import(pathToFileURL(depsPath).href);return{...DEFAULT_IOS_DEPENDENCIES,...deps.iosDependencies??{}}}export function pantryInstallArgs(dependencies){return["install","--user","--no-save",...Object.entries(dependencies).map(([pkg,constraint])=>`${pkg}@${constraint}`)]}export function pantryExecutable(projectRoot,env=process.env,exists=existsSync){const home=env.HOME||homedir(),data=env.XDG_DATA_HOME||join(home,".local","share");return[env.PANTRY_BIN,join(projectRoot,"pantry",".bin","pantry"),join(home,".local","bin","pantry"),join(data,"pantry","global","bin","pantry")].find((candidate)=>candidate&&exists(candidate))??"pantry"}export function missingToolMessage(pkg,constraint,program){return`${program} ${constraint} is not installed through Pantry, and the iOS build only runs the version config/deps.ts pins (\`iosDependencies\`, else the framework default). Run \`buddy setup:ios\`, or \`pantry install --user ${pkg}@${constraint}\`, then build again. `+XCODE_NOTE}export async function resolveXcodegen(projectRoot,options={}){const constraint=(await loadIosDependencies(projectRoot))[XCODEGEN_PACKAGE],tool=findPantryProgram(XCODEGEN_PACKAGE,constraint,"xcodegen",pantryPackageRoots(projectRoot,options.env),options.fs);if(!tool)throw new IosToolchainError(missingToolMessage(XCODEGEN_PACKAGE,constraint,"xcodegen"));return tool}export function generateXcodeProject(xcodegen,output){const result=Bun.spawnSync([xcodegen.path,"generate","--quiet"],{cwd:output,stdout:"pipe",stderr:"pipe"});if(result.exitCode!==0){const detail=`${result.stderr.toString()}${result.stdout.toString()}`.trim();throw new IosToolchainError(`xcodegen ${xcodegen.version} (${xcodegen.path}) could not generate the Xcode project in ${output}${detail?`:
|
|
2
|
+
${detail}`:""}`)}}export function xcodeDeveloperDir(){try{const result=Bun.spawnSync(["xcode-select","--print-path"],{stdout:"pipe",stderr:"ignore"}),path=result.stdout.toString().trim();return result.exitCode===0&&path&&!path.includes("CommandLineTools")?path:null}catch{return null}}
|
package/dist/build/ios.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{existsSync,readFileSync,writeFileSync}from"node:fs";import{pathToFileURL}from"node:url";import process from"node:process";import{log}from"@stacksjs/cli";import{projectPath,storagePath}from"@stacksjs/path";import{craftBuilderSpecifier,craftBuilderVersion}from"./craft-entry";import{resolveCraftBuilderProvenance}from"./craft-provenance";import{assertCraftSupportsCapabilities,portablePaths,resolveMobilePath,toCraftIosConfig,validateIosMobileConfig,writeIosActivityTypes}from"./ios-config";import{compileNativeScreens}from"./native-screens";import{writeIosShellSupport}from"./ios-shell";process.exitCode=1;function craftIosEntry(){const explicit=process.env.CRAFT_IOS_SRC;return explicit?pathToFileURL(explicit).href:craftBuilderSpecifier("ios",projectPath())}async function loadCraftIosBuilder(){try{return await import(craftIosEntry())}catch(error){throw Error("Craft iOS builder is unavailable. Install the current craft-native package or set CRAFT_IOS_SRC to packages/ios/src/index.ts in a Craft checkout.",{cause:error})}}const configPath=projectPath("config/mobile.ts");if(!existsSync(configPath))throw Error("Missing config/mobile.ts. Add an ios section before running `buddy build:ios`.");const configModule=await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`),mobile=configModule.default,config=mobile.ios;validateIosMobileConfig(config);if(config.swipeNavigation===!0&&config.swipeBack!=="webview")log.warn("ios.swipeNavigation turns on WebKit's edge swipe alongside the stx router's own. Remove it, or set ios.swipeBack: 'webview' for an app not on the router.");const output=resolveMobilePath(projectPath(),config.output)??storagePath("framework/mobile/ios"),webAssets=resolveMobilePath(projectPath(),config.webAssets),fallbackWebAssets=resolveMobilePath(projectPath(),config.fallbackWebAssets),craftConfig=toCraftIosConfig(config);craftConfig.appIconPath=resolveMobilePath(projectPath(),config.appIcon);craftConfig.splashImagePath=resolveMobilePath(projectPath(),config.splash?.image);craftConfig.splashImagePathDark=resolveMobilePath(projectPath(),config.splash?.imageDark);const nativeBundle=await compileNativeScreens(config,projectPath(),output);if(nativeBundle){craftConfig.nativeBundle=nativeBundle.outFile;for(const diagnostic of nativeBundle.diagnostics??[])log.warn(`${diagnostic.file?`${diagnostic.file}: `:""}${diagnostic.message}`);log.info(`Compiled ${Object.keys(config.nativeScreens??{}).length} native screen path(s) into ${nativeBundle.outFile}`)}const builder=await loadCraftIosBuilder(),craftVersion=craftBuilderVersion(craftIosEntry(),projectPath());if(craftVersion)assertCraftSupportsCapabilities(config.capabilities,craftVersion.version,craftVersion.manifest);else log.warn(`Could not read the craft-native version behind ${craftIosEntry()}, so the build cannot check that it supports every capability config/mobile.ts turns on.`);await builder.init({name:config.appName,bundleId:config.bundleId,teamId:config.teamId,output,config:craftConfig});const native=config.renderer==="native";if(native&&!nativeBundle)throw Error("ios.renderer 'native' needs native screens to draw: list them in ios.nativeScreens");await builder.build(native?{output,nativeBundlePath:nativeBundle.outFile,generateProject
|
|
1
|
+
import{existsSync,readFileSync,writeFileSync}from"node:fs";import{pathToFileURL}from"node:url";import process from"node:process";import{log}from"@stacksjs/cli";import{projectPath,storagePath}from"@stacksjs/path";import{craftBuilderSpecifier,craftBuilderVersion}from"./craft-entry";import{resolveCraftBuilderProvenance}from"./craft-provenance";import{assertCraftSupportsCapabilities,portablePaths,resolveMobilePath,toCraftIosConfig,validateIosMobileConfig,writeIosActivityTypes}from"./ios-config";import{generateXcodeProject,resolveXcodegen}from"./ios-toolchain";import{compileNativeScreens}from"./native-screens";import{writeIosShellSupport}from"./ios-shell";process.exitCode=1;function craftIosEntry(){const explicit=process.env.CRAFT_IOS_SRC;return explicit?pathToFileURL(explicit).href:craftBuilderSpecifier("ios",projectPath())}async function loadCraftIosBuilder(){try{return await import(craftIosEntry())}catch(error){throw Error("Craft iOS builder is unavailable. Install the current craft-native package or set CRAFT_IOS_SRC to packages/ios/src/index.ts in a Craft checkout.",{cause:error})}}const configPath=projectPath("config/mobile.ts");if(!existsSync(configPath))throw Error("Missing config/mobile.ts. Add an ios section before running `buddy build:ios`.");const configModule=await import(`${pathToFileURL(configPath).href}?t=${Date.now()}`),mobile=configModule.default,config=mobile.ios;validateIosMobileConfig(config);if(config.swipeNavigation===!0&&config.swipeBack!=="webview")log.warn("ios.swipeNavigation turns on WebKit's edge swipe alongside the stx router's own. Remove it, or set ios.swipeBack: 'webview' for an app not on the router.");const output=resolveMobilePath(projectPath(),config.output)??storagePath("framework/mobile/ios"),generateProject=process.env.STACKS_IOS_SKIP_XCODEGEN!=="1",xcodegen=generateProject?await resolveXcodegen(projectPath()):null,webAssets=resolveMobilePath(projectPath(),config.webAssets),fallbackWebAssets=resolveMobilePath(projectPath(),config.fallbackWebAssets),craftConfig=toCraftIosConfig(config);craftConfig.appIconPath=resolveMobilePath(projectPath(),config.appIcon);craftConfig.splashImagePath=resolveMobilePath(projectPath(),config.splash?.image);craftConfig.splashImagePathDark=resolveMobilePath(projectPath(),config.splash?.imageDark);const nativeBundle=await compileNativeScreens(config,projectPath(),output);if(nativeBundle){craftConfig.nativeBundle=nativeBundle.outFile;for(const diagnostic of nativeBundle.diagnostics??[])log.warn(`${diagnostic.file?`${diagnostic.file}: `:""}${diagnostic.message}`);log.info(`Compiled ${Object.keys(config.nativeScreens??{}).length} native screen path(s) into ${nativeBundle.outFile}`)}const builder=await loadCraftIosBuilder(),craftVersion=craftBuilderVersion(craftIosEntry(),projectPath());if(craftVersion)assertCraftSupportsCapabilities(config.capabilities,craftVersion.version,craftVersion.manifest);else log.warn(`Could not read the craft-native version behind ${craftIosEntry()}, so the build cannot check that it supports every capability config/mobile.ts turns on.`);await builder.init({name:config.appName,bundleId:config.bundleId,teamId:config.teamId,output,config:craftConfig});const native=config.renderer==="native";if(native&&!nativeBundle)throw Error("ios.renderer 'native' needs native screens to draw: list them in ios.nativeScreens");await builder.build(native?{output,nativeBundlePath:nativeBundle.outFile,generateProject:!1}:{output,htmlPath:webAssets??fallbackWebAssets,devServer:craftConfig.devServerURL,generateProject:!1});if(xcodegen){generateXcodeProject(xcodegen,output);log.info(`Generated the Xcode project with xcodegen ${xcodegen.version} from Pantry`)}writeIosShellSupport(output,config);const activityTypes=writeIosActivityTypes(output,mobile);if(activityTypes)log.info(`Declared ${activityTypes.length} activity types, so a tapped Spotlight or Siri entry reaches the app`);const sourceRevision=Bun.spawnSync(["git","rev-parse","HEAD"],{cwd:projectPath()}).stdout.toString().trim(),craftConfigPath=`${output}/craft.config.json`,generatedConfig=portablePaths(JSON.parse(readFileSync(craftConfigPath,"utf8")),output,projectPath());writeFileSync(craftConfigPath,`${JSON.stringify(generatedConfig,null,2)}
|
|
2
2
|
`);writeFileSync(`${output}/stacks-mobile.json`,`${JSON.stringify(portablePaths({schemaVersion:"1.0.0",platform:"ios",sourceRevision,source:native?{kind:"native",site:craftConfig.devServerURL}:webAssets?{kind:"bundled",path:webAssets}:{kind:"remote",url:craftConfig.devServerURL,fallback:fallbackWebAssets?{kind:"bundled",path:fallbackWebAssets}:void 0},capabilities:config.capabilities??{},builder:resolveCraftBuilderProvenance(process.env.CRAFT_IOS_SRC),craft:generatedConfig},output,projectPath()),null,2)}
|
|
3
3
|
`);log.success(`Built the Craft iOS project in ${output}`);await log.flush();process.exitCode=0;
|
|
@@ -4,16 +4,16 @@ import type { IosMobileConfig } from '@stacksjs/types';
|
|
|
4
4
|
* `nativeScreens`, in `nativeScreensDir` (`resources/native` by default).
|
|
5
5
|
* A screen named twice, for two paths, is compiled once.
|
|
6
6
|
*/
|
|
7
|
-
export declare function nativeScreenFiles(config: Pick<IosMobileConfig, 'nativeScreens' | 'nativeScreensDir'>, root: string): Record<string, string>;
|
|
7
|
+
export declare function nativeScreenFiles(config: Pick<IosMobileConfig, 'nativeScreens' | 'nativeScreensDir'>, root: string, section?: 'ios' | 'android'): Record<string, string>;
|
|
8
8
|
/** The app script's file (`nativeApp`), or null when the app has none. */
|
|
9
|
-
export declare function nativeAppFile(config: Pick<IosMobileConfig, 'nativeApp'>, root: string): string | null;
|
|
9
|
+
export declare function nativeAppFile(config: Pick<IosMobileConfig, 'nativeApp'>, root: string, section?: 'ios' | 'android'): string | null;
|
|
10
10
|
/**
|
|
11
11
|
* Compiles the app's native screens into the one bundle Craft runs them from,
|
|
12
12
|
* and answers its path; nothing when the app has none. The bundle goes in the
|
|
13
13
|
* generated project's own folder, so a rebuild replaces it and git never sees
|
|
14
14
|
* it.
|
|
15
15
|
*/
|
|
16
|
-
export declare function compileNativeScreens(config: Pick<IosMobileConfig, 'nativeScreens' | 'nativeScreensDir' | 'nativeApp'>, root: string, output: string, compile?: CompileNativeBundle): Promise<{ outFile: string, diagnostics: NativeBundleResult['diagnostics'] } | null>;
|
|
16
|
+
export declare function compileNativeScreens(config: Pick<IosMobileConfig, 'nativeScreens' | 'nativeScreensDir' | 'nativeApp'>, root: string, output: string, compile?: CompileNativeBundle, section?: 'ios' | 'android'): Promise<{ outFile: string, diagnostics: NativeBundleResult['diagnostics'] } | null>;
|
|
17
17
|
/** What stx's native compiler answers (`@stacksjs/stx/native`). */
|
|
18
18
|
declare interface NativeBundleResult {
|
|
19
19
|
outFile: string
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync,mkdirSync}from"node:fs";import{join,resolve}from"node:path";export function nativeScreenFiles(config,root){const dir=resolve(root,config.nativeScreensDir??"resources/native"),files={};for(const name of Object.values(config.nativeScreens??{})){if(files[name])continue;const file=join(dir,`${name}.stx`);if(!existsSync(file))throw Error(
|
|
1
|
+
import{existsSync,mkdirSync}from"node:fs";import{join,resolve}from"node:path";export function nativeScreenFiles(config,root,section="ios"){const dir=resolve(root,config.nativeScreensDir??"resources/native"),files={};for(const name of Object.values(config.nativeScreens??{})){if(files[name])continue;const file=join(dir,`${name}.stx`);if(!existsSync(file))throw Error(`${section}.nativeScreens names the screen "${name}", but ${file} does not exist.`);files[name]=file}return files}export function nativeAppFile(config,root,section="ios"){if(!config.nativeApp)return null;const file=resolve(root,config.nativeApp);if(!existsSync(file))throw Error(`${section}.nativeApp names ${config.nativeApp}, but ${file} does not exist.`);return file}export async function compileNativeScreens(config,root,output,compile,section="ios"){const screens=nativeScreenFiles(config,root,section),names=Object.keys(screens),app=nativeAppFile(config,root,section);if(!names.length){if(app)throw Error(`${section}.nativeApp runs beside native screens: list them in ${section}.nativeScreens`);return null}const compileBundle=compile??(await import("@stacksjs/stx/native")).compileNativeBundle;mkdirSync(output,{recursive:!0});const outFile=join(output,"native-screens.js"),result=await compileBundle({screens,initialScreen:names[0],minify:!0,outFile,root,...app?{app}:{}});return{outFile:result.outFile||outFile,diagnostics:result.diagnostics??[]}}
|
package/dist/bump.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{execSync,log,parseOptions}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{incrementVersion,isReleaseType,isValidVersion,versionBump}from"@stacksjs/bumpx";import{isCalendarBump,nextCalendarVersion}from"./calendar-version";import{pantryLockViolations}from"./pantry-lock";import{generateChangelog,loadLogsmithConfig}from"@stacksjs/logsmith";import{existsSync,readdirSync,readFileSync,unlinkSync,writeFileSync}from"node:fs";import{join,relative}from"node:path";const options=parseOptions(),rawBump=options?.bump?.toString(),bumpArg=rawBump?isReleaseType(rawBump)||isCalendarBump(rawBump)||isValidVersion(rawBump)?rawBump:null:null;if(rawBump&&!bumpArg)throw Error(`Invalid --bump "${rawBump}"; expected a Bumpx release type, calendar, or semantic version`);const isDryRun=options?.dryRun===!0,isVerbose=options?.verbose===!0;async function readPackage(file){if(!existsSync(file))return;return await Bun.file(file).json()}const rootManifest=p.projectPath("package.json"),rootPkg=await readPackage(rootManifest),isFrameworkRelease=rootPkg?.name==="stacks"&&existsSync(p.frameworkPath("core/package.json")),bumpCwd=isFrameworkRelease?p.frameworkPath("core"):p.projectPath(),primaryManifest=isFrameworkRelease?p.frameworkPath("core/package.json"):rootManifest;async function resolveBumpArg(bump){if(!bump)return bump;if(isCalendarBump(bump)){const pkg=await readPackage(primaryManifest);return nextCalendarVersion(pkg?.version)}const pkg=await readPackage(primaryManifest);if(!pkg?.version)throw Error(`${primaryManifest} declares no version`);return incrementVersion(pkg.version,bump,options?.preid)}const resolvedBumpArg=await resolveBumpArg(bumpArg);async function git(args,cwd=p.projectPath(),options={}){return await execSync(["git",...args],{cwd,stdin:"inherit",stderr:"pipe",throwOnError:options.throwOnError??!0})}async function isGitIgnored(file){return(await git(["check-ignore","--",file],p.projectPath(),{throwOnError:!1})).trim().length>0}async function readVersion(file){const pkg=await readPackage(file);if(!pkg?.version||!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(pkg.version))throw Error(`Invalid version in ${file}: ${pkg?.version??"<missing>"}`);return pkg.version}function lockfileVersion(contents){const match=contents.match(/"lockfileVersion"\s*:\s*(\d+)/);return match?Number(match[1]):null}async function packageFilesFor(pattern,cwd){const glob=new Bun.Glob(pattern),files=[];for await(const file of glob.scan({cwd,absolute:!0,onlyFiles:!0})){if(file.includes("/node_modules/")||file.includes("/dist/")||file.includes("/pantry/"))continue;files.push(`./${relative(bumpCwd,file)}`)}return files}async function existingPackageFiles(files){const existing=[];for(const file of files){const absolutePath=join(bumpCwd,file);if(await Bun.file(absolutePath).exists())existing.push(file)}return existing}async function frameworkBumpFiles(){return Array.from(new Set([...await packageFilesFor("package.json",bumpCwd),...await packageFilesFor("*/package.json",bumpCwd),...await packageFilesFor("*/*/package.json",bumpCwd),...await existingPackageFiles(["../package.json","../defaults/ide/vscode/package.json","../api/package.json","../cloud/package.json","../docs/package.json","../orm/package.json","../server/package.json"]),...await packageFilesFor("../libs/**/package.json",bumpCwd)]))}const bumpFiles=isFrameworkRelease?await frameworkBumpFiles():["./package.json"];log.debug(`Release mode: ${isFrameworkRelease?"framework monorepo":"consumer app"} (${rootPkg?.name??"unknown"})`);log.debug(`Bumping ${bumpFiles.length} package manifest(s) in ${bumpCwd}`);await versionBump({release:resolvedBumpArg??void 0,preid:options?.preid,files:bumpFiles,cwd:bumpCwd,recursive:!1,commit:!1,tag:!1,push:!1,changelog:!1,noGitCheck:!0,dryRun:isDryRun,yes:Boolean(resolvedBumpArg),verbose:isVerbose});const nextVersion=isDryRun&&resolvedBumpArg&&isValidVersion(resolvedBumpArg)?resolvedBumpArg:await readVersion(primaryManifest),latestTag=(await git(["describe","--abbrev=0","--tags"],p.projectPath(),{throwOnError:!1}).catch(()=>"")).trim();async function writeChangelog(){const config=await loadLogsmithConfig({dir:p.projectPath(),from:latestTag||void 0,to:"HEAD",output:isDryRun?!1:"CHANGELOG.md",theme:"github",verbose:isVerbose}),result=await generateChangelog(config);if(isDryRun){log.info(result.content);return}const changelogPath=p.projectPath("CHANGELOG.md");if(!existsSync(changelogPath))return;let content=readFileSync(changelogPath,"utf-8");const versionSeen=new RegExp(`\\bv?${nextVersion.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`);if(/\/compare\/[^)\s]+\.\.\.HEAD\)/.test(content))content=content.replace(/(\/compare\/[^)\s]+\.\.\.)HEAD(\))/,`$1v${nextVersion}$2`);else if(!versionSeen.test(content.split(`
|
|
1
|
+
import{execSync,log,parseOptions}from"@stacksjs/cli";import{path as p}from"@stacksjs/path";import{incrementVersion,isReleaseType,isValidVersion,versionBump}from"@stacksjs/bumpx";import{isCalendarBump,nextCalendarVersion}from"./calendar-version";import{describeGitHubRelease,publishGitHubRelease}from"./github-release";import{pantryLockViolations}from"./pantry-lock";import{generateChangelog,loadLogsmithConfig}from"@stacksjs/logsmith";import{existsSync,readdirSync,readFileSync,unlinkSync,writeFileSync}from"node:fs";import{join,relative}from"node:path";const options=parseOptions(),rawBump=options?.bump?.toString(),bumpArg=rawBump?isReleaseType(rawBump)||isCalendarBump(rawBump)||isValidVersion(rawBump)?rawBump:null:null;if(rawBump&&!bumpArg)throw Error(`Invalid --bump "${rawBump}"; expected a Bumpx release type, calendar, or semantic version`);const isDryRun=options?.dryRun===!0,isVerbose=options?.verbose===!0;async function readPackage(file){if(!existsSync(file))return;return await Bun.file(file).json()}const rootManifest=p.projectPath("package.json"),rootPkg=await readPackage(rootManifest),isFrameworkRelease=rootPkg?.name==="stacks"&&existsSync(p.frameworkPath("core/package.json")),bumpCwd=isFrameworkRelease?p.frameworkPath("core"):p.projectPath(),primaryManifest=isFrameworkRelease?p.frameworkPath("core/package.json"):rootManifest;async function resolveBumpArg(bump){if(!bump)return bump;if(isCalendarBump(bump)){const pkg=await readPackage(primaryManifest);return nextCalendarVersion(pkg?.version)}const pkg=await readPackage(primaryManifest);if(!pkg?.version)throw Error(`${primaryManifest} declares no version`);return incrementVersion(pkg.version,bump,options?.preid)}const resolvedBumpArg=await resolveBumpArg(bumpArg);async function git(args,cwd=p.projectPath(),options={}){return await execSync(["git",...args],{cwd,stdin:"inherit",stderr:"pipe",throwOnError:options.throwOnError??!0})}async function isGitIgnored(file){return(await git(["check-ignore","--",file],p.projectPath(),{throwOnError:!1})).trim().length>0}async function readVersion(file){const pkg=await readPackage(file);if(!pkg?.version||!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(pkg.version))throw Error(`Invalid version in ${file}: ${pkg?.version??"<missing>"}`);return pkg.version}function lockfileVersion(contents){const match=contents.match(/"lockfileVersion"\s*:\s*(\d+)/);return match?Number(match[1]):null}async function packageFilesFor(pattern,cwd){const glob=new Bun.Glob(pattern),files=[];for await(const file of glob.scan({cwd,absolute:!0,onlyFiles:!0})){if(file.includes("/node_modules/")||file.includes("/dist/")||file.includes("/pantry/"))continue;files.push(`./${relative(bumpCwd,file)}`)}return files}async function existingPackageFiles(files){const existing=[];for(const file of files){const absolutePath=join(bumpCwd,file);if(await Bun.file(absolutePath).exists())existing.push(file)}return existing}async function frameworkBumpFiles(){return Array.from(new Set([...await packageFilesFor("package.json",bumpCwd),...await packageFilesFor("*/package.json",bumpCwd),...await packageFilesFor("*/*/package.json",bumpCwd),...await existingPackageFiles(["../package.json","../defaults/ide/vscode/package.json","../api/package.json","../cloud/package.json","../docs/package.json","../orm/package.json","../server/package.json"]),...await packageFilesFor("../libs/**/package.json",bumpCwd)]))}const bumpFiles=isFrameworkRelease?await frameworkBumpFiles():["./package.json"];log.debug(`Release mode: ${isFrameworkRelease?"framework monorepo":"consumer app"} (${rootPkg?.name??"unknown"})`);log.debug(`Bumping ${bumpFiles.length} package manifest(s) in ${bumpCwd}`);await versionBump({release:resolvedBumpArg??void 0,preid:options?.preid,files:bumpFiles,cwd:bumpCwd,recursive:!1,commit:!1,tag:!1,push:!1,changelog:!1,noGitCheck:!0,dryRun:isDryRun,yes:Boolean(resolvedBumpArg),verbose:isVerbose});const nextVersion=isDryRun&&resolvedBumpArg&&isValidVersion(resolvedBumpArg)?resolvedBumpArg:await readVersion(primaryManifest),latestTag=(await git(["describe","--abbrev=0","--tags"],p.projectPath(),{throwOnError:!1}).catch(()=>"")).trim();async function writeChangelog(){const config=await loadLogsmithConfig({dir:p.projectPath(),from:latestTag||void 0,to:"HEAD",output:isDryRun?!1:"CHANGELOG.md",theme:"github",verbose:isVerbose}),result=await generateChangelog(config);if(isDryRun){log.info(result.content);return}const changelogPath=p.projectPath("CHANGELOG.md");if(!existsSync(changelogPath))return;let content=readFileSync(changelogPath,"utf-8");const versionSeen=new RegExp(`\\bv?${nextVersion.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`);if(/\/compare\/[^)\s]+\.\.\.HEAD\)/.test(content))content=content.replace(/(\/compare\/[^)\s]+\.\.\.)HEAD(\))/,`$1v${nextVersion}$2`);else if(!versionSeen.test(content.split(`
|
|
2
2
|
`).slice(0,4).join(`
|
|
3
3
|
`)))content=content.replace(/^(#\s.*\n+)?/,(match)=>`${match??""}## v${nextVersion}
|
|
4
4
|
|
|
@@ -9,4 +9,6 @@ import{execSync,log,parseOptions}from"@stacksjs/cli";import{path as p}from"@stac
|
|
|
9
9
|
`).map((line)=>line.trimEnd()).filter((line)=>line&&!line.startsWith("A ")&&!line.startsWith("M "));if(leftover.length)log.warn(`Kept ${leftover.length} change(s) out of the release commit:
|
|
10
10
|
${leftover.slice(0,20).join(`
|
|
11
11
|
`)}${leftover.length>20?`
|
|
12
|
-
\u2026 and ${leftover.length-20} more`:""}`)}if(!isDryRun){await stageReleaseArtifacts();await git(["commit","-m",`chore: release v${nextVersion}`]);await git(["tag",`v${nextVersion}`]);const branch=(await git(["symbolic-ref","--short","HEAD"])).trim();await git(["push","--atomic","origin",`HEAD:refs/heads/${branch}`,`refs/tags/v${nextVersion}`])}
|
|
12
|
+
\u2026 and ${leftover.length-20} more`:""}`)}if(!isDryRun){await stageReleaseArtifacts();await git(["commit","-m",`chore: release v${nextVersion}`]);await git(["tag",`v${nextVersion}`]);const branch=(await git(["symbolic-ref","--short","HEAD"])).trim();await git(["push","--atomic","origin",`HEAD:refs/heads/${branch}`,`refs/tags/v${nextVersion}`])}if(!isFrameworkRelease&&options?.skipGithubRelease!==!0){const tag=`v${nextVersion}`,result=await publishGitHubRelease({cwd:p.projectPath(),tag,ref:isDryRun?"HEAD":void 0,dryRun:isDryRun});if(isDryRun&&result.draft)log.info(`Would create the GitHub release ${tag}:
|
|
13
|
+
|
|
14
|
+
${result.draft.body}`);else if(result.status==="failed")log.warn(describeGitHubRelease(tag,result));else log.info(describeGitHubRelease(tag,result))}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** The GitHub repository a remote URL points at, or null for any other host. */
|
|
2
|
+
export declare function parseGitHubRemote(url: string | undefined): GitHubRepository | null;
|
|
3
|
+
/**
|
|
4
|
+
* A token that can create releases: the environment's, then the GitHub CLI's,
|
|
5
|
+
* then the git credential the push itself used.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveGitHubToken(env?: Record<string, string | undefined>, run?: RunCommand): string | null;
|
|
8
|
+
/** The body cut to GitHub's limit, pointing at the full comparison when cut. */
|
|
9
|
+
export declare function fitReleaseBody(body: string, compareUrl?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Create the release for `draft.tag`, unless that tag already has one.
|
|
12
|
+
* Throws on an API error; {@link publishGitHubRelease} turns that into a warning.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createGitHubRelease(repository: GitHubRepository, draft: GitHubReleaseDraft, options: CreateGitHubReleaseOptions): Promise<{ url: string, created: boolean }>;
|
|
15
|
+
/** The tag before `tag` in its history, or undefined for the first release. */
|
|
16
|
+
export declare function previousTag(cwd: string, tag: string): string | undefined;
|
|
17
|
+
/** The newest tag at or before `ref`, or undefined when there is none. */
|
|
18
|
+
export declare function latestTag(cwd: string, ref?: string): string | undefined;
|
|
19
|
+
/** The GitHub repository `origin` points at, or null. */
|
|
20
|
+
export declare function originRepository(cwd: string): GitHubRepository | null;
|
|
21
|
+
/** The commits between the previous tag and `tag`, as logsmith renders them. */
|
|
22
|
+
export declare function releaseNotes(options: { cwd: string, tag: string, from?: string, repository: GitHubRepository }): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Create the GitHub release for a pushed tag, with the changes since the tag
|
|
25
|
+
* before it. Never throws: the result says what happened and why.
|
|
26
|
+
*/
|
|
27
|
+
export declare function publishGitHubRelease(options: PublishGitHubReleaseOptions): Promise<GitHubReleaseResult & { draft?: GitHubReleaseDraft }>;
|
|
28
|
+
/** One line for the release log, with the retry command when it failed. */
|
|
29
|
+
export declare function describeGitHubRelease(tag: string, result: GitHubReleaseResult): string;
|
|
30
|
+
export declare const GITHUB_API_URL: 'https://api.github.com';
|
|
31
|
+
/** GitHub's limit on a release body is 125,000 characters; stay clear of it. */
|
|
32
|
+
export declare const MAX_RELEASE_BODY: unknown;
|
|
33
|
+
export declare interface GitHubRepository {
|
|
34
|
+
owner: string
|
|
35
|
+
repo: string
|
|
36
|
+
}
|
|
37
|
+
export declare interface GitHubReleaseDraft {
|
|
38
|
+
tag: string
|
|
39
|
+
name: string
|
|
40
|
+
body: string
|
|
41
|
+
prerelease?: boolean
|
|
42
|
+
}
|
|
43
|
+
export declare interface GitHubReleaseResult {
|
|
44
|
+
status: 'created' | 'exists' | 'skipped' | 'failed'
|
|
45
|
+
url?: string
|
|
46
|
+
reason?: string
|
|
47
|
+
}
|
|
48
|
+
export declare interface CreateGitHubReleaseOptions {
|
|
49
|
+
token: string
|
|
50
|
+
apiUrl?: string
|
|
51
|
+
fetch?: typeof globalThis.fetch
|
|
52
|
+
}
|
|
53
|
+
export declare interface PublishGitHubReleaseOptions {
|
|
54
|
+
cwd: string
|
|
55
|
+
tag: string
|
|
56
|
+
name?: string
|
|
57
|
+
summary?: string
|
|
58
|
+
prerelease?: boolean
|
|
59
|
+
ref?: string
|
|
60
|
+
dryRun?: boolean
|
|
61
|
+
env?: Record<string, string | undefined>
|
|
62
|
+
fetch?: typeof globalThis.fetch
|
|
63
|
+
run?: RunCommand
|
|
64
|
+
}
|
|
65
|
+
declare type RunCommand = (cmd: string[], input?: string) => { exitCode: number, stdout: string }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import process from"node:process";export const GITHUB_API_URL="https://api.github.com",MAX_RELEASE_BODY=120000;function runCommand(cmd,input){try{const result=Bun.spawnSync(cmd,{stdin:input===void 0?"ignore":Buffer.from(input),stdout:"pipe",stderr:"ignore",env:{...process.env,GIT_TERMINAL_PROMPT:"0",GH_PROMPT_DISABLED:"1"}});return{exitCode:result.exitCode??1,stdout:result.stdout?.toString()??""}}catch{return{exitCode:127,stdout:""}}}export function parseGitHubRemote(url){const match=url?.trim().match(/^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|(?:ssh:\/\/)?git@github\.com[:/])([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/);if(!match)return null;return{owner:match[1],repo:match[2]}}export function resolveGitHubToken(env=process.env,run=runCommand){for(const key of["GITHUB_TOKEN","GH_TOKEN"]){const value=env[key]?.trim();if(value)return value}const gh=run(["gh","auth","token","--hostname","github.com"]);if(gh.exitCode===0&&gh.stdout.trim())return gh.stdout.trim();const credential=run(["git","credential","fill"],`protocol=https
|
|
2
|
+
host=github.com
|
|
3
|
+
|
|
4
|
+
`);if(credential.exitCode===0){const password=credential.stdout.split(`
|
|
5
|
+
`).find((line)=>line.startsWith("password="))?.slice(9).trim();if(password)return password}return null}export function fitReleaseBody(body,compareUrl){if(body.length<=MAX_RELEASE_BODY)return body;const note=`
|
|
6
|
+
|
|
7
|
+
\u2026the notes were longer than GitHub allows${compareUrl?`; see [every change](${compareUrl})`:""}.
|
|
8
|
+
`,cut=body.slice(0,MAX_RELEASE_BODY-note.length);return`${cut.slice(0,Math.max(0,cut.lastIndexOf(`
|
|
9
|
+
`)))}${note}`}export async function createGitHubRelease(repository,draft,options){const fetcher=options.fetch??globalThis.fetch,base=`${(options.apiUrl??GITHUB_API_URL).replace(/\/$/,"")}/repos/${repository.owner}/${repository.repo}/releases`,headers={Authorization:`Bearer ${options.token}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"},existing=await fetcher(`${base}/tags/${encodeURIComponent(draft.tag)}`,{headers});if(existing.ok)return{url:(await existing.json()).html_url,created:!1};if(existing.status!==404)throw Error(`GitHub API ${existing.status} looking up the release for ${draft.tag}: ${await apiMessage(existing)}`);const response=await fetcher(base,{method:"POST",headers:{...headers,"Content-Type":"application/json"},body:JSON.stringify({tag_name:draft.tag,name:draft.name,body:draft.body,draft:!1,prerelease:draft.prerelease??!1})});if(!response.ok)throw Error(`GitHub API ${response.status} creating the release for ${draft.tag}: ${await apiMessage(response)}`);return{url:(await response.json()).html_url,created:!0}}async function apiMessage(response){const text=await response.text().catch(()=>"");try{return JSON.parse(text).message||text||response.statusText}catch{return text||response.statusText}}function git(cwd,args){const result=Bun.spawnSync(["git",...args],{cwd,stdout:"pipe",stderr:"ignore"});return result.exitCode===0?result.stdout.toString().trim():null}export function previousTag(cwd,tag){return git(cwd,["describe","--tags","--abbrev=0",`${tag}^`])||void 0}export function latestTag(cwd,ref="HEAD"){return git(cwd,["describe","--tags","--abbrev=0",ref])||void 0}export function originRepository(cwd){return parseGitHubRemote(git(cwd,["remote","get-url","origin"])??void 0)}export async function releaseNotes(options){const{generateChangelog,loadLogsmithConfig}=await import("@stacksjs/logsmith"),repoUrl=`https://github.com/${options.repository.owner}/${options.repository.repo}`,config=await loadLogsmithConfig({dir:options.cwd,from:options.from,to:options.tag,output:!1,theme:"github",repo:repoUrl,hideAuthorEmail:!0});return(await generateChangelog(config)).content.trim()||`No changes since ${options.from??"the start of the history"}.`}export async function publishGitHubRelease(options){const repository=originRepository(options.cwd);if(!repository)return{status:"skipped",reason:"origin is not a GitHub repository"};try{const from=options.ref?latestTag(options.cwd,options.ref):previousTag(options.cwd,options.tag),notes=await releaseNotes({cwd:options.cwd,tag:options.ref??options.tag,from,repository}),compareUrl=from?`https://github.com/${repository.owner}/${repository.repo}/compare/${from}...${options.tag}`:void 0,draft={tag:options.tag,name:options.name??options.tag,body:fitReleaseBody(options.summary?`${options.summary.trim()}
|
|
10
|
+
|
|
11
|
+
${notes}`:notes,compareUrl),prerelease:options.prerelease};if(options.dryRun)return{status:"skipped",reason:"dry run",draft};const token=resolveGitHubToken(options.env,options.run);if(!token)return{status:"failed",reason:"no GitHub token (set GITHUB_TOKEN, or sign in with `gh auth login`)",draft};const release=await createGitHubRelease(repository,draft,{token,fetch:options.fetch});return{status:release.created?"created":"exists",url:release.url,draft}}catch(error){return{status:"failed",reason:error instanceof Error?error.message:String(error)}}}export function describeGitHubRelease(tag,result){switch(result.status){case"created":return`Created the GitHub release ${result.url}`;case"exists":return`${tag} already has a GitHub release: ${result.url}`;case"skipped":return`No GitHub release for ${tag}: ${result.reason}`;case"failed":return`Could not create the GitHub release for ${tag} (${result.reason}). The tag is pushed; run \`buddy release:github ${tag}\` to finish it.`}}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { GitHubReleaseResult, PublishGitHubReleaseOptions } from './github-release';
|
|
2
|
+
export type { PantryTool } from './build/ios-toolchain';
|
|
1
3
|
export type { StxLintConfig, StxLintReport, StxLintResult } from './lint/stx-gate';
|
|
2
4
|
export type { RouteActionReference } from './upgrade/route-actions';
|
|
3
5
|
export type { ProjectStructureChange } from './upgrade/package-project';
|
|
@@ -31,6 +33,10 @@ export * from './library/index';
|
|
|
31
33
|
// spawning a deep dist path. Exported from ./lint/lint — the pure module — so
|
|
32
34
|
// importing the barrel never pulls the process-exiting ./lint/index script.
|
|
33
35
|
export { formatProject, lintFix, lintProject } from './lint/lint';
|
|
36
|
+
// Release helpers the release commands call in-process: the GitHub release a
|
|
37
|
+
// pushed tag gets, and the Pantry-managed tools behind `build:ios`.
|
|
38
|
+
export { describeGitHubRelease, parseGitHubRemote, publishGitHubRelease } from './github-release';
|
|
39
|
+
export { DEFAULT_IOS_DEPENDENCIES, IosToolchainError, loadIosDependencies, pantryExecutable, pantryInstallArgs, resolveXcodegen, XCODE_NOTE, xcodeDeveloperDir } from './build/ios-toolchain';
|
|
34
40
|
// stx conformance checks behind `buddy lint --stx`. Pure - returns a report
|
|
35
41
|
// and never exits, so the command owns rendering and the exit code.
|
|
36
42
|
export { DEFAULT_STX_LINT_CONFIG, loadStxLintConfig, runStxLint } from './lint/stx-gate';
|
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{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
|
+
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{describeGitHubRelease,parseGitHubRemote,publishGitHubRelease}from"./github-release";export{DEFAULT_IOS_DEPENDENCIES,IosToolchainError,loadIosDependencies,pantryExecutable,pantryInstallArgs,resolveXcodegen,XCODE_NOTE,xcodeDeveloperDir}from"./build/ios-toolchain";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";
|
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.
|
|
5
|
+
"version": "0.75.175",
|
|
6
6
|
"description": "The Stacks actions.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -66,53 +66,53 @@
|
|
|
66
66
|
"prepublishOnly": "bun run build"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@stacksjs/action-runner": "0.75.
|
|
70
|
-
"@stacksjs/ai": "0.75.
|
|
71
|
-
"@stacksjs/analytics": "0.75.
|
|
72
|
-
"@stacksjs/api": "0.75.
|
|
73
|
-
"@stacksjs/auth": "0.75.
|
|
74
|
-
"@stacksjs/build": "0.75.
|
|
69
|
+
"@stacksjs/action-runner": "0.75.175",
|
|
70
|
+
"@stacksjs/ai": "0.75.175",
|
|
71
|
+
"@stacksjs/analytics": "0.75.175",
|
|
72
|
+
"@stacksjs/api": "0.75.175",
|
|
73
|
+
"@stacksjs/auth": "0.75.175",
|
|
74
|
+
"@stacksjs/build": "0.75.175",
|
|
75
75
|
"@stacksjs/bumpx": "^0.2.21",
|
|
76
76
|
"@stacksjs/bunpress": "^0.2.15",
|
|
77
|
-
"@stacksjs/cli": "0.75.
|
|
78
|
-
"@stacksjs/cloud": "0.75.
|
|
79
|
-
"@stacksjs/cms": "0.75.
|
|
77
|
+
"@stacksjs/cli": "0.75.175",
|
|
78
|
+
"@stacksjs/cloud": "0.75.175",
|
|
79
|
+
"@stacksjs/cms": "0.75.175",
|
|
80
80
|
"@stacksjs/components": "^0.2.483",
|
|
81
|
-
"@stacksjs/config": "0.75.
|
|
82
|
-
"@stacksjs/database": "0.75.
|
|
83
|
-
"@stacksjs/desktop-build": "0.75.
|
|
84
|
-
"@stacksjs/dns": "0.75.
|
|
85
|
-
"@stacksjs/enums": "0.75.
|
|
86
|
-
"@stacksjs/env": "0.75.
|
|
87
|
-
"@stacksjs/error-handling": "0.75.
|
|
81
|
+
"@stacksjs/config": "0.75.175",
|
|
82
|
+
"@stacksjs/database": "0.75.175",
|
|
83
|
+
"@stacksjs/desktop-build": "0.75.175",
|
|
84
|
+
"@stacksjs/dns": "0.75.175",
|
|
85
|
+
"@stacksjs/enums": "0.75.175",
|
|
86
|
+
"@stacksjs/env": "0.75.175",
|
|
87
|
+
"@stacksjs/error-handling": "0.75.175",
|
|
88
88
|
"@stacksjs/gitit": "^0.2.5",
|
|
89
|
-
"@stacksjs/i18n": "0.75.
|
|
90
|
-
"@stacksjs/image": "0.75.
|
|
91
|
-
"@stacksjs/logging": "0.75.
|
|
89
|
+
"@stacksjs/i18n": "0.75.175",
|
|
90
|
+
"@stacksjs/image": "0.75.175",
|
|
91
|
+
"@stacksjs/logging": "0.75.175",
|
|
92
92
|
"@stacksjs/logsmith": "^0.2.11",
|
|
93
|
-
"@stacksjs/mobile": "0.75.
|
|
94
|
-
"@stacksjs/orm": "0.75.
|
|
95
|
-
"@stacksjs/path": "0.75.
|
|
96
|
-
"@stacksjs/payments": "0.75.
|
|
97
|
-
"@stacksjs/queue": "0.75.
|
|
98
|
-
"@stacksjs/registry": "0.75.
|
|
99
|
-
"@stacksjs/router": "0.75.
|
|
93
|
+
"@stacksjs/mobile": "0.75.175",
|
|
94
|
+
"@stacksjs/orm": "0.75.175",
|
|
95
|
+
"@stacksjs/path": "0.75.175",
|
|
96
|
+
"@stacksjs/payments": "0.75.175",
|
|
97
|
+
"@stacksjs/queue": "0.75.175",
|
|
98
|
+
"@stacksjs/registry": "0.75.175",
|
|
99
|
+
"@stacksjs/router": "0.75.175",
|
|
100
100
|
"@stacksjs/rpx": "^0.11.54",
|
|
101
|
-
"@stacksjs/scheduler": "0.75.
|
|
102
|
-
"@stacksjs/search-engine": "0.75.
|
|
103
|
-
"@stacksjs/security": "0.75.
|
|
104
|
-
"@stacksjs/server": "0.75.
|
|
105
|
-
"@stacksjs/sites": "0.75.
|
|
106
|
-
"@stacksjs/storage": "0.75.
|
|
107
|
-
"@stacksjs/strings": "0.75.
|
|
101
|
+
"@stacksjs/scheduler": "0.75.175",
|
|
102
|
+
"@stacksjs/search-engine": "0.75.175",
|
|
103
|
+
"@stacksjs/security": "0.75.175",
|
|
104
|
+
"@stacksjs/server": "0.75.175",
|
|
105
|
+
"@stacksjs/sites": "0.75.175",
|
|
106
|
+
"@stacksjs/storage": "0.75.175",
|
|
107
|
+
"@stacksjs/strings": "0.75.175",
|
|
108
108
|
"@stacksjs/stx": "^0.2.483",
|
|
109
109
|
"@stacksjs/tlsx": "^0.13.19",
|
|
110
110
|
"@stacksjs/ts-cloud": "^0.16.52",
|
|
111
111
|
"@stacksjs/ts-md": "^0.1.1",
|
|
112
|
-
"@stacksjs/tunnel": "0.75.
|
|
113
|
-
"@stacksjs/types": "0.75.
|
|
114
|
-
"@stacksjs/utils": "0.75.
|
|
115
|
-
"@stacksjs/whois": "0.75.
|
|
112
|
+
"@stacksjs/tunnel": "0.75.175",
|
|
113
|
+
"@stacksjs/types": "0.75.175",
|
|
114
|
+
"@stacksjs/utils": "0.75.175",
|
|
115
|
+
"@stacksjs/whois": "0.75.175",
|
|
116
116
|
"bun-plugin-stx": "^0.2.483"
|
|
117
117
|
},
|
|
118
118
|
"peerDependencies": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
130
|
"devDependencies": {
|
|
131
|
-
"@stacksjs/validation": "0.75.
|
|
131
|
+
"@stacksjs/validation": "0.75.175",
|
|
132
132
|
"better-dx": "^0.2.24"
|
|
133
133
|
}
|
|
134
134
|
}
|